目录
[13.1 AD单通道](#13.1 AD单通道)
[13.2 AD多通道](#13.2 AD多通道)
一、ADC简介
>ADC(Analog-Digital Converter)模拟-数字转换器
>ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
>12位逐次逼近型ADC(表示范围:0-2^12-1(4095)),1us转换时间(频率:1MHz)
>输入电压范围:0~3.3V,转换结果范围:0~4095
>18个输入通道,可测量16个外部和2个内部信号源
>规则组和注入组两个转换单元
>模拟看门狗自动监测输入电压范围
>STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道
二、逐次逼近型ADC内部结构
三、STM32内部ADC转换结构
ADCCLK来源于APB2
四、ADC基本结构
五、输入通道
六、转换模式
6.1单次转换,非扫描模式
6.2连续转换,非扫描模式
6.3单次转换,扫描模式
6.4连续转换,扫描模式
七、触发控制
八、数据对齐
(一般采用右对齐,读出来的数据直接就是结果)
九、转换时间
①AD转换的步骤:采样,保持,量化,编码
②STM32 ADC的总转换时间为:
TCONV = 采样时间 + 12.5个ADC周期(12位)
例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期
TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs
十、硬件电路
十一、开发步骤
①开启RCC时钟,包括ADC和GPIO,ADCCLK的分频器也要配置
②配置GPIO,模拟输入模式
③配置多路开关,将通道接入规则组列表中
④结构体配置ADC转换器(单次/多次、扫描/非扫描、通道数、触发源、数据对齐方式)
(可选)⑤模拟看门狗,配置阈值和监测通道,开启中断ITConfig,NVIC
⑤开关控制,调用ADC_Cmd函数,开启ADC,校准
十二、ADC库函数
/*rcc.h*/
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
作用:配置ADCCLK分频器
/*adc.h*/
void ADC_DeInit(ADC_TypeDef* ADCx);//恢复缺省配置
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);//Init初始化
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);//StructInit结构体初始化
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);//使能ADC
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);//开启DMA输出信号
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);//中断输出控制
============================控制校准的函数=============================
void ADC_ResetCalibration(ADC_TypeDef* ADCx);//复位校准
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);//获取复位校准状态
void ADC_StartCalibration(ADC_TypeDef* ADCx);//开始校准
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);//获取开始校准状态
=====================================================================
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
//ADC_软件开始转换控制,用于软件触发的函数(重要)
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);
//ADC获取软件开始转换状态(一般不用)
============================间断模式==================================
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
//每隔几个通道间断一次
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
//是不是启用间断模式
=====================================================================
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
//ADC规则组通道配置
参数:
①ADCx
②ADC_Channel,通道
③Rank,序列几的位置
④ADC_SampleTime,指定通道的采样时间
=====================================================================
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
//ADC_外部触发转换控制,是否允许外部触发转换
=====================================================================
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
//ADC获取转换值(重要)
=====================================================================
uint32_t ADC_GetDualModeConversionValue(void);
//ADC_获取双模式转换值
============================模拟看门狗=================================
void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog);
//是否启动模拟看门狗
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold);
//配置高低阈值
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);
//配置看门的通道
===============================内部两通道==============================
void ADC_TempSensorVrefintCmd(FunctionalState NewState);
//ADC_温度传感器、内部参考电压控制
=============================标志位===================================
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
//获取标志位状态
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
//清除标志位
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);
//获取中断状态
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);
//清除中断挂起位状态
十三、实验
13.1 AD单通道
实验现象:用电位器产生一个0~3.3V连续变化的模拟电压信号,接到PA0,ADC读取数据显示到屏幕上
参考代码:
AD.c
#include "stm32f10x.h" // Device header //*本例程使用连续非扫描模式,软件触发一次即可*// void AD_Init(void) { /*一、开启RCC时钟(ADC,GPIO)*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //ADCCLK分频 RCC_ADCCLKConfig(RCC_PCLK2_Div6);//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); /*三、配置多路开关,左边通道接入右边规则组列表(选择规则组的注入通道)*/ ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5); /*四、配置ADC转换器*/ ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//工作模式(独立/双ADC) ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据对齐(左/右) ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//触发源,此处使用软件触发 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换/单次转换 ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描/非扫描 ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1,&ADC_InitStructure);//扫描模式下用到几个通道 /*五、开关控制,开启ADC*/ ADC_Cmd(ADC1,ENABLE); //校准 ADC_ResetCalibration(ADC1);//复位校准 while(ADC_GetResetCalibrationStatus(ADC1) == SET);//等待复位校准完成 ADC_StartCalibration(ADC1);//启动校准 while(ADC_GetCalibrationStatus(ADC1) == SET);//等待校准是否完成 ADC_SoftwareStartConvCmd(ADC1,ENABLE); } uint16_t AD_GetValue(void) { return ADC_GetConversionValue(ADC1); }
AD.h
#ifndef __AD_H #define __AD_H void AD_Init(void); uint16_t AD_GetValue(void); #endif
main.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "AD.h" uint16_t ADValue;//AD值 float Voltage;//电压值 int main(void) { OLED_Init(); AD_Init(); OLED_ShowString(1,1,"ADValue:"); OLED_ShowString(2,1,"Voltage:0.00V"); while (1) { ADValue = AD_GetValue(); Voltage = (float)ADValue/4095*3.3; OLED_ShowNum(1,9,ADValue,4); OLED_ShowNum(2,9,Voltage,1);//整数 OLED_ShowNum(2,11,(uint16_t)(Voltage*100)%100,2);//小数部分 Delay_ms(100); } }
*OLED代码参考本专栏文章STM32-OLED
13.2 AD多通道
实验设置:将电位器、光敏电阻、热敏电阻、反射红外模块的AO(模拟电压输出端)分别接到PA0/PA1/PA2/PA3
参考代码:
AD.c
#include "stm32f10x.h" // Device header //*本例程使用单次非扫描模式*// void AD_Init(void) { /*一、开启RCC时钟(ADC,GPIO)*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //ADCCLK分频 RCC_ADCCLKConfig(RCC_PCLK2_Div6);//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); /*四、配置ADC转换器*/ ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1,&ADC_InitStructure); /*五、开关控制,开启ADC*/ ADC_Cmd(ADC1,ENABLE); //校准 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
#ifndef __AD_H #define __AD_H void AD_Init(void); uint16_t AD_GetValue(uint8_t ADC_Channel); #endif
main.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "AD.h" uint16_t AD0,AD1,AD2,AD3; int main(void) { 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,5); OLED_ShowNum(2,5,AD1,5); OLED_ShowNum(3,5,AD2,5); OLED_ShowNum(4,5,AD3 ,5); Delay_ms(100); } }