1. 移植前准备
1.1 源码下载
UCOS-III Kernel Source: https://github.com/weston-embedded/uC-OS3.git
Micriμm CPU Source : https://github.com/weston-embedded/uC-CPU.git
Micriμm Lib Source: https://github.com/weston-embedded/uC-LIB.git
1.2. 源码介绍
1.2.1 源码组织结构图
1.2.2 源码简介
1)应用层模块
2)BSP 与板子相关的BSP模块 (需要移植) 主要为系统时钟、时间戳相关API
3)µC-OS3/Source 内核源码 与CPU无关的模块
4)µC-OS3/Ports 与CPU相关的模块 (需要移植 )
5)uC-CPU 与特定平台CPU相关的模块(由uc-cpu提供)
6)uC-LIB 与CPU无关的库函数模块
7) µC-OS3/Cfg 配置头文件(需要配置的部分)
调用关系: UC/OS-III/Ports-->uC-CPU --> CPU_BSP
2. 移植配置列表
1)移植 uC-OS3 CPU 相关部分: os_cpu.h, os_cpu_a.asm, os_cpu_c.c
2)移植 uC/CPU 相关部分: cpu_a.asm , cpu_c.c
3)移植板级支持包(BSP) 部分: bsp.c ,bsp.h
4)配置UC/OS-III 以下部分:
µC/OS-III 功能配置 (os_cfg.h)
µC/OS-III 堆栈、系统任务和其他数据大小 (os_cfg_app.h)
µC/OS-III 数据类型 (os_type.h)
具体配置参考: http://t.csdnimg.cn/GNZcY
3. 移植 uC-OS3 CPU
3.1 找到MCU使用的CPU架构
以GD32F303为例,翻阅其手册,查找CPU对应的架构信息如下:
3.2 找到CPU架构对应的文件
在uC-OS3/Ports目录下查找ARMv7-M,找到 ARMv7-M路径: ARM-Cortex-M/ARMv7-M
3.3 开发编译环境列表
ARM-Cortex-M/ARMv7-M 目录下有4个文件夹,分别对应不同的编译器
- ARM: ARM公司提供的编译器,如Keil MDK开发环境。
- CCS: TI的Code Composer Studio编译器。
- GNU: GNU工具链(如GCC)
- IAR: IAR 开发环境
3.4 使用Keil MDK编译器
uC-OS3/Template/bsp_os_dt.c , 有以下系统时钟相关接口需要移植:
BSP_OS_TickInit() //初始化系统时钟滴答定时器
BSP_OS_TickISR() //系统时钟滴答中断服务程序
uC-OS3/Ports/Template目录里的os_cpu.h ,有以下上下文切换相关接口需要实现:
void OSCtxSw (void); // 任务级上下文切换,触发PendSV异常
void OSIntCtxSw (void); // 中断上下文中进行任务切换,触发PendSV异常
void OSStartHighRdy (void); // 启动最高优先级的就绪任务
void OS_CPU_PendSVHandler (void); // PendSV 异常处理程序,用切换当前任务的上下文到下一个准备就绪的任务
查看MCU的CPU架构及所选开发环境对应的文件
uC-OS3/Ports/ARM-Cortex-M/ARMv7-M/os_cpu_c.c
uC-OS3/Ports/ARM-Cortex-M/ARMv7-M/ARM/os_cpu_a.asm
已实现上述的所有接口,如果原厂没有实现的接口,则需要实现以上接口
3.4.1 BSP_OS_TickInit 接口实现
此函数接口用于初始化系统滴答定时器,设置操作系统时钟中断,该函数必须在 OSStart() 调用之后,并且在处理器初始化完成后调用。
优先级设置需要考虑系统中其他中断的优先级配置,确保实时操作系统的滴答计时器中断能够按期触发。
以下是伪代码描述:
void BSP_OS_TickInit(CPU_INT32U freq) {
Install the interrupt vector for the timer used to generate tick interrupts; // 安装定时器中断向量
Configure the timer to generate interrupts at 'freq' Hz; // 配置定时器以产生指定频率的中断
Enable timer interrupts; // 启用定时器中断
}
以下是ARM-Cortex-M/ARMv7-M/os_cpu_c.c已实现的接口:
voidOS_CPU_SysTickInit(CPU_INT32U cnts)
{
CPU_INT32U prio;
CPU_INT32U basepri;
// 设置 BASEPRI 边界
basepri = (CPU_INT32U)(CPU_CFG_KA_IPL_BOUNDARY << (8u - CPU_CFG_NVIC_PRIO_BITS));
CPU_REG_SYST_RVR = cnts - 1u; // 设置重载寄存器// 设置 SysTick 处理程序的优先级
prio = CPU_REG_SCB_SHPRI3;
prio &= 0x00FFFFFFu;
prio |= (basepri << 24u);
CPU_REG_SCB_SHPRI3 = prio;
// 启用计时器
CPU_REG_SYST_CSR |= CPU_REG_SYST_CSR_CLKSOURCE | CPU_REG_SYST_CSR_ENABLE;
// 启用计时器中断
CPU_REG_SYST_CSR |= CPU_REG_SYST_CSR_TICKINT;
}
3.4.2 BSP_OS_TickISR 接口实现
此接口函数用于处理系统时钟滴答(SysTick)中断
以下是该函数的伪代码描述:
BSP_OS_TickISR:
OS_CTX_SAVE ; 保存当前任务的上下文
Disable Interrupts ; 禁用中断
OSIntNestingCtr++ ; 增加中断嵌套计数器
if (OSIntNestingCtr == 1) {
OSTCBCurPtr->StkPtr = SP ; 如果这是第一个中断,保存任务堆栈指针
}
Clear tick interrupt ; 清除滴答中断标志
OSTimeTick() ; 调用OSTimeTick,通知发生了一个滴答
OSIntExit() ; 通知µC/OS-III中断处理结束
OS_CTX_RESTORE ; 恢复任务的上下文
Return from Interrupt/Exception ; 返回到被中断的任务
以下是ARM-Cortex-M/ARMv7-M/os_cpu_c.c已实现的接口:
void OS_CPU_SysTickHandler(void)
{
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
OSIntEnter(); /* 通知uC/OS-III我们正在进入一个ISR */
CPU_CRITICAL_EXIT();
OSTimeTick(); /* 调用uC/OS-III的OSTimeTick() */
OSIntExit(); /* 通知uC/OS-III我们正在离开ISR */
}
3.4.3 OSCtxSw 接口移植
该函数接口实现任务级上下文切换,以下是该函数的汇编实现伪代码:
; (1) 通过软中断调用OSCtxSw
OSCtxSw:
OS_CTX_SAVE ; (2) 保存当前任务的CPU上下文
LDR R0, =OSTCBCurPtr
LDR R0, [R0]
STR SP, [R0] ; (3) 将当前任务的堆栈指针保存到其TCB
BL OSTaskSwHook
LDR R0, =OSPrioHighRdy
LDR R0, [R0]
STR R0, =OSPrioCur
LDR R0, =OSTCBHighRdyPtr
LDR R0, [R0]
LDR SP, [R0] ; (4) 从新任务的TCB中获取堆栈指针
OS_CTX_RESTORE ; (5) 恢复新任务的CPU上下文
BX LR ; (6) 从中断返回,恢复PC和SR
以下是ARM-Cortex-M/ARMv7-M/ARM/os_cpu_a.asm已实现的接口:
OSCtxSw
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
3.4.4 OSIntCtxSw 接口移植
此函数用于在中断上下文中进行任务切换,以下为该函数的汇编实现伪代码:
在所有嵌套的ISR结束时由 OSIntExit() 调用
OSIntCtxSw:
BL OSTaskSwHook
LDR R0, =OSPrioHighRdy
LDR R0, [R0]
STR R0, =OSPrioCur
LDR R0, =OSTCBHighRdyPtr
LDR R0, [R0]
LDR SP, [R0]
OS_CTX_RESTORE
BX LR
以下是ARM-Cortex-M/ARMv7-M/ARM/os_cpu_a.asm已实现的接口:
OSCtxSw
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
3.4.5 OSStartHighRdy 接口移植
此函数用于启动最高优先级的就绪任务,以下是该函数的汇编实现伪代码:
OSStartHighRdy:
BL OSTaskSwHook ; 调用任务切换钩子函数
LDR R0, =OSTCBHighRdyPtr ; 获取最高优先级任务的TCB指针
LDR R0, [R0]
LDR SP, [R0] ; (1) 获取最高优先级任务的堆栈指针
OS_CTX_RESTORE ; (2) 恢复新任务的CPU上下文
BX LR ; (3) 从中断返回,恢复PC和SR
以下是ARM-Cortex-M/ARMv7-M/ARM/os_cpu_a.asm已实现的接口:
OSStartHighRdy
CPSID I ; Prevent interruption during context switch
MOV32 R0, NVIC_SYSPRI14 ; Set the PendSV exception priority
MOV32 R1, NVIC_PENDSV_PRI
STRB R1, [R0]
MOVS R0, #0 ; Set the PSP to 0 for initial context switch call
MSR PSP, R0
MOV32 R0, OS_CPU_ExceptStkBase ; Initialize the MSP to the OS_CPU_ExceptStkBase
LDR R1, [R0]
MSR MSP, R1
BL OSTaskSwHook ; Call OSTaskSwHook() for FPU Push & Pop
MOV32 R0, OSPrioCur ; OSPrioCur = OSPrioHighRdy;
MOV32 R1, OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
MOV32 R0, OSTCBCurPtr ; OSTCBCurPtr = OSTCBHighRdyPtr;
MOV32 R1, OSTCBHighRdyPtr
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
MSR PSP, R0 ; Load PSP with new process SP
MRS R0, CONTROL
ORR R0, R0, #2
BIC R0, R0, #4 ; Clear FPCA bit to indicate FPU is not in use
MSR CONTROL, R0
ISB ; Sync instruction stream
LDMFD SP!, {R4-R11, LR} ; Restore r4-11, lr from new process stack
LDMFD SP!, {R0-R3} ; Restore r0, r3
LDMFD SP!, {R12, LR} ; Load R12 and LR
LDMFD SP!, {R1, R2} ; Load PC and discard xPSR
CPSIE I
BX R1
3.4.6 OS_CPU_PendSVHandler 接口实现
此接口函数用于引发上下文切换,以下是该函数的实现步骤和详细代码
获取进程堆栈指针:将进程堆栈指针(PSP)保存到R0
保存剩余寄存器:将寄存器R4-R11和R14保存到进程堆栈
保存进程堆栈指针:将进程堆栈指针保存到当前任务的TCB中
调用OSTaskSwHook:执行任务切换钩子函数
更新当前任务和准备就绪任务:更新当前任务的优先级和TCB指针
获取新任务堆栈指针:从准备就绪任务的TCB中获取堆栈指针,并将其加载到PSP中
恢复寄存器:从新任务堆栈恢复寄存器R4-R11和R14
恢复上下文:从异常返回,恢复剩余上下文
以下是ARM-Cortex-M/ARMv7-M/ARM/os_cpu_a.asm已实现的接口:
OS_CPU_PendSVHandler:
CPSID I ; 禁止中断
MOV32 R2, OS_KA_BASEPRI_Boundary ; 设置BASEPRI优先级
LDR R1, [R2]
MSR BASEPRI, R1
DSB
ISB
CPSIE I
MRS R0, PSP ; 获取进程堆栈指针
IF {FPU} != "SoftVFP"
TST R14, #0x10
IT EQ
VSTMDBEQ R0!, {S16-S31} ; 如果任务使用FPU,保存高位FPU寄存器
ENDIF
STMFD R0!, {R4-R11, R14} ; 保存R4-R11和R14寄存器
MOV32 R5, OSTCBCurPtr ; 保存当前任务的堆栈指针
LDR R1, [R5]
STR R0, [R1]
MOV R4, LR ; 保存LR寄存器值
BL OSTaskSwHook ; 调用任务切换钩子函数
MOV32 R0, OSPrioCur ; 更新当前任务优先级
MOV32 R1, OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
MOV32 R1, OSTCBHighRdyPtr ; 更新当前任务的TCB指针
LDR R2, [R1]
STR R2, [R5]
ORR LR, R4, #0x04 ; 确保异常返回使用进程堆栈
LDR R0, [R2] ; 获取新任务的堆栈指针
LDMFD R0!, {R4-R11, R14} ; 恢复R4-R11和R14寄存器
IF {FPU} != "SoftVFP"
TST R14, #0x10
IT EQ
VLDMIAEQ R0!, {S16-S31} ; 恢复高位FPU寄存器
ENDIF
MSR PSP, R0 ; 加载新的进程堆栈指针
MOV32 R2, #0 ; 恢复BASEPRI优先级
CPSID I
MSR BASEPRI, R2
DSB
ISB
CPSIE I
BX LR ; 异常返回,将恢复剩余上下文
ALIGN
END
4. 移植 uC/CPU
uC-CPU/BSP/Template/bsp_cpu.c 有以下CPU时间戳相关接口需要实现:
CPU_TS_TmrInit() //CPU 时间戳计时器初始化
CPU_TS_TmrRd() //获取当前 CPU 时间戳计时器的计数值
CPU_TS32_to_uSec() //将时间戳计数转换为微秒
如果启用了 CPU 时间戳或 CPU 中断禁用时间测量功能,必须实现这些接口
uC-CPU/Template/cpu_a.asm 有以下接口需要实现:
CPU_IntDis ;禁用中断
CPU_IntEn ;启用中断
CPU_SR_Save ;保存CPU状态寄存器
CPU_SR_Restore ;恢复CPU状态寄存器
CPU_CntLeadZeros;计数前导零
uC-CPU/ARM-Cortex-M/ARMv7-M/ARM/cpu_a.asm 已实现上述接口
4.1 CPU_TS_TmrInit() 接口移植
该接口是CPU 时间戳计时器初始化函数 , 该计时器用于记录精确的时间戳或测量 CPU 禁用中断的时间。以下是基于GD32F303的实现代码:
void CPU_TS_TmrInit (void)
{
CPU_INT32U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
BSP_REG_DEM_CR |= (CPU_INT32U)BSP_BIT_DEM_CR_TRCENA; /* Enable Cortex-M4's DWT CYCCNT reg. */
BSP_REG_DWT_CYCCNT = (CPU_INT32U)0u;
BSP_REG_DWT_CR |= (CPU_INT32U)BSP_BIT_DWT_CR_CYCCNTENA;
CPU_TS_TmrFreqSet((CPU_TS_TMR_FREQ)fclk_freq);
}
4.2 CPU_TS_TmrRd 接口移植
该 函数用于获取当前 CPU 时间戳计时器的计数值,以下是基于GD32F303的实现代码:
CPU_TS_TMR CPU_TS_TmrRd (void)
{
CPU_TS_TMR ts_tmr_cnts;
ts_tmr_cnts = (CPU_TS_TMR)BSP_REG_DWT_CYCCNT;
return (ts_tmr_cnts);
}
4.3 CPU_TS32_to_uSec 接口移植
该函数用于将时间戳计数转换为微秒,以下是基于GD32F303的实现代码:
CPU_INT64U CPU_TS32_to_uSec (CPU_TS32 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
5. 移植验证
5.1. 构建工程目录
├── app // 应用程序
├── bsp // 板级支持包,包含系统需要移植的接口及硬件相关库文件及接口
└── gd32f30x
├── bsp.c //需要移植的接口
├── bsp.h
├── gd32f30x_it.c
├── gd32f30x_it.h
├── gd32f30x_libopt.h
├── Library
├── mdk-project
├── uC-CFG //把 uC-CPU 及uC-OS3 需要配置的文件集中到此文件夹
├── uC-CPU
├── uC-LIB
└── uC-OS3