文章目錄
  1. 1. 一、关键字
    1. 1.1. 1.1 interrupt&using
    2. 1.2. 1.2 critical
  2. 2. 二、使用中断时的常见bug
    1. 2.1. 2.1 变量没有被定义为volatile
    2. 2.2. 2.2 非原子指令
    3. 2.3. 2.3 堆栈溢出
    4. 2.4. 2.4 使用了不可重入的函数

一、关键字

1.1 interrupt&using

sdcc中,中断程序使用的关键字和keil一样:

1
2
3
4
void timer_isr (void) __interrupt (1) __using (1)
{

...
}

interrupt指定中断号,using指定register bank。

中断程序可以写在任意一个源文件里,但在包含main程序的源文件里,必须要有一份中断程序的声明。

如果使用register bank 0,那么系统会将当前程序用到的寄存器全都保存进堆栈。如果用到其他register bank,那么只会保存类似dptr这样的特殊寄存器。

1.2 critical

critical用于表示一个程序不能被中断打算,sdcc会在运行__critical修饰的程序前保存当前中断使能状态,并关闭所有中断,运行完了之后再打开。

1
2
3
4
5
int foo () __critical
{
...
...
}

同样对单独的语句也可以使用

1
__critical{ i++; }

二、使用中断时的常见bug

2.1 变量没有被定义为volatile

这是个常见的错误,没有声明为volatele的变量,会使编译器对变量的读取采取优化。

2.2 非原子指令

有些c语句并非对应一句汇编指令,比如在c51下16位变量读取,可能需要2到3条指令来执行。如果在执行第一条指令时发生了中断,很有可能会产生意想不到的后果,而且这种bug很难reproduce。

2.3 堆栈溢出

中断程序的返回地址和保留的寄存器值全都存在堆栈里。如果在调用中断程序时,程序正好调用了多层的子程序里最深的一层,很有可能会没有足够的堆栈给中断使用。

2.4 使用了不可重入的函数

如果中断程序里对16位/32位的变量进行了乘除、取余等操作,其实是额外调用了一些子函数,要使用—stack-auto将这些都编译为可重入函数。

另外,如果在中断程序中调用了其他函数(不推荐这么做),那么要使用#pragma nooverlay来避免子程序使用可覆盖的变量。

文章目錄
  1. 1. 一、关键字
    1. 1.1. 1.1 interrupt&using
    2. 1.2. 1.2 critical
  2. 2. 二、使用中断时的常见bug
    1. 2.1. 2.1 变量没有被定义为volatile
    2. 2.2. 2.2 非原子指令
    3. 2.3. 2.3 堆栈溢出
    4. 2.4. 2.4 使用了不可重入的函数