UCOS-II学习记录
2017-03-14 19:16
197 查看
本文主要记录使用
观察其提供的示例工程,可依照其配置搭建起基于
示例工程中的
即将
该书提供的
在对应示例工程下
该文所涉及到的内容包含
具体
PART4
示例提供的
而在
于是在
并利用
创建信号量:
中断中不允许创建Sem
若freelist中没用空闲的ECB块则创建失败
返回的
等待信号量:
当返回有两种可能:
给予信号量:
若有任务在等这个信号量
将优先级最高的放入就绪队列并消除
删除信号量:
检查信号量:
非阻塞获取信号量
若信号量大于0,则获取信号量
若信号量小于等于0,不等直接返回
测试互斥信号量
运行后,可以看到
新增加信号量
设置缓存
生产者与消费者
生产者与消费者通过信号量
生产者与消费者通过信号量
生产者:
生产者等待到
消费者:
生产者等待到
相比较于信号量,多出了一个优先级参数
若没有人占有,则
若有人占用
若没有提升优先级&&占有mutex的优先级比本任务优先级低,则提升优先级至
其他情况则不提升占有者的优先级。
以上提升步骤完成后,让当前任务等待该互斥信号量。启动调度。当重新回到当前任务时,要么时间超时要么可以获取信号量。
释放信号量要将自己的优先级还原,并给下一个等待者。最后设置
创建是从
利用
如果没等到则调用
重新回到任务判断是否等到或时间超时
将当前任务放到
先将flags改变的位添加进pgrp的flags
查看所有的等待的任务,是否符合他们等待的结果
设置自己的
创建事件
事件标记发送方
事件标记接收方
注意设置等待标志为
测试
实现数字转标志的函数:
每一个显示函数的框架如下:
测试结果如下:
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); } }
测试结果如下:
相关文章推荐
- ucos-ii学习环境的搭建
- uCOS/ii 学习心得及体会
- uCOS-II 学习笔记之事件管理--------事件管理程序
- ucos-ii学习笔记——消息邮箱的原理及使用
- ucos-ii学习笔记——首个多任务程序的创建
- uCOS-II信号量学习
- uCOS-II学习笔记:实时操作系统(一)
- uCOS-II 学习笔记之任务管理--------任务控制块OS_TCB
- uCOS-II 学习笔记之事件管理--------信号量管理的学习
- uCOS-II学习笔记之就绪表
- uCOS-II信号量学习
- UCOS_II学习笔记---任务管理之建立任务函数分析
- UCOS_II学习笔记---任务管理之任务切换
- uCOS-II 学习笔记之时间管理
- uCOS-II学习笔记之- uCOS-II实时操作系统的特点
- 开始学习uCOS-II
- UCOS-II学习1_ex1
- UCOS_II学习笔记---任务管理之请求删除任务函数分析
- 关于学习uCOS-II
- UCOSII task 任务建立笔记记录