[TUT] [C] PWM for complete idiots

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

Intro: This is sort of a temporary completion of Dean's PWM tutorial until it gets finished. I just noticed the lack of PWM tutorials and example code, so I decided to finish this. Please note that I am by no means an expert on PWM (or AVR's in general). I just hope some people find this useful.

If you need an explanation of what PWM is, be sure to see Dean's tutorial I linked to above. I will describe how to implement PWM in AVR-GCC. The examples in this tutorial will be for the ATmega8515, but I will explain how to configure it for other devices.

Let us start with some code:

#include 

int main (void)
{
 
   // Port D5 as output
   DDRD   |= (1 << 5);
   
 
   // Set on match, clear on TOP
   TCCR1A  = ((1 << COM1A1) | (1 << COM1A0));

   // Phase + Frequency Correct PWM, Fcpu speed
   TCCR1B  = ((1 << CS10) | (1 << WGM13));

   for (;;)

     //where 0xa is the desired brightness
     OCR1A=0Xa;
   

} 

Let us first explain OCR1A. OCR1A is an output compare register. It is constantly compared with a timer, which generates a PWM pulse. The duty cycle of the PWM pulse is determined by the value of OCR1A. The PWM waveform is outputted to the OC1A pin. In the case of the ATmega8515, OC1A is located on pin 15, which is also PD5. That's why we needed to set port D5 as output. Setting all of PORTD as an output will also work, if you feel that is easier.

TCCR1A/B are both timers that are compared against to generate the PWM pulse. They determine whether the pulse is frequency correct or not (this example is frequency correct - see Dean's post). I don't really know the exact function of the timers, or their arguments, but I will do some research later and edit the post. If anyone has questions or comments, feel free to post. Just keep in mind this tutorial is nowhere near finished.

-Cory

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

Hello , after some modifications I got this code :

#include  

int main (void) 
{ 
   
  
   // Port D as output 
   DDRB = 255; 
   
  
   // Set on match, clear on TOP 
   TCCR1A  = ((1 << COM1A1) | (1 << COM1A0)); 

   // Phase + Frequency Correct PWM, Fcpu speed 
   TCCR1B  = ((1 << CS10) | (1 << WGM13)); 

   //where 0xA is the desired brightness 
   OCR1A =  18200; 
   ICR1  = 20000;


   while(1)
    {

	}

}

It surly works with my butterfly and sets the servo (Futaba S3003) but now I can change the value of OCR1A on the go or I can and it doesn't work any way , does anyone know how to do it ?? Another problem is that I'm trying to read the joystick state of pins and I can't do it ( I earlier did it in assembler but it doesn't work in c anymore )

Edit: It also makes a sound :) :)

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

I have been following along thru Dean's bit operation, timer and PWM tutorials - which, as an aside, are AWESOME - as well as this thread.

I was trying to walk thru the above code and am confused by the following snippet.

cmwslw wrote:

   // Phase + Frequency Correct PWM, Fcpu speed
   TCCR1B  = ((1 << CS10) | (1 << WGM13));

   for (;;)

     //where 0xa is the desired brightness
     OCR1A=0Xa;
   

} 

Let us first explain OCR1A. OCR1A is an output compare register. It is constantly compared with a timer, which generates a PWM pulse. The duty cycle of the PWM pulse is determined by the value of OCR1A.

Doesn't the above line of code set WGM13:0 to 8? And looking at the datasheet for the ATmega8515, I thought that in phase and frequency correct PWM mode setting WGM13:0 to 8 meant that ICR1 would be "TOP"?

Am very new to AVR and mcu's, so it is very likely that I am misunderstanding the datasheet and/or the tutorials and/or the code- if so I apologize.

Can someone clarify?

Thank you!

Russ

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

Quote:

Doesn't the above line of code set WGM13:0 to 8?

Yes, it does.

Quote:

And looking at the datasheet for the ATmega8515, I thought that in phase and frequency correct PWM mode setting WGM13:0 to 8 meant that ICR1 would be "TOP"?

Correct.

Setting ICR1 is missing from cmwslw's code. It was added, as you can see, in the code variant presented by Pumpkin360.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:
Quote:
Setting ICR1 is missing from cmwslw's code. It was added, as you can see, in the code variant presented by Pumpkin360.

Thanks!

But, is there a reason to set OCR1A in Pumpkin's variant - assuming that it not used by some other part of the code?

Russ

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

