目录
1. ADC是什么
2. ADC主要特性
3. ADC框图
3.1 ADC开关控制
3.2 ADC时钟
3.3 通道选择
3.4 单次转换模式和连续转换模式
3.5 时序图
3.6 模拟看门狗
4 温度传感器
5. ADC中断
6. ADC初始化结构体
6.1 ADC相关实验配置
7. 相关寄存器
7.1 ADC控制寄存器:ADC_CR1和ADC_CR2
7.2 ADC通用控制寄存器:ADC_CCR
7.3 ADC采样时间寄存器:ADC_SMPR1和ADC_SMPR2
7.4 ADC规则序列寄存器:ADC_SQRx(x取值1~3)
7.5 ADC规则数据寄存器:ADC_DR
7.6 ADC状态寄存器:ADC_SR
8. 库函数配置ADC1的通道5进行AD转换
9. 实验程序
9.1 main.c
9.2 ADC.c
9.3 ADC.h
首先明确,这里提到的ADC不是我们游戏中的ADC,这里提及的ADC是STM32的一种重要外设功能,ADC英文全程:Analog to Digital,又叫做模拟数字转换器。
1. ADC是什么
在高中的物理中,我们学习过电压表、电流表和万用表的使用,这些器件可以用来测量电路中的分路电流、电压值;强大的STM32单片机中也存在着这样的外设,称作模数转换器ADC;
很好理解,模数转换器就是把模拟量转换成数字量;数字对于我们来说是很亲切的,之所以存在这样的外设,也是为了更方便于使用者的理解和操作。
模拟量就如电压值、光敏电阻阻值、热敏电阻阻值等,通过该转换器可以转换为我们肉眼可见的数字。
STM32F4的ADC是12位逐次趋近型模数转换器。具有多达19个复用通道,可测量来自16个外部源、两个内部源通道的信号。ADC的结果存储在一个左对齐或者右对齐的16位数据寄存器中。
ADC具有模拟看门狗特性,允许应用检测输入电压是否超过了用户自定义的阈值上限或者下限。
STM32F4xx系列一般都有3个ADC,这些ADC可以独立使用,也可以使用双重/三重模样,来提高采样率。STM32F4的ADC是12位逐次逼近型的模拟数字转换器,它有19个通道,可以测16个外部源、2个内部源和Vbat通道的信号。STM32F4的ADC最大的转换速率为2.4Mhz,也就是转换时间为0.41us。
2. ADC主要特性
- 可配置12位、10位、8位或6位分辨率
- 在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断
- 单次和连续转换模式
- 用于自动将通道0转换为通道“n”的扫描模式
- 数据对齐以保持内置数据一致性
- 可独立设置各通道采样时间
- 外部触发器选项,可为规则转换和注入转换配置极性
- ADC电源要求:全速运行时2.4V到3.6V,慢速运行时1.8V
3. ADC框图
1. 电压输入范围:
表示正模拟参考电压输入,代表ADC高/正参考电压;
表示模拟电源输入,代表模拟电源电压等于;
表示负模拟参考电压输入,代表ADC低/负参考电压;
表示模拟电源接地输入,代表模拟电源接地电压等于;
输入电压: <= VIN <=
和 接地,把和接3V3,表示得到ADC的输入电压范围为0-3.3V;
STM32的ADC只能测0-3.3V的电压,如果超过了这个范围,只能在单片机上改变电压的范围(通过相应芯片的转电平作用),以保证能够被STM32的GPIO口识别。
2. 输入通道:
STM32的每个ADC都具有18个通道,其中外部通道16个:
STM32的ADC有16条复用通道。外部16个通道在转换的过程中又分为规则通道和注入通道。
可以将转换分为两组:规则转换和注入转换。每个组包含一个转换序列,该序列可按任意顺序在任意通道上完成。
其中规则通道由规则序列寄存器ADC_SQRx(x可以取值为1 2 3)来配置,注入通道由注入序列寄存器ADC_JSQR来配置。
一个规则转换组最多由16个转换构成。必须在ADC_SQRx寄存器中选择转换序列的规则通道及其顺序。规则转换组中的转换总数必须写入ADC_SQRx寄存器中的L[3:0]位。
一个注入转换组最多由4个转换构成。必须在ADC_JSQR寄存器中选择转换序列的注入通道及其顺序。注入转换组中的转换总数必须写入ADC_JSQR寄存器中的L[1:0]位。
顾名思义,两个通道的区别就是:规则通道就是很规矩的意思,我们一般使用的就是这个规则通道。注入可以理解为插入、插队的意思,是一种不安分的通道,有点类似于中断程序;注入通道只有在规则通道存在时才会出现。
3. 转换顺序:
输入通道和注入通道的转换顺序分别由规则序列寄存器ADC_SQRx(x可以取值为1 2 3)和注入序列寄存器ADC_JSQR来配置。
4. 触发源:
触发源相当于一个信号,用来告诉ADC可以开始转换了。
触发方式又分为软件触发和外部事件触发(内部定时器/外部IO)。分别对应于上图中的左侧TIM和右侧TIM。
5. 转换时间:
转换时间:Tconv=采样时间+12个周期
采样时间:ADC需要若干个ADC_CLK周期完成对输入的模拟量进行采样,采样的周期数可通过ADC采样时间寄存器ADC_SMPR1和ADC_SMPR2的SMPx[2:0]位设置,ADC_SMPR2控制的是通道0~9,ADC_SMPR1控制的是通道10~17。每个通道可以分别用不同的时间采样。其中采样周期最小是1.5个,也就是说,我们要达到最快的采样,那么应该设置采样周期为31.5个周期,这里的周期就是1/ADC_CLK。
ADC_CLK:ADC模拟电路时钟,最大值为14M,由PCLK2提供,还可以进行分频,2/4/6/8,RCC_CFGR的ADCPRE[1:0]设置。PCLK2=72M。
数字时钟:RCC_APB2ENR,用于访问寄存器。
ag. 最短的转换时间:Tconv=采样时间+12个周期 (其中PCLK2=72M,ADC_CLK=72/6=12M ,Tconv=1.5+12.5=14=14/12us=1.17us)
6. 数据寄存器:
一切准备就绪后,ADC转换后的数据根据转换组不同,规则组的数据存放在ADC_DR寄存器中,注入组的数据放在JDRx寄存器中。
数据寄存器ADC_DR(ADC regular data register)
位31:16 保留,必须保持复位值
位15:0 DATA[15:0]:规则数据(Regular data) 这些位为只读。它们包括来自规则通道的转换结果。
该寄存器的1~16位有效,用于存放独立模式转换完成数据。当在双ADC模式(ADC1和ADC2同时使用)下,ADC1放在低16位上,ADC2放在高16位上。
7. 中断:
STM32F4相比于F1增加了DMA溢出标志。由模拟看门狗来检测采样的模拟量信号值的下限和上限。
电压转换:
所谓电压转换就是如何根据数字量去算出模拟量? 也可以说是外部电压假设是2.5V,通过ADC转换成数字量,也就是数字,存放在数据寄存器ADC_DR中,电压转换就是通过数据寄存器中的数字去反推出外部电压。
1. 电压输入范围为:0~3.3V
2. 分辨率为12位
3. 最小精度为:3.3/2^12
4. 设数字量为x,则有模拟量Y=(3.3/2^12)* x
3.1 ADC开关控制
可以通过将ADC控制寄存器ADC_CR2寄存器中的ADON位置1来为ADC供电。首次将ADON位置1时,会将ADC从掉电模式中唤醒。
SWSTART或JSWSTART位置1,启动AD转换。
可通过将ADON位清零来停止转换并使ADC进入掉电模式,在此模式下,ADC几乎不耗电,也可以说进入了低功耗模式。
3.2 ADC时钟
用于模拟电路的时钟:ADCCLK,所有ADC共用
此时钟来自于经可编程预分频器分频的APB2时钟,该预分频器允许ADC在、/4、/6或/8下工作。
用于数字接口的时钟:(用于寄存器读/写访问)
此时钟等效于APB2时钟。可以通过RCC APB2外设时钟使能寄存器(RCC_APB2ENR)分别为每个ADC使能/禁止数字接口时钟。
3.3 通道选择
STM32的ADC有16条复用通道。外部16个通道在转换的过程中又分为规则通道和注入通道。
可以将转换分为两组:规则转换和注入转换。每个组包含一个转换序列,该序列可按任意顺序在任意通道上完成。
一个规则转换组最多由16个转换构成。必须在ADC_SQRx寄存器中选择转换序列的规则通道及其顺序。规则转换组中的转换总数必须写入ADC_SQRx寄存器中的L[3:0]位。
一个注入转换组最多由4个转换构成。必须在ADC_JSQR寄存器中选择转换序列的注入通道及其顺序。注入转换组中的转换总数必须写入ADC_JSQR寄存器中的L[1:0]位。
顾名思义,两个通道的区别就是:规则通道就是很规矩的意思,我们一般使用的就是这个规则通道。注入可以理解为插入、插队的意思,是一种不安分的通道,有点类似于中断程序;注入通道只有在规则通道存在时才会出现。因为注入通道相当于中断,那么当程序正常执行的时候,中断是可以打断你的执行的。因此注入通道的转换可以打断规则通道的转换,在注入通道被转换完成之后,规则通道才得以继续转换。
如果在转换期间修改 ADC_SQRx 或 ADC_JSQR 寄存器,将复位当前转换并向 ADC 发送一 个新的启动脉冲,以转换新选择的组。
温度传感器、内部通道
对于STM32F40x系列的开发板,温度传感器内部连接到ADC1_IN16。内部参考电压连接到ADC1_IN17。
注意: 温度传感器、只在主ADC1外设上可用。
3.4 单次转换模式和连续转换模式
在单次转换模式下,ADC执行一次转换。CONT位为0时,可以通过以下三种方式启动此模式:
- 将ADC_CR2寄存器中的SWSTART位置1(仅适用于规则通道)
- 将JSWSTART位置1(适用于注入通道)
- 外部触发(适用于规则通道和注入通道)
如果转化了规则通道:
- 转换数据存储在16位ADC_DR寄存器中
- EOC(转换结束)标志位置1
- EOCIE位置1时将产生中断
如果转换了注入通道:
- 转换数据存储在16位ADC_JCR1寄存器中
- JEOC(注入转换结束)标志置1
- JEOCIE位置1时将产生中断
在连续转换模式下,ADC结束一个转换后立即启动一个新的转换。CONT位为1时,可通过外部触发或将ADC_CR2寄存器中的SWSTRT位置1来启动该模式(仅适用于规则通道)
如果转换了规则通道组:
- 上次转换的数据存储在16位ADC_DR寄存器中
- EOC(转换结束)标志置1
- EOCIE位置1时将产生中断
STM32F4的ADC在单次转换模式下,只执行一次转换,该模式可通过ADC_CR2寄存器的ADON位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入通道),这时CONT位为0。
以规则通道为例,一旦所选择的通道转换完成,转换结果将被存储在ADC_DR寄存器中,EOC(转化结束)标志将被置位,如果设置了EOCIE,则会产生中断。然后ADC将停止,直到下次启动。
3.5 时序图
在时序图中, ADC_CLK表示ADC的时钟;ADON是ADC控制寄存器中的最低位,将该位置1表示开启寄存器;SWSTART/JSWSTART用来设置转换模式;
ADC在开始精确转换之前需要一段稳定时间。ADC开始转换并经过15个时钟周期后,EOC标志置1,转换结果存放在16位ADC数据寄存器中。
3.6 模拟看门狗
如果ADC转换的模拟电压低于阈值下限或者高于阈值上限,则AWR模拟看门狗状态位会置1。这些阈值在ADC_HTR或ADC_LTR 16位寄存器的12个最低有效位中进行编程。可以使用ADC_CR1寄存器中的AWDIE位使能中断。
4 温度传感器
我们都很清楚传感器的用处,那么显然温度传感器就是用来测量器件的环境温度()的。
对应STM32F40x系列的开发板器件(其他的型号开发板对应不同的ADC通道),温度传感器内部连接到ADC1_IN16通道,而ADC1用于将传感器输出电压转换为数字值。不使用时可以将传感器置于掉电模式。
注意:必须将TSVREFE位置1才能同时对两个通道进行转换。ADC1_IN16(温度传感器)和ADC1_IN17(VREFINT)。
主要特性:
- 支持的温度范围(可以通过ADC转换成的数字范围):-40℃到125℃
- 精度:1.5℃
读取温度:
- 选择ADC1_IN16输入通道
- 选择一个采样时间,该采样时间要大于数据手册上指定的最低采样时间
- 在ADC_CCR寄存器中将TSVREFE位置1,以便将温度传感器从掉电模式中唤醒
- 通过将SWSTART位置1开始ADC转换
- 读取ADC数据寄存器中生成的数据
计算温度:
温度(单位℃)={-/Avg_Slope}+25
其中: =25℃时的值 Avg_Slope=温度与曲线的平均斜率
注意:
传感器从掉电模式中唤醒需要一个启动时间,启动时间过后其才能正确输出。ADC在上电后同样需要一个启动时间,因此,为尽可能减少延迟时间,应同时将ADON和TSVREFE位置1。
温度传感器的输出电压随温度线性变化。
5. ADC中断
当模拟看门狗状态位和溢出状态位分别置1时,规则组和注入组在转换结束时可能会产生中断。可以使用单独的中断使能位以实现灵活性。
ADC_SR寄存器中存在另外两个标志,但这两个标志不存在中断相关性:
- JSTRT(开始转换注入组的通道)
- STRT(开始转换规则组的通道)
6. ADC初始化结构体
ADC_InitTypeDef
typedef struct
{
uint32_t ADC_Resolution; //ADC分辨率
FunctionalState ADC_ScanConvMode; //ADC扫描多通道或者ADC单通道模式选择 通过ADC_CR1的SCAN位来配置
FunctionalState ADC_ContinuousConvMode; //ADC单次转换或者连续转换选择 通过ADC_CR2的CON位来配置
uint32_t ADC_ExternalTrigConvEdge; //ADC转换触发信号范围选择
uint32_t ADC_ExternalTrigConv; //ADC外部触发转换模式选择 通过ADC_CR2的EXTTRIG和EXTSEL位来配置
uint32_t ADC_DataAlign; //ADC数据寄存器对齐格式
uint8_t ADC_NbrOfConversion; //ADC采集通道数
}ADC_InitTypeDef;
typedef struct
{
uint32_t ADC_Mode; //ADC模式 通过ADC_CR1:DUALMOD位来配置
uint32_t ADC_Prescaler; //ADC预分频值
uint32_t ADC_DMAAccessMode;
uint32_t ADC_TwoSamplingDelay; //ADC多通道模式
}ADC_CommonInitTypeDef;
6.1 ADC相关实验配置
1-独立模式-单通道-中断读取
- 初始化ADC用到的GPIO
- 初始化ADC初始化结构体
- 配置ADC时钟,配置通道的转换顺序和采样时间
- 使能ADC转换完成中断,配置中断优先级
- 使能ADC,准备开始转换
- 校准ADC ADC_StartCalibration ADC_GetCalibrationStatus
- 软件触发ADC,真正开始转换 ADC_SoftwareStartConvCmd
- 编写中断服务函数,读取ADC转换数据
- 编写main函数,把转换的数据打印出来
2-独立模式-单通道-DMA读取 DMA是一种直接存储器,DMA是不占用CPU的,可以一边读取数据,一边处理数据。
- 初始化ADC用到的GPIO
- 初始化ADC初始化结构体
- 配置ADC时钟,配置通道的转换顺序和采样时间
- 使能ADC转换完成中断,配置中断优先级
3-独立模式-多通道-DMA读取
- 初始化ADC用到的GPIO
- 初始化ADC初始化结构体
- 配置ADC时钟,配置通道的转换顺序和采样时间
- 使能ADC转换完成中断,配置中断优先级
配置过程相同,只不过初始化IO口时需要初始化多个IO口通道。并且调用函数ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_480Cycles ); 获取多个通道执行多个功能即可。
7. 相关寄存器
7.1 ADC控制寄存器:ADC_CR1和ADC_CR2
ADC控制寄存器1:ADC_CR1(ADC control register 1)
SCAN位:设置扫描模式位,通过软件将该位置1或者清零可以使能/禁止扫描模式。 在扫描模式下,转换由规则序列寄存器ADC_SQRx或注入序列寄存器ADC_JSQRx选中的通道被转换 。
0:禁止扫描模式
1:使能扫描模式
如果设置了EOCIE和JEOCIE,只在最后一个通道转换完毕后才会产生EOC和JEOC中断。
RES位[1:0]:通过该位来设置分辨率(Resolution)
00:12位(15 ADCCLK周期)
01:10位(13 ADCCLK周期)
10:8位(11 ADCCLK周期)
11:6位(9 ADCCLK周期)
ADC控制寄存器2:ADC_CR2(ADC control register 2)
ADON位:设置A/D转换器开启或者关闭
0:禁止ADC转换并转至掉电模式
1:使能ADC
CONT位:该位用来设置连续转换模式或者单次转换模式
0:单次转换模式
1:连续转换模式
ALIGN位:该位用来设置数据是左对齐还是右对齐
0:右对齐
1:左对齐
EXTEN位:该位用来设置规则通道的外部触发是否使能
00:禁止触发检测
01:上升沿上的触发检测
10:下降沿上的触发检测
11:上升沿和下降沿上的触发检测
SWSTART位:用于开始规则通道的转换,我们每次转换(单次转换模式下)都需要向该位写1
0:复位状态
1:开始转换规则通道
7.2 ADC通用控制寄存器:ADC_CCR
ADC通用控制寄存器:ADC_CCR(ADC common control register)
TSVREFE位:温度传感器和使能位
0:禁止
1:使能
ADCPRE位:ADC预分频器,该位由软件置1或者清0,以选择ADC的时钟频率。该时钟为所有ADC所共有。
00:PCLK2 2分频
01:PCLK2 4分频
10:PCLK2 6分频
11:PCLK2 8分频 STM32的ADC最大工作频率为36Mhz,而ADC时钟来自于APB2,APB2频率一般是84Mhz,所以一般的我们置ADCPRE=01,即4分频,这样得到ADCPRE频率为21Mhz。
MULTI位:该位用来选择ADC的多重模式,本次实验我们使用的是独立模式,将该5位置0即可
- 所有 ADC 均独立:
- 00000:独立模式
- 00001 到 01001:双重模式,ADC1 和 ADC2 一起工作,ADC3 独立
- 00001:规则同时 + 注入同时组合模式
- 00010:规则同时 + 交替触发组合模式
- 00011:Reserved
- 00101:仅注入同时模式
- 00110:仅规则同时模式 仅交错模式
- 01001:仅交替触发模式
- 10001 到 11001:三重模式:ADC1、ADC2 和 ADC3 一起工作
- 10001:规则同时 + 注入同时组合模式
- 10010:规则同时 + 交替触发组合模式
- 10011:Reserved
- 10101:仅注入同时模式
- 10110:仅规则同时模式 仅交错模式
- 11001:仅交替触发模式
7.3 ADC采样时间寄存器:ADC_SMPR1和ADC_SMPR2
这两个寄存器用于设置通道0~18的采样时间,每个通道占用3个位
ADC采样时间寄存器:ADC_SMPR1(ADC sample time register 1)
ADC采样时间寄存器:ADC_SMPR2(ADC sample time register 2)
采样时间通俗的讲就是收集模拟量的时间,为了保证转换后的数字量更加精准,采样时间通常设置的尽量长一些。由此带来的负面影响会降低ADC的转换效率。
ADC转换时间计算:=采样时间+12个周期
7.4 ADC规则序列寄存器:ADC_SQRx(x取值1~3)
ADC规则序列寄存器1:ADC_SQR1(ADC regular sequence register 1)
L位:规则通道序列长度
0000:1次转换
0001:2次转换
……
1111:16次转换
SQx位:分别对应规则序列中的第x次转换(x取值13 14 15 16)其余的1~12分别位于ADC规则序列寄存器2和3中
7.5 ADC规则数据寄存器:ADC_DR
ADC规则数据寄存器:ADC_DR(ADC regular data register)
DATA位: 规则数据位,这些位为只读,它们包括来自规则通道的转换结果。
注意:ADC转换完成后的数字量会存储在该寄存器中,且该寄存器的存储值受ADC_CR2的ALIGN位设置的左对齐还是右对齐控制,所以在读取时需要特别注意。
7.6 ADC状态寄存器:ADC_SR
ADC状态寄存器:ADC_SR(ADC status register)
EOC位: 通过该位来决定是否此次规则通道的AD转换已完成,如果该位为1,则表示转换已经完成了,这时就可以从ADC_DR中读取转换结果,否则等待转换完成。
8. 库函数配置ADC1的通道5进行AD转换
1. 开启PA口时钟和ADC1时钟,设置PA5为模拟输入
STM32F407ZG的ADC12通道IN5位于引脚PA5上,所以我们要使能GPIOA时钟和ADC1时钟。
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟
注意:对于IO口复用的ADC我们要设置模式为模拟输入,而不是复用功能。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入
2. 设置ADC的通用控制寄存器CCR,配置ADC输入时钟分频,模式为独立模式
在库函数中, 初始化CCR寄存器是通过调用ADC_CommonInit来实现的;
typedef struct { uint32_t ADC_Mode; //ADC模式 通过ADC_CR1:DUALMOD位来配置 uint32_t ADC_Prescaler; //ADC预分频值 uint32_t ADC_DMAAccessMode; //DMA模式禁止或者使能相应的DMA模式 uint32_t ADC_TwoSamplingDelay; //ADC两个采样阶段之间的延迟周期数 }ADC_CommonInitTypeDef; 第一个参数ADC_Mode:设置独立模式还是多重模式 第二个参数ADC_Prescaler:设置ADC预分频器,这里设置时一定保证ADC1的时钟频率不超过36MHz 第三个参数ADC_DMAAccessMode:DMA模式禁止或者使能相应的DMA模式 第四个参数ADC_TwoSamplingDelay:设置ADC两个采样阶段之间的延迟周期数
void ADC_CommonInit(ADC_CommonInitTypeDef* ADC_CommonInitStruct)
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //ADC预分频值
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //ADC的DMA模式禁止或者使能相应DMA模式
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
3. 初始化ADC1参数,设置ADC1的转换分辨率,转换方式,对齐方式,以及规则序列等相关信息
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct) //初始化ADC函数
typedef struct { uint32_t ADC_Resolution; //ADC分辨率 FunctionalState ADC_ScanConvMode; //ADC扫描多通道或者ADC单通道模式选择 通过ADC_CR1的SCAN位来配置 FunctionalState ADC_ContinuousConvMode; //ADC单次转换或者连续转换选择 通过ADC_CR2的CON位来配置 uint32_t ADC_ExternalTrigConvEdge; //ADC转换触发信号范围选择 uint32_t ADC_ExternalTrigConv; //ADC外部触发转换模式选择 通过ADC_CR2的EXTTRIG和EXTSEL位来配置 uint32_t ADC_DataAlign; //ADC数据寄存器对齐格式 uint8_t ADC_NbrOfConversion; //ADC采集通道数 }ADC_InitTypeDef; 第一个参数ADC_Resolution:设置ADC转换分辨率 第二个参数ADC_ScanConvMode:设置是否打开扫描模式 第三个参数ADC_ContinuousConvMode:设置时单次转换模式还是连续转换模式 第四个参数ADC_ExternalTrigConvEdge:设置外部通道的触发使能和检测方式 第五个参数ADC_ExternalTrigConv:设置ADC外部触发转换模式 第六个参数ADC_DataAlign:设置数据对齐方式 是左对齐还是右对齐 第七个参数ADC_NbrOfConversion:设置规则序列的长度 单次转换
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //禁止触发检测,使用软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_NbrOfConversion = 1;//1个转换在规则序列中
ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
4. 开启AD转换器
开启AD转换器通过ADC_CR2寄存器控制
ADC_Cmd(ADC1, ENABLE);//开启AD转换器
5. 读取ADC值
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
// 设置规则序列通道及采样周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_480Cycles ); //规则序列中的第一个转换,采样周期设置为480
ADC_SoftwareStartConvCmd(ADC1); //使能指定的ADC1的软件转换启动功能
ADC_GetConversionValue(ADC1); //获取转换ADC转换结果数据
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG) //获取AD转换的状态信息函数
ag. while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
9. 实验程序
该实验需要用杜邦线将PA5引脚接到GND或者3.3V上,测其电压原始值和转化后的值,切记不能接5.5V,会损坏开发板的ADC;
9.1 main.c
#include "stm32f4xx.h"
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "lcd.h"
#include "usmart.h"
#include "Key.h"
#include "ADC.h"
//LCD状态设置函数
void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数
{
LED1=sta;
}
//函数参数调用测试函数
void test_fun(void(*ledset)(u8),u8 sta)
{
led_set(sta);
}
int main(void)
{
u16 adcx;
float temp;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(168);
uart_init(115200);
LED_Init();
LCD_Init();
Adc_Init();
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
LCD_ShowString(30,70,200,16,16,"ADC TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2023/20/23");
POINT_COLOR=BLUE;
LCD_ShowString(30,130,200,16,16,"ADC1_CH5_VAL:");
LCD_ShowString(30,150,200,16,16,"ADC1_CH5_VOL:0.000V"); //显示小数点
while(1)
{
adcx=Get_Adc_Average(ADC_Channel_5,20);//adc通道5,取20次平均值
LCD_ShowxNum(134,130,adcx,4,16,0); //显示ADC采样后的原始值
temp=(float)adcx*(3.3/4096); //获取计算后带小数的实际电压值,如3.1111
adcx=temp; //赋值整数部分给adcx
LCD_ShowxNum(134,150,adcx,1,16,0);//显示电压值的整数部分
temp=temp-adcx; //把已经显示的整数部分去掉,留下小数部分 3.1111-3=0.1111
temp=temp*1000;//把小数部分乘以1000,变成整数打印出来,乘以1000相当于保留3位小数,乘以10000相当于保留四位小数
LCD_ShowxNum(150,150,temp,3,16,0x80); //显示小数部分
LED0=!LED0;
delay_ms(250);
}
}
9.2 ADC.c
#include "stm32f4xx.h"
#include "ADC.h"
#include "delay.h"
//初始化ADC
void Adc_Init(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);// 使能ADC1时钟
//初始化ADC1通道5 IO口
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AN; //模式为模拟输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //PA5 通道5
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL; //不带上下拉
GPIO_Init(GPIOA,&GPIO_InitStructure);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE); //ADC1复位
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE); //复位结束
//初始化CCR通用控制寄存器配置
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_CommonInitStructure.ADC_DMAAccessMode=ADC_DMAAccessMode_Disabled; //DMA失能
ADC_CommonInitStructure.ADC_Mode=ADC_Mode_Independent; //独立模式
ADC_CommonInitStructure.ADC_Prescaler=ADC_Prescaler_Div4; //预分频值设置
ADC_CommonInitStructure.ADC_TwoSamplingDelay=ADC_TwoSamplingDelay_5Cycles;//两个采样阶段之间延迟5个时钟
ADC_CommonInit(&ADC_CommonInitStructure);
//初始化ADC1相关参数
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE; //关闭连续转换
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //右对齐
ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_None; //禁止触发检测,使用软件触发
ADC_InitStructure.ADC_NbrOfConversion=1; //1个转换在规则序列中
ADC_InitStructure.ADC_Resolution=ADC_Resolution_12b; //12位模式
ADC_InitStructure.ADC_ScanConvMode=DISABLE; //非扫描模式
ADC_Init(ADC1,&ADC_InitStructure); //ADC初始化
ADC_Cmd(ADC1,ENABLE); //开启AD转换器
}
//获取ADC值
//ch:通道值 0~16
//返回值:转换结果
u16 Get_Adc(u8 ch)
{
ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_480Cycles);//设置ADC规则组通道,一个序列,采样时间480
ADC_SoftwareStartConv(ADC1);//使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));//读取状态寄存器的状态位EOC,等待转换结束
return ADC_GetConversionValue(ADC1);//返回最近一次的ADC1规则组的转换结果
}
//获取通道ch的转换值,取times次,然后平均
//ch:通道编号 times:获取次数
//返回值:通道ch的times次转换结果平均值
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val=temp_val+Get_Adc(ch); //取times次通道值进行求和
delay_ms(5);
}
return temp_val/times; //返回平均值
}
9.3 ADC.h
#ifndef _ADC__H_
#define _ADC__H_
void Adc_Init(void);
u16 Get_Adc(u8 ch);
u16 Get_Adc_Average(u8 ch,u8 times);
#endif