?
今日分享參加瑞薩RA MCU創(chuàng)意氛圍賽的選手項(xiàng)目——基于優(yōu)先級(jí)的RTOS內(nèi)核。本項(xiàng)目為基于優(yōu)先級(jí)調(diào)度的嵌入式實(shí)時(shí)操作系統(tǒng)內(nèi)核,其中調(diào)度部分使用固定可搶占的優(yōu)先級(jí)調(diào)度機(jī)制;提供了可移植接口以便適配不同架構(gòu)的cpu;重寫了更簡(jiǎn)易更輕量級(jí)的部分庫(kù)函數(shù),比如標(biāo)準(zhǔn)輸入輸出以及字符串相關(guān)操作;除內(nèi)核外還提供部分組件,包括一個(gè)簡(jiǎn)易的shell程序以及設(shè)備驅(qū)動(dòng)框架。具體的操作我們一起來(lái)看看講解吧!
一、簡(jiǎn)介
1.1 項(xiàng)目簡(jiǎn)介
SimpleRTOS(catOS) 是我大學(xué)實(shí)習(xí)期間為了學(xué)習(xí)RTOS編寫的一個(gè)簡(jiǎn)單的內(nèi)核,主要調(diào)度方式基于優(yōu)先級(jí)搶占,該項(xiàng)目重構(gòu)了兩次,故內(nèi)容和功能有所不同,最新版本僅保留了固定優(yōu)先級(jí)調(diào)度方式。
本次項(xiàng)目?jī)?nèi)容為將該內(nèi)核對(duì)野火的瑞薩啟明6M5開發(fā)板進(jìn)行適配,并編寫簡(jiǎn)單的demo驗(yàn)證正確性。如果有什么錯(cuò)誤請(qǐng)大家批評(píng)指正。(文末有項(xiàng)目資料鏈接可供參考)
1.2 開發(fā)板簡(jiǎn)介
官網(wǎng)特性介紹如下:
?支持TrustZone的200MHz Arm Cortex-M33
?安全芯片功能
?1MB - 2MB閃存、448KB支持奇偶校驗(yàn)的SRAM和64KB ECC SRAM
?具有后臺(tái)運(yùn)行能力的雙區(qū)閃存,以及存儲(chǔ)塊交換功能
?8KB數(shù)據(jù)閃存,提供與EEPROM類似的數(shù)據(jù)存儲(chǔ)功能
?100引腳封裝至176引腳封裝
?高速和全速USB 2.0
?CAN FD(也支持CAN 2.0B)
?SCI多功能串口(UART、簡(jiǎn)單SPI、簡(jiǎn)單I2C)
?SPI/I2C多主接口
?SDHI和MMC
二、適配內(nèi)核
2.1 可移植接口概覽
需要實(shí)現(xiàn)的可移植接口包括以下部分
左右滑動(dòng)查看
?
/** * @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 上一個(gè)任務(wù)tcb中堆棧指針變量的 *地址* * @param ?to_task_sp_addr ? 下一個(gè)任務(wù)tcb中堆棧指針變量的 *地址* */ void cat_hw_context_switch(cat_uint32_t from_task_sp_addr, cat_uint32_t to_task_sp_addr); /** * @brief 切換到第一個(gè)任務(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)中斷進(jì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í)鐘中斷頻率,初始化時(shí)設(shè)置時(shí)鐘中斷為關(guān)閉狀態(tài)。
左右滑動(dòng)查看
?
/**
* @brief 硬件初始化
*/
void cat_hw_init(void)
{
/* 設(shè)置系統(tǒng)時(shí)鐘中斷頻率為100Hz(每秒100次) */
cat_set_systick_period(CATOS_SYSTICK_MS);
}
/**
* @brief 初始化時(shí)鐘中斷
*
* @param ms 每個(gè)tick的時(shí)間(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è)置時(shí)鐘中斷優(yōu)先級(jí) */
SysTick->VAL = 0UL; /* 設(shè)置計(jì)數(shù)器裝載值 */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | /* 設(shè)定為內(nèi)核時(shí)鐘FCLK */
SysTick_CTRL_TICKINT_Msk | /* 設(shè)定為systick計(jì)數(shù)器倒數(shù)到0時(shí)觸發(fā)中斷 */
~SysTick_CTRL_ENABLE_Msk; /* 關(guān)閉定時(shí)器中斷,若創(chuàng)建任務(wù)則在catos_start_sched()中開啟該中斷 */
}
?
2.3 開始調(diào)度與切換到第一個(gè)任務(wù)的上下文
開始調(diào)度需要從就緒表中獲取最高優(yōu)先級(jí)任務(wù)并設(shè)置為當(dāng)前任務(wù),并且在恢復(fù)第一個(gè)任務(wù)上下文之前需要打開時(shí)鐘中斷并初始化pendsv中斷以保證調(diào)度的正常工作。
注:這里暫時(shí)沒(méi)有對(duì)不使用fpu的情況適配,參考FreeRTOS的可移植接口實(shí)現(xiàn)。
左右滑動(dòng)查看
?
/**
* @brief 開始調(diào)度
*
*/
void catos_start_sched(void)
{
cat_uint32_t tmp_reg = 0;
struct _cat_task_t *first_task = NULL;
/* 獲取最高優(yōu)先級(jí)任務(wù) */
first_task = cat_sp_task_highest_ready();
/* 因?yàn)槭堑谝粋€(gè)任務(wù),不用像調(diào)度時(shí)判斷是否和上一個(gè)任務(wù)一樣,直接賦值給當(dāng)前任務(wù)就行 */
cat_sp_cur_task = first_task;
/* 允許調(diào)度(打開調(diào)度鎖,并且不在該處進(jìn)行調(diào)度) */
cat_sp_task_sched_enable_without_sched();
/* 開啟時(shí)鐘中斷 */
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
/* 設(shè)置pendsv中斷優(yōu)先級(jí) */
tmp_reg = MEM32(NVIC_SHPR3);
tmp_reg |= NVIC_PENDSV_PRI;
MEM32(NVIC_SHPR3) = tmp_reg;
/* 切換到第一個(gè)任務(wù) */
cat_hw_context_switch_to_first((cat_uint32_t)&(first_task->sp));
}
/**
* @brief 切換到第一個(gè)任務(wù)的上下文
*
*/
void cat_hw_context_switch_to_first(void)
{
__enable_irq();
__ISB();
/* 各個(gè)寄存器地址 */
__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)先級(jí)寄存器 3 (PendSV).
".equ PRI_LVL_PENDSV, 0xFF
"http:// pendsv優(yōu)先級(jí) (最低).
".equ SHPR3_PRI_15, 0xE000ED23
"http:// 系統(tǒng)異常handler優(yōu)先級(jí)寄存器 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中斷,允許中斷后會(huì)立即進(jìn)入pendsv切換 */
"ldr r0, =SCB_ICSR
"
"ldr r1, =ICSR_PENDSVSET
"
"str r1, [r0]
" /* *(SCB_ICSR) = "ICSR_PENDSVSET */
/* 不會(huì)到達(dá)這里 */
"dsb
"
"isb
"
"svc 0
"
);
}
?
2.4 上下文切換
上下文切換使用pendsv中斷進(jìn)行,主要工作為保存當(dāng)前任務(wù)堆棧和寄存器以及恢復(fù)下一個(gè)任務(wù)的堆棧和寄存器。
左右滑動(dòng)查看
?
/**
* void cat_hw_context_switch(void)
* 觸發(fā)pendsv中斷進(jìn)行任務(wù)切換(pendsv的優(yōu)先級(jí)在開始第一個(gè)任務(wù)時(shí)已經(jīng)設(shè)置)
*/
? ?.global cat_hw_context_switch
? ?.type cat_hw_context_switch, %function
cat_hw_context_switch:
? ?/* 將兩個(gè)任務(wù)的堆棧指針變量的 *地址* 加載到臨時(shí)變量中 */
? ?/* 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中斷進(jìn)行切換 */
? ?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)閉全局中斷并保存當(dāng)前中斷屏蔽寄存器中的值方便恢復(fù) */
? ?"mrs r2, primask ? ? ? ? ? ? ? ? ? ? ? ? ? ?
"
? ?"cpsid i ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
"
? ?/* 保存中斷屏蔽寄存器狀態(tài) */
? ?"push {r2} ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
"
? ?/* 獲取from任務(wù)的堆棧指針變量中的值 */
? ?/* 在進(jìn)入pendsv之前 cat_context_from_task_sp_ptr = &(from_task->sp) */
? ?/** 故有:
? ? * r0 = ?&(from_task->sp)
? ? * r1 = *(&(from_task->sp)) 等價(jià)于 r1 = from_task->sp
? ? */
? ?"ldr r0, =cat_context_from_task_sp_ptr ? ? ?
"
? ?"ldr r1, [r0] ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
"
? ?/* 如果為零則說(shuō)明是第一個(gè)任務(wù) */
? ?"cbz r1, switch_to_thread ? ? ? ? ? ? ? ? ?
"
/* 暫時(shí)可能用不到trustzone, 因此直接跳轉(zhuǎn) */
? ?"b contex_ns_store ? ? ? ? ? ? ? ? ? ? ? ? ?
"
/* 暫時(shí)可能用不到 END */
"contex_ns_store: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
"
? ?/* 用戶級(jí)堆棧是psp,特權(quán)級(jí)堆棧是msp */
? ?/* 任務(wù)用的是psp,將當(dāng)前寄存器保存到堆棧中 */
? ?"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)中斷主要是對(duì)primask的操作,和cortex-m3差不多,比較簡(jiǎn)單。
左右滑動(dòng)查看
?
/** * cat_uint32_t cat_hw_irq_disable(void) * 關(guān)中斷方式進(jì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)建時(shí)需要根據(jù)任務(wù)的相關(guān)信息對(duì)任務(wù)棧幀中各項(xiàng)進(jìn)行初始化,包括設(shè)置psr寄存器、任務(wù)入口地址、退出函數(shù)地址和任務(wù)參數(shù)。
需要特別注意的是cortex-m33還需要正確設(shè)置psplim等寄存器。
左右滑動(dòng)查看
?
/**
* @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é)向下取整對(duì)齊(相當(dāng)于四舍五入) */
?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 */
?/* 返回當(dāng)前棧指針 */
?return stack;
}
?
審核編輯:湯梓紅
電子發(fā)燒友App







評(píng)論