STM32学习心得二十三:ADC转换原理及模数转换实验

记录一下,方便以后翻阅~
主要内容:
1) STM32 ADC相关知识;
2) 相关寄存器和库函数配置;
3) 相关实验代码解读。
实验功能:针对GPIOA, 引脚1,实时采集开发版上3.3v的电压信号(用杜邦线连接),在串口调试助手上实时观察该电压值。
官方资料:《STM32中文参考手册V10》第11章——模拟/数字转换(ADC)
1. ADC (Analog-to-Digital Converter)模/数转换器
ADC是指将连续变量的模拟信号转换为离散的数字信号的器件。典型的模拟/数字转换器将模拟信号转换为表示一定比例电压值的数字信号。
2. STM32 ADC特点
2.1 12位逐次逼近型的模拟数字转换器;
2.2 最多带3个ADC控制器(如下图所示,其中144脚芯片因为带PF脚,所以多5个通道,为21个外部通道。小于144脚芯片只有16个外部通道);
在这里插入图片描述
2.3 最多支持18个通道,可最多测量16个外部和2个内部信号源;
2.4 支持单次和连续转换模式;
2.5 转换结束,注入转换结束,和发生模拟看门狗事件时产生中断;
2.6 通道0到通道n的自动扫描模式;
2.7 自动校准;
2.8 采样间隔可以按通道编程;
2.9 规则通道和注入通道均有外部触发选项;
2.10 转换结果支持左对齐或右对齐方式存储在16位数据寄存器;
2.11 ADC转换时间:最大转换速率1us(最大转换速度为1MHz,在ADCCLK=14M,采样周期为1.5个ADC时钟下得到);
2.12 ADC供电要求:2.4V-3.6V(ADC引脚输入不要超过3.3v,否则会烧坏!);
2.13 ADC输入范围:VREF-≤ VIN ≤ VREF+。
3. STM32F10x系列芯片ADC通道和引脚对应关系
在这里插入图片描述
4. ADC引脚
在这里插入图片描述
5. ADC框图
在这里插入图片描述
6. STM32通道组
6.1 规则通道组:相当正常运行的程序。最多16个通道。规则通道和它的转换顺序在ADC_SQRx寄存器中选择,规则组转换的总数应写入ADC_SQR1寄存器的L[3:0]中;
6.2 注入通道组:相当于中断。最多4个通道。注入组和它的转换顺序在ADC_JSQR寄存器中选择。注入组里转化的总数应写入ADC_JSQR寄存器的L[1:0]中。
在这里插入图片描述
STM32F1的ADC的各通道可以单次,连续,扫描或者间断模式执行。
7. 相关寄存器
7.1 单次转化 VS 连续转换
在这里插入图片描述
在这里插入图片描述
7.2 扫描模式
在这里插入图片描述
在这里插入图片描述
7.3 ADC中断
在这里插入图片描述
7.4 ADC时钟配置
在这里插入图片描述
备注:不要让ADC时钟超过14MHz,否则可能不准。
推荐函数配置:

RCC_ADCCLKConfig(RCC_PCLK2_Div6); 

7.5 ADC_CR1寄存器
在这里插入图片描述
在这里插入图片描述
在扫描模式下,由ADC_SQRx或者ADC_JSQRx寄存器选中的通道被转换。如果设置了EOCIE或者JEOCIE,在最后一个通道转换完毕后才会产生EOC或者JEOC中断。
在这里插入图片描述
7.6 ADC_CR2寄存器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
7.7 数据对齐方式
在这里插入图片描述
7.8 ADC_SMPR1寄存器
在这里插入图片描述
7.9 ADC_SMPR2寄存器
在这里插入图片描述
7.10 ACD采样时间
在这里插入图片描述
最小转换时间1us(ADC时钟=14MHz,采样周期为1.5周期下得到)
7.11 ADC_SQR1/SQR2/SQR3规则序列寄存器
在这里插入图片描述
7.12 ADC_JSQR注入系列寄存器
在这里插入图片描述
7.13 ADC_DR规则通道数据寄存器
在这里插入图片描述
7.14 ADC_JDR注入通道数据寄存器
在这里插入图片描述
7.15 ADC_SR状态寄存器
在这里插入图片描述
8. 常用库函数
8.1 常用库函数一览

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
void ADC_DeInit(ADC_TypeDef* ADCx)void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
voidADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_StartCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);

