软中断在51单片机中的实现及其应用

更新时间:2024-05-08 21:13:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

软中断在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) \\ FunctionNum=a; Func_CallBack = (void *)b; SWI_IRQ(); _nop_(); void SWI_ISR(void) interrupt SWI_NUM {//测试传入的参数 CLEAR_SWI();//清除可能需要清除的标志 Func_CallBack();//执行指定的功能函数 FunctionNum = 0;//清除申请功能号 } \\ \\ \\

5、软中断和硬件中断共享一个中断源的实现的方法

前面所描述的软中断实现方法,是利用系统中不使用的中断源,将其分配给软中断来使用,在大多数情况下这都是适的方法。然而有的应用系统确实已经使用的全部的中断资源,还想要软中断功能,这该怎么办?下面就来讨论这个题目:如何共享硬件中断和软中断。

解决这个问题的关键在于正确区分硬件触发与软件触发的中断,既要保证没有多余的触发,又要保证不漏掉任何一个触发。由于中断标志只有一个,若软中断触发时硬中断也触发,就不能分辨了。仅仅加软件标志通常不能解决这个问题,这时需要寻求其他的实现方法。必要时需要附加硬件来帮助实现软中断功能。 选择共享中断源的依据是能够准确区分硬件中断和软中断。比较容易成功的方法是通过附加硬件措施来改造两个外部中断中的一个。

对于用低电平触发的外部中断申请,如果中断的撤销是由CPU来完成的,则CPU在中断服务程序中就能通过查询外部中断线的电平状态来判断是否同时发生了硬件中断的申请,这时软中断的申请标志和硬中断的申请电平就是两个相互独立的标志,也就能够准确区分硬件中断和软中断了。

具有一般性的做法是将外部中断0或外部中断1的中断申请改为利用一个D触发器来进行电平方式的中断申请,并将中断申请的撤销交给CPU来控制,以便系统能够准确识别每一个中断申请。图1是将边沿触发中断改为电平触发的一种实现方式。图2是将主动撤销信号电平申请改为由CPU控制信号撤销电平申请的一种实现方式。

对于其他的应用于系统之间通讯的通讯端口,不推荐用于共享方式实现软中断。这是因为通讯的数据流可能很大,在没有FIFO缓冲的51兼容单片机中,很有可能造成接收溢出。在51兼容单片机中,串口UART即使在不使用发送中断的情况下,也不推荐应用于软中断服务,因为每一个数据发送完成都会自动置位TI标志,会干扰软中断的执行。

D触发起 中断申请 CLR# (74HC74) Q INT0# CPU (51兼容单片机) P1.0 SET#

图1 边沿触发中断申请改为低电平触发中断申请

INT0# CPU (51兼容单片机) P1.0 中断申请 CLK D D触发起 (74HC74) Q SET#

图2低电平触发中断申请改为CPU撤销信号的低电平触发中断申请

6、软中断在实时操作系统环境下的应用

在基于51单片机的嵌入式系统中,数据采集处理、程序控制和数据通讯是三个主要的应用方面,以下三小节分别描述数据采集处理任务、程序控制任务和数据通讯任务中软中断的使用,以帮助读者加深对软中断的理解。

6.1软中断服务在数据采集处理任务中的应用

实时操作系统下,中断处理占用的时间越短,系统的实时性越好。在一些应用中,数据处理的实时性要求很高,系统的响应时间在0.5毫秒以内。如果数据处理的时间在最坏情况下超过0.3毫秒,系统的软件设计就受到挑战。如果将数据处理全部放入中断中进行处理,系统中其它任务的实时性会受到严重影响。解决的办法通常是以下两种:1)不使用51单片机,改用其它处理速度更快的处理器(如ARM处理器);2)不使用操作系统。这两种解决办法要受到产品成本和开发效率的约束。

软中断的应用可以成为上述问题的第3种解决方法,并且是最优的解决方法。 使用一个最低优先级的中断源来实现软中断,将数据处理放入软中断中完成。这样,软中断就可以随时剥夺任何一个正在运行的任务而优先执行,执行完成后可以回到正在执行的任务,也可以切换到已就绪的更高优先级的任务。如此,系统的实时性就提高了,同时,软件开发效率也提高了。具体的办法是,在数据采集中断中,只完成数据的获取,将数据存放到队列中,然后申请软中断服务后退

出中断服务程序。只要系统中没有其它中断事务等待处理,软中断就可以开始处理,并且在软中断处理过程中可以响应其它的硬件中断,从而保证系统的实时性。由于软中断总是优先于任务执行,所以能够保证数据处理的实时性。当队列中没有数据时,软中断退出,执行其它任务。在这样的机制下,没有频繁的上下文切换,因此CPU的使用效率得到提高,以保证整个系统的实时性。

