一、DMA介绍
直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传 输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。当产品对于时序要求较严格时,外设使用DMA的方式能够减轻CPU负担,从而提高整体效率。本文主要介绍USART1 DMA发送配置,以stm32f10系列为例。DMA每次发送固定字节数据保证主循环每次执行DMA发送是均匀的,避免高负载等特殊情况下处理异常。
二、USART1对应的通道
三、配置流程
1)配置DMA1时钟
RCC->AHBENR |= 1 << 0; //¿ªÆôDMA1ʱÖÓ
2)dma初始化配置
DMA初始化配置如下,串口DMA发送属于存储到外设模型,配置DMA单次模式发送,且每次发送存储地址自增1,这里开启了NVIC中断向量,如果要使用DMA中断的话则需要把下面注释段DMA_CHx->CCR |= 1 << 1;和MY_NVIC_Init(3,3,DMA1_Channel4_IRQn,2);打开。
void DMA_Init(void)
{
DMA_Config(DMA1_Channel4, (u32)&USART1->DR);
}
void DMA_Config(DMA_Channel_TypeDef* DMA_CHx, u32 cpar)
{
DMA_CHx->CPAR = cpar; //cfg periph addr
DMA_CHx->CCR |= 3 << 12; //cfg channel prio 3
DMA_CHx->CCR |= 1 << 4; //cfg mem to periph
DMA_CHx->CCR &= ~(1 << 5); //cfg dma single transfer
DMA_CHx->CCR |= 1 << 7; //cfg mem transfer addr inc
//DMA_CHx->CCR |= 1 << 1; //cfg dma ie
//MY_NVIC_Init(3,3,DMA1_Channel4_IRQn,2); //cfg dma channel4 enable nvic if enable ie
}
打开后发现能够进入中断函数。这里并没有使用DMA中断方式,使用的轮询查标志。
void DMA1_Channel4_IRQHandler(void)
{
DMA1->IFCR|=1<<13;
}
3)创建dma发送任务
4)dma数据发送流程
基于前面写的博客STM32 寄存器配置笔记——USART配置中断接收乒乓缓存处理基础上加的dma流程设计。代码如下:
static u32 dma_tx_single_len = 0;
static u32 dma_tx_total_len = 0;
static u32 dma_mem_offset = 0;
static u8 dma_tx_task = 0;
static u8 *g_pdma_tx_dat = NULL;
static DMA_UART_TX_STATUS dma_tx_status = DMA_UART_CHANNEL_IDLE;
void create_dma_tx_task(u8 *pDat, u32 len)
{
if (dma_tx_task)
{
printf("dma_tx_task is running\r\n");
}
else
{
dma_tx_task = 1;
g_pdma_tx_dat = pDat;
dma_tx_total_len = len;
}
}
void data_dma_tx(void)
{
switch(dma_tx_status)
{
case DMA_UART_CHANNEL_IDLE:
{
if (dma_tx_task)
{
dma_tx_status = DMA_UART_SENDING;
dma_mem_offset = 0;
dma_tx_single_len = ((dma_tx_total_len >= DMA_UART_SINGLE_TX_LEN) ? DMA_UART_SINGLE_TX_LEN : dma_tx_total_len);
DMA_UART_TX_ENABLE(DMA1_Channel4, (u32)g_pdma_tx_dat, dma_tx_single_len);
}
}
break;
case DMA_UART_SENDING:
{
if (0 == (DMA1->ISR & (1 << 13)))
return;
DMA1->IFCR |= 1 << 13;
dma_tx_total_len -= dma_tx_single_len;
if (dma_tx_total_len)
{
dma_mem_offset += dma_tx_single_len;
dma_tx_single_len = ((dma_tx_total_len >= DMA_UART_SINGLE_TX_LEN) ? DMA_UART_SINGLE_TX_LEN : dma_tx_total_len);
DMA_UART_TX_ENABLE(DMA1_Channel4, (u32)g_pdma_tx_dat + dma_mem_offset, dma_tx_single_len);
}
else
{
dma_tx_status = DMA_UART_SENDED;
}
}
break;
case DMA_UART_SENDED:
{
dma_tx_status = DMA_UART_CHANNEL_IDLE;
dma_tx_task = 0;
}
break;
default:
break;
}
}
具体调用如下: