单片机日志记录系统

单片机日志系统架构

说明: 日志记录系统按照API封装的思想只在接口文件中保留功能性程序,及 init ,set ,get;存储器使用华邦的w25q64,最低擦除的为1个sector(4096Bytes);日志系统主要分为三个部分 1、日志索引 ,2、断电日志存储区,3、日志存储区

1、日志索引:

为了避免对单个地址持续擦写造成块损坏,日志索引使用两个sector作为日志的索引存储区,日志索引每写一次地址增加16字节。当开机初始化的时候按照一定的方法将最新的索引页读取出来,用于对日志记录的地址进行记录。

2、断电日志

为了能够记录断电瞬间设备的运行情况,需要存储数据到断电日志区,等待下次开机后将断电日志记录到日志中。然后将断电日志区的数据擦除。

3、日志存储区

目前设计的是4096条日志数据,每条日志占用空间为64Bytes,考虑到增加器件寿命,每次写sector到的起始地址时将该sector擦除,然后将日志总长度减 64.

代码实现

mod_logSysInfo.h

/**
*****************************************************************************
* @文  件: mod_logSysInfo.h 
* @作  者: 00Jackey
* @版  本: V1.0.0
* @日  期: 5-Jun-2018
* @描  述: 日志信息系统接口文件
******************************************************************************
* @修改记录:
*   2018/06/05:初始版本
*    
*
******************************************************************************
**/

#ifndef _MOD_LOGSYSINFO_H_
#define _MOD_LOGSYSINFO_H_

#ifdef _cplusplus
	extern "C" {
#endif

//C库
#include 
#include 
#include 

//宏定义
#define LOG_INDEX_TOTAL        	   (uint32_t)(15)
#define EACH_INDEX_SPACE           (uint32_t)(16)

#define LOG_CONTENT_TOTAL          (uint32_t)(52)
#define EACH_LOG_SPACE             (uint32_t)(64)

#define LOG_INFO_TOTAL_CNT         (uint32_t)(4096)

#define LOG_WRITE_FLAG             (uint8_t)(0xA5)
		
//定义枚举
typedef enum {
    LOG_NOT_USE = 0, 
    LOG_BE_WRITE = 1, 
    LOG_BE_READ = 2
}LOG_OPERATE_STATE_ENUM;

typedef enum{
	LOG_PROC_SUCCEED = 0,
	LOG_PROC_FAILED = 1
}LOG_PROC_STATE_ENUM;

//定义结构体
typedef struct{
	uint8_t dateArry[LOG_CONTENT_TOTAL];
	uint32_t curNum;
	uint32_t curTime;
	uint16_t length;
	uint8_t  verifyVal;
	uint8_t  writeFlag;
}LOG_CONTENT_STRUCT;

//定义联合体
typedef union{
	LOG_CONTENT_STRUCT ContentStruct;
	uint8_t ConentArry[EACH_LOG_SPACE];
}LOG_CONTENT_UNION;

//定义结构体
typedef struct{
	uint32_t curIndex;
	uint32_t cntTotal;
    uint32_t rawTotal;
	uint8_t  useless[3]; 	
	uint8_t  writeFlag;
}LOG_INDEX_STRUCT;

//定义联合体
typedef union{
	LOG_INDEX_STRUCT IndexStruct;
	uint8_t IndexArry[LOG_INDEX_TOTAL+1];
}LOG_INDEX_UNION;

//函数封装
typedef struct
{
	LOG_PROC_STATE_ENUM (*init)(void);
	LOG_PROC_STATE_ENUM (*record)(LOG_CONTENT_UNION uLogInfoUnion);
	LOG_PROC_STATE_ENUM (*getRecent)(LOG_CONTENT_UNION* pLogInfoUnion, int32_t recentNum);
    LOG_PROC_STATE_ENUM (*getSerial)(LOG_CONTENT_UNION* pLogInfoUnion, int32_t serialNum);
}LOG_SYS_CONTENT_STRUCT;

typedef struct
{
	LOG_PROC_STATE_ENUM (*clr)(void);
	LOG_PROC_STATE_ENUM (*set)(LOG_CONTENT_UNION  uLogInfoUnion);
	LOG_PROC_STATE_ENUM (*get)(LOG_CONTENT_UNION* pLogInfoUnion);
}LOG_SYS_OUTAGE_STRUCT;

typedef struct
{
	LOG_OPERATE_STATE_ENUM   LogOperateFlag;  //日志读、写、空闲状态

	LOG_SYS_OUTAGE_STRUCT 	 OutageStruct;  //断电日志封装
	LOG_SYS_CONTENT_STRUCT   ContentStruct; //主日志封装

	LOG_PROC_STATE_ENUM (*init)(void);  //日志系统初始化
	
	uint32_t (*getRawTotal)(void);  //获取日志数量
}LOG_SYS_INFO_STRUCT;


//外部调用
extern LOG_SYS_INFO_STRUCT LogSysInfoStruct;


#ifdef _cplusplus
	}
