打印
[牛人杂谈]

浅谈ucos 2的信号量

[复制链接]
923|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ccw1986|  楼主 | 2016-4-14 22:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
这2天在已有空就看信号量,本来没头绪的,查了资料也没找到有用的东西,不过在例子中找到了感觉。于是重新把书那章看了看,然后亲自调试代码,终于摸到了头绪。       首先我很膜拜下大师。首先不说这个操作系统的有多复杂,大师写的代码这么规范,是我自叹不如的,大师的代码层次分明注释详细,可以看出大师是个十分严谨的人。就光这点让人不得不佩服。然后是操作系统,虽然大师也是从ucos 2那里学到得,不过大师愿意把自己写的用于51的操作系统分享给大家,这点更加的崇敬。
     现在回到正题,本来是想理解深透了再写的,但是怕自己忙着进一步的研究,没时间更新这边的**。首先信号量是个什么?信号量就象交通灯一样,它告诉控制了汽车的流动。信号量控制了程序的同步运行。谁拿到了信号就可以享用CPU的时间片,没有信号则任务被挂起。OSSemCreate 创建信号量,实际上是初始化信号量数组。OSSemPend()等待一个信号量
   这里首先在任务里初始化信号量,然后在使用等待信号量的函数,一旦有信号量,则立刻返回OS_SEM_OK,取得信号可以做下一步操作,如果取不到信号,则任务被挂起,在挂起之前,必须把局部变量存放到堆栈中,这也是书上说的为了保证重入性。     
            SP++;
        *((uint8 data *)SP) = Index;  
上面则把信号量的索引保存到堆栈中。然后清任务标志再切换到其他任务中,在这里有必要一提的事,在任务运行中,切到其他任务中只有几种方式,1,中断 2.通过系统睡眠函数 OSWait() 3.任务切换函数OSSched() 。 在OSSemPend()这个函数里开了临界区,所以中断不存在,睡眠函数也没有,那么任务就一致运行直到OSSched() 函数的出现,吧CPU交给其他任务,同时正在运行的任务由于清掉了就绪的标志位,它的恢复有2种情况  1.超时时间到了,中断吧时间片切给它继续运行 2.OSSemPost()它调用OSSemintPost()会把等待信号量的表中吧最高优先级的任务的就绪表置1,然后切换并给与信号量,相当于等待到信号量并继续未完成的工作。
      时间不早了,下回更新吧,我睡觉了。
      虽然头又点疼,但还是吧**更新完再说吧。好像上面已经把信号量说完了。当又信号就OSSemPend会返回得到信号的标志于是就进行对某个硬件的处理,如果没得到则挂起任务,时间片留给其他任务,由于清掉了任务就绪表的标志所以挂起的任务只有等到超时或者某个中断处理中发送了信号,唤醒等待该信号量的任务。  大体就是这样子。至于调试吗,在EN_OS_SEM  宏定义开启,然后就可以使用信号量的函数了。你可以设置个全局变量,然后在2个任务中对他赋值,然后给他们控制信号量来达到测试目的。本人写的代码还有点问题,我挂起的任务唤醒,发现PC走错地址了,知道哪有问题,但一时头疼,无法继续。消息队列下回再更新。
沙发
稳稳の幸福| | 2016-4-14 22:24 | 只看该作者
#include "INCLUDES.h"
#define  TASK_STK_SIZE        512                 
   
char *s1="MyTask";
char *s2="YouTask";
INT8U err;   //定义一个错误信息
INT8U y=0;
OS_EVENT *Fun_Semp;    //声明信号量  是事件控制块ECB类型的
//注意,前面有一个例子2 定义了互斥信号量,定义如下
//BOOLEAN   ac_key;   //信号量,互斥信号量 实质上就是一个标志位,是一个全局变量,来标志共享资源的访问情况
//这样,当已经有任务访问共享资源时,其他的任务就不能访问,知道该资源未被访问,其他的任务才可以进行访问
//注意这两个信号量的区别和使用情况
OS_STK        StartTaskStk[TASK_STK_SIZE];   //定义任务堆栈区
OS_STK        MyTaskStk[TASK_STK_SIZE];
OS_STK        YouTaskStk[TASK_STK_SIZE];
void  Fun(INT8U x,INT8U y);
void  StartTask(void *data);
void  MyTask(void *data);                  
void  YouTask(void *data);
void  main (void)
{
Fun_Semp=OSSemCreate(1); //在主函数中创建信号量 返回值为创建的信号量指针,参数是信号量的计数器的值
//用该参数对信号量计数器OSEventCnt进行初始化
//1即代表只创建一个信号量,代表信号量用于对共享资源的访问(例如,把它当做二值信号量使用),详见P166
    OSInit();                                             
    PC_DOSSaveReturn();                                    
    PC_VectSet(uCOS, OSCtxSw);                             
   
    OSTaskCreate(StartTask,(void *)0, &StartTaskStk[TASK_STK_SIZE - 1], 0); //创建起始函数
   
    OSStart();      
}
void  StartTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3        
    OS_CPU_SR  cpu_sr;
