STM32-FreeRTOS快速學習之總結1

来源:https://www.cnblogs.com/lifexy/archive/2019/03/02/10463050.html
-Advertisement-
Play Games

1. 基礎知識註意:在RTOS中是優先值越高則優先順序越高(和ucos/linux的相反) 在移植的時候,主要裁剪FreeRTOS/Source/portable文件夾,該文件夾用來針對不同MCU做的一些處理,如下圖所示,我們只需要使用: 1.1配置工程時,選擇memMang時,一般使用heap_4. ...


1. 基礎知識
註意:在RTOS中是優先值越高則優先順序越高(和ucos/linux的相反)
在移植的時候,主要裁剪FreeRTOS/Source/portable文件夾,該文件夾用來針對不同MCU做的一些處理,如下圖所示,我們只需要使用:

 

1.1配置工程時,選擇memMang時,一般使用heap_4.c

  • heap_4: 優點在於可以有效的利用記憶體碎片來合併為一個大記憶體.缺點在於只能用來一個ram里.
  • heap_5: 一般針對有外部RAM才用到,優點在於可以同時利用內部ram和外部ram來進行記憶體碎片合併.

最終添加的庫文件有:

然後我們在分配釋放記憶體的時候,就儘量使用RTOS帶的函數來實現,分配/釋放函數如下所示:

void *pvPortMalloc( size_t xWantedSize );    
void vPortFree( void *pv );

1.2 添加頭文件路徑

  • 添加FreeRTOS\include
  • 添加FreeRTOS\portable\RVDS\ARM_CM3
  • 並將原子中的FreeRTOSConfig.h也複製到我們項目的FreeRTOS\include中(用來配置RTOS系統)

 

2. FreeRTOSConfig.h配置介紹
一般會寫configXXXXX或者INCLUDE_XXXX類似的巨集,這兩個巨集區別在於:

  • configXXXXX

用來實現不同功能,比如定義configUSE_COUNTING_SEMAPHORES為1時,表示使用計數信號量

  • INCLUDE_XXXX

用來是否將某個API函數編譯進程式中.
比如定義INCLUDE_xTaskGetSchedulerState 為1 時,則將會編譯xTaskGetSchedulerState()函數,如下圖所示:

 

3. FreeRTOS任務狀態

3.1 運行態
指當前任務正在運行.
3.2 就緒態
指當前任務正在等待調度,因為有個高優先順序/同優先順序的任務正在運行中
3.3 阻塞態
當前任務處於等待外部事件通知或通過vTaskDelay()函數進入休眠了,外部事件通知常見有信號量、等待隊列、事件標誌組、任務通知.
3.4 掛起態
類似於暫停,表示不會再參與任務調度了,通過vTaskSuspend()實現,重新恢復調度則使用xTaskResume()

 

4. FreeRTOS中斷配置

4.1 回憶stm32 NVIC中斷
Stm32可以設置NVIC中斷組數為0~4,其中0~4區別在於如下圖所示:、

比如我們設置為NVIC_PriorityGroup_4時:
表示搶占優先順序為4bit(即為2^4,為0~15個搶占優先順序),副優先順序為0bit(表示沒有).

 

4.2 搶占優先順序和副優先順序的區別:

  • 1. 搶占優先順序和副優先順序的值越低,則優先順序越高
  • 2. 高的搶占優先順序的中斷可以直接打斷低的搶占優先順序的中斷
  • 3. 高的副優先順序的中斷不可以打斷低的副優先順序的中斷(只是兩個相同搶占優先順序的中斷同時來的時候,只會優先選擇高的副優先順序)

 

4.3 FreeRTOS中斷配置巨集

  • configKERNEL_INTERRUPT_PRIORITY

用來配置中斷最低搶占優先順序,也就是可以FreeRTOS可以管理的最小搶占優先順序,所以使用FreeRTOS時,我們儘量設置stm32為NVIC_PriorityGroup_4,這樣就可以管理16個優先順序了.

 

  • configMAX_SYSCALL_INTERRUPT_PRIORITY

