【STM32】标准库与HAL库对照学习教程十一--ADC模数转换
一、前言
本篇是对STM32F1中的ADC的讲解,在本篇文章中你将了解什么是ADC、STM32中的ADC以及STM32关于ADC的配置,本篇使用标准库与HAL库进行对照配置,希望您能在本篇中有所收获。 |
二、准备工作
- STM32F103开发板(我用的是普中的STM32F103ZE开发板)
- cubemx软件、keil 5(MDK)
- 开发板原理图
三、ADC简介
ADC英文名analog to digital converter,模数转换器,它可以将模拟信号转换为数字信号。
按照其转换原理主要分为逐次逼近型、双积分型、电压频率转换型三种。
四、STM32F1中的ADC
1、STM32F1中的ADC介绍
STM32F103 系列一般都有 3个ADC,这些 ADC 可以独立使用,也可以使用双重,这样可以提高采样率。
STM32F1 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它具有多达 18个复用通道,可测量来自16 个外部源、2 个内部源信号。
这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。
ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。
ADC具有模拟看门狗特性,可以使用程序检测输入电压是否超出用户定义的阀值上限或者下限。
2、ADC的结构框图
图片来源于STM32F1xx中文参考手册 ADC章节
3、ADC结构解析
从上图中可以分析ADC的工作模式。
这部分的图片均来源于STM32F1xx中文参考手册 ADC章节
(1)引脚输入电压
引脚输入的ADC转换电压范围由VDDA、VSSA、VREF+、VREF- 决定。它们的定义由下图所示。
通常我们把 VSSA和 VREF- 接GND,把 VREF+和 VDDA 接 3.3V。 因此引脚输入的ADC转换电压范围为:VREF- ≤ VIN ≤ VREF+ 等价于:0 ≤ VIN ≤ 3.3V(VIN表示输入引脚的电压)
(2)输入通道
STM32 的 ADC的输入通道多达 18 个,其中外部的 16 个通道就是框图中的 ADCx_IN0…ADCx_IN5(x=1/2/3,表示ADC数)。
这 16个通道分别对应着不同的 IO口, 具体是哪一个 IO口可以从数据手册查询到。
其中 ADC1 还有2个内部通道:ADC1 的通道16连接到了芯片内部的温度传感器,通道17连接到了内部参考电压 VREFINT。ADC2和ADC3的通道16、17全部连接到了内部的VSS。下图为ADC转换通道对应的引脚。参考普中PPT教程。
(3)转换通道
外部16个通道在转换时,可选择2个通道组:规则通道组和注入通道组。其中规则通道组最多有16路,注入通道组最多有 4 路。
- 规则通道组:从名字来理解,规则通道就是一种规规矩矩的通道,类似于正常执行的程序,通常我们使用的都是这个通道。
- 注入通道组:从名字来理解,注入即为插入,类似于中断。当程序正常往下执行时,中断可以打断程序的执行。同样如果在规则通道转换过程中,有注入通道插入,那么就要先转换完注入通道,等注入通道转换完成后再回到规则通道的转换流程。
每个组包含一个转换序列,该序列可按任意顺序在任意通道上完成。
(4)ADC触发源
① ADC转化的触发源有很多,我们常用软件编写程序设置ADC控制寄存器ADC_CR2的 ADON位为1,直接使能ADC。
② 关于ADC的触发源由ADC 控制寄存器2-ADC_CR2 的 EXTSEL[2:0]和 JEXTSEL[2:0]位来控制。EXTSEL[2:0]用于选择规则通道的触发源,JEXTSEL[2:0]用于选择注入通道的触发源。
③ 触发源选定好之后,触发源是否要激活,则由 ADC 控制寄存器ADC_CR2 的 EXTTRIG 和 JEXTTRIG 这两位来激活。
(5)ADC时钟与转换时间
① ADC 输入时钟ADC_CLK 由 APB2经过分频产生,最大值是14MHz,分频因子由 RCC 时钟配置寄存器 RCC_CFGR 的位15:14 ADCPRE[1:0]设置,可以是 2/4/6/8 分频,但没有 1 分频。 我们知道APB2总线时钟为72M,而ADC最大工作频率为14M,所以一般设置分频因子为6,这样ADC的输入时钟ADC_CLK的频率为12M,周期为1/ADC_CLK。
② ADC要完成对输入电压的采样需要若干个输入时钟ADC_CLK的周期。
采样的周期数可通过ADC 采样时间寄存器 ADC_SMPR1和ADC_SMPR2 中的 SMP[2:0]位设置。
ADC_SMPR2控制的是通道 0到9,ADC_SMPR1 控制的是通道 10到17。每个通道可以分别用不同的时间采样。 其中采样周期最小是1.5个。 ADC 的总转换时间的公式如下: Tconv = 采样时间 + 12.5个周期
当ADC_CLK为14MHz时,采样时间为1.5个周期。 总的采样时间=(1.5+12.5)/14000000=1us 但一般ADC_CLK为12MHz,将上面的14000000换为12000000 采样时间约为1.17us。
(6)对齐方式
由于STM32F1的ADC是12位转换精度,4095为最大值,因此 电压值=(转换的值/4095)*3.3。
数据寄存器是16位,所以ADC在存放数据的时候就有左对齐和右对齐区分。
如果是左对齐,AD转换完成数据存放在 ADC_DR 寄存器的[4:15]位内;如果是右对齐,则存放在 ADC_DR 寄存器的[0:11]位内。
具体选择何种存放方式,需通过ADC_CR2 的 11 位 ALIGN 设置。
(7)中断
当发生如下事件且使能相应中断标志位时,ADC能产生中断。
- 转换结束,规则转换与注入转换结束
- 模拟看门狗事件
- DMA请求
五、ADC的转换模式
1、单次转换
ADC只执行一次转换,转换后停止。
转换结果将被存在 ADC_DR 寄存器。
2、连续转换
ADC 结束一轮转换后立即启动新一轮的转换。
两个模式由ADC控制寄存器 2(ADC_CR2)的CONT位决定。
3、扫描模式
ADC扫描 选择的所有规则通道和注入通道,在每个组的每个通道上执行单次转换。如果开启了连续转换,转换完所有通道后,又会重新从第一个通道开始转换。
模式由ADC控制寄存器 1(ADC_CR1)的SCAN位决定
4、间断模式
触发一次,转换一个通道,在触发,在转换。在所选的所有转换通道,由触发信号启动新一轮的转换,直到转换完成为止。
模式由ADC控制寄存器 1(ADC_CR1)的JDISCEN位与DISCEN决定
六、硬件电路
请参考自己开发板的硬件电路,没有下面的电路,可以找一个可调的电源接到引脚上(注意电压要小于3,3V)
七、标准库配置ADC实验
1、配置步骤
(1)使能端口时钟和ADC时钟,设置引脚模式为模拟输入 (2)设置ADC的分频因子 (3)初始化ADC参数,包括ADC工作模式、规则序列等 (4)使能ADC并校准 (5)设置规则序列通道以及采样周期 (6)读取ADC转换值
2、配置工程
(1)复制上一章的工程,并重命名为11、ADC单通道转换。
(2)进入工程文件,进入APP文件,新建ADC用来存放与ADC相关的文件。
(3)打开工程,新建文件,并命名为adc.h与adc.c。 ① ②
(4)添加文件到目录,并添加头文件路径。 ① ②
(5)要使用ADC需要添加相应的文件。 ① ②
3、实验程序
main.h
#include "Delay.h"
#include "usart.h"
#include "stdio.h"
#include "adc.h"
int main()
{
float Voltage;
SysTick_Init(72);
USART1_Init(9600);
ADC1_CH1_Init();
while(1)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
Voltage = ((float)ADC_GetConversionValue(ADC1)/4095.0)*3.3;
printf("转换的电压为 %.2f V\r\n",Voltage);
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
Delay_ms(500);
}
}
adc.h
#ifndef ADC_H_
#define ADC_H_
#include "stm32f10x.h"
void ADC1_CH1_Init(void);
#endif
adc.c
#include "adc.h"
void ADC1_CH1_Init()
{
GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStruct);
ADC_InitStruct.ADC_NbrOfChannel = 1;
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_InitStruct.ADC_ScanConvMode = DISABLE;
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_Init(ADC1, &ADC_InitStruct);
ADC_Cmd(ADC1, ENABLE);
ADC_StartCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
while(ADC_GetCalibrationStatus(ADC1));
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_1Cycles5);
}
4、实验效果
这里通过改变移动滑动变阻改变输入引脚电压的值。
八、HAL库配置ADC实验
1、使用cubemx配置工程
(1) 打开cubemx,新建工程,选择自己的芯片。
(2) 配置RCC,选择外部高速时钟。
(3) 配置时钟树。
(4) 设置ADC引脚。
(5) 设置ADC时钟分频为6分频。
(5) ADC参数配置。
备注:Rank那里还可以设置转换周期,Discontinuous Conversion Mode是间断模式的设置
(6) 打开串口
(7) 工程文件配置并生成工程 ①
②
2、常用函数介绍
? HAL_ADC_Start(); //开启ADC ? HAL_ADC_Start_IT(); //开启ADC与中断 ? HAL_ADC_Start_DMA();//开启ADC的DMA模式 ? HAL_ADC_Stop(); //关闭ADC ? HAL_ADC_Stop_IT(); //关闭ADC与中断 ? HAL_ADC_Stop_DMA(); //关闭ADC的DMA模式 ? HAL_ADCEx_Calibration_Start(&hadcx); //ADC校准? ? ? HAL_ADC_GetValue();//获取ADC转换值 ? HAL_ADC_PollForConversion(); //等待转换结束函数 hadcx:hadc1、hadc2、hadc3 Timeout:最大等待时间 ADC中断回调函数 ? HAL_ADC_ConvCpltCallback() ? HAL_ADC_ConfigChannel() 配置规则组通道 ? HAL_ADC_AnalogWDGConfig()//看门狗配置
3、实验程序
①
#include<stdio.h>
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
②
float Voltage;
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1,50);
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
Voltage = ((float)HAL_ADC_GetValue(&hadc1)/4095.0)*3.3;
printf("Voltage = %.2f\r\n",Voltage);
}
HAL_Delay(500);
4、实验效果
这里通过改变移动滑动变阻改变输入引脚电压的值 到这里就结束啦!
|