现在的位置: 首页 > 综合 > 正文

uC/OS-II 学习笔记之:信号量

2013年09月01日 ⁄ 综合 ⁄ 共 4069字 ⁄ 字号 评论关闭

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

更多原创“uC/OS-II学习笔记之:系列”基础及嵌入式相关知识详解,请访问可乐虎博客:

http://blog.csdn.net/dcx1205

相信不会让您失望!!
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

一、信号量概念:
    打个形象的比方:假如每个学生进入教室时,必须向管理员申请到一张通行证才能允许进入,出教室时必须将手中通行证归还给管理员才能出来。
    现在分两种情况进行阐述:

    情况一:管理员手中只有1张通行证,那么每次就只能允许一个学生进入教室,只有当进入的学生出来归还通行证后,才允许其它的学生进入教室。
    情况二:管理员手中有N张(N>1)通行证,那么每次就允许N个同学进入教室,从第(N+1)个同学开始,剩下的就只能在教室门外等候,直到有同学出来归还了通行证,剩下的同学才可能进入教室。
 
(1)这里我们要讲的信号量就是通行证的数量。
(2)学生可以看作是“任务”。
(3)学生向管理员申请通行证的过程可以看作是“请求信号量”。
(4)学生归还通行证给管理员的过程可以看作是“释放信号量”。
(5)当管理员手中的通行证数量大于0时,请求信号量才能成功。
(6)请求信号量成功一次,则信号量减1。
(7)释放信号量一次,则信号量加1。

二、对信号量的操作:
(1)创建信号量
     OS_EVENT *OSSemCreate(INT16U cnt);//cnt是信号量计数器设置的初值
(2)请求信号量
     void OSSemPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);//形参分别为信号量的指针,等待时限,错误信息
(3)发送信号量
     INT8U OSSemPost(OS_EVENT *pevent);//信号量的指针
(4)删除信号量
     OS_EVENT *OSSemDel(OS_EVENT *pevent, INT8U opt, INT8U *err);//形参分别为信号量的指针,删除条件选项,错误信息
(5)查询信号量的状态
     INT8U OSSemQuery(OS_EVENT *pevent, OS_SEM_DATA *pdata);//形参分别为信号量的指针,存储信号量状态的结构

三、信号量的应用举例:
(1)创建、请求、发送、信号量
//LED0的状态必须经LED1的允许才能翻转
#include "sys.h"
#include "usart.h"  
#include "delay.h" 
#include "led.h"
#include "key.h" 
#include "includes.h" 
 
//设置任务堆栈大小
#define LED0_STK_SIZE       64
#define LED1_STK_SIZE       64
#define START_STK_SIZE      128

//任务堆栈
OS_STK  TASK_LED0_STK[LED0_STK_SIZE];
OS_STK  TASK_LED1_STK[LED1_STK_SIZE];
OS_STK  TASK_START_STK[START_STK_SIZE];

//声明信号量
OS_EVENT *pSemaphore;
INT8U err;

//设置任务优先级
#define LED0_TASK_Prio      1
#define LED1_TASK_Prio      2
#define START_TASK_Prio     0

//任务申明
void TaskStart(void *pdata);
void TaskLed0(void *pdata);
void TaskLed1(void *pdata);
void SysTick_Configuration(void);//系统时钟配置函数
     
int main(void)
{       
    Stm32_Clock_Init(9);//系统时钟设置
    delay_init(72);//延时初始化
    uart_init(72,9600);//串口初始化为9600
    LED_Init();//初始化与LED连接的硬件接口 
    SysTick_Configuration();//系统时钟配置
    OSInit();//对uC/OS-II进行初始化
   
    //1)创建信号量
    pSemaphore = OSSemCreate(0);

    //创建起始任务
    OSTaskCreate(
                                 TaskStart,//任务指针
                                 (void *)0,//传递给任务的参数
                                 &TASK_START_STK[START_STK_SIZE-1],//任务堆栈栈顶指针
                                 START_TASK_Prio//任务优先级
                               ); 
   
    //开始多任务调度
    OSStart();//此步之后任务就交由操作系统来管理和调度了
   
    return 0;   
}

//起始任务
void TaskStart(void * pdata)
{
    pdata = pdata;//防止某些编译器报错  
    //关中断进入临界段
    OS_ENTER_CRITICAL();//此函数为汇编代码,在os_cpu_a.asm文件 里
   
    //创建任务1  
    OSTaskCreate(TaskLed0, (void *)0, &TASK_LED0_STK[LED0_STK_SIZE-1], LED0_TASK_Prio);
    //创建任务2
    OSTaskCreate(TaskLed1, (void *)0, &TASK_LED1_STK[LED1_STK_SIZE-1], LED1_TASK_Prio);
   
    OSTaskSuspend(OS_PRIO_SELF);//挂起但未删除 
    //开中断退出临界段
    OS_EXIT_CRITICAL();
}

//任务1
//控制DS0的亮灭.
void TaskLed0(void *pdata)
{
     while (1)
    {
         //2)请求信号量
        OSSemPend(pSemaphore, 0, &err);

        LED0 = !LED0;

        //延时函数参数依次为时、分、秒、毫秒
       OSTimeDlyHMSM(0, 0, 1, 0);//延时1秒

    }
}

//任务2
//控制DS1的亮灭.
void TaskLed1(void *pdata)
{
     while (1)
    {  

        LED1 = !LED1;

        //3)发送信号量
        OSSemPost(pSemaphore);

        OSTimeDlyHMSM(0, 0, 3, 0);//延时3秒 
    }
}
    现象:当不考虑信号量的请求与发送时,由任务1和2的延时情况可知,LED0的闪烁频率要比LED1高;
而加入信号量的发送与请求时,可以看到LED0与LED1同步闪烁。为什么呢?
    解析:因为在代码1)中我们可以看见创建的信号量的初始值为0,故任务1总是要在任务2发送信号量后才可以请求信号量成功,
才能使LED0闪烁;而LED1每翻转一次就发送一次信号量,故LED1每翻转一次LED0也跟着翻转一次,所以它们会同步闪烁。
    根据以上推论,若代码1)中所创建的信号量的初始值大于0,那么LED0闪烁的频率是不是要比LED1高呢?答案是肯定的!
因为若信号量的初始值大于0时,任务1可以自由的成功申请信号量,那么任务1的运行就不需要受任务2的限制了。

(2)删除信号量
     如果任务不需要某个信号量,可以调用OSSemDel(OS_EVENT *pevent, INT8U opt, INT8U *err);来删除该信号量。
注:形参opt的值可以取两个:OS_DEL_ALWAYS与OS_DEL_NO_PEND,前者表示在等待任务表中无论是否有等待任务都应该立即删除信号量,
后者表示当等待任务表中已没有等待任务时才删除信号量。

//举例:使用OS_DEL_ALWAYS删除任务1中的信号量
//任务1
//控制DS0的亮灭.
void TaskLed0(void *pdata)
{
     while (1)
    {
        OSSemPend(pSemaphore, 0, &err);//请求信号量
        OSSemDel(pSemaphore, OS_DEL_ALWAYS, &err);//删除信号量
        LED0 = !LED0;   

        //延时函数参数依次为时、分、秒、毫秒
        OSTimeDlyHMSM(0, 0, 1, 0);
    }
}
    现象:LED0闪烁频率高于LED1.
    解析:因为任务1已经删除了信号量,故不再受任务2的限制,可以自由运行了。
需要注意的是信号量只能在任务中被删除而不能在中断服务程序中被删除。

 

 

 

 

 

 

 

 

 

 

 

 

 

抱歉!评论已关闭.