DMA简介
DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据, 但是不需要占用CPU,即在传输数据的时候,CPU可以干其他的事情,好像是多线程一样。 数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是SRAM或者是FLASH。 DMA控制器包含了DMA1和DMA2,其中DMA1有7个通道,DMA2有5个通道,这里的通道可以理解为传输数据的一种管道。要注意的是DMA2只存在于大容量的单片机中。
DMA_InitTypeDef初始化结构体
typedef struct {
uint32_t Direction; //传输方向
uint32_t PeriphInc; //外设递增
uint32_t MemInc; //存储器递增
uint32_t PeriphDataAlignment; //外设数据宽度
uint32_t MemDataAlignment; //存储器数据宽度
uint32_t Mode; //模式选择
uint32_t Priority; //优先级
} DMA_InitTypeDef;
储存器到储存器模式
DMA宏定义及相关变量定义
/* 相关宏定义,使用存储器到存储器传输必须使用DMA2 */
DMA_HandleTypeDef DMA_Handle;
#define DMA_STREAM DMA1_Channel6
#define DMA_CHANNEL DMA_CHANNEL_0 //存储器到存储器传输通道没有硬性规定,可以随意选择。
#define DMA_STREAM_CLOCK() __DMA1_CLK_ENABLE()
#define BUFFER_SIZE 32
/* 定义aSRC_Const_Buffer数组作为DMA传输数据源
const关键字将aSRC_Const_Buffer数组变量定义为常量类型 */
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
0x41424344,0x44564748,0x494A4B4C,0x4D4E4F50,
0x51525345,0x55565758,0x595A5B5C,0x5D5E5F60,
0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80
};
/* 定义DMA传输目标存储器 */
uint32_t aDST_Buffer[BUFFER_SIZE];
aSRC_Const_Buffer[BUFFER_SIZE]定义用来存放源数据,并且使用了const关键字修饰,即常量类型,使得变量是存储在内部flash空间上
DMA数据配置
static void DMA_Config(void)
{
HAL_StatusTypeDef DMA_status = HAL_ERROR;
DMA_STREAM_CLOCK();
//数据流选择
DMA_Handle.Instance=DMA_STREAM;
//存储器到外设HAL_DMA_Init(&DMA_Handle);
DMA_Handle.Init.Direction=DMA_MEMORY_TO_MEMORY;
//外设非增量模式/* Associate the DMA handle */
DMA_Handle.Init.PeriphInc=DMA_PINC_ENABLE;
//存储器增量模式__HAL_LINKDMA(&UartHandle, hdmatx,DMA_Handle);
DMA_Handle.Init.MemInc=DMA_MINC_ENABLE;
//外设数据长度:8位
DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_WORD;
//存储器数据长度:8位
DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_WORD;
//外设普通模式,存储器到存储器模式通道选择没有具体规定,只能使用一次传输模式不能循环传输,最后我调用HAL_DMA_Init函数完成DMA数据流的初始化配置
DMA_Handle.Init.Mode=DMA_NORMAL;
//中等优先级
DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM;
/* 完成DMA数据流参数配置 */
HAL_DMA_Init(&DMA_Handle);
//HAL_DMA_Start函数用于启动DMA数据流传输,源地址和目标地址使用之前定义的数组首地址,返回DMA传输状态。
DMA_status = HAL_DMA_Start(&DMA_Handle,(uint32_t)aSRC_Const_Buffer,
(uint32_t)aDST_Buffer,BUFFER_SIZE);
/* 判断DMA状态 */
if (DMA_status != HAL_OK) {
/* DMA出错就让程序运行下面循环:RGB彩色灯闪烁 */
while (1) {
LED_RED;
Delay(0xFFFFFF);
LED_RGBOFF;
Delay(0xFFFFFF);
}
}
}
存储器数据对比
uint8_t Buffercmp(const uint32_t* pBuffer,
uint32_t* pBuffer1, uint16_t BufferLength)
{
/* 数据长度递减 */
while (BufferLength--) {
/* 判断两个数据源是否对应相等 */
if (*pBuffer != *pBuffer1) {
/* 对应数据源不相等马上退出函数,并返回0 */
return 0;
}
/* 递增两个数据源的地址指针 */
pBuffer++;
pBuffer1++;
}
/* 完成判断并且对应数据相对 */
return 1;
}
储存器到外设模式
USART和DMA相关宏定义
//串口波特率
#define DEBUG_USART_BAUDRATE 115200
//引脚定义
/*******************************************************/
#define DEBUG_USART USART1
#define DEBUG_USART_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE();
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define DEBUG_USART_RX_PIN GPIO_PIN_10
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define DEBUG_USART_TX_PIN GPIO_PIN_9
#define DEBUG_USART_IRQHandler USART1_IRQHandler
#define DEBUG_USART_IRQ USART1_IRQn
/************************************************************/
//DMA
#define SENDBUFF_SIZE 1000//发送的数据量
#define DEBUG_USART_DMA_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE();
#define DEBUG_USART_DMA_STREAM DMA1_Channel4
USART GPIO 配置
void Debug_USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
DEBUG_USART_RX_GPIO_CLK_ENABLE();
DEBUG_USART_TX_GPIO_CLK_ENABLE();
/* 使能 UART 时钟 */
DEBUG_USART_CLK_ENABLE();
/* 配置Tx引脚为复用功能 */
GPIO_InitStruct.Pin = DEBUG_USART_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStruct);
/* 配置Rx引脚为复用功能 */
GPIO_InitStruct.Pin = DEBUG_USART_RX_PIN;
GPIO_InitStruct.Mode=GPIO_MODE_AF_INPUT; //模式要设置为复用输入模式!
HAL_GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStruct);
UartHandle.Instance = DEBUG_USART;
UartHandle.Init.BaudRate = DEBUG_USART_BAUDRATE;
UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&UartHandle);
}
///重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口DEBUG_USART */
HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, 1000);
return (ch);
}
///重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
int ch;
HAL_UART_Receive(&UartHandle, (uint8_t *)&ch, 1, 1000);
return (ch);
}
USART1 发送请求DMA设置
void USART_DMA_Config(void)
{
DEBUG_USART_DMA_CLK_ENABLE();
DMA_Handle.Instance=DEBUG_USART_DMA_STREAM; //数据流选择
DMA_Handle.Init.Direction=DMA_MEMORY_TO_PERIPH; //存储器到外设HAL_DMA_Init(&DMA_Handle);
DMA_Handle.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式/* Associate the DMA handle */
DMA_Handle.Init.MemInc=DMA_MINC_ENABLE; //存储器增量模式__HAL_LINKDMA(&UartHandle, hdmatx, DMA_Handle);
DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; //外设数据长度:8位
DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //存储器数据长度:8位
DMA_Handle.Init.Mode=DMA_NORMAL; //外设普通模式
DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级
HAL_DMA_Init(&DMA_Handle);
/* 关联 DMA 句柄 */
__HAL_LINKDMA(&UartHandle, hdmatx, DMA_Handle);
}
USART1 向 DMA发出TX请求
HAL_UART_Transmit_DMA(&UartHandle, (uint8_t *)SendBuff ,
SENDBUFF_SIZE);
HAL_UART_Transmit_DMA函数用于启动USART的DMA传输。只需要指定源数据地址及长度,运行该函数后USART的DMA发送传输就开始了,根据配置它会通过USART循环发送数据。
Comments | NOTHING