#include <stdint.h>
#include <stdbool.h>
// Definição dos modos da máquina de estados
typedef enum {
M_BRANCO_DIM = 0,
M_BRANCO_MAX,
M_VERMELHO,
M_VERDE,
M_AZUL,
M_AMARELO,
M_CIANO,
M_MAGENTA,
M_FADE_AUTO,
TOTAL_MODOS // Guarda automaticamente a quantidade de modos (9)
} EstadoRGB_t;
EstadoRGB_t modo_atual = M_BRANCO_DIM;
// =========================================================================
// INICIALIZAÇÃO DE HARDWARE
// =========================================================================
void setup() {
// 1. Configuração do Clock do Sistema
// Ativa clock das portas GPIOA e GPIOB, e do TIM2
RCC->IOPENR |= (3 << 0); // Bits 0 e 1 (GPIOAEN e GPIOBEN)
RCC->APB1ENR |= (1 << 0); // Bit 0 (TIM2EN)
// 2. Configuração dos Pinos (GPIOA 0, 1, 2) para PWM
// Zera os bits de modo e seta para 0b10 (Alternate Function)
GPIOA->MODER &= ~(0x3F); // Limpa bits 0 a 5
GPIOA->MODER |= (0x2A); // Seta PA0, PA1, PA2 para AF (10 10 10)
// Configura a Função Alternativa (AF2 = TIM2) nos 3 pinos
GPIOA->AFR[0] &= ~(0xFFF); // Limpa nibbles 0, 1 e 2
GPIOA->AFR[0] |= (0x222); // AF2 para PA0, PA1 e PA2
// 3. Configuração do Pino do Botão (GPIOB 0)
// Modo de Entrada (00) com resistor de Pull-Up ativado (01)
GPIOB->MODER &= ~(3 << 0);
GPIOB->PUPDR &= ~(3 << 0);
GPIOB->PUPDR |= (1 << 0);
// 4. Configuração do Periférico PWM (Timer 2)
// Frequência alvo = 1 kHz (Base clock = 32 MHz)
TIM2->PSC = 31; // Prescaler: 32 MHz / (31 + 1) = 1 MHz
TIM2->ARR = 999; // Auto-Reload: 1 MHz / (999 + 1) = 1 kHz
// Configura Canais 1, 2 e 3 como PWM Modo 1 e habilita Preload
// CCMR1 gerencia CH1 e CH2, CCMR2 gerencia CH3
TIM2->CCMR1 = (0x68 << 8) | (0x68 << 0); // CH2 e CH1
TIM2->CCMR2 = (0x68 << 0); // CH3
// Habilita as saídas físicas (Capture/Compare Enable)
TIM2->CCER |= (1 << 8) | (1 << 4) | (1 << 0);
// Dispara a contagem do Timer
TIM2->CR1 |= (1 << 0);
}
// =========================================================================
// FUNÇÕES AUXILIARES
// =========================================================================
// Atualiza o duty cycle (0 a 1000) de cada canal de cor
void gravar_cores(uint16_t red, uint16_t green, uint16_t blue) {
TIM2->CCR1 = red;
TIM2->CCR2 = green;
TIM2->CCR3 = blue;
}
// Filtro de Debounce com detecção de borda de descida
bool botao_pressionado() {
static uint32_t t_ultimo_estado = 0;
static bool estado_anterior = true;
bool pressionado_agora = false;
// Leitura direta do bit 0 (0 = GND/Apertado)
bool leitura_atual = (GPIOB->IDR & 0x01);
if (leitura_atual != estado_anterior) {
if ((millis() - t_ultimo_estado) > 60) { // Delay de segurança
if (leitura_atual == false) { // Transição de HIGH para LOW
pressionado_agora = true;
}
estado_anterior = leitura_atual;
t_ultimo_estado = millis();
}
}
return pressionado_agora;
}
// =========================================================================
// LOOP PRINCIPAL
// =========================================================================
void loop() {
// Escuta a entrada do usuário
if (botao_pressionado()) {
modo_atual = (EstadoRGB_t)((modo_atual + 1) % TOTAL_MODOS);
}
// Variáveis estáticas para o modo Fade (Efeito de Rampa)
static uint32_t tick_animacao = 0;
static uint16_t rgb_fade[3] = {1000, 0, 0};
static uint8_t fase = 0;
// Seletor de Modos Visuais
switch (modo_atual) {
case M_BRANCO_DIM: gravar_cores(100, 100, 100); break;
case M_BRANCO_MAX: gravar_cores(1000, 1000, 1000); break;
case M_VERMELHO: gravar_cores(1000, 0, 0); break;
case M_VERDE: gravar_cores(0, 1000, 0); break;
case M_AZUL: gravar_cores(0, 0, 1000); break;
case M_AMARELO: gravar_cores(1000, 1000, 0); break;
case M_CIANO: gravar_cores(0, 1000, 1000); break;
case M_MAGENTA: gravar_cores(1000, 0, 1000); break;
case M_FADE_AUTO:
if (millis() - tick_animacao >= 3) {
tick_animacao = millis();
// Lógica elegante de transição HSL -> RGB usando Array e índices
uint8_t cor_sobe = (fase / 2 + 1) % 3;
uint8_t cor_desce = (fase / 2) % 3;
if (fase % 2 == 0) {
rgb_fade[cor_sobe]++;
if (rgb_fade[cor_sobe] == 1000) fase++;
} else {
rgb_fade[cor_desce]--;
if (rgb_fade[cor_desce] == 0) fase++;
}
if (fase > 5) fase = 0; // Reinicia o ciclo
gravar_cores(rgb_fade[0], rgb_fade[1], rgb_fade[2]);
}
break;
}
}