ADC 模数转换器

ADC(Analog-Digital Converter)模拟 - 数字转换器

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

DAC 是数字 - 模拟转换器,但是 PWM 比 DAC 简单好用,一般使用 DAC 驱动电机,目前 DAC 主要应用于波形生成领域

12 位逐次逼近型 ADC,1us 转换时间

分辨率:0 ~ 2^12^ -1, 即 0 ~ 4095 ,位数越高量化结果越精细,分辨率越高

输入电压范围:0 ~ 3.3V,转换结果范围:0 ~ 4095

18 个输入通道,可测量 16 个外部和 2 个内部信号源

STM32F103C8T6 ADC 资源:ADC1、ADC2,10 个外部输入通道

规则组和注入组两个转换单元

一次启动一个组,可以连续转换多个数据

模拟看门狗自动监测输入电压范围

检测指定通道,当数值超出阈值时,自动申请中断,无需手动在主函数中进行判断

一、逐次逼近型 ADC

image-20221211123629747

二、ADC 框图

2.1 完整框图

image-20221211131053264

2.2 分析

image-20221211134242951



image-20221211143348853

2.3 输入通道对应引脚

通道 ADC1 ADC2 ADC3
通道 0 PA0 PA0 PA0
通道 1 PA1 PA1 PA1
通道 2 PA2 PA2 PA2
通道 3 PA3 PA3 PA3
通道 4 PA4 PA4 PF6
通道 5 PA5 PA5 PF7
通道 6 PA6 PA6 PF8
通道 7 PA7 PA7 PF9
通道 8 PB0 PB0 PF10
通道 9 PB1 PB1
通道 10 PC0 PC0 PC0
通道 11 PC1 PC1 PC1
通道 12 PC2 PC2 PC2
通道 13 PC3 PC3 PC3
通道 14 PC4 PC4
通道 15 PC5 PC5
通道 16 温度传感器
通道 17 内部参考电压

三、转换模式

1、单次转换,非扫描模式

2、连续转换,非扫描模式

3、单次转换,扫描模式

4、连续转换,扫描模式

image-20221211144033797

image-20221211144356149

四、触发控制

image-20221211144655873

五、数据对齐

右对齐

image-20221211144528294

左对齐

image-20221211144533308

一般使用右对齐,读出数据直接就是转换结果

左对齐可以降低精度,只读高八位,舍去后四位

六、转换时间

AD 转换的步骤:采样,保持,量化,编码

其中采样、保持放一起,量化、编码放一起

STM32 ADC 的总转换时间为:T~CONV~ = 采样时间 + 12.5 个 ADC 周期

例如:当 ADCCLK=14MHz,采样时间为 1.5 个 ADC 周期

T~CONV~ = 1.5 + 12.5 = 14 个 ADC 周期 = 1μs

七、校准

ADC 有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码 (数字值),这个码用于消除在随后的转换中每个电容器上产生的误差

建议在每次上电后执行一次校准

启动校准前, ADC 必须处于关电状态超过至少两个 ADC 时钟周期

实例:AD 单通道和 AD 多通道

AD 单通道

功能实现:用电位计产生 0~3.3V 的连续变化的模拟电压,在 LCD 显示转换后的数据和处理后得到的电压值

AD.c

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include "stm32f10x.h"                  // Device header

void AD_Init(void){
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);

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);

//选择规则组的输入通道
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
//初始化ADC
ADC_InitTypeDef ADC_InitTypeDefstructure;
ADC_InitTypeDefstructure.ADC_DataAlign = ADC_DataAlign_Right;//对齐方式
ADC_InitTypeDefstructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发源选择
ADC_InitTypeDefstructure.ADC_Mode = ADC_Mode_Independent;//配置ADC工作模式
ADC_InitTypeDefstructure.ADC_ContinuousConvMode = DISABLE;//连续转换
ADC_InitTypeDefstructure.ADC_ScanConvMode = DISABLE;//扫描转换
ADC_InitTypeDefstructure.ADC_NbrOfChannel = 1;//通道数目

ADC_Init(ADC1,&ADC_InitTypeDefstructure);

//开启ADC
ADC_Cmd(ADC1,ENABLE);
//校准ADC
ADC_ResetCalibration(ADC1);//复位校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET);//等待校准完成
ADC_StartCalibration(ADC1);//复位校准
while(ADC_GetCalibrationStatus(ADC1) == SET);

}

