4. ADC采集与电压监测
任务目标
在嵌入式AI MCU上,通过ADC模块采集模拟电压信号,将采集到的原始值转换为实际电压值,并通过串口周期性输出,实现基础电压监测功能。本案例以STM32F407(Cortex-M4内核)为例,使用标准外设库完成ADC单通道采集与串口打印。
硬件与依赖
- MCU:STM32F407VET6(或兼容型号)
- 信号源:电位器(分压0~3.3V)连接至PA1(ADC1通道1)
- 串口:USART2(PA2=TX,PA3=RX),波特率115200
- 开发环境:Keil MDK-ARM 5.x + STM32F4xx标准外设库
关键完整代码
#include "stm32f4xx.h"
#include <stdio.h>
// 重定向fputc到USART2
int fputc(int ch, FILE *f) {
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, (uint8_t)ch);
return ch;
}
// 初始化USART2:115200-8-N-1
void USART2_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// PA2=TX, PA3=RX 复用推挽
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_TX | USART_Mode_RX;
USART_Init(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
}
// 初始化ADC1通道1(PA1),12位分辨率
void ADC1_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// PA1模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_84Cycles);
ADC_Cmd(ADC1, ENABLE);
}
// 读取ADC原始值并转换为电压(mV)
uint16_t ADC_ReadVoltage_mV(void) {
ADC_SoftwareStartConv(ADC1);
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
uint16_t raw = ADC_GetConversionValue(ADC1);
// 3.3V参考电压,12位分辨率:电压(mV) = raw * 3300 / 4095
return (uint16_t)((uint32_t)raw * 3300 / 4095);
}
int main(void) {
USART2_Init();
ADC1_Init();
printf("ADC Voltage Monitor Started\r\n");
while (1) {
uint16_t voltage_mV = ADC_ReadVoltage_mV();
printf("Voltage: %u mV\r\n", voltage_mV);
for (volatile uint32_t i = 0; i < 2000000; i++); // 约1秒延时
}
}
编译与运行
- 在Keil中新建工程,选择芯片型号STM32F407VE,添加上述代码文件。
- 在工程选项中勾选
Use MicroLIB(用于printf重定向),并添加标准外设库源文件路径。 - 编译无误后,通过ST-Link下载到开发板。
- 使用USB转TTL模块连接USART2的TX(PA2)与GND,打开串口助手(115200-8-N-1)。
验证方法
- 旋转电位器改变PA1输入电压,串口助手应周期性打印电压值,数值随电位器转动在0~3300mV范围内变化。
- 用万用表测量PA1引脚电压,与串口打印值对比,误差应在±30mV以内(受参考电压精度影响)。
- 当电位器旋至GND端时,打印值接近0mV;旋至3.3V端时,打印值接近3300mV。
常见问题
- 串口无输出:检查USART2引脚复用配置是否正确,确认MicroLIB已勾选。
- 电压值固定不变:确认PA1已配置为模拟输入,且电位器中间抽头确实连接至PA1。
- 电压值跳变过大:可在ADC采样后增加多次取平均,或检查电源纹波是否过大。