一、ADC简介
ADC(Analog-Digital Converter)模拟-数字转换器
ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
12位逐次逼近型ADC(采样类型有很多,逐次逼近型虽然慢,但是占用内存小,方便)
1us转换时间(这也是有说法的)
F1系列芯片一般有18个输入通道,可测量16个外部和2个内部信号源(但是STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道,2个内部信号源有一个是基准电压,用来校准电压,要使用内部信号源,记得引用一下相关的函数)
规则组和注入组两个转换单元(一般采用规则组,下面会讨论两者的区别)
二、ADC基本结构
ADC的框架可以在芯片手册中看到,但是简化下来其内容是差不多的。
整体的流程就是:
①最左边是输入通道,16个GPIO口外加两个内部通道
②进入AD转换器,规则组可以选择16个通道,但寄存器只有1个(能点16道菜,但是桌子只能放1道),注入组可以选择4个通道,但寄存器有4个(能点4道菜,桌子能放4道)。
③AD转换器下方的触发控制,可以选择软件触发和硬件触发,硬件触发主要来自于定时器。右边是来自于RCC的ADC时钟CLOCK,ADC逐次比较的过程就是由这个时钟推动的。
④AD数据寄存器上方可以安排一个模拟看门狗,用于监测转换结果的范围,如果超出范围,则输出中断,向NVIC申请中断。
⑤最右下角的开关控制,就是ADC_Cmd函数,用于给ADC上电
不难看出,规则组有16个通道,而注入组有4个通道,但两者对应的寄存器呢,规则组只有一个,而注入组有四个。(举个栗子:规则组去点菜,可以在菜单上写下16个菜名,但是由于桌子只能放得下一道菜,所以就算一口气做了16道菜,最后前面的15道还是会被挤掉,ps:这里可以用DMA转存其他的15道菜,下次会说。然后就是注入组去点菜,他们可以同时在菜单上写下4个菜名,而且自己的桌子也能放下4个菜)
所以我们默认使用规则组就行(注入组的优先级更高,正常使用就只设置规则组就行了。)
三、ADC与IO口的关系
两者的关系通过查看芯片的引脚定义就可以得知,由下图我们可以知道,从PA0-PA7以及PB0-PB1这十个通道都是可以作为ADC的,所以上文才说这个芯片只有十个外部输入通道,同时我们还要注意:每个IO口对应的ADC都有固定的通道,你想用哪个IO口,一定要记得开启对应的通道,开启通道的函数为ADC_RegularChannelConfig。
四、ADC工作模式
在你选择规则组,并想好用哪个IO口后,就要考虑用什么工作模式了,ADC的工作模式分为四种,分别为
1.单次转换,非扫描模式
在非扫描模式下,只能使用序列1,可以理解为本来菜单可以一次选择一排,但现在退化为选择一个,具体流程为:
①在序列1指定我们想转换的通道
②然后触发转换,对通道2进行模数转换
③转换完成后,放入数据寄存器中,同时给EOC标志位置1
④最后判断EOC的标志位,如果为1,则转换完成,我们可以从寄存器中读取结果
如果想转启用转换,就需要再将上面的流程②-④重新来一遍,所以才称之为单次转换。
2.连续转换,非扫描模式
因为是非扫描模式,所以菜单列表只有一个,但现在是连续转换,也就是转换完成后可以继续转换,不需要重新触发,也不用判断EOC的标志位,想要读取时直接从寄存器取就可以了。
3.单次转换,扫描模式
单次转换会在转换完成一次后就停下来,但现在是扫描模式,可以在菜单列表中放很多通道,他会依次进行转换,当然这样代码也会多一个参数,也就是通道数目的参数,表明自己要用几个通道,这些通道转换后会依次放到寄存器中,但由于规则组只有一个寄存器,所以最好拿DMA把数据进行转移,不然就会被依次覆盖。
4.连续转换,扫描模式
理解上面的三种后,这种也就不难理解了
五、ADC的数据对齐
ADC是12位,但数据寄存器是16位,这时就要考虑是左对齐还是右对齐,一般我们使用右对齐,读取寄存器的结果就是直接的转换结果,选择左对齐,相当于数据左移四次,每左移一次数据结果就会乘以2,移动四次,相当于乘以16,所以读取值是比实际值大的。(如果你不想要12位分别率,可以选择左对齐,只读取前八位,这样就是8位的分辨率了。)
六、ADC的校准
建议在每次上电后执行一次校准,启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期
七、AD.C文件
下面呢再说一下AD.C文件的内容,单通道应该怎么写。
#include "stm32f10x.h"// Device header
#include "stm32f10x_adc.h"
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
//开启ADC1的时钟,因为ADC1是APB2上的设备,所以用这个函数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//再开启一下GPIOA的时钟,
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
//用来配置ADCCLK分频器的,APB2有72MHZ的时钟,设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
GPIO_InitTypeDef GPIO_InitStructure;//GPIO的结构体
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
//GPIO的模式,选模拟输入,AIN模式下GPIO口是无效的,
防止GPIO的输入输出对模拟电压造成干扰(可以说,AIN模式就是ADC的专属模式)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_55Cycles5);
//这是规则组配置函数,第二个参数是规则组的通道,第三个参数是规则组的序列数,也就是要放的位置,第四个参数是指定通道采样的时间,需要稳定转换,就选择大的参数
//该函数现在的意思就是在规则组的菜单中,第一个表菜单格放通道8,
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
//有独立模式和双ADC模式,一般都选择独立模式,两个ADC各干各的。
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
//选择是左对齐还是右对齐,这个在笔记上面提到了
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
//选择触发控制的触发源,None指不使用外部触发,而使用内部软件触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
//单次转换,笔记中也有提到
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
//非扫描模式
ADC_InitStructure.ADC_NbrOfChannel = 1;
//其实在非扫描模式下这个数字没什么用,毕竟非扫描只有一个通道
ADC_Init(ADC1,&ADC_InitStructure);
//如果后续还需要中断和看门狗的话,可以在上面的代码后面继续配置。
ADC_Cmd(ADC1, ENABLE);//开启ADC的电源
ADC_ResetCalibration(ADC1);//复位校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
//获取复位校准状态,如果复位校准还没有完成,就会一直在while的空循环中进行等待
ADC_StartCalibration(ADC1);//开始校准
while (ADC_GetCalibrationStatus(ADC1) == SET);
//获取开始校准状态,等待校准完成
}
//校准结束后,ADC就处于准备就绪的状态了,下面的函数是启动转换,获取结果
//需要知道的是,下面启动转换的流程是根据上面工作模式的选择而变化的,我们现在是单次转换非扫描模式,下面的流程分为启动、等待、读取三部分
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发函数
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
//EOC是规则组转换完成标志位,先判断EOC是否置1,如果置1,跳出循环,从而得知转换完成
//这个while循环等待多久是可以计算的,通道的采样周期是55.5,
转换周期为固定的12.5,加一起就是68个周期,而我们分频后是12MHz,
所以时间应该是68/12M=5.6微秒
return ADC_GetConversionValue(ADC1);//ADC获取转换值,
}