用PowerBuilder 6.0实现与单片机通讯 2000年 第6期   实现与单片机通讯的方法有很多种,我想谈谈利用微软公司提供的通讯控件MsComm32.ocx 来实现。现将我在实际应用中使用该控件的一些体会介绍如下。   我编写的应用程序是用在小区自动抄表系统上的,即计算机通过Modem与各个小区的单片机相连,单片机下接若干个抄表机,抄表机下连接多个用户电表。这样可以实时监控各小区及各用户的用电情况,并实现对未交费用户的断电控制。 #1 一、 程序中与单片机各参数的约定   (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位为抄表机上的用户地址码。根据用户码来判断单片机接有几个抄表机,来实现对单片机的全抄功能。 #1 二、 对第一个抄表机实现全抄功能的程序简介   假设第一个抄表机只有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事件也可以。希望对与单片机通讯感兴趣的朋友有帮助。