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
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 寄存器配置内容
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 主要特征

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 代码实现
#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;
}
4057

被折叠的 条评论
为什么被折叠?



