Pages: [1] 2 3 ... 9   Go Down
Author Topic: Timer Interrupts on Due  (Read 49702 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've been searching through the library code as well as thoroughly searched Google and haven't come across any working timer interrupt code for the Due. I could do this on the Uno in my sleep, but have yet to figure it out on the Due. Any pointers (http://xkcd.com/138/) or example code would be greatly appreciated. Basically I want to get a function to run once every X microseconds. Thanks.
Logged

Offline Offline
Jr. Member
**
Karma: 9
Posts: 75
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yeah, I was hoping to get timer interrupt hints on my thread for Tone.cpp code.  I've done timers on Uno and Maple, but Maple IO architecture is quite different from DUE.  There is PWM timer code in
hardware/arduino/sam/cores/arduino/wiring_analog.c
but no interrupt code.  The Timer/Counter support routines (no interrupt routines) are in
hardware/arduino/sam/system/libsam/source/tc.c
with the #define's in
hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3sd8/include/component/component_tc.h
The ISR handler's are named TCx_Handler(), e.g.  TC0_Handler()

I'm guessing (don't have a DUE yet) that interrupt enable would be something like
 tc->TC_CHANNEL[chan].TC_IER = TC_IER_CPCS;
but there is also an interrupt mask register TC_IMR?

not much help, but hopeful ...
Logged

Offline Offline
God Member
*****
Karma: 32
Posts: 506
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think I'm getting somewhere with this... try this:
Code:
volatile boolean l;

void TC0_Handler()
{
    long dummy=REG_TC0_SR0; // vital - reading this clears some flag
                            // otherwise you get infinite interrupts
    l= !l;
}

void setup(){
  pinMode(13,OUTPUT);
  pinMode(2,OUTPUT);    // port B pin 25 
  analogWrite(2,255);   // sets up some other registers I haven't worked out yet
  REG_PIOB_PDR = 1<<25; // disable PIO, enable peripheral
  REG_PIOB_ABSR= 1<<25; // select peripheral B
  REG_TC0_WPMR=0x54494D00; // enable write to registers
  REG_TC0_CMR0=0b00000000000010011100010000000000; // set channel mode register (see datasheet)
  REG_TC0_RC0=100000000; // counter period
  REG_TC0_RA0=30000000;  // PWM value
  REG_TC0_CCR0=0b101;    // start counter
  REG_TC0_IER0=0b00010000; // enable interrupt on counter=rc
  REG_TC0_IDR0=0b11101111; // disable other interrupts

  NVIC_EnableIRQ(TC0_IRQn); // enable TC0 interrupts

}

void loop(){
      digitalWrite(13,l);
}
The timer controls the output of pin 2 and the interrupt toggles a flag which is output on pin 13.
Logged


Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
   Whats the best resource you have found for describing the timer registers ?

Thanks

Duane B

rcarduino.blogspot.com
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 19
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Not what you're looking for, but on a similar topic:
I wanted to use the TC* timers to create a software servo library but I couldn't find out how to use the timers so I ended up using the PWM clock to generate the output.

While testing the PWM pulse I used attachInterrupt on the same pin to measure the pulse timing, effectively creating software interrupts. It's a hack, but if you're interested the code is here: http://arduino.cc/forum/index.php/topic,130631.0.html
It doesn't use any registers, so I can't tell you about those - just add attachInterrupt()
Logged

Offline Offline
Jr. Member
**
Karma: 9
Posts: 75
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

timer/counter registers are detailed in chapter 37 of datasheet
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think I'm getting somewhere with this... try this:
Code:
volatile boolean l;

void TC0_Handler()
{
    long dummy=REG_TC0_SR0; // vital - reading this clears some flag
                            // otherwise you get infinite interrupts
    l= !l;
}

void setup(){
  pinMode(13,OUTPUT);
  pinMode(2,OUTPUT);    // port B pin 25 
  analogWrite(2,255);   // sets up some other registers I haven't worked out yet
  REG_PIOB_PDR = 1<<25; // disable PIO, enable peripheral
  REG_PIOB_ABSR= 1<<25; // select peripheral B
  REG_TC0_WPMR=0x54494D00; // enable write to registers
  REG_TC0_CMR0=0b00000000000010011100010000000000; // set channel mode register (see datasheet)
  REG_TC0_RC0=100000000; // counter period
  REG_TC0_RA0=30000000;  // PWM value
  REG_TC0_CCR0=0b101;    // start counter
  REG_TC0_IER0=0b00010000; // enable interrupt on counter=rc
  REG_TC0_IDR0=0b11101111; // disable other interrupts

  NVIC_EnableIRQ(TC0_IRQn); // enable TC0 interrupts

}

void loop(){
      digitalWrite(13,l);
}
The timer controls the output of pin 2 and the interrupt toggles a flag which is output on pin 13.

I'll give this a shot thanks. I am curious though why you didn't do a digitalWrite() inside the handler? Arduino's digitalWrite() function is pretty clock-cycle heavy, but the following code works just fine for writing a digital output and only takes a couple of clock cycles if that was the primary concern.

Code:
static inline void pinOutput(int pin, int val)
{
if (val)
g_APinDescription[pin].pPort->PIO_SODR = g_APinDescription[pin].ulPin;
else
g_APinDescription[pin].pPort->PIO_CODR = g_APinDescription[pin].ulPin;
}

On another note, Maple uses a different processor, but gives as good of an explanation / example of how timers work as I've found so far:
http://leaflabs.com/docs/timers.html
http://leaflabs.com/docs/lang/api/hardwaretimer.html
http://leaflabs.com/docs/libmaple/api/timer.html

I haven't tried to implement any of their code yet, but thought I'd share the links.
Logged

Offline Offline
God Member
*****
Karma: 32
Posts: 506
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I recognize that code fragment smiley-cool

As for digitalWrite not being in the handler, actually it was there originally, but you have to understand that I'd been struggling for hours with interrupt code that wouldn't fire, then it only fired once, then it got hammered with constant interrupts, and I didn't know what was going on. So I did what is pretty much standard debugging practice and changed the interrupt handler to do the absolute bare minimum just to be sure that digitalWrite wasn't causing a side effect (there's some architectures where you can't write certain registers in an interrupt). But the important thing turned out to be the dummy read of TC_SR0. By then it was late so I just posted what I'd done.

Since then I have tried putting digitalWrite and direct port writes back in and they don't seem to cause any odd side effects.

The Maple hardware is completely different unfortunately and the registers are incompatible.
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I understand. Thanks smiley
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 19
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Using this code, you can set a timer for any of the ISRs TC0_Handler through TC8_Handler, see table of parameters below. It is possible to use the timers without a physically mapped pin, such as TC1 channel 0 (TC3_Handler) shown here:

Code:
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(){
}

Here is the table of parameters:
ISR/IRQTC        ChannelDue pins
TC0TC002, 13
TC1TC0160, 61
TC2TC0258
TC3TC10none  <- this line in the example above
TC4TC11none
TC5TC12none
TC6TC204, 5
TC7TC213, 10
TC8TC2211, 12
Logged

Offline Offline
Jr. Member
**
Karma: 9
Posts: 75
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sebastian:

  Well done!  thanks
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Using this code, you can set a timer for any of the ISRs TC0_Handler through TC8_Handler, see table of parameters below. It is possible to use the timers without a physically mapped pin, such as TC1 channel 0 (TC3_Handler) shown here:

Code:
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(){
}

Here is the table of parameters:
ISR/IRQTC        ChannelDue pins
TC0TC002, 13
TC1TC0160, 61
TC2TC0258
TC3TC10none  <- this line in the example above
TC4TC11none
TC5TC12none
TC6TC204, 5
TC7TC213, 10
TC8TC2211, 12

Noob question here, could someone please explain the inputs to the startTimer function? What is channel, and how do we use the frequency input?  Also, what are the pins in the table for?
« Last Edit: November 19, 2012, 12:36:02 pm by mnpumar » Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 9
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Same question; please explain the inputs to the startTimer function.
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 9
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Are there any ready-to-use-libs (like http://www.arduino.cc/playground/Code/Timer1) for using Hardware Timer on Due?

Or is there a beginner-friendly How-To (like http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/) for the Due?
« Last Edit: November 23, 2012, 05:54:44 pm by Selachii » Logged

Forum Administrator
Milano, Italy
Offline Offline
Sr. Member
*****
Karma: 22
Posts: 292
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is the table of parameters:
ISR/IRQTC        ChannelDue pins
TC0TC002, 13
TC1TC0160, 61
TC2TC0258
TC3TC10none  <- this line in the example above
TC4TC11none
TC5TC12none
TC6TC204, 5
TC7TC213, 10
TC8TC2211, 12

A brief explanation:
The SAM3X8E CPU has 3 Timer Counters (TC) they are called TC0, TC1, TC2.
Every Timer Counter contains 3 Channels numbered 0, 1 and 2 (this give us a total of 9 Channels).
Every Channel has its own counters and interrupt handler that are independent from other Channels.

In other words each Channel can be considered as a separate "Timer", and is like having 9 separate timers.

To initialize a Channel you need the following parameters:

TC0/TC1/TC2 - The Timer Counter instance
0 / 1 / 2 - The Channel number inside Timer Counter

If you want to use interrupts you must enable the NVIC (Nested Vector Interrupt Controller) for that channel with:
Code:
NVIC_EnableIRQ(TCx_IRQn);
where TCx_IRQn is the ID of the interrupt to enable. These id are listed in the following table together with the ISR handler function name:

TC        Chan   NVIC "irq"   IRQ handler function   PMC id
TC00TC0_IRQnTC0_HandlerID_TC0
TC01TC1_IRQnTC1_HandlerID_TC1
TC02TC2_IRQnTC2_HandlerID_TC2
TC10TC3_IRQnTC3_HandlerID_TC3
TC11TC4_IRQnTC4_HandlerID_TC4
TC12TC5_IRQnTC5_HandlerID_TC5
TC20TC6_IRQnTC6_HandlerID_TC6
TC21TC7_IRQnTC7_HandlerID_TC7
TC22TC8_IRQnTC8_HandlerID_TC8

(note that TC2_IRQn is the irq id for TC0-channel-2 not for TC2...)

but this is still not enough! Every peripheral in the SAM3X is off by default (to save power) and should be turned on. To turn on you need to run the following command:

Code:
pmc_enable_periph_clk(id);

where id is found on the last column of the above table (ID_TCx). It happened that ID_TCx constant equals TCx_IRQn, so Sebastian Vik has simplified a bit the function using TCx_IRQn as input for pmc_enable_periph_clk:

Code:
pmc_enable_periph_clk((uint32_t)irq);

Hope this helps to decode whats happening with timers inside SAM3X.

Are there any ready-to-use-libs (like http://www.arduino.cc/playground/Code/Timer1) for using Hardware Timer on Due?

Or is there a beginner-friendly How-To (like http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/) for the Due?

Nope, there are no libs neither tutorials.

I've planned a SAM3Timer library (to simplify implementation of Arduino Core and some libraries) but I didn't started it yet.

Volunteers? smiley
« Last Edit: November 24, 2012, 04:15:53 am by cmaglie » Logged

C.

Pages: [1] 2 3 ... 9   Go Up
Jump to: