一、简介
本文主要介绍STM32如何配合USART的IDLE中断实现USART DMA接收不定长的数据。其中使用的接收缓存还是延用前面博客写的乒乓缓存。使用DMA USART接收来替代中断方式或轮询方式的接收主要是为了提高代码的运行效率,中断方式的接收,每接收一个字节便会产生一个中断,当串口有大量数据需要接收时,会造成CPU不停的被中断打断,频繁的出入栈造成整个系统不稳定的情况且也会影响到主任务里其它流程的执行时序,造成主任务里的流程滞后现象,影响其它事情的实时性。引进DMA的接收正是为了解决该问题。
二、设计思路
USART 使用DMA方式接收需要考虑到从USART传输进来的数据可能会出现不定长等现象,在通信协议上该问题属于正常现象。但DMA的传输又需要指定特定长度因此在配置DMA时,我们很难得知要配置多少接收长度,接收长度配置多了,DMA不会产生传输完成标志,配置少了又可能造成频繁触发DMA现象导致数据不好处理,出现要拼包等现象。因此这里使用STM32的特性空闲中断来判断一次DMA的传输完成。空闲是指在串口接收到一帧数据后,没有再接收到数据此时会产生一个空闲帧此时会置位IDLE位,如若配置置IDLEIE位则会进入中断。注:首次配置USART初始化开启TE位的时候也会产生空闲帧需要滤掉。
二、配置流程
1) USART 使能IDLEIE位
使能USART1的空闲中断
USART1->CR1 |= 1 << 4; // enable IDLE interrupt
2) USART 配置DMA接收初始化
DMA_USART1_Receive_Config(DMA1_Channel5, (u32)&USART1->DR);
void DMA_USART1_Receive_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 periph to mem
DMA_CHx->CCR &= ~(1 << 5); //cfg dma single transfer
DMA_CHx->CCR |= 1 << 7; //cfg mem transfer addr inc
}
3)使能USART DMA接收
DMA_UART1_RECEIVE_ENABLE(DMA1_Channel5, (u32)p_cur_Usart1_Handle, 256);
void DMA_UART1_RECEIVE_ENABLE(DMA_Channel_TypeDef* DMA_CHx, u32 cmar, u16 cndtr)
{
USART1->CR3 |= 1 << 6; //enable uart dma rx
DMA_CHx->CCR &= ~(1 << 0); //dma channel disable
DMA_CHx->CMAR = (u32)cmar; //cfg mem addr
DMA_CHx->CNDTR = cndtr; //cfg transfer len
dma_cfg_recieve_cnt = DMA_CHx->CNDTR;
DMA_CHx->CCR |= 1; //dma channel en
}
4)USART IDLE中断处理
在void USART1_IRQHandler(void)中断函数里通过获取DR寄存器清IDLE位。并置位recieve_idle 通知处理数据帧。
if (USART1->SR & (1 << 4))
{
u8 temp;
temp = USART1->DR;
recieve_idle = 1;
}
5)IDLE 的处理
主循环轮询产生IDLE的话表示当前收到一帧完整数据帧,则需要处理,处理流程:关DMA USART接收->获取DMA 传输数据量->切换接收缓存即取发送缓存->使能DMA USART接收。
void idle_process(void)
{
if (recieve_idle)
{
recieve_idle = 0;
DMA_UART1_RECEIVE_DISABLE(DMA1_Channel5);
p_cur_Usart1_Handle->len = GET_DMA_TRANSFER_CNT(DMA1_Channel5);
change_curFifo();
DMA_UART1_RECEIVE_ENABLE(DMA1_Channel5, (u32)p_cur_Usart1_Handle, 256);
}
}
6)USART DMA发送
该内容参考上一篇博客STM32 寄存器配置笔记——USART DMA发送
具体代码如下: