首页/新闻资讯/正文详情

STM32 HAL库开发实战(七)—— ADC

发布时间:2026/11/3 2:42:26 来源:云帆数科 栏目:资讯中心
STM32 HAL库开发实战(七)—— ADC
一、什么是ADCADC---Analog to Digital Converter即模拟信号转化为数字信号的转换器。1、数模信号的转换模拟信号即时间和幅度都连续的信号这种信号一般存在于自然界中比如下图是某地一天的气温随时间变化的曲线可以看到这条曲线光滑且连续。而数字信号与模拟信号相反即在时间和幅度上都离散一般出现在计算机中比如这是气象站给出的温度数据可以看到每隔两小时更新一次温度数据对于这幅图来说他并不是在任意时间点都有数据因此在时间上是离散的而且记录的数据只需要精确到小数点后一位因此无法记录一个精度很高的数据所以对于这个信号来说在幅度上是离散的在时间上也是离散的。而ADC的作用就是把模拟信号转化为数字信号也就是把自然界的信号转化为单片机可识别的信号比如上述温度可以用一个二维数组来表示一维数组代表时间二维代表温度。2、12位逐次逼近型12位代表ADC的采样深度就是用多少位二进制数来表示一个采样点比如对于单片机来说输入到单片机的电压范围是0-3.3v现在通过ADC转化为数字信号也就是使用二进制的形式来表示。比如说这里采用4位ADC来表示0-3.3v在数字信号中的形式就是0000b~1111b需要把0-3.3v分成-1份每一份代表0.22V0000b代表0V0001b代表0.22V0010b代表0.44V因此类推8位ADC的话就是00000000b~11111111b分成-1份每一份代表0.013V,00000000b代表0V00000001b代表0.013V00000010b代表0.026V。也就是说ADC的采样精度越深转换的结果越精细采样深度是衡量ADC性能的重要指标之一。逐次逼近是ADC的一种类型除此之外还有几种别的类型而我们单片机中所使用的就是逐次逼近型。从字面意思理解就像天平称重有许多不同量程的砝码用不同量程的砝码来逐渐逼近所称重物的质量如图是逐次逼近型ADC内部结构采样深度4bit将结果寄存器和模拟信号输入看作天平的两个托盘结果寄存器是放置砝码端模拟信号是重物端通过不断修正电压精度并于进入比较器来逼近输入信号的电压大小。现在要用一个4精度ADC量程为0~3.3V来测量2.21V电压上面说过4精度的每一份代表0.22V2.21V就是10份转化为二进制就是1010b。整个过程就是模拟信号输入2.21v电压闭合开关输入电压就会对充电当这个电容充满电后电容两端的电压就为2.21V再把开关断开此时输入到比较器端的电流i0而电容两端的电压在后续的转换过程中不会发生变化比较器端的电压也为2.21V所以这个采样保持电路的作用就是将输入信号储存在电容中而结果寄存器中的b0-b4就像四个量程的砝码。想要测得2.21V电压也就是10份二进制数就是1010b所以我们需要向b3和b1写1。我们所使用的单片机内部的ADC最多可以接收12路模拟信号其中前10路模拟信号是通过IO引脚输入进来的剩下两个来源于芯片内部一个是温度计另一个是参考电压输入信号有这么多路管理起来也是有讲究的。日常中我们会把要做的事列成计划表然后依次完成在ADC中想要管理这么多路信号也要制定计划ADC中的计划分为两份一份常规序列一份注入序列两种使用方法是一样的都可以控制左边的开关在他们的右边都有一个外部触发信号通过外部触发信号启动计划的执行。比如现在需要对AIN1AIN4AIN2进行转换先把这份计划写入ADC中使用常规序列或注入序列都可以向外部触发输入一个脉冲这样1、4、2会被依次转换。现在将一个正弦波通过AIN5输入到ADC中如何用ADC每1ms对信号转换一次得到一系列点呢和上面一样将5写出常规序列中然后通过外部触发每1ms输入一个脉冲这样每1ms通道5就被转换一次这样就可以的到每1ms间隔一次的点了。3、ADC的时钟我们使用的单片机有两个ADC挂载在APB2总线上ADC1和ADC2的时钟信号都是由PCLK2经过分频之后得到的输入到ADC的时钟频率不能超过14MHz如果输入到ADC的时钟频率超过14MHz其方框会变红这个时候我们通过改变前面的分屏器来降低输入到ADC的时钟信号频率后面说到的采样时间和转换时间都需要表示成时钟周期的倍数假设输入到ADC的时钟频率是14MHz那时钟周期再假设计算得到的采样时间是0.1us转换时间是0.9us采样时间0.1us1.5*1/14us1.5cycle转换时间0.9us12.5*1/14us12.5cycle写成这种周期倍数的格式主要是为了后面的编程。4、转换时间的计算方法我们所使用的单片机采样深度是12用砝码来类比就相当于有12种不同量程的砝码称重时需要尝试不同量程的砝码每尝试一次就消耗一个时钟周期尝试12次就消耗12个时钟周期另外还需要在额外加0.5个周期总共就花费12.5个周期也就是转换时间固定为12.5个周期5、采样时间的计算方法也就是把模拟信号输入到ADC中所花的时间前面说过待转换的模拟信号通过采样保持电路给电容充电充满电后断开开关所以采样时间开关闭合的时间电容充电的时间。随着时间变长电容两端的电压会越来越接近输入电压也就是充电的时间越长误差越小但是也不能让充电的时间太长那样效率就太低了所以我们需要在精度和效率之间寻找一个平衡。ADC的精度本来就是有限的我们只需要让采样误差ADC的最大精度就行了这样即使出现误差也在可接受的范围内具体的标准就是采样误差要1/4*ADC分辨率。例如我们现在使用的单片机采样深度是12那分辨率3.3-0/(2^12-1)0.8mv,采样误差1/4*ADC分辨率0.2mv芯片手册中有如下公式对其进行变形可以得到采样时间计算公式Ts就是最优采样时间查阅数据手册可以知道N就是采样深度是信号源内阻由此可以得知采样时间就取决于信号源的内阻内阻越大采样时间越长。选取几个内阻计算其采样时间再转换为时钟周期的倍数这也就是单片机内部几个固定的采样时间需要在几个固定的挡位中选取一个一共有8个挡位在实际编程中只要选取一个最接近的就行上面选的几个内阻计算出的采样周期我们依次选择最接近的分别是1.5413.555.5。理想情况下ADC每秒可以进行1000000次转换总耗时采样时间转换时间采样时间只取决于信号源内阻理想状况下假设内阻为0频率为14MHz根据公式算出采样时间约为1Cycle选取最接近的挡位就是1.5Cycle转换时间规定为12.5Cycle总共就是14Cycle。每1us完成一次完整的采样1s也就是1000000次。二、使用ADC模块进行实验光敏传感器实验1首先需要用到光敏模块只需要用到模拟输出模块的输出电压随着光照强度的增大而减小 现在使用单片机里的ADC去测量这个模块的输出电压当这个模块的输出电压大于1.5v的时候就熄灭LED小于1.5v的时候就点亮LED。CubeMX配置任意选择ADC1的一路模拟输入比如IN0设置PA9为输出模式外接一个LED。接线图如下。前面我们说过采样时间只和信号源的内阻有关内阻越大采样时间越长。所谓信号源的内阻就是我们 要测量模块的内阻这里我们测量光敏模块的电压那内阻就是光敏模块的内阻。根据戴维南等效定理,算出内阻就是R1 R2并联的电阻由于光敏电阻的范围是0-30kΩ所以并联的结果一定小于10kΩ保守一点就取值10kΩ。代入公式中计算得到采样时间约为0.85us乘以ADC的时钟频率4MHz,得到采样周期为3.4cycle前面说过单片机中提供了8个周期这里选择7.5倍的周期因此这个采样模块对应的采样时间就是7.5倍的周期 。现在需要将输入通道0采样周期7.5cycle写入常规序列中首先第一个参数Enable Regular Conversions使能常规序列第二个参数是Number of Conversion设置转换的数量因为ADC的常规序列有16个格子所以这个参数的取值范围就是1-16这里选择1Rank1就代表常规序列中的第一个方格现在把通道0采样周期7.5cycle填入第一个方格External Trigger Conversion Source用来选择常规序列的外部触发信号可以来自于定时器也可由软件启动这里选择Regular Conversion launched software软件启动配置好之后生成代码。重点说一下这三个编程接口1、HAL_ADC_Start//启动常规序列想启动常规序列就需要通过他左边的那个外部触发信号向其发送脉冲。这个函数会先闭合外部触发的开关从而去使能这个外部触发信号如果选择软件触发这个编程接口会通过软件启动向外部触发信号发送给脉冲这样常规序列就被启动了。2、HAL_ADC_PollForConversion//等待转换结束Poll是轮询的意思其作用就是以轮询标志位的方式等待常规序列转换完成我们使用的是常规序列所以只需要查询EOC这个标志位的值就可以知道常规序列到底有没有转换完成这个编程接口就是通过轮询EOC这个标志位来等待常规序列转换完成的。3、HAL_ADC_GetValue//读取转换结果当 ADC转换结束后结果保存在结果寄存器中结果寄存器总共有5个常规序列只有一个DR结果寄存器注入序列有4个JDR寄存器这个函数将结果从DR寄存器中读出来因为传感器要不断的检测周围光线的强度所以代码要写在while内部。整体思路分为四步首先启动常规序列然后等待转换完成再获取结果最后把结果转换成电压。ADC会把模拟信号转化成数字信号并保存在结果寄存器中但是结果寄存器中国保存的并不是电压实际的值而是二进制数需要将这串二进制数转化为电压值也就是将转换值乘上ADC的分辨率就可以得到电压。代码如下int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_ADC1_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ //1.启动常规序列 HAL_ADC_Start(hadc1); //2.等待转换完成 HAL_ADC_PollForConversion(hadc1, HAL_MAX_DELAY); //3.获取转换结果 uint32_t dr HAL_ADC_GetValue(hadc1); //4.把结果转换为电压 float voltage dr * (3.3f-0.0f) / 4095.0f; if(voltage 1.5f)//光线暗 { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET); } else //光线亮 { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET); } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }光敏传感器实验2下面介绍第二个实验将光敏传感器的的模拟信号通过ADC转换成数字信号再通过串口将转换的结果发送给电脑cubeMX配置首先还是配置调试接口SYS里面 选择Serial Wire选择ADC1的模拟输入0这个通道对应的是PA0光敏传感器的AO引脚就接在这个口。接着是串口配置选择异步同通讯波特率为15200数据长度为8位不使用校验位1位停止位。下面是接线图前面说过光敏模块的采样时间选择7.5周期ADC1我们选择的是通道0我们需要把07.5写入常规序列中常规序列需要每1ms触发一次而常规序列的触发信号是由他左边的外部触发信号启动的那就需要外部触发信号每1ms输入一个脉冲常规序列外部触发信号有很多种上面我们说了软件启动也就是通过编程的方式直接发送一个脉冲上面的几种来源于定时器还有一种来自于TRGO来自于从模式控制器当定时器的从模式控制器作为主机控制别人时可以输出控制信号控制其他模块比如控制ADC此时从模式控制器有8种模式这里选择Update模式每产生一个Update事件就向TRGO输出一个脉冲所以我们需要让定时器溢出的频率为1ms1KHz就可以向TRGO每1ms输出一个脉冲。整体思路就是在TIM3的时基单元设置1ms溢出一次同时选择从模式控制器为TGRO选择Update模式就可以通过外部触发信号输入给常规序列下面对TIM3进行配置以上就是全部配置随后生成代码首先需要在while之前启动定时器3和ADC1其中ADC的启动函数 HAL_ADC_Start(hadc1)它的作用有两个 首先会闭合外部触发信号的开关从而使能外部触发信号其次如果我们使用软件启动外部触发信号它会向常规序列发送脉冲信号。这是要采集的模拟信号下面是常规序列的外部触发信号定时器每隔1ms发过来一个脉冲启动常规序列在常规序列里只包含一个通道连接光敏传感器的模拟输入0模拟输入0的转换需要12.57.520个cycle外部触发信号是定时器自动发过来的不需要手动操作所以我们要做的事只有两件一是等待转换完成二是把转换的结果读出来不断地重复所以需要把代码写在while内部并将结果转化成电压后通过串口发送给上位机这里使用VOFA。代码如下int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM3_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_Base_Start(htim3); HAL_ADC_Start(hadc1); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { //1.等待转换结束 HAL_ADC_PollForConversion(hadc1, HAL_MAX_DELAY); //2.获取转换结果 uint32_t dr HAL_ADC_GetValue(hadc1); //3.将结果转换成电压 float voltage dr * (3.3f - 0.0f) / 4095; //4.使用串口发送结果 char buffer[32]; sprintf(buffer, %.3f\n, voltage); HAL_UART_Transmit(huart1, (uint8_t*)buffer, strlen(buffer), HAL_MAX_DELAY); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }这是上位机中电压随着光照强度的变化而变化的曲线三、总结STM32 的 ADC 本质上是一个以时钟节拍为基准的逐次逼近型SAR模数转换系统在采样阶段外部电压通过信号源等效内阻对内部电容充电内阻越大所需的采样周期数就越长在转换阶段内部硬件通过固定的 12.5 个时钟周期完成二分法量化将物理电压映射为 0-4095 的数字刻度。在系统架构上通道分为支持 16 路顺序扫描的常规序列与具备硬件抢占打断能力的 4 路注入序列实际工程中强烈推荐采用定时器硬件触发TRGO替代易产生抖动的软件轮询启动再配合中断或 DMA 自动搬运结果从而在完全不占用 CPU 算力的情况下实现高精度、零抖动的严格等间隔数据采集与上位机解析呈现。

