1.基本概念 :
Analog-to-Digital Converter的缩写。指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件 。典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。
STM32F407ZGT6包含有 3个 ADC。 STM32F4的 ADC最大的转换速率为 2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为 3个 ADC时钟下得到),不要让 ADC的时钟超过 36M,否则将导致结果准确度下降。
2.STM32F4x ADC特点
-
可配置12位、10位、8位或6位分辨率
-
输入通道
-
ADC 有19 个通道(其中外部通道16个)。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。
-
ADC 多达19 个通道,其中外部的16 个通道就是框图中的ADCx_IN0、ADCx_IN1...ADCx_IN15。这16 个通道对应着不同的IO 口,具体是哪一个IO 口可以从手册查询到。 还有三个内部通道:芯片内部的温度传感器、备用电源VBAT、内部参考电压VREFINT。
外部的16 个通道在转换的时候又分为规则通道和注入通道,其中规则通道最多有16 路,注入通 道最多有4 路。那这两个通道有什么区别?在什么时候使用?
5.规则通道
平时一般使用的就是这个通道,它们的优先级较低,通常用于常规的采样任务。
6.注入通道
注入,可以理解为插入,注入通道则用于在规则通道的基础上提供更高的优先级采样。注入通道的数据采样可以在规则通道采样进行的同时进行,具有更高的优先级。通常用于对某些事件或条件的快速响应,例如紧急事件采样或高优先级传感器的采样。
如果在规则通道转换过程中,有注入通道插队,那么就要先转换完注 入通道,等注入通道转换完成后,再回到规则通道的转换流程。这点跟中断程序很像,所以,注入通道只有在规则通道存在时才会出现。
举例:
假设你正在使用STM32控制一个温度传感器。你需要定期地(每1秒)采集温度数据,并且有时候需要在突发事件发生时(比如温度快速升高)立即采集更多的数据。
对于规则通道:你会设置ADC在每秒钟采样一次,通过规则通道来获取温度数据。这是一种周期性的采样方式,对于大多数情况下的温度监测来说是足够的。
对于注入通道:你可以设置ADC来在检测到温度突然升高时,立即进行更高优先级的采样,以获取更频繁的温度数据,以便更快地做出响应。
7.触发源
ADC 转换可以由ADC 控制寄 存器2: ADC_CR2 的ADON 这个位来控制,写1 的时候开始转换,写0 的时候停止转换,这个 是最简单也是最好理解的开启ADC 转换的控制方式。
除了这种普通的控制方法,ADC 还支持外部事件触发转换,这个触发包括内部定时器触发 和外部IO 触发。触发源有很多,具体选择哪一种触发源,由ADC 控制寄存器2:ADC_CR2 的 EXTSEL[3:0] 和JEXTSEL[3:0] 位来控制。
EXTSEL[3:0] 用于选择规则通道的触发源,
JEXTSEL[3:0] 用于选择注入通道的触发源。
选定好触发源之后,触发源是否要激活,则由ADC 控制寄存器 2:ADC_CR2 的EXTTRIG 和JEXTTRIG 这两位来激活。
如果使能了外部触发事件,我们还可以通过设置ADC 控制寄存器2:ADC_CR2 的EXTEN[1:0] 和 JEXTEN[1:0] 来控制触发极性,可以有4 种状态,分别是:
禁止触发检测、上升沿检测、下降沿 检测以及上升沿和下降沿均检测。
8.转换时间
ADC 时钟
ADC 输入时钟ADC_CLK 由PCLK2 经过分频产生,最大值是36MHz,分频 因子由ADC 通用控制寄存器ADC_CCR 的ADCPRE[1:0] 设置,可设置的分频系数有2、4、6 和 8
对于我们的芯片来说,APB2=84Mhz,而ADC最大时钟频率为36Mhz,如果分频系数为2,则84Mhz/2=42Mhz>36Mhz , 所以一般情况下为4分频,即84/4=21Mhz
采样时间
ADC 需要若干个ADC_CLK 周期完成对输入的电压进行采样,采样的周期数可通过ADC 采样时 间寄存器ADC_SMPR1 和ADC_SMPR2 中的SMP[2:0] 位设置,ADC_SMPR2 控制的是通道0~9, ADC_SMPR1 控制的是通道10~17。每个通道可以分别用不同的时间采样。其中采样周期最小是 3 个。
ADC 的总转换时间跟ADC 的输入时钟和采样时间有关,公式为:
Tconv = 采样时间 + 12 个周期(系统自带12个周期)
其中: Tcovn为总转换时间,采样时间是根据每个通道的 SMP位的设置来决定的。例如,
当 ADCCLK=21Mhz的时候,并设置 3个周期的采样时间,则得到 Tcovn=3+12=15个周期 =0.71us。
9.数据寄存器
一切准备就绪后,ADC 转换后的数据根据转换组的不同,规则组的数据放在ADC_DR 寄存器, 注入组的数据放在JDRx
规则数据寄存器ADC_DR
ADC 规则组数据寄存器ADC_DR 只有一个,是一个32 位的寄存器,只有低16 位有效并且只是 用于独立模式存放转换完成数据。因为ADC 的最大精度是12 位,ADC_DR 是16 位有效,这样 允许ADC 存放数据时候选择左对齐或者右对齐,具体是以哪一种方式存放,由ADC_CR2 的11 位ALIGN 设置。假如设置ADC 精度为12 位,如果设置数据为左对齐,那AD 转换完成数据存 放在ADC_DR 寄存器的[4:15] 位内;如果为右对齐,则存放在ADC_DR 寄存器的[0:11] 位内。
规则通道可以有16 个这么多,可规则数据寄存器只有一个,如果使用多通道转换,那转换的数 据就全部都挤在了DR 里面,前一个时间点转换的通道数据,就会被下一个时间点的另外一个通 道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走,或者开启DMA 模式,把数据 传输到内存里面,不然就会造成数据的覆盖。最常用的做法就是开启DMA 传输。
-
右对齐
-
左对齐
注入数据寄存器ADC_JDRx
ADC 注入组最多有4 个通道,刚好注入数据寄存器也有4 个,每个通道对应着自己的寄存器,不 会跟规则寄存器那样产生数据覆盖的问题。ADC_JDRx 是32 位的,低16 位有效,高16 位保留, 数据同样分为左对齐和右对齐,具体是以哪一种方式存放,由ADC_CR2 的11 位ALIGN 设置。
10.中断
数据转换结束后,可以产生中断,中断分为四种:规则通道转换结束中断,注入转换通道转换结束中断,模拟看门狗中断和溢出中断。
转换结束中断
转换结束中断很好理解,跟我们平时接触的中断一样,有相应的中断标志位和中断使能位
模拟看门狗中断
当被ADC 转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断,前提是我们开启了模 拟看门狗中断,其中低阈值和高阈值由ADC_LTR 和ADC_HTR 设置。例如我们设置高阈值是 2.5V,那么模拟电压超过2.5V 的时候,就会产生模拟看门狗中断,反之低阈值也一样。
溢出中断
如果发生DMA 传输数据丢失,会置位ADC 状态寄存器ADC_SR 的OVR 位,如果同时使能了 溢出中断,那在转换结束后会产生一个溢出中断。
11.DMA请求
规则和注入通道转换结束后,除了产生中断外,还可以产生DMA 请求,把转换好的数据直接存储在内存里面
结构体参数含义:
/**
* @brief ADC Init structure definition
*/
typedef struct
{
uint32_t ADC_Resolution; //ADC 分辨率选择
FunctionalState ADC_ScanConvMode;//ADC 扫描选择
FunctionalState ADC_ContinuousConvMode; //ADC 连续转换模式选择
uint32_t ADC_ExternalTrigConvEdge; //ADC 外部触发极性
uint32_t ADC_ExternalTrigConv; //ADC 外部触发选择
uint32_t ADC_DataAlign; //输出数据对齐方式
uint8_t ADC_NbrOfConversion; //转换通道数目
}ADC_InitTypeDef;
-
ADC_Resolution:配置ADC 的分辨率,可选的分辨率有12 位、10 位、8 位和6 位。分辨率越高,AD 转换数据精度越高,转换时间也越长;分辨率越低,AD 转换数据精度越低,转换时间也越短。
-
ScanConvMode:可选参数为ENABLE 和DISABLE,配置是否使用扫描。如果是单通道AD 转换 使用DISABLE,如果是多通道AD 转换使用ENABLE。
-
ADC_ContinuousConvMode:可选参数为ENABLE 和DISABLE,配置是启动自动连续转换还是单 次转换。使用ENABLE 配置为使能自动连续转换;使用DISABLE 配置为单次转换,转换一次后 停止需要手动控制才重新启动转换。
-
ADC_ExternalTrigConvEdge:外部触发极性选择,如果使用外部触发,可以选择触发的极性,可 选有禁止触发检测、上升沿触发检测、下降沿触发检测以及上升沿和下降沿均可触发检测。
-
ADC_ExternalTrigConv:外部触发选择,ADC 功能框图中列举了很多外部触发条件,可 根据项目需求配置触发来源。实际上,我们一般使用软件自动触发。
-
ADC_DataAlign: 转换结果数据对齐模式, 可选右对齐ADC_DataAlign_Right 或者左对齐 ADC_DataAlign_Left。一般我们选择右对齐模式。
-
ADC_NbrOfChannel:ADC 转换通道数目。
ADC 除了有ADC_InitTypeDef 初始化结构体外,还有一个ADC_CommonInitTypeDef 通用初始化 结构体。ADC_CommonInitTypeDef 结构体内容决定三个ADC 共用的工作环境,比如模式选择、 ADC 时钟等等。
typedef struct
{
uint32_t ADC_Mode;
uint32_t ADC_Prescaler;
uint32_t ADC_DMAAccessMode;
uint32_t ADC_TwoSamplingDelay;
}ADC_CommonInitTypeDef;
-
ADC_Mode:ADC 工作模式选择,有独立模式、双重模式以及三重模式。
-
ADC_Prescaler:ADC 时钟分频系数选择,ADC 时钟是有PCLK2 分频而来,分频系数决定ADC 时钟频率,可选的分频系数为2、4、6 和8。ADC 最大时钟配置为36MHz。
-
ADC_DMAAccessMode:DMA 模式设置,只有在双重或者三重模式才需要设置,可以设置三种 模式,具体可参考参考手册说明。
-
ADC_TwoSamplingDelay:2 次采样阶段之前的延迟,对双重和三重采样有效。
应用
(1)单通道采集电压
(2)单通道采集光敏传感器测量值
代码
1.adc.c
#include "adc.h"
#include "stm32f4xx.h"
/*
1.看原理图确定GPIO与ADC通道 PA5, ADC1IN5?
2.配置GPIO为模拟模式
3.ADC初始化
a.结构体申明 ADC_CommonInitTypeDef
b.时钟使能
c.结构体配置
d.初始化
4.ADC通道初始化
a.结构体申明 ADC_InitTypeDef
b.结构体配置
c.初始化
5.可以进行中断配置
记得开中断,规则通道结束中断标志为ADC_IT_EOC
6.各种使能
a.ADC通道使能
b.ADC使能
c.软件开启ADC转换
7.中断服务函数重写
*/
void adc_rj_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AN;//模拟
//GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //单路模式
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //四分频,84/4 = 21Mhz ,低于36Mhz
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles; //两次混合采样的周期延迟,独立模式怎么设置都不影响
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //是否扫面
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //是否连续
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //无边沿检测
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右对齐
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_84Cycles);
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); //开启规则转换结束中断
ADC_Cmd(ADC1, ENABLE); //使能ADC
ADC_SoftwareStartConv(ADC1); //软件触发,开始转换
}
//PF7 ADC3_IN5
void adc_LIGHT_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AN; //模拟
//GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
GPIO_Init(GPIOF,&GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //单路模式
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //四分频,84/4 = 21Mhz ,低于36Mhz
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles; //两次混合采样的周期延迟,独立模式怎么设置都不影响
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //是否扫面
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //是否连续
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //无边沿检测
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右对齐
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC3, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 1, ADC_SampleTime_84Cycles);
ADC_ITConfig(ADC3, ADC_IT_EOC, ENABLE); //开启规则转换结束中断
ADC_Cmd(ADC3, ENABLE); //使能ADC
ADC_SoftwareStartConv(ADC3); //软件触发,开始转换
}
2.adc.h
#ifndef __ADC_H
#define __ADC_H
void adc_rj_init(void);
void adc_LIGHT_init(void);
#endif
3.重写中断服务函数
uint16_t adc_value;
double dianya;
//光敏传感器
void ADC_IRQHandler(void)
{
if(ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC)==SET)
{
adc_value = ADC_GetConversionValue(ADC3);
dianya = (float)adc_value;//计数器到最大值对应的电压为3.3v
printf("光敏值:%.0f\r\n",dianya);
}
ADC_ClearITPendingBit(ADC3, ADC_IT_EOC);
}
//ADC测电压值
void ADC_IRQHandler(void)
{
if(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)==SET)
{
adc_value = ADC_GetConversionValue(ADC1);
dianya = (float)adc_value * 3.3 / (float)4096 ;//计数器到最大值对应的电压为3.3v
printf("电压值为:%f\r\n",dianya);
}
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
}
void USART1_IRQHandler(void)
{
uint16_t rev;
if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
{
rev=USART_ReceiveData(USART1);
USART_ClearITPendingBit(USART1,USART_FLAG_RXNE); //清除中断标志
}
}
4.主函数
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SysTick_Init(); //系统延时
keg_capture_init();
uart_init();
adc_rj_init();
adc_LIGHT_init();
tim14_pwm_led0(84, 1000);
uint32_t cmp;
u8 direction = 1;
while (1)
{
}
}