| /*---------------------------------------------------------------------------------- LPCARM主程序喂狗正确方法及异常模拟裸奔程序详解 雁塔菜农HotPower 2006.12.26 21:32 于菜地Http://HotPower.21ic.org/ ----------------------------------------------------------------------------------*/ #i nclude <LPC213xDEF.H> //ARM菜鸟HotPower创建定义文件(最新为倒塌版) /*--------------------------------------------------------------------------------- 注意:(今天是菜农时代伟人的113年诞辰日,特发此帖!!!) (姊妹篇--LPCARM中断喂狗正确方法及异常模拟裸奔程序详解)---以先期发布 菜农累了,不想多费口舌,先自己看明白吧。欢迎拍砖有利于菜农进一步改进
点击下栽本裸奔程序详解完整包: uploadfile-/2006-12/1226664240.rar----------------------------------------------------------------------------------*/ /*--------------------------------------------------------- 时钟参数定义 ---------------------------------------------------------*/ #define Fosc 11059200 #define Fcclk (Fosc * 4) #define Fcco (Fcclk * 4) #define Fpclk (Fcclk / 4) * 1
/*--------------------------------------------------------- 申请系统变量 ---------------------------------------------------------*/ __noinit__ volatile unsigned int PFCount;//申请掉电计数器(复位不初始化) __noinit__ volatile unsigned int WdtCount;//申请软狗计数器(复位不初始化) __noinit__ volatile unsigned int TimerCount;//申请软狗计数器(复位不初始化) __noinit__ volatile unsigned int MainLoopAddr;//主程序循环地址寄存器(用于模拟主程序飞后的随机地址) __noinit__ volatile unsigned int SystemRamTest;//RAM不初始化(用于判别是否是首次上电或内存完好情况)
/*--------------------------------------------------------- 系统函数声明 ---------------------------------------------------------*/ void SystemInit(void);//系统初始化 void VicInit(void);//向量中断初始化 void PortInit(void);//IO初始化 void Timer0Init(void);//定时器初始化 void KeyInit(void);//键盘初始化 void VicIntSetup(void);//向量中断设置 void Enable_WDT(unsigned int);//使能看门狗(并设置复位参数) void Reset_WDT(void);//复位看门狗(首次实际是启动看门狗) void MainLoop(void);
void __swi(0) Enable_IRQ(void);//开放向量中断总开关 void __SWI_0 (void) { int tmp; __asm { MRS tmp, SPSR BIC tmp, tmp, #0x80 MSR SPSR_c, tmp } }
void __swi(1) Disable_IRQ(void);//关闭向量中断总开关 void __SWI_1 (void) { int tmp; __asm { MRS tmp, SPSR ORR tmp, tmp, #0x80 MSR SPSR_c, tmp } }
void __swi(2) Enable_FIQ(void);//开放快速中断总开关 void __SWI_2 (void) { int tmp; __asm { MRS tmp, SPSR BIC tmp, tmp, #0x40 MSR SPSR_c, tmp } }
void __swi(3) Disable_FIQ(void);//关闭快速中断总开关 void __SWI_3 (void) { int tmp; __asm { MRS tmp, SPSR ORR tmp, tmp, #0x40 MSR SPSR_c, tmp } }
void __swi(4) Enable_VIC(void);//开放对VIC的写入保护 void __SWI_4 (void) { VIC->Protection = 0;//开放对VIC的写入保护 }
#define Disable_VIC() VIC->Protection = 1;//关闭在用户级对VIC的一些写操作,保护在用户级VIC不被以外改写
/*---------------------------------------------------------------------- 外部中断0异常处理程序(按P0.1关闭定时器0,使定时器0中断不能喂硬狗!) ----------------------------------------------------------------------*/ extern void FIQ_EINT0 (void) __irq { T0->TIMER_TCR &= ~0x01;//关闭定时器0(使定时器0中断不能喂狗) VIC->SoftIntClr = (1 << VICIntSel_EINT0);//增强安全性 INTCON->EXT_INT = (1 << EINT0);//清除INT0中断标志 VIC->VectAddr = 0; }
/*----------------------------------------------------------------------------- 外部中断1异常处理程序(按P0.3改写定时器0中断地址,使定时器0中断不能喂硬狗!) ------------------------------------------------------------------------------*/ void IRQ_EINT1 (void) __irq { VIC->VectAddrs[2] = (unsigned int)PFCount;//修改中断服务程序地址,制造中断程序飞 VIC->SoftIntClr = (1 << VICIntSel_EINT1);;//增强安全性 INTCON->EXT_INT = (1 << EINT1);//清除INT1中断标志 VIC->VectAddr = 0; }
/*---------------------------------------------------------------------------- 外部中断2异常处理程序(按P0.7改写主循环地址,定时器0中断正常也不能喂硬狗!) -----------------------------------------------------------------------------*/ void IRQ_EINT2 (void) __irq { MainLoopAddr = (unsigned int)T0->TIMER_TC;//修改主循环程序地址,制造主程序飞 VIC->SoftIntClr = (1 << VICIntSel_EINT2);;//增强安全性 INTCON->EXT_INT = (1 << EINT2);//清除INT2中断标志 VIC->VectAddr = 0; }
/*---------------------------------------------------------------------- 定时器0中断 ----------------------------------------------------------------------*/ void IRQ_Timer0 (void) __irq { TimerCount ++;//软狗计数器计数 if (TimerCount > 20) {//20*5mS=100mS时认为主程序飞!!! /*---------------------------------------------------------------------- 捕捉到主程序飞!!!在此添加主程序飞事件处理!!! ----------------------------------------------------------------------*/ __nop(); } T0->TIMER_IR = 0x01;//清除中断标志,相当于VIC->IRQStatus &= ~(1 << VICIntSel_Time0);//可惜IRQStatus只能写 VIC->SoftIntClr = (1 << VICIntSel_Time0);//也应该清除安全些!!! VIC->VectAddr = 0x00; //通知VIC中断处理结束 }
/*---------------------------------------------------------------------- 系统默认中断处理 ----------------------------------------------------------------------*/ void IRQ_Default (void) __irq { unsigned int status; status = VIC->IRQStatus | VIC->FIQStatus;//合并2种中断一起处理 switch(status) { case (1 << VICIntSel_EINT0)://拦截非法INT0中断 INTCON->EXT_INT = (1 << EINT0);//清除非法INT0中断标志 VIC->SoftIntClr = (1 << VICIntSel_EINT0);//清除非法INT0软件中断标志 break; case (1 << VICIntSel_EINT1)://拦截非法INT1中断 INTCON->EXT_INT = (1 << EINT1);//清除非法INT1中断标志 VIC->SoftIntClr = (1 << VICIntSel_EINT1);//清除非法INT1软件中断标志 break; case (1 << VICIntSel_EINT2)://拦截非法INT2中断 INTCON->EXT_INT = (1 << EINT2);//清除非法INT2中断标志 VIC->SoftIntClr = (1 << VICIntSel_EINT2);//清除非法INT2软件中断标志 break; case (1 << VICIntSel_EINT3)://拦截非法INT3中断 INTCON->EXT_INT = (1 << EINT3);//清除非法INT2中断标志 VIC->SoftIntClr = (1 << VICIntSel_EINT3);//清除非法INT3软件中断标志 break; case (1 << VICIntSel_SoftInt23)://拦截非法用户软件中断VICIntSel_SoftInt23 VIC->SoftIntClr = (1 << VICIntSel_SoftInt23);//清除非法用户软件中断标志 break; case (1 << VICIntSel_SoftInt24)://拦截非法用户软件中断VICIntSel_SoftInt24 VIC->SoftIntClr = (1 << VICIntSel_SoftInt24);//清除非法用户软件中断标志 break; case (1 << VICIntSel_SoftInt25)://拦截非法用户软件中断VICIntSel_SoftInt25 VIC->SoftIntClr = (1 << VICIntSel_SoftInt25);//清除非法用户软件中断标志 break; case (1 << VICIntSel_SoftInt26)://拦截非法用户软件中断VICIntSel_SoftInt26 VIC->SoftIntClr = (1 << VICIntSel_SoftInt26);//清除非法用户软件中断标志 break; default: VIC->SoftIntClr = 0xffffff;//清除所有非法用户软件中断标志 } VIC->VectAddr = 0;//结束中断处理 }
/*---------------------------------------------------------------------- 系统初始化函数 ----------------------------------------------------------------------*/ void SystemInit(void) { VicInit(); PortInit(); KeyInit(); Timer0Init(); Enable_WDT(0xffff);//使能看门狗并设置复位参数 VicIntSetup(); }
/*---------------------------------------------------------------------- 向量中断初始化函数 ----------------------------------------------------------------------*/ void VicInit(void) { volatile unsigned int start; VIC->IntEnable = 0;//关闭全部中断 VIC->SoftIntClr = 0xffffffff;//清除所有软中断标志 VIC->IntSelect = 0;//全部中断为IRQ中断或默认中断 for (start = 0; start < 10000; start ++);//系统延时等待接口稳定 }
/*---------------------------------------------------------------------- IO初始化函数 ----------------------------------------------------------------------*/ void PortInit(void) { PINSEL->PIN_SEL0 = 0x00000000;//设置管脚连接GPIO PINSEL->PIN_SEL1 = 0x00000000;//设置管脚连接GPIO P0->IODIR = 0x00000000;//设置P0口为输入 P1->IODIR = 0x00000000;//设置P1口为输入 }
/*---------------------------------------------------------------------- 定时器初始化函数 ----------------------------------------------------------------------*/ void Timer0Init(void) { T0->TIMER_TC = 0; //时器设置为0 T0->TIMER_PR = 0; //时钟不分频 T0->TIMER_MCR = 0x03; //设置T0MR0匹配后复位T0TC,并产生中断标志 T0->TIMER_MR0 = Fpclk / 200; //5mS定时 T0->TIMER_TCR = 0x01; //启动定时器 T0->TIMER_IR = 0x01; //清除中断标志 }
/*---------------------------------------------------------------------- 向量中断设置函数 ----------------------------------------------------------------------*/ void VicIntSetup(void) { unsigned int i; VIC->VectCntls[0] = VICIntSel_Enable | VICIntSel_EINT1;//设置外部中断1分配VIC最高优先级 VIC->VectAddrs[0] = (unsigned int)IRQ_EINT1;//设置中断服务程序地址 VIC->IntEnable |= (1 << VICIntSel_EINT1);//使能外部中断1
VIC->VectCntls[1] = VICIntSel_Enable | VICIntSel_EINT2;//设置外部中断2分配VIC次优先级 VIC->VectAddrs[1] = (unsigned int)IRQ_EINT2;//设置中断服务程序地址 VIC->IntEnable |= (1 << VICIntSel_EINT2);//使能外部中断1
/* 设置定时器0中断IRQ */ VIC->VectCntls[2] = VICIntSel_Enable | VICIntSel_Time0;//设置定时器0中断通道分配最高优先级 VIC->VectAddrs[2] = (unsigned int)IRQ_Timer0;//设置中断服务程序地址 VIC->IntEnable |= (1 << VICIntSel_Time0);//使能定时器0中断
VIC->IntSelect = (1 << VICIntSel_EINT0); //设置EINT0为FIQ中断
VIC->DefVectAddr = (unsigned int)IRQ_Default;//定义除VIC->VectAddrs[0]~VIC->VectAddrs[15]外的中断函数 for (i = 0; i < 16; i ++) { if ((VIC->VectCntls[i] & VICIntSel_Enable) == 0) {//该中断向量未使能 VIC->VectAddrs[i] = (unsigned int)IRQ_Default;//全部以默认中断来处理 // VIC->VectAddrs[i] = (unsigned int)0;//全部以复位来处理 } }
INTCON->EXT_INT = (1 << EINT0) //清除INT0中断标志 | (1 << EINT1) //清除INT1中断标志 | (1 << EINT2) //清除INT2中断标志 | (1 << EINT3);//清除INT3中断标志
VIC->SoftIntClr = (1 << VICIntSel_EINT0) //清除EINT0软中断标志 | (1 << VICIntSel_EINT1) //清除EINT1软中断标志 | (1 << VICIntSel_EINT1) //清除EINT2软中断标志 | (1 << VICIntSel_EINT1);//清除EINT3软中断标志
VIC->IntEnable = (1 << VICIntSel_EINT0) //使能EINT0中断 | (1 << VICIntSel_EINT1) //使能EINT1中断 | (1 << VICIntSel_EINT2);//使能EINT2中断 }
/*---------------------------------------------------------------------- 键盘初始化函数 ----------------------------------------------------------------------*/ void KeyInit(void) { PINSEL->PIN_SEL0 |= (P0_1_EINT0 << P0_1_PINSEL) //选择P0.1为INT0外部中断引脚 | (P0_3_EINT1 << P0_3_PINSEL) //选择P0.3为INT1外部中断引脚 | (P0_7_EINT2 << P0_7_PINSEL);//选择P0.7为INT2外部中断引脚
INTCON->EXT_POLAR = ~(1 << EXTPOLAR0);//INT0为低电平有效 INTCON->EXT_POLAR = ~(1 << EXTPOLAR1);//INT1为低电平有效 INTCON->EXT_POLAR = ~(1 << EXTPOLAR2);//INT2为低电平有效
INTCON->EXT_MODE = (1 << EXTMODE0) //设置INT0为边沿触发 | (1 << EXTMODE1) //设置INT1为边沿触发 | (1 << EXTMODE2); //设置INT2为边沿触发 }
/*---------------------------------------------------------------------- 使能看门狗(并设置复位参数) ----------------------------------------------------------------------*/ void Enable_WDT(unsigned int wdtc) { WDT->WDT_WDTC = wdtc; WDT->WDT_WDMOD = (1 << WDEN) | (1 << WDRESET); Reset_WDT();//必须在此复位看门狗一次(首次实际是启动看门狗) }
/*---------------------------------------------------------------------- 复位看门狗(首次实际是启动看门狗) ----------------------------------------------------------------------*/ void Reset_WDT(void) { unsigned int tmp;//设置暂存器以取代Enable()/Disable()函数。 tmp = VIC->IntEnable;//保护,相当与Disable()函数以代替关中断 VIC->IntEnable = 0;//禁止所有FIQ和IRQ中断 WDT->WDT_WDFEED = 0xaa; WDT->WDT_WDFEED = 0x55; VIC->IntEnable = tmp;//恢复,相当与Enable()函数以代替开中断 }
int main(void) { typedef void (* PV)(void);//函数指针 PV func;//声明函数指针 Disable_IRQ();//关闭向量中断总开关 Disable_FIQ();//关闭快速中断总开关
if (SystemRamTest != 0x5555aaaa) {//内存被破坏(或真正上电) TimerCount = 0;//软狗计数器复位 MainLoopAddr = (unsigned int)MainLoop;//只在真正上电时初始化 SystemRamTest = 0x5555aaaa;//内存被破坏处理结束 } else { //下句一定要抢在Enable_WDT()前,以防止WDTOF位被改写 if ((WDT->WDT_WDMOD & (1 << WDTOF)) == 0) {//可能是外狗或手动复位 /*---------------------------------------------------------------------- 再此添加外狗或手动复位异常处理程序 ----------------------------------------------------------------------*/ // PFCount = 0;//掉电计数器复位 } else {//内狗复位 PFCount += 0x100;//掉电计数器复位 } } SystemInit();//系统初始化
Enable_FIQ();//开放快速中断总开关 Enable_IRQ();//注意现在立即进入EINT1硬件中断(因为假定P0.3/P0.14都为高电平)
Disable_VIC();//关闭在用户级对VIC的一些写操作,保护在用户级VIC不被以外改写 while(1) { // POWER->P_CON = 1;//待机 func = (PV)MainLoopAddr; func();//运行MainLoop() if (TimerCount > 0) {//说明定时中断(前台)程序在运行中,即程序正常。 TimerCount = 0;//软狗计数器复位 Reset_WDT();//复位看门狗,首次实际是启动看门狗 } else { WdtCount ++;//软狗计数器计数 if (WdtCount > 0x3000) {//定时器程序跑飞 /*---------------------------------------------------------------------- 捕捉到定时器程序飞!!!在此添加定时器程序飞事件处理!!! 可以在此添加修复定时器代码,当然也可在此死循环以等待看门狗复位。 ----------------------------------------------------------------------*/ Timer0Init();//修复定时器 WdtCount = 0; } } } }
/*---------------------------------------------------------------------- 主程序正常的主循环等待,主程序飞可能不会运行此程序。 ----------------------------------------------------------------------*/ void MainLoop(void) { __nop(); __nop(); __nop(); }
|