|
/*
*********************************************************************************************************
* The Real-Time Kernel For AVR
* CORE FUNCTIONS
*
*
* File : AVR_RTOS.C
* By :
* Version : V1.0
*********************************************************************************************************
*/
#include "AVR_RTOS.h"
/********************************************************************************************************
函数名称: OSTaskCreate
函数原型: void OSTaskCreate(void (*Task)(void),INT8U *Stack,INT8U prio)
函数功能: 用户函数,建立任务
入口参数: *Task:任务函数地址;*Stack:任务堆栈指针;prio:任务优先级
*********************************************************************************************************/
void OSTaskCreate(void (*Task)(void),INT8U *Stack,INT8U prio)
{
INT8U i;
*Stack--=(INT16U)Task; /*将任务的地址低位压入堆栈 */
*Stack--=(INT16U)Task>>8; /*将任务的地址高位压入堆栈 */
*Stack--=0x00; /*R1 __zero_reg__ */
*Stack--=0x00; /*R0 __tmp_reg__ */
*Stack--=0x80; /*SREG 在任务中,开启全局中断 */
for(i=0;i<14;i++)
*Stack--=i;/*在 avr-libc 中的 FAQ中的 What registers are used by the C compiler?
描述了寄存器的作用 */
TCB[prio].OSTaskStackTop = (INT16U)Stack;/*将人工堆栈的栈顶,保存到堆栈的数组中*/
TCB[prio].OSWaitTick = 0; /* 初始化任务延时 */
OSRdyTbl |= 0x01<<prio; /*任务就绪表已经准备好 */
}
/*********************************************************************************************************
函数名称: OSInit
函数原型: void OInit (void)
函数功能: 用户函数,初始化任务时钟和一些系统全局变量,建立空闲任务
*********************************************************************************************************/
void OSInit (void)
{
#if OS_TIME_GET_SET_EN > 0
OSTime=0; /*系统时间清零 */
#endif
OSRdyTbl=0; /*任务就绪表清零 */
OSIntNesting=0; /*中断嵌套计数器清零 */
OSTaskCreate(IdleTask,&IdleStack[IDLE_STACK_SIZE-1],IDLE_TASK_PRIO); /*建立IDLE任务*/
}
/*********************************************************************************************************
函数名称: OSStart
函数原型: void OSStart(void)
函数功能: 用户函数,开始任务调度,从空闲任务开始运行
*********************************************************************************************************/
void OSStart(void)
{
OSPrioCur = OS_TASKS;
OSPrioHighRdy = OS_TASKS;
SP=TCB[OS_TASKS].OSTaskStackTop+17;
__asm__ __volatile__("reti" "\n\t");
}
/*********************************************************************************************************
函数名称: OS_TASK_SW
函数原型: void OS_TASK_SW(void)
函数功能: 进行任务调度
有关说明: 内部函数,中断和任务都可以调用这个任务调度函数
*********************************************************************************************************/
void OS_TASK_SW(void)
{
__asm__ __volatile__("LDI R16,0x01 \n\t");
/*清除中断要求任务切换的标志位,设置正在任务切换标志位 */
__asm__ __volatile__("SEI \n\t");
/*开中断,因为如果因中断在任务调度中进行,要重新进行调度时,已经关中断 */
/*根据中断时保存寄存器的次序入栈,模拟一次中断后,入栈的情况 */
__asm__ __volatile__("PUSH __zero_reg__ \n\t"); /*R1 */
__asm__ __volatile__("PUSH __tmp_reg__ \n\t"); /*R0 */
__asm__ __volatile__("IN __tmp_reg__,__SREG__ \n\t"); /*保存状态寄存器SREG */
__asm__ __volatile__("PUSH __tmp_reg__ \n\t");
__asm__ __volatile__("CLR __zero_reg__ \n\t"); /*R0重新清零 */
__asm__ __volatile__("PUSH R18 \n\t");
__asm__ __volatile__("PUSH R19 \n\t");
__asm__ __volatile__("PUSH R20 \n\t");
__asm__ __volatile__("PUSH R21 \n\t");
__asm__ __volatile__("PUSH R22 \n\t");
__asm__ __volatile__("PUSH R23 \n\t");
__asm__ __volatile__("PUSH R24 \n\t");
__asm__ __volatile__("PUSH R25 \n\t");
__asm__ __volatile__("PUSH R26 \n\t");
__asm__ __volatile__("PUSH R27 \n\t");
__asm__ __volatile__("PUSH R30 \n\t");
__asm__ __volatile__("PUSH R31 \n\t");
__asm__ __volatile__("Int_OSSched: \n\t"); /*当中断要求调度,直接进入这里 */
__asm__ __volatile__("SEI \n\t");
/*开中断,因为如果因中断在任务调度中进行,已经关中断*/
__asm__ __volatile__("PUSH R28 \n\t"); /*R28与R29用于建立在堆栈上的指针 */
__asm__ __volatile__("PUSH R29 \n\t"); /*入栈完成 */
TCB[OSPrioCur].OSTaskStackTop=SP; /*将正在运行的任务的堆栈底保存 */
OSPrioCur = OSPrioHighRdy ; /*运行当前就绪表中的最高优先级任务 */
cli(); /*保护堆栈转换,属于临界代码,要保护 */
SP=TCB[OSPrioCur].OSTaskStackTop;
sei();
/*根据中断时的出栈次序*/
__asm__ __volatile__("POP R29 \n\t");
__asm__ __volatile__("POP R28 \n\t");
__asm__ __volatile__("POP R31 \n\t");
__asm__ __volatile__("POP R30 \n\t");
__asm__ __volatile__("POP R27 \n\t");
__asm__ __volatile__("POP R26 \n\t");
__asm__ __volatile__("POP R25 \n\t");
__asm__ __volatile__("POP R24 \n\t");
__asm__ __volatile__("POP R23 \n\t");
__asm__ __volatile__("POP R22 \n\t");
__asm__ __volatile__("POP R21 \n\t");
__asm__ __volatile__("POP R20 \n\t");
__asm__ __volatile__("POP R19 \n\t");
__asm__ __volatile__("POP R18 \n\t");
__asm__ __volatile__("POP __tmp_reg__ \n\t"); /*SERG 出栈并恢复 */
__asm__ __volatile__("OUT __SREG__,__tmp_reg__ \n\t");
__asm__ __volatile__("POP __tmp_reg__ \n\t"); /*R0 出栈 */
__asm__ __volatile__("POP __zero_reg__ \n\t"); /*R1 出栈 */
/*中断时出栈完成*/
__asm__ __volatile__("CLI \n\t"); /*关中断 */
__asm__ __volatile__("SBRC R16,1 \n\t");
/* SBRC当寄存器位为0则跳过下一条指令
检查系统正在进行任务调度时,是否有中断发生并进行要求任务调度,
如果中断要求调度则重新进行一次任务调度。
0x02是中断要求调度的标志位 */
__asm__ __volatile__("RJMP OSSched \n\t"); /*重新调度 */
__asm__ __volatile__("LDI R16,0x00 \n\t");
/* 清除中断要求任务切换的标志位,清除正在任务切换标志位,
表示任务切换已经完成。 */
__asm__ __volatile__("RETI \n\t"); /*返回并开中断 */
}
/********************************************************************************************************
函数名称: IntSwitch
函数原型: void IntSwitch(void)
函数功能: 从中断退出并进行调度
有关说明: 当无中断嵌套并且中断中要求进行任务切换时才进行任务切换。
因为从中断到运行下一个任务共调用了两次子函数,所以要弹出四个入栈的PC
*********************************************************************************************************/
void IntSwitch(void)
{
if((OSCoreState == 0x02) && (OSIntNesting == 0))
{
/*进入中断时,已经保存了SREG和R0,R1,R18~R27,R30,R31,
所以没有必要再保存一次了,直接跳转到Int_OSSched进行下一个调度*/
__asm__ __volatile__("POP R31 \n\t"); /*去除因调用子程序而入栈的PC*/
__asm__ __volatile__("POP R31 \n\t");
__asm__ __volatile__("POP R31 \n\t");
__asm__ __volatile__("POP R31 \n\t");
__asm__ __volatile__("LDI R16,0x01 \n\t");
/* 清除中断要求任务切换的标志位,设置正在任务切换标志位 */
__asm__ __volatile__("RJMP Int_OSSched \n\t"); /*重新调度 */
}
}
/********************************************************************************************************
函数名称: OSSched
函数原型: void OSSched(void)
函数功能: 任务调度器,进行任务调度
有关说明: 内部函数,只有有更高优先级的任务就绪时才进行一次任务切换,否则不做切换,中断中不可调用此函数
********************************************************************************************************/
void OSSched (void)
{
OS_ENTER_CRITICAL();
if(OSIntNesting == 0) /*判断是否由中断调用,是则直接推出 */
{
OSFindPrioHighRdy(); /*找出就绪表中优先级最高的任务 */
if(OSPrioHighRdy != OSPrioCur) /*如果不是当前运行的任务,进行任务调度*/
{
OS_TASK_SW(); /*调度任务 */
}
}
OS_EXIT_CRITICAL();
}
/********************************************************************************************************
函数名称: OSTaskSuspend
函数原型: void OSTaskSuspend(INT8U prio)
函数功能: 挂起任务
入口参数: prio:任务优先级
有关说明: 用户函数,一个任务可以挂起本身也可以挂起其他任务
*********************************************************************************************************/
void OSTaskSuspend(INT8U prio)
{
OS_ENTER_CRITICAL();
TCB[prio].OSWaitTick=0;
OSRdyTbl &= ~(0x01<<prio); /*从任务就绪表上去除标志位 */
if(OSPrioCur == prio) /*当要挂起的任务为当前任务 */
{
OS_EXIT_CRITICAL();
OSSched(); /*重新调度 */
return;
}
OS_EXIT_CRITICAL();
}
/********************************************************************************************************
函数名称: OSTaskResume
函数原型: void OSTaskResume(INT8U prio)
函数功能: 用户函数,恢复任务 可以让被OSTaskSuspend或 OSTimeDly暂停的任务恢复
入口参数: prio:任务优先级
*********************************************************************************************************/
void OSTaskResume(INT8U prio)
{
OS_ENTER_CRITICAL();
OSRdyTbl |= 0x01<<prio; /*从任务就绪表上重置标志位 */
TCB[prio].OSWaitTick=0; /*将时间计时设为0,到时 */
if(OSPrioCur > prio) /*当前任务的优先级低于重置位的任务的优先级 */
{
OS_EXIT_CRITICAL();
OSSched(); /*重新调度 */
}
OS_EXIT_CRITICAL();
}
/********************************************************************************************************
函数名称: IdleTask(void)
函数原型: void IdleTask(void)
函数功能: 空闲任务,当其他任务没有就绪的时候运行
有关说明: 系统开始运行的时候是从运行空闲任务开始的。该任务不能删除
这里将timer0放在此处,因为该函数是第一个运行的函数,模仿UCos在第一个任务中启动系统节拍
*********************************************************************************************************/
void IdleTask(void)
{
TCN0Init(); /*初始化定时器 */
OSSched();
while(1)
{
OS_ENTER_CRITICAL();
OSIdleCtr++;
OS_EXIT_CRITICAL();
}
}
/********************************************************************************************************
函数名称: OSTimeDly
函数原型: void OSTimeDly(INT32U ticks)
函数功能: 任务延时
入口参数: 延时的时间,系统节拍的个数
有关说明: 如果延时65535(0xffff)则为无限延时
*********************************************************************************************************/
void OSTimeDly(INT16U ticks)
{
if (OSIntNesting>0)
{ /* 判断是否由中断调用 */
return;
}
if(ticks>0) /*当延时有效 */
{
OS_ENTER_CRITICAL();
OSRdyTbl &= ~(0x01<<OSPrioCur); /*把任务从就绪表中去掉 */
TCB[OSPrioCur].OSWaitTick=ticks;
OS_EXIT_CRITICAL();
OSSched(); /*重新调度 */
}
}
/********************************************************************************************************
函数名称: OSTimeDlyHMSM
函数原型: INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U ms)
函数功能: 任务延时
入口参数: 延时的时间,HMSM
*********************************************************************************************************/
#if OS_TIME_DLY_HMSM_EN > 0
INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U ms)
{
INT32U ticks;
INT16U loops;
if (OSIntNesting>0)
{ /* 判断是否由中断调用 */
return;
}
ticks = ((INT32U)hours * 3600L + (INT32U)minutes * 60L + (INT32U)seconds) * OS_TICKS_PER_SEC
+ OS_TICKS_PER_SEC * ((INT32U)ms + 500L / OS_TICKS_PER_SEC) / 1000L;
loops = (INT16U)(ticks >> 16); /* Compute the integral number of 65536 tick delays */
ticks = ticks & 0xFFFFL; /* Obtain the fractional number of ticks */
OSTimeDly((INT16U)ticks);
while (loops > 0)
{
OSTimeDly((INT16U)32768u);
OSTimeDly((INT16U)32768u);
loops--;
}
return(OS_ERR_NONE);
}
#endif
#if OS_TIME_GET_SET_EN > 0
INT32U OSTimeGet (void)
{
INT32U ticks;
OS_ENTER_CRITICAL();
ticks = OSTime;
OS_EXIT_CRITICAL();
return (ticks);
}
void OSTimeSet (INT32U ticks)
{
OS_ENTER_CRITICAL();
OSTime = ticks;
OS_EXIT_CRITICAL();
}
#endif
/********************************************************************************************************
函数名称: OSSemCreat
函数原型: void OSSemCreat(INT8U Index,INT8U Type)
函数功能: 初始化信号量
入口参数: Index:信号量的序号;Type:信号量类型
出口参数: 无
有关说明: Type为 0 信号量独占型;1 信号量共享型
*********************************************************************************************************/
void OSSemCreat(INT8U Index,INT8U Type)
{
Sem[Index].OSEventType=Type; /*定义信号量类型 */
Sem[Index].OSTaskPendTbl=0; /*初始化时任务等待列表为空 */
Sem[Index].OSEventState=0; /*信号量无效 */
}
/*********************************************************************************************************
函数名称: OSSemPend
函数原型: INT8U OSSemPend(INT8U Index,INT16U Timeout)
函数功能: 任务等待信号量,挂起
入口参数: Index:信号量序号;Timeout:等待时间
出口参数: 无
有关说明: 当Timeout==0xffff时,为无限等待,当Timeout==0时不等待也不调度
**********************************************************************************************************/
INT8U OSSemPend(INT8U Index,INT16U Timeout)
{
OS_ENTER_CRITICAL();
if(Sem[Index].OSEventState) /*信号量有效 */
{
if(Sem[Index].OSEventType == 0) /*如果为独占型 */
{
Sem[Index].OSEventState = 0x00; /*信号量被独占,不可用 */
}
OS_EXIT_CRITICAL();
}
else
{ /*加入信号的任务等待表 */
Sem[Index].OSTaskPendTbl |= 0x01<<OSPrioCur;
if(Timeout!=0)
{
TCB[OSPrioCur].OSWaitTick = Timeout; /*定义等待超时 */
OSRdyTbl &= ~(0x01<<OSPrioCur); /*从任务就绪表中去除 */
OS_EXIT_CRITICAL(); /*重新调度 */
OSSched();
}
else{return 0;} /*需要改进*/
}
return 1;
}
/********************************************************************************************************
函数名称: OSSemPost
函数原型: void OSSemPost(INT8U Index)
函数功能: 发送一个信号量,可以从任务或中断发送,
* 发送完了信号量之后不进行任务调度,推荐在中断中调用
入口参数: Index:信号量的序号
出口参数: 无
有关说明: 如果有任务在等待该信号量则将该任务就绪,没有任务等待则仅仅是把信号量置为有效
*********************************************************************************************************/
void OSSemPost(INT8U Index)
{
OS_ENTER_CRITICAL();
if(Sem[Index].OSEventType) /*当要求的信号量是共享型 */
{
Sem[Index].OSEventState=0x01; /*使信号量有效 */
OSRdyTbl |=Sem [Index].OSTaskPendTbl; /*使在等待该信号的所有任务就绪*/
Sem[Index].OSTaskPendTbl=0; /*清空所有等待该信号的等待任务*/
OS_EXIT_CRITICAL();
OSSched();
}
else /*当要求的信号量为独占型 */
{
INT8U i;
i=0;
while ((i < OS_TASKS) && (!(Sem[Index].OSTaskPendTbl & (0x01<<i))))
{
i++;/* 找出信号量等待列表中任务优先级最高的任务*/
}
if(i < OS_TASKS) /*如果有任务需要 */
{
Sem[Index].OSTaskPendTbl &= ~(0x01<<i); /*从等待表中去除 */
OSRdyTbl |= 0x01<<i; /*任务就绪 */
if(OSPrioCur<i) /*如果等待该信号的任务优先级大于当前任务 */
{
OS_EXIT_CRITICAL();
OSSched(); /*重新调度 */
}
else
{
OS_EXIT_CRITICAL();
}
}
else /*没有任务等待该信号量 */
{
Sem[Index].OSEventState = 1; /*使信号量有效 */ OS_EXIT_CRITICAL();
}
}
}
/*********************************************************************************************************
函数名称: OSSemClean
函数原型: void OSSemClean(INT8U Index)
函数功能: 清除一个信号量,只对共享型的有用。 对于独占型的信号量,在任务占用后,就变得不可以用了
入口参数: Index:信号量的序号
*********************************************************************************************************/
void OSSemClean(INT8U Index)
{
Sem[Index].OSEventState = 0; /*要求的信号量无效*/
}
/*********************************************************************************************************
函数名称: OSFindPrioHighRdy
函数原型: void OSFindPrioHighRdy (void)
函数功能: 找出任务就绪表中的最高优先级的任务
**********************************************************************************************************/
void OSFindPrioHighRdy (void)
{
INT8U OSNextTaskPrio;
OSNextTaskPrio = 0;
OS_ENTER_CRITICAL();
while ((OSNextTaskPrio < OS_TASKS) && (!(OSRdyTbl & (0x01<<OSNextTaskPrio))))
{
OSNextTaskPrio++;/* 找出信号量等待列表中任务优先级最高的任务 */
}
OSPrioHighRdy = OSNextTaskPrio;
OS_ENTER_CRITICAL();
}
/**********************************************************************************************************
函数名称: OSIntEnter
函数原型: void OSIntEnter(void)
函数功能: 进入中断时调用此函数
有关说明: OSIntEnter()和OSIntExit()要成对出现使用
***********************************************************************************************************/
void OSIntEnter(void)
{
OS_ENTER_CRITICAL();
if(OSIntNesting<255)
{
OSIntNesting++; /*中断嵌套加1 */
}
OS_ENTER_CRITICAL();
}
/***********************************************************************************************************
函数名称: OSIntExit
函数原型: void OSIntExit(void)
函数功能: 从中断中退出时调用此函数,如果中断让更高优先级的任务就绪就进行任务调度
有关说明: OSIntEnter()和OSIntExit()要成对出现使用
************************************************************************************************************/
void OSIntExit(void)
{
OS_ENTER_CRITICAL();
if(OSIntNesting>0)
{
OSIntNesting--; /*中断嵌套减1 */
}
if(OSIntNesting == 0)
{
OSFindPrioHighRdy(); /*找出就绪表中优先级最高的任务*/
if(OSPrioHighRdy != OSPrioCur)
{
IntSwitch(); /*进行任务调度 */
return;
}
}
OS_EXIT_CRITICAL();
}
/************************************************************************************************************
函数名称: OSTimeTick (void)
函数原型: OSTimeTick (void)
函数功能: 系统节拍服务函数
有关说明: 用来为需要延时的任务进行任务延时并更新系统时间
*************************************************************************************************************/
void OSTimeTick (void)
{
INT8U i;
OS_ENTER_CRITICAL();
for(i=0;i < OS_TASKS;i++) /*刷新各任务时钟 */
{
if(TCB.OSWaitTick && (TCB.OSWaitTick != 0xffff))
{
TCB.OSWaitTick--;
if(TCB.OSWaitTick == 0) /*当任务时钟到时,必须是由定时器减时的才行*/
{
OSRdyTbl |= (0x01<<i); /*将延时到的程序就需表置位 */
}
}
}
#if OS_TIME_GET_SET_EN > 0
OS_ENTER_CRITICAL(); /* 更新系统时间 */
OSTime++;
OS_EXIT_CRITICAL();
#endif
OS_EXIT_CRITICAL();
}
/************************************************************************************************************
函数名称: OSVersion (void)
函数原型: OSVersion (void)
函数功能: 获取系统版本号
************************************************************************************************************/
INT16U OSVersion (void)
{
return (OS_VERSION);
}
/************************************************************************************************************
函数名称: TCN0Init
函数原型: void TCN0Init(void)
函数功能: 初始化定时器0
有关说明: 定时器T0是任务时钟
*************************************************************************************************************/
void TCN0Init(void)
{
TCCR0 = 0;
TCCR0 = 0x05; /* 1024预分频 */
TIMSK |= (1<<TOIE0); /* T0溢出中断允许 */
TCNT0 = 100; /* 置计数起始值 */
//TCNT0 = (256-F_CPU/1024/OS_TICKS_PER_SEC);
}
SIGNAL(SIG_OVERFLOW0)
{
OSIntEnter();
TCNT0=100;
//TCNT0 = (256-F_CPU/1024/OS_TICKS_PER_SEC);
OSTimeTick();
OSIntExit();
} |
阿莫论坛20周年了!感谢大家的支持与爱护!!
一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。
|