ATMega 328P PWM control

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

Hello everyone I am new to programming.  May I ask for your help and advice.

I was trying to enable an LED by PWM  by toggling a switch on PortC.

I am using the fast PWM, Cleasr on compare OC0B function.

I removed the while(1) statement since I wanted the LED to get activated only once and not repeatedly.

Then I want the LED to turn-off after toggling the switch.

However it is not working.  May I ask what is wrong with this code.

Thank you for your ideas

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

void PWM_init(void)
{
    TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);	//Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).  //set to fast PWM
    TCCR0B|=(1<<CS02)|(1<<CS00);		        //set prescale to 1024
}

int main(void)
{
    DDRD=0x20;		                            //set OC0B to output
    DDRC=0x00;
    PWM_init();

if(PORTC&(1<<5))
{
    for(int count=0; count<1; count++)		//set counter to 1
        {
            for(int duty_cycle=0; duty_cycle<256; duty_cycle++)
            {
                OCR0B=duty_cycle;		    //increase duty cycle
                _delay_ms(1);              	//delay of duty cycle
            }
        }
}
else
{
    OCR0B=0;                            //deactivate the LED
}
}

h an input pin.

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

Your code will go through main() once then drop off the edge of the earth. You need a while(1){} around your code to ensure it keeps on running.

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

Kartman wrote:
Your code will go through main() once then drop off the edge of the earth. You need a while(1){} around your code to ensure it keeps on running.

 

Thank you for the advice.  Initially there is a while(1) statement but the LED keeps on resetting.  It gets activated then once it hits the max 255 it resets again.  That is the reason why I removed the while(1) because I wanted the loop to be executed once only.  Application is like in car interior LED dimming feature.  It starts PWM from 0% to 100% then stays at 100%.  Then once I wanted to turn it OFF I just toggle the switch.

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

Put the while (1) at the end of your code then. The processor needs to exrcute something otherwise it will reset.

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

Remember, almost all embedded programs are never supposed to "end"; they may look like they are doing nothing, but they are busy waiting for the next event (power on, limit switch, wakeup light sensor, etc).

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
if(PORTC&(1<<5))

bet that means "PINC" ;-)

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

Thank you all for the advices. I tried putting the while(1) at the end but it did not work. Also I tried putting break after the for loop. The LED responds when turned ON but would not turn OFF after toggling the switch.

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

clawson wrote:

if(PORTC&(1<<5))

bet that means "PINC" ;-)

Yes you are right thank you for correcting. I tried correcting and putting break after the for loop but did not work.

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

ryan2019 wrote:
I tried correcting and putting break after the for loop

Post the actual code.

 

but did not work.

doesn't really tell us much

  • What were you expecting it to do?
  • What did it actually do?
  • What investigation / testing / debugging have you done to find the problem?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

why do you need PWM for an LED?

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

Please tell us exactly what you want to do.  Include all the hardware details that are needed.   For example: 

 

what microcontroller?  

