【UC/OS-II】实验 内存控制

文章介绍了如何使用uCOS-II操作系统中的消息邮箱和信号量集机制,实现多个任务对同一内存区域的读写控制,确保并发访问时的互斥和同步,以保护数据一致性。
摘要由CSDN通过智能技术生成

目录

实验要求

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);
}


运行结果

        当开始写内存时,其他写内存任务和读任务都不能执行

        可以同时进行多个读任务,但写任务不能执行

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值