关于中断号大于31在Keil中编译出错的处理
注:目前Keil各个版本的C51和C251编译器均只支持32个中断号(0~31),经我公司与Keil公司多方协商和探讨,Keil公司答应会在后续某个版本增加我公司对中断号超过32个的需求。但对于目前现有的Keil版本,只能使用本章节的方法进行临时解决。
热心网友有提供一个简单的拓展工具,可将中断号拓展到254。
工具界面如下:
点击“打开”按钮,定位到Keil的安装目录后,点击“确定”即可。
由于Keil的版本在不断更新,而早期版本过多,有无法收集齐,这里列举一下已测试通过的C51.EXE版本和C251.EXE版本
已测试通过的C51.EXE版本:
V6.12.0.1
V8.8.0.1
V9.0.0.1
V9.1.0.1
V9.53.0.0
V9.54.0.0
V9.57.0.0
V9.59.0.0
V9.60.0.0
已测试通过的C251.EXE版本:
V5.57.0.0
V5.60.0.0
查看C51.EXE版本的方法:
在keil中打开一个基于STC8系列或者STC15系列单片机的项目,在Keil软件菜单项“Help”中打开“About uVision...”
查看C251.EXE版本的方法:
在keil中打开一个基于STC32G系列单片机的项目,在Keil软件菜单项“Help”中打开“About uVision...”
在Keil的C51、C251编译环境下,中断号只支持0~31,即中断向量必须小于0100H。
下表是STC目前部分系列的中断列表:
中断号 |
中断向量 |
中断类型 |
0 |
0003 H |
INT0 |
1 |
000B H |
定时器0 |
2 |
0013 H |
INT1 |
3 |
001B H |
定时器1 |
4 |
0023 H |
串口1 |
5 |
002B H |
ADC |
6 |
0033 H |
LVD |
7 |
003B H |
PCA |
8 |
0043 H |
串口2 |
9 |
004B H |
SPI |
10 |
0053 H |
INT2 |
11 |
005B H |
INT3 |
12 |
0063 H |
定时器2 |
13 |
006B H |
|
14 |
0073 H |
系统内部中断 |
15 |
007B H |
系统内部中断 |
16 |
0083 H |
INT4 |
17 |
008B H |
串口3 |
18 |
0093 H |
串口4 |
19 |
009B H |
定时器3 |
20 |
00A3 H |
定时器4 |
21 |
00AB H |
比较器 |
22 |
00B3 H |
波形发生器0 |
23 |
00BB H |
波形发生器异常0 |
24 |
00C3 H |
I2C |
25 |
00CB H |
USB |
26 |
00D3 H |
PWMA |
27 |
00DB H |
PWMB |
28 |
00E3 H |
波形发生器1 |
29 |
00EB H |
波形发生器2 |
30 |
00F3 H |
波形发生器3 |
31 |
00FB H |
波形发生器4 |
32 |
0103 H |
波形发生器5 |
33 |
010B H |
波形发生器异常2 |
34 |
0113 H |
波形发生器异常4 |
35 |
011B H |
触摸按键 |
36 |
0123 H |
RTC |
37 |
012B H |
P0口中断 |
38 |
0133 H |
P1口中断 |
39 |
013B H |
P2口中断 |
40 |
0143 H |
P3口中断 |
41 |
014B H |
P4口中断 |
42 |
0153 H |
P5口中断 |
43 |
015B H |
P6口中断 |
44 |
0163 H |
P7口中断 |
45 |
016B H |
P8口中断 |
46 |
0173 H |
P9口中断 |
不难发现,从波形发生器5中断开始,后面所有的中断服务程序,在keil中均会编译出错,如下图所示:
处理这种错误有如下三种方法:(均需要借助于汇编代码,优先推荐使用方法1)
方法1:借用13号中断向量
0~31号中断中,第13号是保留中断号,我们可以借用此中断号
操作步骤如下:
1、将我们报错的中断号改为“13”,如下图:
2、新建一个汇编语言文件,比如“isr.asm”,加入到项目,并在地址“0103H”的地方添加一条“LJMP 006BH”,如下图:
3、编译即可通过。
此时经过Keil的C51编译器编译后,在006BH处有一条“LJMP PWM5_ISR”,在0103H处有一条“LJMP 006BH”,如下图:
当发生PWM5中断时,硬件会自动跳转到0103H地址执行“LJMP 006BH”,然后在006BH处再执行“LJMP PWM5_ISR”即可跳转到真正的中断服务程序,如下图:
中断服务程序执行完成后,再通过RETI指令返回。整个中断响应过程只是多执行了一条LJMP语句而已。
方法2:与方法1类似,借用用户程序中未使用的0~31的中断号
比如在用户的代码中,没有使用INT0中断,则可将上面的代码作类似与方法1的修改:
执行效果与方法1相同,此方法适用于需要重映射多个中断号大于31的情况。
方法3:将中断服务程序定义成子程序,然后在汇编代码中的中断入口地址中使用LCALL指令执行服务程序
操作步骤如下:
1、首先将中断服务程序去掉“interrupt”属性,定义成普通子程序
2、然后在汇编文件的0103H地址输入如下图所示的代码
3、编译通过后,即可发现在0103H地址的地方即为中断服务程序
此方法不需要重映射中断入口,不过这种方法有一个问题,在汇编文件中具体需要将哪些寄存器压入堆栈,需要用户查看C程序的反汇编代码来确定。一般包括PSW、ACC、B、DPL、DPH以及R0~R7。除PSW必须压栈外,其他哪些寄存器在用户子程序中有使用,就必须将哪些寄存器压栈。