Problema principal
Está configurando el pin de Comparar salida de coincidencia en el registro incorrecto. Reemplazar
TCCR0A |= (1<<WGM01) | (1<<WGM00);
TCCR0B |= (1<<COM0B1) | (1<<WGM02) | (1<<CS01);
con
TCCR0A = (1<<COM0B1) | (1<<WGM01) | (1<<WGM00);
TCCR0B = (1<<WGM02) | (1<<CS01);
Por supuesto, esto supone que tiene una frecuencia de CPU de 16 MHz sin división de reloj, ya que está usando un valor de preescala de 8 con un valor SUPERIOR de 100.
F_OCRO = F_CPU / (Prescaler * (1 + OCR0A)) = 16MHz / (8 * 100) = 20kHz
Otros problemas
Esto podría estar funcionando, pero no es la mejor idea. Hay muchas cosas que podría repasar, pero hablemos de esto
double duty_cyle = 50;
OCR0B = (duty_cycle * 99) / 100;
AVR es de 8 bits. Doble es un número de 64 bits con decimales. En realidad, (en GCC, al menos), los dobles se reemplazan automáticamente con Flotadores (precisión simple). AVR no tiene una unidad de punto flotante (FPU) y las operaciones de punto flotante (que tienen un almacenamiento de 32 bits) tomarán un orden de magnitud MÁS ciclos de reloj para completar que una operación típica de 8 bits. La división tampoco es compatible con el hardware AVR, por lo que el compilador lo emula en el software. También estás haciendo todo esto en una rutina de servicio de interrupción. Eso es ... no es bueno.
OCR0B es un registro de 8 bits (máx. de 255, sin firmar). Ya que está usando OCR0A como el temporizador TOP con un valor de 99, tiene una buena configuración de ciclo de trabajo de 0 a 99%. Entonces, simplemente configure OCR0B para el ciclo de trabajo que desee. ¿Por qué (50/99 * 100) que es igual a 49.5 y se cortará a 49 de todos modos? Solo haz esto:
static volatile uint8_t duty_cycle = 50; // unsigned 8-bit integer
OCR0B = duty_cycle;
La palabra clave "estática" significa que esta variable solo está disponible en el archivo actual, es como un local global . También debe usarlo en cualquier función local a la que no se acceda fuera de este archivo .c. Las palabras clave "volátiles" le dicen al compilador que no optimice la variable porque podría actualizarse en algún lugar que no se esperaba (como en un ISR). No necesita volatile si no está cambiando la variable duty_cycle en un ISR. Mejor aún, ¿por qué utilizar una variable global cuando puede acceder directamente al registro?
OCR0B = 50;
Si no te gusta eso, usa una macro o una función en línea estática:
#define SET_DUTY(PERCENT) OCR0B = (PERCENT)
// ...
SET_DUTY(50);
// OR
static inline void set_duty(const uint8_t percent) {
OCR0B = percent;
}
// ...
set_duty(50);
Además, a menos que planee reiniciar el temporizador en algún momento mientras mantiene otros cambios que ha realizado en esos registros, no necesita hacer "|=", que es una "lectura-modificación-escritura" operación. Solo usa "=". El "|=" significa establecer solo el bit especificado y dejar todo lo demás solo, lo que no tiene sentido durante la inicialización cuando se quiere que todo lo que no está específicamente configurado sea 0.