Pages: 1 [2] 3 4 ... 9   Go Down
Author Topic: Timer Interrupts on Due  (Read 73740 times)
0 Members and 1 Guest are viewing this topic.
Dubai, UAE
Offline Offline
Edison Member
*
Karma: 22
Posts: 1675
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Send me a Due and I will write some libraries for it.

Duane B

rcarduino.blogspot.com
Logged


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

Quote
Quote from: Sebastian Vik on November 05, 2012, 08:48:40 PM
Here is the table of parameters:
ISR/IRQ   TC           Channel   Due pins
TC0   TC0   0   2, 13
TC1   TC0   1   60, 61
TC2   TC0   2   58
TC3   TC1   0   none  <- this line in the example above
TC4   TC1   1   none
TC5   TC1   2   none
TC6   TC2   0   4, 5
TC7   TC2   1   3, 10
TC8   TC2   2   11, 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
TC0   0   TC0_IRQn   TC0_Handler   ID_TC0
TC0   1   TC1_IRQn   TC1_Handler   ID_TC1
TC0   2   TC2_IRQn   TC2_Handler   ID_TC2
TC1   0   TC3_IRQn   TC3_Handler   ID_TC3
TC1   1   TC4_IRQn   TC4_Handler   ID_TC4
TC1   2   TC5_IRQn   TC5_Handler   ID_TC5
TC2   0   TC6_IRQn   TC6_Handler   ID_TC6
TC2   1   TC7_IRQn   TC7_Handler   ID_TC7
TC2   2   TC8_IRQn   TC8_Handler   ID_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.

Thank you for your detailed answer.
Can somebody explain the following code?
I want to use it to execute a function after every XY CPU counts:

        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;
« Last Edit: November 24, 2012, 07:25:01 am by Selachii » Logged

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

Also, what are the pins in the table for?
Good question! If I understood everything correctly, the pins in the table are where you could output the clock of that timer/counter if you wanted to use it for something else than internal software interrupts.
Basically you can set up the hardware counter to output a clock signal. For example, TC0 channel 0 could be used to output a clock on the Arduino pin 2 or 13. So if you use TC0 channel 0 for your timer, you would not be able to output a clock on those pins (except using the frequency you set up the channel for).
That is why it would be better to use TC1 channels 0, 1 or 2 primarily for this kind of interrupt, as their output is not mapped to any of the Arduino pins and therefore does not limit any of your physically mapped outputs.
Logged

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

Quote
Thank you for your detailed answer.
Can somebody explain the following code?
I want to use it to execute a function after every XY CPU counts:

        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;

I have the same question. I'm trying to create an Interrupt every 62.5 microseconds but I'm incapable! Do you know if it is possible and if it is how should I proceed? As I have understand I should modify the variable RC and reading through the net I came with a possible answer being (VARIAN_MCK/8)/SPEED of transmission (I need the interruption to modify the VirtualWire library for the DUE, which I have almost done  smiley-wink) but it doesn't work!
Logged

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

You should use the code Sebastian Vik already posted, I've slightly rewrite it with some comments

Code:

// Black magic
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);

  // Start timer. Parameters are:

  // TC1 : timer counter. Can be TC0, TC1 or TC2
  // 0   : channel. Can be 0, 1 or 2
  // TC3_IRQn: irq number. See table.
  // 40  : frequency (in Hz)
  // The interrupt service routine is TC3_Handler. See table.

  startTimer(TC1, 0, TC3_IRQn, 40);

  // Paramters table:
  // TC0, 0, TC0_IRQn  =>  TC0_Handler()
  // TC0, 1, TC1_IRQn  =>  TC1_Handler()
  // TC0, 2, TC2_IRQn  =>  TC2_Handler()
  // TC1, 0, TC3_IRQn  =>  TC3_Handler()
  // TC1, 1, TC4_IRQn  =>  TC4_Handler()
  // TC1, 2, TC5_IRQn  =>  TC5_Handler()
  // TC2, 0, TC6_IRQn  =>  TC6_Handler()
  // TC2, 1, TC7_IRQn  =>  TC7_Handler()
  // TC2, 2, TC8_IRQn  =>  TC8_Handler()
}

void loop(){
}

volatile boolean l;

// This function is called every 1/40 sec.
void TC3_Handler()
{
  // You must do TC_GetStatus to "accept" interrupt
  // As parameters use the first two parameters used in startTimer (TC1, 0 in this case)
  TC_GetStatus(TC1, 0);

  digitalWrite(13, l = !l);
}

I have the same question. I'm trying to create an Interrupt every 62.5 microseconds but I'm incapable! Do you know if it is possible and if it is how should I proceed? As I have understand I should modify the variable RC and reading through the net I came with a possible answer being (VARIAN_MCK/8)/SPEED of transmission (I need the interruption to modify the VirtualWire library for the DUE, which I have almost done  smiley-wink) but it doesn't work!

62.5 uS is 1000000/62.5 = 16000 Hz, use that value as last parameter of startTimer.
Logged

C.

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

