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

          新聞中心

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

          FreeRTOS 在STM32上的移植 V1.0

          作者: 時間:2016-11-17 來源:網(wǎng)絡 收藏

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

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

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

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

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

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

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

          2)架構相關的定義
          Cortex-M3的堆棧增長方向為高地址向低地址增長
          #define portSTACK_GROWTH( -1 )
          每毫秒的心跳次數(shù)
          #define portTICK_RATE_MS( ( portTickType ) 1000 / configTICK_RATE_HZ )
          訪問SRAM的字節(jié)對齊
          #define portBYTE_ALIGNMENT8

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

          4)定義臨界區(qū)的管理函數(shù)
          中斷允許和關閉
          #define portDISABLE_INTERRUPTS()vPortSetInterruptMask()
          #define portENABLE_INTERRUPTS()vPortClearInterruptMask()
          臨界區(qū)進入和退出
          #define portENTER_CRITICAL()vPortEnterCritical()
          #define portEXIT_CRITICAL()vPortExitCritical()
          用于在中斷環(huá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;/* 任務的入口點 */
          pxTopOfStack--;
          *pxTopOfStack = 0;/* LR */
          pxTopOfStack -= 5;/* R12, R3, R2 and R1. */
          *pxTopOfStack = ( portSTACK_TYPE ) pvParameters;/* 任務的參數(shù) */
          pxTopOfStack -= 8;/* R11, R10, R9, R8, R7, R6, R5 and R4. */
          return pxTopOfStack;
          }

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

          設置并啟動系統(tǒng)的心跳時鐘
          prvSetupTimerInterrupt();

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

          啟動第一個任務
          vPortStartFirstTask();

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

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

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

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

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

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

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

          bl vTaskSwitchContext
          mov r0, #0
          msr basepri, r0
          ldmia sp!, {r3, r14}
          恢復新任務的上下文到各寄存器
          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
          任務切換的示意圖如下:

          2.)中斷允許和關閉的實現(xiàn),通過BASEPRI屏蔽相應優(yōu)先級的中斷源
          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)直接切換任務,用于vPortStartFirstTask第一次啟動任務時初始化堆棧和各寄存器
          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)啟動第一個任務的匯編實現(xiàn)
          vPortStartFirstTask
          通過中斷向量表的定位堆棧的地址
          ldr r0, =0xE000ED08向量表偏移量寄存器 (VTOR)
          ldr r0, [r0]
          ldr r0, [r0]
          msr msp, r0將堆棧地址保存到主堆棧指針msp中
          觸發(fā)SVC軟中斷,由vPortSVCHandler()完成第一個任務的具體切換工作
          svc 0

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

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

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

          通過xTaskCreate()創(chuàng)建4個LED任務vLEDFlashTask(),
          每個任務根據(jù)各自的頻率閃爍,分別對應開發(fā)板上的4個LED
          vStartLEDFlashTasks( mainFLASH_TASK_PRIORITY );

          •創(chuàng)建一個IDLE任務后,通過xPortStartScheduler啟動調(diào)度器
          vTaskStartScheduler();

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

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

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

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

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

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



          評論


          技術專區(qū)

          關閉
          看屁屁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); })();