Assuming an AVR Mega328P  (found on the Arduino Nano and UNO, the most common beginner's learning development boards. Arduino Nano/UNO boards run at 16MHz)

 

Main MCU action:   cause an ordinary LED on OC0b to cycle from completely OFF to a pre-set brightness level (in percentage) over the course of a specific time period upon a pre-determined change in a push-switch on PORT C5. 

 

Is this what you are trying to do?  You have to specifically tell us.   What is the final brightness level?  What is the time interval?  Is it a linear rise in brightness?

What happens when the final brightness level is reached?   Does the LED stay on at that brightness level?

 

Hardware description:

LED (plus 500 ohm resistor) on OC0b.  What pin is that?   

LED Anode on OC0b: LED cathode to ground??    LED lights when OC0b is high.

or the other way around?  LED Cathode on OC0b: LED anode to ground??   LED lights when OC0b is low.

 

 

Push Switch on PORT C5:  How is the switch debounced?  by hardware or software?  Is the switch connected to VCC or GND?  is there a pull-up or pull-down resistor? What value?

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

Simonetta wrote:

Please tell us exactly what you want to do.  Include all the hardware details that are needed.   For example: 

 

what microcontroller?  

Assuming an AVR Mega328P  (found on the Arduino Nano and UNO, the most common beginner's learning development boards. Arduino Nano/UNO boards run at 16MHz)

 

Main MCU action:   cause an ordinary LED on OC0b to cycle from completely OFF to a pre-set brightness level (in percentage) over the course of a specific time period upon a pre-determined change in a push-switch on PORT C5. 

 

Is this what you are trying to do?  You have to specifically tell us.   What is the final brightness level?  What is the time interval?  Is it a linear rise in brightness?

What happens when the final brightness level is reached?   Does the LED stay on at that brightness level?

 

Hardware description:

LED (plus 500 ohm resistor) on OC0b.  What pin is that?   

LED Anode on OC0b: LED cathode to ground??    LED lights when OC0b is high.

or the other way around?  LED Cathode on OC0b: LED anode to ground??   LED lights when OC0b is low.

 

 

Push Switch on PORT C5:  How is the switch debounced?  by hardware or software?  Is the switch connected to VCC or GND?  is there a pull-up or pull-down resistor? What value?

 

 

Good day Simonetta.  Please refer to the modified code and the description of HW interface.

I am using Arduino Uno ATMEGA 328P 16MHz.  Application is LED dimmer.  I use OC0B PD5 as output to connect to a 1k0 resistor.  The resistor is then in series to an LED anode and the cathode is directly connected to GND.

So LED lights up when the OC0B output is high.  For the trigger I used PortC A5 as input.  The input  switch has pull-up resistor to 5V VCC while the other end is connected directly to GND.  I just slide the switch to toggle between VCC and GND.  The OC0B shall output increasing PWM from 0% to 100% once PortC A5 is toggled to input High.  Interval defined is 100ms.  The LED shall stay fully lit at 100% as long as the switch at PortC is at status High(5V) and shall deactivate to totally OFF when PortC switch is switched to GND.   Prescale used is 1024.  Configured as fast PWM and Clear on compare match (non-inverting).

 

I placed a break instruction after the for loop to avoid repetition.  However I did not get the output that I want.  I observe the following:

- The LED is not completely turned OFF when the input toggle switch is connected to GND.  It is dimly activated.

- The LED changes status and can be activated from OFF position until it reaches full activation after toggling the input switch from GND to VCC.  However when I toggled the switch

back to GND the LED did not turn OFF.  It is still fully turned ON.  The LED does not change status anymore even after multiple input switch toggled.

 

I appreciate your thoughts.  Thank you.

 

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

volatile uint16_t duty_cycle;

void Timer0_init(void)
{
    TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);	//Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).  Set to fast PWM.
    TCCR0B|=(1<<CS02)|(1<<CS00);		        //set prescale to 1024
    
}

int main(void)
{				
DDRD=0x20;		//set OC0B to output
DDRC=0x00;      //set to input

Timer0_init();
while(1)
{
    if(PINC&(1<<5)
    {		
        for(int count=0; count<1; count++)		//counter to execute ony once
            {
                for(duty_cycle=0; duty_cycle<256; duty_cycle++)	//increase PWM
                {
                    OCR0B=duty_cycle;		//increase duty cycle
                    _delay_ms(100);	        //delay of duty cycle change
                }		
            }								
        break;	
    }
    else
    {
    OCR0B=0;
    }
}
}


















 

 

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

roy5 wrote:

why do you need PWM for an LED?

 

This is for dimming activation of the LED once the input is triggered. 

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

ryan2019 wrote:

However I did not get the output that I want.  I observe the following:

- The LED is not completely turned OFF when the input toggle switch is connected to GND.  It is dimly activated.

With AVR8 Fast PWM, you cannot get full-on AND full-off.  The usual approach is to use inverted PWM and invert the duty cycle value.  As you observe, you can see a 1/256 duty cycle, but you won't be able to detect 254/255 versus 255/255.

 

https://www.avrfreaks.net/commen...

https://www.avrfreaks.net/commen...

https://www.avrfreaks.net/commen...

https://www.avrfreaks.net/commen... (and link)

...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Fri. Nov 22, 2019 - 01:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

ryan2019 wrote:

However I did not get the output that I want.  I observe the following:

- The LED is not completely turned OFF when the input toggle switch is connected to GND.  It is dimly activated.

With AVR8 Fast PWM, you cannot get full-on AND full-off.  The usual approach is to use inverted PWM and invert the duty cycle value.  As you observe, you can see a 1/256 duty cycle, but you won't be able to detect 254/255 versus 255/255.

 

https://www.avrfreaks.net/comment/2792041#comment-2792041

https://www.avrfreaks.net/comment/2202081#comment-2202081

https://www.avrfreaks.net/comment/2091101#comment-2091101

https://www.avrfreaks.net/comment/2062056#comment-2062056 (and link)

...

Thank you sir I will modify the code based on your hint.
My bigger problem however is that when I toggle the switch from ON to OFF the LED does not turn OFF and stays ON all the time.

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

ryan2019 wrote:

However when I toggled the switch back to GND the LED did not turn OFF.  It is still fully turned ON.  The LED does not change status anymore even after multiple input switch toggled.

Well that sounds completely expected, considering the code effectively breaks from the while loop after LED is turned on.

 

Also, the loop that runs only once (commented with "counter to execute ony once") makes no sense.

 

How about this code?

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

volatile uint16_t duty_cycle;

void Timer0_init(void)
{
    TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);    //Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).  Set to fast PWM.
    TCCR0B|=(1<<CS02)|(1<<CS00);                //set prescale to 1024
}

int main(void)
{                
    DDRD=0x20;        //set OC0B to output
    DDRC=0x00;      //set to input

    Timer0_init();
    
    // Wait until pin is high
    while (PINC&(1<<5) == 0)
    {
    }
        
    for(duty_cycle=0; duty_cycle<256; duty_cycle++)    //increase PWM
    {
        OCR0B=duty_cycle;        //increase duty cycle
        _delay_ms(100);            //delay of duty cycle change
    }
    
    // Wait until pin is low
    while (PINC&(1<<5) != 0)
    {
    }
    
    // Set PWM to 0
    OCR0B=0;
    
    // Do nothing
    while (1)
    {
    }
}

 

/Jakob Selbing

Last Edited: Sat. Nov 23, 2019 - 06:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jaksel wrote:

ryan2019 wrote:

However when I toggled the switch back to GND the LED did not turn OFF.  It is still fully turned ON.  The LED does not change status anymore even after multiple input switch toggled.

Well that sounds completely expected, considering the code effectively breaks from the while loop after LED is turned on.

 

Also, the loop that runs only once (commented with "counter to execute ony once") makes no sense.

 

How about this code?

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

volatile uint16_t duty_cycle;

void Timer0_init(void)
{
    TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);    //Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).  Set to fast PWM.
    TCCR0B|=(1<<CS02)|(1<<CS00);                //set prescale to 1024
}

