编辑
一、代码原理解析
这段代码围绕 “STM32 中 ADC 数据采集、整数与小数计算及串口输出” 展开,核心是数据类型的使用(unsigned int/signed int/float )、ADC 数值转换及串口打印,拆解如下:
1. 变量类型与用途
- unsigned int AD;:
无符号整型变量,用于存储 ADC 原始采样值(ADC 通常输出 12 位、10 位等无符号数据,用 unsigned int 适配其范围 )。 - signed int ADC_ConvertedValueLoca;:
有符号整型变量,用于存储整数形式的转换结果(如电压值按比例换算后的整数部分,或直接放大后的整数值 )。 - float ADC_ConvertedValueLocal;:
单精度浮点型变量,用于存储带小数的精确转换结果(如实际电压值,包含小数部分 )。
2. ADC 数据采集与转换
AD = HAL_ADC_GetValue(&hadc3);
- 功能:调用 STM32 HAL 库函数 HAL_ADC_GetValue ,从 hadc3 对应的 ADC 通道读取原始采样值,存入 AD 。假设 ADC 是 12 位分辨率,AD 的范围是 0~4095(对应电压范围 0~3.3V ,需结合硬件电路 )。
ADC_ConvertedValueLoca = AD * 3.3 / 4096;
- 原理:将 ADC 原始值(AD )转换为整数形式的电压值(或比例值 )。公式 AD * 3.3 / 4096 的意义是: 3.3 是参考电压(假设 ADC 参考电压为 3.3V )。 4096 是 12 位 ADC 的满量程值(2^12 = 4096 )。 计算结果本应是带小数的电压值(如 1.234V ),但因存储到 signed int 变量中,小数部分会被截断,只保留整数部分(如 1 )。
ADC_ConvertedValueLocal = (double)ADC_value * 3.3 / 4096;
- 原理:将 ADC 原始值(ADC_value ,需与 AD 逻辑一致 )转换为浮点型电压值。通过 (double) 强制类型转换,让计算过程在浮点环境下执行,保留小数部分,结果更精确(如 1.234567 )。
3. 串口输出(sprintf + HAL_UART_Transmit )
sprintf((char*)ch1, "AD:%5d", AD);
ch1[18] = '\r';
ch1[19] = '\n';
HAL_UART_Transmit(&huart1, ch1, sizeof(ch1), 0x10);
- sprintf 功能:将 AD 的值按格式 AD:%5d 写入字符串数组 ch1 。%5d 表示以 5 个字符宽度输出整数,不足补空格。
- 手动添加换行符:ch1[18] = '\r'; ch1[19] = '\n'; 是在字符串指定位置添加回车(\r )和换行(\n ),让串口输出的内容自动换行,符合串口调试习惯。
- HAL_UART_Transmit:调用 STM32 HAL 库函数,将 ch1 中的数据通过 huart1 对应的串口发送出去,sizeof(ch1) 是发送数据长度,0x10 是超时时间(可根据需求调整 )。
4. 整数与浮点输出对比
- 整数输出:
- sprintf((char*)ch1, "AD:%10d", ADC_ConvertedValueLoca);
- 因 ADC_ConvertedValueLoca 是 signed int ,存储的是截断后的整数(如 1 ),串口输出为整数形式(如 AD: 1 )。
- 浮点输出:
- printf("计算得出电压值 = %f V \r\n", ADC_ConvertedValueLocal);
- ADC_ConvertedValueLocal 是 float 类型,%f 格式符会输出带小数的数值(如 计算得出电压值 = 1.234567 V ),保留小数精度。
二、应用方法与场景
这种 ADC 数据采集、类型转换及串口输出的代码,常见于嵌入式系统的模拟量采集与调试场景(如电压、温度、传感器信号采集 ),以下说明典型用法和扩展思路:
1. 典型应用场景(电压采集与调试 )
在 STM32 项目中,采集外部电压并通过串口实时打印,流程如下:
#include "stm32f4xx_hal.h"
#include <stdio.h>
#include <string.h>
ADC_HandleTypeDef hadc3;
UART_HandleTypeDef huart1;
char ch1[20]; // 假设串口发送缓冲区大小为 20
int main(void) {
// 1. 初始化 ADC、UART(需调用 HAL_ADC_Init、HAL_UART_Init 等 )
HAL_Init();
MX_ADC3_Init();
MX_USART1_UART_Init();
unsigned int AD;
signed int ADC_ConvertedValueLoca;
float ADC_ConvertedValueLocal;
while (1) {
// 2. 采集 ADC 原始值
AD = HAL_ADC_GetValue(&hadc3);
// 3. 转换为整数形式(截断小数 )
ADC_ConvertedValueLoca = AD * 3.3 / 4096;
// 4. 串口输出原始值(整数 )
memset(ch1, 0, sizeof(ch1)); // 清空缓冲区
sprintf((char*)ch1, "AD:%5d", AD);
ch1[18] = '\r';
ch1[19] = '\n';
HAL_UART_Transmit(&huart1, ch1, sizeof(ch1), 0x10);
HAL_Delay(1000);
// 5. 串口输出整数转换值
memset(ch1, 0, sizeof(ch1));
sprintf((char*)ch1, "AD:%10d", ADC_ConvertedValueLoca);
ch1[18] = '\r';
ch1[19] = '\n';
HAL_UART_Transmit(&huart1, ch1, sizeof(ch1), 0x10);
HAL_Delay(1000);
// 6. 转换为浮点形式(保留小数 )
ADC_ConvertedValueLocal = (double)AD * 3.3 / 4096;
// 7. 串口输出浮点值(用 printf 更方便,需重定向 printf 到串口 )
printf("AD转换原始值 = 0x%04X \r\n", AD);
printf("计算得出电压值 = %f V \r\n", ADC_ConvertedValueLocal);
HAL_Delay(1000);
}
}
- 关键步骤: 初始化 ADC 和 UART 外设,确保硬件能正常工作。 循环采集 ADC 数据,分别转换为整数和浮点类型,通过串口输出,方便调试观察。
2. 扩展与优化思路
- 精度优化:
ADC 原始值是整数,转换为电压时若需更高精度,可改用 double 类型(如 ADC_ConvertedValueLocal = (double)AD * 3.3 / 4096; ),或调整公式中的比例系数。 - 串口输出优化:
- 重定向 printf 到串口:通过实现 fputc 函数,让 printf 直接输出到串口,简化代码(替代 sprintf + HAL_UART_Transmit ):
- int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xFFFF); return ch; }
- 使用环形缓冲区:若数据发送频繁,避免 HAL_UART_Transmit 的阻塞问题,可结合 DMA 或环形缓冲区实现非阻塞发送。
- 数据滤波:
实际采集的 ADC 数据可能有噪声,可添加滑动平均滤波等算法,提升数据稳定性: - #define FILTER_LEN 10 unsigned int adc_buff[FILTER_LEN] = {0}; unsigned int adc_index = 0; unsigned int adc_sum = 0; // 采集并滤波 AD = HAL_ADC_GetValue(&hadc3); adc_sum -= adc_buff[adc_index]; adc_buff[adc_index] = AD; adc_sum += AD; adc_index = (adc_index + 1) % FILTER_LEN; AD = adc_sum / FILTER_LEN; // 取平均值作为滤波后的值
3. 注意事项
- 类型截断问题:
ADC_ConvertedValueLoca = AD * 3.3 / 4096; 中,若 AD 是 unsigned int ,3.3 是 double ,计算时会自动转换为浮点,但赋值给 signed int 会截断小数,若需保留小数必须用 float/double 存储。 - 串口缓冲区溢出:
ch1 数组大小为 20,若 sprintf 输出的内容超过数组长度,会导致缓冲区溢出,破坏其他内存数据。需确保格式化字符串长度不超过数组大小,或动态分配缓冲区。 - ADC 校准与参考电压:
实际应用中,需先执行 ADC 校准(HAL_ADCEx_Calibration_Start ),且确保参考电压(3.3V )与硬件一致,否则转换结果会有偏差。
总结
这段代码的核心是利用不同数据类型(int/float )处理 ADC 原始值的整数与小数转换,结合串口输出实现数据调试。理解其原理后,可扩展到温度传感器(如 NTC )、电流采集等场景,通过优化滤波、串口输出方式,提升数据处理的精度和稳定性,是嵌入式模拟量采集与调试的基础实践。