-1

I'm relatively new to programming microcontrollers, so I started experimenting on an ATtiny85.

First program was simply turning on an LED and off with a little delay (classic blinky program).

Now I wanted to slowly increase the brightness of the LED. Here is the code:

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>

#define LED PB0

void setup(void);
void loop(void);
void analogOutput(int8_t brightness);

int main(void)
{
    setup();
    while (1)
    {
        loop();
    }

    return 0;
}

void setup()
{
    TCCR0A = (1 << WGM00) | (1 << WGM01);
    TCCR0B = (1 << CS00) | (1 << CS01);
    DDRB = (1 << LED);
    PORTB = 0;
}

void loop()
{
    int8_t brightness = 0;
    analogOutput(brightness);
    _delay_ms(500);
    for (brightness = 0; brightness < 256; ++brightness)
    {
        analogOutput(brightness);
        _delay_ms(10);
    }
}

void analogOutput(int8_t brightness)
{
    if (brightness == 0)
    {
        // digital output LOW
        TCCR0A &= ~(1 << COM0A1);
        PORTB &= ~(1 << LED);
    }
    else if (brightness == 255)
    {
        // digital output HIGH
        TCCR0A &= ~(1 << COM0A1);
        PORTB |= (1 << LED);
    }
    else
    {
        PORTB &= ~(1 << LED);
        // analog output
        TCCR0A |= (1 << COM0A1);
        OCR0A = brightness;
    }
}

I expected the LED to be turned off for half a second and then increase the brightness. But if i run the code the LED simply becomes more bright, as if the _delay_ms(500) was ignored.

The analogOutput function is inspired by the function of the Arduino library.

What is wrong here? Why does the delay not work as expected?

EDIT

I changed my loop function, new content:

void loop()
{
    analogOutput(127);
    _delay_ms(500);
    analogOutput(255);
    _delay_ms(500);
}

This works, I now have a LED that constantly switches from dimmed to fully on and back.

Stefan
  • 652
  • 5
  • 10
  • 2
    Firstly, int8_t can be -127 to 127 and not 0 to 255. Morever, for an uint8_t can take a value of 0 to 255 not 256. – Sirac May 01 '20 at 18:03
  • oh, I missed the uint.. ok, I guess that's the problem, I feel stupid now The 256 won't be reached in the for loop though – Stefan May 01 '20 at 18:12
  • I think the mistake is because you don't set the pwm each time (for 0 and 255), try to set OCR0A = brightness; each time an say me the result – Sirac May 01 '20 at 18:15
  • @Sirac the increasing brightness works, I set OCR0A in the `analogOutput` function, it's the delay that is "skipped", or maybe way too fast. I will try what @bigjosh wrote in his answer – Stefan May 01 '20 at 18:17
  • I explain better: after the first loop, the pwm is set to 255, so the duty cycle is maximal. After, you set to 0, so portB0 is off but the pwm set LED few µs after even you wait 500ms. So stop the pwm when you set the brightness to 0 – Sirac May 01 '20 at 18:23
  • @Sirac but isn't this exactly what I did with the `if (brightness == 0)` part? By clearing the `COM0A1` bit the pwm should be turned off, doesn't it? – Stefan May 01 '20 at 18:27
  • Oh, I see, you mean I should set `OCR0A` to 0 and 255 as well? – Stefan May 01 '20 at 18:28
  • no sorry this is mistake because of the reset of COM0A1 which prevent the pwm to set or reset the output – Sirac May 01 '20 at 18:59
  • @Sirac you pointed me to the right direction with your first comment .. I simply ran into overflows and didn't think about it – Stefan May 01 '20 at 20:07

2 Answers2

1

__delay_ms() is converted to cycle-wasting instructions by the compiler, so as long as your F_CPU matches the current speed of the processor, then it should work as expected.

I would look for other places in your code that are not working as you expect them to.

A half second delay at power up is not a great diagnostic test since it happens quickly, only once, and there may be other things going on then as well.

Maybe instead try blinking between two different brightness levels with a 500ms delay between. Visually this should be unambiguous.

if that works as expected, then maybe try doing a brightness ramp and changing the delay inside the loop to see if that works as expected.

Eventually you will work your way up to something that is not as expected, and then you will know where the problem is. It is still possible you will not understand what is going on, but at least you will be able to ask a much more specific question here and be more likely to get a helpful answer.

Report back with your results!

bigjosh
  • 1,273
  • 13
  • 19
  • I will try this out. But the 500ms delay is actually in the endless loop, so I should see it over and over again – Stefan May 01 '20 at 18:15
  • See my edit, I tried blinking the LED dimmed, this worked. Now I don't know why the other code didn't work. Maybe it has to do with the edge cases (0 and 255)? – Stefan May 01 '20 at 18:24
  • Ok, I tested setting the brightness manually from 0 - 127 - 255 - 127 in the loop function which runs as expected. I simply added the for loop after this. In the first execution of `loop()` everything is normal, but then it jumps directly to the for loop and ignores everything before. Can you have a loop inside a loop in avr? It seems like it just repeats the for loop over and over again – Stefan May 01 '20 at 19:30
1

I feel stupid.. It was just an overflow error, since I used uint8_t and set < 256 in the for loop, the condition was always true (255 + 1 = 0).

My solution was to use uint16_t for the loop

Stefan
  • 652
  • 5
  • 10