STM32-ADC配置详解及应用。实例:《中断单通道读取ADC》、《DMA多通道读取ADC》

一、ADC介绍

12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。

二、框图分析

在这里插入图片描述
在这里插入图片描述
按照顺序分析:

1、电压输入范围:ADC 输入范围为:VREF- ≤ VIN ≤ VREF+。由 VREF-、VREF+ 、VDDA、 VSSA、这四个外部引脚决定。一般把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的输入电压范围为:0~3.3V。
2、输入通道:可分为注入通道规则通道 前者常用。关于输入通道查询,可以参考《STM32F103xCDE_数据手册》
在这里插入图片描述
下面是总结好的表格:F103x 系列 对应的引脚几乎完全一样 下面是ZET6的
在这里插入图片描述
3、 转换顺序
规则序列
通过控制ADC规则序列寄存器 ADC_SQR1 、ADC_SQR2、ADC_SQR3 来配置规则通道的转换顺序,
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果通道 16 想第一次转换,那么在 SQ1[4:0] 写 16 即可。SQR2 控制着规则序列中的第 7 到第12 个转换,对应的位为:SQ7[4:0]~SQ12[4:0],如果通道 1 想第 8 个转换,则 SQ8[4:0] 写 1 即可。SQR1 控制着规则序列中的第 13 到第 16 个转换,对应位为:SQ13[4:0]~SQ16[4:0],如果通道 6 想第 10 个转换,则 SQ10[4:0] 写 6 即可。具体使用多少个通道,由 SQR1 的位L[3:0]决定,最多 16 个通道。

注入序列
ADC注入序列寄存器ADC_JSQR 。注入序列寄存器 JSQR 只有一个,最多支持 4 个通道,具体多少个由 JSQR 的 JL[2:0]决定。如果 JL 的 值小于 4 的话,则 JSQR 跟 SQR 决定转换顺序的设置不一样,第一次转换的不是 JSQR1[4:0],而是 JCQRx[4:0] ,x = (4-JL),跟 SQR 刚好相反。如果 JL=00(1个转换),那么转换的顺序是从 JSQR4[4:0]开始,而不是从 JSQR1[4:0]开始,这个要注意,编程的时候不要搞错。当 JL 等于 4 时,跟 SQR 一样。
在这里插入图片描述
4、触发源选择
通道配置完成,转换顺序也已经确定,下面就是触发源的选择。通过ADC控制寄存器 1(ADC_CR1)、ADC控制寄存器 2(ADC_CR2),选择触发源:
在这里插入图片描述
在这里插入图片描述
5、转换时间

ADC 时钟
ADC 输入时钟 ADC_CLK 由 PCLK2 经过分频产生,最大是 14M,分频因子由 RCC 时钟配置寄存器 RCC_CFGR 的位 15:14 ADCPRE[1:0]设置,可以是 2/4/6/8 分频,一般我们设置 PCLK2=HCLK=72M。一般选择8分频。
在这里插入图片描述
在这里插入图片描述

采样时间:
采样的周期数可通过 ADC 采样时间寄存器 ADC_SMPR1ADC_SMPR2 中的SMP[2:0]位设置,ADC_SMPR2 控制的是通道 0~9,ADC_SMPR1 控制的是通道 10~17。每个通道可以分别用不同的时间采样。其中采样周期最小是 1.5 个,即如果我们要达到最快的采样,那么应该设置采样周期为 1.5个周期,这里说的周期就是 1/ADC_CLK。
在这里插入图片描述
在这里插入图片描述
ADC 的转换时间跟 ADC 的输入时钟和采样时间有关公式为:Tconv = 采样时间 + 12.5 个周期。当 ADCLK = 14MHZ (最高),采样时间设置为 1.5 周期(最快),那么总
的转换时间(最短)Tconv = 1.5 周期 + 12.5 周期 = 14 周期 = 1us。一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us(常用1.17us)。

6、数据寄存器:一切准备就绪后,ADC 转换后的数据根据转换组的不同,规则组的数据放在 ADC_DR寄存器,注入组的数据放在 JDRx

规则数据寄存器
在这里插入图片描述

注入数据寄存器
在这里插入图片描述

7、中断

转换结束中断:
数据转换结束后,可以产生中断,中断分为三种:规则通道转换结束中断,注入转换通道转换结束中断,模拟看门狗中断。可以根据相应的中断标志位,编写对应的中断服务函数。事件是否发生,可以访问ADC状态寄存器(ADC_SR)
在这里插入图片描述

