建FreeRTOS工程和第一個(gè)多任務(wù)程序))
上節(jié)我們簡(jiǎn)單了解了FreeRTOS相關(guān)理論現(xiàn)在我們開始正式的實(shí)操我們先介紹如何在CubeMX里面創(chuàng)建FreeRTOS工程1.、在 SYS 選項(xiàng)里將 Debug 設(shè)為 Serial Wire 并且將 Timebase Source 設(shè)為 TIM4 其它定時(shí)器也行。Timebase Source 為什么不能設(shè)置為 SysTick ? 裸機(jī)的時(shí)鐘源默認(rèn)是 SysTick但是開啟 FreeRTOS 后FreeRTOS會(huì)占用 SysTick 用來生成1ms 定時(shí)用于任務(wù)調(diào)度所以需要為其他總線提供另外的時(shí)鐘源。2、將RCC里的HSE設(shè)置為Crystal/Ceramic Resonator 晶振源3.、選擇 FREERTOS 選項(xiàng)并將 Interface 改為 CMSIS_V2 。FreeRTOS 各配置選項(xiàng)卡的解釋Events事件相關(guān)的創(chuàng)建Task and Queues 任務(wù)與隊(duì)列的創(chuàng)建Timers and Semaphores 定時(shí)器和信號(hào)量的創(chuàng)建Mutexes 互斥量的創(chuàng)建FreeRTOS Heap Usage 用于查看堆使用情況Config parameters 內(nèi)核參數(shù)設(shè)置用戶根據(jù)自己的實(shí)際應(yīng)用來裁剪定制FreeRTOS 內(nèi)核Include parameters FreeRTOS 部分函數(shù)的使能User Constants 相關(guān)宏的定義可以自建一些常量在工程中使用Advanced settings高級(jí)設(shè)置接著我們就可以開始創(chuàng)建第一個(gè)多任務(wù)程序在上節(jié)我們了解到了任務(wù)其實(shí)就是在內(nèi)存里分配一塊堆然后在堆里面分配棧即創(chuàng)建任務(wù)任務(wù)通常都含有while1死循環(huán)在FreeRTOS里面任務(wù)的原型如下voidStartDefaultTask(void*argument){while1{//代碼邏輯}}下面示例如何創(chuàng)建任務(wù)voidMyTask(void*argument){while(1){Led_Test();}}xTaskCreate(MyTask,MyFirstTask,128,NULL,osPriorityNormal,NULL);上面的我們從理論那節(jié)都已經(jīng)有很清晰的了解了吧ARM架構(gòu)從上圖我們可以看到CPU在對(duì)內(nèi)存進(jìn)行一些程序操作那那些操作的變量保存在CPU哪里呢我們深入CPU內(nèi)部看看可以看到CPU在對(duì)內(nèi)存進(jìn)行讀寫操作時(shí)會(huì)涉及到四條匯編指令就是我們的程序這些匯編指令保存在Flash上CPU從Flash得到指令然后去對(duì)內(nèi)存執(zhí)行可以看到CPU上有16個(gè)寄存器這些寄存器就是暫時(shí)保留對(duì)內(nèi)存讀寫操作過程的值那這些寄存器只有暫存數(shù)據(jù)的作用嗎往下看可以看到R13保存棧地址,R14,R15寫入到要跳轉(zhuǎn)的地址跳轉(zhuǎn)后執(zhí)行程序還有其它的作用而R14我們之前介紹過匯編指令下面是通過程序來了解匯編指令在程序的運(yùn)行機(jī)器碼就是就是對(duì)應(yīng)的匯編指令如下圖MOV 是指將右邊值賦給左邊注意這里PUSH是指將1rr1r0寄存器該棧的值內(nèi)存寫入到寄存器中CPU所以1r寄存器的值是返回地址。這里的LDRD是指從棧的sp開始的第4個(gè)字節(jié)和第8個(gè)字節(jié)的位置內(nèi)存值分別寫入到r0和r1寄存器中CPUFreeRTOS源碼可以看到我們選擇配置FreeRTOS并生成文件時(shí)會(huì)有上圖中關(guān)于FreeRTOS的源代碼這是入口函數(shù)其中MX_FREERTOS_Init函數(shù)里面就包含了我們?cè)赥ask.c里面的任務(wù)函數(shù)數(shù)據(jù)類型內(nèi)存管理這是堆相關(guān)的配置第一個(gè)是選擇動(dòng)態(tài)分配/靜態(tài)分配第二個(gè)是配置堆的大小第三個(gè)是選擇堆的配置文件這四種配置文件區(qū)別如下平時(shí)我們用到的配置文件都是4和5下面我來說說它們的區(qū)別如上圖文件1只從內(nèi)存中分配堆卻不能釋放也沒有相關(guān)的頭部來存儲(chǔ)堆的信息文件2能從內(nèi)存中分配堆和頭部不過假設(shè)這塊內(nèi)存已經(jīng)滿了而且我把buf1和buf2都釋放了然后我要重新分配一塊120字節(jié)大小的堆會(huì)發(fā)現(xiàn)分配不了因?yàn)閎uf為100字節(jié)大小buf為50字節(jié)大小所以這120字節(jié)大小都分配不進(jìn)去所以這就是文件2的缺點(diǎn)不能解決碎片問題即不能將這兩塊堆合并文件4就是在文件二的基礎(chǔ)上解決了碎片問題文件5可以看到除了能在RAM剩余內(nèi)存中可以分配堆在EXT_RAM中也能分配堆所以文件5就能管理這兩塊堆通過鏈表空閑鏈表頭指向第一個(gè)堆再指向第二個(gè)堆我們需要告訴鏈表頭有多少塊離散的堆函數(shù)如下不過在程序中我們主要使用這兩個(gè)函數(shù)就行了//分配一塊內(nèi)存void*pvPortMaalloc(size_txWantedSize)//釋放一塊內(nèi)存voidvPortFree(void*pv)