Go Down

Topic: Timer Interrupts on Due (Read 91981 times) previous topic - next topic

Hi all. I've implemented Sebastian Vik & cmaglie's timer code. I've got it working as is, with the LED. And I also tried a Serial.println and that works fine too.

However after transplanting the code to something more meaty (RGB Pixel light string) - the TC3_Handler only executes once.

The LEDs driver is using the hardware SPI channel - could it be anything to do with that? To rule out a clash of interrupts with the LED driver, I tried TC4/5 then TC0, but I still only get the first frame.

Any ideas? I'll post a video of the lights, if I get it working. (The code works otherwise - if I base the timer on time, it works fine)

I'll post the code, but you'd need the lights to see it:

https://github.com/MarkEMarkEMark/WS2801MEO

(note the previous version of the code works without the timer)

gst0098

Hi to all, I'm new to the arduino environment, I own an arduino due, I made some experiments with interrupts, apparently I cannot get the frequency higher than 250kHz, is this a known limit of the hardware ? I'm missing something, I read the datasheet and it states that TC_CMR_TCCLKS_TIMER_CLOCK1 is 84MHz / 2 = 41MHz so I was hoping that setting appropriately RA and RC (in my case 20 and 41 respectively) I would be able to acquire timer interrupts at 1MHz frequency.

Where am I wrong ?

Thanks in advance,
Giuseppe

gst0098

I answer myself, I was wrong, with correct values the timer interrupt can be triggered at 1MHz.
The problem was that the analogWrite is too slow to go to that frequency, so I digged into the arduino/sam7core sources and found how to use the SAM library directly, the code (much fast) that I wrote is:

// This function to configure a Timer was given some replies above, I changed only the used CLOCK.
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_CLOCK1);
  uint32_t rc = VARIANT_MCK/2/frequency; //2 because we selected TIMER_CLOCK1 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);
}

volatile boolean l;

void TC3_Handler()
{
  TC_GetStatus(TC1, 0);
  dacc_write_conversion_data(DAC0, (l = !l)*4095);
}

void setup() {
  Serial.begin(115200);
  pinMode(DAC0, OUTPUT);
  analogWriteResolution(12); 
  pmc_enable_periph_clk(DACC_INTERFACE_ID);
  dacc_reset(DACC_INTERFACE);
  dacc_set_transfer_mode(DACC_INTERFACE, 0);
  dacc_set_power_save(DACC_INTERFACE, 0, 0);
  dacc_set_timing(DACC_INTERFACE, 0x0, 1, 0x0);

  dacc_set_analog_control(DACC_INTERFACE, DACC_ACR_IBCTLCH0(0x02) |
                   DACC_ACR_IBCTLCH1(0x02) |
                   DACC_ACR_IBCTLDACCORE(0x01));
  dacc_disable_trigger(DACC_INTERFACE);
  dacc_set_channel_selection(DACC_INTERFACE, 0);
  dacc_enable_channel(DACC_INTERFACE, 0);

  // 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, 1000000);
}


Thanks to all,
Giuseppe Stanghellini

DuaneB

what does this compile to ?

Code: [Select]
dacc_write_conversion_data(DAC0, (l = !l)*4095);

why not l = ~l;

with l initialised to 4095 -it should be a lot faster.

Duane B

rcarduino.blogspot.com
Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

gst0098


what does this compile to ?

Code: [Select]
dacc_write_conversion_data(DAC0, (l = !l)*4095);

why not l = ~l;

with l initialised to 4095 -it should be a lot faster.

Duane B

rcarduino.blogspot.com


Good point! Nevertheless the code was timing correctly at 1MHz. Checked with a picoscope,

Giuseppe

mickeu

Quote
// Set pulse width to 50%
  PWM_Percent = 50;


How to change it to micro seconds?

Thanks in advance

