DRV8874Driver (AI coded)
  1. from machine import Pin, PWM, Timer
  2. import time
  3.  
  4. # ==============================================================================
  5. # 1. HILFSFUNKTION: BERECHNUNG DES IPROPI-WIDERSTANDS
  6. # ==============================================================================
  7. def calculate_ipropi_resistor(vref: float, itrip_max: float) -> float:
  8. """Berechnet den benötigten Widerstandswert (R_IPROPI) für den DRV8874."""
  9. A_IPROPI = 450e-6
  10. if itrip_max <= 0 or vref <= 0:
  11. raise ValueError("VREF und maximaler Strom müssen größer als 0 sein.")
  12. return vref / (itrip_max * A_IPROPI)
  13.  
  14.  
  15. # ==============================================================================
  16. # 2. TREIBER-KLASSE MIT OPTIONALER TIMER-ÜBERGABE
  17. # ==============================================================================
  18. class DRV8874Driver:
  19. def __init__(self, pwm_in1, pwm_in2, pin_nfault, pin_nsleep, use_interrupt=False, use_softstart=False, timer=None):
  20. """
  21. Initialisiert den DRV8874 Motortreiber im nicht-blockierenden Modus.
  22.  
  23. :param pwm_in1: machine.PWM Objekt für IN1
  24. :param pwm_in2: machine.PWM Objekt für IN2
  25. :param pin_nfault: machine.Pin Objekt (Eingang mit PULL_UP)
  26. :param pin_nsleep: machine.Pin Objekt (Ausgang)
  27. :param use_interrupt: Wenn True, wird ein Hardware-Interrupt auf nFAULT gesetzt
  28. :param use_softstart: Wenn True, rampt der Motor sanft auf die Zielgeschwindigkeit
  29. :param timer: Optionales machine.Timer Objekt. Wenn None, wird ein interner Timer erstellt.
  30. """
  31. self.in1_pwm = pwm_in1
  32. self.in2_pwm = pwm_in2
  33. self.nfault = pin_nfault
  34. self.nsleep = pin_nsleep
  35.  
  36. self.interrupt_active = use_interrupt
  37. self.softstart_active = use_softstart
  38.  
  39. self.has_fault = False
  40. self.current_duty = 0
  41. self.target_duty = 0
  42. self.duty_step = 0
  43. self.current_direction = None
  44.  
  45. # Timer zuweisen oder automatisch erstellen
  46. if timer is not None:
  47. self.ramp_timer = timer
  48. print("--> Externer Timer für Soft-Start zugewiesen.")
  49. else:
  50. # Fallback auf Standard-Timer, falls kein externer übergeben wurde
  51. self.ramp_timer = Timer(-1) if hasattr(Timer, "KEYBOARD_INTERRUPT") else Timer(0)
  52. print("--> Interner Timer für Soft-Start automatisch erstellt.")
  53.  
  54. # Chip aufwachen lassen & stoppen
  55. self.nsleep.value(1)
  56. self.stop(mode='brake')
  57.  
  58. if self.interrupt_active:
  59. self.nfault.irq(trigger=Pin.IRQ_FALLING, handler=self._fault_isr)
  60. print("--> Hardware-Interrupt für nFAULT aktiviert.")
  61. if self.softstart_active:
  62. print("--> Nicht-blockierender Soft-Start aktiviert.")
  63.  
  64. def _fault_isr(self, pin):
  65. """Interne ISR bei Überstrom. Schaltet sofort aus und stoppt den Timer."""
  66. self.in1_pwm.duty_u16(0)
  67. self.in2_pwm.duty_u16(0)
  68. self.ramp_timer.deinit()
  69. self.has_fault = True
  70.  
  71. def _timer_ramp_callback(self, t):
  72. """Wird im Hintergrund periodisch aufgerufen, um die PWM zu erhöhen."""
  73. if self.check_fault():
  74. self.stop(mode='coast')
  75. return
  76.  
  77. if abs(self.target_duty - self.current_duty) <= abs(self.duty_step):
  78. self.current_duty = self.target_duty
  79. self.ramp_timer.deinit()
  80. else:
  81. self.current_duty += self.duty_step
  82.  
  83. self._apply_pwm(self.current_direction, self.current_duty)
  84.  
  85. def check_fault(self) -> bool:
  86. """Prüft auf Fehler."""
  87. if self.interrupt_active:
  88. return self.has_fault
  89. else:
  90. return self.nfault.value() == 0
  91.  
  92. def reset_fault(self):
  93. """Fehler zurücksetzen."""
  94. print("Setze DRV8874 via nSLEEP zurück...")
  95. self.stop(mode='brake')
  96. self.has_fault = False
  97. self.nsleep.value(0)
  98. time.sleep_ms(5)
  99. self.nsleep.value(1)
  100. time.sleep_ms(5)
  101.  
  102. def stop(self, mode='brake'):
  103. """Stoppt den Motor SOFORT und bricht jede aktive Beschleunigungsrampe ab."""
  104. self.ramp_timer.deinit()
  105.  
  106. if mode == 'brake':
  107. self.in1_pwm.duty_u16(0)
  108. self.in2_pwm.duty_u16(0)
  109. elif mode == 'coast':
  110. self.in1_pwm.duty_u16(0)
  111. self.in2_pwm.duty_u16(0)
  112. self.nsleep.value(0)
  113.  
  114. self.current_duty = 0
  115. self.target_duty = 0
  116. self.current_direction = None
  117.  
  118. def drive_motor(self, direction: str, speed_percent: float, duration_ms=500):
  119. """Startet den Motor oder ändert die Geschwindigkeit verzögerungsfrei."""
  120. self.nsleep.value(1)
  121. self.ramp_timer.deinit()
  122.  
  123. if self.check_fault():
  124. print("WARNUNG: Motor kann nicht starten. nFAULT ist aktiv!")
  125. return
  126.  
  127. if speed_percent > 100.0: speed_percent = 100.0
  128. if speed_percent < 0.0: speed_percent = 0.0
  129.  
  130. self.target_duty = int((speed_percent / 100.0) * 65535)
  131. self.current_direction = direction.lower()
  132.  
  133. if self.softstart_active and self.target_duty > self.current_duty:
  134. step_interval_ms = 20
  135. total_steps = duration_ms // step_interval_ms
  136. if total_steps < 1: total_steps = 1
  137.  
  138. self.duty_step = (self.target_duty - self.current_duty) // total_steps
  139. if self.duty_step == 0: self.duty_step = 1
  140.  
  141. self.ramp_timer.init(period=step_interval_ms, mode=Timer.PERIODIC, callback=self._timer_ramp_callback)
  142. else:
  143. self.current_duty = self.target_duty
  144. self._apply_pwm(self.current_direction, self.current_duty)
  145.  
  146. def _apply_pwm(self, direction, duty):
  147. if direction == 'forward':
  148. self.in2_pwm.duty_u16(0)
  149. self.in1_pwm.duty_u16(duty)
  150. elif direction == 'backward':
  151. self.in1_pwm.duty_u16(0)
  152. self.in2_pwm.duty_u16(duty)
  153.  
  154.  
  155. # ==============================================================================
  156. # 3. HAUPTPROGRAMM (ANWENDUNGSBEISPIEL MIT EXTERNEM TIMER)
  157. # ==============================================================================
  158.  
  159. # Hardware-Pins konfigurieren (Beispiel für Raspberry Pi Pico)
  160. pwm1 = PWM(Pin(16)); pwm2 = PWM(Pin(17))
  161. pwm1.freq(20000); pwm2.freq(20000)
  162. fault_pin = Pin(18, Pin.IN, Pin.PULL_UP)
  163. sleep_pin = Pin(19, Pin.OUT)
  164.  
  165. # --- Schritt A: Timer-Objekt AUSSERHALB erstellen ---
  166. # Wir deklarieren hier einen dedizierten Hardware-Timer (z.B. Timer ID 1)
  167. mein_externer_timer = Timer(1)
  168.  
  169. # --- Schritt B: Objekt initialisieren und den externen Timer übergeben ---
  170. motor = DRV8874Driver(
  171. pwm_in1=pwm1,
  172. pwm_in2=pwm2,
  173. pin_nfault=fault_pin,
  174. pin_nsleep=sleep_pin,
  175. use_interrupt=True,
  176. use_softstart=True,
  177. timer=mein_externer_timer # <-- Hier wird das externe Objekt übergeben
  178. )
  179.  
  180. # Motor testen
  181. print("Starte Motor...")
  182. motor.drive_motor('forward', 100.0, duration_ms=1000)
  183.  
Pasted 2026-10-13 23:42:10

Short link: