北屋教程网

专注编程知识分享,从入门到精通的编程学习平台

STM32-ADC如何把采集的数据转换为小数

编辑

一、代码原理解析

这段代码围绕 “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 )、电流采集等场景,通过优化滤波、串口输出方式,提升数据处理的精度和稳定性,是嵌入式模拟量采集与调试的基础实践。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言