【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★

目录

一、DLT645-2007多功能电能表通信协议

1、硬件描述

2、帧数据格式

3、数据标识

4、读写数据帧指令

(1)读数据,控制码C=11H

(2)读后续数据,控制码C=12H

(3) 写数据,控制码C=14H​

 (4)读通信地址,控制码C=13H​

 (5)写通信地址,控制码C=15H​

 (6)广播校时,控制码C=08H​

 (7)冻结命令,控制码C=16H​

(8)更改通信速率,控制码C=17H

 (9)修改密码,控制码C=18H​

 (10)最大需量清零,控制码C=19H​

 (11)电表清零,控制码C=1AH​

 (12)事件清零,控制码C=1BH​

 (13)跳合闸、报警、保电,控制码C=1CH​

 (14)多功能端子输出控制命令,控制码C=1DH​

 (15)安全认证命令,控制码C=03H

5、数据编码

二、RT-Thread框架下程序驱动代码

1、相关软件配置

1.1 RT-Thread 物联网操作系统

1.2 RT-Thread 文档中心

1.3 UART设备驱动

1.4 开发工具:RT-Thread Studio

 1.5 软件参数配置

1.6 串口调试助手相关指令测试记录

2、具体驱动代码

测试的电表数据数组

2.1 串口接收中断方式的驱动测试代码

2.2 串口DMA中断方式的驱动测试代码

三、相关链接

