前言

这几天去现场,给一台艾默生ExmUPS写采集数据的插件。
发现有的包回的数据是0. 看协议,发包时,要指定模块号才行, 这样就要LENGTH不为0, INFO中放数据。
INFO中带1个字节数据,LENGTH= 1 * 2;
INFO中带2个字节数据,LENGTH = 2 * 2;
INFO中带3个字节数据,LENGTH = 3 * 2;
以前自己写了一个电总发包的拼包程序,整错了. INFO带了数据,拼出的包不对,修正了一下。

具体的电总协议,可以看 <<YD-T_1363.3-2005_通信局(站)电源、空调及环境集中监控管理系统第3部分:前端智能设备协议.pdf>>,拼包,回包分析都讲的很清楚。 csdn的ailun1982同学上传过一份YD-T_1363.3-2005_通信局(站)电源、空调及环境集中监控管理系统第3部分:前端智能设备协议.pd

其实自己想用QT写一个电总协议测试程序(其实想写一个串口助手带协议插件,插件中包含发包的拼包和回包的分析,电总协议只是其中一个插件),虽然网上有同学已经写了类似的程序,不过自己还想写一个(可以将自己想用的功能加上,最能满足自己需求,维护也方便)。不过忙的跟狗一样,只能先在心里想一下:)

电总协议如果手工拼包基本不可能,如果没有顺手的工具,也要整一个简陋的测试工程给自己用。虽然不通用,不过遇到新协议,改起来也容易。

修正过的测试程序<<gen_YDN23_protocol_send_cmd>>如下:
编译环境 vs2017 vc++ console

// @file gen_YDN23_protocol_send_cmd.cpp
// @brief gen YDN23 protocol(电总协议) send cmd

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <tchar.h>
#include <math.h>

const unsigned char SOI = 0x7E; // 开始字符是固定的0x7E => ~
const unsigned char EOI = 0x0D; // 结束字符是固定的0x0D

void calc_and_print_YND23_cmd(const char* psz_tip, unsigned char VER, unsigned char ADR, unsigned char CID1, unsigned char CID2, unsigned char* INFO, unsigned short int LENGTH, unsigned char* YND23_cmd);

int gen_YND23_cmd(unsigned char VER, unsigned char ADR, unsigned char CID1, unsigned char CID2, unsigned short int LENGTH, unsigned char* INFO, unsigned char* YND23_cmd);
void uint8_to_2ascii_char(unsigned char uc_in, char& c1, char& c2);
void uint16_to_4ascii_char(unsigned int ui_in, char& c1, char& c2, char& c3, char& c4);
char fn_4bits_to_char(unsigned char uc);
unsigned short int LENGTH_add_LCHKSUM(unsigned short int LENGTH);
unsigned short int calc_YDN23_CHKSUM(unsigned char* YND23_cmd, int index_begin, int index_end);
void print_ary_as_hex(unsigned char* ary, int len);

