简单实现基于stm32HAL库的串口转发功能

最近做个项目需要用到串口转发功能,简单的理解也就是串口2收到的数据通过串口3发出来,串口3收到的数据通过串口2发出来。从思路上来理解是很简单。只需要把一个串口收到的数据原封不动的通过另一个串口发送出去就ok了。那么用代码该怎么实现,以及串口配置成什么方式?这也是我要讲的。并且,针对实现这个功能带出来的几个其他问题我也要描述一下。


软件工具:stm32cubeMx     MDK5.15

硬件:stm32F103c8系统板


中间我测试了好几种方法,有的是效率低有的是实现起来麻烦。这里我就介绍我最终选定的这种方式。使用DMA的方式,思路就是uart2和uart3的发送和接收dma分别打开。这样也就是打开了四个通道的DMA。然后我在一上电的时候调用hal接口函数,让串口2和串口3分别都接收一个字节的数据。

比如串口2现在接收到了一个字节的数据以后,在串口2的接收终端服务函数里面调用串口3的DMA发送函数把刚才接收到的数据转发出去,调用函数接口如下:

发送完以后我会在串口3的中断服务函数里面在次调用串口2的dma接收函数接收一个字节,这样子就构成了一个循环过程,就可以保证串口2的数据完美的转发的串口2,使用的dma方式相率自然也比较高。整体的思路如下:

上电打开串口2的dma接收1个字节->接收到以后进入接收中断服务函数->中断服务函数里面调用串口3的dma发送一个字节->进入到串口3的发送中断服务函数->再次调用串口2 dma接收一个字节。

串口3的接收和串口的循环是一个样的,只是方向变了。


好了,思路如上,但是如何实现?这才是关键点,也是我碰到问题最多的地方。

我通过cubeMx配置,打开串口2和串口3,并且打开发送和接收dma。20170514093505 20170514093550

这是串口2的配置方法,串口3是一样的。生成工程,之后就是写自己的应用代码了。

按照正常思路来,我就在串口的发送和接收回调函数里面去按照上面的思路来实现。但是我发现一个问题:串口2接收到串口3转发出去,每次复位之后只能发送一个字节,之后就没反应了。这就尴尬了,本来用cubemx想省点事儿少写点代码,看来是不行了,我只能去阅读hal的实现思路了。后来发现调用dma发送完成以后他并没有进入串口的发送回调函数:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);

经过调试终于发现原因,是dma有两种模式,一种是normal模式,一种是circlular模式。模式hal是这么处理的,选择使用normal模式在发送完成以后是不会调用这个回调函数的。(中断模式不存在这种情况,只要发送完成就会调用这个函数)。

我也不想修改hal库的源码,只能另辟蹊径,不用回调函数,而是直接写在中断服务函数里面。也就是stm32F1xx_it.c这个文件里面。

找到dma发送完成中断服务函数

我不经过hal库自带的中断服务处理函数,而是自己另建立了一个USER_DmaUsart2TxHandler() 放在了中断服务函数里面。这样我就可以保证每次串口3发送完成以后重新打开串口2接收的函数。下载验证,终于可以ok了?你真的是太傻太天真,作为码农要能经得起折腾,问题会不断的向你袭来。

这样子的结果是,我还是只能发送一个字节。之后又没反应了,这个不应该啊,不过我调试了之后发现我自己的处理函数USER_DmaUsart2TxHandler()确定是已经被执行到了。那问题就不出在这里了。

不断的摸索,后面再次跟踪到hal库里面一个个函数去看,终究功夫不负有心人啊。发现每次我调用HAL_UART_Transmit_DMA(&huart3,&userUart2Rec,1);发送成功以后。huart3->state的状态变成了一直发送繁忙的状态HAL_UART_STATE_BUSY_TX。所以这里大家要注意了,调用完hal uart dma发送完成以后,下次就会发送不成功。这时候就要手动去清楚TX状态标志。如此我修改了如下的代码:

在dma发送完成中断服务函数里面把串口的发送繁忙标志给清掉。这样终于ok了。当然如果不想手动写这个代码,还有一种思路可以实现,通过cubeMx配置同时打开串口的中断。这样每次发送完成以后也会进入串口的中断服务函数里面去,会自动清除busy状态标志。

实现这个功能,现在来看看就很少的代码,但是不明白hal的运行机制这个问题也确实不好解决。stm32cubeMx在大部分时候确实是帮我们节省了开发时间,但是遇到问题花的时间就会多一点。不过归根结底就是对这个库用的不够熟。看透了他,你才能驾驭的了他。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注