Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   This topic is locked: you cannot edit posts or make replies.
View previous topic Printable version Log in to check your private messages View next topic
Author Message
apc3161
PostPosted: Jul 17, 2009 - 01:10 AM
Rookie


Joined: Jun 14, 2009
Posts: 20


Ahh no! The tutorial is not finished! That is such a shame.

The timer tutorial was absloutely incredible and I was hoping I be able to learn PWM in the same manner. sigh...
 
 View user's profile Send private message  
Reply with quote Back to top
Latrocinium
PostPosted: Sep 06, 2009 - 12:23 AM
Newbie


Joined: Feb 23, 2009
Posts: 3


Hey guys,
N00b here. First thanks for the help Dean. Loved the tuts, but ya left me feeling like I was dating in middle school again. jk. (Only on the PWM, though)

However, I did find a good example. That I was able to understand. Needed a lil reformatting for all of us to understand.

So here is the pwm code. I can't take credit for it's generation. Check the Beer-ware license for that info. I did however edit it into one file, and note the freakin hell out of it.

This works perfectly on my tiny2313. If you get the original from the website, and get the iocompat.h it should work for any of the differences for your MCU. I find that making those changes helps me understand it better though.

Code:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

enum { UP, DOWN };


int main (void)
{

    /* Timer 1 is 10-bit PWM (8-bit PWM on some ATtinys).
      Setting WGM10 & WGM11 only will give phase correct PWM
      Setting WGM12 also will give fast PWM, but that's on TCCR1B
      COM1A1 = Clear OC1A on match when upcounting-Set OC1A
               when downcounting on match
   */
    TCCR1A |= ((1<<WGM10)|(1<<WGM11)|(1<<COM1A1));
   /* Clock Selects are for prescallers
      Current CS10 is for clock without prescalling
      Prescaler    8 = (1<<CS11)
      Prescaler   64 = ((1<<CS11)|(1<<CS10))
      Prescaler  256 = (1<<CS12)
      Prescaler 1024 = ((1<<CS12)|(1<<CS10))
   */
    TCCR1B |= ((1<<WGM12)|(1<<CS10));

    // Set Timer1/PWM register value to 0.
    OCR1A = 0;

    /* Enable OC1A or PB3 as output.
      Not sure why but only works with this pin
   */
    DDRB = (1<<3);

    // Enable timer1 overflow interrupt.
    TIMSK = (1<<TOIE1);

    sei ();

    // loop forever, the interrupts are doing the rest
    for (;;)                   
        sleep_mode();

    return (0);
}

ISR (TIMER1_OVF_vect)
{
    static uint16_t pwm;      //pwm counter var
    static uint8_t direction;   //enum var

   /*
   For those who are wondering by ++pwm, it's actually
      incrementing or decrementing everytime through the switch
      However, it does that before comparing to min or max
   */
    switch (direction)         
    {
        case UP:   //increment counter and check against Max-1
            if (++pwm == 1023)
            //start the counting other direction
                direction = DOWN;
            break;

        case DOWN:   //decrement counter and check against min
            if (--pwm == 0)
            //start counting in other direction
                direction = UP;
            break;
    }
   //set the calc'd value to Timer1 output register
    OCR1A = pwm;
}



/*
 * Obtained from http://www.nongnu.org/avr-libc/user-manual/group__demo__project.html
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 43):
 * <joerg@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
 * Hey I wanna beer too ;) Actually jack & coke or a smoke, but same deal: Bart Robinson
 * ----------------------------------------------------------------------------
 *
 * Simple AVR demonstration.  Controls a LED that can be directly
 * connected from OC1/OC1A to GND.  The brightness of the LED is
 * controlled with the PWM.  After each period of the PWM, the PWM
 * value is either incremented or decremented, that's all.
 *
 * $Id: group__demo__project.html,v 1.1.1.18 2009/03/05 20:35:12 joerg_wunsch Exp $
 */


Enjoy :-p Twisted Evil
 
 View user's profile Send private message  
Reply with quote Back to top
JohanEkdahl
PostPosted: Sep 06, 2009 - 12:31 AM
10k+ Postman


Joined: Mar 27, 2002
Posts: 22262
Location: Lund, Sweden

Quote:

Code:
    /* Enable OC1A or PB3 as output.
      Not sure why but only works with this pin
   */
    DDRB = (1<<3);



There is no provision to select which pins outputs the PWM channels of the timer. It is hard-wired in the chip. Thus Timer/Counter1's channel A always has it's output on PB3.

On some other microcontrollers you can "route" peripheral outputs to different pins, but not so on AVRs.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Latrocinium
PostPosted: Sep 06, 2009 - 01:01 AM
Newbie


Joined: Feb 23, 2009
Posts: 3


