IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32F103(十八)ADC总结(5W字) -> 正文阅读

[嵌入式]STM32F103(十八)ADC总结(5W字)

学习板:STM32F103ZET6

ADC

一、ADC简介

1、详述

ADC(Analog Digital Converter),顾名思义为模拟to数字转换,将模拟信号转化为一定比例的电压值。对于32的GPIO来讲,通常只有高电平和低电平之分,即当识别到高电平时并不能知道是3.3V还是3.2V。使用ADC后就可以很好的检查出电平具体是多少伏特了。所以不要把ADC单纯的理解为模拟->数字的转换,而要理解为未知(伏值)信号—>已知(伏值)信号的转换。

可以这样理解,当一个信号输入的MCU时,哪怕它是模拟信号,由于MCU的识别占用一定时间,所以看似是识别模拟信号,其实信号已经算是抽样信号了。

学过《信号与系统》的朋友应该知道模拟信号、抽样信号、数字信号的区别。现再区别一下:
模拟信号:时间连续、幅值连续的信号。比如声音信号、温度信号
抽样信号:时间离散、幅值连续的信号。比如每隔一个时间取一个声音信号值。
数字信号:时间离散、幅值离散的信号。比如每隔一个时间取一个声音信号值,然后将所有取值量化到一个幅值区间。

通过《信号与系统》我们知道,模拟信号到数字信号必须经过抽样和量化。

再回到ADC内容,如果将模拟信号输入到单片机的GPIO,由于单片机的识别有一定时间,如每识别一次信号需要1us,则此时单片机其实已经将模拟信号自动转换为时间间隔为1us的抽样信号。但是仅仅是抽样信号,单片机只知道它是每隔1us的信号是高电平还是低电平,并不知道它具体幅值多少。那么通过ADC,可以较为准确的将幅值量化(经过量化就变成数字信号了)。量化的幅值范围与设置的参考电压有关,将幅值量化到参考电压范围内。

2、STM32的ADC数量和通道数

STM32拥有1~3个ADC,具体几个与芯片型号有关系。都是采用12位逐次逼近型模数转换器,有18个通道,其中16个通道测量外部输入,2个为内部信号源检测(注意不是外设),从芯片图中也能看到,下图已高亮:

在这里插入图片描述

归结为下图:

在这里插入图片描述

3、STM32的ADC时钟

在这里插入图片描述

上图红色箭头显示,ADC的最大时钟为14MHZ。图中绿色标注和蓝色标注是底层中的设置的各类时钟。系统时钟为72MHZ,AHB预分频系数为1,则ADC预分频系数必须大于等于6,所以我习惯把预分频系数设置为6,此时ADC的时钟频率为12MHZ。

4、STM32的ADC转换速率

ADC转换时间=采样时间+12.5个时钟周期

这个采样时间可以设置,最小为1.5个时钟周期
在这里插入图片描述

所以最大转换速率=1.5+12.5=14(个时钟周期),ADC最大时钟频率为14MHZ,则14个时钟周期为1us,即最快时(采样周期最小、时钟最大)为1us

5、STM32的ADC规则通道和注入通道

ADC的规则通道和注入通道很好理解。正常情况下只需使用规则通道,规则通道最多16个通道转换,根据实际需求选择转换通道数量。如有8路信号,则可以把每一路信号接ADC的不同引脚,并都可设置为规则通道,此时有8个规则通道。

注入通道就可以把它看做是中断。当执行某一功能的ADC转换A时,突然想执行另一个ADC转换B,这样需要中断此时的ADC转换A去执行ADC转换B,此时可用将转换A设置为规则通道,转换B设置为注入通道。注入通道最多有四个。这个控制触发注入通道的信号可用是内部信号、外部中断、定时器输入捕获通道的信号。

6、ADC框图(比较重要)

在这里插入图片描述

上图中:
绿色框框是注入通道的触发,主要是外部中断、定时器输入捕获通道、定时器内部信号。

红色框框是ADC的0~15的16个外部通道、内部温度传感器和VREFINT两个内部通道。

蓝色框框是参考电压、供电电压。ADC量化后输出负参考电压~正参考电压直接的值;一般参考电压为0 ~3.3V;VDDA和VSSA给ADC的模数转换器供电

如下图所示:
正参考电压=VDDA=3.3V
负参考电压=VSSA=0
供电正=VDDA=3.3V
供电负==VSSA=0
在这里插入图片描述

黄色框框是设置中断、看门狗。当规则通道转换结束后ADC状态寄存器ADC_SR位1(EOC)硬件置1,当注入通道转换结束后ADC状态寄存器ADC_SR位2(JEOC)硬件置1。如果使能了ADC中断,则会进入中断服务函数。

深蓝色框框是ADC模拟看门狗,ADC输入的模拟电压值幅值要在一定范围内,这个范围在下面俩个寄存器中设定。当输入电压低于低阈值或高于高阈值时ADC模拟看门狗被激活。注意这个看门狗可以作用于1个或多个通道,且只需将对应通道的ADC转换重新复位,程序不用完全复位。

在这里插入图片描述

二、ADC相关寄存器

1、 ADC状态寄存器(ADC_SR)

在这里插入图片描述

位0:模拟看门狗标志位

当输入的模拟信号低于低阈值或高于高阈值时(ADC_LTR和ADC_HTR寄存器定义),该位硬件置1,发生看门狗事件。

位1:转换结束标志位

当只进行规则通道转换时,各通道转换结束后该位置1;当一次ADC转换期间包含了注入通道转换,则当规则和注入通道转换都结束后该位才置1 。(相当于中断返回后继续执行完本通道转换才置1)
注意该位可以软件清零或者读ADC_DR寄存器清零,正因为需要软件清零或者读DR寄存器清零,所以才不用担心多个规则通道连续模式时数据来不及读取。

位2:注入通道转换结束标志位

当注入通道转换结束(可能一次“中断”有多个注入通道转换,需要全部结束)后该位硬件置1。

位3:注入通道开始标志位
当发生“中断”进行注入通道转换时,该位硬件置1 。

位4:规则通道开始标志位
当规则通道开始转换时,该位硬件。

当然在程序中可以通过识别该寄存器的对应位来得到状态,如判断注入通道转换是否结束,代码:

	if(ADC1->SR&0x04)
	{
	}

2、 ADC控制寄存器 1(ADC_CR1)(极其重要

在这里插入图片描述

位4:0:模拟看门狗通道选择

在这里插入图片描述

需要注意的是ADC1的16个外部通道和2个内部通道都用到了。而ADC2、ADC3的通道只用到一部分。

位5:允许产生EOC中断位

在这里插入图片描述

当规则通道全部转换完成后,SR寄存器的位1位硬件置1,如果CR1寄存器位5置1允许中断,则会发生中断。

位6:允许产生模拟看门狗中断位
在这里插入图片描述

如果输入模拟信号的电源低于低阈值或高于高阈值,会发生错误,如果允许了看门狗,则会重新执行通道转换。如果该寄存器位6置1,允许产生看门狗中断,则会发生中断。

位7:允许产生注入通道转换结束中断位
在这里插入图片描述

当注入通道转换结束后,寄存器SR的位2硬件置1表示结束。当CR1寄存器位7置1允许中断时,会产生中断。

位8:扫描模式设置位
在这里插入图片描述

该位设置连续扫描模式,当该位值1时,会扫描所有被ADC_SQRX寄存器(对规则通道)或ADC_JSQR(对注入通道)选中的通道。且每个通道只转换1次,一个通道转换结束后,会自动转换下一个通道,当所有选中的通道都被转换一次后,再重新开始转换所有通道。

如果设置了规则通道和注入通道中断,当所有通道都被转换后才能发生中断。

如果设置了DMA位,在每次EOC后,DMA控制器把规则组通道的转换数据传输到SRAM中。而注入通道转换的数据总是存储在ADC_JDRx寄存器中。

位9:扫描模式中单一通道使用模拟看门狗
在这里插入图片描述

当位8置1设置了扫描模式,每个选中的通道都会进行一次转换。对于单个通道,如果输入模拟信号低于设置的低阈值或高于设置的高阈值会触发看门狗,并重新执行对应通道的转换。

位10:自动的注入通道组转换
在这里插入图片描述

之前也总结过,注入通道转换时要触发,有外部中断触发、定时器输入捕获通道信号触发、定时器内部信号源触发。如果位10置1设置自动注入通道转换,则不再需要触发,当所有规则通道执行完毕后,自动执行注入通道转换。

位11:规则通道上的间断模式
在这里插入图片描述位12:注入通道上的间断模式
在这里插入图片描述
位15:13:间断模式通道计数

在这里插入图片描述

这个间断模式细讲一下:
首先间断模式的通道个数:本寄存器的位15:13设置1~8个,即选择一次外部触转换几个通道。

其次间断模式的哪几个通道:转换的所有通道时SQRx(1~3)选择的通道。SQRx设置了一个转换序列的通道次序。
如设置有10个通道:1、9、8、2、4、11、3、5、7、6;正常情况下一个转换序列就是按照这个顺序来的。现在把它设置为间断模式,且CR1寄存器位15:13=011,即每次外部触发后转换三个通道。则有:
第一次外部触发:转换1、9、8
第二次外部触发:转换2、4、11
第三次外部触发:转换3、5、7
第四次外部触发:转换6
第四次转换完成后,SR寄存器的位1硬件置1表示规则通道转换完成。

以上说的是规则通道,注入通道也一样,不过注入通道的序列长度、通道顺序都在JSQR寄存器中设置。

如:JSQR寄存器位21:20=11,表示4个注入通道,然后位19:15、14:10、9:5、4:0分别设置注入通道顺序。注入通道:15、10、13、12
每次外部触发后,只能执行一次注入通道转换:
第一次外部触发:转换通道15
第二次外部触发:转换通道10
第三次外部触发:转换通道13
第四次外部触发:转换通道12
第四次触发并转换完毕后会SR寄存器相关位置1表示转换结束,如果设置了注入通道中断,还会发生中断。

第五次外部触发:转换通道15…

注意:自动注入和间断模式不能同时使用

位19:16:双模式选择
在这里插入图片描述

这个了解即可,反正我从来没用到过。可以查看中文参考手册了解了解。

位23、位22:规则通道、注入通道开启模拟看门狗
在这里插入图片描述
当对应位置1后,如果输入信号超出定义的阈值,会发生看门狗事件,重新转换对应通道。

3、 ADC控制寄存器2(ADC_CR2)

在这里插入图片描述

位0:开/关A/D转换器
在这里插入图片描述需要注意的是要单独对该寄存器设置,最好不要用下面的方法:

ADC1—>CR2|=0x...1//(非1数)

即开启AD转换时最好单独成行:

ADC1->CR2|=1;

位1:连续转换设置位
在这里插入图片描述

如果设置该位设置了1,则前面的ADC转换(整个序列)刚结束就开启另一次转换。不过每个规则通道转换结束后都能产生结束标志(SR寄存器)、都可产生中断(如果使能了中断)。注入通道也一样。

位2:A/D校准
在这里插入图片描述

ADC内置一个自校准模式,可大幅减小因内部电容器组的变化而造成的准精度误差,校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。

当校准结束后,该位会被硬件置1

注意校准时机,一般在ADC上电之前校准,校准完成后,只是隔2个ADC时钟周期再启动ADC。

位3:复位校准
在这里插入图片描述

由软件设置,硬件清零,一般情况下可以不用。

位8:直接存储器访问模式
在这里插入图片描述

设置DMA

位11:设置数据对齐
在这里插入图片描述

在这里插入图片描述

通道转换数据会存储在DR寄存器,DR寄存器为32位寄存器,除了双模式下用到高16位,其他情况下通道转换数据都存储在低16位中,上图所示的对齐,一般采用右对齐,即该位置0

位14:11:选择启动注入通道组转换的外部事件
在这里插入图片描述

之前也总结到,启动注入通道时,需要外部中断触发、定时器捕获通道信号触发、定时器内部信号源触发,这3位就是来设置触发方式的。

位15:选择启动注入通道组转换的外部事件
在这里插入图片描述
该位使能外部触发注入通道,与位14:11一起配合设置。

位19:17:选择启动规则通道组转换的外部事件
在这里插入图片描述
在扫描模式下,各规则通道时按照设定序列连续、循环转换的。如果不采用扫码模式,可以使用外部触发的方式,当有外部信号触发时再启动ADC转换。

位20:规则通道的外部触发转换模式
在这里插入图片描述
使能规则通道的外部触发转换模式,与位19:17配合设置。

位21:开始转换注入通道
在这里插入图片描述当该位置1后停止规则通道转换(那怕规则通道序列没有执行完毕),开始转入注入通道转换。

位22:开始转换规则通道
在这里插入图片描述
该位置1后开始规则通道序列转换。

位22:温度传感器和VREFINT使能
在这里插入图片描述
温度传感器和VREFINT使能是ADC1特有的通道16、和17两个内部信号转换,用来使能这两个通道功能。下一博客会总结这两块的内容。

4、ADC采样时间寄存器 1(ADC_SMPR1)和 ADC采样时间寄存器 2(ADC_SMPR2)

在这里插入图片描述在这里插入图片描述

ADC_SMPR1寄存器来设置通道10~17的采样时间,每3位设置一个通道。

ADC_SMPR2寄存器来设置通道0~9的采样时间,每3位设置一个通道。

当然可以不设置,这样采样时间就默认为1.5个周期,此时也是通道转换最快的情况。

5、 ADC注入通道数据偏移寄存器x (ADC_JOFRx)(x=1…4)

在这里插入图片描述
这四个寄存器分别对应四个注入通道(注入通道最多四个)

该寄存器中存储的偏移量只用于注入通道的数据对齐,而对规则通道无效。

目的是将注入通道的转换的数据值减去该寄存器存储的偏移量,让这个值可以为负数,并储存在ADC_JDRx寄存器中。

6、ADC看门狗高阀值寄存器(ADC_HTR)和ADC看门狗低阀值寄存器(ADC_LRT)

在这里插入图片描述

这两个寄存器来设置模拟看门狗的上下限的,设置了上下限后,ADC的模拟输入幅值就有了一个范围限定,如果模拟输入超过这个上、下限范围,会触发模拟看门狗,重新转换对应通道,程序不会跳转到下一通道的采集。

7、ADC规则序列寄存器 1~3(ADC_SQR1 ~3)(重点说明一个东西

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

这三个寄存器用来设置某次AD转换中所有规则通道的转换次序,如果多路信号输入,由于选用的引脚不同,即可能不是只用到前几个通道,通道占用是打乱的,那么怎么才能有序的识别出来呢?
比如:
第一路信号使用ADC通道1
第二路信号使用ADC通道5
第三路信号使用ADC通道2
第四路信号使用ADC通道10
第五路信号使用ADC通道14
第六路信号使用ADC通道3
第七路信号使用ADC通道4

则可以将ADC_SQR3的:
位4:0(序列中第一个要转换的通道序号)值1,表示第一个转换通道是通道1,所以位4:0=00001

位9:5(序列中第二个要转换的通道序号)值5,表示第二个转换通道是通道5,所以位9:5=0x00101

位14:10(序列中第三个要转换的通道序号)值2,表示第三个转换通道是通道2,所以位14:10=0x00010

位19:15(序列中第四个要转换的通道序号)值10,表示第四个转换通道是通道10,所以位19:15=0x01010

位24:20(序列中第五个要转换的通道序号)值14,表示第五个转换通道是通道14,所以位24:20=0x01110

位29:25(序列中第六个要转换的通道序号)值3,表示第六个转换通道是通道3,所以位29:25=0x00011

ADC_SQR2寄存器:

位4:0(序列中第七个要转换的通道序号)值4,表示第七个转换通道是通道4,所以位4:0=0x00100

8、ADC注入序列寄存器(ADC_JSQR)

在这里插入图片描述

该寄存器与SQRx(1~3)类似,用来设置注入通道序列顺序的。

接上面例子,将第8、9、10三路信号作为注入通道信号。

第八路信号使用ADC通道6、则JSQR寄存器位4:0=6,表示注入序列执行的第一个通道为通道6,位4:0=0x00110

第九路信号使用ADC通道7、则JSQR寄存器位9:5=7,表示注入序列执行的第二个通道为通道7,位9:5=0x00111

第十路信号使用ADC通道8、则JSQR寄存器位14:10=8,表示注入序列执行的第三个通道为通道8,位14:10=0x01000

9、ADC 注入数据寄存器x (ADC_JDRx) (x= 1…4)

在这里插入图片描述
该寄存器用来储存每次注入序列的每个通道转换完成后的值。需要及时读取,否则会被注入序列的下一次转换结果覆盖。

10、ADC规则数据寄存器(ADC_DR)

在这里插入图片描述

用来储存规则序列的各通道转换结果也需要及时读取,否则会覆盖。

三、ADC相关库函数

ADC的函数主要集中在stm32f10x_adc.h中
在这里插入图片描述使用前需要将.c文件添加进来:
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1、ADC时钟设置函数RCC_ADCCLKConfig()

该函数在stm32f10x_rcc.h中
在这里插入图片描述
参数:
在这里插入图片描述
在这里插入图片描述

表示几分频后作为ADC时钟。上图红色箭头所示,ADC最大时钟频率是14MHZ,默认状态下系统时钟是72MHZ,故至少需要6分频,所以一般情况下设置参数为RCC_PCLK2_Div6即可。

2、ADC初始化函数ADC_Init()(+扫描模式和连续转换区别

在这里插入图片描述
(1)参数1
在这里插入图片描述
表示哪个ADC

(2)参数2
参数2是一个结构体,看一下结构体的各成员变量:

①ADC_Mode
选择ADC的模式
在这里插入图片描述上述的模式:
在这里插入图片描述
之后项目中用到可能会回来总结一下双ADC模式,也可能不总结了。本博已经总结太多了。

正常情况先选则独立模式: ADC_Mode_Independent

②ADC_ScanConvMode

在这里插入图片描述

选择是否开启扫描模式,对于多个规则通道来讲,可以开启扫描模式,每个通道都循环转换;或者想一直显示,可以开启扫描模式。核心意图就是是否让ADC不停的去转换。对于单次转换,可以不设置扫描模式。

③ADC_ContinuousConvMode
设置是否开启连续转换。对于单次转换,可以不设置。

这里区分一下连续转换和扫描模式的区别:
只要记住扫描模式要执行完全部通道的;连续模式是你执行完毕后还要不要自动重新执行。

举个例子:

假如有5个规则通道的序列,且规则序列的顺序被设置好了,转换顺序为A、B、C、D、E
不在扫描模式下:
单次转换模式下:
启动ADC——>执行A——>等待下次启动——>启动ADC——>执行A——>等待下次启动
连续转换模式下:
启动ADC——>执行A——>执行A——>执行A…

扫描模式下:
单次转换模式下:
启动ADC——>执行A——>执行B——>执行C——>执行D——>执行E——>等待ADC启动——>启动ADC——>执行A——>执行B——>执行C——>执行D——>执行E——>等待ADC启动
连续转换模式下:
启动ADC——>执行A——>执行B——>执行C——>执行D——>执行E——>执行A——>执行B——>执行C——>执行D——>执行E——>执行A…

扫描模式下,如果设置了DMA位,在每次EOC后,DMA控制器把规则组通道的转换数据传输到SRAM中。而注入通道转换的数据总是存储在ADC_JDRx寄存器中。

所以扫描模式要配合DMA使用的。

④ADC_ExternalTrigConv
这个参数用来设置启动规则转换组转换的外部事件
在这里插入图片描述
根据需要设置,如果设置了定时器捕获通道信号触发,则输入捕获那一块也得设置。所以一般可以用ADC_ExternalTrigConv_None,采用软件触发。
当然可以灵活运用,使用外部中断,当有外部中断时设置ADC转换也可以实现相应的功能。

⑤ADC_DataAlign
设置ADC数据存储是左对齐还是右对齐
在这里插入图片描述
一般采用右对齐ADC_DataAlign_Right,即低位对齐

⑥ADC_NbrOfChannel

设置规则序列长度,即设置使用ADC几个通道。

3、ADC使能函数ADC_Cmd()

在这里插入图片描述
参数1:
选择哪个ADC

参数2:
DISABLE或ENABLE

4、ADC的DMA使能函数ADC_DMACmd()

在这里插入图片描述

参数1:
选择哪个ADC

参数2:
DISABLE或ENABLE

这个函数使能了DMA,但是DMA还需要另外设置,这个之后博客会有专门一篇博客来总结。

5、ADC中断使能函数ADC_ITConfig()

在这里插入图片描述

参数1:
选择哪个ADC

参数2:
在这里插入图片描述分别是规则通道转换中断、模拟看门狗中断、注入通道转换中断

参数3:
DISABLE或ENABLE

6、ADC初始化取消函数ADC_DeInit()

在这里插入图片描述

从函数体上也可以看到,将ADC的寄存器设置为缺省值。

7、ADC结构体初始化函数ADC_StructInit()

在这里插入图片描述
通过函数体可以看到,设置ADC初始化函数的那个结构体参数为:
独立模式、非扫描模式、非连续模式、定时器1通道1捕获信号触发、右对齐、一个ADC通道。

8、ADC复位校准函数ADC_ResetCalibration()

参数:
选择哪个ADC

是对CR2寄存器位3的操作。
在这里插入图片描述

9、获取复位校准状态函数FlagStatus ADC_GetResetCalibrationStatus()

参数:

选择哪个ADC

看一下函数体:
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

当处于复位校准状态时,CR2寄存器位3为1,此时执行:

ADCx->CR2 & CR2_RSTCAL_Set

后值为1

当复位校准完成后,位3硬件置0,执行:

ADCx->CR2 & CR2_RSTCAL_Set

后值为0。
故该函数返回SET表示正处于校准状态,返回RESET表示复位校准已经完成。

10、ADC校准函数ADC_StartCalibration()

在这里插入图片描述

该函数是对CR2寄存器位2的操作
在这里插入图片描述
将该位置1,进行ADC校准。
可以在启动ADC之前使用该函数校准ADC

11、获取ADC校准状态函数FlagStatus ADC_GetCalibrationStatus()

在这里插入图片描述

当处于校准状态时,CR2寄存器位2为1,此时执行:

ADCx->CR2 & CR2_CAL_Set

后值为1

当复位校准完成后,位2硬件置0,执行:

ADCx->CR2 & CR2_CAL_Set

后值为0。
故该函数返回SET表示正处于校准状态,返回RESET表示校准已经完成。

12、启用或禁用ADC软件转换函数ADC_SoftwareStartConvCmd()

在这里插入图片描述
在这里插入图片描述
参数1:
选择哪个ADC

参数2:
DISABLE或ENABLE

当参数为ENABLE时:
执行:

  ADCx->CR2 |= CR2_EXTTRIG_SWSTART_Set;

在这里插入图片描述

此时CR2寄存器的位22和位20都为1,即使用外部时间触发转换,并同时开启转换通道(虽然打开,但只有当外部信号到了触发完成后才开始转换)。

故当参数为ENABLE是选择外部触发来开启转换

当参数为DISABLE时:
执行:

ADCx->CR2 &= CR2_EXTTRIG_SWSTART_Reset;

此时CR2寄存器的位22和位20都为0,即不使用外部时间触发转换,并关闭转换通道
故当参数为DISABLE是选择软件触发来开启转换

13、ADC软件开启转换状态获取函数FlagStatus ADC_GetSoftwareStartConvStatus()

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

该函数是对CR2寄存器位22的操作
当转换通道开启时,该位为1,执行

ADCx->CR2 & CR2_SWSTART_Set

后的值为1,返回SET。

当转换通道未开启时,该位为0,执行

ADCx->CR2 & CR2_SWSTART_Set

后的值为0,返回RESET。

故返回SET时表示转换通道开启,返回RESET时表示转换通道未开启。

14、ADC间断模式下通道数设置函数ADC_DiscModeChannelCountConfig()

在这里插入图片描述
参数1:
选择哪个ADC

参数2:
为1~8之间的数。
该函数是对CR1寄存器位15:13的操作
在这里插入图片描述
间断模式之前总结过,不在赘述
理解一下以下代码:

 tmpreg1 = ADCx->CR1;
  /* Clear the old discontinuous mode channel count */
  tmpreg1 &= CR1_DISCNUM_Reset;
  /* Set the discontinuous mode channel count */
  tmpreg2 = Number - 1;
  tmpreg1 |= tmpreg2 << 13;
  /* Store the new register value */
  ADCx->CR1 = tmpreg1;

第一行是把CR1目前的状态赋值给tmpreg1 。

第二行是tmpreg1 =tmpreg1 &0xffff1fff,即对CR1寄存器位15:13清零

第三行对第二个参数Number -1,因为输入1的时候表示间断模式下一次转换的通道数是1,此时位15:13=0,故位15:13的值与Number 相差1.

第四行对tmpreg2 先左移13位后Number - 1的值与位15:13对齐了,然后或运算,覆盖位15:13

第五行直接赋值,对CR1寄存器的位15:13重新赋值。

15、ADC间断模式使能函数ADC_DiscModeCmd()

在这里插入图片描述在这里插入图片描述在这里插入图片描述
参数1:
选择哪个ADC

参数2:
ENABLE或DISABLE
该函数是对CR1寄存器位11操作,当参数2为ENABLE时表示设置为间断模式,当参数2为DISABLE时设置为不使用间断模式。

16、ADC规则通道设置函数ADC_RegularChannelConfig()

在这里插入图片描述

参数1:
选择哪个ADC

参数2:
表示哪个规则通道:
在这里插入图片描述
参数3:
规则序列中参数2所在的第几位。
在这里插入图片描述

参数3范围是1~16,对应SQR1 ~3寄存器,之前非常详细的说过。

参数4:
在这里插入图片描述在这里插入图片描述

参数4是选择采用时间。

17、外部触发转换设置函数ADC_ExternalTrigConvCmd()

在这里插入图片描述
在这里插入图片描述
参数1:
选择哪个ADC
参数2:
ENABLE或DISABLE
该函数是对CR2寄存器的位20操作,当参数2为ENABLE时表示使用外部触发ADC转换,当参数为DISABLE时表示不使用外部触发ADC转换。

这个函数与本目:12、启用或禁用ADC软件转换函数ADC_SoftwareStartConvCmd()功能类似,总结的时候一直觉得哪个函数不太好,所以以后可以用这个函数。

18、获取ADC转换值函数uint16_t ADC_GetConversionValue()

在这里插入图片描述
参数:
选择哪个ADC
该函数是对DR寄存器的操作,通道转换结束后会把转换值存储在DR寄存器中,通过该函数获取转换值

19、返回DR寄存器值函数uint32_t ADC_GetDualModeConversionValue(void);

在这里插入图片描述
在这里插入图片描述

20、自动的注入通道转换设置函数ADC_AutoInjectedConvCmd()

在这里插入图片描述

参数1:
选择哪个ADC

参数2:
ENABLE或DISABLE

该寄存器是对CR1寄存器位10的操作

在这里插入图片描述

当第二个参数为ENABLE时开启自动注入通道转换,当参数为DISABLE时关闭自动注入通道转换。

之前也总结过,当自动注入通道转换时,每次执行完规则通道,都自动去执行注入通道。而注入通道是把它看做是“中断”来用的,所以这个自动注入通道转换需慎用!

21、注入通道上的间断模式设置函数ADC_InjectedDiscModeCmd()

在这里插入图片描述

参数1:
选择哪个ADC

参数2:
ENABLE或DISABLE

该函数是对CR1寄存器位12的操作。

在这里插入图片描述
当参数2为ENABLE时,位12为1,表示开启注入通道的间断模式,当参数2为DISABLE时,位12为0,表示禁用注入通道间断模式。

22、启动注入通道组转换的外部事件设置函数ADC_ExternalTrigInjectedConvConfig()

在这里插入图片描述参数1:
选择哪个ADC

参数2:
选择哪个事件:

在这里插入图片描述

该函数是对CR2寄存器位14:12的设置。
在这里插入图片描述
选择哪种触发方式作为注入通道转换的外部事件。

23、外部事件触发注入通道转换的使能函数ADC_ExternalTrigInjectedConvCmd()

在这里插入图片描述参数1:
选择哪种ADC

参数2:
ENABLE或DISABLE

在这里插入图片描述
还函数是对CR2寄存器为15的操作
在这里插入图片描述
当参数为ENABLE时,位15为1,表示使用外部事件启动注入通道转换;参数为DISABLE时,位15为0,表示不用外部事件启动注入通道转换

24、软件触发注入通道使能函数ADC_SoftwareStartInjectedConvCmd()

12、启用或禁用ADC软件转换函数ADC_SoftwareStartConvCmd()类似
在这里插入图片描述
参数1:
选择哪种ADC

参数2:
ENABLE或DISABLE

该函数是对CR2寄存器位15和位21的操作
在这里插入图片描述
在这里插入图片描述

总觉得这个函数和12这个函数编写的反人类,能稍微改一下就好了。

当参数为ENABLE时,位15和位21都置1,表示使用外部事件启动转换并开始转换注入通道(虽然打开转换通道,但是只有外部触发信号到来之时才转换)

当参数为DISABLE时,位15和位21都置0,表示禁用外部事件启动注入通道,并关闭注入通道转换。

25、获取ADC注入通道状态函数FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus()

在这里插入图片描述
参数:
选择哪个ADC

该函数是对CR2寄存器位21的操作。
当注入通道处于转换状态时,位21为1,此时返回SET。
当注入通道未处于转换状态时,位21为0,此时返回RESET。

注意当转换开始后硬件马上清除位21,所以转换过程中该函数返回的还是RESET,所以该函数慎用!

26、ADC注入通道设置函数ADC_InjectedChannelConfig()

在这里插入图片描述参数1:

选择哪个ADC

参数2:
选择哪个通道
在这里插入图片描述参数3:
在这里插入图片描述

大于等于1小于等于4表示在注入通道序列中参数2的通道所在序列中的第几位置。

参数4:

采用周期
在这里插入图片描述
在这里插入图片描述

27、注入通道序列长度设置函数ADC_InjectedSequencerLengthConfig()

在这里插入图片描述

参数1:
选择哪个ADC

参数2:
选择注入通道序列长度,由于注入通道最多为4个,故长度最大为4
在这里插入图片描述

28、设置注入通道偏移量函数ADC_SetInjectedOffset()

在这里插入图片描述
参数1:
选择哪个ADC

参数2:
选择注入通道
在这里插入图片描述
参数3:
设置偏移量,因为注入通道转换结束后输出的是12位数,故偏移量要小于等于0xfff
在这里插入图片描述
这里的__IO uint32_t*的操作应该是把值存储在以它为地址的寄存器中的操作,不好理解的话可以把底层改为:

void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset)
{
  __IO uint32_t tmp = 0;
  
  /* Check the parameters */
  assert_param(IS_ADC_ALL_PERIPH(ADCx));
  assert_param(IS_ADC_INJECTED_CHANNEL(ADC_InjectedChannel));
  assert_param(IS_ADC_OFFSET(Offset));  
  
  tmp = (uint32_t)ADCx;
  tmp += ADC_InjectedChannel;
  
  /* Set the selected injected channel data offset */
//  *(__IO uint32_t *) tmp = (uint32_t)Offset;
	

//	if(ADC_InjectedChannel==ADC_InjectedChannel_1)
//		ADCx->JOFR1=Offset;
//	else if(ADC_InjectedChannel==ADC_InjectedChannel_2)
//		ADCx->JOFR2=Offset;
//	else if(ADC_InjectedChannel==ADC_InjectedChannel_3)
//		ADCx->JOFR3=Offset;
//	else
//		ADCx->JOFR4=Offset;
}

29、获取注入通道转换值函数uint16_t ADC_GetInjectedConversionValue()

在这里插入图片描述

参数1:
选择哪个ADC

参数2:
选择哪个通道

这个函数返回的JDR寄存器里的值,通过*(__IO uint32_t*)操作。不好理解的话可以将底层改为:

uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel)
{
  __IO uint32_t tmp = 0;
  
  /* Check the parameters */
  assert_param(IS_ADC_ALL_PERIPH(ADCx));
  assert_param(IS_ADC_INJECTED_CHANNEL(ADC_InjectedChannel));

  tmp = (uint32_t)ADCx;
  tmp += ADC_InjectedChannel + JDR_Offset;
  
  /* Returns the selected injected channel conversion data value */
  //return (uint16_t) (*(__IO uint32_t*)  tmp);   
	if(ADC_InjectedChannel==ADC_InjectedChannel_1)
		return ADCx->JDR1;
	else if(ADC_InjectedChannel==ADC_InjectedChannel_2)
		return ADCx->JDR2;
	else if(ADC_InjectedChannel==ADC_InjectedChannel_3)
		return ADCx->JDR3;
	else
		return ADCx->JDR4;
}

30、模拟看门狗设置函数ADC_AnalogWatchdogCmd()

在这里插入图片描述
参数1:
选择哪个ADC

参数2:
设置哪个模式:
在这里插入图片描述
具体意思看下面注释:

 ADC_AnalogWatchdog_SingleRegEnable     
  //扫描模式中单一规则通道使用模拟看门狗
  
 ADC_AnalogWatchdog_SingleInjecEnable    
 //扫描模式中单一注入通道使用模拟看门狗
  
ADC_AnalogWatchdog_SingleRegOrInjecEnable  
//扫描模式中单一规则通道和单一注入通道使用模拟看门狗

 ADC_AnalogWatchdog_AllRegEnable      
 //规则通道使用模拟看门狗(非单一、不一定需要扫描模式)    
   
 ADC_AnalogWatchdog_AllInjecEnable   
  //注入通道使用模拟看门狗(非单一、不一定需要扫描模式)   
            
 ADC_AnalogWatchdog_AllRegAllInjecEnable   
 //规则通道和注入通道使用模拟看门狗(非单一、不一定需要扫描模式) 
 
 ADC_AnalogWatchdog_None     
 //不使用模拟看门狗            

31、模拟看门狗阈值设置函数ADC_AnalogWatchdogThresholdsConfig()

在这里插入图片描述
参数1:

选择哪个ADC

参数2:
模拟看门狗上限

参数3:
模拟看门狗下限

上下限值不能超过0xfff,毕竟输入模拟信号的幅值要在这个阈值区间内,二转换值存储在12位寄存器中,故需要保证阈值上下限最大不能超过0xfff。

32、通道的模拟看门狗设置函数ADC_AnalogWatchdogSingleChannelConfig()

在这里插入图片描述参数1:
选择哪种ADC

参数2:
选择通道

该函数是对CR1寄存器位4:0的操作。通过该函数,设置对应通道使用模拟看门狗
在这里插入图片描述

33、启用或禁用温度传感器和Vrefint通道函数ADC_TempSensorVrefintCmd()

在这里插入图片描述
参数:

ENABLE或DISABLE

该函数是对CR2寄存器位23的操作。通过该函数使能了温度传感器通道和Vrefint通道
在这里插入图片描述

34、获取ADC转换状态函数FlagStatus ADC_GetFlagStatus()

在这里插入图片描述

参数1:
选择哪个ADC

参数2:
选择哪种中断类型

在这里插入图片描述
中断类型如下注释:

 ADC_FLAG_AWD    		//模拟看门狗中断                           
 ADC_FLAG_EOC         //规则通道转换结束中断                      
 ADC_FLAG_JEOC        	//注入通道转换结束中断                     
 ADC_FLAG_JSTRT        //注入通道转换开始中断             
 ADC_FLAG_STRT   			//规则通道转换开始中断

返回SET表示转换完成,返回RESET表示还在转换。

35、清除标志位函数ADC_ClearFlag()

在这里插入图片描述
参数1:

选择哪个ADC

参数2:

选择哪个标志位,参数和上一个函数参数一样
在这里插入图片描述

 ADC_FLAG_AWD    		//模拟看门狗标志位                          
 ADC_FLAG_EOC         //规则通道转换结束标志位                      
 ADC_FLAG_JEOC        	//注入通道转换结束标志位                    
 ADC_FLAG_JSTRT        //注入通道转换开始标志位            
 ADC_FLAG_STRT   			//规则通道转换开始标志位 

36、获取中断状态函数ADC_GetITStatus()

在这里插入图片描述
参数1:
选择哪个ADC

参数2:

选择哪个中断在这里插入图片描述

 ADC_IT_EOC    //规则通道转换结中断                                
 ADC_IT_AWD    //模拟看门狗中断                           
 ADC_IT_JEOC  //注入通道转换结束中断                              

返回SET表示发生了中断,返回RESET表示未发生中断。

37、清除中断标志位函数ADC_ClearITPendingBit()

在这里插入图片描述
参数1:
选择哪个ADC

参数2:
选择哪个中断
在这里插入图片描述

38、最后说明

ADC的库函数有点多,如果可以的话库函数和寄存器结合起来编程,因为有的库函数的确麻烦,还不如寄存器直接操作好。

四、ADC编程顺序

1、使能GPIO时钟、使能ADC时钟

使用RCC_APB2PeriphClockCmd()函数使能时钟

2、设置ADC时钟分频

根据需要选择分频系数,要注意F1的ADC时钟最大为14MHZ,然后看看自己板子、底层默认系统时钟是多少、AHB预分频系数是多少然后选择ADC分配系数。

我的板子默认系统时钟72MHZ,AHB预分频系数为1。故选择ADC预分频系数为6,保证ADC时钟不超过14MHZ

使用RCC_ADCCLKConfig()函数设置ADC时钟分频

3、GPIO初始化

使用GPIO_Init()函数来初始化GPIO,需要注意的是初始化函数选择引脚的时候一定要跟外部信号接线对应起来,即连线和配置相匹配。

4、复位ADC

使用ADC_DeInit()函数复位ADC

5、ADC结构体初始化(可以不用)

使用ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);函数进行初始化,需要注意的是传递的结构体参数一定要是ADC_Init()的结构体参数。

6、ADC初始化

使用ADC_Init()函数,具体用法(三)中已经详述了

7、设置规则通道序列

如果需要几个规则通道转换,则需要设置每个通道的转换顺序,使用ADC_RegularChannelConfig()设置

8、注入通道序列长度设置(如果需要)

使用ADC_InjectedSequencerLengthConfig()设置

9、注入通道设置函数(如果需要)

如果需要几个注入通道转换,则需要设置每个通道的转换顺序,使用ADC_InjectedChannelConfig()设置

10、使能ADC

使用ADC_Cmd使能ADC

11、开启复位校准

使用ADC_ResetCalibration()函数,需要等待校准完成:

	while(ADC_GetResetCalibrationStatus(ADC1)) //等待复位校准结束
	{
		//空
	}

12、开启ADC校准

使用ADC_StartCalibration()函数开启ADC校准,需要等待校准完成:

while(ADC_GetCalibrationStatus(ADC1))//等待校准结束
{
//空
}

13、设置规则通道

使用ADC_RegularChannelConfig()函数设置规则通道,如果有多个规则通道,则多次调用这个函数完成各个规则通道配置。

14、使能软件启动转换(或外部触发转换(如果需要))

使用ADC_SoftwareStartConvCmd()使能软件触发转换。
如果需要外部触发,则ADC_Init()函数中的ADC_ExternalTrigConv参数需要注意设置。注意使能后就同时开启了转换。需要等待转换 结束:ADC_GetFlagStatus()

15、获取转换值

使用ADC_GetConversionValue()函数

16、最后说明

这个顺序不固定,还有一些配置比如看门狗、中断什么的如果用到,需要配置。

因为外部触发或者软件触发不能单独对某一通道作用,即如果设置了多个通道,软件触发后,会全部开始转换,而又不是并列执行,所以最终结果只会是转换设置的序列的第一个通道,第一个通道转换完成后标志位置1,不在进行转换(单次转换)。而对于连续模式,也只能是单个通道的连续转换,对于扫描模式,虽然所有通道都可以执行,但是由于通道转换结果只存储在一个寄存器中断。

说的有点乱,总而言之,不能同时开启所有通道转换,不能同时接收到所有通道转换结果

所以最佳解决办法是把规则通道设置**ADC_RegularChannelConfig**、软件开启转换ADC_SoftwareStartConvCmd、等待转换完成ADC_GetFlagStatus、获取转换值ADC_GetConversionValue()写在一个调用函数中,实时去调用这个函数就行,传递的参数可以是通道序号、规则序列位号、采样周期等待。

五、例1(单通道转换一次)

手中刚好有几块松下的激光位移传感器。请添加图片描述

不过该传感器模拟输出是5V,STM32F1的ADC通道输入都是3.3V的。具体可以查看芯片手册,ADC引脚都是不带FT标志的,所以该传感器不能用。
在这里插入图片描述

所以举例就放弃用它了。

刚好手中有MC遥控器和接收机,接收机输出信号为PWM,且有8个通道,可以用来举例。其PWM波形如下:
请添加图片描述虽然PWM算是数字信号(我是这么理解的),但是博客开头也说到过,不要把ADC看成时模拟转数字,而要看成未知信号转已知信号。

1、题

使用ADC1通道1(PA0),将MC接收机输出的PWM信号的幅值量化,并在串口中显示出来。

2、分析

按照编程顺序一步步的来分析处理:
①使能PA0、ADC1时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE);

②设置ADC时钟分频

之前也说到过,一定要保证ADC时钟频率不超过14MHZ,我的板子外接25MHZ晶振,经过底层时钟配置(可自主修改,之前博客有过总结)后系统时钟为72MHZ,AHB预分频系数为1.所以到达ADC预分频器的时钟为72MHZ,故采用6分频。

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

③配置GPIO
ADC处理模拟信号,所以将PWM当做模拟信号输入,采用模拟输入模式。

  GPIO_InitTypeDef	 GPIO_InitStructure;
  
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

④ADC复位

其实可以不用复位,上电后会自动清除寄存器,不过养成习惯,复位一下比较严谨。

	ADC_DeInit(ADC1);

⑤ADC初始化
因为只有一个通道,故采用单次转换、不连续、不扫描,一般采用右对齐

    ADC_InitTypeDef		 ADC_InitStruct;
    
	ADC_InitStruct.ADC_ContinuousConvMode=DISABLE;//不连续模式
	ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel=1;
	ADC_InitStruct.ADC_ScanConvMode=DISABLE;//不扫描模式
	ADC_Init(ADC1, &ADC_InitStruct);

⑥使能ADC

ADC_Cmd(ADC1,ENABLE);

⑦复位校准、并等待校准

	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)) //等待复位校准结束
	{
	}

⑧ADC校准并等待校准

	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1))//等待校准结束
	{
	}

⑨编写获取函数
由于需要不停的获取ADC的值,每获取一次,就软件开启转换,所以将通道设置、软件启动专门放置一个函数里,方便主函数的while循环中调用。

因为只有一个通道工作,故PA0通道设置为规则序列的第一位。

u16 adc_getval(u8 ch)
{
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_1Cycles5);//规则通道设置
	
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发、并开启转换
	while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC))
	{
		//转换过程中执行空语句,等待转换完成
	}
	return  ADC_GetConversionValue(ADC1);
}

⑩结果转化
每次转化值都存储在DR寄存器,该寄存器12位有效。
注意DR寄存器储存的不是最终结果,DR寄存器12位有效,即将0~3.3V的电平映射到0 ~4096,DR寄存存储的是这个映射值,真正的电平值为:DR值*(3.3/4096))

3、完整代码

(1)adc.h代码

//adc.h
#ifndef _ADC_
#define _ADC_
#include "stm32f10x.h"
void adc_Init(void);
u16 adc_getval(u8 ch);
#endif

(2)adc.c代码

//adc.c
#include "adc.h"
#include "stm32f10x.h"
void adc_Init(void)
{
	GPIO_InitTypeDef	 GPIO_InitStructure;
	ADC_InitTypeDef		 ADC_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	ADC_DeInit(ADC1);
	
	ADC_InitStruct.ADC_ContinuousConvMode=DISABLE;//不连续模式
	ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel=1;
	ADC_InitStruct.ADC_ScanConvMode=DISABLE;//不扫描模式
	ADC_Init(ADC1, &ADC_InitStruct);
	
	ADC_Cmd(ADC1,ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)) //等待复位校准结束
	{
	
	}
	
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1))//等待校准结束
	{
	}
}

u16 adc_getval(u8 ch)
{
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_1Cycles5);//规则通道设置
	
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发、并开启转换
	while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC))
	{
		//转换过程中执行空语句,等待转换完成
	}
	return  ADC_GetConversionValue(ADC1);
}

(3)main.c代码

#include "stm32f10x.h"
#include "delay.h"
#include  "adc.h"
#include "usart.h"
u16 a=0;
 int main(void)
 {	
	delay_init();
	adc_Init();
	uart_init(115200);	
	 while(1)
	 {
	  a=adc_getval(ADC_Channel_0);
	  printf("ADC_VAL= %d \r\n",a);//显示电压值
	  printf("PWM_VAL= %1.3f V \r\n",(float)a*(3.3/4096));//显示电压值
	  printf("\r\n");
	  delay_ms(1000); 
	 }
 }

4、效果

请添加图片描述

在这里插入图片描述

上图红色框框为PWM高电平时的转换值,其他的为低电平时的值。因为没有输入捕获,不知道啥时候是高电平啥时候是低电平,而且还延时1s,完全打乱了转换,故这些结果都是随机高、低电平的。

六、举例(多规则通道,扫描模式转换)

1、题

使用ADC1通道0(PA0)、通道1(PA1)、通道4(PA4)、通道5(PA5)分别接MC接收机的四个信号,用ADC扫描模式转换输入信号的电平,并从串口中打印出来。

2、分析

相比较例1,本例题多了三路信号所以只总结一下需要修改的部分。

(1)规则通道长度
在ADC初始化函数中,把规则序列长度改为4
在这里插入图片描述

(2)获取函数中,传递的参数不仅仅是通道了,还需要对应通道在规则序列中的序号。
在这里插入图片描述

3、完整代码

(1)adc.h代码

//adc.h
#ifndef _ADC_
#define _ADC_
#include "stm32f10x.h"
void adc_Init(void);
u16 adc_getval(u8 ch,u8 num);
#endif

(2)adc.c代码

//adc.c
#include "adc.h"
#include "stm32f10x.h"

void adc_Init(void)
{
	GPIO_InitTypeDef	 GPIO_InitStructure;
	ADC_InitTypeDef		 ADC_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	ADC_DeInit(ADC1);
	
	ADC_InitStruct.ADC_ContinuousConvMode=DISABLE;//不连续模式
	ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel=4;
	ADC_InitStruct.ADC_ScanConvMode=DISABLE;//不扫描模式
	ADC_Init(ADC1, &ADC_InitStruct);
	
	ADC_Cmd(ADC1,ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)) //等待复位校准结束
	{
	}
	
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1))//等待校准结束
	{
	}
	
}

u16 adc_getval(u8 ch ,u8 num)//通道序号、规则序号
{
	ADC_RegularChannelConfig(ADC1, ch, num, ADC_SampleTime_1Cycles5);//规则通道设置
	
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发、并开启转换
	while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC))
	{
		//转换过程中执行空语句,等待转换完成
	}
	return  ADC_GetConversionValue(ADC1);
}

(3)main.c代码

//main.c
#include "stm32f10x.h"
#include "delay.h"
#include  "adc.h"
#include "usart.h"
u16 a,b,c,d;
 int main(void)
 {	
	delay_init();
	adc_Init();
	uart_init(115200);	
	 while(1)
	 {
	  a=adc_getval(ADC_Channel_0,1);//PA0设置为通道0、规则序列1
	  b=adc_getval(ADC_Channel_1,2);//PA1设置为通道1、规则序列2
	  c=adc_getval(ADC_Channel_4,3);//PA4设置为通道4、规则序列3
	  d=adc_getval(ADC_Channel_5,4);//PA5设置为通道5、规则序列4
	 
	  printf("ADC_PA0= %d          PWM_PA0= %1.3f V \r\n",a,(float)a*(3.3/4096));//显示通道0
	  printf("ADC_PA1= %d          PWM_PA1= %1.3f V \r\n",b,(float)b*(3.3/4096));//显示通道1
	  printf("ADC_PA4= %d          PWM_PA4= %1.3f V \r\n",c,(float)c*(3.3/4096));//显示通道4
	  printf("ADC_PA5= %d          PWM_PA5= %1.3f V \r\n",d,(float)d*(3.3/4096));//显示通道5
	  printf("\r\n");
	  delay_ms(1000); 
	 }
 }

4、效果

在这里插入图片描述

七、例3(单通道连续模式)

1、题

使用ADC1通道1的连续模式转换MC接收机的一路信号并从串口打印出来

2、分析

在主函数中软件启动ADC1次,然后while循环例输出转换值,如果是连续模式,则会持续从串口打印输出,且数值上有变化。

需要改动的地方:
(1)ADC初始化时设置连续输出模式,规则通道数为1
在这里插入图片描述(2)主函数中只需启动一次ADC
在这里插入图片描述

3、完整代码

(1)adc.h代码

//adc.h
#ifndef _ADC_
#define _ADC_
#include "stm32f10x.h"
void adc_Init(void);
u16 adc_getval(u8 ch,u8 num);
#endif

(2)adc.c代码

//adc.c
#include "adc.h"
#include "stm32f10x.h"

void adc_Init(void)
{
	GPIO_InitTypeDef	 GPIO_InitStructure;
	ADC_InitTypeDef		 ADC_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	ADC_DeInit(ADC1);
	
	ADC_InitStruct.ADC_ContinuousConvMode=ENABLE;//连续模式
	ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel=1;
	ADC_InitStruct.ADC_ScanConvMode=DISABLE;//不扫描模式
	ADC_Init(ADC1, &ADC_InitStruct);
	
	ADC_Cmd(ADC1,ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)) //等待复位校准结束
	{
	
	}
	
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1))//等待校准结束
	{
	}
}

(3)main.c代码

//main.c
#include "stm32f10x.h"
#include "delay.h"
#include  "adc.h"
#include "usart.h"
u16 a,b,c,d;
 int main(void)
 {	
	delay_init();
	adc_Init();
	uart_init(115200);	
	 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_1Cycles5);//规则通道设置
	 ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发、并开启转换
	 while(1)
	 {
		 while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC))
		{
		//转换过程中执行空语句,等待转换完成
		}
	  a=ADC_GetConversionValue(ADC1);//PA0设置为通道0、规则序列1
	
	  printf("ADC_PA0= %d          PWM_PA0= %1.3f V \r\n",a,(float)a*(3.3/4096));//显示通道0
	  printf("\r\n");
	 }
 }

4、效果

在这里插入图片描述数值上是有变化的,说明在不停的转换。

八、结束语

本篇博客写的我身心疲惫呀,还想总结一下注入通道、看门狗、外部触发转换等,但是到最后还是放弃了,等度过这段烦躁期可能会回来继续总结这方面的东西。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-04 17:42:22  更:2021-09-04 17:43:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:36:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码