一、原理
DMA(Direct Memory Access)直接存储器存取。可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源。多个独立可配置的通道,每个通道都支持软件触发和特定的硬件触发。
1、DMA框图
(1)DMA总线:连接在DMA和总线矩阵之间,用于访问各个存储器。
(2)DMA通道:DMA内部多个通道,可以独立的数据转运,但每次只有一个一个通道可以使用DMA。
(3)仲裁器:用于调度各个通道,防止发生冲突。
(4)AHB从设备:用于配置DMA参数
(5)DMA请求:连接DMA与外设之间,用于硬件触发DMA的数据转运
2、DMA基本结构图
其中,由M2M给1/0来确定是软件触发/硬件触发。软件触发通常用于存储器与存储器中间的数据转运,硬件触发常用于外设与存储器之间的数据转运。
传输计数器内部的值是DMA转运的次数,在DMA每转运一次就自动减一,直到为0时,DMA停止。DMA转运可以设置自动重装器在传输计数器为0时再装入新值,这样DMA就会不停的传输,这是DMA的连续模式。如果没有设置自动重装器,要手动改写时,一定要先关闭开关控制,然后去改写传输计数器的值,再打开开关才可以。
3、DMA内部框图
4、DMA数据宽度与对其
第一列是源端宽度、第二列是目标宽度、第三列是传输数目、第四列是源段的地址/数据、第五列是传输操作、第六列是目标的地址/数据。
当源端数据宽度 > 目标宽度时,只有低位传入,高位被砍掉。
当源端数据宽度 < 目标宽度时,数据全部传入,高位补0。
二、代码实现
1、参数配置
先开了个定时器,让他100ms更新一次ADC转换事件
然后配置ADC转换,配置3个通道并使能扫描模式,使能DMA功能,由定时器3触发。
最后配置3个DMA。他们三个分3个通道。其中接收配置为循环模式,发送和ADC是普通模式。
2、程序设计
在main函数里定义一个接收ADC采样数据的的数组和接收数组。
分别打开ADC的DMA转换、定时器、串口接收DMA功能。
回调函数.c如下
#include "funtion.h"
uint8_t u_buf[256];
#define printF(...) HAL_UART_Transmit_DMA(&hlpuart1,(uint8_t *)u_buf,sprintf((char*)u_buf,__VA_ARGS__))
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
for(int i = 0;i < 27;)
{
ad1 += adc_value[i++];
ad2 += adc_value[i++];
ad3 += adc_value[i++];
}
ad1 /= 10;
ad2 /= 10;
ad3 /= 10;
HAL_ADC_Stop(&hadc1);
HAL_ADC_Start_DMA(&hadc1, adc_value, 30);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &hlpuart1)
{
if(Rx_Data[0] == '0')
{
printF("---------------ADC DMA Example---------------\r\n");
}
else if(Rx_Data[0] == '1')
{
printF("AD1 value = %1.3fV\r\n", ad1*FACTOR_ADC);
}
else if(Rx_Data[0] == '2')
{
printF("AD2 value = %1.3fV\r\n", ad2*FACTOR_ADC);
}
else if(Rx_Data[0] == '3')
{
printF("AD3 value = %1.3fV\r\n", ad3*FACTOR_ADC);
}
}
}
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &hlpuart1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
}
回调函数.h如下
#include "main.h"
#include "adc.h"
#include "usart.h"
#include "stdio.h"
#define FACTOR_ADC 0.00080586
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
uint32_t adc_value[30];
uint32_t ad1=0, ad2=0, ad3=0;
extern unsigned char Rx_Data[2];
PUTCHAR_PROTOTYPE
{
// HAL_UART_Transmit(&hlpuart1 , (uint8_t *)&ch, 1, 0xffff);
HAL_UART_Transmit_DMA(&hlpuart1 , (uint8_t *)&ch, sizeof(&ch));
return ch;
}