Timer Interrupts on Due

@Selfonlypath:

Thanks for your contribution. I don't necessarily disagree with your position, but the documentation (even the complete data sheet) isn't entirely clear on what / how these registers are programmed. Further, there is a serious shortage of programming examples in Atmel's documentation for these chips (well, in our opinion anyway).

All references to the Sam3A4C registers include stuff like setting timers as peripherals, using nested interrupts, programming the timers and cascades; all without examples and/or any guiding principles.

Consequently it is difficult (if not near-on impossible) for those of us without experience with this chip to even begin to explore its potential. Even using the Arduino IDE - which is still in Beta stage - we are still experiencing issues - it's early days yet.

For now, we are getting a relatively clean 9Khz signal over which we have full control - because it is software generated. We can modify the waveform - if and as we please. We can change its duty cycle - even by fractions of a percent if needed, and - because the waveform is generated in software - we are able to synchronise it accurately across multiple Arduino Due's.

So, in conclusion, yes - all things being equal your point is entirely valid.

However, until there is more clarity for us on how to accurately program the registers, and until we are able to identify some examples out there that we can piggy-back on, this does the job for us.

It's not an ideal world out there; we simply get the job done for now.

J.

I do agree that the Sam... datasheet register are not very detailed, maybe a mix of Atmel releasing too early beta datasheet or whatever.

You might want see this video where I'm using the four 16-bits MEGA timers, each being specifically delayed 90°, 180°, 270° with less than 62.5 ns

P.S. In fact, there really SIX 16-bit timers, two of them are being emulated by my specific protoshield using some 74HCxy chips

Just hoping the arduino team will soon fix the USB serial issue via a new IDE release, at least on my side I cannot afford to migrate my actual project MEGA software using very advanced timer register tricks since i'm shuttling more than 5000 VAR's with my igbt's drivers, blue smoking and $$$ gone !

Once DUE serial USB will be OK, I'll look deeper on the DUE timers which i feel from datasheet are very very interesting with fascinating new features.

@ SelfOnlyPath:

We're out on the yoga bit... :grin: - informative video.
There is broad agreement that chip-generated PWM / waveforms will end up being superior to our (temporary) solution.

However, let's not forget that this is software; not hardware. We work with what we know and can realistically deliver at the highest possible quality in an acceptable timeframe. Once we understand the timers a bit better, we'll simply replace that bit of code.

Eliminates wasteful research, achieves the objective, and saves standing still altogether waiting for the right solution to arrive - :slight_smile:

I really hope to have access to a new IDE so I can start using DUE on ALL my projects, more particularly its timers and interrupt routines.

You'll notice some of my boards hosting the MEGA as piggy board are in fact fully space compatible with DUE, the board were designed more than 1 year ago betting DUE will come up.

In fact, my special loose-coupled ferro-resonant project is bit stuck due to lack of more advanced timer low level inside MEGA.

As you said, world is not perfect but keeps moving.

P.S. You might notice from time 7min 0s in the video that actually I live update by ISR (Interrupt Software Routines) all the FOUR internal 16-bit timers register so you'll se a global frequency sweeping up. Due to the special topology of my circuit, this is equivalent to have arduino MEGA-generate phase locked 8 independent PWM rails aka 8 channel frequency generator.

Once DUE will become more mature and guess as you said, the Atmel datasheet, such project will be easier with more timers and software possibilities with incredible few ns precision jitter.

@selfonlypath: Very impressive - well done!

@WomensFashionArt:

WomensFashionArt:
No can do with the sync I'm afraid, there's 20m of cables between them and all sync is being done over this cable. I'm pretty sure that we'll end up using an interrupt of some sort so that all of them are able to reset at exactly the same time.

... the Due can't handle the logic at the requisite 1,000,000 interrupts / second

Couple of queries:

  • How well synchronized do the different Dues have to be? 10 us, 1 ms, 1s ?

  • And how did you get the 1 MHz figure? Is it from the precision you want for each part of the PWM cycle?

Best,
Jim

Hi Jim,

Thanks for the questions.

  1. "How well synchronised do the different Dues have to be"

We'd like to get it below 20us. However, there are some obstacles to this. Firstly, total capacitance, line driver & logic gate delays will add some delays. We have not yet calculated those, but they are - presently - thought to be within our limits. Our second problem is that - for a new design - we'd like to go radio (probably some variant of 802.11 for its broad availability). That puts the cat among the pigeons because now that very fast response time is becoming increasingly difficult to achieve. Again, we haven't done the maths, but we are pretty sure that there are going to be complications. Our middle name... LOL.

  1. "How did you get the 1 Mhz figure"?

Well, 1,000,000 interrupts for 10 Khz leaves 100 interrupts for each cycle. In other words, 10 Khz @ between 0% and 100% duty cycle in 1% increments.

However, as these units will - initially - only see service inside our studios, we are fine with 5 Khz (which corresponds to exactly 5 pulses of light for a 1/1000 shutter speed on our cameras) - something which, thanks to the Due's fast clock - we have already achieved.

==============================

