zoukankan      html  css  js  c++  java
  • 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版本)帧格式存在一定的错误,抄读正向有功电能数据域多了一个字节

  • 相关阅读:
    ElementUI表格表头自定义添加checkbox,点击不生效问题
    同步个人vs code 习惯性配置gists 到码云(Gitee)
    el-select 设置宽度自适应上层div
    vue-element-template模板vue-cli3本地使用proxy解决跨域问题
    vue-cli3用axios+proxy简单解决在dev开发环境下的跨域问题
    windows下EasyMock的安装与部署遇到的各种问题(爬坑)
    git同步代码至github和gitee(码云)
    Git使用总结(包含Git Bash和Git GUI的使用)
    Leetcode No.53 Maximum Subarray(c++实现)
    Leetcode No.35 Search Insert Position(c++实现)
  • 原文地址:https://www.cnblogs.com/codecamel/p/4882341.html
Copyright © 2011-2022 走看看