ESP32 串口发送数据
首先要明确一下,在使用ESP32 IDF配置串口的时候有没有用ESP32自带的串口中断服务。
1 2 |
uart_driver_install(EXP_UART, 256,0, 0, NULL, 0); //这个是安装驱动程序,默认会使用自带的中断服务 uart_isr_free(EXP_UART); //这个函数是释放掉自带中断服务 |
如果只是调用了uart_driver_install函数,那么默认会使用IDF中自带的中断服务函数。这里需要强调的是如果你要用自己的中断服务函数,就把uart_driver_install第三个参数tx_buffer_size 设定为0。为什么要这么做?这就和之后调用串口发送函数有关了。
1 |
uart_write_bytes(EXP_UART,"1234567890\r\n",12); |
我后面通过串口发送数据使用的是这个函数,经过测试我要是把uart_driver_install中发送缓冲区第三个参数设置为256,那么ESP就不停的复位,应该是看门狗复位。产生复位的原因分析后也很简单,当我们调用uart_write_bytes函数发送数据的时候,会跟据我们前面设定的发送缓冲区的大小进入不同的处理流程。假如我们设定的发送缓冲区大于0,就会先把发送数据写入发送缓冲区,然后IDF自带的串口中断就会读取发送缓冲区的数据并它发送出去。因为我已经不用IDF自带的串口中断服务函数了,所以它就会卡在触发了串口发送中断而没有清除标志位的情况,进而导致看门狗没法喂狗造成复位。
所以这里有三种解决思路:第一,修改中断服务函数带上发送功能,但是显然这个工作量会比较大,还要用IDF配置好的发送缓冲区。第二种就是把uart_driver_install中第三个参数配置为0,这样调用uart_write_bytes的时候就会直接把数据存入硬件的发送FIFO,而不会再等待发送中断的处理。还有最后一种思路就是调用uart_tx_chars函数来代替uart_write_bytes进行数据发送。该函数也会把发送的数据写入硬件的FIFO而不会再用IDF中间建立的发送缓冲区。
还有地方要注意,uart_driver_install配置的发送缓冲区是软件实现的,但是串口硬件本身带的也有发送FIFO。不要把这两个弄混淆了。实际发送数据量不大的时候,我们就没有必要用IDF软件实现的发送缓冲区了,硬件的就完全可以够我们使用!!!
补充:
后来经过测试,前面描述的地方还是有疏忽。当深入进去看uart_write_bytes代码的时候发现,发现即便把软件的发送缓冲区配置为0有一种情况还是会依赖于IDF原来自带的中断服务函数。即当一次性写入硬件FIFO的值超过128个时,uart_write_bytes会等待中断服务函数发送一个信号量来指示TX_FIFO中数据少于10个。如果不在自己添加的中断服务函数里面做处理,那么这个任务就会一直在这里等待进而又产生复位。可以解决的办法是自己模仿IDF中断服务添加发送tx_fifo_sem信号量的代码。要么一次不要发超过128个byte数据并确保TX_FIFO有空闲空间再调用uart_write_bytes。
最终决定使用uart_tx_chars()函数,该函数可以保证不依赖IDF自带的中断服务函数。但是该函数有个缺点,不会判断硬件TX_FIFO是否有空间,这样假如TX_FIFO已经满了却还在用这个函数往里面丢数据,将导致有部分数据没有存入TX_FIFO从而导致发送出去的数据不是预期的。
可以增加一个计数值和信号量来进行同步。当连续发送数据的时候就进行计数,假如计数值接近128(TX_FIFO默认大小128),我们就停止发数据,这时候打开串口发送空闲中断,并等待一个信号量:
1 2 |
uart_enable_tx_intr(EXP_UART,1,50); xSemaphoreTake(semUart1TxEmpty,portMAX_DELAY ); |
这个信号量在中断中检测到TX_FIFO里面数量少于设定值10的时候就会触发。发送任务会暂时挂起,等待这个信号量到来的时候继续往下执行。从而可以保证数据可以完整的存入硬件TX_FIFO 进而通过TX脚发送出去。