Visão Geral do Recurso
Em sistemas embarcados, a funcionalidade de um botão pode ser estendida além do simples toque, permitindo a detecção de pressionamentos contínuos para acionar sequências de comandos com um padrão de tempo específico. Este artigo detalha a implementação de um sistema que detecta quando um botão em um microcontrolador STM32F4 é mantido pressionado, e então inicia uma sequência de disparos que acelera gradualmente até atingir uma velocidade constante. A biblioteca HAL da STMicroelectronics será utilizada para gerenciar as interações com o hardware.
Componentes Necessários
Para construir este exemplo, você percisará dos seguintes itens:
- Uma placa de desenvolvimento STM32F4 (com um microcontrolador STM32F407VGT6 ou equivalente).
- Um botão tátil (push-button) independente.
- Um resistor (tipicamente entre 220Ω e 330Ω, embora não estritamente necessário se usar pull-up interno).
- Fios de conexão.
Configuração Física do Botão
O botão será conectado ao pino PB0 do microcontrolador STM32F4. Para uma configuração simples e eficaz, conecte um terminal do botão ao pino PB0 e o outro terminal ao terra (GND). Utilizaremos o resistor de pull-up interno do STM32F4, fazendo com que o pino PB0 leia um estaddo LÓGICO ALTO (HIGH) quando o botão estiver solto e LÓGICO BAIXO (LOW) quando pressionado.
Configuração do GPIO no STM32F4
O pino PB0 será configurado como uma entrada digital com pull-up interno. Esta configuração é ideal para detecção de botão, pois garante que o pino não fique flutuante quando o botão está solto, proporcionando um estado elétrico bem definido.
O primeiro passo é habilitar o clock para a porta GPIO B, onde o pino PB0 está localizado, e em seguida configurar os parâmetros específicos do pino usando a estrutura GPIO_InitTypeDef.
/* Habilita o clock para a porta B */
__HAL_RCC_GPIOB_CLK_ENABLE();
/* Declara e inicializa a estrutura de configuração GPIO */
GPIO_InitTypeDef cfg_gpio = {0};
/* Define o pino a ser configurado (PB0) */
cfg_gpio.Pin = GPIO_PIN_0;
/* Configura como entrada */
cfg_gpio.Mode = GPIO_MODE_INPUT;
/* Ativa o resistor de pull-up interno */
cfg_gpio.Pull = GPIO_PULLUP;
/* Define a velocidade do pino (baixa para botões, suficiente) */
cfg_gpio.Speed = GPIO_SPEED_FREQ_LOW;
/* Aplica as configurações ao GPIOB */
HAL_GPIO_Init(GPIOB, &cfg_gpio);
Estrutura do Módulo de Controle do Botão
Para manter o código modular e fcailitar a manutenção, a lógica de detecção e controle do botão será encapsulada em arquivos de cabeçalho (.h) e de implementação (.c). Sugerimos criar um diretório App/Input e, dentro dele, os arquivos input_button.h e input_button.c. É crucial adicionar o caminho para este diretório nas configurações de inclusão do seu projeto (por exemplo, no Keil MDK ou STM32CubeIDE).
Arquivo: input_button.h
Este arquivo define as constantes de temporização para o anti-rebatimento e para os limites de tempo de pressionamento, além de declarar os protótipos das funções que compõem o módulo do botão.
#ifndef INPUT_BUTTON_H
#define INPUT_BUTTON_H
#include "stm32f4xx_hal.h"
/* Tempos em milissegundos para detecção e controle */
#define TEMPO_ANTI_REBOTE 20 // Duração da janela de anti-rebatimento
#define LIMITE_PRESSAO_CURTA 500 // Duração máxima para considerar um toque curto
#define LIMITE_PRESSAO_LONGA 1500 // Duração mínima para considerar um toque longo
#define PASSO_ACELERACAO 100 // Quantidade de milissegundos a subtrair do intervalo
#define INTERVALO_MINIMO 200 // Intervalo mínimo de disparo (velocidade constante)
/* Enumeração para os estados de toque do botão */
typedef enum {
BTN_NENHUM_TOQUE = 0,
BTN_TOQUE_CURTO,
BTN_TOQUE_LONGO
} TipoToqueBotao;
/**
* @brief Inicializa o pino GPIO configurado para o botão.
*/
void Botao_ConfigurarGPIO(void);
/**
* @brief Avalia o estado físico do botão e retorna o tipo de toque detectado.
* @return Um valor da enumeração TipoToqueBotao.
*/
TipoToqueBotao Botao_ObterEstado(void);
/**
* @brief Gerencia a lógica de disparo acelerado e contínuo para toques longos.
* Esta função deve ser chamada repetidamente no loop principal.
*/
void Botao_GerenciarDisparoContinuo(void);
#endif /* INPUT_BUTTON_H */
Arquivo: input_button.c
Este arquivo contém a implementação das funções declaradas no cabeçalho, incluindo a lógica para anti-rebatimento, detecção de toque curto e longo, e o mecanismo de disparo acelerado/contínuo.
#include "input_button.h"
/* Variáveis estáticas para rastreamento de estado interno do botão */
static uint32_t momento_pressionamento = 0; // Timestamp do início do pressionamento
static uint8_t estado_interno_botao = 0; // 0: Solto, 1: Pressionado (curto/indefinido), 2: Pressionado (longo)
static uint32_t ultimo_disparo_continuo = 0; // Último timestamp de um disparo contínuo
static uint32_t intervalo_disparo_atual = 0; // Intervalo de tempo entre os disparos contínuos
/**
* @brief Implementa a configuração GPIO do botão.
*/
void Botao_ConfigurarGPIO(void) {
GPIO_InitTypeDef configuracao_gpio = {0};
__HAL_RCC_GPIOB_CLK_ENABLE(); // Habilita o clock para a porta B
configuracao_gpio.Pin = GPIO_PIN_0;
configuracao_gpio.Mode = GPIO_MODE_INPUT;
configuracao_gpio.Pull = GPIO_PULLUP; // Ativa pull-up interno
configuracao_gpio.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &configacao_gpio);
}
/**
* @brief Analisa o estado físico do botão e retorna o tipo de toque.
* Incorpora um mecanismo de anti-rebatimento.
* @return TipoToqueBotao (BTN_NENHUM_TOQUE, BTN_TOQUE_CURTO, BTN_TOQUE_LONGO).
*/
TipoToqueBotao Botao_ObterEstado(void) {
// Implementação de anti-rebatimento simples
static uint32_t debounce_timer = 0;
static GPIO_PinState ultimo_estado_fisico = GPIO_PIN_SET; // Botão inicialmente solto
GPIO_PinState estado_atual_fisico = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
if (estado_atual_fisico != ultimo_estado_fisico) {
debounce_timer = HAL_GetTick();
}
ultimo_estado_fisico = estado_atual_fisico;
// Se ainda estamos na janela de anti-rebatimento, ignorar
if ((HAL_GetTick() - debounce_timer) < TEMPO_ANTI_REBOTE) {
return BTN_NENHUM_TOQUE;
}
if (estado_atual_fisico == GPIO_PIN_RESET) { // Botão Pressionado (nível baixo)
if (estado_interno_botao == 0) {
momento_pressionamento = HAL_GetTick();
estado_interno_botao = 1; // Marca como "pressionado, aguardando identificação"
}
uint32_t duracao_pressionamento = HAL_GetTick() - momento_pressionamento;
if (duracao_pressionamento >= LIMITE_PRESSAO_LONGA) {
estado_interno_botao = 2; // Identificado como toque longo
return BTN_TOQUE_LONGO;
} else if (duracao_pressionamento >= LIMITE_PRESSAO_CURTA) {
// Detecta toque curto APENAS UMA VEZ enquanto ainda não é longo
if (estado_interno_botao == 1) {
estado_interno_botao = 0; // Reseta para detectar novo toque curto após liberação
return BTN_TOQUE_CURTO;
}
}
} else { // Botão Solto (nível alto)
estado_interno_botao = 0; // Reseta o estado interno ao liberar
return BTN_NENHUM_TOQUE;
}
return BTN_NENHUM_TOQUE; // Estado padrão
}
/**
* @brief Gerencia a lógica de disparo acelerado para toques longos.
* Deve ser chamada frequentemente (e.g., no loop principal) para funcionar.
*/
void Botao_GerenciarDisparoContinuo(void) {
if (estado_interno_botao == 2) { // Apenas se o botão está em estado de toque longo
uint32_t tempo_atual = HAL_GetTick();
// Inicializa o intervalo de disparo na primeira vez que o toque longo é detectado
if (intervalo_disparo_atual == 0) {
intervalo_disparo_atual = LIMITE_PRESSAO_CURTA; // Define um intervalo inicial
ultimo_disparo_continuo = tempo_atual;
// <-- ADICIONE A AÇÃO DO PRIMEIRO DISPARO AQUI (opcional) -->
// Exemplo: HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12); // Toggle LED
}
// Verifica se é hora de um novo disparo
if (tempo_atual - ultimo_disparo_continuo >= intervalo_disparo_atual) {
ultimo_disparo_continuo = tempo_atual;
// <-- ADICIONE A AÇÃO DE DISPARO AQUI -->
// Exemplo: HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12); // Toggle LED azul no STM32F4-Discovery
// Lógica de aceleração: diminui o intervalo até atingir o mínimo
if (intervalo_disparo_atual > INTERVALO_MINIMO) {
intervalo_disparo_atual -= PASSO_ACELERACAO;
if (intervalo_disparo_atual < INTERVALO_MINIMO) {
intervalo_disparo_atual = INTERVALO_MINIMO;
}
}
}
} else {
// Se o botão não está em toque longo, reseta o intervalo de disparo
intervalo_disparo_atual = 0;
}
}
Implementação no Loop Principal (main.c)
No arquivo main.c, após as inicializações do sistema e do clock, as funções do módulo do botão devem ser chamadas. A função Botao_ObterEstado() deve ser chamada repetidamente para monitorar o estado do botão, e Botao_GerenciarDisparoContinuo() deve ser acionada quando um toque longo for detectado.
#include "stm32f4xx_hal.h"
#include "input_button.h" // Inclui o módulo do botão
/* Assume-se que SystemClock_Config() está definida em outro lugar ou gerada pelo CubeMX */
extern void SystemClock_Config(void);
int main(void) {
/* Inicializa o subsistema HAL */
HAL_Init();
/* Configura o clock do sistema */
SystemClock_Config();
/* Configura o GPIO para o botão */
Botao_ConfigurarGPIO();
// <-- ADICIONE AQUI OUTRAS INICIALIZAÇÕES DE PERIFÉRICOS, SE NECESSÁRIO -->
// Exemplo: Inicialização de LEDs, UART, etc.
// __HAL_RCC_GPIOD_CLK_ENABLE();
// GPIO_InitTypeDef LedGPIO;
// LedGPIO.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
// LedGPIO.Mode = GPIO_MODE_OUTPUT_PP;
// LedGPIO.Pull = GPIO_NOPULL;
// LedGPIO.Speed = GPIO_SPEED_FREQ_LOW;
// HAL_GPIO_Init(GPIOD, &LedGPIO);
while (1) {
/* Obtém o tipo de toque atual do botão */
TipoToqueBotao tipo_toque = Botao_ObterEstado();
switch (tipo_toque) {
case BTN_TOQUE_CURTO:
/* Ação a ser executada em um toque curto */
// Exemplo: HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13); // Toggle LED azul
break;
case BTN_TOQUE_LONGO:
/* Ação a ser executada quando o botão é mantido pressionado (toque longo) */
Botao_GerenciarDisparoContinuo(); // Lida com a aceleração e disparo contínuo
break;
case BTN_NENHUM_TOQUE:
default:
/* Nenhuma ação necessária ou estado de botão não relevante */
break;
}
}
}