#include <stdint.h>
// =================================================================
// 1. MAPA DE MEMÓRIA (Endereços Base)
// =================================================================
#define ADDR_RCC_BASE 0x40021000
#define ADDR_GPIOA_BASE 0x50000000
#define ADDR_EXTI_BASE 0x40010400
#define ADDR_SYSCFG_BASE 0x40010000
#define ADDR_TIM21_BASE 0x40010800
// =================================================================
// 2. DEFINIÇÃO DOS REGISTRADORES
// =================================================================
// Controle do NVIC e System Control Block (Cortex-M0+)
#define REG_NVIC_ISER (*(volatile uint32_t*)(0xE000E100))
#define REG_SCB_VTOR (*(volatile uint32_t*)(0xE000ED08))
// Barramentos de Clock (RCC)
#define REG_RCC_IOPENR (*(volatile uint32_t*)(ADDR_RCC_BASE + 0x2C))
#define REG_RCC_APB2ENR (*(volatile uint32_t*)(ADDR_RCC_BASE + 0x34))
// Controle das Portas (GPIOA)
#define REG_GPIOA_MODER (*(volatile uint32_t*)(ADDR_GPIOA_BASE + 0x00))
#define REG_GPIOA_PUPDR (*(volatile uint32_t*)(ADDR_GPIOA_BASE + 0x0C))
#define REG_GPIOA_ODR (*(volatile uint32_t*)(ADDR_GPIOA_BASE + 0x14))
// Interrupções Externas (EXTI e SYSCFG)
#define REG_EXTI_IMR (*(volatile uint32_t*)(ADDR_EXTI_BASE + 0x00))
#define REG_EXTI_FTSR (*(volatile uint32_t*)(ADDR_EXTI_BASE + 0x0C))
#define REG_EXTI_PR (*(volatile uint32_t*)(ADDR_EXTI_BASE + 0x14))
#define REG_SYSCFG_EXTICR1 (*(volatile uint32_t*)(ADDR_SYSCFG_BASE + 0x08))
// Hardware Timer 21 (TIM21)
#define REG_TIM21_CR1 (*(volatile uint32_t*)(ADDR_TIM21_BASE + 0x00))
#define REG_TIM21_DIER (*(volatile uint32_t*)(ADDR_TIM21_BASE + 0x0C))
#define REG_TIM21_SR (*(volatile uint32_t*)(ADDR_TIM21_BASE + 0x10))
#define REG_TIM21_PSC (*(volatile uint32_t*)(ADDR_TIM21_BASE + 0x28))
#define REG_TIM21_ARR (*(volatile uint32_t*)(ADDR_TIM21_BASE + 0x2C))
// =================================================================
// 3. VARIÁVEIS GLOBAIS DE ESTADO (Voláteis)
// =================================================================
volatile uint8_t flag_contando = 1; // Controla Pause/Play
volatile uint32_t tick_absoluto_ms = 0; // Relógio geral do sistema em ms
volatile uint16_t acumulador_1seg = 0; // Soma os milissegundos até dar 1s
volatile uint8_t valor_saida_leds = 0; // Armazena a contagem atual (0 a 15)
volatile uint32_t tick_ultimo_clique = 0; // Utilizado para o debounce do botão
// Matriz na RAM para realocar os vetores de interrupção (Fora da Flash)
uint32_t vetor_interrupcoes_ram[48] __attribute__((aligned(256)));
// =================================================================
// 4. TRATADORES DE INTERRUPÇÃO (ISRs)
// =================================================================
// Rotina executada ao pressionar o botão (Pino PA0 / EXTI0)
extern "C" void isr_botao_pa0(void) {
// Checa se a interrupção realmente veio da Linha 0
if (REG_EXTI_PR & (1 << 0)) {
REG_EXTI_PR |= (1 << 0); // No hardware STM32, a limpeza exige escrever 1
// Estratégia de Debounce: Aceita cliques separados por pelo menos 200ms
if ((tick_absoluto_ms - tick_ultimo_clique) > 200) {
flag_contando = !flag_contando; // Alterna o estado (Inicia/Pausa)
tick_ultimo_clique = tick_absoluto_ms;
}
}
}
// Rotina executada pelo Timer a cada exato 1 milissegundo
extern "C" void isr_tempo_tim21(void) {
if (REG_TIM21_SR & (1 << 0)) {
REG_TIM21_SR &= ~(1 << 0); // Abaixa a flag de update por software
tick_absoluto_ms++; // Alimenta o relógio do sistema
// Só atualiza os LEDs se o sistema não estiver em "Pause"
if (flag_contando) {
acumulador_1seg++;
// Atingiu 1000 milissegundos = 1 Segundo
if (acumulador_1seg >= 1000) {
acumulador_1seg = 0;
valor_saida_leds++;
// Restringe a contagem ao limite de 4 bits (15)
if (valor_saida_leds > 15) {
valor_saida_leds = 0;
}
}
}
}
}
// =================================================================
// 5. INICIALIZAÇÃO GERAL (Setup)
// =================================================================
void setup() {
// A. Bypass do Framework Arduino (Redirecionando vetor para a RAM)
uint32_t *vetores_flash = (uint32_t*)0x08000000;
for (int i = 0; i < 48; i++) {
vetor_interrupcoes_ram[i] = vetores_flash[i];
}
// Assumimos a posse dessas duas interrupções específicas:
vetor_interrupcoes_ram[16 + 5] = (uint32_t)isr_botao_pa0;
vetor_interrupcoes_ram[16 + 20] = (uint32_t)isr_tempo_tim21;
REG_SCB_VTOR = (uint32_t)vetor_interrupcoes_ram;
// B. Ativar a Energia (Clock) dos periféricos
REG_RCC_IOPENR |= (1 << 0); // Clock do GPIOA
REG_RCC_APB2ENR |= (1 << 0) | (1 << 2); // Clock do SYSCFG e do TIM21
// C. Configurar Botão - PA0 (Modo Entrada, com Pull-up interno)
REG_GPIOA_MODER &= ~(3U << 0);
REG_GPIOA_PUPDR &= ~(3U << 0);
REG_GPIOA_PUPDR |= (1U << 0);
// D. Configurar LEDs - PA1, PA3, PA4 e PA5 (Modo Saída)
// Limpamos os bits duplos referentes às posições de cada pino (Multiplicador x2)
REG_GPIOA_MODER &= ~((3U << (1*2)) | (3U << (3*2)) | (3U << (4*2)) | (3U << (5*2)));
// Injetamos o valor binário '01' (Saída) nessas posições
REG_GPIOA_MODER |= ((1U << (1*2)) | (1U << (3*2)) | (1U << (4*2)) | (1U << (5*2)));
// E. Configurar Mapeamento do EXTI (Interrupção Externa)
REG_SYSCFG_EXTICR1 &= ~(0x0FU << 0); // Associa a porta A ao EXTI0
REG_EXTI_IMR |= (1 << 0); // Tira a máscara de bloqueio
REG_EXTI_FTSR |= (1 << 0); // Configura para disparo em Borda de Descida
// F. Configurar o Timer 21 para disparar a cada 1ms (Base de 32MHz)
REG_TIM21_PSC = 32 - 1; // Prescaler divide o clock base para 1 MHz (1 tick por microsegundo)
REG_TIM21_ARR = 1000 - 1; // Auto-reload conta mil microsegundos para gerar 1 ms
REG_TIM21_DIER |= (1 << 0); // Ativa solicitação de interrupção
REG_TIM21_CR1 |= (1 << 0); // Habilita a contagem do Timer
// G. Acordar as linhas no Controlador de Interrupções do Processador (NVIC)
REG_NVIC_ISER |= (1 << 5) | (1 << 20);
}
// =================================================================
// 6. LOOP PRINCIPAL
// =================================================================
void loop() {
// Lê o estado atual físico dos pinos
uint32_t pinos_atuais = REG_GPIOA_ODR;
// Zera APENAS as saídas dos nossos 4 LEDs (garante a segurança dos outros pinos da placa)
pinos_atuais &= ~((1 << 1) | (1 << 3) | (1 << 4) | (1 << 5));
// Faz a varredura dos bits da variável do contador e atribui aos pinos fisicos correspondentes
if (valor_saida_leds & 0x01) pinos_atuais |= (1 << 1); // Extrai Bit 0 -> Atribui ao Pino PA1
if (valor_saida_leds & 0x02) pinos_atuais |= (1 << 3); // Extrai Bit 1 -> Atribui ao Pino PA3
if (valor_saida_leds & 0x04) pinos_atuais |= (1 << 4); // Extrai Bit 2 -> Atribui ao Pino PA4
if (valor_saida_leds & 0x08) pinos_atuais |= (1 << 5); // Extrai Bit 3 -> Atribui ao Pino PA5
// Atualiza o registrador de uma só vez para evitar piscas incorretos (Ghosting)
REG_GPIOA_ODR = pinos_atuais;
}Loading
st-nucleo-l031k6
st-nucleo-l031k6