四极管:rtos 之AVR

 任务的实现为上面几篇文章中的驱动,由此可知,这是一个循序渐进的过程。任务有18B20,8563,1602液晶显示,语音4004等等··

#include "avr/io.h"
#include "avr/interrupt.h"
#include "util/delay.h"


#include "CPU.h"
#include "MyBit.h"

unsigned char REKEY=0;

#include "1602driver.c"



const unsigned char strA[]="task1";
const unsigned char strA_1[]="task1_1";
const unsigned char strB[]="task2";
const unsigned char strB_1[]="task2_2";
const unsigned char strC[]="task3";
const unsigned char strC_1[]="task3_1";
/**********************************************
//任务0 读温度  显示温度
**********************************************/
void Task0(void) 
{  
    Displaypstr(0,1,"mytask");
	_delay_ms(1000);
    while(1) 
    {
        Displaypstr(0,1,"mytask1");
		_delay_ms(1000);
        //display_DS18B20();
	    OSTimeDly(50);
    }
} 
/**********************************************
//任务1 语音报时
**********************************************/
void Task1(void) 
{ 
    OSTaskSuspend(1);//挂起自己
	_delay_ms(1000);
    while(1)
    {
        Displaypstr(0,1,"mytask2");
		_delay_ms(1000);
	    OSTaskSuspend(1);
    }
} 
/*************************************************
//任务2 读时间 显示时间
*************************************************/
void Task2(void) 
{ 
   Displaypstr(0,1,"mytask3");
   _delay_ms(1000);
    while(1) 
    {       
       Displaypstr(0,1,"mytask3");
      _delay_ms(1000);
	    OSTimeDly(50);
    } 
}

/******************************************
//任务4 空任务
******************************************/
void TaskScheduler(void)
{
    OSSched();
    while(1)
    {
    }
}
/*****************************************
//主函数
*****************************************/
int main(void)
{
    Init_1602();     //1602初始化
	Displaypstr(1,1,"mytask1");
   // Init_pcf8563();//8563初始化
    TCN0Init();//定时器0初始化
	//int_interr1();//
	
    OSRdyTbl=0;
    IntNum=0;
    OSTaskCreate(Task0,&Stack[79],0);
    OSTaskCreate(Task1,&Stack[159],1);
    OSTaskCreate(Task2,&Stack[239],2);
   // OSTaskCreate(Task3,&Stack[319],3);
    OSTaskCreate(TaskScheduler,&Stack[399],OS_TASKS);
    OSStartTask();
}


 四极管:CPU.h

unsigned char Stack[400]; 

register unsigned char OSRdyTbl          asm("r2");    //任务运行就绪表 
register unsigned char OSTaskRunningPrio asm("r3");    //正在运行的任务 
register unsigned char IntNum            asm("r4");     //中断嵌套计数器 
//只有当中断嵌套数为0,并且有中断要求时,才能在退出中断时,进行任务调度 
register unsigned char OSCoreState       asm("r16"); // 系统核心标志位 ,R16 编译器没有使用 
//只有大于R15的寄存器才能直接赋值 例LDI R16,0x01
//0x01 正在任务 切换  0x02 有中断要求切换

#define OS_TASKS 3                   //设定运行任务的数量
struct TaskCtrBlock
{
  unsigned int OSTaskStackTop;  //保存任务的堆栈顶 
  unsigned int OSWaitTick;      //任务延时时钟 
} TCB[OS_TASKS+1]; 

//防止被编译器占用 
//register unsigned char tempR4  asm("r4"); 
register unsigned char tempR5  asm("r5"); 
register unsigned char tempR6  asm("r6"); 
register unsigned char tempR7  asm("r7"); 
register unsigned char tempR8  asm("r8"); 
register unsigned char tempR9  asm("r9"); 
register unsigned char tempR10 asm("r10"); 
register unsigned char tempR11 asm("r11"); 
register unsigned char tempR12 asm("r12"); 
register unsigned char tempR13 asm("r13"); 
register unsigned char tempR14 asm("r14"); 
register unsigned char tempR15 asm("r15"); 
//register unsigned char tempR16 asm("r16"); 
register unsigned char tempR16 asm("r17"); 

/*******************************************************
** 函数名: OSTaskCreate()
** 子函数: 无
** 输  入: *task(void)函数指针 *stack 堆栈顶 任务ID
** 输  出: 无
** 功  能: 初始化任务堆栈
--------------------------------------------------------
*******************************************************/
void OSTaskCreate(void (*Task)(void),unsigned char *Stack,unsigned char TaskID) 
{ 
  unsigned char i;                      
  *Stack--=(unsigned int)Task;       //将任务的地址低位压入堆栈, 
  *Stack--=(unsigned int)Task>>8;    //将任务的地址高位压入堆栈, 
  
  *Stack--=0x00;                     //R1 __zero_reg__             
  *Stack--=0x00;                     //R0 __tmp_reg__ 
  *Stack--=0x80;                                         

//SREG 在任务中,开启全局中断         
  for(i=0;i<14;i++)    //在 avr-libc 中的 FAQ中的 What registers are used by the C compiler? 
    *Stack--=i;                    //描述了寄存器的作用     
  TCB[TaskID].OSTaskStackTop=(unsigned int)Stack;    //将人工堆栈的栈顶,保存到堆栈的数组中 
  OSRdyTbl|=0x01<<TaskID;      //任务就绪表已经准备好 
} 

/*******************************************************
** 函数名: OSStartTask()
** 子函数: 无
** 输  入: 无
** 输  出: 无
** 功  能: 开始任务调度,从最低优先级的任务的开始 
--------------------------------------------------------
*******************************************************/
//
void OSStartTask(void)    //第一个要执行的任务,这个任务又去反复不断调用OSSched函数   
{ 
  OSTaskRunningPrio=OS_TASKS; 
  SP=TCB[OS_TASKS].OSTaskStackTop+17;
  __asm__ __volatile__(    "reti"       " \
\t"  );  
} 

