目录
实验要求
1、在内存空间开辟了一片区域,供4个任务共享读写。一个任务在写内存时,其它任务不能读写。多个任务可以同时读该内存区域数据。
键盘按键1-4分别是task1、task2、task3、task4的写控制键,键盘按键5-8分别是task1、task2、task3、task4的读控制键。
每按下一次写控制键,在共享区域的数据加1,超过99变为0,显示“写倒计时”在屏幕上。按下读控制键时,对应的读任务读取共享区域的数据并显示数据。一个任务的写控制键按下且“写倒计时”未结束时,其他任务的写控制键不能写,同时所有的读控制键也不能读,如果此时有读写动作(按下相应的按键),屏幕应显示报警。
采用消息邮箱机制完成此实验。一次读或写任务持续时间5秒。
2、在实验1的基础上增加如下功能:读任务在读共享数据时,所有写任务不能向共享区域写入数据,但其它读任务依然可以读取共享数据,每个读任务的时间5秒,在屏幕上显示“读倒计时”。写任务必须在所有读任务都完成读操作后才能开始写操作。比如,一个读了一次,2秒后另一个任务再次读共享区,此后就没有任务读共享数据,那么写任务只能在7秒后写入数据。
用信号量集完成此实验。
参考链接:uCOS-II多任务内存资源使用_ucosii有两个任务对同一个数组进行写入与读取操作-CSDN博客
一、消息邮箱
#include "includes.h"
#define TASK_STK_SIZE 512 //任务堆栈长度
#define TIME 5 //eat_time -> TIME - j
#define col 10
OS_STK StartTaskStk[TASK_STK_SIZE]; //定义任务堆栈区
OS_STK WriteReadTaskStk[4][TASK_STK_SIZE];
INT8U numberID[]={1,2,3,4};
INT16S key; //用于退出uCOS_II的键
INT16S key_write[] = {0x1B,0x31,0x32,0x33,0x34}; //检测输入
INT16S key_read[] = {0x1B,0x35,0x36,0x37,0x38};
INT16S wait_ticks; //用于循环遍历的变量
INT8U Intpart[5][4]; //3个块的分区
INT8U *IntBlkPtr; //定义内存块的指针,这是全局变量呀!
char *msg1 = "can"; //write
char *msg2 = "can't"; //read
OS_MBOX_DATA *p;
OS_MEM *IntBuffer; //块的指针。
BOOLEAN getKey; //用于获取键值的变量
OS_EVENT *StrBox; //创建一个消息邮箱指针
void StartTask(void *data);
void Task(void *data);
void my_write(INT8U id);
void my_read(INT8U id);
/************************主函数*********************************************/
void main (void)
{
INT8U err;
char temp[50];
PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK);
OSInit(); //初始化uCOS_II
PC_DOSSaveReturn(); //保存Dos环境
PC_VectSet(uCOS, OSCtxSw); //安装uCOS_II中断
//创建一个消息邮箱
StrBox = OSMboxCreate(msg1); //初始化消息是可以写
IntBuffer = OSMemCreate(Intpart,5,4,&err); //管理分区
OSTaskCreate(StartTask,0,&StartTaskStk[TASK_STK_SIZE - 1],3);
OSStart();
}
//*****************************StartTask********************************************
void StartTask(void *pdata)
{
char s1[100];
INT8U err1,i;
pdata=pdata;
OS_ENTER_CRITICAL();
PC_VectSet(0x08, OSTickISR);
PC_SetTickRate(OS_TICKS_PER_SEC);
OS_EXIT_CRITICAL();
OSStatInit();
//请求一个内存块
IntBlkPtr = OSMemGet(IntBuffer,&err1); //获得一块内存
*IntBlkPtr = 0; //首先赋初值,不用它了
for(i=0;i<4;i++)
{
OSTaskCreate(Task,(void*)&numberID[i],&WriteReadTaskStk[i][TASK_STK_SIZE-1],i+6);
}
for(; ;)
{
getKey = PC_GetKey(&key); //实时获取按键值
if(key==key_write[0]) //退出程序
{
PC_DOSReturn();
}
sprintf(s1,"%x",key);
PC_DispStr(col,0,s1,DISP_BGND_BLACK+DISP_FGND_WHITE); //打印了键值
OSTimeDly(1); //为了能够实时获取按键值
}
}
void Task(void *pdata)
{
char *s1= "write ok",*s2 = "read ok",*s3 = "wating for operation! ";
char *s4 = "You can't read now! ";
char *s5 = "You can't operate now! ";
INT8U err1,err2;
char *sr; //write
char *sr2; //read
char ss[50];
INT8U ID;
ID = *(INT8U *)pdata;
PC_GetKey(&key);
for(;;)
{
if (key == key_write[ID])
{
PC_DispStr(20,20,s2,DISP_BGND_BLACK+DISP_FGND_WHITE);
sr=OSMboxPend(StrBox,1,&err1); //我要保证一定能读取到
if(err1==OS_NO_ERR ) //可以写的情况下
{
if(*IntBlkPtr<9)
{
*IntBlkPtr += 1;
}
else
{
*IntBlkPtr = 0;
}
my_write(ID);
OSMboxPost(StrBox,msg2);
}
else
{
PC_DispStr(col,ID+10,s5,DISP_BGND_BLACK+DISP_FGND_RED);
OSTimeDly(200*TIME-wait_ticks);
}
}
else if(key == key_read[ID])
{
sr2 = OSMboxPend(StrBox,1,&err2); //这个表示可以读
if(err2==OS_NO_ERR )
{
OS_ENTER_CRITICAL();
OSMboxPost(StrBox,msg2);
OS_EXIT_CRITICAL();
my_read(ID);
}
else
{
PC_DispStr(col,ID+10,s5,DISP_BGND_BLACK+DISP_FGND_RED);
OSTimeDly(200*TIME-wait_ticks);
}
}
else
{
PC_DispStr(col,ID+10,s3,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSTimeDly(1);
}
}
}
void my_write(INT8U id)
{
char ss[100];
INT8U err;
for(wait_ticks=0;wait_ticks<TIME*200;wait_ticks++)
{
sprintf(ss,"task_%d is writing, count down: %d ",id,(TIME-wait_ticks/200));
PC_DispStr(col,id+10,ss,DISP_BGND_BLACK+DISP_FGND_YELLOW);
OSTimeDly(1);
}
OSMboxPost(StrBox,msg1);
}
void my_read(INT8U id)
{
char ss[100];
INT8U err;
INT8U i;
for(i=0;i<5;i++)
{
sprintf(ss,"task_%d read the number is: %d count down: %d",id,*IntBlkPtr,5-i);
PC_DispStr(col,id+10,ss,DISP_BGND_BLACK+DISP_FGND_YELLOW);
OSTimeDly(200);
}
OSMboxPost(StrBox,msg1);
}
二、信号量集
方法有多种,可以用信号量集的两个标志位或四个标志位。笔者在此用了信号量集中的六个标志位,相对较复杂,仅供参考。第六位判断是否允许读,第五位判断是否允许写。
#include "includes.h"
#define TASK_STK_SIZE 512 //任务堆栈长度
#define TIME 5 //eat_time -> TIME - j
#define col 10
OS_STK StartTaskStk[TASK_STK_SIZE]; //定义任务堆栈区
OS_STK WriteReadTaskStk[4][TASK_STK_SIZE];
INT8U numberID[]={1,2,3,4};
INT16S key; //用于退出uCOS_II的键
INT16S key_write[] = {0x1B,0x31,0x32,0x33,0x34}; //检测输入
INT16S key_read[] = {0x1B,0x35,0x36,0x37,0x38};
INT16S wait_ticks; //用于循环遍历的变量
//INT8U flag_write[]={0,7,11,13,14};
INT8U flag_write[]={0,23,27,29,30};
// INT8U flag_read[]={0,1,8,4,2};
INT8U flag_read[]={0,33,40,36,34};
INT8U Intpart[5][4]; //5个块的分区
INT8U *IntBlkPtr; //定义内存块的指针,这是全局变量呀!
char *msg1 = "can"; //write
char *msg2 = "can't"; //read
OS_MBOX_DATA *p;
OS_MEM *IntBuffer; //块的指针。
BOOLEAN getKey; //用于获取键值的变量
OS_EVENT *StrBox; //创建一个消息邮箱指针
OS_FLAG_GRP *Sem_f;
void StartTask(void *data);
void Task(void *data);
void my_write(INT8U id);
void my_read(INT8U id);
/************************主函数*********************************************/
void main (void)
{
INT8U err;
char temp[50];
PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK);
OSInit(); //初始化uCOS_II
PC_DOSSaveReturn(); //保存Dos环境
PC_VectSet(uCOS, OSCtxSw); //安装uCOS_II中断
//创建一个消息邮箱
Sem_f=OSFlagCreate(0,&err);
StrBox = OSMboxCreate(msg1); //初始化消息是可以写
IntBuffer = OSMemCreate(Intpart,5,4,&err); //管理分区
OSTaskCreate(StartTask,0,&StartTaskStk[TASK_STK_SIZE - 1],3);
OSStart();
}
//*****************************StartTask********************************************
void StartTask(void *pdata)
{
char s1[100]; //必须先分配好内存,这样不会跑飞
INT8U err1,i;
pdata=pdata;
OS_ENTER_CRITICAL();
PC_VectSet(0x08, OSTickISR);
PC_SetTickRate(OS_TICKS_PER_SEC);
OS_EXIT_CRITICAL();
OSStatInit();
//请求一个内存块
IntBlkPtr = OSMemGet(IntBuffer,&err1); //获得一块内存
*IntBlkPtr = 0; //首先赋初值,不用它了
for(i=0;i<4;i++)
{
OSTaskCreate(Task,(void*)&numberID[i],&WriteReadTaskStk[i][TASK_STK_SIZE-1],i+6);
}
for(; ;)
{
getKey = PC_GetKey(&key); //实时获取按键值
if(key==key_write[0]) //退出程序
{
PC_DOSReturn();
}
sprintf(s1,"%x",key);
PC_DispStr(col,0,s1,DISP_BGND_BLACK+DISP_FGND_WHITE); //打印了键值
OSTimeDly(1); //为了能够实时获取按键值
}
}
INT8U count=0;
void Task(void *pdata)
{
char *s1= "write ok",*s2 = "read ok",*s3 = "wating for operation! ";
char *s4 = "You can't read now! ";
char *s5 = "You can't operate now! ";
INT8U err1,err2;
INT8U err3,err4;
char *sr; //write
char *sr2; //read
char ss[50];
INT8U ID;
ID = *(INT8U *)pdata;
PC_GetKey(&key);
for(;;)
{
if (key == key_write[ID])
{
OSFlagPost(Sem_f, //此时不允许读
(OS_FLAGS)32, //OS_FLAG_WAIT_SET_ALL
OS_FLAG_SET,&err2);
OSFlagPend(Sem_f,
(OS_FLAGS)flag_write[ID], //OS_FLAG_WAIT_SET_ALL
OS_FLAG_WAIT_CLR_ALL+OS_FLAG_CONSUME,1,&err1);
PC_DispStr(20,20,s2,DISP_BGND_BLACK+DISP_FGND_WHITE);
if(err1==OS_NO_ERR ) //可以写的情况下
{
if(*IntBlkPtr<9)
{
*IntBlkPtr += 1;
}
else
{
*IntBlkPtr = 0;
}
my_write(ID);
}
else
{
PC_DispStr(col,ID+10,s5,DISP_BGND_BLACK+DISP_FGND_RED);
OSTimeDly(200);
}
}
else if(key == key_read[ID])
{
OSFlagPost(Sem_f, //此时不允许写
(OS_FLAGS)16,
OS_FLAG_SET,&err2);
OSFlagPend(Sem_f,
(OS_FLAGS)flag_read[ID],
OS_FLAG_WAIT_CLR_ALL,1,&err2);
if(err2==OS_NO_ERR) //这个表示可以读
{
OS_ENTER_CRITICAL();
OS_EXIT_CRITICAL();
count++;
my_read(ID);
}
else
{
PC_DispStr(col,ID+10,s5,DISP_BGND_BLACK+DISP_FGND_RED);
OSTimeDly(200);
}
}
else
{
PC_DispStr(col,ID+10,s3,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSTimeDly(1);
}
}
}
void my_write(INT8U id)
{
char ss[100];
INT8U err;
for(wait_ticks=0;wait_ticks<TIME*200;wait_ticks++)
{
sprintf(ss,"task_%d is writing, count down: %d ",id,(TIME-wait_ticks/200));
PC_DispStr(col,id+10,ss,DISP_BGND_BLACK+DISP_FGND_YELLOW);
OSTimeDly(1);
}
OSFlagPost(Sem_f,
(OS_FLAGS)flag_write[id],
OS_FLAG_CLR,&err);
OSFlagPost(Sem_f, //允许读
(OS_FLAGS)32,
OS_FLAG_CLR,&err);
}
void my_read(INT8U id)
{
char ss[100];
INT8U err;
INT8U i;
for(i=0;i<5;i++)
{
sprintf(ss,"task_%d read the number is: %d count down: %d",id,*IntBlkPtr,5-i);
PC_DispStr(col,id+10,ss,DISP_BGND_BLACK+DISP_FGND_YELLOW);
OSTimeDly(200);
}
count--;
if(count==0)
{
OSFlagPost(Sem_f, //允许写
(OS_FLAGS)16,
OS_FLAG_CLR,&err);
}
OSFlagPost(Sem_f,
(OS_FLAGS)flag_read[id],
OS_FLAG_CLR,&err);
}
运行结果
当开始写内存时,其他写内存任务和读任务都不能执行
可以同时进行多个读任务,但写任务不能执行