STM32-ADC如何把采集的数据转换为小数
wptr33 2025-07-06 17:24 4 浏览
编辑
一、代码原理解析
这段代码围绕 “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 )、电流采集等场景,通过优化滤波、串口输出方式,提升数据处理的精度和稳定性,是嵌入式模拟量采集与调试的基础实践。
相关推荐
- F103C8T6移植FATFS文件系统 版本R0.15
-
STM32F103C8T6芯片在W25Q64上移植FATFS(版本R0.15)实现过程:1、首先完成USART初始化和调试,用于传输信息到串口调试软件。2、完成SPI相关参数配置及调试,用于单片机和存...
- stm32使用MPU6050或ADXL345控制的车辆减速灯
-
本实验例程采用MPU6050六轴运动处理组件...
- STM32F103串口输出prtinf覆盖(stm32printf函数的串口输出)
-
采用正点原子的板子,有如下坑,记录如下:(1)main中应用头文件#include"stdio.h"(2)采用hal进行fputc和fgetc覆盖,如下intfputc(intc...
- STM32 学习8 USART串口通讯与printf重定向
-
一、串口通信介绍STM32F103ZET6包含多个UART、USART串口。...
- 教你如何使用SEGGER RTT优雅的实现日志系统
-
今天开始了BMS系统的软件代码部分的搭建,计划是分成三层:硬件驱动,AFE层和系统应用层。第一步肯定是先把底层的IIC通信调通,CG861xx的IIC通信和TI的BQ769X0...
- 终极调试利器,各种Link通吃(link4a调制方式)
-
今天继续更新一期KEIL调试方法。事实上,关于调试方法,鱼鹰写了一个系列,汇总文为《佛祖保佑,永无BUG,永不修改|KEIL调试系列总结篇》,对于KEIL方法感兴趣的可以看看。这个调试...
- 在 STM32 中使用 printf() 函数,别漏掉这几行代码!
-
问:在STM32上轻松使用printf函数除了点亮LED外,向串行控制台发送打印信息可能是调试嵌入式项目时最简单、最直接且最常用的技术。虽然大多数平台都拥有可以在UART总线上传输数据的API,但它们...
- 高性能异步io机制:io_uring(异步io select)
-
io_uring是linux内核5.10引入的异步io接口。相比起用户态的DPDK、SPDK,io_uring作为内核的一部分,通过mmap的方式实现用户和内核共享内存,并基于m...
- 精品博文ARM中打印函数print 的几种实现方法
-
1利用C库函数printf步骤:1)首先需要包含头文件stdio.h。2)然后定义文件句柄。实际上就是一个int型变量封装在结构体中。struct__FILE{inthandle;};3)定...
- C语言char的详解(c语言(char))
-
在C语言中,char是一种基础数据类型,用于表示字符或小整数值。对char的理解和处理非常重要,尤其是在字符串操作、文件读写或其他需要直接控制内存的应用场景中。下面从基本定义、存储方式、常见用法...
- C语言之文件操作(c语言文件操作实验总结)
-
文件操作是C语言中非常重要的功能,用于读取和写入文件中的数据。C语言提供了一组标准库函数(如fopen、fclose、fread、fwrite等)来实现文件操作。以下是针对C语言初学者的详细讲解。...
- STM32-ADC如何把采集的数据转换为小数
-
编辑一、代码原理解析这段代码围绕“STM32中ADC数据采集、整数与小数计算及串口输出”展开,核心是数据类型的使用(unsignedint/signedint/float)、ADC数...
- 循环队列原理及在单片机串口通讯中的应用(二)
-
前言书接上回,前文主要介绍了环形队列的实现原理以及C语言实现及测试过程,本文将回归到嵌入式平台的应用中,话不多说,淦,上干货!...
- STM32编程中printf函数重定向背后的原理
-
在C语言中,printf是一个非常好用的函数,尤其是在程序调试阶段,我们可以通printf打印变量的值来帮助查错。在学习C语言的时候我们的开发环境和运行环境都是PC机,printf函数打印到PC机...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
git 执行pull错误如何撤销 git pull fail
-
面试官:git pull是哪两个指令的组合?
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)