stm32实现电表电量采集

stm32实现电表电量采集

wangxl@20180815

一、概述

  1. 本章中,不特殊说明都以stm32F030为例,软件平台为iar6.4
  2. 通过开发板的485与电表通讯,波特率采用1200,根据DLT645-1997通讯规约通信规约,采集电表的电量。

二、本章学习目标

  1. 学会了解DLT645-1997通讯规约通信规约
  2. 理解stm32 485配置
  3. 学会485通讯及串口收发数据处理
  • DLT645-1997通讯规约
  1. 帧格式:

  

  

帧起始符

68H

地址域

A0

A1

A2

A3

A4

A5

帧起始符

68

控制码

C

数据长度域

L

数据域

DATA

校验码

CS

结束符

16H

帧起始符68H :标识一帧信息的开始,其值为 68H;

地址域  A0 ∽ A5 :地址域由 6 个字节构成,每字节 2 位 BCD 码。

当地址为999999999999H 时,为广播地址;

控制码:读数据主站请求帧 控制码 C=01H;从站正常应答 控制码 C=81H 无后续数据帧;

数据长度域 L :L 为数据域的字节数;

数据域  DATA :数据域包括数据标识和数据、密码等,其结构随控制码的功能而改变。传输时发送方按字节进行加 33H 处理,接收方按字节进行减 33H 处理。

校验码  CS :从帧起始符开始到校验码之前的所有各字节的模 256 的和, 即各字节二进制算术和,不计超过 256 的溢出值。

当前正向有功总电能的数据标识为:9010H,数据格式为 XXXXXX.XX(kWh)

  1. 报文格式简介

(1)发送求请当前正向有功总电能的数据:

报文: FE FE FE 68 99 99 99 99 99 99 68 01 02 43 c3 6f 16

数据分析:

FE:为唤醒符,报文之前可以加若干个FE;

68:帧起始符;

99 99 99 99 99 99:地址域,为广播表号;

68:帧起始符;

01:控制码,为读数据(主站请求帧);

02:指数据区域字节数,表示之后2个字节为数据区域,也就是43 c3;

43 c3:数据区域,传输时发送方按字节进行加 33H 处理,接收方按字节进行减 33H 处理,接收处理后为10 90,刚好等于当前正向有功总电能的数据标识为0x9010;

6f :校验码,校验方式为和校验,取最低位字节,

(16进制68+99+99+99+99+99+99+68+01+02+43+c3=56F,取6F)

16:结束符

 

  1. 接收当前正向有功总电能的数据

报文:FE FE FE FE 68 15 32 00 67 00 00 68 81 06 43 C3 84 77 33 33 6C 16

FE:为唤醒符;

15 32 00 67 00 00:地址域,表号;

81:从站正常应答

06:数据区域字节数为6字节;

43 C3 84 77 33 33:减33H后为10 90 51 44 00 00 ,数据标识为10 90,相当于u16数据类型的0x9010,根据协议解析为读正向有功总电能,数据为51 44 00 00,当于u32数据类型的0x00004451,即解析出电量数据为44.51kWh;参见下表电能量数据标识编码表;

stm32实现电表电量采集_第1张图片

详见:DL/T645-2007  通讯规约通信规约

四、RS485配置及数据处理

1.配置外设时钟

需要配置串口所在引脚IO的时钟及串口的时钟,本例用串口1做485,开启GPIOA的时钟,及USART1时钟。 参考代码:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);


2.配置 CS引脚

 RS485需要设置CS脚来控制收发模式;

#define  RS485_CS_PIN          GPIO_Pin_4 // 485  CS--PA4

#define CS_485_TX  GPIO_ResetBits(GPIOA, RS485_CS_PIN) #define  CS_485_RX  GPIO_SetBits(GPIOA, RS485_CS_PIN)  

GPIO_InitTypeDef    GPIO_InitStructure;

      GPIO_InitStructure.GPIO_Pin =  RS485_CS_PIN ;

      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;

      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

      GPIO_Init(GPIOA, &GPIO_InitStructure);

初始USART1配置

需要跟电表485通讯方式一致;电表为半双口通讯方式,波特率:1200bps。

通讯字节格式:每字节含8位二进制码,传输时加上一个起始位(0)、一个偶校验位和一个停止位(1)共11;配置如下:

USART_InitTypeDef USART_InitStructure;

 USART_InitStructure.USART_BaudRate = 1200;

      USART_InitStructure.USART_WordLength = USART_WordLength_9b;

      USART_InitStructure.USART_StopBits = USART_StopBits_1;

      USART_InitStructure.USART_Parity =USART_Parity_Even;

      USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

      USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

USART_Init(USART1, &USART_InitStructure);

注:

在STM32中WordLength需要包含数据位数和奇偶校验的位数;

如果需要8位数据,无奇偶校验,则WordLength=8;

如果需要8位数据,有奇偶校验,则WordLength=9;(电表需要偶校验所以WordLength配为9)

 

3、发送数据

485是半双工(发时不能收,收时不能发)工作模式,在485通信中,收发是通过CS脚来控制切换R/D高低电平来实现的,通过了解基本的RS485电路,R/D为低电平时,发送禁止,接收有效,R/D为高电平时,则发送有效,接收截止;所以发送数据时,需要先配置成可发送的状态,数据发送完后,再切换成接收状态;

 

u16 Uart_E_Meter_Write_data(u8 *odata,u16 odatalen)

{

         

    CS_485_TX ; //设置为发送模式

    

      for(u8 i=0;i<4;i++)

    {

 

         USART_SendData(CMU_E_METER_USART_PORT, 0xfe);

         while(USART_GetFlagStatus(CMU_E_METER_USART_PORT, USART_FLAG_TXE) == RESET );

 

    }

    

       while(odatalen--)

    {

                 USART_SendData(CMU_E_METER_USART_PORT, *odata++);

         while(USART_GetFlagStatus(CMU_E_METER_USART_PORT, USART_FLAG_TXE) == RESET );

 

    }

      delay_ms(20); //跟据芯片做调整

      CS_485_RX ;//发送完设置为接收模式

      

    return 0;

}

 

4、数据编码函数

*功能描述: 数据编码成dtl645格式 返回包长度

*返回值  :  dtl645包长度

*参数    :  *meter_id电表ID,*src控制码+长度+DATA , srcLenth src长度,*dst 返回数据包

u16 Eleout_Encode(u8 *meter_id, u8 *src, u8 srcLenth, u8 *dst)

{

    u8 i = 0, prt = 0;

    u8 CHK = 0;

    u16 packlen = srcLenth + 10;

    

    dst[prt++] = 0x68;                    /*STX*/

    memcpy((u8 *)&dst[prt], (u8 *)meter_id, 6);

    prt += 6;

    dst[prt++] = 0x68;           /*STX*/

    dst[prt++] = src[i++];                      /*控制码*/

    dst[prt++] = src[i++];                      /*数据域长度*/

    

    for( ; i < srcLenth; i++)               /*DATA*/

    {    

        dst[prt++] = src[i] + 0x33;           /*加33H处理*/

    }

 

    for(i = 0; i < packlen - 2; i++)

    {

        CHK += dst[i];                           /*CHK*/

    }

    

    dst[prt++] = CHK;            /*CHK*/

    

    dst[prt++] = 0x16;              /*ETX*/

 

    return packlen;

}

5、参考本例程下载

https://download.csdn.net/download/flyme2010/10607580

你可能感兴趣的:(stm32)