在线时间234 小时
UID3301905
注册时间2017-1-8
NXP金币104
TA的每日心情 | 开心 2018-4-20 15:04 |
---|
签到天数: 8 天 [LV.3]偶尔看看II
金牌会员
- 积分
- 2945
- 最后登录
- 2023-7-24
|
【飞凌RT1052】串口空闲中断+接收DMA实现不定长接收详解
因为后面的Demo中需要用到8266做设备控制,因此调通板上LPUART2串口的不定长接收是很有必要的。
在单片机中,实现串口不定长接收有两种方式,第一是循环队列+结束标志+溢出标志位,当一次接收的数据超出队列结构长度即队列满时,触发溢出标志位;当上一次的数据处理完毕后,回收内存空间,以用于下次队列周期的数据存放;判断一次接收完成的标志是结束标志字节,通常是0x0d或0x0a或0x0d+0x0a。这种方法的优势是能最大限度合理利用内存空间,缺点是必须要加结束标志位,所以还有第二种方法,即串口空闲中断+串口接收DMA通道实现的不定长接收,这种方法代码好写,易于理解,适合新手学习和串口项目的快速搭建。空闲中断的原理是当串口在一定的、很短的单位时间以后没有接收到新的数据,就触发中断,通常用于下位机串口发送一帧的场合,空闲中断能很好的判断一次或一帧串口数据的接收,但是空闲中断本身是不可以实现不定长接收的,要实现不定长接收,也只能通过结束标志位或DMA的方式实现。在空闲中断中,接收DMA的作用并不是像单片机新手教程写的那样,是为了减轻CPU负担,而是为了搬运串口外设中长度未知的数据,这帧接收到的数据到底有多长呢,就是在空闲中断触发之后结束DMA接收,用DMA通道总长度减去DMA末端未使用的长度(LPUARTx->TCD寄存器值):
int count;
LPUART_TransferGetReceiveCountEDMA(LPUART1, &g_lpuartEdmaHandle,&count);
LPUART_TransferAbortReceiveEDMA(LPUART1, &g_lpuartEdmaHandle);
硬件连接,为了测试板上十针LPUART2接口,我接了一个CH340模块,注意TX RX要反接:
然后就是编写程序了,首先要启用串口2的空闲中断和接收DMA(EDMA外设),并且数组还必须是非缓存优化的,不然数组的内容不会刷新:
AT_NONCACHEABLE_SECTION_INIT(uint8_t uart2_rx_buffer[255]) = {0};
void LPUART2_Init(int baud)
{
LPUART_GetDefaultConfig(&config);
config.baudRate_Bps=115200;
config.enableTx = true;
config.enableRx = true;
LPUART_Init(LPUART2, &config,BOARD_DebugConsoleSrcFreq());
LPUART_EnableInterrupts(LPUART2,kLPUART_IdleLineInterruptEnable);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_02_LPUART2_TX,0);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_03_LPUART2_RX,0);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_02_LPUART2_TX,0x10B0u);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_03_LPUART2_RX,0x10B0u);
EnableIRQ(LPUART2_IRQn);
DMAMUX_Init(DMAMUX);
DMAMUX_SetSource(DMAMUX,1,kDmaRequestMuxLPUART2Rx);
DMAMUX_EnableChannel(DMAMUX,1);
EDMA_GetDefaultConfig(&econfig);
EDMA_Init(DMA0,&econfig);
EDMA_CreateHandle(&g_lpuartRxEdmaHandle,DMA0,1);
LPUART_TransferCreateHandleEDMA(LPUART2,&g_lpuartEdmaHandle,NULL,NULL,NULL,&g_lpuartRxEdmaHandle);
receiveXfer.data=uart2_rx_buffer;
receiveXfer.dataSize=255;
LPUART_ReceiveEDMA(LPUART2,&g_lpuartEdmaHandle,&receiveXfer);
}
在中断服务函数里面获得数据长度以及结束DMA传输,注意这个长度必须为全局变量,因为别的代码要用到:
int count;
void LPUART2_IRQHandler()
{
if((kLPUART_IdleLineFlag)&LPUART_GetStatusFlags(LPUART2))
{
lpuart2_dma_flag=1;
LPUART_TransferGetReceiveCountEDMA(LPUART2,&g_lpuartEdmaHandle,(uint32_t*)&count);
printf("%d\n",count);
LPUART2->STAT |= LPUART_STAT_IDLE_MASK;
//清除空闲中断
LPUART_TransferAbortReceiveEDMA(LPUART2,&g_lpuartEdmaHandle);
}
}
在主循环中,如果检测到串口空闲中断触发了,就在中断响应结束之后重新开启DMA传输,并且数组每次都要清空,不然数组的内容同样不会刷新:
while(1)
{
...
LPUART_DMA_Get();
...
}
void LPUART_DMA_Get()
{
int i;
if(lpuart2_dma_flag)
{
lpuart2_dma_flag=0;
memset(uart2_rx_buffer,0,255);
LPUART_ReceiveEDMA(LPUART2,&g_lpuartEdmaHandle,&receiveXfer);
printf("%s\n",uart2_rx_buffer);
}
}
观察效果,一个是LPUART1负责printf打印信息,另一个是LPUART2负责接收:
最后提醒一下,由于串口接收处理涉及到DMA接收,需要占用主循环的时间片,因此不能与DHT11 RX8010等传感器的检测工作同时开启,需要用开关切换串口接收,当串口接收开启的时候,DHT11和RX8010的检测工作暂停。
|
|