未知 [信息技术] 串口通信编程有关资料     阅读:24700  

串口通信编程有关资料
作者: haiyun.net 编辑: 海韵 发布日期: 2004-03-02 00:00:00 点击量: 24700 来源: haiyun.net


用Activex实现串口通信

WIN95 界 面 下 的VC++ 串 口 通 讯 程 序

利 用WINDOWS 定 时 器 实 现 串 口 通 信 控 制

VC++下串行通信的编程方法

串行通讯通用接口程序设计

VC在win95下编写用于串行通讯的程序

用Activex实现串口通信

为实现Windows的串口通信可用API实现,但十分不便。也可以用Active控件实现。

先在应用程序中插入Active控件Microsoft comm contronl(MSComm)。

SComm控件的属性

MSComm控件的属性

CommPort:设置串口号,类型 short :1-comm1 2-comm2.

Settings:设置串口通讯参数,类型 CString :B-波特率 P-奇偶性?
∟-无校验,E-偶校验,O-奇验) D-字节有效位数S-停止位。

PortOpen:设置或返回串口状态,类型 BOOL:TURE-打开 FALSE-关闭。

InputMode:设置从接收缓冲区读取数据的格式,类型 long: 0-Text 1-Bin.

Input:从接收缓冲区读取数据 类型 VARIANT。

InBufferCount:接收缓冲区中的字节数,类型:short.

InBufferSize:接收缓冲区的大小,类型:short.

Output:向发送缓冲区写入数据,类型:VARIANT.

OutBufferCount:发送缓冲区中的字节数,类型:short.

OutBufferSize:发送缓冲区的大小,类型:short.

InputLen:设置或返回Input读出的字节数,类型:short.

CommEvent:串口事件,类型:short.

程序示例

串口初始化

if(!m_comm.GetPortOpen())

m_comm.SetPortOpen(TURE); /*打开串口

m_comm.SetSettings("4800,n,8,1"); /*串口参数设置

m_comm.SetInputMode(0); /*设置TEXT缓冲区输入方式

m_comm.SetRthresHold(1); /*每接收一个字符则激发OnComm()事件

接收数据

m_comm.SetInputLen(1); /*每次读取一个字符

VARINAT V1=m_comm.GetInput(); /*读入字符

m_V1=V1.bstrval;

发送字符

m_comm.SetOutput(COlevariant("Hello");/*发送"Hello"

 

WIN95 界 面 下 的VC++ 串 口 通 讯 程 序

南 京 石 油 物 探 研 究 所

吴 凤 华

---- Microsoft Visual C++ 以 其 界 面 的 开 放 性 著 称, 因 其 简 单 易 学、32 位

面 向 对 象 的 程 序 设 计 及Activate X 的 灵 活 性 而 受 广 大 计 算 机 开

发 者 的 青 睐, 广 泛 应 用 于 各 个 领 域。 怎 样 利 用VC++ 进 行 串 口 的

通 讯 编 程, 这 是 一 种 基 本 功。 特 别 是 在 实 时 监 控、 大 小 型 设

备 的 整 个 生 产 过 程 控 制 等 方 面,VC++ 的 串 口 编 程 显 得 尤 为 重

要。 我 曾 参 与 编 写 了 无 线 电 非 法 频 段 占 用 的 实 时 监 控 系 统、

混 凝 土 搅 拌 自 动 化 生 产 控 制 系 统。 下 面 就 自 己 的 一 点 经 验

体 会 与 读 者 分 享。

---- 一 般 情 况 下 的 串 口 通 讯 无 非 采 用 两 种 方 法: 一 是 利 用Windows 的 通

讯API 函 数, 二 是 利 用VC++( 或 其 它 语 言) 的 标 准 通 讯 函 数_inp、_outp 来 实 现。

---- Windows 的API 函 数 虽 然 使 用 面 很 广, 但 由 于 比 较 复 杂 而 有 一 定 的 难

度, 故 简 单 述 之, 其 编 程 过 程 分 为 初 始 化 串 口、 配 置 串 口、 超 时 控 制、

数 据 传 输、 关 闭 串 口 等。

---- VC++ 串 口 通 讯

---- 一、 初 始 化 串 行 通 讯 口

---- 通 过 计 算 机 串 口 进 行 通 讯 之 前, 必 须 根 据 监 控 设 备 的 有 关 通 讯 参

数, 约 定 双 方 的 通 讯 方 式, 包 括 波 特 率 的 设 置、 奇 偶 位 校 验 及 停 止 位 的

设 立。 确 定 数 据 传 输 帧 格 式, 确 定UART 操 作 方 式。 逐 个 对 线 路 控 制 寄 存

器、 波 特 率 因 子 寄 存 器 和M0DEM 寄 存 器 写 入 操 作。

---- 先 确 定 计 算 机 的 通 讯 口 地 址 是3F8 还 是2F8( 根 据 用 户 的 不 同 要 求, 可

加 插 卡 而 有3E8、2E8, 甚 至 使 用 多 用 户 卡 而 有3220、3228、4220、4228、5220、5228)

, 例 中 以2F8 做 为 端 口 地 址。 利 用_outp(PORT,0x0c) 将 波 特 率 设 置 为9600,(_outp

(PORT,0x30) 将 波 特 率 设 置 为2400,_outp(PORT,0x18) 将 波 特 率 设 置 为4800)。

---- 数 据 传 输 格 式 则 由 地 址PORT+3 确 定, 如 选 用 七 位 一 停 止 位 偶 校 验, 则

采 用outp(PORT+3,0x3a) 及outp(PORT+3 ,0x1a) 两 个 语 句; 如 选 用 八 位 一 停 止 位 无

奇 偶 校 验, 则 采 用outp(PORT+3,0x3a) 及outp(PORT+3 ,0x03) 两 个 语 句; 至 于 为 什 么

, 可 参 考 有 关 异 步 通 讯 数 据 格 式 方 面 的 书。

---- 二、 查 询 发 送 流 程

---- 只 需CPU 检 测 到UART 发 送 器 保 持 寄 存 器 为 空, 即 向UART 输 出 一 个 字 符。 发

送 方 首 先 输 出RTS 和DTR 有 效, 检 测MODEM 寄 存 器, 只 有 收 到DEC 输 入 的CTS、DSR 有

效,CPU 才 向UART 输 出 一 发 送 字 符。

---- 三、 查 询 接 收 流 程

---- 只 需CPU 检 测 到UART 接 收 器 数 据 准 备 就 绪, 即 可 从 接 收 器 数 据 寄 存 器 中

读 取 一 字 符。 接 收 方 首 先 输 出 数 据 终 端 就 绪 有 效(DTR=1), 然 后 检 测MODEM 状

态 寄 存 器, 只 有DSR=1,CPU 才 接 收 一 字 符。

---- 程 序 例

---- 为 了 编 程 的 方 便, 我 把 实 时 监 控 过 程 中 的 串 口 初 始 化、 串 口 接 收、 串

口 发 送 等 以 子 程 序 的 形 式 呈 现。

确 定 通 讯 口 地 址:int PORT=0x2F8;

接 收 字 符 子 程 序:

char near readbyte(void)

{

int time_limit;

char sta;

time_limit=5000;

while ((sta=inp(PORT+5) & 0x01) !=1)

{ time_limit--;

if (time_limit==0) {

/*printf("s=%4xm ",sta);*/return 0;}

}

