目錄
一、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中斷方式的驅動測驗代碼
3、驅動完善
三、相關鏈接
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相電流、瞬時總有功功率、瞬時總無功功率、總功率因數、電網頻率等資料,

3、驅動完善
后續將繼續更新,完善驅動代碼,
三、相關鏈接
1、 DLT645-2007_[帶2013備案檔案】.pdf
DLT645-2007_[帶2013備案檔案】.pdf(帶書簽)-嵌入式檔案類資源-CSDN下載
2、 程式代碼
相關程式代碼的下載鏈接待后續完善后上傳!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/377207.html
標籤:其他