模拟看门狗中断
当被 ADC 转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断,前提是我们开启了模拟看门狗中断,其中低阈值和高阈值由 ADC_LTRADC_HTR 设置。例如我们设置高阈值是 2.5V,那么模拟电压超过 2.5V 的时候,就会产生模拟看门狗中断,反之低阈值也一样。
在这里插入图片描述

三、编程相关

在编程的时候,我们只需要配置对用的结构体即可:

ADC_InitTypeDef 结构体

typedef struct
{
  uint32_t ADC_Mode; // ADC 工作模式选择
  FunctionalState ADC_ScanConvMode;			/* ADC 扫描(多通道) 或者单次(单通道)模式选择 */
  FunctionalState ADC_ContinuousConvMode; 	// ADC 单次转换或者连续转换选择
  uint32_t ADC_ExternalTrigConv; // ADC 转换触发信号选择
  uint32_t ADC_DataAlign; // ADC 数据寄存器对齐格式
  uint8_t ADC_NbrOfChannel; // ADC 采集通道数
}ADC_InitTypeDef;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

ADC_Mode:配置 ADC 的模式,当使用一个 ADC 时是独立模式,使用两个 ADC 时
是双模式,在双模式下还有很多细分模式可选。

ScanConvMode:可选参数为 ENABLE 和 DISABLE,配置是否使用扫描。如果是单通
道 AD 转 换 使 用 DISABLE , 如 果 是 多 通 道 AD 转 换 使 用 ENABLE 。

ADC_ContinuousConvMode:可选参数为 ENABLE 和 DISABLE,配置是启动自动连
续转换还是单次转换。使用 ENABLE 配置为使能自动连续转换;使用 DISABLE 配置为单
次转换,转换一次后停止需要手动控制才重新启动转换。

ADC_ExternalTrigConv:外部触发选择,框图中中列举了很多外部触发条件,可根据
项目需求配置触发来源。一般使用软件自动触发。

ADC_DataAlign:转换结果数据对齐模式,可选右对齐 ADC_DataAlign_Right 或者左
对齐 ADC_DataAlign_Left。一般选择右对齐模式。不然读取数据 还得处理。

ADC_NbrOfChannel:AD 转换通道数目,根据实际设置即可。具体的通道数和通道的
转换顺序是配置规则序列或注入序列寄存器

实例一:中断单通道读取ADC

编程要点

  1. 初始 ADC 用到的 GPIO;
  2. 设置 ADC 的工作参数并初始化;
  3. 设置 ADC 工作时钟;
  4. 设置 ADC 转换通道顺序及采样时间;
  5. 配置使能 ADC 转换完成中断,在中断内读取转换完数据;
  6. 使能 ADC;
  7. 使能软件触发 ADC 转换。
    ADC 转换结果数据使用中断方式读取,这里没有使用 DMA 进行数据传输。

1.初始 ADC 用到的 GPIO

