文章目录
- 参考教程
- 模拟数字转换技术ADC
- [stm32 ADC的实现原理](#stm32 ADC的实现原理)
- cubemx配置
- 代码实现
- 总结
参考教程
视频 【STM32入门教程】ADC 模拟数字转换 ,超清晰动画讲解
作者 【b站UP 】keysking
模拟数字转换技术ADC
平常的电信号分为数字信号与模拟信号,数字信号就是分为高低电平两种状态,模拟信号就是任意的电压值。STM32芯片内就是一整套的数字逻辑电路来实现我们的程序执行以及各种各样的外设功能。
STM32是如何将模拟信号转化为数字信号进行处理的,这就是本期学习的模拟数字转换技术analog to digital conversion。
stm32 ADC的实现原理
ADC技术有许多种实现形式,而stm32中使用的是最常见的一种逐次逼近法SAR,这是一种通过不断进行二分比较,最终确定电压值的办法。以我们使用的STM32F103内的12位adc为例,12位就是指最终结果是以12个二进制位存储,也就是从 00 ... 0 ⏟ 12 个 0 \underbrace{00\ldots0}{12\text{个}0} 12个0 00...0到 11 ... 1 ⏟ 12 个 1 \underbrace{11\ldots1}{12\text{个}1} 12个1 11...1,转化成10进制,也就是0~ 2 12 − 1 2^{12}-1 212−1,也就是 4096 − 1 = 4095 4096-1=4095 4096−1=4095,其中0就代表0V,最大值4095就代表测量的最大值,也就是我们称为参考电压的值。
在STM32中,此值一般就是我们的供电电压3.3V,中间的其他值也是一一线性对应,例如2048就对应着大约1.65V,这个12位是衡量adc的重要属性之一,江湖人称adc的分辨率。
回到逐次逼近法上来,假设我们要测量的电压值为0.9V,STM32首先通过GPIO口将待测电压采用到电容上,随后切断与待测信号的连接,将待测电压保持到电容上,这样可以防止待测信号的波动,影响到测量结果。随后adc先将参考电压3.3V的一半,也就是2048所代表的电压1.65V与待测电压进行比较。
注意2048的二进制是 1 00 ... 0 ⏟ 11 个 0 1\underbrace{00\ldots0}{11\text{个}0} 111个0 00...0,比较结果当然是待测电压更小,因而adc就知道结果的第十二位上为0,即小于2048。随后ABC再将2048的一半,1024所代表的电压0.825V与待测电压进行比较,1024的二进制是 01 00 ... 0 ⏟ 10 个 0 01\underbrace{00\ldots0}{10\text{个}0} 0110个0 00...0,比较结果是待测电压更大,因而ADC就知道了结果的第十一位上为1,代表大于等于1024,随后adc再加1024与2048的中间,也就是1536,即 011 00 ... 0 ⏟ 9 个 0 011\underbrace{00\ldots0}_{9\text{个}0} 0119个0 00...0代表了电压大约1.238V,与待测电压进行比较,比较结果是待测电压更小,因而第十位为0,代表小于1536。
就这样循环往复进行二分比较,最终ADC便能确认出一个比较结果。

随后adc便将此结果放入到专门用来放adc转换结果的数据计算器中,随后我们的程序就可以从此计算器中取到转换结果,将其除以4095,再乘以参考电压3.3伏便可以成功获得待测电压。
在stm32F103系列芯片中,有16个gpio口可以进行电压值的采样工作,我们称其为16个adc外部通道,不过在STM32F103C876芯片上只引出了前10个通道。另外还有两个内部通道用于采集STM32内部提供的电压值,一个连接在芯片内部的温度传感器上,一个连接在内部参考电压上。
有两个用于转换的ADC结构,ADC1与ADC2,每个adc中有注入组与规则组,两种ADC通道组,注入组基础教程就先不涉及。规则组可以理解为普通组,它就像一个用于注册排队的表格,我们将某个ADC通道注册在上面,当触发adc时,adc就会对此通道进行采样转换,转换结果放入规则通道数据计算器中,等待程序读取,我们甚至还可以注册上多个通道,让ABC依次进行转换。
cubemx配置
设置debug模式为series wire
设置外部时钟为晶振,
时钟设置中设置HCLK为72M赫兹。
回到配置界面,点击连接到电位器上的GPIO口PA5,可以发现它可以被设置为adc1或者adc2的通道5,
找到adc1的设置,将通道5启用.可以发现PA5的功能现在是adc1的通道5,其他设置先不做改动,
随后打开串口,用于输出adc测量结果,
发现时钟设置里好像出现了错误,
adc1\2都是依靠APB2的时钟线,并且频率不宜太快,在F103上不要超过14M赫兹,而默认的36M赫兹明显太快,因而cubemx向我们发出了警告,避免我们因为疏忽而发生错误,将adc专用的分频器改为除6,ABC频率降为12M赫兹,就一切正常了,让我们保存并生成代码,因为提供东西先为这一会的串口输出演一下头文件。
代码实现
- 引入头文件
c
#include <stdio.h>
#include <string.h>
- main()中定义变量,一个value变量用于读取ABC的结果值,一个voltage变量用来将ADC结果转化为电压值,一个message变量用于串口输出拼接字符串
3.while循环中,首先触发ADC采样与转换,HAL_ADC_Start函数,参数只有一个adc1操作句柄的指针。随后用HAL_ADC_GetValue将adc值读取到value变量中,然后根据ADC值计算电压值。
不过这里有个问题,就是我们在读取adc时,adc并不一定转化完成了,可能读取到默认值或者上次的转换值,因而还要库还提供了一个等待完成的函数,HAL_ADC_PollForConversion,意思是一直轮询检查是否转换完成,两个参数,第一个是adc1操作具体的指针,第二个是等待超时时间,我们可以填HAL_MAX_DELAY代表无限等待。

- 将adc值和电压值输出出来
别忘了输出浮点数需要到工程的设置中勾选使用浮点数,最后加个延时,防止过快
连接好串口,可以看到串口正在输出测量到的adc值与电压值,调节电位器,ABC测量到的电压值就会改变。完美?
-
有些小伙伴们可能会发现电压值转到最大,怎么不是4095 3.3V呢?STM32官方在手册中建议我们每次上电后都要对ADC进行一次校准,校准流程STM32会自动执行,我们要做的就是在触发第一次adc转换前,就调用一下HAL库为我们准备的校准函数HAL_ADCEx_Calibration_start,参数是adc1操作句柄的指针,编译下载,这样一切看起来就正常多了。
-
当前程序我们是HAL_ADC_Start触发一次采样与转换,等待转换完成后读取测量结果,其实STM32的adc还为我们提供了一种连续转换功能,开启此功能后,我们只需要触发一次adcadc在完成一次转换后,会马上进行下一次的采样转换工作,持续不断的更新计算器里的测量结果。我们使用的时候就只要读取存在计算器中的值就好了。
使用办法很简单,来到cubemx界面找到ADC的设置,我们将持续转化模式设置为开启,随后保存并生成代码。
由于只需要触发一次,我们将HAL_ADC_Start的函数移到循环前,因为一直在不停的转换更新测量记录,所以我们随时从计算器中取数据就好了。所以等待转换完成的函数也移到循环前,避免第一次获取到默认值就好了。改造完成编译下载。
总结
STM32adc的使用很简单,只需要将对应的GPUIO的ADC通道开启使用前进行一次校准,单次模式下要首先触发测量,等待转换完成后读取测量结果就好,简单的计算就可以得出电压值,也可以开启连续转换模式,ABC会持续不断的进行测量,因而只需要触发一次测量,需要时随时取值就好了。