目录

一、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型单相电子式电能表(导轨)

出厂电能表的默认地址如同所示:190319014070

测试接线电路

1、硬件描述

默认通信速率:2400bps

RS- - 485  标准串行电气接口

2、帧数据格式

帧数据格式:每帧由帧起始符、从站地址域、控制码、数据域长度、数据域、帧信息纵向校验码及帧结束符 7 个域组成

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

    //串口参数配置config.baud_rate = BAUD_RATE_2400;     //波特率2400config.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设备驱动

RT-Thread 文档中心https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart_v1/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
实际地址:1903190140702.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.8V4.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.042A5.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.2W6.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 kvar7.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.5148.串口指令:电网频率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;  //波特率2400config.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 Hzenum 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 => 190319014070float   ep;     //(当前)正向有功总电能 XXXXXX.XX KWhfloat   u;      //A相电压 XXX.X Vfloat   i;      //A相电流 XXX.XXX Afloat   p;      //瞬时总有功功率 XX.XXXX KWfloat   q;      //瞬时总无功功率 XX.XXXX kvarfloat   pf;     //总功率因数 X.XXXfloat   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;  //波特率2400config.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]){ //读通信地址响应码0x93pos++;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]){   //读数据响应码0x91pos++;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 KWhsprintf(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 Vsprintf(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 Asprintf(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 KWsprintf(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 kvarsprintf(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.XXXsprintf(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 Hzsprintf(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相电流、瞬时总有功功率、瞬时总无功功率、总功率因数、电网频率等数据。

三、相关链接

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

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】UART串口设备驱动★DLT645-2007多功能电能表通信协议★RTThread★相关推荐

  1. DL/T 645多功能电能表通信协议测试方法

    1 前言 DL/T645多功能电能表通信协议(Multi-function watt-hour meter communication protocol)标准是为统一和规范电能表的多功能电能表与数据终 ...

  2. RT-Thread uart串口设备驱动代码结构剖析

    硬件测试平台:正点原子潘多拉STM32L4开发板 OS内核版本:4.0.0 注意:下面的示例代码是从原子提供的例程中摘录,因此可能与最新的RT-Thread源码有出入(因为RT-Thread源码在不断 ...

  3. 安科瑞导轨式多功能电能表DTSD1352指导性技术要求-Susie 周

    1.参考标准 1)GB/T 17215.322-2008 交流电测量设备 特殊要求 第22部分:静止式有功电能表(0.2S级和0.5S级): 2)GB/T 17215.323-2008 交流电测量设备 ...

  4. xpt 2046的触摸屏 rt thread设备驱动框架

    1 基于rtt 开发触摸屏驱动 准备使用rtt 框架 , 驱动xpt 2046的触摸屏, 翻阅大量资料发现, 大部分文章强调的是时序图, 而且很多代码要么直接操作寄存器, 要么是io 口模拟, 只能用 ...

  5. 基于GD32F103C8T6添加RT Thread nano设备框架并添加串口设备(以控制台console( uart0 )为例)

    最近没事琢磨了一下使用设备框架的问题.因为将串口注册到设备框架可以应用十分丰富的软件包. 于是就整理了一下手上的工程,重新将工程梳理了一遍. 像这样是十分清爽了,其中RTOS是操作系统源代码 并且学习 ...

  6. RT Thread之 Uart2 操作

    官网连接:https://docs.rt-thread.org/#/rt-thread-version/rt-thread-standard/programming-manual/device/uar ...

  7. Linux驱动开发——串口设备驱动

    Linux驱动开发--串口设备驱动 一.串口简介 串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单.使用两条线即可实现双向通信,一条用于发送,一条用于 ...

  8. usb 转 uart cp210x 驱动解析

    USB 转 uart (cp210x.c) 驱动解析 * usb_serial_driver 结构体解析 include/linux/usb/serial.h/** 描述一个usb 串口设备驱动 * ...

  9. rt thread studio使用QBOOT和片外flash实现OTA升级

    我们这里要使用单片机外部flash作为OTA的下载分区,外部flash硬件连接关系 PB3-->SPI3_CLK PB4-->SPI3_MISO PB5-->SPI3_MOSI PE ...

最新文章

  1. AtCoder AGC037E Reversing and Concatenating
  2. AIX卷管理介绍以及利用空闲PP来创建文件系统
  3. 机器学习实战(十四)利用SVD简化数据
  4. 【Python】ffmpeg模块查询视频、音频信息
  5. 转:性能测试中常见的性能问题及识别方法
  6. 万字解读鸿蒙轻内核物理内存模块
  7. eclipse导入项目发生的Android Private Libraries丢失
  8. GDB使用小结- 可带参数
  9. mac上最好用的免费PDF阅读器是哪个
  10. C#版TXT文本分割器
  11. 计算机信息安全专业代码0839,网络安全/信息安全专业大学排名(2017-2018-安全导航)...
  12. 2007年会计从业资格练习第三章会计科目和账户
  13. Python 手写体识别
  14. CMake mingw 编译glm
  15. 安卓 魔窗SDK 快速接入
  16. HDU5510 Bazinga(KMP)
  17. hustoj安装16种判题语言
  18. 设计模式---004策略模式(转载自我的老师 Alley-巷子)
  19. c语言 proteus 延迟2秒_几种延时的汇编执行代码对比与总结
  20. C语言的前置++和后置++

热门文章

  1. 华为鸿蒙系统智能手机_知科技-新鲜事|华为将发布鸿蒙系统智能手机
  2. 基于MATLAB Simulink的光伏特性程序,改程序说明了太阳辐射强度、光伏电池温度
  3. vim-surround 插件用法
  4. 利用while 循环判断
  5. 神秘大佬写的运营思维课
  6. Python爬虫-使用Jupyter爬虫
  7. 有哪些道理你后悔没有早点知道?
  8. 超宽带室内信道模型研究与matlab仿真,复杂室内环境超宽带信号信道模型及仿真结果分析.pdf...
  9. 下载及配置maven详细步骤
  10. Swift基础语法学习笔记(1)