电总协议又称为空调协议,广泛应用于家用空调和一些高压输电场合,在电力电子当中使用非常的广泛!!!美的空调就是采用的电总协议进行红外遥控控制的!
先看数据帧格式

在基本格式中的各项除SOI和EOI是以十六进制解释(SOI = 7EH,EOI = 0DH),十六进制传输外,其余各项都是以十六进制解释,以ASCII码的方式传输,每个字节用
两个ASCII码表示,即高四位用一个ASCII码表示,低四位用一个ASCII码表示。例:CID2 = 4BH,传送时顺序发送34H,42H。
因此,上表以及以下各表中“字节数”是指“解释字节数”,除SOI和EOI外,实际
传输字节数应该乘以2。
1.物理接口
串行通信口采用RS485/RS232。
信息传输方式为异步方式,起始位1位,数据位8位,停止位1位,无校验。
数据传输速率为1200、2400、4800、9600和19200bits可以设置。
2.通信方式
在局站内的监控系统为分布式结构。局站监控单元(SU)与设备监控模块(SM)的通信为主从方式,监控单元为上位机,监控模块为下位机。SU呼叫SM并下发命令,SM收到命令后返回响应信息。SU 500ms内接收不到SM响应或接收响应信息错误,则认为本次通信过程失败。
在本系统中,精密空调控制器为SM,上位机为SU。
3.信息类型及协议的基本格式
信息分两种类型:
(1) 由SU(上位机)发出到SM(精密空调控制器)的命令信息(简称命令信息);
(2) 由SM(精密空调控制器)返回到SU(上位机)的响应信息(简称响应信息)。

返回码

4.协议内容



好了,看了协议格式,直接解析电总协议报文
#include <string.h>
#include "Serial.h"
#define SOI 0x7E // 起始标志
#define VER 0x10 // v1.0
#define ADR 0x01 // 地址描述符
#define CID1 0x2A // 设备描述符
#define EOI 0x0D // 结束码
#define CID2 0x43
MSG MSG_SERIAL;
uint16_t leng_id = 0;
uint16_t leng_id1 = 0;
#define MODX 0x10000
#define sendData MSG_SERIAL.SendBuf
static uint16_t LENID(void);
static uint8_t LCHKSUM(uint16_t h1);
static uint16_t ChkSum(uint8_t* pData, uint16_t count);
static void SerialTimeOut(void);
static uint8_t atohex16(uint8_t *cp);
static uint8_t RcvChk(void);
static void atoh2b(uint8_t *hp, uint8_t *cp);
static void htoa(uint8_t *pA, uint8_t H);
static void htoa3b(uint8_t *cp, uint16_t h1);
static void htoa4b(uint8_t *cp, uint16_t h1);
static void ClrRcvBuf(void);
static void SerialDecoding(uint8_t pData);
static void SerialStartSend(uint8_t *pData);
static uint8_t * FloatToChar(float *pFData);
static float * CharToFloat(uint8_t *pChar);
static uint8_t htoa_H(uint8_t H);
static uint8_t htoa_L(uint8_t H);
static void SendBuff(void);
static void SerialSend41h(void);
static void SerialSend43h(void);
// 串口接收数据
void SerialInput(uint8_t RecData)
{
if(MSG_SERIAL.RecOver) return; // 停止接收?
if((RecData==SOI)&&(MSG_SERIAL.RecCount==0))
{ MSG_SERIAL.SleepTime=0;
MSG_SERIAL.SleepFlag=0;
MSG_SERIAL.RecBuf[0]=SOI;MSG_SERIAL.RecCount=1; // 启动接收
}
else if((MSG_SERIAL.RecBuf[0]==SOI)&&(MSG_SERIAL.RecCount<REC_MAX)) // 如果已经接收到起始位
{
MSG_SERIAL.RecBuf[MSG_SERIAL.RecCount++]=RecData;
} // 将数据添加到缓存区
else
{
MSG_SERIAL.RecCount=0;
}
if((RecData==EOI)&&(MSG_SERIAL.RecCount==(LENID()+18)))
{
MSG_SERIAL.RecOver=1;
MSG_SERIAL.RecTimer=0; // 清计时器
} // 接收完成
}
static uint16_t LENID(void)
{
uint8_t LF_hex[2] = {0};
uint16_t LENGTH = 0;
uint8_t lchsum = 0;
atoh2b(&LF_hex[0], &MSG_SERIAL.RecBuf[9]);
atoh2b(&LF_hex[1], &MSG_SERIAL.RecBuf[11]);
LENGTH = (uint16_t)LF_hex[0]<<8|LF_hex[1];
lchsum = htoa_L((uint8_t) ((~((((LENGTH >> 8)&0x0F) | ((LENGTH >> 4) & 0x0F) | (LENGTH & 0x0F)) % MODX) + 1) & 0x0F));
#if 1
if(lchsum == MSG_SERIAL.RecBuf[9])
{
leng_id = (uint16_t) (LENGTH & 0x0FFF);
}
#else
leng_id = (uint16_t) (LENGTH & 0x0FFF);
#endif
return leng_id;
}
static uint16_t ChkSum(uint8_t* pData, uint16_t count)
{
uint16_t chkSum = 0;
while (count--)
{
chkSum += (*pData++);
}
chkSum=~chkSum%MODX+1;
return (chkSum);
}
//=============================================================
// CONVERTS 2 ASCII BYTES REPRESENTING A HEX NUMBER INTO BYTE HEX NUMBER.
// ON ENTRY - cp POINTS TO 1ST ASCII BYTE (HIGH NIBBLE OF HEX BYTE),
// - hp POINTS TO A LOCATION TO RECEIVE THE BYTE HEX NUMBER.
//-------------------------------------------------------------
static void atoh2b(uint8_t *hp, uint8_t *cp)
{
// CONVERT 1ST ASCII BYTE TO HIGH NIBBLE OF HEX BYTE:
if (*cp <= '9')
*hp = *cp - '0';
else
{
*cp &= 0xdf; // IF IT'S 'A'-'F' DIGIT MAKE IT UPPERCASE
*hp = *cp - '7';
}
*hp = *hp << 4;
++cp;
if (*cp <= '9')
*hp += *cp - '0';
else
{
*cp &= 0xdf;
*hp += *cp - '7';
}
return;
}
//=============================================================
// CONVERTS A HEX BYTE NUMBER TO 2 ASCII BYTES REPRESENTING THAT NUMBER.
// ON ENTRY - H HEX BYTE TO BE CONVERTED TO 2 ASCII BYTES,
// - pA POINTS TO 1ST ASCII BYTE WHICH WILL RECEIVE HIGH NIBBLE
// OF HEX NUMBER H.
//-------------------------------------------------------------
static void htoa(uint8_t *pA, uint8_t H)
{
uint8_t h1;
h1 = H >> 4; // h1 = HI NIBBLE OF HEX BYTE
// CONVERT HIGH NIBBLE TO 1ST ASCII BYTE:
if (h1 <= 0x09)
*pA = h1 + '0';
else
*pA = h1 + '7';
++pA; // POINT TO 2ND ASCII BYTE
h1 = H & 0x0f; // h1 = LOW NIBBLE OF HEX BYTE
// CONVERT LOW NIBBLE OF HEX BYTE TO 2ND ASCII BYTE:
if (h1 <= 0x09)
*pA = h1 + '0';
else
*pA = h1 + '7';
++pA; // POINT TO NEXT FREE ASCII BYTE
return;
}
//=============================================================
// CONVERTS A HEX WORD NUMBER TO 4 ASCII BYTES REPRESENTING THAT NUMBER.
// ON ENTRY - h1 HEX BYTE TO BE CONVERTED TO 4 ASCII BYTES,
// - cp POINTS TO 1ST ASCII BYTE WHICH WILL RECEIVE HIGH NIBBLE
// OF HIGH BYTE OF THE HEX NUMBER IN h1.
//-------------------------------------------------------------
static void htoa4b(uint8_t *cp, uint16_t h1)
{
uint8_t c1;
// cp POINTS TO ASCII BYTE 0
// HI NIBBLE OF HI BYTE:
c1 = (uint8_t)(h1 >> 12);
if (c1 <= 0x09)
*cp = c1 + '0';
else
*cp = c1 + '7';
// POINT TO ASCII BYTE 1:
++cp;
// LO NIBBLE OF HI BYTE:
c1 = (uint8_t)((h1 >> 8) & 0x0F);
if (c1 <= 0x09)
*cp = c1 + '0';
else
*cp = c1 + '7';
// POINT TO ASCII BYTE 2:
++cp;
// HI NIBBLE OF LO BYTE:
c1 = (uint8_t)((h1 >> 4) & 0x0F);
if (c1 <= 0x09)
*cp = c1 + '0';
else
*cp = c1 + '7';
// POINT TO ASCII BYTE 3:
++cp;
// LO NIBBLE OF LO BYTE:
c1 = (uint8_t)(h1 & 0x0F);
if (c1 <= 0x09)
*cp = c1 + '0';
else
*cp = c1 + '7';
return;
}
static uint8_t LCHKSUM(uint16_t h1)
{
uint8_t sum = 0;
sum = htoa_L((uint8_t) ((~((((h1 >> 8)&0x0F) | ((h1 >> 4) & 0x0F) | (h1 & 0x0F)) % MODX) + 1) & 0x0F));
//print("sum = 0x%02x\r\n",sum);
return sum;
}
static void htoa3b(uint8_t *cp, uint16_t h1)
{
uint8_t c1;
// LO NIBBLE OF HI BYTE:
c1 = (uint8_t)((h1 >> 8) & 0x0F);//print("c1 = 0x%02x\r\n",c1);
if (c1 <= 0x09)
*cp = c1 + '0';
else
*cp = c1 + '7';
// POINT TO ASCII BYTE 2:
++cp;
// HI NIBBLE OF LO BYTE:
c1 = (uint8_t)((h1 >> 4) & 0x0F);//print("c1 = 0x%02x\r\n",c1);
if (c1 <= 0x09)
*cp = c1 + '0';
else
*cp = c1 + '7';
// POINT TO ASCII BYTE 3:
++cp;
// LO NIBBLE OF LO BYTE:
c1 = (uint8_t)(h1 & 0x0F);//print("c1 = 0x%02x\r\n",c1);
if (c1 <= 0x09)
*cp = c1 + '0';
else
*cp = c1 + '7';
return;
}
static uint8_t atohex16(uint8_t *cp)
{
uint8_t hex = 0;
uint8_t dat1 = 0;
uint8_t dat2 = 0;
// CONVERT 1ST ASCII BYTE TO HIGH NIBBLE OF HEX BYTE:
if (*cp <= '9')
dat1 = *cp - '0';
else
{
*cp &= 0xdf; // IF IT'S 'A'-'F' DIGIT MAKE IT UPPERCASE
dat1 = *cp - '7';
}
++cp; // POINT TO 2ND ASCII BYTE
// CONVERT 2ND ASCII BYTE TO LOW NIBBLE OF HEX BYTE:
if (*cp <= '9')
dat2 += *cp - '0';
else
{
*cp &= 0xdf; // IF IT'S 'A'-'F' DIGIT MAKE IT UPPERCASE
dat2 += *cp - '7';
}
hex = (uint16_t)(dat1<<4|dat2);
return hex;
}
static uint8_t htoa_H(uint8_t H)
{
uint8_t h1 = 0;
uint8_t DATA = 0;
h1 = H >> 4; // h1 = HI NIBBLE OF HEX BYTE
// CONVERT HIGH NIBBLE TO 1ST ASCII BYTE:
if (h1 <= 0x09)
DATA = h1 + '0';
else
DATA = h1 + '7';
return DATA;
}
static uint8_t htoa_L(uint8_t H)
{
uint8_t h1 = 0;
uint8_t DATA = 0;
h1 = H & 0x0f; // h1 = LOW NIBBLE OF HEX BYTE
// CONVERT LOW NIBBLE OF HEX BYTE TO 2ND ASCII BYTE:
if (h1 <= 0x09)
DATA = h1 + '0';
else
DATA = h1 + '7';
return DATA;
}
// 接收数据校验
static uint8_t RcvChk(void)
{
uint8_t LF_TxB[2] = {0};
uint16_t CHKSUM = 0;
atoh2b(&LF_TxB[0], &MSG_SERIAL.RecBuf[13+leng_id]);
atoh2b(&LF_TxB[1], &MSG_SERIAL.RecBuf[15+leng_id]);
CHKSUM = (uint16_t)(LF_TxB[0]<<8|LF_TxB[1]);
if(CHKSUM==ChkSum(&MSG_SERIAL.RecBuf[1],12+leng_id))
{return 1;}
else
{return 0;}
}
// 由定时器调用
void SerialTimer(void)
{
if(MSG_SERIAL.RecCount!=0)
{
MSG_SERIAL.RecTimer++;
}
if(MSG_SERIAL.RecCount==0)
{
MSG_SERIAL.SleepTime++;
}
}
// 接收超时处理
static void SerialTimeOut(void)
{
if(MSG_SERIAL.RecTimer>(TIME_OUT/SERIAL_TIME_ISR)) // 如果接收超时
{
MSG_SERIAL.RecCount=0; // 清接收数据计数器
MSG_SERIAL.RecTimer=0; // 清计时器
MSG_SERIAL.RecOver=0; // 允许接
}
if(MSG_SERIAL.SleepFlag) return;
if(MSG_SERIAL.SleepTime>(TIME_SLEEP/SERIAL_TIME_ISR))
{
MSG_SERIAL.SleepFlag=1;
}
}
// 清接收缓存区
static void ClrRcvBuf(void)
{
memset(MSG_SERIAL.RecBuf,0,REC_MAX);
MSG_SERIAL.RecCount=0; // 清接收数据计数器
MSG_SERIAL.RecTimer=0; // 清计时器
MSG_SERIAL.RecOver=0; // 允许接
}
// 数据解码
void SerialDecode(void)
{
SerialTimeOut();
if(!MSG_SERIAL.RecOver) {return;} // 接收没有完成
if(atohex16(&MSG_SERIAL.RecBuf[1])!= VER) {return;} //
if(atohex16(&MSG_SERIAL.RecBuf[3])!= ADR) {return;} //
if(atohex16(&MSG_SERIAL.RecBuf[5])!= CID1) {return;} //
if(!RcvChk()) {return;} // 如果校验错误
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
#if 1
uint16_t i = 0;
print("MSG_SERIAL.RecCount= %d\r\n",MSG_SERIAL.RecCount);
for(i=0;i<MSG_SERIAL.RecCount;i++)
{
print("MSG_SERIAL.RecBuf[%02d]= 0x%02x\r\n",i,MSG_SERIAL.RecBuf[i]);
}
#endif
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
SerialDecoding(atohex16(&MSG_SERIAL.RecBuf[7])); //
ClrRcvBuf(); // 清缓存区
}
// 将浮点数分解为4个字节
static uint8_t * FloatToChar(float *pFData)
{
void *pVoid;
static uint8_t chart[4];
uint8_t i;
pVoid=pFData;
for(i=0;i<4;i++)
{
chart[i]=*((uint8_t *)pVoid+i);
}
return (&chart[0]);
}
// 将4个字节 合并为浮点数
static float * CharToFloat(uint8_t *pChar)
{
static float fData;
void *pVoid;
uint8_t i;
pVoid=&fData;
for(i=0;i<4;i++)
{
*((uint8_t *)pVoid+i)=*(pChar+i);
}
return (&fData);
}
// 启动串口发送数据
static void SerialStartSend(uint8_t *pData)
{
uint8_t i;
MSG_SERIAL.SendCount=leng_id1 + 18;
for(i=0;i<MSG_SERIAL.SendCount;i++)
{
MSG_SERIAL.SendBuf[i]=*(pData+i);
}
uart9_Send_Bytes(MSG_SERIAL.SendBuf,MSG_SERIAL.SendCount);
}
// 数据提取
static void SerialDecoding(uint8_t pData)
{
switch(pData)
{
case 0x41: // 电池电压(0x02),电流(0x03),电量(0x04),数据请求
print("0x41\r\n");
SerialSend41h();
break;
case 0x43: // 内部用命令
print("0x43\r\n");
SerialSend43h();
break;
case 0x44: // 内部用命令
print("0x44\r\n");
break;
case 0x4F: // 内部用命令
print("0x4F\r\n");
break;
case 0x50: // 内部用命令
print("0x50\r\n");
break;
case 0x51: // 内部用命令
print("0x51\r\n");
break;
case 0xE1: // 内部用命令
print("0xE1\r\n");
break;
case 0xE2: // 内部用命令
print("0xE2\r\n");
break;
case 0xE3: // 内部用命令
print("0xE3\r\n");
break;
case 0xDB: // 内部用命令
print("0xDB\r\n");
break;
default:
print("unsported command!\r\n");
break;
}
}
static void SendBuff(void)
{
uint8_t index = 0;
sendData[index++] = SOI; //print("index = %02d\r\n",index);
sendData[index++] = htoa_H(VER);//print("index = %02d\r\n",index);
sendData[index++] = htoa_L(VER);
sendData[index++] = htoa_H(ADR);
sendData[index++] = htoa_L(ADR);
sendData[index++] = htoa_H(CID1);
sendData[index++] = htoa_L(CID1);
sendData[index++] = htoa_H(CID2);
sendData[index++] = htoa_L(CID2);//print("index = %02d\r\n",index);
}
static void SerialSend41h(void)
{
uint16_t checksum = 0;
uint8_t i = 0;
SendBuff();
leng_id1 = 18;
sendData[9]=LCHKSUM(leng_id1); //9
htoa3b(&sendData[10], leng_id1);//10 11 12
sendData[13]=htoa_H(1);sendData[14]=htoa_L(1);//1
sendData[15]=htoa_H(00);sendData[16]=htoa_L(00);sendData[17]=htoa_H(0xFA);sendData[18]=htoa_L(0xFA);
sendData[19]=htoa_H(2);sendData[20]=htoa_L(2);//2
sendData[21]=htoa_H(00);sendData[22]=htoa_L(00);sendData[23]=htoa_H(0xFA);sendData[24]=htoa_L(0xFA);
sendData[25]=htoa_H(3);sendData[26]=htoa_L(3);//3
sendData[27]=htoa_H(00);sendData[28]=htoa_L(00);sendData[29]=htoa_H(0xFA);sendData[30]=htoa_L(0xFA);
checksum = ChkSum(&sendData[1],12+leng_id1);//print("checksum = 0x%02x\r\n",checksum);
htoa4b(&sendData[31], checksum);//31 32 33 34
sendData[35]=EOI;
SerialStartSend(sendData); // 启动发送
}
static void SerialSend43h(void)
{
uint16_t checksum = 0;
leng_id1 = 0;
SendBuff();
sendData[9] = LCHKSUM(leng_id1);
htoa3b(&sendData[10], leng_id1);//10 11 12
checksum = ChkSum(&sendData[1],12+leng_id1);
htoa4b(&sendData[13], checksum);
sendData[17]=EOI;
SerialStartSend(sendData); // 启动发送
}
#ifndef COMM_H
#define COMM_H
#include "usart9.h"
#define REC_MAX 1024 // 定义接收缓存区的大小
#define SEND_MAX 1024 // 定义发送缓存区的大小
#define print printf
// 通讯常量定义
#define SERIAL_TIME_ISR 5 // 定时器间隔 ms
#define TIME_OUT 500 // 接收超时时间 500ms
#define TIME_SLEEP 10000 // 休眠超时时间 10S
typedef struct
{
uint8_t RecBuf[REC_MAX]; // 接收缓存区
uint16_t RecCount; // 接收的字节数
uint8_t SendBuf[REC_MAX]; // 发送缓存区
uint16_t SendCount; // 要发送的字节数
uint8_t RecOver; // 接收完成标志
uint16_t RecTimer; // 接收计时器
uint16_t SleepTime; // 休眠计时
uint8_t SleepFlag; // 通讯休眠标志
} MSG;
typedef enum
{
Normal,
VER_ERROR,
CHKSUM_ERROR,
LCHKSUM_ERROR,
CID2_INVALID,
CMD_ERROR,
DARA_ERROR,
ASCII_ERROR=0x80,
} RTN;
extern MSG MSG_SERIAL;
extern void SerialInput(uint8_t RevData);
extern void SerialTimer(void); // 由定时器调用
extern void SerialDecode(void);// 由主函数调用
#endif
在串口接收中断里直接调用解析

主函数里调用

SerialDecode();




编译工程,烧录代码

打开电总协议上位机软件

1,发送41命令数据

解析的报文如下
[2025-09-01 06:36:03.500][发送]: 7e 31 30 30 31 32 41 34 33 45 30 30 32 34 32 46 44 32 37 0d (~10012A43E00242FD27 )
[2025-09-01 06:36:03.566][接收]: 7e 31 30 30 31 32 41 34 33 30 30 30 30 46 44 41 34 0d (~10012A430000FDA4 )
2,发送4F数据


解析的报文如下:
[2025-09-01 06:37:02.192][发送]: 7e 31 30 30 31 32 41 34 33 45 30 30 32 34 46 46 44 31 33 0d (~10012A43E0024FFD13 )
[2025-09-01 06:37:02.267][接收]: 7e 31 30 30 31 32 41 34 33 30 30 30 30 46 44 41 34 0d (~10012A430000FDA4 )