STM32_基础入门(十三)ADC

嵌入式之路,贵在日常点滴

                                                                —阿杰在线送代码

目录

一、ADC简介

二、ADC特点 

三、 ADC通道和引脚对应关系

四、 ADC框图

五、 ADC测量的电压范围

六、输入通道分类 

​​七、常用库函数 

八、库函数配置 

九、代码区 

单通道转换

         单通道转换DMA模式

多通道转换DMA模式


一、ADC简介

Analog-to-Digital Converter的缩写。指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件。Analog-to-Digital Converter的缩写。指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件。

典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。

二、ADC特点 

12位逐次逼近型的模拟数字转换器。

最多带3个ADC控制器

–》只有大容量的芯片有ADC3   注:ADC2不可以DMA

最多支持18个通道,可最多测量16个外部和2个内部信号源。

支持单次和连续转换模式

转换结束,注入转换结束,和发生模拟看门狗事件时产生中断。

通道0到通道n的自动扫描模式

自动校准

采样间隔可以按通道编程

规则通道和注入通道均有外部触发选项

转换结果支持左对齐或右对齐方式存储在16位数据寄存器

 ADC转换时间:最大转换速率 1us。(最大转换速度为1MHz,在ADCCLK=14M,采样周期为1.5个ADC时钟下得到。)

 ADC供电要求:2.4V-3.6V

 ADC输入范围:VREF- ≤  VIN  ≤  VREF+

三、 ADC通道和引脚对应关系

四、 ADC框图

五、 ADC测量的电压范围

电压范围为: 0~3.3V

超出0~3.3V的电压怎么测?

六、输入通道分类 

外部的 16 个通道在转换的时候又分为规则通道注入通道,其中规则通道最多有 16路,注入通道最多有 4 路。那这两个通道有什么区别?在什么时候使用? 

规则通道:顾名思意,规则通道就是很规矩的意思,我们平时一般使用的就是这个通道。

注入通道:注入,可以理解为插入,插队的意思,是一种不安分的通道。它是一种在规则通道转换的时候强行插入要转换的一种。这点跟中断程序很像,都是不安分的主。所以,注入通道只有在规则通道存在时才会出现。

STM32F1的ADC的各通道可以单次,连续,扫描或者间断模式执行。

(以下内容建议直接看数据手册)

七、常用库函数 

八、库函数配置 


开启
PA
口时钟和
ADC1
时钟,设置
PA1
为模拟输入。

      GPIO_Init();     

     APB2PeriphClockCmd();

② 复位ADC1,同时设置ADC1分频因子。

      RCC_ADCCLKConfig(RCC_PCLK2_Div6);

      ADC_DeInit(ADC1);

③ 初始化ADC1参数,设置ADC1的工作模式以及规则序列的相关信息。

     void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)

④ 使能ADC并校准。

       ADC_Cmd(ADC1, ENABLE);

⑤ 配置规则通道参数:

     ADC_RegularChannelConfig();

⑥开启软件转换:ADC_SoftwareStartConvCmd(ADC1);

⑦等待转换完成,读取ADC值。

   ADC_GetConversionValue(ADC1);

九、代码区 

单通道转换

使用ADC1的通道4去检测电压并把检测到的结果显示在OLED屏幕上。

*adc.c

 #include "adc.h"
 #include "delay.h"
  		   
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3																	   
void  Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC, ENABLE );	  
    //使能ADC1通道时钟 

    //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   

	//PA4 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值

    // 只使用一个ADC,属于单模式
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	
    // 禁止扫描模式,多通道才要,单通道不需要
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    //模数转换工作在单次转换模式(即软件转换启动功能启动一次转换一次)	
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;	
    //转换由软件而不是外部触发启动
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	
    //ADC数据右对齐
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	
    //顺序进行规则转换的ADC通道的数目(有多少通道就写多少)
	ADC_InitStructure.ADC_NbrOfChannel = 1;	
	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   
	ADC_Init(ADC1, &ADC_InitStructure);
  
	ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1
	
	ADC_ResetCalibration(ADC1);	//使能复位校准  	 
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束	
	ADC_StartCalibration(ADC1);	 //开启AD校准 
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束 
}
				  
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)   
{
  //设置指定ADC的规则组通道,一个序列,采样时间
    //ADC1,ADC通道,采样时间为239.5周期
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );		  			    
      
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//使能指定的ADC1的软件转换启动功能	
	 
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束,转换结束EOC置一

	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
}

//获得连续取times次后的平均值
u16 Get_Adc_Average(u8 ch,u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=Get_Adc(ch);
		delay_ms(5);
	}
	return temp_val/times;
} 	
*main.c