int main(int argc, char** argv)
{
	int i = 0;

	unsigned char VER = 0;
	unsigned char ADR = 0;
	unsigned char CID1 = 0;
	unsigned char CID2 = 0;
	unsigned short int LENGTH = 0;

	const char* psz_tip = NULL;
	unsigned char VER_real = 0x20;

	// LENGTH中的低12位 所能带的INFO数据个数为 2^12 = 4096;
	// INFO中的数据为WORD, WORD的最大数量为 4096/2 = 2048;
	unsigned char INFO[4096] = {'\0'}; // 如果LENGTH中的低12位是0, INFO域没内容, 不出现在产生的命令序列中

	// unsigned short int CHKSUM = 0;

	// 最终拼好的命令序列最大长度 = SOI(1bytes) + VER(2bytes) + ADR(2bytes) + CID1(2bytes) + CID2(2bytes) + LENGTH(4bytes) + INFO(4096 max bytes) + CHKSUM(4bytes) + EOI(1byte) = 4906 + 18
	unsigned char YND23_cmd[4906 + 18] = {'\0'}; // 根据参数, 防止最终拼好的命令序列

	do {
		setlocale(LC_ALL, ".936"); ///< 设置中文代码页, 用来显示中文字符

		// 不传参数进来了, 将参数写死,运行一次产生一个发送的命令序列
		// 如果要产生不同的命令, 重新写参数, 然后编译运行一次后, 产生一个新命令

		// 电总协议, 要给定以下6个参数(VER/ADR/CID1/CID2/LENGTH_for_INFO/INFO)

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		//psz_tip = "这条指令只是验证计算和拼包, 用已知的正确包参数构造一个新包, 能还原原始包,说明拼包算法正确";
		//VER = 0x20; // 对于同一种设备, 通讯协议版本是固定的, 只要发一条命令取一下就行
		//ADR = 0x01; // 设备地址在设备仪表上有显示
		//CID1 = 0x42; // CID1/CID2的组合说明该命令具体任务
		//CID2 = 0x44;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		printf("Exm电总后台通讯协议_V120.pdf\n");



	//1 获取系统模拟量量化数据(浮点数) 2AH 41H
		psz_tip = "1 获取系统模拟量量化数据(浮点数) 2AH 41H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x41;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

	//2 获取开关输入状态 2AH 43H
		psz_tip = "2 获取开关输入状态 2AH 43H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x43;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//3 获取告警状态 2AH 44H
		psz_tip = "3 获取告警状态 2AH 44H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x44;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//4 获取协议版本号 2AH 4FH
		psz_tip = "4 获取协议版本号 2AH 4FH";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x4F;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//5 获取设备地址 2AH 50H
		psz_tip = "5 获取设备地址 2AH 50H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x50;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//6 获取设备厂家信息 2AH 51H
		psz_tip = "6 获取设备厂家信息 2AH 51H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x51;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//7 获取自定义模拟量量化数据1(浮点数) 2AH E1H
		psz_tip = "7 获取自定义模拟量量化数据1(浮点数) 2AH E1H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE1;

		LENGTH = 1; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		INFO[0] = 0x00;

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//8 获取自定义模拟量量化数据2(浮点数) 2AH E2H
		psz_tip = "8 获取自定义模拟量量化数据2(浮点数) 2AH E2H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE2;

		LENGTH = 1; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		INFO[0] = 0x00;

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//9 获取自定义模拟量量化数据3(浮点数) 2AH E3H
		psz_tip = "9 获取自定义模拟量量化数据3(浮点数) 2AH E3H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE3;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//10 获取自定义模拟量量化数据4(浮点数) 2AH E7H
		psz_tip = "10 获取自定义模拟量量化数据4(浮点数) 2AH E7H 电池组1";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE7;

		LENGTH = 2; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		INFO[0] = 1;
		INFO[1] = 0;

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);


		//10 获取自定义模拟量量化数据4(浮点数) 2AH E7H
		psz_tip = "10 获取自定义模拟量量化数据4(浮点数) 2AH E7H 电池组2";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE7;

		LENGTH = 2; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		INFO[0] = 2;
		INFO[1] = 0;

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
















		//// -----------------------------------------------------------------------------
		//// 产生YDN23协议的发送命令
		//// -----------------------------------------------------------------------------
		//// 获取通信协议版本号 2AH 4FH
		//psz_tip = "获取通信协议版本号";
		//VER = 0x00; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A; // CID1 = 0x2A, CID2 = 0x4F
		//CID2 = 0x4F;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//// 回包
		//// 7E 32 36 30 31 32 41 30 30 30 30 30 30 46 44 41 34 0D

		//// 回包分析
		//// 7E // SOI
		//// 32 36 // VER = "26" = 2.6
		//// printf("%c%c\n", 0x32, 0x36);
		//// 30 31 // ADR
		//// 32 41 // CID1
		//// 30 30 // RTN = 0x00(正常)
		//// 30 30 30 30 // LENGTH = 0(NO INFO)
		//// 46 44 41 34 // CHKSUM
		//// 0D // EOI

		//VER_real = 0x26; // @todo 从回包中取到实际的VER

		//// -----------------------------------------------------------------------------
		//// 产生YDN23协议的发送命令
		//// -----------------------------------------------------------------------------
		//// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		//// 获取设备厂家信息 2AH 51H
		//psz_tip = "获取设备厂家信息";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0x51;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
		//// 产生的发包 : 7E 32 36 30 31 32 41 35 31 30 30 30 30 46 44 39 45 0D

		//// -----------------------------------------------------------------------------
		//// 产生YDN23协议的发送命令
		//// -----------------------------------------------------------------------------
		//// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		//// 获取告警状态 2AH 44H
		//psz_tip = "获取告警状态";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0x44;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//// -----------------------------------------------------------------------------
		//// 产生YDN23协议的发送命令
		//// -----------------------------------------------------------------------------
		//// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		//// 获取开关输入状态 2AH 43H
		//psz_tip = "获取开关输入状态";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0x43;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//// -----------------------------------------------------------------------------
		//// 产生YDN23协议的发送命令
		//// -----------------------------------------------------------------------------
		//// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		//// 获取自定义模拟量量化数据3 2AH E3H
		//psz_tip = "获取自定义模拟量量化数据3";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0xE3;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//// -----------------------------------------------------------------------------
		//// 产生YDN23协议的发送命令
		//// -----------------------------------------------------------------------------
		//// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		//// 获取自定义模拟量量化数据2 2AH E2H
		//psz_tip = "获取自定义模拟量量化数据2";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0xE2;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//// -----------------------------------------------------------------------------
		//// 产生YDN23协议的发送命令
		//// -----------------------------------------------------------------------------
		//// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		//// 获取自定义模拟量量化数据1 2AH E1H
		//psz_tip = "获取自定义模拟量量化数据1";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0xE1;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//// -----------------------------------------------------------------------------
		//// 产生YDN23协议的发送命令
		//// -----------------------------------------------------------------------------
		//// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		//// 获取系统模拟量量化数据 2AH 41H
		//psz_tip = "获取系统模拟量量化数据";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0x41;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
	} while (0);

	return EXIT_SUCCESS;
}

