#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// Definir constantes
#define PWM_PIN PB1
#define SQUARE_WAVE_PIN PB0
#define FEEDBACK_PIN PB4
// Variables globales
volatile uint8_t pwm_value = 0; // Valor actual del PWM
volatile uint8_t max_pwm = 255; // Máximo valor del PWM
volatile uint8_t min_pwm = 0; // Mínimo valor del PWM
volatile uint8_t pwm_step = 0; // Paso actual del PWM
volatile uint8_t square_state = 0; // Estado de la onda cuadrada
// Tabla de valores senoidales (un cuarto de ciclo)
const uint8_t sine_table[64] = {
128, 140, 153, 165, 177, 188, 199, 209, 218, 226, 233, 239, 244, 248, 251, 252,
252, 251, 248, 244, 239, 233, 226, 218, 209, 199, 188, 177, 165, 153, 140, 128,
115, 102, 90, 78, 67, 56, 46, 37, 29, 22, 16, 11, 7, 4, 3, 3,
4, 7, 11, 16, 22, 29, 37, 46, 56, 67, 78, 90, 102, 115, 128, 140
};
// Configuración inicial
void setup() {
// Configurar pines como salida
DDRB |= (1 << PWM_PIN) | (1 << SQUARE_WAVE_PIN);
DDRB &= ~(1 << FEEDBACK_PIN); // Configurar FEEDBACK_PIN como entrada
// Configurar ADC para leer el pin PB4
ADMUX = (1 << REFS0) | (FEEDBACK_PIN); // Referencia interna AVCC, seleccionar canal PB4
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Habilitar ADC, prescaler 128
// Configurar Timer0 para interrupción cada 60 Hz
TCCR0A = (1 << WGM01); // Modo CTC
TCCR0B = (1 << CS01); // Prescaler de 8
OCR0A = 125; // Frecuencia de interrupción: 16 MHz / (8 * (125 + 1)) = 16 kHz
TIMSK = (1 << OCIE0A); // Habilitar interrupción por comparación
sei(); // Habilitar interrupciones globales
}
// Leer el voltaje de realimentación y ajustar el rango PWM
void adjust_pwm_range() {
ADCSRA |= (1 << ADSC); // Iniciar conversión ADC
while (ADCSRA & (1 << ADSC)); // Esperar hasta que termine la conversión
uint16_t adc_value = ADC; // Leer el valor ADC directamente del registro
// Mapear el voltaje (0.5V a 4.5V) al rango PWM (0 a 255)
float voltage = (adc_value * 5.0) / 1024.0; // Convertir a voltaje (0-5V)
if (voltage < 0.5) voltage = 0.5;
if (voltage > 4.5) voltage = 4.5;
max_pwm = ((voltage - 0.5) * (255.0 / 4.0)); // Escalar el máximo PWM
min_pwm = 0; // Mantener el mínimo en 0
}
// Interrupción del Timer0
ISR(TIMER0_COMPA_vect) {
static uint8_t counter = 0;
// Actualizar el valor PWM
if (pwm_step < 128) {
pwm_value = sine_table[pwm_step];
} else {
pwm_value = 255 - sine_table[255 - pwm_step];
}
// Aplicar rango ajustado
pwm_value = (pwm_value * (max_pwm - min_pwm)) / 255 + min_pwm;
// Alternar la onda cuadrada
if (square_state == 0) {
PORTB &= ~(1 << SQUARE_WAVE_PIN); // Bajar la onda cuadrada
_delay_us(3); // Tiempo muerto
PORTB |= (1 << PWM_PIN); // Subir el PWM
square_state = 1;
} else {
PORTB &= ~(1 << PWM_PIN); // Bajar el PWM
_delay_us(3); // Tiempo muerto
PORTB |= (1 << SQUARE_WAVE_PIN); // Subir la onda cuadrada
square_state = 0;
}
// Incrementar el paso PWM
pwm_step++;
if (pwm_step >= 255) {
pwm_step = 0; // Reiniciar el ciclo
}
}
int main(void) {
setup();
while (1) {
adjust_pwm_range(); // Ajustar el rango PWM periódicamente
}
}