您的位置:首页 > 其它

UCOS-II学习记录

2017-03-14 19:16 197 查看
本文主要记录使用
UCOS II
的相关内容。包括如何完成第一个
UCOS II
应用程序,和如何创建任务,如何获取系统时间,和利用
i3086 驱动
完成时间获取,屏幕显示,按键驱动,信号量等内容。

UCOS-II 基本输入输出 任务创建

PART1 关于X86架构32位系统上UCOS的移植

本文使用的
UCOS II
系统为实时操作系统一书打包好的
Win32
环境下的
UCOS II
操作系统。

观察其提供的示例工程,可依照其配置搭建起基于
Win32
UCOS II
运行环境。

示例工程中的
Makefile
文件中使用了如下工具及工作目录:

######################################################################
#                                   TOOLS
######################################################################

BORLAND=C:\BC45

CC=$(BORLAND)\BIN\BCC
ASM=$(BORLAND)\BIN\TASM
LINK=$(BORLAND)\BIN\TLINK
TOUCH=$(BORLAND)\BIN\TOUCH

######################################################################
#                                DIRECTORIES
######################################################################

LST=..\LST
OBJ=..\OBJ
SOURCE=..\SOURCE
TARGET=..\TEST
WORK=..\WORK

OS=\SOFTWARE\uCOS-II\SOURCE
PC=\SOFTWARE\BLOCKS\PC\BC45
PORT=\SOFTWARE\uCOS-II\Ix86L\BC45


即将
Borland c
TASM
安装到根目录下,并将本示例工程
SOFTWARE
文件夹放置到根目录下即可。

该书提供的
Win32
控制台驱动放置在
SOFTWARE/BLOCKS/PC
目录下,
UCOS II
源码放置在
SOFTWARE/uCOS-II/SOURCE
目录下。

在对应示例工程下
Test
目录运行
make
文件即可生成可执行文件。

PART2 关于CONFIG文件

每一个
UCOS II
工程中,要包含一个
Config
文件,用来联合
UCOS II
中的条件编译语句,做到定制化操作系统的功能。

该文所涉及到的内容包含
Config
文件中的如下部分

#define OS_MAX_TASKS             11    /* 任务最大数量  */
#define OS_LOWEST_PRIO           12    /* 最低优先级    */


PART3 实现显示功能

Jean J. Labrosse先生提供的示例程序均在
Win 32
控制台上输出了一片区域用于模拟实际嵌入式操作环境中的显示器,该函数为
TaskStartDispInit
,其中利用了
PC_DispStr
在屏幕上显示字符串。

具体
Win 32
控制台显示驱动函数可以前往上文所提到的
PC.h
文件中查看。

PART4
UCOS II
任务的组成

UCOS II
的任务组成如下:

void Task(void *pdata){
//Init Data In Task
while(1){
//Do whatever You Want In task
OSTimeDly(100);
//让出CPU让其他任务执行
}
}


示例提供的
TaskStart
的主要工作包括初始化操作系统,利用上诉显示函数初始化显示屏,调用
TaskStartCreateTasks
初始化用户操作程序,以及在运行时更新显示屏显示内容,并判断用户是否按下
esc
键退出模拟环境会到
Win32
控制台。

PART5 任务的创建

TASK_START
为示例代码利用的
OSTaskCreate
创建的。

而在
TASK_START
中我们的
Task0
TASK1
是利用
OSTaskCreateExt
来创建的

OSTaskCreate
的参数为

OSTaskCreate (void (*task)(void *pd), /* 函数指针,void *pd为函数的参数*/
void *pdata,            /* 建立任务时,传递的参数*/
OS_STK *ptos,           /* 指向堆栈任务栈顶的指针*/
INT8U prio)             /* 任务优先级 */


OSTaskCreateExt
的参数为

