RT1060--串口空闲中断&EDMA接收不定长数据
串口空闲中断 & EDMA 接收不定长数据
客户有接收不定长数据的需求,但是如果采用协议适配的方法会非常繁琐。使用UART的IDLE中断+DMA的方式,即可高效的解决这一问题!
另外,如果通过协议先接收单独的单字节,串口进中断接收在数据量大的时候效率会低很多
以RT1060EVK为例来进行这一操作!
代码部分
1 | 让我仔细分析一下这个main函数的执行流程和中断调用关系... |
让我详细解释main函数的逻辑和中断调用关系:
初始化配置:
1 | // 配置UART中断 |
中断和DMA的调用流程:
接收数据流程:
- DMA持续接收数据到
g_rxBuffer
- 当检测到空闲线(一段时间没有新数据)时,触发IDLE中断;
- 在IDLE中断处理函数中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18void DEMO_LPUART_IRQHandler(void)
{
if ((kLPUART_IdleLineFlag)&stat)
{
// 1. 获取已接收的数据长度
LPUART_TransferGetReceiveCountEDMA(...);
// 2. 复制数据到发送缓冲区
memcpy(g_txBuffer, g_rxBuffer, rx_count);
// 3. 设置发送标志
reciveFrame = true;
// 4. 重新启动接收
LPUART_TransferAbortReceiveEDMA(...);
LPUART_ReceiveEDMA(...);
}
}
发送数据流程:
- main循环检测到
reciveFrame
为true时启动发送 - DMA开始发送数据
- 发送完成后触发DMA完成中断,调用
LPUART_UserCallback
- 在callback中使能TC中断:
1
2
3
4
5
6
7
8void LPUART_UserCallback(...)
{
if (kStatus_LPUART_TxIdle == status)
{
// 使能TC中断
LPUART_EnableInterrupts(DEMO_LPUART, kLPUART_TransmissionCompleteInterruptEnable);
}
} - TC中断触发后,在中断处理函数中:
1
2
3
4
5
6
7
8
9
10
11
12if((kLPUART_TransmissionCompleteFlag)&stat)
{
// 1. 禁用TC中断
LPUART_DisableInterrupts(DEMO_LPUART, kLPUART_TransmissionCompleteInterruptEnable);
// 2. 切换到接收模式
Set_RS485_Receive();
// 3. 重新启动接收
LPUART_TransferAbortReceiveEDMA(...);
LPUART_ReceiveEDMA(...);
}
主循环逻辑:
1 | while(1) |
关键点说明
- DMA用于实际的数据传输,不占用CPU
- IDLE中断用于检测一帧数据的接收完成
- TC中断用于确保最后一个字节完全发送出去
- 使用回调和中断机制实现自动的收发切换
- 主循环只负责检测标志并启动新的发送
这种设计实现了高效的非阻塞式数据收发,同时正确处理了RS485半双工通信的方向切换时序。
备注:详细的回调/中断实现的自动收发切换
1 | flowchart TD |
让我详细解释每个阶段的切换过程:
接收阶段:
1
2
3// 初始配置为接收模式
Set_RS485_Receive(); // GPIO设为低电平
LPUART_ReceiveEDMA(...); // 启动DMA接收接收完成检测:
1
2
3
4
5
6
7
8// IDLE中断处理函数中
if ((kLPUART_IdleLineFlag)&stat)
{
// 获取接收到的数据
LPUART_TransferGetReceiveCountEDMA(...);
memcpy(g_txBuffer, g_rxBuffer, rx_count);
reciveFrame = true; // 设置标志通知主循环
}切换到发送模式:
1
2
3
4
5
6
7// 主循环中
if(reciveFrame)
{
Set_RS485_Transimit(); // GPIO设为高电平
LPUART_SendEDMA(...); // 启动DMA发送
reciveFrame = false;
}发送完成处理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// DMA发送完成回调
void LPUART_UserCallback(...)
{
if (kStatus_LPUART_TxIdle == status)
{
// 使能TC中断,等待最后一个字节发送完成
LPUART_EnableInterrupts(DEMO_LPUART, kLPUART_TransmissionCompleteInterruptEnable);
}
}
// TC中断处理
if((kLPUART_TransmissionCompleteFlag)&stat)
{
LPUART_DisableInterrupts(DEMO_LPUART, kLPUART_TransmissionCompleteInterruptEnable);
Set_RS485_Receive(); // 切换回接收模式
// 重新启动接收
LPUART_TransferAbortReceiveEDMA(...);
LPUART_ReceiveEDMA(...);
}
关键设计考虑:
为什么需要TC中断?
- DMA完成不代表最后一个字节已经完全发送出去
- TC中断确保最后一个字节完全发送后才切换方向
- 避免方向切换过早导致数据截断
方向切换时序:
- 发送前:先切换方向,再开始发送
- 发送后:等待TC中断确认完全发送后,再切换回接收
自动化处理:
- 使用中断和回调机制,无需主循环干预
- 主循环只负责启动新的发送
- 其他切换过程由中断自动完成
这种设计保证了RS485半双工通信的可靠性,同时通过DMA和中断机制提高了效率。
RT1060--串口空闲中断&EDMA接收不定长数据
https://dustofstars.github.io/NXP/RT1060/rt1060-串口空闲中断-edma接收不定长数据/