用來配置FreeRTOS能夠安全管理的的最高優先順序.比如原子的FreeRTOSConfig.h里就設置為5,而0~4的優先順序中斷就不會被FreeRTOS因為開關中斷而禁止掉(一直都會有),並且不能調用RTOS中的”FromISR”結尾的API函數.比如喂看門狗中斷函數就需要設置為0~4

 

  • 如下圖所示(來自原子手冊):


4.3 FreeRTOS中斷開關函數

portENABLE_INTERRUPTS();    
//開中斷,將configMAX_SYSCALL_INTERRUPT_PRIORITY至 configKERNEL_INTERRUPT_PRIORITY之間的優先順序中斷打開

portDISABLE_INTERRUPTS();    
//關中斷,將configMAX_SYSCALL_INTERRUPT_PRIORITY至 configKERNEL_INTERRUPT_PRIORITY之間的優先順序中斷禁止掉

 

 

5.任務常用API函數

5.1 xTaskCreate創建任務函數
定義如下:

xTaskCreate(    TaskFunction_t pxTaskCode,     //任務函數,用來供給函數指針調用的
const char * const pcName,              //任務的字元串別名
const uint16_t usStackDepth,             //任務堆棧深度,實際申請到的堆棧是該參數的4倍
void * const pvParameters,               //函數參數,用來供給指針調用的
UBaseType_t uxPriority,                //優先順序,越高優先順序高,範圍為0~configMAX_PRIORITIES-1 
                            //註意優先順序0會創建為空閑任務, 優先順序configMAX_PRIORITIES-1會創建一個軟體定時器服務任務(管理定時器的)
TaskHandle_t * const pxCreatedTask ); //任務句柄,該句柄可以用於掛起/恢復/刪除對應的任務

//返回值 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(-1):表示創建任務堆空間不足pdPASS(1):創建成功

 

5.2 taskENTER_CRITICAL()和taskEXIT_CRITICAL()
用於任務中進入/退出臨界區,調用taskENTER_CRITICAL()主要會關閉其他任務調度.而taskEXIT_CRITICAL()則會恢復任務調度,一般用於初始化外設等.


5.3 taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()
用於在中斷函數中進入/退出臨界區,作用和上面一樣

 

5.4 掛起/恢復/刪除任務函數

void vTaskSuspend( TaskHandle_t xTaskToSuspend );        //掛起一個任務,參數為掛起任務的句柄,如果為NULL則表示掛起自身任務


void vTaskResume( TaskHandle_t xTaskToResume );     //恢復一個任務

BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume);//從中斷函數中恢復一個任務,返回1表示恢覆成功

void vTaskDelete( TaskHandle_t xTaskToDelete );   //刪除一個任務,如果從任務函數中退出的話,則需要調用vTaskDelete(NULL)來刪除自身任務

 

5.5 vTaskDelay()延時函數

void vTaskDelay( const TickType_t xTicksToDelay );    //參數表示延時的系統滴答數

比如延時500ms可以寫為: vTaskDelay( 500/portTICK_RATE_MS );
portTICK_RATE_MS是個巨集,表示當前系統的1個滴答需要多少ms,而500/portTICK_RATE_MS則表示當前500ms需要多少個系統滴答數.


6. 隊列
6.1簡介
隊列用於任務與任務或者任務與中斷之間的通信.比如key任務檢測到按鍵按下時,則可以通過隊列向lcd顯示任務發送信息,使得lcd切換界面.
隊列採用先進先出存儲機制.隊列發送數據可以有兩種方式:淺拷貝、深拷貝.

  • 數據量不大的情況下,都使用深拷貝(會分配新的空間,併進行數據拷貝,缺點在於耗時)
  • 數據量大的情況下,都使用淺拷貝(通過指針方式,前提是要發送的數據必須不會被釋放的)