return inp(PORT);

printf("%s",inp(PORT));

}

发 送 字 符 子 程 序:

void sendbyte(unsigned char sdata)

{

long int time_limit;

time_limit=50000;

while ((inp(PORT+5) & 0xf0) != 0x60)

{ time_limit--;

if (time_limit==0) break;

}

outp(PORT,sdata);

}

串 口 初 始 化 子 程 序:

void init_com(PORT)

{

char i;

outp(PORT+3,0x80);

outp(PORT ,0x0C); /* baud rate 9600 */

outp(PORT+1,0);

/*8bit 1stop no even */

outp(PORT+3 ,0x3a);

outp(PORT+3 ,0x03);

i=inp(PORT+5) & 0xfe;

outp(PORT+5,i);

}

字 符 串 发 送 子 程 序:

void sendstrn(char *mess)

{

for (; *mess; mess++)

sendbyte(*mess);

sendbyte(0X0D);

/*sendbyte(0X0A); */

}

简 单 的 主 程 序:

init_com(ox2f8);

sendstrn(PORT,"FUNC");

j=0;

line=0;

do {

for(i=0;i<25;i++) ttemp[i]="\0" ; if(readbyte(PORT)="='M')" { i="0;" do { ttemp[i]="readbyte(PORT);" i++; }

while((ttemp[i-2]!="13)&&(ttemp[i-1]!=10));" line++; dcqd.TextOut(450,line*20+180,ttemp);} j++;}

while((j< 1000)&&(line< 5)); Wed, 16 Sep 1998 "acejet" < cejet@public1.ptt.js.cn>

利 用WINDOWS 定 时 器 实 现 串 口 通 信 控 制

---- 1: 串 口 通 信 概 述

---- 在 计 算 机 外 设 中,RS-232 串 口 因 为 其 组 成 方 式 简 单, 编 程 控 制 方 便 而 成 为 最 为 应 用

广 泛 的I/O 通 道 之 一。 在 硬 件 连 接 方 面, 最 为 简 单 的 方 式 一 条 公 用 地 线 和 两 条 信 号 线 即

可 组 成 串 行 双 工 通 信 线 路。 而 在 软 件 编 程 控 制 方 面, 操 作 系 统 亦 提 供 了 对 应 的 编 程 接

口, 使 的 开 发 者 能 灵 活 的 控 制 串 口 工 作。

---- 在DOS 的 系 统 接 口 中DOS INT21H 的03H 和04H 号 功 能 调 用 为 异 步 串 行 通 信 的 接 收 和 发 送 功

能, 而BIOS INT14H 有4 组 功 能 调 用 为 串 行 通 信 服 务, 正 因 为 如 此 在DOS 中 采 用 寄 存 器 直 接 读

写、BIOS 调 用、 通 信 中 断 程 序 等 方 法 可 以 比 较 容 易 实 现 串 口 通 信。 但 是 在WINDOWS 中 由 于

WINDOWS 采 用 消 息 驱 动 和 设 备 统 一 管 理, 以 及 利 用 消 息 对 列 进 行 程 序 控 制, 所 以DOS 下 的

方 法 都 不 宜 采 用. 在WINDOWS 中 为 实 现 串 口 通 信,WINDOWS 的SDK 提 供 了 完 备 的API 函 数 和 以 中

断 方 式 驱 动 的 通 信 驱 动 程 序, 使 编 程 变 的 较 为 容 易。

---- WINDOWS 提 供 的 标 准 通 信API 函 数 很 多(win16 和win32 中 有 所 不 同), 一 般 来 说 常 用 的 有:

win16 win32 作用

OpenComm CreateFile (打开通信口);

BuildCommDCB BuildCommDCB (填充DCB数据结构);

SetCommState SetCommStae (设置通信口状态);

FlushComm PurgeComm (清除通信接收或发送缓冲区);

GetCommError ClearCommError

(恢复或取得通信口的状态);

ReadComm ReadFile (从通信接收缓冲区读字符);

WriteComm WriteFile (向通信发送缓冲区写字符);

CloseComm CloseHandle (关闭通信口);

---- 在win16 中 通 信 设 备 由 专 门 通 信 函 数 处 理, 而wn32 中 将 这 些 设 备 当 作 面 向 文 件 的 设 备 来 处 理。

---- 2:WINDOWS 定 时 器

---- WINDOWS 系 统 中, 除 键 盘 和 鼠 标 外, 定 时 器 亦 是 一 种 输 入 设 备, 使 用 定 时 器 的 方 法 比 较 简

单, 通 常 告 诉WINDOWS 一 个 时 间 间 隔, 然 后WINDOWS 以 此 时 间 间 隔 周 期 性 触 发 程 序。

---- WINDOWS 的WM_ TIMER 消 息 与BIOS 的08H、1CH 不 同, 传 统 的PC 驻 留 程 序 地 接 收 到08H 中 断 和1CH 中 断,

而WINDOWS 应 用 程 序 可 以 周 期 接 收 到WM_TIMER 消 息, 这 两 者 道 理 不 一 样. 在WINDOWS 中, 有SYSTEM.DRV 处

理 硬 件 定 时 中 断,WINDOWS 应 用 程 序 从 消 息 队 列 中 获 得WM_TIMER 消 息 而 不 会 收 到08H 硬 件 中 断,WM_TIMER

消 息 并 不 是 随 时' 插 入' 应 用 程 序, 而 是 应 用 程 序 主 动 地、 有 次 序 的 从 消 息 队 列 中 得 到 时 间 消 息。

而DOS 中 由 计 算 机 每 隔18.2s 产 生08H 中 断,08H 中 断 再 调 用1CH 中 断, 硬 件 中 断 有 一 重 要 特 征, 即 发

生 的 时 间 是 在 任 何 时 刻, 不 管 程 序 运 行 到 任 何 时 刻, 硬 件 中 断 都 可 以 插 入 程 序。

---- 3:WINDOWS 中 利 用 定 时 器 控 制 串 口 通 信

---- 在WINDOWS 中 由 串 口 进 来 的 数 据 经 过 两 个 缓 冲 区, 首 先 是 硬 件 的 接 收 缓 冲 区( 这 种 缓 冲 通 常

有8 位 或16 位), 然 后 进 入 应 用 程 序 设 置 的 串 口 接 收 缓 冲( 这 个 缓 冲 大 小 在WIN16 中 由 应 用 程 序 设

置, 而WIN32 中 由 于 串 口 作 为 面 向 文 件 设 备 处 理, 所 以 缓 冲 大 小 不 用 设 置), 数 据 从 硬 件 缓 冲 到

应 用 程 序 缓 冲 这 一 过 程 由WINDOWS 本 身 完 成, 作 为 编 程 者 不 用 考 虑 它, 应 用 程 序 只 关 心 何 时 有

数 据。 虽 然WINDOWS 提 供WM_COMMNOTIFY 消 息, 但 是 笔 者 在 使 用 中 发 现 利 用 它 控 制 比 较 麻 烦, 而 且 经

常 发 生 丢 失 数 据 现 象, 而 利 用 定 时 器 处 理 则 可 达 到 控 制 灵 活 的 效 果。

---- 在 程 序 初 始 化 串 口 以 后, 在 程 序 中 为 串 口 设 立 一 个 定 时 器, 利 用 该 定 时 器 定 时 检 查 串 口

状 态, 如 果 有 数 据 则 开 始 接 收 处 理, 否 则 该 定 时 器 消 息 无 处 理。 也 许 有 人 认 为 有 在 数 据 处 理

未 结 束 时 下 一 个 定 时 器 消 息 会 发 生 的 现 象, 对 于 这 种 情 况 除 了 加 大 定 时 器 间 隔 外 更 为 主 动 的

办 法 是 在 发 现 有 数 据 时 关 闭 定 时 器, 然 后 在 数 据 接 收 完 后 在 打 开。

---- 4.WINDOWS 通 信 实 例

---- 通 信 实 例 的 环 境 为:586/100 计 算 机; 中 文WINDOWS`95; 对 方 为 交 换 机。 通 信 参 数 为: 波 特 率9600bps;

数 据 位8 位; 停 止 位1; 无 校 验。 通 信 协 议: 帧 开 始 和 结 束 都 为0X7E0X7E, 帧 数 据 不 定 长。

编程环境:BORLANDC C++5.0,WINDOWS `95

//======================================

// FUNCTION: short InitCom()

// PURPOSE: init com1

// RETURN VALUE:

// 1-------No error

// 0-------init Error

//======================================

short InitCom()

{

if(com_select==1)

{

IDCommDevice=CreateFile("COM1",

GENERIC_READ|GENERIC_WRITE,

0,NULL,OPEN_EXISTING,0,NULL);

if(IDCommDevice==INVALID_HANDLE_VALUE)

{

MessageBox(hWnd,"OPenComm error","info",MB_OK);

CloseHandle(IDCommDevice);

return 0;

}

BuildCommDCB("COM1:9600,N,8,1",&dCB);

}else

{

IDCommDevice=CreateFile("COM2",

GENERIC_READ|GENERIC_WRITE,

0,NULL,OPEN_EXISTING,0,NULL);

if(IDCommDevice==INVALID_HANDLE_VALUE)

{

MessageBox(hWnd,"OPenComm error","info",MB_OK);

CloseHandle(IDCommDevice);

return 0;

}

BuildCommDCB("COM2:9600,N,8,1",&dCB);

}

if (SetCommState(IDCommDevice,&dCB)<0) { MessageBox(hWnd,"Read SET CommState error","info",MB_OK);

CloseHandle(IDCommDevice); return 0; } if(!PurgeComm(IDCommDevice,PURGE_RXCLEAR))

{ MessageBox(hWnd,"IN buffer flush error","info",MB_OK); CloseHandle(IDCommDevice); return 0; }

if(!PurgeComm(IDCommDevice,PURGE_TXCLEAR)) { MessageBox(hWnd,"OUT buffer flush error","info",MB_OK);

CloseHandle(IDCommDevice); return 0; } return 1; } . . . .

/****************************************** Function:JKTWndProc(HWND hDlg,UINT message, WPARAM wParam,LPARAM lParam)

Purpose: 处理交换机的信息 Message: WM_INITDIALOG---对话框初化 WM_COMMAND------所有对窗口的操作

**********************************************/

#pragma argsused LRESULT CALLBACK JKTWndProc(HWND hDlg, UINT message, UINT wParam, LONG lParam) {

LPDWORD num; short sign; char *temp_char; short x; BOOL lresult; int t_com; hdlg="hDlg;" switch (message)

{ case WM_TIMER: switch(LOWORD(wParam)) { case TIMER_REC: ClearCommError(IDCommDevice, &lpErrors,&ComStat);

if(ComStat.cbInQue!="0)" PostMessage(hDlg,WM_COMMAND ,IDP_DEALDATA,0L); break; } return 0;

case WM_COMMAND: switch(LOWORD(wParam)) { case IDP_DEALDATA: //处理交换机发的信息

for(t_com="0;t_com<" ComStat.cbInQue;t_com++)

{ num="0;" l result="ReadFile(IDCommDevice," temp_char,1,&n um,&lpOverlapped); temp_char++; }

return 0; case IDBEGIN: //开始工作 sign="InitCom(hDlg);" if(sign="=0)"

{ MessageBox(hDlg,"串口设置失败!","Information",MB_OK); return 0; }

i SetTimer(hDlg,TIMER_REC,20,NULL); return 0; } break; } return (0); } . . . . .

---- 由 于 篇 幅 有 限, 只 给 出 程 序 的 小 部 分, 由 于 水 平 有 限, 有 所 失 误 的 地 方 请 多 多 指 正。

--------------------------------------------------------------------------------

 

VC++下串行通信的编程方法

张 力 吕振肃 赵庆林

1 引言

  串行通信程序是微机监控软件中基础、关键的一个环节,如何有效地开发通信程序,是软件开发人员所关心的话题。

Microsoft公司从1993年推出VC++1.0开始,不断升级,从 VC++1.52到目前最新的 VC++5.0,拥有大量的用户,用VC++开发

串行通信程序也随版本的不同各有特点。本文详细地介绍了用VC编写通信程序的两种方法:

  . 用API(Application Programm Interface用户程序接口)函数;

  . 用OLE(Object Linking and Embedding对象链接与嵌入)控件。

2 用API函数实现串行通信

  API函数是Windows程序开发使用的主要函数集,其中提供了十几个专门的通信函数。对应Windows3.x和Win95及 NT开发

环境,有16位和32位通信API函数,VC++ 2.0和以前的版本都保持16位和32位两个版本,从 VC++4.0 以后,软件只支持32位

应用程序的开发。目前较多的应用是在Win95及NT的32位开发环境下,但也存在着Windows3.x下开发的16位通信程序向32位程

序的移植问题。基于这种考虑,16位通信函数也列举出来。

2.1 主要通信函数的对比

  32位通信函数与16位函数的主要区别在于,32位API函数把对通信口的操作当作一个普通文件。这是因为在Windows95和NT

下,WM-COMMNOTIFY消息已被取消,操作系统已将通信设备封装起来了,系统为每个通信设备定义了用户可定义大小的读/写缓

冲区,应用程序只对读/写缓冲区操作,而不用和通信驱动程序打交道。

功能描述16位函数改进的32位函数打开通信资源OpenCommCreatFile关闭通信资源CloseCommCloseHandle读通信缓冲区ReadCommReadFile

写通信缓冲区WriteCommWriteFile设置通信事件SetCommEventSetCommMask通信错误检查GetCommErrorClearCommError

2.2 用API函数编程的过程

  首先由应用向导(AppWizard)生成程序的主框架。在集成开发环境下选File/New菜单(以VC5.0为例),弹出New对话框,单击project

标签后选MFC AppWizard (exe)项,在创建的第一步选中单文档,其他五步接受缺省选项,只需编辑生成的视类.cpp文件即可。可以用两种

方法编程:一种是利用Windows的 WM-COMMNOTIFY消息,映射出该消息的处理函数,通信编码就放在该函数中,这主要适用于VC2.0及以前

的版本 ;另一种方法是直接在.CPP文件中编写通信部分,把通信当作视类的成员函数。不论用哪种方法,编程思路是相同的:设置初始

化并打开串口-配置串口-在双方间传输数据,并进行校验等处理-不需要时释放串口,供其他程序使用。

2.3 注意事项

  在Windows 3.x 中,有专门的消息WM-COMMNOTIFY用于通信。在Windows95和NT下,该消息已被取消,但用户也可以用自定义的

WM-COMMNOTIFY消息,这时要注意消息映射的声明必须如下:

  ON-MESSAGE (WM-COMMNOTIFY,memberFxn); 其中memberFxn是该消息的处理函数名。

  Win95中新增了基于线程的多任务,当应用程序希望仅在通信设备有数据出现时才对其操作,应创建线程来监视通信设备。考虑

到通信较费时,建议使用多线程来管理通信,让通信在后台完成。在VC++的 MFC(Microsoft Foundation Classes 微软基础类库)

中提供了专门管理线程的类 CwinThread,同时VC++还提供了一些与线程有关的全局函数,如AfxBeginThread等。多线程中资源共享

时,必须借助一些Win32同步函数来协调线程的活动,避免冲突。VC的MFC也提供了几个与同步有关的类,如CSyncObject、CMutex、

CSemaphore、Cevent等。在视类的.cpp文件下可创建通信线程,并根据实际需要确定线程的优先级,合理调用 API函数以实现通信。

3 用OLE通信控件实现串行通信

  用VC++的OLE控件实现串行通信,是一种简洁的编程方法。VC4.0版和VC5.0版所提供的通信控件略有不同,下面针对这两个版本,

对OLE通信控件做具体介绍。

3.1 通信控件的插入

  在VC++4.0 下,首先生成一个项目,或者打开一个存在的项目。选择菜单Insert/Component…,出现Component Gallery窗口,

选中OLE Controls下的Microsoft CommControl,双击就将控件嵌入到项目文件中了。在VC++5.0下,选菜单Project/Add To Project,

将会有一子菜单, 选中 Component and Controls项(注意如果没有打开项目,Component项不会出现在子菜单项中),接着双击

Registered ActiveX Controls,在其中会找到通信控件5.0,双击之便可插入。此时打开对话框编辑器,在工具条中会看到新增的通信

控件。

3.2 在程序中使用通信控件

  象所有其他OLE控件一样,通信控件也是用一系列的属性和用户接口,用户用Get…函数得到属性的当前值,用 Set…函数设置属

性值。控件提供了27个属性,大部分属性仅和Modem连接有关,现将常用的属性列举如下:

  CommPort: 设置并返回通信口号,缺省值为COM1,可设置1-16个。

  Settings: 设置并返回波特率、奇偶校验、数据位、停止位的字符串。其中波特率的范围为300b/s-19200b/s。

  PortOpen: 设置并返回通信口的状态,同时用来打开和关闭通信口。

  InputLen: 决定每次Input读入的字符个数,缺省为0,表示读取接收缓冲区的全部内容。

  Input: 读入并清除接收缓冲区的字符。

  InBufferCount: 返回接收缓冲区已接收的字符数,通过置0可清除接收缓冲区。

  Output: 将发送的字符串或数组写到发送缓冲区。

  InputMode: 定义Inpput属性获得数据的方式(VC5.0新增属性)。

  Rthreshold: 设置、返回在通信控件置 ComEvRecieve并激发 OnComm事件前要接收的字符数。

  SThreshold: 设置、返回通信控件置ComEvSend并激发OnComm事件前发送缓冲区中的最少字符数。

  将通信控件放到对话框中,当作一个普通的控件来用。控件提供了两种处理方法:一是查询,一是事件驱动。对于较简单的通

信任务,可通过查询串口的CommEvent属性来了解最近发生的事件或错误并进行相应的处理,较复杂的通信任务就应使用事件驱动接

口。用事件驱动方法时,要定义CMSComm变量,以后针对此对象操作即可。添加消息处理函数时要用类向导ClassWizard,选中定义

的 CMSComm变量并单击,在消息栏双击 OnComm项就生成了消息处理函数。 在编写发送和接收函数时,要重点注意几个属性的用法。

比如InBufferCount属性返回接收缓冲区中已收到的字节数,查询方法可利用该属性,看是否收到了规定数目的字节。还有 Rthreshold

和 SThreshold属性,对由发送和接收激发的OnComm事件起着决定性的作用。Rthreshold 的值代表每当接收缓冲区中有该数目的字

符时就产生 OnComm事件, 这对固定长度的帧接收非常有用,可将 Rthreshold 设为帧长度,每收到一帧就会激发 OnComm事件,在消

息处理函数中添加代码就能完成接收后的处理。发送比接收简单,只不过用SThreshold属性。

  概括起来,用控件实现串行通信应该遵循和用API函数编写程序相同的过程,只不过这里用控件的属性代替了API函数。

3.3 其它注意事项

  VC4.0下和VC5.0下Input属性的返回值不同。在 VC4.0下Input 返回的是CString类,而在VC5.0下,返回 COleVariant类。

COleVariant类封装了VARIANT数据类型,该结构也是Output属性所要求的类型。要用好控件就必须了解 VARIANT结构, 该结构囊括

了所有常用的简单数据类型。但是结构中每次可用的数据类型只有一种,由结构的成员vt识别。例如对字符串bstrVal,vt=8,对short

类型,vt=2等等。同时注意到VC5.0控件增加了 InputMode属性,提供两种输入模式:文本和二进制。两种方式决定了Input接收的两种

数据类型。在文本方式下,Input返回值在成员bstrVal中,类型为vt=8,这和VC4.0下返回CString类是相同的。在二进制模式下,Input

接受值在安全数组parray中,类型为 vt=0x2000 ,这是编程时容易忽略的地方。一般上下位机通信时,下位机的数据是以16进制形式存

放,编写通信程序建议选择二进制输入模式。用文本模式就必须在编码时仔细考虑,否则容易丢失数据。另外,用事件驱动方法编程时

还应注意,OnComm事件在通信事件或错误发生时都会激发, 在处理函数中应将各种可能的情况列举出来分别处理,很重要的一点是及时

处理通信错误。

4 结束语

  总而言之,比起繁多的API函数,通信控件不失为一种简洁有效的实现串行通信的途径。只要掌握好控件的属性,就能轻松地编写

通信程序。笔者在编写一套电力系统的实时监控软件时,用VC++5.0下的通信控件成功地实现了微机与下位机--3720ACM 仪表之间的通

信,该仪表是加拿大PML公司生产的高级电力综合监控仪表。

张 力 硕士研究生。研究方向:计算机控制与信息工程。 吕振肃 硕士,教授。从事计算机控制与信息工程、CAD等方面的教学与

研究工作。 赵庆林 硕士研究生。研究方向:计算机控制与信息工程。

作者单位: 兰州大学电子与信息科学系 甘肃.兰州(730000)

参考文献

 [1] [美]Microsoft Corporation.Microsoft Win32程序员参考大全(二). 北京:清华大学出版社,1995

 [2] [美]Scott Stanfield Ralph Arvesen.Visual C++ 4开发人员指南.北京:机械工业出版社,1997

串行通讯通用接口程序设计

  卢望忠

  串行通讯在通讯领域被广泛应用,标准的RS232接口已成为计算机、计算机外设、交换机和许多通讯设备的标准接口。微机与微机、

微机与外设、微机与程控交换机等都可以通过RS232接口进行方便的连接,以实现控制外设和传输数据等目的。

  在串行接口连接中,按连接方式可分为两类:有规程连接和无规程连接。无规程连接原理比较简单,通讯双方无握手过程。一方有

数据需要发送,则立即通过串行接口发送出去,另一方被动接受。该方式虽然容易实现但数据发送的完整性却无法得到保证,在关键任

务的连接中均不采用该方式,笔者在此不再赘述。而有规程通讯过程则不同,在甲方有数据发送请求时,则向乙方发送"请求发送数据"

命令。乙方收到后,如准备就绪,则回送确认信息。甲方得到乙方的确认后方可发送数据。在大多数情况下,乙方要对收到的数据进行

校验,校验正确发送"通讯终止"命令,否则可发送"重发"命令。在实践过程中,有规程的串行连接过程通常要复杂得多,而且不同的设

备厂商都有不同的通讯规程,目前尚未形成统一的标准。即便是同一种类型的外设,在与不同品牌的设备进行连接时,也要修改原代码

来满足不同种类规程的要求,此类问题常常会困扰程序开发人员。笔者在实际工作中总结出一种简单实用的通用规程的程序设计方法,

在与多种串行通讯设备的连接中取得了满意的效果。在此简单介绍其原理,并与读者共同探讨。

  在通讯的过程中,虽然规程的组合千变万化,但通讯过程中的每一步都有很多共性的东西存在。通过对多种串行通讯设备的研究,

笔者将通讯过程的每一步骤进行总结,抽象出共性的东西,在此称作通讯"步骤对象"。通讯步骤对象的定义和应用是该程序设计方法的

核心内容。每一种通讯规程都可以定义多个通讯步骤对象。整个通讯过程的的实现,就是每一个通信步骤对象依次被激活并执行各自定

义的方法的一个连贯过程。

  通讯步骤对象(objComStep)一般包括如下属性:

  (1)序号(StepId):每一个通讯步骤都有一个唯一的标识号,一般依据该步骤在整个规程中的执行顺序号来定义。

  (2)状态(Status):每一个步骤可能有四种状态:①发送:发送命令代码;②接受:接受命令代码;③判断:对上一步骤接收到

的命令进行判断,检验接收的命令或数据是否正确;④接收数据:接收真正要传输的数据。

  (3)命令代码(Command):发送或进行接收判断的控制代码。

  (4)数据代码(Data):发送或接收判断的数据代码。

  (5)肯定序号(AckStep):在发送成功后跳转到肯定序号指定的步骤;如接收判断成功,同样跳转到肯定行号。

  (6)否定序号(NakStep):在发送不成功后跳转到否定序号指定的步骤;如接收判断不成功,同样跳转到否定行号。

  (7)延迟(DelayTime):当前步骤执行完毕,延迟相应时间后跳转到下一个步骤。

  (8)循环上限(MaxRunCounts):由于每一个步骤可被重复执行,对于每一个步骤有一个循环上限,如果执行次数大于该数值,

则跳转到循环跳转序号。

  (9)循环跳转序号(BeyondStep):对于当前步骤被循环执行的次数超过上限时跳转到所指定的步骤。

  (10)计数器(Counter):记载执行次数。

  通讯步骤对象的方法定义如下:

  (1)发送(Send):通过串行端口发送数据或命令代码。

  (2)接收(Receive):从串行端口接收数据或命令代码。

  (3)判断(Identify):判断接收数据是否正确。

  (4)输出数据(OutPut):将得到的数据通过网络或消息传送给其它程序模块。

  完成了对objComSetp的定义,下一步我们就能够很容易地用程序语言来将其实现。在实际编程过程中,笔者将程序总体结构分为

步骤定义模块和步骤执行模块。通过步骤定义模块定义所用规程的每一个步骤的属性,将属性信息写入配置文件。步骤执行模块按配

置文件中的信息依次创建步骤对象,由该对象执行相应的方法。步骤执行模块总体控制每个objComStep的激活顺序,直至完成整个通

讯规程。并将在通讯过程中得到的数据输出给其它程序模块。

  下面是一个串行通讯的实例,笔者用上述的程序设计方法实现了PC机和NEC程控交换机之间的通讯。为了方便叙述,笔者将该程

序称为"通讯模块"。通讯模块的目的是通过串行通讯的方法来完成用PC机对NEC电话程控交换机物业管理PMS(Property Management

System)系统进行控制的任务。系统成功地实现了对交换机的控制,通过PC机实现自动开通、关闭、长途权限、电话叫早、电话留言

、电话免扰、电话设置房间状态等多种功能。

  图1是该规程的状态图。

  图1

  说明:(1)在接收和发送之间的时间间隔是1秒;(2)在接收到NAK后,重复发送的次数为3;(3)接收方没有回应(NO ANSWER)

而重复询问的次数为15;(4)eot规程终止。

  在通讯模块中,首先将通讯规程的每一步骤用步骤对象的定义方式定义该规程,其具体步骤如下:

序号 状态 命令代码 数据代码 肯定序号 否定序号 延迟 循环上限 循环跳转序号

1 发送 sa,ua,enq 2 1 1 15    9

2 接收 ack 5    3 1 1    9

3 判断 nak 1    4 1 3    9

4 判断 无回应 1    1 1 15    9

5 发送 data,bcc 6 5    1 15 9   

6 接收 ack 9    7 1    9

7 判断 nak 5    8 1 3    9

8 判断 无回应 5    5 1 15    9

9 发送 eot 结束 0 1   

  完成上述配置后,将信息写入配置文件或数据库。在通讯模块执行时,读入该配置信息 ,依次生成通讯步骤对象。对象方法的

执行过程即是通讯规程的执行行过程。在实际编程中可将该模块与其它程序连接编译成EXE文件或生成独立的DLL文件供其它通讯程

序调用,均可取得满意的效果。在其它类型的通讯、消息传递、网络数据传输中均可用该思路写出通用的接口程序,解决异种机型连

接的通讯问题。

VC在win95下编写用于串行通讯的程序

------作者 清华BBS 贝贝

既然有这么多人问这个文体,贝贝就给个Visual C++ 4.2写的

Window 95串口通讯函数集合(只适用于32位)

需要说明的是:这是我程序的一部分,因此有一些与具体应用无关的部分。

但我觉得关键是原理,而不是程序本身.后面有些使用介绍,帮助理解这长的程序。

头文件(.H)

#include "StdAfx.h"

#define GWL_PGPSINFO 0

#define GPSEXTRABYTES sizeof( LONG )

#define MAXPORTS 4

#define CN_SEND WM_USER+100

#define RXQUEUE 4096

#define TXQUEUE 4096

// cursor states

#define CS_HIDE 0x00

#define CS_SHOW 0x01

// Flow control flags

#define FC_DTRDSR 0x01

#define FC_RTSCTS 0x02

#define FC_XONXOFF 0x04

// ascii definitions

#define ASCII_BEL 0x07

#define ASCII_BS 0x08

#define ASCII_LF 0x0A

#define ASCII_CR 0x0D

#define ASCII_XON 0x11

#define ASCII_XOFF 0x13

// data structures

typedef struct tagGPSINFO

{

HANDLE idComDev;

BYTE bPort;

BOOL fConnected;

BYTE bByteSize,bParity,bStopBits;

DWORD dwBaudRate;

HANDLE hPostEvent,hWatchThread,hWatchEvent;

HWND hTermWnd;

DWORD dwThreadID;

OVERLAPPED osWrite,osRead;

} GPSINFO, *PGPSINFO ;

#define COMDEV( x ) (x -> idComDev)

#define PORT( x ) (x -> bPort)

#define CONNECTED( x ) (x -> fConnected)

#define BYTESIZE( x ) (x -> bByteSize)

#define PARITY( x ) (x -> bParity)

#define STOPBITS( x ) (x -> bStopBits)

#define BAUDRATE( x ) (x -> dwBaudRate)

#define POSTEVENT( x ) (x -> hPostEvent)

#define HTHREAD( x ) (x -> hWatchThread)

#define THREADID( x ) (x -> dwThreadID)

#define WRITE_OS( x ) (x -> osWrite)

#define READ_OS( x ) (x -> osRead)

// function prototypes (private)

LRESULT NEAR CreateGPSInfo(HWND,BYTE nPort=1);

BOOL NEAR DestroyGPSInfo();

int NEAR ReadCommBlock(LPSTR,int);

BOOL NEAR WriteCommBlock(LPSTR,DWORD);

BOOL NEAR OpenConnection();

BOOL NEAR SetupConnection();

BOOL NEAR CloseConnection();

// function prototypes (public)

DWORD FAR PASCAL CommWatchProc(LPSTR);

具体实现请看下文(为了这文章,我都段线2次了)

CPP实现部分:

#include "StdAfx.h"

#include "Com.h"

HWND hGPSWnd=NULL;

PGPSINFO npGPSInfo=NULL;

LRESULT NEAR CreateGPSInfo(HWND hWnd,BYTE nPort)

{

if (NULL==(npGPSInfo=(PGPSINFO)LocalAlloc(LPTR,sizeof(GPSINFO))))

return ((LRESULT)-1) ;

hGPSWnd=hWnd;

COMDEV(npGPSInfo)=0;

CONNECTED(npGPSInfo)=FALSE;

PORT(npGPSInfo)=nPort;

BAUDRATE(npGPSInfo)=CBR_9600;

BYTESIZE(npGPSInfo)=8;

PARITY(npGPSInfo)=NOPARITY;

STOPBITS(npGPSInfo)=ONESTOPBIT;

WRITE_OS(npGPSInfo).Offset=0;

WRITE_OS(npGPSInfo).OffsetHigh=0;

READ_OS(npGPSInfo).Offset=0;

READ_OS(npGPSInfo).OffsetHigh=0;

// create I/O event used for overlapped reads / writes

READ_OS(npGPSInfo).hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

if (READ_OS(npGPSInfo).hEvent==NULL)

{ LocalFree( npGPSInfo ) ;

return ( -1 ) ;

}

WRITE_OS(npGPSInfo).hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

if (NULL==WRITE_OS(npGPSInfo).hEvent)

{ CloseHandle(READ_OS(npGPSInfo).hEvent);

LocalFree(npGPSInfo) ;

return (-1) ;

}

return ( (LRESULT) TRUE ) ;

}

BOOL NEAR DestroyGPSInfo()

{

if (!npGPSInfo) return (FALSE);

if (CONNECTED(npGPSInfo)) CloseConnection();

CloseHandle(READ_OS(npGPSInfo).hEvent);

CloseHandle(WRITE_OS(npGPSInfo).hEvent);

CloseHandle(POSTEVENT(npGPSInfo));

LocalFree(npGPSInfo);

return (TRUE);

}

BOOL NEAR OpenConnection()

{

char szPort[15];

BOOL fRetVal;

HCURSOR hOldCursor,hWaitCursor;

HANDLE hCommWatchThread;

DWORD dwThreadID;

COMMTIMEOUTS CommTimeOuts;

if (!npGPSInfo) return (FALSE);

hWaitCursor=LoadCursor(NULL,IDC_WAIT) ;

hOldCursor=SetCursor(hWaitCursor) ;

wsprintf(szPort,"COM%d",PORT(npGPSInfo));

if

((COMDEV(npGPSInfo)=CreateFile(szPort,GENERIC_READ|GENERIC_WRITE,

0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,

NULL))==(HANDLE)-1)

return ( FALSE ) ;

else

{ SetCommMask(COMDEV(npGPSInfo),EV_RXCHAR);

SetupComm(COMDEV(npGPSInfo),4096,4096);

PurgeComm(COMDEV(npGPSInfo),PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_R

XCLEAR);

CommTimeOuts.ReadIntervalTimeout=0xFFFFFFFF;

CommTimeOuts.ReadTotalTimeoutMultiplier=0;

CommTimeOuts.ReadTotalTimeoutConstant=1000;

CommTimeOuts.WriteTotalTimeoutMultiplier=0;

CommTimeOuts.WriteTotalTimeoutConstant=1000;

SetCommTimeouts(COMDEV(npGPSInfo),&CommTimeOuts);

}

fRetVal=SetupConnection();

if (fRetVal)

{ CONNECTED(npGPSInfo)=TRUE;

if

(NULL==(hCommWatchThread=CreateThread((LPSECURITY_ATTRIBUTES)NULL,

0,(LPTHREAD_START_ROUTINE)CommWatchProc,

(LPVOID)NULL,0,&dwThreadID)))

{ CONNECTED(npGPSInfo)=FALSE;

CloseHandle(COMDEV(npGPSInfo));

fRetVal=FALSE;

}

else

{ THREADID(npGPSInfo)=dwThreadID;

HTHREAD(npGPSInfo)=hCommWatchThread;

EscapeCommFunction(COMDEV(npGPSInfo),SETDTR);

}

}

else

{ CONNECTED(npGPSInfo)=FALSE;

CloseHandle(COMDEV(npGPSInfo));

}

SetCursor(hOldCursor);

return (fRetVal);

}

BOOL NEAR SetupConnection()

{ BOOL fRetVal;

DCB dcb;

if (!npGPSInfo) return(FALSE);

dcb.DCBlength=sizeof(DCB);

GetCommState(COMDEV(npGPSInfo),&dcb);

dcb.BaudRate=BAUDRATE(npGPSInfo);

dcb.ByteSize=BYTESIZE(npGPSInfo);

dcb.Parity=PARITY(npGPSInfo);

dcb.StopBits=STOPBITS(npGPSInfo);

dcb.fOutxDsrFlow=FALSE;

dcb.fDtrControl=DTR_CONTROL_ENABLE;

dcb.fOutxCtsFlow=FALSE;

dcb.fRtsControl=RTS_CONTROL_ENABLE;

dcb.fInX=dcb.fOutX=FALSE;

dcb.fBinary=TRUE;

dcb.fParity=TRUE;

fRetVal=SetCommState(COMDEV(npGPSInfo),&dcb);

return (fRetVal);

}

BOOL NEAR CloseConnection()

{

if (!npGPSInfo) return(FALSE);

CONNECTED(npGPSInfo)=FALSE;

SetCommMask(COMDEV(npGPSInfo),0);

while(THREADID(npGPSInfo)!=0);

EscapeCommFunction(COMDEV(npGPSInfo),CLRDTR);

PurgeComm(COMDEV(npGPSInfo),PURGE_TXABORT|PURGE_RXABORT|

PURGE_TXCLEAR|PURGE_RXCLEAR);

CloseHandle(COMDEV(npGPSInfo));

return (TRUE);

}

int NEAR ReadCommBlock(LPSTR lpszBlock,int nMaxLength)

{

BOOL fReadStat ;

COMSTAT ComStat ;

DWORD dwErrorFlags;

DWORD dwLength;

DWORD dwError;

if (!npGPSInfo) return(FALSE);

ClearCommError(COMDEV(npGPSInfo),&dwErrorFlags,&ComStat);

dwLength=min((DWORD)nMaxLength,ComStat.cbInQue);

if (dwLength>0)

{ fReadStat=ReadFile(COMDEV(npGPSInfo),lpszBlock,

dwLength,&dwLength,&READ_OS(npGPSInfo));

if (!fReadStat)

{ if (GetLastError()==ERROR_IO_PENDING)

{ OutputDebugString("\n\rIO Pending");

while(!GetOverlappedResult(COMDEV(npGPSInfo),&READ_OS(npGPSInfo),&dwLength,TR

UE))

{ dwError=GetLastError();

if(dwError == ERROR_IO_INCOMPLETE)

continue;

}

}

else

{ dwLength=0;

ClearCommError(COMDEV(npGPSInfo),&dwErrorFlags,&ComStat);

}

}

}

return ( dwLength ) ;

}

BOOL NEAR WriteCommBlock(LPSTR lpByte,DWORD dwBytesToWrite)

{ BOOL fWriteStat;

DWORD dwBytesWritten;

DWORD dwErrorFlags;

DWORD dwError;

COMSTAT ComStat;

if (!npGPSInfo) return(FALSE);

fWriteStat=WriteFile(COMDEV(npGPSInfo),lpByte,dwBytesToWrite,

&dwBytesWritten,&WRITE_OS(npGPSInfo));

if (!fWriteStat)

{ if(GetLastError()==ERROR_IO_PENDING)

{ while(!GetOverlappedResult(COMDEV(npGPSInfo),

&WRITE_OS(npGPSInfo),&dwBytesWritten,TRUE))

{ dwError=GetLastError();

if(dwError == ERROR_IO_INCOMPLETE)

continue;

else

{

ClearCommError(COMDEV(npGPSInfo),&dwErrorFlags,&ComStat);

break;

}

}

}

else

{

ClearCommError(COMDEV(npGPSInfo),&dwErrorFlags,&ComStat);

return ( FALSE );

}

}

return ( TRUE ) ;

}

DWORD FAR PASCAL CommWatchProc(LPSTR)

{ DWORD dwEvtMask;

OVERLAPPED os;

int nLength;

BYTE abIn[1024];

memset(&os,0,sizeof(OVERLAPPED));

// create I/O event used for overlapped read

os.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

if (os.hEvent==NULL)

{ MessageBox(NULL,"Failed to create event for thread!","GPS

Error!",MB_ICONEXCLAMATION|MB_OK);

return ( FALSE ) ;

}

if (!SetCommMask(COMDEV(npGPSInfo),EV_RXCHAR)) return (FALSE);

while (CONNECTED( npGPSInfo))

{ dwEvtMask=0 ;

WaitCommEvent(COMDEV(npGPSInfo),&dwEvtMask,NULL);

if ((dwEvtMask&EV_RXCHAR)==EV_RXCHAR)

{ do

{ if

(nLength=ReadCommBlock((LPSTR)abIn,1024))

{

//WriteCommBlock((LPSTR)abIn,nLength );

*(abIn+nLength)=0;

::SendMessage(hGPSWnd,CN_SEND,nLength,(LONG)(LPSTR)abIn);

}

}

while ((nLength>0)&&(CONNECTED( npGPSInfo)));

}

}

CloseHandle(os.hEvent);

THREADID(npGPSInfo)=0;

HTHREAD(npGPSInfo)=NULL;

return(TRUE);

}

就这些了,希望能对问这些问题的朋友有所帮助!

一般使用的顺序是:

CreateGPSInfo(被通知的窗口句柄,串口端口号1或2);

OpenConnection();//建立联结它会调用SetupConnection

DestroyGPSInfo();//解除联结它会调用CloseConnection

可以用ReadCommBlock/WriteCommBlock来读/写串口

CommWatchProc是监视串口的线程,由OpenConnection建立

当串口有数据来的时侯,它会通知'被通知的窗口句柄'的窗口数据传到的消息(自定义的)

SendMessage(hGPSWnd,CN_SEND,nLength,(LONG)(LPSTR)abIn);

好了,文章结束了!希望能帮助你!


[ 海韵 于 2004-03-02 00:00:00  星期二   ]  
相关文章:

网友评论
正在加载评论...


免责申明 | 版权申明 | 联系我们 | 关于
Copyright © 2001-2025 Haiyun.Net All Rights Reserved
地址:深圳市南山区南油天安工业区1栋3楼 邮编:518000
有我可以帮您的吗?QQ:12457275  
粤ICP备05017321号-1