相关推荐

只判断不生成:Jev 跑出 70–500ms,AI 到底偷偷省掉了哪几步?
只判断不生成:Jev 跑出 70–500ms,AI 到底偷偷省掉了哪几步?

只判断不生成:Jev 跑出 70–500ms,AI 到底偷偷省掉了哪几步? 你有没有过这种体验:在 App 里点“转人工”,页面转圈三秒,最后告诉你“已为您分配到售后组”。 可这件事的本质,只是在一个下拉框里… · 2026/11/3 2:41:26

WeVisOne-VL-4B指代分割(Referring Segmentation):一句话“剪“出像素级精确掩码的实战指南
WeVisOne-VL-4B指代分割(Referring Segmentation):一句话“剪“出像素级精确掩码的实战指南

WeVisOne-VL-4B指代分割(Referring Segmentation):一句话"剪"出像素级精确掩码的实战指南 WeVisOne-VL-4B 是腾讯微信视觉(WeChat Vision)推出的统一视觉-语言模型,它最亮眼的像素级能力之一就是… · 2026/11/3 2:41:26

同一个“AI换脸”,掘金刷到的是实时变脸,CSDN 刷到的却是 BFS:这波信息差把新手坑惨了
同一个“AI换脸”,掘金刷到的是实时变脸,CSDN 刷到的却是 BFS:这波信息差把新手坑惨了

