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

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

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

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

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

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

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

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

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

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

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