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

文章目录

    • [ADC 和 DMA](#ADC 和 DMA)
        • [1. ADC](#1. ADC)
          • [1.1 ADC 概述](#1.1 ADC 概述)
          • [1.2 ADC 工作原理](#1.2 ADC 工作原理)
            • [1.2.1 ADC 的主要特征](#1.2.1 ADC 的主要特征)
            • [1.2.2 ADC 内部架构框图](#1.2.2 ADC 内部架构框图)
            • [1.2.3 ADC 数据转换规则](#1.2.3 ADC 数据转换规则)
          • [1.3 ADC 编程实现和相关寄存器](#1.3 ADC 编程实现和相关寄存器)
            • [1.3.1 ADC 时钟问题](#1.3.1 ADC 时钟问题)
            • [1.3.2 ADC_CR1 寄存器](#1.3.2 ADC_CR1 寄存器)
            • [1.3.3 ADC_CR2 寄存器](#1.3.3 ADC_CR2 寄存器)
            • [1.3.4 ADC_SMPR 寄存器](#1.3.4 ADC_SMPR 寄存器)
            • [1.3.5 ADC_SQR 寄存器](#1.3.5 ADC_SQR 寄存器)
          • [1.4 ADC 读取板载光敏传感器数据代码实现··](#1.4 ADC 读取板载光敏传感器数据代码实现··)
        • [2. DMA](#2. DMA)
          • [2.1 DMA 引入](#2.1 DMA 引入)
          • [2.2 DMA 主要特征](#2.2 DMA 主要特征)
          • [2.3 DMA 框图](#2.3 DMA 框图)
          • [2.4 DMA 映射关系](#2.4 DMA 映射关系)
          • [2.5 DMA 开发](#2.5 DMA 开发)
          • [2.6 DMA 开发相关寄存器](#2.6 DMA 开发相关寄存器)
            • [2.6.1 DMA_CCRx 寄存器](#2.6.1 DMA_CCRx 寄存器)
            • [2.6.2 DMA_CPARx和DMA_CMARx](#2.6.2 DMA_CPARx和DMA_CMARx)
            • [2.6.3 DMA_CNDTRx](#2.6.3 DMA_CNDTRx)
          • [2.7 开发流程](#2.7 开发流程)
          • [2.8 代码实现](#2.8 代码实现)

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 编程实现和相关寄存器
1.3.1 ADC 时钟问题
  • 根据原理图分析
  • 当前引脚为 PF8 引脚,同时对应的 ADC 通道是 ADC3_IN6
  • ADC3 所在时钟是 APB2

ADC 预分频器寄存器控制

  • 利用 RCC->CFGR 寄存器配置当前 ADC 预分频器参数
  • 当前 STM32F103ZET6 对应 72 MHz ,ADCCLK 要求最大 14 MHz ,当前预分频器可选倍数 6 或则 8。
1.3.2 ADC_CR1 寄存器
  • 虽然寄存器默认值是 0x0000000 在实际开发中,如果对应寄存器位为 0 是当前开发所需,也需要明确的给予对应寄存器标志位赋 0 操作。
  • DUALMOD位[19:16] : ADC 独立模式 ==> 0000
  • SCAN**[位8]** : 扫描模式关闭 ==> 0
c 复制代码
ADC3->CR1 &= ~(0x0F << 16); // ADC 独立模式
ADC3->CR1 &= ~(0x01 << 8);  // SCAN 扫描模式关闭
1.3.3 ADC_CR2 寄存器
  • 当前寄存器主要控制,ADC 采用数据通道,通信触发规则,数据对齐方式,校验和 ADC开启

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

ADC 采用周期计算

  • ADC 采样周期越短,对应的采样时间越少,但是相对的 ADC 精度较低
  • ADC 采样周期越长,对应的采样时间略高,同时 ADC 精度较高,适用于较大电阻电路。
1.3.5 ADC_SQR 寄存器
  • 需要两个寄存器配合
    • ADC_SQR1 配置规则通道打开个数
    • ADC_SQR3 配置 SQ1 寄存器位对应的 ADC3_IN6

ADC_SQR 寄存器配置内容

c 复制代码
ADC3->SQR1 &= ~(0x0F << 20);
ADC3->SQR3 |= 0x06;
1.4 ADC 读取板载光敏传感器数据代码实现··
c 复制代码
#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 引入
c 复制代码
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 主要特征
2.3 DMA 框图
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 寄存器
2.6.2 DMA_CPARx和DMA_CMARx

设置当前

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

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

  • 例如选择 半字,当前地址是对应半字空间的首地址
  • 例如选择 全字,当前地址是对应全字空间的首地址
2.6.3 DMA_CNDTRx
2.7 开发流程
  • 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 代码实现
c 复制代码
#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;
}
相关推荐
小莞尔3 小时前
【51单片机】【protues仿真】基于51单片机自动售货机系统
c语言·单片机·嵌入式硬件·物联网·51单片机
编码追梦人3 小时前
基于 STM32 的智能语音唤醒与关键词识别系统设计 —— 从硬件集成到算法实现
stm32·算法·struts
AI浩3 小时前
自监督 YOLO:利用对比学习实现标签高效的目标检测
学习·yolo·目标检测
柒月玖.10 小时前
基于AT89C52单片机的温度检测报警设计
单片机·嵌入式硬件
m0_5557629010 小时前
GPIO配置详细解析
单片机·嵌入式硬件
黑科技Python10 小时前
生活中的“小智慧”——认识算法
学习·算法·生活
Yupureki10 小时前
从零开始的C++学习生活 16:C++11新特性全解析
c语言·数据结构·c++·学习·visual studio
青云交10 小时前
Java 大视界 -- Java 大数据在智能教育学习社区互动模式创新与用户活跃度提升中的应用(426)
java·大数据·学习·flink 实时计算·智能教育社区·互动模式创新·用户活跃度
兆龙电子单片机设计10 小时前
【STM32项目开源】STM32单片机多功能饮水机系统
stm32·单片机·物联网·开源·毕业设计