以下代码片断是一种车辆检测器的代码,使用了软中断来处理数据。可以看出,找外部中断0中将数据放入队列,然后申请软中断。在软中断中,只要队列中还有数据,就循环处理直到队列为空才退出软中断。

?? void int0_int(void) interrupt IE0_VECTOR {//外部中断0 TR0 = 0; //读计数器 NewNode = QMalloc();//申请分配内存 if(NewNode < Q_MAX_LEN) {//内存分配成功 LoopQ[NewNode].LoopChannel = LoopCounter; // TH2,TH1,TH0,TL7,TL6,TL5,TL4,TL3,TL2,TL1,TL0,P2.7,P2.6,P2.5,P2.4,P2.3 // 读取21位计数值拼成2字节,组成16位计数值 LoopQ[NewNode].LoopNv = ((TH0 & 0x03) * 8192) + (TL0 * 32) + ((P2>>3) & 0x1F); LoopQ[NewNode].TimeStamp = 0;//没有测速,不需要时间戳 AddLoopNode(NewNode); SWI_IRQ();//申请软件中断,以便处理数据 } LoopSet();//计算并设置下一个通道 LoopProcTimer = 0; } ?? //软中断服务函数 void SWI_ISR(void) interrupt SWI_NUM {// while(1) { ProcNode = GetLoopNode();//从队列中取待处理的数据 if(ProcNode == 0xFF) break;//队列中没有数据,退出 lopcout = LoopQ[ProcNode].LoopChannel;//取通道号 nv[lopcout] = LoopQ[ProcNode].LoopNv;//取测量数据 QFree(ProcNode);//释放内存以便循环使用 LoopProc();//通道计算 } CLEAR_SWI();//清除可能需要清除的标志 }

6.2 软中断在程序控制任务中的应用

在操作系统中的程序控制任务,其运行受系统内核支配。如果程序控制任务

在逻辑上有严格的时间安排,很可能被高优先级任务剥夺运行而导致逻辑失败。 这种任务如果全部安排在中断中完成处理,会影响系统中其它任务的实时性。这时比较好的方法就是利用软中断来处理时间要求很严格部分。通过软中断,申请在指定时刻执行指定的代码。

以下的代码是程序控制任务计算出来的控制序列,通过申请软中断服务,将待执行的程序控制交给系统,系统在定时器的严格同步下,将非常准确地完成控制过程。代码中,35毫秒后,功能1和功能4将“同时”启动执行,实际上功能1先于功能4执行,时间上相差大约10uS左右。

?? SWI_REQ(20,func1,data1);//申请20ms后执行功能1,其所需的参数在data1 SWI_REQ(25,func2,data2);//申请25ms后执行功能2,其所需的参数在data2 SWI_REQ(26,func3,data3);//申请26ms后执行功能3,其所需的参数在data3 SWI_REQ(35,func1,data4);//申请35ms后执行功能1,其所需的参数在data4 SWI_REQ(35,func4,data5);//申请35ms后执行功能4,其所需的参数在data5 SWI_REQ(36,func3,data6);//申请36ms后执行功能3,其所需的参数在data6 ??

6.3 软中断在数据通讯任务中的应用

在数据通讯中,有些通讯协议对系统的响应时间有严格的要求,两个数据包之间的空闲时间有严格的限制,超过一定时间就认为超时。

这种情况下,数据通讯任务如果全部安排在中断中完成处理,则会影响系统其它任务的实时性,如果放在任务中运行,运行的时间又不可控制。当然可以在任务中关闭中断来处理实时性要求较强的代码,可是这样做会影响其他部分的实时性。

比较好的方法,就是利用软中断,将需要提供的实时服务通过软中断申请将目标代码和数据在不影响系统实时性的条件下优先执行。

首先,在系统中建立一个软中断函数执行的链表,将等待执行代码入口地址和数据入口地址放在这个链表中。一旦内核运行完毕,就自动来检测这个链表中是否存在待执行的软中断代码。如果有软中断代码,立即按照优先顺序执行。当所有的软中断代码执行完成后,就检查是否有高优先级的任务已经就绪等待执行,若有,进行任务调度。当然,申请软中断服务本身就可能引发任务调度。这得看软中断服务完成后,是否有更高优先级任务进入就绪态。

比如,有一个任务在发送完成一个数据包后,有严格的时间要求在20ms后准确启动第二个数据包的发送。如果利用OSWait()或OSTimeDly()的方式,则完全不可能在20ms后准确开始。如果关闭中断等待20ms,系统的效率将十分低下。用软中断的方法是,向系统申请一个软中断服务,将服务要求的启动时刻、提供服务函数入口地址以及数据的存储地址提供给系统,就可以在指定的时刻运行这段代码,完成数据的发送工作。即使当前任务在申请完软中断服务后被剥夺,也能保证指定的数据在20ms后发送出去。

软中断函数执行链表的结构:

typedef struct Swi_List _MALLOC_MEM_ * SwiNode; typedef struct Swi_List{ unsigned int TimeDelay;//延迟多少时间开始执行,等于0表示立即执行 void (*Func)(unsigned char * pInOut);//执行的函数入口地址 unsigned char * pInOut;//执行函数的数据输入输出地址 SwiNode NextNode;//下一个节点的地址 }; 这样的伪代码就是:

?? //准备第一个数据包 Send(Packet1);//发送第一个数据包 //准备第二个数据包 SWI_REQ(20,Send,Pachet2);//申请20ms后发送第二个数据包 ?? 7、无操作系统环境下软中断的应用

在没有操作系统软件环境中,如果要执行的事务比较简单也比较少,用轮询的方法调度各个模块就基本可以满足。然而当事务比较多或事务比较复杂时,实时性的协调就很难。在这种情况下,就可以利用软中断来简化各个模块之间的时间协调,可大大提高系统的实时性和可协调性。

在一个没有操作系统的51单片机应用系统中开发软件,软中断不仅可以用来帮助对临界段的管理,以实现硬件资源的互斥访问以及共享数据的互斥访问,还可以实现无阻塞等待。

还举6.3节的例子,一个模块在发送完成一个数据包后,有严格的时间要求在20ms后准确启动第二个数据包的发送。

用软中断在系统中申请一个20ms的延时定时器,定时器到时执行回调函数Send(),完成第二个数据包的发送。

这样的伪代码就是:

unsigned char Packet_Send(void) { ?? If(CanSend==0)return 1;//发送忙,返回1 If(IsPendInTimer(Send))return 2;//Send函数被挂起在定时器的服务列表中,第二个数据包还没有启动发送,返回2 ?? //准备第一个数据包 Send(Packet1,len1);//发送第一个数据包 //准备第二个数据包 SWI_REQ(20,Send,Pachet2,len2);//申请20ms后发送第二个数据包 return 0;//发送完成,返回0 } 下面一个例子,揭示软中断如何实现硬件资源的互斥访问。

大家都使用过I2C总线器件,比如实时时钟、温度传感器、EEPROM等等。不论单片机自带的硬件I2C控制器还是用软件模拟的I2C控制器,我们都必须保证对22

IC总线操作的完整性,避免多个模块之间同时争用IC总线。在实时操作系统环境下,这个很容易实现,但在没有实时操作系统来管理时,就需要设置信号量来帮助各个模块之间协调使用。我们都知道,I2C总线是低速总线,软件模拟中线控制器时将耗费很多CPU时间,并且一个模块必须完成访问后才能释放访问权限。在没有实时操作系统的软件环境中,CPU将不得不等待。为了提高CPU的利用率,在I2C总线的驱动程序中使用软中断,能够很好地协调各模块之间使用I2C总线访问不同的器件。

软中断服务执行指定的发送或接收函数,并将结果复制到指定的内存地址中,然后还可以执行指定的回调函数(如果有的话)。 由于软中断优于任何模块获得CPU运行,故模块之间提交的软中断独立于模块代码而完整运行(硬件中断不妨碍其正确执行)。由于收发的硬件操作仅在软中断中执行,自然实现了硬件资源的互斥访问。同时,软中断中对共享的变量操作也具有了原子性。参阅例程4所示代码片断。

//模块1: Void M1() { SWI(1,&MySwiNode1);//申请软中断 //退出该模块,软件中断执行完成后,实时时钟的数据自动保存在clock[]中 } //模块2: Void M2() { (省略) SWI(1,&MySwiNode2);//申请软中断 //退出该模块,软件中断执行完成后,温度数据自动保存在temp[]中 } void main(void) { ?? //运行模块1 M1(); //运行模块2 M2(); ?? } 8、结语

在51单片机中,利用硬件中断的中断向量,实现软中断是完全可行的,并且使用软中断能够优化应用软件的执行效率。

例2:一个SWI演示例程(STC12C5AxxS2单片机)

#include #include sbit LED = P1^0; //定义软件中断使用的中断号, 可选0~9 #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 //使用外部中断1作软中断 #elif SWI_NUM == 2 #define SWI_IRQ() IE1 = 1 #define CLEAR_SWI() //IE1 = 0 //使用T1中断作软件中断 #elif SWI_NUM == 3 #define SWI_IRQ() TF1 = 1 #define CLEAR_SWI() TF1 = 0 //使用UART中断作软件中断 #elif SWI_NUM == 4 #define SWI_IRQ() RI = 1 #define CLEAR_SWI() RI = 0 //使用ADC中断作软件中断 #elif SWI_NUM == 5 #define SWI_IRQ() ADC_CONTR = 0x10 #define CLEAR_SWI() ADC_CONTR = 0x00 //使用LVD中断作软件中断 #elif SWI_NUM == 6 #define SWI_IRQ() PCON |= 0x20 #define CLEAR_SWI() PCON &= 0xDF //使用PCA中断作软件中断 #elif SWI_NUM == 7 #define SWI_IRQ() CF = 1 #define CLEAR_SWI() CF = 0 //使用UART2中断作软件中断 #elif SWI_NUM == 8 #define SWI_IRQ() S2CON |= 0x01 #define CLEAR_SWI() S2CON &= 0xFE //使用SPI中断作软件中断 #elif SWI_NUM == 9 #define SWI_IRQ() SPDAT = 0xFF #define CLEAR_SWI() SPSTAT = 0xC0 #endif #ifdef SWI_NUM unsigned char FunctionNum;// unsigned char * InputParameter; unsigned char * OutputParameter; #define SWI(a,b,c) \\ FunctionNum=a; \\ InputParameter = (unsigned char *)&b \\ OutputParameter = (unsigned char *)&c; \\ SWI_IRQ(); \\ _nop_(); #endif void SwiFunction_Init(void) { //将硬件中断源置于高优先级, IP = 0xFF; IP2 = 0xFF; IPH = 0x00; IPH2 = 0x00; //将软中断所使用的中断源置于低优先级 #if SWI_NUM < 8 IP &= ~(0x01<

#include #include void Set_LED0(void); void Set_LED1(void); sbit LED = P1^0; //定义软件中断使用的中断号 省略,同例2 #ifdef SWI_NUM unsigned char FunctionNum; void (*Func_CallBack)(void); #define SWI(a,b) \\ FunctionNum=a; \\ Func_CallBack = (void *)b; \\ SWI_IRQ(); \\ _nop_(); #endif void SwiFunction_Init(void){//省略,代码同例2} void Set_LED0(void){LED = 0;} void Set_LED1(void){LED = 1;} void main(void) { unsigned char SwiFun_Num; int i; P0 = 0xFF; P1 = 0xFF; P2 = 0xFF; P3 = 0xFF; SwiFunction_Init();//初始化软中断功能 SwiFun_Num = 0; while(1) { SwiFun_Num++; //申请软中断服务 if(LED ==1) SWI(SwiFun_Num,Set_LED0); Else SWI(SwiFun_Num,Set_LED1); for(i=0;i<32767;i++) _nop_(); } } #ifdef SWI_NUM void SWI_ISR(void) interrupt SWI_NUM {//软中断服务函数,提供的服务是执行指定的功能函数 CLEAR_SWI();//清除可能需要清除的标志 Func_CallBack();//执行指定的功能函数 FunctionNum = 0;//清除申请功能号 } #endif 例程4:一个SWI演示例程,互斥访问I2C总线(STC12C5AxxS2单片机)

typedef struct Swi_List * SwiNode; typedef struct Swi_List{ unsigned int TimeDelay;//延迟多少时间开始执行,等于0表示立即执行 void (*Func)(unsigned char * pInOut);//执行的函数入口地址 unsigned char * pInOut;//执行函数的数据输入输出地址 SwiNode NextNode;//下一个节点的地址 }; SwiNode SWI_Head_Node;//系统内核使用的指针变量 #ifdef SWI_NUM unsigned char FunctionNum; SwiNode SwiIRQNode; #define SWI(a,b) \\ FunctionNum = a; \\ SwiIRQNode = b; \\ SWI_IRQ(); \\ _nop_(); #endif uint8 IICRead(uint8 *Ret,uint8 Addr,uint8 NByte) {//硬件上操作总线,完成读取功能 (省略) } uint8 IICWrite(uint8 Addr,uint8 *Data,uint8 NByte) {//硬件上操作总线,完成写入功能 (省略) } void PCF8563Read(uint8 *Data) { uint8 NByte; NByte = *Data++; IICWrite(PCF8563Addr,Data,1); IICRead(Data - 1,PCF8563Addr,NByte); } void PCF8563Write(uint8 *Data) { uint8 NByte; NByte = *Data++; NByte++; IICWrite(PCF8563Addr,Data,NByte); } void Get_Temperature(int *RegTemperature) {//读传感器的温度,传感器的地址由*RegTemperature指定 uint8 SensorAddr; uint8 TempReg[2]; SensorAddr = (uint8 *) RegTemperature;//访问的传感器地址 TempReg[0] = SENSOR_REG_TEMPERATURE; 访问传感器的温度值寄存器 IICWrite(WRITE_SENSOR_ADDR0+(SensorAddr&0x07)*2,TempReg,1);//写一个字节 IICRead(Temp, READ_SENSOR_ADDR0+(SensorAddr&0x07)*2,2);//读两个字节 *RegTemperature = (Temp [0]<<8)+ Temp [1];

本文来源:https://www.bwwdw.com/article/84bg.html

Top