Bootstrap

STM32 ADC

STM32暑假学习Day1 ADC模拟转换器



前言

今天我们学习的内容是STM32里面的ADC模拟转换器,是实现模拟电路到数字电路的桥梁,本博主是在b站向江协科技学习。


一、ADC是什么?

ADC(Analog-Digital Converter) 模拟-数字转换器

  • ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
  • 12位逐次逼近型ADC,1us转换时间
  • 输入电压范围:0 ~ 3.3V,转换结果范围:0 ~ 4095
  • 18个输入通道,可测量16个外部和2个内部信号源
  • 规则组和注入组两个转换单元
  • 模拟看门狗自动监测输入电压范围(可用于做监测装置)
  • STM32F103C8T6 ADC 资源:ADC1、ADC2、10个外部输入通道

二、ADC基础结构图

ADC基础结构图

  • GPIO:有16个GPIO口,外加两个内部通道(温度、V~REFINT)
  • AD转换器:一个规则组(最多可以选用16个通道)一个注入组(最多可以选择4个通道),转换完成后有个EOC信号,它会置一个标志位,可以通向NVIC
  • AD数据寄存器:存储转换的结果。规则组只有一个数据寄存器,注入组有4个。
  • 触发控制:可以选择软件触发和硬件触发。硬件触发主要来自定时器,也可以选择外部中断引脚
  • RCC的ADC时钟CLOCK:ADC逐次比较的过程就是由这个时钟推动的
  • 模拟看门狗:监测转换结果的范围。如果超出设定阈值,就通过中断输出控制,向NVIC申请中断
  • 开关控制:在库函数中就是ADC_Cmd函数,用于给ADC上电的

1.输入通道

输入通道
由于我们的芯片没有PC0-PC5,所以只有10个输入通道

2.转换模式

  • 单次转换,非扫描模式

在这里插入图片描述

点菜点一个,上菜上一个
选择一个通道,然后触发 转换,然后ADC转换完成后转换结果放到数据寄存器里,同时给EOC标志位置1。判断EOC标志位,如果转换完了就可以在数据寄存器里读取结果。

  • 连续转换,非扫描模式
    在这里插入图片描述
    与上一个单次转换不同的是,它在一次转换结束后不会停止,而是立刻开启下一轮的转换,然后一直持续下去
    好处:开始一次转换之后不需要等待时间

  • 单次转换、扫描模式
    在这里插入图片描述

每次都需要触发,然后使用了菜单,可以把多个通道写进菜单里,然后需要给定通道数目,16个通道可以不用完,通道顺序可以任意放。每次触发之后,它就依次对前7个位置进行AD转换,转换结果都放在数据寄存器里,为了防止数据被覆盖,DMA会及时将数据挪走,然后7个通道转换完成之后,产生EOC信号,转换结束。

  • 连续转换、扫描模式

在这里插入图片描述

在单次转换,扫描模式的基础上,实现了只需要触发一次,一次转换结束后不会停止,而是立刻开启下一轮的转换,然后一直持续下去

三、接线图及代码部分

在这里插入图片描述

1. AD库代码示例

代码如下:

void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);

用来配置ADCCLK分频器的,可以对APB2的72MHz时钟选择2、4、6、8分频 ,输入到ADCCLK

void ADC_DeInit(ADC_TypeDef* ADCx);//恢复缺省配置
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);//初始化
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);//结构体初始化
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);//软件触发控制
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);//每隔几个通道间断1次
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规则组通道配置*** 
//uint8_t ADC_Channel 你想指定的通道   uint8_t Rank序列几的位置,  uint8_t ADC_SampleTime 就是指定通道的采样时间
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);//获取AD转换的数据寄存器
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);//清除中断挂起位






2.判断AD转换是否结束

FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);

获取标志位状态,然后参数给EOC的标志位,判断EOC标志位是不是置1了,如果转换结束,EOC标志位置1,调用ADC_GetFlagStatus(),判断标志位

3.示例代码

ad.c

#include "Device/Include/stm32f10x.h"   // Device header

void AD_Init(void)
{
	//启用RCC_APB2时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//配置ADCCLK
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	//配置GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AIN;//模拟输入,ADC专用模式
	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_InitStruct;
	ADC_InitStruct.ADC_ContinuousConvMode= DISABLE;//是否选择连续转换
	ADC_InitStruct.ADC_DataAlign= ADC_DataAlign_Right;//指定ADC数据对齐,右对齐
	ADC_InitStruct.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None;//外部触发的转换选择
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//ADC的工作模式
	ADC_InitStruct.ADC_NbrOfChannel = 1;//指定规则组转换列表里通道的数目
	ADC_InitStruct.ADC_ScanConvMode = DISABLE;//是否使用扫描模式
	ADC_Init(ADC1,&ADC_InitStruct);
	//启用ADC
	ADC_Cmd(ADC1,ENABLE);
	//ADC校准
	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);//软件触发
	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(void);

#endif

4.在OLED里显示出电压

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,"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);
	}
}

在这里插入图片描述
可以把单次,非扫描模式修改成连续,非扫描模式

修改一下AD.c里的代码

#include "Device/Include/stm32f10x.h"   // Device header

void AD_Init(void)
{
	//启用RCC_APB2时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//配置ADCCLK
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	//配置GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AIN;//模拟输入,ADC专用模式
	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_InitStruct;
	ADC_InitStruct.ADC_ContinuousConvMode= ENABLE;//是否选择连续转换
	ADC_InitStruct.ADC_DataAlign= ADC_DataAlign_Right;//指定ADC数据对齐,右对齐
	ADC_InitStruct.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None;//外部触发的转换选择
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//ADC的工作模式
	ADC_InitStruct.ADC_NbrOfChannel = 1;//指定规则组转换列表里通道的数目
	ADC_InitStruct.ADC_ScanConvMode = DISABLE;//是否使用扫描模式
	ADC_Init(ADC1,&ADC_InitStruct);
	//启用ADC
	ADC_Cmd(ADC1,ENABLE);
	//ADC校准
	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多通道的接线图及代码

1.接线图

在这里插入图片描述

2.示例代码

AD.c代码如下

#include "Device/Include/stm32f10x.h"   // Device header

void AD_Init(void)
{
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	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_InitTypeDef ADC_InitStruct;
	ADC_InitStruct.ADC_ContinuousConvMode= DISABLE;
	ADC_InitStruct.ADC_DataAlign= ADC_DataAlign_Right;
	ADC_InitStruct.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None;
	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStruct.ADC_NbrOfChannel = 1;
	ADC_InitStruct.ADC_ScanConvMode = DISABLE;
	ADC_Init(ADC1,&ADC_InitStruct);
	
	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);
}

main.c代码如下

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0,AD1,AD2,AD3;
float Voltage;
 

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,AD1,4);
		OLED_ShowNum(4,5,AD3,4);

		
		Delay_ms(100);
	}
}

在这里插入图片描述


总结

以上就是所学习到ADC的内容,本文仅仅简单介绍了ADC的原理及基础使用,编写ADC_init的c文件是需要RCC时钟,选定ADCCLK通道,对GPIO口进行配置,对ADC初始化进行配置,还有ADC校准。利用模拟信号可以更加精细的控制,是实现许多智能产品的关键。

;