1 ADC简介
- ADC(Analog-Digital Converter, 模拟-数字转换器)可以将连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。
- 分辨率:与ADC的位数相关,位数越高分辨率越高。如果12位的ADC的转换范围为0 ~ 4095,如果输入电压为0 ~ 3.3V,就是将0 ~ 3.3映射到0 ~ 4095.
- 具有18个输入通道,其中包括16个外部(对应16个GPIO口PIN脚)和2个内部信号源.
- 转换组分为注入通道组和规则通道组两种,其中规则通道组最多能够组织16个通道,但存放转换结果的寄存器只有一个,因而需要配合DMA来运输数据防止数据被覆盖;而注入通道组则最多组织4个通道,存放转换结果的寄存器有4个,与注入通道一一对应。
转换触发:分为软件触发和硬件触发两种,前者就是代码中直接调用进行转换,而后者则是通过TIM或者EXTI来实现。ADC转换通常是周期性的进行转换,因而很直觉地知道可以用TIM中断来实现,当TIM产生中断时在中断处理函数中调用ADC转换;然后,频繁地中断会影响主程序的运行,也可能导致部分数据来不及转换,因而TIM可以选择TRGO,当产生中断时,通过TRGO传到ADC模块从而实现硬件触发转换。转换完成的标志存放在EOC中,可以传送到NVIC中申请中断。
规则组转换模式:通过初始化结构体进行配置
- 单次转换、非扫描模式:只有序列1有效,单次转换完成后就结束并将EOC转换,如果需要继续转换需要继续调用
- 单次转换、扫描模式:按序列进行转换,且单次转换会转换整个序列然后产生EOC信号并结束转换
- 连续转换、非扫描模式:只有序列1有效并且可以进行连续转换,只需要触动一次
- 连续转换、扫描模式:连续扫描整个序列进行转换
2 ADC初始化
- 使能时钟(ADC+GPIO)、配置ADCCLK分频器(最高为14MHz)
- 配置GPIO输入模式
- 配置多路开关
- 配置ADC转换器
- 配置模拟看门狗、中断
- 开关控制
3 ADC库函数实现
- 使能时钟(ADC+GPIO)、配置ADCCLK分频器(最高为14MHz)
- 函数原型:
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
- 输入:
RCC_PCLK2
- 功能:配置ADCCLK分分频器
- 配置GPIO输入模式为模拟输入模式
- 配置多路开关
- 函数
- 函数原型:
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
- 功能:使能DMA
- 函数原型:
- 配置ADC转换器
- 函数1
- 函数原型:
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
- 功能:初始化ADC转换器初始化结构体成员。
- 函数原型:
- 函数2
- 函数原型:
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
- 功能:初始化ADC转换器
- 函数原型:
- 函数3
- 函数原型:
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
- 功能:配置ADC触发模型为软件触发
- 函数原型:
- 函数4
- 函数原型:
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
- 功能:配置规则通道,channel为通道,rank为序列
- 函数原型:
- 配置模拟看门狗、中断
- 函数1
- 函数原型:
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);
- 功能:使能中断
- 函数原型:
- 函数2
- 函数原型:
void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog);
- 功能:开启看门狗
- 函数原型:
- 函数3
- 函数原型:
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold);
- 功能:设置高低阈值
- 函数原型:
- 函数4
- 函数原型:
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);
- 功能:看门狗通道配置
- 函数原型:
- 函数5
- 函数原型:
void ADC_TempSensorVrefintCmd(FunctionalState NewState);
- 使能内部温度传感器
- 函数原型:
- 开关控制
- 函数1
- 函数原型:
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
- 功能:使能或失能ADC
- 函数原型:
- 函数2
- 函数原型:
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
- 功能:获取ADC转换结果
- 函数原型:
- 校准
- 函数1:
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
- 函数2:
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
- 函数3:
void ADC_StartCalibration(ADC_TypeDef* ADCx);
- 函数4:
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
- 代码实现
使用ADC1并配置为单通道、单次非扫描、软件触发模式
void ADC_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitType GPIO_InitStructrue;
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructrue);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_1Cycles5);
// 如果要多通道则多次调用ADC_RegularChannelConfig函数
ADC_DMACmd(ADC1, ENABLE);
ADC_InitTypeDef ADC_InitStructrue;
ADC_StructInit(&ADC_InitStructrue);
ADC_InitStructrue.ADC_Mode = ADC_Mode_Independent; // 工作模式:独立?双adc?
ADC_InitStructrue.ADC_ScanConvMode = DISABLE;
ADC_InitStructrue.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructrue.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 定义用于启动规则组转换的外部触发源
ADC_InitStructrue.ADC_DataAlign = ADC_DataAlign_Right; // 数据对齐模式
ADC_InitStructrue.ADC_NbrOfChannel = 1; // 通道数目
// 多通道需要修改通道数目
ADC_Init(ADC1, &ADC_InitStructrue);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET); // 等待复位校准完成
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET); // 获取校准状态
}
uint16_t ADC_GetValue(void){
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1); // 自动清除了EOC
}
多通道模式下一般需要用扫描模式,但是需要DMA来传输数据结果。也可以采用单次非扫描,只要每次读取数据的时候对通道进行修改即可,代码如下:
void ADC_Init(void){ //... GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //... } // 采用单次非扫描模式对指定通道进行转换 uint16_t(uint8_t ADC_Channel){ ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_1Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); return ADC_GetConversionValue(ADC1); // 自动清除了EOC }