20Khz 50% ciclo de trabajo PWM generación Problema en atmega 328p

1

Quería crear un PWM de 20 KHz con un ciclo de trabajo del 50 por ciento (se puede variar dentro del bucle while) en el temporizador 0 de atmega 328p con MOODE 7: modo PWM rápido con valor superior como OCROA. Usé el registro OCROA para hacer 20 kHz. y OCR0B para crear un ciclo de trabajo del 50 por ciento (se puede variar) actualizando el ciclo de trabajo en cada comparación de OCR0B con el valor de registro TCNT0. La hoja de datos dice que la salida se encontrará en el pin OC0B (11th pin). Compilo el código sin ningún error y la quema con éxito a 328p. Pero el led no está encendido en absoluto. Lo adjunto puede codificar:

    
pregunta Zarzisur

1 respuesta

2

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.

    
respondido por el Kurt E. Clothier

Lea otras preguntas en las etiquetas