STM32模数转换
原理结构
-
每个 ADC 有 12 位、10 位、8 位和 6 位可选,每个 ADC 有 16 个外部通道,还有内部通道,不同的芯片的ADC内部通道连接不相同,可以查看数据手册
-
独立模式就是仅仅适用三个ADC的其中一个,双重模式就是同时使用ADC1和 ADC2,而三重模式就是三个 ADC 同时使用。
-
ADC结构如下:
-
电压输入范围:V REF- ≤ V IN ≤ V REF+ ,一般把 V SSA 和 V REF- 接地,把 V REF+ 和 V DDA 接 3V3,得到 ADC 的输
入电压范围为:0~3.3V。 -
外部的 16 个通道在转换的时候又分为规则通道和注入通道,其中规则通道最多有 16 路,注入通道最多有 4 路。
- 注入通道和中断程序很像,当该通道有输入时,会先进行该通道的转换,之后再去对其他的通道进行操作
转换顺序
-
规则顺序:
-
在设置多个通道的转换时,可以通过规则序列寄存器来设置优先级,规则序列寄存器有 3 个,分别为 SQR3、SQR2、SQR1
-
比如想让第16通道在第7个转换,则在SQR2的SQ7写入16
-
-
注入序列寄存器 JSQR 只有一个,最多支持 4 个通道,具体多少个由 JSQR 的 JL[1:0] 决定
-
如果JL的值小于4的话,则JSQR跟SQR决定转换顺序的设置不一样,第一次转换的不是JSQR1[4:0],
而是 JCQRx[4:0] ,x = (4-JL),跟 SQR 刚好相反。 -
如果 JL=00(1 个转换),那么转换的顺序是从 JSQR4[4:0] 开始,而不是从 JSQR1[4:0] 开始,
触发源
- ADC 转换可以由 ADC 控制寄存器 2: ADC_CR2 的 ADON 这个位来控制,写 1 的时候开始转换,写 0 的时候停止转换
- ADC 还支持外部事件触发转换,这个触发包括内部定时器触发和外部 IO 触发
- 如果使能了外部触发事件,我们还可以通过设置 ADC 控制寄存器 2:ADC_CR2 的 EXTEN[1:0] 和JEXTEN[1:0] 来控制触发极性,可以有 4 种状态,分别是:禁止触发检测、上升沿检测、下降沿检测以及上升沿和下降沿均检测。
转换时间
- ADC时钟:
- ADC 输入时钟 ADC_CLK 由 PCLK2 经过分频产生,最大值是 36MHz,典型值为 30MHz,分频因子由 ADC 通用控制寄存器 ADC_CCR 的 ADCPRE[1:0] 设置
- ADCADC 需要若干个 ADC_CLK 周期完成对输入的电压进行采样
- 周期数可通过 ADC 采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0] 位设置,ADC_SMPR2 控制的是通道 0~9,
ADC_SMPR1 控制的是通道 10~17 - 每个通道可以分别用不同的时间采样。其中采样周期最小是3 个,即如果我们要达到最快的采样,那么应该设置采样周期为 3 个周期,这里说的周期就是1/ADC_CLK。
- ADC 的总转换时间跟 ADC 的输入时钟和采样时间有关,公式为:
Tconv = 采样时间 + 12 个周期 - 一般我们设置 PCLK2=84MHz,经过 ADC 预分频器能分频到最大的时钟只能是 21M,采样周期设置为 3 个周期,算出最短的转换时间为 0.7142us
数据寄存器
- 规则组的数据放在 ADC_DR 寄存器,注入组的数据放在 JDRx
- ADC_DR和 JDRx都是32位寄存器,低16位有效,可以选择数据的对齐方式
- 通道有很多,但对应的寄存器只有一个,因此当转换完成之后,应立刻将数据取出,可以使用DMA将数据传出去,如果不使用DMA就要使用 ADC 状态寄存器 ADC_SR 获取当前 ADC 转换的进度状态,进而进行程序控制
- 如果是使用双重或者三重模式那规矩组的数据是存放在通用规矩寄存器 ADC_CDR
电压转换
-
模拟电压经过 ADC 转换后,是一个 12 位的数字值
-
如果ADC的电压范围是0~3.3V,ADC 是 12 位的,那么 12 位满量程对应的就是 3.3V,12 位满量程对应的数字值是:2^12。数值 0 对应的就是 0V,那么通过比例关系就可以计算出当前的电压值
-
例如当前数值是X,外部电压为Y
X 2 12 = Y 3.3 ; Y = 3.3 ∗ X 2 12 \frac{X}{2^{12}}= \frac{Y}{3.3} ; Y= \frac{3.3*X}{2^{12}} 212X=3.3Y;Y=2123.3∗X实际测试
-
传感器为火焰强度传感器,通道stm32的AD转换将其输入的模拟信号转化为数字信号,并进行显示
-
先在CubeMX中进行相关的配置
-
主要程序如下:
#include "Flame_Sensor.h" uint32_t Flame_Sensor_Get() { HAL_ADC_Start(&hadc1); //轮询转换开启 HAL_ADC_PollForConversion(&hadc1,10); //轮询转换 return HAL_ADC_GetValue(&hadc1); } uint32_t temp_val=0; uint32_t Flame_Sensor_Get_Val(void) { uint8_t t; for(t=0;t<LSENS_READ_TIMES;t++) { temp_val+=Flame_Sensor_Get(); //读取ADC值 osDelay(5); } temp_val/=LSENS_READ_TIMES;//得到平均值 if(temp_val>4000)temp_val=4000; //HAL_ADC_Stop(&hadc1); temp_val=(100-(temp_val/40)); return temp_val; } //主函数循环部分如下: for(;;) { Flame_Sensor_Value=0; Flame_Sensor_Value=Flame_Sensor_Get_Val(); sprintf(shuchu0,"Strength:%d",Flame_Sensor_Value); for(m=0;shuchu0[m]!='\0';m++) { shuchu1[m]=shuchu0[m]; } shuchu1[m]='\0'; OLED_CLS(); OLED_ShowStr(0,4,shuchu1,2); }
-
部分内容整理自野火