用PowerBuilder 6.0实现与单片机通讯

Author: Date: 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事件也可以。希望对与单片机通讯感兴趣的朋友有帮助。