#include <Arduino.h>
#include <string.h>
#include <stdio.h> // Necessário para sscanf
#define TAMANHO_BUFFER 32
char cmd_buffer[TAMANHO_BUFFER];
uint8_t buf_pos = 0;
uint32_t intervalo_pisca = 0;
uint32_t relogio_pisca = 0;
void iniciar_hardware_serial() {
/* 1. Configuração do Clock (HSI a 16 MHz) */
RCC->CR |= RCC_CR_HSION;
while((RCC->CR & RCC_CR_HSIRDY) == 0); // Aguarda estabilização
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_HSI;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI);
/* 2. Habilitação dos Clocks de Periféricos (Porta A, Porta B e USART2) */
RCC->IOPENR |= (RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN);
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
/* 3. Configuração do Pino do LED (PB3) como Saída Digital */
GPIOB->MODER &= ~(3U << (3 * 2)); // Limpa os bits referentes ao pino 3
GPIOB->MODER |= (1U << (3 * 2)); // Define como saída de propósito geral (01)
/* 4. Configuração dos Pinos TX (PA2) e RX (PA15) para Função Alternativa */
GPIOA->MODER &= ~(GPIO_MODER_MODE2 | GPIO_MODER_MODE15);
GPIOA->MODER |= (GPIO_MODER_MODE2_1 | GPIO_MODER_MODE15_1); // Modo AF (10)
// Mapeamento da Função Alternativa 4 (AF4) para a USART2
GPIOA->AFR[0] = (GPIOA->AFR[0] & ~(0xF << (2 * 4))) | (4 << (2 * 4));
GPIOA->AFR[1] = (GPIOA->AFR[1] & ~(0xF << ((15 - 8) * 4))) | (4 << ((15 - 8) * 4));
/* 5. Configuração da USART2 (115200 bps, 8 Data bits, Sem Paridade, 1 Stop bit) */
USART2->BRR = 139; // Cálculo: 16MHz / 115200
USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // Ativa Transmissor e Receptor
}
void enviar_byte(char dado) {
// Segura a execução até que o registrador de transmissão (TXE) esteja livre
while ((USART2->ISR & USART_ISR_TXE) == 0) {
// Aguardando liberação do hardware...
}
USART2->TDR = dado;
}
void enviar_texto(const char *mensagem) {
int i = 0;
while (mensagem[i] != '\0') {
enviar_byte(mensagem[i]);
i++;
}
}
void interpretar_mensagem() {
if (!strcmp(cmd_buffer, "HELP")) {
enviar_texto("Comandos Suportados: HELP, ON, OFF, TOGGLE, STATUS, BLINK <ms>\r\n");
}
else if (!strcmp(cmd_buffer, "ON")) {
GPIOB->BSRR = GPIO_BSRR_BS_3; // Liga pino via Bit Set/Reset Register
intervalo_pisca = 0;
enviar_texto("-> O LED foi ativado.\r\n");
}
else if (!strcmp(cmd_buffer, "OFF")) {
GPIOB->BSRR = GPIO_BSRR_BR_3; // Desliga pino via Bit Set/Reset Register
intervalo_pisca = 0;
enviar_texto("-> O LED foi desativado.\r\n");
}
else if (!strcmp(cmd_buffer, "TOGGLE")) {
GPIOB->ODR ^= (1 << 3); // Inverte estado do pino via porta de dados
intervalo_pisca = 0;
enviar_texto("-> Estado do LED invertido.\r\n");
}
else if (!strcmp(cmd_buffer, "STATUS")) {
if (GPIOB->ODR & (1 << 3)) {
enviar_texto("-> Sistema atual: LIGADO\r\n");
} else {
enviar_texto("-> Sistema atual: DESLIGADO\r\n");
}
}
else if (strncmp(cmd_buffer, "BLINK ", 6) == 0) {
uint32_t param_tempo = 0;
// Utiliza sscanf para extrair o valor inteiro de forma limpa
if (sscanf(cmd_buffer, "BLINK %lu", ¶m_tempo) == 1 && param_tempo > 0) {
intervalo_pisca = param_tempo;
enviar_texto("-> Modo temporizado iniciado.\r\n");
} else {
enviar_texto("-> Falha: Intervalo invalido. Tente 'BLINK 1000'.\r\n");
}
}
else if (buf_pos > 0) {
enviar_texto("-> Comando nao reconhecido. Envie HELP.\r\n");
}
}
void setup() {
iniciar_hardware_serial();
enviar_texto("=== Console de Supervisao STM32 ===\r\nEnvie HELP para iniciar.\r\n");
}
void loop() {
/* 1. Rotina de escuta da porta Serial (Interrogacao do bit RXNE) */
if (USART2->ISR & USART_ISR_RXNE) {
char caractere = (char)USART2->RDR;
// Loopback para o terminal do usuario
enviar_byte(caractere);
// Identifica fim da instrução (Enter)
if (caractere == '\r' || caractere == '\n') {
enviar_texto("\n");
cmd_buffer[buf_pos] = '\0'; // Finaliza o vetor de caracteres
interpretar_mensagem();
buf_pos = 0; // Prepara para a proxima string
} else {
// Evita estouro de buffer (Buffer Overflow)
if (buf_pos < (TAMANHO_BUFFER - 1)) {
cmd_buffer[buf_pos] = caractere;
buf_pos++;
}
}
}
/* 2. Processamento do evento BLINK sem travar o microcontrolador */
if (intervalo_pisca > 0) {
uint32_t tempo_atual = millis();
if ((tempo_atual - relogio_pisca) >= intervalo_pisca) {
relogio_pisca = tempo_atual;
GPIOB->ODR ^= (1 << 3); // Comuta o LED
}
}
}