I am confused about writing fast pwm values via timer1 on an Arduino Uno.
I'm using ISRs to generate audio outputs on two pwm pins by writing 8bit values to OCR1AL and OCR1BL.
I'm using timer1 in high speed 8 bit mode.
I assumed that OCR1AL and OCR1AH values are written to pin9 and OCR1BL and OCR1BH to pin10. Is this correct?
If not, is it at all possible to control two pins with timer1 with the high speed 8 bit mode?
Thank you, this is what I meant – vaguely at least
I'll try to describe my assumption more precisely (all referring to an Arduino Uno):
If I set for example OCR1AL = 127, this will cause timer1 to output a High value on Arduino Pin 9 during the first half if its cycle, and a low value during the second half of its cycle.
If I set OCR1BL = 127, the same thing will happen on Arduino Pin 10.
The action depends on how you have set the timer control registers, which you did not clearly state. See the data sheet for details.
Or, try it and see.
DDRB = 6; // Make timer's PWM 9 and 10 pin an output
TCCR1B = (1 << CS10); // Set prescaler to full speed 16MHz (no prescaling)
TCCR1A = (1 << COM1A1);
TCCR1A |= (1 << COM1A0);
TCCR1A |= (1 << WGM10); // Put timer into 8-bit fast PWM mode
TCCR1B |= (1 << WGM12);
The reason why I'm asking all of this is because I'm trying to generate two audio outputs with different frequencies on the Arduino pins 9 and 10. Pin9 is working fine, pin 10 isn't.
The values of pin 9 are set by an ISR running on timer2. The values of pin 10 are set by an ISR running on timer0. The former is writing the resulting values to OCR1AL, the latter to OCR1BL.
Here are the setup settings:
TCCR2A = 0; // We need no options in control register A
TCCR2B = (1 << CS20); // no prescalar
TIMSK2 = (1 << OCIE2A); // Set timer to call ISR when TCNT2 = OCRA2
TCCR0A = 0; // same for timer0
TCCR0B = (1 << CS20);
TIMSK0 = (1 << OCIE0A);
The ISRs look as follows:
ISR(TIMER2_COMPA_vect) { // Called each time TCNT2 == OCR2A
static byte index = 0; // Points to successive entries in the wavetable
OCR1AL = wave[index++]; // Update the PWM 9 output, working fine
asm("NOP;NOP"); // Fine tuning
TCNT2 = 5; // Timing to compensate for time spent in ISR
}
ISR(TIMER0_COMPA_vect) { // Called each time TCNT2 == OCR2B
static byte index = 0; // Points to successive entries in the wavetable
OCR1BL = wave[index++]; // Update the PWM 10 output. Not working!
asm("NOP;NOP"); // Fine tuning
TCNT0 = 5; // Timing to compensate for time spent in ISR
}
I assume that something in the control registers of timer1 is wrong, but I can't figure out what it is...
Here's the complete code, in case this looks more transparent:
/******** Load AVR timer interrupt macros ********/
#include <avr/interrupt.h>
/******** Sine wave parameters ********/
#define PI2 6.283185 // 2 * PI - saves calculating it later
#define AMP 127 // Multiplication factor for the sine wave
#define OFFSET 128 // Offset shifts wave to just positive values
/******** Lookup table ********/
#define LENGTH 256 // The length of the waveform lookup table
byte wave[LENGTH]; // Storage for the waveform
void setup() {
/******** Populate the waveform lookup table with a sine wave ********/
for (int i=0; i<LENGTH; i++) {
float v = (AMP*sin((PI2/LENGTH)*i)); // Calculate current entry
wave[i] = int(v+OFFSET); // Store value as integer
}
/******** Set timer1 for 8-bit fast PWM output ********/
DDRB = 6;// pinMode(9, OUTPUT) and pinMode(10, OUTPUT); // Make timer's PWM pin an output
DDRD = 255;
TCCR1B = (1 << CS10); // Set prescaler to full speed 16MHz (no prescaling)
TCCR1A = (1 << COM1A1); // PWM pin to go low when TCNT1= OCR1A
TCCR1A |= (1 << COM1A0);
TCCR1A |= (1 << WGM10); // Put timer into 8-bit fast PWM mode
TCCR1B |= (1 << WGM12);
/******** Set up timer 2 to call ISR ********/
TCCR2A = 0; // We need no options in control register A
TCCR2B = (1 << CS20); // Set prescaller to divide by 8 // try TCCR2B = (1 << CS20) for finer range
TIMSK2 = (1 << OCIE2A); // Set timer to call ISR when TCNT2 = OCRA2
// TIMSK2 |= (0 << OCIE2B); // doesn't work, if TCCR1A |= (1 << COM1B1) is not set
/******** Set up timer 0 to call ISR ********/
TCCR0A = 0; // We need no options in control register A
TCCR0B = (1 << CS20); // Set prescaller to divide by 8 // try TCCR2B = (1 << CS20) for finer range
TIMSK0 = (1 << OCIE0A); // Set timer to call ISR when TCNT0 = OCRA2
OCR2A = 60; // sets the frequency of the generated wave has to be > TCNT2
OCR0A = 80;
sei(); // Enable interrupts to generate waveform!
}
void loop() {
}
/******** Called every time TCNT2 = OCR2A ********/
ISR(TIMER2_COMPA_vect) { // Called each time TCNT2 == OCR2A
static byte index = 0; // Points to successive entries in the wavetable
OCR1AL = wave[index++]; // Update the PWM output
asm("NOP;NOP"); // Fine tuning
TCNT2 = 5; // Timing to compensate for time spent in ISR
}
/******** Called every time TCNT2 = OCR2B ********/
ISR(TIMER0_COMPA_vect) { // Called each time TCNT2 == OCR2B
static byte index = 0; // Points to successive entries in the wavetable
OCR1BL = wave[index++]; // Update the PWM output
asm("NOP;NOP"); // Fine tuning
TCNT0 = 5; // Timing to compensate for time spent in ISR
}
the reason why I'm using ISRs is that I wasn't able to generate clean audio by using the loop function. I suspect that its timing is not accurate enough and I thought that using the timers in combination with ISRs would yield much more precise timing.
So far, I was indeed able to generate remarkably clean audio signals on pin9.
Anyway, I still don't understand why I can't get pin10 to work.
Can you tell, if my register values for timer1 are correct for what I intend to do?
TCCR1B = (1 << CS10); // Set prescaler to full speed 16MHz (no prescaling)
TCCR1A = (1 << COM1A1);
TCCR1A |= (1 << COM1A0);
TCCR1A |= (1 << WGM10); // Put timer into 8-bit fast PWM mode
TCCR1B |= (1 << WGM12);
The timers WILL generate much more precise timing, when used directly on the output pins, than any ISR approach.
It is just plain silly to think you can improve on the hardware, using software. Again, you should not be using ISRs.
For the record, though, your code is extremely confusing. I can't imagine why you would be writing to Timer1 control registers when you claim to be using Timer0 and Timer2.
The values of pin 9 are set by an ISR running on timer2. The values of pin 10 are set by an ISR running on timer0. The former is writing the resulting values to OCR1AL, the latter to OCR1BL.
Edit: OK, now I understand what you are trying to do, but there are certainly better approaches.
You have not set Timer1 COM1B1:0 appropriately, so pin 10 is not being changed by the timer.
A major problem with ISRs in timing is the latency. My approach would be to turn off ALL interrupts, and just use a loop. Look up Direct Digital Synthesis.
However, I would still like to understand why the example I posted doesn't work. I would expect the Pin 10 to react to the values that are written to OCR1BL which is not happening.
Are the configurations of timer1 correct? I suspect that this is where it's going wrong.
Here are those lines again.
TCCR1B = (1 << CS10); // Set prescaler to full speed 16MHz (no prescaling)
TCCR1A = (1 << COM1A1);
TCCR1A |= (1 << COM1A0);
TCCR1A |= (1 << WGM10); // Put timer into 8-bit fast PWM mode
TCCR1B |= (1 << WGM12);
As @jremington says, you have not set up Timer1 correctly for output on both pins.
I do not think that using Timer2 and Timer0 to change the values of OCR1A and OCR1B is necessary or working the way you think it is.
You are running all timers at the same clock rate with no prescaler. At OCR2A = 60 and OCR0A = 80 you change the values of OCR1A and OCR1B to the next value in your wave matrix.
But, Timer 1 is running in mode 5 -- 8 bit fast pwm to 255. In that mode, the update of OCR1X is at BOTTOM. That is, Timer 1 will run up to 255, reset itself to 0 and change the compare match values when it does so.
I think that it would be much simpler and without multi-timer ISR timing issues to just use an overflow interrupt on Timer1 to change the values of OCR1A and OCR1B.
@jremington: thank you very much. For some reason at first my browser only showed the first two lines of your reply #7 before I sent my last reply. Therefore I didn't see your comment regarding COM1B1 until now. I'll try that first thing tomorrow.
@cattledog: thanks for the advice. I'll try to do it the way you suggest, but first I'll try to get this example working.
I get the same pitch on pin 9 as on pin 10, although I'm setting OCR0A and OCR2A to different values.
The output duty cycle is determined by OCR1A and OCR1B which are both set to wave[index++] in the respective timer0 and timer2 ISRs. I would expect them to be the same.
With no prescaler and 8 bit pwm, the frequency of the pwm is 62,500 Hz.
the ISRs are resetting TCNT0 and TCNT2 respectively to a low value. Therefore I would expect the duty cycle of the timers 0 and 2 to be independent of each other – according to the value of OCR0A and OCR2A –, as they are stepping through the wave array at different speeds.
I would expect the resulting frequenzy to be 16MHz/(256 * OCR0A) for timer0 and 16MHz/(256 * OCR2A) for timer2.
I don't really understand why they tune in to the same frequency while OCR0A and OCR2A are set to different values.
Do you see my mistake?
I'm trying to create two sine tone oscillators that can be tuned independently of each other. One oscillator should be output through pin9, the other through pin10. I'm using a 10kOhm resistor and 0.1 microF capacitor as a lowpass filter per output, in order to average the PW modulation.
Right now I have output on both pins, but the resulting frequency is always the same.
It should be adjustable by assigning values to OCR2A and OCR0A. If is set OCR2A and OCR0A close to each other (e.g. OCR2A = 50 and OCR0A = 60) I get the exact same frequency on both pins. If I tune them further apart (e.g. OCR2A = 40 and OCR0A = 100) they lock into an octave interval. When I slightly change any of those values, both frequencies change but they always remain in unison or in an octace interval.
the ISRs are resetting TCNT0 and TCNT2 respectively to a low value. Therefore I would expect the duty cycle of the timers 0 and 2 to be independent of each other - according to the value of OCR0A and OCR2A -, as they are stepping through the wave array at different speeds.
Yes, I see that now.
I still think that there is an issue with the updating of Timer1 in 8 bit fast pwm mode. OCR1X will update only when the cycle completes.
All timers are set to the same prescaler, and I think that if, for example, OCR2A is set at 50 and the ISR will update the value of OCR1A 5 times along Timer1's run up to 255, only the last value will be valid for the update.
I think that you can do what you are trying to do if Timer0 and Timer2 which are telling Timer1 when to change are running slower than Timer1.
For example if Timer 2 changes OCR1A every cycle of Timer 1 and Timer 0 changes OCR1B every two cycles of Timer 1, then the frequencies will be 2/1. You just can't be faster than the fundamental frequency of Timer 1.
Your original code had Timer2 and Timer 0 with a prescaler of 8. Why did you change to no prescaler?
I don't think the two outputs can be at a ratio something other than an integral divider of the fundamental frequency i.e. 1/2, 1/3, 1/4 of the 62,000 HZ.
There may be other ways of setting up the timers for more flexibility. For example, if you set Timer 1 to Fast PWM with ICR1 as TOP in mode 14 you will have more control over the fundamental frequency then when you are using 8bit with 255 TOP. Still, I think you are going to be limited in the two outputs, by updates every n cycles with >=1.
Dear Cattledog,
your explanation makes a lot of sense. Thank you very much. Now I understand better what the problem is. I'll try the mode 14 solution with ICR1 you suggested.