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,
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
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 frequencyCould 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.
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;