STM32AD模数转换教程及应用指南

文章目录

  • 前言
  • 一、介绍部分
  • ADC简介
  • 逐次逼近型ADC
  • STM32的ADC框图
  • ADC基本结构
  • 五个标志位
  • ADC通道选择
  • ADC通道与引脚关系
  • 规则通道的转换模式
  • 单次转换,非扫描模式
  • 连续转换,非扫描模式
  • 单次转换,扫描模式
  • 连续转换,扫描模式
  • AD转换触发相关
  • 数据对齐方式
  • AD转换时间
  • 校准
  • 硬件电路
  • 二、代码部分
  • AD单通道读取电压
  • 连接电路
  • 代码实现
  • AD多通道
  • 连接电路
  • 代码实现
  • 总结
  • 相关函数
  • ADC时钟分频

  • 前言

    介绍AD,了解逐次逼近型ADC的原理,了解STM32的ADC运行流程以及通道选择,介绍STM32ADC规则通道组的部分使用方法,了解使用相关库函数。


    一、介绍部分

    ADC简介

    逐次逼近型ADC

    逐次逼近:对8位依次判断,若小于这个值则置0判断下一位,若大于这个值则置1继续判断下一位

    STM32的ADC框图

    也是逐次逼近型的,不过是16位,即从256开始二分查找

    ADC基本结构

    五个标志位

    ADC通道选择

    ADC通道与引脚关系

    规则通道的转换模式

    单次转换,非扫描模式

    连续转换,非扫描模式

    单次转换,扫描模式

    连续转换,扫描模式

    AD转换触发相关

    数据对齐方式

    寄存器是16位的,数据12位

    AD转换时间

    校准

    硬件电路

    二、代码部分

    AD单通道读取电压

    连接电路

    代码实现

    封装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);
    	}
    }
    
    

    AD多通道

    由于扫描模式需要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);
    

    ADC时钟分频

    参考STM32时钟树,ADC时钟设置相关

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32AD模数转换教程及应用指南

    发表评论