#endif

#endif

mod_logSysInfo.c

/**
*****************************************************************************
* @文  件: mod_logSysInfo.c 
* @作  者: 00Jackey
* @版  本: V1.0.0
* @日  期: 6-Jun-2018
* @描  述: 日志信息系统主文件
******************************************************************************
* @修改记录:
*   2018/06/06:初始版本,待通信完成后做完整性测试
*   2018/07/01:做完完整性测试,改了一些问题 
*
******************************************************************************
**/

//接口头文件
#include "mod_logSysInfo.h"

//硬件驱动
#include "hardware.h"

//宏定义
#define CHIPSET_PAGE               (uint32_t)256
#define CHIPSET_SECTOR_SIZE		   (uint32_t)(CHIPSET_PAGE * 16)
#define CHIPSET_BLOCK_SIZE		   (uint32_t)(CHIPSET_SECTOR_SIZE * 16)
#define CHIPSET_TOTAL_SIZE		   (uint32_t)(CHIPSET_BLOCK_SIZE * 128)

#define LOG_SYS_OUTAGE_ADDRESS     (uint32_t)(CHIPSET_SECTOR_SIZE * 13)
#define LOG_SYS_INDEX_ADDRESS      (uint32_t)(CHIPSET_SECTOR_SIZE * 14)
#define LOG_SYS_INFO_ADDRESS       (uint32_t)(CHIPSET_SECTOR_SIZE * 16)

#define LOG_SYS_INFO_TOAL_SIZE     (uint32_t)(LOG_INFO_TOTAL_CNT * EACH_LOG_SPACE)

#define LOG_SYS_INDEX_TOAL_SIZE    (uint32_t)(CHIPSET_SECTOR_SIZE * 2)

#define LOG_SYS_WRITE				W25qxx_writeBuffer
#define LOG_SYS_READ				W25qxx_readBuffer
#define LOG_SYS_ERASE				W25qxx_eraseOneSector

#define LOG_INDEX_ADDR  			sLogIndexAddr




//静态函数
static LOG_PROC_STATE_ENUM LogSysInfo_initInfo(void);
static LOG_PROC_STATE_ENUM LogSysInfo_recordInfo(LOG_CONTENT_UNION uLogContentUnion);
static LOG_PROC_STATE_ENUM LogSysInfo_getRecentInfo(LOG_CONTENT_UNION* pLogContentUnion, int32_t recentNum);
static LOG_PROC_STATE_ENUM LogSysInfo_getSerialInfo(LOG_CONTENT_UNION* pLogContentUnion, int32_t serialNum);

static LOG_PROC_STATE_ENUM LogSysInfo_initIndex(void);
static LOG_PROC_STATE_ENUM LogSysInfo_setIndex(LOG_INDEX_UNION uLogIndexUnion);
static LOG_PROC_STATE_ENUM LogSysInfo_getIndex(LOG_INDEX_UNION* pLogIndexUnion);

