OCR0A is initialized to 65535..? (That'll just delay the first cycle, you may not even notice it; but it's not good form -- imagine what would happen if you used a 24 or 32 bit timer!)
Wouldn't your code be doing a... some sort of asynchronous frequency modulation? The main() loop runs flat out, incrementing count in a ramp, probably several times faster than the timer is running. The timer only samples duty_cycle when it interrupts, and if it happens to read the same value twice in a row, you don't get any PWM.
You've also got a hazard that ints are two byte variables. If the interrupt fires while main() is writing to duty_cycle, it can read one old byte and one new byte, getting some trash Frankensteined number. (For values as shown, this won't ever happen because the high byte is always zero -- duty and next_duty are both within the +127/-128 inclusive range of a 1-byte char.)
You've also got the problem that the compiler doesn't know anything else can read duty_cycle; since main() is only reading it in the loop, it might never bother to update the memory value, just changing around values in registers (or optimizing the whole thing down to nothing; I don't think avr-gcc is nearly smart enough to do that, but other flavors are).
The solution is to declare,
volatile int duty_cycle = duty;
which tells the compiler that, hey, other things want to read this value, so whenever you write to it, remember to save it to memory as well. And then when accessing it, use:
#include <util/atomic.h>
...
ATOMIC_BLOCK(ATOMIC_FORCEON) {
duty_cycle = foo;
}
This is equivalent to putting cli() and sei() around the statement, it's just a more portable way of doing so. (You can also use the parameter ATOMIC_RESTORESTATE instead, if you are in a function where you don't know whether interrupts are supposed to be on or off; it saves and restores the flag.)
An atomic operation is indivisible, as the name suggests. By disabling interrupts, you guarantee TIMER0_OVF_vect never divides the writing process.
![ThumbsUp :-+](https://www.eevblog.com/forum/Smileys/default/icon_smile_thumbsup.gif)
So that's important, but it won't actually accomplish what you wanted to do! As for that -- I'm not sure how you expected count to track the timer but it's just a variable in main(), it doesn't have any relation to device state. Perhaps you meant to read TCNT0, the counter register; perhaps you meant to increment count (making it a global variable first) in the interrupt; perhaps you meant to set a flag in the interrupt (protip: 1-byte (char) variables are atomic so you don't need to use the ATOMIC macros!), and load duty or next_duty based on that, or poll that flag in the main() loop to the same end. All are possibilities, with the interrupt handling itself being one of the best.
The best of course is to simply use the PWM mode, or use TC1 if you need the one bit extra resolution that your emulated counting mode provides (assuming of course, you aren't using it for anything else later; it is rather important after all, being the only 16-bit counter on the ATMEGA324).
In my last AVR project, I used three timers and have plans for a 4th, most of them literally just setting a flag and returning (the flag is then read by e.g. main() loop to trigger housekeeping functions, reading pushbuttons, updating display, that sort of stuff). Helps that the XMEGA I'm using has tons of timers.
![Tongue :P](https://www.eevblog.com/forum/Smileys/default/tongue.gif)
Tim