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

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

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

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

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

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

######################################################################
#                                   TOOLS
######################################################################BORLAND=C:\BC45CC=$(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=..\WORKOS=\SOFTWARE\uCOS-II\SOURCE
PC=\SOFTWARE\BLOCKS\PC\BC45
PORT=\SOFTWARE\uCOS-II\Ix86L\BC45

即将Borland cTASM安装到根目录下,并将本示例工程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 Taskwhile(1){//Do whatever You Want In taskOSTimeDly(100);//让出CPU让其他任务执行}
}

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

PART5 任务的创建

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

而在TASK_START中我们的Task0TASK1是利用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 错误类型
  • peventOSEventCnt 保存信号量的值

当返回有两种可能:

  • errOS_NO_ERR 则为获取了该信号量
  • errOS_TIMEOUT 则为超时

给予信号量:OSSemPost()

INT8U OSSemPost (OS_EVENT *pevent)

  • 若有任务在等这个信号量
  • 将优先级最高的放入就绪队列并消除TCB的标志。

删除信号量:OSSemDel()

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

  • optOS_DEL_NO_PEND 没有任务在等的时候删除。
  • optOS_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);}
}

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

Part 3 生产者消费者

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

#define OS_MAX_EVENTS             10   

新增加信号量

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

设置缓存

int buff[10];

生产者与消费者

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

生产者:

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

void  Task0 (void *pdata){INT8U *err;int count = 13;int cur_pos = 0;char s[40];pdata = pdata;while(1){//等待emptyOSSemPend (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 ++;//通知fullOSSemPost (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){//等待fullOSSemPend (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;//通知emptyOSSemPost (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中所有位为0OS_FLAG_WAIT_SET_ALL   期望指定flags中所有位为1OS_FLAG_WAIT_CLR_ANY   期望指定flags中任意一位及以上为0OS_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学习记录相关推荐

  1. CFA II 学习记录-Quantitative

    只能感叹,学习的起点就是记忆:而遗忘就是衰老的起点. 我本来的打算是以CFA II的知识点辅助我弥补申请中的不足,没想到竟然如此难以理解,每个知识点都要折腾许久,不过明白了这句话的含义"知识 ...

  2. CFA II学习记录-Economics

    看来就是搞清外汇就行,那么就是宏观经济学,那么就是之前学过的Global Economics中的一部分,那么暂时不急. CFA二级经济学相对于一级经济学或者相对于二级其他科目,内容少了很多,学习起来也 ...

  3. Redis的学习记录

    Redis的学习记录 1.先导了解 1.1 NOSQL概述 1.1.1 为什么要用NoSql? 1.1.2 NoSql了解 1.1.3 NoSql特点 1.1.4 NoSQL的四大分类 2. Redi ...

  4. ucos iii学习笔记——为什么选择ucos iii

    首先我们得先讨论前后台系统和RTOS(Real Time OS)的区别.前后台系统,也即是我们所说的裸机程序,它的结构通常包括一个死循环和若干个中断服务程序,直接上图,我们有一个直观认识: 假如Tas ...

  5. 小余学调度:学习记录(2021.9.13-2021.9.19)母线操作和线路操作

    小余学调度系列文章,记录小余同学入职电力调度员一路的学习记录,由于工作性质,在这个系列,只写能公开的知识点,不涉及机密. 提示:专栏解锁后,可以看这个专栏所有文章,划算. 文章目录 一.母线停送电和倒 ...

  6. 小余学调度:学习记录(2021.8.30-2021-9.5)

    小余学调度系列文章,记录小余同学入职电力调度员一路的学习记录,由于工作性质,在这个系列,只写能公开的知识点,不涉及机密. 提示:专栏解锁后,可以看这个专栏所有文章,划算. 文章目录 一.如何从接线图中 ...

  7. 2021-01-22学习记录 || 通过二维数组初始化窗体并进行代码重构

    今天主要是通过二维数组将整个界面16个数字块展示出来,并为了下一步添加左移.右移功能创建子类MainFrame继承JFrame类并进行代码重构. 二维数组展示初始化界面 由于2048小游戏需要16个数 ...

  8. linux个人学习记录

    linux学习记录 资料: Linux 黑马程序员_bilibili AcWing Linux基础课 可能是东半球最全面易懂的 Tmux 使用教程! Shell 教程 | 菜鸟教程 (runoob.c ...

  9. Matlab学习记录-矩阵的生成

    Matlab学习记录-矩阵的生成 matlab中生成矩阵有两种方式: 1.中括号加分号. 中括号表示矩阵,分号表示分行:每一行之间的元素可以用逗号分开也可以用空格分开: 例如 a=[1 2 3; 4 ...

最新文章

  1. 【 FPGA 】FIR滤波器目录
  2. 字符串转64位int(strtol,_atoi64)
  3. 深挖BAT内部级别和薪资待遇
  4. CentOS7安装mysql数据库
  5. POJ 1002 487-3279
  6. 基于【CentOS-7+ Ambari 2.7.0 + HDP 3.0】搭建HAWQ数据仓库02 ——使用ambari-server安装HDP...
  7. oracle mysql 透明网关_如何在Oracle中建立透明网关
  8. Ubuntu中Qt5.7.0无法输入中文
  9. vue暂存功能_vue路由缓存的几种实现方式小结
  10. python软件下载安装百度网盘-Python自动化测试视频教程【百度云盘下载】
  11. 深入浅出Java反射机制
  12. 转载+收藏 数理化地生常用软件
  13. js清空浏览器cokie缓存_JS设置cookie,删除cookie
  14. 常用APDU指令错误码
  15. C#生成Code39条形码【非条形码字体】
  16. 游戏给你带来了什么,你还在执迷不悟吗?
  17. 爬虫数据分析:上海旅游景点排名分析
  18. 判断图片是否为现场照片(Live Photo亦即内含Exif信息)
  19. 系统工程--011详细设计 伪码 程序流程图 PAD图 N-S图 判断表和判断树
  20. win10 更新后摄像头问题

热门文章

  1. 【Shell】测试脚本
  2. Vulnhub-Tr0ll
  3. OpenCV图像处理学习二十,图像直方图均衡化原理与实现
  4. 车载充电器E-mark认证需要的时间
  5. 笔记本电脑C盘变红或变满,怎么处理?
  6. c语言 long long %d,定义一个long long类型,用%d打印的结果
  7. 基于51单片机密码锁数码管显示步进电机开锁设计
  8. Java基础 项目实例五 简易聊天系统
  9. 个人收款码跟聚合码的区别
  10. 【机器学习课程-华盛顿大学】:4 聚类和检索 4.4 MoG混合高斯模型编程测试