static LOG_PROC_STATE_ENUM LogSysInfo_clrOutage(void);
static LOG_PROC_STATE_ENUM LogSysInfo_setOutage(LOG_CONTENT_UNION  uLogContentUnion);
static LOG_PROC_STATE_ENUM LogSysInfo_getOutage(LOG_CONTENT_UNION* pLogContentUnion);

static LOG_PROC_STATE_ENUM LogSysInfo_init(void);

static uint32_t LogSysInfo_getRawTotal(void);

static uint8_t LogSysInfo_calcXor8(uint8_t *pVarArry, uint8_t len);

//常量
const LOG_INDEX_UNION  cDefaultLogIndexUnion = {

	.IndexStruct.curIndex = (LOG_SYS_INFO_ADDRESS - EACH_LOG_SPACE),
    .IndexStruct.cntTotal = 0,
	.IndexStruct.rawTotal = 0,
    .IndexStruct.writeFlag = LOG_WRITE_FLAG,
};

//静态变量
LOG_INDEX_UNION sLogIndexUnion;
uint32_t sLogIndexAddr;

//全局变量
LOG_SYS_INFO_STRUCT LogSysInfoStruct = {

	.ContentStruct.init = LogSysInfo_initInfo,
    .ContentStruct.record = LogSysInfo_recordInfo,
    .ContentStruct.getRecent = LogSysInfo_getRecentInfo,
    .ContentStruct.getSerial = LogSysInfo_getSerialInfo,

    .OutageStruct.clr = LogSysInfo_clrOutage,
    .OutageStruct.set = LogSysInfo_setOutage,
    .OutageStruct.get = LogSysInfo_getOutage,

    .init = LogSysInfo_init,
	
	.getRawTotal = LogSysInfo_getRawTotal,
};



