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

本文探讨嵌入式实时操作系统smallRTOS51中的任务间通信机制,重点讲解信号量的使用,包括信号量的创建、等待、发送,以及如何解决任务间的资源竞争问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

嵌入式实时操作系统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 是将那个任务的运行标志清除

(稍后补充)

编辑推荐 本书主要特点: 分为原理应用篇,原理与实践相结合,两篇相对独立,又紧密联系。 原理篇以分析源码为特色,深入浅出地介绍RTOS的基本概念以及Small RTOS51的工作原理应用篇以DP-51单片综合仿真实验仪为硬件平台,通过对单片机常用热门外围器件实用驱动程序的分析,详细介绍基于Small RTOS51的编程方法,以及如何为这些外围器件编写中间件,并给出一个完整的例子,让读者全面掌握基于Small RTOS51的编程方法,体会了RTOS下编程的优点。 内容简介 Small RTOS51是为51系列单片机而编写的。它是完全免费的、源代码公开的多任务实时操作系统。它可在无任何外部数据存储器的单片80C51系统上运行,并且是可移植的。全书分两部分。第一部分为基础篇,介绍Small RTOS51一些基本概念,并详细分析Small RTOS51的工作原理。第二部分为应用篇,给出部分常用硬件在Small RTOS51下驱动程序的源代码。这些源代码在DP—51单片机仿真实验仪上全部调试通过,且只要经过很少的改动,或是不改动,就可以在其他环境下使用。应用篇还通过对这些源代码的分析,让读者理解基于RTOS的编程方法,并给出完整的例子让读者全面掌握基于RTOS的编程方法。 本书可以作为高等院校相关专业的课程教材、实验参考资料或课外读物,对嵌入工应用开发人员也有重要的参考价值。 目录 第一部分 原理篇 第0章 绪论 第1章 Small RTOS51简介 1.1 Small RTOS51的特点 1.2 Small RTOS51的运行条件 1.3 Small RTOS51的存储器需求 1.4 Small RTOS51任务堆栈的计算 1.5 关于可重入功能 1.6 关于C51的库函数 1.7 关于51系列单片机派生类型的多数据指针数学单元 1.8 关于51系列单片机的寄存器段 1.9 关于局部变量 第2章 基本概念 2.1 嵌入式系统 2.2 实时系统 2.3 前、后台系统 2.4 操作系统 2.5 实时操作系统 2.6 代码的临界区 2.7 资源 2.8 共享资源 2.9 任务 2.10 任务切换 2.11 内核 2.12 调度 2.13 非占先式内核 2.14 占先式内核 2.15 可重入性 2.16 任务优先级 2.17 信号量 2.18 死锁 2.19 消息队列 2.20 中断 2.21 时钟节拍 第3章 一个简单的例子 3.1 Small RTOS51的安装及目录结构 3.2 例子简介 3.3 Config、h、Os_cfg、hOs_cpu.h 3.4 与编程器无关的数据类型 3.5 OS_ENTER_CRITICAL()OS_EXIT_CRITICAL() 3.6 main()函数 3.7 建立任务 3.8 删除任务 3.9 用户任务 3.10 软非屏蔽中断 3.11 中断服务程序的编写 3.12 Os_cpu.h可改变的其他内容 3.13 Small RTOS51的其他注意事项 第4章 任务管理—核心的核心 4.1 临界区 4.2 任务 4.3 任务状态 4.4 与任务相关的数据结构 4.5 任务调度 4.6 Small RTOS51中的中断处理 4.7 时钟节拍 4.8 Small RTOS51初始化启动 4.9 建立任务 4.10 任务堆栈 4.11 删除任务 4.12 时间服务及任务的挂起恢复 4.13 获取当前Small RTOS51的版本号 4.14 OSInt…()函数 第5章 如何切换任务 5.1 CPU可以执行多个任务的原因 5.2 CPU怎样运行才能执行多个任务 5.3 何时进行任务切换 5.4 Small RTOS51任务切换时的程序框图 5.5 数组OSTsakStackBotton[]Small RTOS51的堆栈结构 5.6 变量OSFastSwap 5.7 常量数值OSMapTb[] 5.8 软非屏蔽中断的堆栈SP2[] 5.9 中断切换函数OSIntCtxSw()OSIntCtxSw() 5.10 任务主动放弃CPU-OS_TASK_SW() 5.11 堆栈变换函数C_OSCtxSw() 5.12 恢复新任务环境LoadCtx() 5.13 优先级最低的任务OSIdle() 5.14 Small RTOS51初始化函数OSStart() 5.15 系统时钟节拍中断OSTickISR() 第6章 任务之间通信同步信号量 6.1 概述 6.2 使Keil C51函数具有重入性的特殊方法 6.3 数据结构 6.4 IN_OS_SEM_CHK宏及相关代码 6.5 初始化一个信号量 6.6 等待一个信号量 6.7 发送一个信号量 6.8 无等待地请求一
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值