Johan,
I really appreciate that answer. I've been banging my head against the monitor for a couple(a lot more, but I'll pretend) hours.
Now to figure out another way to do a multi-channel PWM.
 
 View user's profile Send private message  
Reply with quote Back to top
JohanEkdahl
PostPosted: Sep 06, 2009 - 02:21 AM
10k+ Postman


Joined: Mar 27, 2002
Posts: 22262
Location: Lund, Sweden

Quote:

a multi-channel PWM

How many channels?

Most AVRs have several Timer/Counters each being capable of driving two PWM channels. The tiny2313 has the eight bit Timer/Counter0 with two PWM channels, and the sixteen bit Timer/Counter1 also with two PWM channels, making for a total of four PWM channels. While the frequency must be the same for the two channels within each T/C the duty cycle is independently set for each channel.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
clawson
PostPosted: Sep 06, 2009 - 10:51 AM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71817
Location: (using avr-gcc in) Finchingfield, Essex, England

I hadn't thought about it before but I keep posting this code segment to other threads and maybe this is the appropriate place for it. What it shows is how to do PWM on an arbitrary pin by doing "soft PWM" but with timer interrupts. It works by starting a timer to have both overflow (PWM frequency) and compare (duty cycle) interrupts. In this case I wanted to vary a pulse width on PC0 using the fastest PWM frequency I could get (so timer 0 counting 256 with no prescaler) and OCR0 can be varied from 0x00 to 0xFF to vary the duty cycle:
Code:
// The following two ISRs are doing "poor man's PWM"
//  but this allows it to be on a pin of my choice
ISR(TIMER0_COMP_vect) {
        // clear the output pin on OCR0 match
   PORTC &= ~(1<<PC0);
}
ISR(TIMER0_OVF_vect) {
        // set the output pin at timer overflow
   PORTC |= (1<<PC0);
}

