表6-1IE——中断使能寄存器的位分配(地址0xA8、可位寻址) 位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 符号 | EA | -- | ET2 | ES | ET1 | EX1 | ET0 | EX0 | 复位值 | 0 | -- | 0 | 0 | 0 | 0 | 0 | 0 |
表6-2IE——中断使能寄存器的位描述 位 | 符号 | 描述 | 7 | EA | 总中断使能位,相当于总开关 | 6 | -- | -- | 5 | ET2 | 定时器2中断使能 | 4 | ES | 串口中断使能 | 3 | ET1 | 定时器1中断使能 | 2 | EX1 | 外部中断1使能 | 1 | ET0 | 定时器0中断使能 | 0 | EX0 | 外部中断0使能 |
中断使能寄存器IE的位0~5控制了6个中断使能,而第6位没有用到,第7位是总开关。总开关就相当于我们家里或者学生宿舍里的那个电源总闸门,而0~5位这6个位相当于每个分开关。那么也就是说,我们只要用到中断,就要写EA=1这一句,打开中断总开关,然后用到哪个分中断,再打开相对应的控制位就可以了。 我们现在就把前面的数码管动态显示的程序改用中断再实现出来,同时数码管显示抖动和“鬼影”也一并处理掉了。程序运行的流程跟图6-1所示的流程图是基本一致的,但因为加入了中断,所以整个流程被分成了两部分,秒计数和转换为数码管显示字符的部分还留在主循环内,而动态扫描部分则移到了中断函数内,并加入了消隐的处理。下面来看程序: #include sbitADDR0=P1^0; sbitADDR1=P1^1; sbitADDR2=P1^2; sbitADDR3=P1^3; sbitENLED=P1^4; unsignedcharcodeLedChar[]={//数码管显示字符转换表 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8, 0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E }; unsignedcharLedBuff[6]={//数码管显示缓冲区,初值0xFF确保启动时都不亮 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }; unsignedchari=0;//动态扫描的索引 unsignedintcnt=0;//记录T0中断次数 voidmain() { unsignedlongsec=0;//记录经过的秒数 EA=1;//使能总中断 ENLED=0;//使能U3,选择控制数码管 ADDR3=1;//因为需要动态改变ADDR0-2的值,所以不需要再初始化了 TMOD=0x01;//设置T0为模式1 TH0=0xFC;//为T0赋初值0xFC67,定时1ms TL0=0x67; ET0=1;//使能T0中断 TR0=1;//启动T0 while(1) { if(cnt>=1000)//判断T0溢出是否达到1000次 { cnt=0;//达到1000次后计数值清零 sec++;//秒计数自加1 //以下代码将sec按十进制位从低到高依次提取并转为数码管显示字符 LedBuff[0]=LedChar[sec%10]; LedBuff[1]=LedChar[sec/10%10]; LedBuff[2]=LedChar[sec/100%10]; LedBuff[3]=LedChar[sec/1000%10]; LedBuff[4]=LedChar[sec/10000%10]; LedBuff[5]=LedChar[sec/100000%10]; } } } /*定时器0中断服务函数*/ voidInterruptTimer0()interrupt1 { TH0=0xFC;//重新加载初值 TL0=0x67; cnt++;//中断次数计数值加1 //以下代码完成数码管动态扫描刷新 P0=0xFF;//显示消隐 switch(i) { case0:ADDR2=0;ADDR1=0;ADDR0=0;i++;P0=LedBuff[0];break; case1:ADDR2=0;ADDR1=0;ADDR0=1;i++;P0=LedBuff[1];break; case2:ADDR2=0;ADDR1=1;ADDR0=0;i++;P0=LedBuff[2];break; case3:ADDR2=0;ADDR1=1;ADDR0=1;i++;P0=LedBuff[3];break; case4:ADDR2=1;ADDR1=0;ADDR0=0;i++;P0=LedBuff[4];break; case5:ADDR2=1;ADDR1=0;ADDR0=1;i=0;P0=LedBuff[5];break; default:break; } } 大家可以先把程序抄下来,编译下载到单片机里运行,看看实际效果。是否可以看到,近乎完美的显示效果经过我们的努力终于做成功了。下面我们还要再来解析一下这个程序。 在这个程序中,有两个函数,一个是主函数,一个是中断服务函数。主函数main()我们就不用说了,重点强调一下中断服务函数,它的书写格式是固定的,首先中断函数前边void表示函数返回空,即中断函数不返回任何值,函数名是InterruptTimer0(),这个函数名在符合函数命名规则的前提下可以随便取,我们取这个名字是为了方便区分和记忆,而后是interrupt这个关键字,一定不能错,这是中断特有的关键字,另外后边还有个数字1,这个数字1怎么来的呢?我们先来看表6-3。 表6-3中断查询序列 中断 函数编号 | 中断名称 | 中断 标志位 | 中断 使能位 | 中断 向量地址 | 默认 优先级 | 0 | 外部中断0 | IE0 | EX0 | 0x0003 | 1(最高) | 1 | T0中断 | TF0 | ET0 | 0x000B | 2 | 2 | 外部中断1 | IE1 | EX1 | 0x0013 | 3 | 3 | T1中断 | TF1 | ET1 | 0x001B | 4 | 4 | UART中断 | TI/RI | ES | 0x0023 | 5 | 5 | T2中断 | TF2/EXF2 | ET2 | 0x002B | 6 |
这 |