概 述
在实际的串口通信开发中,“数据长度不固定”几乎是一个不可回避的场景。例如,上位机发送的AT指令、智能传感器输出的数据帧,往往根据业务逻辑动态变化。这类不定长数据接收如果仍然采用传统的定长接收方式(例如每次固定接收16字节),不仅效率低下,还容易导致数据截断或冗余处理问题。因此,如何准确识别一帧数据的结束位置,成为UART通信设计中的关键环节。
目前,NXP SDK提供的示例大多基于LPUART的定长接收模式,在实际工程中适用性有限。本文以NXP MCXA156 MCU为基础,结合实际应用场景,介绍一种简单且高效的UART不定长接收实现方案,帮助你轻松应对复杂的数据接收需求。
目前SDK中的例子都为lpuart定长接收。
本文基于NXP MCXA156 MCU,介绍一种常用的不定长接收实现方法:
硬件环境:
开发板:FRDM-MCXA156
软件环境:
IDE:MCUXpresso IDE v24.12.0
SDK:SDK Builder | MCUXpresso SDK Builder (nxp.com)
基础工程: frdmmcxa156_lpuart_edma_transfer
一、设计思路
本方案的设计目标是:支持UART不定长接收,同时尽量减少CPU负担。整体思路如下:
a. DMA自动接收
使用eDMA将UART接收数据直接搬运到内存缓冲区,避免逐字节触发中断,提高效率。
b. 空闲中断识别帧结束
当一帧数据接收完成后,UART硬件会检测到总线空闲并触发Idle Line中断。该信号可作为数据帧结束的标志。
c. 计算接收长度
在中断中查询eDMA的剩余传输字节数,从而计算实际接收的数据长度。
d. 数据交付处理
将接收到的数据交给应用层进行解析、存储或回显。
e. 重新启动接收
在处理完成后,重新启动eDMA接收,为下一帧数据做好准备。
二、系统流程图

三、使用限制与注意事项
需要注意的是,本方案基于UART Idle Line中断判断一帧数据结束,在实际应用中需满足以下使用条件和约束:
a. Idle 时间与波特率相关
UART的Idle判定时间通常约为1个字符时间(Character Time),该时间随波特率变化而变化。例如在115200bps下,约为86μs。
因此,Idle中断并不等价于严格的帧结束标志,仅适用于帧与帧之间存在明显空闲间隔的场景。
b. 需要保证帧间最小间隔
应用层需保证相邻数据帧之间存在足够的空闲间隔,例如:
Frame1 → Idle → Frame2
若帧间隔过小,可能导致:
Idle 中断未能及时触发或处理
DMA 仍处于当前传输状态
下一帧数据已开始写入缓冲区
从而引发数据粘包或帧截断问题。
c. 受系统处理能力限制
该方案依赖 “Idle 中断 + DMA重启” 机制,因此系统需满足一定实时性要求:
UART 波特率不能过高
CPU能够及时响应Idle中断
DMA重启时间小于帧间间隔
否则可能导致数据丢失或帧边界识别错误。
四、实现方法
4.1 数据处理函数
void ProcessReceivedData(uint8_t *data, size_t length)
{
//
回显数据
LPUART_WriteBlocking(DEMO_LPUART, data, length);
PRINTF("length=%d.
", length);
}
4.2 UART 中断回调函数
void DEMO_LPUART_IRQHandler(void)
{
uint32_t flags = LPUART_GetStatusFlags(DEMO_LPUART);
if (flags & kLPUART_IdleLineFlag)
{
// 清除空闲中断标志
LPUART_ClearStatusFlags(DEMO_LPUART, kLPUART_IdleLineFlag);
if (g_lpuartEdmaHandle.rxState == kLPUART_RxBusy)
{
// 计算已接收的数据长度
size_t remaining = EDMA_GetRemainingMajorLoopCount(
g_lpuartEdmaHandle.rxEdmaHandle->base,
g_lpuartEdmaHandle.rxEdmaHandle->channel);
LPUART_TransferAbortReceiveEDMA(DEMO_LPUART, &g_lpuartEdmaHandle);
size_t receivedLength = RX_DMA_BUFFER_SIZE - remaining;
// 数据交给上层处理
ProcessReceivedData(g_rxBuffer, receivedLength);
// 重启 DMA 接收
lpuart_transfer_t receiveXfer;
receiveXfer.data = g_rxBuffer;
receiveXfer.dataSize = RX_DMA_BUFFER_SIZE;
LPUART_ReceiveEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, &receiveXfer);
}
}
}
4.3 主函数初始化
int main(void)
{
BOARD_InitHardware();
// UART 初始化
// eDMA 初始化和绑定
// ... 省略配置 ...
// 开启空闲中断
LPUART_EnableInterrupts(DEMO_LPUART, kLPUART_IdleLineInterruptEnable);
EnableIRQ(DEMO_LPUART_IRQn);
// 启动首次接收
lpuart_transfer_t receiveXfer;
receiveXfer.data = g_rxBuffer;
receiveXfer.dataSize = RX_DMA_BUFFER_SIZE;
LPUART_ReceiveEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, &receiveXfer);
while (1)
{
// 主循环空转
}
}
五、测试结果
在实验中,MCXA156 LPUART被配置为115200bps,接收来自上位机的变长数据。测试结果如下:
当上位机发送14字节数据时,系统正确接收到长度为14的数据,并回显。

五、结语
本文基于MCXA156 MCU,利用UART Idle Line中断 + eDMA实现了不定长串口接收。该方案具有以下优点:
灵活性:支持任意长度数据帧,无需固定长度缓冲区;
高效性:DMA自动搬运数据,CPU仅在帧结束时介入;
可靠性:利用UART硬件的Idle Line信号,避免了基于定时器的软件超时判定。
在实际应用中,该方法尤其适用于变长协议通信、AT 命令解析、传感器数据采集以及工业协议(如 Modbus)等典型场景。
通过这一设计,可以让系统在复杂通信环境下依然保持良好的响应性能与数据完整性,是嵌入式串口通信中的一种值得推广的工程实践方案。
TIC: Harry Zhang
END
全部0条评论
快来发表一下你的评论吧 !