六、Stm32学习-ADC-单通道与多通道-扫描模式与连续或单次转换

今天就学个ADC吧。

ADC的应用场景很广泛,目前接触一个多路ADC测电压的储能管理。特别需要注意的点是数据的对比,数据的转存,数据的精度,数据的符号等,在这次储能管理项目中,这是最基本的底层。ADC检测准确了,才可以做后面的业务代码。

1.ADC简介

 要注意,不同芯片的ADC是不同位数的,这款单片机 是12位的ADC,所以它的转换结果就是0~4095的范围。这个范围也就是熟知的AD值。

2.规则组与注入组的区别

先看注入组,最多4个通道,一次最多检测4个ADC通道,然后传入注入通道的数据寄存器,有4 * 16位,所以不用担心数据覆盖的问题,直接读取。

再规则组,最多16个通道,一次最多检测16个ADC通道,然后传入规则组的数据寄存器,只有16位,这边的数据是一个通道一个通道传送过去的, 所以如果数据不及时转存,数据就会被覆盖。一般怕配合DMA使用,达到硬件全自动。

3.ADC转换模式

单次转换,非扫描模式:在第一次ADC触发之后,将AD值存在数据寄存器里面,并且会产生一个EOC标志位置1。那我们通过判断这个标志位,如果转换完了,那就读取数据。然后阅读手册发现,在读取数据寄存器之后,硬件会自动清零EOC的标志位。那么就可以开始下一次转换了。

如果一次想要测量不同通道的AD值,那么就要在开始转换之前,将序列1处的通道换成你所需要的通道。

连续转换,非扫描模式:这个模式开启转换之后不需要判断标志位,一次转换结束之后他会继续开启下一次转换,直到你想要关闭ADC。所以只需要配置完ADC后,开启一次转换即可,然后需要AD值的数据的时候直接去寄存器读取就好。(似乎想要多个通道的话,需要关闭ADC,切换通道,再开启ADC,有用到再试试)

单次转换,扫描模式:在这个模式中,可以一次转换多个通道,一次将多个通道转换的数据存入数据寄存器,然后在转换结束的时候产生一个EOC标志位,和第一个模式一样,读取数据寄存器后硬件会自动清除EOC的标志位。等待开启下一次的转换。

需要注意的是,左边多了个通道数目的配置,这个是只在扫描模式的时候会有用。一次扫描多个通道,进行AD转换。 如果是7,就只看前7个序列所对应的通道。

 连续转换,扫描模式:这个模式,在开启一次转换时候,转换结束会自动进行下一次转换。然后规则组的数据寄存器只有16位,所以只能存入1个通道的数据,如果你不转存数据的话,他会立马被下一个通道的数据覆盖,这边一般配合DMA使用。所以还要面临一个问题16位的数据寄存器和AD值范围为12位的数据该如何存储呢?

然后还有一个间断模式,这个间断模式是在扫描模式中才会出现的。比如说,转换几个通道之后,间断一会,再进行下面通道的转换。

4.外部触发控制

有很多的信号可以控制ADC的触发

 当然可以手动触发开启,然后由ADC触发DMA,再搬运数据。

5.数据对齐

右对齐就是高位补零,那么数据读出来就是原来的数据。

左对齐相当于整体左移,也就是扩大16倍,提高精度 。也可以取出高八位,舍弃后面的位置,降低分辨率,使你的AD值变成8位的AD值,相当于从0~4095变成了0~255

6.转换时间

如果对速度没有要求,转换速率就忽略。

采样的时间就是,内部的DAC通过电压比较器会去对比外部的电压,然后输出近似的电压值,然后DAC输出的值就是AD值,这个过程需要一定的时间。

7.自校准模式

8.程序编写(ADC单通道+单次转换+非扫描模式)

(1) ADC时钟分配

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz

开启ADC外设时钟,阅读手册,给ADC的时钟进行6分配,变成12MHZ。

 

时钟框图很重要,可以对应寄存器去看,包括AHB预分频在哪里设置了,APB2又在哪里设置了,似乎代码里面没有?CTRl+F去找找吧,都有的。还有系统时钟部分等。虽然有些官方demo里面都有,可以自己找找看对应框图加深理解。

(2)初始化对应GPIO引脚 

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

这边GPIO的模式要配置位ADC专属输入模式。阅读手册可以知道。

(3)ADC初始化配置

	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
	
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;
	ADC_InitStructure.ADC_NbrOfChannel=1;
	ADC_InitStructure.ADC_ScanConvMode=DISABLE;
	ADC_Init(ADC1,&ADC_InitStructure);
	
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行

因为使用的是规则组,所以要先配置规则组里面的通道,这句代码的意思就是,在序列1的位置配置为ADC的通道0,并且ADC的采样周期为55。

然后初始化结构体。选择单次转换模式;选择数据右对齐;选择ADC外部触发无;选择ADC的模式为单ADC模式;选择一次转换的通道为1;选择非扫描模式;最后使能ADC。那么ADC的配置就ok了,只差开启ADC。

(4)ADC自检

	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);

这种一般的demo里面都有,直接复制。

(5)ADC数据读取

uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

 第一句代码开启ADC的转换,然后判断标志位,等待标志位变成1时。代表着转换完成,就可以用第三句代码读取ADC的数据了。

(6)AD值转换

Voltage = (float)ADValue / 4095 * 3.3;		//将AD值线性变换到0~3.3的范围,表示电压

需要注意的是AD值是整型的,需要强转为浮点型。

9.程序编写(AD单通道+连续转换+非扫描模式)

(1)ADC的配置部分修改一下

ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;

开启ADC的连续转换模式,其余不变。

(2)ADC数据读取

ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//软件触发AD转换一次

将这句代码放在自检之后,开启ADC转换就好。

然后我们直接读取寄存器数据就好

uint16_t AD_GetValue(void)
{
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

10.程序编写(AD单通道+单次转换+扫描模式)

单次转换+扫描模式和单次转换+非扫描模式一样,因为扫描的通道只有一个。不多赘述。

ADC_InitStructure.ADC_ScanConvMode=ENABLE;

开启扫描模式,关闭连续转换模式,其余代码和单次转换+非扫描模式一样 

11.程序编写(AD单通道+连续转换+扫描模式)

ADC配置先开启连续转换和扫描模式。

但是我们不再需要去判断EOC的标志位,连续转换模式只要开启了就会不断进行转换。然后我们只有一个通道,只扫描一个。所以一直读一个数据寄存器的数据就可以了,不怕数据覆盖。

12.程序编写(AD多通道+单次转换+非扫描模式)

下次会用DMA来实时搬用ADC的数据,这次就在转换开启之前就切换通道,这样就可以读很多通道的AD值了,但是会慢一些。而且我们一次只读一个通道,所以扫描模式也可以关闭。

(1)重要代码

uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);	//在每次转换前,根据函数形参灵活更改规则组的通道1
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	return ADC_GetConversionValue(ADC1);
}

		AD0 = AD_GetValue(ADC_Channel_0);		//单次启动ADC,转换通道0
		AD1 = AD_GetValue(ADC_Channel_1);		//单次启动ADC,转换通道1

  • 38
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值