STM32学习(MCU控制)(DMA and ADC)

ADC 和 DMA

1. ADC
1.1 ADC 概述

ADC ==> 模拟量转数字量,在 STM32 开发中,利用 ADC 端口的电压数据,转换为对应的具体数字量数据内容。可以通过 ADC 方式获取常用的数据内容有

  • 光敏电阻,电池电量,油箱油量

ADC 转换的数据,可以用于执行器控制行为,低电量警告,油箱油量不足警告,【阈值警告】【阈值处理】

1.2 ADC 工作原理
1.2.1 ADC 的主要特征

1.2.2 ADC 内部架构框图

1.2.3 ADC 数据转换规则

ADC 转换器进行数据转换操作使用的是逐次逼近法

  • ADC 数据采样精度范围是 12 位 ==> 0000 0000 0000 ~ 1111 1111 1111
  • 按照当前 Vref+ = 3.3V , Vref- = 0V
  • ADC 采用每一个数据对应的电压是 3.3V / 4096 == 0.0008V
  • 假设当前 ADC 读取到电压为 1.83 V

1.3 ADC 编程实现和相关寄存器

6

1.3.1 ADC 时钟问题
  • 根据原理图分析
  • 当前引脚为 PF8 引脚,同时对应的 ADC 通道是 ADC3_IN6
  • ADC3 所在时钟是 APB2

ADC 预分频器寄存器控制

  • 利用 RCC->CFGR 寄存器配置当前 ADC 预分频器参数
  • 当前 STM32F103ZET6 对应 72 MHz ,ADCCLK 要求最大 14 MHz ,当前预分频器可选倍数 6 或则 8。

8

1.3.2 ADC_CR1 寄存器

  • 虽然寄存器默认值是 0x0000000 在实际开发中,如果对应寄存器位为 0 是当前开发所需,也需要明确的给予对应寄存器标志位赋 0 操作。
  • DUALMOD位[19:16] : ADC 独立模式 ==> 0000
  • SCAN**[位8]** : 扫描模式关闭 ==> 0
ADC3->CR1 &= ~(0x0F << 16); // ADC 独立模式
ADC3->CR1 &= ~(0x01 << 8);  // SCAN 扫描模式关闭
1.3.3 ADC_CR2 寄存器
  • 当前寄存器主要控制,ADC 采用数据通道,通信触发规则,数据对齐方式,校验和 ADC开启

11

ADC_CR2 寄存器配置内容

  • SWSTART**[位22]** : 开始转换规则通道 ==> 1
  • EXTTRIG**[位20]**:规则通道的外部触发转换模式 ==> 1
  • EXTSEL**[位19:17]**: 选择启动规则通道组转换的外部事件 ==> 111
  • ALIGN**[位11]**::数据对齐(Data alignment) ==> 0 右对齐
  • RSTCAL**[位3]**::复位校准(Reset calibration) ==> 1
  • CAL**[位2]**::A/D校准(A/D Calibration) ==> 1
  • CONT**[位1]**::连续转换(Continuous conversion) ==> 1
  • ADON**[位0]**::开/关A/D转换器(A/D converter ON/ OFF) ==> 1
1.3.4 ADC_SMPR 寄存器

ADC 中的 SMPR 寄存器是用于控制当前 ADC 的采样周期

  • ADC_SMPR1:SMP10 ~ SMP15 对应 ADC 对外通道, SMP16 对应 MCU 芯片内部温度 ADC ,SMP17 对应 MCU 工作电压 ADC
  • ADC_SMPR2:SMP0 ~ SMP9 对应 ADC 对外通道

当前光敏传感器对应 ADC 通道 ADC3_IN6,对应寄存器配置是 SMPR2

18

ADC 采用周期计算

  • ADC 采样周期越短,对应的采样时间越少,但是相对的 ADC 精度较低
  • ADC 采样周期越长,对应的采样时间略高,同时 ADC 精度较高,适用于较大电阻电路。

1.3.5 ADC_SQR 寄存器
  • 需要两个寄存器配合
    • ADC_SQR1 配置规则通道打开个数
    • ADC_SQR3 配置 SQ1 寄存器位对应的 ADC3_IN6

