目录
LPUART_DRV_InstallRxCallback串口接收回调函数
LPUART_DRV_SetRxBuffer设置数据存放函数
LPUART_DRV_GetReceiveStatus获取LPUART状态函数函数
LPUART_DRV_SendDataBlocking发送阻塞函数
S32DSLPUART
Usart基本概念
S32K144芯片的Usart串口,是低功耗串口,英文单词LPUART0然后是异步通信没有时钟线,然后Usart的区别是带有时钟线,收发双方使用同一根时钟线连接是同步通信,同时S32K144的特别在于芯片进入STOP或者VLPS模式之后,外设LPUART依旧可以工作。
同时LPUART在芯片上有三个分别是LPUART0、LPUART1、LPUART2芯片内置了三个串口模块。
串口初始化流程
LPUART_DRV_Init->LPUART_DRV_InstallCallback->LPUART_DRV_ReceiveData
串口初始化外设 串口回调函数 开启串口数据接受
S32DS配置LPUART
S32DS添加LPUART库函数
首先在S32DS的组件库中添加LPUART库。

这里添加完成之后,在双击对应组件,在组件中配置LPUART的参数具体如下图所示。

然后这个是组件库中的外设实际参数,这里能够选择芯片中LPUART的串口几,然后选择外设的传输方式是中断还是DMA方式,以及配置串口的波特率,同时能够看到串口的实际波特率,具体见下图。

在图形化配置完成之后,这里通过S32DS生成一下代码,具体见下图。

S32DS引脚映射LPUART
这里添加LPUART然后配置基本LPUART基本参数之后,需要打开Pinmux映射一下引脚用来使用LPUART功能,具体见下图。

然后对LPUART1进行引脚映射,选择对应的引脚进行使用。

S32DS串口相关函数
LPUART_DRV_Init初始化函数
这里在LPUART的库中找到初始化函数,拖拽到代码中进行初始化,这里初始化代码中,参数已经根据当前配置自动生成了,具体图片如下所示。

这个是初始化函数,这里填写的参数是生成代码生成的,具体见下函数原型。
cpp
status_t LPUART_DRV_Init(uint32_t instance, lpuart_state_t * lpuartStatePtr,
const lpuart_user_config_t * lpuartUserConfig)


LPUART_DRV_InstallRxCallback串口接收回调函数
这里函数里面有个RX是代表接收的意思,然后这个函数的作用是,LPUART是配套中断进行使用的,在中断函数里面,调用LPUART_DRV_InstallRxCallback这个接收回调函数用来完成数据帧的读写等操作,同样的还有一个TX发送使用的函数,具体函数见下图。