int main(void)
{                
    DDRD=0x20;        //set OC0B to output
    DDRC=0x00;      //set to input

    Timer0_init();
    
    // Wait until pin is high
    while (PINC&(1<<5) == 0)
    {
    }
        
    for(duty_cycle=0; duty_cycle<256; duty_cycle++)    //increase PWM
    {
        OCR0B=duty_cycle;        //increase duty cycle
        _delay_ms(100);            //delay of duty cycle change
    }
    
    // Wait until pin is low
    while (PINC&(1<<5) != 0)
    {
    }
    
    // Set PWM to 0
    OCR0B=0;
    
    // Do nothing
    while (1)
    {
    }
}

 

 

Thank you for this code.  However this did not work as well.  What happened is that the LED activated slowly due to PWM then turns OFF (dimly activated because of the non-inverting code) then could not respond anymore to switch toggle.  I think I need more time to solve this.

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

ryan2019 wrote:
What happened is that the LED activated slowly due to PWM then turns OFF (dimly activated because of the non-inverting code) then could not respond anymore to switch toggle. 

Wasn't that the intention? I thought you wanted to turn on, then off, then do nothing forever? Well then try this:

 

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

volatile uint16_t duty_cycle;

void Timer0_init(void)
{
    TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);    //Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).  Set to fast PWM.
    TCCR0B|=(1<<CS02)|(1<<CS00);                //set prescale to 1024
}