8.2 ADC初始化函数

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
typedef struct
{
  uint32_t ADC_Mode;                      //ADC模式:配置ADC_CR1寄存器的位[19:16]:DUALMODE[3:0]位//
  FunctionalState ADC_ScanConvMode;       //是否使用扫描模式。ADC_CR1位8:SCAN位// 
  FunctionalState ADC_ContinuousConvMode; //单次转换OR连续转换:ADC_CR2的位1:CONT//
  uint32_t ADC_ExternalTrigConv;          //触发方式:ADC_CR2的位[19:17]:EXTSEL[2:0]//                
  uint32_t ADC_DataAlign;                 //对齐方式:左对齐还是右对齐:ADC_CR2的位11:ALIGN// 
  uint8_t ADC_NbrOfChannel;               //规则通道序列长度:ADC_SQR1的位[23:20]:L[3:0]//       
}
ADC_InitTypeDef;

例:

ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;                   //独立模式
ADC_InitStruct.ADC_ScanConvMode = DISABLE;                        //不开启扫描 
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;                  //单次转换模式
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  //触发软件 
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;               //ADC数据右对齐
ADC_InitStruct.ADC_NbrOfChannel = 1;                              //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure);      

8.3 ADC使能函数 ADC_Cmd();

void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);

例:

ADC_Cmd(ADC1, ENABLE);                      //使能指定的ADC1//

8.4
ADC使能软件转换函数

void ADC_SoftwareStartConvCmd(ADC_TypeDef*  ADCx,  FunctionalState NewState);

例:

ADC_SoftwareStartConvCmd(ADC1, ENABLE);    //使能ADC1的软件转换启动//

8.5 ADC 规则通道配置函数

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);

例:

ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 );

8.6 ADC 获取转换结果函数

uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);

例:

ADC_GetConversionValue(ADC1);               //获取ADC1转换结果//

9. 一般配置步骤
9.1 开启PA口时钟和ADC1时钟,设置PA1为模拟输入;

GPIO_Init(); 
APB2PeriphClockCmd();

9.2 复位ADC1,同时设置ADC1分频因子;

RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_DeInit(ADC1);

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

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)

9.4 使能ADC并校准;

ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);                 //使能复位校准//   
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束// 
ADC_StartCalibration(ADC1);                 //开启AD校准//
while(ADC_GetCalibrationStatus(ADC1));      //等待校准结束//

9.5 配置规则通道参数;

ADC_RegularChannelConfig();

9.6 开启软件转换:

ADC_SoftwareStartConvCmd(ADC1);

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

while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));   //等待转换结束,EOC=1时,转换完成,则While停止//
ADC_GetConversionValue(ADC1);
  1. 相关代码解读
    10.1 adc.h头文件代码解读
#ifndef __ADC_H
#define __ADC_H 
#include "sys.h"
//申明了三个函数//
void Adc_Init(void);  
u16  Get_Adc(u8 ch); 
u16 Get_Adc_Average(u8 ch,u8 times); 
#endif 

10.2 adc.c文件代码解读

