嵌入式实时操作系统small RTOS51原理及应用 ----笔记 第六章 任务之间的通信和同步之信号量

嵌入式实时操作系统small RTOS51原理及应用 ----笔记 第六章 任务之间的通信和同步之信号量

使 Keil C51 函数 具有 重入性的特殊 方法:

在这里插入图片描述

详细情况 请看 Keil C51的编译手册:(具体网址是啥?)

比如有一个 子函数:
int add(int a ,int b)
{
int sum ;
sum = a + b ;
sum = sum +2 ;
return sum ;
}

比如任务A 调用该函数,运行到一半,切换到任务C调用,此时任务C也调用该函数,运行完毕,返回,这个时候sum的值是变化的。如果在回到任务A,继续执行,return的sum值,就是错误的了。

信号量使用场景

Small_RTOS1.12.1\small_rtos\SerialOut

使用场景:

void TaskA(void)
{
	InitSerial();
	    
	OSSemCreate(0,1);
    while (1)
    {
        OSSemPend(0,0);
        putch('A');
        putch('0');
        putch('1');
        putch('2');
        putch('3');
        putch('\n');
        OSSemPost(0);
        OSWait(K_TMO,TL0);
    } 
}

void TaskB(void)
{
    while (1)
    {
        OSSemPend(0,0);
        putch('B');
        putch('0');
        putch('1');
        putch('2');
        putch('3');
        putch('\n');
        OSSemPost(0);
        OSWait(K_TMO,TL0);
    } 
}

void TaskC(void)
{
    while (1)
    {
        OSSemPend(0,0);
        putch('C');
        putch('0');
        putch('1');
        putch('2');
        putch('3');
        putch('\n');
        OSSemPost(0);
        OSWait(K_TMO,TL0);
    } 
}


void TaskD(void)
{
    uint8 i;
    
    while (1)
    {
        OSSemPend(0,0);
        for (i = 0; i < 100; i++)
        {
            putch('D');
            putch('E');
            putch('F');
            putch('G');
            putch(' ');
        }
        OSSemPost(0);
    } 
}

串口互斥

信号量的实现

OS_CFG.h

#define OS_MAX_SEMS 2 /* 最大信号量数目 */

uint8 OsSemBuf[OS_MAX_SEMS * 2]; // 全局变量

OsSemBuf[]数组两个字节表示一个信号量相关的内容
第一个字节表示:信号量的计数器
第二个字节表示:都有哪些任务正在等待该信号量,
该字节的每一位 代表一个任务,
该位为1 ,表示该任务正在等待该信号量

初始化信号量

//index 表示是 那个信号量,信号量的ID
//Data 表示信号量的初始值
uint8 OSSemCreate(uint8 Index,uint8 Data)
{
    OS_ENTER_CRITICAL(); //关中断
    if (Index < OS_MAX_SEMS )
    {

        OsSemBuf[2 * Index] = Data;             /* 计数器置初值 */
        OsSemBuf[2 * Index + 1] = 0;            /* 清空等待队列 */

        OS_EXIT_CRITICAL();
        return OS_SEM_OK;
    }
    OS_EXIT_CRITICAL();//开中断
    return NOT_OK;
}

阻塞等待信号量到来,会超时退出

OS_SEM_OK:得到信号量
OS_SEM_TMO:超时到
OS_SEM_NOT_OK:没有得到信号量
OSRunningTaskID,OSClearSignal,OSSched,OS_ENTER_CRITICAL,OS_EXIT_CRITICAL

//Index --- 信号量ID
//Tick   ---  超时等待时间
//OSRunningTaskID()  当前正在运行的任务

