首页 > 范文大全 > 正文

深入Windows通信编程

开篇:润墨网以专业的文秘视角,为您筛选了一篇深入Windows通信编程范文,如需获取更多写作素材,在线客服老师一对一协助。欢迎您的阅读与分享!

一、windows通信机制

windows与dos编程的重要差别在于windows程序是消息驱动和设备统一管理。体现在通信方面,dos中的寄存器直接读写、bios调用和通信中断程序等编程方法都不能或不宜采用。windows通过通信驱动程序comm.drv与硬件接口,向程序员提供了多达17个标准函数,功能强大,但也增加了理解和编程的难度。

windows3.1通信函数主要有:

opencomm

打开一通信设备

buildcimmdcb将一设备定义字符串转变为dcb数据结构

enablecommnotification使能或禁止传送wm_commnotify消

setcommstate设置通信设备状态

setcommeventmask设置通信事件掩码

readcomm从通信设备读字符

writecomm向通信设备写字符

flushcomm清除一发送或接收队列

getcommeventmask返回通信事件掩码

getcommstate返回设备控制块(dcb)

getcommerror恢复通信设备状态

closecomm关闭一通信设备

dcb数据结构、其它通信函数及各函数的具体用法请参见有关资料。

一般windows通信编程应包括两部分:设备初始化及wm_commnotif

y消息处理。

设备初始化典型流程如图1。

图1

wm_commnotify消息处理典型流程如图2。

图2

对于大多数实际通信来说,可能只需要处理流程图中的一部分。

设备初始化及wm_commnotify消息处理两部分密切相关。所有类型wm_commnotify消息的传送都是因为在初始化函数中进行了相应的设置。

换言之,可以根据通信的实际情况有选择地设置,控制windows向应用程序发送的wm_commnotify消息的数量和类型,以期达到高效、可靠的通信。例如,对于固定长度消息型的通信可以在enablecommnotification函数中设置cbwritenotify和cboutqueue参数为消息长度;对于以固定字符结尾的消息型通信可以在事件掩码中包括ev_rxflag,将dcb数据结构中的evtchar变量置为结尾字符,然后调用setcommstate和setcommeventmask函数;对于遵循v.25bis之类协议的通信,由于用到了大量信号线来作握手信号,则事件掩码中要包含ev_cts、ev_dsr、ev_rsld及ev_ring等;而对于文件传送型的通信,则宜将opencomm函数中的cbinque和cboutque变量、enableccommnotification中的cbwritenotify和cboutqueue变量设置为较大值,以加快文件传送速度。 二、windows通信疑难探讨

现将笔者在实际编程中遇到的疑难和解决办法描述如下,希望对遇到类似问题的朋友有所启发。

1.怎样用windows未提供的波特率通信?

windows提供了由110bps至256000bps共十三种波特率,一般情况下已足够使用。但在某种特定情况下,例如通信对方使用150bps、又无法要求对方改变波特率时,windows通信就比较困难了。

首先想到的解决方法是直接调用bios中断14h来设置波特率(dos提供了150bps的波特率)。结果是windows屏蔽了该中断,尝试失败。

最后的是采用"蒙混过关"的办法解决问题的:首先,以任一windows支持的波特率(例如300bps)构造通信参数字符串,调用buildcommdcb产生dcb数据结构;然后调用setcommstate设置通信参数;最后再调用自编函数直接修改串口通信寄存器的值。经实验,设置成功,且对windows程序运行无任何不良影响。

2.接收数据为何"丢失"?

通过设置enablecommnotification函数中的cbwritenotify参数(在发送wm_commnotify消息之前,通信设备驱动程序必须向应用程序出入队列中写入的字节数),可以使系统每收到固定个字符发出一wm_commnotify消息,这对于固定长度消息型的通信是很方便的。但实际应用时有时会发生接收数据"丢失"现象,即收到wm_commnotify消息后从接收队列读出cbnotify个数据时,发现只有前面部分数据正确。

经检查,"丢失"现象是由于接收数据超时引起的,当通信对方时钟频率较低时,规定时间内收不到cbwritenotify指定的数据量,即所谓"超时",windows照样向应用程序发送带cn_receive标志的wm_commnoti

fy消息。然后,在应用程序输入队列数据读出之前,windows不再发送该类消息。

解决的方法是减小cbwritenotify的设定值直到不再发生"超时"现象。

发送数据时同样应正确设定cboutque值,以免产生"超时"现象。

如果将cbwritenotify或cboutque设为-1,则windows不传送带cn_receive或cn_transmit标志的wm_commnotify消息。

3.怎样合理使用flushcomm与getcommerror函数?

flushcomm函数的功能是清除指定设备接收或发送队列。getcommerror函数的功能是返回指定设备最近错误码和当前状态,更重要的是"解锁"功能:当出现通信错误时,windows会锁死通信端口直到调用getcommerror。

调用flushcomm的时机很重要,如果通信端口发生错误,不调用该函数就有可能会使接收队列包含不期望的数据;若随便调用该函数,也有可能造成尚未读入或发出的数据丢失。总之,调用该函数要做到"心中有数"。