/*******************************************************
** 函数名: OSSched()
** 子函数: 无
** 输  入: 无
** 输  出: 无
** 功  能: 进行任务调度,SP是指向堆栈的指针,在该任务要改变
--------------------------------------------------------
*******************************************************/
void OSSched(void) //刚开始执行时保存的是第一个任务OSTaskCreate()里面的寄存器,压的是芯片硬件内部寄内部堆栈区
{  

  __asm__ __volatile__("LDI  R16,0x01              \
\t");   
  //清除中断要求任务切换的标志位,设置正在任务切换标志位 
  __asm__ __volatile__("SEI                        \
\t");       
   //开中断,因为如果因中断在任务调度中进行,要重新进行调度时,已经关中断 
   //根据中断时保存寄存器的次序入栈,模拟一次中断后,入栈的情况
  __asm__ __volatile__("PUSH __zero_reg__          \
\t");  //R1 
  __asm__ __volatile__("PUSH __tmp_reg__           \
\t");  //R0  
  __asm__ __volatile__("IN   __tmp_reg__,__SREG__  \
\t");  //保存状态寄存器SREG 
  __asm__ __volatile__("PUSH __tmp_reg__           \
\t"); 
  __asm__ __volatile__("CLR  __zero_reg__          \
\t");  //R0重新清零 
  __asm__ __volatile__("PUSH R18                   \
\t"); 
  __asm__ __volatile__("PUSH R19                   \
\t"); 
  __asm__ __volatile__("PUSH R20                   \
\t"); 
  __asm__ __volatile__("PUSH R21                   \
\t"); 
  __asm__ __volatile__("PUSH R22                   \
\t"); 
  __asm__ __volatile__("PUSH R23                   \
\t"); 
  __asm__ __volatile__("PUSH R24                   \
\t"); 
  __asm__ __volatile__("PUSH R25                   \
\t"); 
  __asm__ __volatile__("PUSH R26                   \
\t"); 
  __asm__ __volatile__("PUSH R27                   \
\t"); 
  __asm__ __volatile__("PUSH R30                   \
\t");     
  __asm__ __volatile__("PUSH R31                   \
\t"); 
     
  __asm__ __volatile__("Int_OSSched:               \
\t");  //当中断要求调度,直接进入这里 
  __asm__ __volatile__("SEI                        \
\t");  
    //开中断,因为如果因中断在任务调度中进行,已经关中断
  __asm__ __volatile__("PUSH R28                   \
\t");  //R28与R29用于建立在堆栈上的指针
  __asm__ __volatile__("PUSH R29                   \
\t");  //入栈完成 
     
  TCB[OSTaskRunningPrio].OSTaskStackTop=SP;           //将正在运行的任务的堆栈底保存 

  unsigned char OSNextTaskPrio;                            //在现有堆栈上开设新的空间  
  for (OSNextTaskPrio = 0;                                 //进行任务调度 
    OSNextTaskPrio < OS_TASKS && !(OSRdyTbl & (0x01<<OSNextTaskPrio));  
    OSNextTaskPrio++); 
    OSTaskRunningPrio = OSNextTaskPrio ; 

  cli();  //保护堆栈转换 
  SP=TCB[OSTaskRunningPrio].OSTaskStackTop; //将将要运行的任务的堆栈底赋给SP
  sei(); 
  //根据中断时的出栈次序 
  __asm__ __volatile__("POP  R29                   \
\t");     
  __asm__ __volatile__("POP  R28                   \
\t");         
  __asm__ __volatile__("POP  R31                   \
\t");     
  __asm__ __volatile__("POP  R30                   \
\t");     
  __asm__ __volatile__("POP  R27                   \
\t");     
  __asm__ __volatile__("POP  R26                   \
\t");     
  __asm__ __volatile__("POP  R25                   \
\t");     
  __asm__ __volatile__("POP  R24                   \
\t");     
  __asm__ __volatile__("POP  R23                   \
\t");     
  __asm__ __volatile__("POP  R22                   \
\t");     
  __asm__ __volatile__("POP  R21                   \
\t");     
  __asm__ __volatile__("POP  R20                   \
\t");     
  __asm__ __volatile__("POP  R19                   \
\t"); 
  __asm__ __volatile__("POP  R18                   \
\t"); 
  __asm__ __volatile__("POP  __tmp_reg__           \
\t");      //SERG 出栈并恢复 
  __asm__ __volatile__("OUT  __SREG__,__tmp_reg__  \
\t");      // 
  __asm__ __volatile__("POP  __tmp_reg__           \
\t");      //R0 出栈 
  __asm__ __volatile__("POP  __zero_reg__          \
\t");      //R1 出栈 
  //中断时出栈完成 
  __asm__ __volatile__("CLI                        \
\t");  //关中断     
  __asm__ __volatile__("SBRC R16,1                 \
\t");  //SBRC当寄存器位为0刚跳过下一条指令 
  //检查是在调度时,是否有中断要求任务调度 0x02是中断要求调度的标志位 
  __asm__ __volatile__("RJMP OSSched               \
\t");  //重新调度 
  __asm__ __volatile__("LDI  R16,0x00              \
\t");   
  //清除中断要求任务切换的标志位,清除正在任务切换标志位 
  __asm__ __volatile__("RETI                       \
\t");     //返回并开中断 
} 

/*******************************************************
** 函数名: IntSwitch() 
** 子函数: OSSched()
** 输  入: 无
** 输  出: 无
** 功  能: 从中断退出并进行调度
--------------------------------------------------------
*******************************************************/
void IntSwitch(void) 
{     
  //当中断无嵌套,并且没有在切换任务的过程中,直接进行任务切换 
  if(OSCoreState == 0x02 && IntNum==0)  
  { 
    //进入中断时,已经保存了SREG和R0,R1,R18~R27,R30,R31 
    __asm__ __volatile__("POP  R31                   \
\t");  //去除因调用子程序IntSwitch()而入栈的PC
    __asm__ __volatile__("POP  R31                   \
\t"); 
    __asm__ __volatile__("LDI  R16,0x01              \
\t");   //清除中断要求任务切换的标志位,设置正在任务切换标志位 
    __asm__ __volatile__("RJMP Int_OSSched           \
\t");  //重新调度 
  } 
} 