同一个“AI换脸”,掘金刷到的是实时变脸,CSDN 刷到的却是 BFS:这波信息差把新手坑惨了 先说一个挺魔幻的亲身经历。 前几天我想给自己的视频封面换张脸,分别在两个技术社区搜“AI 换脸”。 在掘金,首页蹦出来的是 Deep… · 2026/11/3 2:41:26

HDFS数据块机制:128MB设计的核心原理与优化实践
HDFS数据块机制:128MB设计的核心原理与优化实践

1. HDFS数据块机制的设计哲学HDFS(Hadoop Distributed File System)作为大数据生态的基石存储系统,其数据块(Block)机制的设计直接影响着整个分布式文件系统的性能表现。128MB的默认块大小绝非随意选择,而是… · 2026/11/3 3:11:31

PSpice模型创建与宏模型建模:从.MODEL到行为级仿真
PSpice模型创建与宏模型建模:从.MODEL到行为级仿真

简介:围绕 PSpice 模型创建主题的实践文档,面向需要进行电路仿真的初学者与硬件工程师,重点解决软件自带模型库参数不适用、新建元件缺少可用模型以及宏模型搭建思路不清等问题。文档从模型对仿真精度与速度的影响切入,梳理直流、… · 2026/11/3 3:11:31

PSS分析实战:谐波平衡与打靶法选型及收敛排查
PSS分析实战:谐波平衡与打靶法选型及收敛排查