int main(void)
{
    DDRD=0x20;    //set OC0B to output
    DDRC=0x00;    //set to input

    Timer0_init();
    
    while (1)
    {
        // Wait until pin is high
        while (PINC&(1<<5) == 0)
        {
        }
        
        for(duty_cycle=0; duty_cycle<256; duty_cycle++)        //increase PWM
        {
            OCR0B=duty_cycle;    //increase duty cycle
            _delay_ms(100);        //delay of duty cycle change
        }
    
        // Wait until pin is low
        while (PINC&(1<<5) != 0)
        {
        }
    
        // Set PWM to 0
        OCR0B=0;
    
        // NB: probably need some debouncing here, for simplicity I inserted a delay
        _delay_ms(100);
    }
}

 

/Jakob Selbing

Last Edited: Mon. Nov 25, 2019 - 08:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jaksel wrote:

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

volatile uint16_t duty_cycle;

void Timer0_init(void)
{
    TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);    //Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).  Set to fast PWM.
    TCCR0B|=(1<<CS02)|(1<<CS00);                //set prescale to 1024
}

int main(void)
{
    DDRD=0x20;    //set OC0B to output
    DDRC=0x00;    //set to input

    Timer0_init();
    
    while (1)
    {
        // Wait until pin is high
        while (PINC&(1<<5) == 0)
        {
        }
        
        for(duty_cycle=0; duty_cycle<256; duty_cycle++)        //increase PWM
        {
            OCR0B=duty_cycle;    //increase duty cycle
            _delay_ms(100);        //delay of duty cycle change
        }
    
        // Wait until pin is low
        while (PINC&(1<<5) != 0)
        {
        }
    
        // Set PWM to 0
        OCR0B=0;
    
        // NB: probably need some debouncing here, for simplicity I inserted a delay
        _delay_ms(100);
    }
}

Thank you for this code jaksel.

