|
写了一个Modbus从站,起初使用串口8,N,1的配置,所有功能正常,但是设定成带校验之后,整个程序看起来没什么问题,中断接收的数据也是正常的,但是放到缓存里面的时候数据就完全不对,比对自己的代码,唯一的改变就是从无校验改成了带校验,后来查看HAL库,发现了问题。 我自己定义了一个结构体,用来给串口用的,相关部分如下 __packed typedef struct MODBUS_SLAVE_DRV { uint8_t uartBuf;//用于串口中断接收数据用 uint8_t *pucRecBuf;//接收数据帧存放地址 uint8_t *pucTranBuf;//发送数据帧存放地址 采用中断接收的时候数据放到uartBuf这里面,HAL_UART_Receive_IT(uart, &pMbSlaveDrv_t->uartBuf, 1);开启中断接收,中断来的时候会进入UART_Receive_IT,这个函数会把你坑死,如果使用了校验,就要设定UART_WORDLENGTH_9B,一旦设定了9B,这个函数会做如下动作 if(huart->Init.WordLength == UART_WORDLENGTH_9B) { tmp = (uint16_t*) huart->pRxBuffPtr;//这个就是我之前结构体里面的uartBuf,在这里居然被扩成了uint16 if(huart->Init.Parity == UART_PARITY_NONE)//下面进行了uint16的赋值 { *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF); huart->pRxBuffPtr += 2U; } else { *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF); huart->pRxBuffPtr += 1U; } } 以上代码会将紧跟在uartBuf这个后面的内存给写掉,如果你很幸运,你的代码里面uartBuf后面的内存没有放置别的变量,恭喜你,不会有任何问题,杯具的我,后面的缓存指针直接被改写。 所以,如果你使用了HAL库,那么请使用uint16的变量来作为接收数据的缓存,但是在调用HAL_UART_Receive_IT的时候需要进行类型的转换,因为这个函数传入的类型是uint8. 我用的F1和F4的HAL库都是这样的。 |
微信公众号
手机版
我现在用的是FW_F4 V1.15.0
/* Computation of UART mask to apply to RDR register */
UART_MASK_COMPUTATION(huart);
uhMask = huart->Mask;
/* as long as data have to be received */
while(huart->RxXferCount > 0U)
{
if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
tmp = (uint16_t*) pData ;
*tmp = (uint16_t)(huart->Instance->RDR & uhMask);
pData +=2U;
}
else
{
*pData++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
}
huart->RxXferCount--;
}
假设如用来接收数据的字节为uint8_t buf,其所在的地址为0x2000000(随便写的),在buf变量后面有另一个变量buf2,地址为0x2000001,当执行tmp = (uint16_t*) pData的时候,bmp会指向0x2000000,那么执行下面的代码 *tmp = (uint16_t)(huart->Instance->RDR & uhMask);,这个时候buf2会被影响,如果要用校验的,那么buf就应该定义成uint_16,这样能保证不会意外操作到别的变量