S32KS328上实现串口接收并判断长度的几种办法

1:问题描述

在S32DS上,使用RTD驱动层中的Lpuart_Uart模块,Lpuart_Uart 封装的几种串口接收方法,均不会返回已接收的串口数据长度;但是实际使用中,我们对已接收的串口数据长度是需要明确的;

2:RTD配置

使用串口中断前,需要先在RTD里面进行相关配置,参考如下
Lpuart_Uart配置
在这里插入图片描述

在这里插入图片描述
IntCtrl_Ip配置
在这里插入图片描述

3:读取方法

3.1 阻塞读取

// 方法1 会阻塞读取数据,数据长度判断不会被0x00影响
Lpuart_Uart_Ip_AsyncReceive(LPUART_UART_IP_INSTANCE_USING_2, prbuf, read_size);
// Wait for transfer to be completed
rx_status = LPUART_UART_IP_STATUS_BUSY;
while(LPUART_UART_IP_STATUS_BUSY == rx_status)
{
rx_status = Lpuart_Uart_Ip_GetReceiveStatus(LPUART_UART_IP_INSTANCE_USING_2, &rx_len);
};
if (rx_len >
0)
real_read_size = (read_size-rx_len);

实测使用阻塞读取的时候,基本不丢包;

阻塞读取需要单独开一个线程处理

3.2 超时读取

推荐使用 3.3.2 的中断方式读取数据

/**
* @brief: 从最后一个byte开始判断第一个非零数据, 并返回非0数据长度
* @param: check_buff--待判断的数据指针
* @param: buff_size--数据长度
* @return: >0 表示非0数据长度,<=0 参考 "enum FuncResultTag"
*/
static int check_none_zero_length(uint8 *check_buff, uint32 buff_size)
{
int result = kFuncResultOk;
int i = 0;
if ((check_buff == NULL) || (buff_size == 0))
return kFuncResulParamError;
else
i = buff_size - 1;
while (i >= 0)
{
if (check_buff[i] != 0)
{
return i + 1;
}
i--;
}
return result;
}
/* 方法2
1: 使用
超时接口读取数据;人为判断数据读取长度, 如果最后一个数据是 0x00 的话,则会判断出错
2: 需要灵活调整函数调用频率和timeout超时时间,以减少丢包率
*/
read_ret = Lpuart_Uart_Ip_SyncReceive(LPUART_UART_IP_INSTANCE_USING_2, prbuf, read_size, 100000u);
if ((read_ret == LPUART_UART_IP_STATUS_SUCCESS) || (read_ret == LPUART_UART_IP_STATUS_TIMEOUT))
real_read_size = check_none_zero_length(prbuf, read_size);

通过减少任务的休眠时间(增加任务调度次数) 和 延长超时时间“ * @param Timeout value in microseconds.”,能显著减少串口丢包,或者不丢包;

看阻塞读取和超时读取代码最终都是调用的“Lpuart_Uart_Ip_Getchar”函数,超时读取也只是在 timeout 时间段内等待是否有数据过来? 所以超时读取很容易造成丢包, 不推荐使用该方法;

3.3 中断读取

Lpuart_Uart_Ip_SetRxBuffer
3.3.1:利用空闲中断

理论上可以配置空闲中断,产生空闲中断说明一包数据已传输完成,此时再去读取数据;

LPUART_UART_IP_EVENT_IDLE_STATE

RTD配置了空闲中断之后,LPUART_UART_IP_EVENT_RX_FULL 测试进不去了,待处理异常;

3.3.2:将 LPUART_UART_IP_EVENT_RX_FULL 设置为1
int uart_init(char *pdev, uint8_t number)
{
// init的时候必须设置一下长度为 1,才会进入 RX_FULL 中断
Lpuart_Uart_Ip_AsyncReceive(LPUART_UART_IP_INSTANCE_USING_2, g_os_uart[eOsUartNumberSim8810].rx_buff, 1);
}
void UartCallback(const uint8 HwInstance, const Lpuart_Uart_Ip_EventType Event, const void *UserData)
{
...
else if (Event == LPUART_UART_IP_EVENT_RX_FULL)
{
// 每次只取 1个数据,填充至buff; 第一个byte已被接收,所以这里从第二个byte开始填充;读取完之后设置一下下一次的数据
g_os_uart[uart_num_os].rx_size += BYTES_PER_READ;
Lpuart_Uart_Ip_SetRxBuffer(uart_rtd_num, &g_os_uart[uart_num_os].rx_buff[g_os_uart[uart_num_os].rx_size], 1);
if (g_os_uart[uart_num_os].rx_size > OS_UART_MAX_SIZE)
{
g_os_uart[uart_num_os].err_flag = kFuncResultUarRxOverflow;
g_os_uart[uart_num_os].rx_size = 0;
}
}
}
int uart_read(uint8_t number, uint8_t *prbuf, uint16_t buf_size)
{
...
// 读取完之后必须再设置一下,确保下一帧数据的第一个byte能够正常接收
Lpuart_Uart_Ip_SetRxBuffer(LPUART_UART_IP_INSTANCE_USING_2,&g_os_uart[eOsUartNumberSim8810].rx_buff[0],1);
}

将“LPUART_UART_IP_EVENT_RX_FULL”设置为1之后,每接收一个byte,就会进一次中断;

每次只处理 一个byte数据到缓存buff里面,即可计算当前缓存buff数据的总长度;之后再由应用调用 uart_read 接口去处理数据即可;

这里仅仅只是使用了普通的buff进行数据缓存,实际使用的时候可以使用环形缓存ring_buff来处理数据

4:异常处理

如果使用上述方法之后,发现串口还是存在丢数据现象;那么可以简单通过如下方法查看发送端串口数据是否发送成功;
串口通信监控方法
如果使用条件允许的话,也可以使用示波器或者逻辑分析仪来分析数据,查看具体的数据丢失原因;

posted @ 2025-08-22 15:05  yfceshi  阅读(42)  评论(0)    收藏  举报