STM32 ADC模拟/数字转换详解

一、ADC的简介

PWM—》实际上就是使用DAC(数字–>模拟)。因为PWM只有完全断开和完全接通,所以在大功率电机中损耗很小,所以使用PWM比较合适。

1.什么是ADC

1)将【电信号】–>【电压】–>【数字量】

2)ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字量,建立模拟电路到数字电路的桥梁。

3)12位逐次逼近型ADC,1us转换时间(表示从产生电压到转换得出结果所使用的时间)

2.常见的ADC

3.并联比较型工作示意图

1)比较器:当两个数值相同时才会生成信号传输给编码器

2)D0,D1,D2(从低位–》高位)—》二进制数(分辨率)—》2^3

4.逐次逼近型工作示意图(DAC工作原理)

如果是n位的锁存缓冲器(数码控制器),就需要进行n次的判断

逐次逼近:就是如果是12位的DAC转换,则我们先将DAC赋值为2^12的一半,然后逐次的按照一半递减(增加)。所以12位DAC转换就需要比较12次。

如果DAC输出的电压比较小,就增大DAC

如果DAC输出的电压比较大,就减小DAC

直到DAC输出的电压和外部通道输入的电压近似相等。 

5.ADC特性参数

1.分辨率:刻度划分

表示ADC能分辨的最小模拟量,用二进制位数进行表示,比如;8,10,12

比如此时电压为3.3V,我们使用12位进行表示   2^12=4096   3.3%4096=0.0008V,表示当数字量为1的时候,输出电压为0.0008V。

输入电压范围:0-3.3V,转换结果范围:0-4095(2^12)

2.转换时间

表示完成一次A/D转换所需要的时间,转换时间越短,采用频率就越高

假设1s的时间中转换时间为200ms,则表示可以转换5次

3.精度:物理量的精确程度

4.量化误差

6.STM32各系列ADC主要特性

7.ADC基本结构

二、ADC工作原理

1.ADC框图简介

1.参考电压/模拟部分电压

1) 输入电压的范围在参考电压的两个范围之间

2)Vref+和Vref-分别接着Vdda(3.3V)和Vssa(0V)

2.输入通道

输入的GPIO必须具有模拟输入功能的IO口才可以。

GPIO通道:快速通道VS慢速通道

内部ADC源直接绑定,外部ADC绑定GPIO

18个输入通道,可测量16个外部和2个内部信号源(温度传感器 && 内部的参考电压)

 ADC1和ADC2两个的通道都使用同一个引脚。—->双ADC

3.转换序列(转换顺序)

1)转换被组织分为2组:规则组&&注入组【注入组可以打断规则组的转换】

2)规则组最多可以有16个转换(通道),注入组最多有4个转换(通道)

 规则组和注入组执行优先级对比

注入组(类似于中断)的优先级比规则组的优先级高

规则序列:(regular channel)

1)规则组有16个通道

2)这里的意思理解为,有16个规则通道,即为16个规则的不同编号的盘子,每个盘子可以放18个通道即为GPIO其中的一个口

3)必须按顺序来执行(如果想要执行通道3,则通道1和通道2都要执行)

4)我们有3个ADC,表示可以设置3个最高的优先级【可以同步进行】

5)一个菜单,可以点16个菜,也可以只写一个菜。【同时上16个菜,但是只能一个一个上(因为只有一个寄存器),否则前面会被覆盖—>所以我们使用DMA】

注入序列:(injected channel)

1)注入组有4个通道

2)注入组的寄存器写入位是反向写入的

3)一次性最多可以点4个菜,且可以同时上4个菜,不会被覆盖【因为有4个寄存器】

4.触发源

1)触发转换分为:a.ADON位触发转换  b.外部事件触发转换(规则组和注入组)

规则组外部触发

注入组外部触发

5.转换时间

如何设置ADC时钟?

1)ADCCLK的最大时钟频率是:14MHZ

如何设置ADC转换时间

1)ADC中的最短转换时间为:1us【在ADC时钟频率为:14MHZ,采样时间为1.5个ADC时钟周期,12.5个周期(固定值–>12位寄存器)的情况下】

2)采样时间(可以进行编程的)越大,就可以尽量避免毛刺信号的干扰,精确度越高

6.数据寄存器

规则组只有一个寄存器,注入组有四个寄存器。在没有DMA的帮助下,规则组只能一次输入ADC,如果多几个,会被覆盖。

数据对齐

7.中断

DMA请求(只适用于规则组)

规则组每一个通道转换结束后,除了可以产生中断外,还可以产生DMA请求,我们利用DMA及时把转换好的数据传输到指定的内存中,防止数据被覆盖

2.单次转换模式VS连续转换模式

单次转换模式:如果我们不需要实时检测,则使用单次

连续转换模式:如果需要实时检测,则使用连续

3.转换/扫描模式

关闭扫描模式:只能扫描第一个通道