21

23

ADC_SQR 寄存器配置内容

ADC3->SQR1 &= ~(0x0F << 20);
ADC3->SQR3 |= 0x06;
1.4 ADC 读取板载光敏传感器数据代码实现··
#include "adc.h"

void LSEN_Init(void)
{
	// 1. RCC 时钟使能,需要提供 GPIOF 和 ADC3 
	RCC->APB2ENR |= (0x01 << 7) | (0x01 << 15);
	
	// 2. GPIOF --> PF8 模拟输入模式 ==> 0000
	GPIOF->CRH &= ~(0x0F);
	
	/*
	3. ADC 配置
	*/
	/*
	3.1 ADC 预分配倍数配置
		因为当前 STM32F103ZET6 对应 72 MHz,
		ADCCLK 不得大于 14 MHz,预分频倍数最小可以选择 6
	*/
	RCC->CFGR &= ~(0x03 << 14);
	RCC->CFGR |= (0x02 << 14);
	
	/*
	3.2 配置 ADC 的工作通道
		选择工作通道为 ADC3_IN6,规则通道打开一个,配置 SQ1
	*/
	ADC3->SQR1 &= ~(0x0F << 20);
	ADC3->SQR3 &= ~(0x1F);
	ADC3->SQR3 |= 0x06;
	
	/*
	3.3 ADC 采用周期
		采样周期选择 239.5 + 12.5 最大 ADC 采样周期,可以
		获取到更大的数据精度。
	*/
	ADC3->SMPR2 |= (0x07 << 18);
	
	/*
	3.4. 配置 ADC CR 寄存器相关内容
		CR1
			- DUALMOD位 [19:16] : ADC 独立模式 ==> 0000
			- SCAN [位8] :  扫描模式关闭 ==> 0
		CR2
			- SWSTART [位22] : 开始转换规则通道 ==> 1
			- EXTTRIG [位20]:规则通道的外部触发转换模式 ==> 1
			- EXTSEL [位19:17]: 选择启动规则通道组转换的外部事件 ==> 111
			- ALIGN [位11]::数据对齐(Data alignment)  ==> 0 右对齐
			- CONT [位1]::连续转换(Continuous conversion) ==> 1
	*/
	ADC3->CR1 &= ~(0x0F << 16);
	ADC3->CR1 &= ~(0x01 << 8);
	
	ADC3->CR2 &= ~(0xFFFFFFFF);
	ADC3->CR2 |= (0x01 << 22);  // SWSTART [位22] : 开始转换规则通道 ==> 1
	ADC3->CR2 |= (0x01 << 20);  // EXTTRIG [位20]:规则通道的外部触发转换模式 ==> 1
	ADC3->CR2 |= (0x07 << 17);  // EXTSEL [位19:17]: 选择启动规则通道组转换的外部事件 ==> 111
	ADC3->CR2 &= ~(0x01 << 11); // ALIGN [位11]::数据对齐(Data alignment)  ==> 0 右对齐
	ADC3->CR2 |= (0x01 << 1);
	
	/*
	3.5 ADC 复位 + 校准
	ADC 自校准 + 重启过程
		- RSTCAL [位3]::复位校准(Reset calibration) ==> 1	
		- CAL [位2]::A/D校准(A/D Calibration) ==> 1
		- ADON [位0]::开/关A/D转换器(A/D converter ON/ OFF) ==> 1
	*/
	ADC3->CR2 &= ~(0x01); // 关闭 ADC
	Delay_ms(10);         // 延时 10 ms
	ADC3->CR2 |= 0x01;    // 打开 ADC
	
	/*
	开始复位校准,给予对应寄存器标志位 1,如果 ADC 复位校准结束
	对应寄存器位置硬件清除为 0
	*/
	ADC3->CR2 |= (0x01 << 3); 
	// while 循环是等待当前复位校准结束 
	while ((ADC3->CR2 & (0x01 << 3)));
	
	Delay_ms(10); 
	
	/*
	开始 A/D 校准,给予对应寄存器标志位 1,ADC A/D 校准之后
	对应寄存器位置硬件清除为 0
	*/
	ADC3->CR2 |= (0x01 << 2);
	// while 循环是等待当前 A/D 校准结束 
	while ((ADC3->CR2 & (0x01 << 2)));
	
	ADC3->CR2 |= 0x01;    // 打开 ADC
}

