教材:《ARM 9嵌入式系统设计与开发应用》(熊茂华,杨震伦编著)(清华大学出版社)
考试题型:选择题40分(20题),填空题10分(5题),简答题20分(4题),读程序20分(4题),写程序10分(1题)。
留个存档作记录,因为复习提纲太长,因此分开了两篇来发。
用来设置总线宽和等待状态。例如,只要通过配置相应寄存器的值,可以使CPU时钟频率改变。
总线宽度和等待控制寄存器BWSCON和BANKCON控制寄存器的初始化程序:
// config.h
/* 总线宽度控制定义(0表示8位,1表示16位,2表示32位) */
#define DW8 (0x0)
#define DW16 (0x1)
#define DW32 (0x2)
#define WAIT (0x1<<2)
#define UBLB (0x1<<3)
/* Bank时序控制(位域)定义 */
#define MT 15 /* 存储类型选择,仅对Bank6和Bank7有效 (2bit) */
#define Trcd 2 /* RAS到CAS延迟,仅对SDRAM有效 (2bit) */
#define SCAN 0 /* 列地址位数,仅对SDRAM有效 (2bit) */
#define Tacs 13 /* 在nGCSn有效之前,地址信号的建立时间 (2bit) */
#define Tcos 11 /* 在nOE有效之前,片选的建立时间 (2bit) */
#define Tacc 8 /* 访问周期 (3bit) */
#define Tcoh 6 /* nOE结束之后,片选信号的保持时间 (2bit) */
#define Tcah 4 /* nGCSn结束之后,地址信号的保持时间 (2bit) */
#define Tacp 2 /* Page模式的访问周期 (2bit) */
#define PMC 0 /* Page模式配置 (2bit) */
/**** 外部总线配置,用户可根据实际需要修改 ****/
#define B7_BWCON (DW16|WAIT|UBLB)
#define B6_BWCON (DW32|UBLB) /* SDRAM所用的Bank,不要修改 */
#define B5_BWCON (DW16|WAIT|UBLB)
#define B4_BWCON (DW16|WAIT|UBLB)
#define B3_BWCON (DW16|WAIT|UBLB)
#define B2_BWCON (DW16|WAIT|UBLB)
#define B1_BWCON (DW16|WAIT|UBLB)
#define B7_BANKCON ((0<<MT)|(1<<Tacs)|(1<<Tcos)|(7<<Tacc)|(1<<Tcoh)|(1<<Tcah)|(1<<Tacp)|(0<<PMC))
#define B6_BANKCON ((3<<MT)|(1<<Trcd)|(1<<SCAN)) /* SDRAM所用的Bank,不要修改 */
#define B5_BANKCON ((1<<Tacs)|(1<<Tcos)|(7<<Tacc)|(1<<Tcoh)|(1<<Tcah)|(1<<Tacp)|(0<<PMC))
#define B4_BANKCON ((1<<Tacs)|(1<<Tcos)|(7<<Tacc)|(1<<Tcoh)|(1<<Tcah)|(1<<Tacp)|(0<<PMC))
#define B3_BANKCON ((1<<Tacs)|(1<<Tcos)|(7<<Tacc)|(1<<Tcoh)|(1<<Tcah)|(1<<Tacp)|(0<<PMC))
#define B2_BANKCON ((1<<Tacs)|(1<<Tcos)|(7<<Tacc)|(1<<Tcoh)|(1<<Tcah)|(1<<Tacp)|(0<<PMC))
#define B1_BANKCON ((1<<Tacs)|(1<<Tcos)|(7<<Tacc)|(1<<Tcoh)|(1<<Tcah)|(1<<Tacp)|(0<<PMC))
#define B0_BANKCON ((1<<Tacs)|(1<<Tcos)|(7<<Tacc)|(1<<Tcoh)|(1<<Tcah)|(1<<Tacp)|(0<<PMC))
// s3c2410.h
// BWSCON寄存器基地址定义
#define BWSCON_ADDR 0x48000000
// Memory control
#define rBWSCON (*(volatile unsigned *)0x48000000) //Bus width & wait status
#define rBANKCON0 (*(volatile unsigned *)0x48000004) //Boot ROM control
#define rBANKCON1 (*(volatile unsigned *)0x48000008) //BANK1 control
#define rBANKCON2 (*(volatile unsigned *)0x4800000c) //BANK2 cControl
#define rBANKCON3 (*(volatile unsigned *)0x48000010) //BANK3 control
#define rBANKCON4 (*(volatile unsigned *)0x48000014) //BANK4 control
#define rBANKCON5 (*(volatile unsigned *)0x48000018) //BANK5 control
#define rBANKCON6 (*(volatile unsigned *)0x4800001c) //BANK6 control
#define rBANKCON7 (*(volatile unsigned *)0x48000020) //BANK7 control
#define rREFRESH (*(volatile unsigned *)0x48000024) //DRAM/SDRAM refresh
#define rBANKSIZE (*(volatile unsigned *)0x48000028) //Flexible Bank Size
#define rMRSRB6 (*(volatile unsigned *)0x4800002c) //Mode register set for SDRAM
#define rMRSRB7 (*(volatile unsigned *)0x48000030) //Mode register set for SDRAM
// target.c
// 总线配置数据表 (用户可以在config.h文件中配置总线)
const uint32 __BUS_INIT[] =
{
(B7_BWCON<<28)|(B6_BWCON<<24)|(B5_BWCON<<20)|(B4_BWCON<<16)|(B3_BWCON<<12)|(B2_BWCON<<8)|(B1_BWCON<<4), // BWSCON寄存器
B0_BANKCON, // BANKCON0寄存器
B1_BANKCON, // BANKCON1寄存器
B2_BANKCON, // BANKCON2寄存器
B3_BANKCON, // BANKCON3寄存器
B4_BANKCON, // BANKCON4寄存器
B5_BANKCON, // BANKCON5寄存器
B6_BANKCON, // BANKCON6寄存器 (SDRAM)
B7_BANKCON, // BANKCON7寄存器 (SRAM)
(1<<23)|(0<<22)|(0<<20)|(3<<18)|(1113), // REFRESH寄存器(SDRAM) 例如:period=15.6us, HCLK=60Mhz, (2048+1-15.6*60)
(1<<7)|(1<<5)|(1<<4)|(2<<0), // BANKSIZE寄存器,128MB
(3<<4), // MRSRB6寄存器
(3<<4) // MRSRB7寄存器
};
// target.c
/*********************************************************************************************************
** Function name: TargetBusInit
** Descriptions: 针对目标板的总线系统初始化,包括Bank的宽度、SDRAM控制器等等。
** 不要在此函数中加入任何用户代码。
** Input: 无
** Output: 无
********************************************************************************************************/
void TargetBusInit(void)
{
#ifdef __Release
int i;
volatile uint32 *cp1;
// 总线设置,初始化SDRAM控制器
cp1 = (void *)BWSCON_ADDR;
for(i=0; i<13; i++)
{
*cp1++ = __BUS_INIT[i];
}
#endif
}
中断向量表初始化:
;定义堆栈的大小
USR_STACK_LEGTH EQU 64
SVC_STACK_LEGTH EQU 0
FIQ_STACK_LEGTH EQU 16
IRQ_STACK_LEGTH EQU 64
ABT_STACK_LEGTH EQU 0
UND_STACK_LEGTH EQU 0
AREA Example5,CODE,READONLY ; 声明代码段Example5
ENTRY ; 标识程序入口
CODE32 ; 声明32位ARM指令
START MOV R0,#0
MOV R1,#1
MOV R2,#2
MOV R3,#3
MOV R4,#4
MOV R5,#5
MOV R6,#6
MOV R7,#7
MOV R8,#8
MOV R9,#9
MOV R10,#10
MOV R11,#11
MOV R12,#12
BL InitStack ; 初始化各模式下的堆栈指针
; 打开IRQ中断 (将CPSR寄存器的I位清零)
MRS R0,CPSR ; R0 <= CPSR
BIC R0,R0,#0x80
MSR CPSR_cxsf,R0 ; CPSR <= R0
; 切换到用户模式
MSR CPSR_c, #0xd0
MRS R0,CPSR
; 切换到管理模式
MSR CPSR_c, #0xdf
MRS R0,CPSR
HALT B HALT
; 名称:InitStack
; 功能:堆栈初始化,即初始化各模式下的堆栈指针。
; 入口参数:无
; 出口参数:无
; 说明:在特权模式下调用此子程序,比如复位后的管理模式
InitStack
MOV R0, LR ; R0 <= LR,因为各种模式下R0是相同的
;设置管理模式堆栈
MSR CPSR_c, #0xd3
LDR SP, StackSvc
;设置中断模式堆栈
MSR CPSR_c, #0xd2
LDR SP, StackIrq
;设置快速中断模式堆栈
MSR CPSR_c, #0xd1
LDR SP, StackFiq
;设置中止模式堆栈
MSR CPSR_c, #0xd7
LDR SP, StackAbt
;设置未定义模式堆栈
MSR CPSR_c, #0xdb
LDR SP, StackUnd
;设置系统模式堆栈
MSR CPSR_c, #0xdf
LDR SP, StackUsr
MOV PC, R0
StackUsr DCD UsrStackSpace + (USR_STACK_LEGTH - 1)*4
StackSvc DCD SvcStackSpace + (SVC_STACK_LEGTH - 1)*4
StackIrq DCD IrqStackSpace + (IRQ_STACK_LEGTH - 1)*4
StackFiq DCD FiqStackSpace + (FIQ_STACK_LEGTH - 1)*4
StackAbt DCD AbtStackSpace + (ABT_STACK_LEGTH - 1)*4
StackUnd DCD UndtStackSpace + (UND_STACK_LEGTH - 1)*4
; 分配堆栈空间
AREA MyStacks, DATA, NOINIT, ALIGN=2
UsrStackSpace SPACE USR_STACK_LEGTH * 4 ; 用户(系统)模式堆栈空间
SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ; 管理模式堆栈空间
IrqStackSpace SPACE IRQ_STACK_LEGTH * 4 ; 中断模式堆栈空间
FiqStackSpace SPACE FIQ_STACK_LEGTH * 4 ; 快速中断模式堆栈空间
AbtStackSpace SPACE ABT_STACK_LEGTH * 4 ; 中止义模式堆栈空间
UndtStackSpace SPACE UND_STACK_LEGTH * 4 ; 未定义模式堆栈
END
堆栈初始化:
/*********************************************************************************************************
** 函数名称: OSTaskStkInit
** 功能描述: 任务堆栈初始化代码,本函数调用失败会使系统崩溃
** 输 入: task : 任务开始执行的地址
** pdata :传递给任务的参数
** ptos :任务的堆栈开始位置
** opt :附加参数,当前版本对于本函数无用,具体意义参见OSTaskCreateExt()的opt参数
** 输 出: 栈顶指针位置
** 全局变量:
** 调用模块:
********************************************************************************************************/
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
opt = opt; /* 'opt' 没有使用。作用是避免编译器警告 */
stk = ptos; /* 获取堆栈指针 */
/* 建立任务环境,ADS1.2使用满递减堆栈 */
*stk = (OS_STK) task; /* pc */
*--stk = (OS_STK) task; /* lr */
*--stk = 0; /* r12 */
*--stk = 0; /* r11 */
*--stk = 0; /* r10 */
*--stk = 0; /* r9 */
*--stk = 0; /* r8 */
*--stk = 0; /* r7 */
*--stk = 0; /* r6 */
*--stk = 0; /* r5 */
*--stk = 0; /* r4 */
*--stk = 0; /* r3 */
*--stk = 0; /* r2 */
*--stk = 0; /* r1 */
*--stk = (unsigned int) pdata; /* r0,第一个参数使用R0传递 */
*--stk = (USER_USING_MODE|0x00); /* spsr,允许 IRQ, FIQ 中断 */
*--stk = 0; /* 关中断计数器OsEnterSum; */
return (stk);
}
常见的有3种方式:
void string_copy(char *dst,const char *str)
{
char ch;
__asm
{
loop
LDRB ch,[str],#1
STRB ch,[dst],#1
CMP ch,#0
BNE loop
}
}
int main()
{
char *a="hello world!"
char b[64];
string_copy(a,b);
return 0;
}
(1)汇编export
(2)C语言定义 extern function
(3)C语言调用汇编
myArm.s
AREA myARM,CODE ,READONLY
export my_strcopy
my_strcopy
loop
LDRB R4,[R0],#1
CMP R4,#0
BEQ OVER
STRB R4,[R1],#1
B loop
OVER
end
myMain.c
extern void my_strcopy(char *dtr,char*str);
int main()
{
char *a="hello world!"
char b[64];
my_strcopy(a,b);
return 0;
}
(1)C语言实现函数
(2)汇编import函数名
(3)BL 函数名
myArm.s
AREA myARM,CODE ,READONLY
IMPORT c_fun
ENTRY
start
mov R0,#1
mov R1,#2
mov R2,#3
BL c_fun
end
myMain.c
int c_fun(int a,int b,int c)//从汇编中调到这里,此时a=1,b=2,c=3
{
return a+b+c;//从这里再返回去,此时R0=6;
}
ATPCS即ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)的简称。
PCS规定了应用程序的函数可以如何分开地写,分开地编译,最后将它们连接在一起,所以它实际上定义了一套有关过程(函数)调用者与被调用者之间的协议。
PCS强制实现如下约定:调用函数如何传递参数(即压栈方法,以何种方式存放参数),被调用函数如何获取参数,以何种方式传递函数返回值。
部分规则:
uC/OS内核调度主要有以下特点:
OS_MAX_TASKS
)由用户决定。(都是些什么乱七八糟的)
主要参数的功能如下:
*OSTCBStkPtr
是指向当前任务栈顶的指针。*OSTCBExtPtr
是任务扩展模块使用。*OSTCBStkBottom
是指向任务堆栈栈底的指针。OSTCBStkSize
是存有栈中可容纳的指针元数目。OSTCBOpt
把“选择项”传给函数OSTashCreateExt()
。只有当用户将OS_CFG.H
文件中的OS_TASK_CREATE_EXT
设为1时,这个变量才有效。OSTCBId
用于存储任务的识别码(ID)。OSTCBNext
和OSTCBPrev
用于任务控制OS_TCBs
的双向链表的前后连接,该链表在时钟节拍函数OSTimerTick()
。OSTCBEventPtr
是指向事件控制块的指针。OSTCBMsg
是指向传给任务消息的指针。OSTCBDly
当需要把任务延时若干个时钟节拍时要用到这个变量,或者需要把任务挂起一段时间以等待某事件的发生。OSTCBStat
是任务的状态字。OSTCBPrio
是任务优先级,高优先级任务的OSTCBPrio
值最小。OSTCBDelReq
是一个布尔量,用于表示该任务是否需要删除。OSTCBX OSTCBY OSTCBBitX OSTCBBitY
用于加速任务进入就绪态的过程或进入等待事件发声状态的过程。这些值是在任务建立时算好的,或者是在改变任务优先级时算出的。任务就绪表记录了系统中所有处于就绪状态的任务,从代码上来看它就是一个类型为INT8U的数组OSRdyTbl[]。。系统中的任务为32个时,OSRdyTbl[]就有4个成员。每个成员占据8位,所以OSRdyTbl[]的每一个数据元素对应8个任务,这8个任务称为一个任务组。在就绪表中,以任务优先二进制位,当该位为1时表示对应的任务处于就绪状态,反之为非就绪状态。
考虑到查找效率,uCOS-II定义了一个INT8U的变量OSRdyGrp,该变量的每一位都对应OSRdyTbl[]的一个任务组(即数据的一个成员)。若某任务任务所对应的位置置为1,否则为0。
举例:OSRdyGrp=00001011,那么就意味着OSRdyTbl[0]、OSRdyTbl[1]、OSRdyTbl[3]中有就绪的任务。由图可知,uCOS-II最多可以管理8 * 8 = 64个任务。
任务就绪表是以任务的优先级从低到高排序的,那么想要根据任务的优先级来找到该任务所处于就绪表中位置就轻而易举了:
由于系统至多支持64个任务,所以优先级至多也就到63,即二进制的00111111,只占据低6位,每一个OSRdyTbl[]元素只是占据8,所以只需要用3个二进制位即可表示这8位中的哪一位为1,同理,高3位用于表示至多8个OSRdyTbl[]元素的哪一个元素。即:优先级的高3位二进制位(D5、D4、D3)指明,即:优先级的高3位二进制位(D5、D4、D3)指明OSRdyTbl[]的数组下标n,低3位(D2、D1、D0)指明OSRdyTbl[n]的哪一位数据位。另外,确定OSRdyTbl[]的下标n,也说明OSRdyGrp的第几位置位。
举例:某任务的优先级prio=24,问该任务落在就绪表中的哪一位?
24的二进制位为00011000,D5、D4、D3位011,即OSRdyTbl[]的下标为3,D2、D1、D0为0,即优先级prio=24的任务在OSRdyTbl[3]的第0位。OSRdyGrp的第3位置位。
每个任务的就绪态标志都放入到就绪表中,就绪表中有两个变量
OSRdyGrp
和OSRdyTbl[]
。在
OSRdyGrp
中,任务按优先级分组,8个任务为一组。OSRdyGrp
中的每一位表示8组任务中每一组是否有进入就绪态的任务。任务进入就绪态时,就绪表
OSRdyTbl[]
中的相应元素的相应位也置为1。就绪表OSRdyTbl[]
数组的大小取决于OS_LOWEST_PRIO
。为确定下次该哪个优先级的任务运行了,内核调度器总是将最低优先级的任务在就绪表中相应字节的相应位置1,即
OS_LOWEST_PRIO = 1
。
使任务进入就绪表(通过OSMapTbl[]来在就绪表相应的行和列置1):
OSRdyGrp |= OSMapTbl[prio >> 3];
OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
任务优先级的低三位用于确定任务在总就绪表OSRdyTbl[]中的所在位,接下去的三位用于确定是在OSRdyTbl[]数组的第几个元素。OSMapTbl[]用于限制OSRdyTbl[]数组元素下标为0-7。
从就绪表中删除一个任务(通过OSMapTbl[]来在就绪表相应的行和列置0):
if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0)
OSRdyGrp &= ~OSMapTbl[prio >> 3];
将就绪任务表数组OSRdyTbl[]中相应元素的相应位清0。而对于OSRdyGrp,只有当被删除任务所在任务组中全组任务一个都没有进入就绪态时才将相应位清0,即OSRdyTbl[prio >> 3]所有位为0时,OSRdyGrp的相应位才清零。
使用的是哈希算法。
所以要找出优先级最高的任务,分两步:
第一步,确定任务组(OSRdyTbl[]的下标)Y:找出OSRedyGrp中为1的最低位Y;
第二步,确定任务组中的位X:找出任务组中OSRdyTbl[x]中为1的最低位X。
综上,找出目标任务的核心算法在于确定某数值为1的最低位,uCOS-II的具体实现是,借助OSUnMapTbl[]数组:
例如0x06(00000110),为1的最低位是Bit[1],那么OSUnMapTbl[0x06]=1;0x20(00100000),为1的最低位是Bit[5],即OSUnMapTbl[0x20]=5。
INT8U y;
y = OSUnMapTbl[OSRdyGrp]; //最高优先级所在的任务组
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); //最高优先级任务所在的任务组的位
INT8U const OSUnMapTbl[256] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */
};
任务的优先级prio = (任务组Y) << 3 | 任务组的上哪一位X
y = OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
prio = (y << 3) + x;
进程间的通信方式有两种,一种是使用共享内存,这种方式基本不依赖OS,也没有相应的系统开销。另一种则需要OS支持,通过建立链接器实现任务间的通信。
在UCOSii中,多个任务使用同一块内存区域需要提供一种互斥存取的方法。否则该段共享数据很有可能在被访问前就被其他任务重置了。
其中OS_ENTER_CRITICAL()就是关中断函数,而OS_EXIT_CRITICAL()就是开中断函数。
利用关中断宏OS_ENTER_CRITICAL()、OS_EXIT_CRITICAL()以及开调度锁是利用函数 OSSchedLock()、 OSSchekUnlock()可以实现单任务对某一资源的暂时性独享。
用这种方法实现数据共享存在很大的局限性,一个简单的例子,当一个共享资源允许被多个任务同时占用,这种方式就很低效。
RTOS会提供信号量、邮箱和消息队列来支持任务间的通信与同步,即使是非实时性操作系统也同样有这样的接口,这已经类似于一种规范。
信号量的概念最初由Edsger Dijkstra提出。
假定有多个任务需要读写一块板卡上的flash芯片,如果他们之间没有协商,而是各自单独对flash进行读写,就会使flash的读写操作处于不可预料的状态中。此时可以建立一个信号量,当有任务进行读写操作时,便申请该信号量,操作完成后再释放。如果已经有任务占用了该信号量,请求信号量就会失败,任务可以等待该信号量被释放,再进行相关的操作。一个共享资源也可能最多被几个任务占用,这种情况下信号量可以为一个计数器,当有任务占用共享资源时,信号量减一。
很明显,信号量只解决了共享资源的占用的问题。它不能传递信息,假如某下位机有一个专门用来解释上位机控制命令的任务,当上位机没有数据传送过来时,该任务处于挂起状态。但当通讯中断发生后,该任务不仅要知道已经有控制字被传送过来,还需要知道该控制字是什么。所以信号量在这里并不适用,解决的办法是建立一个邮箱。由于要传递的信息很可能超过一个常规变量的大小,所以邮箱的内容是一个指针。将命令解释器的优先级设置为高于其他任务,它始终要等待一个邮箱。将该邮箱里指针的值指向接收缓冲区,该任务就开始处理控制字。
消息队列是一组指针,它可以被看成是一组邮箱的集合。
假设有一个流水线分拣系统,传感器会检测货物的一些物理参数。每来一个产品,系统就建立一个关于该产品的结构体用于描述该产品的属性。如果使用邮箱,那么在一个产品被分拣任务处理前,新到产品就必须延时处理。使用消息队列,可以将一组指针推入队列,每一个指针都描述一个产品。这样分拣任务可以根据先后入队列的顺序,依次分拣每个产品。
事件控制块Ecb用来维护一个事件控制块的所有信息,该结构体不仅包含信号量/邮箱/消息队列的值,还包含等待它的任务列表。
Ecb反映了一种朴素的简化程序逻辑结构的思想。用统一的数据结构来描述对象的属性,再在处理程序里统一处理。对信号量/邮箱/消息队列的创建、维护都只是读写Ecb,在调度程序里,统一处理Ecb。
typedef struct {
void *OSEventPtr; /* 指向消息或者消息队列的指针 */
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待任务列表 */
INT16U OSEventCnt; /* 计数器(当事件是信号量时) */
INT8U OSEventType; /* 事件类型 */
INT8U OSEventGrp; /* 等待任务所在的组 */
} OS_EVENT;
OSTaskCreate()
:作用是创建一个任务。 有4个参数(任务的入口地址、任务的参数、任务的堆栈的首地址、任务的优先级)。调用该函数后,系统会首先从TCB空闲列表内申请一个空的TCB指针,然后会根据用户给出的参数初始化任务堆栈,并在内部的任务就绪表内标记该任务为就绪状态。返回后即可成功创建一个任务。这个函数应该至少在main
函数内调用一次,在OSInit
函数调用之后调用。
OSTaskSuspend()
:作用将指定的任务挂起。如果挂起的是当前任务,将会引发系统执行任务先导函数OSShed
来进行一次任务切换。 只有一个参数(指定任务的优先级)。注意,任务的ID就是其优先级,所以ucos不允许有相同的优先级。
OSTaskResume()
:作用是将挂起的任务恢复成就绪状态。如果恢复任务的优先级高于当前任务就会引发一次任务切换。 注意,本函数不必跟上面函数成对搭配使用。
OSMBoxCreate()
:作用是创建消息邮箱。 消息邮箱是操作系统的任务间通信的一种方式,一个任务可以阻塞或不阻塞地等待另外一个任务发送到它的邮箱的邮箱消息,而根据消息内容进行动作。(这说的是人话吗???)
OSMBoxPost()
:用于一个任务向另一个任务的邮箱发邮箱消息,消息的内容在参数中指定。
OSMBoxPend()
:用于一个任务获取本任务邮箱中的消息。 如果邮箱中没有消息,则等待,任务处于阻塞状态。
OSSemCreate()
:作用是创建信号量。 信号量是操作系统的任务间同步的一种方式,两个或者多个任务可以获取信号量的状态并根据它进行动作从而实现同步。(这写的有点拗口,语文不合格!)
OSSemPost()
:用于一个任务对一个信号量进行设置。 设置的内容在参数中指定。
OSSemPend()
:用于一个任务获取本信号量的状态。 如果信号量不为零,则成功获取信号量并将信号量减去1;如果信号量为零,则等待,任务处于阻塞状态。
OSTimeDly()
:作用是先挂其当前任务,然后进行任务切换,在指定时间到来后,将当前任务恢复为就绪状态,但是不一定运行,如果恢复后是优先级最高的就绪任务,那么就运行它。OSMemCreate()
:作用是创建一个内存分区。OSMemGet()
:作用是从一个指定的内存区中分配一个内存块。OSMemPut()
:作用是释放一个内存块。处理器必须满足的要求:
移植的4个步骤:
os_cpu.h
中与处理器和编译器相关的代码。回去给我复习C语言基础!不再赘述!
假设task1优先级为3,task2优先级为7,task3优先级为9。运行顺序:task1、task2、task1、task2、task3。(哈哈,我不会做,因此直接背了以下框架,可以捞分,打算寒假重新认真学ucos)
#define Task1StkLengh 64 // 定义用户任务1的堆栈长度
#define Task2StkLengh 64 // 定义用户任务2的堆栈长度
#define Task3StkLengh 64 // 定义用户任务3的堆栈长度
OS_STK Task1Stk [Task1StkLengh]; // 定义用户任务1的堆栈
OS_STK Task2Stk [Task2StkLengh]; // 定义用户任务2的堆栈
OS_STK Task3Stk [Task3StkLengh]; // 定义用户任务3的堆栈
OS_EVENT *Sem1; // 信号量1
OS_EVENT *Sem2; // 信号量2
OS_EVENT *Sem3; // 信号量3
//OS_EVENT *RandomMBox;
void Task1 (void *pdata);
void Task2 (void *pdata);
void Task3 (void *pdata);
int main (void)
{
OSInit ();
OSTaskCreate (Task1, (void *)0, &Task1Stk[Task1StkLengh - 1], 3);
OSTaskCreate (Task2, (void *)0, &Task2Stk[Task2StkLengh - 1], 7);
OSTaskCreate (Task3, (void *)0, &Task3Stk[Task3StkLengh - 1], 9);
Sem1 = OSSemCreate (0);
Sem2 = OSSemCreate (1);
Sem3 = OSSemCreate (0);
//RandomMBox = OSMBoxCreate((void *)0);
OSStart ();
return 0;
}
void Task1 (void *pdata)
{
INT8U Reply;
pdata = pdata;
for (;;)
{
OSSemPend (Sem1, 0, &Reply);
OSSemPost (Sem2);
//OSMboxPost (RandomMBox, s);
OSTimeDlyHMSM (0, 0, 0, 350);
}
}
void Task2 (void *pdata)
{
INT8U err;
pdata = pdata;
for (;;)
{
//msg = OSMBoxPend (RandomMBox, 0, &err);
OSTimeDlyHMSM (0, 0, 0, 350);
}
}
void Task3 (void *pdata)
{
pdata = pdata;
for (;;)
{
OSTimeDlyHMSM (0, 0, 0, 350);
}
}