Sodaq One Sleep + Timer Interrupt

Hi all,

I’m currently trying to put the Sodaq One into Sleep and program a Timer to execute a blink every few seconds. But somethink doesn’t work and the Green LED remain On …

Can someone Help me ?

Here the code:

bool isLEDOn = false;
int globalCounter = 0;

void tcConfigure(int sampleRate);
void tcDisable();
void tcReset();

long counter = 10;

void setup() {
pinMode(LED_GREEN,OUTPUT);
pinMode(LED_BLUE,OUTPUT);
pinMode(LED_RED,OUTPUT);
digitalWrite(LED_GREEN, HIGH); // Blue
digitalWrite(LED_BLUE, HIGH); // Green
digitalWrite(LED_RED, HIGH); // Green
FastBlinkGreenLED();
tcConfigure(5); //configure the timer to run at Hertz
SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk;
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
PM->SLEEP.reg |= PM_SLEEP_IDLE_CPU;

USBDevice.detach();
delay(1000);
}

void loop() {
__DSB(); // Flushes pipeline before sleep
__WFI();
FastBlinkRedLED(); //Just to know that we never enter in this part of code
delay(1000);
}

//this function gets called by the interrupt at Hertz
void TC4_Handler() {
if (TC4->COUNT32.INTFLAG.bit.MC0 == 1) {
TC4->COUNT32.INTFLAG.bit.MC0 = 1;
// Callback
FastBlinkGreenLED();
}
}

// GCLK_CLKCTRL_ID_TC4_TC5

void tcConfigure(int seconds) {
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TC4_TC5) ;
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync

// Disable. Must be done before anythink
TC4->COUNT32.CTRLA.reg &= ~TC_CTRLA_ENABLE;
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync
while (TC4->COUNT16.CTRLA.bit.SWRST);

//Configure
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync
// Set to 16 bits
TC4->COUNT32.CTRLA.reg |= TC_CTRLA_MODE_COUNT32;
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync
// Set Match mode
TC4->COUNT32.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync
//set prescaler
TC4->COUNT32.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1024;
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync
//set TC5 timer counter
//TC5->COUNT16.CC[0].reg = (uint16_t) (48000000 /(1024 * frequencyHz) - 1);
TC4->COUNT32.COUNT.reg = (uint32_t)(0);
TC4->COUNT32.CC[0].reg = (uint32_t)((48000000 / 1024) * seconds);
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync

// Enable the TC5 interrupt request
TC4->COUNT32.INTENSET.reg = 0;
TC4->COUNT32.INTENSET.bit.MC0 = 1;
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync

// Configure interrupt request
//NVIC_DisableIRQ(TC3_IRQn);
//NVIC_ClearPendingIRQ(TC3_IRQn);
//NVIC_SetPriority(TC3_IRQn, 0);
NVIC_EnableIRQ(TC4_IRQn);

// Start Counter
TC4->COUNT32.CTRLA.reg |= TC_CTRLA_ENABLE; //set the CTRLA register
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync
}

/*

  • TIMER SPECIFIC FUNCTIONS FOLLOW
  • you shouldn’t change these unless you know what you’re doing
    */

//Reset TC5
void tcReset()
{
TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync
while (TC5->COUNT16.CTRLA.bit.SWRST);
}

//disable TC5
void tcDisable()
{
TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync
}

void tcSetCounter(int milliSecond){
TC4->COUNT32.COUNT.reg = (uint32_t)(0);
TC4->COUNT32.CC[0].reg = (uint32_t)((48000 / 1024) * milliSecond);
while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync
}

void FastBlinkGreenLED(){
int i;
int val = 0;
digitalWrite(LED_GREEN, HIGH); // Red
digitalWrite(LED_BLUE, HIGH); // Blue
digitalWrite(LED_RED, HIGH); // Green
for(i = 0;i < 30;i++){
if(val == 0){
digitalWrite(LED_GREEN, LOW);
} else {
digitalWrite(LED_GREEN, HIGH);
}
val = 1-val;
delay(50);
}
digitalWrite(LED_GREEN, HIGH); // Red
digitalWrite(LED_BLUE, HIGH); // Blue
digitalWrite(LED_RED, HIGH); // Green
}

void FastBlinkRedLED(){
int i;
int val = 0;
digitalWrite(LED_GREEN, HIGH); // Red
digitalWrite(LED_BLUE, HIGH); // Blue
digitalWrite(LED_RED, HIGH); // Green
for(i = 0;i < 30;i++){
if(val == 0){
digitalWrite(LED_RED, LOW);
} else {
digitalWrite(LED_RED, HIGH);
}
val = 1-val;
delay(50);
}
digitalWrite(LED_GREEN, HIGH); // Red
digitalWrite(LED_BLUE, HIGH); // Blue
digitalWrite(LED_RED, HIGH); // Green
}

Try using delayMicroseconds(ms*1000) instead of delay(ms) in FastBlinkGreenLED().

Thak you Gabriel,

It Work, Is this about delay() deactivating interrupt ? I’m not sure to understand the issue

I believe it is all to do with interrupt priority levels.

When an interrupt is triggered, if a specified Interrupt Service Routine (ISR) function has been attached, normal code execution will be suspended, the ISR will then be executed and then return and resume the normal code when complete. If during the execution of an ISR, another interrupt is triggered, the interrupt priority levels are then compared. If a higher priority level interrupt has been triggered the original ISR will be suspended and the new higher priority ISR will be executed. After the higher priority ISR is finished, the lower priority ISR will continue and finally it will return and continue executing the normal code.

Effectively an ISR will block all equal or lower tiered interrupts until it has completed. There are four levels of priority on the SAMD21, However, I believe most of the Arduino code uses level 0 (highest).

If I understand correctly, you are using sleep mode plus ISRs which return to sleep mode when then finish? This is fine, but you need to be careful that you don’t call any functions which rely on interrupts from within your ISR(s). Either that, or lower the priority of your ISR routine(s).

Normally, I would suggest keeping any ISR as simple as possible, such as simply setting a flag. Then in loop() (normal code execution), add blocks of code which execute if that flag has been set.

The reason why you were having issues is to do with the delay()_ method. This method uses the global millis() counter and waits in a loop until that counter reaches a desired value. The problem here is that the milliseconds counter is based on a timer which generates an interrupt at fixed intervals, the ISR of that interrupt then increments the milliseconds counter. It is very likely that your ISR is blocking the millisecond ISR and so when you call delay() it never returns as the millisecond counter never increases. The delayMicroseconds() method does not rely on an interrupt.

I just tried implementing this code as a simple timer interrupt which seemed to work but I could not work out a way to get the main loop starting again.

I tried following the examples of setting an interrupt on a pin (as per the universal tracker) with the extra code you need after the attachInterrupt but could not get the main loop working again.

Is there anyway to implement the above and have it start the loop() function?

Cheers,
Nathan