December 19, 2012
by BaranSahin
|
Hello,
I need some help understanding the PWM concepts.
Using the servo example I was able to get 0-3.3V output from PB2.
I tried duplicating this for PB3 without any luck.
I think I'm failing to set up TCCR2A/TCCR2B properly.
Here is my code below:
// dsp_from_keyboard
// for NerdKits with ATmega168
// - If in Linux #include <unistd.h>
#include <stdio.h>
#include <math.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "../../NerdKits/SampleCode/Code/libnerdkits/delay.h"
#include "../../NerdKits/SampleCode/Code/libnerdkits/lcd.h"
#include "../../NerdKits/SampleCode/Code/libnerdkits/uart.h"
// PIN DEFINITIONS:
//
// PB2 - PWM(1)
// PB3 - PWM(2)
// DEFINES
#define F_CPU 14745600
//#define PWM_MIN 1300
//#define PWM_MAX 4450
//#define PWM_START 2765
#define PWM_MIN 0
#define PWM_MAX 24200
#define PWM_START 10000
// SUB ROUTINES
void pwm_set(uint16_t x,int y) {
if (y == 2) OCR1B = x; // Control PWM-1 - PB2
if (y == 3) OCR2B = x; // Control PWM-2 - PB3
}
void pwm_init() {
// setup Timer1 for Fast PWM mode, 16-bit
// COM1B1 -- for non-inverting output
// WGM13, WGM12, WGM11, WGM10 -- for Fast PWM with OCR1A as TOP value
// CS11 -- for CLK/8 prescaling
OCR1A = 36864; // sets PWM to repeat pulse every 20.0ms
OCR2A = 36864; // sets PWM to repeat pulse every 20.0ms
//
pwm_set(PWM_START,2);
pwm_set(PWM_START,3);
//
TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<WGM10);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
//
//TCCR2A = _BV(COM2A1) | _BV(WGM22) | _BV(WGM20);
//TCCR2B = _BV(CS20);
TCCR2A = (1<<COM2A1) | (1<<WGM22) | (1<<WGM20);
//TCCR2B = (1<<CS20);
TCCR2B = (1<<CS21);
// each count is 8/14745600 = 0.5425us.
// so 1.0ms = 1843.2
// 1.5ms = 2764.8
// 2.0ms = 3686.4
// 20.0ms = 36864
}
int main() {
// start up the LCD
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_home();
lcd_line_one();
lcd_write_string(PSTR("Init. LCD done!"));
// start up the serial port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;
lcd_line_one();
lcd_write_string(PSTR("Init. SERIAL PORT done!"));
//Clear LCD display
lcd_line_one();
lcd_write_string(PSTR(" "));
lcd_line_two();
lcd_write_string(PSTR(" "));
lcd_line_three();
lcd_write_string(PSTR(" "));
lcd_line_four();
lcd_write_string(PSTR(" "));
// set PB2,PB3 as output
DDRB |= (1<<PB2) | (1<<PB3);
char tc;
uint16_t pos_tr = PWM_START;
uint16_t pos_fb = PWM_START;
// init PWM
pwm_init();
// Print the current servo position to the LCD.
lcd_line_two();
fprintf_P(&lcd_stream, PSTR(" PB2 pos: %d "), pos_tr);
lcd_line_three();
fprintf_P(&lcd_stream, PSTR(" PB3 pos: %d "), pos_fb);
while(1) {
//pwm_set(pos);
// Wait for a character to be sent to the serial port.
tc = uart_read();
if(tc==']')
{pos_tr+= 300;
pwm_set(pos_tr,2);}
if(tc=='[')
{pos_tr-= 300;
pwm_set(pos_tr,2);}
//
if(tc=='w')
{pos_fb+= 300;
pwm_set(pos_fb,3);}
//PORTB &= ~(1<<PB3);}
if(tc=='s')
{pos_fb-= 300;
pwm_set(pos_fb,3);}
//PORTB |= (1<<PB3);}
// bounds checking
if(pos_tr > PWM_MAX) pos_tr = PWM_MAX;
if(pos_tr < PWM_MIN) pos_tr = PWM_MIN;
if(pos_fb > PWM_MAX) pos_fb = PWM_MAX;
if(pos_fb < PWM_MIN) pos_fb = PWM_MIN;
// Print current to the LCD.
lcd_line_two();
fprintf_P(&lcd_stream, PSTR(" PB2 pos: %d "), pos_tr);
lcd_line_three();
fprintf_P(&lcd_stream, PSTR(" PB3 pos: %d "), pos_fb);
}
return 0;
}
All help is much appreciated.
Going through the forums is what got me so far.
Thank you. |