My intention however is to deactivate the LED once PORTC Pin5 is switched to GND.  At the moment the LED just activates starting from 0%DC and once it reaches full duty cycle it deactivates then cycles again.  It does not respond when PORT C Pin5 is pulled to GND.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
int main(void)
{
    DDRD=0x20;    //set OC0B to output
    DDRC=0x00;    //set to input
    PORTC |= (1<<5); //enable pullup on PORTC.5

Add the last line into the code. You've not shown us how you're controlling the port pin. If you leave it to float, then strange things will happen. Enabling the pullup feature stops this.

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

Kartman wrote:

int main(void)
{
    DDRD=0x20;    //set OC0B to output
    DDRC=0x00;    //set to input
    PORTC |= (1<<5); //enable pullup on PORTC.5

Add the last line into the code. You've not shown us how you're controlling the port pin. If you leave it to float, then strange things will happen. Enabling the pullup feature stops this.

Thank you sir for the hint. My switch has pull up resistor to VCC and the other side is directly connected to GND so it should be ok. Still the LED does not react when switch is toggled.

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

Now you want a ‘toggle’ feature! Precisely, how do you want the unit to work?

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

Are you sure that the MCU is not simply resetting? Is watchdog enabled? What are the fuses set to?

/Jakob Selbing

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

Kartman wrote:
Now you want a ‘toggle’ feature! Precisely, how do you want the unit to work?

 

Sorry for the confusion.  Please refer to the circuit I attached.

Here's what I want to do:

 

I am using Arduino Uno ATMEGA 328P 16MHz.  Application is LED dimmer.  I use OC0B PD5 as output to connect to a 1k0 resistor.  The resistor is then in series to an LED anode and the cathode is directly connected to GND.

So LED lights up (0% duty cycle to 100% duty cycle) when the OC0B output is enabled.  For the trigger I used PortC A5 as input.  The input  switch has pull-up resistor to 5V VCC while the other end is connected directly to GND.  I just slide the switch to choose between VCC or GND.  The OC0B shall output increasing PWM from 0% to 100% once PortC A5 is switched to input High.  Interval defined is 100ms.  The LED shall stay fully lit at 100% as long as the switch at PortC is at status High(5V) and shall deactivate to totally OFF when PortC switch is switched to GND.   Prescale used is 1024.  Configured as fast PWM and Clear on compare match (non-inverting).

Attachment(s): 

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

jaksel wrote:

Are you sure that the MCU is not simply resetting? Is watchdog enabled? What are the fuses set to?

 

No watchdog enabled.  Fuses not modified.  Please refer to the circuit I attached.

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

You might to check your schematic - I can't see a pullup resistor on the input.

 

You have a switch and a led - what do you want it to do? Don't talk in terms of implementation details.

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

Kartman wrote:

You might to check your schematic - I can't see a pullup resistor on the input.

 

You have a switch and a led - what do you want it to do? Don't talk in terms of implementation details.

 

 

Switch is the input and LED is the output.    LED shall activate once switch is pulled High and deactivate once pulled Low.

Attachment(s): 

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

ryan2019 wrote:
My intention however is to deactivate the LED once PORTC Pin5 is switched to GND.  At the moment the LED just activates starting from 0%DC and once it reaches full duty cycle it deactivates then cycles again.  It does not respond when PORT C Pin5 is pulled to GND.
That is because you for() loop varyng the duty and the code checking the pin input are separate. Once you enter the 256 step for() loop it has to run to completion before a check of the input is made. That is you have:

        // Wait until pin is high
        while (PINC&(1<<5) == 0)
        {
        }
        
        for(duty_cycle=0; duty_cycle<256; duty_cycle++)        //increase PWM
        {
            OCR0B=duty_cycle;    //increase duty cycle
            _delay_ms(100);        //delay of duty cycle change
        }
    
        // Wait until pin is low
        while (PINC&(1<<5) != 0)
        {
        }

as you have a 256 step loop with a 0.1s delay in it then it will take 25.6 seconds before it gets to the line where PINC.5 is checked. Presumably you want something more like:

        // Wait until pin is high
        while (PINC&(1<<5) == 0)
        {
        }
        
        for(duty_cycle=0; duty_cycle<256; duty_cycle++)        //increase PWM
        {
            OCR0B=duty_cycle;    //increase duty cycle
            _delay_ms(100);        //delay of duty cycle change
            if (PINC & (1 << 5)) {
               break;
            }
        }
    
        // Wait until pin is low
        while (PINC&(1<<5) != 0)
        {
        }

so the maximum it waits is 100ms before it next tests the state of PINC.5 and breaks out of the for() loop as soon as it sees it has gone low.

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

The resistor goes onto the port pin, not the switch. Your resistor currently serves no purpose.

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

Why are you using a 3 terminal switch---only use 2 terminals!

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

clawson wrote:
if (PINC & (1 << 5)) {   break;   }

 

Thank you clawson that line really helped control the LED now I can turn it On or OFF.

However I do not understand why the LED still resets after reaching 100% duty cycle.  It keeps on cycling again and repeating from 0%DC to 100%DC.

The break line should have addressed this issue but this is still the problem until now.

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

avrcandies wrote:

Why are you using a 3 terminal switch---only use 2 terminals!

 

I did use 2 terminals as well and the result is the same.

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

clawson wrote:
Presumably you want something more like:

        // Wait until pin is high
        while (PINC&(1<<5) == 0)
        {
        }
        
        for(duty_cycle=0; duty_cycle<256; duty_cycle++)        //increase PWM
        {
            OCR0B=duty_cycle;    //increase duty cycle
            _delay_ms(100);        //delay of duty cycle change
            if (PINC & (1 << 5)) {
               break;
            }
        }
    
        // Wait until pin is low
        while (PINC&(1<<5) != 0)
        {
        }

so the maximum it waits is 100ms before it next tests the state of PINC.5 and breaks out of the for() loop as soon as it sees it has gone low.

 

I think you have the wrong condition in the if-statement. The while-loop should break if pin goes low - not high.

 

I also never liked the implicit integer-to-bool conversion style so here is my attempt:

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

volatile uint16_t duty_cycle;

void Timer0_init(void)
{
	TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);    //Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).  Set to fast PWM.
	TCCR0B|=(1<<CS02)|(1<<CS00);                //set prescale to 1024
}

int main(void)
{
	DDRD=0x20;    //set OC0B to output
	DDRC=0x00;    //set to input

	Timer0_init();
	
	while (1)
	{
		// Wait until pin is high
		while ((PINC&(1<<5)) == 0)
		{
		}
		
		for(duty_cycle=0; duty_cycle<256; duty_cycle++)        //increase PWM
		{
			OCR0B=duty_cycle;    //increase duty cycle
			_delay_ms(100);        //delay of duty cycle change
			
			// Early break if pin goes low
			if ((PINC & (1 << 5)) == 0)
				break;
		}
		
		// Wait until pin is low
		while ((PINC&(1<<5)) != 0)
		{
		}
		
		// Set PWM to 0
		OCR0B=0;
		
		// NB: probably need some debouncing here, for simplicity I inserted a delay
		_delay_ms(100);
	}
}

 

/Jakob Selbing

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

But you missed one important comment: you have no debouncing implemented.

 

At least you could use an RC-filter on the switch signal to make things a little better. Otherwise a short bouncing may very well cause erratic behaviour.

/Jakob Selbing

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

jaksel wrote:

clawson wrote:
Presumably you want something more like:

        // Wait until pin is high
        while (PINC&(1<<5) == 0)
        {
        }
        
        for(duty_cycle=0; duty_cycle<256; duty_cycle++)        //increase PWM
        {
            OCR0B=duty_cycle;    //increase duty cycle
            _delay_ms(100);        //delay of duty cycle change
            if (PINC & (1 << 5)) {
               break;
            }
        }
    
        // Wait until pin is low
        while (PINC&(1<<5) != 0)
        {
        }

so the maximum it waits is 100ms before it next tests the state of PINC.5 and breaks out of the for() loop as soon as it sees it has gone low.

 

I think you have the wrong condition in the if-statement. The while-loop should break if pin goes low - not high.

 

I also never liked the implicit integer-to-bool conversion style so here is my attempt:

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

volatile uint16_t duty_cycle;

void Timer0_init(void)
{
	TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);    //Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).  Set to fast PWM.
	TCCR0B|=(1<<CS02)|(1<<CS00);                //set prescale to 1024
}