////////////////////////////////////////////任务处理 
/*******************************************************
** 函数名: OSTaskSuspend() 
** 子函数: OSSched();
** 输  入: prio 要挂起的任务ID
** 输  出: 无
** 功  能: 挂起任务,即从任务就绪表上去除标志位 
--------------------------------------------------------
*******************************************************/
void OSTaskSuspend(unsigned char prio)  
{ 
    TCB[prio].OSWaitTick=0;
    OSRdyTbl &= ~(0x01<<prio);   //从任务就绪表上去除标志位 
    if(OSTaskRunningPrio==prio)  //当要挂起的任务为当前任务 
    OSSched();                   //从新调度 
} 

/*******************************************************
** 函数名: OSTaskResume()
** 子函数: OSSched();
** 输  入: prio 要恢复的任务ID
** 输  出: 无
** 功  能: 恢复任务 可以让被OSTaskSuspend或 OSTimeDly暂停的任务恢复 
--------------------------------------------------------
*******************************************************/
//
void OSTaskResume(unsigned char prio) 
{ 
    OSRdyTbl |= 0x01<<prio;      //从任务就绪表上重置标志位 
    TCB[prio].OSWaitTick=0;      //将时间计时设为0,到时 
    if(OSTaskRunningPrio>prio)   //当要当前任务的优先级低于重置位的任务的优先级 
    OSSched();                   //从新调度
} 

/*******************************************************
** 函数名: OSTimeDly() 
** 子函数: OSSched();
** 输  入: ticks 延迟的时钟节拍数
** 输  出: 无
** 功  能: 任务延时 
--------------------------------------------------------
*******************************************************/
void OSTimeDly(unsigned int ticks) 
{ 
    if(ticks)     //当延时有效,即ticks!=0
    { 
        OSRdyTbl &= ~(0x01<<OSTaskRunningPrio); //清就绪标志
        TCB[OSTaskRunningPrio].OSWaitTick=ticks;//赋时钟节拍数
        OSSched(); //从新调度 
    } 
} 
/***********************
**      信号量        ** 
***********************/
struct SemBlk 
{ 
  unsigned char OSEventType;     //型号 0,信号量独占型;1信号量共享型  
  unsigned char OSEventState;    //状态 0,不可用;1,可用 
  unsigned char OSTaskPendTbl;   //等待信号量的任务列表
} Sem[10]; 

/*******************************************************
** 函数名: OSSemCreat() 
** 子函数: 无
** 输  入: Index任务ID Type信号量类型
** 输  出: 无
** 功  能: 初始化信号量
--------------------------------------------------------
*******************************************************/
void OSSemCreat(unsigned char Index,unsigned char Type) 
{ 
  Sem[Index].OSEventType=Type;  //型号 0,信号量独占型;1信号量共享型  
  Sem[Index].OSTaskPendTbl=0;
  Sem[Index].OSEventState=0;
} 

/***********************************************************************
** 函数名: OSTaskSemPend() 
** 子函数: 无
** 输  入: Index任务ID Timeout等待时间
** 输  出: 无
** 功  能: 任务等待信号量,挂起 当Timeout==0xffff时,为无限延时 
------------------------------------------------------------------------
************************************************************************/
unsigned char OSTaskSemPend(unsigned char Index,unsigned int Timeout) 
{ 

  //unsigned char i=0; 
  if(Sem[Index].OSEventState)                      //信号量有效 
  {  
    if(Sem[Index].OSEventType==0)                  //如果为独占型 
    Sem[Index].OSEventState = 0x00;                //信号量被独占,不可用 
  } 
  else 
  {                                                //加入信号的任务等待表 
    Sem[Index].OSTaskPendTbl |= 0x01<<OSTaskRunningPrio;
    TCB[OSTaskRunningPrio].OSWaitTick=Timeout;    //如延时为0,刚无限等待 
    OSRdyTbl &= ~(0x01<<OSTaskRunningPrio);       //从任务就绪表中去除     
    OSSched();                                     //从新调度
    if(TCB[OSTaskRunningPrio].OSWaitTick==0 )     //超时,未能拿到资源
    return 0;         
  } 
  return 1; 
} 


