本文主要记录使用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学习记录的更多相关文章

  1. Matlab 进阶学习记录

    最近在看 Faster RCNN的Matlab code,发现很多matlab技巧,在此记录: 1. conf_proposal  =  proposal_config('image_means', ...

  2. DE1-SOC开发板上搭建NIOS II处理器运行UCOS II

    DE1-SOC开发板上搭建NIOS II处理器运行UCOS II   今天在DE1-SOC的开发板上搭建NIOS II软核运行了UCOS II,整个开发过程比较繁琐,稍微有一步做的不对,就会导致整个过 ...

  3. Java 8 学习记录

    Java 8 学习记录 官方文档 https://docs.oracle.com/javase/8/ https://docs.oracle.com/javase/8/docs/index.html ...

  4. Quartz 学习记录1

    原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...

  5. Java 静态内部类与非静态内部类 学习记录.

    目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ...

  6. Apache Shiro 学习记录4

    今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...

  7. UWP学习记录12-应用到应用的通信

    UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...

  8. UWP学习记录11-设计和UI

    UWP学习记录11-设计和UI 1.输入和设备 通用 Windows 平台 (UWP) 中的用户交互组合了输入和输出源(例如鼠标.键盘.笔.触摸.触摸板.语音.Cortana.控制器.手势.注视等)以 ...

  9. UWP学习记录10-设计和UI之控件和模式7

    UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ...

随机推荐

  1. 设计模式笔记——GoF设计模式汇总

    目录 · 总述 · 记忆 · 效果 · 面向对象设计原则 · 创建型模式 · 单例模式(Singleton) · 效果 · 分类 · 代码(饿汉式) · 代码(懒汉式) · 代码(双重检测锁式) ·  ...

  2. 教你用python写:HDU刷题神器

    声明:本文以学习为目的,请不要影响他人正常判题 HDU刷题神器,早已被前辈们做出来了,不过没有见过用python写的.大一的时候见识了学长写这个,当时还是一脸懵逼,只知道这玩意儿好屌-.时隔一年,决定 ...

  3. python--DenyHttp项目(2)--ACM监考客户端1.0版

    修复了: 360搜索可以使用的漏洞 更新版本,上一版本复制的Hosts文件保留的漏洞 #coding:gbk import os import sys from subprocess import * ...

  4. java中构造方法及其作用

    构造方法作用就是对类进行初始化. 如果你没有定议任何构造方法的形式,程式会为你取一个不带任何参数的构造函数,那么你产生类的对像时只能用不带参数的方法,如:class a {}//没有任何构造函数. 构 ...

  5. 在Entity Framework 中用 Code First 创建新的数据库

    在Entity Framework 中用 Code First 创建新的数据库 (原文链接) 本文将逐步介绍怎样用Code First 创建新数据库,使用在代码中定义类和API中提供的特性(Attri ...

  6. Python requests 安装与开发

    Requests 是用Python语言编写HTTP客户端库,跟urllib.urllib2类似,基于 urllib,但比 urllib 更加方便,可以节约我们大量的工作,完全满足 HTTP 测试需求, ...

  7. python基础===zip在python3中的用法

    name=["ad","kein","tom"] age=[23,45,22] tel=['157','139','167'] print( ...

  8. python基础===Python 代码优化常见技巧

    Python 代码优化常见技巧 代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构.优化.扩展以及文档相关的事情通常需要消耗 8 ...

  9. FFT 专题讲解

    FFT是什么? FFT是快速傅里叶变换(fast Fourier transform)的简称.在ACM领域主要是用来快速求解多项式乘法的算法, 在信号领域也有很大用途 基础知识 卷积 举个例子,给你两 ...

  10. 使用Openfire配置安卓客户端聊天服务器

    Openfire是一种即时通信服务器,采用开放的XMPP协议,您可以使用各种支持XMPP协议的IM客户端软件登陆服务. Openfire 采用Java开发,开源的实时协作(RTC)服务器基于XMPP( ...