My classmate and I have written a timer library that encapsulates cmaglie's "Black Magic" code and allows the user to pass their own function that they want to be executed by the timer as a function pointer. I have included an example program that shows how this is used. It also cleanly allows the user to start multiple timers for multiple functions in the same sketch. I would appreciate any suggestions, criticism, comments, etc.

Thanks!

Hi SomeRandomGuy.

That looks pretty good to me! I've already implemented two timers in my own code - one for lights and the other to debounce/read some buttons. I think that your library will make things much neater. I'll try it next weekend.

I had a chance to use your timer, SomeRandomGuy. It works perfectly, and has tidied up my code somewhat.

Thanks for the feedback! Good to know that it is working well. If anyone thinks of some useful additions or improvements, I would be happy to add those in.

Thanks for taking a look at it.

CakeBoss

Thanks alot for all the activity on this post. This is the first time I used timers and all I could find were explainations on how to do it on the atmega328. I read the datasheet but couldnt make up how to do it on the due, so this helped alot!

BKM

Nice library - thank you for sharing it.

One idea: it would be nice if startTimer() picked the clock source with the least error, given the desired frequency (credit for this idea goes to RCArduino - see comments in http://rcarduino.blogspot.com/2012/12/arduino-due-dds-part-1-sinewaves-and.html).

I hacked together some code to demonstrate it.   Caveat:  I'm an arduino newb.


Code: [Select]

/*
* pick clock that provides the least error for specified frequency.
*/
uint8_t pickClock(uint32_t frequency, uint32_t& retRC)
{
/*
Timer Definition
TIMER_CLOCK1 MCK/2
TIMER_CLOCK2 MCK/8
TIMER_CLOCK3 MCK/32
TIMER_CLOCK4 MCK/128
*/

struct {
uint8_t flag;
uint8_t divisor;
} clockConfig[] = {
{ TC_CMR_TCCLKS_TIMER_CLOCK1, 2 },
{ TC_CMR_TCCLKS_TIMER_CLOCK2, 8 },
{ TC_CMR_TCCLKS_TIMER_CLOCK3, 32 },
{ TC_CMR_TCCLKS_TIMER_CLOCK4, 128 }
};

int clkId = 3;
int bestClock = 3;
float bestError = 1.0;

do {
float ticks = (float) VARIANT_MCK / (float) frequency / (float) clockConfig[clkId].divisor;
float error = abs(ticks - round(ticks));
if (abs(error) < bestError) {
bestClock = clkId;
bestError = error;
}
} while (clkId-- > 0);

float ticks = (float) VARIANT_MCK / (float) frequency / (float) clockConfig[bestClock].divisor;
retRC = (uint32_t) round(ticks);
return clockConfig[bestClock].flag;
}

void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t frequency, volatile void (*function)())
{
pmc_set_writeprotect(false);
pmc_enable_periph_clk((uint32_t)irq);

uint32_t rc = 0;
uint8_t clock = pickClock(frequency, rc);
TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | clock);

... remainder of startTimer() unchanged ...

}

Thanks for the idea BKM! I have added it to the library along with several other major improvements that should completely remove any IRQ references from the sketches and make it much cleaner and more user friendly. I will attach a link to my github repo tomorrow night once I get to test it a little bit.

#58
Mar 02, 2013, 05:51 am Last Edit: Mar 02, 2013, 05:53 am by SomeRandomGuy Reason: 1
Here is a link to the new library version: https://github.com/SomeRandomGuy/DueTimer

When you download this, remove the "-master" part of the folder name so that it will show up correctly in the Arduino IDE and drag it into your libraries folder.

This has several new improvements. I would love some more testing/feedback if any of you get a chance.

Hope this is helpful!

SRG

Great library, Thank you very much for sharing it!

I noted that the function startTimer accepts as parameter the frequency at which the ISR is executed so I figure out that the maximum time between two consecutive calls of the ISR is 1 second. If I would like to executed some code at intervals of more than one second this should be managed inside the function corresponding to the ISR?

Thanks again,

Simon

Go Up