Timer Interrupts on Due

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:

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

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

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

1 Like

what does this compile to ?

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

DuaneB:
what does this compile to ?

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

// 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!

ARMtimer.zip (2.76 KB)

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.

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!

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 RCArduino: Arduino Due DDS - Part 1 - Sinewaves and Fixed Point Maths).

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

/*
 * 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.

Here is a link to the new library version: GitHub - SomeRandomGuy/DueTimer: Timer Library for the Arduino Due

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

Hi!
I read trough this thread and find it very very helpful!
At the moment i am trying to measure times between signal-flanks. Therefore i need to stop the time between two (external)interrupts. I'd like to do this with a timer - but don't need the timer interrupt.
As far as i can see the actual time is hold in the TC_CV register. How do i get this value?
I saw you make a lot of use of functions like pmc_set_write_protect and others. Where do you get the documentation of these functions from? And why do you know they even exist? :slight_smile:
Thanks a lot!

SomeRandomGuy:
Here is a link to the new library version: GitHub - SomeRandomGuy/DueTimer: Timer Library for the Arduino Due

Could someone offer some more explanation on this library I am trying to get my head around it.
I would like to set up a 500Khz clock output to a ATA6870. Is this possible using this library?
Does setting up these timers on certain pins have any affects on other parts of the system like the SPI Bus as I am using this to communicate with the ATA6870 which will use a SCK of half the CLK input I provide as recommended in the ATA6870 datasheet?

I am new to ARM processor and I am struggling making the timer work as well. The content in this post have been very helpful. But I have a very very dumb question.

Where did you guys find the reference manual for those functions used in these code? Like(NVIC_EnableIRQ, TC_Configure, TC_SetRA, etc). From the files in IDE it seems to be a library from Atmel. I probably used wrong keyword; I tried to search those on google for hours and got nothing. =( =( =( =( =(

I used:

  1. ATMEL data sheet: http://www.atmel.com/Images/doc11057.pdf
  2. API ref: http://asf.atmel.com/docs/latest/api.html
  3. the code (in the arduino dist)

You have to do some homework as none of the above is a tutorial. Most of the functions/defines were not obvious until I read the code. Had to do the same thing for the DAC interface (e.g., to enable flexible & word mode updates).

Best,
Bruce

This may be off topic, but is it possible to modify the library to be able to measure pulse duration on a pin using the timer interrupts?

Many thanks for your time in advance

Kind regards Rob