From 8fa1ba3039ba0e985f000302a4be680f8fc0cb92 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Tue, 12 Apr 2022 14:32:10 +0800 Subject: [PATCH] SAMD21 DCC waveform working --- CommandStation-EX.ino | 22 +++++++++--------- DCCTimerSAMD.cpp | 54 +++++++++++++++++++++++-------------------- MotorDrivers.h | 3 ++- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 219e245..1834228 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -85,8 +85,8 @@ void setup() EthernetInterface::setup(); #endif // ETHERNET_ON -// Initialise HAL layer before reading EEprom or setting up MotorDrivers -// IODevice::begin(); + // Initialise HAL layer before reading EEprom or setting up MotorDrivers + IODevice::begin(); // Responsibility 3: Start the DCC engine. // Note: this provides DCC with two motor drivers, main and prog, which handle the motor shield(s) @@ -107,10 +107,10 @@ void setup() #undef SETUP #endif -// #if defined(LCN_SERIAL) -// LCN_SERIAL.begin(115200); -// LCN::init(LCN_SERIAL); -// #endif + #if defined(LCN_SERIAL) + LCN_SERIAL.begin(115200); + LCN::init(LCN_SERIAL); + #endif LCD(3, F("Ready")); CommandDistributor::broadcastPower(); } @@ -121,7 +121,7 @@ void loop() // Responsibility 1: Handle DCC background processes // (loco reminders and power checks) -// DCC::loop(); + DCC::loop(); // Responsibility 2: handle any incoming commands on USB connection SerialManager::loop(); @@ -134,18 +134,18 @@ void loop() EthernetInterface::loop(); #endif -// RMFT::loop(); // ignored if no automation + RMFT::loop(); // ignored if no automation #if defined(LCN_SERIAL) LCN::loop(); #endif -// LCDDisplay::loop(); // ignored if LCD not in use + LCDDisplay::loop(); // ignored if LCD not in use // Handle/update IO devices. - //IODevice::loop(); + IODevice::loop(); - //Sensor::checkAll(); // Update and print changes + Sensor::checkAll(); // Update and print changes // Report any decrease in memory (will automatically trigger on first call) static int ramLowWatermark = __INT_MAX__; // replaced on first loop diff --git a/DCCTimerSAMD.cpp b/DCCTimerSAMD.cpp index deff05b..159dd55 100644 --- a/DCCTimerSAMD.cpp +++ b/DCCTimerSAMD.cpp @@ -70,55 +70,59 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK to TCC0/1 while (GCLK->STATUS.bit.SYNCBUSY); - // PMA - assume we're using TCC0 - REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide 48MHz by 1 - GCLK_GENDIV_ID(4); // Apply to GCLK4 - while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization - - REG_GCLK_GENCTRL = GCLK_GENCTRL_GENEN | // Enable GCLK - GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source - GCLK_GENCTRL_ID(4); // Select GCLK4 - while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization - - REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable generic clock - 4 << GCLK_CLKCTRL_GEN_Pos | // Apply to GCLK4 - GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK to TCC0/1 - while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization - + // PMA - assume we're using TCC0... as we're bit-bashing the DCC waveform output pins anyway + // for "normal accuracy" DCC waveform generation. For high accuracy we're going to need + // to a good deal more. The TCC waveform output pins are mux'd on the SAMD, and OP pins + // for each TCC are only available on certain pins TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Select NPWM as waveform while (TCC0->SYNCBUSY.bit.WAVE); // Wait for sync - TCC0->INTENSET.reg = TCC_INTENSET_OVF; // Interrupt on overflow // PMA - set the frequency TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV1_Val); - unsigned long cycles = F_CPU / 10000000 * 58; // 58uS to cycles - unsigned long pwmPeriod = cycles / 2; - TCC0->PER.reg = pwmPeriod; + TCC0->PER.reg = CLOCK_CYCLES * 2; while (TCC0->SYNCBUSY.bit.PER); // PMA - start it TCC0->CTRLA.bit.ENABLE = 1; while (TCC0->SYNCBUSY.bit.ENABLE); + // PMA - set interrupt condition, priority and enable + TCC0->INTENSET.reg = TCC_INTENSET_OVF; // Only interrupt on overflow + NVIC_SetPriority((IRQn_Type)TCC0_IRQn, 0); // Make this highest priority NVIC_EnableIRQ((IRQn_Type)TCC0_IRQn); // Enable the interrupt interrupts(); } -// PMA - IRQ handler copied from rf24 branch +// PMA - Timer IRQ handlers replace the dummy handlers (cortex_handlers) +// copied from rf24 branch // TODO: test void TCC0_Handler() { if(TCC0->INTFLAG.bit.OVF) { - TCC0->INTFLAG.bit.OVF = 1; + TCC0->INTFLAG.bit.OVF = 1; // writing a 1 clears the flag + interruptHandler(); + } +} + +void TCC1_Handler() { + if(TCC1->INTFLAG.bit.OVF) { + TCC1->INTFLAG.bit.OVF = 1; // writing a 1 clears the flag + interruptHandler(); + } +} + +void TCC2_Handler() { + if(TCC2->INTFLAG.bit.OVF) { + TCC2->INTFLAG.bit.OVF = 1; // writing a 1 clears the flag interruptHandler(); } } bool DCCTimer::isPWMPin(byte pin) { - //TODO: SAMD digitalPinHasPWM - (void) pin; - return false; // TODO what are the relevant pins? - } + //TODO: SAMD test this works! +// return digitalPinHasPWM(pin); + return false; +} void DCCTimer::setPWM(byte pin, bool high) { // TODO: what are the relevant pins? diff --git a/MotorDrivers.h b/MotorDrivers.h index 74aab48..5237298 100644 --- a/MotorDrivers.h +++ b/MotorDrivers.h @@ -47,7 +47,8 @@ // // Arduino standard Motor Shield #if defined(ARDUINO_ARCH_SAMD) -// PMA - senseFactor for 3.3v systems is 1.95 as calculated when using 10-bit A/D samples, +// PMA - Setup for SAMD21 Sparkfun DEV board +// senseFactor for 3.3v systems is 1.95 as calculated when using 10-bit A/D samples, // and for 12-bit samples it's more like 0.488, but we probably need to tweak both these #define STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), \ new MotorDriver(3, 12, UNUSED_PIN, 9, A0, 1.95, 2000, UNUSED_PIN), \