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失败!
");
        return FALSE;
    }
    else
    {
        printf("COM打开成功!
");
    }
    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
",str);
        bReadStat=ReadFile(hCom,str,23,&wCount,NULL);
        //9:要读入的字节数,wCount指向实际读取字节数的指针,比如要读取50个,实际可能只读取了49个
        if(!bReadStat)
        {
            printf("读串口失败!");
            return FALSE;
        }
        else
        {
            //str[8]='';
            printf("%x
",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版本)帧格式存在一定的错误,抄读正向有功电能数据域多了一个字节

原文地址:https://www.cnblogs.com/codecamel/p/4882341.html