Sorry, I’m totally confused now. The applied voltage is a constant 15V. I want to use PWM to regulate this 15V so that the coreless motor (the locomotive) runs slowly, brakes to a stop, and then slowly starts again. What I meant by the sentence I mentioned is that the coreless motor runs at full load in both directions if I adjust the code so that it doesn’t use PWM. Once I implement PWM in the code, it no longer works.
This Code works but without PWM!
#include <Arduino.h>
// --- Pin-Definitionen ---
const int MOTOR_IN1_PIN = D2; // XIAO Pin D2 verbunden mit IN1 des DRV8871
const int MOTOR_IN2_PIN = D1; // XIAO Pin D1 verbunden mit IN2 des DRV8871
const unsigned long PHASE_DURATION_MS = 5000; // 5 Sekunden pro Phase
// --- Haupt-Setup-Funktion ---
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("--- Starte DRV8871 DIRECT HIGH/LOW TEST ---");
Serial.println("Dies testet die 4 Grundzustaende des DRV8871 mit festen HIGH/LOW Signalen.");
Serial.println("Keine PWM in diesem Test, Motor faehrt mit Vollgas.");
Serial.println("Pruefen Sie das Motorverhalten und die Ausgaben am Seriellen Monitor.");
pinMode(MOTOR_IN1_PIN, OUTPUT);
pinMode(MOTOR_IN2_PIN, OUTPUT);
// Initialer Zustand: Beide LOW (Freilauf), Motor sollte sich nicht bewegen
digitalWrite(MOTOR_IN1_PIN, LOW);
digitalWrite(MOTOR_IN2_PIN, LOW);
Serial.println("Initialisierung: IN1=LOW, IN2=LOW (Freilauf)");
delay(2000); // Kurze Wartezeit vor dem ersten Test
}
// --- Haupt-Loop-Funktion ---
void loop() {
// --- Phase 1: Motor dreht in eine Richtung (z.B. Vorwärts in Ihrem Test) ---
// DRV8871 Datenblatt: IN1=HIGH, IN2=LOW
Serial.println("\n--- Phase 1: MOTOR EINE RICHTUNG (IN1=HIGH, IN2=LOW) ---");
digitalWrite(MOTOR_IN1_PIN, HIGH); // D2 auf HIGH setzen
digitalWrite(MOTOR_IN2_PIN, LOW); // D1 auf LOW setzen
delay(PHASE_DURATION_MS);
// --- Phase 2: Stillstand (Freilauf) ---
// DRV8871 Datenblatt: IN1=LOW, IN2=LOW
Serial.println("\n--- Phase 2: MOTOR STILLSTAND (FREILAUF: IN1=LOW, IN2=LOW) ---");
digitalWrite(MOTOR_IN1_PIN, LOW); // D2 auf LOW setzen
digitalWrite(MOTOR_IN2_PIN, LOW); // D1 auf LOW setzen
delay(PHASE_DURATION_MS);
// --- Phase 3: Motor dreht in die andere Richtung (z.B. Rückwärts in Ihrem Test) ---
// DRV8871 Datenblatt: IN1=LOW, IN2=HIGH
Serial.println("\n--- Phase 3: MOTOR ANDERE RICHTUNG (IN1=LOW, IN2=HIGH) ---");
digitalWrite(MOTOR_IN1_PIN, LOW); // D2 auf LOW setzen
digitalWrite(MOTOR_IN2_PIN, HIGH); // D1 auf HIGH setzen
delay(PHASE_DURATION_MS);
// --- Phase 4: Aktives Bremsen ---
// DRV8871 Datenblatt: IN1=HIGH, IN2=HIGH
Serial.println("\n--- Phase 4: MOTOR STILLSTAND (BREMSE: IN1=HIGH, IN2=HIGH) ---");
digitalWrite(MOTOR_IN1_PIN, HIGH); // D2 auf HIGH setzen
digitalWrite(MOTOR_IN2_PIN, HIGH); // D1 auf HIGH setzen
delay(PHASE_DURATION_MS);
}
and this dosn’t Work
#include <Arduino.h>
#include <samd21/include/samd21.h> // Für direkten Registerzugriff auf dem SAMD21
// --- Pin-Definitionen ---
// XIAO D2 (PA10) für PWM an DRV8871 IN1
// XIAO D1 (PA04) für Richtung an DRV8871 IN2
const int MOTOR_PWM_PIN = D2; // XIAO Pin D2 verbunden mit IN1 des DRV8871 (PWM)
const int MOTOR_DIR_PIN = D1; // XIAO Pin D1 verbunden mit IN2 des DRV8871 (Richtung HIGH/LOW)
// --- PWM-Konfiguration ---
const long PWM_FREQUENCY = 32000; // 32.000 Hz für unhörbaren Betrieb
const int PWM_RESOLUTION_BITS = 10; // 10 Bit Auflösung (0-1023)
const int PWM_MAX_VALUE = (1 << PWM_RESOLUTION_BITS) - 1; // z.B. 1023 für 10 Bit
// --- Rampen- und Zeitkonfiguration ---
const int PWM_RAMP_STEP = 5; // Schrittweite pro Rampe (kleiner für sanftere Rampe)
const unsigned long RAMP_INTERVAL_MS = 20; // Zeit zwischen PWM-Änderungen (ms) für Rampe (länger für sanftere Rampe)
const int TARGET_SPEED = 150; // Zielgeschwindigkeit (von max 1023)
const unsigned long FAHR_ZEIT_MS = 8000; // Fahrzeit bei voller Geschwindigkeit (Millisekunden)
const unsigned long HALTE_ZEIT_MS = 5000; // Haltezeit an den Enden (Millisekunden)
// --- Zustandsvariablen ---
int currentMotorPWM = 0; // Aktueller PWM-Wert des Motors (0 bis PWM_MAX_VALUE)
unsigned long lastRampUpdateTime = 0;
unsigned long currentStateStartTime = 0; // Zeitstempel wann der aktuelle Zustand begann
// Enum für die Zustandsmaschine der Pendelzugsteuerung
enum LocoState {
STATE_ACCELERATE_FWD, // Beschleunigen vorwärts
STATE_DRIVE_FWD, // Fahren vorwärts
STATE_DECELERATE_FWD, // Abbremsen vorwärts
STATE_HALT_FWD_END, // Halten am Ende der Vorwärtsfahrt
STATE_ACCELERATE_BWD, // Beschleunigen rückwärts
STATE_DRIVE_BWD, // Fahren rückwärts
STATE_DECELERATE_BWD, // Abbremsen rückwärts
STATE_HALT_BWD_END // Halten am Ende der Rückwärtsfahrt
};
LocoState currentLocoState = STATE_ACCELERATE_FWD; // Startzustand
// --- Funktion zur Hochfrequenz-PWM-Konfiguration (für TCC1 auf D2) ---
// D2 ist PA10, was TCC1/WO[0] entspricht.
void configureHighFrequencyPWM(long frequency, int resolutionBits) {
// 1. Aktiviere den APBC-Takt für TCC1
PM->APBCMASK.reg |= PM_APBCMASK_TCC1;
// 2. Richte GCLK für TCC1 ein. Wir nutzen GCLK_GEN0 (48MHz).
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCLK_CLKCTRL_ID_TCC0_TCC1_Val);
while (GCLK->STATUS.bit.SYNCBUSY); // Warte auf Synchronisierung
// 3. TCC1 (Timer/Counter for Control) Konfiguration
TCC1->CTRLA.reg &= ~TCC_CTRLA_ENABLE; // Deaktiviere TCC1 zur Konfiguration
while (TCC1->SYNCBUSY.bit.ENABLE); // Warte auf Synchronisierung
// Setze den Prescaler (Teiler) auf 1 (keine Teilung) -> 48MHz Takt
TCC1->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1;
// Setze den Wellenform-Generierungsmodus auf Normal-PWM (NPWM)
TCC1->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;
while (TCC1->SYNCBUSY.bit.WAVE); // Warte auf Synchronisierung
// Setze die Periode (TOP-Wert) für die gewünschte Frequenz
// Periode = Taktfrequenz / Gewünschte_PWM_Frequenz - 1
uint32_t period = (SystemCoreClock / frequency) - 1; // SystemCoreClock ist 48MHz
TCC1->PER.reg = period;
while (TCC1->SYNCBUSY.bit.PER); // Warte auf Synchronisierung
// 4. Port-Multiplexing für MOTOR_PWM_PIN (D2/PA10) für TCC1/WO[0]
// PMUXE ist für even pins (0, 2, 4, ...), PMUXO für odd pins (1, 3, 5, ...)
// PA10 ist even (Index 10), also PMUXE. Funktion E (Wert 0x5).
PORT->Group[g_APinDescription[MOTOR_PWM_PIN].ulPort].PINCFG[g_APinDescription[MOTOR_PWM_PIN].ulPin].reg = PORT_PINCFG_PMUXEN | PORT_PINCFG_DRVSTR;
PORT->Group[g_APinDescription[MOTOR_PWM_PIN].ulPort].PMUX[g_APinDescription[MOTOR_PWM_PIN].ulPin / 2].reg |= PORT_PMUX_PMUXE(5);
// 5. Setze die Compare/Capture-Register (Duty Cycle) initial auf 0
TCC1->CC[0].reg = 0; // Für D2 (WO[0] an TCC1)
while (TCC1->SYNCBUSY.bit.CC0); // Warte auf Synchronisierung
// 6. Aktiviere TCC1
TCC1->CTRLA.reg |= TCC_CTRLA_ENABLE;
while (TCC1->SYNCBUSY.bit.ENABLE); // Warte auf Synchronisierung
Serial.println("PWM-Timer auf D2 konfiguriert.");
}
// --- Funktion zum Setzen des PWM-Tastverhältnisses (für D2) ---
// Diese Funktion wird nur für den MOTOR_PWM_PIN (D2) verwendet.
void setPWMDutyCycle(int value) {
// Begrenze den Wert auf den erlaubten Bereich
value = constrain(value, 0, PWM_MAX_VALUE);
// Skaliere den 'value' (0 bis PWM_MAX_VALUE) auf den Bereich des TCC1-Periodenregisters.
uint32_t compare_value = map(value, 0, PWM_MAX_VALUE, 0, TCC1->PER.reg);
TCC1->CC[0].reg = compare_value; // Setze den Vergleichswert für TCC1/CC[0]
while (TCC1->SYNCBUSY.bit.CC0); // Warte auf Synchronisierung
}
// --- Steuerung des Motors ---
// DRV8871 Logik (IN1 = PWM, IN2 = Richtung):
// Basierend auf Ihrem letzten Test:
// Vorwärts: IN1 = PWM, IN2 = HIGH
// Rückwärts: IN1 = PWM, IN2 = LOW
// Stillstand (Freilauf): IN1 = 0% PWM (LOW), IN2 = LOW
void controlMotor(int pwmValue, bool forward) {
Serial.print(" -> Steuerung: PWM_Value="); Serial.print(pwmValue);
Serial.print(", Richtung="); Serial.println(forward ? "Vorwaerts" : "Rueckwaerts");
if (pwmValue == 0) {
// Wenn Geschwindigkeit 0, Freilauf (IN1=LOW, IN2=LOW)
// Dies ist der schonendste Stillstand für den Glockenanker-Motor.
digitalWrite(MOTOR_DIR_PIN, LOW); // IN2 auf LOW setzen
setPWMDutyCycle(0); // IN1 auf 0% PWM (LOW)
Serial.println(" Motor im Freilauf (IN1=LOW, IN2=LOW)");
} else {
if (forward) { // Vorwärtsfahrt
digitalWrite(MOTOR_DIR_PIN, HIGH); // IN2 auf HIGH setzen (basierend auf erfolgreichem Test)
setPWMDutyCycle(pwmValue); // IN1 erhält PWM-Signal
Serial.print(" Motor vorwaerts: IN1="); Serial.print(pwmValue); Serial.println(", IN2=HIGH");
} else { // Rückwärtsfahrt
digitalWrite(MOTOR_DIR_PIN, LOW); // IN2 auf LOW setzen (basierend auf erfolgreichem Test)
setPWMDutyCycle(pwmValue); // IN1 erhält PWM-Signal
Serial.print(" Motor rueckwaerts: IN1="); Serial.print(pwmValue); Serial.println(", IN2=LOW");
}
}
}
// --- Haupt-Setup-Funktion ---
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("--- Starte Pendelzugsteuerung (XIAO D2 PWM / D1 Richtung) ---");
Serial.println("Richtungspolaritaet D1/IN2 angepasst und Geschwindigkeit reduziert.");
// Initialisiere BEIDE Steuerpins für den DRV8871 als Ausgänge
pinMode(MOTOR_PWM_PIN, OUTPUT); // D2 als PWM-Ausgang
pinMode(MOTOR_DIR_PIN, OUTPUT); // D1 als digitaler Richtungsausgang
// Konfiguriere den TCC1-Timer für die gewünschte Hochfrequenz-PWM auf D2
configureHighFrequencyPWM(PWM_FREQUENCY, PWM_RESOLUTION_BITS);
// Stelle sicher, dass der Motor am Anfang gestoppt ist (Freilauf).
controlMotor(0, true); // Initialer Freilauf
Serial.print("PWM-Frequenz: "); Serial.print(PWM_FREQUENCY); Serial.println(" Hz.");
Serial.print("PWM-Auflösung: "); Serial.print(PWM_RESOLUTION_BITS); Serial.println(" Bit.");
Serial.print("Zielgeschwindigkeit (PWM): "); Serial.print(TARGET_SPEED); Serial.println(" (Max ist 1023)");
Serial.println("DRV8871 Motortreiber sollte jetzt verkabelt und bereit sein.");
// Setze den Startzeitpunkt für den ersten Zustand
currentStateStartTime = millis();
}
// --- Haupt-Loop-Funktion ---
void loop() {
unsigned long currentTime = millis();
switch (currentLocoState) {
case STATE_ACCELERATE_FWD:
if (currentMotorPWM < TARGET_SPEED && (currentTime - lastRampUpdateTime >= RAMP_INTERVAL_MS)) {
currentMotorPWM += PWM_RAMP_STEP;
if (currentMotorPWM > TARGET_SPEED) currentMotorPWM = TARGET_SPEED;
lastRampUpdateTime = currentTime;
}
controlMotor(currentMotorPWM, true); // Beschleunigen vorwärts
if (currentMotorPWM >= TARGET_SPEED) {
currentLocoState = STATE_DRIVE_FWD;
currentStateStartTime = currentTime;
Serial.print("Zustand: FAHRT VORWAERTS (PWM: "); Serial.print(TARGET_SPEED); Serial.println(")");
}
break;
case STATE_DRIVE_FWD:
controlMotor(TARGET_SPEED, true); // Volle Fahrt vorwärts
if (currentTime - currentStateStartTime >= FAHR_ZEIT_MS) {
currentLocoState = STATE_DECELERATE_FWD;
Serial.println("Zustand: BREMSE VORWAERTS");
}
break;
case STATE_DECELERATE_FWD:
if (currentMotorPWM > 0 && (currentTime - lastRampUpdateTime >= RAMP_INTERVAL_MS)) {
currentMotorPWM -= PWM_RAMP_STEP;
if (currentMotorPWM < 0) currentMotorPWM = 0;
lastRampUpdateTime = currentTime;
}
controlMotor(currentMotorPWM, true); // Abbremsen vorwärts
if (currentMotorPWM == 0) {
currentLocoState = STATE_HALT_FWD_END;
currentStateStartTime = currentTime;
Serial.println("Zustand: HALTE VORWAERTS (Freilauf)");
}
break;
case STATE_HALT_FWD_END:
controlMotor(0, true); // Freilauf
if (currentTime - currentStateStartTime >= HALTE_ZEIT_MS) {
currentLocoState = STATE_ACCELERATE_BWD;
Serial.println("Zustand: BESCHLEUNIGE RUECKWAERTS");
}
break;
case STATE_ACCELERATE_BWD:
if (currentMotorPWM < TARGET_SPEED && (currentTime - lastRampUpdateTime >= RAMP_INTERVAL_MS)) {
currentMotorPWM += PWM_RAMP_STEP;
if (currentMotorPWM > TARGET_SPEED) currentMotorPWM = TARGET_SPEED;
lastRampUpdateTime = currentTime;
}
controlMotor(currentMotorPWM, false); // Beschleunigen rückwärts
if (currentMotorPWM >= TARGET_SPEED) {
currentLocoState = STATE_DRIVE_BWD;
currentStateStartTime = currentTime;
Serial.print("Zustand: FAHRT RUECKWAERTS (PWM: "); Serial.print(TARGET_SPEED); Serial.println(")");
}
break;
case STATE_DRIVE_BWD:
controlMotor(TARGET_SPEED, false); // Volle Fahrt rückwärts
if (currentTime - currentStateStartTime >= FAHR_ZEIT_MS) {
currentLocoState = STATE_DECELERATE_BWD;
Serial.println("Zustand: BREMSE RUECKWAERTS");
}
break;
case STATE_DECELERATE_BWD:
if (currentMotorPWM > 0 && (currentTime - lastRampUpdateTime >= RAMP_INTERVAL_MS)) {
currentMotorPWM -= PWM_RAMP_STEP;
if (currentMotorPWM < 0) currentMotorPWM = 0;
lastRampUpdateTime = currentTime;
}
controlMotor(currentMotorPWM, false); // Abbremsen rückwärts
if (currentMotorPWM == 0) {
currentLocoState = STATE_HALT_BWD_END;
currentStateStartTime = currentTime;
Serial.println("Zustand: HALTE RUECKWAERTS (Freilauf)");
}
break;
case STATE_HALT_BWD_END:
controlMotor(0, false); // Freilauf
if (currentTime - currentStateStartTime >= HALTE_ZEIT_MS) {
currentLocoState = STATE_ACCELERATE_FWD;
Serial.println("Zustand: BESCHLEUNIGE VORWAERTS");
}
break;
}
}