u16 LSEN_GetValue(void)
{
	/*
	ADC->SR 状态寄存器 EOC [位1] 位置,如果数据未转换完成
		EOC 为 0 ,转换完成 EOC 为 1
	*/
	while (!(ADC3->SR & (0x01 << 1)));
	
	return ADC3->DR;
	
}
2. DMA
2.1 DMA 引入
int a = 10;
int b = 20;

a = b;
  • 以上操作
    • 变量定义和初始化
    • 变量数据赋值
  • 都需要 MCU / CPU 参与整个过程。因为数据内容需要通过 MCU/CPU 寻址行为,找到对应数据存储空间,进行数据提取,数据定义,内存空间申请,数据内容赋值,都需要 MCU / CPU 进行操作
  • 以上操作会占用 MCU/CPU 资源,期望数据可以在 MCU/CPU 内部进行指定通道的特定流转。设定规则之后,数据可以通过特定的数据通道,快递的进行数据传递。进行高速响应,提升整个 MCU 的工作效率。
  • 以上问题可以采用 DMA 解决(Direct Memory Access,直接内存访问/高速数据总线),可以实现 Memory to Memory 数据传递。不涉及 MCU 的逻辑判断或者说控制指向
2.2 DMA 主要特征

25

2.3 DMA 框图

28

2.4 DMA 映射关系

DMA1 映射关系

DMA2 映射关系

2.5 DMA 开发

DMA 是一个通道!!!不占用内存空间,在 MCU 内部设计预留的数据管道。】

  • 数据源 SRC
    • 【数据源地址】
    • 【数据宽度】全字,半字,字节
    • 【数据增量】从指定内存位置提取数据之后,下一次数据提取是从当前位置继续读取,还是选择读取下一个空间地址
      • 例如 ADC 转换通道
        • 规则通道:数据转换完成存储寄存器有且只有 2 个字节,每一次 ADC 转换之后的数据,都输存储到对应的指定寄存器位置,地址不变,【不设置数据增量】
        • 注入通道:数据转换完成寄存器有 4 * 16 位组成,每一组数据是占用 2 个字节,需要从注入通道中读取数据,需要设置【数据增量】,同时限制增量范围。
  • 目标地址 DEST
    • 【目标接收数据地址】
    • 【数据宽度】全字,半字,字节,通常情况下,数据源和目标地址对应的【数宽】一致
    • **【数据增量】**如果设置数据增量,下一次存储数据地址是当前地址 + 数宽。如果不设置,每一次存储数据的地址都是固定地址
      • 例如多数据存储到数组中,需要利用数据增量按照数组下标 0 ~ N 进行逐一的数据存储
      • 例如单数据存储,仅考虑当前固定地址进行数据存储操作。
2.6 DMA 开发相关寄存器
2.6.1 DMA_CCRx 寄存器

34

2.6.2 DMA_CPARx和DMA_CMARx

设置当前

  • 外设地址寄存器地址
  • 存储器地址寄存器地址

两个地址都存储目标内存的首地址,且根据当前选择的数宽,自动延展到目标内存

  • 例如选择 半字,当前地址是对应半字空间的首地址
  • 例如选择 全字,当前地址是对应全字空间的首地址

2.6.3 DMA_CNDTRx

2.7 开发流程

