<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > FreeRTOS 在STM32上的移植 V1.0

          FreeRTOS 在STM32上的移植 V1.0

          作者: 時(shí)間:2016-11-17 來(lái)源:網(wǎng)絡(luò) 收藏

          FreeRTOS作為開(kāi)源的輕量級(jí)實(shí)時(shí)性操作系統(tǒng),不僅實(shí)現(xiàn)了基本的實(shí)時(shí)調(diào)度、信號(hào)量、隊(duì)列和存儲(chǔ)管理,而且在商業(yè)應(yīng)用上不需要授權(quán)費(fèi)。

          本文引用地址:http://www.ex-cimer.com/article/201611/315439.htm

          FreeRTOS的實(shí)現(xiàn)主要由list.c、queue.c、croutine.c和tasks.c 4個(gè)文件組成。list.c 是一個(gè)鏈表的實(shí)現(xiàn),主要供給內(nèi)核調(diào)度器使用;queue.c 是一個(gè)隊(duì)列的實(shí)現(xiàn),支持中斷環(huán)境和信號(hào)量控制;croutine.c 和task.c是兩種任務(wù)的組織實(shí)現(xiàn)。對(duì)于croutine,各任務(wù)共享同一個(gè)堆棧,使RAM的需求進(jìn)一步縮小,但也正因如此,他的使用受到相對(duì)嚴(yán)格的限制。而task則是傳統(tǒng)的實(shí)現(xiàn),各任務(wù)使用各自的堆棧,支持完全的搶占式調(diào)度。

          FreeRTOS的主要功能可以歸結(jié)為以下幾點(diǎn):
          1)優(yōu)先級(jí)調(diào)度、相同優(yōu)先級(jí)任務(wù)的輪轉(zhuǎn)調(diào)度,同時(shí)可設(shè)成可剝奪內(nèi)核或不可剝奪內(nèi)核
          2)任務(wù)可選擇是否共享堆棧(co-routines & tasks),并且沒(méi)有任務(wù)數(shù)限制
          3)消息隊(duì)列,二值信號(hào)量,計(jì)數(shù)信號(hào)量,遞歸互斥體
          4)時(shí)間管理
          5)內(nèi)存管理

          與UC/OSII一樣,F(xiàn)reeRTOS在STM32移植大致由3個(gè)文件實(shí)現(xiàn),一個(gè).h文件定義編譯器相關(guān)的數(shù)據(jù)類(lèi)型和中斷處理的宏定義;一個(gè).c文件實(shí)現(xiàn)任務(wù)的堆棧初始化、系統(tǒng)心跳的管理和任務(wù)切換的請(qǐng)求;一個(gè).s文件實(shí)現(xiàn)具體的任務(wù)切換。

          在本次移植中,使用的編譯軟件為IAR EWARM 5.2。
          一、各文件關(guān)鍵部分的實(shí)現(xiàn):

          1、PORTMACRO.H 宏定義部分
          1)定義編譯器相關(guān)的各種數(shù)據(jù)類(lèi)型
          #define portCHARchar
          #define portFLOATfloat
          #define portDOUBLEdouble
          #define portLONGlong
          #define portSHORTshort
          #define portSTACK_TYPEunsigned portLONG
          #define portBASE_TYPElong

          2)架構(gòu)相關(guān)的定義
          Cortex-M3的堆棧增長(zhǎng)方向?yàn)楦叩刂废虻偷刂吩鲩L(zhǎng)
          #define portSTACK_GROWTH( -1 )
          每毫秒的心跳次數(shù)
          #define portTICK_RATE_MS( ( portTickType ) 1000 / configTICK_RATE_HZ )
          訪問(wèn)SRAM的字節(jié)對(duì)齊
          #define portBYTE_ALIGNMENT8

          3)定義用戶主動(dòng)引起內(nèi)核調(diào)度的2個(gè)函數(shù)
          強(qiáng)制上下文切換,用在任務(wù)環(huán)境中調(diào)用
          #define portYIELD()vPortYieldFromISR()
          強(qiáng)制上下文切換,用在中斷處理環(huán)境中調(diào)用
          #define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) vPortYieldFromISR()

          4)定義臨界區(qū)的管理函數(shù)
          中斷允許和關(guān)閉
          #define portDISABLE_INTERRUPTS()vPortSetInterruptMask()
          #define portENABLE_INTERRUPTS()vPortClearInterruptMask()
          臨界區(qū)進(jìn)入和退出
          #define portENTER_CRITICAL()vPortEnterCritical()
          #define portEXIT_CRITICAL()vPortExitCritical()
          用于在中斷環(huán)境的中斷允許和關(guān)閉
          #define portSET_INTERRUPT_MASK_FROM_ISR()0;vPortSetInterruptMask()
          #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)vPortClearInterruptMask();(void)x

          2、PORT.C C接口部分
          1)堆棧初始化
          portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )
          {
          *pxTopOfStack = portINITIAL_XPSR;/* 程序狀態(tài)寄存器 */
          pxTopOfStack--;
          *pxTopOfStack = ( portSTACK_TYPE ) pxCode;/* 任務(wù)的入口點(diǎn) */
          pxTopOfStack--;
          *pxTopOfStack = 0;/* LR */
          pxTopOfStack -= 5;/* R12, R3, R2 and R1. */
          *pxTopOfStack = ( portSTACK_TYPE ) pvParameters;/* 任務(wù)的參數(shù) */
          pxTopOfStack -= 8;/* R11, R10, R9, R8, R7, R6, R5 and R4. */
          return pxTopOfStack;
          }

          2)啟動(dòng)任務(wù)調(diào)度
          portBASE_TYPE xPortStartScheduler( void )
          {
          讓任務(wù)切換中斷和心跳中斷位于最低的優(yōu)先級(jí),使更高優(yōu)先級(jí)可以搶占mcu
          *(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI;
          *(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI;

          設(shè)置并啟動(dòng)系統(tǒng)的心跳時(shí)鐘
          prvSetupTimerInterrupt();

          初始化臨界區(qū)的嵌套的個(gè)數(shù)
          uxCriticalNesting = 0;

          啟動(dòng)第一個(gè)任務(wù)
          vPortStartFirstTask();

          執(zhí)行到vPortStartFirstTask函數(shù),內(nèi)核已經(jīng)開(kāi)始正常的調(diào)度
          return 0;
          }

          3)主動(dòng)釋放mcu使用權(quán)
          void vPortYieldFromISR( void )
          {
          觸發(fā)PendSV系統(tǒng)服務(wù)中斷,中斷到來(lái)時(shí)由匯編函數(shù)xPortPendSVHandler()處理
          *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;
          }
          進(jìn)入臨界區(qū)時(shí),首先關(guān)閉中斷;當(dāng)退出所以嵌套的臨界區(qū)后再使能中斷
          void vPortEnterCritical( void )
          {
          portDISABLE_INTERRUPTS();
          uxCriticalNesting++;
          }
          void vPortExitCritical( void )
          {
          uxCriticalNesting--;
          if( uxCriticalNesting == 0 )
          {
          portENABLE_INTERRUPTS();
          }
          }
          4)心跳時(shí)鐘處理函數(shù)
          void xPortSysTickHandler( void )
          {
          unsigned portLONG ulDummy;

          如果是搶占式調(diào)度,首先看一下有沒(méi)有需要調(diào)度的任務(wù)
          #if configUSE_PREEMPTION == 1
          *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;
          #endif

          ulDummy = portSET_INTERRUPT_MASK_FROM_ISR();
          { 通過(guò)task.c的心跳處理函數(shù)vTaskIncrementTick(),進(jìn)行時(shí)鐘計(jì)數(shù)和延時(shí)任務(wù)的處理
          vTaskIncrementTick();
          }
          portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy );
          }

          3、PORTASM.S 匯編處理部分
          1)請(qǐng)求切換任務(wù)
          xPortPendSVHandler:
          保存當(dāng)前任務(wù)的上下文到其任務(wù)控制塊
          mrs r0, psp
          ldrr3, =pxCurrentTCB獲取當(dāng)前任務(wù)的任務(wù)控制塊指針
          ldrr2, [r3]

          stmdb r0!, {r4-r11}保存R4-R11到該任務(wù)的堆棧
          str r0, [r2]將最后的堆棧指針保存到任務(wù)控制塊的pxTopOfStack

          stmdb sp!, {r3, r14}
          關(guān)閉中斷
          mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
          msr basepri, r0
          切換任務(wù)的上下文,pxCurrentTCB已指向新的任務(wù)

          bl vTaskSwitchContext
          mov r0, #0
          msr basepri, r0
          ldmia sp!, {r3, r14}
          恢復(fù)新任務(wù)的上下文到各寄存器
          ldr r1, [r3]
          ldr r0, [r1]/* The first item in pxCurrentTCB is the task top of stack. */
          ldmia r0!, {r4-r11}/* Pop the registers. */
          msr psp, r0
          bx r14
          任務(wù)切換的示意圖如下:

          2.)中斷允許和關(guān)閉的實(shí)現(xiàn),通過(guò)BASEPRI屏蔽相應(yīng)優(yōu)先級(jí)的中斷源
          vPortSetInterruptMask:
          push { r0 }
          mov R0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
          msr BASEPRI, R0
          pop { R0 }

          bx r14

          vPortClearInterruptMask:
          PUSH { r0 }
          MOV R0, #0
          MSR BASEPRI, R0
          POP { R0 }

          bx r14

          3)直接切換任務(wù),用于vPortStartFirstTask第一次啟動(dòng)任務(wù)時(shí)初始化堆棧和各寄存器
          vPortSVCHandler;
          ldrr3, =pxCurrentTCB
          ldr r1, [r3]
          ldr r0, [r1]
          ldmia r0!, {r4-r11}
          msr psp, r0
          mov r0, #0
          msrbasepri, r0
          orr r14, r14, #13
          bx r14

          4)啟動(dòng)第一個(gè)任務(wù)的匯編實(shí)現(xiàn)
          vPortStartFirstTask
          通過(guò)中斷向量表的定位堆棧的地址
          ldr r0, =0xE000ED08向量表偏移量寄存器 (VTOR)
          ldr r0, [r0]
          ldr r0, [r0]
          msr msp, r0將堆棧地址保存到主堆棧指針msp中
          觸發(fā)SVC軟中斷,由vPortSVCHandler()完成第一個(gè)任務(wù)的具體切換工作
          svc 0

          FreeRTOS內(nèi)核調(diào)度器啟動(dòng)的流程如下:

          以上3個(gè)文件實(shí)現(xiàn)了FreeRTOS內(nèi)核調(diào)度所需的底層接口,相關(guān)代碼十分精簡(jiǎn)。

          二、創(chuàng)建測(cè)試任務(wù):
          下面創(chuàng)建第一個(gè)測(cè)試任務(wù),LED測(cè)試
          int main( void )
          {
          設(shè)置系統(tǒng)時(shí)鐘,中斷向量表和LED使用的GPIO
          使用stm32的固件包提供的初始化函數(shù),具體說(shuō)明見(jiàn)相關(guān)手冊(cè)
          prvSetupHardware();

          通過(guò)xTaskCreate()創(chuàng)建4個(gè)LED任務(wù)vLEDFlashTask(),
          每個(gè)任務(wù)根據(jù)各自的頻率閃爍,分別對(duì)應(yīng)開(kāi)發(fā)板上的4個(gè)LED
          vStartLEDFlashTasks( mainFLASH_TASK_PRIORITY );

          •創(chuàng)建一個(gè)IDLE任務(wù)后,通過(guò)xPortStartScheduler啟動(dòng)調(diào)度器
          vTaskStartScheduler();

          調(diào)度器工作不正常時(shí)返回
          return 0;
          }

          portTASK_FUNCTION()是FreeRTOS定義的函數(shù)聲明,沒(méi)特殊作用
          static portTASK_FUNCTION( vLEDFlashTask, pvParameters )
          {
          ……省略……,具體為計(jì)算各LED的閃爍頻率
          for(;;)
          {
          vTaskDelayUntil( &xLastFlashTime, xFlashRate );
          vParTestToggleLED( uxLED );

          vTaskDelayUntil()的延時(shí)時(shí)間xFlashRate,是從上一次的延時(shí)時(shí)間xLastFlashTime算起的,
          相對(duì)vTaskDelay()的直接延時(shí)更為精準(zhǔn)。
          vTaskDelayUntil( &xLastFlashTime, xFlashRate );
          vParTestToggleLED( uxLED );
          }
          }

          FreeRTOS的任務(wù)創(chuàng)建與UC/OSII差異不大,主要參數(shù)為任務(wù)函數(shù),堆棧大小和任務(wù)的優(yōu)先級(jí)。如:
          xTaskCreate( vLEDFlashTask, ( signed portCHAR * ) "LEDx", ledSTACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );

          下面再創(chuàng)建一個(gè)LCD顯示任務(wù),以最低優(yōu)先級(jí)運(yùn)行:
          xTaskCreate( vLCDTask, ( signed portCHAR * ) "LCD", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );

          void vLCDTask( void *pvParameters )
          {
          ……省略……
          for( ;; )
          {
          vTaskDelay(1000);
          printf("%c ", usDisplayChar);
          }
          }
          該任務(wù)很簡(jiǎn)單,每隔1000個(gè)ticks(就是1000ms),從LCD上刷新一個(gè)數(shù)字。



          關(guān)鍵詞: FreeRTOSSTM32移植V1.

          評(píng)論


          技術(shù)專(zhuān)區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();