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