#endif
    INT16S        key;            
    pdata = pdata;        
    OS_ENTER_CRITICAL();
    PC_VectSet(0x08, OSTickISR);                           
    PC_SetTickRate(OS_TICKS_PER_SEC);                     
    OS_EXIT_CRITICAL();
    OSStatInit();                                          
   
    OSTaskCreate(MyTask,(void *)0, &MyTaskStk[TASK_STK_SIZE - 1], 1); //创建任务函数
OSTaskCreate(YouTask,(void *)0, &YouTaskStk[TASK_STK_SIZE - 1], 2); //创建任务函数
for (;;)
    {
     //如果恩下ESC键,则退出UC/OS-II
        if (PC_GetKey(&key) == TRUE)
         {                     
            if (key == 0x1B)
            {                             
                PC_DOSReturn();                           
            }
        }
        OSTimeDlyHMSM(0,0,3,0);                        
    }
}
//MyTask的函数代码
void  MyTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3        
    OS_CPU_SR  cpu_sr;
#endif
   
    pdata = pdata;   
    for (;;)
    {
     OSSemPend(Fun_Semp,0,&err);   //请求信号量 参数Fun_Semp是信号量指针  0那一项是等待时限timeout0表示无限等待
     //err表示错误信息
   
PC_DispStr(0,++y,s1,DISP_BGND_BLACK+DISP_FGND_WHITE);  //显示MyTask字符串
Fun(7,y);  //调用Fun函数
OSSemPost(Fun_Semp);    //发送信号量 释放信号量,函数的参数Fun_Semp代表信号量的指针
        OSTimeDlyHMSM(0,0,1,0);  
    }
}
void  YouTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3        //Allocate storage for CPU status register
    OS_CPU_SR  cpu_sr;
#endif
pdata=pdata;
for (;;)
{
OSSemPend(Fun_Semp,0,&err);    //请求信号量
PC_DispStr(0,++y,s2,DISP_BGND_BLACK+DISP_FGND_WHITE);
Fun(7,y);   //调用FUN函数
OSSemPost(Fun_Semp);   //释放信号量
        OSTimeDlyHMSM(0,0,2,0);    //等待2s               
    }
}
//公共的函数Fun的代码
void Fun(INT8U x,INT8U y)
{
PC_DispStr(x,y,"  Calling FUN()",DISP_BGND_BLACK+DISP_FGND_WHITE); //显示字符串,表示调用了Fun函数
}

使用特权

评论回复
板凳
稳稳の幸福| | 2016-4-14 22:25 | 只看该作者
创建信号量时,用的参数为1,即Fun_Semp=OSSemCreate(1); ,只创建了一个信号量,这种情况一般是信号量用于对
//共享资源的访问(例如,可以把它当做二值信号量使用)
//在上面的程序中,当MyTask运行时,先请求获得了信号量,对共享资源Fun函数进行访问,由于只创建了一个信号量,
//所以在MyTask的访问期间,即使任务YouTask也进行申请信号量,此时OSEventCnt是值已经为0了,所以会把任务
//YouTask列入任务等代表OSEventTbl[]中,使任务处于等待状态。
//只有等MyTaskFun函数访问完成了,调用OSSemPost(Fun_Semp);释放了信号量,该释放信号量的函数会先检查任务等待
//表中是否还有等待信号量的任务,如果有,则使任务进入就绪态后,调用调度器OS_Sched()引发一次任务调度,去运行等待
//任务列表中优先级最高的任务。如果没有,则就把信号量计数器OSSemCnt1.
//所以任务YouTask要想访问Fun()函数,必须等到任务MyTaskFun访问完毕,释放了信号量之后,才能访问,反之亦然
//所以由上面可以看出,只创建一个信号量,即OSSemCreate(1);,作用就相当于使用一个二值信号量,标志共享资源是否正在
//被访问
//看懂了上面的分析,也就可以解释实验现象了,由于YouTask等待2sMyTask等待1s,所以有可能在MyTask访问Fan函数期间,YouTask
//也来访问(也有可能是反过来),但是由于信号量已经被MyTask占用了,所YouTask只好等待,MyTask使用完了,释放了信号量,YouTask才能正常使用Fun函数
//这样也就解决了多任务对共享资源的使用的问题,使任务之间得到了同步
//要仔细分析信号量工作的原理,把上面的内容看懂了,基本上也就可以使用信号量了

使用特权

评论回复
地板
zhuotuzi| | 2016-4-17 21:49 | 只看该作者
关于操作系统的知识是很抽象的,坐等楼主更新

使用特权

评论回复
5
Micachl| | 2016-4-18 22:27 | 只看该作者
我记得我用信号量的时候总是死机,也不太清楚为什么

使用特权

评论回复
6
gejigeji521| | 2016-4-18 22:38 | 只看该作者
UC OS只会用,不深度研究,没楼主这么认真,研究这么透彻

使用特权

评论回复
7
deviceplugs| | 2016-4-19 11:19 | 只看该作者
ucos ii中的互斥信号量到底该怎样用呢,感觉一头雾水啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

84

主题

925

帖子

6

粉丝