NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Support Forum » Need help understanding PWM

December 19, 2012
by BaranSahin
BaranSahin's Avatar

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.

December 19, 2012
by esoderberg
esoderberg's Avatar

BaranSahin,

OCR2A is an 8 bit register.

Eric

December 19, 2012
by pcbolt
pcbolt's Avatar

Baran -

Timer 2 is only an 8-bit timer and maxes out at 255. You should still be able to use it, but you will have to scale down your PW parameters and suffer a little loss in resolution.

December 19, 2012
by pcbolt
pcbolt's Avatar

Sorry Eric -

You posted while I was composing my post.

December 20, 2012
by BaranSahin
BaranSahin's Avatar

Thank you! Although I'm still having a hard time following the spec sheet with all the registers and their definitions (I just need to make some time and study it), I got the code to work. Much appreciated.

Post a Reply

Please log in to post a reply.

Did you know that interrupts can be used to trigger pieces of code when events happen? Learn more...