最近用hal库调试简单的串口回传出现了问题,一个是发满size数据后再发数据卡在硬件错误的地方,排查了很久最后发现是因为extern数组的时候没有加下标,看来数组和地址并不是完全相等的,extern的时候要将除数据以外的全部粘贴。
还有一个问题就是编好程序烧录以后发现传长数据时我传多个数据会出现传一个字节丢一个字节的问题。
接收中断回调函数如图
如图会发生传输一个数据丢失一个数据这样的情况,一开始以为是波特率,但是降低了也一样,而且丢数据总是有规律的隔一个丢一个,所以判断应该是程序问题
先给结论:错误出现在不应该用HAL_UART_Transmit_IT这个函数而要使用HAL_UART_Transmit就完美解决这个问题了。
先详细分析一下为什么要在回调里重新加入HAL_UART_Receive_IT,查看源码可以发现函数主要是设置一些句柄的成员,然后跳转到UART_Start_Receive_IT,内容是开启中断,所以HAL_UART_Receive_IT这个函数是一个填充句柄状态然后开启接收中断的函数
而我们知道,在hal库中所有的中断处理过程都是由hal库完成的,cubemx配置的代码的中断管理函数it.c里只有HAL_UART_IRQHandler,在hal库的中断管理中接收中断是引用了UART_Receive_IT,这个函数是接收中断服务函数主要执行的函数,内容也比较简单
判断传输是8bit还是9bit,然后将数据读出,写入我们句柄的指针,再让指针+1,等下次中断的时候写入的地址就变成了指针+1,注意在读取的过程中32自动清除了标志位,所以不需要手动清除标志位,每次中断都会执行这个函数。
先对size自减,然后判断还剩多少个数据没有接收,当size为0,接收完了以后才会关闭usart中断,最后进入回调函数HAL_UART_RxCpltCallback,注意,所有直接对句柄的操作都已经改变了句柄成员的值。
所以可以发现,当进入了回调函数以后我们的句柄已经面目全非了,size变成了0,而地址变成了最后一个数存的地址,然后中断也被关闭了,所以这时候重新使用HAL_UART_Receive_IT进行一个设置。一个开启中断的函数写的好像主动接收一样,不知道为什么不加给start在里面,理解起来真的费力。
再来看发送中断
函数HAL_UART_Transmit_IT,与接收中断一样,也是开启了发送空时候的中断,指针位移,发送完以后关闭中断
因为是单字节接收所以为了追踪是接收不到还是没发送,在回调函数中将接收的数据存入数组
发现数据都能接收到而是没发出去,再写发送回调,进行计数,结果发现发送回调只执行了一半次数
统计进入中断和进入回调的次数:
收1字节进1次中断,进入1次收回调。
收发1字节进3次中断,进1次收回调,进1次发回调,丢失1字节数据
收发2字节进4次中断,进2次收回调,进1次发回调,丢失1字节数据
收发3字节进7次中断,进3次收回调,进2次发回调,丢失1字节数据
收发4字节进8次中断,进4次收回调,进2次发回调,丢失2字节数据
收发5字节进11次中断,进5次收回调,进3次发回调,丢失2字节数据
判断可能是有某个中断被触发了,这个中断偶数时与发回调次数相同。
我猜测可能是两个函数都填充句柄导致中断判断的时候错误,但是改过顺序好像以后还是一样的,具体原因我也探究不太下去了,总而言之连接的时候收发中断只用一个,中断收里面阻塞发就行了。
不得不说hal库既进行了高度的封装,使用cubemx配置起来比较方便,但是由于高度封装当出现问题以后就变得十分棘手,STD库很少出现这样找不到问题的情况,一般开个debug看看是不是哪儿漏配了,实在不行可以直接重写。hal库冗余的代码太多了,阅读起来也比较吃力,甚至有时候我会感觉hal库比STD更接近寄存器,因为有些函数和句柄真的要跑回去翻寄存器才知道是什么意思,但是配置一些freertos lwip的比较方便,或许以后等ll库教程多了会有第三个选择吧。