#include "stm32f10x.h"	
#include "oled.h"
#include "adc.h"

u8 string[16] = {0};

int main(void)
{	
    //ADC采集变量
	u16 adcx;
	float temp;

    OLED_Init();			//初始化OLED  
	OLED_Clear(); 
	Adc_Init();		  		//ADC初始化

    while(1)
	{
        adcx = Get_Adc(ADC_Channel_4);//得到数字量
//		adcx = Get_Adc_Average(ADC_Channel_4,10);
		temp=(float)adcx*(3.3/4096);//转化为电压值
		sprintf(string,"temp:%.2f",temp);//将数据转换为字符串
		OLED_ShowString(6,6,string,16);//显示字符串,“string只能是字符串”
    }

}

单通道转换DMA模式

使用ADC1的通道4去检测电压并把检测到的结果显示在OLED屏幕上。

adc.c

#include "adc.h"
#include "delay.h"

u16 ADC_ConvertedValue;

void adc_int(void)
{
	ADC_InitTypeDef ADC_InitStructure;  
    GPIO_InitTypeDef GPIO_InitStructure;
	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
	
	// 配置ADC时钟N狿CLK2的8分频,即9MHz	
	//设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);
 
	//PA4 作为模拟通道输入引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
	ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值			
	
	// 只使用一个ADC,属于单模式  
    ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;  
	// 禁止扫描模式,多通道才要,单通道不需要	
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;	
	// 连续转换模式 即调用一次就一直不间断转换
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; 
	// 不用外部触发转换,软件开启即可
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	// 转换结果右对齐
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  
	// 转换通道个数
    ADC_InitStructure.ADC_NbrOfChannel = 1;    						 
    ADC_Init(ADC1,&ADC_InitStructure);			// 初始化ADC
	
	//配置ADC 通道的转换顺序和采样时间
    //参数1:ADCx(选用哪个ADC;一般使用ADC1,ADC2没有DMA功能,ADC3只有大容量芯片才有)
    //参数2:选择第几通道  参数3:转换顺序  参数4:转换时间
	// 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);
    ADC_DMACmd(ADC1, ENABLE);	// 使能ADC DMA 请求
    ADC_Cmd(ADC1,ENABLE);     // 开启ADC ,并开始转换
		
	// 初始化ADC 校准寄存器  
    ADC_ResetCalibration(ADC1);
	// 等待校准寄存器初始化完成
    while(ADC_GetResetCalibrationStatus(ADC1))	;	
    ADC_StartCalibration(ADC1); // ADC开始校准                            
    while(ADC_GetCalibrationStatus(ADC1)); // 等待校准完成  
		
	// 由于没有采用外部触发,所以使用软件触发ADC转换 
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

void DMA_Configure(void)
{	 
   DMA_InitTypeDef DMA_InitStructure;
	
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);// 打开DMA时钟
	
   DMA_DeInit(DMA1_Channel1);   		// 复位DMA控制器   
	
   // 外设基址为:ADC 数据寄存器地址
   DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;  
   // 存储器地址,实际上就是一个内部SRAM的变量
   DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;                                
   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  // 数据源来自外设
   // 缓冲区大小为1,缓冲区的大小应该等于存储器的大小   多少个通道就写多少    	             
   DMA_InitStructure.DMA_BufferSize = 1;    
   // 外设寄存器只有一个,地址不用递增
   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                   
   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // 存储器地址固定  
   // 外设数据大小为半字,即两个字节
   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;   
   // 内存数据大小也为半字,跟外设数据大小相同
   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 
   // 循环传输模式  选择循环模式,有数据就一直会传输
   DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  
   // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
   DMA_InitStructure.DMA_Priority = DMA_Priority_High;
   // 禁止存储器到存储器模式,因为是从外设到存储器	 
   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;      
   DMA_Init(DMA1_Channel1, &DMA_InitStructure);// 初始化DMA  
  
   DMA_Cmd(DMA1_Channel1,ENABLE); // 使能 DMA 通道
}
main.c

#include "sys.h"	
#include "oled.h"
#include "adc.h"

u8 string[16] = {0};

// ADC1转换的电压值通过MDA方式传到SRAM
extern u16 ADC_ConvertedValue;

// 局部变量,用于保存转换计算后的电压值 	 
float ADC_ConvertedValueLocal; 

