【STM32】片上ADC的初步使用

基于stm32f103系列

基于《零死角玩转 STM32F103---指南者》

ADC简介

stm32f103上的ADC

数量:3
精度:12bit(4096)
通道:ADC1,ADC2均有16个通道,ADC3有8个
功能:

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

  单次和连续转换模式

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

  自校准

  带内嵌数据一致性的数据对齐

  采样间隔可以按通道分别编程

  规则转换和注入转换均有外部触发选项

  间断模式

  双重模式(带2个或以上ADC的器件)

输入电压范围

输入通道

根据手册选择输入通道

转换顺序

规则序列

规则序列 从小到大依次转换

配置SQR3、SQR2、SQR1寄存器。按顺序从SQR3中的SQ1,SQ2到SQR1中的SQ16

注入序列

注入序列 依据通道数目,从大到小或者从小到大

  注入序列寄存器 JSQR 只有一个,最多支持 4 个通道。具体多少个由 JSQR 的 JL2:0决定。如果 JL的 值小于 4 的话,则 JSQR 跟 SQR决定转换顺序的设置不一样,第一次转换的不是 JSQR14:0,而是 JCQRx4:0 ,x = (4-JL),跟 SQR 刚好相反。如果 JL=00(1 个转换),那么转换的顺序是从 JSQR44:0开始,而不是从 JSQR14:0开始,这个要注意,编程的时候不要搞错。当 JL 等于 4 时,跟 SQR 一样。

触发源

1.CR寄存器中的CONbit位使能,开始转换

2.触发启动:

    内部定时器触发和外部 IO 触发

触发源有很多,具体选择哪一种触发源,由 ADC 控制寄存器 2:ADC_CR2 的EXTSEL2:0和 JEXTSEL2:0位来控制。EXTSEL2:0用于选择规则通道的触发源,JEXTSEL2:0用于选择注入通道的触发源。选定好触发源之后,触发源是否要激活,则由ADC 控制寄存器 2:ADC_CR2 的 EXTTRIG 和 JEXTTRIG 这两位来激活。其中 ADC3 的规则转换和注入转换的触发源与 ADC1/2 的有所不同。

EXTSEL2:0和JEXTSEL2:0]控制位允许应用程序选择8个可能的事件中的某一个,可以触发规则和注入组的采样。

转换时间

ADC 时钟

ADC 输入时钟 ADC_CLK 由 PCLK2 经过分频产生,最大是 14M,分频因子由 RCC 时钟配置寄存器 RCC_CFGR 的位 15:14 ADCPRE1:0设置,可以是 2/4/6/8 分频,注意这里没有 1 分频。一般我们设置 PCLK2=HCLK=72M。

采样时间

ADC 使用若干个 ADC_CLK 周期对输入的电压进行采样,采样的周期数可通过 ADC 采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP2: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,这个才是最常用的。

数据寄存器 ADC->DR

ADC 转换后的数据根据转换组的不同,规则组的数据放在 ADC_DR寄存器,注入组的数据放在 JDRx。

规则数据寄存器 ADC_DR寄存器

ADC 规则组数据寄存器 ADC_DR 只有一个,是一个 32 位的寄存器,低 16 位在单 ADC时使用,高 16 位是在 ADC1 中双模式下保存 ADC2 转换的规则数据,双模式就是 ADC1 和ADC2 同时使用。

规则通道可以有 16 个这么多,可规则数据寄存器只有一个,如果使用多通道转换,那转换的数据就全部都挤在了 DR 里面,前一个时间点转换的通道数据,就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走,或者开启 DMA 模式,把数据传输到内存里面,不然就会造成数据的覆盖。最常用的做法就是开启 DMA 传输。

注入数据寄存器 JDRx寄存器

ADC 注入组最多有 4 个通道,刚好注入数据寄存器也有 4 个,每个通道对应着自己的寄存器,不会跟规则寄存器那样产生数据覆盖的问题。ADC_JDRx 是 32 位的,低 16 位有效,高 16 位保留,数据同样分为左对齐和右对齐,具体是以哪一种方式存放,由ADC_CR2 的 11 位 ALIGN 设置。

