Hi all,
I'm working on a project of creating a 16 bit PWM output using only the Arduino board (Duemilanove 328).
And it's working great really! I can control all of the pins individually with a unique 16 bit duty cycle.
Only problem however is: my Timer1 interrupt sometimes "compares" too early with the OCR1A that I've set.
This problem only occurs in the interval OCR1AH/OCR1AL of opproximately 2/128 to 3/128.
So almost 256 steps of around 56000 total I get a miscompare. This is quite annoying and I have no idea why this is happening. The past few days I've been breaking my head over this with no avail.
I really hope somebody can help me out
Below is my program, quite lengthy I know. But no need to inspect everything, only the ISR routine really. The other code is mainly to enable PWM on all pins rather than one alone.
Basically what my code does is: it checks for the pin with the lowest duty cycle (OCR1AH/OCR1AL) and sets that as the compare value. Next it checks the pin with the lowest but one duty cycle and sets that as OCR1AH/OCR1AL. This goes on until it has checked all the pins at which it sets the OCR1AH/OCR1AL to 255/255 and updates the appropriate values of all the pins.
What the program outputs is this: TCTN1H TCNT1L - OCR1AH OCR1AL
These are the timer values at which the interrupt is executed and at which it should have been executed. Usually there is an offset of about 70 low bytes, due to variable declaration, ..
However, wait a bit and you'll see a very strange behavior when it should execute around 2/128 at which it suddenly executes at 0/96.
This is NOT when it should have happened! I truly have no idea why this happens, and only around that value.
For those who want to visualize the problem, look at the onboard LED to see it become somewhat more dim at the "problem zone". Or you could attach an LED with a resistor to pin 8.
A very mysterious problem. Anybody who has any ideas or has experienced this in the past, please let me know.
Thanks in advance.
#include <avr/interrupt.h>
#include <avr/io.h>
const int aantalPwmPins = 9; // edit this
volatile byte pwmPins[aantalPwmPins] = {0,1,2,8,4,10,11,12,13}; // edit this
volatile byte compValH[aantalPwmPins];
volatile byte compValL[aantalPwmPins];
volatile byte tempCompValH[aantalPwmPins];
volatile byte tempCompValL[aantalPwmPins];
volatile byte tempOCH;
volatile byte tempOCL;
volatile byte beginIndex = 0;
volatile byte tempOrder[aantalPwmPins];
volatile byte tempPwmOrder[aantalPwmPins];
volatile byte pwmOrder[aantalPwmPins];
volatile byte flagOrder[aantalPwmPins];
//TEMP
volatile byte hi;
volatile byte lo;
ISR(TIMER1_OVF_vect) {
};
ISR(TIMER1_COMPA_vect) {
tempOCH = OCR1AH;
tempOCL = OCR1AL;
lo = TCNT1L;
hi = TCNT1H;
for(byte i = beginIndex; i<aantalPwmPins; i++)
{
if(tempOCL == compValL[pwmOrder[i]] && tempOCH == compValH[pwmOrder[i]])
{
writeOffToPin(pwmPins[pwmOrder[i]]);
if(i != aantalPwmPins - 1)
{
OCR1AH = compValH[pwmOrder[i+1]];
OCR1AL = compValL[pwmOrder[i+1]];
}
else
{
OCR1AH = 255;
OCR1AL = 255;
}
TCNT1H = tempOCH;
TCNT1L = tempOCL;
}
}
if(tempOCL == 255 && tempOCH == 255)
{
TIMSK1 &= 253;
for(byte j = 0; j < aantalPwmPins; j++)
{
compValH[j] = tempCompValH[j];
compValL[j] = tempCompValL[j];
pwmOrder[j] = tempPwmOrder[j];
}
for(byte i = 0; i < aantalPwmPins; i++)
{
if(compValH[pwmOrder[i]] == 0)
{
if(compValL[pwmOrder[i]] == 0)
continue;
beginIndex = i;
OCR1AH = compValH[pwmOrder[i]];
OCR1AL = compValL[pwmOrder[i]];
break;
}
else
{
beginIndex = i;
OCR1AH = compValH[pwmOrder[i]];
OCR1AL = compValL[pwmOrder[i]];
break;
}
}
for(byte j = 0; j < aantalPwmPins; j++)
{
if(!(tempCompValH[pwmOrder[j]] == 0 && tempCompValL[pwmOrder[j]] == 0))
writeOnToPin(pwmPins[pwmOrder[j]]);
}
TCNT1H = 0;
TCNT1L = 0;
TIMSK1 |= 2;
}
};
void setup() {
Serial.begin(115200);
for(int i = 0; i < 14; i++)
pinMode(i, OUTPUT);
setupTimers();
}
void loop()
{
for(int h = 0; h < 50; h++)
{
for(int l = 0; l < 256; l++)
{
updatePwm(8, (byte) h, (byte) l);
updatePwm(13, (byte) h, (byte) l);
Serial.print(hi, DEC);
Serial.print(" ");
Serial.print(lo, DEC);
Serial.print(" - ");
Serial.print(h, DEC);
Serial.print(" ");
Serial.println(l, DEC);
delay(10);
}
analogWrite(6, h);
}
}
void setupTimers()
{
TCCR1A = 0;
TCCR1B = 0;
TCCR1B = _BV(CS10);
TIMSK1 = _BV(OCIE1A) | _BV(TOIE1);
OCR1AH = 255;
OCR1AL = 255;
TIFR1 = 1;
}
void writeOnToPin(byte pin)
{
if(pin < 8) //PORTD
{
PORTD |= 1<<pin;
}
else if(pin < 14) //PORTB
{
pin -= 8;
PORTB |= 1<<pin;
}
}
void writeOffToPin(byte pin)
{
if(pin < 8) //PORTD
{
PORTD &= ~(1<<pin);
}
else if(pin < 14) //PORTB
{
pin -= 8;
PORTB &= ~(1<<pin);
}
}
void printArray(volatile byte *a)
{
for (int i = 0; i < aantalPwmPins; i++)
{
if(a[i] < 100) Serial.print(" ");
if(a[i] < 10) Serial.print(" ");
Serial.print(a[i], DEC);
Serial.print(' ');
}
//Serial.println();
}
void updatePwm(byte pin, byte high, byte low)
{
byte c = pwmIndex(pin);
if(c != 255)
{
tempCompValH[c] = high;
tempCompValL[c] = low;
newQuickSort();
}
}
byte pwmIndex(byte c)
{
for(byte i = 0; i < aantalPwmPins; i++)
if(pwmPins[i] == c) return i;
return 255; //error
}
void newQuickSort()
{
for(byte i = 0; i < aantalPwmPins; i++)
flagOrder[i] = false;
byte lowestH = 255;
byte lowestL = 255;
byte lowestIndex;
for(byte i = 0; i < aantalPwmPins; i++)
{
for(byte j = 0; j < aantalPwmPins; j++)
{
if(flagOrder[j] == false)
{
if(tempCompValH[j] < lowestH)
{
lowestIndex = j;
lowestH = tempCompValH[j];
lowestL = tempCompValL[j];
}
else if(tempCompValH[j] == lowestH)
{
if(tempCompValL[j] <= lowestL)
{
lowestIndex = j;
lowestH = tempCompValH[j];
lowestL = tempCompValL[j];
}
}
}
}
flagOrder[lowestIndex] = true;
tempOrder[i] = lowestIndex;
lowestH = 255;
lowestL = 255;
}
cli();
for(byte i = 0; i < aantalPwmPins; i++)
tempPwmOrder[i] = tempOrder[i];
sei();
}