标题用PowerBuilder 6.0实现与单片机通讯
栏目软件世界
发布2000年第6期
实现与单片机通讯的方法有很多种,我想谈谈利用微软公司提供的通讯控件MsComm32.ocx 来实现。现将我在实际应用中使用该控件的一些体会介绍如下。
我编写的应用程序是用在小区自动抄表系统上的,即计算机通过Modem与各个小区的单片机相连,单片机下接若干个抄表机,抄表机下连接多个用户电表。这样可以实时监控各小区及各用户的用电情况,并实现对未交费用户的断电控制。
一、 程序中与单片机各参数的约定
(1) 数据与速率的约定
每个用户设有状态位1个字节(判断是否正常用电,由低四位确定),电表读数、尖峰、峰、平、谷期用电量都用3字节,且最后字节的低四位为小数位,因此每个用户须传送16个字节的信息,也就是说,数值的最大值为99999.9(如果用ASCII码,则通讯数据量非常的多,设定的缓冲区也大,通讯时间长,电话费用也高)。
速率约定9600bps
数据传输的起始位为:0xAAH = 170
(2) 命令的约定
第一个抄表机的敲门命令:0x0AH = 10;发送命令:0x1AH = 26
第二个抄表机的敲门命令:0x0BH = 11;发送命令:0x1BH = 27
.
.
.
全抄命令(传送抄表机上的全部用户信息):0x7CH = 124
单抄命令(传送抄表机上的单个用户信息):0x7AH = 122
说明:由于抄表机每时每刻都在对用户电表采集数据,因此,当通讯时,抄表机与单片机之间必然存在一种切换,使用敲门命令的目的,就是命令哪个抄表机将进行切换。抄表机切换后,单片机将接收到的发送命令,回传给计算机,表示要发送数据了。
(3) 用户码的设定
用户码设7位,前2位为小区码,对应与单片机相连接的电话号码;3、4位为抄表机码,对应使用的敲门命令与发送命令,后3位为抄表机上的用户地址码。根据用户码来判断单片机接有几个抄表机,来实现对单片机的全抄功能。
二、 对第一个抄表机实现全抄功能的程序简介
假设第一个抄表机只有3个用户
(1) 创建一个窗口,命名为w_comm,并在窗口内添加控件
命令按钮:cb_receive,text:接收;
cb_close,text:关闭;
多行编辑器:选中Hscroll Bar和Vscroll Bar;
0LE:选择ole →Insert Control→Microsoft Commications Control(如果没有该控件,可注册。MsComm32.ocx在windows/system或winnt/system32目录下);
OCX属性:使用缺省值(如果COM1端口被占用,必须将CommPort设为未使用端口值)。
(2) 输入API函数
选择菜单Declare →Local External Functions,弹出Declare Local External Functions窗口,输入API函数:Function Boolean Sleep ( ulong dwMilliseconds ) Library "Kernel32.dll"。
(3) 窗口函数
wf_is_convert( integer chart,integer bits )
//功能:将16进制值分解为ASCII码的数值;
//参数:
//chart: 16进制值
//bits: 判断是否状态位
if mod ((bits - 2) + 16,16) = 0 then //能被16尽除的位数,为状态位
mle_1.text = mle_1.text + char(mod (chart,8) + 48)//低四位为状态位
else
mle_1.text = mle_1.text + char(chart /16 + 48)//高位
mle_1.text = mle_1.text + char(mod (chart,16) + 48) //低位
end if
wf_is_receive( )
功能:读取接收数据
charch[]
longi
long ReceieveCount
ReceieveCount = 3*16 - 1 //3是抄表机上的用户个数,16为每个用户的信息量
if ole_1.object.InBufferCount > ReceieveCount then
ch = ole_1.object.Input
if asc(ch[1]) <> 170 then
messagebox("错误","未接收到启始位",stopsign!)
else
for i = 2 to ReceieveCount
wf_is_convert(asc(ch[i]),i)
next
wf_ transact()//数据处理
end if
elseif ole_1.object.InBufferCount = 0 then
messagebox("错误","无法接收到数据",stopsign!)
end if
说明:ch[]数组必须是char型,如果用string型,有的数据就可能取不到。当然,如果单片机发送的是ASCII码,用string型也可以,那就不必用数组方式了。
(4) 脚本的编写
ole_1 的getfocus事件写入:
ole_1.Object.InputMode = 1
if ole_1.Object.PortOpen = False then ole_1.Object.PortOpen = True //如果端口未打开,
//则打开
cb_close 的clicked事件写入:close ( parent )
cb_receive 的clicked事件写入:
string ls_comm
charcallback//应答变量
long j
OLE_1.OBJECT.OUTBUFFERCOUNT=0 //清缓冲区
//发送两次0x0AH
ls_comm = char(10)
OLE_1.OBJECT.OUTPUT = ls_comm //输出命令 0x0aH
SLEEP(10)
OLE_1.OBJECT.OUTPUT = ls_comm
SLEEP(10)
j = 0
DO WHILE OLE_1.OBJECT.INBUFFERCOUNT = 0 and j < 3000 //无回号就发命令
Ls_comm = char(26)
OLE_1.OBJECT.OUTPUT = ls_comm //输出命令 0x1aH
SLEEP(100)
j = j + 1
LOOP
if j = 3000 then
messagebox("错误","无回号",stopsign!)
else
callback = char(ole_1.object.input)
if asc(callback) = 26 then
ole_1.object.inbuffercount=0
ls_comm = char(124)
OLE_1.OBJECT.OUTPUT = ls_comm //输出命令 0x7cH
sleep(200)
wf_is_receieve()
else
messagebox("错误","回号错",stopsign!)
end if
end if
以上程序只是自动抄表系统中的部分程序摘要。如果用户多、数据量大,使用timer事件也可以。希望对与单片机通讯感兴趣的朋友有帮助。