int main(void)
{
	DDRD=0x20;    //set OC0B to output
	DDRC=0x00;    //set to input

	Timer0_init();

	while (1)
	{
		// Wait until pin is high
		while ((PINC&(1<<5)) == 0)
		{
		}

		for(duty_cycle=0; duty_cycle<256; duty_cycle++)        //increase PWM
		{
			OCR0B=duty_cycle;    //increase duty cycle
			_delay_ms(100);        //delay of duty cycle change

			// Early break if pin goes low
			if ((PINC & (1 << 5)) == 0)
				break;
		}

		// Wait until pin is low
		while ((PINC&(1<<5)) != 0)
		{
		}

		// Set PWM to 0
		OCR0B=0;

		// NB: probably need some debouncing here, for simplicity I inserted a delay
		_delay_ms(100);
	}
}

 

 

 

Hello Jaksel you nailed it brother!  They just need to be in a single while loop.  Thank you very much for this code it's working according to the requirement.

After trying your working code I also modified my original code but this time I used 2 while loops with a jump statement.  However I could not set the PINC to active High this I do not know why.

If I try to set to active High the LED reacts with a delay and does not go to early exit if I toggled the PINC from High to Low.  So I have to do it the other way then.

I also found out that for fade in and fade out scenarios inverting the PWM does not work.  Correct me If I am wrong that is what I observed so that is why I resorted to Phase correct instead of Fast PWM in order to fully shut OFF the LED when PINC is pulled HIGH. For this reason I need to set the prescale lower to avoid the LED jitter during ON transition.  I also made 2 while loops and read about the jump statement to avoid the code getting stuck when I toggled the PINC HIGH and have it implemented as well.  I also put some debounce. 

Thank you all for your help guys.  Appreciate your time and effort!

 

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

volatile uint16_t duty_cycle;

void Timer0_init(void)
{
	TCCR0A|=(1<<COM0B1)|(1<<WGM00);    //Clear OC0B on Compare Match, set OC0B at BOTTOM, (non-inverting mode).  Set to PWM, Phase Correct.  TOP 0xFF since at fast PWM settings the LED is not fully DIM OUT (not possible 0%DC)
	TCCR0B|=(1<<CS01)|(1<<CS00);       //set prescale to 64 since at higher prescale setting of phase correct PWM you can notice the LED jittering before full PWN ON at 100%
}

