Serial.print() Timer Routine - Arduino Due

Hello,

I am trying to understand timers and figuring out how to print to a serial port at a specified frequency. I am having troubles finding some code or example to follow, specifically for the Arduino Due.

Thank you,

Why you can't you just use millis() to see if it's time?

here's a basic example using timer1, channel 0, at 4 Hz
the last argument to startTimer() sets the frequency

http://forum.arduino.cc/index.php?topic=219941.0

PaulS:
Why you can't you just use millis() to see if it's time?

PaulS,

Thank you but this method still requires polling of a variable and lots of overhead that is not needed. Perhaps I should have been a little more clear of my application. It is real-time embedded control. So mills() has shown to fail in these types of applications. Certainly mine it did.

SignTorch:
here's a basic example using timer1, channel 0, at 4 Hz
the last argument to startTimer() sets the frequency

Could someone help me about this DUE Timer or Interrupt code? - Arduino Due - Arduino Forum

Thank you SignTorch,

I actually have found a thread explaining this more in detail. I appreciate the alternate link. Here is the one I found.

http://forum.arduino.cc/index.php?topic=130423.0

Perhaps this is a general timer interrupt question but I was wondering what changes would you do if you would like an interrupt every 5 seconds (0.2 Hz). It is not as intuitive to just using 0.5 as the final parameter in startTimer() as the function requires an int. I successfully did this by multiplying the variable 'rc' by 5, but I would like to figure out a more intuitive way of doing this.

I am going to post the repeatedly posted code for reference.

volatile boolean l;

//TC1 ch 0
void TC3_Handler()
{
        TC_GetStatus(TC1, 0);
        digitalWrite(13, l = !l);
}

void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t frequency) {
        pmc_set_writeprotect(false);
        pmc_enable_periph_clk((uint32_t)irq);
        TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4);
        uint32_t rc = VARIANT_MCK/128/frequency; //128 because we selected TIMER_CLOCK4 above
        TC_SetRA(tc, channel, rc/2); //50% high, 50% low
        TC_SetRC(tc, channel, rc);
        TC_Start(tc, channel);
        tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS;
        tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS;
        NVIC_EnableIRQ(irq);
}

void setup(){
        pinMode(13,OUTPUT);
        startTimer(TC1, 0, TC3_IRQn, 4); //TC1 channel 0, the IRQ for that channel and the desired frequency
}

void loop(){
}

first off, tc3_Handler() is an ISR (interrupt service routine), it is best to keep ISR code to a minimum

usually, you can set a flag in the ISR, then process that flag in the loop

digitalWrite is SLOOOW, the example below shows a much faster alternative

to get 0.2 Hz, use a static counter variable in the ISR

volatile int flag=0;

void setup()
{pinMode(13,OUTPUT);
 startTimer(TC1, 0, TC3_IRQn, 1); //TC1 channel 0, the IRQ for that channel and the desired frequency
}

void loop()
{if(flag)
 {flag=0;
  // do ISR flag processing here
  digitalWriteDirect(13,l=!l);
 }
}

void tC3_Handler()
{static int counter=0;

 if(++counter>=5)
 {flag=1;
  counter=0;
 }
}


static __inline__ void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

I think as things currently stand Serial output isn't interrupt driven on the Due
so you can use Serial.print in an ISR.

However I wouldn't rely on this state of affairs persisting, its likely to change at some point
I suspect. Careful choice of interrupt priorities would then be advised.

Back to the real issue - you need a version of that timer setup routine that takes
a period in microseconds, not a frequency.

serial.write is blocking if the tx buffer is not empty

so typically you would not do serial writes in an ISR

the example below avoids blocking by making sure that the txempty bit in the usart status register is set before writing

for Serial2 it checks the USART1 status register

if(flag&&(USART1->US_CSR & US_CSR_TXEMPTY))

for Serial1 you would check USART0 status

if(flag&&(USART0->US_CSR & US_CSR_TXEMPTY))

for Serial3 check USART2 status

if(flag&&(USART2->US_CSR & US_CSR_TXEMPTY))

for Serial (programing port) check UART status

if(flag&&(UART->UART_SR & bit(9)))

for SerialUSB I don't yet know what to check

code below is non-blocking serial write on Serial2 every 5 seconds

volatile int flag=0;
int buffer[5];

void setup()
{
 startTimer(TC1, 0, TC3_IRQn, 1);
}

void loop()
{ 
 if(flag&&(USART1->US_CSR & US_CSR_TXEMPTY))
 {
  Serial2.write(buffer[--flag]);
  
 } else if(!flag)
 {
  // update buffer between writes
 }
 
}

void tC3_Handler()
{static int counter=0;

 if(++counter>=5)
 {flag=5; // set # of bytes to write
  counter=0;
 }
}

oops, that writes the buffer out backwards

subtract flag from 5 to maintain corect order

  Serial2.write(buffer[5-flag]);
  --flag;