Esta solução explora o recurso de watchdog analógico do ADC presente nos microcontroladores STM32 para monitorar uma tensão crítica em tempo real. Quando o valor lido ultrapassa os limites programados, o hardware gera automaticamente uma interrupção, permitindo que o firmware acione alertas sonoros e visuais (buzzer e LED) e transmita informações pelo canal UART. Toda a configuração é realizada graficamente com o STM32CubeMX, enquanto a lógica de aplicação utiliza as HAL Libraries.
Arquitetura do Projeto
Entrada Analógica --> ADC1 (com Watchdog) --> Controle de Alarme
|
+-----------------+-----------------+
| | |
Buzzer (PB0) LED (PB1) UART (PA9/PA10)
Esquema de Hardware
Componentes Principais
| Componente |
Especificação |
Função |
| MCU |
STM32F103C8T6 (Cortex-M3, 72 MHz) |
Controle central |
| Canal ADC |
PA0 (ADC1_IN0) |
Sensor de tensão (0 – 3,3 V) |
| Buzzer |
Ativo 5 V |
Alarme sonoro |
| LED |
Vermelho difuso |
Indicação visual de estado |
| Divisor Resistivo |
10 kΩ + 20 kΩ |
Adequação de 0–10 V para 0–3,3 V |
Conexões Elétricas
| Sinal |
Pino STM32 |
Destino |
Observação |
| Entrada analógica |
PA0 |
Divisor resistivo |
Tensão atenuada |
| Buzzer |
PB0 |
BZ1 |
Acionado em nível alto |
| LED |
PB1 |
LED1 |
Acionado em nível alto |
| TX |
PA9 |
CH340G (USB-UART) |
Depuração |
| RX |
PA10 |
CH340G |
Depuração |
Configuração no STM32CubeMX
Clock
- HSE: 8 MHz → PLL (×9) → SYSCLK 72 MHz
- ADC clock: 12 MHz (PCLK2 dividido por 6)
- APB2: 72 MHz, APB1: 36 MHz
ADC1
- Modo Scan: Desabilitado
- Conversão Contínua: Habilitada
- Alinhamento: à direita
- Tempo de amostragem: 239,5 ciclos
- Watchdog analógico: Single regular channel (canal 0)
- Limiar alto: 3000 (~2,4 V), limiar baixo: 1000 (~0,8 V)
- Interrupção do watchdog e de fim de conversão habilitadas
GPIO
| Pino |
Configuração |
Observação |
| PA0 |
Analog (ADC) |
– |
| PB0 |
GPIO Output Push-Pull |
Buzzer |
| PB1 |
GPIO Output Push-Pull |
LED |
| PA9 |
USART1_TX |
UART TX |
| PA10 |
USART1_RX (pull-up) |
UART RX |
NVIC
ADC1 global interrupt e USART1 global interrupt ativadas, com priroidade média e baixa respectivamente.
Implementação de Software
Inicialização do ADC e Configuração do Watchdog Analógico
// adc.c
#include "adc.h"
ADC_HandleTypeDef hadc1;
void Setup_ADC_Watchdog(void) {
ADC_ChannelConfTypeDef channel = {0};
ADC_AnalogWDGConfTypeDef watchdog = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
HAL_ADC_Init(&hadc1);
channel.Channel = ADC_CHANNEL_0;
channel.Rank = 1;
channel.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &channel);
watchdog.WatchdogMode = ADC_ANALOGWATCHDOG_SINGLE_REG;
watchdog.Channel = ADC_CHANNEL_0;
watchdog.ITMode = ENABLE;
watchdog.HighThreshold = 3000; // ~2.4 V
watchdog.LowThreshold = 1000; // ~0.8 V
HAL_ADC_AnalogWDGConfig(&hadc1, &watchdog);
HAL_ADC_Start_IT(&hadc1);
}
Lógica de Alarme com Tratamento de Estados
// alarm.c
#include "alarm.h"
#include "gpio.h"
#include "usart.h"
typedef enum {
STATUS_NORMAL,
STATUS_HIGH_V,
STATUS_LOW_V
} VoltageStatus;
static VoltageStatus currentStatus = STATUS_NORMAL;
void Alarm_Process(uint16_t rawAdc) {
const uint16_t HI_LIMIT = 3000;
const uint16_t LO_LIMIT = 1000;
if (rawAdc > HI_LIMIT) {
currentStatus = STATUS_HIGH_V;
HAL_GPIO_WritePin(BUZZ_GPIO_Port, BUZZ_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(ALARM_LED_GPIO_Port, ALARM_LED_Pin, GPIO_PIN_SET);
} else if (rawAdc < LO_LIMIT) {
currentStatus = STATUS_LOW_V;
HAL_GPIO_WritePin(BUZZ_GPIO_Port, BUZZ_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(ALARM_LED_GPIO_Port, ALARM_LED_Pin, GPIO_PIN_SET);
} else {
currentStatus = STATUS_NORMAL;
HAL_GPIO_WritePin(BUZZ_GPIO_Port, BUZZ_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(ALARM_LED_GPIO_Port, ALARM_LED_Pin, GPIO_PIN_RESET);
}
char buffer[64];
const char *stateStr = (currentStatus == STATUS_HIGH_V) ? "ALTA" :
(currentStatus == STATUS_LOW_V) ? "BAIXA" : "NORMAL";
float voltage = rawAdc * 3.3f / 4095.0f;
snprintf(buffer, sizeof(buffer), "Estado: %s | ADC: %d (%.2f V)\r\n",
stateStr, rawAdc, voltage);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), 100);
}
Loop Principal e Callbacks
// main.c
#include "main.h"
#include "adc.h"
#include "alarm.h"
static uint32_t periodicTimer = 0;
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
Setup_ADC_Watchdog();
MX_USART1_UART_Init();
while (1) {
if (HAL_GetTick() - periodicTimer > 5000) {
periodicTimer = HAL_GetTick();
const char *msg = "Sistema em execucao...\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 100);
}
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
if (hadc->Instance == ADC1) {
uint16_t value = HAL_ADC_GetValue(hadc);
Alarm_Process(value);
}
}
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc) {
if (hadc->Instance == ADC1) {
uint16_t value = HAL_ADC_GetValue(hadc);
Alarm_Process(value);
}
}
Funcionalidades Avançadas
Classsificação de Alarme em Múltiplos Níveis
void Alarm_Multilevel(uint16_t raw) {
static uint8_t beepCounter = 0;
const uint16_t CRITICAL_HIGH = 3500;
const uint16_t HIGH = 3000;
const uint16_t LOW = 1000;
const uint16_t CRITICAL_LOW = 500;
if (raw > CRITICAL_HIGH) {
Buzzer_Set(1);
LED_Set(1);
UART_SendString("CRITICO: Tensao muito alta!\r\n");
} else if (raw < CRITICAL_LOW) {
Buzzer_Set(1);
LED_Set(1);
UART_SendString("CRITICO: Tensao muito baixa!\r\n");
} else if (raw > HIGH) {
Buzzer_Set((beepCounter % 10) < 5 ? 1 : 0);
LED_Set(1);
beepCounter++;
} else if (raw < LOW) {
Buzzer_Set((beepCounter % 10) < 5 ? 1 : 0);
LED_Set(1);
beepCounter++;
} else {
Buzzer_Set(0);
LED_Set(0);
beepCounter = 0;
}
}
Ajuste Dinâmico dos Limiares
void Adjust_Thresholds(uint16_t reference) {
uint16_t newHigh = reference + (reference / 5); // +20 %
if (newHigh > 4095) newHigh = 4095;
uint16_t newLow = reference - (reference / 5); // -20 %
if (newLow < 0) newLow = 0;
ADC1->HTR = newHigh;
ADC1->LTR = newLow;
// Persistir em memória não volátil se necessário
Store_Thresholds(newHigh, newLow);
}
Filtragem Digital do Sinal
#define FILTER_DEPTH 7
static uint16_t sampleBuf[FILTER_DEPTH];
static uint8_t index = 0;
uint16_t Filter_Value(uint16_t fresh) {
sampleBuf[index] = fresh;
index = (index + 1) % FILTER_DEPTH;
// Ordenação simples para mediana
uint16_t sorted[FILTER_DEPTH];
memcpy(sorted, sampleBuf, sizeof(sampleBuf));
for (int i = 0; i < FILTER_DEPTH - 1; i++) {
for (int j = i + 1; j < FILTER_DEPTH; j++) {
if (sorted[i] > sorted[j]) {
uint16_t tmp = sorted[i];
sorted[i] = sorted[j];
sorted[j] = tmp;
}
}
}
uint16_t median = sorted[FILTER_DEPTH / 2];
// Média móvel
uint32_t sum = 0;
for (int i = 0; i < FILTER_DEPTH; i++) {
sum += sampleBuf[i];
}
uint16_t average = (uint16_t)(sum / FILTER_DEPTH);
// Combinação ponderada (60% média, 40% mediana)
return (uint16_t)((average * 3 + median * 2) / 5);
}
Registro de Eventos em Flash
typedef struct {
uint32_t timestamp;
uint16_t adcValue;
uint8_t status; // 0=normal, 1=alta, 2=baixa
} EventLog;
void Log_Occurrence(uint16_t raw, VoltageStatus st) {
static EventLog history[10];
// Desloca registros
for (int i = 9; i > 0; i--) {
history[i] = history[i - 1];
}
history[0].timestamp = HAL_GetTick();
history[0].adcValue = raw;
history[0].status = (uint8_t)st;
// Simulação de escrita em setor reservado da Flash
Flash_Write_Log(history, sizeof(history));
}
Otimizações do Sistema
Redução de Consumo
void Enter_StopMode(void) {
// Desabilita conversão contínua
hadc1.Init.ContinuousConvMode = DISABLE;
HAL_ADC_Init(&hadc1);
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// Restaura clock e ADC
SystemClock_Config();
hadc1.Init.ContinuousConvMode = ENABLE;
HAL_ADC_Init(&hadc1);
HAL_ADC_Start_IT(&hadc1);
}
Watchdog Independente (IWDG)
IWDG_HandleTypeDef hiwdg;
void IWDG_Setup(void) {
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_32; // 1 kHz LSI
hiwdg.Init.Reload = 1000; // timeout 1 s
HAL_IWDG_Init(&hiwdg);
}
// No loop principal, periodicamente:
HAL_IWDG_Refresh(&hiwdg);
Testes de Validação
| Cenário |
Entrada (V) |
ADC Esperado |
Ação |
| Tensão normal |
1,5 |
~1860 |
LED apagado, buzzer inativo |
| Limite superior |
2,4 |
3000 |
Normal (sem alarme) |
| Alarme alto |
2,5 |
3100 |
LED aceso, buzzer ativo |
| Limite inferior |
0,8 |
1000 |
Normal |
| Alarme baixo |
0,5 |
620 |
LED aceso, buzzer ativo |
| Recuperação |
Volta a 1,5 V |
1860 |
Alarme desligado automaticamente |
Os testes foram realizados com fonte ajustável e multímetro digital, veirficando também as mensagens na console serial.
Referências
- RM0008 – Manual de Referência STM32F10xxx
- AN3116 – Utilização do Watchdog Analógico no STM32
- STM32CubeMX 6.9.0 e STM32CubeIDE 1.13.0