OCR1A is setting the duty cycle. If you never plan to vary it then just setting it once is fine. It's true that:

   for (;;) 

     //where 0xa is the desired brightness 
     OCR1A=0Xa; 

is a bit pointless (and the lack of braces makes my flesh creep!). Why bother repeatedly setting it to the same thing? But I think the author's intention was really something like:

   for (;;) {

     new_value = events_that_vary_duty();
     OCR1A = new_value; 
   }

or possibly:

   for (;;) {
     OCR1A = new_value; 
   }
}

volatile uint16_t new_value;

ISR(SOME_vect) {
  new_value = based_on_interrupt_occurrence;
}

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

Quote:

But, is there a reason to set OCR1A in Pumpkin's variant - assuming that it not used by some other part of the code?

Only if you actually want to vary the duty cycle of the PWM signal... :roll:

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:
Quote:

But, is there a reason to set OCR1A in Pumpkin's variant - assuming that it not used by some other part of the code?

Only if you actually want to vary the duty cycle of the PWM signal... :roll:

What I am grappling with is the connection between OCR1A and duty cycle. If you set TOP to OCR1A, then wouldn't TOP and COMPARE be the same and wouldn't duty cycle then be 100% for Fast PWM and 50% for other PWM modes? It seems that there are times when you want/need to use OCR1A as TOP. For example, I don't currently have access to the datasheet for the ATMega8515, but this AVR Freaks' thread quotes the datasheet for the ATMega168 and states, in part, as follows:

Quote:
By using ICR1, the OCR1A Register is free to be used for generating a PWM output on OC1A. However, if the base PWM frequency is actively changed (by changing the TOP value), using the OCR1A as TOP is clearly a better choice due to its double buffer feature.

As I initally posted, I am really "wet behind the ears" when it comes to AVR and MCUs. Clearly, I nned to spend some more time parsing thru how this works. :)

Russ

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

Quote:
If you set TOP to OCR1A

But in the mode that we are talking about, TOP is ICR1, not OCR1A. In the modes where OCR1A is TOP, then it can not be used to set the duty cycle. You must use one of the other OCR registers.

Regards,
Steve A.

The Board helps those that help themselves.

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

[edit]This rather lengthy post contains factual errors, pointed out by Steve A in a post below, followed by a post where I try to rectify my errors. I will leave this partly erroneous post as it is so as not to disturb the thread flow.[/edit]

Quote:

What I am grappling with is the connection between OCR1A and duty cycle. [...]

Lets start with the overall basics of PWM. For sakes of simlicity we will bother only with some modes (specifically the ones where the counder only counts in one direction):
The counter counts from zero up to a TOP value. This TOP value can either be fixed (modes 0, 1, 2, 3, 5, 6 and 7), or it can be in the ICR1 register (modes 8, 10, 12 and 14), or it can be the value in the OCR1A register. The OCR1A has an extra property that ICR1 does not: It is double buffered. This means that it has a mechanism where, when you write to OCR1AH the value is actually stored in a temporary register internal to the Timer/Counter. Then, when you write to OCR1AL the value in the temp register is stored in OCR1AH in the same instant. This mechanism is there to avoid spurious OCR1 values which would be in place in between the writes of the two bytes of the register.

Now, only OCR1A has this double buffering. ICR1 has not. If your code will change the TOP value while the timer is running you will probably need this double buffering so that explains the use as OCR1A as TOP.

Let move over to the modes where ICR1 holds the TOP value, ans see how PWM is generated. As stated eralier the counter counts from zero and up. When it reaches the TOP value it resets to zero. This is the essence of the CTC modes (Cleat Timer on Compare match).

For PWM we add this to the CTC operation: Every time the counter wraps it resets the PWM signal. For the first PWM channel (OC1A) the signal is set when it passes the compare value (in OCR1A) and for the second chanel (on OC1B) it sets the PWM signal when it passes its compare value (in OCR1B). Thus the ICR1 register defines the frequency of both PWM channels and OCR1A and OCR1B registers define the duty cycles. As both OCR1A and OCR1B are bouble buffered you can modify the duty cycle while the timer is running without falling into the trap of spurious values (careful though: Both OCR1A and OCR1B use the same temp register so you cant write OCR1AH, OCR1BH, OCR1AL, OCR1BL in that sequence!). (The opposite/inverted waveform can also be generated, ie. set on timer wrap, reset on compare match.)

