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

 出厂电能表的默认地址如同所示: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设备驱动

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
实际地址: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相电流、瞬时总有功功率、瞬时总无功功率、总功率因数、电网频率等数据。

三、相关链接

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

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值