您当前的位置:首页 > 计算机论文>信息管理论文

深入Windows通信编程

2015-07-04 09:28 来源:学术参考网 作者:未知
一、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
对于大多数实际通信来说,可能只需要处理流程图中的一部分。www.133229.COM
设备初始化及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;
}
相关文章
学术参考网 · 手机版
https://m.lw881.com/
首页