夜深人静学32系列18——DMA+ADC单/多通道采集
- DMA & ADC (理论篇)
- DMA
- DMA框图
- DMA通道与外设对应表
- ADC
- 重要知识
- 不同模式组合的作用
- 为什么要是用DMA + ADC?
- DMA & ADC (实战篇)
- 任务要求
- 原理图
- CubeMX配置
- 代码实现
- 实验现象
很久没更新了,这次我们浅浅的学习一下 DMA+ADC采集
DMA & ADC (理论篇)
DMA
DMA,全称Direct Memory Access,即直接存储器访问,用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可
供其它操作使用。
DMA数据流:每个 DMA 控制器有 8 个数据流,每个数据流有多达 8 个通道(或称请求)
DMA传输 将数据从一个地址空间复制到另一个地址空间。可以是外设到外设、外设到内存、内存到内存。
DMA的优势:DMA传输无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高,
DMA框图
DMA通道与外设对应表
- DMA1
- DMA2
ADC
ADC,全称:Analog-to-Digital Converter,指模拟/数字转换器
STM32F411配载一个12 位 ADC, 是逐次趋近型模数转换器。它具有多达 19 个复用通道,可测量来自 16 个外部 源、两个内部源和 VBAT 通道的信号。这些通道的 A/D 转换可在单次、连续、扫描或不连续
采样模式下进行。ADC 的结果存储在一个左对齐或右对齐的 16 位数据寄存器中。
重要知识
- 转换顺序:ADC上的转换通过到被分为两组,规则组和注入组,注入组的优先级大于规则组,我们通常使用规则组。每个组都有一个转换顺序,该序列可按任意顺序在任意通道上完成。
- 单次转换:ADC在转换外成一次转换后便停止。
- 连续转换:完成一次转换后自动进行下一次转换
- 扫描模式:ADC会自动完成所有通道的转换
- 不连续采样模式(i间断模式):控制每次转换一组中的几个通道(或者说是选择通道中的几个进行一次转化,)
- 数据对齐:ADC采集的12为数据在16位寄存器中的数据存放方式。
- 采样时间:各通道的采样时间 = 采样时间 + 12时钟周期,其中采样时间,可以自行配置,采样时间越长,采集的电压越精确。
- 时钟频率 : 由APB2总线时钟分频得到
- 触发源:可以由软件触发或者外部事件触发
不同模式组合的作用
为什么要是用DMA + ADC?
- 在我们开启多个通道对多个模拟量进行采集时,单片机会将每个通道采集的数据都存放到ADC_DR寄存器内,如果我们处理不当,就会导致下一次采集的数据,覆盖上一次采集的数据,导致数据丢失。
- 这个时候DMA就很重要了,我们可以配置软件,使得ADC每采集完一次数据,就将其通过DMA搬运到指定的存储单元,这样就不会由数据丢失,并且可以大大的减少CPU的工作量
DMA & ADC (实战篇)
任务要求
- 通过单片机采集PB1和PB0的电压
- 使用DMA传输采集的数据
- 通过串口助手打印采集信息
原理图
- 通过观察原理图,我们知道PB1是连接到一个电位器,那我采集的数据应该是0 ~ 2 ^12 - 1 (4095)
- 而PB0则是通过一个上拉电阻连接到VCC,所以采集的数据应该是4095
CubeMX配置
以上就是有关ADC和DMA的配置,其他部分这里不做介绍
代码实现
main.c
uint16_t ADC_Buffer[2]; //定义ADC数据存放数组```
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t i;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC_Buffer, 2); //开启ADC转换,并开始DMA传输
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// printf("Hello");
for(i=0;i<2;i++)
{
// printf("ADC_Buffer[%d] = %d ",i,hadc1.Instance->DR);
printf("DMA_Buffer[%d] = %d ",i,ADC_Buffer[i]);
}
printf("\r\n");
// HAL_ADC_Start_DMA(&hadc1,ADC_Buffer,1);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
实验现象
解释一下原理:
- 我们通过函数 HAL_ADC_Start_DMA 开启ADC转换,ADC每转换一个通道,就会发起DMA请求,DMA将ADC_DR寄存器的数据搬运到ADC_Buffer 数组,由于开启了扫描模式,在完成转换后ADC进行下一通道的转换,再次发起DMA请求,重复上述步骤
- 所有的通道都转换完成,由于开启了持续转换,又会重复进行步骤1。自此完成ADC自动转换,并且DMA自动搬运转换数据。
- 通过串口打印对应部分,即可完成本实验。