?
今日分享參加瑞薩RA MCU創(chuàng)意氛圍賽的選手項目——基于優(yōu)先級的RTOS內(nèi)核。本項目為基于優(yōu)先級調(diào)度的嵌入式實時操作系統(tǒng)內(nèi)核,其中調(diào)度部分使用固定可搶占的優(yōu)先級調(diào)度機制;提供了可移植接口以便適配不同架構(gòu)的cpu;重寫了更簡易更輕量級的部分庫函數(shù),比如標準輸入輸出以及字符串相關(guān)操作;除內(nèi)核外還提供部分組件,包括一個簡易的shell程序以及設(shè)備驅(qū)動框架。具體的操作我們一起來看看講解吧!
一、簡介
1.1 項目簡介
SimpleRTOS(catOS) 是我大學(xué)實習(xí)期間為了學(xué)習(xí)RTOS編寫的一個簡單的內(nèi)核,主要調(diào)度方式基于優(yōu)先級搶占,該項目重構(gòu)了兩次,故內(nèi)容和功能有所不同,最新版本僅保留了固定優(yōu)先級調(diào)度方式。
本次項目內(nèi)容為將該內(nèi)核對野火的瑞薩啟明6M5開發(fā)板進行適配,并編寫簡單的demo驗證正確性。如果有什么錯誤請大家批評指正。(文末有項目資料鏈接可供參考)
1.2 開發(fā)板簡介
官網(wǎng)特性介紹如下:
?支持TrustZone的200MHz Arm Cortex-M33
?安全芯片功能
?1MB - 2MB閃存、448KB支持奇偶校驗的SRAM和64KB ECC SRAM
?具有后臺運行能力的雙區(qū)閃存,以及存儲塊交換功能
?8KB數(shù)據(jù)閃存,提供與EEPROM類似的數(shù)據(jù)存儲功能
?100引腳封裝至176引腳封裝
?高速和全速USB 2.0
?CAN FD(也支持CAN 2.0B)
?SPI/I2C多主接口
?SDHI和MMC
二、適配內(nèi)核
2.1 可移植接口概覽
需要實現(xiàn)的可移植接口包括以下部分
左右滑動查看
?
/** * @brief 硬件初始化 */ void cat_hw_init(void); /** * @brief 開始調(diào)度 * */ void catos_start_sched(void); /** * @brief 上下文切換 * */ //void cat_hw_context_switch(void); /** * @brief 上下文切換 * * @param ?from_task_sp_addr 上一個任務(wù)tcb中堆棧指針變量的 *地址* * @param ?to_task_sp_addr ? 下一個任務(wù)tcb中堆棧指針變量的 *地址* */ void cat_hw_context_switch(cat_uint32_t from_task_sp_addr, cat_uint32_t to_task_sp_addr); /** * @brief 切換到第一個任務(wù)的上下文 * * @param ?first_task_sp_addr ?要切換的任務(wù)tcb中堆棧指針變量的 *地址* */ void cat_hw_context_switch_to_first(cat_uint32_t first_task_sp_addr); /** * @brief 關(guān)中斷進臨界區(qū) * * @return cat_uint32_t */ cat_uint32_t cat_hw_irq_disable(void); /** * @brief 開中斷出臨界區(qū) * * @param status */ void cat_hw_irq_enable(cat_uint32_t status); /** * @brief 棧初始化 * * @param task_entry ? ?任務(wù)入口函數(shù)地址 * @param parameter ? ? 參數(shù) * @param stack_addr ? ?棧起始地址 * @param exit ? ? ? ? ?任務(wù)退出函數(shù)地址 * @return cat_uint8_t* ? ? 初始化后的棧頂?shù)刂?*/ cat_uint8_t *cat_hw_stack_init(void *task_entry, void *parameter, cat_uint8_t *stack_addr, void *exit);
?
2.2 硬件初始化
在硬件初始化中主要是設(shè)置系統(tǒng)時鐘中斷頻率,初始化時設(shè)置時鐘中斷為關(guān)閉狀態(tài)。
左右滑動查看
?
/** * @brief 硬件初始化 */ void cat_hw_init(void) { /* 設(shè)置系統(tǒng)時鐘中斷頻率為100Hz(每秒100次) */ cat_set_systick_period(CATOS_SYSTICK_MS); } /** * @brief 初始化時鐘中斷 * * @param ms 每個tick的時間(ms) */ static void cat_set_systick_period(cat_uint32_t ms) { cat_uint32_t err = 0; cat_uint32_t IT_Period = 0; IT_Period = ms * SystemCoreClock / 1000; //err = SysTick_Config(IT_Period); /* 如果設(shè)定的周期太離譜就停在這 */ if ((IT_Period - 1UL) > SysTick_LOAD_RELOAD_Msk) { err = 1; } assert(0 == err); SysTick->LOAD = (uint32_t)(IT_Period - 1UL); /* 設(shè)置重裝載寄存器 */ NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* 設(shè)置時鐘中斷優(yōu)先級 */ SysTick->VAL = 0UL; /* 設(shè)置計數(shù)器裝載值 */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | /* 設(shè)定為內(nèi)核時鐘FCLK */ SysTick_CTRL_TICKINT_Msk | /* 設(shè)定為systick計數(shù)器倒數(shù)到0時觸發(fā)中斷 */ ~SysTick_CTRL_ENABLE_Msk; /* 關(guān)閉定時器中斷,若創(chuàng)建任務(wù)則在catos_start_sched()中開啟該中斷 */ }
?
2.3 開始調(diào)度與切換到第一個任務(wù)的上下文
開始調(diào)度需要從就緒表中獲取最高優(yōu)先級任務(wù)并設(shè)置為當前任務(wù),并且在恢復(fù)第一個任務(wù)上下文之前需要打開時鐘中斷并初始化pendsv中斷以保證調(diào)度的正常工作。
注:這里暫時沒有對不使用fpu的情況適配,參考FreeRTOS的可移植接口實現(xiàn)。
左右滑動查看
?
/** * @brief 開始調(diào)度 * */ void catos_start_sched(void) { cat_uint32_t tmp_reg = 0; struct _cat_task_t *first_task = NULL; /* 獲取最高優(yōu)先級任務(wù) */ first_task = cat_sp_task_highest_ready(); /* 因為是第一個任務(wù),不用像調(diào)度時判斷是否和上一個任務(wù)一樣,直接賦值給當前任務(wù)就行 */ cat_sp_cur_task = first_task; /* 允許調(diào)度(打開調(diào)度鎖,并且不在該處進行調(diào)度) */ cat_sp_task_sched_enable_without_sched(); /* 開啟時鐘中斷 */ SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 設(shè)置pendsv中斷優(yōu)先級 */ tmp_reg = MEM32(NVIC_SHPR3); tmp_reg |= NVIC_PENDSV_PRI; MEM32(NVIC_SHPR3) = tmp_reg; /* 切換到第一個任務(wù) */ cat_hw_context_switch_to_first((cat_uint32_t)&(first_task->sp)); } /** * @brief 切換到第一個任務(wù)的上下文 * */ void cat_hw_context_switch_to_first(void) { __enable_irq(); __ISB(); /* 各個寄存器地址 */ __asm volatile ( ".equ SCB_ICSR, 0xE000ED04 " // 中斷控制寄存器 ".equ SCB_VTOR, 0xE000ED08 " // 中斷向量表偏移寄存器 ".equ ICSR_PENDSVSET, 0x10000000 " // pendsv觸發(fā)值 ".equ SHPR3_PRI_14, 0xE000ED22 " // 系統(tǒng)異常handler優(yōu)先級寄存器 3 (PendSV). ".equ PRI_LVL_PENDSV, 0xFF "http:// pendsv優(yōu)先級 (最低). ".equ SHPR3_PRI_15, 0xE000ED23 "http:// 系統(tǒng)異常handler優(yōu)先級寄存器 3 (Systick). "ldr r1, =cat_context_to_task_sp_ptr " "str r0, [r1] " #if __FPU_USED //#error "__FPU_USED" /* 清除control寄存器的FPCA */ "mrs r2, control " /* read */ "bic r2, r2, #0x04 " /* modify */ "msr control, r2 " /* write-back */ #else #error "must use fpu" #endif /* 將變量 cat_context_from_task_sp_ptr 設(shè)置為0*/ "ldr r1, =cat_context_from_task_sp_ptr " "mov r0, #0 " "str r0, [r1] " "mov r4, #0x1234 " /* 觸發(fā)pendsv中斷,允許中斷后會立即進入pendsv切換 */ "ldr r0, =SCB_ICSR " "ldr r1, =ICSR_PENDSVSET " "str r1, [r0] " /* *(SCB_ICSR) = "ICSR_PENDSVSET */ /* 不會到達這里 */ "dsb " "isb " "svc 0 " ); }
?
2.4 上下文切換
上下文切換使用pendsv中斷進行,主要工作為保存當前任務(wù)堆棧和寄存器以及恢復(fù)下一個任務(wù)的堆棧和寄存器。
左右滑動查看
?
/** * void cat_hw_context_switch(void) * 觸發(fā)pendsv中斷進行任務(wù)切換(pendsv的優(yōu)先級在開始第一個任務(wù)時已經(jīng)設(shè)置) */ ? ?.global cat_hw_context_switch ? ?.type cat_hw_context_switch, %function cat_hw_context_switch: ? ?/* 將兩個任務(wù)的堆棧指針變量的 *地址* 加載到臨時變量中 */ ? ?/* cat_context_from_task_sp_ptr = &(cat_sp_cur_task->sp) */ ? ?ldr r2, =cat_context_from_task_sp_ptr ? ?str r0, [r2] ? ?/* cat_context_to_task_sp_ptr = &(cat_sp_next_task->sp) */ ? ?ldr r2, =cat_context_to_task_sp_ptr ? ?str r1, [r2] ? ?/* 觸發(fā)pendsv中斷進行切換 */ ? ?ldr r0, =SCB_ICSR ? ? ? ? ?ldr r1, =ICSR_PENDSVSET ? ?str r1, [r0] ? ? ? ? ? ? ? ?/* *(SCB_ICSR) = ICSR_PENDSVSET */ ? ?bx ?lr void PendSV_Handler(void) { ? ?__asm volatile ( ? ? ? ?/* 關(guān)閉全局中斷并保存當前中斷屏蔽寄存器中的值方便恢復(fù) */ ? ?"mrs r2, primask ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?"cpsid i ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?/* 保存中斷屏蔽寄存器狀態(tài) */ ? ?"push {r2} ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?/* 獲取from任務(wù)的堆棧指針變量中的值 */ ? ?/* 在進入pendsv之前 cat_context_from_task_sp_ptr = &(from_task->sp) */ ? ?/** 故有: ? ? * r0 = ?&(from_task->sp) ? ? * r1 = *(&(from_task->sp)) 等價于 r1 = from_task->sp ? ? */ ? ?"ldr r0, =cat_context_from_task_sp_ptr ? ? ? " ? ?"ldr r1, [r0] ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?/* 如果為零則說明是第一個任務(wù) */ ? ?"cbz r1, switch_to_thread ? ? ? ? ? ? ? ? ? " /* 暫時可能用不到trustzone, 因此直接跳轉(zhuǎn) */ ? ?"b contex_ns_store ? ? ? ? ? ? ? ? ? ? ? ? ? " /* 暫時可能用不到 END */ "contex_ns_store: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?/* 用戶級堆棧是psp,特權(quán)級堆棧是msp */ ? ?/* 任務(wù)用的是psp,將當前寄存器保存到堆棧中 */ ? ?"mrs r1, psp ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " #if __FPU_USED ? ?"tst lr, #0x10 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?"it eq ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?"vstmdbeq r1!, {s16-s31} ? ? ? ? ? ? ? ? ? ? " #if BSP_TZ_NONSECURE_BUILD #else ? ? ? ?/* Stack R4-R11 on the process stack. Also stack LR since the FPU is supported. */ ? ? ? ?"STMDB ? ? R1!, {R4-R11, LR} ? ? ? ? ? ? " #endif #else #error "must use fpu" #endif #if BSP_TZ_NONSECURE_BUILD #error "should not use BSP_TZ_NONSECURE_BUILD" #elif RM_CATOS_PORT_PSPLIM_PRESENT ? ? ? ?"mrs ? ? r3, psplim ? ? ? ? ? ? ? ? ? ? ? " /* R3 = PSPLIM. */ ? ? ? ?"stmdb ? r1!, {r3} ? ? ? ? ? ? ? ? ? ? ? " #endif ? ?/* 記錄最后的指針到任務(wù)棧curstk->stack */ ? ?/* 更新tcb的堆棧指針變量值 */ ? ?/** ? ? ? ?from_task->sp = r1 ? ? */ ? ?"ldr r0, [r0] ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?"str r1, [r0] ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " #if !__FPU_USED #error "must use fpu" #endif ? ? ? ?/* 上下文保存結(jié)束 */ "switch_to_thread: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?"ldr r1, =cat_context_to_task_sp_ptr ? ? ? ? " ? ?"ldr r1, [r1] ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?"ldr r1, [r1] ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " #if BSP_TZ_NONSECURE_BUILD #error "not support BSP_TZ_NONSECURE_BUILD" #elif RM_CATOS_PORT_PSPLIM_PRESENT ? ? ? ?"LDMIA ? R1!, {R2} ? ? ? ? ? ? ? ? ? ? ? " /* R1 = PSPLIM */ ? ? ? ?"MSR ? ? PSPLIM, R2 ? ? ? ? ? ? ? ? ? ? ? " /* Restore the PSPLIM register value for the task. */ #endif #if BSP_TZ_NONSECURE_BUILD #error "not support BSP_TZ_NONSECURE_BUILD" #endif ? ? ? ?"b contex_ns_load ? ? ? ? ? ? ? ? ? ? ? ? ? " "contex_ns_load: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " #if __FPU_USED #if BSP_TZ_NONSECURE_BUILD ?#error "not support BSP_TZ_NONSECURE_BUILD" #else ? ? ? ?/* Restore R4-R11 and LR from the process stack. */ ? ? ? ?"LDMIA ? R1!, {R4-R11, LR} ? ? ? ? ? ? ? " #endif ? ? ? ?/* Check to see if the thread being restored is using the FPU. If so, restore S16-S31. */ ? ? ? ?"TST ? ? ? LR, #0x10 ? ? ? ? ? ? ? ? ? ? " ? ? ? ?"IT ? ? ? ?EQ ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ? ? ?"VLDMIAEQ ?R1!, {S16-S31} ? ? ? ? ? ? ? ? " #else #error "must use fpu" #endif "pendsv_exit: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?"msr psp, r1 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " #if !__FPU_USED #error "must use fpu" #endif ? ?"pop {r2} ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " ? ?/* 恢復(fù)屏蔽寄存器值 */ ? ?"msr primask, r2 ? ? ? ? ? ? ? ? ? ? ? ? ? ? " #if __FPU_USED ? ?"bx lr ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " #else #error "must use fpu" #endif ? ?); }
?
2.5 臨界區(qū)的開關(guān)中斷
開關(guān)中斷主要是對primask的操作,和cortex-m3差不多,比較簡單。
左右滑動查看
?
/** * cat_uint32_t cat_hw_irq_disable(void) * 關(guān)中斷方式進入臨界區(qū) * primask-->r0 */ ? ?.global cat_hw_irq_disable ? ?.type cat_hw_irq_disable, %function cat_hw_irq_disable: ? ?mrs r0, primask ? ? ? ? ? ? /* ret = primask */ ? ?cpsid I ? ? ? ? ? ? ? ? ? ? /* disable irq */ ? ?bx lr ? ? ? ? ? ? ? ? ? ? ? /* return ret */ /** * void cat_hw_irq_enable(cat_uint32_t status) * 開中斷方式出臨界區(qū) * r0-->status */ ? ?.global cat_hw_irq_enable ? ?.type cat_hw_irq_enable, %function cat_hw_irq_enable: ? ?msr primask, r0 ? ? ? ? ? ? /* primask = status */ ? ?bx lr
?
2.6 任務(wù)棧初始化
在任務(wù)創(chuàng)建時需要根據(jù)任務(wù)的相關(guān)信息對任務(wù)棧幀中各項進行初始化,包括設(shè)置psr寄存器、任務(wù)入口地址、退出函數(shù)地址和任務(wù)參數(shù)。
需要特別注意的是cortex-m33還需要正確設(shè)置psplim等寄存器。
左右滑動查看
?
/** * @brief 棧初始化 * * @param task_entry ? ?任務(wù)入口函數(shù)地址 * @param parameter ? ? 參數(shù) * @param stack_addr ? ?棧起始地址 * @param exit ? ? ? ? ?任務(wù)退出函數(shù)地址 * @return cat_uint8_t* ? ? 初始化后的棧頂?shù)刂?*/ cat_uint8_t *cat_hw_stack_init(void *task_entry, void *arg, cat_uint8_t *stack_addr, void *exit_func) { ?struct _stack_frame *stack_frame; ?cat_uint32_t ? ? ? ? *stack; ?cat_uint32_t ? ? ? ? i; ?/* 先加上4字節(jié)再8字節(jié)向下取整對齊(相當于四舍五入) */ ?stack = stack_addr + sizeof(cat_uint32_t); ?stack = (cat_uint8_t *)CAT_ALIGN_DOWN((cat_uint32_t)stack, 8); ? ?/* task context saved & restore by hardware: */ ? ?*(--stack) = (cat_uint32_t)0x01000000L; /* xPSR: EPSR.T = 1, thumb mode ? */ ? ?*(--stack) = (cat_uint32_t)task_entry; ? ? ? /* Entry Point */ ? ?*(--stack) = (cat_uint32_t)exit_func; /* R14 (LR) ? ? ? ? ? ? ?*/ ? ?*(--stack) = (cat_uint32_t)0x12121212L; /* R12 ? ? ? ? ? ? ? ? ? ? ? ? ? ?*/ ? ?*(--stack) = (cat_uint32_t)0x03030303L; /* R3 ? ? ? ? ? ? ? ? ? ? ? ? ? ? */ ? ?*(--stack) = (cat_uint32_t)0x02020202L; /* R2 ? ? ? ? ? ? ? ? ? ? ? ? ? ? */ ? ?*(--stack) = (cat_uint32_t)0x01010101L; /* R1 ? ? ? ? ? ? ? ? ? ? ? ? ? ? */ ? ?*(--stack) = (cat_uint32_t)arg; ? ? ? ? /* R0 : argument ? ? ? ? ? ? ? ? ?*/ #if __FPU_USED && !BSP_TZ_NONSECURE_BUILD ? ?*(--stack) = (cat_uint32_t)portINITIAL_EXC_RETURN; /* exe_return值 */ #endif ? ?*(--stack) = (cat_uint32_t)0x11111111L; /* R11 */ ? ?*(--stack) = (cat_uint32_t)0x10101010L; /* R10 */ ? ?*(--stack) = (cat_uint32_t)0x09090909L; /* R9 ?*/ ? ?*(--stack) = (cat_uint32_t)0x08080808L; /* R8 ?*/ ? ?*(--stack) = (cat_uint32_t)0x07070707L; /* R7 ?*/ ? ?*(--stack) = (cat_uint32_t)0x06060606L; /* R6 ?*/ ? ?*(--stack) = (cat_uint32_t)0x05050505L; /* R5 ?*/ ? ?*(--stack) = (cat_uint32_t)0x04040404L; /* R4 ?*/ #if RM_CATOS_PORT_PSPLIM_PRESENT ? ?*(--stack) = (cat_uint32_t)0x00; /* psplim */ #endif ? ?stack_frame = (struct _stack_frame *)stack; #endif /* #if 0 */ ?/* 返回當前棧指針 */ ?return stack; }
?
審核編輯:湯梓紅
評論
查看更多