UCOS-II学习记录
本文主要记录使用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
放入等待队列) - 其他情况则不提升占有者的优先级。
以上提升步骤完成后,让当前任务等待该互斥信号量。启动调度。当重新回到当前任务时,要么时间超时要么可以获取信号量。
- 若没有提升优先级&&占有mutex的优先级比本任务优先级低,则提升优先级至
释放互斥信号量
释放互斥信号量的函数为
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
设置清0pgrp->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学习记录的更多相关文章
- Matlab 进阶学习记录
最近在看 Faster RCNN的Matlab code,发现很多matlab技巧,在此记录: 1. conf_proposal = proposal_config('image_means', ...
- DE1-SOC开发板上搭建NIOS II处理器运行UCOS II
DE1-SOC开发板上搭建NIOS II处理器运行UCOS II 今天在DE1-SOC的开发板上搭建NIOS II软核运行了UCOS II,整个开发过程比较繁琐,稍微有一步做的不对,就会导致整个过 ...
- Java 8 学习记录
Java 8 学习记录 官方文档 https://docs.oracle.com/javase/8/ https://docs.oracle.com/javase/8/docs/index.html ...
- Quartz 学习记录1
原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...
- Java 静态内部类与非静态内部类 学习记录.
目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ...
- Apache Shiro 学习记录4
今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...
- UWP学习记录12-应用到应用的通信
UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...
- UWP学习记录11-设计和UI
UWP学习记录11-设计和UI 1.输入和设备 通用 Windows 平台 (UWP) 中的用户交互组合了输入和输出源(例如鼠标.键盘.笔.触摸.触摸板.语音.Cortana.控制器.手势.注视等)以 ...
- UWP学习记录10-设计和UI之控件和模式7
UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ...
随机推荐
- metasploit快速入门
今天没上班,在小黑屋里看了一个一百多页的书<metasploit新手指南>,在此将笔记分享给大家.欢迎大家批评指正,共同学习进步. metasploit新手指南 笔记 kali 0 ...
- JDBC连接数据库的基本步骤
第一步:注册驱动==>:Class.forName("数据库驱动的完整名称(mysql的数据库驱动名称:com.mysql.jbdc.Driver)"); 第二步:创建一个数 ...
- 使用Nginx搭建本地流媒体服务器
Mac搭建nginx+rtmp服务器 1.打开终端,查看是否已经安装Homebrew,直接输入命令 man brew 如果Mac已经安装了, 会显示一些命令的帮助信息. 此时输入Q退出即可, 直接进入 ...
- There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
解题思路:合并两个数组,创建一个 Map对象,用以存放排好顺序的键值对,键为序号,值为数组值,中位数的结果分两种情况讨论: 1.m+n为奇数:(m+n)/2为中位数 2.m+n为偶数:(((m+n)/ ...
- gitlab 实现自动部署(简单Python实现)
功能说明: 当本地master分支执行push动作的时候,服务器端会自动执行master分支的pull操作(还可以执行一些自动化脚本) 原理: git hooks就是那些在git执行特定事件(如com ...
- Eclipse中将hadoop项目放在集群中运行
1.加入配置文件到项目源码目录下(src) <configuration> <property> <name>mapreduce.framework.name< ...
- oracle 内连接,外连接
--内连接 inner join ... on --左外连接 left join ... on --右外连接 right join ... on 列: select * from stud ...
- SSE图像算法优化系列十:简单的一个肤色检测算法的SSE优化。
在很多场合需要高效率的肤色检测代码,本人常用的一个C++版本的代码如下所示: void IM_GetRoughSkinRegion(unsigned char *Src, unsigned char ...
- python进阶(8):常用模块2+异常处理
前段时间讲了很多的模块应为当时面向对象没有讲有几个没有说今天补上,再说一个异常处理. 一.hashlib模块 Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等. 摘要算法又称哈 ...
- 从源码理解Spring原理,并用代码实现简易Spring框架
前言(本文为原创,转载请注明出处) 个人之前对于框架的学习,就停留在配置,使用阶段.说实话过段时间就会忘得荡然无存.也不知道框架的运行逻辑,就是知道添加个注解,就可以用了. 由于实习,时间比较多,也感 ...