STM32-ADC如何把采集的数据转换为小数
wptr33 2025-07-06 17:24 82 浏览
编辑
一、代码原理解析
这段代码围绕 “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 )、电流采集等场景,通过优化滤波、串口输出方式,提升数据处理的精度和稳定性,是嵌入式模拟量采集与调试的基础实践。
相关推荐
- oracle数据导入导出_oracle数据导入导出工具
-
关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...
- 继续学习Python中的while true/break语句
-
上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个...
- python continue和break的区别_python中break语句和continue语句的区别
-
python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...
- 简单学Python——关键字6——break和continue
-
Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
-
用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...
- Python 中 break 和 continue 傻傻分不清
-
大家好啊,我是大田。...
- python中的流程控制语句:continue、break 和 return使用方法
-
Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...
- L017:continue和break - 教程文案
-
continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...
- 作为前端开发者,你都经历过怎样的面试?
-
已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...
- 面试被问 const 是否不可变?这样回答才显功底
-
作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...
- 2023金九银十必看前端面试题!2w字精品!
-
导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。...
- 前端面试总结_前端面试题整理
-
记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...
- 由浅入深,66条JavaScript面试知识点(七)
-
作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录...
- 2024前端面试真题之—VUE篇_前端面试题vue2020及答案
-
添加图片注释,不超过140字(可选)...
- 今年最常见的前端面试题,你会做几道?
-
在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...
- 一周热门
- 最近发表
-
- oracle数据导入导出_oracle数据导入导出工具
- 继续学习Python中的while true/break语句
- python continue和break的区别_python中break语句和continue语句的区别
- 简单学Python——关键字6——break和continue
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
- Python 中 break 和 continue 傻傻分不清
- python中的流程控制语句:continue、break 和 return使用方法
- L017:continue和break - 教程文案
- 作为前端开发者,你都经历过怎样的面试?
- 面试被问 const 是否不可变?这样回答才显功底
- 标签列表
-
- 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)
