Monitoramento de Tensão com ADC e Watchdog Analógico no STM32

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

Tags: STM32 adc AnalogWatchdog HAL STM32CubeMX

Publicado em 10-23 13:19