本文结构
- DMA简要介绍
- DMA主体思路
- DMA代码实现与分析
DMA简要介绍
- 什么是DMA?
DMA(Direct Memory Access, 直接存储器访问),允许外设与内存直接传输数据的技术,无需CPU干预。 - DMA的特点是什么?
不受CPU直接控制的"数据搬运工",说人话,如果不用DMA,你在调试串口时,上位机发送数据过来,你在接收的地方打了断点,你就接收不到完整的数据;但是,如果你用了DMA,你打了断点数据照常接收。
DMA主体思路

DMA代码的实现
c
/**
* @brief 串口dma接收完成中断处理
* @param
* @retval
*/
void uart_dmarx_done_isr(uint8_t uart_id)
{
uint16_t recv_size;
recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;
s_UartTxRxCount[uart_id*2+1] += recv_size;
fifo_write(&s_uart_dev[uart_id].rx_fifo,
(const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
s_uart_dev[uart_id].last_dmarx_size = 0;
}
/**
* @brief 串口dma接收缓存大小一半数据中断处理
* @param
* @retval
* @描述:
* 将接收的数据
*/
void uart_dmarx_half_done_isr(uint8_t uart_id)
{
uint16_t recv_total_size;
uint16_t recv_size;
if(uart_id == 0)
{
recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();
}
else if (uart_id == 1)
{
recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
}
recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
s_UartTxRxCount[uart_id*2+1] += recv_size;
fifo_write(&s_uart_dev[uart_id].rx_fifo,
(const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
s_uart_dev[uart_id].last_dmarx_size = recv_total_size;
}
/**
* @brief 串口空闲中断处理
* @param
* @retval
*/
void uart_dmarx_idle_isr(uint8_t uart_id)
{
uint16_t recv_total_size;
uint16_t recv_size;
if(uart_id == 0)
{
recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();
}
else if (uart_id == 1)
{
recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
}
recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
s_UartTxRxCount[uart_id*2+1] += recv_size;
fifo_write(&s_uart_dev[uart_id].rx_fifo,
(const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
s_uart_dev[uart_id].last_dmarx_size = recv_total_size;
}
代码解析:
- 这里有三个中断处理函数:半满中断、满中断和空闲中断处理函数,
- 半满中断和全满中断:我们设置DMA接收区域的长度128Byte,DMA的CNDTR寄存器的值为128,每接收一个字节数据,数值就减1。当该数值减少为64时,进入半满中断。当数值减少为0时,进入全满中断;
- 空闲中断:除非每帧数据刚好是半帧长度或者满帧长度,否则空闲中断处理必然有效;
案例:



初始条件下,以帧数据长度10为例,连续发送6次都不会进入半满中断,仅进入空闲中断;第7次时便会进入半满中断,同时也会进入空闲中断(图片中NDT的值不为64);第13次时便会进入全满中断,同时也会进入空闲中断(图片中NDT的值不为0而是翻板到0X7E)。