然后再函数参数中,对应的芯片外设参数是函数自动填写进去的,但是回调函数是仍然需要自己去编写函数原型的,具体函数原型解析如下。
cpp
uart_callback_t LPUART_DRV_InstallRxCallback(uint32_t instance,
uart_callback_t function,
void * callbackParam)
{
DEV_ASSERT(instance < LPUART_INSTANCE_COUNT);
lpuart_state_t * lpuartState = (lpuart_state_t *)s_lpuartStatePtr[instance];
uart_callback_t currentCallback = lpuartState->rxCallback;
lpuartState->rxCallback = function;
lpuartState->rxCallbackParam = callbackParam;
return currentCallback;
}
然后这里回调函数的参数类型如下所示。
cpp
typedef void (*uart_callback_t)(void *driverState, uart_event_t event, void *userData);
所以编写的入参,类型也必须跟函数声明里面的保持一致。
cpp
void Serial_Rx(void *driverState, uart_event_t event, void *userData)
{
(void)driverState;
(void)userData;
}
然后再代码中因为,只有event参数有使用到,有两个参数没有进行使用,但是如果没有使用编译器会提示警告,具体如下所示。
warning: unused parameter 'driverState' [-Wunused-parameter]
warning: unused parameter 'userData' [-Wunused-parameter]
然后这里为了消除警告,通过使用强转的方式,将参数转换类型,然后不接收这个数据,但是在这个过程中又使用了数据,从而消除编译器发出的警告。
cpp
(void)driverState;
(void)userData;
然后回调函数的原型如下所示,在原型中通过判断event的状态查看是否接受数据,然后通过限制BUFF_SIZE的大小保留最后一位,用来接受\0字符串内存结束标识符号,同时在判断到没有到'\n'的时候,继续接受数据将数据继续保存在buff[]中具体函数原型如下。
cpp
void Serial_Rx(void *driverState, uart_event_t event, void *userData)
{
(void)driverState;
(void)userData;
if(event == UART_EVENT_RX_FULL)
{
if(buff[idx]!='\n' && idx<BUFF_SIZE-2)
{
idx++;
LPUART_DRV_SetRxBuffer(INST_LPUART1,&buff[idx],1);
}
}
}
如果使用 BUFF_SIZE - 1(即 254)会发生什么?
我们来推演边界情况(假设一直没有收到 \n):
| 接收轮次 | 接收前的 idx |
数据存入位置 | 判断 idx < 254 |
判断后操作 |
|---|---|---|---|---|
| 第 253 轮 | 252 | buff[252] |
252 < 254 ✅ | idx++ 变为 253,设置下一次收到数据存入 buff[253] |
| 第 254 轮 | 253 | buff[253] |
253 < 254 ✅ | idx++ 变为 254,设置下一次收到数据存入 buff[254] |
| 第 255 轮 | 254 | buff[254] |
254 < 254 ❌ | 不递增 ,也不设置下一次接收地址 |
结果:
-
最后一轮(第 255 次)数据被存入了
buff[254],这是数组的最后一个合法位置。 -
此时
idx停在了 254,且接收地址没有被重新设置(意味着后续数据不会再进缓冲区)。
问题出在哪里?
当你想把接收到的数据当作 C 语言字符串处理时,通常需要追加一个空字符 '\0'。此时你会这样写:
c
buff[idx] = '\0'; // 如果 idx = 254,这一行会写入 buff[254](覆盖掉最后一字节数据)
// 或者
buff[idx + 1] = '\0'; // 如果 idx = 254,这一行会写入 buff[255] —— 数组越界,程序崩溃!
使用 BUFF_SIZE - 2 是如何规避的?
我们再次推演(条件改为 idx < 253):
| 接收轮次 | 接收前的 idx |
数据存入位置 | 判断 idx < 253 |
判断后操作 |
|---|---|---|---|---|
| 第 253 轮 | 252 | buff[252] |
252 < 253 ✅ | idx++ 变为 253,设置下一次存入 buff[253] |
| 第 254 轮 | 253 | buff[253] |
253 < 253 ❌ | 不递增,不设置 |
结果:
-
数据最多存到
buff[253]。 -
idx最终停留在 253。 -
此时数组最后一个字节
buff[254]从头到尾没有被使用过,是完全干净的。
LPUART_DRV_SetRxBuffer设置数据存放函数
该函数的作用是告诉驱动,下一次硬件收到数据时,应该把数据存放在哪里,具体截图如下。

