利用DMA接收串口数据,完全不需要MCU去轮询串口状态或利用串口的中断去搬运数据,简直爽得不得了
但是,DMA搬运过程中,只能设定一个固定的长度,等到DMA传输到一半或传输完成时,才能有中断产生,或者轮询DMA搬运长度,这不是增加了MCU的负荷嘛。
熟悉AC78xx 串口的小伙伴应该知道,AC78xx的串口有个IDLE中断,简单的说,就是在串口开始接收后,Rxd引脚空闲1byte长度,即产生一个IDLE中断。为了让大家有个直观的认识,来张图吧。
从图中可以看出,RXD空闲1个字节的时候,立即产生IDLE中断。在IDLE的中断中,我们可以查询DMA接收的长度,可以将对搬运到缓存内的数据进行处理。
要执行串口IDLE的中断,有以下步骤:
1. NVIC模块中是能串口中断,NVIC_EnableIRQ(UARTX_IRQn);
2. 串口模块是能IDLE中断和多机通信, UART_SetIdleEn(UART1, TRUE); UART_SetMulComEn(UART1, TRUE);
注意:
3. 给串口一个回调函数,在回掉中处理数据,我们在这里就将接收到的内容通过DMA发送出去。
UART_SetEventCallback(UART1, UART_IntHandler);
int32_t UART_IntHandler(uint8_t port, uint32_t lsr0, uint32_t lsr1)
{
int32_t DMARcvLen = 0;
if (lsr1 & LSR1_IDLE) { // IDLE
DMARcvLen = UART_GetDMAReceiveCount(port);
if ((DMARcvLen>0) && (DMARcvLen < UART_DATA_LEN_SEL)) {
GPIOD_OUT(11) = 1;
memcpy(g_sendDataBuf, g_recvedDataBuf, DMARcvLen);
memset(g_recvedDataBuf, 0, sizeof(g_recvedDataBuf));
UART_StartDMAReceive(1, 8, (uint32_t)g_recvedDataBuf, UART_DATA_LEN_SEL, UART_RxDMACallback);//¿ªÆôDMA,×¼±¸½ÓÊÕÊý¾Ý
UART_StartDMATransmit(1, 8, (uint32_t)g_sendDataBuf, DMARcvLen, UART_TxDMACallback);//ÅäÖÃDMA´«ÊäÊý¾Ý
GPIOD_OUT(11) = 0;
}
}
}
我们使用串口助手来演示一下效果。
可以看到,利用IDLE中断接收不定长度的数据,效果还是很不错的。
不过要注意的是,DMA接收缓存需要开得足够大,还有在IDLE的中断内,快速读出缓存并开始下一次的DMA接收,降低数据丢失的概率。
DMA接收函数,被我改动了一点点,后面再跟大家聊聊,为什么会改动DMA的配置吧。
UART_Sample.rar
(471.4 KB)
|