How to set up interrupt for DAC with Servos?

Hey guys,

I need to set up an interrupt which sends a couple bytes out to my DAC at a high rate of speed. I was looking at the Audio Shield source to figure out how to do this since I know nothing about interrupts, and from what I understand it's using timer 1 which is a 16-bit timer. I also understand that the Servo library also uses timer 1.

I am aware that there is a timer 2 which I could also use, but that one is 8-bit.

Here's what I need to know:

  • What is the difference between an 8-bit and a 16-bit timer? Does one trigger faster, or with more precision?

I need my DAC to be updated X number of times per second, where that value may be anywhere from 22,000 to 44,000. I have already got hardware SPI code up and running so I can write it as fast as possible, but updating it each time through my main loop doesn't cut it for updating it fast enough and on time. So if 8-bit means I have a coarse selection of speeds to which I can set the timer, that might be okay since I don't need to vary it. But if it means it can't be updated more than 256 times a second or something... that would be bad.

  • TIMER1_COMPA_vect is the constant I need to use to attach to timer 1... is TIMER2_COMPA_vect the constant for timer 2? What does the COMPA_vect bit mean?

  • I think I saw the term vector used somewhere. Is that another word for an interrupt?

  • I think I read you can chain interrupts. Do you suppose I could chain the servo and DAC update code? How does one accomplish that?

  • Do interrupts trigger at a fixed speed? Or is there a way to set how fast the timers trigger? I don't see in the Audio shield code where a speed might be set.

  • What's with the ISR(TIMER1_COMPB_vect) in the Audio shield code which fills the play buffer? Is there more than one interrupt you can attach to for each timer?

Okay, found this page:
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

Vector = A pointer to an interrupt.
Vector table = A list of pointers to all the interrupt routines.

So okay, that's a familair concept from my DOS days. I hooked into timer interrupts back then.

And TIMER2_COMPA_vect is a particular interrupt. A timer interrupt. I would guess it is triggered when some comparison is evaluated as true.

I also see how to link one interrupt routine to another:
ISR(..., ISR_ALIASOF(...))

Though I see other parameters like ISR_NOBLOCK, and I wonder how one would specify that at the same time one is attaching one's ISR to a prexisting one.

I still don't know what the difference between an 8-bit and a 16-bit timer is.

I still don't know if I need to attach my DAC update routine to TIMER1_COMPA_vect or TIMER1_COMPB_vect or TIMER2_COMPA_vect or ...

I guess I could use that alias thing to make it run when the first timer interrupt hits, but then it would be triggered as often as that one is triggered , and I don't know if that is often enough or too often, or what.

So I'm still in the dark about whether or not I'll be able to update my dac fast enough to even output a simple 4000hz sine wave.

The only difference between 8 and 16 bit timers that affect you is the size, the 16-bit one will count for 256x longer. If 8-bits isn't long enough you can either use the 16-bit timer or accumulate th enumber of interrupts in the 8-bit ISR and take action when you get to the right number.

To use them load the count reg (eg TCNT0) with 0 and the compare reg (OCR0A) with the appropriate value.

When the timer reaches the OCR0A value it will cause the interrupt.

Remember to reset the the count reg to 0 in the ISR.


Rob

So I can make an 8-bit timer trigger as fast as a 16-bit timer... but while I have coarse control over when each tick occurs, I have finer control over how many ticks are needed to actually trigger an interrupt with the 16-bit timer?

I notice the datasheet says:

For Timer/Counter2, the possible prescaled selections are: clkT2S/8, clkT2S/32, clkT2S/64, clkT2S/128, clkT2S/256, and clkT2S/1024.

Assuming clkT2S triggers at 16mhz, which is the speed my Arduino is running at, and I want my interrupt to trigger 44,000 times a second...

16000000 / 44000 = 363.63

Which is not one of my dividers.

256 would give me 62500hz which is too high.
And 1024 would give me 15625hz which is too low.

128 would give me 125000, and if I divided that by 3 with the counter, that would be around 41666hz. Dividing by 4 though would give me 31250hz. 5 and 6 would be 25000 and 20833 respectively.