简介:这份PDF文档围绕集成电路周期性稳态(PSS)仿真展开,面向具备一定电路仿真基础、使用Spectre的研发人员与技术专家,帮助其理解PSS分析在受驱动电路与振荡器等自治电路中的适用范围及参数配置方法。包内仅含1个PDF文… · 2026/11/3 3:11:31

UniPro v2.0协议栈拆解:M-PHY HS-G5分层、重传流控与带宽优化
UniPro v2.0协议栈拆解:M-PHY HS-G5分层、重传流控与带宽优化

简介:MIPI UniPro Specification v2.0 官方规范文档,面向从事移动设备内部通信、M-PHY/UniPro 协议栈开发与验证的工程师、芯片设计人员及高校研究者,用于系统查阅 UniPro v2.0 的协议框架、特性变更与兼容性边界。资源为单个 PDF 文件&#… · 2026/11/3 3:11:31

工厂模式解析:对象创建的艺术与工程实践
工厂模式解析:对象创建的艺术与工程实践

1. 工厂模式:对象创建的工业化革命第一次接触工厂模式是在2013年参与一个电商平台开发时。当时系统需要对接多个物流供应商,每个物流公司的API接口和计费规则都不相同。如果直接在业务代码中new出各个物流服务对象,代码很快就会变成一团乱麻。… · 2026/11/3 3:11:31

SpringBoot+微信小程序智慧旅游系统开发实践
SpringBoot+微信小程序智慧旅游系统开发实践

1. 项目背景与核心价值在移动互联网时代,智慧旅游已成为行业发展的必然趋势。传统旅游服务存在信息不对称、预订流程繁琐、服务响应滞后等问题,而基于微信小程序的智慧旅游系统恰好能解决这些痛点。作为一名长期从事旅游信息化开发的工程师,我… · 2026/11/3 3:10:31

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码