然后具体该函数的驱动原型如下,这里三个参数分别填入对应芯片外设,将数据存放的数组,然后长度。
cpp
status_t LPUART_DRV_SetRxBuffer(uint32_t instance,
uint8_t * rxBuff,
uint32_t rxSize)
{
DEV_ASSERT(instance < LPUART_INSTANCE_COUNT);
DEV_ASSERT(rxBuff != NULL);
DEV_ASSERT(rxSize > 0U);
lpuart_state_t * lpuartState = (lpuart_state_t *)s_lpuartStatePtr[instance];
lpuartState->rxBuff = rxBuff;
lpuartState->rxSize = rxSize;
return STATUS_SUCCESS;
}
然后再调用该函数的过程中,函数会将接收到的数据存放在,函数填写参数的 uint8_t * rxBuff 中来进行保存。
LPUART_DRV_GetReceiveStatus获取LPUART状态函数函数
该函数用来返回RX的接收状态,然后根据当前状态来知道RX具体的情况,具体函数原型如下所示。
|-----------|----------------------------------------------------------------------------------|
| 数作用 | 查询 LPUART 接收状态,并可选获取剩余待接收字节数。 |
| 第二个参数 | 输出参数,返回还有多少字节没收到 。如果接收完成,值为 0。 |
| 返回值 | STATUS_SUCCESS(完成)、STATUS_BUSY(进行中)、STATUS_ERROR(出错)、STATUS_TIMEOUT(超时)。 |
cpp
status_t LPUART_DRV_GetReceiveStatus(uint32_t instance,
uint32_t * bytesRemaining)
{
DEV_ASSERT(instance < LPUART_INSTANCE_COUNT);
const lpuart_state_t * lpuartState = (lpuart_state_t *)s_lpuartStatePtr[instance];
if (bytesRemaining != NULL)
{
if (lpuartState->isRxBusy)
{
/* Fill in the bytes transferred. */
if (lpuartState->transferType == LPUART_USING_INTERRUPTS)
{
/* In interrupt-based communication, the remaining bytes are retrieved
* from the state structure
*/
*bytesRemaining = lpuartState->rxSize;
}
#if FEATURE_LPUART_HAS_DMA_ENABLE
else
{
/* In DMA-based communication, the remaining bytes are retrieved
* from the current DMA major loop count
*/
*bytesRemaining = EDMA_DRV_GetRemainingMajorIterationsCount(lpuartState->rxDMAChannel);
}
#endif
}
else
{
*bytesRemaining = 0;
}
}
return lpuartState->receiveStatus;
}
然后具体调用如下图所示。

memcpy复制数据函数
该函数的作用是将参数的数据复制到另一个函数参数中,具体函数原型跟调用如下所示。
cpp
_PTR _EXFUN(memcpy,(_PTR __restrict, const _PTR __restrict, size_t));
memcpy(rx_buff,buff,idx);
然后代码会将buff中的数据转移到rx_buff中去,最后一个参数idx的作用是代表从buff复制到rx_buff中的字节总数,也就是吧buff中的前rdx内容复制到rx_buff中去。
LPUART_DRV_SendDataBlocking发送阻塞函数
然后在代码将buff中的数据复制到rx_buff中之后,通过这个LPUART_DRV_SendDataBlocking函数将数据发送出去,具体函数原型跟具体调用如下所示。
cpp
status_t LPUART_DRV_SendDataBlocking(uint32_t instance,
const uint8_t * txBuff,
uint32_t txSize,
uint32_t timeout)
LPUART_DRV_SendDataBlocking(INST_LPUART1,rx_buff,strlen(rx_buff),500);
然后函数的参数作用,具体如下所示。
| 参数 | 类型 | 作用 | 如何填写 |
|---|---|---|---|
instance |
uint32_t |
指定硬件实例。告诉驱动用哪个 LPUART 外设发送数据。 | 填写 SDK 定义的宏,例如 INST_LPUART0、INST_LPUART1。 |
txBuff |
const uint8_t * |
数据来源指针。指向存放待发送数据的数组或内存区域。 | 传入一个 uint8_t 类型数组的地址。如果是字符串常量,需要强转:(const uint8_t*)"Hello"。 |
txSize |
uint32_t |
发送长度 。从 txBuff 开始连续发送多少个字节。 |
必须小于等于实际缓冲区的有效数据长度。常用 strlen((char*)buff) 发送字符串(不含 \0)。 |
timeout |
uint32_t |
超时阈值(单位:毫秒)。若在此时间内无法将全部数据写入硬件,函数放弃等待并返回错误。 | 典型值:100~1000。若填 0,则尝试发送一次,若硬件忙则立即返回 STATUS_TIMEOUT。 |