历史上的今天
今天是:2025年02月07日(星期五)
2020年02月07日 | 建立一个属于自己的AVR的RTOS(第八篇:完善的服务)
2020-02-07 来源:eefocus
第八篇:占先式内核(完善的服务)
如果将前面所提到的占先式内核和协作式内核组合在一起,很容易就可以得到一个功能较为完善的占先式内核,它的功能有:
1,挂起和恢复任务
2,任务延时
3,信号量(包括共享型和独占型)
另外,在本例中,在各个任务中加入了从串口发送任务状态的功能。
#include #include #include unsignedcharStack[400]; registerunsignedcharOSRdyTblasm("r2");//任务运行就绪表 registerunsignedcharOSTaskRunningPrioasm("r3");//正在运行的任务 registerunsignedcharIntNumasm("r4");//中断嵌套计数器 //只有当中断嵌套数为0,并且有中断要求时,才能在退出中断时,进行任务调度 registerunsignedcharOSCoreStateasm("r16");//系统核心标志位,R16编译器没有使用 //只有大于R15的寄存器才能直接赋值例LDIR16,0x01 //0x01正在任务切换0x02有中断要求切换 #defineOS_TASKS3//设定运行任务的数量 structTaskCtrBlock { unsignedintOSTaskStackTop;//保存任务的堆栈顶 unsignedintOSWaitTick;//任务延时时钟 }TCB[OS_TASKS+1]; //防止被编译器占用 //registerunsignedchartempR4asm("r4"); registerunsignedchartempR5asm("r5"); registerunsignedchartempR6asm("r6"); registerunsignedchartempR7asm("r7"); registerunsignedchartempR8asm("r8"); registerunsignedchartempR9asm("r9"); registerunsignedchartempR10asm("r10"); registerunsignedchartempR11asm("r11"); registerunsignedchartempR12asm("r12"); registerunsignedchartempR13asm("r13"); registerunsignedchartempR14asm("r14"); registerunsignedchartempR15asm("r15"); //registerunsignedchartempR16asm("r16"); registerunsignedchartempR16asm("r17"); //建立任务 voidOSTaskCreate(void(*Task)(void),unsignedchar*Stack,unsignedcharTaskID) { unsignedchari; *Stack--=(unsignedint)Task>>8;//将任务的地址高位压入堆栈, *Stack--=(unsignedint)Task;//将任务的地址低位压入堆栈, *Stack--=0x00;//R1__zero_reg__ *Stack--=0x00;//R0__tmp_reg__ *Stack--=0x80; //SREG在任务中,开启全局中断 for(i=0;i<14;i++)//在avr-libc中的FAQ中的WhatregistersareusedbytheCcompiler? *Stack--=i;//描述了寄存器的作用 TCB[TaskID].OSTaskStackTop=(unsignedint)Stack;//将人工堆栈的栈顶,保存到堆栈的数组中 OSRdyTbl|=0x01< //开始任务调度,从最低优先级的任务的开始 voidOSStartTask() { OSTaskRunningPrio=OS_TASKS; SP=TCB[OS_TASKS].OSTaskStackTop+17; __asm____volatile__("reti"" t"); } //进行任务调度 voidOSSched(void) { __asm____volatile__("LDIR16,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__("PUSHR18 t"); __asm____volatile__("PUSHR19 t"); __asm____volatile__("PUSHR20 t"); __asm____volatile__("PUSHR21 t"); __asm____volatile__("PUSHR22 t"); __asm____volatile__("PUSHR23 t"); __asm____volatile__("PUSHR24 t"); __asm____volatile__("PUSHR25 t"); __asm____volatile__("PUSHR26 t"); __asm____volatile__("PUSHR27 t"); __asm____volatile__("PUSHR30 t"); __asm____volatile__("PUSHR31 t"); __asm____volatile__("Int_OSSched: t");//当中断要求调度,直接进入这里 __asm____volatile__("SEI t"); //开中断,因为如果因中断在任务调度中进行,已经关中断 __asm____volatile__("PUSHR28 t");//R28与R29用于建立在堆栈上的指针 __asm____volatile__("PUSHR29 t");//入栈完成 TCB[OSTaskRunningPrio].OSTaskStackTop=SP;//将正在运行的任务的堆栈底保存 unsignedcharOSNextTaskPrio;//在现有堆栈上开设新的空间 for(OSNextTaskPrio=0;//进行任务调度 OSNextTaskPrio OSTaskRunningPrio=OSNextTaskPrio; cli();//保护堆栈转换 SP=TCB[OSTaskRunningPrio].OSTaskStackTop; sei(); //根据中断时的出栈次序 __asm____volatile__("POPR29 t"); __asm____volatile__("POPR28 t"); __asm____volatile__("POPR31 t"); __asm____volatile__("POPR30 t"); __asm____volatile__("POPR27 t"); __asm____volatile__("POPR26 t"); __asm____volatile__("POPR25 t"); __asm____volatile__("POPR24 t"); __asm____volatile__("POPR23 t"); __asm____volatile__("POPR22 t"); __asm____volatile__("POPR21 t"); __asm____volatile__("POPR20 t"); __asm____volatile__("POPR19 t"); __asm____volatile__("POPR18 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__("SBRCR16,1 t");//SBRC当寄存器位为0刚跳过下一条指令 //检查是在调度时,是否有中断要求任务调度0x02是中断要求调度的标志位 __asm____volatile__("RJMPOSSched t");//重新调度 __asm____volatile__("LDIR16,0x00 t"); //清除中断要求任务切换的标志位,清除正在任务切换标志位 __asm____volatile__("RETI t");//返回并开中断 } //从中断退出并进行调度 voidIntSwitch(void) { //当中断无嵌套,并且没有在切换任务的过程中,直接进行任务切换 if(OSCoreState==0x02&&IntNum==0) { //进入中断时,已经保存了SREG和R0,R1,R18~R27,R30,R31 __asm____volatile__("POPR31 t");//去除因调用子程序而入栈的PC __asm____volatile__("POPR31 t"); __asm____volatile__("LDIR16,0x01 t"); //清除中断要求任务切换的标志位,设置正在任务切换标志位 __asm____volatile__("RJMPInt_OSSched t");//重新调度 } } ////////////////////////////////////////////任务处理 //挂起任务 voidOSTaskSuspend(unsignedcharprio) { TCB[prio].OSWaitTick=0; OSRdyTbl&=~(0x01< OSSched();//从新调度 } //恢复任务可以让被OSTaskSuspend或OSTimeDly暂停的任务恢复 voidOSTaskResume(unsignedcharprio) { OSRdyTbl|=0x01< if(OSTaskRunningPrio>prio)//当要当前任务的优先级低于重置位的任务的优先级 OSSched();//从新调度//从新调度 } //任务延时 voidOSTimeDly(unsignedintticks) { if(ticks)//当延时有效 { OSRdyTbl&=~(0x01< OSSched();//从新调度 } } //信号量 structSemBlk { unsignedcharOSEventType;//型号0,信号量独占型;1信号量共享型 unsignedcharOSEventState;//状态0,不可用;1,可用 unsignedcharOSTaskPendTbl;//等待信号量的任务列表 }Sem[10]; //初始化信号量 voidOSSemCreat(unsignedcharIndex,unsignedcharType) { Sem[Index].OSEventType=Type;//型号0,信号量独占型;1信号量共享型 Sem[Index].OSTaskPendTbl=0; Sem[Index].OSEventState=0; } //任务等待信号量,挂起 //当Timeout==0xffff时,为无限延时 unsignedcharOSTaskSemPend(unsignedcharIndex,unsignedintTimeout) { //unsignedchari=0; if(Sem[Index].OSEventState)//信号量有效 { if(Sem[Index].OSEventType==0)//如果为独占型 Sem[Index].OSEventState=0x00;//信号量被独占,不可用 } else {//加入信号的任务等待表 Sem[Index].OSTaskPendTbl|=0x01< OSRdyTbl&=~(0x01< if(TCB[OSTaskRunningPrio].OSWaitTick==0)//超时,未能拿到资源 return0; } return1; } //发送一个信号量,可以从任务或中断发送 voidOSSemPost(unsignedcharIndex) { if(Sem[Index].OSEventType)//当要求的信号量是共享型 { Sem[Index].OSEventState=0x01;//使信号量有效 OSRdyTbl|=Sem[Index].OSTaskPendTbl;//使在等待该信号的所有任务就绪 Sem[Index].OSTaskPendTbl=0;//清空所有等待该信号的等待任务 } else//当要求的信号量为独占型 { unsignedchari; for(i=0;i Sem[Index].OSTaskPendTbl&=~(0x01<OSRdyTbl|=0x01<} else { Sem[Index].OSEventState=1;//使信号量有效 } } } //从任务发送一个信号量,并进行调度 voidOSTaskSemPost(unsignedcharIndex) { OSSemPost(Index); OSSched(); } //清除一个信号量,只对共享型的有用。 //对于独占型的信号量,在任务占用后,就交得不可以用了。 voidOSSemClean(unsignedcharIndex) { Sem[Index].OSEventState=0;//要求的信号量无效 } voidTCN0Init(void)//计时器0 { TCCR0=0; TCCR0|=(1< } SIGNAL(SIG_OVERFLOW0) { IntNum++;//中断嵌套+1 sei();//在中断中,重开中断 unsignedchari; for(i=0;i if(TCB[i].OSWaitTick&&TCB[i].OSWaitTick!=0xffff) { TCB[i].OSWaitTick--; if(TCB[i].OSWaitTick==0)//当任务时钟到时,必须是由定时器减时的才行 { OSRdyTbl|=(0x01<OSCoreState|=0x02;//要求任务切换的标志位 } } } TCNT0=100; cli(); IntNum--;//中断嵌套-1 IntSwitch();//进行任务调度 } unsignedchar__attribute__((progmem))proStrA[]="Task"; unsignedcharstrA[20]; SIGNAL(SIG_UART_RECV)//串口接收中断 { strA[0]=UDR; } /////////////////////////////////////串口发送 unsignedchar*pstr_UART_Send; unsignedintnUART_Sending=0; voidUART_Send(unsignedchar*Res,unsignedintLen)//发送字符串数组 { if(Len>0) { pstr_UART_Send=Res;//发送字串的指针 nUART_Sending=Len;//发送字串的长度 UCSRB=0xB8;//发送中断使能 } } //SIGNAL在中断期间,其它中断禁止 SIGNAL(SIG_UART_DATA)//串口发送数据中断 { IntNum++;//中断嵌套+1,不充许中断 if(nUART_Sending)//如果未发完 { UDR=*pstr_UART_Send;//发送字节 pstr_UART_Send++;//发送字串的指针加1 nUART_Sending--;//等待发送的字串数减1 } if(nUART_Sending==0)//当已经发送完 { OSSemPost(0); OSCoreState|=0x02;//要求任务切换的标志位 UCSRB=0x98; } cli();//关发送中断 IntNum--; IntSwitch();//进行任务调度 } voidUARTInit()//初始化串口 { #definefosc8000000//晶振8MHZUBRRL=(fosc/16/(baud+1))%256; #definebaud9600//波特率 OSCCAL=0x97;//串口波特率校正值,从编程器中读出 //UCSRB=(1< //UCSRB=0x08; UBRRL=(fosc/16/(baud+1))%256; UBRRH=(fosc/16/(baud+1))/256; UCSRC=(1< UDR=0; } //打印unsignedint到字符串中00000 voidstrPUT_uInt(unsignedchar*Des,unsignedinti) { unsignedcharj; Des=Des+4; for(j=0;j<5;j++) { *Des=i%10+'0'; i=i/10; Des--; } } voidstrPUT_Star(unsignedchar*Des,unsignedchari) { unsignedcharj; for(j=0;j{ *Des++='*'; } *Des++=13; } unsignedintstrPUT_TaskState(unsignedchar*Des, unsignedcharTaskID, unsignedcharNum) { //unsignedinti=0; *(Des+4)='0'+TaskID; strPUT_uInt(Des+6,Num); strPUT_Star(Des+12,TaskID); return12+TaskID+1; } voidTask0() { unsignedintj=0; while(1) { PORTB=j++; if(OSTaskSemPend(0,0xffff)) { unsignedintm; m=strPUT_TaskState(strA,OSTaskRunningPrio,j); UART_Send(strA,m); } OSTimeDly(200); } } voidTask1() { unsignedintj=0; while(1) { PORTC=j++; if(OSTaskSemPend(0,0xffff)) { unsignedintm; m=strPUT_TaskState(strA,OSTaskRunningPrio,j); UART_Send(strA,m); } OSTimeDly(100); } } voidTask2() { unsignedintj=0; while(1) { if(OSTaskSemPend(0,0xffff)) { unsignedintm; m=strPUT_TaskState(strA,OSTaskRunningPrio,j); UART_Send(strA,m); } PORTD=j++; OSTimeDly(50); } } voidTaskScheduler() { OSSched(); while(1) { } } intmain(void) { strlcpy_P(strA,proStrA,20); UARTInit(); TCN0Init(); OSRdyTbl=0; IntNum=0; OSTaskCreate(Task0,&Stack[99],0); OSTaskCreate(Task1,&Stack[199],1); OSTaskCreate(Task2,&Stack[299],2); OSTaskCreate(TaskScheduler,&Stack[399],OS_TASKS); OSStartTask(); } 结束语 本文中的例子,基本上用WinAVR和Proteus调试仿真成功,一定可能存在某些方面的缺陷,因为工作上时间的压力,就没有进一步查找。
上一篇:AVR应用经验-经典
下一篇:AVR单片机主要的特性
史海拾趣
|
1.802.15简介 2.Microchip ZigBee 协议栈更改版 3.Ti原版开发板说明 4.Zigbee毕业设计提纲 5.ZigBee概述 6.ZigBee技術規格與測試方案之發展 7.单极天线的PCB设计说明(new) 8.基于CC2430的经济实用型Zigbee开发套件 9.基于MSP430 的ZigBee80 ...… 查看全部问答> |
|
电脑的reset按钮都是非常非常小的,不过Windows出生以后,人们对于reset按钮的应用就频繁起来。当然我们也可以应用Ctrl + Alt + Del,但对于Windows 98致命的Kernel 32毛病,我们还是不得不弯下腰寻找那渺小的reset按钮。国外一名玩友看来厌倦了这 ...… 查看全部问答> |
|
初学CE,平台是S3C2440 + wince5.0 在网上找了个周立功的按键中断流驱动程序,就按着这个程序开始写了个串口流驱动,程序在WaitForSingleObject()处停止了,无法进入线程处理。可能是什么原因呀? com0是CE调试串口,我写的驱动是对com1操作。 ...… 查看全部问答> |
|
WinCE5.0 我的CE Device已经有FAT32的分区了 请问如何把CE Device在连接PC后,当作U盘? 看到很多大侠已经实现了: “WinCE5有,支持那个U盘的。那个core os service下面那个usb host support加一个usb storeage class driver,然后加一个devic ...… 查看全部问答> |
|
请各位参加工作的朋友们给小弟指明一条路~~感谢! 偶现在大三下了,今年11月份就开始找工作了。。眼前想抓紧时间学点东西,好找个好工作·但不知道学什么好,望各位朋友给点建议~ 我想做嵌入式开发,软件,硬件,现在还没有定,准备都要好好学 ...… 查看全部问答> |
|
三星44B0X+SL811HST,无操作系统,可以正确识别到1.1的U盘,但因为现在大多是2.0的U盘,发现有的2.0的U盘在做完 GetMaxLUN后,如果做Inquiry,也可以成功,但接下来做ReadFormatCapacity时,BulkIn会产生STALL,也就是读不到 FormatCapacity的内 ...… 查看全部问答> |
|
做一个控制摄像头的单片机... 控制IC的参数 调节参数是白天适当的变暗 晚上适当的变亮 使效果最佳 \'要求I2C接口\' 市面上哪些型号的单片机能满足功能而又实惠的&n ...… 查看全部问答> |
|
【低功耗】满足低功耗FPGA需求,Actel推出5μW产品延长电池寿命 为了满足便携式应用对功耗的严苛要求,Actel公司宣布推出业界最低功耗的现场可编程门阵列(FPGA)--IGLOO系列。这个以Flash为基础的产品系列的静态功耗为5μW,是最接近竞争产品功耗的四分之一;与目前领先的PLD产品比较,更可延长便携式应用的电池寿 ...… 查看全部问答> |




