真是很郁闷,昨天晚上边看移植代码边记下来的笔记不知道怎么回事在保存的时候竟然不见了。。。5555。。。一个晚上工作的结果啊,关键是我是第一次也是正式开始移植的学习之路啊。。。真是够倒霉的。。。。今天在工作之前先把昨天的笔记重新回顾一下,其实后来想想也许是件好事,可以让我今天在不借助其他的帮助的情况下自己看代码自己跟自己讲一遍,其实很多看起来是倒霉看起来是灰心的事情把我们的观点换一下那么就是一件好事。。这样的情况发生在我的身上已经挺多次了。。。。好啦,废话不说,开始补昨天的日记
UCOS-II的移植需要提供2,3个文件分别介绍如下:
一:OS_CPU.H
1 与编译器有关的数据类型
只是按照不同的编译器编写对应的数据类型的typedef
对应于ARM7的数据类型的编写如下
typedef unsigned char BOOLEAN; /* 布尔变量 */
typedef unsigned char INT8U; /* 无符号8位整型变量 */
typedef signed char INT8S; /* 有符号8位整型变量 */
typedef unsigned short INT16U; /* 无符号16位整型变量 */
typedef signed short INT16S; /* 有符号16位整型变量 */
typedef unsigned int INT32U; /* 无符号32位整型变量 */
typedef signed int INT32S; /* 有符号32位整型变量 */
typedef float FP32; /* 单精度浮点数(32位长度) */
typedef double FP64; /* 双精度浮点数(64位长度) */
在上面定义的各种数据类型中按照ARM7的堆栈宽度选择INT32U
typedef INT32U OS_STK; /* 堆栈是32位宽度 */
接下来一部分是为了兼容低版本UCOS的数据类型所编写的代码,在UCOS-II中暂不考虑
2 与处理器相关的代码
先定义中断的实现方式,预先设定的中断方式有三种,在ARM7中设置为方式2
#define OS_CRITICAL_METHOD 2 /* 选择开、关中断的方式 */
接下来的一段是我暂时还没有完全搞懂的一部分,只知道是设定了12个软件中断的函数,当调用这些函数之前都会执行对应中断号的事情。。具体的看到后面应该能完全搞懂软件中断的实现方式,该段代码在后面的文件中会有具体的解释,这里暂时不看
定义堆栈的生长方式,ARM7内核支持两种生长方式,但是ADS的C语言编译器只支持从上往下的生长方式,因此:
#define OS_STK_GROWTH 1 /* 堆栈是从上往下长的,0-从下往上的生长方式 */
最后几行分别定义了用户模式01和系统模式1f以及IRQ中断禁止的指令80三个立即数,方便调用。
还有两个预定义往后看应该知道作用,暂不考虑,不是很重要。
二:OS_CPU_C.C
个文件中要求用户编写10个简单的C函数,但是只有1个函数是必要的,其余的函数必须声明,但不一定要包含任何代码,大致看了一下作用好像是用来调试之类的。唯一要编写的是OSTaskStkInit()
返回的地址指针是指向的最后一个存入的数据,而不是一个空地址。
void SWI_Exception(int SWI_Num, int *Regs):参数SWI_Num对应前面文件中定义的功能号,其中0、1号的功能在后面的文件中定义,这里只定义了其他10个功能。
2、3分别对应关中断和开中断
关中断:MRS R0, SPSR //在软件中断的时候直接对程序状态保存寄存器SPSR操作也就是对CPSR的操作
ORR R0, R0, #NoInt //在汇编语言中对寄存器的对应位置位用ORR,清零用BIC
MSR SPSR_c, R0 //
SPSR_c表示的是只改变SPSR的控制段的8位代码,其他三段_f,_s,_x中标志位在_f段,其他为保留位
开中断:MRS R0, SPSR //在开中断中基本与上面相同,只是ORR改成BIC清零
BIC R0, R0, #NoInt
MSR SPSR_c, R
由于需要实现中断嵌套,所以只有当关中断的计数器减为0的时候才能够开中断,而且每次关中断的时候该计数器都应该加1。另外,插入汇编语言时用_asm指令。
80、81、82、83分别对应系统模式、用户模式、ARM指令集、THUMB指令集
系统模式:MRS R0, SPSR
BIC R0, R0, #0x1f //先将控制模式的低5位清零
ORR R0, R0, #SYS32Mode //设置成系统模式的1F
MSR SPSR_c, R0
用户模式:MRS R0, SPSR
BIC R0, R0, #0x1f
ORR R0, R0, #USR32Mode //设置成用户模式的10
MSR SPSR_c, R0
ARM指令集与THUMB指令集的代码如下:
ptcb = OSTCBPrioTbl[Regs[0]];
if (ptcb != NULL)
{
ptcb -> OSTCBStkPtr[1] &= ~(1 << 5);
}
ptcb = OSTCBPrioTbl[Regs[0]];
if (ptcb != NULL)
{
ptcb -> OSTCBStkPtr[1] |= (1 << 5);
}
昨天就是看到这里,出现了一个意识到是不能忽悠的地方就是UCOS里面的任务控制块OS_TCB的概念,因此今天的任务就是把这部分看看。。。
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
UCOS-II移植ARM的读书笔记(12.13)
一点一点来,什么不会就学什么,都不会就都学。。。没有问题只要你肯努力。。。。。。
__OSStartHighRdy
MSR CPSR_c, #(NoInt | SYS32Mode)
;MSR:在ARM中只有MSR能够直接设置状态寄存器CPSR或SPSR,可以是立即数或者源寄存器,NoInt是禁止中断,SYS32Mode是系统模式
;告诉uC/OS-II自身已经运行
LDR R4, =OSRunning ;OSRunning正在运行多任务的标志,
=OSRunning是把OSRunning的地址加载到R4,R4里存的是一个地址。。。
MOV R5, #1
STRB R5, [R4] ;将R5存储到R4存的地址的变量即OSRunning中,也就是将OSRunning置1
BL OSTaskSwHook ;调用钩子函数,OSTaskSwHook 是用于扩展的,在任务切换的时候执行用户自己定义的功能。
OSTCBHighRdy指向最高优先级任务的控制块TCB的指针!!!将放指针的地址放到R6中。
LDR R6, [R6] ;将R6地址处的数据读出即
OSTCBHighRdy的地址放到R6中
B OSIntCtxSw_1 ;跳转到OSIntCtxSw_1
AREA SWIStacks, DATA, NOINIT,ALIGN=2
SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;管理模式堆栈空间
继续昨天没有看完的代码
OSIntCtxSw
;下面为保存任务环境
LDR R2, [SP, #20] ;获取PC,放入R2
LDR R12, [SP, #16] ;获取R12,//
R12存的什么东西啊???
MRS R0, CPSR
MSR CPSR_c, #(NoInt | SYS32Mode) ;进入系统模式并禁止中断
MOV R1, LR ;R1放LR值
STMFD SP!, {R1-R2} ;保存LR,PC,将R1,R2存入SP
STMFD SP!, {R4-R12} ;保存R4-R12,将R4-12存入SP
MSR CPSR_c, R0 ;再回到之前的模式
LDMFD SP!, {R4-R7} ;获取R0-R3
ADD SP, SP, #8 ;出栈R12,PC
MSR CPSR_c, #(NoInt | SYS32Mode)
STMFD SP!, {R4-R7} ;保存R0-R3
LDR R1, =OsEnterSum ;获取OsEnterSum
LDR R2, [R1]
STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum
;保存当前任务堆栈指针到当前任务的TCB
LDR R1, =OSTCBCur
LDR R1, [R1]
STR SP, [R1]
BL OSTaskSwHook ;调用钩子函数
;OSPrioCur <= OSPrioHighRdy
LDR R4, =OSPrioCur
LDR R5, =OSPrioHighRdy
LDRB R6, [R5]
STRB R6, [R4]
;OSTCBCur <= OSTCBHighRdy
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, =OSTCBCur
STR R6, [R4]
OSIntCtxSw_1
;获取新任务堆栈指针
LDR R4, [R6] ;把
OSTCBHighRdy指向最高优先级任务的控制块TCB的指针
给R4
ADD SP, R4, #68 ;17寄存器:CPSR,OsEnterSum,R0-R12,LR,SP
LDR LR, [SP, #-8] ;取出LR放到LR
MSR CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式并且保持禁止中断
MOV SP, R4 ;设置堆栈指针
LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum。LDMFD数据出栈,放入R4,R5
;恢复新任务的OsEnterSum
LDR R3, =OsEnterSum ;OsEnterSum的地址存入R3
STR R4, [R3] ;把R4的值赋给OsEnterSum
MSR SPSR_cxsf, R5 ;恢复CPSR;在管理模式里是修改SPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;运行新任务 ,恢复现场,异常处理返回
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
UCOS-II移植ARM的读书笔记(12.15)
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
第一个范例:
void main (void)
{
PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); /* PC的清屏函数,PC_DispClrScr(INT8U color),这里调用时是前景色为白色,背景色为黑色*/
OSInit(); /* Initialize uC/OS-II ,初始化UCOS-II,在使用任何功能之前必须调用OSInit函数,建立两个任务:空闲任务-所有其他任务均未就绪时运行,统计任务-计算CPU的利用率*/
PC_DOSSaveReturn(); /* Save environment to return to DOS 允许程序在正式开始多任务前,保存重要的寄存器的值,以保证UCOS能够正常地返回DOS */
PC_VectSet(uCOS, OSCtxSw); /* Install uC/OS-II's context switch vector ,用于设定中断向量表的内容。*/
RandomSem = OSSemCreate(1); /* Random number semaphore,建立一个信号量,并制定信号量的初值为1,OSSemCreate(1)返回一个指向信号量的指针,那么之后对该信号量的操作全部通过这个指针来实现 */
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0); /*至少建立一个任务,TaskStart为指向该任务运行代码的指针,第二个参数是一个指向任务初始化数据的指针,第三个是任务的堆栈栈顶,当堆栈是从上到下,必须把堆栈高地址传给该函数,最后一个参数指定建立的任务的优先级,数值越小优先级越高,每个任务的优先级都是介于0-62之间独一无二的。*/
OSStart(); /* Start multitasking,调用该函数将控制权交给内核,开始运行多任务 */
}
OSStart函数会让优先级最高的就绪任务开始运行,即TaskStart
void TaskStart (void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
char s[100];
INT16S key;
pdata = pdata; /* Prevent compiler warning,这是这个参数是当任务建立时传递过来的一个指针 */
TaskStartDispInit(); /* Initialize the display 初始化屏幕显示 */
OS_ENTER_CRITICAL(); /*关中断*/
PC_VectSet(0x08, OSTickISR); /* Install uC/OS-II's clock tick ISR */
PC_SetTickRate(OS_TICKS_PER_SEC); /* Reprogram tick rate */
OS_EXIT_CRITICAL(); /*开中断*/
OSStatInit(); /* Initialize uC/OS-II's statistics测试所使用的处理器的速度,得知处理器在运行所有应用任务时实际的CPU使用率 */
TaskStartCreateTasks(); /* Create all the application tasks建立更多任务,10个显示不同字符的任务,在每次建立一个新任务的时候,UCOS都会判断新建立的任务是否比建立它们的任务优先级更高,如果更高,这个新建立的任务将立刻开始运行。 */
for (;;) {
TaskStartDisp(); /* Update the display */
if (PC_GetKey(&key) == TRUE) { /* See if key has been pressed */
if (key == 0x1B) { /* Yes, see if it's the ESCAPE key */
PC_DOSReturn(); /* Return to DOS */
}
}
OSCtxSwCtr = 0; /* Clear context switch counter 每秒都将记录任务切换次数的清零 */
OSTimeDlyHMSM(0, 0, 1, 0); /* Wait one second 将自身挂起1s,1s是通过四个参数传送的,小时,分钟,秒,毫秒 */
}
}
static void TaskStartCreateTasks (void)
{
INT8U i;
for (i = 0; i < N_TASKS; i++) { /* Create N_TASKS identical tasks */
TaskData[i] = '0' + i; /* Each task will display its own letter */
OSTaskCreate(Task, (void *)&TaskData[i], &TaskStk[i][TASK_STK_SIZE - 1], i + 1);
}
}
上面的函数循环建立了十个Task任务,下面是Task任务的代码
void Task (void *pdata)
{
INT8U x;
INT8U y;
INT8U err;
for (;;) {
OSSemPend(RandomSem, 0, &err); /* 获取信号量*/
x = random(80); /* 获得随机数x*/
y = random(16); /*获得随机数y*/
OSSemPost(RandomSem); /* 释放信号量*/
/* Display the task number on the screen */
PC_DispChar(x, y + 5, *(char *)pdata, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
OSTimeDly(1); /* Delay 1 clock tick 通知ucos该任务本次运行已经结束,可以让其他低优先级的任务运行了,参数1代表该任务延时1个时钟节拍,在200Hz的情况下就是5ms */
}
}
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
UCOS-II移植ARM的读书笔记(12.16)
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
范例二:
void main (void)
{
OS_STK *ptos;
OS_STK *pbos;
INT32U size;
PC_DispClrScr(DISP_FGND_WHITE); /* Clear the screen */
OSInit(); /* Initialize uC/OS-II */
PC_DOSSaveReturn(); /* Save environment to return to DOS */
PC_VectSet(uCOS, OSCtxSw); /* Install uC/OS-II's context switch vector */
PC_ElapsedInit(); /* Initialized elapsed time measurement 初始化时间测量功能,用来精确地记录PC_ElapsedStart()和PC_ElapsedStop()的函数调用时刻,通过这两个时刻的差值可以很容易得到这两个时刻之间的执行代码的运行时间 */
ptos = &TaskStartStk[TASK_STK_SIZE - 1]; /* TaskStart() will use Floating-Point */
pbos = &TaskStartStk[0];
size = TASK_STK_SIZE;
OSTaskStkInit_FPE_x86(&ptos, &pbos, &size);
OSTaskCreateExt(TaskStart,
(void *)0,
ptos,
TASK_START_PRIO,
TASK_START_ID, /*任务标志符,范例二中没有使用它*/
pbos,
size,
(void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); /* 使用了OSTaskCreate的扩展函数,支持对堆栈的修改和运行时对堆栈容量的检查。最后一个参数的设置表明允许堆栈检查并且需要在任务建立时将堆栈清零*/
OSStart(); /* Start multitasking 让最高优先级的任务运行即TaskStart*/
}
TaskStart的代码如下:
void TaskStart (void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
INT16S key;
pdata = pdata; /* Prevent compiler warning */
TaskStartDispInit(); /* Setup the display 初始化屏幕,在该函数内部设定屏幕初始化的图像*/
OS_ENTER_CRITICAL(); /* 关中断 */
PC_VectSet(0x08, OSTickISR);
PC_SetTickRate(OS_TICKS_PER_SEC); /* Reprogram tick rate 设定时钟节拍大小 */
OS_EXIT_CRITICAL();
OSStatInit(); /* Initialize uC/OS-II's statistics 测试所使用的处理器的速度,得知处理器在运行所有应用任务时实际的CPU使用率 */
AckMbox = OSMboxCreate((void *)0); /* Create 2 message mailboxes在范例二中涉及到了消息的概念,任务4将向任务5发送消息,并且任务5会回复一个应答消息,因此这里建立了两个通信工具即邮箱,它允许任务或中断向另一个任务发送指针变量*/
TxMbox = OSMboxCreate((void *)0);
TaskStartCreateTasks(); /* Create all other tasks 创建所有其他的任务 */
for (;;) {
TaskStartDisp(); /* Update the display 更新各项统计数据并显示*/
if (PC_GetKey(&key)) { /* See if key has been pressed */
if (key == 0x1B) { /* Yes, see if it's the ESCAPE key */
PC_DOSReturn(); /* Yes, return to DOS */
}
}
OSCtxSwCtr = 0; /* Clear context switch counter */
OSTimeDly(OS_TICKS_PER_SEC); /* Wait one second 挂起1s */
}
}
创建其他六个任务,暂不执行,只是创建,等待CPU被放出
static void TaskStartCreateTasks (void)
{
OSTaskCreateExt(TaskClk,
(void *)0,
&TaskClkStk[TASK_STK_SIZE - 1],
TASK_CLK_PRIO,
TASK_CLK_ID,
&TaskClkStk[0],
TASK_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
OSTaskCreateExt(Task1,
(void *)0,
&Task1Stk[TASK_STK_SIZE - 1],
TASK_1_PRIO,
TASK_1_ID,
&Task1Stk[0],
TASK_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
OSTaskCreateExt(Task2,
(void *)0,
&Task2Stk[TASK_STK_SIZE - 1],
TASK_2_PRIO,
TASK_2_ID,
&Task2Stk[0],
TASK_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
OSTaskCreateExt(Task3,
(void *)0,
&Task3Stk[TASK_STK_SIZE - 1],
TASK_3_PRIO,
TASK_3_ID,
&Task3Stk[0],
TASK_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
OSTaskCreateExt(Task4,
(void *)0,
&Task4Stk[TASK_STK_SIZE-1],
TASK_4_PRIO,
TASK_4_ID,
&Task4Stk[0],
TASK_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
OSTaskCreateExt(Task5,
(void *)0,
&Task5Stk[TASK_STK_SIZE-1],
TASK_5_PRIO,
TASK_5_ID,
&Task5Stk[0],
TASK_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
}
分别看其他几个任务的代码如下:
void Task1 (void *pdata)
{
INT8U err;
OS_STK_DATA data; /* Storage for task stack data */
INT16U time; /* Execution time (in uS) */
INT8U i;
char s[80];
pdata = pdata;
for (;;) {
for (i = 0; i < 7; i++) {
PC_ElapsedStart();
err = OSTaskStkChk(TASK_START_PRIO + i, &data); /*该函数是用来检查任务堆栈使用情况*/
time = PC_ElapsedStop(); /*测量上面的OSTaskStkChk函数的运行时间,方法是将这个函数放在PC_ElapsedStart()和PC_ElapsedStop()之间即可,它会返回以ms计量的时间间隔*/
if (err == OS_NO_ERR) {
sprintf(s, "%4ld %4ld %4ld %6d",
data.OSFree + data.OSUsed,
data.OSFree,
data.OSUsed,
time);/*data是上面检查任务堆栈使用情况的函数的第二个参数,也是它的返回值之一*/
PC_DispStr(19, 12 + i, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);/*把统计结果打印出来*/
}
}
OSTimeDlyHMSM(0, 0, 0, 100); /* Delay for 100 mS 100ms挂起一次 */
}
}
void Task2 (void *data)
{
data = data;
for (;;) {
PC_DispChar(70, 15, '|', DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDly(10);
PC_DispChar(70, 15, '/', DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDly(10);
PC_DispChar(70, 15, '-', DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDly(10);
PC_DispChar(70, 15, '//', DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDly(10);/*由四个字符轮流显示的任务*/
}
}
void Task3 (void *data)
{
char dummy[500];
INT16U i;
data = data;
for (i = 0; i < 499; i++) { /* Use up the stack with 'junk' */
dummy[i] = '?';
}
for (;;) {
PC_DispChar(70, 16, '|', DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDly(20);
PC_DispChar(70, 16, '//', DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDly(20);
PC_DispChar(70, 16, '-', DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDly(20);
PC_DispChar(70, 16, '/', DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDly(20);
}
}
void Task4 (void *data)
{
char txmsg;
INT8U err;
data = data;
txmsg = 'A';
for (;;) {
OSMboxPost(TxMbox, (void *)&txmsg); /* Send message to Task #5向邮箱TxMbox发送一个字符 */
OSMboxPend(AckMbox, 0, &err); /* Wait for acknowledgement from Task #5等待应答,第二个参数指定了等待超时的时限,单位为时钟节拍 */
txmsg++; /* Next message to send更新消息 */
if (txmsg == 'Z') {
txmsg = 'A'; /* Start new series of messages */
}
}
}
void Task5 (void *data)
{
char *rxmsg;
INT8U err;
data = data;
for (;;) {
rxmsg = (char *)OSMboxPend(TxMbox, 0, &err); /* Wait for message from Task #4无限期等待邮箱消息 */
PC_DispChar(70, 18, *rxmsg, DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDlyHMSM(0, 0, 1, 0); /* Wait 1 second 挂起1s */
OSMboxPost(AckMbox, (void *)1); /* Acknowledge reception of msg 给邮箱AckMbox发送消息 */
最后一个任务
void TaskClk (void *data)
{
char s[40];
data = data;
for (;;) {
PC_GetDateTime(s); /*得到PC当前的日期和时间*/
PC_DispStr(60, 23, s, DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDly(OS_TICKS_PER_SEC);
}
}
}
}
--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
范例三:
在该范例中首先定义了一个用户任务数据结构,在这个结构中有该范例所需要的数据参数,和每个任务相关的,因此定了一个该数据结构类型的变量数组,分配给7个自建的任务
另外使用了消息队列的通讯方式,可以容纳多则消息。建立消息队列需要两个元素即OS_EVENT的数据结构和一串指针。
void main (void)
{
PC_DispClrScr(DISP_BGND_BLACK); /* Clear the screen */
OSInit(); /* Initialize uC/OS-II */
PC_DOSSaveReturn(); /* Save environment to return to DOS */
PC_VectSet(uCOS, OSCtxSw); /* Install uC/OS-II's context switch vector */
PC_ElapsedInit(); /* Initialized elapsed time measurement */
strcpy(TaskUserData[TASK_START_ID].TaskName, "StartTask");
OSTaskCreateExt(TaskStart,
(void *)0,
&TaskStartStk[TASK_STK_SIZE - 1],
TASK_START_PRIO,
TASK_START_ID,
&TaskStartStk[0],
TASK_STK_SIZE,
&TaskUserData[TASK_START_ID],/*在UCOS中,每个任务的TCB控制块都可以保存一个用户定义的数据结构的指针*/
0);
OSStart(); /* Start multitasking */
}
void TaskStart (void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
INT16S key;
pdata = pdata; /* Prevent compiler warning */
TaskStartDispInit(); /* Setup the display */
OS_ENTER_CRITICAL(); /* Install uC/OS-II's clock tick ISR */
PC_VectSet(0x08, OSTickISR);
PC_SetTickRate(OS_TICKS_PER_SEC); /* Reprogram tick rate */
OS_EXIT_CRITICAL();
OSStatInit(); /* Initialize uC/OS-II's statistics */
MsgQueue = OSQCreate(&MsgQueueTbl[0], MSG_QUEUE_SIZE); /* Create a message queue 建立了一个消息队列*/
TaskStartCreateTasks();
for (;;) {
TaskStartDisp(); /* Update the display */
if (PC_GetKey(&key)) { /* See if key has been pressed */
if (key == 0x1B) { /* Yes, see if it's the ESCAPE key */
PC_DOSReturn(); /* Yes, return to DOS */
}
}
OSCtxSwCtr = 0; /* Clear the context switch counter */
OSTimeDly(OS_TICKS_PER_SEC); /* Wait one second */
}
}
void TaskStartCreateTasks (void)
{
strcpy(TaskUserData[TASK_CLK_ID].TaskName, "Clock Task");
OSTaskCreateExt(TaskClk,
(void *)0,
&TaskClkStk[TASK_STK_SIZE - 1],
TASK_CLK_PRIO,
TASK_CLK_ID,
&TaskClkStk[0],
TASK_STK_SIZE,
&TaskUserData[TASK_CLK_ID],
0);
strcpy(TaskUserData[TASK_1_ID].TaskName, "MsgQ Rx Task");
OSTaskCreateExt(Task1,
(void *)0,
&Task1Stk[TASK_STK_SIZE - 1],
TASK_1_PRIO,
TASK_1_ID,
&Task1Stk[0],
TASK_STK_SIZE,
&TaskUserData[TASK_1_ID],
0);
strcpy(TaskUserData[TASK_2_ID].TaskName, "MsgQ Tx Task #2");
OSTaskCreateExt(Task2,
(void *)0,
&Task2Stk[TASK_STK_SIZE - 1],
TASK_2_PRIO,
TASK_2_ID,
&Task2Stk[0],
TASK_STK_SIZE,
&TaskUserData[TASK_2_ID],
0);
strcpy(TaskUserData[TASK_3_ID].TaskName, "MsgQ Tx Task #3");
OSTaskCreateExt(Task3,
(void *)0,
&Task3Stk[TASK_STK_SIZE - 1],
TASK_3_PRIO,
TASK_3_ID,
&Task3Stk[0],
TASK_STK_SIZE,
&TaskUserData[TASK_3_ID],
0);
strcpy(TaskUserData[TASK_4_ID].TaskName, "MsgQ Tx Task #4");
OSTaskCreateExt(Task4,
(void *)0,
&Task4Stk[TASK_STK_SIZE - 1],
TASK_4_PRIO,
TASK_4_ID,
&Task4Stk[0],
TASK_STK_SIZE,
&TaskUserData[TASK_4_ID],
0);
strcpy(TaskUserData[TASK_5_ID].TaskName, "TimeDlyTask");
OSTaskCreateExt(Task5,
(void *)0,
&Task5Stk[TASK_STK_SIZE - 1],
TASK_5_PRIO,
TASK_5_ID,
&Task5Stk[0],
TASK_STK_SIZE,
&TaskUserData[TASK_5_ID],
0);
}
void Task1 (void *pdata)
{
char *msg;
INT8U err;
pdata = pdata;
for (;;) {
msg = (char *)OSQPend(MsgQueue, 0, &err); /*无限期等待消息队列的消息*/
PC_DispStr(70, 13, msg, DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDlyHMSM(0, 0, 0, 100);
}
}
void Task2 (void *pdata)
{
char msg[20];
pdata = pdata;
strcpy(&msg[0], "Task 2");
for (;;) {
OSQPost(MsgQueue, (void *)&msg[0]); /*向消息队列发送一个字符串的指针*/
OSTimeDlyHMSM(0, 0, 0, 500);
}
}
void Task3 (void *pdata)
{
char msg[20];
pdata = pdata;
strcpy(&msg[0], "Task 3");
for (;;) {
OSQPost(MsgQueue, (void *)&msg[0]);
OSTimeDlyHMSM(0, 0, 0, 500);
}
}
void Task4 (void *pdata)
{
char msg[20];
pdata = pdata;
strcpy(&msg[0], "Task 4");
for (;;) {
OSQPost(MsgQueue, (void *)&msg[0]);
OSTimeDlyHMSM(0, 0, 0, 500);
}
}
Task3和4都是发送字符串,间隔半秒
void Task5 (void *pdata)
{
pdata = pdata;
for (;;) {
OSTimeDlyHMSM(0, 0, 0, 100);
}
}
void TaskClk (void *pdata)
{
char s[40];
pdata = pdata;
for (;;) {
PC_GetDateTime(s);
PC_DispStr(60, 23, s, DISP_FGND_YELLOW + DISP_BGND_BLUE);
OSTimeDlyHMSM(0, 0, 0, 500);
}
}
关于定义的接口函数的修改
OSTaskSwHook函数是在一个低优先级的任务切换到一个高优先级的任务时被调用
void OSTaskSwHook (void)
{
INT16U time;
TASK_USER_DATA *puser;
time = PC_ElapsedStop(); /* This task is done可以得到当前被切换的任务的运行时间 */
PC_ElapsedStart(); /* Start for next task开始记录当前任务的运行时间 */
puser = OSTCBCur->OSTCBExtPtr; /* Point to used data
这个函数被调用的时候高优先级的任务还没有执行,因此OSTCBCur指向当前任务的任务控制块,而OSTCBHighRdy指向即将被调用运行的任务的任务控制块,
OSTCBCur->OSTCBExtPtr是当前任务在建立时指定的数据结构的指针!!!*/
if (puser != (TASK_USER_DATA *)0) {
puser->TaskCtr++; /* Increment task counter 记录切换次数*/
puser->TaskExecTime = time; /* Update the task's execution time记录本次执行时间 */
puser->TaskTotExecTime += time; /* Update the task's total execution time 执行时间总和 */
}
}
当允许使用统计任务时即OS_TASK_STAT_EN配置常数设为1,统计任务将首先调用定义的OSTaskStatHook接口函数
void OSTaskStatHook (void)
{
char s[80];
INT8U i;
INT32U total;
INT8U pct;
total = 0L; /* Totalize TOT. EXEC. TIME for each task */
for (i = 0; i < 7; i++) {
total += TaskUserData[i].TaskTotExecTime;
DispTaskStat(i); /* Display task data 计算所有任务的执行时间 */
}
if (total > 0) {
for (i = 0; i < 7; i++) { /* Derive percentage of each task */
pct = 100 * TaskUserData[i].TaskTotExecTime / total;
sprintf(s, "%3d %%", pct);
PC_DispStr(62, i + 11, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
}
}
if (total > 1000000000L) { /* Reset total time counters at 1 billion */
for (i = 0; i < 7; i++) {
TaskUserData[i].TaskTotExecTime = 0L;
}
}
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
UCOS-II移植ARM的读书笔记(12.17)
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
1 前后台系统
不复杂的小系统通常选择前后台系统,应用程序是一个无限循环。在循环中调用相应的函数完成相应的操作,这部分可以看成后台行为。中断服务程序处理异步事件,可以看成前台行为。
2 代码的临界段
需要在前后关开中断的代码,不能被打断的代码
3 资源
输入输出设备,各种变量,结构,数组
4 共享资源
可以被多个任务使用的资源
5多任务
通过CPU在许多任务之间转换和调度
6任务
每个任务都是一个无限循环,都可能处于五种状态之一:休眠,就绪,运行,挂起,被中断。UCOS中提供各种函数使任务能从一个状态变为另一个状态
每个任务都有自己的CPU寄存器和栈空间以及TCB任务控制块
7任务切换
任务切换过程是将正在运行任务的CPU寄存器全部内容保存到任务自己的栈区中,再将下一个要运行的任务的当前状况从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行
8内核
负责管理各个任务,为每个任务分配CPU时间,并负责任务间的通信
9调度
决定该轮到哪个任务运行。主要是通过优先级来决定。总是让处于就绪态的优先级最高的任务先运行。
10不可剥夺型内核
在任务级,不可剥夺型内核允许使用不可重入函数 函数不必担心被重复调用,因为每个时刻都只有一个任务在运行。当然,该函数不能具有放弃CPU使用权的功能。
11可剥夺型内核
当系统响应时间很重要,就需要使用可剥夺型内核。最高优先级任务一旦就绪,总能得到CPU的使用权。当运行的任务使一个更高优先级的任务进入就绪态,当前任务的CPU使用权就被剥夺或挂起,更高优先级的任务获得CPU的使用权。如果是中断服务子程序造成的,中断完成后被中断的任务被挂起,执行更高优先级的任务。
在可剥夺型内核中,要谨慎使用不可重入函数。因为低优先级的和高优先级的任务有可能都调用该函数。
总是让就绪态的高优先级的任务先运行,中断服务程序可以抢占CPU
12可重入函数
可重入函数或者只使用局部变量,要么就是使用全局变量并且对全局变量予以保护。
13时间片轮番调度法
UCOS中各个任务的优先级都是不同的。。不支持时间片轮番调度
14任务优先级
从0开始,越小的优先级越高
15静态优先级
在执行过程中优先级是不变的
16动态优先级
优先级在运行过程中可以变化
17优先级反转
避免优先级反转的方法是使占有共享资源的任务的优先级升到最高。
18任务优先级分配
19互斥条件
满足互斥条件的一般方法:
关中断
使用测试并置位指令,获得共享资源时置位,只有为零才有权获得共享资源
禁止作任务切换:用给任务切换上锁然后开锁的方法
利用信号量:信号量的操作:初始化create 挂起pend 发送post
UCOS中是等待信号量任务中优先级最高的
20死锁
死锁也称为抱死指两个任务无限期地互相等待对方控制着的资源。
21同步
可以单向同步也可以双向同步
22事件标志
当某个任务要与多个事件同步时,须使用事件标志。
23任务间通信
任务间或中断服务与任务间的通信。有两种途径:通过全程变量或者发消息给另一个任务
24消息邮箱
把一则消息放到邮箱,通过内核也可以接收这则消息。
25消息队列
用于给任务发消息。
26中断
27中断延迟
28中断响应
29中断恢复时间
对于可剥夺型内核,中断的恢复要复杂一些。在中断服务子程序的末尾,要调用一个由实时内核提供的函数。这个函数用于判断中断是否脱离了所有嵌套。如果脱离了嵌套就判断是否中断让一个更高优先级的任务进入就绪。
30中断延迟、响应及恢复
31中断处理时间
32非屏蔽中断
33时钟节拍
34对存储器的需求
使用多任务内核时内核本身需要额外的代码空间ROM。因为每个任务都是独立运行的,必须给每个任务提供单独的栈空间RAM。
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
1临界段,OS_ENTER_CRITICAL和OS_EXIT_CRITICAL
开关中断的实现方法分三种:
1)直接用处理器指令
2)在堆栈中保存中断的开关状态,然后再关中断。
3)通过编译器提供的c函数来保存处理器状态字的值。
2任务
3任务状态
睡眠态:在ROM或RAM中,交给UCOS要调用下面两个函数之一:OSTaskCreate或者OSTaskCreateExt,调用之后告诉了UCOS任务的起始地址,优先级,要使用多少栈空间。
就绪态:建立了任务之后,就进入就绪态。如果是由任务建立的新任务且优先级更高,那么新建的任务将立即得到CPU使用权。通过OSTaskDel将一个任务返回到睡眠态。
运行态:调用OSStart可以启动多任务,该函数运行用户初始化代码中已建的优先级最高的任务。
等待态:正在运行的任务通过两个函数将自己延迟即进入等待状态。OSTimeDly或者OSTimeDlyHMSM。这两个函数将会立即执行强制任务切换,让下一个优先级最高且进入就绪态的任务运行。当延时时间到后,系统服务函数OSTimeTick将会让等待的任务进入就绪态。在运行中的任务如果调用OSFlagPend、OSSemPend、OSMutexPend、OSMboxPend或者OSQPend时时间并没有发生,那么该任务就进入等待态,此时最高优先级的就绪态任务进入运行态。当等待事件发生或者等待超时,该任务就从等待态进入就绪态。
中断服务态:正在运行的任务可以被中断。被中断了的任务进入中断服务态。响应中断时该任务被挂起,中断服务子程序可能报告多个事件发生,从而使得更高优先级的任务进入就绪态,当中断返回时会重新判断优先级,让最高优先级的任务运行,如果由更高优先级的任务那么先执行,直到被挂起的任务的优先级最高才会返回到被中断挂起的任务。
4任务控制块TCB
空任务控制块指针OSTCBFreeList指向的任务控制块便赋给了该任务。任务建立时,任务建立函数调用任务控制块初始化函数OS_TCBInit。在初始化OS_TCB的时候调用了用户自定义的函数OSTCBInitHook和OSTaskCreateHook函数。两个函数都是扩展作用的。当新建的任务块要插入表中时先要关中断,新任务的控制块总是插在OSTCBList表的开头。
5就绪表
每个就绪的任务都放在就绪表中,就绪表中有2个变量OSRdyGrp和OSRdyTbl【】。OSRdyGrp中任务按优先级分组,8个任务一组,8位表示8组任务中每组是否有进入就绪态的任务。进入就绪态后,就绪表OSRdyTbl【】中相应元素的相应位置1。因此,使任务进入就绪态的程序为:
OSRdyGrp |= OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |= OSMapTbl[prio & 0x07]; OSMapTbl[]是屏蔽字,将0-7的下标转换成各自位置1的8位值
通过优先级判定表OSUnMapTbl查找任务的优先级,即OSUnMapTbl[OSRdyGrp]×8+OSUnMapTbl[OSRdyTbl[OSUnMapTbl[OSRdyGrp]]]。得到优先级后,查任务控制块优先级表OSTCBPrioTbl【】得到指向相应任务的任务控制块OS_TCB。
6任务调度
最后就可以使用宏调用OS_TASK_SW完成实际上的任务切换
因此UCOS运行就绪态任务要做的就是恢复所有的CPU寄存器并运行中断返回指令。这里是一段重点,为了实现任务切换,运行OS_TASK_SW人为模仿了一次中断。在ARM中由软中断指令来实现上述操作。必须给汇编语言函数OSCtxSw提供中断向量。OSCtxSw除了需要OS_TCBHighRdy指向即将被挂起的任务,还需让当前任务控制块OSTCBCur指向即将被挂起的任务。OSCtxSw挂起了正在执行的任务而让CPU执行更重要的任务。
7任务级的任务切换 OSCtxSw
OSCtxSw是宏调用通常含有软中断指令,切换是靠中断级代码完成的。UCOS将与实际处理器相关的软件中断机制封装起来易于移植。P93-P95详细介绍了整个过程。恩。。大概明白了任务切换的实现方式,为我理解移植工作打下了不小的基础啊。。。。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
UCOS-II移植ARM的读书笔记(12.20)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
郁闷,昨天写的东西又忘记存起来了。。。今天又要返工
简单回顾一下昨天看的
8 给调度器上锁和开锁
上锁函数OSSchedlock,调用该函数可以禁止任务调度,保持该任务对CPU的使用权,不过中断还是可以识别,中断服务也能得到,因为中断是开着的,中断和调度是两个意思。其中变量OSLockNesting跟踪OSSchedLock函数被调用的次数所以允许嵌套函数。如果OSLockNesting=0调度重新得到允许。
9空闲任务
UCOS-II中总要建立一个空闲任务,主要是计数,然后有一个用户定义的函数OSTaskIdleHook。
10统计任务
除了空闲任务还有一个统计运行时间的任务OSTaskStat,它告诉用户应用程序使用了多少CPU时间,用百分比表示。
11UCOS-II中的中断
中断服务子程序的编写:保存全部CPU寄存器;调用OSIntEnter或者OSIntNesting直接加1;如果是中断的第一层,立即将堆栈指针保存到这个任务;如果需要重新允许中断,必须清中断源,重新开中断;用户设定;调用脱离中断函数OSIntExit,标志着中断服务子程序的结束。
OSIntExit是使中断离开的函数,当中断嵌套层数计数器和锁定嵌套计数器都为0才重新开始调度,然后选择一个优先级最高的任务。最后应该调用中断切换函数OSIntCtxSw而不应该调用任务切换函数OS_TASK_SW。因为在中断之前已经把CPU寄存器存入到中断了的任务堆栈中不需要再用。这部分也牵涉到后面的移植部分。
12时钟节拍
在调用OSStart之后应做的第一件事情就是初始化定时器中断。时钟节拍服务是通过在中断服务子程序中调用OSTimeTick实现的。
时钟节拍中断服务子程序:
OSTickISR(void)
{
保存CPU寄存器的值
调用OSIntEnter或是将OSIntNesting加1
如果OSIntNesting等于1则将当前堆栈指针存入当前任务控制块的堆栈中
调用OSTimeTick
清发出中断设备的中断
重新允许中断
调用OSIntExit
恢复处理器寄存器的值
执行中断返回指令
}
其中OSTimeTick通过OSTimeTickHook函数进行扩展。除此之外最大的任务就是给每个用户任务控制块OS_TCB中的时间延时项OSTCBDly减1
13UCOS-II初始化
OSInit函数
void OSInit (void)
{
#if OS_VERSION >= 204
OSInitHookBegin(); /* Call port specific initialization code */
#endif
OS_InitMisc(); /* Initialize miscellaneous variables */
OS_InitRdyList(); /* Initialize the Ready List */
OS_InitTCBList(); /* Initialize the free list of OS_TCBs */
OS_InitEventList(); /* Initialize the free list of OS_EVENTs */
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
OS_FlagInit(); /* Initialize the event flag structures */
#endif
#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
OS_MemInit(); /* Initialize the memory manager */
#endif
#if (OS_Q_EN > 0) && (OS_MAX_QS > 0)
OS_QInit(); /* Initialize the message queue structures */
#endif
OS_InitTaskIdle(); /* Create the Idle Task */
#if OS_TASK_STAT_EN > 0
OS_InitTaskStat(); /* Create the Statistic Task */
#endif
#if OS_VERSION >= 204
OSInitHookEnd(); /* Call port specific init. code */
#endif
}
初始化中会建立两个任务,并且初始化5个空的数据结构缓冲区:任务控制缓冲池、事件控制块缓冲池、消息队列缓冲池、标志控制块缓冲池、存储控制块缓冲池,缓冲池的容量在OS_CFG.H中定义
14UCOS-II的启动
OSInit初始化UCOS-II;
通过调用OSTaskCreate或者OSTaskCreateExt创建至少一个任务;
OSStart开始多任务调度,永远不会返回;
OSStart的主要任务:
从任务就绪表中找到用户建立的优先级最高任务的任务控制块;
调用高优先级就绪任务启动函数OSStartHighRdy。OSStartHighRdy函数与选择的微处理器相关,也就是与移植相关,实际上,函数OSStartHighRdy是将任务栈中保存的值弹回到CPU寄存器中,然后执行一条中断返回指令,中断返回指令强制执行该任务代码,该函数永远不会返回到OSStart
内核终于裹完了一遍,也明朗了很多,感觉可以开始继续上个星期的移植工作了,恩。。。加油加油
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
之前刚开始的时候是直接看移植代码,后来看到后面实在看不下去了,转过头回去看了一个星期的内核结构,以前也看过一遍内核结构,但是有点晕晕的,现在重新看了一次清楚多了,相信回过头来看移植部分也应该更清楚了。
现在先来掌握一下关于软件中断swi的内容,这是我比较发晕的源泉
软中断:
中断不返回形式:void _swi(swi_num) swi_name(arguments)
返回一个结果到R0中 int _swi(swi_num) swi_name(arguments);最多可以返回四个结果R0-R3到一个结构struct type{ int a,b,c,d}中 type(返回类型) _value_in_regs(返回多个结果的修饰符) _swi(swi_num) swi_name(arguments);
在ARM中实现软中断的方法我在blog里面搜了很多文章也没有看到讲的通俗一点的,还是自己看ARM的移植代码吧
首先定义了一堆软中断的中断号,其中0和1的中断服务子程序是用汇编编写的,其他的都是在c语言编写的中断服务子程序SWI_Exception中。
__swi(0x00) void OS_TASK_SW(void); /* 任务级任务切换函数 */
__swi(0x01) void _OSStartHighRdy(void); /* 运行优先级最高的任务 */
__swi(0x02) void OS_ENTER_CRITICAL(void); /* 关中断 */
__swi(0x03) void OS_EXIT_CRITICAL(void); /* 开中断 */
__swi(0x40) void *GetOSFunctionAddr(int Index); /* 获取系统服务函数入口 */
__swi(0x41) void *GetUsrFunctionAddr(int Index);/* 获取自定义服务函数入口 */
__swi(0x42) void OSISRBegin(void); /* 中断开始处理 */
__swi(0x43) int OSISRNeedSwap(void); /* 判断中断是否需要切换 */
__swi(0x80) void ChangeToSYSMode(void); /* 任务切换到系统模式 */
__swi(0x81) void ChangeToUSRMode(void); /* 任务切换到用户模式 */
__swi(0x82) void TaskIsARM(INT8U prio); /* 任务代码是ARM代码 */
__swi(0x83) void TaskIsTHUMB(INT8U prio); /* 任务代码是THUMB */
比如在程序运行到调用OS_TASK_SW(void)函数时,就产生软件中断,然后就进入中断服务子程序,按照什么指令走呢?恩,就按照下面这个代码,这个代码是将软件中断异常处理程序挂接到内核的作用的,是在启动代码中实现的:
LDR PC,SWI_Addr
SWI_Addr DCD SoftwareInterrupt
因此当产生软中断之后PC就跳到了SoftwareInterrupt,这时就算真正进入了软件异常中断处理部分了,然后就是执行下面的汇编代码
SoftwareInterrupt
LDR SP, StackSvc ; 重新设置堆栈指针
STMFD SP!, {R0-R3, R12, LR}
MOV R1, SP ; R1指向参数存储位置
MRS R3, SPSR
TST R3, #T_bit ; 中断前是否是Thumb状态,判断SPSR的T位是不是为零
LDRNEH R0, [LR,#-2] ; 不为零即THUMB指令集: 取得Thumb状态SWI号
BICNE R0, R0, #0xff00 ;在THUMB指令集中软中断功能号为8位,所以取低八位即为功能号
LDREQ R0, [LR,#-4] ; 为零即ARM指令集: 取得arm状态SWI号
BICEQ R0, R0, #0xFF000000 ;在ARM指令集中软中断功能号为24位,所以取低6位即为功能号
; r0 = SWI号,R1指向参数存储位置
CMP R0, #1
LDRLO PC, =OSIntCtxSw ;功能号为0到OSIntCtxSw执行中断任务切换函数
LDREQ PC, =__OSStartHighRdy ; SWI 0x01为第一次任务切换
BL SWI_Exception ;否则进入c编写的中断服务函数
LDMFD SP!, {R0-R3, R12, PC}^
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
怎么进入c编写的中断服务子程序SWI_Exception呢?通过下面的申明
IMPORT SWI_Exception ;软中断异常处理程序,表示将c程序中的该函数挂接到此段汇编代码中
同样的道理
EXPORT __OSStartHighRdy
EXPORT OSIntCtxSw ;中断退出时的入口,参见startup.s中的IRQ_Handler
EXPORT SoftwareInterrupt ;软中断入口
上面的申明是将该段汇编代码挂接到外面,因此在外部可以直接调用函数名
继续看OS_CPU_A.S的其他部分代码,就是两个软件异常中断处理函数OSIntCtxSw和OSStarHighRdy
OSIntCtxSw代码是中断服务子程序使得更高优先级的任务进入就绪状态后,中断返回后需要切换到该任务时调用的,这是被切换的任务的CPU寄存器的值已经在响应中断后存入了堆栈中,因此,这里不需要重复保存了直接切换任务即可,具体过程看代码
OSIntCtxSw
;下面为保存任务环境
;当响应软件异常中断后进入了系统模式,在上面的代码中我们可以看到,进入系统模式时保存的堆栈结构从顶到底依次是:R0,R1,R2,R3,R12,LR,而在用户模式中任务的堆栈结构应该是:OsEnterSum,CPSR,RO-12,LR,PC,所以在进行软件中断任务切换之前先要保存原来任务的堆栈结构。
LDR R2, [SP, #20] ;获取PC
LDR R12, [SP, #16] ;获取R12
MRS R0, CPSR
MSR CPSR_c, #(NoInt | SYS32Mode)
MOV R1, LR
STMFD SP!, {R1-R2} ;保存LR,PC
STMFD SP!, {R4-R12} ;保存R4-R12
MSR CPSR_c, R0
LDMFD SP!, {R4-R7} ;获取R0-R3
ADD SP, SP, #8 ;出栈R12,PC
MSR CPSR_c, #(NoInt | SYS32Mode)
STMFD SP!, {R4-R7} ;保存R0-R3
LDR R1, =OsEnterSum ;获取OsEnterSum
LDR R2, [R1]
STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum
;保存当前任务堆栈指针到当前任务的TCB
LDR R1, =OSTCBCur
LDR R1, [R1]
STR SP, [R1]
BL OSTaskSwHook ;调用钩子函数
;OSPrioCur <= OSPrioHighRdy
LDR R4, =OSPrioCur
LDR R5, =OSPrioHighRdy
LDRB R6, [R5]
STRB R6, [R4]
;OSTCBCur <= OSTCBHighRdy
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, =OSTCBCur
STR R6, [R4]
OSIntCtxSw_1
;获取新任务堆栈指针
LDR R4, [R6]
ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
LDR LR, [SP, #-8]
MSR CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式
MOV SP, R4 ;设置堆栈指针
LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum
;恢复新任务的OsEnterSum
LDR R3, =OsEnterSum
STR R4, [R3]
MSR SPSR_cxsf, R5 ;恢复CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;运行新任务
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
UCOS-II移植ARM的读书笔记(12.25)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
__OSStartHighRdy
MSR CPSR_c, #(NoInt | SYS32Mode) ;调整到管理模式
;告诉uC/OS-II自身已经运行
LDR R4, =OSRunning
MOV R5, #1
STRB R5, [R4] ;标记多任务运行标记为真
BL OSTaskSwHook ;调用钩子函数,可以运行用户自定义的函数
LDR R6, =OSTCBHighRdy ;R6存有最高优先级的就绪任务的控制块地址
LDR R6, [R6]
B OSIntCtxSw_1 ;转到前面编写的中断返回函数块的任务跳转部分的代码,因为这两个函数都要用到这部分代码,进入这段代码之前高优先级的就绪任务的任务控制快地址存在R6中。 AREA SWIStacks, DATA, NOINIT,ALIGN=2
SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;管理模式堆栈空间
OSIntCtxSw_1的代码:
OSIntCtxSw_1
;获取新任务堆栈指针
LDR R4, [R6] ;任务控制块的堆栈指针放在R6中,现在放在R4中
ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
LDR LR, [SP, #-8]
MSR CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式
MOV SP, R4 ;设置堆栈指针,R4存有没有改动过的堆栈指针
LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum
;恢复新任务的OsEnterSum
LDR R3, =OsEnterSum
STR R4, [R3]
MSR SPSR_cxsf, R5 ;恢复CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;运行新任务,恢复现场,异常处理返回;中断返回指令的寄存器列表其中必须包括PC后的^符号,表示这是一条特殊形式的指令。这条指令在从存储器中装载PC的同时,CPSR也得到恢复。这里使用的堆栈指针SP是属于异常模式的寄存器,每个异常模式有自己的堆栈指针。
SoftwareInterrupt
LDR SP, StackSvc ; 重新设置堆栈指针
STMFD SP!, {R0-R3, R12, LR} ;保存寄存器
MOV R1, SP ; R1指向参数存储位置
MRS R3, SPSR
TST R3, #T_bit ; 中断前是否是Thumb状态
LDRNEH R0, [LR,#-2] ; 是: 取得Thumb状态SWI号
BICNE R0, R0, #0xff00
LDREQ R0, [LR,#-4] ; 否: 取得arm状态SWI号
BICEQ R0, R0, #0xFF000000
; r0 = SWI号,R1指向参数存储位置
CMP R0, #1
LDRLO PC, =OSIntCtxSw
LDREQ PC, =__OSStartHighRdy ; SWI 0x01为第一次任务切换
BL SWI_Exception
LDMFD SP!, {R0-R3, R12, PC}^
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
OSIntCtxSw
;下面为保存任务环境
LDR R2, [SP, #20] ;获取PC(LR)
LDR R12, [SP, #16] ;获取R12
MRS R0, CPSR
MSR CPSR_c, #(NoInt | SYS32Mode)
MOV R1, LR
STMFD SP!, {R1-R2} ;保存LR,PC
STMFD SP!, {R4-R12} ;保存R4-R12
MSR CPSR_c, R0
LDMFD SP!, {R4-R7} ;获取R0-R3
ADD SP, SP, #8 ;出栈R12,PC
MSR CPSR_c, #(NoInt | SYS32Mode)
STMFD SP!, {R4-R7} ;保存R0-R3
LDR R1, =OsEnterSum ;获取OsEnterSum
LDR R2, [R1]
STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum
;保存当前任务堆栈指针到当前任务的TCB
LDR R1, =OSTCBCur
LDR R1, [R1]
STR SP, [R1]
BL OSTaskSwHook ;调用钩子函数
;OSPrioCur <= OSPrioHighRdy
LDR R4, =OSPrioCur
LDR R5, =OSPrioHighRdy
LDRB R6, [R5]
STRB R6, [R4] ;把OSPrioHighRdy最高优先级的就绪任务传给OSPrioCur
;OSTCBCur <= OSTCBHighRdy
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, =OSTCBCur
STR R6, [R4] ;将最高优先级的任务控制块指针传给当前任务控制块指针
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
关于中断和时钟节拍,UCOS-II对于ARM7通用的中断服务程序的汇编与c函数接口如下:
MACRO和MEND伪指令用于宏定义,MACRO标识宏定义的开始,MEND标识宏定义的结束。定义之后在程序中就可以通过宏指令多次调用该段代码
MACRO
$IRQ_Label HANDLER $IRQ_Exception_Function
EXPORT $IRQ_Label ; 输出的标号
IMPORT $IRQ_Exception_Function ; 引用的外部标号
$IRQ_Label
SUB LR, LR, #4 ; 计算返回地址
STMFD SP!, {R0-R3, R12, LR} ; 保存任务环境
MRS R3, SPSR ; 保存状态
STMFD SP, {R3, SP, LR}^ ; 保存用户状态的R3,SP,LR,注意不能回写
; 如果回写的是用户的SP,所以后面要调整SP
LDR R2, =OSIntNesting ; OSIntNesting++
LDRB R1, [R2]
ADD R1, R1, #1
STRB R1, [R2]
SUB SP, SP, #4*3
MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式
CMP R1, #1
LDREQ SP, =StackUsr
BL $IRQ_Exception_Function ; 调用c语言的中断处理程序
MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式
LDR R2, =OsEnterSum ; OsEnterSum,使OSIntExit退出时中断关闭
MOV R1, #1
STR R1, [R2]
BL OSIntExit
LDR R2, =OsEnterSum ; 因为中断服务程序要退出,所以OsEnterSum=0
MOV R1, #0
STR R1, [R2]
MSR CPSR_c, #(NoInt | IRQ32Mode) ; 切换回irq模式
LDMFD SP, {R3, SP, LR}^ ; 恢复用户状态的R3,SP,LR,注意不能回写
; 如果回写的是用户的SP,所以后面要调整SP
LDR R0, =OSTCBHighRdy
LDR R0, [R0]
LDR R1, =OSTCBCur
LDR R1, [R1]
CMP R0, R1
ADD SP, SP, #4*3 ;
MSR SPSR_cxsf, R3
LDMEQFD SP!, {R0-R3, R12, PC}^ ; 不进行任务切换
LDR PC, =OSIntCtxSw ; 进行任务切换
MEND