PWM led without timer

wdariusw

Nov 10, 2014
149
Joined
Nov 10, 2014
Messages
149
Hi. How can i do smooth pwm using delay loops ? I tied like this:

int period;
int i=0;

while
{
i++;
Turn_LED_off();
_delay(i);
Turn_LED_on();
_delay(period-i);

if (i>=period)
{
i=0;

}
}
It works bad....
 

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
Hi. How can i do smooth pwm using delay loops ? I tied like this:

It works bad....

Code:
int period = 50;
int i = 0;

while(true)
{
    i++;
    PORTB = PORTB & B11111110;  // Turn LED off
    _delay(i);
    PORTB = PORTB | B00000001;  // Turn LED on
    _delay(period - i);

    if (i >= period)
    {
        i = 0;
    }
}

You don't show us enough of your code, but I'd say there's too much overhead with the "Turn_LED_on()" and "Turn_LED_off()" functions, among other things. What is executed within these functions? Direct pin access would be better than calling functions, too.

For instance, to turn the LED on if it's attached to pin 0 of PORTB without affecting other pins:-
PORTB = PORTB | B00000001

And to turn it off without affecting other pins:-
PORTB = PORTB & B11111110
(See my modified version of your code above.)
(This exact code may not work with your compiler and micro, it's just an example. I've chosen PORTB, pin 0. Binary may need to be entered in a different manner with your compiler too. Something similar should work.)

