|
这是OS操作系统主程序,
/**********************************************************************************************
本程序只供学习使用,不得用于其它任何用途,否则后果自负。
ARM_OS_main.c file
LPC213x上的操作系统——ARM_OS
作者:heciang
联系电话:13811129591
QQ:176780142
Email:heciang@126.com
建立日期:2006-5-1
修改日期:2006-5-15
最后修改时间:2006-08-25
版本:V1.0
Copyright(C) Computer-lov 2006-2016
All rights reserved
**********************************************************************************************/
#include "CPU.H"
#include "My_type.h"
#include "LED.H"
#include "interrupt.h"
#include "ARM_00_OS_TaskSwitch.H"
#include "ARM_00_OS_Core.H"
#include "UART.H"
#include "KEYS.H"
/*********************************************************************************************/
void SysClkInit(void)
{
PLLCON=0x01; //Enable PLL
PLLCFG=0x24; //set M=4,P=2 so the core clock is about 5*11.0592M
PLLFEED=0xAA;
PLLFEED=0x55; //send PLL feed sequence
while((PLLSTAT&(1<<10))==0); //Wait for PLL Locked
PLLCON=3; //Enable and connect PLL
PLLFEED=0xAA;
PLLFEED=0x55; //send PLL feed sequence
VPBDIV=0x01; //VPB clock set. 0x01 --- same as the core clock
MAMCR=0x00;
MAMTIM=0x03; //set MAMTIM to 3cclk for core clock >40MHz
MAMCR=0x02; //use MAM
}
///////////////////////////////////////////////////////////////////////////////////////////////
/**********************************************************************************************
main函数
**********************************************************************************************/
void main(void)
{
DisEnableInterrupt(OS_I_Bit | OS_F_Bit); //关中断
SysClkInit(); //系统时钟初始化
UART_init(); //串口初始化
DelayXms(100); //延迟100ms
cls(); //清屏
prints("SystemStart",1); //显示开机信息
OSInit(); //操作系统初始化
DelayXms(2000); //延时2S。软件仿真时最好去掉该句,否则有得你等^_^
OSEnterCritical(); //进入临界段
EnableInterrupt(OS_I_Bit | OS_F_Bit); //开中断
OSTickInit(); //系统时钟节拍初始化
OSStart((uint32)OSSystemIdle,OS_THUMB_MODE); //系统开始运行
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
本程序只供学习使用,不得用于其它任何用途,否则后果自负。
ARM_OS_main.c file
LPC213x上的操作系统——ARM_OS
作者:heciang
联系电话:13811129591
QQ:176780142
Email:heciang@126.com
建立日期:2006-5-1
修改日期:2006-5-15
最后修改时间:2006-08-25
版本:V1.0
Copyright(C) Computer-lov 2006-2016
All rights reserved
**********************************************************************************************/
#include "CPU.H"
#include "My_type.h"
#include "LED.H"
#include "UART.H"
#include "KEYS.H"
#include "interrupt.h"
#include "ARM_00_OS_Core.H"
#include "ARM_00_OS_TaskSwitch.H"
/**********************************************************************************************
功能:禁止中断。
入口参数1:DisEnabledBit。即CPSR中对应的I位和Q位。
头文件中有定义,#define OS_I_Bit 0x80 #define OS_F_Bit 0x40
关IRQ中断时,使用OS_I_Bit,关FIQ中断时,使用OS_F_Bit。
两个都关时,将两者按位或,即 OS_I_Bit | OS_F_Bit
返回:无。
使用资源:使用软中断号0。
备注:使用了内嵌的ARM指令,该函数所在的文件必须设置为ARM模式。
**********************************************************************************************/
void DisEnableInterrupt(uint32 DisEnableBit) __swi(0)
{
__asm LDMIA SP!,{R8} //堆栈中保存的是SPSR寄存器(参看SWI_VEC.s文件),将其弹出至R8中。
__asm ORR R8,R8,R0,LSL #0 //R8中的值,跟传递进来的DisEnableBit(被放在R0中)相或。{}
__asm STMDB SP!,{R8} //将设置好的SPSR寄存器,压回堆栈
DisEnableBit=0; //防止编译器警告。请不要删除该语句。如果删除该语句,编译器可能会删除某些语句
//从而导致程序运行错误
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:使能中断。
入口参数1:EnabledBit。即CPSR中对应的I位和Q位。
头文件中有定义,#define OS_I_Bit 0x80 #define OS_F_Bit 0x40
关IRQ中断时,使用OS_I_Bit,关FIQ中断时,使用OS_F_Bit。
两个都关时,将两者按位或,即 OS_I_Bit | OS_F_Bit
返回:无。
使用资源:使用软中断号1。
备注:使用了内嵌的ARM指令,该函数所在的文件必须设置为ARM模式。
**********************************************************************************************/
void EnableInterrupt(uint32 EnableBit) __swi(1)
{
__asm LDMIA SP!,{R8} //堆栈中保存的是SPSR寄存器(参看SWI_VEC.s文件),将其弹出至R8中
__asm MVN R0,R0 //EnableBit(被放在R0中)取反{}
__asm AND R8,R8,R0 //R8中的值,跟R0相与{}
__asm STMDB SP!,{R8} //将设置好的SPSR寄存器,压回堆栈
EnableBit=0; //防止编译器警告。请不要删除该语句
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:启动操作系统。
入口参数1:AddrOfSystemIdle。必须设置为系统空闲任务的入口地址。系统启动后,从系统空闲任务开始运行。
入口参数2:Mode。系统空闲任务代码的模式。可以选择ARM_MODE或者THUMB_MODE。
返回:无。
使用资源:使用软中断号2。
备注:使用了内嵌的ARM指令,该函数所在的文件必须设置为ARM模式。系统启动后,进入系统空闲任务。
**********************************************************************************************/
void OSStart(uint32 AddrOfSystemIdle,uint32 Mode) __swi(2)
{
__asm ADD SP,SP,#20 //{}调整SP,使其指向返回地址的前一个字
__asm STMDB SP,{R0} //将入口地址压入堆栈中的返回地址处
__asm SUB SP,SP,#20 //{}将堆栈指针调回
__asm LDMIA SP!,{R8} //将SPSR弹出,放入R8中
Mode|=~(0x20); //将传递进来的Mode其它位设置为1,只保留T位
__asm ORR R8,R8,#0x20 //将SPSR中的T位设置为1{}
__asm AND R8,R8,R1 //将SPSR的值与Mode相与。从而T位跟Mode的T位相同{}
__asm STMDB SP!,{R8} //将SPSR压回栈中
OSCurrentPcb=&OSSystemIdlePcb; //当前任务为系统空闲任务
AddrOfSystemIdle=0; //防止编译器警告。请不要删除该语句
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:保存堆栈指针。堆栈指针被保存在当前任务的TaskSP成员中。
入口参数1:sp。当前任务堆栈指针的地址值。
返回:无。
备注:sp由R0寄存器传入。
**********************************************************************************************/
void OSSaveSP(uint32 sp)
{
OSCurrentPcb->TaskSP=sp; //保存当前堆栈指针
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:恢复堆栈指针。将当前任务的堆栈指针恢复。
入口参数1:无。
返回:堆栈指针的地址值。被保存在R0中。
备注:无。
**********************************************************************************************/
uint32 OSResumeSP(void)
{
return OSCurrentPcb->TaskSP; //将堆栈指针的地址值返回
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:设置堆栈。任务创建时,要设置好其堆栈,使其看起来,就像任务刚被切换的任务一样。
入口参数1:StackAddr。32位的堆栈入口地址值,堆栈是往下生长的,所以入口地址应该是堆栈区的最高地址。
入口参数2:TaskEntryAddr。32位的任务入口地址值。
入口参数3:Mode。被创建任务代码的模式。可选择为OS_ARM_MODE或者OS_THUMB_MODE。
返回:32位的堆栈地址。被压入堆栈后,堆栈指针会更新。
使用资源:使用软中断号4。
备注:无。
**********************************************************************************************/
uint32 OSSetStack(uint32 StackAddr,uint32 TaskEntryAddr,uint32 Mode) __swi(4)
{
#define PushedBytes (16*4) //压入了16个字,共64字节
//R0中保存的是堆栈入口地址
__asm MOV R12,R0 //{}StackAddr传进时,被放在了R0中。将R0转存至R12中。
__asm STMDB R12!,{R3} //入口地址被TaskEntryAddr被编译器转移至R3中。将R3压栈
__asm MOV R8,#0 //{}R8清零
__asm STMDB R12!,{R8} //该位置保存的是R3。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R2。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R1。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R0。将其清0。
__asm LDMIA SP,{R8} //将刚压入的SPSR(见SWI_VEC.S文件)出栈至R8中。
Mode|=~(0x20); //将Mode的其它位置1,只保留T位。
__asm ORR R8,R8,#0x20 //将SPSR中的T位置1。
__asm AND R8,R8,R2 //将SPSR的值与Mode相与。从而T位跟Mode的T位相同{}
__asm STMDB R12!,{R8} //将SPSR压栈。任务被调度时,SPSR将被返回至CPSR。
__asm MOV R8,#0 //R8清0{}
__asm STMDB R12!,{R8} //该位置保存的是用户模式下的R14。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R12。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R11。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R10。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是9。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R8。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R7。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R6。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R5。将其清0。
__asm STMDB R12!,{R8} //该位置保存的是R4。将其清0。
TaskEntryAddr=0; //防止编译器警告。请不要删除该语句。
return StackAddr-PushedBytes; //返回堆栈指针
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:切换任务。任务被切换到优先级最高的就绪态任务。
入口参数:无。
返回:无。
使用资源:使用软中断号3。
备注:该函数为任务切换函数,修改这里的代码是要注意,可能会导致跑飞。
**********************************************************************************************/
void OSTaskSwitch(void) __swi(3)
{
//将用户模式下的堆栈地址装入到R8中
__asm MOV R8,SP //{}特权模式下的堆栈指针暂时放到R8{}
__asm STMDB R8!,{R13}^ //将用户模式下的堆栈指针R13放入到特权模式下的堆栈中{}
__asm NOP //插入一个NOP指令,在访问用户模式下的寄存器后,后面不能紧跟访问备份寄存器的指令,所以插入一个NOP指令{}
__asm MOV SP,R8 //刚刚是借用R8来访问堆栈的,R8被更新后,应该将其写回SP中{}
__asm LDMIA SP!,{R8} //将刚压入的用户堆栈地址弹出至R8
__asm ADD SP,SP,#20 //{}调整SP的值,使SP指向堆栈内的返回地址。堆栈的结构:返回地址,R12,R8,R3,SPSR,R14。当前的SP指向R14
//调整后,SP指向返回地址
__asm LDMDA SP!,{R12} //{}将返回地址弹出至R12
__asm STMDB R8!,{R12} //将返回地址压入用户栈
__asm STMDB R8!,{R0-R3} //将R0-R3压入用户栈
__asm LDMDA SP!,{R3} //弹出R12放入到R3中
__asm LDMDA SP!,{R2} //弹出R8放入到R2中
__asm SUB SP,SP,#4 //跳过被压入的R3,这里未用到{}
__asm LDMDA SP!,{R1} //弹出SPSR放入到R1中
__asm MOV R0,R8 //R8是堆栈地址,转存到R0{}
__asm STMDB R0!,{R1} //{}R1在SPSR中,将其压入用户堆栈中
__asm STMDB R0!,{R14}^ //将用户模式下的R14压入用户堆栈中
__asm MOV R8,R2 //R2中保存的是R8,将其放入R8中{}
__asm MOV R12,R3 //R3中保存的是R12,将其放入R12{}
__asm STMDB R0!,{R4-R12} //将R4-R12压入用户堆栈中
__asm BL OSSaveSP //保存堆栈指针{}
if(TimeOfTaskStart>T0TC) //T0TC是计数器0的值。T0VAL的值是增大的。如果T0TC小于上一次的值,则说明已经溢出
{
OSCurrentPcb->RunTimeInThisRefreshPeriod+=MaxOfTimer0-TimeOfTaskStart+T0TC; //计算时间差
}
else //否则
{
OSCurrentPcb->RunTimeInThisRefreshPeriod+=T0TC-TimeOfTaskStart; //计算时间差
}
TimeOfTaskStart=T0TC; //保存当前T0TC的值
OSCurrentPcb=OSReadyList; //当前进程控制块设置为就绪态任务列表中的第一个。它是优先级最高的就绪任务。
OSResumeSP(); //恢复出堆栈指针{}
__asm LDMIA R0!,{R4-R12} //将R4-R12从用户堆栈中恢复
__asm MOV R2,R8 //将R8保存到R2中{}
__asm MOV R3,R12 //将R12保存到R3中{}
__asm LDMIA R0!,{R14}^ //恢复用户模式下的R14
__asm LDMIA R0!,{R1} //将用户堆栈中的SPSR弹出到R1中
__asm ADD SP,SP,#24 //调整特权模式下的SP,使SP指向堆栈中返回地址的前一个位置{}
__asm ADD R0,R0,#16 //调整R0,使R0指向用户栈中的返回地址{}
__asm LDMIA R0!,{R12} //将用户栈中的返回地址弹出至R12{}
__asm STMDB SP!,{R2,R3,R12} //R2中保存的是R8,R3中保存的是R12,R12中保存的是返回地址
//将它们压入到特权模式下的堆栈中
__asm SUB R8,R0,#20 //调整用户模式下的堆栈并将其保存至R8{}
__asm SUB SP,SP,#4 //{}调整SP,使其指向堆栈中的SPSR前一个位置
__asm STMDB SP!,{R1} //{}R1中保存的是SPSR,将其压入堆栈中
__asm ADD SP,SP,#8 //{}调整回SP
__asm LDMIA R8!,{R0-R3} //将R0-R3弹出
__asm STMDB SP!,{R3} //R3的值压入特权模式下的堆栈中
__asm SUB SP,SP,#8 //调整回SP,使其指向正确的位置
__asm ADD R8,R8,#4 //调整R8{}
__asm STMDB SP,{R8} //将R8入栈,R8中保存的是堆栈指针,要将其恢复到用户模式下的SP中。但并未更新SP的值。
__asm SUB R8,SP,#4 //{}将特权模式下的SP-4的值放入到R3中,因为不能直接使用SP,要借用R8作为堆栈指针。
//-4是因为刚刚未更新SP的值。
__asm LDMIA R8,{SP}^ //将刚压入的用户模式下的堆栈指针弹出至用户模式下的SP中。
__asm NOP //{}加入一个空操作,在特权模式下访问用户模式下的SP后,后面不能紧跟访问备份寄存器的操作
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:FIQ中断服务程序。
入口参数:无。
返回:无。
备注:时钟节拍在此产生,修改这里的代码是要注意,可能会导致程序跑飞。
**********************************************************************************************/
void FIQ_Handler(void) __fiq //FIQ中断服务程序
{
//函数进来时,编译器会自动将R0-R3,R14压入堆栈
if((VICRawIntr & (1<<TIMER0))&&(T0IR & 1)) //读取中断状态,判断中断源。这里为定时器0中断。
{
OSpcb * TempDelayPcb; //临时用的进程控制块,查找延迟列表时使用。
OSpcb * TempReadyPcb; //临时用的进程控制块,查找就绪列表时使用。
uint32 DeleteFirstFlag; //是否删除表头的标志
T0IR=1; //清除MR0中断
__asm MOV R0,SP //将FIQ模式下的SP移入R0{}
__asm STMDB R0!,{SP}^ //将用户模式下的堆栈指针SP压入FIQ的堆栈
__asm NOP //插入一个NOP指令。在访问用户模式下的寄存器后,后面不能紧跟访问备份寄存器的指令,所以插入一个NOP指令{}
__asm MOV SP,R0 //将R0放回SP中{}
__asm LDMIA SP!,{R0} //将用户模式下的SP弹出至R0
__asm ADD SP,SP,#16 //{}调整堆栈指针SP,使其指向堆栈中的返回地址
__asm LDMDA SP!,{R8} //{}将返回地址弹出到R8中
__asm SUB R8,R8,#4 //{}因为FIQ发生时,PC已经更新,所以要减去4,使其指向中断发生时的下一条指令。
__asm STMDB R0!,{R8} //将返回地址压入用户堆栈中
__asm LDMDA SP!,{R8} //将R3弹出至R8中
__asm STMDB R0!,{R8} //将被弹出的R3压入到用户堆栈中
__asm LDMDA SP!,{R8} //将R2弹出至R8中
__asm STMDB R0!,{R8} //将被弹出的R2压入到用户堆栈中
__asm LDMDA SP!,{R8} //将R1弹出至R8中
__asm STMDB R0!,{R8} //将被弹出的R1压入到用户堆栈中
__asm LDMDA SP,{R8} //将R0弹出至R8中
__asm STMDB R0!,{R8} //将被弹出的R0压入到用户堆栈中
__asm ADD SP,SP,#20 //调整堆栈指针,使其指向进入中断前的位置{}
__asm MRS R8,SPSR //将SPSR放入R8中。SPSR用来恢复CPSR{}
__asm STMDB R0!,{R8} //将SPSR压入到用户堆栈中
__asm STMDB R0!,{R4-R12,R14}^ //将用户模式下的R4-R12,R14压入到用户堆栈中
__asm BL OSSaveSP //保存堆栈指针
if(TimeOfTaskStart>=T0TC) //T0TC是计数器0的值。T0VAL的值是增大的。如果T0TC小于或等于上一次的值,则说明已经溢出
{
OSCurrentPcb->RunTimeInThisRefreshPeriod+=MaxOfTimer0-TimeOfTaskStart+T0TC; //计算时间差
}
else //否则
{
OSCurrentPcb->RunTimeInThisRefreshPeriod+=T0TC-TimeOfTaskStart; //计算时间差
}
TimeOfTaskStart=T0TC; //保存当前T0TC的值
TempDelayPcb=OSDelayList; //从延时列表的表头开始查找
while(TempDelayPcb->Next) //直到查到表尾
{
TempDelayPcb->Delay--; //延迟列表中的任务延迟数,减1
DeleteFirstFlag=0; //删除表头的标志清0
if(TempDelayPcb->Delay==0) //如果延时的任务时间到,则
{
TempDelayPcb->Status=OSInReadyStatus;
TempReadyPcb=OSReadyList; //将就绪表表头放入TempReadyPcb中,从表头开始查找
while(1)
{
if((TempDelayPcb->Priority)<=(TempReadyPcb->Priority)) //如果被唤醒的任务的优先级比被查找的就绪表中的任务的优先级高
{
if(TempDelayPcb==OSDelayList) //如果被唤醒的任务处于延时列表的表头
{
OSDelayList=TempDelayPcb->Next; //延时列表的表头指向被唤醒的任务的下一个
OSDelayList->Prior=OSDelayList; //延时列表的前趋指向自己
DeleteFirstFlag=1; //设置表头被删除的标志
}
else //如果被唤醒的任务不处于延时列表的表头
{
TempDelayPcb->Next->Prior=TempDelayPcb->Prior; //被唤醒任务的下一个任务的前一个任务指向被唤醒任务的前一个任务
TempDelayPcb->Prior->Next=TempDelayPcb->Next; //被唤醒任务的前一个任务的下一个任务指向被唤醒任务的下一个任务
}
if(TempReadyPcb==OSReadyList) //如果被唤醒的任务要插入到就绪任务列表的表头
{
OSReadyList=TempDelayPcb; //就绪列表的表头设置为被唤醒的任务
OSReadyList->Prior=OSReadyList; //就绪列表的表头的前趋指向自己
}
else //如果被唤醒的任务不是被插入到表头
{
//则将被唤醒的任务,插入到就绪表中
//被唤醒的任务被插在TempReadyPcb之前
TempReadyPcb->Prior->Next=TempDelayPcb; //TempReadyPcb的前趋的后趋设置为被唤醒的任务
TempDelayPcb->Prior=TempReadyPcb->Prior; //被唤醒的任务的前趋设置为TempReadyPcb的前趋
}
TempReadyPcb->Prior=TempDelayPcb; //TempReadyPcb的前趋设置为被唤醒的任务
TempDelayPcb=TempDelayPcb->Next->Prior; //将TempDelayPcb被设置为被唤醒任务的前一个任务
TempReadyPcb->Prior->Next=TempReadyPcb; //因为TempReadyPcb->Prior等于TempDelayPcb,所以相当于
//TempDelayPcb=TempReadyPcb。即被唤醒任务的后趋被置为TempReadyPcb。
break;
}
if(TempReadyPcb->Next==(OSpcb *)0)break; //如果已经查找到就绪表的尾部,则退出
TempReadyPcb=TempReadyPcb->Next; //TempReadyPcb移到下一个
}
}
if(DeleteFirstFlag) //如果刚刚被唤醒的任务处于延时列表的表头,则
{
TempDelayPcb=OSDelayList; //TempDelayPcb指向延时列表的表头
}
else
{
TempDelayPcb=TempDelayPcb->Next; //TempDelayPcb设置为下一个
}
}
if((OSCurrentPcb->Status)==OSInReadyStatus) //如果当前任务处于就绪态
{
if((OSCurrentPcb->Next)!=0) //如果当前任务不是空闲任务
{
if((OSCurrentPcb->Priority)==(OSCurrentPcb->Next->Priority)) //如果当前任务的优先级跟它的下一个就绪态任务的优先级一样
{
OSCurrentPcb=OSCurrentPcb->Next; //则切换到下一个任务,实现时间片轮循调度
}
else //如果不相等
{
OSCurrentPcb=OSReadyList; //则将就绪表的表头的任务做为运行任务
}
}
else //如果当前任务是空闲任务
{
OSCurrentPcb=OSReadyList; //则将就绪表的表头的任务做为运行任务
}
}
else //如果当前任务被挂起或者延时了,
{
OSCurrentPcb=OSReadyList; //则将就绪表的表头的任务做为运行任务
}
OSResumeSP(); //恢复出任务堆栈指针
__asm LDMIA R0!,{R4-R12,R14}^ //恢复出R4-R12,R14
__asm LDMIA R0!,{R8} //将SPSR出栈至R8
__asm MSR SPSR_fsxc,R8 //将R8写入SPSR。在函数返回时,以恢复CPSR{}
__asm ADD R0,R0,#16 //调整堆栈指针,使其指向堆栈中的返回地址{}
__asm LDMDA R0!,{R8} //返回地址弹出至R8
__asm ADD R8,R8,#4 //由于函数返回时,编译器会自动将返回地址减4后才返回,所以事先加上4{}
__asm STMDB SP!,{R8} //将返回地址压入到FIQ的堆栈中
__asm LDMDA R0!,{R8} //将R3弹出至R8
__asm STMDB SP!,{R8} //将刚弹出的R3,压入FIQ的堆栈中
__asm LDMDA R0!,{R8} //将R2弹出至R8
__asm STMDB SP!,{R8} //将刚弹出的R2,压入FIQ的堆栈中
__asm LDMDA R0!,{R8} //将R1弹出至R8
__asm STMDB SP!,{R8} //将刚弹出的R1,压入FIQ的堆栈中
__asm LDMDA R0!,{R8} //将R0弹出至R8
__asm STMDB SP!,{R8} //将刚弹出的R0,压入FIQ的堆栈中
__asm ADD R0,R0,#24 //{}将R0调整到出栈后的值。R0作为堆栈地址用。
__asm STMDB SP,{R0} //将堆栈地址暂时压入FIQ的堆栈中。不更新SP,所以下面需要-4操作。
__asm SUB R0,SP,#4 //将SP-4放入R0中,借用R0来压栈,因为访问用户模式下的寄存器时,不能访问备份寄存器{}
__asm LDMIA R0,{SP}^ //将堆栈指针弹出至用户模式下的堆栈指针中
__asm NOP //{}加入一个空操作,在特权模式下访问用户模式下的SP后,后面不能紧跟访问备份寄存器的操作
return;
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
本程序只供学习使用,不得用于其它任何用途,否则后果自负。
ARM_OS_main.c file
LPC213x上的操作系统——ARM_OS
作者:heciang
联系电话:13811129591
QQ:176780142
Email:heciang@126.com
建立日期:2006-5-1
修改日期:2006-5-15
最后修改时间:2006-08-25
版本:V1.0
Copyright(C) Computer-lov 2006-2016
All rights reserved
**********************************************************************************************/
#include "CPU.H"
#include "my_type.h"
#include "ARM_00_OS_TaskSwitch.H"
#include "ARM_00_OS_Core.H"
#include "UART.H"
#include "KEYS.H"
#include "Task.h"
/**********************************************************************************************
功能:系统空闲任务。
入口参数:无。
返回:无。
备注:操作系统必须从系统空闲任务开始运行。并且系统空闲任务永远不能挂起或者延时。
**********************************************************************************************/
void OSSystemIdle(void)
{
prints("",1); //显示任务被启动
prints(OSCurrentPcb->Title,0);
prints(" Start..........................",1);
OSAddDevice(OS_PRINTER_DEVICE_ID);
OSTaskCreat((uint32)OSTaskManager,256,1,OS_THUMB_MODE,"TaskManager"); //创建任务管理器任务
OSTaskCreat((uint32)Task3,256,8,OS_THUMB_MODE,"Task3"); //创建任务3
OSTaskCreat((uint32)Task4,256,8,OS_THUMB_MODE,"Task4"); //创建任务4
OSTaskCreat((uint32)Task5,256,3,OS_THUMB_MODE,"Task5");
KEYBoardInit(); //初始化键盘
OSExitCritical(); //退出临界段
while(1)
{
PCON=1; //处理器进入空闲模式,节省能源。绿色环保。
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
本程序只供学习使用,不得用于其它任何用途,否则后果自负。
ARM_OS_main.c file
LPC213x上的操作系统——ARM_OS
作者:heciang
联系电话:13811129591
QQ:176780142
Email:heciang@126.com
建立日期:2006-5-1
修改日期:2006-5-15
最后修改时间:2006-08-25
版本:V1.0
Copyright(C) Computer-lov 2006-2016
All rights reserved
**********************************************************************************************/
#include "CPU.H"
#include "my_type.h"
#include "ARM_00_OS_TaskSwitch.H"
#include "ARM_00_OS_Core.H"
#include "UART.H"
#include "KEYS.H"
#include "Task.h"
#include "ADC.H"
/**********************************************************************************************
功能:显示任务的PID号。
入口参数1:pcb。要显示的任务的进程控制块地址。
返回:无。
**********************************************************************************************/
void DisplayPID(OSpcb * pcb)
{
print_uint32((pcb->PID)-0x40000000); //显示任务ID号(PID)。
//由于LPC213X系列的RAM起始地址在0x40000000,减掉显示时会比较短
//当然,不减也无所谓。
send_a_byte(' ');
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:显示任务的CPU使用率。
入口参数1:pcb。要显示的任务的进程控制块地址。
返回:无。
**********************************************************************************************/
void DisplayCPU(OSpcb * pcb)
{
uint32 CpuUsedRatio;
CpuUsedRatio=((pcb->RunTimeInThisRefreshPeriod)*100+TotalTime/2)/TotalTime; //计算CPU使用率。加TotalTime/2是为了四舍无入
pcb->TotalRunTime+=pcb->RunTimeInThisRefreshPeriod; //累加任务的CPU使用总时间
pcb->RunTimeInThisRefreshPeriod=0; //清本次统计的时间
if(CpuUsedRatio>=100) //如果统计的结果大于100
{
CpuUsedRatio=99; //结果为99
}
send_a_byte((CpuUsedRatio/10)+'0'); //显示结果
send_a_byte((CpuUsedRatio%10)+'0');
send_a_byte('%');
send_a_byte(' ');
send_a_byte(' ');
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:显示任务使用CPU的总时间。
入口参数1:pcb。要显示的任务的进程控制块地址。
返回:无。
**********************************************************************************************/
void DisplayCPUTime(OSpcb * pcb)
{
uint32 Time; //用来保存时间
uint8 H,M,S; //用来保存时,分,秒
Time=(uint32)((pcb->TotalRunTime)/((MaxOfTimer0+1)*100)); //计算总时间。结果为多少秒。
S=Time%60; //计算秒
Time/=60;
M=Time%(60); //计算分
Time/=60;
H=Time%(24); //计算时
send_a_byte((H/10)+'0'); //显示时
send_a_byte((H%10)+'0');
send_a_byte(':');
send_a_byte((M/10)+'0'); //显示分
send_a_byte((M%10)+'0');
send_a_byte(':');
send_a_byte((S/10)+'0'); //显示秒
send_a_byte((S%10)+'0');
send_a_byte(' ');
send_a_byte(' ');
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:显示内存使用量
入口参数1:pcb。要显示的任务的进程控制块地址。
返回:无。
**********************************************************************************************/
void DispalyMemroyUsed(OSpcb * pcb)
{
print_uint32(pcb->StackLength); //显示内存使用量
send_a_byte('B'); //显示单位
send_a_byte(' ');
send_a_byte(' ');
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
功能:显示任务所处的状态。
入口参数1:pcb。要显示的任务的进程控制块地址。
返回:无。
**********************************************************************************************/
void DispalyStatus(OSpcb * pcb)
{
switch(pcb->Status) //根据状态来显示
{
case OSInReadyStatus: prints("Ready ",0);break; //就绪态
case OSInDelayStatus: prints("Delay ",0);break; //延时态
case OSInSuspendStatus: prints("Suspend",0);break; //挂起态
default:break;
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
#define REF_VOL 2.475 /*LPC213X 开发板上参考电压为1K电阻跟3K电阻分压3.3V所得,为2.475V*/
//////////////////////////////显示内核电压////////////////////////////////////
void DisplayCoreVoltage(void)
{
uint16 value;
uint32 result;
uint8 s_result[10];
prints("ADC3 voltage: ",0);
value=read_ADC(3); //通道ADC3的电压
result=REF_VOL*10000; //换算成电压,结果为多少mV的10倍
result=result*value/1023;
s_result[9]=0; //将结果转换成字符串
s_result[8]=' ';
s_result[7]='V';
s_result[6]='m';
s_result[5]='0'+result%10;
result/=10;
s_result[4]='.';
s_result[3]='0'+result%10;
result/=10;
s_result[2]='0'+result%10;
result/=10;
s_result[1]='0'+result%10;
result/=10;
s_result[0]='0'+result%10;
prints(" ",0);
prints(s_result,1); //显示电压
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////显示内核温度////////////////////////////////
void DisplayCoreTemperature(void)
{
prints("Core temperature: ",0);
prints("Sorry! LPC213x MCU have no Temperature Sensor!",1);
prints("",1);
}
//////////////////////////////////////////////////////////////////////////////
/**********************************************************************************************
功能:任务管理器。
入口参数:无。
返回:无。
备注:优先级被设置为1,处于高的优先级。
**********************************************************************************************/
void OSTaskManager(void)
{
OSpcb * pcb; //
uint32 TempTime; //TempTime用来保存 在显示任务管理器信息所花费的时间
uint32 DelayCount=RefreshPeriod; //延时多少个时钟节拍刷新一次任务管理器
uint32 * p; //
uint32 i; //
uint32 CpuUsedRatio; //保存CPU使用率
uint32 nBytes; //保存申请内存的字节数
uint32 PrinterDeviceAddr; //打印机设备的入口地址
PrinterDeviceAddr=OSGetDeviceAddr(OS_PRINTER_DEVICE_ID); //获取打印机设备的入口地址
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("",1); //显示任务启动
prints(OSCurrentPcb->Title,0);
prints(" Start..........................",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机
ADC_init(); //ADC初始化。用来检测内核电压以及温度
while(1)
{
OSTaskDelay(DelayCount); //延时DelayCount个时钟节拍
pcb=OSCurrentPcb; //当前运行的任务。即任务管理器本身
TempTime=pcb->RunTimeInThisRefreshPeriod; //保存这时的时间
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("",1); //换行
prints("",1);
prints("TaskName PID CPU CPUTime MemoryUsed Status",1); //显示表头
prints("------------ ----------- --- --------- ---------- ------",1);
nBytes=OSSizePerBlock*(((TaskAmount+1)*sizeof(p)+OSSizePerBlock-1)/OSSizePerBlock); //计算需要申请多少字节内存
p=(uint32 *)OSMalloc(nBytes); //申请一块内存,用来保存所有任务的pcb指针。供下面显示用
OSCurrentPcb->StackLength+=nBytes; //申请内存后,调节任务管理器的内存使用长度
i=0;
prints(pcb->Title,0); //显示任务名字
DisplayPID(pcb); //显示任务的的PID
TempTime=pcb->RunTimeInThisRefreshPeriod-TempTime; //计算出前面显示花费的时间
DisplayCPU(pcb); //显示任务管理器所使用的CPU使用率
DisplayCPUTime(pcb); //显示任务管理器所使用的CPU总时间
DispalyMemroyUsed(pcb); //显示任务管理器所使用的内存
prints("Running",0); //显示任务管理器的状态为运行状态
prints("",1); //换行
OSEnterCritical(); //进入临界段
pcb=OSReadyList->Next; //pcb保存就绪表的下一个。就绪表的第一个为当前运行的任务,即任务管理器
while((pcb->Next)!=(OSpcb *)0) //遍历整个就绪表
{
p[i++]=(uint32)pcb; //保存就绪表的pcb
pcb=pcb->Next; //移向就绪表的下一个
}
p[i++]=(uint32)pcb; //最后一个为空闲任务的pcb。
//计算CPU使用率并保存至CpuUsedRatio。CPU使用率是100%减掉空闲任务的使用率
CpuUsedRatio=100-((pcb->RunTimeInThisRefreshPeriod)*100+TotalTime/2)/TotalTime;
pcb=OSDelayList; //延时态表
while((pcb->Next)!=(OSpcb *)0) //遍历整个延时态表
{
p[i++]=(uint32)pcb; //保存延时态表的pcb
pcb=pcb->Next; // 移向下一个
}
pcb=OSSuspendList; //挂起态表
while((pcb->Next)!=(OSpcb *)0) //遍历整个挂起态表
{
p[i++]=(uint32)pcb; //保存挂起态pcb
pcb=pcb->Next; //移向下一个
}
p=0; //结束
OSExitCritical(); //退出临界段
i=0; //准备显示任务信息
while(p!=0) //直到结束为止
{
prints(((OSpcb *)(p))->Title,0); //显示任务名
DisplayPID((OSpcb *)(p)); //显示任务ID
DisplayCPU((OSpcb *)(p)); //显示任务CPU使用率
DisplayCPUTime((OSpcb *)(p)); //显示任务使用CPU的总时间
DispalyMemroyUsed((OSpcb *)(p)); //显示任务使用的内存量
DispalyStatus((OSpcb *)(p)); //显示任务当前所处的状态
prints("",1); //换行
i++; //移到下一个
}
prints("",1); //显示CPU使用率
prints("CPU used ratio: ",0);
if(CpuUsedRatio>=100) //如果CPU使用率为100%,
{
prints("100%",1); //则显示100%
}
else
{
send_a_byte(CpuUsedRatio/10+'0'); //显示十位
send_a_byte(CpuUsedRatio%10+'0'); //显示个位
prints("%",1);
}
prints("Total memory: ",0); //显示总内存量
print_uint32(OSGetMemoryPoolSize());
prints("B Memory used: ",0); //显示内存使用量
i=OSMemoryStatistic(); //获取内存使用量
print_uint32(i);
prints("B Memory used ratio: ",0); //显示内存使用率
i=i*100/OSGetMemoryPoolSize(); //计算内存使用率
send_a_byte(i/10+'0');
send_a_byte(i%10+'0');
prints("%",1);
DisplayCoreVoltage(); //显示内核电压
DisplayCoreTemperature(); //显示内核温度
prints("",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机使用权
OSFree((uint32)p,nBytes); //释放p的内存
OSCurrentPcb->StackLength-=nBytes; //调整任务管理器的内存使用数量
TempTime+=OSCurrentPcb->RunTimeInThisRefreshPeriod; //计算上面显示所花费的时间
DelayCount=RefreshPeriod-(TempTime)/(MaxOfTimer0+1); //计算下一次统计资源,应该需要多少个时钟节拍 //因为显示信息时要花费时间,应该将其减去
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************
UART.c file
LPC213X的串口函数
本程序只供学习使用,不得用于其它任何用途,否则后果自负。
ARM_OS_main.c file
LPC213x上的操作系统——ARM_OS
作者:heciang
联系电话:13811129591
QQ:176780142
Email:heciang@126.com
建立日期:2006-5-1
修改日期:2006-5-15
最后修改时间:2006-08-25
版本:V1.0
Copyright(C) Computer-lov 2006-2016
All rights reserved
**********************************************************************************************/
**********************************************************************/
#include "CPU.H"
#include "My_type.h"
#include "UART.H"
//////////////////////////////////////////////////////////////////////
void UART_init(void)
{
PINSEL0 = 0x00000005; //设置串口0的相应引脚
U0IER=0; //禁止串口0所有中断
U0LCR = 0x83; //8个数据位,无奇偶校验,1个停止位,DLAB=1
#define Fpclk (5*11059200)/*Pclk为5*11059200Hz*/
#define BitRate 57600 /*波特率设置为57600bps*/
U0DLM=(Fpclk/16/BitRate)/256;
U0DLL=(Fpclk/16/BitRate)%256;
U0LCR = 0x03; // DLAB = 0
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////往串口0发送一字节数据//////////////////////////////
void send_a_byte(uint8 a_byte)
{
while (!(U0LSR & 0x20)); //等待发送完毕
U0THR=a_byte;
}
//////////////////////////////////////////////////////////////////////
/////////////////////////从串口0读取一字节数据//////////////////////////////////
/*int get_a_byte(void) //
{
while (!(U0LSR & 0x01));
return (U0RBR);
}*/
//////////////////////////////////////////////////////////////////////
////////////////////////////// 发送一个字符串 ///////////////////////////////////////////
//////////////////入口参数: s-要发送的字符串的指针(字符串长度不得大于255)/////////////
///////////////////////////////////// newline-是否需要换行 0-不换行 非0-换行////////
void prints(uint8 * s,uint32 newline)
{
unsigned char i;
i=0;
while(s!=0) //判断字符串是否结束
{
while (!(U0LSR & 0x20)); //等待发送完毕
U0THR=s;
i++; //移到下字节
}
if(newline) //如果需要换行
{
send_a_byte(13);
send_a_byte(10); //发送回车换行
}
}
////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
void cls(void)
{
send_a_byte(0x0C); //超级终端清屏指令
send_a_byte(0x0C);
}
//////////////////////////////////////////////////////////////////////////////////
//转换成十六进制时用的表
const uint8 HEX_TABLE[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
//////////////////////////将一个16位的整数按十六进制发到串口/////////////////////////////////
void print_uint16(uint16 number)
{
send_a_byte('0'); //发送0x
send_a_byte('x');
send_a_byte(HEX_TABLE[(number>>12) & 0x0F]); //依次发送各部分
send_a_byte(HEX_TABLE[(number>>8) & 0x0F]);
send_a_byte(HEX_TABLE[(number>>4) & 0x0F]);
send_a_byte(HEX_TABLE[(number) & 0x0F]);
send_a_byte(' ');
}
//////////////////////////////////////////////////////////////////////////////////
/////////////////////////将一个32位的整数按十进制发送到串口/////////////////////////////
void print_uint32(uint32 number)
{
uint8 buf[12];
uint32 i;
for(i=0;i<10;i++) //转换成字符串
{
buf[9-i]=(number%10)+'0';
number/=10;
}
for(i=0;i<9;i++) //将前面的0转换成空格
{
if(buf=='0')
{
buf=' ';
}
else
{
break;
}
}
buf[10]=' ';
buf[11]=0; //字符串结束符
prints(buf,0);
}
//////////////////////////////////////////////////////////////////////////////////////////
/**********************************************************************************************
本程序只供学习使用,不得用于其它任何用途,否则后果自负。
Task.c file
本程序只供学习使用,不得用于其它任何用途,否则后果自负。
ARM_OS_main.c file
LPC213x上的操作系统——ARM_OS
作者:heciang
联系电话:13811129591
QQ:176780142
Email:heciang@126.com
建立日期:2006-5-1
修改日期:2006-5-15
最后修改时间:2006-08-25
版本:V1.0
Copyright(C) Computer-lov 2006-2016
All rights reserved
**********************************************************************************************/
**********************************************************************************************/
#include "CPU.H"
#include "interrupt.h"
#include "LED.H"
#include "ARM_00_OS_TaskSwitch.H"
#include "my_type.h"
#include "ARM_00_OS_Core.H"
#include "UART.H"
#include "KEYS.H"
#include "Task.h"
/**********************************************************************************************
任务1。处于挂起状态,等待一条消息。系统空闲任务会扫描按键,当按键按下后,系统空闲任务会将
键值通过消息发送给任务1。任务1接到消息后,显示消息的发送者以及消息的信息等。然后控制LED4的亮灭。
**********************************************************************************************/
void Task1(void)
{
uint32 PrinterDeviceAddr; //保存打印机设备的地址
PrinterDeviceAddr=OSGetDeviceAddr(OS_PRINTER_DEVICE_ID); //获取打印机设备地址
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("",1);
prints(OSCurrentPcb->Title,0); //显示任务启动
prints(" Start..........................",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机
EN_LED1(); //使能LED1
while(1)
{
OSTaskSuspend(OSCurrentPcb); //任务挂起
if(OSCurrentPcb->Msg) //如果收到消息
{
OSRequestDevice(PrinterDeviceAddr,0); //申请打印机
prints("",1);
prints("",1);
prints(OSCurrentPcb->Title,0); //显示任务标题
prints(":",1);
prints("Haha,I have got a message from: ",0); //显示相关信息
prints(OSCurrentPcb->Msg->Sender->Title,1); //显示发送者的标题
prints("The sender's PID is: ",0);
print_uint32(OSCurrentPcb->Msg->Sender->PID); //显示发送者的PID
prints("",1);
prints("The message type is:",0); //消息类型
print_uint32(OSCurrentPcb->Msg->MsgType);
prints("",1);
prints("The message value is",0); //消息的值
print_uint32(*(OSCurrentPcb->Msg->pMsg));
prints("",1);
prints("",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机
switch(*(OSCurrentPcb->Msg->pMsg)) //键值散转
{
case KEY1: ON_LED1();break; //如果是按键1按下,则点亮LED1
case KEY2: OFF_LED1();break; //如果是按键2按下,则熄灭LED1
default: break;
}
OSCurrentPcb->Msg=0; //消息处理完毕
}
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
任务2。延迟1S,等待一条消息。系统空闲任务会扫描按键,当按键按下后,系统空闲任务会将
键值通过消息发送给任务1。任务1接到消息后,显示消息的发送者以及消息的信息等。然后控制LED4的亮灭。
如果1S过后,还未收到消息,则显示接收超时。
/*********************************************************************************************/
void Task2(void)
{
uint32 PrinterDeviceAddr; //保存打印机设备的地址
PrinterDeviceAddr=OSGetDeviceAddr(OS_PRINTER_DEVICE_ID); //获取打印机设备地址
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("",1);
prints(OSCurrentPcb->Title,0); //显示任务启动
prints(" Start..........................",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机
EN_LED2();
while(1)
{
OSTaskDelay(100); //延时等待消息的到来
if(OSCurrentPcb->Msg) //如果收到了消息
{
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("",1);
prints("",1);
prints(OSCurrentPcb->Title,0); //显示任务的标题
prints(":",1);
prints("Haha,I have got a message from: ",0);
prints(OSCurrentPcb->Msg->Sender->Title,1); //显示消息发送者的标题
prints("The sender's PID is: ",0);
print_uint32(OSCurrentPcb->Msg->Sender->PID); //显示消息发送者的PID
prints("",1);
prints("The message type is:",0);
print_uint32(OSCurrentPcb->Msg->MsgType); //显示消息的类型
prints("",1);
prints("The message value is",0);
print_uint32(*(OSCurrentPcb->Msg->pMsg)); //显示消息
prints("",1);
prints("",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机
switch(*(OSCurrentPcb->Msg->pMsg)) //键值散转
{
case KEY3: ON_LED2();break; //如果是按键3按下,则点亮LED2
case KEY4: OFF_LED2();break; //如果是按键4按下,则熄灭LED2
default: break;
}
OSCurrentPcb->Msg=0; //消息处理完毕
}
else //如果1秒后还未收到消息,则
{
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("",1);
prints("",1);
prints(OSCurrentPcb->Title,0); //显示任务的标题
prints(":",1);
prints("I have not got a message in 1 second!",1); //显示未收到消息
prints("Time out!",1); //超时
prints("",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机
}
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
任务3。每隔500mS点亮、熄灭一次LED6,并显示一些信息。
**********************************************************************************************/
void Task3(void)
{
uint32 PrinterDeviceAddr; //保存打印机设备的地址
PrinterDeviceAddr=OSGetDeviceAddr(OS_PRINTER_DEVICE_ID); //获取打印机设备地址
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("",1);
prints(OSCurrentPcb->Title,0); //显示任务启动
prints(" Start..........................",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机
EN_LED3();
while(1)
{
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("HaHa,Running in Task3! ",1); //显示信息
OSFreeDevice(PrinterDeviceAddr); //释放打印机
OSTaskDelay(50); //延时50个时钟节拍
ON_LED3(); //开LED3
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("HaHa,Running in Task3! ",1); //显示信息
OSFreeDevice(PrinterDeviceAddr); //释放打印机
OSTaskDelay(50); //延时50个时钟节拍
OFF_LED3(); //关LED3
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
任务4。每隔1000mS点亮、熄灭一次LED6,并显示一些信息。
**********************************************************************************************/
void Task4(void)
{
uint32 PrinterDeviceAddr; //保存打印机设备的地址
PrinterDeviceAddr=OSGetDeviceAddr(OS_PRINTER_DEVICE_ID); //获取打印机设备地址
OSRequestDevice(PrinterDeviceAddr,0); // //申请使用打印机
prints("",1);
prints(OSCurrentPcb->Title,0); //显示任务启动
prints(" Start..........................",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机
EN_LED4();
while(1)
{
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("HaHa,Running in Task4! ",1); //显示信息
OSFreeDevice(PrinterDeviceAddr); //释放打印机
OSTaskDelay(100); //延时100个时钟节拍
ON_LED4(); //开LED4
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("HaHa,Running in Task4! ",1); //显示信息
OSFreeDevice(PrinterDeviceAddr); //释放打印机
OSTaskDelay(100); //延时100个时钟节拍
OFF_LED4(); //关LED4
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************************************
任务5。键盘扫描并发送消息。
**********************************************************************************************/
void Task5(void)
{
uint32 PrinterDeviceAddr; //保存打印机设备的地址
OSpcb * Task1Pcb,* Task2Pcb; //用来保存任务pcb
OSMsg Msg; //消息
uint32 MsgValue; //保存消息值
PrinterDeviceAddr=OSGetDeviceAddr(OS_PRINTER_DEVICE_ID); //获取打印机设备地址
OSRequestDevice(PrinterDeviceAddr,0); //申请使用打印机
prints("",1);
prints(OSCurrentPcb->Title,0); //显示任务启动
prints(" Start..........................",1);
OSFreeDevice(PrinterDeviceAddr); //释放打印机
Task1Pcb=(OSpcb *)(uint32)OSTaskCreat((uint32)Task1,256,4,OS_THUMB_MODE,"Task1"); //创建任务1
Task2Pcb=(OSpcb *)(uint32)OSTaskCreat((uint32)Task2,256,5,OS_THUMB_MODE,"Task2"); //创建任务2
while(1)
{
OSTaskDelay(1); //延迟10ms
ScanKey(); //键盘扫描
if(Key.Value) //如果有键按下
{
MsgValue=Key.Value; //保存按键值
(&Msg)->MsgType=KEYBOARD_MSG; //设置要发送的消息类型
(&Msg)->pMsg=&MsgValue; //设置要发送的消息的值
(&Msg)->Length=1; //设置消息的长度
OSSendMsg(Task1Pcb,&Msg); //将消息发送给任务1
OSSendMsg(Task2Pcb,&Msg); //将消息发送给任务2
Key.Value=0; //清除键值,表示该次按键已经处理
}
}
}
//////////////////////////////////End of function//////////////////////////////////////////////
/**********************************************************************
ADC.c file
LPC213X的ADC函数
本程序只供学习使用,不得用于其它任何用途,否则后果自负。
ARM_OS_main.c file
LPC213x上的操作系统——ARM_OS
作者:heciang
联系电话:13811129591
QQ:176780142
Email:heciang@126.com
建立日期:2006-5-1
修改日期:2006-5-15
最后修改时间:2006-08-25
版本:V1.0
Copyright(C) Computer-lov 2006-2016
All rights reserved
**********************************************************************************************/
**********************************************************************/
#include "CPU.H"
#include "My_type.h"
#include "ADC.h"
#include "KEYS.H"
////////////////////////由软件启动一次转换,并将ADC结果返回//////////////////////////
uint16 ADC_software_conversion(void)
{
uint16 result;
AD0CR |= 1<<24; //开始启动一次转换
while((AD0DR & 0x80000000)==0); //等待转换完成
AD0CR |= 1<<24; //再次启动一次转换
while((AD0DR & 0x80000000)==0); //等待转换完成
result=(AD0DR>>6)&0x3FF; //读取结果
return result; //返回结果
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////ADC校准/////////////////////////////////////////////////
void ADC_calibration(void)
{
//LPC213x的ADC没有校准功能。
}
//////////////////////////////////////////////////////////////////////////////////
////////////////////////////ADC初始化/////////////////////////////////////////////
void ADC_init(void)
{
//将P0.30连接到AD0.3
PINSEL1 &= ~0x20000000;
PINSEL1 |= 0x10000000;
//ADC模块设置
AD0CR=(1<<3)|(55<<8)|(1<<21)|(1<<24);
while((AD0DR & 0x80000000)==0); //等待转换完成
}
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////选择通道ch,启动转换,并将ADC结果返回///////////////////
uint16 read_ADC(uint8 ch)
{
//设置通道
AD0CR &= ~0xFF;
AD0CR |= (1<<ch);
return ADC_software_conversion();
}
//////////////////////////////////////////////////////////////////////////////////
/**********************************************************************************************
KEYS.c file
按键处理程序
本程序只供学习使用,不得用于其它任何用途,否则后果自负。
ARM_OS_main.c file
LPC213x上的操作系统——ARM_OS
作者:heciang
联系电话:13811129591
QQ:176780142
Email:heciang@126.com
建立日期:2006-5-1
修改日期:2006-5-15
最后修改时间:2006-08-25
版本:V1.0
Copyright(C) Computer-lov 2006-2016
All rights reserved
**********************************************************************************************/
**********************************************************************************************/
#include "CPU.H"
#include "KEYS.h"
KEYBoard Key; //定义一个Key结构体。
/*********************************************************************************************/
void KEYBoardInit(void)
{
PINSEL1 &= ~0xFFF; //将按键对对应的IO口设置为GPIO口. P0.16--P0.21
IO0DIR &= ~0x001F0000; //将按键对应的IO设置为输入口
Key.Value=0; //Key初始化
Key.OldValue=0;
Key.NewValue=0;
Key.PressedTime=0;
}
///////////////////////////////////////////////////////////////////////////////////////////////
/**********************************************************************************************
功能:延时x毫秒。
入口参数:x。要延时的毫秒数。
返回:无。
备注:纯软件延时,不同的指令执行速度,运行结果会不一样。
**********************************************************************************************/
void DelayXms(uint32 x)
{
uint32 i;
while(x--)
{
i=9000;
while(i--);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
/**********************************************************************************************
功能:键盘扫描程序。
入口参数:无。
返回:无。
备注:按键相关信息保存在Key中。请参看KEYBoard结构体。
**********************************************************************************************/
void ScanKey(void) //键盘扫描
{
if(Key.Value)return; //如果上次按键还未处理,则直接返回,不扫描新的按键
if(KEY1IN && KEY2IN && KEY3IN && KEY4IN && KEY5IN && KEY6IN) //如果输入全部是高,则按键处于释放状态
{
if(!(Key.OldValue)) //如果上次无按键按下
{
return; //返回
}
else //如果上次有按键按下
{
if(Key.PressedTime>100) //按键时间超过一秒,长按键码
{
Key.Value=Key.OldValue+0x80; //长按键码等于普通按键码加0x80
}
else if(Key.PressedTime>1)
{
Key.Value=Key.OldValue; //按键值
}
Key.OldValue=0;
}
return;
}
else //按键按下
{
Key.NewValue=0;
if(!KEY1IN)Key.NewValue=KEY1; //KEY1键按下
if(!KEY2IN)Key.NewValue=KEY2; //KEY2键按下
if(!KEY3IN)Key.NewValue=KEY3; //KEY3键按下
if(!KEY4IN)Key.NewValue=KEY4; //KEY4键按下
if(!KEY5IN)Key.NewValue=KEY5; //KEY5键按下
if(!KEY6IN)Key.NewValue=KEY6; //KEY6键按下
if(Key.NewValue==Key.OldValue) //如果连续两次扫描到一样的键,即按键按下长于10ms
{
if(Key.PressedTime>200) //如果按键按下超过2s
{
Key.PressedTime-=4; //则自动按键。相当于每隔50ms按一次
Key.Value=Key.NewValue;
return;
}
else
{
Key.PressedTime++; //按键按下计时加一
}
}
else
{
Key.PressedTime=0; //如果本次按下跟上次按下不一样,则按键按下时间清零
}
Key.OldValue=Key.NewValue; //保存本次按键值
}
}
/////////////////////////////////////////////////////////////////////////////////////////////// |
阿莫论坛20周年了!感谢大家的支持与爱护!!
你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。
|