I am well familiar with PWM generation in Atmega128 and its family microcontrollers. I have been using prescalar and other registers for generating frequency. But I have to generate 20KHz pwm signal. I tried but I could not get the desired output. Can anyone suggest me or help me how to do it ?
As far as I know, in atmega128, 1 instruction takes 1 cycle. Using 16MHz crystal, 1 instruction completes in 1/16M sec. I tried to generate 20Khz signal (50 us)with 25us duty cycle. But I get different frequency (277.78 Hz) in oscilloscope which is far less than 20KHz My calculation was 16MH = 20000Hz * 800. for 0-399 count, I made port high and 399-799 count, I made port low.
void frequency(void){ // 20kHz Frequency
if (cnt1 <= 399){
PORTB |= (1<<7);
} else {
PORTB &= ~(1<<7);
}
cnt1++;
if (cnt1 >= 800) cnt1 = 0;
}
I don't have access to the 128 but verified its 16-bit Timer 1 is similar to that in the 328 and 32U4 so the following should work with minor modification (the main sticking point is probably looking up what pin the overflow register is bound to):
#include <avr/io.h>
#include <util/delay.h>
struct CTC1
{
static void setup()
{
// CTC mode with TOP-OCR1A
TCCR1A = 0;
TCCR1B = _BV(WGM12);
// toggle channel A on compare match
TCCR1A = (TCCR1A & ~(_BV(COM1A1) | _BV(COM1A0))) | _BV(COM1A0);
// set channel A bound pin PB1 to output mode
#if defined(__AVR_ATmega32U4__)
DDRB |= _BV(5);
#else
DDRB |= _BV(1);
#endif
}
static void set_freq(float f)
{
static const float f1 = min_freq(1), f8 = min_freq(8), f64 = min_freq(64), f256 = min_freq(256);
uint16_t n;
if (f >= f1) n = 1;
else if (f >= f8) n = 8;
else if (f >= f64) n = 64;
else if (f >= f256) n = 256;
else n = 1024;
prescale(n);
OCR1A = static_cast<uint16_t>(round(F_CPU / (2 * n * f) - 1));
}
static void prescale(uint16_t n)
{
uint8_t bits = 0;
switch (n)
{
case 1: bits = _BV(CS10); break;
case 8: bits = _BV(CS11); break;
case 64: bits = _BV(CS11) | _BV(CS10); break;
case 256: bits = _BV(CS12); break;
case 1024: bits = _BV(CS12) | _BV(CS10); break;
default: bits = 0;
}
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | bits;
}
static inline float min_freq(uint16_t n)
{
return ceil(F_CPU / (2 * n * 65536));
}
};
void setup()
{
CTC1::setup();
CTC1::set_freq(20e3);
}
void loop()
{
// do whatever
_delay_ms(1);
}
int main()
{
setup();
for (;;)
loop();
}
I tested on my scope and measure exactly 20kHz off a 328p running at 16MHz. If 20kHz is the only frequency you need then you can simplify this substantially. Translating the code to use one of the 8-bit timers is also straightforward though I haven't verified that it's possible to hit exactly 20kHz with those.