Now, if you want to change the frequency of the PWM while the timer is running then you will risk spurious values when using ICR1 as it is not double buffered. For the sake of handling this situation there are modes where a double buffered register (OCR1A) is used as TOP. The obvious drawback is that you cant have one register defining both the TOP value and the duty cycle, so you are loosing the first PWM channel. Oly OC1B, with duty cycler controlled by OCR1B, is now a functioning PWM output.

This is what the quote is all about. It says: "If you use ICR1 as TOP then you have the PWM channel whose dudty cycle is controlled by OCR1A available. If you plan to change the TOP value while the timer is running then you should use OCR1A as TOP as it is double bufered, but you will loose one PWM channel).

PWM in three sentences: Frequency for both channels is controlled by TOP, be it ICR1 or OCR1A. PWM channel duty cycle is controlled by the OCR1x register. If TOP is in OCR1A then that PWM channel is not available.

Clearer, or did I make you more confused?

Further reading: Accessing 16-bit registers, p. 100 in the data sheet. Modes of operation, pp. 110-116 in the data sheet.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Thu. Jan 7, 2010 - 06:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
It is double buffered. This means that it has a mechanism where, when you write to OCR1AH the value is actually stored in a temporary register internal to the Timer/Counter. Then, when you write to OCR1AL the value in the temp register is stored in OCR1AH in the same instant. This mechanism is there to avoid spurious OCR1 value

Sorry, Johan, this is not really a description of double buffering, it is a description of 16 bit register access (which buffers the high byte only). ICR1 also has this type of buffering.

When the datasheet refers to double buffering in PWM modes, the entire 16 bit value is saved in a temporary register (when the low byte is written), and the value of the actual register (all 16 bits) is updated only when TOP is reached (or BOTTOM in the up/down modes). This buffering is only done with the OCR1x registers.

Regards,
Steve A.

The Board helps those that help themselves.

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

Oh dear, mixing up two things. Too long since I did this in practice. Urgg.. Let's rectify then:

The double buffering is there to avoid glitches in the PWM signal or its frequency.

Scenario 1: The TOP value is in ICR1 and is currently 200. Counter is counting up and is currently at 100. OCR1A is currently 150(for a 75% duty cycle). Now the value 50 is written to OCR1A. If it actually ended up there right away the d/c toggle would be lost for this cycle. We would never hit the "toggle value" this time around. Double buffering of OCR1A makes the value written to OCR1A actually end up there only when the timer wraps to zero (still simplifying by not looking at up/down-modes).

Scenario 2: The TOP value is in ICR1, and is currently 200. The counter is counting up currently at 100. Now the value 50 is written to ICR1 (for quadrupling the frequency). The value ends up in ICR1 right away as that register is not double buffered, and the counter continues to count upwards, running past 200 and just going. It will eventually wrap at 65535 making this period much too long. When changing frequency (period) while the timer is running you want to have the TOP value in a double buffered register, ie OCR1A.

Better Steve?

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Koshchi wrote:
But in the mode that we are talking about, TOP is ICR1, not OCR1A. In the modes where OCR1A is TOP, then it can not be used to set the duty cycle. You must use one of the other OCR registers.

JohanEkdahl wrote:

Clearer, or did I make you more confused?

Thank you, Steve and Johan. This very helpful.

Russ

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

Hi guys!
I need some help whit my Led project.

I'm working in a Fast PWM mode project but cannot change brightness as i wan it.
If the PD2 is "1" than the OCR0A = 15, when PD3 is "1" the OCR0A = 30, but if both are "1" then should be OCR0A = 255(100%) and if both inputs are "0" than OCR0A = 0.
first two are fine, but when i wan to check both inputs the problem appears.

I use:
8bit Timer/counter0
PB2 for PWM output.
PD2 and PD3 for input.

#include 

unsigned int new_value;

int main()
{

	DDRB |= (1<<PB2);
	DDRD |=	(0<<PD2);
	DDRD |=	(0<<PD3);

while (1)          
    {

	TCCR0A |= (1<<WGM00) | (1<<WGM01) | (1<<COM0A1);

	TCCR0B |=  (1<<CS01);

	OCR0A = new_value;


 if (PIND & (1<<3))
  {
			
			new_value = 30;	
		 	   
	}
		
  
else if (PIND & (1<<2))
{
	
	new_value = 15;	
			
}


else if ( (PIND & (1<<2)) && (PIND & (1<<3)) )   // here i need solution 
{
	
	new_value = 255;
	
}

else 

{
	new_value = 0;	

}
   
	}

}