void calc_and_print_YND23_cmd(const char* psz_tip, unsigned char VER, unsigned char ADR, unsigned char CID1, unsigned char CID2, unsigned char* INFO, unsigned short int LENGTH, unsigned char* YND23_cmd)
{
	// 一个有效的电总协议发送命令
	// 7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D // 命令序列最小为18个字节

	// 分析
	// 7E // SOI = 0x7E
	// 32 30 // VER = 0x20
	// 30 31 // ADR = 0x01
	// 34 32 // CID1 = 0x42
	// 34 34 // CID2 = 0x44
	// 30 30 30 30 // LENGTH = 0
	// INFO(no data)
	// 46 44 41 46 // CHKSUM = "FDAF" = 0xFDAF
	// 0D // EOI = 0x0D

	// CHKSUM = 0x0000; // 除了SOI/CHKSUM/EOI之外字节的累加和(4个字节一组进行累加)

	int len_YND23_cmd = 0; // 最终拼好的命令有多少个字节?

	memset(YND23_cmd, 0, sizeof(YND23_cmd)); // 除了SOI/EOI是16进制字节,其他的位置都要将16进制字节表示成2个ASCII字符标识的16进制数(e.g. 0x01 => "01" => 0x30 0x31)

	// 看看给定的参数,是否能还原上面的命令序列

	// 产生出的命令为YND23_cmd,字节长度为len_YND23_cmd
	len_YND23_cmd = gen_YND23_cmd(VER, ADR, CID1, CID2, LENGTH, INFO, YND23_cmd);

	// 打印出YND23_cmd的内容供贴到串口助手做测试
	printf("%s:\n", psz_tip);
	print_ary_as_hex(YND23_cmd, len_YND23_cmd);
	printf("请将回包贴在下面一行:\n");
	printf("\n\n");

	// 7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D // run result
	// 7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D // original cmd, CHKSUM = "FDAF" = 0xFDAF
	// ok :)
}