1、 DLT645-2007_[带2013备案文件】.pdf

2、 参考资料 



一、DLT645-2007多功能电能表通信协议

电能表规格型号:正泰DDSU666型单相电子式电能表(导轨)

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第1张图片

 出厂电能表的默认地址如同所示:190319014070【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第2张图片

 测试接线电路【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第3张图片

1、硬件描述

 默认通信速率:2400bps

RS- - 485  标准串行电气接口【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第4张图片

2、帧数据格式

 帧数据格式:每帧由帧起始符、从站地址域、控制码、数据域长度、数据域、帧信息纵向校验码及帧结束符 7 个域组成【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第5张图片

 串口传输字节配置则为9位数据位、偶校验、1位停止位

    //串口参数配置
    config.baud_rate = BAUD_RATE_2400;     //波特率2400
    config.data_bits = DATA_BITS_9;        //9位数据位 
    config.stop_bits = STOP_BITS_1;        //1位停止位
    config.bufsz = 128;
    config.parity = PARITY_EVEN;           //偶校验

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第6张图片

 【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第7张图片

 【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第8张图片

 【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第9张图片

 注意:数据标识、密码、操作者代码、数据、帧序号传输时发送方按字节进行加 33H 处理。

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第10张图片

 注意:通信主机发送帧需先发 4 个字节 FEH唤醒电表从机

 注意:所有数据项均先传送低位字节,后传送高位字节。在程序代码计算时,先接收的数据为低位字节。【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第11张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第12张图片

3、数据标识

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第13张图片

4、读写数据帧指令

(1)读数据,控制码C=11H

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第14张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第15张图片

(2)读后续数据,控制码C=12H

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第16张图片

(3) 写数据,控制码C=14H【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第17张图片

 (4)读通信地址,控制码C=13H【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第18张图片

 (5)写通信地址,控制码C=15H【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第19张图片

 (6)广播校时,控制码C=08H【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第20张图片

 (7)冻结命令,控制码C=16H【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第21张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第22张图片

(8)更改通信速率,控制码C=17H

 【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第23张图片

 (9)修改密码,控制码C=18H【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第24张图片

 (10)最大需量清零,控制码C=19H【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第25张图片

 (11)电表清零,控制码C=1AH【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第26张图片

 (12)事件清零,控制码C=1BH【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第27张图片

 (13)跳合闸、报警、保电,控制码C=1CH【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第28张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第29张图片

 (14)多功能端子输出控制命令,控制码C=1DH【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第30张图片

 (15)安全认证命令,控制码C=03H

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第31张图片

5、数据编码

(当前)正向有功总电能        =>        数据标识:DI3 DI2 DI1 DI0        00 01 00 00 

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第32张图片

 A 相电压         =>        数据标识:DI3 DI2 DI1 DI0        02 01 01 00         

A 相电流        =>        数据标识:DI3 DI2 DI1 DI0        02 02 01 00

瞬时总有功功率        =>        数据标识:DI3 DI2 DI1 DI0        02 03 00 00

瞬时总无功功率        =>        数据标识:DI3 DI2 DI1 DI0        02 04 00 00

瞬时总视在功率        =>        数据标识:DI3 DI2 DI1 DI0        02 05 00 00

总功率因数        =>        数据标识:DI3 DI2 DI1 DI0        02 06 00 00

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第33张图片

 电网频率        =>        数据标识:DI3 DI2 DI1 DI0        02 80 00 02

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第34张图片

二、RT-Thread框架下程序驱动代码

1、相关软件配置

1.1 RT-Thread 物联网操作系统

操作系统:RT-Thread 小而美的物联网操作系统

RT-Thread, RTOS, 物联网操作系统 - RT-Thread物联网操作系统

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第35张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第36张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第37张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第38张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第39张图片

1.2 RT-Thread 文档中心

RT-Thread 文档中心【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第40张图片https://www.rt-thread.org/document/site/#/

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第41张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第42张图片

1.3 UART设备驱动

RT-Thread 文档中心【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第43张图片https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart_v1/uart

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第44张图片

 【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第45张图片

1.4 开发工具:RT-Thread Studio

一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效。

RT-Thread Studio - RT-Thread物联网操作系统RT-Thread Studiohttps://www.rt-thread.org/page/studio.html【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第46张图片

 【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第47张图片

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第48张图片

 1.5 软件参数配置

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第49张图片

配置UART串口,使能DMA配置

 【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第50张图片

在board.h手动添加串口相关宏定义

 【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第51张图片

1.6 串口调试助手相关指令测试记录

电表串口测试指令记录

1.1串口指令:查询电表地址
FE FE FE 68 AA AA AA AA AA AA 68 13 00 DF 16
1.2电表响应:
FE FE FE FE 68 70 40 01 19 03 19 68 93 06 A3 73 34 4C 36 4C 67 16
电表地址:70 40 01 19 03 19
实际地址:190319014070


2.1串口指令:(当前)正向有功总电能XXXXXX.XX
数据标识:  DI3 DI2 DI1 DI0     00 01 00 00
DI0-00-33  DI1-00-33   DI2-01-34   DI3-00-33
FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 33 34 33 98 16
2.2电表响应:
FE FE FE FE 68 70 40 01 19 03 19 68 91 08 33 33 34 33 76 39 33 33 31 16 
电能数据:76 39 33 33
76-33=43
39-33=6
电能为6.43kwh(度)


3.1串口指令:A 相电压XXX.X
数据标识:DI3 DI2 DI1 DI0    02  01  01  00
FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 34 34 35 9B 16
3.2电表响应
FE FE FE FE 68 70 40 01 19 03 19 68 91 06 33 34 34 35 BB 56 2E 16
BB-33=88
56-33=23
A相电压为238.8V

4.1串口指令:A 相电流XXX.XXX
FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 34 35 35 9C 16
4.2电表响应:
FE FE FE FE 68 70 40 01 19 03 19 68 91 07 33 34 35 35 75 33 33 FA 16
75-33=42
33-33=0
33-33=0
A相电流为0.042A


5.1串口指令:瞬时总有功功率XX.XXXX 
FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 33 36 35 9C 16
5.2电表响应:
FE FE FE FE 68 70 40 01 19 03 19 68 91 07 33 33 36 35 85 33 33 0A 16
85-33=52
33-33=0
33-33=0
瞬时总有功功率为0.0052KW,即5.2W

6.1串口指令:瞬时总无功功率
FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 33 37 35 9D 16
6.2电表响应XX.XXXX
FE FE FE FE 68 70 40 01 19 03 19 68 91 07 33 33 37 35 33 33 33 B9 16 
瞬时总无功功率为 0 kvar


7.1串口指令:总功率因数X.XXX 
FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 33 39 35 9F 16
7.2电表响应
FE FE FE FE 68 70 40 01 19 03 19 68 91 06 33 33 39 35 47 38 A0 16
47-33=14
38-33=5
总功率因数为0.514


8.串口指令:电网频率XX.XX
数据标识:DI3 DI2 DI1 DI0     02  80  00  02
DI0-02-35    DI1-00-33   DI2-80-B3   DI3-02-35
FE FE FE FE 68 70 40 01 19 03 19 68 11 04 35 33 B3 35 1B 16
电表响应:
FE FE FE FE 68 70 40 01 19 03 19 68 91 06 35 33 B3 35 33 83 53 16
33-33=0
83-33=50
电网频率为50.00Hz
FE FE FE FE 68 70 40 01 19 03 19 68 91 06 35 33 B3 35 C9 7C E2 16 
C9-33=96
7C-33=49
电网频率为49.96Hz

2、具体驱动代码

测试的电表数据数组

/**
 * @brief 查询电表的实际地址(默认为电表标签地址,可能存在被修改地址)
 * FE FE FE FE 68 AA AA AA AA AA AA 68 13 00 DF 16
 * {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x68, 0x13, 0x00, 0xDF, 0x16}
 * @example 电表响应数据:FE FE FE FE 68 70 40 01 19 03 19 68 93 06 A3 73 34 4C 36 4C 67 16
 * 响应有效数据:70 40 01 19 03 19 对应电表地址 190319014070
 * */
const uint8_t cmdAddr[16] = {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x68, 0x13, 0x00, 0xDF, 0x16};

/**
 * @brief (当前)正向有功总电能 XXXXXX.XX KWh
 * 数据标识:  DI3 DI2 DI1 DI0     00 01 00 00
 * DI0-00-33  DI1-00-33   DI2-01-34   DI3-00-33
 * FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 33 34 33 98 16
 * {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x33, 0x34, 0x33, 0x98, 0x16}
 * @example 电表响应数据:FE FE FE FE 68 70 40 01 19 03 19 68 91 08 33 33 34 33 76 39 33 33 31 16
 * 响应有效数据:76 39 33 33         0x76-0x33=0x43  0x39-0x33=0x06  0x33-0x33=0  0x33-0x33=0 对应电能 6.43KWh(度)
 * */
const uint8_t cmdEp[20] = {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x33, 0x34, 0x33, 0x98, 0x16};

/**
 * @brief A相电压 XXX.X V
 * 数据标识:DI3 DI2 DI1 DI0    02  01  01  00
 * DI0-00-33  DI1-01-34   DI2-01-34   DI3-02-35
 * FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 34 34 35 9B 16
 * {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x34, 0x34, 0x35, 0x9B, 0x16}
 * @example 电表响应数据:FE FE FE FE 68 70 40 01 19 03 19 68 91 06 33 34 34 35 BB 56 2E 16
 * 响应有效数据:BB 56     0xBB-0x33=0x88    0x56-0x33=0x23    对应A相电压  238.8V
 * */
const uint8_t cmdU[20] = {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x34, 0x34, 0x35, 0x9B, 0x16};

/**
 * @brief A相电流 XXX.XXX A
 * 数据标识:DI3 DI2 DI1 DI0    02  02  01  00
 * DI0-00-33  DI1-01-34   DI2-01-35   DI3-02-35
 * FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 34 35 35 9C 16
 * {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x34, 0x35, 0x35, 0x9C, 0x16}
 * @example 电表响应数据:FE FE FE FE 68 70 40 01 19 03 19 68 91 07 33 34 35 35 75 33 33 FA 16
 * 响应有效数据:75 33 33     0x75-0x33=0x42    0x33-0x33=0    0x33-0x33=0     对应A相电流  0.042A
 * */
const uint8_t cmdI[20] = {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x34, 0x35, 0x35, 0x9C, 0x16};

/**
 * @brief 瞬时总有功功率 XX.XXXX KW
 * 数据标识:DI3 DI2 DI1 DI0    02  03  00  00
 * DI0-00-33  DI1-00-33   DI2-03-36   DI3-02-35
 * FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 33 36 35 9C 16
 * {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x33, 0x36, 0x35, 0x9C, 0x16}
 * @example 电表响应数据:FE FE FE FE 68 70 40 01 19 03 19 68 91 07 33 33 36 35 85 33 33 0A 16
 * 响应有效数据:85 33 33     0x85-0x33=0x52    0x33-0x33=0    0x33-0x33=0     对应瞬时总有功功率   0.0052KW 即 5.2W
 * */
const uint8_t cmdP[20] ={0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x33, 0x36, 0x35, 0x9C, 0x16};

/**
 * @brief 瞬时总无功功率 XX.XXXX kvar
 * 数据标识:DI3 DI2 DI1 DI0    02  04  00  00
 * DI0-00-33  DI1-00-33   DI2-04-37   DI3-02-35
 * FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 33 37 35 9D 16
 * {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x33, 0x37, 0x35, 0x9D, 0x16}
 * @example 电表响应数据:FE FE FE FE 68 70 40 01 19 03 19 68 91 07 33 33 37 35 33 33 33 B9 16
 * 响应有效数据:33 33 33     0x33-0x33=0    0x33-0x33=0    0x33-0x33=0     对应瞬时总无功功率  0kvar
 * */
const uint8_t cmdQ[20] ={0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x33, 0x37, 0x35, 0x9D, 0x16};

/**
 * @brief 总功率因数 X.XXX
 * 数据标识:DI3 DI2 DI1 DI0    02  06  00  00
 * DI0-00-33  DI1-00-33   DI2-06-39   DI3-02-35
 * FE FE FE FE 68 70 40 01 19 03 19 68 11 04 33 33 39 35 9F 16
 * {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x33, 0x39, 0x35, 0x9F, 0x16}
 * @example 电表响应数据:FE FE FE FE 68 70 40 01 19 03 19 68 91 06 33 33 39 35 47 38 A0 16
 * 响应有效数据:47 38     0x47-0x33=0x14    0x38-0x33=0x05    对应总功率因数 0.514
 * */
const uint8_t cmdPF[20] ={0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x33, 0x39, 0x35, 0x9F, 0x16};

/**
 * @brief 电网频率 XX.XX Hz
 * 数据标识:DI3 DI2 DI1 DI0     02  80  00  02
 * DI0-02-35    DI1-00-33   DI2-80-B3   DI3-02-35
 * FE FE FE FE 68 70 40 01 19 03 19 68 11 04 35 33 B3 35 1B 16
 * {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x35, 0x33, 0xB3, 0x35, 0x1B, 0x16}
 * @example 电表响应数据:FE FE FE FE 68 70 40 01 19 03 19 68 91 06 35 33 B3 35 33 83 53 16
 * 响应有效数据:33 83     0x33-0x33=0    0x83-0x33=0x50    对应电网频率 50.00Hz
 * @example 电表响应数据:FE FE FE FE 68 70 40 01 19 03 19 68 91 06 35 33 B3 35 C9 7C E2 16
 * 响应有效数据:C9 7C     0xc9-0x33=0x96    0x7C-0x33=0x49    对应电网频率 49.96Hz
 * */
const uint8_t cmdFreq[20] ={0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x35, 0x33, 0xB3, 0x35, 0x1B, 0x16};

2.1 串口接收中断方式的驱动测试代码

//串口数据接收回调函数
static rt_err_t uart_rx_callback(rt_device_t dev,rt_size_t size)
{
    if(size > 0)
        rt_sem_release(&rx_sem);
    return RT_EOK;
}

static void dlt645_rx_entry(void *param)
{
    uint8_t ch;

    while(1){
        /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
        while (rt_device_read(serial, -1, &ch, 1) != 1)
        {
            /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }

    }
}

static void dlt645_send_cmd(const uint8_t *buf, uint8_t size)
{
    UART_RS485_TXEN_ON();
    rt_device_write(serial, 0, buf, size);
    UART_RS485_TXEN_OFF();
}

static void dlt645_tx_entry(void *param)
{
    while (1)
    {
        dlt645_send_cmd(cmdAddr, sizeof(cmdAddr));
        rt_thread_mdelay(1000);
        dlt645_send_cmd(cmdEp, sizeof(cmdEp));
        rt_thread_mdelay(1000);
        dlt645_send_cmd(cmdU, sizeof(cmdU));
        rt_thread_mdelay(1000);
        dlt645_send_cmd(cmdI, sizeof(cmdI));
        rt_thread_mdelay(1000);
        dlt645_send_cmd(cmdP, sizeof(cmdP));
        rt_thread_mdelay(1000);
        dlt645_send_cmd(cmdQ, sizeof(cmdQ));
        rt_thread_mdelay(1000);
        dlt645_send_cmd(cmdPF, sizeof(cmdPF));
        rt_thread_mdelay(1000);
        dlt645_send_cmd(cmdFreq, sizeof(cmdFreq));
        rt_thread_mdelay(53000);
    }
}

int dlt645_drive_init(void)
{
    rt_pin_mode(UART_RS485_TXEN_PIN, PIN_MODE_OUTPUT);

    serial = rt_device_find(ENERGY_UART_NAME);

    if(RT_NULL == serial) {
        rt_kprintf("find device serial failed !\r\n");
        return RT_ERROR;
    } else {
        rt_kprintf("find device serial success !\r\n");
    }

    config.baud_rate = BAUD_RATE_2400;  //波特率2400
    config.data_bits = DATA_BITS_9;
    config.stop_bits = STOP_BITS_1;
    config.bufsz = 128;
    config.parity = PARITY_EVEN;    //偶校验

    /* 控制串口设备。通过控制接口传入命令控制字,与控制参数 */
    rt_device_control(serial,RT_DEVICE_CTRL_CONFIG,&config);

    /* 以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial,RT_DEVICE_FLAG_INT_RX);

    rt_sem_init(&rx_sem,"rx_sem",0, RT_IPC_FLAG_FIFO);

    rt_device_set_rx_indicate(serial, uart_rx_callback);

    tid_dlt645_rx = rt_thread_create("dlt_rx",
                                dlt645_rx_entry, RT_NULL,
                                THREAD_DLT645_RX_STACK_SIZE,
                                THREAD_DLT645_RX_PRIORITY, THREAD_DLT645_RX_TIMESLICE);
    /* 如果获得线程控制块,启动这个线程 */
    if (tid_dlt645_rx != RT_NULL)
        rt_thread_startup(tid_dlt645_rx);

    tid_dlt645_tx = rt_thread_create("dlt_tx",
                                dlt645_tx_entry, RT_NULL,
                                THREAD_DLT645_TX_STACK_SIZE,
                                THREAD_DLT645_TX_PRIORITY, THREAD_DLT645_TX_TIMESLICE);
    /* 如果获得线程控制块,启动这个线程 */
    if (tid_dlt645_tx != RT_NULL)
        rt_thread_startup(tid_dlt645_tx);

    return RT_EOK;
}

INIT_APP_EXPORT(dlt645_drive_init);

2.2 串口DMA中断方式的驱动测试代码

(1)头文件相关定义

//数据标识SDID  DI3 DI2 DI1 DI0
#define SDID_EP     0X00010000  //数据标识:(当前)正向有功总电能 XXXXXX.XX KWh
#define SDID_U      0X02010100  //数据标识:A相电压 XXX.X V
#define SDID_I      0X02020100  //数据标识:A相电流 XXX.XXX A
#define SDID_P      0X02030000  //数据标识:瞬时总有功功率 XX.XXXX KW
#define SDID_Q      0X02040000  //数据标识:瞬时总无功功率 XX.XXXX kvar
#define SDID_PF     0X02060000  //数据标识:总功率因数 X.XXX
#define SDID_FREQ   0X02800002  //数据标识:电网频率 XX.XX Hz

enum UART_STATE_ENUM
{
    UART_INIT,UART_ING,UART_OVER
};

struct rx_msg
{
    rt_device_t dev;
    rt_size_t size;
};

typedef struct
{
    enum UART_STATE_ENUM state;       //串口接收状态标志
    uint8_t head;       //串口接收起始索引
    uint8_t end;       //串口接收结束索引
    char buf[128];     //串口接收缓存数据
    char data[128];     //串口接收命令数据
    uint8_t length;     //命令的数组长度
    uint8_t index;      //数组索引号
    uint8_t addr[6];//电表地址 70 40 01 19 03 19 => 190319014070
    float   ep;     //(当前)正向有功总电能 XXXXXX.XX KWh
    float   u;      //A相电压 XXX.X V
    float   i;      //A相电流 XXX.XXX A
    float   p;      //瞬时总有功功率 XX.XXXX KW
    float   q;      //瞬时总无功功率 XX.XXXX kvar
    float   pf;     //总功率因数 X.XXX
    float   freq;   //电网频率 XX.XX Hz
} DLT645_DATA_t;

(2)串口初始化、创建发送接收的动态线程

int dlt645_drive_init(void)
{
    static char msg_pool[256];

    rt_pin_mode(UART_RS485_TXEN_PIN, PIN_MODE_OUTPUT);

    serial = rt_device_find(ENERGY_UART_NAME);

    if(RT_NULL == serial) {
        rt_kprintf("find device serial failed !\r\n");
        return RT_ERROR;
    } else {
        rt_kprintf("find device serial success !\r\n");
    }

    rt_mq_init(&rx_mq, "rx_mq", msg_pool, sizeof(struct rx_msg), sizeof(msg_pool), RT_IPC_FLAG_FIFO);
    rt_sem_init(&rx_sem,"rx_sem",0, RT_IPC_FLAG_FIFO);

    config.baud_rate = BAUD_RATE_2400;  //波特率2400
    config.data_bits = DATA_BITS_9;
    config.stop_bits = STOP_BITS_1;
    config.bufsz = 128;
    config.parity = PARITY_EVEN;    //偶校验

    /* 控制串口设备。通过控制接口传入命令控制字,与控制参数 */
    rt_device_control(serial,RT_DEVICE_CTRL_CONFIG,&config);

    /* 以 DMA 接收及轮询发送方式打开串口设备 */
    rt_device_open(serial,RT_DEVICE_FLAG_DMA_RX);

    rt_device_set_rx_indicate(serial, uart_rx_callback);

    tid_dlt645_rx = rt_thread_create("dlt_rx",
                                dlt645_rx_entry, RT_NULL,
                                THREAD_DLT645_RX_STACK_SIZE,
                                THREAD_DLT645_RX_PRIORITY, THREAD_DLT645_RX_TIMESLICE);
    /* 如果获得线程控制块,启动这个线程 */
    if (tid_dlt645_rx != RT_NULL)
        rt_thread_startup(tid_dlt645_rx);

    tid_dlt645_tx = rt_thread_create("dlt_tx",
                                dlt645_tx_entry, RT_NULL,
                                THREAD_DLT645_TX_STACK_SIZE,
                                THREAD_DLT645_TX_PRIORITY, THREAD_DLT645_TX_TIMESLICE);
    /* 如果获得线程控制块,启动这个线程 */
    if (tid_dlt645_tx != RT_NULL)
        rt_thread_startup(tid_dlt645_tx);

    return RT_EOK;
}

INIT_APP_EXPORT(dlt645_drive_init);

(3)串口接收回调函数,DMA接收完成后发送消息队列

//串口数据接收回调函数
static rt_err_t uart_rx_callback(rt_device_t dev,rt_size_t size)
{
    struct rx_msg msg;
    rt_err_t result;
    msg.dev = dev;
    msg.size = size;

    result = rt_mq_send(&rx_mq, &msg, sizeof(msg));
    if(result == -RT_EFULL){
        /* 消息队列满 */
        rt_kprintf("message queue full!\n");
    }
    return result;
}

(4)接收线程等待消息队列,电表协议数据解析

static void dlt645_rx_entry(void *param)
{
    struct rx_msg msg;
    rt_err_t result;
    rt_uint32_t rx_length;
    static char rx_buffer[RT_SERIAL_RB_BUFSZ  +1];
    char crc_sum = 0;
    rt_uint32_t data_type = 0;
    char data_dis[32] = {0};
    uint8_t pos = 0;


    while(1){
        rt_memset(&msg, 0, sizeof(msg));
        result = rt_mq_recv(&rx_mq, &msg, sizeof(msg), RT_WAITING_FOREVER);

        if(result == RT_EOK){
            rx_length = rt_device_read(msg.dev, 0, rx_buffer, msg.size);

            if(dlt645_data.state == UART_INIT){
                for (int i = 0; i < rx_length; ++i) {
                    if((rx_buffer[i] == 0xFE)&&(rx_buffer[i+1] == 0x68)){
                        dlt645_data.head = i+1;
                        dlt645_data.state = UART_ING;
                    }
                    if(dlt645_data.state == UART_ING){
                        if(rx_buffer[i] == 0x16){
                            dlt645_data.end = i;
                            dlt645_data.state = UART_OVER;
                        }
                    }
                }
                if(dlt645_data.state == UART_ING){
                    for (int i = dlt645_data.head; i < rx_length; ++i) {
                        dlt645_data.buf[dlt645_data.index++] = rx_buffer[i];
                        dlt645_data.length++;
                    }
                }else if(dlt645_data.state == UART_OVER){
                    for (int i = dlt645_data.head; i < dlt645_data.end; ++i) {
                        dlt645_data.buf[dlt645_data.index++] = rx_buffer[i];
                        dlt645_data.length++;
                    }
                }
            } else if(dlt645_data.state == UART_ING){
                for (int i = 0; i < rx_length; ++i) {
                    if(dlt645_data.state == UART_ING){
                        if(rx_buffer[i] == 0x16){
                            dlt645_data.end = i;
                            dlt645_data.state = UART_OVER;
                        }
                    }
                }
                if(dlt645_data.state == UART_ING){
                    for (int i = 0; i < rx_length; ++i) {
                        dlt645_data.buf[dlt645_data.index++] = rx_buffer[i];
                        dlt645_data.length++;
                    }
                }else if(dlt645_data.state == UART_OVER){
                    for (int i = 0; i < dlt645_data.end; ++i) {
                        dlt645_data.buf[dlt645_data.index++] = rx_buffer[i];
                        dlt645_data.length++;
                    }
                }
            }

            if(dlt645_data.state == UART_OVER){
                dlt645_data.state = UART_INIT;
                rt_kprintf("\r\n");
                for (int i = 0; i < dlt645_data.length; ++i) {
                    dlt645_data.data[i] = dlt645_data.buf[i];
                    rt_kprintf("%x ", dlt645_data.data[i]);
                }

                for (int i = 0; i < dlt645_data.length-1; ++i) {
                    crc_sum += dlt645_data.data[i];
                    if((0x68 == dlt645_data.data[i])&&((0x91 == dlt645_data.data[i+1])||(0x93 == dlt645_data.data[i+1]))){
                        pos = i+1;
                    }
                }

                if(dlt645_data.data[dlt645_data.length-1] == crc_sum){
                    if(0x93 == dlt645_data.data[pos]){ //读通信地址响应码0x93
                        pos++;
                        if(0x06 == dlt645_data.data[pos])
                            LOG_D("Addr = %02x%02x%02x%02x%02x%02x",dlt645_data.data[pos+6]-0x33,dlt645_data.data[pos+5]-0x33,dlt645_data.data[pos+4]-0x33,\
                                    dlt645_data.data[pos+3]-0x33,dlt645_data.data[pos+2]-0x33,dlt645_data.data[pos+1]-0x33);
                    }else if(0x91 == dlt645_data.data[pos]){   //读数据响应码0x91
                        pos++;
                        data_type = ((dlt645_data.data[pos+4]-0x33)<<24) + ((dlt645_data.data[pos+3]-0x33)<<16) + ((dlt645_data.data[pos+2]-0x33)<<8) + (dlt645_data.data[pos+1]-0x33);
                        switch (data_type) {
                            case SDID_EP:   //(当前)正向有功总电能 XXXXXX.XX KWh
                                sprintf(data_dis,"%x%x%x.%02x",dlt645_data.data[pos+8]-0x33,dlt645_data.data[pos+7]-0x33,dlt645_data.data[pos+6]-0x33,dlt645_data.data[pos+5]-0x33);
                                dlt645_data.ep = atof(data_dis);
                                LOG_D("EP= %.2f KWh",dlt645_data.ep);
                                break;
                            case SDID_U:    //A相电压 XXX.X V
                                sprintf(data_dis,"%x%x.%x",dlt645_data.data[pos+6]-0x33,(dlt645_data.data[pos+5]-0x33)>>4,(dlt645_data.data[pos+5]-0x33)&0x0F);
                                dlt645_data.u = atof(data_dis);
                                LOG_D("U= %.1f V",dlt645_data.u);
                                break;
                            case SDID_I:    //A相电流 XXX.XXX A
                                sprintf(data_dis,"%x%x.%x%02x",dlt645_data.data[pos+7]-0x33,(dlt645_data.data[pos+6]-0x33)>>4,\
                                        (dlt645_data.data[pos+6]-0x33)&0x0F,dlt645_data.data[pos+5]-0x33);
                                dlt645_data.i = atof(data_dis);
                                LOG_D("I= %.3f A",dlt645_data.i);
                                break;
                            case SDID_P:    //瞬时总有功功率 XX.XXXX KW
                                sprintf(data_dis,"%x.%02x%02x",dlt645_data.data[pos+7]-0x33,dlt645_data.data[pos+6]-0x33,dlt645_data.data[pos+5]-0x33);
                                dlt645_data.p = atof(data_dis);
                                LOG_D("P= %.4f KW",dlt645_data.p);
                                break;
                            case SDID_Q:    //瞬时总无功功率 XX.XXXX kvar
                                sprintf(data_dis,"%x.%02x%02x",dlt645_data.data[pos+7]-0x33,dlt645_data.data[pos+6]-0x33,dlt645_data.data[pos+5]-0x33);
                                dlt645_data.q = atof(data_dis);
                                LOG_D("Q= %.4f kvar",dlt645_data.q);
                                break;
                            case SDID_PF:   //总功率因数 X.XXX
                                sprintf(data_dis,"%x.%x%x",(dlt645_data.data[pos+6]-0x33)>>4,(dlt645_data.data[pos+6]-0x33)&0x0F,dlt645_data.data[pos+5]-0x33);
                                dlt645_data.pf = atof(data_dis);
                                LOG_D("PF= %.3f ",dlt645_data.pf);
                                break;
                            case SDID_FREQ: //电网频率 XX.XX Hz
                                sprintf(data_dis,"%x.%2x",dlt645_data.data[pos+6]-0x33,dlt645_data.data[pos+5]-0x33);
                                dlt645_data.freq = atof(data_dis);
                                LOG_D("FREQ= %.2f Hz",dlt645_data.freq);
                                break;
                            default:
                                LOG_D("crc sum ok");
                                break;
                        }
                    }
                }else{
                    LOG_D("crc sum err");
                }

                rt_kprintf("\r\n");
                pos = 0;
                crc_sum = 0;
                dlt645_data.head = 0;
                dlt645_data.end = 0;
                dlt645_data.index = 0;
                dlt645_data.length = 0;
            }

        }
    }
}

(5)发送线程,串口读取电表数据的指令

//读数据,控制码C=11H
static void dlt645_read_data_code11(uint32_t sdid)
{
    uint crc_sum = 0;

    uint8_t buf[20] = {0xFE, 0xFE, 0xFE, 0xFE, 0x68, 0x70, 0x40, 0x01, 0x19, 0x03, 0x19, 0x68, 0x11, 0x04, 0x33, 0x33, 0x34, 0x33, 0x98, 0x16};
    buf[5] = dlt645_data.addr[0];
    buf[6] = dlt645_data.addr[1];
    buf[7] = dlt645_data.addr[2];
    buf[8] = dlt645_data.addr[3];
    buf[9] = dlt645_data.addr[4];
    buf[10] = dlt645_data.addr[5];
    buf[14] = (sdid & 0xFF) + 0x33;
    buf[15] = (sdid>>8 & 0xFF) + 0x33;
    buf[16] = (sdid>>16 & 0xFF) + 0x33;
    buf[17] = (sdid>>24 & 0xFF) + 0x33;
    for(int i = 4; i < 18; i++){
        crc_sum += buf[i];
    }
    buf[18] = crc_sum;
    crc_sum = 0;

    UART_RS485_TXEN_ON();
    rt_device_write(serial, 0, buf, sizeof(buf));
    UART_RS485_TXEN_OFF();
}

//读通信地址,控制码C=13H
static int dlt645_read_addr_code13()
{
    UART_RS485_TXEN_ON();
    rt_device_write(serial, 0, cmdAddr, sizeof(cmdAddr));
    UART_RS485_TXEN_OFF();

    rt_thread_mdelay(3000);

    if((0 == dlt645_data.addr[0])&&(0 == dlt645_data.addr[1])&&(0 == dlt645_data.addr[2])\
            &&(0 == dlt645_data.addr[3])&&(0 == dlt645_data.addr[4])&&(0 == dlt645_data.addr[5])){
        return -1;
    }

    return 0;
}


static void dlt645_tx_entry(void *param)
{
    int result = 0;

    result = dlt645_read_addr_code13();
    if(-1 == result){
        rt_kprintf("DL645 electricity meter is not exist !\n");
        return;
    }
    rt_kprintf("DL645 electricity meter is exist .\n");

    while(1){
        dlt645_read_data_code11(SDID_EP);
        rt_thread_mdelay(1000);
        dlt645_read_data_code11(SDID_U);
        rt_thread_mdelay(1000);
        dlt645_read_data_code11(SDID_I);
        rt_thread_mdelay(1000);
        dlt645_read_data_code11(SDID_P);
        rt_thread_mdelay(1000);
        dlt645_read_data_code11(SDID_Q);
        rt_thread_mdelay(1000);
        dlt645_read_data_code11(SDID_PF);
        rt_thread_mdelay(1000);
        dlt645_read_data_code11(SDID_FREQ);
        rt_thread_mdelay(23000);
    }
}

3、测试结果

通过测试串口读取的电表数据正常,串口实时打印电能表的电能表地址、(当前)正向有功总电能、A相电压、A相电流、瞬时总有功功率、瞬时总无功功率、总功率因数、电网频率等数据。

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第52张图片

三、相关链接

1、 DLT645-2007_[带2013备案文件】.pdf

DLT645-2007_[带2013备案文件】.pdf(带书签)-嵌入式文档类资源-CSDN下载

DLT645-2007_[带2013备案文件】.pdf的百度网盘下载链接:

链接:https://pan.baidu.com/s/1FQAZf8OYg9KlXTKUOUZIfQ 
提取码:5uhj 

2、 参考资料 

https://github.com/hassin23ayz/dlt645_2007

【RT-Thread】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★_第53张图片

DLT645_2007: DL/T 645 - 2007协议解析

DLT645-2007电能表通讯协议_u013184273的博客-CSDN博客_dlt645

DLT645-2007 多功能电表通讯_chyubo的博客-CSDN博客

DLT645-2007电表协议解析_超级浣熊-CSDN博客_dlt645

关于DLT645-2007 充电桩上应用 笔记_mengqingbin5219的博客-CSDN博客_dlt645-2007

DLT645-2007通讯规约解析_allemn的专栏-CSDN博客_dlt645

DLT645-2007电能表通讯协议_u013184273的博客-CSDN博客_dlt645

你可能感兴趣的:(RT_Thread,RT-Thread,DLT645-2007,UART,RS485)