6.2隊列的優點
隊列可以通過任何任務或者中斷進行訪問,可以隨時存取數據消息.
並且出入隊的時候可以進行任務阻塞,比如某個任務進行讀消息出隊時,如果沒有消息,則可以實現進入休眠狀態,直到有消息才喚醒任務.

 

6.3隊列創建刪除相關API

QueueHandle_t xQueueCreate( uxQueueLength, uxItemSize );    
 //動態創建隊列,記憶體會交給RTOS自動分配
 // uxQueueLength:隊列長度(表示隊列中最大多少條消息),uxItemSize:每個隊列消息的長度(以位元組為單位)
 //返回值: NULL(0, 表示分配失敗),非0(表示返回該隊列分配好的地址)
 //註意:使用自動分配時,需要配置configSUPPORT_DYNAMIC_ALLOCATION巨集為1,否則只能由用戶來分配.

QueueHandle_t xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ); //靜態創建隊列,記憶體需要由用戶事先分配好 // uxQueueLength:隊列長度(表示隊列中最大多少條消息),uxItemSize:每個隊列消息的長度(以位元組為單位) // pucQueueStorage:指向用戶事先分配好的存儲區記憶體(必須為uint8_t型) // pxQueueBuffer:指向隊列結構體,用來提供給RTOS初始化.然後給用戶使用 //返回值: NULL(0, 表示分配失敗),非0(表示返回該隊列分配好的地址) vQueueDelete( QueueHandle_t xQueue ); //刪除隊列,並釋放空間 xQueueReset( xQueue ); //將隊列里的消息清空一次,也就是恢復初始狀態

 

6.4隊列出入隊相關API

xQueueSend( xQueue, pvItemToQueue, xTicksToWait );
//插入隊尾,和xQueueSendToBack函數效果一致
// xQueue:隊列句柄
//PvItemToQueue:消息數據,會通過數據拷貝到隊列中,如果想使用淺拷貝,則可以發送一個變數來存儲要真正發送的緩衝區地址即可.
// xTicksToWait:阻塞時間,單位為RTOS時鐘滴答值,如果configTICK_RATE_HZ是1000,則填入的值表示阻塞的是多少ms,否則的話需要通過X/portTICK_RATE_MS來轉換一下,才能實現阻塞Xms.
//xTicksToWait==0:表示入隊滿了,則直接退出該函數
// xTicksToWait==portMAX_DELAY:表示一直阻塞,直到隊列有空位為止.
//註意: INCLUDE_vTaskSuspend巨集必須為1,否則任務無法進入休眠狀態實現阻塞效果.
//返回值: errQUEUE_FULL(隊列已滿) pdPASS(通過)

xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ); //插入隊頭,參數和上面描述一致

xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ); //插入隊尾,參數和上面描述一致 xQueueOverwrite( xQueue, pvItemToQueue ); //將之前未出隊的舊數據全部清空,然後再入隊,該函數適用於長度為1的隊列 xQueueReceive( xQueue, pvBuffer, xTicksToWait ); //從隊列頭部讀出一個消息,並且這個消息會出隊(刪除掉) xQueuePeek( xQueue, pvBuffer, xTicksToWait ); //從隊列頭部讀出一個消息,但是這個消息不會出隊(不會刪除)

PS:這些API函數只能用於任務里調用,如果要在中斷服務函數中調用,則在函數名後添加FromQueue即可,比如xQueueSendFromQueue()函數

 

6.5 中斷發送/讀取消息隊列時,要註意的事情

使用中斷相關的讀寫隊列相關的API時,第3個參數是不一樣的,比如xQueueSendFromISR():

 

  • PxHigherPriorityTaskWoken

用來標記退出該函數後是否需要進行任務切換,因為我們發送隊列時,有可能會將某個阻塞任務退出阻塞態,而此時又在中斷中,所以當PxHigherPriorityTaskWokenpdTRUE時,我們則必須進行一次任務切換.

 

可以通過portYIELD_FROM_ISR()來進行任務切換,並且我們不需要去判斷PxHigherPriorityTaskWoken是否為pdTRUE,因為該函數內部有判斷的,如下圖所示: 

 