/***********************************************************************
** 函数名: OSSemPost() 
** 子函数: 无
** 输  入: Index任务ID
** 输  出: 无
** 功  能: 发送一个信号量,可以从任务或中断发送 
------------------------------------------------------------------------
************************************************************************/
void OSSemPost(unsigned char Index)
{ 
if(Sem[Index].OSEventType)                  //当要求的信号量是共享型 
  { 
    Sem[Index].OSEventState=0x01;           //使信号量有效 
    OSRdyTbl |=Sem [Index].OSTaskPendTbl;   //使在等待该信号的所有任务就绪 
    Sem[Index].OSTaskPendTbl=0;             //清空所有等待该信号的等待任务 
  }   
  else                                       //当要求的信号量为独占型 
  {       
    unsigned char i; 
    for (i = 0; i < OS_TASKS && !(Sem[Index].OSTaskPendTbl & (0x01<<i));  i++); 
    if(i < OS_TASKS)                          //如果有任务需要 
    { 
      Sem[Index].OSTaskPendTbl &= ~(0x01<<i);//从等待表中去除 
      OSRdyTbl |= 0x01<<i;                   //任务就绪 
    } 
    else 
    { 
      Sem[Index].OSEventState =1;            //使信号量有效
    } 
  } 
} 

/***********************************************************************
** 函数名: OSTaskSemPost() 
** 子函数: 无
** 输  入: Index任务ID
** 输  出: 无
** 功  能: 从任务发送一个信号量,并进行调度
------------------------------------------------------------------------
************************************************************************/
void OSTaskSemPost(unsigned char Index)  
{ 
  OSSemPost(Index); 
  OSSched();
} 

//
//
/***********************************************************************
** 函数名: OSSemClean() 
** 子函数: 无
** 输  入: Index任务ID
** 输  出: 无
** 功  能: 清除一个信号量,只对共享型的有用。 
**         对于独占型的信号量,在任务占用后,就交得不可以用了。  
------------------------------------------------------------------------
************************************************************************/
void OSSemClean(unsigned char Index)
{ 
  Sem[Index].OSEventState =0;          //要求的信号量无效 
} 


/***********************************************
** 函数名: TCN0Init
** 功  能: 初始化定时器0作为时钟节拍 5ms
** 输  入: 无
** 输  出: 无
------------------------------------------------
***********************************************/
void TCN0Init(void)    // 计时器0 
{ 
  TCCR0 = 0; 
  TCCR0 |= (1<<CS02);  // 256预分频 
  TIMSK |= (1<<TOIE0); // T0溢出中断允许                   
  TCNT0 = 100;         // 置计数起始值 
} 

/***********************************************
** 函数名: SIGNAL
** 功  能: 时钟节拍中断 5ms
** 输  入: 无
** 输  出: 无
------------------------------------------------
***********************************************/
SIGNAL(SIG_OVERFLOW0) 
{ 
  IntNum++;     //中断嵌套+1 
  sei();  //在中断中,重开中断 
     
  unsigned char i; 
  for(i=0;i<OS_TASKS;i++)        //任务时钟 
  { 
    if(TCB[i].OSWaitTick && TCB[i].OSWaitTick!=0xffff)  
    { 
      TCB[i].OSWaitTick--; 
      if(TCB[i].OSWaitTick==0)         //当任务时钟到时,必须是由定时器减时的才行 
      {   
        OSRdyTbl |= (0x01<<i);     //使任务可以重新运行 
        OSCoreState|=0x02;         //要求任务切换的标志位 
      } 
    } 
  } 
  TCNT0=100; 
  cli(); 
  IntNum--;               //中断嵌套-1 
  IntSwitch();         //进行任务调度 
} 


 

 

转载请注明出处。作者:四极管。广西师范大学 电子工程学院大学生科技创新基地 邮箱: [email protected]

 

你可能感兴趣的:(OS,任务调度,任务,编译器,Signal,delay)