Hi,
I'm trying to orchestrate a sequence of signals triggered periodically by a signal and implemented with a timer (or 2)
My end goal is to sequence the following series of events:
- Rising edge on an input pin triggers interrupt ("trigger interrupt")
- Interrupt starts a timer ("ONESHOT timer"), which after a settable period expires and sets a pin HIGH
- After a fixed delay, the pin goes back to LOW
What I have so far is:
- I can receive an interrupt on a pin and flash an LED, using attachInterrupt()
- I can run the timer for some chosen time period and change the state of a pin, using a lot of bit-bashing setup code for TC4. I have it set in ONESHOT mode, and I can see an output on WO[0] and WO[1] (alternating).
The ONESHOT interrupt does not seem to be running because I try toggling an LED inside this ISR and nothing happens.
My confusion is the following:
- What's the correct way to start a ONESHOT timer? I am using
TC4->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_RETRIGGER_Val
inside the trigger interrupt to start the timer. This does seem to work, but is it the right way? - How do I set up an interrupt handler to run when the ONESHOT ends? I have followed other examples in connecting up the NVIC, and I believe I am only looking for an interrupt on MC0, so I have disabled all other interrupts on TC4.
I am attaching the current code, cleaned up a little bit, but still messier than I like.
Thanks for your analysis. Having spent all day reading the specs and trying things out, I think I am close, but am still missing something.
Best,
Charles
#define TRIGGER_PIN 8
#define DEB1_OUT 3
#define DEB2_OUT 4
int print_count = 0;
bool state = false;
bool state2 = false;
void setup_timer4()
{
// PORT STUFF
// Enable the pin multiplexer for digital pins 0 and 1
PORT->Group[g_APinDescription[0].ulPort].PINCFG[g_APinDescription[0].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[1].ulPort].PINCFG[g_APinDescription[1].ulPin].bit.PMUXEN = 1;
// Set the multiplexer switch to peripheral E for digtial pins 0 and 1
PORT->Group[g_APinDescription[1].ulPort].PMUX[g_APinDescription[1].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;
// GENERIC CLOCK STUFF
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) | // Divide the 48MHz system clock by 1 = 48MHz
GCLK_GENDIV_ID(5); // Set division on Generic Clock Generator (GCLK) 5
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK 5
GCLK_GENCTRL_SRC_DFLL48M | // Set the clock source to 48MHz
GCLK_GENCTRL_ID(5); // Set clock source on GCLK 5
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable the generic clock...
GCLK_CLKCTRL_GEN_GCLK5 | // ....on GCLK5
GCLK_CLKCTRL_ID_TC4_TC5; // Feed the GCLK5 to TC4 and TC5
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// TIMER TC4 SETUP
TC4->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV8 | // Set print_countr to 8, 48MHz/8 = 6MHz
TC_CTRLA_WAVEGEN_MFRQ | // Toggle output when match val reached
TC_CTRLA_MODE_COUNT16; // Set the TC4 timer to 16-bit mode
TC4->COUNT16.CTRLA.bit.ENABLE = 1; // Enable TC4
while (TC4->COUNT32.STATUS.bit.SYNCBUSY); // Wait for synchronization
TC4->COUNT16.READREQ.reg = TC_READREQ_RCONT | // Enable a continuous read request
TC_READREQ_ADDR(0x10); // Offset of the COUNT register
while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for (read) synchronization
TC4->COUNT16.CTRLBSET.bit.ONESHOT = 1; // Stop counting after match
while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for (read) synchronization
TC4->COUNT16.CC[0].reg = 10000; // Set CC0 to the TOP value we want
while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for (read) synchronization
NVIC_SetPriority(TC4_IRQn, 3);
NVIC_EnableIRQ(TC4_IRQn);
// Clear all interrupt flags
TC4->COUNT16.INTFLAG.reg = TC_INTFLAG_OVF | TC_INTFLAG_ERR | TC_INTFLAG_SYNCRDY | TC_INTFLAG_MC0 | TC_INTFLAG_MC1;
// Disable all interrupts except MC0
TC4->COUNT16.INTENCLR.reg = TC_INTENCLR_OVF | TC_INTENCLR_ERR | TC_INTENCLR_SYNCRDY | TC_INTENCLR_MC1;
TC4->COUNT16.INTENSET.reg = TC_INTENSET_MC0;
}
void triggerInterrupt()
{
digitalWrite(DEB1_OUT, HIGH);
state = !state;
TC4->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_RETRIGGER_Val; // Make the TC retrigger
while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for (read) synchronization
digitalWrite(DEB1_OUT, LOW);
}
void TC4_Handler()
{
if (TC4->COUNT16.INTFLAG.bit.MC0 && TC4->COUNT16.INTENSET.bit.MC0)
{
// Clear interrupt flag first
REG_TC4_INTFLAG = TC_INTFLAG_MC0;
/* write interrupt code here */
digitalWrite(DEB2_OUT, state2);
state2 = !state2;
}
}
void setup()
{
SerialUSB.begin(115200);
while (!SerialUSB);
setup_timer4();
SerialUSB.println("===================================================");
SerialUSB.print("TC4->COUNT32->CTRLA: ");
SerialUSB.println(TC4->COUNT32.CTRLA.reg, HEX);
SerialUSB.print("TC4->COUNT32->READREQ: ");
SerialUSB.println(TC4->COUNT32.READREQ.reg, HEX);
SerialUSB.print("TC4->COUNT32->CTRLBCLR: ");
SerialUSB.println(TC4->COUNT32.CTRLBCLR.reg, HEX);
SerialUSB.print("TC4->COUNT32->CTRLBSET: ");
SerialUSB.println(TC4->COUNT32.CTRLBSET.reg, HEX);
SerialUSB.print("TC4->COUNT32->CTRLC: ");
SerialUSB.println(TC4->COUNT32.CTRLC.reg, HEX);
pinMode(DEB1_OUT, OUTPUT);
pinMode(DEB2_OUT, OUTPUT);
attachInterrupt(digitalPinToInterrupt(TRIGGER_PIN),triggerInterrupt, RISING);
}
void loop()
{
if (print_count == 100000)
{
//SerialUSB.println(TC4->COUNT32.COUNT.reg); // Output the results
print_count = -1;
}
print_count++;
}