1、第三节 串口数据通讯的实现过程,一.串行通信协议,通信协议:是通信双方的一种约定。约定包括对数据格式、同步方式、传送速度、传送步骤、检纠错方式以及控制字符定义等问题作出统一规定,通信双方必须共同遵守。因此,也叫做通信控制规程,或称传输控制规程。目前,采用的通信协议有两类:异步协议和同步协议。同步协议又有面向字符和面向比特以及面向字节计数三种。,1 面向字符的同步协议,传输特点:一次传送由若干个字符组成的数据块,而不是只传送一个字符,并规定了10个字符作为这个数据的开头与结束标志以及整个传输过程的控制信息,它们也叫作通信控制字 。,SYN是同步字符,传送数据时,接收端不断检测,一旦检测到同步字符
2、,就知道是一帧开始了; SOH表示标题的开始; STX代表传送的数据块开始; ETB/ETX用在正文很长,需要分成若干个分数据块,分别在不同帧中发送的场合; 校验码对从SOH开始到ETX字段进行校验,特定字符定义,2 面向比特的同步协议,面向比特的同步协议中HDLC的帧结构如图:,特点:所传输的一帧数据可以是任意比特,靠约定的比特组合模式,而不是靠特定字符来标志帧的开始和结束。故称“面向比特的协议”,帧信息的分段:,SDLC/HDLC标志字符:信息传输以标志字符开始,以标志字符结束。从开始标志到结束标志之间构成一个完整的信息单位,称为“帧”。 地址场和控制场:地址场用来规定与之通信的次站的地址
3、,控制场可规定若干个命令。 信息场:信息场包含要传送的数据。 帧校验信息:除了标志场和自动插入的“0”以外,所有的信息都参加CRC计算。,3 起止式异步协议,异步传输数据格式如下:,特点:一个字符一个字符传输,并且传送一个字符总是以起始比特开始,以停止位结束,字符之间没有固定的时间间隔要求。,0,起/止比特的作用:,起始比特变为低电平时,告诉接收方传送开始。 停止位标志着一个字符传送完毕。 传送前,需统一规定起止式格式和数据传输速率。开始后,接收设备不断检测传输线,当收到一系列的“1”(停止比特或空闲比特)后,检测到一个下跳沿,说明起始位出现,起始位经确认后,就开始接收所规定的数据比特和奇偶校
4、验比特以及停止比特。经过处理将停止比特去掉,把数据比特拼装成一个并行字节,并且经校验后,无奇偶错误才算正确的接收一个字符。一个字符接收完毕,接收设备又继续测试传输线,监视“0”电平的到来和下一个字符的开始,直到全部数据传送完毕。,二.编程实现串行通信,在TC编程环境下,使用端口操作函数实现 利用API函数实现 利用MSCOM控件实现,在TC下实现串口编程,1.使用outputb和inputb函数实现 (1)设置比特率 (2)设置线控制寄存器 (3)设置其他寄存器 (4)根据线状态寄存器执行读/写串口操作,在DOS系统接口中DOS INT21H的03H和04H号功能调用为异步串行通信的接收和发送
5、功能,而BIOS INT14H有4组功能调用为串行通信服务,正因为如此,在DOS中采用寄存器直接读写、BIOS调用、通信中断程序等方法可以比较容易实现串口通信。,注意 :对串口初始化时,波特率的值、奇偶校验位、数据位、停止位的设置由上述值得到,返回值:bioscom()函数总是返回一个16位整数,其高位字节为状态位,低字节随cmd值的变化而不同。,当cmd的值为1,发送字符时,如果数据未被传递,则位15置位。 当cmd的值为2,接收字符时,如果不发生错误返回值低字节为读入字节,如果发生错误,则至少有高字节中一位置位 当cmd的值为0或3时,低字节置入附加状态信息位域 含义7 数据载体检测状态6
6、 响铃指示器状态5 数据设置就绪状态4 清除发送状态,位域 含义3 数据载体变化检测2 后沿响铃检测1 数据设置就绪状态变化0 清除状态发送变化 应用于基本串行口操作的函数 串行口初始化 如biscom(0,0xeb,0),此语句设置第一个串行口为9600波特,奇校验、一个停止位、8个数据位。,0xeb=0xe0|0x08|0x00|0x03 检查串行口状态: 若数据就绪,下面的函数返回0,若数据等待,下面的函数返回1; 函数check_stadus()函数如下: int check_stadus(int port) int stadus;stadus=bioscom(3,0,port);/*
7、 检查串口的状态*/if(stadus/*数据等待*/, 向串行口发送一个字符Send_port()函数通过调用bioscom()向串行口发送一个字符。若发送过程中出现错误,则位15被置位,否则正常发送过去。, 从串行口接收一个字符Receive_port()利用bioscom()从串行口接收一个字符。 如果返回值高位字节中有一个被置位,则发生接收错 误;否则,返回值的低字节便是接收到的字符。,使用WIN32 API来实现串口操作,(1) 应用CreatFile()来打开串口 API 函数CreatFile()如下: HANDLE CreatFile( LPCTSTR lpszName,/文件
8、名 DWORD fdwAccess,/访问模式 DWORD fdwShareMode,/共享模式 LPSECURITY_ATTRIBUTES lpsa,/通常为NULL DWORD fdwCreate,/创建方式 DWORD fdwAttrsAndFlags,/文件属性和标志 HANDLE hTemplateFile/临时文件的句柄,通常为NULL );,如果调用成功,那么该函数返回文件的句柄, 如果调用失败,则函数返回INVALID_HANDLE_VALUE 例如,下面的例程就是用来打开COM1端口的: HANDLE hCom; DWORD dwError; hCom=CreateFile(
9、“COM1”,/文件名 GENERIC_READ|GENERIC_WRITE,/允许文件读写 0,/独占方式 NULL, OPEN_EXISTING,/打开而不是创建 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,/重叠方式 NULL );,If(hCom=INVALID_HANDLE_VALUE) dwError=GetLastError(); /处理错误 ,(2) 初始化串口 对串口初始化需要通过一个DCB结构来进行。DCB结构包含了诸如数据传输速率、每个字符的数据位数、奇偶校验和停止位数等信息。在查询或配置串行口的属性时,都要用DCB结构来作为缓冲
10、区。调用GetCommState函数可以获得串口的配置,该函数把当前配置填充到一个DCB结构中。要修改串口的配置,应该先修改DCB结构,然后再调用SetCommState函数用指定的DCB结构来设置串口。 例:DCB dcb; BuildCommDCB(“COM1:9600,n,8,1”,(3) 利用ReadFile()和WriteFile() 进行串口读写操作 BOOL ReadFile( HANDLE hFile,/文件句柄 LPVOID lpBuffer,/读缓冲区 DWORD nNumberOfBytesToRead,/要求读入的字节数 LPDWORD lpNumberOfBytesR
11、ead,/实际读入的字符 LPOVERLAPPED lpOverlapped/指向一个OVERLAPPED结构 );/若返回TRUE则表明操作成功ReadFile()函数对同步或异步操作都支持,BOOL WriteFile( HANDLE hFile,/文件句柄 LPVOID lpBuffer,/读缓冲区 DWORD nNumberOfBytesToWrite,/要求读入的字节数 LPDWORD lpNumberOfBytesWritten,/实际读入的字符 LPOVERLAPPED lpOverlapped/指向一个OVERLAPPED结构 );/若返回TRUE则表明操作成功WriteFil
12、e()函数对同步或异步操作都支持,(4) 关闭串口 使用串口时一般要关闭它,如果忘记关闭串口,串口就会始终处于打开状态,其它应用程序就不能打开并使用串口了。 BOOL CloseHandle(HANDLE hObject/设备句柄 );,用MSComm控件实现串口通信,MSComm是Microsoft公司提供的简化Windows下串口通信编程的ActiveX控件,它为应用程序提供了通过串行接口收发数据的简便方法。具体来说,它提供了两种处理通信问题的方法:一是事件驱动方法,一是查询法。,1.MSComm控件两种处理通讯的方式 1.1 事件驱动方式 事件驱动通讯是处理串行端口交互作用的一种非常有效
13、的方法。在许多情况下,在事件发生时需要得到通知,例如,在串口接收缓冲区中有字符,或者 Carrier Detect (CD) 或 Request To Send (RTS) 线上一个字符到达或一个变化发生时。在这些情况下,可以利用 MSComm 控件的 OnComm 事件捕获并处理这些通讯事件。,OnComm 事件还可以检查和处理通讯错误。所有通讯事件和通讯错误的列表,参阅 CommEvent 属性。在编程过程中,就可以在OnComm事件处理函数中加入自己的处理代码。这种方法的优点是程序响应及时,可靠性高。每个MSComm 控件对应着一个串行端口。如果应用程序需要访问多个串行端口,必须使用多个
14、 MSComm 控件。,1.2 查询方式 查询方式实质上还是事件驱动,但在有些情况下,这种方式显得更为便捷。在程序的每个关键功能之后,可以通过检查 CommEvent 属性的值来查询事件和错误。如果应用程序较小,并且是自保持的,这种方法可能是更可取的。例如,如果写一个简单的电话拨号程序,则没有必要对每接收一个字符都产生事件,因为唯一等待接收的字符是调制解调器的“确定”响应。,2.MSComm 控件的常用属性 MSComm的几个重要属性: CommPort:设置并返回通讯端口号。116之间的数(端口必须存在,否则出错。) Settings:以字符串的形式设置并返回波特率、奇偶校验、数据位、停止位
15、。 “9600,N,8,1” “19200,E,8,1” “2400,O,8,1” PortOpen:设置并返回通讯端口的状态。也可以打开和关闭端口。TRUE:打开; FALSE:关闭 Input : 从接收缓冲区移走一串字符,将缓冲区收到的数据读入变量。 Output :向发送缓冲区写数据流。,InputMode 属性: 常数 值 描述 comInputModeText 0 (缺省)通过 Input 属性以文本方式取回数据。 comInputModeBinary 1 通过 Input 属性以二进制方式检取回数据。,InputLen:设置并返回Input属性从接收缓冲区读取的字符数。 默认值为
16、0,表示使用Input属性将使MSComm控件读取接收缓冲区中的全部内容。 InBuffersize:设置或返回输入缓冲区的大小,默认值为1024字节。 InBufferCount:用于返回输入缓冲区内的等待读取的字节数。 OutBuffersize:设置或返回发送缓冲区的大小,默认值为512字节。 OutBufferCount:用于返回发送缓冲区内的等待发送的字节数。,通信事件类型: 常数 值 描述 comEvSend 1 发送事件 comEvReceive 2 接收事件 comEvCTS 3 clear-to-send 线变化 comEvDSR 4 data-set ready 线变化 c
17、omEvCD 5 carrier detect 线变化 comEvRing 6 振铃检测 comEvEOF 7 文件结束,CommEvent属性:发映错误或事件类型,通信错误类型: 常数 值 描述 comEventBreak 1001 接收到中断信号 comEventCTSTO 1002 Clear-to-send 超时 comEventDSRTO 1003 Data-set ready 超时 comEventFrame 1004 帧错误 comEventOverrun 1006 端口超速 comEventCDTO 1007 Carrier detect 超时 comEventRxOver 1
18、008 接收缓冲区溢出 comEventRxParity 1009 Parity 错误 comEventTxFull 1010 传输缓冲区满 comEventDCB 1011 检索端口设备控制 (DCB)时的意外错误,MSComm 控件的事件:OnComm事件利用MSComm控件编写的应用程序在通信时如果发生错误或事件,将会触发OnComm事件并且改变其属性值,通过GetCommEvent()可获得OnComm产生的事件或错误的代码。,值 描述 380 无效属性值 comInvalidPropertyValue 383 属性为只读 comSetNotSupported 394 属性为只读 co
19、mGetNotSupported 8000 端口打开时操作不合法 comPortOpen 8001 超时值必须大于 0 8002 无效端口号 comPortInvalid 8003 属性只在运行时有效 8004 属性在运行时为只读 8005 端口已打开comPortAlreadyOpen,MSComm控件所能捕获的错误说明:,8006 设备标识符无效或不支持该标识符 8007 不支持设备的波特率 8008 指定的字节大小无效 8009 缺省参数错误 8010 硬件不可用(被其它设备锁定) 8011 函数不能分配队列 8012 设备没有打开 comNoOpen 8013 设备已经打开 8014
20、不能使用 comm 通知 8015 不能设置 comm 状态comSetCommStateFailed,8016 不能设置 comm 事件屏蔽 8018 仅当端口打开时操作才有效comPortNotOpen 8019 设备忙 8020 读 comm 设备错误 comReadError 8021 为该端口检索设备控制块时的内部错误 comDCBError,第四节 举例,串口通讯试验 实验要求:用TC对计算机的串口进行编程,并作一个简单的串口查询通讯程序。 实验方案: 用一条九针的DB-9串口通讯线将计算机的com1口和com2口相连,com1作为接受口,com2作为发送口。,实验步骤:1、 首先
21、对计算机串口控制寄存器进行初始化和设置;2、 对com2串口发送一个数据前,查询com2串口的发送移位寄存器是否为空,如果是,则发送数据,否则继续查询,直到条件成立;执行完发送指令后,再次查询com2口的发送移位寄存器是否为空,如果是则结束发送;如果为否,则继续查询,直到条件成立。,3、 从com1串口接受刚才从com2串口发送的数据前,先查询com1口的接收数据标志位是否为,如果是则表示com1口接收数据准备就绪,执行接收指令,即从com1口读入数据,否则表示没有准备好接收,继续查询com1口的接收数据标志位,直到为;接收完数据后,再次查询com1串口的接受寄存器是否为空的标志位的状态,如果
22、为则程序转入com2串口的数据发送程序;否则继续查询等待,直到接收。,注:com1口的基地址为:3F8; com2口的基地址为:2F8。 这里设置波特率为9600bps;即设置波特率的高八位的寄存器控制字为00h, 设置波特率的低八位的寄存器控制字为0ch;,最简连接,简单连接,完全连接,DB9串口线连接方式:,程序如下: #include #include #include #define Com1Base 0x3f8 /定义串口com1的基地址; #define Com2Base 0x2f8 /定义串口com2的基地址;,void main() int st,fp,jp,op; int d
23、=0; outportb(Com2Base+3,0x80); /允许访问com2的比特率 outportb(Com2Base+0,0x0c); /设置com2的波特率低八位; outportb(Com2Base+1,0x00); /设置com2的波特率高八位; outportb(Com2Base+3,0x03); /无校验,无停止位,八位数据; outportb(Com2Base+4,0); /初始化串口com2的MODEM寄存器; outportb(Com2Base+1,0); /初始化com2的中断允许寄存器,屏蔽串口com2的中断;,outportb(Com1Base+3,0x80);
24、/允许访问com1的比特率 outportb(Com1Base+0,0x0c); /设置com1的波特率低八位; outportb(Com1Base+1,0x00); /设置com1的波特率高八位; outportb(Com1Base+3,0x03); /无校验,无停止位,八位数据; outportb(Com1Base+4,0); /初始化串口com1的MODEM寄存器; outportb(Com1Base+1,0); /初始化com1的中断允许寄存器,屏蔽串口com1的中断;,printf(“please transfer a character to com2:n“); do do st=
25、inportb(Com2Base+5); printf(“%xn“,st); while(st /查询com2的线状态寄存器(LSR)的第五位的状态是否为1,即发送保持寄存器是否为空?也就是com2口是否准备好发送数据;否则循环等待;,while(1) printf(“%xn“,d+1); outportb(Com2Base,+d);/从com2口发送数据; do fp=inportb(Com2Base+5); while(fp/com2的线路状态寄存器(LSR)的第五位的状态为1,即发送保持寄存器为空,跳出数据发送程序。 ,printf(“display the character while was just received from com1n“); do jp=inportb(Com1Base+5); while(jp /com1口读入数据,并在屏幕上显示出来;,do op=inportb(Com1Base+5); while(op/敲任意键退出程序; ,