I notice that you don't actually initialise "period" in this snippet, either.
If you initialise "period" elsewhere, your code should work but the overhead I mentioned would be your problem.
(I'm assuming that the aim was to have wider pulses at the beginning, getting narrower with each "while" loop iteration and repeating this forever while keeping a constant frequency.)
This loop would run forever, if it does compile, since there's no condition to the "while" statement.

In C, it's usually necessary to use a condition in the 'while' statement, even if it's fixed as 'true':-
Code:
while(true)
{
      // Do things here
}

And keep in mind that even "delay()" will have overhead code.

Does your code compile without errors?
Which micro-processor and compiler are you using?

Finally, you can use the "insert" button at the top of the Edit window, and select "Code", to enter code in a post, as I've done.

Edit: I did lots of edits to make things more understandable, but i'm done now. :D
 
Last edited:

BobK

Jan 5, 2010
7,682
Joined
Jan 5, 2010
Messages
7,682
What are you trying to achieve with this code?

What the code will do is cause a PWM with a duty cycle that increases from 0 to 100% then repeats.

If you just want a PWM with a fixed duty cycle, you should have 2 variables, period and duty_cycle, then

Code:
while(true)
{
    turn_on();
    delay(duty_cycle);
    turn_off();
    delay(period-duty_cycle);
}

Bob
 

wdariusw

Nov 10, 2014
149
Joined
Nov 10, 2014
Messages
149
This is what i want :
1. PA0 turn On LED;
2. PA0 LED light slowly goes out.
3. PA1 turn On LED;
4. PA1 LED light slowly goes out.
5. PA2 turn On LED;
6. PA2 LED light slowly goes out.
.......................................................until PA7 is reached..
 

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
What are you trying to achieve with this code?
What the code will do is cause a PWM with a duty cycle that increases from 0 to 100% then repeats.
Other way 'round - decreasing pulses. I figured that was what he was after.

wdariusw said:
This is what i want :
1. PA0 turn On LED;
2. PA0 LED light slowly goes out.
3. PA1 turn On LED;
4. PA1 LED light slowly goes out.
5. PA2 turn On LED;
6. PA2 LED light slowly goes out.
.......................................................until PA7 is reached..
@wdariusw
1. First up, you didn't say what the actual problems is. You merely said "It works bad....".
That's not very descriptive. What is the actual problem?

2. Does your program have to do anything else? (Because it won't while a perpetual loop is running.)

3. Are your delays in milliseconds or microseconds?
ie What frequency do you want the PWM to run at? 50Hz or higher is a good idea, so that the individual flashes are not visible.

4. Over what period do you want the LEDs to go from full-on to off?
You need to hold each step of the decreasing PWM for a short period, or the result will be too fast to see.
ie.
PWM=100%
Delay 10mS
PWM=99%
Delay 10mS
PWM=98%
Delay 10mS
etc.

5. What actual instruction makes the pin either high or low with your chip/compiler?
It's hard to write a code example that will compile for you if we don't know how to address the pins.

6. Finally, what compiler and micro-controller are you using?

If you answer the above questions, you'll have a far better chance of getting help.

Also, why not use a timer-based interrupt version? Then your program can be doing other things while this is going on.
 
Last edited:

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
You could wrap it in a function, to be called with each pin number, like this:-

Code:
for(int pin=0;pin<3;pin++)
    FadeLED(pin);

void FadeLED(int pin)
{
    int period=10;                                  // 10mS cycles (100Hz).
    int mark=period+1;                                // Pulse length, starts at 100% duty-cycle.
    int space;
    while(mark)
    {
        mark--;
        space=period-mark;
        for(int holdTime=0;holdTime<10;holdTime++)  // Provide a hold time at each duty cycle.
        {                                           // 100mS with current values.
            setPin(pin,HIGH);              // *Substitute call to suit your compiler
            _delay(mark);
            setPin(pin,LOW);              // *Substitute call to suit your compiler
            _delay(space);
        }
    }
    return;
}
 
Last edited:

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
If you want to improve smoothness, so the eye can't see the steps so easily, and you can get microsecond timing, you could try this.
(I wrote and tested it in a PIC chip with the PICBasic Pro compiler, then converted it to Arduino, so you'll need to convert it to suit your compiler. It shows the basic idea though, and works well in a 20MHz PIC. Each LED takes about 2 seconds to go from 100% duty-cycle to 0% duty-cycle):-
Code:
// PWMwithoutTimer2

void setup()
{
    pinMode(0,OUTPUT);
    pinMode(1,OUTPUT);
    pinMode(2,OUTPUT);
}

void loop()
{
    for(int pin=0;pin<3;pin++)    // Cycle through the 3 LEDs forever.
        FadeLED(pin);
}

void FadeLED(int pin)
{
    int period=10000;
    int mark=period+50;
    int space;
    while(mark)
    {
        mark-=50;    
        space=period-mark;
        digitalWrite(pin,HIGH);
        delayMicroseconds(mark);
        digitalWrite(pin,LOW);
        delayMicroseconds(space);
    }
    return;
}

Also, using an RC filter helps - a cap across the LED.
 
Last edited:

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
And since I took the trouble to write and test it, so it's not completely wasted, here's the PICBasic Pro version:-

Code:
TRISB=%11111000    ' Output to LED1 on RB0,
                    ' output to LED2 on RB1,
                    ' output to LED3 on RB2,
                    ' others unused, (inputs).
Init:
    HIGH 0    ' LED1 flash on startup
    pause 500
    LOW 0

Main:
    for pin=0 to 2    ' Cycle through the 3 LEDs forever.
        gosub FadeLED
    next
    goto Main
'---------------------------------------------------------------
' Subroutines:-
FadeLED:
    period=10000     ' 10mS cycles (100Hz).
    mark=period+50
    while (mark>0)
        mark=mark-50
        space=period-mark
        HIGH pin
        pauseus mark
        LOW pin
        pauseus space
    wend
    return
 
Last edited:

wdariusw

Nov 10, 2014
149
Joined
Nov 10, 2014
Messages
149
I'm working with atmega32 , and AVR studio with avr-gcc compiler. Will try all of yours offered codes today after work and will report results here, thanks !
 

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
I'm working with atmega32 , and AVR studio with avr-gcc compiler. Will try all of yours offered codes today after work and will report results here, thanks !
I have the avr-gcc compiler here too, in the Win-AVR package, but haven't even got as far as working out how to install it all properly yet. I struck makefile problems. Been too busy with other stuff.
Let us know how you go. The microseconds method is best, for a visibly smooth fade.

I just downloaded AVR Studio 5 to try out. (I'm running XP, so decided it's probably best not to download the latest, V6.2)
 
Last edited:

wdariusw

Nov 10, 2014
149
Joined
Nov 10, 2014
Messages
149
No, do not work... led blinks and at the moment turns off... I need to get first effect of this video
. Everywhere are written about nice atmega software pwm, but no luck with working code..
 

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
No, it seems that pins can't be accessed in AVR Studio as easily as they can in PICBasic Pro and Arduino.
I had a PIC doing exactly what you want many hours ago, running the PICBasic Pro code I posted.
This is what I've come up with so far in AVR Studio. You need to include "delay.h".
You'll also need to alter it to whatever port you're using.
:-
Code:
     DDRB=0b00000111;                    // Make PB0 TO 3 outputs, all others inputs.
    for(int pin=0;pin<3;pin++)
    {
        FadeLED(pin);
    }    
    goto Loop;
}

void FadeLED(int pin)
{
    int period=10000;
    int mark=period+50;
    int space;
    while(mark)
    {
        mark-=50;    
        space=period-mark;
        PORTB=PORTB ^ (1<<pin);    // Toggles pin.
        _delay_us(mark);
        PORTB=PORTB ^ (1<<pin);    // Toggles pin again.
        _delay_us(space);
    }
    return;
}

You might also need to add a hold time as in my post #6.

Post your complete code that you last tried please. Exactly how are you making the pins high and low?
I asked that and other questions in post #5, but you didn't answer. Your one posted code snippet could not possibly compile.
 
Last edited:

wdariusw

Nov 10, 2014
149
Joined
Nov 10, 2014
Messages
149
Very strange thing...Finally "almost" got pretty soft PWM, bet with minimum duty cycle (my rigol scope shows 100mV) led still is on.
 

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
As an alternative, with the later chips, you can toggle pins like this, too:-
PINB = 0x5A; // This toggles pins 1,3,4 and6.
(Every bit that is set toggles the corresponding pin.)

rather than like this:-
PORTB=PORTB ^ (1<<pin); // Toggles pin.
(This will work in both old and new chips.)
 

wdariusw

Nov 10, 2014
149
Joined
Nov 10, 2014
Messages
149
Today morning finally did what i want! This is code for one LED on PA0 port:

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

#define fadeSpeed 50


void loop()
{

for(int fade=255;fade>0;fade--)
{

PORTA=(PORTA|(0b00000001));
_delay_us(fadeSpeed*(255-fade));
PORTA=(PORTA&0b11111110);
_delay_us(fadeSpeed*fade);

}

}


int main(void) {

DDRA=0xFF;

while(1)

{
loop();
}


}
 

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
Your code throws errors on both _delay_us() lines when I try to compile it.

Error 1 __builtin_avr_delay_cycles expects an integer constant.
Error 2 __builtin_avr_delay_cycles expects an integer constant.

Not that it really matters. I don't need this code. Personally, I'd use a timer.

And it looks to me like your code 'brightens' a LED, rather than 'fading' it.

I've got it working, fading, using a timer now. No errors for me this way. ;)
 
Last edited:

wdariusw

Nov 10, 2014
149
Joined
Nov 10, 2014
Messages
149
Your code throws errors on both _delay_us() lines when I try to compile it.

Error 1 __builtin_avr_delay_cycles expects an integer constant.
Error 2 __builtin_avr_delay_cycles expects an integer constant.

Not that it really matters. I don't need this code. Personally, I'd use a timer.

And it looks to me like your code 'brightens' a LED, rather than 'fading' it.

I've got it working, fading, using a timer now. No errors for me this way. ;)


Do you have avr-gcc code ? :)
 

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
You ignor
Do you have avr-gcc code ? :)
You ignored all of my questions, including several requests to post your code, yet expect me to jump and upload more code for you?
You asked for a solution without timers. You have it, you said.
 

Old Steve

Jul 23, 2015
734
Joined
Jul 23, 2015
Messages
734
I'll go so far as to show you how to set a timer. You can work out the rest. It'll be good practice:-

Code:
    // Set up prescaling:-
    // 0x00=stopped, 0x01=CLK/1, 0x02=CLK/8, 0x03=CLK/64, 0x04=CLK/256,  0x05=CLK/1024
    TCCR0B = 0x05;
   
    // Initialize the counter:-
    TCNT0 = 0;
   
    // Start timer:-
    TCCR0A |= (1 << CS00);

        if (TCNT0 >= pulse)            // Must be <= 255 or Timer0 overflows! (8-bit timer.)
        {
            PORTD ^= (1 << 0);        // Toggles the led.
            TCNT0 = 0;                // Reset the counter.
        }

Enjoy.
 
Top