3

I am trying to blink an LED with PWM on Arduino.

I don't know what is wrong but my LED is not fading as expected. What is wrong?

I think that I have bad registers settings, but I am not sure.

LED is connected to Arduino pin 9.

Here is my code:

#include <avr/io.h>
#include <util/delay.h>
const int delay=1000; 
void initialize_PWM()
{
    TCCR0A|=(1<<WGM00)|(1<<WGM01)|(1<<COM0A1);
    TCCR0B=1;
    DDRB|=(1<<PB1); 
}

void set_pwm(uint8_t data)
{
    OCR0A=data;
}

int main (void)
{
    initialize_PWM();
    uint8_t brightness=200;
    while(1)
    {
        for(brightness=0;brightness<255;brightness++)
        {
            set_pwm(brightness);
            _delay_ms(1);
        }

        for(brightness=255;brightness>0;brightness--)
        {
            set_pwm(brightness);
            _delay_ms(1);
        }
    }
    return 0;
}
  • 1
    Which Arduino? You appear to be using port B1 which maps to D9 on ATMega328p, so Uno, perhaps? What do you see - A bright LED? A dark one? Is your LED wired right way round? Have you tried using that pin as digital I/O using library functions to confirm that the LED can work as wired? What else have you tried? – JRobert Jun 15 '14 at 12:56
  • Arduino Uno with ATMega328p. I see dark LED. Wires are in right way. And LED is working when I plug it to +5V and resistor. – wair92 Jun 15 '14 at 16:16
  • can't you use analogWrite( pwm )? – nandu Jun 15 '14 at 19:12
  • No, because I want use it without external libraries. – wair92 Jun 15 '14 at 19:50
  • is this using the arduino ide? – Madivad Jun 16 '14 at 3:49
5

From your code you are using Timer0 in FastPWM mode with top = 0xFF.

In this mode, as used in your code, OCR0A defines the pulse width rate (more precisely, OCR0A / 256).

Timer0 uses port OC0A and OC0B to ouput PWM signals, in your code, you are using only OC0A (which is perfectly fine).

Now if you take a look at ATmega 328 data sheet, chapter 14.3.3 you will see that pin OC0A maps to port D, pin PD6 (on Arduino UNO, this is port D6, according to http://arduino.cc/en/Hacking/PinMapping168).

So this means that you have used the wrong port in your setup (Arduino pin D9 maps to OC1A, which is pin PB1 of port B on ATmega328, OC1A is used by Timer1, not Timer0).

Hence you should do the following to fix the problem:

  1. Plug the LED to Arduino pin D6 instead of D9
  2. Fix initialize_PWM() by replacing DDRB|=(1<<PB1); with DDRD|=(1<<PD6);

Otherwise, if you want to keep Arduino pin D9, then you will have to change the timer you use: Timer1 instead of Timer0. Updating the code is left as an exercise!

1

It's much too soon to optimize. Get your LED working using the standard (known working) libraries. Only then - and perhaps never, unless you find a need to - should your optimize (use direct register I/O). If it works with the libraries but not with your direct I/O, you'll know the bug lies in the direct I/O code and nowhere else, ruling out failed hardware, improper or unreliable connections, and programming errors elsewhere in your project. Right now, you don't know even that much.

1

The easiest way is to use the timer1 library written for arduino. You can download it here.

To install, simply unzip and put the files in Arduino/hardware/libraries/Timer1/


Prescaler set for 16Mhz

Prescale    Time per counter tick   Max Period
1   0.0625 uS   8.192 mS
8   0.5 uS  65.536 mS
64  4 uS    524.288 mS
256 16 uS   2097.152 mS
1024    64uS    8388.608mS

In general:

Max Period = (Prescale)(1/Frequency)(2^17) Time per Tick = (Prescale)*(1/Frequency)


Instructions available using this library are:

initialize(period) You must call this method first to use any of the other methods. You can optionally specify the timer's period here (in microseconds), by default it is set at 1 second. Note that this breaks analogWrite() for digital pins 9 and 10 on Arduino.

setPeriod(period) Sets the period in microseconds. The minimum period or highest frequency this library supports is 1 microsecond or 1 MHz. The maximum period is 8388480 microseconds or about 8.3 seconds. Note that setting the period will change the attached interrupt and both pwm outputs' frequencies and duty cycles simultaneously.

pwm(pin, duty, period) Generates a PWM waveform on the specified pin. Output pins for Timer1 are PORTB pins 1 and 2, so you have to choose between these two, anything else is ignored. On Arduino, these are digital pins 9 and 10, so those aliases also work. Output pins for Timer3 are from PORTE and correspond to 2,3 & 5 on the Arduino Mega. The duty cycle is specified as a 10 bit value, so anything between 0 and 1023. Note that you can optionally set the period with this function if you include a value in microseconds as the last parameter when you call it.

attachInterrupt(function, period) Calls a function at the specified interval in microseconds. Be careful about trying to execute too complicated of an interrupt at too high of a frequency, or the CPU may never enter the main loop and your program will 'lock up'. Note that you can optionally set the period with this function if you include a value in microseconds as the last parameter when you call it.

setPwmDuty(pin, duty) A fast shortcut for setting the pwm duty for a given pin if you have already set it up by calling pwm() earlier. This avoids the overhead of enabling pwm mode for the pin, setting the data direction register, checking for optional period adjustments etc. that are mandatory when you call pwm().

detachInterrupt() Disables the attached interrupt.

disablePwm(pin) Turns PWM off for the specified pin so you can use that pin for something else.

read() Reads the time since last rollover in microseconds.


Example - Sets up PWM output on pin 9 with a 50% duty cycle, and attaches an interrupt that toggles digital pin 10 every half second.

/*
 *  Timer1 library example
 *  June 2008 | jesse dot tane at gmail dot com
 */

#include "TimerOne.h"

void setup()
{
  pinMode(10, OUTPUT);
  Timer1.initialize(500000);         // initialize timer1, and set a 1/2 second period
  Timer1.pwm(9, 512);                // setup pwm on pin 9, 50% duty cycle
  Timer1.attachInterrupt(callback);  // attaches callback() as a timer overflow interrupt
}

void callback()
{
  digitalWrite(10, digitalRead(10) ^ 1);
}

void loop()
{
  // your program here...
}

Source: http://playground.arduino.cc/Code/Timer1

  • The OP mentioned in a comment that he does not want to use external libs (and that seemed to include arduino libs also) – jfpoilpret Jun 17 '14 at 18:09
  • Oh I've missed that probably. Sorry. Though, I'll keep this answer standing in case someone else who's searching for an answer like this. – Handoko Jun 17 '14 at 20:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.