#include "adc.h"
#include "delay.h"
//编写void Adc_Init(void)函数//                   
void  Adc_Init(void)
{  
 ADC_InitTypeDef  ADC_InitStructure;              //ADC_Init初始化结构体//
 GPIO_InitTypeDef GPIO_InitStructure;             //GPIO_Init初始化结构体//
 //使能GPIOA和ADC1通道时钟//
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE );  
 //配置GPIOA,引脚1,模拟输入//                        
 GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;       //引脚1//
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;    //模拟输入//
 GPIO_Init(GPIOA, &GPIO_InitStructure); 
 //设置ADC分频因子6,72M/6=12M,ADC最大时间不能超过14M//
 RCC_ADCCLKConfig(RCC_PCLK2_Div6);   
 ADC_DeInit(ADC1);                                //复位ADC1,可写可不写// 
 //配置ADC_Init结构体参数//
 //ADC_CR1寄存器,位[19:16]:DUALMODE双模式选择,ADC_Mode_Independent=0x00000000,独立模式//
 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                   
 //ADC_CR1寄存器,位8:SCAN扫描模式,0关闭扫描模式//
 ADC_InitStructure.ADC_ScanConvMode = DISABLE;   
 //ADC_CR2寄存器,位1:CONT连续转换,0单次转换模式// 
 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
 //ADC_CR2寄存器,位[19:17]:EXTSEL选择启动规则通道组转换的外部事件,ADC_ExternalTrigConv_None=0x000E0000,SWSTART软件控制// 
 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; 
 //ADC_CR2寄存器,位11:ALIGN数据对齐,ADC_DataAlign_Right=0x00000000,右对齐//
 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; 
 //ADC_SQR1寄存器,位[23:20]:L规则通道序列长度,1指1个转换,即位[23:20]设为0000//
 ADC_InitStructure.ADC_NbrOfChannel = 1; 
 ADC_Init(ADC1, &ADC_InitStructure);    
 //使能指定的ADC1//
 ADC_Cmd(ADC1, ENABLE); 
 //下面四个函数用于校准//
 ADC_ResetCalibration(ADC1);                     //使能复位校准//   
 while(ADC_GetResetCalibrationStatus(ADC1));     //等待复位校准结束// 
 ADC_StartCalibration(ADC1);                     //开启AD校准//
 while(ADC_GetCalibrationStatus(ADC1));          //等待校准结束//
}      
//编写u16 Get_Adc函数(u8 ch)//
u16 Get_Adc(u8 ch)   
{
 //设置指定ADC的规则组通道,四个入口参数//
 ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );           
 ADC_SoftwareStartConvCmd(ADC1, ENABLE);          //使能指定的ADC1的软件转换启动功能// 
 while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));  //等待转换结束,EOC=1时,转换完成,则While停止//
 return ADC_GetConversionValue(ADC1);             //返回最近一次ADC1规则组的转换结果//
}
//编写u16 Get_Adc_Average(u8 ch,u8 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;
}   

10.3 main.c代码解读

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"  
#include "adc.h"
int main(void)
 {  
  u16 adcx;
  float temp;
  delay_init();        //延时函数初始化   
  uart_init(115200);   //串口初始化为115200
  LED_Init();          //LED端口初始化
  Adc_Init();          //ADC初始化    
 while(1)
 {
  adcx=Get_Adc_Average(ADC_Channel_1,10);
  temp=(float)adcx*(3.3/4096);
  printf("电压大小(从二进制寄存器读取后转换为十进制):%d\n",adcx); 
  printf("电压大小(换算成电压单位后):%f\n",temp);
  LED0=!LED0;
  delay_ms(250); 
 }
 }

旧知识点
1)复习如何新建工程模板,可参考STM32学习心得二:新建工程模板
2)复习基于库函数的初始化函数的一般格式,可参考STM32学习心得三:GPIO实验-基于库函数
3)复习寄存器地址,可参考STM32学习心得四:GPIO实验-基于寄存器
4)复习位操作,可参考STM32学习心得五:GPIO实验-基于位操作
5)复习寄存器地址名称映射,可参考STM32学习心得六:相关C语言学习及寄存器地址名称映射解读
6)复习时钟系统框图,可参考STM32学习心得七:STM32时钟系统框图解读及相关函数
7)复习延迟函数,可参考STM32学习心得九:Systick滴答定时器和延时函数解读
8)复习ST-LINK仿真器的参数配置,可参考STM32学习心得十:在Keil MDK软件中配置ST-LINK仿真器
9)复习ST-LINK调试方法,可参考STM32学习心得十一:ST-LINK调试原理+软硬件仿真调试方法
10)复习如何对GPIO进行复用,可参考STM32学习心得十二:端口复用和重映射
11)复习串口通信相关知识,可参考STM32学习心得十四:串口通信相关知识及配置方法
12)复习通用定时器基本原理,可参考STM32学习心得十八:通用定时器基本原理及相关实验代码解读

猜你喜欢

转载自blog.csdn.net/Leisure_ksj/article/details/106245834