/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_init
*	功能说明: 日志系统初始化
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_init(void)
{
	if(LOG_PROC_FAILED == LogSysInfo_initIndex())
		return LOG_PROC_FAILED;
	else 
		return LOG_PROC_SUCCEED;
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_getRawTotal
*	功能说明: 获取日志中所存储的日志数量
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
uint32_t LogSysInfo_getRawTotal(void)
{
	return sLogIndexUnion.IndexStruct.rawTotal;
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_initIndex
*	功能说明: 日志索引初始化
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_initIndex(void)
{
	
    LOG_PROC_STATE_ENUM rState;
	LOG_INDEX_UNION tLogIndexUnion;
	int32_t i_reord = -1;

	/* 遍历第一个块,判断每个位置是否都有写入标记*/
	for(int32_t i = 0; i < 256; i++){
		LOG_SYS_READ(tLogIndexUnion.IndexArry,LOG_SYS_INDEX_ADDRESS+(i*EACH_INDEX_SPACE),sizeof(LOG_INDEX_UNION));
		if(tLogIndexUnion.IndexArry[LOG_INDEX_TOTAL] == LOG_WRITE_FLAG){
			i_reord = i;
		}else{
			break;
		}
	}
	/* 如果255个位置中不全是有标志的,则提前当前位置的日志索引 */
	if((i_reord != 255)&&(i_reord != -1)){
		LOG_INDEX_ADDR =  LOG_SYS_INDEX_ADDRESS + (i_reord * EACH_INDEX_SPACE);
        LOG_SYS_READ(tLogIndexUnion.IndexArry,LOG_INDEX_ADDR,sizeof(LOG_INDEX_UNION));
		memcpy(sLogIndexUnion.IndexArry,tLogIndexUnion.IndexArry,sizeof(LOG_INDEX_UNION));
		LOG_SYS_ERASE(LOG_SYS_INDEX_ADDRESS + CHIPSET_SECTOR_SIZE);
		return LOG_PROC_SUCCEED;
	}
	/* 遍历第二个块,判断每个位置是否都有写入标记*/
	for(int32_t i = 256; i < 512; i++){
		LOG_SYS_READ(tLogIndexUnion.IndexArry,LOG_SYS_INDEX_ADDRESS+(i*EACH_INDEX_SPACE),sizeof(LOG_INDEX_UNION));
		if(tLogIndexUnion.IndexArry[LOG_INDEX_TOTAL] == LOG_WRITE_FLAG){
			i_reord = i;
		}else{
			break;
		}
	}
	/* 如果255个位置中不全是有标志的,则提前当前位置的日志索引 */
	if((i_reord != 511)&&(i_reord != -1)){
		LOG_INDEX_ADDR =  LOG_SYS_INDEX_ADDRESS + (i_reord * EACH_INDEX_SPACE);
        LOG_SYS_READ(tLogIndexUnion.IndexArry,LOG_INDEX_ADDR,sizeof(LOG_INDEX_UNION));
		memcpy(sLogIndexUnion.IndexArry,tLogIndexUnion.IndexArry,sizeof(LOG_INDEX_UNION));
		LOG_SYS_ERASE(LOG_SYS_INDEX_ADDRESS);
		return LOG_PROC_SUCCEED;
	}
	/* 如果两个块中都没有标记,则写入默认值 */
	if(i_reord == -1){
		LOG_SYS_WRITE((uint8_t*)cDefaultLogIndexUnion.IndexArry,LOG_SYS_INDEX_ADDRESS,sizeof(LOG_INDEX_UNION));
		rState = LogSysInfo_getIndex(&tLogIndexUnion);
		if(rState == LOG_PROC_FAILED){
			return LOG_PROC_FAILED;
		}else{
			memcpy(sLogIndexUnion.IndexArry,tLogIndexUnion.IndexArry,sizeof(LOG_INDEX_UNION));
			return LOG_PROC_SUCCEED;
		}
	}
	/* 所有512个位置都有标记,则设定最后一个为索引存储位置 */
	LOG_INDEX_ADDR = LOG_SYS_INDEX_ADDRESS + (511 * EACH_INDEX_SPACE);
	memcpy(sLogIndexUnion.IndexArry,tLogIndexUnion.IndexArry,sizeof(LOG_INDEX_UNION));
	LOG_SYS_ERASE(LOG_SYS_INDEX_ADDRESS);

	return LOG_PROC_SUCCEED;

}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_setIndex
*	功能说明: 存储日志索引
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_setIndex(LOG_INDEX_UNION uLogIndexUnion)
{
	//memcpy(sLogIndexUnion.IndexArry,uLogIndexUnion.IndexArry,sizeof(LOG_INDEX_UNION));
	LOG_INDEX_ADDR = LOG_INDEX_ADDR + EACH_INDEX_SPACE;
	if(LOG_INDEX_ADDR >= (LOG_SYS_INDEX_ADDRESS + LOG_SYS_INDEX_TOAL_SIZE)){
		LOG_INDEX_ADDR = LOG_SYS_INDEX_ADDRESS;
		LOG_SYS_ERASE(LOG_SYS_INDEX_ADDRESS);
		LOG_SYS_WRITE(uLogIndexUnion.IndexArry,LOG_INDEX_ADDR,sizeof(LOG_INDEX_UNION));
	}else{
		LOG_SYS_WRITE(uLogIndexUnion.IndexArry,LOG_INDEX_ADDR,sizeof(LOG_INDEX_UNION));
	}
	
	return LOG_PROC_SUCCEED;
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_getIndex
*	功能说明: 读取日志索引
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_getIndex(LOG_INDEX_UNION* pLogIndexUnion)
{
	LOG_SYS_READ(pLogIndexUnion->IndexArry,LOG_INDEX_ADDR,sizeof(LOG_INDEX_UNION));
	if(pLogIndexUnion->IndexArry[LOG_INDEX_TOTAL] != LOG_WRITE_FLAG){
		return LOG_PROC_FAILED;
	}else{
		return LOG_PROC_SUCCEED;
	}
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_initInfo
*	功能说明: 日志系统读取掉电日志到正常日志中
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_initInfo(void)
{
	LOG_INDEX_UNION tLogIndexUnion;
	LOG_CONTENT_UNION  tLogOutageUnion;

	//1st: get the SYS_LOG_INDEX_ADDRESS page 
	LogSysInfo_getIndex(&tLogIndexUnion);
	//2nd: get the powerdown logging
	LogSysInfo_getOutage(&tLogOutageUnion);
	//3rd: write the log at the syslog
	if(tLogOutageUnion.ContentStruct.writeFlag == LOG_WRITE_FLAG){
		LogSysInfo_recordInfo(tLogOutageUnion);
	}else{
		/* warnning  --------------------*/
	}
	//4th: erase the power outage buff page
	LogSysInfo_clrOutage();
	
	return LOG_PROC_SUCCEED;
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_recordInfo
*	功能说明: 记录一条日志信息,索引更新
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_recordInfo(LOG_CONTENT_UNION uLogContentUnion)
{

	/*1st: According to index calculate the adress */
	sLogIndexUnion.IndexStruct.cntTotal += 1;
	sLogIndexUnion.IndexStruct.rawTotal = (sLogIndexUnion.IndexStruct.cntTotal >= LOG_INFO_TOTAL_CNT) ?\
		LOG_INFO_TOTAL_CNT : sLogIndexUnion.IndexStruct.cntTotal;
	sLogIndexUnion.IndexStruct.curIndex = (sLogIndexUnion.IndexStruct.cntTotal - 1)%\
        LOG_INFO_TOTAL_CNT * EACH_LOG_SPACE + LOG_SYS_INFO_ADDRESS;
	if(sLogIndexUnion.IndexStruct.curIndex >= (LOG_SYS_INFO_TOAL_SIZE + LOG_SYS_INFO_ADDRESS))
		sLogIndexUnion.IndexStruct.curIndex = LOG_SYS_INFO_ADDRESS;
	/* Earaze the sector , cut down the total log */
	if(0 == (sLogIndexUnion.IndexStruct.curIndex % CHIPSET_SECTOR_SIZE)){
		LOG_SYS_ERASE(sLogIndexUnion.IndexStruct.curIndex);
		sLogIndexUnion.IndexStruct.rawTotal -= (CHIPSET_SECTOR_SIZE / EACH_LOG_SPACE);
	}
	/*2nd: record the log index to eeprom */
    sLogIndexUnion.IndexStruct.writeFlag = LOG_WRITE_FLAG;
	LogSysInfo_setIndex(sLogIndexUnion);
	/*3rd: get the log number */
	uLogContentUnion.ContentStruct.curNum = sLogIndexUnion.IndexStruct.cntTotal;
	uLogContentUnion.ContentStruct.curTime = RTC_GetCounter();	//to update
	uLogContentUnion.ContentStruct.verifyVal = LogSysInfo_calcXor8(uLogContentUnion.ConentArry,LOG_CONTENT_TOTAL);
	uLogContentUnion.ContentStruct.writeFlag = LOG_WRITE_FLAG;
    /*4th: record the loginfo */
	LOG_SYS_WRITE(uLogContentUnion.ConentArry,sLogIndexUnion.IndexStruct.curIndex,EACH_LOG_SPACE);

	return LOG_PROC_SUCCEED;
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_getRecentInfo
*	功能说明: 获取最近某一条日志信息
*	形    参: pLogContentUnion:日志信息指针 recentNum: 0表示最近的日志,(0~LOG_INFO_TOTAL_CNT-1)
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_getRecentInfo(LOG_CONTENT_UNION* pLogContentUnion, int32_t recentNum)
{
	int32_t tSysLogAddr = 0;

	/*Only more than need is ok*/
	if(sLogIndexUnion.IndexStruct.cntTotal <= recentNum){
		return LOG_PROC_FAILED;
	}
	
	/* Get the real index */
	tSysLogAddr = ((sLogIndexUnion.IndexStruct.cntTotal - recentNum - 1) % LOG_INFO_TOTAL_CNT) *\
		EACH_LOG_SPACE + LOG_SYS_INFO_ADDRESS;
	/* For protect , do not reach there */
	if(tSysLogAddr < LOG_SYS_INFO_ADDRESS){
		tSysLogAddr = LOG_SYS_INFO_TOAL_SIZE + LOG_SYS_INFO_ADDRESS - EACH_LOG_SPACE;
	}
	
	LOG_SYS_READ(pLogContentUnion->ConentArry,tSysLogAddr,EACH_LOG_SPACE);
	
	/* Judge the log info is right  */
	if(pLogContentUnion->ContentStruct.writeFlag == LOG_WRITE_FLAG)
		return LOG_PROC_SUCCEED;
	else
		return LOG_PROC_FAILED;
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_getSerialInfo
*	功能说明: 获取指定序号的日志
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_getSerialInfo(LOG_CONTENT_UNION* pLogContentUnion, int32_t serNum)
{
	int32_t tSysLogAddr = 0x000000;
	int32_t tSysLogNum = 0;

	/*Only more than need is ok*/
	if(serNum <= 0){
		return LOG_PROC_FAILED;
	}
	
	if(sLogIndexUnion.IndexStruct.cntTotal < serNum){
		return LOG_PROC_FAILED;
	}
	
	tSysLogNum = serNum % LOG_INFO_TOTAL_CNT;
	tSysLogAddr = (tSysLogNum -1) * EACH_LOG_SPACE + LOG_SYS_INFO_ADDRESS; 

	LOG_SYS_READ(pLogContentUnion->ConentArry,tSysLogAddr,EACH_LOG_SPACE);
	
	/* Judge the log info is right  */
	if(pLogContentUnion->ContentStruct.writeFlag == LOG_WRITE_FLAG)
		return LOG_PROC_SUCCEED;
	else
		return LOG_PROC_FAILED;

}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_clrLogOutage
*	功能说明: 清楚掉电区日志数据
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_clrOutage(void)
{
	uint8_t tBytesFill[EACH_LOG_SPACE]= {0};
	
	LOG_SYS_WRITE(tBytesFill,LOG_SYS_OUTAGE_ADDRESS,EACH_LOG_SPACE);
	
	return LOG_PROC_SUCCEED;
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_setLogOutage
*	功能说明: 存储掉电数据
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_setOutage(LOG_CONTENT_UNION uLogContentUnion)
{
	LOG_SYS_WRITE(uLogContentUnion.ConentArry,LOG_SYS_OUTAGE_ADDRESS,EACH_LOG_SPACE);
	
	return LOG_PROC_SUCCEED;
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_getSerLogInfo
*	功能说明: 获取掉电数据
*	形    参: 无
*	返 回 值: 执行状态
*********************************************************************************************************
*/
LOG_PROC_STATE_ENUM LogSysInfo_getOutage(LOG_CONTENT_UNION* pLogContentUnion)
{
	LOG_SYS_READ(pLogContentUnion->ConentArry,LOG_SYS_OUTAGE_ADDRESS,EACH_LOG_SPACE);

	return LOG_PROC_SUCCEED;
}

/*
*********************************************************************************************************
*	函 数 名: LogSysInfo_calcXor8
*	功能说明: 计算校验值
*	形    参: pVarArry:需要校验的数组 len:数据长度
*	返 回 值: 执行状态
*********************************************************************************************************
*/
uint8_t LogSysInfo_calcXor8(uint8_t *pVarArry, uint8_t len)
{
	uint8_t rvalue;
	rvalue = pVarArry[0];

	for(uint8_t i = 1; i < len; i++){
		rvalue ^= pVarArry[i];
	}

	return rvalue;
}


你可能感兴趣的:(C语言算法,IOT通信)