基于MCXA156的UART不定长接收实战: 轻松实现智能帧结束判定

描述

概  述

    在实际的串口通信开发中,“数据长度不固定”几乎是一个不可回避的场景。例如,上位机发送的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

三、使用限制与注意事项

需要注意的是,本方案基于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的数据,并回显。

uart

五、结语

本文基于MCXA156 MCU,利用UART Idle Line中断 + eDMA实现了不定长串口接收。该方案具有以下优点:

灵活性:支持任意长度数据帧,无需固定长度缓冲区;

高效性:DMA自动搬运数据,CPU仅在帧结束时介入;

可靠性:利用UART硬件的Idle Line信号,避免了基于定时器的软件超时判定。

在实际应用中,该方法尤其适用于变长协议通信、AT 命令解析、传感器数据采集以及工业协议(如 Modbus等典型场景。

通过这一设计,可以让系统在复杂通信环境下依然保持良好的响应性能与数据完整性,是嵌入式串口通信中的一种值得推广的工程实践方案。

TIC: Harry Zhang      

END

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分