int main(void)
{	
	delay_init();
	
	OLED_Init();			//初始化OLED  
	OLED_Clear(); 

	adc_int();
	DMA_Configure();
	
	while(1)
	{
		ADC_ConvertedValueLocal =(float) ADC_ConvertedValue/4096*3.3; 
        // 读取转换的AD值

		sprintf(string,"temp:%.2f",ADC_ConvertedValueLocal);//将数据转换为字符串
		OLED_ShowString(6,6,string,16);//显示字符串,“string只能是字符串” 	
	}
}

多通道转换DMA模式

adc.c

u16 ADC_ConvertedValue[6]={0,0,0,0};

void adc_int(void)
{
	ADC_InitTypeDef ADC_InitStructure;  
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    RCC_AHBPeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
 
    GPIO_InitStructure.GPIO_Pin =             
    GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
	// 配置ADC时钟N狿CLK2的8分频,即9MHz	
    //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	
	// 只使用一个ADC,属于单模式
    ADC_InitStructure.ADC_Mode=ADC_Mode_Independent; 
	// 扫描模式
    ADC_InitStructure.ADC_ScanConvMode=ENABLE; 
	// 连续转换模式 即调用一次就一直不间断转换
    ADC_InitStructure.ADC_ContinuousConvMode=ENABLE; 
	// 不用外部触发转换,软件开启即可
    ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	// 转换结果右对齐
    ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;   
	// 转换通道个数
    ADC_InitStructure.ADC_NbrOfChannel =6;    						 
    ADC_Init(ADC1,&ADC_InitStructure);		// 初始化ADC
	
	// 配置ADC 通道的转换顺序和采样时间
	//参数1:ADCx(选用哪个ADC;一般使用ADC1,ADC2没有DMA功能,ADC3只有大容量芯片才有)
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);   
    ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);  
    ADC_RegularChannelConfig(ADC1,ADC_Channel_4,5,ADC_SampleTime_55Cycles5);  
    ADC_RegularChannelConfig(ADC1,ADC_Channel_5,6,ADC_SampleTime_55Cycles5); 
		
    ADC_DMACmd(ADC1, ENABLE);	// 使能ADC DMA 请求
    ADC_Cmd(ADC1,ENABLE);     // 开启ADC ,并开始转换
		
	// 初始化ADC 校准寄存器  
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));// 等待校准寄存器初始化完成
    ADC_StartCalibration(ADC1); // ADC开始校准                                                                                               
    while(ADC_GetCalibrationStatus(ADC1)); // 等待校准完成  
		
	// 由于没有采用外部触发,所以使用软件触发ADC转换
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);   
}

void DMA_Configure(void)
{	 
   DMA_InitTypeDef DMA_InitStructure;
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);// 打开DMA时钟
	
   DMA_DeInit(DMA1_Channel1);   		// 复位DMA控制器  
	
   // 外设基址为:ADC 数据寄存器地址
   DMA_InitStructure.DMA_PeripheralBaseAddr=(u32)&ADC1->DR;
   // 存储器地址
   DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;
   // 数据源来自外设 
   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 
   // 缓冲区大小,应该等于数据目的地的大小  即理解为有多少个通道转换就写多少
   DMA_InitStructure.DMA_BufferSize = 6;      
   // 外设寄存器只有一个,地址不用递增
   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
   // 存储器地址递增
   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  
   // 外设数据大小为半字,即两个字节
   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
   // 内存数据大小也为半字,跟外设数据大小相同
   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
   // 循环传输模式   选择循环模式,有数据就一直会传输
   DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
   // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
   DMA_InitStructure.DMA_Priority = DMA_Priority_High;  
   //禁止存储器到存储器模式,因为是从外设到存储器 
   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;     
   DMA_Init(DMA1_Channel1, &DMA_InitStructure);// 初始化DMA
   
   DMA_Cmd(DMA1_Channel1,ENABLE); // 使能 DMA 通道
}
main.c

#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
#include "adc.h"

// ADC1转换的电压值通过MDA方式传到SRAM
extern u16 ADC_ConvertedValue[6];

// 局部变量,用于保存转换计算后的电压值 	 
float ADC_ConvertedValueLocal[6]; 

int main(void)
{ 
	u8 i;
	
    delay_init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	SysTick_Config(SystemCoreClock/1000);
	uart_init(9600);
	adc_int();
	DMA_Configure();
    
    while(1)
	{ 
		for(i=0;i<6;i++)
		{
            ADC_ConvertedValueLocal[i] =(float) ADC_ConvertedValue[i]/4096*3.3;
			
            
			printf("\r\n CH%d value = %f V \r\n",(u16) i,ADC_ConvertedValueLocal[i]);
			delay_ms(50);
		}
	}
}

物联沃分享整理
物联沃-IOTWORD物联网 » STM32_基础入门(十三)ADC

发表评论