使用扫描模式:表示扫描全部通道

连续是一个通道多次采集,扫描是每个通道依次采集

不同模式组合的作用

扫描:切换通道(遍历),连续:多次

单次转换,非扫描模式 

 单次转换,扫描模式

连续转换,非扫描模式 

连续转换模式 

4.ADC校准

如果需要使用到精确计算,则需要校准

影响ADC转换的因素:

1)温飘(温度影响)

2)基准电压值

	/*ADC校准
	1)先重置校准位:ADC_ResetCalibration	-->重置所选ADC校准寄存器。
	2)判断校准位是否被清除:	ADC_GetResetCalibrationStatus-->	获取所选ADC复位校准寄存器状态。
	3)进行ADC校准: ADC_StartCalibration--->启动选定的ADC校准过程。
	4)判断校准是否完成: ADC_GetCalibrationStatus-->获取所选ADC校准状态
	*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);//判断是否校准完成【1表示未完成校准】
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);

5.ADC与低功耗

外部引脚输入也会耗电—>采样时间

采样间隔周期

要采样则唤醒,不需要就进入睡眠。

 三、ADC采集实验

1.实验简要

2.寄存器描述

1.ADC控制寄存器 1(ADC_CR1)

2.ADC控制寄存器 2(ADC_CR2)

3.ADC采样时间寄存器 1(ADC_SMPR1)

通道10-通道17的设置

4.ADC采样时间寄存器 2(ADC_SMPR2)

通道0-通道9的设置

5.ADC规则序列寄存器 1(ADC_SQR1)

设置第13-第16个转换

6.ADC规则序列寄存器 2(ADC_SQR2)

设置通道12-通道7

7.ADC规则序列寄存器 3(ADC_SQR3)

设置通道6-通道0

8.ADC规则数据寄存器(ADC_DR)

9.ADC状态寄存器(ADC_SR)

3.ADC采集实验配置步骤

相关HAL库介绍

关键结构体介绍

ADC句柄

ADC通道设置

四、AD单通道

1.硬件接线

通过控制接入的阻值来读取引脚上的电压值,来输出模拟信号

2.代码编写

1)ADC输入的通道对应的是GPIO的AIN(模拟输入)模式。

2)我们在使用【ADC_GetConversionValue】的时候,内部会读取DR寄存器的值,而读取DR寄存器的值会自动清除EOC标志【所以我们在后面不需要手动清除】

adc.c

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*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);					//将PA0引脚初始化为模拟输入
	
	/*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);		//规则组序列1的位置,配置为通道0
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,失能,每转换一次规则组序列后停止
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式,失能,只转换规则组的序列1这一个位置
	ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1
	
	/*ADC使能*/
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行
	
	/*ADC校准
	1)先重置校准位:ADC_ResetCalibration	-->重置所选ADC校准寄存器。
	2)判断校准位是否被清除:	ADC_GetResetCalibrationStatus-->	获取所选ADC复位校准寄存器状态。
	3)进行ADC校准: ADC_StartCalibration--->启动选定的ADC校准过程。
	4)判断校准是否完成: ADC_GetCalibrationStatus-->获取所选ADC校准状态
	*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);//判断是否校准完成【1表示未完成校准】
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

/**
  * 函    数:获取AD转换的值
  * 参    数:无
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(void)
{
	//启用或禁用所选ADC软件启动转换
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//软件触发AD转换一次
	//判断ADC转换是否完成
	/**
	上面我们设置了【将时钟进行RCC_ADCCLKConfig(RCC_PCLK2_Div6);	6分频--->则我们最后的结果为72 MHZ / 6= 12
	然后我们设置ADC采用频率为【ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);55个时间周期,整个周期--->12.5+55=68个周期】
	则我们需要的时间是--->f=1/T=1/12 --》时间=f*周期=1/12*68=5.6us
	*/	
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);	//等待EOC标志位,即等待AD转换结束
	//获取转换值
	return ADC_GetConversionValue(ADC1);					//读数据寄存器,得到AD转换的结果
}

main.c

uint16_t ADValue;			//定义AD值变量
float Voltage;				//定义电压变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();			//OLED初始化
	AD_Init();				//AD初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "ADValue:");
	OLED_ShowString(2, 1, "Voltage:0.00V");
	
	while (1)
	{
		ADValue = AD_GetValue();					//获取AD转换的值
		Voltage = (float)ADValue / 4095 * 3.3;		//将AD值线性变换到0~3.3的范围,表示电压
		
		OLED_ShowNum(1, 9, ADValue, 4);				//显示AD值
		OLED_ShowNum(2, 9, Voltage, 1);				//显示电压值的整数部分
		OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);	//显示电压值的小数部分
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间
	}
}

五、AD多通道

1.硬件接线

2.代码编写

