初次听说ucos还是在还大学即将毕业的时候,姐夫给我介绍他研发的产品,用的ucosII,还特别热心的给我准备了一套调试的东西和代码,当时也是没耐心,心思也没放在上面,后来也就没下文了,想起来当时还是挺无知的,有价值的东西放在面前,都没有发现,早点开始学习,没准现在也应该能在另一方面有点成就了。
不过学习这种事,对于普通人来说,很少是天生就能去接受,毕竟这是一件令人承受压力的过程,尤其是对于年轻人来说,就是需要一点挫折一点悔悟,才有一点积累,才能享受一点进步。我们都希望孩子从小热爱学习,其实都是那份成绩带来的虚荣在推动,很少去思考学习真正的目的。
直到我们进入了社会,才知道学习能够带来的巨大作用,原来,学习,是真的香,知识,真的有力量
言归正传
正好手里有一块STM8的单片机,前几天搭了开发环境,那就试一下移植个操作系统上去吧,想起了这个ucosII。
uC/OS II(Micro Control Operation System Two)是一个可以基于ROM运行的、可裁减的、抢占式、实时多任务内核,具有高度可移植性,特别适合于微处理器和控制器,是和很多商业操作系统性能相当的实时操作系统(RTOS)。
网上找了很多资料,大概把思路理清了。
如果用户理解了处理器和 C 编译器的技术细节,移植µC/OS-Ⅱ的工作实际上是非常简 单的。前提是您的处理器和编译器满足了µC/OS-Ⅱ的要求,并且已经有了必要工具。移植
工作包括以下几个内容: l 用#define 设置一个常量的值(OS_CPU.H)
l 声明 10 个数据类型(OS_CPU.H)
l 用#define 声明三个宏(OS_CPU.H)
l 用 C 语言编写六个简单的函数(OS_CPU_C.C)
l 编写四个汇编语言函数(OS_CPU_A.ASM)
根据处理器的不同,一个移植实例可能需要编写或改写 50 至 300 行的代码,需要的时
间从几个小时到一星期不等。
-----摘自《uCOS-II(邵贝贝).pdf》
万事俱备,开干
代码准备
首先创建好工程文件夹,里面定义好各个模块代码放置的文件夹,我是如下定义的
这里只是存放用,后续的工程,我们会在工程中建立对应的group,添加上对应的文件
然后把所用的文件拷贝过去,其中stm8l的标准库,ucosII的源码,都是官方提供的,无需修改,只需要放过去即可,俗称,拷贝。
下面是文件图
因为用到了定时器4做中断,所以这里用了clk和tim4,如果用到其他标准库,例如GPIO等,就还是放在这里就可以。
这里,我们记得将源码中的os_cfg_r.h修改为os_cfg.h
stm8l15x_conf.h中注释掉其他头文件,只保留两个我们用到的c文件的头文件
其他文件,我们稍后再添加。
创建工程
然后我们打开IAR,创建一个新的工程,然后创建对应group,并且添加好对应的c文件。这里只需要添加c文件即可。
其中main文件为测试ucos创建任务写的,后面我们添加内容。
然后我们修改工程的芯片类型
增加头文件路径及宏定义
好了,准备开始接受error和warnnings的冲击吧
编译过程
其中,我们为移植工作做的修改,全部放在mywork文件下,这样就显得比较清楚
开始编译,
错误1
增加一个app_cfg.h放在mywork文件夹下。
错误2,(如果前面我们修改重命名之后,就不会在出现了)
重命名原有的os_cfg_r.h为os_cfg.h
错误3
新建一个空文件os_cpu.h放在mywork文件夹下
错误4
101个错误,显示大量宏定义缺失,
在os_cpu.h中增加宏定义
#ifndef __OS_CPU_H
#define __OS_CPU_H
#include <intrinsics.h>
#ifdef OS_CPU_GLOBALS
#define OS_CPU_EXT
#else
#define OS_CPU_EXT extern
#endif
/*********************************************************************************************************
Date types(Compiler specific) 数据类型(和编译器相关)
*********************************************************************************************************/
typedef unsigned char BOOLEAN; /* Boolean 布尔变量 */
typedef unsigned char INT8U; /* Unsigned 8 bit quantity */
typedef signed char INT8S; /* Signed 8 bit quantity */
typedef unsigned short INT16U; /* Unsigned 16 bit quantity */
typedef signed short INT16S; /* Signed 16 bit quantity */
typedef unsigned long INT32U; /* Unsigned 32 bit quantity */
typedef signed long INT32S; /* Signed 32 bit quantity */
typedef float FP32; /* Single precision floating */
typedef double FP64; /* Double precision floating */
typedef unsigned char OS_STK; /* Each stack entry is 32-bit */
typedef unsigned char OS_CPU_SR; /* Define size of CPU status register (PSR = 32 bits) */
typedef signed char int8;
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef signed long int32;
typedef unsigned long uint32;
/*********************************************************************************************************
Other definitions 其他定义
*********************************************************************************************************/
//栈指针增长方向
#define OS_STK_GROWTH 1
//任务切换 是在 uC/OS-II 从低优先级切换到高优先级任务,时调用的,通过引发一次中断来进行任务切换,所以这个宏定义是一条软中断指令
#define OS_TASK_SW() OSCtxSw()
/*********************************************************************************************************
Method of critical section management 临界区管理方法
*********************************************************************************************************/
#define OS_CRITICAL_METHOD 3
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() cpu_sr=CriticalEnter()
#define OS_EXIT_CRITICAL() CriticalExit(cpu_sr)
#endif
//这里声明了几个函数,后续会定义
#if __CODE_MODEL__==__SMALL_CODE_MODEL__
void __near_OSCtxSw(void);
void __near_OSIntCtxSw(void);
void __near_OSStartHighRdy(void);
#define OSCtxSw __near_OSCtxSw
#define OSIntCtxSw __near_OSIntCtxSw
#define OSStartHighRdy __near_OSStartHighRdy
#else
void __far_OSCtxSw(void);
void __far_OSIntCtxSw(void);
void __far_OSStartHighRdy(void);
#define OSCtxSw __far_OSCtxSw
#define OSIntCtxSw __far_OSIntCtxSw
#define OSStartHighRdy __far_OSStartHighRdy
#endif
#endif
错误5
在app_cfg.h中增加
#ifndef APP_CFG_H
#define APP_CFG_H
#define OS_TASK_TMR_PRIO (OS_LOWEST_PRIO - 2)
#endif
这几个问题才是小菜
关键步骤
到这一步,就需要我们自己实现(参考别人的代码实现)了
其中汇编函数:
函数 | 含义 |
---|---|
OSIntCtxSw | 中断服务程序切换准备 |
OSStartHighRdy | 这个函数由 OSStart()函数调用,作用是使就绪态任务中优先级最高的任务开始运行。 |
OSCtxSw | 中断服务程序切换 |
OSTickISR | 提供一个周期性的时钟源,来实现时间的延迟和超时功能。名字可以随意定,我们用的是_interrupt_27 |
C语言函数:
函数 | 含义 |
---|---|
OSSTaskStkInit | 由 OSTaskCreat 函数调用,作用是初始化任务的堆栈结构 |
关于CPU 钩子函数有关的函数 |
我们在mywork下创建如下文件
首先在os_cpu_c.c中,定义相关的C语言函数定义
#include <intrinsics.h>
#include "os_cpu.h"
#include "os_cfg.h"
#include "ucos_ii.h"
#include "stm8l15x_conf.h"
#define OS_CPU_GLOBALS
INT8U __zero_in_ram__=0;
/*********************************
Local variables 局部变量
**********************************/
#if OS_TMR_EN > 0
static INT16U OSTmrCtr;
#endif
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSInitHookBegin (void)
{
#if OS_TMR_EN > 0
OSTmrCtr = 0;
#endif
}
#endif
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSInitHookEnd (void)
{
}
#endif
#if OS_CPU_HOOKS_EN > 0
void OSTaskCreateHook (OS_TCB *ptcb)
{
(void)ptcb; /* Prevent compiler warning 防止编译警告 */
}
#endif
#if OS_CPU_HOOKS_EN > 0
void OSTaskDelHook (OS_TCB *ptcb)
{
(void)ptcb; /* Prevent compiler warning 防止编译警告 */
}
#endif
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION >= 251
void OSTaskIdleHook (void)
{
}
#endif
#if OS_CPU_HOOKS_EN > 0
void OSTaskStatHook (void)
{
}
#endif
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt)
{
INT8U *psoft_stk;
INT32U tmp;
(void)opt;
psoft_stk = (INT8U *)ptos;
tmp = (INT32U)task;
*psoft_stk-- = (INT8U)(tmp & 0xFF); //PCL
tmp >>= 8;
*psoft_stk-- = (INT8U)(tmp & 0xFF); //PCH
tmp >>= 8;
*psoft_stk-- = (INT8U)(tmp & 0xFF); //PCE
tmp >>= 8;
*psoft_stk-- = (INT8U)0x01; //YL
*psoft_stk-- = (INT8U)0x02; //YH
*psoft_stk-- = ((INT16U)p_arg)&0XFF; //XL
*psoft_stk-- = (((INT16U)p_arg)>>8)&0XFF; //XH
*psoft_stk-- = (INT8U)0x05; //A
*psoft_stk-- = (INT8U)0x20; //CC
*psoft_stk-- = (INT8U)0x00; //?B0
*psoft_stk-- = (INT8U)0x01; //?B0
*psoft_stk-- = (INT8U)0x02; //?B0
*psoft_stk-- = (INT8U)0x03; //?B0
*psoft_stk-- = (INT8U)0x04; //?B0
*psoft_stk-- = (INT8U)0x05; //?B0
*psoft_stk-- = (INT8U)0x06; //?B0
*psoft_stk-- = (INT8U)0x07; //?B0
*psoft_stk-- = (INT8U)0x08; //?B0
*psoft_stk-- = (INT8U)0x09; //?B0
*psoft_stk-- = (INT8U)0x10; //?B0
*psoft_stk-- = (INT8U)0x11; //?B0
*psoft_stk-- = (INT8U)0x12; //?B0
*psoft_stk-- = (INT8U)0x13; //?B0
*psoft_stk-- = (INT8U)0x14; //?B0
*psoft_stk-- = (INT8U)0x15; //?B0
return ((OS_STK *)psoft_stk);
}
void OSTaskSwHook (void)
{
}
#if (OS_CPU_HOOKS_EN > 0) && (OS_VERSION > 203)
void OSTCBInitHook (OS_TCB *ptcb)
{
(void)ptcb; /* Prevent compiler warning 防止编译警告 */
}
#endif
#if ((OS_CPU_HOOKS_EN > 0) && (OS_TIME_TICK_HOOK_EN > 0)) || (OS_VERSION < 283)
void OSTimeTickHook (void)
{
#if OS_TMR_EN > 0
OSTmrCtr++;
if (OSTmrCtr >= (OS_TICKS_PER_SEC / OS_TMR_CFG_TICKS_PER_SEC)) {
OSTmrCtr = 0;
OSTmrSignal();
}
#endif
}
#endif
然后在os_cpu_a.s实现其汇编的三个函数,这里用到的汇编方式,根据__CODE_MODEL__不同定义了两组函数,最终只有一组起作用,根据你在IAR中的code model选项来决定的
#include "os_cpu_a.inc"
EXTERN OSTaskSwHook
EXTERN OSRunning
EXTERN OSTCBHighRdy
EXTERN OSTCBCur
EXTERN OSPrioCur
EXTERN OSPrioHighRdy
EXTERN OSIntEnter
EXTERN OSIntExit
EXTERN OSTimeTick
PUBLIC __far_OSStartHighRdy
PUBLIC __far_OSCtxSw
PUBLIC __far_OSIntCtxSw
PUBLIC __near_OSStartHighRdy
PUBLIC __near_OSCtxSw
PUBLIC __near_OSIntCtxSw
EXTERN __zero_in_ram__
SECTION `.far_func.text`:CODE:ROOT(0)
__far_OSStartHighRdy:
CALLX OSTaskSwHook //OSTaskSwHook()
MOV L:OSRunning,#1 //OSRunning=1
LOAD_NEW_STK_PTR
RESTORE_REGS
RETX
SECTION `.far_func.text`:CODE:ROOT(0)
__far_OSCtxSw:
SAVE_REGS
SAVE_OLD_STK_PTR
CALLX OSTaskSwHook //OSTaskSwHook()
MOV L:OSPrioCur,L:OSPrioHighRdy //OSPrioCur=OSPrioHighRdy
LDW X, L:OSTCBHighRdy
LDW L:OSTCBCur, X //OSTCBCur=OSTCBHighRdy
LOAD_NEW_STK_PTR
RESTORE_REGS
RETX
SECTION `.far_func.text`:CODE:ROOT(0)
__far_OSIntCtxSw:
CALLX OSTaskSwHook //OSTaskSwHook()
MOV L:OSPrioCur,L:OSPrioHighRdy //OSPrioCur=OSPrioHighRdy
LDW X, L:OSTCBHighRdy
LDW L:OSTCBCur, X //OSTCBCur=OSTCBHighRdy
LOAD_NEW_STK_PTR
INT_RESTORE_REGS
IRET
SECTION `.near_func.text`:CODE:ROOT(0)
__near_OSStartHighRdy:
CALLX OSTaskSwHook //OSTaskSwHook()
MOV L:OSRunning,#1 //OSRunning=1
LOAD_NEW_STK_PTR
RESTORE_REGS
RETX
SECTION `.near_func.text`:CODE:ROOT(0)
__near_OSCtxSw:
SAVE_REGS
SAVE_OLD_STK_PTR
CALLX OSTaskSwHook //OSTaskSwHook()
MOV L:OSPrioCur,L:OSPrioHighRdy //OSPrioCur=OSPrioHighRdy
LDW X, L:OSTCBHighRdy
LDW L:OSTCBCur, X //OSTCBCur=OSTCBHighRdy
LOAD_NEW_STK_PTR
RESTORE_REGS
RETX
SECTION `.near_func.text`:CODE:ROOT(0)
__near_OSIntCtxSw:
CALLX OSTaskSwHook //OSTaskSwHook()
MOV L:OSPrioCur,L:OSPrioHighRdy //OSPrioCur=OSPrioHighRdy
LDW X, L:OSTCBHighRdy
LDW L:OSTCBCur, X //OSTCBCur=OSTCBHighRdy
LOAD_NEW_STK_PTR
INT_RESTORE_REGS
IRET
END
os_cpu_a.inc中定义了一组操作。
periph_ticks.s中定义了一个中断处理函数_interrupt_27。
最后,我们打开inline函数许可,这里调整为High。
如果遇到这个错误
关闭debug,IS_DEBUG_EN改为0u
系统部分就编译通过。哈哈哈哈。
系统测试
然后我们写一个测试主函数,创建两个任务,看能否顺利切换。
void task0( void * pdata )
{
int test=0;
while(1)
{
test++;
OSTimeDly(LONG_DELAY_TICKS);
test++;
}
}
void task1(void * pdata )
{
int test=0;
while(1)
{
test++;
OSTimeDly(LONG_DELAY_TICKS);
test++;
}
}
int main()
{
TIM4_Init();
OSInit();
OSTaskCreate(task0,(void *)OS_TASK_0_STK_SIZE, &Task0Stack[OS_TASK_0_STK_SIZE-1], OS_TASK_0_PRIO );
OSTaskCreate(task1,(void *)OS_TASK_1_STK_SIZE, &Task1Stack[OS_TASK_1_STK_SIZE-1], OS_TASK_1_PRIO );
OSStart();
return 0;
}
我们开启stlink调试模式
连接好单片机,就可以下载程序进行调试了。设置两个断点,运行程序查看
发现能够顺利切换进程。
补充
内容大部分为实际操作结果,涉及汇编的部分,代码还在理解当中,如果有错误之处还请多多担待。后续还会补充说明。
所有代码均可下载
传送门
写在最后
最近有位老师,她质疑三十万。