中断

转换结束中断

数据转换结束后,可以产生中断,中断分为三种:规则通道转换结束中断,注入转换通道转换结束中断,模拟看门狗中断。

模拟看门狗中断

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

DMA请求

规则和注入通道转换结束后,除了产生中断外,还可以产生 DMA 请求,把转换好的数据直接存储在内存里面。要注意的是只有 ADC1 和 ADC3 可以产生 DMA 请求。

编程要点

独立单通道

ADC GPIO 初始化

c 复制代码
 static void ADCx_GPIO_Config(void)
 {
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2,ENABLE)
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	GPIO_Init(GPIOC, &GPIO_InitStructure);
 }

ADC 工作模式配置

c 复制代码
// ADC 编号选择
// 可以是 ADC1/2,如果使用 ADC3,中断相关的要改成 ADC3 的
#define ADC_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADCx ADC2
#define ADC_CLK RCC_APB2Periph_ADC2

// ADC GPIO 宏定义
// 注意:用作 ADC 采集的 IO 必须没有复用,否则采集电压会有影响
#define ADC_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADC_GPIO_CLK RCC_APB2Periph_GPIOC
#define ADC_PORT GPIOC
#define ADC_PIN GPIO_Pin_1
// ADC 通道宏定义
#define ADC_CHANNEL ADC_Channel_11
 
// ADC 中断相关宏定义
#define ADC_IRQ ADC1_2_IRQn
#define ADC_IRQHandler ADC1_2_IRQHandler


static void ADCx_Mode_Config(void)
{
	ADC_InitTypeDef ADC_InitStructure;

	// 打开 ADC 时钟
	ADC_APBxClock_FUN ( ADC_CLK, ENABLE );

	// ADC 模式配置
	// 只使用一个 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;
 
	 // 转换通道 1 个
	 ADC_InitStructure.ADC_NbrOfChannel = 1;
 
	 // 初始化 ADC
	 ADC_Init(ADCx, &ADC_InitStructure);
 
	 // 配置 ADC 时钟N狿 CLK2 的 8 分频,即 9MHz
	 RCC_ADCCLKConfig(RCC_PCLK2_Div8);
 
	 // 配置 ADC 通道转换顺序为 1,第一个转换,采样时间为 55.5 个时钟周期
	 ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1,
	 ADC_SampleTime_55Cycles5);
 
	 // ADC 转换结束产生中断,在中断服务程序中读取转换值
	 ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE);
 
	 // 开启 ADC ,并开始转换
	 ADC_Cmd(ADCx, ENABLE);
 
	 // 初始化 ADC 校准寄存器
	 ADC_ResetCalibration(ADCx);
	 // 等待校准寄存器初始化完成
	 while (ADC_GetResetCalibrationStatus(ADCx));

	 // ADC 开始校准
	 ADC_StartCalibration(ADCx);
	 // 等待校准完成
	 while (ADC_GetCalibrationStatus(ADCx));

	 // 由于没有采用外部触发,所以使用软件触发 ADC 转换
	 ADC_SoftwareStartConvCmd(ADCx, ENABLE);
 }

ADC 中断配置

NVIC配置

c 复制代码
static void ADC_NVIC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

	NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 
	NVIC_Init(&NVIC_InitStructure);
}

开启ADC中断

c 复制代码
// ADC 转换结束产生中断,在中断服务程序中读取转换值
 ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE);

ADC中断服务函数


1.ADC1和ADC2共有一个中断服务函数

2.中断服务函数中读取ADC状态寄存器SR的相应位置

c 复制代码
void ADC_IRQHandler(void)
{
	if (ADC_GetITStatus(ADCx,ADC_IT_EOC)==SET) {
	// 读取 ADC 的转换值
	ADC_ConvertedValue = ADC_GetConversionValue(ADCx);

	}
	ADC_ClearITPendingBit(ADCx,ADC_IT_EOC);
}

独立模式多通道采集

  1. 初始化 ADC GPIO;
  2. 初始化 ADC 工作参数;
  3. 配置 DMA 工作参数;
  4. 读取 ADC 采集的数据;

