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