PWM problem

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

I've used on the ATmega8 the 8 bit fast pwm feature before. It was really simple, I just had to go

OCR2 = 200; 

for example and then the output port OC2 would start PWM.

I'm trying to do the same with the 16 bit PWM now, but its not working. I've been looking at this for a few hours now and have decided that I'm stuck. Please help if you can.

TCCR1A |= (1 << COM1A1) | ( 1 << COM1B1 ) ; //set both to non inverting mode
   TCCR1A |= (1 << WGM11); 
   TCCR1B |= (1 << WGM13) | (1 << WGM12) ;
   ICR1 = 65535;

I did ICR1 = 65535; because from the data sheet it seemed that this would be necessary to make it a 16 bit PWM channel, i.e. the values would go from 0-65535.

that is how I set up my PWM

and I'm just trying to do

OCR1A = 60000;
		OCR1B = 60000;

I tried

 OCR1AH = (60000 >> 8);
OCR1AL = 60000;

but that didnt work

When I probe the OC1 A&B channels on the breadboard, I never read a voltage.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did You enable the counter? Setting the CS1x bits in TCCR1B register.

Also, have You set the corresponding port bit as output ?

Also, remember to load the 16 bit registers so that You load the high byte first and then the low one. If You are using interrupts and the code is not in an ISR routine - it is better to turn off the interrupts while doing this.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for responding, yes, I have both of these in my code

TCCR1B |= (1 << CS10); // no prescalar

and

DDRB   = (1 << 3 ) | ( 1 << 2) | (1 << 1);

(ports 1 and 2 are where oc1a and oc2b are)

OCR1AH = (60000 >> 8);
OCR1AL = 60000; 

I tried that, thinking I would be loading the upper part first like you said, but that didn't seem to work. Neither did

OCR1A=60000; OCR1B=60000

any ideas?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have never tried to make this a 16 bit PWM.

I would try to put something else than 0xFFFF (65535) to ICR1. I would try 0xFFFE maybe even 0xFFFD.

Another thing is the fact the the register is really a capture register - so it gets loaded if there is an event in the ICP1 pin. This should be disabled.

edit: Seems to be autoimatic behaviour of the chip.

edit2: I found this code snippet:

TCCR1A = 0;          // disable all PWM on Timer1 whilst we set it up

ICR1 = 19999 ;   // frequency is every 20ms

// Configure timer 1 for Fast PWM mode using ICR1, with no prescaling

TCCR1A = (1<<WGM11)
    TCCR1B = (1 << WGM13) | (1<<WGM12) | (1 << CS10);

Why is the PWM disabled before setting the ICR1 ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I found in ATmega8 datasheet: "For generating a waveform output in CTC mode, the OC1A output can be set to toggle its logical level on each Compare Match by setting the Compare Output mode bits to toggle mode(COM1B1:0 = 1)"

Just try this also as u have already tried COM1B1=1, COM1B0=0.

Also,
in TCCR1A:Bit 2 – FOC1B: Force Output Compare for channel B
the datasheet says "for ensuring compatibility with future devices, these bits must be set to zero when
TCCR1A is written when operating in a PWM mode"

But I am not very much clear about it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I tried that, thinking I would be loading the upper part first like you said, but that didn't seem to work. Neither did ...

Did you wait long enough? In the PWM modes the OCR1A and OCR1B registers do not update until the timer reaches TOP. So since you start the timer, then set OCR1A or OCR1B, you won't see the registers change until the timer reaches 65565. Also, the original simulator does not handle all 16 bit timer functions properly. Simulator 2 will work, but the mega8 is not supported in that.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

eskoilola wrote:

edit2: I found this code snippet:
TCCR1A = 0;          // disable all PWM on Timer1 whilst we set it up

ICR1 = 19999 ;   // frequency is every 20ms

// Configure timer 1 for Fast PWM mode using ICR1, with no prescaling

TCCR1A = (1<<WGM11)
    TCCR1B = (1 << WGM13) | (1<<WGM12) | (1 << CS10);

Why is the PWM disabled before setting the ICR1 ?

May be by error...