int main(void) {

   // going to use PORTC.0 to PWM the contrast voltage
   DDRC = (1<<DDC0);
   TIMSK |= ((1<<OCIE0) | (1<<TOIE0)); // use both interrupts
   OCR0 = 10; // 10 out of 256 means very short on period (low voltage)
   TCCR0 = (1<<CS00); // timer on - nice high PWM frequency

   // Might later consider PWMing the backlight voltage too
   // so it would also be adjustable ...
   sei();

this is just a code "snippet" for GCC, not a complete program.

The application was actually to vary the pin 3 (contrast) voltage of an HD44780 LCD module. As the comments say it would be possible to vary the backlight voltage in a similar way.

The way this example works is that it runs an 8bit timer with no prescale so every 256 clock cycles it will overflow and cause an OVF interrupt. At this point the output is turned on. The counter then starts to count up 0, 1, 2, 3,... and I have set the OCR0 register to 10 so when it counts up to 10 and TCNT0==OCR0 it will trigger the COMP(are) interrupt. At this point I switch the output off. So in a complete period of 256 counts the output is on for 10 and off for 246. So the output is only "on" for about 4% of the time. As the output pin is switching between 0V and 5V then it'll be like a voltage that is just 4% of this will be produced by the output pin (after a bit of RC filtering). So it's 4% * 5V = 0.19V

In my example I don't vary OCR so the output voltage is always at this level. But say I now set OCR to 211 (out of 256) then the output signal will be on for 211 clocks and off for 256-211=45 clocks. Because of this the output will appear to be 211/256 * 5V = 4.12V (and so on).


Cliff

_________________


Last edited by clawson on Jun 15, 2011 - 09:30 AM; edited 1 time in total
 
 View user's profile Send private message  
Reply with quote Back to top
Latrocinium
PostPosted: Sep 06, 2009 - 04:11 PM
Newbie


Joined: Feb 23, 2009
Posts: 3


Actually 3 timers proved to be enough, but I wound up doing something close to what clawson just posted.
lol, wish I had seen that lastnight.
 
 View user's profile Send private message  
Reply with quote Back to top
npat_avr
PostPosted: Sep 20, 2009 - 03:54 PM
Posting Freak


Joined: Feb 02, 2009
Posts: 1004


Ok I tried the code from Joerg above and it worked. The LED is dimming perfectly (Thanks, Joerg!!).

Now I am trying to understand what the waveform of the LED current would look like? I understand the average DC value is changing for every cycle but can anyone help me with a waveform picture? I have 8MHz as my clock with no prescalar. The rest of the code is exactly identical to Joerg's code above. Thanks.
 
 View user's profile Send private message  
Reply with quote Back to top
peterminj
PostPosted: Oct 04, 2009 - 07:17 AM
Wannabe


Joined: Sep 05, 2009
Posts: 51


Dean,thanks for the wonderful tutorial.Can you give some example codes for the PWM section like in previous cases and complete this section whenever possible.

Regards,
Peter
 
 View user's profile Send private message  
Reply with quote Back to top
sagunms
PostPosted: Oct 08, 2009 - 06:59 PM
Newbie


Joined: Oct 08, 2009
Posts: 3


Thank you so much Dean for all that effort of explaining the intro of PWM to newbie like me. It was very easy to visualize how PWM works after reading this article. So, when's the remaining tutorial coming?
 
 View user's profile Send private message  
Reply with quote Back to top
abcminiuser
PostPosted: Oct 08, 2009 - 10:27 PM
Moderator


Joined: Jan 23, 2004
Posts: 10228
Location: Melbourne, Australia

I've been saying this for a very long time now, but "as soon as I get the chance". I've got a heck of a lot of University on at the moment, but that'll be ending for the year in a little more than a month.

IIRC, another user wrote his own PWM tutorial here, which was complete - that might be helpful in the interim.

- Dean Twisted Evil

_________________
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
tech20
PostPosted: Oct 19, 2009 - 06:28 AM
Newbie


Joined: Sep 16, 2009
Posts: 4


can't wait until this tutorial is finished, i have look through many tutorials explaining PWM and Timers on the AVRs and they are all incomprehensible. This will make life a bit easier for all new AVR users Smile
 
 View user's profile Send private message  
Reply with quote Back to top
chanseng738
PostPosted: Nov 12, 2009 - 02:50 AM
Hangaround


Joined: Feb 24, 2009
Posts: 398


Hi Dean,

It is very long long time since you posted this "incomplete" tutorial on PWM. When can you finish this tutorial, really like your tutorial and the way you present your idea. You are a superb lecturer! Pls finish the tutorial, there are thousands of ppl waiting for your PWM tutorial!

_________________
cs


I'm happy ytd, today, and tmr Smile
 
 View user's profile Send private message  
Reply with quote Back to top
abcminiuser
PostPosted: Nov 12, 2009 - 02:55 AM
Moderator


Joined: Jan 23, 2004
Posts: 10228
Location: Melbourne, Australia

Hi,

I do intend on finishing it soon (after exams, in two weeks), but it's been very low on my priority list as I think it would be largely redundant now. There are two other complete PWM tutorials posted here now:

http://aquaticus.info/pwm

And:

http://www.avrfreaks.net/index.php?name ... mp;t=76406


Which means any effort I make will just add to the noise and cover already covered ground.

- Dean Twisted Evil

_________________
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
hmimo87
PostPosted: Nov 27, 2009 - 03:29 AM
Newbie


Joined: Sep 12, 2009
Posts: 2


Good tutorial, any ideas?
I have a potentiometer to adjust with the ADC the value of the frequency of the pwm but I want to put another pot to adjust the duty cycle, using the 16 bits with the OCR and ICR.
I'm using the assembly code, any ideas on how to do it?
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Nov 27, 2009 - 09:32 AM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71817
Location: (using avr-gcc in) Finchingfield, Essex, England

Use one of the WGM modes where ICR1 is "TOP". That is used to vary the PWM frequncy then OCR1 is used to vary the duty cycle.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
hmimo87
PostPosted: Nov 27, 2009 - 05:07 PM
Newbie


Joined: Sep 12, 2009
Posts: 2


I'm using that, but the duty cycle is like a porcentage. I have 10 bits of the ADC for the duty cycle. 1024 would be 100% duty cycle but depending of the frequency of the another pot that are adjusting it that is also 10 bits. I know I have to do some conversions, I can add and multiply, I have the code in Assembly. Any Ideas to adjust the duty cycle. Thank you
 
 View user's profile Send private message  
Reply with quote Back to top
Koshchi
PostPosted: Nov 27, 2009 - 06:07 PM
10k+ Postman


Joined: Nov 17, 2004
Posts: 15118
Location: Vancouver, BC

Code:
duty = top * value / 1024;

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
apc3161
PostPosted: Dec 02, 2009 - 10:22 PM
Rookie


Joined: Jun 14, 2009
Posts: 20


Dean, by chance if you read this. Could you help me out?

I'm trying to get 16 bit PWM modulation to work. Here is my code, it doesn't seem to be working

Code:



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

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

OCR1A = 60000;
OCR1B = 60000;




But it doesn't seem to be working. I also tried

Code:


OCR1AH = 60000 >> 8;
OCR1AL = 60000;



I'm just trying to get an output on ports OCR1A and OCR1B. For the 8 bit pwm channel, this always worked fine and was easy. Just had to set OCR2 = X and that would be it. But it doesn't seem to work as easily for 16 PWM.

Any ideas?
 
 View user's profile Send private message  
Reply with quote Back to top
Koshchi
PostPosted: Dec 03, 2009 - 12:25 AM
10k+ Postman


Joined: Nov 17, 2004
Posts: 15118
Location: Vancouver, BC

Please do not cross post. This is already being discussed here.

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   This topic is locked: you cannot edit posts or make replies.
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits