VC控制台串口操作【645电表抄读】

有时候需要编写简单的测试软件,使用VC++6.0,研究了两种方式操作串口,VC 串口编程方法分为利用 VC 串口控件(或 VC 串口类)和直接调用Windows底层API函数(我称之为VC API 串口编程)两种方法

在Windows 32位以上操作系统(Win98以上)中,将串口(包括其它通信设备)作为文件来处理,所以串口的打开、读写和关闭所用API函数与文件操作函数一样。所以打开串口用CreateFile,读串口用ReadFile,写串口用WriteFile,关闭串口用CloseHandle。

电力行业国网645协议帧结构定义在头文件userdefine.h中

 1 typedef unsigned char uint8_t;
 2
 3 /* 以下是对645规约中的数据帧做的结构体封装 */
 4 struct frame645 {
 5
 6     uint8_t frame_start_flag1;    //帧起始符
 7     uint8_t meter_addr[6];        //地址域
 8     uint8_t frame_start_flag2;    //帧起始符
 9     //控制码
10     union
11     {
12         uint8_t control_byte;
13         struct{
14           uint8_t function_flag:5;//功能码,内容见下
15             //00000:保留
16             //01000:广播校时
17             //10001:读数据
18             //10010:读后续数据
19             //10011:读通信地址
20             //10100:写数据
21             //10101:写通信地址
22             //10110:冻结命令
23             //10111:更改通信速率
24             //11000:修改密码
25             //11001:最大需量清零
26             //11010:电表清零
27             //11011:事件清零
28           uint8_t following_flag:1;//后续帧标志,0:无后续数据帧,1:有后续数据帧
29           uint8_t exception_flag:1;//从站应答标志,0:从站正确应答,1:从站异常应答
30           uint8_t direction_flag:1;//传送方向,0:主站发出的命令帧,1:从站发出的应答帧
31         }control_bits;
32     }control_code;
33     uint8_t datalen;//数据域长度L
34     uint8_t data[50];//数据域+校验码+结束符
35 };

主程序操作代码如下:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include "userdefine.h"
 4 static uint8_t calculate_cs(uint8_t *buf, int len);
 5 void SetDLT645_ZXYG(void);
 6
 7 struct frame645 DLT647_97;
 8 int main(int argc, char* argv[])
 9 {
10     FILE* pFile;
11     char lpBuf[]="Hello World!";
12     DLT647_97.frame_start_flag1=0x68;
13     DLT647_97.frame_start_flag2=0x68;
14     memset(&DLT647_97.meter_addr[0],0xAA,6);
15     DLT647_97.control_code.control_byte=0x01;
16     DLT647_97.datalen=0x02;//2007版本为0x04
17     DLT647_97.data[0]=0x10;
18     DLT647_97.data[1]=0x90;
19     DLT647_97.data[2]=calculate_cs((uint8_t *)&DLT647_97,12);
20     DLT647_97.data[3]=0x16;
21
22     pFile=fopen("COM2","w");
23     if (pFile==NULL)
24     {
25         return 1;
26     }
27     //fwrite(lpBuf,sizeof(char),strlen(lpBuf),pFile);
28     fwrite((uint8_t *)&DLT647_97,sizeof(char),14,pFile);
29     fclose(pFile);
30
31     return 0;
32 }
33
34
35 /* 计算总加校验和 */
36 static uint8_t calculate_cs(uint8_t *buf, int len)
37 {
38     int i = 0;
39     uint8_t cs = 0;
40
41     for(i = 0; i < len; i++)
42     {
43         cs += buf[i];
44     }
45     return(cs);
46 }

接下来看第二种方法:

#include <Windows.h>
#include <stdio.h>
HANDLE hCom;
int main(void)
{
    COMMTIMEOUTS TimeOuts;
    DCB dcb;
    DWORD wCount;//读取的字节数
    BOOL bReadStat;
    char str[50]={0};

    hCom=CreateFile(TEXT("COM3"),//COM2口
    GENERIC_READ|GENERIC_WRITE, //允许读和写
    0, //独占方式
    NULL,
    OPEN_EXISTING, //打开而不是创建
    0, //同步方式
    NULL);

    if(hCom==(HANDLE)-1)
    {
        printf("打开COM失败!\n");
        return FALSE;
    }
    else
    {
        printf("COM打开成功!\n");
    }
    SetupComm(hCom,1024,1024); //输入缓冲区和输出缓冲区的大小都是1024

    //设定读超时
    TimeOuts.ReadIntervalTimeout=1000;
    TimeOuts.ReadTotalTimeoutMultiplier=500;
    TimeOuts.ReadTotalTimeoutConstant=5000;
    //设定写超时
    TimeOuts.WriteTotalTimeoutMultiplier=500;
    TimeOuts.WriteTotalTimeoutConstant=2000;
    SetCommTimeouts(hCom,&TimeOuts); //设置超时

    GetCommState(hCom,&dcb);
    dcb.BaudRate=9600; //波特率为9600
    dcb.ByteSize=8; //每个字节有8位
    dcb.Parity=ODDPARITY;//偶校验  //NOPARITY; //无奇偶校验位
    dcb.StopBits=ONESTOPBIT; //-->ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS 1停止位 1.5停止位 2停止位
    SetCommState(hCom,&dcb);

    SetDLT645_ZXYG();
    bReadStat=WriteFile(hCom,(uint8_t *)&DLT647_97,14,&wCount,NULL);

    while(1)
    {
#if 1
        PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR); //清空缓冲区
        //printf("%s\n",str);
        bReadStat=ReadFile(hCom,str,23,&wCount,NULL);
        //9:要读入的字节数,wCount指向实际读取字节数的指针,比如要读取50个,实际可能只读取了49个
        if(!bReadStat)
        {
            printf("读串口失败!");
            return FALSE;
        }
        else
        {
            //str[8]=‘\0‘;
            printf("%x\n",str);
        }
#endif
        Sleep(100);
    }
}

/* 计算总加校验和 */
static uint8_t calculate_cs(uint8_t *buf, int len)
{
    int i = 0;
    uint8_t cs = 0;

    for(i = 0; i < len; i++)
    {
        cs += buf[i];
    }
    return(cs);
}

void SetDLT645_ZXYG(void)
{
    DLT647_97.frame_start_flag1=0x68;
    DLT647_97.frame_start_flag2=0x68;
    memset(&DLT647_97.meter_addr[0],0xAA,6);
    DLT647_97.control_code.control_byte=0x01;
    DLT647_97.datalen=0x02;//2007版本为0x04
    DLT647_97.data[0]=0x10+0x33;
    DLT647_97.data[1]=0x90+0x33;
    DLT647_97.data[2]=calculate_cs((uint8_t *)&DLT647_97,12);
    DLT647_97.data[3]=0x16;
}

附录645协议解析:

  1 //DLT645-1997 规约
  2 //常见报文解析
  3 //2012.9.29 by legend
  4 //V1.1
  5
  6 TX://读(当前)正向有功总电能
  7    发送的原始报文 68 03 00 00 00 00 00 68 01 02 43 C3 DC 16  [用于发送]
  8    数据域减33报文 68 03 00 00 00 00 00 68 01 02 10 90 xx 16  [用于理解]
  9 RX://28 01 00 00 = 128 = 1.28 kWh
 10    接收的原始报文 68 03 00 00 00 00 00 68 81 06 43 C3 5B 34 33 33 55 16
 11    数据域减33报文 68 03 00 00 00 00 00 68 81 06 10 90 28 01 00 00 xx 16
 12
 13 TX://读(当前)反向有功总电能
 14    发送的原始报文 68 03 00 00 00 00 00 68 01 02 53 C3 EC 16  [用于发送]
 15    数据域减33报文 68 03 00 00 00 00 00 68 01 02 20 90 xx 16  [用于理解]
 16 RX://28 00 00 00 = 28 = 0.28 kWh
 17    接收的原始报文 68 03 00 00 00 00 00 68 81 06 53 C3 5B 33 33 33 64 16
 18    数据域减33报文 68 03 00 00 00 00 00 68 81 06 20 90 28 00 00 00 xx 16
 19
 20 TX://读(当前)正向无功总电能
 21    发送的原始报文 68 03 00 00 00 00 00 68 01 02 43 C4 DD 16
 22    数据域减33报文 68 03 00 00 00 00 00 68 01 02 10 91 xx 16
 23 RX://28 10 00 00 = 1028 = 10.28 kvarh
 24    接收的原始报文 68 03 00 00 00 00 00 68 81 06 43 C4 5B 43 33 33 65 16
 25    数据域减33报文 68 03 00 00 00 00 00 68 81 06 10 91 28 10 00 00 xx 16
 26
 27 TX://读(当前)反向无功总电能
 28    发送的原始报文 68 03 00 00 00 00 00 68 01 02 53 C4 ED 16
 29    数据域减33报文 68 03 00 00 00 00 00 68 01 02 20 91 xx 16
 30 RX://29 00 00 00 = 29 = 0.29 kvarh
 31    接收的原始报文 68 03 00 00 00 00 00 68 81 06 53 C4 5C 33 33 33 66 16
 32    数据域减33报文 68 03 00 00 00 00 00 68 81 06 20 91 29 00 00 00 xx 16
 33
 34 TX://读正向有功尖峰
 35    发送的原始报文 68 03 00 00 00 00 00 68 01 02 44 C3 DD 16
 36    数据域减33报文 68 03 00 00 00 00 00 68 01 02 11 90 xx 16
 37
 38 RX://00 00 00 00 = 0 = 0 kWh
 39    接收的原始报文 68 03 00 00 00 00 00 68 81 06 44 C3 33 33 33 33 2D 16
 40    数据域减33报文 68 03 00 00 00 00 00 68 81 06 11 90 00 00 00 00 xx 16
 41
 42 TX://读正向有功峰
 43    发送的原始报文 68 03 00 00 00 00 00 68 01 02 45 C3 DE 16
 44    数据域减33报文 68 03 00 00 00 00 00 68 01 02 12 90 xx 16
 45 RX://43 00 00 00 = 43 = 0.43 kWh
 46    接收的原始报文 68 03 00 00 00 00 00 68 81 06 45 C3 76 33 33 33 71 16
 47    数据域减33报文 68 03 00 00 00 00 00 68 81 06 12 90 43 00 00 00 xx 16
 48
 49 TX://读正向有功平
 50    发送的原始报文 68 03 00 00 00 00 00 68 01 02 46 C3 DF 16
 51    数据域减33报文 68 03 00 00 00 00 00 68 01 02 13 90 xx 16
 52 RX://84 00 00 00 = 84 = 0.84 kWh
 53    接收的原始报文 68 03 00 00 00 00 00 68 81 06 46 C3 B7 33 33 33 B3 16
 54    数据域减33报文 68 03 00 00 00 00 00 68 81 06 13 90 84 00 00 00 xx 16
 55
 56 TX://正向有功谷
 57    发送的原始报文 68 03 00 00 00 00 00 68 01 02 47 C3 E0 16
 58    数据域减33报文 68 03 00 00 00 00 00 68 01 02 14 90 xx 16
 59 RX://00 00 00 00 = 0 = 0 kWh
 60    接收的原始报文 68 03 00 00 00 00 00 68 81 06 47 C3 33 33 33 33 30 16
 61    数据域减33报文 68 03 00 00 00 00 00 68 81 06 14 90 00 00 00 00 xx 16
 62
 63 TX://读A相电压
 64    发送的原始报文 68 03 00 00 00 00 00 68 01 02 44 E9 03 16
 65    数据域减33报文 68 03 00 00 00 00 00 68 01 02 11 B6 xx 16
 66 RX://00 01 = 100 = 100 V
 67    接收的原始报文 68 03 00 00 00 00 00 68 81 04 44 E9 33 34 EC 16
 68    数据域减33报文 68 03 00 00 00 00 00 68 81 04 11 B6 00 01 xx 16
 69
 70 TX://B相电压
 71    发送的原始报文 68 03 00 00 00 00 00 68 01 02 45 E9 04 16
 72    数据域减33报文 68 03 00 00 00 00 00 68 01 02 12 B6 xx 16
 73 RX://00 00 = 0 = 0 V
 74    接收的原始报文 68 03 00 00 00 00 00 68 81 04 45 E9 33 33 EC 16
 75    数据域减33报文 68 03 00 00 00 00 00 68 81 04 12 B6 00 00 xx 16
 76
 77 TX://C相电压
 78    发送的原始报文 68 03 00 00 00 00 00 68 01 02 46 E9 05 16
 79    数据域减33报文 68 03 00 00 00 00 00 68 01 02 13 B6 xx 16
 80 RX://00 01 = 100 = 100 V
 81    接收的原始报文 68 03 00 00 00 00 00 68 81 04 46 E9 33 34 EE 16
 82    数据域减33报文 68 03 00 00 00 00 00 68 81 04 13 B6 00 01 xx 16
 83
 84 TX://A相电流
 85    发送的原始报文 68 03 00 00 00 00 00 68 01 02 54 E9 13 16
 86    数据域减33报文 68 03 00 00 00 00 00 68 01 02 21 B6 xx 16
 87 RX://99 04 = 499 = 4.99 A
 88    接收的原始报文 68 03 00 00 00 00 00 68 81 04 54 E9 CC 37 98 16
 89    数据域减33报文 68 03 00 00 00 00 00 68 81 04 21 B6 99 04 xx 16
 90
 91 TX://B相电流
 92    发送的原始报文 68 03 00 00 00 00 00 68 01 02 55 E9 14 16
 93    数据域减33报文 68 03 00 00 00 00 00 68 01 02 22 B6 xx 16
 94 RX://00 00 = 0 = 0 A
 95    接收的原始报文 68 03 00 00 00 00 00 68 81 04 55 E9 33 33 FC 16
 96    数据域减33报文 68 03 00 00 00 00 00 68 81 04 22 B6 00 00 xx 16
 97
 98 TX://C相电流
 99    发送的原始报文 68 03 00 00 00 00 00 68 01 02 56 E9 15 16
100    数据域减33报文 68 03 00 00 00 00 00 68 01 02 23 B6 xx 16
101 RX://99 04 = 499 = 4.99 A
102    接收的原始报文 68 03 00 00 00 00 00 68 81 04 56 E9 CC 37 9A 16
103    数据域减33报文 68 03 00 00 00 00 00 68 81 04 23 B6 99 04 xx 16
104
105 TX://读瞬时有功功率,电度表不上传符号位,只上传幅值
106    发送的原始报文 68 03 00 00 00 00 00 68 01 02 63 E9 22 16
107    数据域减33报文 68 03 00 00 00 00 00 68 01 02 30 B6 xx 16
108 RX://61 86 00 = 8661 = 0.8661 kW
109
110 接收的原始报文 68 03 00 00 00 00 00 68 81 05 63 E9 94 B9 33 25 16
111    数据域减33报文 68 03 00 00 00 00 00 68 81 05 30 B6 61 86 00 xx 16
112
113 TX://读瞬时无功功率
114    发送的原始报文 68 03 00 00 00 00 00 68 01 02 73 E9 32 16
115    数据域减33报文 68 03 00 00 00 00 00 68 01 02 40 B6 xx 16
116 RX://00 00 = 0 = 0 kvar
117    接收的原始报文 68 03 00 00 00 00 00 68 81 04 73 E9 33 33 1A 16
118    数据域减33报文 68 03 00 00 00 00 00 68 81 04 40 B6 00 00 xx 16
119
120 TX://读总功率因数
121    发送的原始报文 68 03 00 00 00 00 00 68 01 02 83 E9 42 16
122    数据域减33报文 68 03 00 00 00 00 00 68 01 02 50 B6 xx 16
123 RX://99 09 = 999 = 0.999 [功率因数没有单位]
124    接收的原始报文 68 03 00 00 00 00 00 68 81 04 83 E9 CC 3C CC 16
125    数据域减33报文 68 03 00 00 00 00 00 68 81 04 50 B6 99 09 xx 16
126
127 //END

使用虚拟串口软件http://www.cr173.com/soft/21406.html和虚拟电表软件http://download.csdn.net/detail/wanun55/3830244#comment

可以在VC上开发模拟抄读电表的通信协议程序,注意虚拟电表软件的645国网协议(97版本)帧格式存在一定的错误,抄读正向有功电能数据域多了一个字节

时间: 2024-08-29 18:49:27

VC控制台串口操作【645电表抄读】的相关文章

MATLAB串口操作和GUI编程

简单的MATLAB GUI编程和串口控制.Word编辑,如需PDF版本,请留言.说实话这个挺难看的……     概述 本文介绍了程序AD9512_Serial_GUI的编程思路和功能.该程序设计到MATLAB的图像用户界面编程的基本方法和串口的基本操作.程序目的在于通过串口写控制字对AD9512进行配置(AD9512通过SPI写入寄存器,本程序只是整个控制程序中的一部分). 修订历史 以下表格展示了本文档的修订过程 日期 版本号 修订内容 2015/01/15 V0.0 初始版本,试验版[1]

Android串口操作,简化android-serialport-api的demo(转载)

原帖地址:点击打开 最近在做android串口的开发,找到一个开源的串口类android-serialport-api.其主页在这里http://code.google.com/p/android-serialport-api/  ,这里可以下到APK及对源码. 但是下载源码之后发现源码不能直接使用,而且源码结构较为复杂.关于串口的操作不外乎几步: 1.打开串口(及配置串口): 2.读串口: 3.写串口: 4.关闭串口. android-serialport-api的代码使用了继承等复杂的行为,

VC++中文件操作(一)---CFileFind,CFileDialog,CFile,CArchive,CStdioFile

各种关于文件的操作在程序设计中是十分常见,如果能对其各种操作都了如指掌,就可以根据实际情况找到最佳的解决方案,从而在较短的时间内编写出高效的代码,因而熟练的掌握文件操作是十分重要的.本文将对Visual C++中有关文件操作进行全面的介绍,并对在文件操作中经常遇到的一些疑难问题进行详细的分析. VC++中文件操作(一) ***************************************************************************××××××××××第一.V

V-rep学习笔记:串口操作

VREP Regular API提供了串口操作的相关函数,可以对串口进行打开.关闭和读写: 下面使用一款淘宝上常见的AHRS(Attitude and heading reference system,航姿参考系统)模块来驱动VREP中的虚拟模型,控制其姿态.VREP通过串口读取传感器实时发送的数据并进行解析. 传感器通过串口发送2种数据: 解算后的姿态角和气压高度等数据 原始的传感器ADC数据(直接从传感器读取出来的测量值,没有经过解算处理) 下面是VREP中以16进制显示的接收到的串口数据:

QT5 串口操作

Qt5 提供了两个类用于串口操作,分别是:QSerialPort和QSerialPortInfo. 最基本的操作示例代码如下: 1 #ifndef DIALOG_H 2 #define DIALOG_H 3 4 #include <QDialog> 5 6 #include <QDebug> 7 #include <QSerialPort> 8 #include <QSerialPortInfo> 9 10 namespace Ui { 11 class D

C#异步数据接收串口操作类

C#异步数据接收串口操作类 使用C#调用传统32位API实现串口操作,整个结构特别的简单.接收数据只需要定义数据接收事件即可. 上传源代码我不会,需要源代码的请与我([email protected])联系.你也可以教我怎么上传源代码. using System; using System.Runtime.InteropServices; /// <summary> /// (C)2003-2005 C2217 Studio  保留所有权利 /// /// 文件名称:     IbmsSeri

使用influx控制台工具操作InfluxDB

这里记录下influx控制台的简单使用,如需更多功能请参考InfluxDB官方文档: https://docs.influxdata.com/influxdb/v1.1/ 环境: CentOS6.5_x64InfluxDB版本:1.1.0 准备工作 启动服务器 执行如下命令: service influxdb start 示例如下: [[email protected] ~]# service influxdb start Starting influxdb... influxdb proces

【转】C# 串口操作系列(3) -- 协议篇,二进制协议数据解析

我们的串口程序,除了通用的,进行串口监听收发的简单工具,大多都和下位机有关,这就需要关心我们的通讯协议如何缓存,分析,以及通知界面. 我们先说一下通讯协议.通讯协议就是通讯双方共同遵循的一套规则,定义协议的原则是尽可能的简单以提高传输率,尽可能的具有安全性保证数据传输完整正确.基于这2点规则,我们一个通讯协议应该是这样的:头+数据长度+数据正文+校验 例如:AA 44 05 01 02 03 04 05 EA 这里我假设的一条数据,协议如下: 数据头:     AA 44 数据长度: 05 数据

【转】C# 串口操作系列(1) -- 入门篇,一个标准的,简陋的串口例子。

我假设读者已经了解了c#的语法,本文是针对刚打算解除串口编程的朋友阅读的,作为串口编程的入门范例,也是我这个系列的基础. 我们的开发环境假定为vs2005(虽然我在用vs2010,但避免有些网友用2005,不支持lambda,避免不兼容,就用2005来做例子) 一个基本的串口程序,既然是个程序了.我们就先从功能说起,包含 串口选择 波特率选择 打开 关闭 接受数据显示 发送数据输入 发送数据 数据量提示以及归零 好吧,有了这些功能,我们就先画出界面.例如: 这里,波特率就定死几种好了.直接界面上