37

  • ADC 采样光敏传感器数据相关内容已完成开发。
  • 根据原理分析,和 DMA 映射关系分析,需要开启 DMA2 通道 5,根据当前数据情况完成开发
  • DMA2 时钟使能 ==> AHBENR
  • DMA2 通道 5 对应源地址和目标地址处理
    • 源地址 src ==> ADC3_DR ADC3 的数据寄存器。
    • 目标地址 dst ==> 选择 u16 类型的变量作为当前数据的目标存储空间,需要提供当前变量的首地址作为 DMA 数据存储端的【目标地址】
  • DMA2_Channel5 配置
    • MEM2MEM [位14] ==> 当前数据是从 ADC 到内存,属于外设到内存,选择 0 外设到内存模式
    • PL [位 13:12] ==> 当前通道优先级,选择 11 最高,10 高, 01 中, 00 最低
    • MSZIE[位11:10] ==> 存储器数宽,对应当前用于作为存储目标的 u16 类型变量,对应数据类型占用内存空间为 2 字节,选择半字 ==> 01
    • PSZIE[位9:8] ==> 外设数据宽度,对应 ADC->DR ,考虑到 ADC 规则数据存储器为 2 个字节,当前设置为 半字 ==> 01
    • MINC[位7] > 存储器地址增量,当前数据不需要增量处理,> 0
    • PINC[位6] > 外设地址增量,当前数据不需要增量处理,> 0
    • CIRC[位5] ==> 循环模式打开,需要进行 ADC 数据连续采样 ==> 1
    • DIR[位4] ==> 当前操作是外设到内部存储器 ==> 0
    • EN[位0] ==> DMA 打开 ,需要 ==> 1
  • 0011 0101 0010 0001 ==> 0x3521
2.8 代码实现
#include "adc.h"

u16 adc_dma_value = 0;

