目录
学习目标
运行结果
内容
特点
引脚
框图
模式
单次转换
连续转换
扫描模式
中断
采样时间
寄存器
配置
代码
总结
今天我们来学习一下有关ADC模数转换的知识,STM32中并未对AD/DA做出讲解,更多的是让我们如何去配置,所以建议先学习一点有关AD的知识,再来理解一下,可能会好一点。之前51的笔记如下:51单片机——AD/DA转换
ADC
ADC:Analog-to-Digital Converter的缩写。指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件。典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。
- 可配置 12 位、10 位、8 位或 6 位分辨率。
- 在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断。
- 单次和连续转换模式。
- 用于自动将通道 0 转换为通道“n”的扫描模式。
- 数据对齐以保持内置数据一致性。
- 可独立设置各通道采样时间。
- 外部触发器选项,可为规则转换和注入转换配置极性。
- 不连续采样模式。
- 双重/三重模式(具有 2 个或更多 ADC 的器件提供)。
- 双重/三重 ADC 模式下可配置的 DMA 数据存储。
- 双重/三重交替模式下可配置的转换间延迟。
- ADC 转换类型(参见数据手册)。
- ADC 电源要求:全速运行时为 2.4 V 到 3.6 V,慢速运行时为 1.8 V。
- ADC 输入范围:VREF— <= VIN <= VREF+。
- 规则通道转换期间可产生 DMA 请求。
这是引脚与通道的对应关系,STM32F4一共有三路通道,每路都有16个通道。一共对应了24个引脚。
其实这个框图比较简单,值得我们注意的是注入通道以及规则通道,规则通道就是我们的正常流程;而注入通道,我们听这个名字 ,就知道它类似于一个强盗(中断),他会打断规则通道的执行,转而去执行注入通道。
单次转换模式就是我们理解的普通模式,就是完成一次ADC转换。
连续转换模式就是连续完成多次的单次扫描。
扫描模式是用于扫描一组模拟通道,有点像连续模式,但是要注意两者的区别。
在使用独立看门狗的时候,有可能会使用到这个中断,但我们今天的实验不会使用到这个中断。
对于每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样会降低 ADC 的转换速率。ADC 的转换时间可以由以下公式计算: Tcovn=采样时间+12 个周期 其中:Tcovn 为总转换时间,采样时间是根据每个通道的 SMP 位的设置来决定的。例如, 当 ADCCLK=21Mhz 的时候,并设置 3 个周期的采样时间,则得到:Tcovn=3+12=15 个周期 =0.71us。
最小采样时间为0.42us(ADC时钟=36MHz,采样周期为3周期下得到的)。
本节的寄存器非常多,而且非常重要,碍于篇幅,我们也不做介绍,感兴趣同学可以自行查阅手册。
1、开启 PA 口时钟和 ADC1 时钟,设置 PA5 为模拟输入。
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能 GPIOA 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能 ADC1 时钟 //模拟输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
2、复位ADC.
ADC_DeInit();//ADC1复位
3、设置 ADC 的通用控制寄存器 CCR,配置 ADC 输入时钟分频,模式为独立模式等。
void ADC_CommonInit(ADC_CommonInitTypeDef* ADC_CommonInitStruct)
4、初始化 ADC1 参数,设置 ADC1 的转换分辨率,转换方式,对齐方式,以及规则序列等相关信息。
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)
5、使能 AD 转换器。
ADC_Cmd(ADC1, ENABLE);//开启 AD 转换器
6、配置规则通道参数。
ADC_RegularChannelConfig();
7、开启软件转换。
ADC_SoftwareStartConvCmd(ADC1);
8、等待转换完成,读取ADC值。
ADC_GetConversionValue(ADC1);
#include "adc.h"
#include "delay.h"
//初始化ADC
void Adc_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟
//先初始化ADC1通道5 IO口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
ADC_DeInit();//ADC1复位
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;//关闭DMA
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;//两个采样阶段之间的延迟5个时钟
ADC_CommonInit(&ADC_CommonInitStructure);
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个转换在规则序列中 也就是只转换规则序列1
ADC_Init(ADC1 ,&ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);//开启AD转换器
}
//获得ADC值
//ch: @ref ADC_channels
//通道值 0~16取值范围为:ADC_Channel_0~ADC_Channel_16
//返回值:转换结果
u16 Get_Adc(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_480Cycles ); //ADC1,ADC通道,480个周期,提高采样时间可以提高精确度
ADC_SoftwareStartConv(ADC1); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_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
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "adc.h"
int main(void)
{
u16 adcx;
float temp;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为115200
LED_Init(); //初始化LED
Adc_Init(); //初始化ADC
while(1)
{
adcx=Get_Adc_Average(ADC_Channel_5,20);//获取通道1的转换值,20次取平均
printf("%d",adcx); //显示ADCC采样后的原始值
printf("\t\n\t\n");
temp=(float)adcx*(3.3/4096); //获取计算后的带小数的实际电压值,比如3.1111
printf("%f",temp);; //显示电压值部分(前面转换为了整形显示),这里显示的就是111.
printf("\t\n\t\n");
LED0=!LED0;
delay_ms(250);
}
}
本节内容难度比较大,东西也比较多,多重ADC也没有用过,所以我自己也会慢慢学习,也希望对大家有所帮助,共勉!