In datasheet:

The ICR1 Register can only be written when using a Waveform Generation mode that
utilizes the ICR1 Register for defining the counter’s TOP value. In these cases the
Waveform Generation mode (WGM13:0) bits must be set before the TOP value can be
written to the ICR1 Register.

bits must be set _before_ ...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,
I've used 16 bit PWM for a servo drive, albeit on a ATiny24 and not the ATmega8. The code snippet below shows how I set up the Timer/Counter and maybe that will give you some clues. It was simple to do yielded a fixed frame rate of around 50Hz and the output on OC1A. The pulse width was varied by simply writing the appropriate value to OCR1A. My example has the clock prescaler set to a division of 2 - you may not need this. I've not used the ATmega8 but I guess it will not be to dissimilar. Mind you I have been wrong before.

   CLKPR = 0x80;				//Set CLKPCE (Clock prescaler enable)
   CLKPR = 0x01;				//Set CLKPS bits (Clock prescale select) 
                            //to a division of 2. System clock now 4MHz

// init timer/Counter1	 Used for servo PWM output
	TCCR1A = 0b10000000;			//Set COM1A1 high (Clear OC1A on compare match)
	TCCR1B = 0b00010001;			//Set WGM13 high (Phase and Frequency correct PWM), 
                                //set CS10 high - prescaler = 1
	ICR1 = 40000;				    //Set PWM frame rate to 50 Hz
    
//Set up input / outputs

    DDRA  = 0xff;         		// use port A for outputs
    PORTA = 0xff;				// Clear Port A - inverted output

I make no claims for the tidiness or efficiency of the code, all I can say is that it works well on the ATiny24.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

apc3161:
I have tested your code in real and it works.
I only changed OCR and prescaler so that the signal is visible with LED.

//mega8, 8MHz
int main()       
{                    

   DDRB=255;

   TCCR1A |= (1 << COM1A1) | ( 1 << COM1B1 ) ; //set both to non inverting mode
   TCCR1A |= (1 << WGM11);
   TCCR1B |= (1 << WGM13) | (1 << WGM12);
   TCCR1B |= (1 << CS12);
   
   ICR1 = 65535;

   OCR1A = 30000;
   OCR1B = 30000;

   for(;;)
   {

   }                  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would rearrange that (though it may not be important).

I would put the setting of ICR1 before setting the CS bit(s) (but after setting the WGM bits). This is because ICR1 may be at a low value that will cause an overflow before you set it to its disired value (admittedly this is an unlikely circumstance).

I would move the setting of OCR1A and OCR1B to before the setting of the WGM bits. The way you have it, the first time through the timer the compare match will be wrong since these two registers are double buffered when in PWM mode and will not actually change value until TOP is reached. Again, this may not be important to you.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is a program for the Atmega 8 using 16bit PWM operating 2 servos. It may not be pretty but it works

/*************************************************************************
*               Servo Driver Program using  ATMEGA 8 					 *
*	           Uses Fast PWM   Type 14 on Datasheet                      *
*  Drives 1 or 2 Servo Motors    PB1 drives Servo 1;  PB2 Drives Servo 2 *
*          Input Pins PC0 and PC1 Control the direction of Servo 1		 *
*		   Input Pins PC2 and PC3 Control the direction of Servo 2		 *
*        PB3 can be used to bink an LED at 1hz to check timer setup		 *
*    Based on 180 degree Servos; if < 180 tighten up CCW and CW limits 	 *
*																		 *
*                   								 *
*                  Last Revision  11/19/09								 *
*************************************************************************/

#include 
#define F_CPU 1000000UL  // 1 MHz
#include 

#define CCW_Limit1   2100  //  Servo 1 Counter clockwise limit
#define CW_Limit1   500	   //  Servo 1  Clockwise limit
#define Servo1_speed  4    //  Control the speed of Servo 1; lower number = faster

#define CCW_Limit2   2100  //  Servo 2 CCW limit
#define CW_Limit2   500    //  Servo 2  CW limit
#define Servo2_speed  2    //  Control the speed of Servo 2; higher number= slower



int main (void) {

// Select  Timer 1 for Fast PWM using ICR1 by setting bits WGM13 and WGM12
//  CS10 is selected for prescaler = 1  
//  Using Fast PWM number 14 on Datasheet   
TCCR1B  = (1<< WGM13) | (1<<WGM12) | (1<<CS10);

//Need 50hz output (20MS); 1,000,000hz/sec /50hz = 20000sec;  so ICR1 is 20000
ICR1=20000;

DDRB |= _BV(1);  // Make  PB1 an output port for Servo 1
DDRB |= _BV(2);  // Make  PB2 an output port for Servo 2

DDRC |= 0x00;  // Port C Data direction is automatically input but be sure
// Enable pullups for switch detection on PBC 0-3
PORTC = (1<<0) | (1<<1)| (1<<2) | (1<<3);    

//  Set bits 7,5 & 1 for non-inverting mode, TCCR1A Mode 14 for Timer 1
TCCR1A = (2 << 4) | (2<<6) | (1<<WGM11); //Enable PWM on PB1 in non-inverted compare mode 2. 
// note 2<<4,  2 is equal to binary 10 shifted 4 places, equal to binary 100000
//TCCR1A |= 2 << 6;  // Enable PWM on PB2, binary 10000000
//  By "|" "or"ing  TCCR1A, we have set the bits as 10100000
//Then include 1<<WGM11;  Sure, I could have just used TCCR1A = 0xA2; 


OCR1A = 1500; //OCR1A is the actual output pulse to the servo1 on PB1.
//  OCR1A is pumping out a 1.5 ms pluse every 50 hz. 
//  This pulse is necessary to hold it in position.  
//  Changing this value will alter the servo position.

OCR1B = 1500;  //  OCR1B is output to server2 on PB2. This is center


while (1) {
       //  Test to see if servo 1 is working by cycling through range.
      //for (OCR1A = CW_Limit1; OCR1A <= CCW_Limit1;OCR1A ++)
	  // { _delay_ms(2);}
      //for (OCR1A = CCW_Limit1; OCR1A >= CW_Limit1;OCR1A --)
	   //{ _delay_ms(2);}

			  
     //Test on PB3 to see if timing is correct on STK500 led -uncomment next 3 lines
	 // Led should blink every second -if not clock speed or prescaler problem.      
     //DDRB ^= 1 << 3;
	 //_delay_ms (500);
	 

//CONTROLS SERVO 1 with buttons on PBC0  and PBC1

	if (((PINC & (1<<0)) == 0) && (OCR1A > CW_Limit1))  {
	    //_delay_ms(0.1);// pause a second for debouncing
		 if (((PINC & (1<<0)) == 0)) { // double check to make sure key pressed
			OCR1A --;  //Reduce OCR1A and Turn Servo 1 clockwise
			}
			_delay_ms(Servo1_speed);
           }
    if (((PINC & (1<<1)) == 0) && (OCR1A  CW_Limit2))  {
	   // _delay_ms(0.1);// pause a second for debouncing
		 if (((PINC & (1<<2)) == 0)) { // double check to make sure key pressed
			OCR1B --;  //Reduce OCR1B and Turn Servo 1 clockwise
			}  _delay_ms(Servo2_speed);
           }

    if (((PINC & (1<<3)) == 0) && (OCR1B 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Visovian wrote:
apc3161:
I have tested your code in real and it works.
I only changed OCR and prescaler so that the signal is visible with LED.

//mega8, 8MHz
int main()       
{                    

   DDRB=255;

   TCCR1A |= (1 << COM1A1) | ( 1 << COM1B1 ) ; //set both to non inverting mode
   TCCR1A |= (1 << WGM11);
   TCCR1B |= (1 << WGM13) | (1 << WGM12);
   TCCR1B |= (1 << CS12);
   
   ICR1 = 65535;

   OCR1A = 30000;
   OCR1B = 30000;

   for(;;)
   {

   }                  
} 

Visovian, thanks so much. This worked. Although I had to change that one section from

1<< CS12 to 1<<CS10 because my chip is clocked to a lower frequency.