介绍AD,了解逐次逼近型ADC的原理,了解STM32的ADC运行流程以及通道选择,介绍STM32ADC规则通道组的部分使用方法,了解使用相关库函数。
逐次逼近:对8位依次判断,若小于这个值则置0判断下一位,若大于这个值则置1继续判断下一位
也是逐次逼近型的,不过是16位,即从256开始二分查找
寄存器是16位的,数据12位
封装AD相关设置AD.c
#include "stm32f10x.h" // Device header
void AD_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 设置ADC时钟 72/6=12MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
// 初始化GPIO
GPIO_InitTypeDef GPIO_Structure;
GPIO_Structure.GPIO_Pin = GPIO_Pin_0;
GPIO_Structure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Structure.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入模式(ADC专用)
GPIO_Init(GPIOA,&GPIO_Structure);
// 使用规则组
// 配置规则组通道(ADC、此ADC的通道、序号、采样时间)
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
// 初始化ADC
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 连续模式还是单次模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 数据对齐,右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 触发转换的触发源,不使用外部触发使用软件触发
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 独立模式or双ADC模式
ADC_InitStructure.ADC_NbrOfChannel = 1; // 指定要使用多少个通道
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 扫描模式还是非扫描模式
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(void){
// 软件触发转换
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
// 获取转换标志位,等待转换完成EOC(规则通道或注入通道完成)置1,未完成时循环
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
// ADC获取转换值,会自动清除EOC标志位
return ADC_GetConversionValue(ADC1);
}
主函数main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t ADValue;
float Voltage;
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"AD:");
OLED_ShowString(2,1,"Voltage: . V");
while (1)
{
ADValue = AD_GetValue();
// 4095 对应 3.3V
Voltage = (float)ADValue/4095*3.3;
OLED_ShowNum(1,10,ADValue,4);
OLED_ShowNum(2,10,Voltage,1);
OLED_ShowNum(2,12,(int16_t)(Voltage*100)%100,2); // 小数部分
Delay_ms(100);
}
}
由于扫描模式需要DMA来辅助处理,这里暂时先使用手动扫描的方式实现
AD.c修改如下:
#include "stm32f10x.h" // Device header
void AD_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 设置ADC时钟 72/6=12MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
// 初始化GPIO
GPIO_InitTypeDef GPIO_Structure;
GPIO_Structure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_Structure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Structure.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入模式(ADC专用)
GPIO_Init(GPIOA,&GPIO_Structure);
// 使用规则组
// 配置规则组通道(ADC、此ADC的通道、序号、采样时间)
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
// 初始化ADC
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 连续模式还是单次模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 数据对齐,右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 触发转换的触发源,不使用外部触发使用软件触发
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 独立模式or双ADC模式
ADC_InitStructure.ADC_NbrOfChannel = 1; // 指定要使用多少个通道
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 扫描模式还是非扫描模式
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);
// 获取转换标志位,等待转换完成EOC(规则通道或注入通道完成)置1,未完成时循环
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
// ADC获取转换值,会自动清除EOC标志位
return ADC_GetConversionValue(ADC1);
}
主函数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,4);
OLED_ShowNum(2,5,AD1,4);
OLED_ShowNum(3,5,AD2,4);
OLED_ShowNum(4,5,AD3,4);
Delay_ms(100);
}
}
根据实列反复对照ADC框图与基本结构来加深理解。
// ADC初始化
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
// 为ADC初始化结构体赋初值
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
// 启动ADC
void ADC_Cmd(ADC_TypeDef* ADCx, 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通道以及所在序号
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
// 获取转换值
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
// 获取转换成功标志位(EOC,JEOC,AWD)
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
// 清除转换成功标志位
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
// 适用于中断函数内,获取转换成功标志位(EOC,JEOC,AWD)
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);
// 适用于中断函数内,清除转换成功标志位
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);
// 设置ADC时钟,此函数在rcc相关文件中
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);