void print_ary_as_hex(unsigned char* ary, int len)
{
	int i = 0;

	for (i = 0; i < len; i++) {
		printf("%2.2X", ary[i]); // 为了贴到串口助手中直接用,不要换行
		if ((i + 1) != len) {
			printf(" ");
		}
		else {
			printf("\n");
		}
	}
}

int gen_YND23_cmd(unsigned char VER, unsigned char ADR, unsigned char CID1, unsigned char CID2, unsigned short int LENGTH, unsigned char* INFO, unsigned char* YND23_cmd)
{
	int len = 0;
	int ipos = 0;
	char c1 = '\0';
	char c2 = '\0';
	char c3 = '\0';
	char c4 = '\0';
	unsigned short int LENGTH_have_ehck_sum = 0;
	unsigned short int YDN23_CHKSUM = 0;
	int i = 0;

	do {
		// @todo 不检查入参了

		// 7E // SOI = 0x7E
// 32 30 // VER = 0x20
// 30 31 // ADR = 0x01
// 34 32 // CID1 = 0x42
// 34 34 // CID2 = 0x44
// 30 30 30 30 // LENGTH = 0
// INFO(no data)
// 46 44 41 46 // CHKSUM = "FDAF" = 0xFDAF
// 0D // EOI = 0x0D

		// SOI
		YND23_cmd[ipos++] = SOI;

		// VER
		uint8_to_2ascii_char(VER, c1, c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// ADR
		uint8_to_2ascii_char(ADR, c1, c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// CID1
		uint8_to_2ascii_char(CID1, c1, c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// CID2
		uint8_to_2ascii_char(CID2, c1, c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// LENGTH
		LENGTH = LENGTH & (~(1 >> 12)); // 只取低12位, 最多4096(2^12)个数据
		LENGTH_have_ehck_sum = LENGTH_add_LCHKSUM(LENGTH);
		uint16_to_4ascii_char(LENGTH_have_ehck_sum, c1, c2, c3, c4);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;
		YND23_cmd[ipos++] = c3;
		YND23_cmd[ipos++] = c4;

		// INFO
		for (i = 0; i < LENGTH; i++) {
			uint8_to_2ascii_char(INFO[i], c1, c2);
			YND23_cmd[ipos++] = c1;
			YND23_cmd[ipos++] = c2;
		}

		// CHKSUM

		// test calc_YDN23_CHKSUM
		//ipos = 0;
		//YND23_cmd[ipos++] = SOI;
		//YND23_cmd[ipos++] = '1';
		//YND23_cmd[ipos++] = '2';
		//YND23_cmd[ipos++] = '0';
		//YND23_cmd[ipos++] = '3';
		//YND23_cmd[ipos++] = '4';
		//YND23_cmd[ipos++] = '0';
		//YND23_cmd[ipos++] = '0';
		//YND23_cmd[ipos++] = '4';
		//YND23_cmd[ipos++] = '5';
		//YND23_cmd[ipos++] = '6';
		//YND23_cmd[ipos++] = 'A';
		//YND23_cmd[ipos++] = 'B';
		//YND23_cmd[ipos++] = 'C';
		//YND23_cmd[ipos++] = 'D';
		//YND23_cmd[ipos++] = 'F';
		//YND23_cmd[ipos++] = 'E';
		//// 1203400456ABCDFE
		//ipos = 16 + 1;
		//YDN23_CHKSUM = calc_YDN23_CHKSUM(YND23_cmd, 1 /*不要算SOI*/, ipos - 1 /*不算累加和本身和EOI*/);

		YDN23_CHKSUM = calc_YDN23_CHKSUM(YND23_cmd, 1 /*不要算SOI*/, ipos - 1 /*不算累加和本身和EOI*/);
		uint16_to_4ascii_char(YDN23_CHKSUM, c1, c2, c3, c4);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;
		YND23_cmd[ipos++] = c3;
		YND23_cmd[ipos++] = c4;

		// EOI
		YND23_cmd[ipos++] = EOI;
		len = ipos;
	} while (0);

	return len;
}

char fn_4bits_to_char(unsigned char uc)
{
	char c_out = '0';
	// 0 => '0'
	// 1 => '1'
	// 10 => 'A'
	// 11 => 'B'
	// 12 => 'C'
	// 13 => 'D'
	// 14 => 'E'
	// 15 => 'F'

	if ((uc >= 0x00) && (uc < 0x0A)) {
		c_out = uc - 0 + '0';
	} else if ((uc >= 0x0A) && (uc <= 0x0F)) {
		c_out = uc - 0x0A + 'A';
	}

	return c_out;
}

void uint8_to_2ascii_char(unsigned char uc_in, char& c1, char& c2)
{
	// 0x12 => '1' '2'

	unsigned char uc1 = (uc_in >> 4) & 0x0f;
	unsigned char uc2 = (uc_in >> 0) & 0x0f;

	c1 = fn_4bits_to_char(uc1);
	c2 = fn_4bits_to_char(uc2);
}

void uint16_to_4ascii_char(unsigned int ui_in, char& c1, char& c2, char& c3, char& c4)
{
	// 0x1234 => '1' '2' '3' '4'

	unsigned char uc1 = (unsigned char)((ui_in >> 12) & 0x0f);
	unsigned char uc2 = (unsigned char)((ui_in >> 8) & 0x0f);
	unsigned char uc3 = (unsigned char)((ui_in >> 4) & 0x0f);
	unsigned char uc4 = (unsigned char)((ui_in >> 0) & 0x0f);

	c1 = fn_4bits_to_char(uc1);
	c2 = fn_4bits_to_char(uc2);
	c3 = fn_4bits_to_char(uc3);
	c4 = fn_4bits_to_char(uc4);
}

unsigned short int LENGTH_add_LCHKSUM(unsigned short int LENGTH)
{
	if (LENGTH > 0) {
		LENGTH = LENGTH;
	}

	LENGTH *= 2; // 给定的长度是指的字节长度,在电总协议里面表示的是拆分后的字节长度,1个字变成了2个字节。
	unsigned short int ui_out = 0;
	unsigned short int ui_shift = 0;
	unsigned char uc1 = (LENGTH >> 8) & 0x0f;
	unsigned char uc2 = (LENGTH >> 4) & 0x0f;
	unsigned char uc3 = (LENGTH >> 0) & 0x0f;
	unsigned short int uc_LCHKSUM = '\0'; // 高4位的电总校验和

	uc_LCHKSUM = uc1 + uc2 + uc3; // 低12位分为3个4位,进行累加

	uc_LCHKSUM %= 0x10; // 模16取余数
	uc_LCHKSUM = ~uc_LCHKSUM; // 取反
	uc_LCHKSUM += 1; // 加1

	ui_out = (uc_LCHKSUM << 12);
	ui_shift = (0x0F << 12);
	ui_out = ui_out & ui_shift; // 将算出的累加和放到高4位
	ui_out |= (LENGTH & 0xFFF); // 将LENGTH的低12位放到返回值的低12位

	return ui_out;
}

unsigned short int calc_YDN23_CHKSUM(unsigned char* YND23_cmd, int index_begin, int index_end)
{
	// CHKSUM的计算是除SOI、 EOI和CHKSUM外,其他字符ASCII码值累加求和
	// 结果模65535取余数
	// 取反加1

	// YND23_cmd送进来时, 已经刨掉了SOI, CHKSUM, EOI
	int i = 0;
	unsigned int ui_chksum = 0;
	unsigned short int ui_chksum_rc = 0;

	for (i = index_begin; i <= index_end; i++) {
		ui_chksum += YND23_cmd[i];
	}

	ui_chksum %= 0xFFFF;
	ui_chksum = ~ui_chksum;
	ui_chksum += 1;

	ui_chksum_rc = ui_chksum & 0xffff;
	return ui_chksum_rc;
}


Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