來個中斷函數發送隊列示例:

extern QueueHandle_t Message_Queue;                          //信息隊列句柄

void USART1_IRQHandler(void)                       //串口1中斷服務程式
{
         BaseType_t xHigherPriorityTaskWoken;          //定義任務切換標誌位
         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 
         {
                   //處理中斷接收數據
         }


         if (Message_Queue!=NULL)             //判斷Message_Queue是否已創建
         {
                   xQueueSendFromISR(Message_Queue, RX_BUF,&xHigherPriorityTaskWoken);
//向隊列Message_Queue中發送RX_BUF portYIELD_FROM_ISR(xHigherPriorityTaskWoken); //通過portYIELD_FROM_ISR()判斷是否需要切換任務 } }

PS:儘量將portYIELD_FROM_ISR()寫在中斷函數末尾處

 

6.6示例-任務之間的偽代碼

按鍵任務向列印任務發送按鍵消息隊列,代碼如下:

QueueHandle_t Key_Queue; //按鍵值消息隊列句柄

int main()
{
   //...省略N行代碼

   Key_Queue=xQueueCreate(1,sizeof(u8)); //創建消息Key_Queue,長度為1
   
//
創建兩個任務:key_task()、print_task() //...省略N行代碼 }
key_task()
//獲取按鍵值 {   while(1)   {     key=KEY_Scan(0); //掃描按鍵     if((Key_Queue!=NULL)&&(key)) //消息隊列Key_Queue創建成功,並且按鍵被按下     {       err=xQueueSend(Key_Queue,&key,10);       if(err==errQUEUE_FULL) //發送按鍵值       {         printf("隊列Key_Queue已滿,數據發送失敗!\r\n");       }     }     vTaskDelay(10); //延時10個時鐘節拍   } } print_task() //列印按鍵值 {   u8 key;   while(1)   {     if(Key_Queue!=NULL)     {       if(xQueueReceive(Key_Queue,&key,portMAX_DELAY))//請求消息Key_Queue       {         printf("key=%d\r\n",key);       }     }     vTaskDelay(10); //延時10個時鐘節拍   } }

 

7. RTOS軟體定時器

7.1簡介
在之前的任務創建的時候有講到過,RTOS會自動創建一個優先順序configMAX_PRIORITIES-1的軟體定時器服務任務(管理定時器的).
所以我們寫一個定時器回調函數時,則會被該定時器服務任務調用,所以在我們軟體定時器函數中不能使用vTaskDelay()阻塞之類的API函數,否則會將系統中的定時器服務函數給阻塞掉.

7.2 FreeRTOSConfig.h相關的定時器配置

#define configUSE_TIMERS 1              //為1時啟用軟體定時器

#define configTIMER_TASK_PRIORITY 31    //設置軟體定時器優先順序可設置的值範圍為0~31

#define configTIMER_QUEUE_LENGTH 5      //軟體定時器隊列長度

#define configTIMER_TASK_STACK_DEPTH 200 //設置每個軟體定時器任務堆棧大小

 

7.3定時創建相關API

TimerHandle_t xTimerCreateStatic(const char * const pcTimerName,    //定時器字元串別名
                    const TickType_t xTimerPeriodInTicks,          
                   //需要定時的周期值,比如通過200/ portTICK_RATE_MS來轉換實現定時200毫秒                     const UBaseType_t uxAutoReload,        
                   //是否重載(周期性/單次性),若為pdTRUE(1)表示為周期性,為pdFALSE(0)表示為單次                     void * const pvTimerID,
                    //定時器ID號,一般用於多個定時器共用一個定時器回調函數,否則填0即可                     TimerCallbackFunction_t pxCallbackFunction);//定時器回調函數
xTimerDelete( xTimer, xTicksToWait );
//刪除定時器
//xTicksToWait:指定該定時器在多少時鐘節拍數之前刪除掉,為0則立即刪除,一般設為100(如果設為0,則如果在該操作之前還有其它設置定時器操作的話,則不會進行阻塞等待,從而返回false)

 

7.4 定時器其它常用API

xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait );
//修改定時器周期,在中斷中則使用xTimerChangePeriodFromISR()
// xNewPeriod:要修改的周期值 
//xTicksToWait:指定該定時器在多少時鐘節拍數之前修改好,為0則立即刪除
//xTimerReset( xTimer, xTicksToWait );
//複位定時器,讓定時器重新計數,在中斷中則使用xTimerResetFromISR()
// xTicksToWait:和上面內容類似

xTimerStart( xTimer, xTicksToWait );
//啟動定時器,如果定時器正在運行的話調用該函數的結果和xTimerReset()一樣, 在中斷中則使用xTimerResetFromISR ()

xTimerStop( xTimer, xTicksToWait );
//停止定時器, 在中斷中則使用xTimerStopFromISR ()

 PS:中斷中使用定時器API時,同樣和隊列一樣,也需要在函數末尾通過portYIELD_FROM_ISR()進行一次任務切換判斷


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一.事務 (1) 事務接著上篇繼續講完。如果使用了多種數據訪問技術,來訪問關係型資料庫,則可能希望在這些不同技術所執行的操作之間共用事務。下麵示例顯示瞭如何在同一事務中執行 ADO.NET SqlClient 操作和 Entity Framework Core 操作。 (2) 使用 System.T ...
  • 在 asp.net core 中有些日誌我們可能想輸出到資料庫或文件或elasticsearch等,如果不自己去實現一個 LoggerProvider 的話就需要藉助第三方日誌框架實現了,而一些第三方框架的實現大多比較完善和成熟,不失為一個好辦法。 自己寫了一個 log4net 的擴展 Weiha... ...
  • 自己有幾個自己的小項目,有許多公用的方法/擴展/工具類等等,於是封裝了一些常用的工具類/擴展方法做了一個類庫 WeihanLi.Common,日誌使用了自己比較常用的 log4net,開始預設使用的log4net進行處理日誌,在1.0.12版本之前直接依賴 log4net,後來覺得這樣做不太好,一是... ...
  • 本文知識點: 1.掌握常量的定義和使用方法 2.理解枚舉的作用和特點 3.掌握枚舉的使用方法 1.1.常量的定義語法 const 數據類型 常量名稱 = 值; 1.2.常見錯誤 1.3常量的使用時機 經常使用並且值不變的變數,可以定義為常量 2.1枚舉的作用及其特點 表示一組描述性的名稱,名稱可以對 ...
  • 一、XML: 1、基本瞭解: xml,Extensible markup language可擴展標記語言,用於數據的傳輸或保存,特點,格式非常整齊數據清晰明瞭,並且任何語言都內置了xml分析引擎, 不需要再單獨寫引擎,但是相較於json解析較慢,現在很多項目任然有廣泛的應用。 2、幾種解析和生成xm ...
  • 1、添加 Zabbix 軟體倉庫 2、啟用可選 rpms 的軟體倉庫 3、安裝 Server/前端/agent 4、安裝mariaDB資料庫 5、創建資料庫 6、創建賬戶並授權 7、導入數據 (導入需要一點時間,不要以為卡住按ctrl+c停止) 8、修改Zabbix server配置文件 9、為Za ...
  • 情況描述黑蘋果主機突然無法進入睡眠。考慮到可能是後臺程式阻礙了系統正常進入睡眠, 於是想要通過Activity Monitor查看系統的活動情況,然而,Activity Monitor閃退。重新開機,快速打開Activity Monitor,可以運行,但是隔一會,Activity Monitor又... ...
  • linux基礎命令一 1、date命令 date命令介紹:顯示或者設置系統日期 date命令的語法: 顯示日期:date [options...] [+FORMAT] FORMAT:為顯示日期的格式符號 %D:以斜杠為分割符顯示日期 %F:顯示當前系統的日期,以中橫線分割日期 %T:顯示當前系統的的 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...