void LSEN_Init(void)
{
	// 1. RCC 时钟使能,需要提供 GPIOF 和 ADC3 
	RCC->APB2ENR |= (0x01 << 7) | (0x01 << 15);
	
	// 2. GPIOF --> PF8 模拟输入模式 ==> 0000
	GPIOF->CRH &= ~(0x0F);
	
	/*
	3. ADC 配置
	*/
	/*
	3.1 ADC 预分配倍数配置
		因为当前 STM32F103ZET6 对应 72 MHz,
		ADCCLK 不得大于 14 MHz,预分频倍数最小可以选择 6
	*/
	RCC->CFGR &= ~(0x03 << 14);
	RCC->CFGR |= (0x02 << 14);
	
	/*
	3.2 配置 ADC 的工作通道
		选择工作通道为 ADC3_IN6,规则通道打开一个,配置 SQ1
	*/
	ADC3->SQR1 &= ~(0x0F << 20);
	ADC3->SQR3 &= ~(0x1F);
	ADC3->SQR3 |= 0x06;
	
	/*
	3.3 ADC 采用周期
		采样周期选择 239.5 + 12.5 最大 ADC 采样周期,可以
		获取到更大的数据精度。
	*/
	ADC3->SMPR2 |= (0x07 << 18);
	
	/*
	3.4. 配置 ADC CR 寄存器相关内容
		CR1
			- DUALMOD位 [19:16] : ADC 独立模式 ==> 0000
			- SCAN [位8] :  扫描模式关闭 ==> 0
		CR2
			- SWSTART [位22] : 开始转换规则通道 ==> 1
			- EXTTRIG [位20]:规则通道的外部触发转换模式 ==> 1
			- EXTSEL [位19:17]: 选择启动规则通道组转换的外部事件 ==> 111
			- ALIGN [位11]::数据对齐(Data alignment)  ==> 0 右对齐
			- CONT [位1]::连续转换(Continuous conversion) ==> 1
	*/
	ADC3->CR1 &= ~(0x0F << 16);
	ADC3->CR1 &= ~(0x01 << 8);
	
	ADC3->CR2 &= ~(0xFFFFFFFF);
	ADC3->CR2 |= (0x01 << 22);  // SWSTART [位22] : 开始转换规则通道 ==> 1
	ADC3->CR2 |= (0x01 << 20);  // EXTTRIG [位20]:规则通道的外部触发转换模式 ==> 1
	ADC3->CR2 |= (0x07 << 17);  // EXTSEL [位19:17]: 选择启动规则通道组转换的外部事件 ==> 111
	ADC3->CR2 &= ~(0x01 << 11); // ALIGN [位11]::数据对齐(Data alignment)  ==> 0 右对齐
	ADC3->CR2 |= (0x01 << 1);
	
	/*
	3.5 ADC 复位 + 校准
	ADC 自校准 + 重启过程
		- RSTCAL [位3]::复位校准(Reset calibration) ==> 1	
		- CAL [位2]::A/D校准(A/D Calibration) ==> 1
		- ADON [位0]::开/关A/D转换器(A/D converter ON/ OFF) ==> 1
	*/
	ADC3->CR2 &= ~(0x01); // 关闭 ADC
	Delay_ms(10);         // 延时 10 ms
	ADC3->CR2 |= 0x01;    // 打开 ADC
	
	/*
	开始复位校准,给予对应寄存器标志位 1,如果 ADC 复位校准结束
	对应寄存器位置硬件清除为 0
	*/
	ADC3->CR2 |= (0x01 << 3); 
	// while 循环是等待当前复位校准结束 
	while ((ADC3->CR2 & (0x01 << 3)));
	
	Delay_ms(10); 
	
	/*
	开始 A/D 校准,给予对应寄存器标志位 1,ADC A/D 校准之后
	对应寄存器位置硬件清除为 0
	*/
	ADC3->CR2 |= (0x01 << 2);
	// while 循环是等待当前 A/D 校准结束 
	while ((ADC3->CR2 & (0x01 << 2)));
	
	ADC3->CR2 |= 0x01;    // 打开 ADC
	
	/*
	【ADC3 ==> 引入 DMA 模块开发】
	*/
	/*
	DMA 开发模块
	【第一步】 DMA2 使能 同时开启 ADC3 对应的 DMA 支持
	*/
	RCC->AHBENR = (0x01 << 1);
	ADC3->CR2 |= (0x01 << 8);
	
	/*
	DMA 开发模块
	【第二步】 设置 DMA2_Channel5 DMA2 第五个通道
		CPAR 存储地址对应 SRC ==> ADC3->DR
		CMAR 存储地址对应 DST ==> adc_dma_value
	
	当前配置是将 DMA2_Channel5 通道,数据从 ADC3->DR 中提取
	利用通道转移搬运到 adc_dma_value
	*/
	DMA2_Channel5->CPAR = (uint32_t)(&(ADC3->DR));
	DMA2_Channel5->CMAR = (uint32_t)(&(adc_dma_value));
	
	/*
	DMA 开发模块
	【第三步】 设置 DMA2_Channel5 设置搬运存储数据个数
	*/
	DMA2_Channel5->CNDTR = 1;
	
	
	/*
	DMA 开发模块
	【第四步】配置 DMA2_Channel5 CCR 配置寄存器内容
	主要内容有
		- MEM2MEM [位14]  ==> 当前数据是从 ADC 到内存,属于外设到内存,选择 0 外设到内存模式
		- PL [位13:12]    ==> 当前通道优先级,选择 11 最高,10 高, 01 中, 00 最低
		- MSZIE [位11:10] ==> 存储器数宽,对应当前用于作为存储目标的 u16 类型变量,
								对应数据类型占用内存空间为 2 字节,选择半字 ==> 01
		- PSZIE [位9:8]   ==> 外设数据宽度,对应 ADC->DR ,考虑到 ADC 规则数据存储器
								为 2 个字节,当前设置为 半字 ==> 01
		- MINC [位7]      ==> 存储器地址增量,当前数据不需要增量处理,==> 0 
		- PINC [位6]      ==> 外设地址增量,当前数据不需要增量处理,==> 0 
		- CIRC [位5]      ==> 循环模式打开,需要进行 ADC 数据连续采样 ==> 1
		- DIR [位4]       ==> 当前操作是外设到内部存储器 ==> 0
		- EN [位0]        ==> DMA 打开 ,需要 ==> 1
	对应二进制数据情况
		0011 0101 0010 0001 ==> 0x3521
	*/
	DMA2_Channel5->CCR = 0;
	DMA2_Channel5->CCR = 0x3521;
}

u16 LSEN_GetValue(void)
{
	return adc_dma_value;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值