Hm. Is there some math equation which makes it easier to calculate the clock and divider to use to get closest to a specific frequency? :slight_smile:

Anyway, all that assumes clkT2S ticks along at 16mhz. And that I understood what you were saying about the timers counting. But the docs say it's tied to clkI/O, and there's exactly two places in the documentation where that seems to appear and neither say if it is clocked at the same frequency as the processor is running at.

The prescaler is only half of the equation, you can choose not to use it. If you use the 16-bit timer you can select div by 1 prescaler then divide the 16MHz by 364 which is pretty close to your perfect frequency.

The 8-bit timer could also be used with a div 8 prescaler and a count of 45, but that would be further off because the actual count should be 45.4545 which is about 1% off.

You can get the same resolution from the 8-bit timer by letting it overflow once then timing for 364-256 counts.

  • Disable compare int, enable overflow int.

  • Set timer to 0 and let run

  • When you get the overflow int set timer to 108 (a bit less really to allow for the interrupt latency or read th etimer and sub that value from 108) and enable compare int.

  • When you get the compare int you've done 364.

Much easier to use the 16-bit timer though.


Rob

Servo lib uses Timer1 though. I could do software servos, but for my purpouses, any sample rate above around 30khz would do.

I'm still trying to figure out how to set up those interrupts though. I'm looking at the audio sheild's source and I cannot figure out where they set up the interrupt rate.

I'm also reading the AtMega328 datasheet section on the timers but there's so many acronyms I don't know it's slow going.

One thing that I have been looking for an answer to is that there seems to be two values you can set for the counter to trigger an interrupt on. But one of those values is always gonna be lower than the other, so if the timer resets after the first triggers how could the second ever trigger? And if the counter is allowed to continue counting to trigger the second and only then is it reset, then that would add time to how long it takes for the first to retrigger. So how is it you can have two interrupts on the same timer with different values being compared to the ticks?

Servo lib uses Timer1 though.

So you're stuck with timer 0 at 8 bits.

there seems to be two values you can set for the counter to trigger an interrupt on.

Do you mean the two compare regs? If so forget about one and use the other. The two are for PWM I think to give you the high and low periods.

any sample rate above around 30khz would do.

That helps.

Use /8 prescaler and a compare reg value of 60, this gives you 33333Hz (16000000/8/60) interrupt freq. Play with the 60 if you want a different freq.


Rob

Timer 0? Isn't that the millisecond timer? Kinda need that. I guess in a pinch I could increment a time value myself but I suspect it would mess up other libraries like the sero lib.

I was planning on using Timer 2. I don't need PWM. I do need SPI though. I hope taking over timer 2 won't disable that.

Do you mean the two compare regs? If so forget about one and use the other.[/quite]

But I notice the audio shield lib seems to use both compare regs. One to update the dac, and one to update the sample buffer... I might need to do that.

Isn't that the millisecond timer?

Sorry, timer 2. All the above still applies. As for the other requirements you have I don't know, I'm just looking at creating the 30-odd kHz interrupt.

Can't you do both tasks (buffer & dac) on the same interrupt? Or do they run at different rates?


Rob

Well the idea behind having a ring buffer is that as long as my average processing time for the sound over the space of the buffer is less than the rate at which the dac updates, the dac will have data to write.

If I update the dac and the buffer at the same time though, then I may as well not even have a buffer because every update will have to be less than the time I have between writing bytes to the dac, no matter what.

This is all just theory of course. I don't know how long my sounds will take to generate. I'm just trying to cover my bases.

The most important thing right now though is that I actually get my dac outputting a square waves over SPI hardware communications. If I can do that, then I know I can make simple beeps. Anything more than that will be icing on the cake, and I'll have around 12 days to experiment on that front while I wait for the boards to come back. But I gotta get my boards out friday at the latest.

OK but this is the first time you've mentioned a ring buffer being used to absorb differences in input and output.

I need my DAC to be updated X number of times per second, where that value may be anywhere from 22,000 to 44,000.

Obviously the DAC updates aren't that critical, what are you reading and at what rate? If it's anywhere between 22 and 44ksps then you don't need a buffer.