GPIO配置

c 复制代码
static void ADCx_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	// 打开 ADC IO 端口时钟
	ADC_GPIO_APBxClock_FUN ( ADC_GPIO_CLK, ENABLE );

	// 配置 ADC IO 引脚模式
	GPIO_InitStructure.GPIO_Pin = ADC_PIN1
	 |ADC_PIN2
	 |ADC_PIN3
	 |ADC_PIN4
	 |ADC_PIN5;
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	 // 初始化 ADC IO
	 GPIO_Init(ADC_PORT, &GPIO_InitStructure);
 }

ADC以及DMA模式配置

c 复制代码
static void ADCx_Mode_Config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;

	// 打开 DMA 时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	// 打开 ADC 时钟
	ADC_APBxClock_FUN ( ADC_CLK, ENABLE );
 
	 /* ------------------DMA 模式配置---------------- */
	 // 复位 DMA 控制器
	 DMA_DeInit(ADC_DMA_CHANNEL);
	 // 配置 DMA 初始化结构体
	 // 外设基址为:ADC 数据寄存器地址
	 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&( ADCx->DR ));
	 // 存储器地址
	 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;
	 // 数据源来自外设
	 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	 // 缓冲区大小,应该等于数据目的地的大小
	 DMA_InitStructure.DMA_BufferSize = NOFCHANEL;
	 // 外设寄存器只有一个,地址不用递增
	 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
	 DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
	 // 使能 DMA 通道
	 DMA_Cmd(ADC_DMA_CHANNEL , ENABLE);
 
	 /* ----------------ADC 模式配置--------------------- */
	 // 只使用一个 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 = NOFCHANEL;
	 // 初始化 ADC
	 ADC_Init(ADCx, &ADC_InitStructure);
	 // 配置 ADC 时钟N狿 CLK2 的 8 分频,即 9MHz
	 RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	 // 配置 ADC 通道的转换顺序和采样时间
	 ADC_RegularChannelConfig(ADCx, ADC_CHANNEL1, 1,
	 ADC_SampleTime_55Cycles5);
	 ADC_RegularChannelConfig(ADCx, ADC_CHANNEL2, 2,
	 ADC_SampleTime_55Cycles5);
	 ADC_RegularChannelConfig(ADCx, ADC_CHANNEL3, 3,
	 ADC_SampleTime_55Cycles5);
	 ADC_RegularChannelConfig(ADCx, ADC_CHANNEL4, 4,
	 ADC_SampleTime_55Cycles5);
	 ADC_RegularChannelConfig(ADCx, ADC_CHANNEL5, 5,
	 ADC_SampleTime_55Cycles5);
	 // 使能 ADC DMA 请求
	 ADC_DMACmd(ADCx, ENABLE);
	 // 开启 ADC ,并开始转换
	 ADC_Cmd(ADCx, ENABLE);
	 // 初始化 ADC 校准寄存器
	 ADC_ResetCalibration(ADCx);
	 // 等待校准寄存器初始化完成
	 while (ADC_GetResetCalibrationStatus(ADCx));
	 // ADC 开始校准
	 ADC_StartCalibration(ADCx);
	 // 等待校准完成
	 while (ADC_GetCalibrationStatus(ADCx));
	 // 由于没有采用外部触发,所以使用软件触发 ADC 转换
	 ADC_SoftwareStartConvCmd(ADCx, ENABLE);
 }

双重 ADC 同步规则模式采集

参看《零死角玩转STM32---F103指南者》

相关推荐
国科安芯6 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
茯苓gao8 小时前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
梁朝辉10 小时前
STM32H750VBT6中ADCINP与INN什么区别
stm32·单片机·嵌入式硬件
省四收割者11 小时前
一文详解信号完整性(1)
python·嵌入式硬件·数学建模·信息与通信·信号处理·智能硬件
崇山峻岭之间12 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious13 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
KaifuZeng16 小时前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计18 小时前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
kebidaixu18 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片详解
stm32
kebidaixu19 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片调试实录:四个关键问题与解决方案
stm32