[原创] EEDrone开源四旋翼从零开始(8)--串口无中断DMA传输

lb8820265   2017-5-11 22:30 楼主
很抱歉长时间未更新,四旋翼还在继续,目前在测试硬件。这次带来一个串口改进的分享,FreeRTOS的串口接收采用DMA模式,发送采用LL库的polling方式。之前做过一个FreeRTOS CLI的Demo,采用的是中断读和写的方式,我认为在一个对时延要求很高的控制系统中,不适合出现中断这样的方式。要一切以控制为中心。
下面从操作系统层面上进行解释,在FreeRTOSConfig.h中有两个重要的中断配置:
  • configKERNEL_INTERRUPT_PRIORITY
  • configMAX_SYSCALL_INTERRUPT_PRIORITY
第一个参数是用来设定操作系统内核使用的优先级,在将中断分好组后通常设为15(最低优先级),第二个参数是用来设定最高多少优先级内可以调用操作系统的API函数,值通常是大于0小于15的。
由于内核的优先级最低,所以任何一个外部中断都可以打断,正在运行的程序。这里根据中断优先级的高低分两种情况,一种是中断中可以调用API函数,这样通常的做法是在中断函数中发出信号量,然后在任务中接收信号量并读取,这样可以保证中断运行时间最少。另一种不能调用API函数,这样可以保证最快速响应,但是同时也会打断正在运行的程序。无论哪种方式的中断都会影响到程序的运行,虽热影响可能很小,但是却多了很多不确定的因素,有强迫症的我无法接受。
于是决定使用DMA,DMA的接收普遍的做法是使用中断,然后在中断中读取,这并不是我想要的,我需要它自己在后台默默的接收,需要的时候去读取,事实证明这也是可以的,但是有个问题:默认会地址自加的接收数据,无法直接知道目前接收了哪些数据。解决这个问题的主要思路是使用一个大数组来循环保存接收到的数据,然后用一个指针的指示位置。主要代码如下:
  1. int UART_ReceivedMSG(TMsg *Msg)
  2. {
  3. uint16_t j, k, l;
  4. uint16_t DmaCounter, length;
  5. if(__HAL_DMA_GET_FLAG(&hdma_rx, __HAL_DMA_GET_FE_FLAG_INDEX(&hdma_rx)) == RESET)
  6. {
  7. DmaCounter = UART_RxBufferSize - __HAL_DMA_GET_COUNTER(&hdma_rx);
  8. if (DmaCounter >= Uart_Engine.StartOfMsg)
  9. {
  10. length = DmaCounter - Uart_Engine.StartOfMsg;
  11. }
  12. else
  13. {
  14. length = UART_RxBufferSize + DmaCounter - Uart_Engine.StartOfMsg;
  15. }
  16. if(length<1)
  17. return 0;
  18. j = Uart_Engine.StartOfMsg;
  19. l=j;
  20. Msg->Len = length;
  21. for(k=0;k<length;k++){
  22. Msg->Data[k]=UART_RxBuffer[l];
  23. l++;
  24. if (l >= UART_RxBufferSize) {
  25. l = 0;
  26. }
  27. }
  28. Uart_Engine.StartOfMsg =(j+length)% UART_RxBufferSize;
  29. }
  30. return 1;
  31. }
在线程中不断的运行上面的函数就可以得出这次本次运行接收到的数据以及长度了。
接收的问题解决了,发送却难住了,据我所知发送必须要配合中断,如果不使用中断的话,只能正常的发送一次。无法,最后还是采用的LL库的连续发送模式。如果哪位小伙伴知道如何使用DMA来无中断的发送,还请不吝赐教哦。
同时编写好调试输出函数,在任何地方都可以通过如下的函数输出信息:
  1. sprintf(pcWriteBuffer,"%d",i);
  2. vOutputString(pcWriteBuffer);
其中,sprintf是用来整理格式,vOutputString用来输出,如果要增加一些信息可以如下:
  1. char pcWriteBuffer[100];
  2. char mumber[10];
  3. strcpy( pcWriteBuffer, "GYR_X:" );
  4. sprintf(mumber,"%d",GYR_Value.AXIS_X);
  5. strncat( pcWriteBuffer, mumber, strlen( mumber ) );
  6. strncat( pcWriteBuffer, "\r\n", strlen( "\r\n" ) );
  7. vOutputString(pcWriteBuffer);
本帖最后由 lb8820265 于 2017-5-11 22:32 编辑
QQ:252669569

回复评论 (7)

看楼主之前发的和这次更新,学到了很多东西,感谢分享。
点赞  2017-5-15 09:24
使用FreeRTOS的话,可以考虑CMSIS-RTOS v2,比v1好用

CMSIS-RTOS Choices: Keil RTX or FreeRTOS http://www.keil.com/pr/article/1280.htm

FreeRTOS adaptation for CMSIS-RTOS Version 2 https://github.com/ARM-software/CMSIS-FreeRTOS
点赞  2017-5-16 16:55
引用: savager007 发表于 2017-5-16 16:55
使用FreeRTOS的话,可以考虑CMSIS-RTOS v2,比v1好用

CMSIS-RTOS Choices: Keil RTX or FreeRTOS http:/ ...

恩,不错,我使用过CMSIS-RTOS,ST的很多例程都是使用的这个,我感觉这个就仅仅是在操作系统上面再加一层封装而已,不知跟直接使用FreeRTOS的API相比,使用这个有什么好处呢?
QQ:252669569
点赞  2017-5-16 23:34
引用: lb8820265 发表于 2017-5-16 23:34
恩,不错,我使用过CMSIS-RTOS,ST的很多例程都是使用的这个,我感觉这个就仅仅是在操作系统上面再加一层 ...

随着MCU的资源性能不断提高,单个工程的代码量逐渐增加,软件开始出现模块化分层开发,软件标准化是发展趋势,CMSIS-RTOS的好处就是实现了RTOS接口标准化,以ARM当今的地位及发展势头,CMSIS-RTOS应该会成为RTOS届的POSIX。。。。。

RTOS那么多,API都不一样,但功能却大同小异,所以可能的话尽量采用标准API,代码复用的效率将大大提高
点赞  2017-5-17 16:10
引用: savager007 发表于 2017-5-17 16:10
随着MCU的资源性能不断提高,单个工程的代码量逐渐增加,软件开始出现模块化分层开发,软件标准化是发展 ...

恩,如果仅仅是为了接口的标准化,那这个就没什么用了,而且封装一层之后还会损失很多FreeRTOS很多优势地方,函数功能查起来也不方便,而且增加了函数的复杂度。
何况现在还做不到各种操作系统平台的无缝对接,一个程序不是单单的只有操作系统的几个API就能完成,很多时候HAL,操作系统,BSP,应用层都是存在着很强的耦合的,我认为目前仅仅实现RTOS接口的标准化意义不大。
QQ:252669569
点赞  2017-5-17 22:36
楼主你好,这种方式会不会出现这种情形,就是UART_ReceivedMSG()函数因为某些原因没有及时运行,DMA会不会覆盖掉还未读取的数据?
点赞  2017-10-13 17:12
引用: ZhuMX 发表于 2017-10-13 17:12
楼主你好,这种方式会不会出现这种情形,就是UART_ReceivedMSG()函数因为某些原因没有及时运行,DMA会不会 ...

恩,理论上是会出现这种情况,所以我接收缓冲数组设置的非常的大,有1024字节。当然根据实际情况还是可以调整大小。
QQ:252669569
点赞  2017-10-16 21:56
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复