If it's slower than 22 then you'll have to interpolate, faster than 44 and you won't make it.


Rob

Obviously the DAC updates aren't that critical, what are you reading and at what rate?

I don't know yet how I am going to generate the sound, so I don't know what I'm reading and at what rate.

There's a specific sound effect I'm trying to replicate. I may need to mix a couple tiny samples in memory. Very short samples on the order of milliseconds long, but played very rapidly. Or, I may need to calculate the result of mixing several sine waves and forget the samples. Any samples would be in progmem, possibly read into the regular ram.

All I need right now though is to get the dac outputting a simple tone. If I can do that I can send my boards off for manufacture knowing that in the worst case the prop will just beep. Then I can spend the next ten days while I wait for them to come back figuring out how to get a better sound out of it.

Looking at the datasheet again.

So if I undertsand these interrupts and timers and output command registers right...

If I have one timer ticking along at say, 22khZ, I can make an interrupt trigger after say 4 ticks. I can also prevent the timer from resetting its counter when that happens, and have another interrupt occur after 6 ticks. So I can have two different interrupts occur, one potentially to start an action, and another to stop it.

But the way the timing would work would be like so:

xxxAxBxxxAxBxxxAxB

Where clearly, there would not be 4 ticks between each trigger of A, but rather, 6, just as there are for B. The time which the longer one takes would be the limiting factor.

Now on first glance, it might appear that I could make A and B triggeer at the same time and just have an internal counter in B to make it actually execute its code half the time... which would be fine if I only actually needed to update B half the time.

But because doing anything in B would prevent A from triggering again on time, this method prevents me from doing what I really want to do with it, which is potentially have B take 1.5x as long as A half the time and .5x as long as A the other half, and stick the results in a buffer so A always has data to read.

However, it occurs to me that if my buffer is big enough, maybe I could simply update it in my main loop and keep ahead of A. Or, I could attach B to the regular millisecond timer interrupt in which case it would presumably be called 1000 times a second, which is far less than A, but far more than it might be called in my main loop.

That's neither here nor there though. Right now my main concern is just getting A up and running at my chosen speed, whatever that happens to be.

So does anyone know where in the audio shield code I can find where it is setting the speed of the interrupt it is using for the DAC?

[edit]

But because doing anything in B would prevent A from triggering again on time, this method prevents me from doing what I really want to do with it, which is potentially have B take 1.5x as long as A half the time and .5x as long as A the other half, and stick the results in a buffer so A always has data to read.

Unless... perhaps if B's interrupt were set to ISR_NOBLOCK... A could trigger again if B is taking too long? Because the timer would continue running in the background comparing its counter against A?

Hm... It's just occured to me that I the prescaler and counter values tell me how many computations I can do. (DUH!)

On each system clock on average, we might assume 1 instruction is executed. So, if I set my divider for Timer2 to /256 and set my counter compare bit to trigger every time the counter hits 2, and my interrupt would be triggered 31250 times a second, then that means every 512 clocks I trigger my interrupt. So that means I could execute a little less than 512 assembly instructions during that interrupt and still be ahead of the game.

Of course I have no idea how many instructions would be generated by my C code, and I don't know how much the setup and cleanup code takes, and I don't know how much time my other code is wasting. But 512 is quite a few clocks, even if my instructions actually take 4 clocks each because I'm playing with memory values. I might get away with mixing 8 (very short) samples or adding 8 LUT sine waves with that much time.

I think this code may be just what I needed to understand how to set up my interrupt:
http://www.arduino.cc/playground/Code/PCMAudio

That sets up timer 1 but timer 2 can't be all that different. And it even shows how to stick sample data into PROGMEM and get it back out.

Obviously I'll need to modify it to suit my purpouses of outputting data to my DAC, but gives me the basics. From here I can look at the datasheet to see exactly what each of those register settings does, in context.

If you want to get any efficiency out of a 9v battery, you may need to incorporate a UBEC like below.

http://www.hobbycity.com/hobbycity/store/uh_viewItem.asp?idProduct=4319