软中断在51单片机中的实现及其应用
彭树林
摘要 本文讨论软中断在51单片机(兼容)中的实现方法以及软中断在51单片机(兼容)系统中的应用。详细说明了在51单片机实现软中断的方法,并就软中断的应用作了简明扼要的阐述。
关键词 软中断 单片机 51单片机 实时操作系统 1、序言
现代的单片机应用中,某些单片机为了方便操作系统编程,会保留一些特权指令给RTOS操作系统,以便实时控制整个机器;软件中的一些原子操作不允许中断破坏,也需要一些特权指令。软中断指令表面上类似于函数调用,主要是使单片机进入特权运行状态,并在这个状态下,操作一些用户状态下不能使用的功能。 51兼容单片机没有特权功能,也不存在软中断指令。但是我们的一些应用确实需要软中断特权指令来完成一些特殊的操作。
本文所讨论软中断在51兼容单片机中的实现方法以及软中断在51兼容单片机系统中的应用。
2、软中断与硬中断
软中断,最早出现在Intel8086处理器中,该处理器的指令系统中,可使用INT指令来申请中断服务。在DOS操作系统下,利用INT 21这条指令,应用软件可以申请多达84个DOS系统服务。在Linux、Windows、SOLAIRS 等操作系统中,都有软中断在为系统提供服务。
软中断和硬中断区别不大,软中断是程序中用软件代码人为地产生中断,让CPU去执行相应的中断服务程序。硬中断由硬件产生并触发中断,让CPU去执行相应的中断服务程序以响应该事件。
可以这样理解,软中断以软件中断指令所携带的参数作为工作的依据,而硬中断是以计算机系统中硬件所发生的事件为工作依据。可见软件中断的灵活性非常强,可根据程序的运行状态、硬件状态以及任务之间的消息作为触发条件,申请相应的软中断功能服务。
软中断最大的用途在于RTOS提供系统级服务,提高系统的实时性。在没有RTOS的情况下,我们也可利用软中断服务对敏感代码和敏感数据进行保护。比如互斥访问共享硬件以及互斥访问共享数据等。
3、51单片机(兼容)中断系统的秘密
51单片机(兼容)的中断系统中,各个中断标志的置位条件和如何清除,都在公开的技术资料中进行了详细的描述。然而,公开的技术细节实际上并没有将中断系统的工作原理讲述清楚。大家使用的51兼容单片机,其中断系统的工作细节以及工作的结果与公开的技术资料有些小小的差异。比如关于外部中断标志IE0的置位,附1是五个公司对IE0的描述。各个公司开发的51单片机(兼容),其中断系统也兼容,仅仅是中断源的多少,中断优先级的级数以及中断向量的多少的不同,附2列举了六家公司51单片机(兼容)的中断系统资料。
中断系统的秘密在于:中断标志完全可以通过一定的方式实现软件置位和软件清除。可否通过软件置位和清除中断标志,是在51单片机中实现软中断的关键,如果不能用软件申请中断,软中断的实现就无从谈起。
硬件置位中断申请标志只是方法之一而不是唯一的方法,CPU完全可以设置这些标志位为“0”或者“1”。这是理解本文的基础,对于有丰富经验的51单片机开发者,只要知道这个道理就好,基本没有必要再阅读后面的部分。本文接下来的部分具体描述如何在51单片机中实现软中断,主要针对51单片机开发经验还不是十分丰富的读者。
IE0和IE1的秘密在于,如果将中断检测的方式(IT0、IT1)设置为边沿检测,就可以用软件来设置或清除IE0和IE1(清除也可以让中断系统在响应中断时自动完成)。TF0、TF1、TF2、TI以及RI是已知的可以软件操控的中断申请标志。 对于各个公司的51单片机(兼容)增强部分的中断,可以根据实际应用,在关闭模块功能的情况下一般都可以自由使用其中断资源。具体应用时,应编写一个测试软件,测试具体型号的51单片机各个中断资源是否能够被利用,避免走弯路。读者应当注意到,有些单片机的某些中断申请标志,其清除方法是通过对该标志位写“1”来完成的,这种中断源就不适合用于软中断,比如STC12C5AxxS2单片机的SPI中断。
如果要使用STC12C5AxxS2单片机的SPI中断作为软中断,就必须放弃该模块占用的IO引脚,以实现软中断。例如在40脚DIP封装的STC12C5AxxS2单片机中,将SPI影射到P4口不会占用外部的引脚,将SPI设置为MASTER模式,就可以通过对SPDAT写入0xFF数据来是SPI工作,SPI工作完成后会自动设置中断申请标志,从而完成软中断的申请。而在44或48脚封装的STC12C5AxxS2单片机,用SPI中断源实现软中断就必须考虑放弃SPI占用的引脚(P1口或P4口)。 大部分51单片机(兼容)增强部分,在系统的应用中不能全部同时使用,因为它们可能共享了IO端口上的引脚。因此,总是能在一个51单片机中找到空闲资源实现软中断。
4、软中断实现的方法
只要是代码兼容的51单片机,就可以利用空闲不用的中断来实现软中断。对于没有空闲中断的应用系统,也可以通过本文描述的方法来实现共享硬件中断和软中断。
在51单片机应用系统中,实现软中断的基本原理是用软件置位硬件中断的申请标志位,迫使系统承认一个“硬件中断”发生进而去响应这个“硬件中断事件”。这样,就实现了用软件申请实现中断服务。
具体在一个应用系统中实现时,可能还需要作如下的安排:1)将应用系统中已经使用的全部硬件中断设置为高优先级;2)让软中断使用的中断源的优先级为最低优先级,以确保软中断中可以响应硬件中断,实现中断嵌套。
4.1 使用外部中断
使用外部中断0和1作为软中断时,应当使中断的触发方式设置为边沿触发,并且对应的引脚只能进行输出,不能为输入,若设计为输入时系统不能分辨硬件触发和软件触发。即使是对外输出,也需要限制为软中断的服务来提供输出,就能避免输出的变化错误地触发中断。
经过上述的设置后,就可以利用软件来控制中断标志IE0或IE1的置位和清除,由于51单片机(兼容)中断系统能够在中断返回时自动清除外部中段标志IE0或IE1,因此在结束软中断服务时可以不清除中断标志。
例如,使用外部中断0来实现软中断。首先,开放外部中断0,使EX0=1;其次,设置外部中断0为沿触发,使IT0=1;接下来用“IE0 = 1”来置位IE0申请软中断服务,而使用“IE0 = 0”来清除IE0结束软中断服务(可以不使用“IE0 = 0”而由中断系统在中断服务中自动清除)。参见例2所示例程。 4.2 使用定时器中断
使用定时器0、定时器1和定时器2中断,需要考虑的是关闭定时器,以避免定时器溢出产生中断申请。这时软件就能完全控制中断标志TF0、TF1或TF2的置位以及清除,以申请软中断和结束软中断服务。应当注意,51单片机(兼容)的中断系统在中断返回时不会自动清除TF0、TF1和TF2,因此,在结束软中断服务时,一定要清除相应得中断标志TF0、TF1或TF2。对于具有定时计数器2的单片机,如果将其应用于串口的波特率发生器,就是最好的软中断资源。因为定时器而一旦被应用于串口产生发送时钟或者接收时钟,定时器的溢出就不会设置TF2(TF2 will not be set when either RCLK = 1 or TCLK = 1.)。 例如,使用定时器0来实现软中断。首先,关闭定时器0,使TR0=0;其次,开放定时器0的中断,使ET0=1;接下来用“TF0 = 1”来置位TF0申请软中断服务,而使用“TF0 = 0”来清除TF0结束软中断服务。参见例2所示例程。
4.3 使用其它中断资源
其他的中断资源,只要没有和引脚有关联,就可以在关闭模块功能的条件下直接,用软件来设置或清除相应模块的中断申请标志,以申请软中断服务或结束软中断服务。
例如,在51单片机(兼容)应用系统中没有使用串口(对于多串口单片机,是串口0)通讯,就可以使用串口的RI中断标志来实现软中断。首先,禁止接收
(复位状态就是禁止的),使REN=0;其次,开放串口中断,使ES=1;接下来,利用“RI = 1”来置位RI申请软中断服务,而使用“RI = 0”来清除RI结束软中断服务。参见例2所示例程。
再例如,在STC12C5AxxS2应用系统中,不使用ADC模块,就可以使用ADC模块的ADC_FLAG中断标志来实现软中断。首先,禁止ADC模块(复位状态就是禁止的),使ADC_POWER=0,关闭ADC模块的电源;其次,开放ADC中断,使EADC=1;接下来用“ADC_CONTR = 0x10”来置位ADC_FLAG申请软中断服务,而使用“ADC_CONTR = 0x00”来清除ADC_FLAG结束软中断服务。参见例2所示例程。
4.3 定义软中断
为了使软件具有很好的移植性,可以使用宏定义来实现具体应用系统的软中断。在这个宏定义中,应当完成参数的传递以及软中断的申请。例1定义了一个可以携带3个参数的宏,第一个参数是申请的功能号,第二个参数是输入数据的地址,第三个参数是结果输出的地址。利用这三个参数,就可以处理任意类型的数据(参考例2所示的例程)。对于简单的系统,如果软中断提供的服务没有其他的数据需要输入输出,就可只使用一个参数:服务功能号。
例1:软中断的宏定义
#define SWI(a,b,c) \\ FunctionNum=a; \\ InputParameter = (unsigned char *)&b; \\ OutputParameter = (unsigned char *)&c; \\ SWI_IRQ(); \\ _nop_(); 在例1所示的宏中,SWI_IRQ()也是一个宏,用来在宏中自动地选择中断源,而不需
要修改代码。参照例2,一个SWI_IRQ()定义的例子:
//定义软件中断使用的中断号 #define SWI_NUM 1 //使用外部中断0作软中断 #if SWI_NUM == 0 #define SWI_IRQ() IE0 = 1 #define CLEAR_SWI() //IE0 = 0 //使用T0中断作软件中断 #elif SWI_NUM == 1 #define SWI_IRQ() TF0 = 1 #define CLEAR_SWI() TF0 = 0 ?? 对于复杂的系统,可以动态指定软中断服务的具体函数以及数据输入输出的入口地址。参照例3,定义软中断携带两个参数:一个是功能号,一个是函数入口地址,就可在软中断服务中执行动态的代码Func_CallBack,以完成不同类型的任务。
#define SWI(a,b) \\
相关推荐: