文章目录
一、ADC框图
1.ADC的引脚
设计原理图时,将VSSA和VREF-接地,VDDA和VREF+接3.3V,即可得到ADCX_INX的输入范围为:0~3.3V,使用现成的板子就不用在意这些引脚。可查看手册查看。
2.ADC的通道
ADC有多达18个通道,其中,有16个作为外部信号源转换通道ADCX_INX,2个作为内部信号源转换通道:温度传感器、VREFINT。每个外部通道都有对应的GPIO引脚。具体可查看手册。
3.规则通道和注入通道
-
规则通道:正常运行的通道,最多16个通道。
规则通道的转换顺序在ADC_SQR寄存器选择,转换的规则通道的总数写在ADC_SQR1的L[3:0]中。 -
注入通道:插入规则通道的通道(插队),最多4个通道,
其顺序写入ADC_JSQR寄存器中,转换的通道总数写入ADC_JSQR寄存器的L[1:0]中。
各个通道之间可以单次、连续、扫描、或间断模式执行。具体看一下章节
4.外部触发源
转换可以由外部事件触发(例如定时器捕获,EXTI线)。如果设置了EXTTRIG控制位,则外部事
件就能够触发转换。
5.转换时间
采样时间: (ADC_SMPRx)
完成一个通道的读取叫做采样周期。公式为:采样周期=采样时间+12.5个时钟周期。
采样时间就是采样一个通道所花费的时钟周期,通过“ADC采样时间寄存器 (ADC_SMPRx)”来设置各个通道的采样时间(每个通道均可以使用不同的采样时间),设置采样时间越长ADC转换越精确,反过来说,采样时间越短,转换的速度就越快。转换时间计算方法:1/ADCCLK采样周期,例:ADCCLK为12MHz,采样时间设置为1.5周期,则转换时间为:1/12000000(1.5+12.5)≈1.17us。注:ADC_SMPRx设置的是采样时间的时种周期,不是采样周期,它还要加上12.5个时钟周期才是采样周期,不要搞混了!
6.数据寄存器
数据寄存器(ADC_DR)” //规则通道
“ADC 注入数据寄存器x (ADC_JDRx) (x= 1..4)” //注入通道
-
规则数据寄存器: 32位的寄存器,若是同时使用两个ADC则ADC1使用低16位,ADC2使用高16位,由于ADC是12位,ARM为小端,所以需要将数据设置为右对齐模式。
规则数据寄存器仅有一个,为应付多个转换通道, DR寄存器采用覆盖数据的方法,将新的转换数据替换掉旧的转换数据,因此,单个通道转换完成后,必须先读取它的数据(读取DR寄存器),才可以继续下个转换,否则数据会被覆盖。最好的方法是使用DMA,将数据传输到内存里(sram等)。 -
注入通道数据寄存器: 与规则数据寄存器不同的是,注入数据寄存器有4个,对应着4个ADC注入通道,因此不存在数据覆盖的问题。
-
DMA请求 ADC转换完成后,可以直接通过DMA存储。
7.中断
ADC,转换完成后,可以产生中断。中断分为三种:
规则通道转换结束中断
注入转换通道转换结束中断
模拟看门狗中断
-
(规则、注入)转换结束中断:
转换结束中断就和普通的中断一样,中断后拉标志位,写中断服务函数等。通过ADC_CR1的“EOCIE规则通道转换结束中断”位和“JEOCIE注入通道转换结束中断”位,来允许中断。标志位在“ADC状态寄存器(ADC_SR)”中有说明。 -
模拟看门狗中断:
图 4
模拟看门狗,例如图4,我使用ADC转换模拟量,得到一个电压值为1.5V,而我的模拟看门狗设置的低阈值(LTR)为0.5V,高阈值(HTR)为2V,则不触发模拟看门狗中断。
图 5
例如图5,转换模拟量,得到的电压值为3V,超过了我设置的高阈值2V,结果触发模拟看门狗中断。同理,若得到的电压值低于低阈值,也触发模拟看门狗中断。高阈值由“ADC看门狗高阀值寄存器(ADC_HTR)”设置,低阈值由“ADC看门狗低阀值寄存器(ADC_LRT)”设置,因为ADC数据是12位的,因此这两个寄存器也使用12个位来设置其阈值,即最大值为4095(0xFFF),ADC->HTR=阈值/3.3*4095。
注:阈值简单来说就是“限”,上限或者下限,超过上限或超过下限就产生中断。
8.电压转换
ADC有一个电压转换公式为:Y = (3.3 * X ) / 4096。Y为模拟电压值,X为转换后的数值,3.3是电压测量量程的最大值3.3V,4095是12位ADC的最大数值212,就有:
①:3.3V为模拟量,得3.3=Y;4095为ADC的数值,得4095=X。
②:因为最大测量电压为3.3V,而12位ADC的最大量程为4095,所以3.3V就对应4095。因此3.3X=3.34095,同理3.3/X=3.3/4095。
二、规则通道的工作模式
1.扫描模式(ADC_ScanConvMode)
ADCx_SCAN位,扫描ADCx_SQRX寄存器选中的所有通道。
控制是否使用多通道,启用后:ADC每次转换完一个通道,会继续去到下一个通道进行采样转换;否则仅转换当前通道。启用后通常需要配合DMA进行数据存储,否则在进入下一个通道转换后的值会覆盖上一次转换的结果(ADC规则通道的数据寄存器只有一个)。
2.双ADC模式(ADC_Mode)
STM32芯片有ADC1-2-3,三个ADC,其中ADC1和3可以直接和DMA相连,ADC2和ADC1配合才能使用DMA,因此该模式主要是针对ADC1和ADC2,两者可以独立运行,也可以相互配合。
详细内容查看寄存器手册
- 独立模式(ADC_Mode_Independent),两个ADC相互独立工作,通常在仅使用一个ADC时使用;
- 同步规则模式(ADC_Mode_RegSimult),ADC1和ADC2同时转换规则通道,但不能是同一个规则通道,即同一时间内两个ADC不能转换同一个规则通道,转换结果存储为32位(各占高低16位)。
- 快速交叉模式(ADC_Mode_FastInterl),外部触发启动,ADC2先工作进行采样,延迟7个ADC时钟周期后ADC1启动进行采样,此模式通常仅采样一个规则通道,能加快采样率。要注意采样时间不要小于7个ADC时钟周期,避免重复采样(如下图)。
- 慢速交叉模式(ADC_Mode_SlowInterl),与快速交叉模式类似,ADC1、ADC2轮流工作,但均需要等待上一次采样结束14个ADC时钟周期后才启动。该模式会比快速交叉模式的采样速度慢一些,但比独立模式还是快不少,同样要注意采样时间不要小于14个ADC时钟周期。
3.连续转换模式(ADC_ContinuousConvMode)
ADCx_CR2的ADON位。
控制是否连续转换,使能后通道会进行连续的转换,在一轮转换后(即所有通道转换完一遍),又从第一个通道重新开始下一轮;否则通道在结束一轮转换后停止工作。
-
在配置了外部触发(尤其是定时器触发,如ADC_ExternalTrigConv_T2_CC2)后,每次相关事件发生时会触发ADC的转换,此时若仍然连续转换,便失去了外部触发的意义。但也有例外,即IO口的触发,通常仅触发一次,相当于一次使能操作,便可以设置连续转换。外部触发方式的优点是容易控制,更容易符合特定场景需求;
-
如果没有设置外部触发(ADC_ExternalTrigConv_None),则由ADC内部时钟和寄存器配合转换工作。在连续转换模式下,其采样率远远大于普通定时器触发,但缺点也很明显,过快的采样速度要求后续的工作也有超高的处理速度,会给CPU带来巨大的运算负担,在一些计算复杂度较高的场景并不适用。
4.中断触发方式(ADC_ExternalTrigConv)
具体查看寄存器手册
其中前6种都是定时器触发,而第八种则由软件控制使能(仅需使能一次)。
要注意,使用外部触发方式时,只有上升沿才能开启转换,这涉及到TIM的配置。
5.软件触发
-
如果不使用中断触发,后面需要使能软件触发。
使用ADC_SoftwareStartConvCmd()函数
其中SR寄存器的EOC位,标志在ADC转换结束,当读取ADC->DR寄存器的值,或调用清除标志位函数,会清除中断。while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) != SET); return ADC_GetConversionValue(ADC1);
6.ADC结构体
typedef struct
{
uint32_t ADC_Mode; /* 配置双ADC模式 */ADC1和ADC2配合,单个ADC一般设置为独立模式
FunctionalState ADC_ScanConvMode; /*多通道扫描模式 或者 单通道模式 */
FunctionalState ADC_ContinuousConvMode; /* 连续转化模式 还是 单次转换模式*/
uint32_t ADC_ExternalTrigConv; /*定义规则通道的外部触发方式*/,选择None时,选用软件触发
uint32_t ADC_DataAlign; /*设置数据对齐方式*/STM32小端,右对齐
uint8_t ADC_NbrOfChannel; /*设置ADC模拟通道个数*/
}ADC_InitTypeDef;
示例:
ADCInitDef.ADC_ContinuousConvMode=ENABLE; //是否开启连续转换
ADCInitDef.ADC_DataAlign=ADC_DataAlign_Right; //右对齐
ADCInitDef.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
ADCInitDef.ADC_Mode=ADC_Mode_Independent; //独立模式1
ADCInitDef.ADC_NbrOfChannel=2; //2个通道
ADCInitDef.ADC_ScanConvMode=ENABLE; //是否开启扫描,多通道时开启扫描模式
ADC_Init(ADC1,&ADCInitDef); //ADC初始化
三、配置流程(未使用DMA)
1.开启GPIO和ADC时钟 :RCC_APB2PeriphClockCmd
2.配置ADC分频: RCC_ADCCLKConfig(RCC_PCLK2_Div6); //6分频
3.初始化GPIO:GPIO_Init()
4.初始化ADC:ADC_Init()
5.配置通道信息: //ADC_SQRx寄存器、(ADC_SMPRx)寄存器
ADC_RegularChannelConfig(ADC1,ADC_Channel_13,1,ADC_SampleTime_239Cycles5);
ADC1,通道13,转换顺序为第1个,采样时间为239.5周期
6.使能ADC:ADC_Cmd();
7.ADC校准
ADC_ResetCalibration(ADC1); //校准
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
8.开启软件转换:ADC_SoftwareStartConvCmd()
9.等待转换完成,读取ADC的数值,也可用作中断处理,,依照ADC框图中的7中的标志位
使用的是软件触发,不配置中断,不使用中断服务函数。
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));
return ADC_GetConversionValue(ADC1);
注:ADC_DMACmd() //ADC-DMA使能,在运用DMA时使用,此时不使用上面的第9个步骤
四、求得电压的数值
获取采样值的平均值
u32 get_value(void)
{
u16 adc_value = 0;
u8 i = 0;
for(i = 0; i < 10; i++)
{
//开始转换
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
//转换是否结束
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) != SET);
adc_value += ADC_GetConversionValue(ADC1);//读ADC中的值
}
return adc_value / 10;//取样10次的平均值
}
获取电压值
void get_power()
{
u16 ad = 0;
float power;
ad = get_value();
power=3.3 * ad /4096;
printf("电压值:%f\r\n",power); //实际电压值
delay_ms(1000);
}
五、ADC-DMA
如果是多通道采集数据,开启扫描模式,DR寄存器的值会被覆盖,一般使用DMA存储。
将ADC采集到的数据直接通过DMA直接存储到数组,可以防止数据的覆盖
可详细查看DMA的文章介绍
1.DMA工作模式(DMA_Mode)
-
正常缓存模式(DMA_Mode_Normal),在该模式下,每次使能仅进行一轮传输,一旦计数器的值减至0时,DMA传输会停止。想要再次传输,必须重新设置计数器CNDTR的数值;
-
循环工作模式(DMA_Mode_Circular),主要处理连续的数据传输,每进行一轮传输,计数器会被自动恢复为初始值,同时DMA传输进入下一轮,非常适合同ADC多通道(扫描)模式一起工作。
2.DMA通道选择
3.DMA中断
DMA的中断是这个实验的桥梁,连接着用户和外设,我们需要在中断里处理数据,包括计算和展示数据等,但这样很浪费资源。有一种更好的做法是,基于UCOS系统,在中断服务程序中通过事件标志的方式通知数据处理相关的任务及时运行。
4.数据大小
ADC传输的时16位,内存存储大小时32位。