Can somebody clarify this for me... please
Thanks a lot.

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

That question has nothing to do with PWM but I think I'd go with something like:

uint8_t values[] = { 0, 15, 30, 255 };
uint8_t temp = PIND;
temp >>= 2;
temp &= 0x03;
new_value = values[temp];

this shifts bits 2/3 to the 0 position. Masks with 0x03 to isolate just those two bits and guarantee a value 0..3 and then use this to index the array of values to be set.

Cliff

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

Thank you Mr. Clawson, i had solved in other way!

Now I have problem whit keeping the same frequency(Around 200hz and brightness 50%(OCR0A=127) and 90%(OCR0A=227)) but modifying the brightness ….
The MC is Attiny2313 run at 1Mhz(internal clock).

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

From what I've been able to summarize;

-TCCR1 enables PWM, CTC, the prescaler and
resets TCNT1 upon compare match with
OCR1n

-TCNT1 records the clock count in which OCR1n is compared

-OCR1A would contain the compare value (in clock count) for OC1A and OC1A'

OC1A and OC1A' are the output pins
---------------------------------------------------
The conundrum I'm running into while writing .asm for an ATtiny25a ;

from the datasheet;

TCCR1 – Timer/Counter1 Control Register
0x30

Bit 6- PWM1A: Pulse Width Modulator A Enable
When set (one) this bit enables PWM mode based on comparator OCR1A in Timer/Counter1
and the counter value is reset to $00 in the CPU clock cycle after a compare match with OCR1C
register value.

What I am not understanding is shouldn't the value in OCR1A be resetting TCCR1. To me, its sort of vague on what register I should write the duty cycle value to.

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

Quote:
What I am not understanding is shouldn't the value in OCR1A be resetting TCCR1.

No, TCCR1 is the configuration register. The only thing that should be changing that is you.
Quote:
To me, its sort of vague on what register I should write the duty cycle value to.

In this mode, OCR1A is the duty cycle and OCR1C sets the frequency (TOP).

Regards,
Steve A.

The Board helps those that help themselves.

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

So if I wanted an output of 4mhz on OC1A with a dual Duty cycle (~35 or ~90) I would:

1) 0xF1 (1111 0001) to TCCR1 to enable
CLR Timer on compare Match to OCR1C,
PWM A enable
SET OC1A upon reaching OCR1A value
prescaler of 1

2) 0xFF (1111 1111) to OCR1C for 4mhz operation

2) 0xA5 (1011 0101 ) to OCR1A for 35% Duty cycle
-or- 0x19 (0001 1001) for 90% Duty cycle

3) enable OC1A by writing 1 to PortB.1

anything I'm missing?

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

Thanks for answer guys.

So what I understand that I should use Time/Counter1 because in this can be found both OCR1A and ICR1.
- 8 divider
- The WGM should be 14(Fast PWM).

How can I calculate the value of ICR1 for 200hz?
The value of ICR1 is in micro seconds?

And the duty cycle for 30..90%:

For ex. 30% OCR1A = 19660.5
90% OCR1A = 58981.5

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

Quote:

How can I calculate the value of ICR1 for 200hz?
The value of ICR1 is in micro seconds?

avrcalc or kavrcalc (or, manually, the formulae from the datasheet). But maybe you could say what your F_CPU is?

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

My cpu run at 1 Mhz.
In datasheet I found this: F_ocia= F_cpu/(8*(1+top)) but whit this I can calculate the frequency for a specific duty cycle value and in my case I want to change the duty cycle(during run) but the frequency should remain the same (200hz .. or something closer).

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

Well start with the 200Hz. If your CPU is ticking away at 1,000,000Hz then you need the timer to overflow every 1,000,000/200 = 5,000 ticks. The easiest way to achieve that I guess is to use a 16 bit timer with no prescaling and just have it count up to and overflow after a count of 5,000. So run timer1 in mode 14 and set ICR1 to 5,000. That sorts out the 200Hz frequency. All that remains is how to vary the duty cycle. That is done using OCR1A. vary it between 0 and 5,000 for 0%..100%. 30% would be 0.3 * 5000 = 1,500 while 85% would be 0.85 * 5000 = 4,250

