/**
**************************
* @file : main.c
* @author : Fernando Hermosillo Reynoso (y tu asistente AI)
* @brief : Dimmer AC - Control por Botones
**************************
*/
/**
**************************
* @file : main.c
* @author : Fernando Hermosillo Reynoso (y tu asistente AI)
* @brief : Dimmer AC - Control por Botones (Versión Corregida)
**************************
*/
#include <stdint.h>
#include "stm32f103xb.h"
#include "stm32f103_hal.h"
/* Defines -----------------------------------------------------*/
// Tiempos ajustados para una red de 60Hz (Semiciclo = 8333 us)
#define TRIAC_MIN_DELAY_TICKS 500 // Brillo máximo (Disparo temprano)
#define TRIAC_MAX_DELAY_TICKS 7500 // Brillo mínimo (Disparo tardío, antes de que acabe el semiciclo)
#define TRIAC_NUM_LEVELS 10
#define TRIAC_DELAY_STEP_TICKS ((TRIAC_MAX_DELAY_TICKS-TRIAC_MIN_DELAY_TICKS)/TRIAC_NUM_LEVELS)
/* Funciones de retardo ----------------------------------------*/
void delay_ms(uint16_t ms) {
volatile unsigned long t = 0;
for(uint16_t i = 0; i < ms; i++) {
for(t = 0; t < 800; t++);
}
}
void delay_us(uint16_t us) {
for (volatile unsigned int cycles = 0; cycles < us; cycles++);
}
/* Variables globales ------------------------------------------*/
volatile uint16_t firing_delay = TRIAC_MIN_DELAY_TICKS;
volatile uint8_t triac_on = 1;
int main(void)
{
// ==========================================================
// CONFIGURACIONES INICIALES
// ==========================================================
// 1. Habilitar reloj de GPIOA (Bit 2) y AFIO (Bit 0) - CRÍTICO PARA EXTI
RCC->APB2ENR |= (1 << 2) | (1 << 0);
// 2. Configurar pines por registros y tu librería
// Configurar PA1 como salida Push-Pull (Para el disparo del TRIAC) a 2MHz
GPIOA->CRL &= ~(0x0F << 4);
GPIOA->CRL |= (0x02 << 4);
GPIOA->ODR &= ~(1 << 1); // Empezar apagado
// Configurar Entradas (PA0, PA2 y PA3)
// NOTA: Si tu compilador no reconoce los enums GPIO_INPUT_FLOAT o GPIO_INPUT_PD,
// revisa cómo se llaman exactamente en tu stm32f103_hal.h
gpio_set_input(GPIOA, 0, 0x04); // PA0: Cruce por cero (Floating Input)
gpio_set_input(GPIOA, 2, 0x08); // PA2: Bajar brillo (Pull-Down/Up)
gpio_set_input(GPIOA, 3, 0x08); // PA3: Subir brillo (Pull-Down/Up)
// Si usaste Pull-Down para los botones, asegúrate de que el ODR esté en 0
GPIOA->ODR &= ~(1 << 2);
GPIOA->ODR &= ~(1 << 3);
// 3. TIMER config (Usando tu librería)
// PSC = 7 (asumiendo reloj de 8MHz, esto da Ticks de 1us)
timer_init(TIM3, TIMER_MODE_UP, TRIAC_MIN_DELAY_TICKS, 7);
timer_set_interrupt(TIM3, TIMER_IRQ_UPDATE, IRQ_ENABLE);
NVIC_EnableIRQ(TIM3_IRQn); // CMSIS Estándar
// 4. EXTI config (Cruce por cero en PA0)
// Cambia EXTI_IRQ_RISING por EXTI_IRQ_FALLING o EXTI_IRQ_BOTH si tu circuito lo requiere
exti_set_interrupt(0x00, EXTI_IRQ_RISING); // Reemplacé PA0 por su posible valor hexadecimal genérico (Puerto A, Pin 0). Ajusta al enum de tu librería (ej. PIN_PA0) si marca error.
NVIC_EnableIRQ(EXTI0_IRQn); // CMSIS Estándar
// 5. Habilitar interrupciones globales
__enable_irq();
// ==========================================================
// BUCLE PRINCIPAL (Lectura de botones)
// ==========================================================
while (1)
{
// Botón PA3 (Sube el brillo / Reduce el retardo)
if( GPIOA->IDR & (1 << 3) ) // Lectura directa por registro
{
triac_on = 1; // Encender foco
if(firing_delay > TRIAC_MIN_DELAY_TICKS) {
firing_delay -= TRIAC_DELAY_STEP_TICKS;
}
delay_ms(10); // Antirrebote
while( GPIOA->IDR & (1 << 3) ); // Esperar a que sueltes
delay_ms(10);
}
// Botón PA2 (Baja el brillo / Aumenta el retardo)
if( GPIOA->IDR & (1 << 2) ) // Lectura directa por registro
{
if(firing_delay < TRIAC_MAX_DELAY_TICKS) {
firing_delay += TRIAC_DELAY_STEP_TICKS;
}
else {
triac_on = 0; // Apagar foco si llegas al mínimo
firing_delay = TRIAC_MAX_DELAY_TICKS;
}
delay_ms(10); // Antirrebote
while( GPIOA->IDR & (1 << 2) ); // Esperar a que sueltes
delay_ms(10);
}
}
}
// ==========================================================
// RUTINAS DE INTERRUPCIÓN
// ==========================================================
void EXTI0_IRQHandler(void)
{
// Verificamos si la bandera de la línea 0 se activó
if (EXTI->PR & (1 << 0))
{
// Limpiamos la bandera escribiendo un 1
EXTI->PR = (1 << 0);
if (triac_on == 1)
{
TIM3->CR1 &= ~TIM_CR1_CEN; // Detenemos el timer por si estaba corriendo
TIM3->CNT = 0; // Reiniciamos el contador a 0
TIM3->ARR = firing_delay; // Cargamos el nuevo retardo calculado
TIM3->SR &= ~TIM_SR_UIF; // Limpiamos bandera de interrupción por precaución
TIM3->CR1 |= TIM_CR1_CEN; // Arrancamos el timer
}
}
}
void TIM3_IRQHandler(void)
{
// Verificamos si la bandera de Update (desbordamiento) se activó
if (TIM3->SR & TIM_SR_UIF)
{
TIM3->SR &= ~TIM_SR_UIF; // Limpiamos bandera
TIM3->CR1 &= ~TIM_CR1_CEN; // Detenemos el timer hasta el próximo cruce por cero
// Mandar pulso al TRIAC por PA1
GPIOA->ODR |= (1 << 1);
delay_us(10);
GPIOA->ODR &= ~(1 << 1);
}
}