|
写了一个Modbus从站,起初使用串口8,N,1的配置,所有功能正常,但是设定成带校验之后,整个程序看起来没什么问题,中断接收的数据也是正常的,但是放到缓存里面的时候数据就完全不对,比对自己的代码,唯一的改变就是从无校验改成了带校验,后来查看HAL库,发现了问题。! \8 D, d" V+ y$ _; y9 P 我自己定义了一个结构体,用来给串口用的,相关部分如下/ L9 E0 a2 d! w% l* `# y0 ` __packed typedef struct MODBUS_SLAVE_DRV! c$ T; z; n6 x( g; L' ` {( h$ w% t- M3 v% a& D uint8_t uartBuf;//用于串口中断接收数据用+ P9 o' W+ U2 z uint8_t *pucRecBuf;//接收数据帧存放地址7 ~% l; ~5 x; G+ Q% I; X- q+ Z uint8_t *pucTranBuf;//发送数据帧存放地址- _4 Y; [8 @8 V1 R( G' Z 采用中断接收的时候数据放到uartBuf这里面,HAL_UART_Receive_IT(uart, &pMbSlaveDrv_t->uartBuf, 1);开启中断接收,中断来的时候会进入UART_Receive_IT,这个函数会把你坑死,如果使用了校验,就要设定UART_WORDLENGTH_9B,一旦设定了9B,这个函数会做如下动作 if(huart->Init.WordLength == UART_WORDLENGTH_9B) {6 Z) P0 f( V7 U4 y# b5 x tmp = (uint16_t*) huart->pRxBuffPtr;//这个就是我之前结构体里面的uartBuf,在这里居然被扩成了uint16& L$ i$ l4 n9 p if(huart->Init.Parity == UART_PARITY_NONE)//下面进行了uint16的赋值 {( t; ~7 H- B9 ~) f4 {1 [3 g *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF); huart->pRxBuffPtr += 2U;/ n7 E) C* A! l: J }$ d- }; z: {& W2 O( e; X else { *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF); huart->pRxBuffPtr += 1U;& v/ j1 Y* S9 @, }4 z* K; o" v }) Z4 d/ {% w O9 D% [" X }- `. K' m) J: H& Q& _1 q1 ? - h* S0 ^ \$ c+ j( z, \0 m' j( m0 P2 Y. s6 L 以上代码会将紧跟在uartBuf这个后面的内存给写掉,如果你很幸运,你的代码里面uartBuf后面的内存没有放置别的变量,恭喜你,不会有任何问题,杯具的我,后面的缓存指针直接被改写。/ L& U- x. w3 m- G+ [. H" W 所以,如果你使用了HAL库,那么请使用uint16的变量来作为接收数据的缓存,但是在调用HAL_UART_Receive_IT的时候需要进行类型的转换,因为这个函数传入的类型是uint8. 我用的F1和F4的HAL库都是这样的。1 m& A, H& g6 F( b: h" j$ | |
微信公众号
手机版
( A$ ]2 R, _$ L5 {
我现在用的是FW_F4 V1.15.0
2 n, U# e% @& n$ P
0 e# f1 K" d0 p$ R' f/ j
/* Computation of UART mask to apply to RDR register */
UART_MASK_COMPUTATION(huart);
uhMask = huart->Mask;
$ c8 I3 n7 V8 a- K' _9 |
/* as long as data have to be received */( \; J6 l: X' O
while(huart->RxXferCount > 0U)
{+ o/ e; M; r" a
if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)% {' U; ?. L# O) T1 W
{
return HAL_TIMEOUT;- t4 J. y$ P! m9 E, q7 v1 K' ^
}$ Z6 c3 G& f# S! n7 f# x, q
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)). H( `2 [! |( L* G! U) l3 }
{$ A4 k0 Z! H& v! I! G; P
tmp = (uint16_t*) pData ;
*tmp = (uint16_t)(huart->Instance->RDR & uhMask);
pData +=2U;( l/ o; E2 k1 W
}+ r* O$ i# E4 g
else. T0 i/ ~6 V' \0 c% E1 Q# J; S
{% C8 d# T" ?+ M+ z1 L! `6 U: o, X
*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,这样能保证不会意外操作到别的变量