INT8U  OSTaskCreateExt (void   (*task)(void *pd),
void    *pdata,
OS_STK  *ptos,
INT8U    prio,
INT16U   id,       /* 任务ID,2.52版本,无实际作用,保留作为扩展用*/
OS_STK  *pbos,     /* 指向堆栈底部的指针,用于OSTaskStkChk()函数*/
INT32U   stk_size, /* 指定任务堆栈的大小,由OS_STK类型决定 */
void    *pext,     /* 定义数据结构的指针,作为TCB的扩展*/
INT16U   opt)      /* 存放于任务操作相关的信息*/
{


PART6 方向键驱动

Jean J. Labrosse先生提供的驱动为PC下的
BOOLEAN PC_GetKey(INT16S *c)
,而我们方向键为两个字节的消息。若只用一个字节来读取的话,会导致如
Up Arrow
其与
H
混淆。

于是在
PC
驱动程序中加入专门读取方向键的驱动

BOOLEAN PC_GetArrowKey (INT16S *key_1,INT16S *key_2){
if (kbhit()) {
*key_1 = (INT16S)getch();
*key_2 = (INT16S)getch();
return (TRUE);
} else {
*key_1 = 0x00;
*key_2 = 0x00;
return (FALSE);
}
}


并利用
Task0
读取键盘动作(其中
OSTaskStkChk
用来检查栈)

void  Task0 (void *pdata){
INT8U       err;
OS_STK_DATA data;
char        s[40];
char        key_s[10];
INT16S      key_1 = 0;
INT16S      key_2 = 0;

pdata = pdata;
PC_DispStr(5, 3 , "Task 0 Stack Size:", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
while(1){
err  = OSTaskStkChk(TASK_1_PRIO, &data);
if (err == OS_NO_ERR) {
sprintf(s, "Total:%4ld    Free:%4ld        Used:%4ld   ",
data.OSFree + data.OSUsed,
data.OSFree,
data.OSUsed);
PC_DispStr(10, 4 , s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
}
if (PC_GetArrowKey(&key_1,&key_2) == TRUE) {
if(key_1==0){
switch(key_2){
case 72:sprintf(key_s,"key is UP   ");break;
case 75:sprintf(key_s,"key is LEFT ");break;
case 77:sprintf(key_s,"key is Right");break;
case 80:sprintf(key_s,"key is DOWN ");break;
}
PC_DispStr(10, 5, key_s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
}
}
OSTimeDly(10);
}
}


Result



UCOS-II 使用信号量

Part 1 UCOS-II 信号量

UCOS-II中信号量利用时间控制块实现,事件控制块中保存有类似任务调度的等待组。实现方式类似从等待队列中抽取优先级最高的任务。信号量相关有以下方法:

创建信号量:
OSSemCreate()

OS_EVENT  *OSSemCreate (INT16U cnt)


cnt
是信号量的初始值

中断中不允许创建Sem

若freelist中没用空闲的ECB块则创建失败

返回的
pevent
指向该信号量的
ECB


等待信号量:
OSSemPend()

void  OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)


timeout
超时,放到了
TCB
中的
dly


err
错误类型

pevent
OSEventCnt
保存信号量的值

当返回有两种可能:

err
OS_NO_ERR
则为获取了该信号量

err
OS_TIMEOUT
则为超时

给予信号量:
OSSemPost()

INT8U  OSSemPost (OS_EVENT *pevent)


若有任务在等这个信号量

将优先级最高的放入就绪队列并消除
TCB
的标志。

删除信号量:
OSSemDel()

OS_EVENT  *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *err)


opt
OS_DEL_NO_PEND
没有任务在等的时候删除。

opt
OS_DEL_ALWAYS
即使有任务在等,也要删除。将所有的等待任务放入就绪队列。

检查信号量:
OSSemAccept()

INT16U OSSemAccept(OS_EVENT *pevent)


非阻塞获取信号量

若信号量大于0,则获取信号量

若信号量小于等于0,不等直接返回

Part2 信号量测试

在主函数中创建信号量:

/*Create semaphore*/
mutex = OSSemCreate(1);


测试互斥信号量

OS_STK_DATA data;

void  Task0 (void *pdata){
INT8U *err;
char s[40];
pdata = pdata;
while(1){
OSSemPend (mutex, 1000, err);
err  = OSTaskStkChk(TASK_1_PRIO, &data);
if (err == OS_NO_ERR) {
sprintf(s, "Task0 : Total:%4ld    Free:%4ld        Used:%4ld   ",
data.OSFree + data.OSUsed,
data.OSFree,
data.OSUsed);
PC_DispStr(10, 6 , s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr(10, 7 , "                                                                 ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
}
OSSemPost (mutex);
OSTimeDly(800);
}
}

void  Task1 (void *pdata){
INT8U *err;
char s[40];
pdata = pdata;
while(1){
OSSemPend (mutex, 1000, err);
err  = OSTaskStkChk(TASK_1_PRIO, &data);
if (err == OS_NO_ERR) {
sprintf(s, "Task1 : Total:%4ld    Free:%4ld        Used:%4ld   ",
data.OSFree + data.OSUsed,
data.OSFree,
data.OSUsed);
PC_DispStr(10, 7 , s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr(10, 6 , "                                                                 ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);

}
OSSemPost (mutex);
OSTimeDly(600);
}
}


运行后,可以看到
Task0
Task1
打印出的两条消息交替出现。





Part 3 生产者消费者

一定要检查CFG中Event的上限参数

#define OS_MAX_EVENTS             10


新增加信号量

full = OSSemCreate(0);
empty = OSSemCreate(10);


设置缓存

int buff[10];


生产者与消费者

生产者与消费者通过信号量
mutex
实现互斥,防止同时读取或写入

生产者与消费者通过信号量
full
empty
进行协作

生产者:

生产者等待到
empty
信号量后获取锁向
buff
中写入内容。写入完成后通知
full
信号量

void  Task0 (void *pdata){
INT8U *err;
int count = 13;
int cur_pos = 0;
char s[40];
pdata = pdata;
while(1){
//等待empty
OSSemPend (empty,1000, err);
//向buff中写入

OSSemPend (mutex,1000, err);
buff[cur_pos] = count;
OSSemPost (mutex);

sprintf(s,"Write data %d empty = %d cur %d",buff[cur_pos],empty->OSEventCnt,cur_pos);

//更新数据
cur_pos = (cur_pos + 1) % 10;
count ++;

//通知full
OSSemPost (full);
PC_DispStr(10, 6, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
OSTimeDly(300);
}
}


消费者:

生产者等待到
full
信号量后获取锁读取
buff
中内容。读取完成后通知
empty
信号量

void  Task1 (void *pdata){
INT8U *err;
int cur_pos = 0;
char s[40];
int reg;
pdata = pdata;
while(1){
//等待full
OSSemPend (full,1000, err);
//向buff中读取

OSSemPend (mutex,1000, err);
reg = buff[cur_pos];
OSSemPost (mutex);

sprintf(s,"Read data %d full = %d cur = %d",reg,full->OSEventCnt,cur_pos);

//更新数据
cur_pos = (cur_pos + 1) % 10;
//通知empty
OSSemPost (empty);

PC_DispStr(10, 8, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
OSTimeDly(800);
}
}


Result



UCOS-II 互斥信号量

创建互斥信号量

创建函数为

OS_EVENT  *OSMutexCreate (INT8U prio, INT8U *err)


相比较于信号量,多出了一个优先级参数

获得互斥信号量

获得互斥信号量的函数为

void  OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)


若没有人占有,则
cnt
的低八位为
AVAILABLE
的flag,则获取该信号量,并将当前的优先级保存在低八位。

若有人占用

若没有提升优先级&&占有mutex的优先级比本任务优先级低,则提升优先级至
prio
(修改TCB中的prio,讲
task
从原来的就绪队列中清除,并根据新的
prio
放入等待队列)

其他情况则不提升占有者的优先级。

以上提升步骤完成后,让当前任务等待该互斥信号量。启动调度。当重新回到当前任务时,要么时间超时要么可以获取信号量。

释放互斥信号量

释放互斥信号量的函数为

INT8U  OSMutexPost (OS_EVENT *pevent)


释放信号量要将自己的优先级还原,并给下一个等待者。最后设置
AVAILABLE


UCOS-II 事件标志

事件标志

OS_FLAG_GRP

typedef struct {                            /* Event Flag Group                                        */
INT8U         OSFlagType;               /* Should be set to OS_EVENT_TYPE_FLAG                     */
void         *OSFlagWaitList;           /* Pointer to first NODE of task waiting on event flag     */
OS_FLAGS      OSFlagFlags;              /* 8, 16 or 32 bit flags                                   */
} OS_FLAG_GRP;


OS_FLAG_NODE

typedef struct {                            /* Event Flag Wait List Node                               */
void         *OSFlagNodeNext;           /* Pointer to next     NODE in wait list                   */
void         *OSFlagNodePrev;           /* Pointer to previous NODE in wait list                   */
void         *OSFlagNodeTCB;            /* Pointer to TCB of waiting task                          */
void         *OSFlagNodeFlagGrp;        /* Pointer to Event Flag Group                             */
OS_FLAGS      OSFlagNodeFlags;          /* Event flag to wait on                                   */
INT8U         OSFlagNodeWaitType;       /* Type of wait:                                           */
/*      OS_FLAG_WAIT_AND                                   */
/*      OS_FLAG_WAIT_ALL                                   */
/*      OS_FLAG_WAIT_OR                                    */
/*      OS_FLAG_WAIT_ANY                                   */
} OS_FLAG_NODE;


OS_FLAGS

OS_FLAGS由自己定义,UCOS中并未给出定义。

Create

OS_FLAG_GRP  *OSFlagCreate (OS_FLAGS flags, INT8U *err)


创建是从
OSFlagFreeList
中取出一个可用的
OS_FLAG_GRP
并将其赋值。返回的是
OS_FLAG_GRP
中已经赋值完毕的该结构体。

Pend

OS_FLAGS  OSFlagPend (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *err)


flags
为期望等待的内容

wait_type


OS_FLAG_WAIT_CLR_ALL   期望指定flags中所有位为0

OS_FLAG_WAIT_SET_ALL   期望指定flags中所有位为1

OS_FLAG_WAIT_CLR_ANY   期望指定flags中任意一位及以上为0

OS_FLAG_WAIT_SET_ANY   期望指定flags中任意一位及以上为1


利用
wait_type
+
OS_FLAG_CONSUME
设置清0

pgrp->OSFlagFlags &= ~flags_rdy;


如果没等到则调用
OS_FlagBlock


重新回到任务判断是否等到或时间超时

OS_FlagBlock

static  void  OS_FlagBlock (OS_FLAG_GRP *pgrp, OS_FLAG_NODE *pnode, OS_FLAGS flags, INT8U wait_type, INT16U timeout)


将当前任务放到
OS_FLAG_GRP->OSFlagWaitList
中,并从就绪队列中移出该任务。

POST

OS_FLAGS  OSFlagPost (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *err)


先将flags改变的位添加进pgrp的flags

opt


set     (OS_FLAG_SET)
cleared (OS_FLAG_CLR)


查看所有的等待的任务,是否符合他们等待的结果
OS_FlagTaskRdy




测试事件标志

设置
OS_MAX_FLAGS


#define OS_MAX_FLAGS              5


设置自己的
OS_FLAGS


typedef INT16U             OS_FLAGS;
/* Date type for event flag bits (8, 16 or 32 bits)             */
OS_FLAG_GRP *pgrp;


创建事件

/*Create Event Flag*/
pgrp = OSFlagCreate(init_flag,err);


事件标记发送方

void  Task0 (void *pdata){
INT8U *err;
char s[40];
OS_FLAGS task0_flag = 0x01;
OS_FLAGS cur_flag = 0;

pdata = pdata;
while(1){
cur_flag = OSFlagPost(pgrp,task0_flag,OS_FLAG_SET,err);
sprintf(s,"write flag 0x%x cur flag 0x%x",task0_flag,cur_flag);
task0_flag = task0_flag << 1;
PC_DispStr(10, 6, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
OSTimeDly(300);
}
}


事件标记接收方

void  Task1 (void *pdata){
INT8U *err;
char s[40];
OS_FLAGS wish_flag = 0xF;
pdata = pdata;
while(1){
OSFlagPend(pgrp,wish_flag,OS_FLAG_WAIT_SET_ALL+OS_FLAG_CONSUME,10000000,err);
if(*err == OS_TIMEOUT){
sprintf(s,"Time out");
PC_DispStr(10, 8, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
}
else if(*err == OS_NO_ERR){
sprintf(s,"Wait end");
PC_DispStr(10, 8, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
}
OSTimeDly(300);
}
}


注意设置等待标志为
OS_FLAG_WAIT_SET_ALL+OS_FLAG_CONSUME


测试



利用事件标志模拟实现数码管

由上面的测试很容易实现数码管,每个任务等待对应的标志。

实现数字转标志的函数:

INT8U get_led(int num){
INT8U show = 0;
switch(num){
case 0:show = 0x3F;break;
case 1:show = 0x06;break;
case 2:show = 0x5b;break;
case 3:show = 0x4f;break;
case 4:show = 0x66;break;
case 5:show = 0x6d;break;
case 6:show = 0x7d;break;
case 7:show = 0x07;break;
case 8:show = 0x7f;break;
case 9:show = 0x6f;break;
default:show = 0xff;break;
}
return show;
}


每一个显示函数的框架如下:

//show led
void  Task2 (void *pdata){
INT8U *err;
char s[40];
int randnum = 0;
OS_FLAGS led_flag = 0x00;
OS_FLAGS cur_flag = 0x00;
pdata = pdata;

TaskStartCreateLED();
while(1){
randnum = (randnum+1)%10;
led_flag = (OS_FLAGS)get_led(randnum);
cur_flag = OSFlagPost(pgrp,led_flag,OS_FLAG_SET,err);
sprintf(s,"Task 2 %d cur_flag 0x%x",randnum,cur_flag);
PC_DispStr(10, 9, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
OSTimeDly(300);
}
}


测试结果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息