Hi
Recently I wrote a code which programs the timer, based on frequency and duty.
The code is part of my frequency generator, which is an example of my GUI interface library.
Code is hosted here:
http://code.google.com/p/m2tklib/source/browse/arduino/glcd/FreqGen/FreqGen.pde
Project picture is here:
http://code.google.com/p/m2tklib/wiki/FreqGen
In the code below, the parameters are assigned to global variables:
uint8_t fc_pin = 3; // 3, 5, 11 (only OCRnB)
uint32_t fc_user_freq; // requested user frequency
uint8_t fc_duty = 1; // PWM value 1..99 %
Then simply call
void fc_calc(void)
which programs the timer based on the values above.
Any frequency can be assigned to fc_user_freq, however not all frequencies are possible, so fc_calc() will choose a frequency next to the requested value. E.g. for 5800Hz it will choose 5813Hz. The value 5813Hz can be found in the variable fc_calc_freq.
// Timer 0 & 1, N = { 1, 8, 64, 256, 1024 }
uint8_t fc_prescalar_div01[] = { 3, 3, 2, 2, 0 };
// Timer 2, N = { 1, 8, 32, 64, 128, 256, 1024 }
uint8_t fc_prescalar_div2[] = { 3, 2, 1, 1, 1, 2, 0 };
// input values
uint8_t fc_pin = 3; // 3, 5, 11 (only OCRnB)
uint32_t fc_io_freq = F_CPU;
uint32_t fc_user_freq; // requested user frequency
uint8_t fc_duty = 1; // PWM value 1..99 %
#define FC_DUTY_MAX 100
// output values
uint8_t fc_timer = 0; // timer: 0, 1, 2
uint8_t fc_ab = 0; // 0 = A, 1 = B
uint8_t fc_prescalar_idx; // calculated value for CS22:0 bits
uint8_t fc_top_value; // calculated value for OCRnA
uint32_t fc_calc_freq; // calculated real frequency
uint8_t fc_calc_duty; // calculated real duty
// based on the fc_pin value, calculate the timer
void fc_calc_timer_ab(void)
{
switch(fc_pin)
{
case 5: fc_timer = 0; fc_ab = 1; break;
//case 6: fc_timer = 0; fc_ab = 0; break;
//case 9: fc_timer = 1; fc_ab = 0; break;
case 10: fc_timer = 1; fc_ab = 1; break;
// case 11: fc_timer = 2; fc_ab = 0; break;
case 3: fc_timer = 2; fc_ab = 1; break;
default: fc_timer = 0; fc_ab = 1; break;
}
}
// Fast PWM Mode: f = fc_io_freq / ( N * ( 1 + OCRnA) )
// ==> N * ( 1 + OCRnA) = fc_io_freq / f = q
// Algorithm:
// calculate q
// try values for N, until q/N is below 256
// if found, then OCRnA = q/N - 1
uint8_t fc_calc_prescalar_idx_and_top_value(void)
{
uint8_t idx = 0;
uint8_t div;
uint32_t q;
q = fc_io_freq;
q /= fc_user_freq;
for(;;)
{
if ( q <= 256UL )
{
idx++;
fc_prescalar_idx = idx;
q--;
fc_top_value = q;
return 1;
}
if ( fc_timer == 2 )
div = fc_prescalar_div2[idx];
else
div = fc_prescalar_div01[idx];
if ( div == 0 )
break;
q >>= div;
idx++;
}
fc_prescalar_idx = 0;
fc_top_value = 0;
return 0; // no prescalar found
}
// calculate frequency from prescalar index and top value
// f = fc_io_freq / ( N * ( 1 + OCR2A) )
void fc_calc_frequency_and_duty(void)
{
uint32_t t;
uint8_t idx = fc_prescalar_idx;
if ( idx == 0 )
{
fc_calc_freq = 0;
return ;
}
t = fc_top_value;
t++;
idx--;
while( idx > 0 )
{
idx--;
if ( fc_timer == 2 )
t <<= fc_prescalar_div2[idx];
else
t <<= fc_prescalar_div01[idx];
}
fc_calc_freq = fc_io_freq;
fc_calc_freq /= t;
fc_calc_duty = ((uint16_t)fc_top_value * (uint16_t)fc_duty)/FC_DUTY_MAX;
}
// OCRnA contains the top value
// OCRnB contains the duty value
// --> Only port pin B can be used
void fc_set_hw(void)
{
uint8_t d;
d = fc_calc_duty;
pinMode(fc_pin, OUTPUT);
//analogWrite(fc_pin, 40);
//digitalWrite(fc_pin, HIGH);
if ( fc_timer == 0 )
{
// Set fast PWM mode, TOP is OCRA
TCCR0A |= _BV(WGM01) | _BV(WGM00);
TCCR0B |= _BV(WGM02);
// none inverting fast PWM
if ( fc_ab == 0 )
{
TCCR0A |= _BV(COM0A1);
TCCR0A &= ~_BV(COM0A0);
}
else
{
TCCR0A |= _BV(COM0B1);
TCCR0A &= ~_BV(COM0B0);
}
// prescalar
TCCR0B &= ~7;
TCCR0B |= fc_prescalar_idx;
// set top value
OCR0A = fc_top_value;
// set duty
OCR0B = d;
}
else if ( fc_timer == 1 )
{
// Set fast PWM mode, TOP is OCRA
TCCR1A |= _BV(WGM11) | _BV(WGM10);
TCCR1B |= _BV(WGM12) | _BV(WGM13) ;
// none inverting fast PWM
if ( fc_ab == 0 )
{
TCCR1A |= _BV(COM1A1);
TCCR1A &= ~_BV(COM1A0);
}
else
{
TCCR1A |= _BV(COM1B1);
TCCR1A &= ~_BV(COM1B0);
}
// prescalar
TCCR1B &= ~7;
TCCR1B |= fc_prescalar_idx;
// set top value
sei();
OCR1AH = 0;
OCR1AL = fc_top_value;
// set duty
OCR1BH = 0;
OCR1BL = d;
cli();
}
else if ( fc_timer == 2 )
{
// ensure internal clock
ASSR &= ~(_BV(EXCLK) | _BV(AS2));
// Set fast PWM mode, TOP is OCRA
TCCR2A |= _BV(WGM21) | _BV(WGM20);
TCCR2B |= _BV(WGM22);
// none inverting fast PWM
if ( fc_ab == 0 )
{
TCCR2A |= _BV(COM2A1);
TCCR2A &= ~_BV(COM2A0);
}
else
{
TCCR2A |= _BV(COM2B1);
TCCR2A &= ~_BV(COM2B0);
}
// prescalar
TCCR2B &= ~7;
TCCR2B |= fc_prescalar_idx;
// set top value
OCR2A = fc_top_value;
// set duty
OCR2B = d;
}
}
// input: fc_timer, fc_user_freq
void fc_calc(void)
{
fc_calc_timer_ab();
fc_calc_prescalar_idx_and_top_value();
fc_calc_frequency_and_duty();
fc_set_hw();
}
Oliver