ad.c

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						//选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA0、PA1、PA2和PA3引脚初始化为模拟输入
	
	/*不在此处配置规则组序列,而是在每次AD转换前配置,这样可以灵活更改AD转换的通道*/
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;						//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		//模式,选择独立模式,即单独使用ADC1
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//数据对齐,选择右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//外部触发,使用软件触发,不需要外部触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		//连续转换,失能,每转换一次规则组序列后停止
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;			//扫描模式,失能,只转换规则组的序列1这一个位置
	ADC_InitStructure.ADC_NbrOfChannel = 1;					//通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
	ADC_Init(ADC1, &ADC_InitStructure);						//将结构体变量交给ADC_Init,配置ADC1
	
	/*ADC使能*/
	ADC_Cmd(ADC1, ENABLE);									//使能ADC1,ADC开始运行
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

/**
  * 函    数:获取AD转换的值
  * 参    数:ADC_Channel 指定AD转换的通道,范围:ADC_Channel_x,其中x可以是0/1/2/3
  * 返 回 值:AD转换的值,范围:0~4095
  */
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);					//读数据寄存器,得到AD转换的结果
}

main.c

uint16_t AD0, AD1, AD2, AD3;	//定义AD值变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	AD_Init();					//AD初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "AD0:");
	OLED_ShowString(2, 1, "AD1:");
	OLED_ShowString(3, 1, "AD2:");
	OLED_ShowString(4, 1, "AD3:");
	
	while (1)
	{
		AD0 = AD_GetValue(ADC_Channel_0);		//单次启动ADC,转换通道0
		AD1 = AD_GetValue(ADC_Channel_1);		//单次启动ADC,转换通道1
		AD2 = AD_GetValue(ADC_Channel_2);		//单次启动ADC,转换通道2
		AD3 = AD_GetValue(ADC_Channel_3);		//单次启动ADC,转换通道3
		
		OLED_ShowNum(1, 5, AD0, 4);				//显示通道0的转换结果AD0
		OLED_ShowNum(2, 5, AD1, 4);				//显示通道1的转换结果AD1
		OLED_ShowNum(3, 5, AD2, 4);				//显示通道2的转换结果AD2
		OLED_ShowNum(4, 5, AD3, 4);				//显示通道3的转换结果AD3
		
		Delay_ms(100);			//延时100ms,手动增加一些转换的间隔时间
	}
}

四、使用CubeMX创建ADC和DMA

将外部0-3.3V的模拟信号接入到单片机底座脚P11(PA1)口

1.CubeMX使用

因为ADC1和ADC2所使用的通道对应的GPIO引脚是一致的,所以使用ADC1或者ADC2都可以。

0.其他相关设置

1)选择外部晶振

2)启动DMA

1.通道选择

2.中断选择

此时我们的实验是将从ADC获取到的模拟信号通过转换为电压传输给DAM,然后DMA在通知CPU。

1)ADC不需要中断:因为当ADC采样到的结果直接丢给DMA,而不需要停下来告诉DMA,ADC不需要考虑是否传输成功。因为他们两个之间有专门的传输通道。

2)DMA需要中断:因为当接收到ADC传输过来的数据后,DMA需要告诉CPU,我接收到ADC的数据了。

3.ADC中的DMA Setting

绑定ADC

我们一般要将结果计算出来,我们都会多测量几次,然后求平均值,所以我们的Memory地址要递增,要不然会将上一个数据覆盖掉。

4.ADC中的Parameter Settings

5.总结

本实验使用了APB2(ADC1的时钟频率为14MhZ)

使用了ADC1的通道1,测试外部0-3.3V的模拟信号

2.代码编写

3.MDA 中​​​​​​断处理的内部逻辑

1.HAL_ADC_Start_DMA

这个函数实际上是ADC中的函数,通过DMA来传输ADC数据

ADC传输完的中断入口

2.HAL_DMA_Start_IT

设置DMA中断的相关事宜

3.DMA_SetConfig

完成DMA和DAC之间的联系

4.HAL_ADC_ConvCpltCallback

DMA中断实际上是调用了ADC【HAL_ADC_ConvCpltCallback】—>真正的中断回调函数

如果想要在中断中处理什么,就重写这个函数

5.DMA1_Channel1_IRQHandler

我们往里面查看发现并没有什么真正有执行的代码

      /* Half transfer callback */
      hdma->XferHalfCpltCallback(hdma);

      /* Transfer complete callback */
      hdma->XferCpltCallback(hdma);

分析可知【HAL_UART_IRQHandler】实际上并没有做什么

五、STM32随机数生成器

1.什么是随机数

1.真正的随机数

2.伪随机数

2.随机数的生成

1.用纯软件算法

伪随机数生成算法 – shine-lee – 博客园 (cnblogs.com)

2.采集随机事件为元素生成

3.用Soc内置伪随机数发生模块生成:使用硬件方法【HAL_RNG】

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 ADC模拟/数字转换详解

发表评论