July 01, 2012 by Hexorg Hey everyone! Long time no seeing! Sorry I haven't been with you guys, I've been very busy. On a bright note, I got a bachelors in EE, and I'm now enrolled in a grad school :D Anyway, I'm trying to program a software PWM, because I need to control 9 PWM channels. For now let's just try to make one channel work. I figured I'd use a timer in normal mode with an overflow interrupt to calculate PWM routine. So I have two variables - volatile uint8_t counter, and volatile uint8_t pwm1; In the interrupt routine I check if counter is smaller then pwm1, and if it is, then I set a pin HIGH, otherwise - LOW. Afterwards, if the counter equals to 255, I set it to 0, otherwise I increment it. Sounds not too hard... But I can't seem to get anything working. Here's my code: ``````#include #include #define LED1_PORT PORTB #define LED1_DDR DDRB #define LED1_R PB1 #define LED1_G PB0 #define LED1_B PB0 volatile uint8_t counter; volatile uint8_t led1_rch, led1_gch, led1_bch; ISR(TIMER0_OVF_vect) { if (counter < led1_rch) LED1_PORT |= _BV(LED1_R); else LED1_PORT &= ~_BV(LED1_R); if (counter < led1_gch) LED1_PORT |= _BV(LED1_G); else LED1_PORT &= ~_BV(LED1_G); if (counter < led1_bch) LED1_PORT |= _BV(LED1_B); else LED1_PORT &= ~_BV(LED1_B); if (counter == 255) counter = 0; else counter++; } void leds_init() { LED1_DDR |= _BV(LED1_R) | _BV(LED1_G) | _BV(LED1_B); LED1_PORT &= ~(_BV(LED1_R) | _BV(LED1_G) | _BV(LED1_B)); TIMSK0 |= _BV(TOIE0); // Enable overflow interrupt TCCR0B |= _BV(CS00);// no prescaler counter = led1_rch = led1_gch = led1_bch = 0; } void LED1(uint8_t red, uint8_t green, uint8_t blue) { led1_rch = red; led1_gch = green; led1_bch = blue; } int main() { leds_init(); //LED1_PORT |= _BV(LED1_G); sei(); LED1(1, 0, 0); while(1) { } return 0; } `````` Also, Does anyone know the recommended PWM frequency? I tried using clk/8 prescaler, and i get a TON of random blinking. Hexorg - First off...congrats on the EE! It looks like the use of LED's might be part of the problem. With no prescaler, the counter variable gets updated 57,600 times per second and gets 'reset' 225 times per second, way too fast for human vision to detect. Even with using 8 as a prescaler, you may not be certain all is working as planned. If you have an O-scope, you could test your output with better precision. I think the best way to improve your code would be to make 'counter' a 16-bit UINT. You could test it against PWM1 and reset it to 0 using two variables and keep the fine resolution of the timer. Just as a side note, you don't have to set 'counter' to 0 when it reaches 255, since it is a uint8_t, it will reset to 0 automatically. pcbolt, "and gets 'reset' 225 times per second" well it sounds reasonable enough, I don't want the eye to detect the LED blink, I want the LED to appear dimmer. Actually I just got the code working, my avr-gcc was messed up. However now, the LED never reaches full brightness. even when I make led1_rch to be uint16_t and set it to 300, theoretically the pin should just stay high, but it doesn't : I'll check it with a scope today, does pin switch everytime there is a PORT change? I did some thinking, and replaced if {} else {} statements in the interrupt with this ``````ISR(TIMER0_OVF_vect) { if (counter == 0) LED1_PORT |= (_BV(LED1_R) | _BV(LED1_G) | _BV(LED1_B)); if (counter == led1_rch) LED1_PORT ^= _BV(LED1_R); if (counter == led1_gch) LED1_PORT ^= _BV(LED1_G); if (counter == led1_bch) LED1_PORT ^= _BV(LED1_B); counter++; } `````` Seems to be a little lighter on the logic and that changed the LED to maximum brightness! No sure why, but it did :D Ah...gotcha. I misunderstood the problem. Glad to see you got it to go. I'm told the 'toggle' operator (XOR) or ^ works better if you write 1 to the input port when that port is designated as an output. For example, ``````#define LED1_PORT PORTB #define LED1_PIN PINB #define LED1_DDR DDRB LED1_DDR |= _BV(LED1_R); LED1_PIN |= _BV(LED1_R); /// toggles the output `````` This apparently saves a few machine code operations each time.