如何用Delphi开发串口通信程序 以下是一篇网上的文章,你可以参考,不过我在开发过程中是用VC编制的串口动态库再用DELPHI调用。如有问题可与我联系:Email:[email protected]
如何用Delphi开发串口通信程序
熊彪
Delphi 是新一代可视化开发工具,它具有功能强大、简便易用和代码执行速度快等特点,Delphi系列开发工具在国际各媒体上曾获得过三十多项大奖,是全球公认的快速应用开发工具技术的先驱者,他越来越在构架企业信息系统方面发挥着重要作用。
它的功能很强大,但在对串口通信的控制上Delphi的帮助文档和其他书籍中很少提及,可这是在开发应用系统时会经常遇到却又令人头痛的事情。下面介绍三种解决方案,以实现接收和发送数据的功能。 一、 使用ActiveX控件
由于Delphi中没有串口控件可用,所以首先需要把ActiveX控件MSCOMM加到元件选项板上。这是一个非常好的控件它不仅能对串口进行操作,而且还可以对Modem进行控制。下面结合一个具体的实例来说明如何用MSCOMM控件开发出串口通信程序。
创建一个Communication.dpr工程,把窗体的Name属性变为CommForm,将标题改为The Communication Test,选择File/Save As将新的窗体存储为CommFrm.pas,接下来参照图1和图2将控件添加到主窗体中。 图1 图2
由图1和图2我们可以看出,通过设置页可选定进行数据传输的通信端口和端口的波特率、奇偶校验、数据位和停止位,通信时每传输一个字符都将触发响应事件,在通信页“传输显示”位置可看到当前正在进行传输的数据。同时当出现回车换行符时将整行内容显示在memDisplay新的一行中,而全部接收的内容还将存在一个文件中。部分源代码如下:
变量说明
var
CommForm: TCommForm;
ss :string;
savef,readf :file of char;
i,j :longint; 初始化
procedure TCommForm.FormCreate(Sender: TObject);
begin
mscomm.commport:=1;
mscomm.settings:='9600,n,8,1';
mscomm.inputlen:=1;
mscomm.inbuffercount:=0;
mscomm.portopen:=true;
ss:='';
i:=0;
j:=0;
assignfile(savef,'save1');
rewrite(savef);
assignfile(readf,'read1');
reset(readf);
end; 设置确定
procedure TCommForm.btnConfirmClick(Sender: TObject);
begin
if mscomm.portopen then
mscomm.portopen:=false;
mscomm.commport:=strtoint(edtCommport.text);
mscomm.settings:=edtCommsetting.Text;
end; 传输事件
procedure TCommForm.MSCommComm(Sender: TObject);
var
filenrc :char;
buffer :variant;
s1:string;
c :char;
begin
case mscomm.commEvent of
comEvSend:
begin
while not(eof(readf)) do
begin
read(readf,filenrc);
mscomm.output:=filenrc;
j:=j+1;
lblDisplay.caption:=inttostr(j);
if mscomm.outbuffercount>=2 then
break;
end;
end;
comEvReceive:
begin
buffer:=mscomm.Input;
s1:=buffer;
c:=s1[1];
ss:=ss+c;
i:=i+1;
lblDisplay.caption:=c+inttostr(i);
write(savef,c);
if (c=chr(10))or(c=chr(13)) then
begin
lblDisplay.caption:='cr'+inttostr(i);
memDisplay.lines.add(ss);
ss:='';
end;
end;
end;
end; 当然还有许多串口控制的ActiveX控件,有控件用是方便,但是是否说没有控件就不能用Delphi本身解决问题呢。那么就请来看一下下面的解决方案: 二、使用行间汇编
对于硬件的控制,汇编语言是最方便的了,而Delphi中又允许使用行间汇编那么来看一下怎样使用行间汇编接收下位机传来的数据,在下面的例子中我们简化了问题,比如我们通过COM2接收400个传来的字符并将这些内容保存在AAA.DAT文件中当接收完毕后显示‘Receive end’。但是为什么说我们简化了问题呢,因为实际情况中双方通信可能是约定传送字符的个数,也可能约定的是规定好的起始字符和结束字符,或是多种条件同时约定待各种条件全部满足时才表示完成一次成功的接收否则如有一个条件未满足都表示接收失败需要重新传送。这些约定在使用了行间汇编的Delphi程序中都可实现。下面的程序就是按一个按钮进入的一个简化的串口接收程序。
procedure TForm1.Button1Click(Sender: TObject);
var ca:array[1..400]of char;
c:char;
i,j:integer;
f1:file of char;
label loop1;
begin
i:=1;
asm
mov dx,0001
mov ax,005eh
int 14h
end;
for j:=1 to 400 do
begin
asm
loop1: mov dx,0001
mov ah,02
int 14h
test ah,80h
jnz loop1
mov c,al
end;
ca[i]:=c;
i:=i+1;
end;
assignfile(f1,'aaa.dat');
rewrite(f1);
for j:=1 to i-1 do
write(f1,ca[j]);
closefile(f1);
label1.caption:='receive end';
end; 三、用Delphi调用可执行应用程序
采用其它编程技术编制的串口通信程序在完成数据传输后将数据保存到磁盘上,然后用Delphi对数据内容进行处理。通过在Delphi中与其他编程技术协同工作也不失为一种解决问题的好方法。
如何用Delphi开发串口通信程序
熊彪
Delphi 是新一代可视化开发工具,它具有功能强大、简便易用和代码执行速度快等特点,Delphi系列开发工具在国际各媒体上曾获得过三十多项大奖,是全球公认的快速应用开发工具技术的先驱者,他越来越在构架企业信息系统方面发挥着重要作用。
它的功能很强大,但在对串口通信的控制上Delphi的帮助文档和其他书籍中很少提及,可这是在开发应用系统时会经常遇到却又令人头痛的事情。下面介绍三种解决方案,以实现接收和发送数据的功能。 一、 使用ActiveX控件
由于Delphi中没有串口控件可用,所以首先需要把ActiveX控件MSCOMM加到元件选项板上。这是一个非常好的控件它不仅能对串口进行操作,而且还可以对Modem进行控制。下面结合一个具体的实例来说明如何用MSCOMM控件开发出串口通信程序。
创建一个Communication.dpr工程,把窗体的Name属性变为CommForm,将标题改为The Communication Test,选择File/Save As将新的窗体存储为CommFrm.pas,接下来参照图1和图2将控件添加到主窗体中。 图1 图2
由图1和图2我们可以看出,通过设置页可选定进行数据传输的通信端口和端口的波特率、奇偶校验、数据位和停止位,通信时每传输一个字符都将触发响应事件,在通信页“传输显示”位置可看到当前正在进行传输的数据。同时当出现回车换行符时将整行内容显示在memDisplay新的一行中,而全部接收的内容还将存在一个文件中。部分源代码如下:
变量说明
var
CommForm: TCommForm;
ss :string;
savef,readf :file of char;
i,j :longint; 初始化
procedure TCommForm.FormCreate(Sender: TObject);
begin
mscomm.commport:=1;
mscomm.settings:='9600,n,8,1';
mscomm.inputlen:=1;
mscomm.inbuffercount:=0;
mscomm.portopen:=true;
ss:='';
i:=0;
j:=0;
assignfile(savef,'save1');
rewrite(savef);
assignfile(readf,'read1');
reset(readf);
end; 设置确定
procedure TCommForm.btnConfirmClick(Sender: TObject);
begin
if mscomm.portopen then
mscomm.portopen:=false;
mscomm.commport:=strtoint(edtCommport.text);
mscomm.settings:=edtCommsetting.Text;
end; 传输事件
procedure TCommForm.MSCommComm(Sender: TObject);
var
filenrc :char;
buffer :variant;
s1:string;
c :char;
begin
case mscomm.commEvent of
comEvSend:
begin
while not(eof(readf)) do
begin
read(readf,filenrc);
mscomm.output:=filenrc;
j:=j+1;
lblDisplay.caption:=inttostr(j);
if mscomm.outbuffercount>=2 then
break;
end;
end;
comEvReceive:
begin
buffer:=mscomm.Input;
s1:=buffer;
c:=s1[1];
ss:=ss+c;
i:=i+1;
lblDisplay.caption:=c+inttostr(i);
write(savef,c);
if (c=chr(10))or(c=chr(13)) then
begin
lblDisplay.caption:='cr'+inttostr(i);
memDisplay.lines.add(ss);
ss:='';
end;
end;
end;
end; 当然还有许多串口控制的ActiveX控件,有控件用是方便,但是是否说没有控件就不能用Delphi本身解决问题呢。那么就请来看一下下面的解决方案: 二、使用行间汇编
对于硬件的控制,汇编语言是最方便的了,而Delphi中又允许使用行间汇编那么来看一下怎样使用行间汇编接收下位机传来的数据,在下面的例子中我们简化了问题,比如我们通过COM2接收400个传来的字符并将这些内容保存在AAA.DAT文件中当接收完毕后显示‘Receive end’。但是为什么说我们简化了问题呢,因为实际情况中双方通信可能是约定传送字符的个数,也可能约定的是规定好的起始字符和结束字符,或是多种条件同时约定待各种条件全部满足时才表示完成一次成功的接收否则如有一个条件未满足都表示接收失败需要重新传送。这些约定在使用了行间汇编的Delphi程序中都可实现。下面的程序就是按一个按钮进入的一个简化的串口接收程序。
procedure TForm1.Button1Click(Sender: TObject);
var ca:array[1..400]of char;
c:char;
i,j:integer;
f1:file of char;
label loop1;
begin
i:=1;
asm
mov dx,0001
mov ax,005eh
int 14h
end;
for j:=1 to 400 do
begin
asm
loop1: mov dx,0001
mov ah,02
int 14h
test ah,80h
jnz loop1
mov c,al
end;
ca[i]:=c;
i:=i+1;
end;
assignfile(f1,'aaa.dat');
rewrite(f1);
for j:=1 to i-1 do
write(f1,ca[j]);
closefile(f1);
label1.caption:='receive end';
end; 三、用Delphi调用可执行应用程序
采用其它编程技术编制的串口通信程序在完成数据传输后将数据保存到磁盘上,然后用Delphi对数据内容进行处理。通过在Delphi中与其他编程技术协同工作也不失为一种解决问题的好方法。
16进制、2进制、ASCII...
---- Windows 操 作 系 统 的 机 制 禁 止 应 用 程 序 直 接 访 问 计 算 机 硬 件, 但 它 为 程 序
员 提 供 了 一 系 列 的 标 准API 函 数, 使 得 应 用 程 序 的 编 制 更 加 方 便 并 且 免 除 了
对 有 关 硬 件 的 调 试 麻 烦。 在Windows95/NT 中, 原 来Windows3.X 的WM_COMMNOTIFY 消 息
已 被 取 消, 操 作 系 统 为 每 个 通 信 设 备 开 辟 了 用 户 可 定 义 大 小 的 读/ 写 缓 冲
区, 数 据 进 出 通 信 口 均 由 操 作 系 统 后 台 完 成, 应 用 程 序 只 需 对 读/ 写 缓 冲 区
操 作 即 可。WIN95/NT 中 几 个 常 用 的 串 行 通 信 操 作 函 数 如 下:
CreatFile
打 开 串 行 口
CloseHandle
关 闭 串 行 口
SetupComm
设 置 通 信 缓 冲 区 的 大 小
ReadFile
读 串 口 操 作
WriteFile
写 串 口 操 作
SetCommState
设 置 通 信 参 数
GetCommState
获 取 默 认 通 信 参 数
ClearCommError
清 除 串 口 错 误 并 获 取 当 前 状 态
---- 除 上 述 几 个 函 数 外, 还 要 经 常 用 到 一 个 重 要 的 记 录DCB( 设 备 控 制
块)。DCB 中 记 录 有 可 定 义 的 串 行 口 参 数, 设 置 串 行 口 参 数 时 必 须 先 用
GetCommState 函 数 将 系 统 默 认 值 填 入DCB 控 制 块, 然 后 才 可 把 用 户 想 改 变 的 自 定
义 值 设 定。
---- 在WIN95/NT 中 进 行 串 行 通 信 除 了 解 基 本 的 通 信 操 作 函 数 外, 还 要 掌 握 多 线
程 编 程。 线 程 是 进 程 内 部 执 行 的 路 径, 是 操 作 系 统 分 配CPU 时 间 的 基 本 实
体。 每 个 进 程 都 由 单 线 程 开 始 完 成 应 用 程 序 的 执 行。 串 行 通 信 需 要 利 用 多
线 程 技 术 实 现, 其 主 要 的 处 理 逻 辑 可 以 表 述 如 下: 进 程 一 开 始 先 由 主 线 程
做 一 些 必 要 的 初 始 化 工 作, 然 后 主 线 程 根 据 需 要 在 适 当 时 候 建 立 通 信 监 视
线 程 监 视 通 信 口, 当 指 定 的 串 行 口 事 件 发 生 时, 向 主 线 程 发 送
WM_COMMNOTIFY 消 息( 由 于WIN95 取 消 了WM_COMMNOTIFY 消 息, 因 此 必 须 自 己 创
建), 主 线 程 对 其 进 行 处 理。 若 不 需 要WM_COMMNOTIFY 消 息, 则 主 线 程 终 止 通
信 监 视 线 程。
---- 多 线 程 同 时 执 行, 将 会 引 起 对 共 享 资 源 的 冲 突。 为 避 免 冲 突, 就 要 用 同
步 多 线 程 对 共 享 资 源 进 行 访 问。WIN95 提 供 了 许 多 保 持 线 程 同 步 的 方 法, 笔
者 采 用 创 建 事 件 对 象 来 保 持 线 程 同 步。 通 过CraeteEvent() 创 建 事 件 对 象, 使
用SetEvent() 或PulseEvent() 函 数 将 事 件 对 象 设 置 成 信 号 同 步。 在 应 用 程 序 中,
利 用WaitSingleObject() 函 数 等 待 同 步 的 触 发, 等 到 指 定 的 事 件 被 其 它 线 程 设 置
为 有 信 号 时, 才 继 续 向 下 执 行 程 序。
---- Delphi 下 的 具 体 实 现 方 法
---- Delphi 的 强 大 功 能 和 支 持 多 线 程 的 面 向 对 象 编 程 技 术, 使 得 实 现 串 行 通 信
非 常 简 单 方 便。 它 通 过 调 用 外 部 的API 函 数 来 实 现, 主 要 步 骤 如 下: 首 先, 利
用CreateFile 函 数 打 开 串 行 口, 以 确 定 本 应 用 程 序 对 此 串 行 口 的 占 有 权, 并 封
锁 其 它 应 用 程 序 对 此 串 口 的 操 作; 其 次, 通 过GetCommState 函 数 填 充 设 备 控 制
块DCB, 再 通 过 调 用SetCommState 函 数 配 置 串 行 口 的 波 特 率、 数 据 位、 校 验 位 和
停 止 位。 然 后, 创 建 串 行 口 监 视 线 程 监 视 串 行 口 事 件。 在 此 基 础 上 就 可 以
在 相 应 的 串 口 上 操 作 数 据 的 传 输; 最 后, 用CloseHandle 函 数 关 闭 串 行 口。 具 体
的 程 序 如 下, 本 程 序 用Delphi3.0 编 制 在Win95 环t 境 下 调 试 通 过, 已 投 入 实 际 应 用
中, 供 广 大 读 者 参 考。
---- 程 序:
unit comdemou;
interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs;
const
Wm_commNotify=Wm_User+12;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
Procedure comminitialize;
Procedure MsgcommProcess(Var
Message:Tmessage); Message Wm_commnotify;
{ Private declarations }
public
{ Public declarations }
end;
//线程声明
TComm=Class(TThread)
protected
procedure Execute;override;
end;
var
Form1: TForm1;
hcom,Post_Event:Thandle;
lpol:Poverlapped;
implementation
{$R *.DFM}
Procedure TComm.Execute; //线程执行过程
var
dwEvtMask:Dword;
Wait:Boolean;
Begin
fillchar(lpol,sizeof(toverlapped),0);
While True do Begin
dwEvtMask:=0;
Wait:=WaitCommEvent(hcom,dwevtmask,lpol);
//等待串行口事件;
if Wait Then Begin
waitforsingleobject(post_event,infinite);
//等待同步事件置位;
resetevent(post_event); //同步事件复位;
PostMessage(Form1.Handle,
WM_COMMNOTIFY,0,0);//发送消息;
end;
end;
end;
procedure Tform1.comminitialize;
//串行口初始化
var
lpdcb:Tdcb;
Begin
hcom:=createfile('com2',generic_read or
generic_write,0,nil,open_existing,
file_attribute_normal or
file_flag_overlapped,0);//打开串行口
if hcom=invalid_handle_value then
else
setupcomm(hcom,4096,4096);
//设置输入,输出缓冲区皆为4096字节
getcommstate(hcom,lpdcb);
//获取串行口当前默认设置
lpdcb.baudrate:=2400;
lpdcb.StopBits:=1;
lpdcb.ByteSize:=8;
lpdcb.Parity:=EvenParity; //偶校验
Setcommstate(hcom,lpdcb);
setcommMask(hcom,ev_rxchar);
//指定串行口事件为接收到字符;
end;
Procedure TForm1.Msgcomm
Process(Var Message:Tmessage);
var
Clear:Boolean;
Coms:Tcomstat;
cbNum,ReadNumber,lpErrors:Integer;
Read_Buffer:array[1..100]of char;
Begin
Clear:=Clearcommerror(hcom,lpErrors,@Coms);
if Clear Then Begin
cbNum:=Coms.cbInQue;
ReadFile(hCom,Read_Buffer,
cbNum,ReadNumber,lpol);
//处理接收数据
SetEvent(Post_Event);
//同步事件置位
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
comminitialize;
post_event:=CreateEvent
(nil,true,true,nil); //创建同步事件;
Tcomm.Create(False);
//创建串行口监视线程;
end;
end.
you shi shuo hua [email protected]