BTW here is avrcalc calculating 200Hz on a 1.0MHz crystal. Note the OCR values which, when combined is 0x1388 - guess what that is in decimal ;-)

Attachment(s): 

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

thanks a lot.

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

used some code from https://www.dropbox.com/sh/7qdeb6n8rsm0li8/AADQ2XInlKhH14BJc1P4eq__a/Example%20Code/PWM/PWM/PWM.c?dl=0

Made a few changes to have it match my hardware, but nothing came out, even had trouble loading it until I changed the timout.

Than wrote this off an turtorial I followed and still nothing

#define F_CPU 1000000L


#include <avr/io.h>
#include <util/delay.h>


void initPWM()
{
	TCCR0A |= 1 << WGM00 | 1 << WGM01 | 1 << CS00 | 1 << COM0A1;
	DDRB |= 1 << PINB3;
}

void SetPWMoutput(int duty)
{
	OCR0A = duty;
}

void wait()
{
	_delay_loop_2(3200);
}
int main(void)
{
	int brighness = 0;
	
	initPWM();
	
	while(1)
	{
		for(brighness = 0; brighness < 255; brighness++)
		{
			SetPWMoutput(brighness);
			wait();
		}
		for(brighness = 255; brighness > 0; brighness--)
		{
			SetPWMoutput(brighness);
			wait();
		}	
    }
}

 

screw up in progress 

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

Which AVR? Is OC0 even on PB3 in the model you are using?

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

clawson wrote:

Which AVR? Is OC0 even on PB3 in the model you are using?

 

(PCINT11/OC0A/AIN1) PB3 says the data sheet. I'm using the ATmega 1284. 

 

 

Something happens when I set the PB3 as an output because it carry current if I connect between PB3 and +5V, but wont if I connect from GND to PB3. This behavior stops if I don't set it to an output. 

Was first thinking that polarity might have changed for PWM mode, but guess it's just how the ports operate. 

 

Edit: I have no external hardware other than resistor and LED. No crystals or anything. I also attempted to move the function to other pins, but with same result or lack of result..

 

Edit2: I should maybe have done like this. 

void initPWM()
{
	DDRB = (1 << PORTB3);
	TCCR0A |= 1 << WGM00 | 1 << WGM01 | 1 << COM0A1;
	TCCR0B |= 1 << CS00;
}

Will try it when I get home. I'm glad for any help if someone see any other screw ups or can tell me this is not it. 

 

Edit3: Changed the code and still did not appear to do much until I removed a resistor and I then had some light in my LED. Changing the prescaler to CLKio/256 showed fast blinking of LED so I got PWM.

But there is a but and it don't look like I have any change in duty cycle because there is no change in brightness. Can it be the type of LED in question? 

screw up in progress 

Last Edited: Tue. Mar 17, 2015 - 08:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

got it fixed. 

#define F_CPU 1000000L


#include <avr/io.h>
#include <util/delay.h>


void initPWM()
{
	DDRB |= 1 << PORTB3;
	TCCR0A |= 1 << WGM00 | 1 << WGM01 | 1 << COM0A1;
	TCCR0B |= 1 << CS00; #CS00 was shifted into TCCROA#
}

void SetPWMoutput(int duty)
{
	OCR0A = duty; #was OCR1A#
}

void wait()
{
	_delay_loop_2(1000);
}
int main(void)
{
	int brighness = 0;
	
	initPWM();
	
	while(1)
	{
		for(brighness = 0; brighness < 255; brighness++)
		{
			SetPWMoutput(brighness);
			wait();
		}
		for(brighness = 255; brighness > 0; brighness--)
		{
			SetPWMoutput(brighness);
			wait();
		}	
    }
}

 

screw up in progress 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	TCCR0B |= 1 << CS00; #CS00 was shifted into TCCROA#

Do you know I even pulled this datasheet and looked it up to check the value you were writing to TCCR0A and I mis-read the thing and thought the CS bits were in there myself!

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

clawson wrote:

	TCCR0B |= 1 << CS00; #CS00 was shifted into TCCROA#

Do you know I even pulled this datasheet and looked it up to check the value you were writing to TCCR0A and I mis-read the thing and thought the CS bits were in there myself!

 

Is it in TCCR0A for other AVR's? I think it must have been for the controller where I "stole" the code. Been mis-reading the datasheet for a week myself so no wonder...

 

Edit: Been playing around with the code and think for a mid 30's mechanic I'm where I would expect to be after owning a Atmega for two week's.