uint16_t AD_GetValue(void){
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);//获取标志位状态
return ADC_GetConversionValue(ADC1);//获取转换值
}

AD.h

c
1
2
3
4
5
6
7
#ifndef __AD_H__
#define __AD_H__

void AD_Init(void);
uint16_t AD_GetValue(void);
#endif

main.c

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "AD.h"

uint16_t value;
float V;
int main(){
OLED_Init();
AD_Init();

OLED_ShowString(1,1,"ADValue:");
OLED_ShowString(2,1,"V:0.00V");
while(1){
value = AD_GetValue();
V = (float)value /4095 * 3.3;
OLED_ShowNum(1,9,value,4);
OLED_ShowNum(2,3,V,1);
OLED_ShowNum(2,5,(uint16_t)(V*100)%100,2);

}
}

OLED.c

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "stm32f10x.h"                  // Device header

void LCD_Init(void){

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);
}

void LED1_on(void){
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
void LED1_off(void){
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
void LED1_Turn(void){
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1) == 0){
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
else{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
}
void LED2_on(void){
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
void LED2_off(void){
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
void LED2_Turn(void){
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2) == 0){
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
else{
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
}

OLED.h

c
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef __LCD_H__
#define __LCD_H__

void LCD_Init(void);
void LED1_on(void);
void LED1_off(void);
void LED2_on(void);
void LED2_off(void);
void LED1_Turn(void);
void LED2_Turn(void);
#endif

AD 多通道

功能实现:同时测光敏、热敏、反射红外、电位计的转换值,并显示

AD.c

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "stm32f10x.h"                  // Device header

void AD_Init(void){
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);

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);


//初始化ADC
ADC_InitTypeDef ADC_InitTypeDefstructure;
ADC_InitTypeDefstructure.ADC_DataAlign = ADC_DataAlign_Right;//对齐方式
ADC_InitTypeDefstructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发源选择
ADC_InitTypeDefstructure.ADC_Mode = ADC_Mode_Independent;//配置ADC工作模式
ADC_InitTypeDefstructure.ADC_ContinuousConvMode = DISABLE;//连续转换
ADC_InitTypeDefstructure.ADC_ScanConvMode = DISABLE;//扫描转换
ADC_InitTypeDefstructure.ADC_NbrOfChannel = 1;//通道数目

ADC_Init(ADC1,&ADC_InitTypeDefstructure);

//开启ADC
ADC_Cmd(ADC1,ENABLE);
//校准ADC
ADC_ResetCalibration(ADC1);//复位校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET);//等待校准完成
ADC_StartCalibration(ADC1);//复位校准
while(ADC_GetCalibrationStatus(ADC1) == SET);

}

uint16_t AD_GetValue(uint8_t ADC_Channel){
//选择规则组的输入通道
ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);//获取标志位状态
return ADC_GetConversionValue(ADC1);//获取转换值
}

AD.h

c
1
2
3
4
5
6
7
#ifndef __AD_H__
#define __AD_H__

void AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);
#endif

main.c

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "AD.h"

uint16_t AD0,AD1,AD2,AD3;
float V;
int main(){
OLED_Init();
AD_Init();

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);
AD1 = AD_GetValue(ADC_Channel_1);
AD2 = AD_GetValue(ADC_Channel_2);
AD3 = AD_GetValue(ADC_Channel_3);

OLED_ShowNum(1,5,AD0,4);
OLED_ShowNum(2,5,AD1,4);
OLED_ShowNum(3,5,AD2,4);
OLED_ShowNum(4,5,AD3,4);

}
}

OLED.c

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "stm32f10x.h"                  // Device header

void LCD_Init(void){

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);
}

void LED1_on(void){
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
void LED1_off(void){
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
void LED1_Turn(void){
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1) == 0){
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
else{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
}
void LED2_on(void){
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
void LED2_off(void){
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
void LED2_Turn(void){
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2) == 0){
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
else{
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
}

OLED.h

c
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef __LCD_H__
#define __LCD_H__

void LCD_Init(void);
void LED1_on(void);
void LED1_off(void);
void LED2_on(void);
void LED2_off(void);
void LED1_Turn(void);
void LED2_Turn(void);
#endif