uint8 OSSemPend(uint8 Index, uint8 Tick)
{
    if (Index >= OS_MAX_SEMS)
    {
        return 0;
    }
        
    OS_ENTER_CRITICAL();// 关闭中断
	
	//当前正在运行的任务,等待的时间
	//有可能在等待的时间超时之前,信号量就有效了
    OSWaitTick[OSRunningTaskID()] = Tick;       /* 设置超时时间         */
    
    //该任务对应的bit位设置为1,表示该任务正在等待这个信号量                                           
    OsSemBuf[Index * 2 + 1] |= OSMapTbl[OSRunningTaskID()];
    
    /* 信号量 计数量 为0 ,表示需要等待 */
    while (OsSemBuf[Index * 2] == 0)
    {
                /* 使用堆栈是为了使函数具有重入性 */
                // 将 Index 放入 堆栈
#ifdef __C51__
        SP++;
        *((uint8 data *)SP) = Index;
#endif
	
            /* 信号量无效 */
        OSClearSignal(OSRunningTaskID());   /* 任务进入等待状态 */
        OSSched();                          /* 运行下一个任务 ,此处是一个断点,下次该任务 继续运行是从这个地方,继续开始的*/

#ifdef __C51__
        Index = *((uint8 data *)SP);  // 任务 又切换回来了,堆栈中,恢复变量Index的值
        SP--;
#endif
			
			// 任务切换回来的原因,1个是 该信号量 计数值为1 了,另外一个是 超时了。
			// 如果是非以上两种原因,切换回来,这个地方是一个while循环,任务会继续切换出去,继续等待			

             /* 任务再次运行,如果超时到,退出循环 */
        if (OSWaitTick[OSRunningTaskID()] == 0)
        {
            break;
        }
        
    }   // end of  while (OsSemBuf[Index * 2] == 0)

    

/* 将任务从信号量的等待队列中清除(可以删除) */

    OsSemBuf[Index * 2 + 1] &= ~OSMapTbl[OSRunningTaskID()];

            /* 判断信号量是否有效。有效,信号量计数器减一 */

    if (OsSemBuf[Index * 2] > 0)
    {
        // 信号量的值 大于 0 ,使用该信号量    
        OsSemBuf[Index * 2]--;


        OS_EXIT_CRITICAL(); // 开启中断
        return OS_SEM_OK;
    }
    else
    {
        /* 无信号返回信号无效 */
        OS_EXIT_CRITICAL();
        return OS_SEM_TMO;
    }
}

ddd


//任务运行标志位设置为 0
//** 功能描述: 清除指定任务信号,既使指定任务休眠
void OSClearSignal(uint8 TaskId)
{
    if (TaskId < OS_MAX_TASKS)
    {
        OS_ENTER_CRITICAL();
#if OS_MAX_TASKS < 9
        OSTaskRuning &= ~OSMapTbl[TaskId];

#endif
        OS_EXIT_CRITICAL();
    }
}

#define  OS_ENTER_CRITICAL()  EA = 0,Os_Enter_Sum++             /* 禁止中断                                     */
#define  OS_EXIT_CRITICAL()   if (--Os_Enter_Sum==0) EA = 1     /* 允许中断                                     */

发送信号量

// Index : 信号量 ID
uint8 OSSemPost(uint8 Index)
{
    if (OSSemIntPost(Index) == OS_SEM_OK)
    {
        OSSched();  // 发送信号量,会将当前任务切换出去,开始运行别的任务
        return OS_SEM_OK;
    }
    else
    {
        return NOT_OK;
    }
}

// Index : 信号量 ID
uint8 OSSemIntPost(uint8 Index)
{
    uint8 temp,i;
    OS_ENTER_CRITICAL(); // 关闭中断
    if (OsSemBuf[Index * 2] <255)
    {
    	/* 信号量计数器加一 */
        OsSemBuf[Index * 2]++;
    }
        /* 察看信号量的等待任务队列 */
    temp = OsSemBuf[Index * 2 + 1];
    for (i = 0; i < OS_MAX_TASKS; i++)
    {
        if ((temp & 0x01) != 0)
        {
        	//优先级最高的任务
            break;
        }
        temp = temp >> 1;
    }
        /* 有任务等待信号,使其中优先级最高的进入就绪状态,并将其从等待队列中清除 */
    if (i < OS_MAX_TASKS)
    {
        OsSemBuf[Index * 2 + 1] &= ~OSMapTbl[i];
        OSIntSendSignal(i);
    }


    OS_EXIT_CRITICAL();
    return OS_SEM_OK;
}

//** 功能描述: 中断中给指定任务发送信号,既使指定任务就绪
    void OSIntSendSignal(uint8 TaskId)
    {
        if (TaskId < OS_MAX_TASKS)
        {
            OS_ENTER_CRITICAL();
    
            OSTaskRuning |= OSMapTbl[TaskId];
            
            OS_EXIT_CRITICAL();
        }
    }

有一个地方需要注意:OSIntSendSignal 是增加一个准备运行的任务。
OSClearSignal 是将那个任务的运行标志清除

(稍后补充)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值