第二节C语言接口编程技术
一、C语言特点
C语言以其简洁、灵活、表达能力强、产生的目标代码质最高、可移植性好为其基本特点而著称于世,详细归纳起来,它具有以下几个特点:
• C语言兼容了其他计算机语言的一些优点,其程序结构紧凑、简洁、规整,表达式简练、灵活、实用。用C语言编写的程序可读性强、编译效率高。
• C语言具有丰富的数据类型。在系统软件中,特别是操作系统中,对计算机的所有软件、硬件资源要实施管理和调度,这就要求有相应的数据结构作为操作基础。C语言具有5种基本的数据类型和多种构造数据类型(数组、结构、联合、枚举)以及复杂的导出类型。C 语言还提供了与地址密切相关的指针及其运算符。
• C语言具有丰富的运算符,多达科种。丰富的数据类型与丰富的运算符相结合,使C 语言具有表达灵活和效率高等特点。
•语言是一种结构化程序设计语言,特别适合于大型程序的模块化设计:
• C语言是处于汇编语言和高级语言之间的一种中间型程序设计语言,它既具有高级语言面向用户、可读性
强、容易编程和维护等特点,又具有汇编语言面向硬件和系统,可以直接访问硬件的功能,C语言的运行效率可以与汇编语言媲美。
• C语言具有较高的可移植性。在C语言的语句中,没有依赖于硬件的输人输出语句,程序的输人输出功能是通过调用输人输出函数实现的,而这些函数是由系统提供的独立于C 语言的程序模块。从而便于硬件结构不同的计算机之间实现程序的移植。
二、C语言端口操作编程
利用C语言进行计算机的接口编程,主要是利用C语言对端口输人输出操作的支持,PC/XT/AT微型计算机上运行的几种C语言版本,都支持对端口的输人输出操作。C语言对这些端口的输人输出函数以库函数的形式进行了封装,这些库函数实际上调用了汇编语言中的INI}UT指令,开发者不必自己开发C语言的接口函数,简化了开发难度。
1.端口操作函数
C程序为了要访问端口(即口地址),专门设计了端口输人输出函数,它们是
int inport()
int inpartb()
int outport()
int outportb()
这四个函数的函数原型都在函数库"dos. h"中,所以在使用这四个函数之前,需要在程序中包含进库函数"dos. h。
(1)端口读函数C语言中的读端口的函数有两个:inport()和inportb()。他们都是从指定的端口读人数据。其调用形式为:
int inpart(int port
int inportb(int port)
inport()函数从指定的端口port读人一个字长的值,其长度为l6位;inportb()函数从指定的端口part读人一个字节,其长度为8位。
如:下列语句将从口地址2F0H中得到一个字长的数:
unsigned int c;
c=import (Ox2f0);
(2)端口写函数C语言中向端口输出数据的函数有两个:outport()和outportb()。他们都是向指定的端口输出数据。其调用形式为:
void outport(int port。int worn)
void outportb(int peat,char byte)
outport ( )函数向指定的端口art输出一个字长值word,其长度为16位;
outportb()函数向指定的端口port输出一个字节值byte,其长度为8位。
如:下列语句将数4095送往口地址为2F2H和2F3H的接口中去:
outport(Ox2f2,4095);
数据发送时.程序将4095转变为二进制数,低8位送2F2H,高8位送2F3H
2.端口访问实例
(1)微机发声在PC机主板内装有定时器与计数器( CTC ),芯片为8253 --5。其中1号计数器定期向4个DMA通道请求一次I/O传送;0号计数/本时器为通用的计数器,实现日时钟;2号计数/定时器支持扬声器的声调发生器。上述CTC的口地址已由机器内部译码,口地址为4Q-43H。主机板上还安装有8255并行接口,其口地址为60-63H,已由机上译码。用CTC配合8255产生声音的原理如图9一1所示。
由图9一1可知.C程序实现发声喇叭,应分别访问42H,43H,61H,并在
访问61H时将原输人的字节的bit0和bit1置1才能使喇叭发声。面我们列出发声程序,主要为了理解上述端口操作函数的应用二该程序运行时。可在屏幕上显示由随机数得到的频率数,同时发出相应的声音二由于声音是断续的(开与关8255),同时频率数是随机的,所以使人得到一种似乎是旧式科学幻想电影中的音乐效果。程序清单如下:
#define DELAY ZUODa
void sound(unsigned int freg);
main()
{do
do{
freg=rand();
}while (freg>5000);
sound ( freg );
}while(!kbhi());
}
void sound( unsigned int freg)
{unsigned
while语句都可以用for改写
union{
long divisor;
unsigned char c[2];
}count;
unsigned char p;
count.divisor=119328O/freg;
outportb (Ox43,Oxc6);
outpartb (0x42,count.c[0]);
???
转换完成后,将转换结果用函数inpartb()采集到计算机内并进行处理。所采集到的8个通道数据,是十进制数0一2.55。我们暂不作工程量转换,只是原理性地将它们在屏幕上轮流显示。
上述ALE, START, OE等信号,主要采用类似于汇编语言中的累加器输人输出方法,即采用inpartb ( )和outpartb ( )函数。作为外设的口地址,需A9?1,故选用2F0H, 2F1H,  2F2H,这很方便地可由A0-A9地址线输人进行译码。
全部用C语言写出的数据采集系统的清单如下:
main()
{
int i;
unsigned char c;
unsigned char a;
do{
a=0;
do{
outportb (Ox02f0,a);
outportb(0x02f1,a) ;/*发送通道地址并启动转换*/
for(i=0;i<100;i++);
c = inportb (Ox02f2 );/*采集转换结果*/
if( a<=6)printf("%03d",c);
else  printf("%03d\n",c);
}while(++a<=7)
for(i=0;i<=10000;i++);
}while(!kbhit());
}
三、中断服务程序
1,中断的概念
所谓的中断,是指CPU在正常运行一个程序时,由于程序中的事先安排或是由外界事件的触发,导致CPU中断了当前正在运行的程序,而转人相应的服务程序中去的过程。这些引起程序中断的事件称为中断源,程序安排的事件是指中断指令,程序执行到中断指令
后,立即跳转到相应的服务程序中。中断指令可以看作是计算机内部事件,而对应的外部事件则是指计算机中某些接口上的设备发出的请求中断执行的信号。这些信号称为中断请求信号。中断服务程序在执行完后,将返回原来程序中的断点处,继续执行原来的程序,这称为中断返回。有时可能同时有多个中断源产生,而CPU在响应这些中断时需按照一定的顺序进行,这称为中断的优先级,优先级高的中断首先被响应,而优先级低的中断则暂不响应,这称为挂起。也可以在程序中强迫CPU对某些中断不予响应,这称为中断屏蔽。实质上,CPU在响应中断时,是按照中断源所对应的地址,引导程序跳转到相应
的服务程序中去,这个与中断源有着一一对应关系的地址称为中断向量。
PC机有两种类型的中断,即软中断和硬中断。软中断由执行某些指令产生;硬中断则是由接口设备引起。
PC机在其内部存储了256个中断向量,每一个占用4个字节。每个中断向量用其类型码加以区别。实际执行过程中,CPU根据其类型码,将其乘以4得到中断向量的地址,即中断服务程序的入口地址。
2.用Turbo C编写中断程序的方法
用Turbo C编写中断程序可用三部分来实现:即编写中断服务程序、安装中断服务程序、激活中断服务程序,下面分述之。
(1)编写中断服务程序中断服务程序的任务是,当产生中断后,脱离被中断的程序,使系统执行中断服务的程序,它必须打断当前执行的程序,急需完成一些特定操作,因此该程序中应包括一些能完成这些操作的语句和函数,因涉及DOS的重入问题,因而不应该有与DOS系统调用有关的库函数,如printf(),sprint()等。
由于产生中断时,必须保留被中断程序中断时的一些现场数据,如保存断点等(这些值都在寄存器中,若不保存,当中断服务程序用到这些寄存器时,将改变它的值)、以便恢复中断时使这些值复原。从而继续
执行原来中断了的程序。Turba C为此提供了一种新的函数类型interrupt,它将保存由该类型函数参数指出的各寄存器的值,而在退出该函数,即中断恢复时,再复原这些寄存器的值,因而用户的中断服务程序必须定义成这种类型的函数。如中断服务程序名定为myp,则可将这个函数说明成这样(其中的参数将保存各寄存器值): vold interrupt myp(unsigned bp,unsigned di,unsigned si,unsigned ds,unsigned es。unsigned dx,unsigned cx,unsigned bx,unsigned ax,unsigned ip,unsigned cs,unsigned flags) 若是在小模式下的程序,只有一个段,在中断服务程序中用户就可以像用无符号整数变量一样,使用这些寄存器。
若中断服务程序中不使用上述寄存器,也就不会改变这些寄存器原来的值,因而也就不需保存它们,这样在定义这种中断类型的函数时,可不写这些寄存器参数.如可写成:    void interrupt myp()
对于硬中断,则在中断服务程序结束前要送中断结束命令字给系统的中断控制寄存器,其口地址为。}o.中断结束命令字也为ox2o,即
outportb(0x20,0x20);
在中断服务程序中,若不允许别的优先级较高的中断打断它,则要禁止中断,可用函数disable()来关闭中断。若允许中断,则可用开中断函数enable()来开放中断。
(2)安装中断服务程序定义了中断服务函数后、还需将这个函数的人口地址填人中断向量表中,以便产生中断时程序能转人中断服务程序去执行。为了防止正在改写中断向量表时,又产生别的中断而导致程序混乱,可以关闭中断,当改写完毕后,再开放中断。一般的,常定义一个安装函数来实现这些操作,如:
void install(void interrltpt(*faddr()),int inum)
{disable();
setvect(inum,faddy);
enable{);
其中faddy是中断服务程序的人口地址,其函数名就代表了人口地址,而inum表示中断类型号,setvect()函数就是设置中断向量的函数,上述定义的install()函数,将完成把中断服务程序入口地址填人中断向量inum中去。
(3)中断服务程序的激活当中断服务程序安装完后.如何产生中断,从而执行这个中断服务程序呢?如前所述,对硬件中断,就要在相应的中断请求线(IRQj,i=0,1, 2,…,7)产生一个由低到高的中断请求电平,这个过程必须由接口电路来实现,当中断产生时,便会执行中断。
对于软中断,有几种调用方法二由于中断类型的函数不同于用户定义的一般函数,因此也不能用调用一般函数的方法来调用它,一般软中断调用可用如下方法:
1)使用库函数geninterrupt(中断类型号)在主函数中适当的地方,用setvect函数将中断服务程序的地址写人中断向量表中,然后在需要调用的地方用geninterrupt()函数调用。
2)直接调用如已用setvect(类型号,mvp)设置了中断向量值,则可用myp()直接调用,或用指向地址的方法调用:
(*myp());
3)也可用在TurboC程序中插人汇编语句的方法来调用,如:
setvect(inum,myp);
通常上述的调用可定义成一个中断激活函数来完成,该函数中可附加一些别的操作,主程序在适当的地方调用它就可以了。
(4)恢复被修改的中断向量这一步视情况而定,当用户采用系统己定义过的中断向量,并且将其中断服务程序进行了改写,或用新的中断服务程序代替了原来的中断服务程序,为了在主程序结束后,恢复原来
的中断向量,以指向原中断服务程序,可以在主程序开始时,存下原中断向量的内容,这可以用取中断向量函数getvect()来实现,如j =(char*)getvect (Ox1c),这样j指针变量中将是0x1c中断服务程序的人口地址.由于DQS已定义了0x1c中断的服务程序入口
地址,但它是一条无作用的中断服务,因而我们可以利用d。中断来完成一些用户想执行的一些操作,实际上就是用户自己的中断服务程序代替了原来的。当主程序要结束时,为了保持系统的完整性,我们可以恢复原来的中断服务人口地址,如可用以下方法实现。
setvect ( 0xlc , j);
3.中断编程举例
下面以采用中断方式的信号采集为例说明C语言中断编程的方法。图9-3示出了这个光电隔离信号采集电路的示意图。它采用硬中断方式,共有多路模拟信号输人通道,该电路在模拟转换部分和PC机数字输入部分之间有一光电隔离模块,使PC微机和信号现场部分进行了隔离,使得它们之间不发生电的联系,而通过光作为媒介来传递转换后的信号,这样可有效地防止一些现场电的于扰,并保护微机不受偶然事故的破坏。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。