void ADCx_GPIO_Config(void)
{  //以PA1为例
	GPIO_InitTypeDef GPIO_InitStruct;
	//开启GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//选择模拟模式
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(ADCx_GPIO_PORT,&GPIO_InitStruct);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.配置ADC

void ADCx_Config(void)
{  //ADC1  Channel_1
	ADC_InitTypeDef ADC_InitStruct;
	//开启ADC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;		// 只使用一个ADC,属于单模式
	ADC_InitStruct.ADC_ScanConvMode = DISABLE; //只有一个通道,关闭扫描模式
	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;		// 连续转换模式
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 不用外部触发转换,软件启即可
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;	// 转换结果右对齐
	ADC_InitStruct.ADC_NbrOfChannel = 1;   // 转换通道个数
	//初始化
	ADC_Init(ADC1,&ADC_InitStruct); //配置ADC时钟N狿CLK2的8分频,即9MHz
	RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC 通道的转换顺序和采样时间
	ADC_RegularChannelConfig(ADC1,ADC_1_Channel,1,ADC_SampleTime_55Cycles5); // ADC 转换结束产生中断,在中断服务程序中读取转换值
	ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);

	// 开启ADC ,并开始转换
	ADC_Cmd(ADC1,ENABLE); ADC_ResetCalibration(ADC1);
	// 等待校准寄存器初始化完成
	while(ADC_GetResetCalibrationStatus(ADC1));
	// ADC开始校准
	ADC_StartCalibration(ADC1);
	// 等待校准完成
	while(ADC_GetCalibrationStatus(ADC1)); // 由于没有采用外部触发,所以使用软件触发ADC转换 
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

3.配置NVIC

void ADC_NVIC_Config(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
	// 优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 配置中断优先级
  NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4.编写中断服务函数

void ADC1_2_IRQHandler(void)
{ uint8_t i = 0;
	if (ADC_GetITStatus(ADC_x,ADC_IT_EOC)==SET) 
	{ //读取ADC的转换值 ADC_Convert_Value =(float)ADC_GetConversionValue(ADC_x)/4096*3.3;
		ADC_ClearITPendingBit(ADC_x,ADC_IT_EOC); //清除转换完成标志位,等待下一次
	}
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

5.测试函数

uint16_t ADC_USART_Value = 0;
uint16_t ADC_USART_Value2 = 0;
void _ADC_IT_Init(void)
{
	ADCx_GPIO_Config();
	ADCx_Config();
	ADC_NVIC_Config();
	
}
void  delay(uint32_t count)
{
	while(count--);
}
int main(void)
{
	LED_GPIO_Config();
	USARTx_Config();
	ADC_IT_Init();
	printf("\r\n ----这是一个ADC多通道采集实验----\r\n"); while(1)
	{
		ADC_USART_Value 2=(float) ADC_USART_Value ; printf("\r\n The current AD value = 0x%04X \r\n", ADC_USART_Value; printf("\r\n The current AD value = %f V \r\n",ADC_USART_Value2); printf("\r\n\r\n");
		delay(0x3fffff); } }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

实例二:DMA多通道读取ADC

编程要点

  1. 初始 ADC 用到的 GPIO;
  2. 设置 ADC 的工作参数并初始化;
  3. 设置 ADC 工作时钟;
  4. 设置 ADC 转换通道顺序及采样时间;
  5. 配置使能 ADC 转换完成中断,在中断内读取转换完数据;
  6. 使能 ADC;
  7. 使能软件触发 ADC 转换。
    这里使用 DMA 进行数据传输,还需配置DMA。

有一点需要注意 仅 ADC1 和 ADC3 有DMA传输功能。分别对应
DMA1 的通道 1
DMA2 的通道 5
在这里插入图片描述
在这里插入图片描述
1.初始 ADC 用到的 GPIO

void ADCx_GPIO_Config(void)
{ GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(ADCx_GPIO_CLK,ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStruct.GPIO_Pin = ADC_1_Pin| ADC_1_Pin| //ADC_2_Pin和SUART冲突  因此删除 ADC_3_Pin| ADC_4_Pin| ADC_5_Pin| ADC_6_Pin| ADC_7_Pin;
	GPIO_Init(ADCx_GPIO_PORT,&GPIO_InitStruct);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.初始 DMA

void ADC_DMA_Config(void)
{
	DMA_InitTypeDef DMA_InitStruct; RCC_AHBPeriphClockCmd(DMA_CLK,ENABLE); //外设基址为:ADC 数据寄存器地址
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC_x->DR)); // 存储器地址
	DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_USART_Value; // 数据源来自外设
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //缓冲区大小,应该等于数据目的地的大小
	DMA_InitStruct.DMA_BufferSize = BUFFERSIZE; // 外设寄存器只有一个,地址不用递增 
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //存储器地址递增
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //外设数据大小为半字,即两个字节			
	DMA_InitStruct.DMA_PeripheralDataSize =	DMA_PeripheralDataSize_HalfWord; //内存数据大小也为半字,跟外设数据大小相同
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //循环传输模式
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //DMA传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
	DMA_InitStruct.DMA_Priority = DMA_Priority_High; //禁止存储器到存储器模式,因为是从外设到存储器
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA_CHANNEL,&DMA_InitStruct); DMA_Cmd(DMA_CHANNEL,ENABLE);
	
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

3.初始 ADC

void ADCx_Config(void)
{
	ADC_InitTypeDef ADC_InitStruct;
	//开启ADC时钟
	RCC_APB2PeriphClockCmd(ADCx_CLK,ENABLE);

	ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;	// 只使用一个ADC,属于单模式
	ADC_InitStruct.ADC_ScanConvMode = ENABLE;  //6通道开启扫描
	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;// 连续转换模式
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 不用外部触发转换,软件开启即可
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;	// 转换结果右对齐
	ADC_InitStruct.ADC_NbrOfChannel = ADC_num;   // 转换通道个数
	//初始化
	ADC_Init(ADC_x,&ADC_InitStruct); //配置ADC时钟N狿CLK2的8分频,即9MHz
	RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC 通道的转换顺序和采样时间
	ADC_RegularChannelConfig(ADC_x,ADC_1_Channel,1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC_x,ADC_4_Channel,2,ADC_SampleTime_55Cycles5); //A2  A3用于串口
	ADC_RegularChannelConfig(ADC_x,ADC_5_Channel,3,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC_x,ADC_6_Channel,4,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC_x,ADC_7_Channel,5,ADC_SampleTime_55Cycles5);

	// 使能ADC DMA 请求
	ADC_DMACmd(ADC_x, ENABLE); // 开启ADC ,并开始转换
	ADC_Cmd(ADC_x,ENABLE); ADC_ResetCalibration(ADC_x);
	// 等待校准寄存器初始化完成
	while(ADC_GetResetCalibrationStatus(ADC_x));
	// ADC开始校准
	ADC_StartCalibration(ADC_x);
	// 等待校准完成
	while(ADC_GetCalibrationStatus(ADC_x)); // 由于没有采用外部触发,所以使用软件触发ADC转换 
	ADC_SoftwareStartConvCmd(ADC_x,ENABLE);
}

void ADCx_DMA_Init(void)
{
	ADCx_GPIO_Config();
	ADC_DMA_Config();
	ADCx_Config();
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

4.测试函数

void  delay(uint32_t count)
{
	while(count--);
}

int main(void)
{
		LED_GPIO_Config();
		USARTx_Config(); ADCx_DMA_Init(); //ADC_IT_Init(); printf("\r\n ----这是一个ADC多通道采集实验----\r\n"); while(1)
		{ printf("\r PA1 value = %f V \r\n",(float)ADC_USART_Value[0]/4096*3.3); printf("\r PA4 value = %f V \r\n",(float)ADC_USART_Value[1]/4096*3.3); printf("\r PA5 value = %f V \r\n",(float)ADC_USART_Value[2]/4096*3.3); printf("\r PA6 value = %f V \r\n",(float)ADC_USART_Value[3]/4096*3.3); printf("\r PA7 value = %f V \r\n\n\n\n\n",(float)ADC_USART_Value[4]/4096*3.3); delay(0x3fffff); } }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

头文件
#include "bsp_dma.h"

#ifndef _BSP_DMA_H_
#define	_BSP_DMA_H_

#include "stm32f10x.h"

#define	BUFFERSIZE 5
#define DMA_CHANNEL DMA1_Channel1   //手册
#define DMA_CLK RCC_AHBPeriph_DMA1


void ADC_DMA_Config(void);

#endif /*_BSP_DMA_H_*/

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

#include "bsp_adc.h"

#ifndef _BSP_ADC_H_
#define	_BSP_ADC_H_
#include "stm32f10x.h"

#define	ADC_num 5

#define	ADC_x ADC1
#define ADCx_CLK RCC_APB2Periph_ADC1

#define ADCx_GPIO_PORT GPIOA
#define ADCx_GPIO_CLK RCC_APB2Periph_GPIOA

#define ADC_1_Pin GPIO_Pin_1
#define ADC_2_Pin GPIO_Pin_2
#define ADC_3_Pin GPIO_Pin_3
#define ADC_4_Pin GPIO_Pin_4
#define ADC_5_Pin GPIO_Pin_5
#define ADC_6_Pin GPIO_Pin_6
#define ADC_7_Pin GPIO_Pin_7

#define ADC_1_Channel ADC_Channel_1
#define ADC_2_Channel ADC_Channel_2
#define ADC_3_Channel ADC_Channel_3
#define ADC_4_Channel ADC_Channel_4
#define ADC_5_Channel ADC_Channel_5
#define ADC_6_Channel ADC_Channel_6
#define ADC_7_Channel ADC_Channel_7

extern uint16_t ADC_USART_Value[ADC_num];
extern float ADC_Convert_Value[ADC_num];

void ADCx_GPIO_Config(void);
void ADCx_Config(void);
void ADC_DMA_Config(void);
void ADCx_DMA_Init(void);

#endif /*_BSP_ADC_H_*/


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

欢迎交流探讨,大家一起进步吧!冲冲冲!

文章来源: blog.csdn.net,作者:Successful 、,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq_45689790/article/details/113862143

(完)