为了合理调用flushcomm和getcommerror函数,建议在事件掩码中包含ev_err与ev_break。

4.windows多串口通信

windows最多可支持四个串口的通信,但对于isa总线的pc,由于其com1与com3、com2与com4分别共用irq3和irq4,所以只能同时使用两个串口。mca、eisa总线系统没有此限制。

如果需要使用的端口不止四个,可以在pc护展槽中加插多用户卡,如美国的comtrol、台湾的moxa(摩莎)等,就可以支持几个到几十个串口,加上随卡提供的windows驱动程序,就可以进行多串口通信。具体用法请参阅扩展卡说明书。

三、windows通信实例

实例的通信环境为:本方compaq 4/50微机,安装中文windows3.2;对方为8031单片机。通信参数设置:波特率150bps,数据位8,停止位1,无校验。通信协议是:对方发ff,本方收到后回0f,对方收到0f后发一条十字节的消息,本方回0f,结束一次通信。

编程环境为中文windows3

2、borland c++3.1 owl。

#include<windows.h>

#include<owl.h>

#include<window.h>

#include<string.h>

int com=1;//串口号

unsigned char receivebuff〔11〕;//接收数据缓存

_classdef(tcommapp)

class tcommapp: public tapplication

{

public:

tcommapp(lpstr aname, hinstance hinstance, hinstance

hprevinstance, lpstr 1p

cmdline, int ncmdshow)

:tapplication(aname, hinstance, hprevinstance, 1pcmd

line, ncmdshow){};

virtual void initmain window();

};

_classdef(tcommwin)//主窗口类

class tcomm win: public twindow

{

public:

tcomm win(ptwindowsobject aparent, lpstr atitle):

twindow(aparent, atitle){}

int initcom();

void setbaud();//设置windows不支持的波特率

virtual bool wmcommnotify(tmessage & mg)=〔wm_first+

wm_commnotify〕;

virtual void setup window();

};

//该函数设置串口2的波特率为150bps,若用windows提//供的波特率通信,则无须该函数

void tcommwin::setbaud()

{

asm cli;

asm mov dx,2fbh;

asm mov al,80h;

asm out dx,al;

asm mov dx,2f8h;

asm mov al,00h;

asm out dx,al;

asm mov dx,2f9h;

asm mov al,3;

asm out dx,al;

asm mov dx,2fbh;

asm mov al,03;

asm out dx,al;

asm mov dx,2fch;

asm mov al,0bh;

asm out dx,al;

asm mov dx,2f9h;

asm mov al,0fh;

asm out dx,al;

asm mov al,20h;

asm out 21h,al;

asm sti;

}

int tcomm win::initcom()

{

char str〔20〕,s〔2〕;

int comid,err;

dcb dcb;//设备控制块

uint mask=ev_break|ev_err|ev_rxflag;//事件掩码

strcpy(str,"com");

strcat(str,itoa(com+1,s,10));

comid=opencomm(str,128,1);

if(comid<0) return comid;

strcat(str,":300,n,8,1");

err=buildcommdcb(str,&dcb);

dcb.evtchar=-1;//事件字符0xff

err=setcommstate(&dcb);

setbaud();

if(err>0) return err;

flushcomm(comid,1);

if(!enablecomunnotification(comid,hwindow,10,-1))

return -1;

setcommeventmask(comid,mask);

return comid;

}

void tcommwin::setupwindow()

{

twindow::setupwindow();

initcom();

}

bool tcommwin::wmcommnotify(tmessage &mg)

{

uint flag=0;

int id;

comstat stat;

unsigned char sendchar;

static unsigned char

*p=receivebuff;

static num=0;

int ret;

id=mg.wparam;

switch(mg.lp.lo)

{

case cn_event://有事件掩码中定义的事件发生

flag=getcommeventmask(id,ev_break);

if(flag & ev_break)

flushcomm(id,1);

flag=getcommeventmask(id,ev_rxflag);

if(flag & ev_err)

flushcomm(id,1);

flag=getcommeventmask(id,ev_rxflag);

if(flag & ev_rxflag)//收到了事件字符0xff

{

sendchar=0x0f;

writecomm(id,& sendchar,1);//向对方回0x0f

}

break;

case cn_receive://接收到了规定个字符或超时

do

{

ret=readcomm(id,p,1);

if(ret>0)

{

p++;

num++;

}

}while((ret>0)&(num<10));

if(num>=10)//接收完一条消息

{

num=0;

//此处处理接收到的消息

p=receivebuff;

sendchar=0x0f;

writecomm(id,& sendchar,1);//向对方回0x0f

flushcomm(id,1);

}break;

}

flag=getcommerror(id,&stat);//消除错误(若有)

return 1;

}

void tcommapp::initmainwindow()

{

mainwindow=new tcommwin(null, "windows通信示例");

}

int pascal winmain(hinstance hinstance, hinstance hprevi

nstance,lpstr 1pcmdline,

int ncmdshow)

{

tcommapp commapp("通信", hinstance,hprevinstance,1pc

mdline, ncmdshow);

commapp.run();

return commapp.status;

}