wangxl@20180815
说 明 |
代 码 |
帧起始符 |
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)发送求请当前正向有功总电能的数据:
报文: 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:结束符
报文: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;参见下表电能量数据标识编码表;
详见:DL/T645-2007 通讯规约通信规约
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