Unfortunately we only have a small hobby scope at our disposal and its resolution is inadequate for testing the Sync. However, there is a solution (we think) in that we are considering using an 8 input AND gate which will allow us to test the synchronicity of up to 8 Due's by the following methodologies:

  • Assume pulses are initially out-of-phase by - say - 50%. That gives a net duty cycle of 25% on a 50% PWM (the AND gate is shut for the rest of the time because not all inputs are high).
  • Issue a SYNC pulse to the Due's
  • Verify that the AND gate output pulses now show a 50% duty cycle.

Whilst the above is inadequate to verify whether or not we were able to keep our response time sub 20 us, it will show us that our SYNC system is working, and that all the PWM signals are now lining up nicely.

The reason "jitter" isn't that much of an issue for us is, that at lower shutter speeds (and @ 10 Khz), each exposure is likely to be subjected to - say - 80 light pulses (1/125 sec). Even if we missed an entire pulse (not overly desirable, but not catastrophic either), we'd only experience a variance of just over one percent; not distinguishable by the naked eye.

Hope that answers some of your questions (as well as some you haven't asked, but are directly related).

Rgds, J.

Hi all,
We were wondering, with so much confusion about timers, PWM etc. whether one of our standard code blocks might come in handy.
This one trades resolution for frequency (as you do). Hope it is useful to somebody; nothing lost if it's not.

// Software PWM for the Arduino Atmel 328p @ 16MHz
//
// Women's Fashion Art Dev team
// 7 January 2013
//
// Default settings:
// volatile byte Maximum_Resolution_In_Percent = 50;
// byte Duty_Cycle_In_Percent = 50;
//
// Measured performance
// Freq: 3.27kHz
// Duty: 49.9%
// Vrms: 2.44V
// Vpp: 5.02V
// Vmin: 80.0 mV
// Vmax: 5.12V
// Vavg: 2.60V
// Target: Arduino Duemilanove - genuine
//
// Version 1.0 - 18 December 2012
//

#include <io.h>

// Variables
volatile byte Count;

// User variables - change these to suit
// volatiles
//
volatile byte Pin = 9;
volatile byte HighCount = 0;
volatile byte Maximum_Resolution_In_Percent = 50; // do not exceed 100% here...

// non volatiles
//
byte Duty_Cycle_In_Percent = 50; // do not exceed 100% here either...

// Setup routine
//
void setup(){

// Set user's preferred pin to output
//
pinMode(Pin, OUTPUT);

// Set up the high count
HighCount = DoCalcs(Maximum_Resolution_In_Percent, Duty_Cycle_In_Percent);

// Clear interrupts
//
cli();

// Configure timer registers
//
TCCR2A = 0;//
TCCR2B = 0;//
TCNT2 = 0;//

// set compare match register
// OCR2A = 99; // 200 Hz at 100% or 400 Hz at 50%
// OCR2A = 49; // 400 Hz at 100% or 800 Hz at 50%
// OCR2A = 24; // 800 Hz at 100% or 1.6 Khz at 50%
OCR2A = 11; // 1.6 kHz at 100% or 3.2 kHz at 50%

// CTC mode
//
TCCR2A |= (1 << WGM21);
//CS11 bit for 8 prescaler
//
TCCR2B |= (1 << CS11);
// timer compare interrupt
//
TIMSK2 |= (1 << OCIE2A);

// Re-enable interrupts
//
sei();

// Create a dependable PWM sequence
//
digitalWrite(Pin, HIGH); // Start off with a high pulse
}

// Interrupt Service Vector (IDE preset)
//
ISR(TIMER2_COMPA_vect){

// PWM High / Low Routine
// Pull pin low once HighCount is achieved
//
if (Count == HighCount) {
digitalWrite(Pin, LOW);
}
// Pull pin high once at the end of our resolution count
//
else if (Count == Maximum_Resolution_In_Percent) {
digitalWrite(Pin, HIGH);
Count = 0;
}
// Bump count by 1
//
Count += 1;
}

// Subroutines
//
int DoCalcs (byte Resolution, byte DutyCycle)
{
// Set up the high count
return (Duty_Cycle_In_Percent / (100 / Maximum_Resolution_In_Percent));
}

// Main loop
// This does nothing at present, but can add other stuff to this
// depending on how hard the timer is being driven....
//
void loop()
{
while (1);
{
// do other stuff here
}
}

@SelfOnlyPath:
Yes we know. However, sometimes it is desirable - for example for PCB layout purposes - to be able to use a user-assigned pin. In some cases that will allow you to use a single-sided board instead of double, or a double-sided board in place of 4 layers. We take a rather holistic approach to such things and this is only one of several libraries - each with a specific purpose. We chose to list this one because it is the most flexible for users not familiar with the timers on these chips.

@SelfOnlyPath:
Points taken. Frankly, if we needed jitter-free PWM we'd either use a dedicated chip (i.e. the uM-PWM) or drive an interrupt with a high precision crystal at the MCU's highest priority level. Personally I'd never rely on a software timer inside an MCU for highly time-critical PWM - no matter what anyone suggests. A nice 10Mhz crystal running through a /10 would give you a nice clean and (if temperature controlled) highly predictable reference signal that is ns accurate with a small correction lookup table (if needed).

If you want high precision, jitter free, you have to - in my humble view - look outside the MCU software counters at any rate. As noted hereinbefore, jitter is the least of our problems; we have up to 1.5% tolerance - well within the MCU's software capabilities for our timeframes (down to 1/125th of a second).

@SelfOnlyPath:
Thanks for your expert help. All the best.

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.