ADC(模拟-数字转化器)
- ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁;
- AD转换步骤:采样、保持、量化、编码;
- 12位逐次逼近型ADC,1us转换时间;
- 输入电压范围:0~3.3V,转换结果范围:0~4095;
- 18个输入通道,可以测量16个外部和2个内部信号源(整个系列);
- 有规则组和注册组两个转换单位;
- 模拟看门狗自动监测输入电压范围;
- 总转换时间:T = 采样时间 + 12.5个ADC周期;
- STM32F1003C8T6 ADC资源:ADC1、ADC2、10个外部输入通道。
整个ADC的基本结构图如下:

一、ADC单通道测量
本实验用PA0连接一个简易继电器对模拟信号进行转换。
先对ADC模块进行编写,ADC.c文件:
#include "stm32f10x.h" // Device header
void ADC_init(void)
{
//打开ADC1时钟
RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC1 ,ENABLE);
//打开GPIOA时钟
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA ,ENABLE);
//对ADC时钟进行分频配置,ADC频率不能超过14MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
//对PA0进行初始化配置,这里使用模拟输入模式(ADC专属模式)
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init (GPIOA ,&GPIO_InitStruct);
//"点菜"对规则组的通道进行配置
ADC_RegularChannelConfig(ADC1 ,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
//对ADC进行初始化配置
ADC_InitTypeDef ADC_InitStructer;
ADC_InitStructer.ADC_Mode =ADC_Mode_Independent;//使用的是ADC单通道模式
ADC_InitStructer.ADC_DataAlign =ADC_DataAlign_Right;//选择右对齐方式
ADC_InitStructer.ADC_ExternalTrigConv =ADC_ExternalTrigConv_None;
//软件触发,这里不需要外部触发
ADC_InitStructer.ADC_ContinuousConvMode =DISABLE;//单次转换模式
ADC_InitStructer.ADC_ScanConvMode =DISABLE;//非扫描模式
ADC_InitStructer.ADC_NbrOfChannel =1;//需要转换的通道数量
ADC_Init(ADC1 ,&ADC_InitStructer);
//打开ADC1
ADC_Cmd(ADC1 ,ENABLE);
//对ADC进行校正,标志位是独立的,完成了某个操作才会将对应的标志位SET
ADC_ResetCalibration(ADC1 );//复位校正
while(ADC_GetResetCalibrationStatus(ADC1) == SET);//检测是否复位校正
ADC_StartCalibration(ADC1);//进行校正
while(ADC_GetCalibrationStatus(ADC1) == SET);//检测是否进行校正
}
//读值函数:将ADC转换后的值读取出来
uint16_t Get_Value(void)
{
ADC_SoftwareStartConvCmd(ADC1 ,ENABLE );//选择软件触发方式
while(ADC_GetFlagStatus(ADC1 ,ADC_FLAG_EOC) == RESET);//判断是否完成转换
uint16_t value = ADC_GetConversionValue(ADC1);//将DR中的值读出来
ADC_ClearFlag(ADC1 ,ADC_FLAG_EOC);//清除标志位
return value;//返回读取值
}
ADC.h文件:
#ifndef _ADC_H
#define _ADC_H
void ADC_init(void);
uint16_t Get_Value(void);
#endif
再在主函数中对各模块函数进行调用,main.c文件:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "ADC.h"
uint16_t Value;//创建变量用于存储转换后直接读取值
float Voltage;//创建变量用于存储电压值
int main(void)
{
OLED_Init();
ADC_init ();//各模块初始化值
OLED_ShowString (1,1,"Value:");//显示提示字符串
OLED_ShowString (2,1,"Voltage:0.000");//显示提示字符串
while(1)
{
Value = Get_Value();//实时读取值
Voltage = (float)Get_Value() / 4095 * 3.3;//对电压进行转换
OLED_ShowNum (1,7,Value,4);//实时显示直接读取值
OLED_ShowNum (2,9,Voltage,1);
//实时显示电压值,由于函数的局限性,将其分为整数、小数两部分进行显示
OLED_ShowNum (2,11,(uint16_t)(Voltage *1000) % 1000,3);
Delay_ms (500);//延时500ms,让ADC转换效果更加明显
}
}
二、ADC多通道
本实验分别用PA0~PA3连接继电器、光敏传感器、热敏传感器、发射式红外传感器,将使用ADC1的0~3四个通道接收各模块的模拟量,并将其转换为数字量。(由于ADC规则组,只有一个寄存器用于寄存数据,这里还是选择使用单次转换、非扫描模式进行转换)
先对ADC模块进行编写,ADC.c文件:
#include "stm32f10x.h" // Device header
void ADC_init(void)
{
//打开ADC1和GPIOA
RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC1 ,ENABLE);
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA ,ENABLE);
//对ADC时钟进行分频配置
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
//对PA0~PA3四个引脚进行初始化配置,这里均配置为:模拟输入模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init (GPIOA ,&GPIO_InitStruct);
//不在这里对ADC规则组通道进行配置
//对ADC单元进行初始化配置
ADC_InitTypeDef ADC_InitStructer;
ADC_InitStructer.ADC_Mode =ADC_Mode_Independent;
ADC_InitStructer.ADC_DataAlign =ADC_DataAlign_Right;
ADC_InitStructer.ADC_ExternalTrigConv =ADC_ExternalTrigConv_None;
ADC_InitStructer.ADC_ContinuousConvMode =DISABLE;
ADC_InitStructer.ADC_ScanConvMode =DISABLE;
ADC_InitStructer.ADC_NbrOfChannel =1;
ADC_Init(ADC1 ,&ADC_InitStructer);
//打开ADC1
ADC_Cmd(ADC1 ,ENABLE);
//对ADC进行校正
ADC_ResetCalibration(ADC1 );
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);
}
//对ADC规则组进行灵活配置,对转换后的数值进行读取
uint16_t Get_Value(uint8_t ADC_Channel)//将具体通道作为函数参数
{
//对ADC规则组进行灵活配置
ADC_RegularChannelConfig(ADC1 ,ADC_Channel,1,ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1 ,ENABLE );//选择软件触发ADC转换
while(ADC_GetFlagStatus(ADC1 ,ADC_FLAG_EOC) == RESET);//判断数据是否转换完成
uint16_t value = ADC_GetConversionValue(ADC1);//读取转换值
ADC_ClearFlag(ADC1 ,ADC_FLAG_EOC);//清除标志位
return value;//返回读取值
}
ADC.h文件:
#ifndef _ADC_H
#define _ADC_H
void ADC_init();
uint16_t Get_Value(uint8_t ADC_Channel);
#endif
最后在主函数中调用ADC模块函数实现ADC多通道转换,mian.c文件:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "ADC.h"
uint16_t Value0;
uint16_t Value1;
uint16_t Value2;
uint16_t Value3;//创建四个变量,用于存储四个通道的读取值
int main(void)
{
OLED_Init();
ADC_init();//各模块初始化
OLED_ShowString (1,1,"Value0:");
OLED_ShowString (2,1,"Value1:");
OLED_ShowString (3,1,"Value2:");
OLED_ShowString (4,1,"Value3:");//四个通道对应的提示字符串
while(1)
{
Value0 = Get_Value(ADC_Channel_0);
Value1 = Get_Value(ADC_Channel_1);
Value2 = Get_Value(ADC_Channel_2);
Value3 = Get_Value(ADC_Channel_3);//对四个通道实时读取值进行存储,函数要进行传参
OLED_ShowNum (1,8,Value0,4);
OLED_ShowNum (2,8,Value1,4);
OLED_ShowNum (3,8,Value2,4);
OLED_ShowNum (4,8,Value3,4);//对四个通道读取值进行实时显示
Delay_ms (100);//延时增强观感
}
}
最终效果:四个通道均能实时将模拟量转换为数字量,并在OLED屏幕上显示出来。
注意事项:
- ADC的规则组只有一个寄存器,注入组有四个寄存器;
- 在使用扫描模式进行转换时,转换完一个值需要将ADC中DR的值用DMA进行转运;
- OLED_ShowNum()函数无法直接显示浮点数,需要使用取巧的方法进行显示。