Well this last explanation was perfect! I have now managed to translate the VirtualWire library to the Arduino DUE. It is to be knowledge that it is a primary version and that I have tested it only with the standard speed of transmission (2000 bps) and it may have errors changing it (because the time of interrupt is not more dependent of the speed).

* VirtualWireDUE.zip (258.33 KB - downloaded 61 times.)
Logged

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

Hi,
   Is this or something similar going to be included in the Due API ?

EDIT - I have used this approach to get a timer interrupt running at 44.1Khz.

What is a good source of documentation on ARM Development ? I have the datasheet, I have found the API documentation, I have even read the .h and .c source files, but without the head start from Mr Vik I would not have had a hope in hell of getting this working.

As a quick example where is it documented that TC_GetStatus has to be called to re-enable the interrupt ?

   Duane B

rcarduino.blogspot.com
« Last Edit: November 30, 2012, 11:14:00 am by DuaneB » Logged


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

DuaneB, I see your point, unfortunately I guess the only document close to what you're searching is the datasheet. Indeed having a reference implementation to look at helps a lot.

As a quick example where is it documented that TC_GetStatus has to be called to re-enable the interrupt ?

I discovered it by trial and error. I don't know if there is any book that explain it in this level of detail...

Logged

C.

Adelaide, South Australia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Dev team
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for posting the timer code. We need a 10Khz signal but using 10,000 as the value in the last parameter (as suggested by cmaglie) seems to - in our case at least - deliver half the required frequency (i.e. 5Khz) - according to our scope at least. However, when we use the value of 20,000 - and look at the value on the scope - we get a 100us square wave.

Are we misinterpreting something? This is the code we use to get our 10Khz signal - could someone please verify our findings for us?

Also, we need to synchronise the start of these pulses with some other Due's so that they all output synchronised pulses. Can anyone help us with that? Do we simply re-start the timer and, if so, how?

Thanks all,
Dev team

Quote
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, 20000); //TC1 channel 0, the IRQ for that channel and the desired frequency
        // 20,000 seems to give 100uS/div signal, which is equivalent to 10,000Hz or 10Khz
}

void loop(){
}

Logged

Kind regards
Dev team

France
Offline Offline
Sr. Member
****
Karma: 0
Posts: 262
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

it seems to me you're subject to different waveform coding, with arduino mega look at fast PWM versus phase correct PWM but also frequency & phase correct PWM. The last 2 cases will divide by 2 the actual timer frequency generation.

I've quicked look on SAM3X... datasheet for the equivalent story, same except they use WAVSEL to choose whatever mode. Later on the datasheet, they also speak of center align or left align.

So there is no bug, just program correctly the registers...
Logged

Adelaide, South Australia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Dev team
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@SelfOnlyPath - thanks for the clarification. Certainly the left / centre align is something we'll need to look into and the halving of the clock in certain modes makes perfect sense in the context of our experience. It must be time for us to hit the docco's hard wethinks. Thanks again.
Logged

Kind regards
Dev team

Brighton, UK
Offline Offline
Newbie
*
Karma: 0
Posts: 47
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@WomensFashionArt

Just tried your sketch here unmodified and I get a nice 10 KHz (100 us cycle) square wave output on pin 13, as measured on our Rigol scope.

But on further investigation I see that the timer interrupt is in fact at 20 KHz (50 us), but you are toggling the output state in the handler, so each cycle you see is twice 50 us = 100 us.

Try this instead and you'll see a 2 us pulse at 20 KHz:

Code:
void TC3_Handler()
{
        TC_GetStatus(TC1, 0);

        digitalWrite(13, 1);    // ON

        for (int i = 0; i < 10; i++)
          int j = i;

        digitalWrite(13, 0);    // OFF
}

HTH
Jim
« Last Edit: January 05, 2013, 04:23:33 am by jgmdavies » Logged

Adelaide, South Australia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Dev team
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@ Jim; thanks for verifying that mate, much obliged.
Just as a "caution" for anyone using this routine, the setting TC_SetRA doesn't appear to be doing what the routine claims it does; We've varied its value and the pulse ratio seems "stuck" (or remains) at around 50%. We're investigating at present and will post updated code that - hopefully - takes a PWM percentage as well as a Frequency as part of its calling parameters. Much easier for us that way, and for anyone who wants to use it that way.
Logged

Kind regards
Dev team

Brighton, UK
Offline Offline
Newbie
*
Karma: 0
Posts: 47
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@WomensFashioArt

Thanks.

Just EDITED previous post, please review!

Jim
Logged

Adelaide, South Australia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Dev team
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@Jim; fantastic. Thanks. The code is a copy of some other code posted in this forum and - frankly - we didn't quite understand it all, but it's much clearer now (relatively old hands at Arduino, but new to the Due)...

So to make sure we understand it correctly, the frequency corresponds with the calling frequency of the routine (uint32_t frequency) delivering a 20Khz timed interrupt, but the digitalWrite(13, I = !I) flips the pin 13 logic value causing a 50us high, followed by a 50us low - creating a 10Khz square wave. That makes perfect sense.
Logged

Kind regards
Dev team

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