RT-ThreadDLT645-2007多功能电能表通信协议及程序驱动代码 ★★★★★
Posted 风浪云HH
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RT-ThreadDLT645-2007多功能电能表通信协议及程序驱动代码 ★★★★★相关的知识,希望对你有一定的参考价值。
目录
1、 DLT645-2007_[带2013备案文件】.pdf
一、DLT645-2007多功能电能表通信协议
电能表规格型号:正泰DDSU666型单相电子式电能表(导轨)
出厂电能表的默认地址如同所示:190319014070
测试接线电路
1、硬件描述
默认通信速率:2400bps
RS- - 485 标准串行电气接口
2、帧数据格式
帧数据格式:每帧由帧起始符、从站地址域、控制码、数据域长度、数据域、帧信息纵向校验码及帧结束符 7 个域组成
串口传输字节配置则为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; //偶校验
注意:数据标识、密码、操作者代码、数据、帧序号传输时发送方按字节进行加 33H 处理。
注意:通信主机发送帧需先发 4 个字节 FEH唤醒电表从机
注意:所有数据项均先传送低位字节,后传送高位字节。在程序代码计算时,先接收的数据为低位字节。
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、数据编码
(当前)正向有功总电能 => 数据标识:DI3 DI2 DI1 DI0 00 01 00 00
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
电网频率 => 数据标识:DI3 DI2 DI1 DI0 02 80 00 02
二、RT-Thread框架下程序驱动代码
1、相关软件配置
1.1 RT-Thread 物联网操作系统
操作系统:RT-Thread 小而美的物联网操作系统
RT-Thread, RTOS, 物联网操作系统 - RT-Thread物联网操作系统
1.2 RT-Thread 文档中心
RT-Thread 文档中心https://www.rt-thread.org/document/site/#/
1.3 UART设备驱动
1.4 开发工具:RT-Thread Studio
一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效。
RT-Thread Studio - RT-Thread物联网操作系统RT-Thread Studiohttps://www.rt-thread.org/page/studio.html
1.5 软件参数配置
配置UART串口,使能DMA配置
在board.h手动添加串口相关宏定义
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相电流、瞬时总有功功率、瞬时总无功功率、总功率因数、电网频率等数据。
3、驱动完善
后续将继续更新,完善驱动代码。
三、相关链接
1、 DLT645-2007_[带2013备案文件】.pdf
DLT645-2007_[带2013备案文件】.pdf(带书签)-嵌入式文档类资源-CSDN下载
2、 程序代码
相关程序代码的下载链接待后续完善后上传!
以上是关于RT-ThreadDLT645-2007多功能电能表通信协议及程序驱动代码 ★★★★★的主要内容,如果未能解决你的问题,请参考以下文章
国网智能电表数据采集集中抄表DLT645协议规约电力测试软件
我要做电能采集程序,电能表里面是DL/T645的规约,还要用到modbus,请问这两个协议怎么去理解协作啊?