Think I understand how most of the code work, but I cant quite get my head around the _delay_loop_2 vs _delay_ms, but _delay_ms don't work so there is something. 

Also thinking how I can get this to work with digital potentiometers. Somehow writing to TCNT2 and read the value out as the pwm value for TCNT0? Am I far off? 

screw up in progress 

Last Edited: Wed. Mar 18, 2015 - 06:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Strugling with this code still.

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>
void initPWM()
{
DDRB |= 1 << PORTB3 | 1 << PORTB4;
TCCR0A |= 1 << WGM00 | 1 << WGM01 | 1 << COM0A1 | 1 << COM0B1;
TCCR0B |= 1 << CS00;
}
void SetPWMoutput1(int duty1)
{
OCR0A = duty1;
}
void SetPWMoutput2( int duty2)
{
OCR0B = duty2;
}
void wait1()
{
_delay_loop_2(2000);
}
void wait2()
{
_delay_loop_2(300);	
}
int main(void)
{
int brighness = 0;
initPWM();
while(1)
{
for(brighness = 0; brighness < 255; brighness++)
{
SetPWMoutput1(brighness);
wait1();
}
for(brighness = 255; brighness > 0; brighness--)
{
SetPWMoutput1(brighness);
wait1();
}	
for(brighness = 0; brighness < 255; brighness++)
{
SetPWMoutput2(brighness);
wait2();
}
for(brighness = 255; brighness > 0; brighness--)
{
SetPWMoutput2(brighness);
wait2();
}
}
}

It's doing what it's suposed to do, but not what I want it to do. I was trying to get the LED's to dim independenten from each other. Is there a way to get the two PWM outputs to run at the same time? Is there a timer that will do the same function as the delay_loop?

Can I count the PWM sycle and use that as an "interrupt" where I can increase the duty by a certain value for every 100PWM cycles? and would it be a better way to write code than using the delay_loop?

It's not my intention with the code, but wanting to know.

screw up in progress 

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

Tweaked the code to look like this. 

#define F_CPU 1000000L

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

void initPWM0()
{
	DDRB |= 1 << PORTB3 | 1 << PORTB4;
	TCCR0A |= 1 << WGM00 | 1 << WGM01 | 1 << COM0A1 | 1 << COM0B1;
	TCCR0B |= 1 << CS01;
}

void SetPWMoutput1(int duty1)
{
	OCR0A = duty1;
}

void SetPWMoutput2(int duty2)
{
	OCR0B = duty2;
}


int main(void)
{
	initPWM0();
	
	while(1)
	{
		_delay_ms(100);
	    SetPWMoutput1() += 10;
		
		if (SetPWMoutput1() > 255)
		{
			SetPWMoutput1()= 0;
		}
	}
}

Get errors when I compile that: too few arguments to function "SetPWMoutput1"

No idea if it's right of me to post it here, but please bear with me. 

screw up in progress 

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

No idea if it's right of me to post it here

It isn't - tutorial threads are about  teaching beginners some new technique - not about diagnosing specific implementations. We really should discuss any issues in a separate thread in a more appropriate forum.

Get errors when I compile that: too few arguments to function "SetPWMoutput1"

Is it not obvious to you why that is then? You define the function as:

void SetPWMoutput1(int duty1)
{
	OCR0A = duty1;
}

Which says that when you call it you will pass a "duty1" value to it as an "int". But then you call it with:

	    SetPWMoutput1() += 10;

in which you are not passing it an int. In fact this line is wrong for many reasons. To use +=10 it implies that the function would be returning some kind of numeric value yet you defined the function to return "void". I supposed what you really want in main() is something like:

int main(void)
{
        int myOCR = 0;
	initPWM0();
	
	while(1)
	{
		_delay_ms(100);
  	        SetPWMoutput1(myOCR);
                myOCR += 10;
		if (myOCR > 255)
		{
			myOCR = 0;
		}
	}
}

Last Edited: Mon. Mar 23, 2015 - 11:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is software or hardware pwm?

mahesh

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

H/W as it uses the Timer/counter to generate the pwm output

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

Code in post #28 is hardware PWM.

 

There is a thread in the tutorial form about timer interrupts. In it I made a post showing how to do "soft PWM" though it is still using a hardware timer to achieve it. However it is "soft" in the sense that the output switching is using bit-bang and could be any IO pin (not a dedicated timer hardware PWM pin).