Timer Interrupts on Due

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 KB)

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

1 Like

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.

DuaneB:
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...

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

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

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

@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.

@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:

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

@ 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.

@WomensFashioArt

Thanks.

Just EDITED previous post, please review!

Jim

@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.

@WomensFashionArt

Yep, your understanding is correct. The software delay I inserted (the 'for' loop) is irrelevant, as the 'digitalWrite' is causing the 2 us odd delay I wanted to get a reasonable pulse out.

I know how easy it is easy to miss something when you're head-scratching, like the toggle 'I = !I' :smiley:

The Due Timer code contributed by others in this thread will be very useful.

Re. your other query:

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?

I would output a pulse each cycle from a digital pin on the 'master' Due, and connect it to an unused digital input on each 'slave' Due. You then need to use a hardware interrupt, i.e. an ISR, on each slave Due to reset each slave timer. I'm afraid I haven't got suitable code for the Due handy, but maybe someone else has?

Jim

@Jim; thanks so much for your pro-active engagement. 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. This is our progress so far. It's reduced the PWM frequency to 2Khz for now (the Due can't handle the logic at the requisite 1,000,000 interrupts / second, but we'll work on that). This seems to work just fine and the comments should show the way.

It's late and we're off for the day, but hope that this is of use to somebody.

We'll update it as we go along so that others may benefit from the discussion.

Thanks again Jim, your help is very much appreciated.

Rgs,
WFA_Dev

@Jim; the code would be rather useful here, wouldn't it... 8)

[FOLLOW-UP EDIT - 6 JAN 2013] Updated the code to approximate our 10Khz signal with 0...100% duty cycle.
Note: the frequency generation algorithm (uint32_t rc = VARIANT_MCK/3000/frequency; ) does not provide an accurate frequency. We've tested between 5Khz and 10Khz, for 5Khz we get approx. 5.1Khz, and for 10Khz we get 9.26Khz - as close as we can get it to 10Khz. In between the 5Khz and 10Khz, results vary wildly... please check your own numbers before relying too much on this routine.

volatile char On;
volatile char Total;
volatile char Max;

int PWM_Percent;
int PWM_Freq;

//TC1 ch 0
void TC3_Handler()
{

// reset interrupt
TC_GetStatus(TC1, 0);

// process percentage-based PWM waveforms

// If count is at leading edge, raise pulse
if (Total == 0) {
digitalWrite(13,1);
}
// If count is at trailing edge, drop pulse
else if (Total == On) {
digitalWrite(13,0);
}

Total += 1;

// If count is at 100%, reset to zero and start all over again
if (Total > Max) {
Total = 0;
}
}

// End TC3_Handler()

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_CLOCK3);
uint32_t rc = VARIANT_MCK/3000/frequency;
TC_SetRA(tc, channel, rc/2);
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(){

// Use the LED for now
pinMode(13,OUTPUT);

// Set the total to zero
Total = 0;

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

// Set interrupt frequency
PWM_Freq = 10000; //

// -------------------------------
// calculate cycles
On = PWM_Percent; //

// set maximum percentage at full 100%
Max = 100; //

// -------------------------------

// Start the timer
startTimer(TC1, 0, TC3_IRQn, PWM_Freq); //TC1 channel 0, the IRQ for that channel and the desired frequency

}

void loop(){
}

I really don't understand why you guys are not fully using the the complete potential of timer possibilities, of course requires fine register initialization but you could get very precise high frequency PWM even beyond 500 KHz or 1 MHz.

For the moment, i've doing so for years with arduino mega on my power electronics project (plasma drivers, full H-bridge, MWO inverters...) many of them, see old post then specific update code even though now is even more refined http://arduino.cc/forum/index.php/topic,8162.msg65115.html#msg65115 )

I do have an arduino DUE but as explained on other threads recently, I cannot download my actual MEGA projects using advanced arduino mega timer management because USB link of DUE is full of bugs.

Again I insist, just program the proper WAVESEL value and other registers, there is NO need to make interrupts slowing down and skewing the PWM jitter precision if using a timer, in particular with DUE being much more sophisticated timer possibilities than MEGA !

Just my 2 cents

1 Like

@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