uint16_t debouncer_init(void)			//set debounce of 50ms Active Low config.  Active high config is not possible as if you want to immediately turn LED OFF while in ON transition it waits for the full 255 count before it turns OFF.  I do not know why?
{
	if(bit_is_clear(PINC, PC5))			//Clear for active Low.
	{
		_delay_ms(50);
		if(bit_is_clear(PINC, PC5));
		{
			return (1);
		}
	}
		return (0);
}

int main(void)
{
	DDRD=0x20;                                                          //set OC0B to output
	DDRC=0x00;                                                          //set to input

	Timer0_init();

	fade_in:
	while(1)
	{
		if(debouncer_init())	                                    	         //PINC is pulled LOW
		{
			for(int count=0; count<1; count++)		                 //Set counter to 1.  Only excute once.
			{
				for(duty_cycle=0; duty_cycle<255; duty_cycle++)         //increase PWM to 255
				{
					OCR0B=duty_cycle;
					_delay_ms(100);		        //delay of duty cycle change

					if(PINC&(1<<5))
					{
						break;	               //immediately turn OFF led once PIN C is toggled High.
					}
				}
			}
			break;				    	       //get out of the loop once the counter 1 is executed.
		}
	}

    while(1)
	{
		if(!debouncer_init())	    //PINC is pulled HIGH
		{
			OCR0B=0;			//turn OFF the PWM
			goto fade_in;			//jump back to the first loop so that the code is not stuck here.
		}

	}

}

 

Last Edited: Mon. Dec 2, 2019 - 02:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

ryan2019 wrote:

goto fade_in;

It's 2019 not 1969. The language may include "goto" but there's no real excuse for using it (except maybe in an exception handling situation).

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

clawson wrote:

ryan2019 wrote:

goto fade_in;

It's 2019 not 1969. The language may include "goto" but there's no real excuse for using it (except maybe in an exception handling situation).

 

:-)  Thank you brother.  Thank you for the hints I hope to improve further :-)

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

ryan2019 wrote:

After trying your working code I also modified my original code but this time I used 2 while loops with a jump statement.  However I could not set the PINC to active High this I do not know why.

If I try to set to active High the LED reacts with a delay and does not go to early exit if I toggled the PINC from High to Low.  So I have to do it the other way then.

I also found out that for fade in and fade out scenarios inverting the PWM does not work.  Correct me If I am wrong that is what I observed so that is why I resorted to Phase correct instead of Fast PWM in order to fully shut OFF the LED when PINC is pulled HIGH. For this reason I need to set the prescale lower to avoid the LED jitter during ON transition.  I also made 2 while loops and read about the jump statement to avoid the code getting stuck when I toggled the PINC HIGH and have it implemented as well.  I also put some debounce.

I'm not sure about how the PWM module handles the boundary cases, but if you want to set 0% or 100% duty cycle why not just disable the PWM and set the pin high or low?

 

I also don't understand why you would need to set a low prescaler value to avoid LED jitter? I am quite sure the PWM module synchronizes updates of the OCR registers to avoid invalid duty cycles if that is what you're worried about.

 

Also I would advice you to:

1) use better debouncing code (use e.g. any of the example code that you can find on avrfreaks)

2) use state machines wherever applicable

3) avoid using delays.  Instead use a timer (you could even use the same as the PWM is running off) to generate a tick and let your code use it for time-keeping.

 

 

 

/Jakob Selbing

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

jaksel wrote:
I'm not sure about how the PWM module handles the boundary cases, but if you want to set 0% or 100% duty cycle why not just disable the PWM and set the pin high or low?
The usual issue with PWM is that (where LEDs are concerned) you cannot switch them totally off while PWM is enabled so the usual solutions are either to turn off PWM and just drive the pin to the inactive state as you suggest. The alternative is to use the complementary mode of PWM in which case there is a "totally off" value available (but tshis is at the cost of there not being a "100% on" (but you get very close with about 99.5% on as the top value). Human eyes can't really discern between almost totally on and totally on. But they can see "not quite off" ;-)