文档库 最新最全的文档下载
当前位置:文档库 › 软中断在51单片机中的实现及其应用

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

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

软中断在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 标志,会干扰软中断的执行。

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

图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控制器,我们都必须保证对I2C总线操作的完整性,避免多个模块之间同时争用I2C总线。在实时操作系统环境下,这个很容易实现,但在没有实时操作系统来管理时,就需要设置信号量来帮助各个模块之间协调使用。我们都知道,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<

#elif SWI_NUM < 10

IP2 &= ~(0x01<<(SWI_NUM-8));

#endif

//使用外部中断0作软中断

#if SWI_NUM == 0

IT0 =1;//边沿触发

EX0 =1;

//使用T0中断作软件中断

#elif SWI_NUM == 1

ET0 =1;

//使用外部中断1作软中断

#elif SWI_NUM == 2

IT1 =1;//边沿触发

EX1 =1;

//使用T1中断作软件中断

#elif SWI_NUM == 3

ET1 =1;

//使用UART中断作软件中断

#elif SWI_NUM == 4

ES =1;

//使用ADC中断作软件中断

#elif SWI_NUM == 5

EADC =1;

//使用LVD中断作软件中断

#elif SWI_NUM == 6

ELVD =1;

//使用PCA中断作软件中断

#elif SWI_NUM == 7

CMOD |= 0x01;//ECF=1

//使用UART2中断作软件中断

#elif SWI_NUM == 8

IE2 |=0x01;//ES2=1

//使用SPI中断作软件中断

#elif SWI_NUM == 9

AUXR1 |= 0x20;//SPI切换到P4口

SPCTL = 0xD0;//MASTER模式

IE2 |=0x02;//ESPI=1

#endif

EA = 1;

}

void main(void)

{

unsigned char SwiFun_Num;

int DataIn,result,i;

P0 = 0xFF;

P1 = 0xFF;

P2 = 0xFF;

P3 = 0xFF;

SwiFunction_Init();//初始化软中断功能

DataIn = 0;

result = 0;

SwiFun_Num = 0;

while(1)

{

SwiFun_Num++;

DataIn++;

SWI(SwiFun_Num,DataIn,result); //申请软中断服务

if(result != (DataIn + SwiFun_Num)) {for(;;) _nop_();}//错,停在这里

for(i=0;i<32767;i++) _nop_();

}

}

#ifdef SWI_NUM

void SWI_ISR(void) interrupt SWI_NUM

{//软中断服务函数提供的功能是输出=输入+申请功能号

LED ^= 1;//便于观察是否进入软中断服务

CLEAR_SWI();//清除可能需要清除的标志

*(int *) OutputParameter = *(int *)InputParameter + FunctionNum;

FunctionNum = 0;//清除申请功能号

}

#endif

例3:一个具有动态代码执行能力的SWI演示例程(STC12C5AxxS2单片机)#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];

}

void SWI_ISR(void) interrupt SWI_NUM

{//软中断服务函数提供的服务是执行链表中的函数

if(FunctionNum == 1)

{//功能1:增加软中断服务,将软中断申请放到队列中等待执行

if(SWI_Head_Node == 0)

{

//申请内存

SWI_Head_Node = ( SwiNode )malloc(sizeof(Swi_List_Struct));

if(SWI_Head_Node != 0)

{

SWI_Head_Node->TimeDelay = SwiIRQNode->TimeDelay;

SWI_Head_Node->Func = SwiIRQNode->Func;

SWI_Head_Node->pInOut = SwiIRQNode->pInOut;

SWI_Head_Node->NextNode = 0;

}

}

else

{

(省略)

}

}

//寻找是否有就绪的软中断任务需要执行

pNode = SWI_Head_Node;

pRightNode = SWI_Head_Node;

for(;;)

{

if(pNode != 0)

{

if((pNode->TimeDelay)==0)

{//发现一个等待执行的软中断

if(pNode == SWI_Head_Node)

{//是第一个节点

if((pNode->NextNode) != 0)

{//还有下一个节点

//撤销节点

SWI_Head_Node = pNode->NextNode;

pRightNode = SWI_Head_Node;

//执行函数

pNode->Func(pNode->pInOut);

//释放内存

free(pNode);

pNode = pRightNode;//指向下一个节点

}

else

{//没有下一个节点

(省略)

}

}

else

{//不是第一个节点

(省略)

}

}

else

{//移动指针

pRightNode = pNode;

pNode = pNode->NextNode;

}

}

else break;

}

FunctionNum = 0;//清除申请功能号

CLEAR_SWI();//清除可能需要清除的标志

}

//模块1:

Void M1()

{

Swi_List_Struct MySwiNode1;//

static uint8 clock[8]={0,0,0,0,0,0,0,0};//用于读写PCF8563实时时钟//根据温度执行相应的操作

(省略)

……

//读取PCF8563的时间数据

MySwiNode1.Func = PCF8563Read;//执行的函数是PCF8563Read

MySwiNode1.pInOut = clock;//输入输出的参数clock[8]

clock [0] = 7;//读7个字节

clock [1] = 2;//从PCF寄存器2开始读

MySwiNode1.TimeDelay = 0;//执行功能的延迟时间为0,要求立即开始执行SWI(1,&MySwiNode1);//申请软中断

//退出该模块,软件中断执行完成后,实时时钟的数据自动保存在clock[]中}

//模块2:

Void M2()

{

Swi_List_Struct MySwiNode2;//

static uint8 temp[2]={0,0};//用于读写AD7416温度传感器

int * T;

T = (int *) temp;//指向存储温度的内存

//根据温度执行相应的操作

(省略)

……

//读取AD7416的温度数据

MySwiNode2.Func = Get_Temperature;//执行的函数是Get_Temperature MySwiNode2.pInOut = temp;//输入输出的参数temp[4]

temp[0] = 0;//读地址编号为0个温度传感器的温度

MySwiNode2.TimeDelay = 0;//执行功能的延迟时间为0,要求立即开始执行SWI(1,&MySwiNode2);//申请软中断

//退出该模块,软件中断执行完成后,温度数据自动保存在temp[]中

}

void main(void)

{

……

//运行模块1

M1();

//运行模块2

M2();

……

}

附1:对51单片机外部中段标志IE0的描述

INTEL80C51的描述:Set by hardware when external interrupt edge detected. Cleared when interrupt processed.

MSC12xx/MSC13xx的描述:This bit is set when an edge/level of the type defined by IT0 is detected. If IT0 = 1, this bit will remain set until cleared in software or the start of the External Interrupt 0 service routine. If IT0 = 0, this bit will inversely reflect the state of the INT0 pin.

ADuC812的描述:Set by hardware by a falling edge or zero level being applied to external interrupt pin INT0, depending on bit IT0 state.

C8051F120的描述:This flag is set by hardware when an edge/level of type defined by IT0 is detected. It can be cleared by software but is automatically cleared when the CPU vectors to the External Interrupt 0 service routine if IT0 = 1. This flag is the inverse of the /INT0 signal if IT0 = 0.

DS89C420的描述:If the interrupt is edge triggered, cleared automatically by hardware when the service routine is vectored to. If the interrupt is level triggered, the flag follows the state of the pin.

附2:几个公司的51单片机中断系统资料

ATMEL公司的AT89x51单片机的中断系统,具有2个优先级,5个中断向量。AT89x52单片机的中断系统,具有2个优先级,6个中断向量。

ADI公司的51单片机,应用也比较广泛,比如ADuC8xx系列单片机。ADuC8xx单片机内部有多个外设,包括T0、T1、T2、ADC、PSM(电源监视)、I2C以及SPI等等。ADuC812单片机,具有10个中断源,2个优先级,9个中断向量。

TI公司的51单片机,尤其是MSC12xx系列单片机,有很多个中断源。在MSC1213单片机的中断系统中,有3个优先级,有21个中断源,13个中断向量。

SILICON LABS公司的单片机,比如C8051F12x/C8051F13x的中断系统,有20个中断源,2个优先级,20个中断向量。该公司的单片机中断系统最大的特点就是明确指出,任何一个中断挂起标志都可以软件置位,以模拟一个中断事件。因此在该公司的单片机上实现软中断比较容易。

PHILIPS公司的单片机,比如P87C51,有6个中断源,4个优先级,5个中断向量。

DALLAS公司的单片机,比如DS89C420,有13个中断源,5个优先级,13个中断向量。

STC公司的51单片机,比如STC12C5AxxS2系列单片机,具有13个中断源,4个优先级,10个中断向量。

单片机中断程序大全

单片机中断程序大全公司内部编号:(GOOD-TMMT-MMUT-UUPTY-UUYY-DTTI-

//实例42:用定时器T0查询方式P2口8位控制L E D闪烁#include // 包含51单片机寄存器定义的头文件void main(void) { // EA=1; //开总中断 // ET0=1; //定时器T0中断允许 TMOD=0x01; //使用定时器T0的模式1 TH0=(65536-46083)/256; //定时器T0的高8位赋初值 TL0=(65536-46083)%256; //定时器T0的高8位赋初值 TR0=1; //启动定时器T0 TF0=0; P2=0xff; while(1)//无限循环等待查询 { while(TF0==0) ; TF0=0; P2=~P2; TH0=(65536-46083)/256; //定时器T0的高8位赋初值 TL0=(65536-46083)%256; //定时器T0的高8位赋初值 //实例43:用定时器T1查询方式控制单片机发出1KHz音频

#include // 包含51单片机寄存器定义的头文件sbit sound=P3^7; //将sound位定义为P3.7引脚 void main(void) {// EA=1; //开总中断 // ET0=1; //定时器T0中断允许 TMOD=0x10; //使用定时器T1的模式1 TH1=(65536-921)/256; //定时器T1的高8位赋初值 TL1=(65536-921)%256; //定时器T1的高8位赋初值 TR1=1; //启动定时器T1 TF1=0; while(1)//无限循环等待查询 { while(TF1==0); TF1=0; sound=~sound; //将P3.7引脚输出电平取反 TH1=(65536-921)/256; //定时器T0的高8位赋初值 TL1=(65536-921)%256; //定时器T0的高8位赋初值 } } //实例44:将计数器T0计数的结果送P1口8位LED显示 #include // 包含51单片机寄存器定义的头文件sbit S=P3^4; //将S位定义为P3.4引脚

关于51单片机外部中断响应

关于51单片机外部中断响应 外部中断方式最好设为下降沿方式,特别是中断引 脚接按键的情况。 外部下降沿中断:SETB IT0。每个机器周期都由硬 件对引脚自动采样,若连续在2个周期采样到电平从高 到低,则认定有中断请求,IE0=1。IE0会一直保持到该 中断请求被CPU响应,响应前都不会自动清零,只有在 响应后硬件才自动将IE0清零IE0=0。 外部低电平中断:CLR IT0。当中断引脚为低电平时,并保持一个机器周期,硬件自动置IE0=1。如果在下一个 周期采样到中断引脚为高电平时,硬件自动将IE0清0。中断标志位自动清0条件:下降沿中断只有CPU响应中断同时才会清0,否则一直保持。低电平中断:任何时候 当外部中断引脚为低电平时,IE0=1;为高电平时, IE0=0,所以不需要响应中断才会清0,与引脚状态有关。注意:当EA=0时,中断引脚为低电平也不会将IE0自动置1,只有EA=1时才会自动置1 单片机设计中有两个CPU时,主CPU控制副CPU中断时应注意:主CPU发出中断信号的时候,副CPU能够及时接收到,也就是副CPU工作状态不允许在关中断CLR EA的程序中运行。只要副CPU不工作在关中断的程序中运行,

主CPU发出的中断信号副CPU都能够及时响应中断。还有就是如果采用下降沿方式,主CPU发出的高低电平之间间隔时间只需一条NOP指令。所以应该尽可能考虑这个时差问题。有时候就是副CPU还没有运行完屏蔽中断的程序的时候,主CPU就发出了中断信号,造成副CPU无法中断或时好时坏。 键盘中断到今天为止终于可以告一段落了。 现在才知道,程序架构有了并不代表程序就容易完成,更多的时间在于调试,防真,再调试,如此循环。所以遇到问题要有耐心,信心,细心。做到这三点,不行也得行!!!!

51单片机中断程序大全

//实例42:用定时器T0查询方式P2口8位控制LED闪烁#include // 包含51单片机寄存器定义的头文件 /************************************************************** 函数功能:主函数 **************************************************************/ void main(void) { // EA=1; //开总中断 // ET0=1; //定时器T0中断允许 TMOD=0x01; //使用定时器T0的模式1 TH0=(65536-46083)/256; //定时器T0的高8位赋初值 TL0=(65536-46083)%256; //定时器T0的高8位赋初值 TR0=1; //启动定时器T0 TF0=0; P2=0xff; while(1)//无限循环等待查询 { while(TF0==0) ; TF0=0; P2=~P2; TH0=(65536-46083)/256; //定时器T0的高8位赋初值 TL0=(65536-46083)%256; //定时器T0的高8位赋初值 } } //实例43:用定时器T1查询方式控制单片机发出1KHz音频#include // 包含51单片机寄存器定义的头文件 sbit sound=P3^7; //将sound位定义为P3.7引脚 /************************************************************** 函数功能:主函数 **************************************************************/ void main(void) { // EA=1; //开总中断 // ET0=1; //定时器T0中断允许 TMOD=0x10; //使用定时器T1的模式1 TH1=(65536-921)/256; //定时器T1的高8位赋初值

51单片机中断系统编程

51单片机中断系统编程 51单片机中断系统编程 上传的图片 抱歉,您所在的组无权下载附件,请注册或登陆中断是指如下过程(如下图所示):CPU 与外设同时工作,CPU执行主程序,外设做准备工作。当外设准备好时向CPU发中断请求信 号,若条件满足,则CPU终止主程序的执行,转去执行中断服务程序。在中断服务程序中 CPU与外设交换信息,待中断服务程序执行完后,CPU再返回刚才终止的主程序继续执行。 5.3.1 中断系统 MCS-51单片机提供了5个固定的可屏蔽中断源,3个在片内,2个在片外,它们在程序存储 器中各有固定的中断入口地址,由此进入中断服务程序。5个中断源的符号、名称及产生 的条件如下。 ? INT0:外部中断0,由P3.2端口线引入,低电平或下跳沿引起。 ? INT1:外部中断1,由P3.3端口线引入,低电平或下跳沿引起。 ? T0:定时器/计数器0中断,由T0计数溢出引起。 ? T1:定时器/计数器l中断,由T1计数溢出引起。 ? TI/RI:串行I/O中断,串行端口完成一帧字符发送/接收后引起。 中断源有两级中断优先级,可形成中断嵌套。两个特殊功能寄存器用于中断控制和条件设 置。整个中断系统的结构框图如图所示。 上传的图片 抱歉,您所在的组无权下载附件,请注册或登陆 中断系统结构框图 2 中断系统的控制寄存器 中断系统有两个控制寄存器(IE和IP),它们分别用来设定各个中断源的打开/关闭和中

断优先级。此外,在TCON中另有4位用于选择引起外部中断的条件并作为标志位。 (1)中断允许寄存器IE IE在特殊功能寄存器中,字节地址为A8H,位地址(由低位到高位)分别是A8H-AFH。IE 用 来打开或关断各中断源的中断请求,基本格式如下: 上传的图片 抱歉,您所在的组无权下载附件,请注册或登陆 ? EA:全局中断允许位。EA=0,禁止一切中断;EA=1,打开全局中断控制,此时,由各 个中断控制位确定相应中断的打开或关闭。 ? ×:无效位。 ? ES:串行I/O中断允许位。ES=1,允许串行I/O中断;ES=0,禁止串行I/O中断。 ? ETl;定时器/计数器T1中断允许位。ETl=1,允许T1中断;ETl=0,禁止T1中断。 ? EXl:外部中断l中断允许位。EXl=1,允许外部中断1中断;EXl=0,禁止外部中断1中 断。 ? ET0:定时器/计数器T0中断允许位。ET0=1,允许T0中断;ET0=0,禁止TO中断。 ? EX0:外部中断0中断允许位。EX0=1,允许外部中断0中断;EX0=0,禁止外部中断0中 断。 (2)中断优先级寄存器IP IP在特殊功能寄存器中,字节地址为B8H,位地址(由低位到高位)分别是B8H一BFH。 MCS-51单片机的中断分为两个优先级,IP用来设定各个中断源属于两级中断中的哪一级, 其基本格式如下: 上传的图片

基于51单片机的外部中断实例

51单片机一般有两个外部中断输入端,并允许外部中断源以低电平或负边沿两种触发方式输入中断请求信号。本例就是利用一只按钮,在按下时产生的负边沿触发外部中断。 1 硬件设计 将一只按钮接在外部中断输入0(12脚),八支发光二极管分别接在P0.0~P0.7,其电路如下图所示。 ⒉软件设计 通过按下按钮SW触发外部中断,从而改变发光二极管D1~D8的亮、灭,当第一次按下按钮时,只有D1发光二极管亮;第二次按下按钮时,只有D2发光二极管亮;第三次按下按钮时,只有D3发光二极管亮;……第八次按下按钮时,只有D8发光二极管亮;第九次按下按钮时,D1~D8发光二极管全亮;第十次按下按钮时,D1~D8发光二极管全都不亮;第十一次按下按钮时,只有D1发光二极管亮; 按钮SW触发外部中断,从而控制D1~D8亮灭的详细C51程序如下。 /***************外部中断****************/ #include unsigned char count=0; //外部中断计数 unsigned char F0=0; main(){ F0=0;

IE=0X81;//打开外部中断0和总中断使能或者(EX0=1;EA=1) IT0=1; //标志位清零,开中断 ,边沿激活(或者TCON|=0X01) while(1) { while(F0==0); switch(count%10){ case(0): P0=0XFF;break; case(1): P0=0XFE;break; case(2): P0=0XFD;break; case(3): P0=0XFB;break; case(4): P0=0XF7;break; case(5): P0=0XEF;break; case(6): P0=0XDF;break; case(7): P0=0XBF;break;case(8): P0=0X7F;break; case(9): P0=0X00;break; } F0=0; } } void int_int0() interrupt 0 //外部中断 { count++;F0=1; }

51单片机定时中断C语言的写法步骤

51单片机定时中断C语言的写法步骤 程序说明:51单片机定时器0工作于方式一,定时50ms中断一次 晶振为12M #include void main { TOMD = 0X01;//配置定时器0工作于方式一 TH1 = (65536-50000)/256; //高八位装入初值 TL1 = (65536-50000)%256; //低八位装入初值 ET0 = 1; //开定时器0中断 EA = 1; //开总中断 TR0 = 1; //启动定时器0 while(1) { ; } } void Timer0_int() interrupt 1 { //重新装初值 TH1 = (65536-50000)/256; //高八位装入初值 TL1 = (65536-50000)%256; //低八位装入初值 } /****************************************************************************** *********************************/ 上面是比较好理解的。如果实在要求简洁的话,看下面的,跟上面功能一样 #include void main { TOMD = 0X01;//配置定时器0工作于方式一 TH1 = 0x3c; //高八位装入初值 TL1 = 0xb0; //低八位装入初值 IE = 0x82;//开总中断并开定时器0中断 TR0 = 1; //启动定时器0 while(1) { ; } }

void Timer0_int() interrupt 1 { //重新装初值 TH1 = 0x3c; //高八位装入初值TL1 = 0xb0; //低八位装入初值}

51单片机中断总结

51单片机中断总结: 1. 查询优先级为固定的(外部中断0>定时器0>外部中断1>定时器1>串行中断)。 2. 执行优先级可以通过IP寄存器进行设置(高/低)。 3. CPU同时收到多个中断请求时,首先响应优先级较高者,然后相应优先级较低者;如果 优先级相同,则按照查询优先级顺序依次响应。 4. 正在执行的中断服务,不能被同级或更低级的中断请求打断,但会被更高级的中断请求 打断。推论(1)高优先级的中断不能被任何其它中断所打断(2)低优先级的中断只能在没有任何中断服务运行时得到响应。 5. 对于定时器和外部中断,在进入中断服务后,其中断标志位会自动清零;对于串行中断,由于有两个中断源,需要手动查询并清零RI或/和TI。 if (RI) { // processing RI = 0; } if (TI) { // processing TI = 0; } 6. 如果是使用汇编写中断服务,需要保护累加器、状态寄存器、寄存器组等 8051 Tutorial: Interrupts https://www.wendangku.net/doc/7417209397.html,/tutint.php As the name implies, an interrupt is some event which interrupts normal program execution. As stated earlier, program flow is always sequential, being altered only by those instructions which expressly cause program flow to deviate in some way. However, interrupts give us a mechanism to "put on hold" the normal program flow, execute a subroutine, and then resume normal program flow as if we had never left it. This subroutine, called an interrupt handler, is only executed when a certain event (interrupt) occurs. The event may be one of the timers "overflowing," receiving a character via the serial port, transmitting a character via the serial

51单片机中断详解

一、中断的概念 CPU在处理某一事件A时,发生了另一事件B请求C PU迅速去处理(中断发生); CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务); 待C PU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断二、中断源 在51单片机中有5个中断源 中断号优先级中断源中断入口地址 0 1(最高)外部中断0 0003H 1 2 定时器0 000BH 2 3 外部中断1 0013H 3 4 定时器1 0018H 4 5 串口总段0023H 三、中断寄存器 单片机有10个寄存器主要与中断程序的书写控制有关 1.中断允许控制寄存器IE 2.定时器控制寄存器TC ON 3.串口控制寄存器SCON 4.中断优先控制寄存器IP 5.定时器工作方式控制寄存器TMOD 6.定时器初值赋予寄存器(TH0/TH1,TL0/TL1)

四、寄存器功能与赋值说明 注:在用到中断时,必须要开总中断EA,即EA=1。//开总中断 1.中断允许控制寄存器IE EX0(EX1):外部中断允许控制位 EX0=1 外部中断0开关闭合//开外部0中断 EX0=0 外部中断0开关断开 ET0(ET1):定时中断允许控制位 ET0=1 定时器中断0开关闭合//开内部中断0 ET0=0 定时器中断0开关断开 ES: 串口中断允许控制位 ES=1 串口中断开关闭合//开串口中断 ES=0 串口中断开关断开 2.定时器控制寄存器TCON //控制外部中断和定时器中断 外部中断: IE0(IE1):外部中断请求标志位 当INT0(INT1)引脚出现有效的请求信号,此位由单片机自动置1,cpu开始响应,处理终端,而当入

51单片机外部中断实验

实验6 外部中断实验 (仿真部分) 一、实验目的 1. 学习外部中断技术的基本使用方法。 2. 学习中断处理程序的编程方法。 二、实验内容 在INT0和INT1上分别接了两个可回复式按钮,其中INT0上的按钮每按下一次则计数加一,其中INT1上的按钮每按下一次则计数减一。P1.0~ P1.3接LED灯,以显示计数信号。 三、实验说明 编写中断处理程序需要注意的问题是: 1.保护进入中断时的状态,并在退出中断之前恢复进入时的状态。 2.必须在中断处理程序中设定是否允许中断重入,即设置EX0位。 3.INT0和INT1分别接单次脉冲发生器。P1.0~ P1.3接LED灯,以查看计数信号. 四、硬件设计 利用以下元件:AT89C51、BOTTON、CAP、CAP-POL、CRYSTAL、RES、NOT、LED-Yellow。设计出如下的硬件电路。晶振频率为12MHz。 五、参考程序框图 开始 设置有关中断控制寄存器 开外中断INT0、INT1 设置P1.0~ 3初始状态 显示循环等待中断 INT0中断入口 计数加一 保护现场 恢复现场 中断返回

主程序框图INT0中断处理程序框图 实验6 外部中断实验 (实验箱部分) 1.实验目的 认识中断的基本概念 学会外部中断的基本用法 学会asm和C51的中断编程方法 2.实验原理 图按键中断 【硬件接法】 P1.1控制LED,低电平点亮 P3.3/INT1接按键,按下时产生低电平 【运行效果】 程序工作于中断方式,按下按键K2后,LED点亮,1.5秒后自动熄灭。 8051单片机有/INT0和/INT1两条外部中断请求输入线,用于输入两个外部中断源的中断请求信号,并允许外部中断源以低电平或下降沿触发方式来输入中断请求信号。/INT0和/INT1中断的入口地址分别是0003H和0013H。 TCON寄存器(SFR地址:88H)中的IT0和IT1位分别决定/INT0和/INT1的触发方式,置位时为下降沿触发,清零时为低电平触发。实际应用时,如果外部的中断请求信号在产生后能够在较短时间内自动撤销,则可以选择低电平触发。在中断服务程序里要等待其变高后才能返回主程序,否则会再次触发中断,产生不必要的麻烦。 如果外部的中断请求信号产生后可能长时间后才能撤销,则为了避免在中断服务程序里长时间无谓等待,可以选择下降沿触发。下降沿触发是“一次性”的,每次中断只会有1个下降沿,因此中断处理程序执行完后可以立即返回主程序,而不必等待中断请求信号恢复为高电平,这是一个重要的技巧。 3. 实验步骤 ●参考实验例程,自己动手建立Keil C51工程。注意选择CPU类型。Philips半导体的 P89V51RB2。 ●编辑源程序,编译生成HEX文件。 ●ISP下载开关扳到“00”,用Flash Magic软件下载程序HEX文件到MCU BANK1,运行。 运行Flash Magic软件。各步骤操作如下:

51单片机中断系统程序实例

51单片机中断系统程序实例(STC89C52RC) 51单片机有了中断,在程序设计中就可以做到,在做某件事的过程中,停下来先去响应中断,做别的事情,做好别的事情再继续原来的事情。中断优先级是可以给要做的事情排序。 单片机的学习不难,只要掌握学习方法,学起来并不难。什么是好的学习方法呢,一定要掌握二个要点: 1. 要知道寄存器的英文全拼,比如IE = interrupt中断 不知道全拼,要去猜,去查。这样就可以理解为什么是这个名称,理解了以后就不用记忆了。 2. 每个知识点要有形像的出处 比如看到TF0,脑子里马上要形像地定位到TCON寄存器的某位 看到ET0, 马上要形像地定位到IE寄存器的第2位 https://www.wendangku.net/doc/7417209397.html,/tuenhai/独家揭秘:形像是记忆的最大技巧。当人眼看到某个图时,是把视觉信号转化成电信号,再转化成人能理解的形像。当我们回忆形像时,就是在重新检索原先那个视觉信号,并放大。在学习过程中,不断练习检索、放大信号,我们的学习能力就会越来越强。 写程序代码时,也要把尽量把每行代码形像化。 51单片机内中断源 8051有五个中断源,有两个优先级。与中断系统有关的特殊功能寄存器有IE(中断允许寄存器)、IP(中断优先级控制寄存器)、中断源控制寄存器(如TCON、SCON的有关位)。51单片机的中断系统结构如下图(注意,IF0应为TF0):

8052有6个中断源,它比8051多一个定时器/计数器T2中断源。 8051五个中断源分别是: (1)51单片机外部中断源 8051有两个外部中断源,分别是INT0和INT1,分别从P3.2和P3.3两个引脚引入中断请求信号,两个中断源的中断触发允许由TCON的低4位控制,TCON的高4位控制运行和溢出标志。 INT0也就是Interrupt 0。在这里应该看一下你的51单片机开发板的电路原理图。离开形像的记忆是没有意义的。读到上面这句,你应该回忆起原理图上的连接。任何记忆都转化为形像,这是学习的根本原理,我们通过学习单片机要学会这种学习方法,会让你一辈子受益无穷。 TCON的结构如下图: (a)定时器T0的运行控制位TR0

51单片机中断详解

一.中断的概念 1.中断发生 CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理 2.中断响应和中断服务 CPU暂时中断当前的工作,转去处理事件B 3.中断返回 待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A 这一过程称为中断 二.中断过程示意图 三.MCS51中断系统的结构

MCS51的中断系统有5个中断源(8052有6个),2个优先级,可实现二级中断嵌套 四.中断寄存器 单片机有10个寄存器主要与中断程序的书写控制有关 1.中断允许控制寄存器IE 2.定时器控制寄存器TCON 3.串口控制寄存器SCON 4.中断优先控制寄存器IP 5.定时器工作方式控制寄存器TMOD 6.定时器初值赋予寄存器(TH0/TH1,TL0/TL1) 五.部分寄存器详解

1.中断允许控制寄存器(IE) EX0:外部中断0允许位; ET0:定时/计数器T0中断允许位; EX1:外部中断1允许位; ET1:定时/计数器T1中断允许位; ES :串行口中断允许位; EA :CPU中断允许(总允许)位。 2.定时器/计数器控制寄存器控制寄存器(TCON) IT0:外部中断0触发方式控制位 当IT0=0时,为电平触发方式(低电平有效) 当IT0=1时,为边沿触发方式(下降沿有效) IE0:外部中断0中断请求标志位 IT1:外部中断1触发方式控制位 IE1:外部中断1中断请求标志位

TF0:定时/计数器T0溢出中断请求标志位 TF1:定时/计数器T1溢出中断请求标志位 3.串行口控制寄存器(SCON) RI:串行口接收中断标志位。当允许串行口接收数据时,每接收完一个串行帧,由硬件置位RI。注意,RI必须由软件清除。 TI:串行口发送中断标志位。当CPU将一个发送数据写入串行口发送缓冲器时,就启动了发送过程。每发送完一个串行帧,由硬件置位TI。CPU响应中断时,不能自动清除TI,TI必须由软件清除。 4.中断优先级控制寄存器(IP) PX0:外部中断0优先级设定位 PT0:定时/计数器T0优先级设定位 PX1:外部中断0优先级设定位 PT1:定时/计数器T1优先级设定位

51单片机中断程序大全

//实例42 :用定时器TO查询方式P2 口8位控制LED闪烁#include // 包含 51 单片机寄存器定义的头文件/************************************************************** 函数功能:主函数 void main(void) { // EA=1; // 开总中断 // ETO=1; // 定时器 TO 中断允许 TMOD=OxO1; // 使用定时器 TO 的模式 1 THO=(65536-46O83)/256; // 定时器 TO 的高 8 位赋初值 TLO=(65536-46O83)%256; // 定时器 TO 的高 8 位赋初值 TRO=1; // 启动定时器 TO TFO=O; P2=Oxff; while(1)// 无限循环等待查询 { while(TFO==O) TFO=O; P2=~P2; THO=(65536-46O83)/256; // 定时器 TO 的高 8 位赋初值

TL0=(65536-46083)%256; // 定时器 T0 的高 8 位赋初值 } // 实例43 :用定时器T1 查询方式控制单片机发出1KHz 音频#include // 包含 51 单片机寄存器定义的头文件 sbit sou nd=P3^7; // 将 sound 位定义为 P3.7 引脚 /************************************************************** 函数功能:主函数 **************************************************************/ void main(void) { // EA=1; // 开总中断 // ET0=1; // 定时器 T0 中断允许 TMOD=0x10; // 使用定时器 T1 的模式 1 TH1=(65536-921)/256; // 定时器 T1 的高 8 位赋初值 TL1=(65536-921)%256; // 定时器 T1 的高 8 位赋初值 TR1=1; // 启动定时器 T1 TF1=0; while(1)// 无限循环等待查询 {

51单片机汇编语言教程:18课单片机中断系统

51单片机汇编语言教程:第18课-单片机中断系统

MCS-51单片机中断系统的结构: 5个中断源的符号、名称及产生的条件如下。 INT0:外部中断0,由P3.2端口线引入,低电平或下跳沿引起。 INT1:外部中断1,由P3.3端口线引入,低电平或下跳沿引起。 T0:定时器/计数器0中断,由T0计满回零引起。 T1:定时器/计数器l中断,由T1计满回零引起。 TI/RI:串行I/O中断,串行端口完成一帧字符发送/接收后引起。整个中断系统的结构框图见下图一所示。

<51单片机中断系统结构> 如图所示,由与中断有关的特殊功能寄存器、中断入口、次序查询逻辑电路等组成,包括5个中断请求源,4个用于中断控制的寄存器IE、IP、ECON和SCON来控制中断类弄、中断的开、关和各种中断源的优先级确定。 中断请求源: (1)外部中断请求源:即外中断0和1,经由外部管脚引入的,在单片机上有两个管脚,名称为INT0、INT1,也就是P3.2、P3.3这两个管脚。在内部的TCON中有四位是与外中断有关的。IT0:INT0触发方式控制位,可由软件进和置位和复位,IT0=0,INT0为低电平触发方式,IT0=1,INT0为负跳变触发方式。这两种方式的差异将在以后再谈。IE0:INT0中断请求标志位。当有外部的中断请求时,这位就会置1(这由硬件来完成),在CPU响应中断后,由硬件将IE0清0。IT1、IE1的用途和IT0、IE0相同。(2)内部中断请求源TF0:定时器T0的溢出中断标记,当T0计数产生溢出时,由硬件置位TF0。当CPU响应中断后,再由硬件将TF0清0。TF1:与TF0类似。TI、RI:串行口发送、接收中断,在串行口中再讲解。2、中断允许寄存器IE在MCS-51中断系统中,中断的允许或禁止是由片内可进行位寻址的8位中断允许寄存器IE来控制的。见下表EAX 其中EA是总开关,如果它等于0,则所有中断都不允许。ES-串行口中断允许ET1-定时器1中断允许EX1-外中断1中断允许。ET0-定时器0中断允许EX0-外中断0中断允许。如果我们要设置允许外中断1,定时器1中断允许,其它不允许,则IE能是EAX 即8CH,当然,我们也能用位操作指令SETB EA SETB ET1SETB EX1 来实现它。3、五个中断源的自然优先级与中断服务入口地址外中断0:0003H定时器0:000BH 外中断1:0013H定时器1:001BH串行口:0023H它们的自然优先级由高到低排列。写到这里,大家应当明白,为什么前面有一些程序一始我们这样写: ORG0000HLJMP START ORG0030H START:。 这样写的目的,就是为了让出中断源所占用的向量地址。当然,在程序中没用中断时,直接从0000H开始写程序,在原理上并没有错,但在实际工作中最好不这样做。优先级:单片机采用了自然优先级和人工设置高、低优先级的策略,即能由程序员设定那些中断是高优先级、

51单片机外部中断详解

一.外部中断相关寄存器 1.定时器/计数器控制寄存器控制寄存器(TCON) IT0:外部中断0触发方式控制位 当IT0=0时,为电平触发方式(低电平有效) 当IT0=1时,为边沿触发方式(下降沿有效) IT1:外部中断1触发方式控制位 当IT1=0时,为电平触发方式(低电平有效) 当IT1=1时,为边沿触发方式(下降沿有效) 2.中断允许控制寄存器(IE) EX0:外部中断0允许位; EX1:外部中断1允许位; EA :CPU中断允许(总允许)位。 二.外部中断的处理过程 1、设置中断触发方式,即IT0=1或0,IT1=1或0

2、开对应的外部中断,即EX0=1或EX1=1; 3、开总中断,即EA=1; 4、等待外部设备产生中断请求,即通过,口连接外部设备产生中断 5、中断响应,执行中断服务函数 三.程序编写 要求:通过两位按键连接外部中断0和1,设定外部中断0为下降沿触发方式,外部中断1为低电平触发方式,按键产生中断使数字加减,用一位共阳极数码管来显示数值。 目的:感受外部中断对程序的影响,体会低电平触发和下降沿触发的区别。 #include<>#define uint unsigned int #define uchar unsigned char uchar code dat[]={0xc0, 0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};uint num; void main() { ! EA=1; //开总中断 IT0=1; //下降沿触发 IT1=0; //低电平触发 EX0=1; //外部中断0允许 EX1=1; //外部中断1允许 while(1) { P0=dat[num%10]; } } { void plus() interrupt 0//外部中断0

用单片机的外部中断来测频率

用单片机的外部中断来测频率 用51单片机的外部中断来测频率 作者:佚名来源:本站原创点击数: 302 更新时间:2010年08月08日【字体:大中小】 //晶振:12M //实验方法:首先要把51hei单片机开发板上的ne555的方波输出信号J7的第一脚用杜邦线 // 引入P3.2口,数码管即可显示ne555震荡电路当前的频率值,旋动PR1电位器可 // 发现数字有变化. //原理:1秒钟内计数外部脉冲个数,如计数1000次,则表示频率为1000Hz //1秒钟定时采用51单片机定时器0实现,外部脉冲由外部中断0引脚接入 #include<reg51.h> #include"51hei.h" #define uchar unsigned char uchar counter1; sbit duan=P2^6; //74HC573的LE端U5 LED的段选端 sbit wei=P2^7; //74HC573的LE端U4 LED的位选端 unsigned int counter2,tmp; uchar table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; // 共阳型数码管0—9显示 unsigned int dis[6]; uchar con[]={0xfe,0xfd,0xfb,0xf7,0xef}; //共阳型数码管控制端 init(); delay(uchar); display(); jishu(); void main() {guandz(); init(); while(1) { jishu(); }

实验四 单片机外部中断实验

实验四单片机外部中断实验 一、实验目的 1.理解单片机AT89C51的中断原理及其中断过程。 2.用proteus设计、仿真AT89C51单片机的外部中断。 外部中断就是单片机AT89C51的重要功能,本实验用AT89C51单片机外部中断功能改变数码管的显示状态。当无外部中断0时,主程序运行状态为七段数码管的a~g段依次点亮,不断循环;当有外部中断0(单片机P3、2脚上有下降沿电压)输入时,立即产生中断,转而执行中断服务程序,数码管显示状态为“8”亮灭闪烁显示,亮灭闪烁显示8次以后,返回主程序原断点处继续执行,数码管继续段点亮的循环显示。 1 ③7SEG-COM-AN-GRN:绿色发光二极管; ④CAP、CAP-ELEC:电容、电解电容; ⑤CRYSTAL:晶振; ⑥BUTTON:按钮。 2、放置元器件

3、放置电源与地 4、连线 5、元器件属性设置 6、电气检测 四、源程序设计、生成目标代码文件 1、流程图 2、源程序设计 通过菜单“sourc e→Add/Remove Source Files…”新建源程序文件:DZC33、ASM。 通过菜单“sourc e→DZC34、ASM”,打开PROTEUS提供的文本编辑器SRCEDIT,在其中编辑源程序。 程序编辑好后,单击按钮存入文件DZC34、ASM。 3、源程序编译汇编、生成目标代码文件 通过菜单“sourc e→Build All”编译汇编源程序,生成目标代码文件。若编译失败,可对程序进行修改调试直至汇编成功。 五、PROTEUS仿真 1、加载目标代码文件 2、全速仿真 单击按钮,启动仿真。 3、仿真调试 (1)带断电仿真

c51单片机t0外部中断

#include #include void delay_ms(unsigned int x) //误差0us { unsigned char a,b,c; for(c=x;c>0;c--) for(b=142;b>0;b--) for(a=2;a>0;a--); } void main(void) { ET0=1;//外部中断0、1下降沿中断 ET1=1; EX0=1; EX1=1; EA=1; while(1) P0=0x00; // P1的8个LED,高电平亮} void int0_int() interrupt 0 { unsigned char i ,j, tmp=0x01; for(i=3; i>0; i--) for(j=8; j>0; j--) { P0=tmp; delay_ms(500); //500ms闪烁 tmp = _crol_(tmp,1); } } void int1_int() interrupt 2 { unsigned char i ,j, tmp=0x80; for(i=3; i>0; i--) for(j=8; j>0; j--) { P0=tmp; delay_ms(500); //500ms闪烁 tmp = _cror_(tmp,1); } }

#include sbit S1=P3^2; sbit S2=P3^3; main() { P1=0xff; while(1) { while(S1&&S2); if(S1==0){while(S1==0); P1<<=1;} if(S2==0){while(S2==0); P1>>=1;P1|=0x80;} } }

51单片机中断编程

第6章中断系统 在CPU与外设交换信息时,存在一个快速的CPU与慢速的外设间的矛盾。为解决这个问题,采用了中断技术。良好的中断系统能提高计算机实时处理的能力,实现CPU 与外设分时操作和自动处理故障,从而扩大了计算机的应用范围。 当CPU正在处理某项事务的时候,如果外界或内部发生了紧急事件,要求CPU暂停正在处理的工作转而去处理这个紧急事件,待处理完以后再回到原来被中断的地方,继续执行原来被中断了的程序,这样的过程称为中断。向CPU提出中断请求的源称为中断源。微型计算机一般允许有多个中断源。当几个中断源同时向CPU发出中断请求时,CPU应优先响应最需紧急处理的中断请求。为此,需要规定各个中断源的优先级,使CPU 在多个中断源同时发出中断请求时能找到优先级最高的中断源,响应它的中断请求。在优先级高的中断请求处理完了以后。再响应优先级低的中断请求。 当CPU正在处理一个优先级低的中断请求的时候,如果发生另一个优先级比它高的中断请求,CPU能暂停正在处理的中断源的处理程序,转去处理优先级高的中断.请求,待处理完以后,再回到原来正在处理的低级中断程序,这种高级中断源能中断低级中断源的中断处理称为中断嵌套。 MCS-51系列单片机允许有五个中断源,提供两个中断优先级(能实现二级中断嵌套)。每一个中断源的优先级的高低都可以通过编程来设定。中断源的中断请求是否能得到响应,受中断允许寄存器IE的控制;各个中断源的优先级可以由中断优先级寄存器IP 中的各位来确定;同一优先级中的各中断源同时请求中断时,由内部的查询逻辑来确定响应的次序。这些内容都将在本节中讨论。 6 . 1 中断请求源和中断请求标志 1、中断请求源 MCS-51中断系统可用图6-1来表示。五个中断源是: INT来自P3.2引脚上的外部中断请求(外中断0)。 ◆0 INT来自P3.3引脚上的外部中断请求(外中断1)。 ◆1 ◆T0 片内定时器/计数器0溢出(TF0)中断请求。 ◆T1片内定时器/计数器1溢出(TF1)中断请求。 ◆串行口片内串行口完成一帧发送或接收中断请求源TI或RI。 每一个中断源都对应有一个中断请求标志位,它们设置在特殊功能寄存器TCON和SCON中。当这些中断源请求中断时,分别由TCON和SCON中的相应位来锁存。

51单片机中断系统详解(定时器、计数器)

51单片机中断系统 51单片机中断级别 中断源默认中断级别序号(C语言用) INT0---外部中断0 最高0 T0---定时器/计数器0中断第2 1 INT1---外部中断1 第3 2 T1----定时器/计数器1中断第4 3 TX/RX---串行口中断第5 4 T2---定时器/计数器2中断最低 5 中断允许寄存器IE 位序号DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 符号位EA ------- ET2 ES ET1 EX1 ET0 EX0 EA---全局中允许位。 EA=1,打开全局中断控制,在此条件下,由各个中断控制位确定相应中断的打开或关闭。EA=0,关闭全部中断。 -------,无效位。 ET2---定时器/计数器2中断允许位。EA总中断开关,置1为开; ET2=1,打开T2中断。EX0为外部中断0(INT0)开关,…… ET2=0,关闭T2中断。ET0为定时器/计数器0(T0)开关,……ES---串行口中断允许位。EX1为外部中断1(INT1)开关,…… ES=1,打开串行口中断。ET1为定时器/计数器1(T1)开关,…… ES=0,关闭串行口中断。ES为串行口(TX/RX)中断开关,…… ET1---定时器/计数器1中断允许位。ET2为定时器/计数器2(T2)开关,…… ET1=1,打开T1中断。 ET1=0,关闭T1中断。 EX1---外部中断1中断允许位。 EX1=1,打开外部中断1中断。 EX1=0,关闭外部中断1中断。 ET0---定时器/计数器0中断允许位。 ET0=1,打开T0中断。 ET0=0,关闭T0中断。 EX0---外部中断0中断允许位。 EX0=1,打开外部中断0中断。 EX0=0,关闭外部中断0中断。 中断优先级寄存器IP 位序号DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 位地址--- --- --- PS PT1 PX1 PT0 PX0 -------,无效位。 PS---串行口中断优先级控制位。

51单片机中断程序大全

void main(void) { // EA=1; // // ET0=1; // TMOD=0x10; // TH1=(65536-921)/256; // 开总中断 定时器T0中断允许使用定时器T1的模式1 定时器T1的高8位赋初值 //实例42 :用定时器TO查询方式P2 口8位控制LED闪烁 #include<> // 包含51 单片机寄存器定义的头文件 /************************************************************** 函数功能:主函数 void main(void) { // EA=1; // // ETO=1; // TMOD=OxO1; // 开总中断 定时器TO 中断允许使用定时器TO 的模式1 TH0=(65536-46083)/256; // 定时器TO的高8位赋初值 TL0=(65536-46083)%256; // 定时器TO的高8位赋初值 TR0=1; // 启动定时器T0 TF0=0; P2=0xff; while(1)// 无限循环等待查询{ while(TF0==0) TF0=0; P2=~P2; TH0=(65536-46083)/256; // 定时器T0的高8位赋初值 TL0=(65536-46083)%256; // 定时器T0的高8位赋初值 } } // 实例43:用定时器T1 查询方式控制单片机发出 #include<> // 包含51 单片机寄存器定义的头文件 sbit sou nd=P3^7; // 将sou nd 位定义为引脚 /************************************************************** 函数功能:主函数 **************************************************************/ 1KHz音频

相关文档