#include #include "eeprom.h" #include "prj_com.h" #include "bill.h" #include "tdc_tof.h" #include "param.h" #include "app_nb.h" #include "alarm.h" Param_t Paramx; uint8_t rewrite_para_flag = 0; // DIAMETER_AND_TEMP_COMPENSATION_DEF Temp_compensation;//其中的diameter保存的校验值 const char SOFT_VER[5] = { 'V', VER_TYPE, VER_OPERATOR, VER_MODULE, VER_FIRMWARE}; // EEPROM_MAX_WRITE_SIZE 125 // EEPROM_PAGE_SIZE 256 /****************************************************************/ ////由于_read_eeprom write_eeprom是针对存bill记录的,最后一个字节 /// 自动校验,写参数不能用,这里需另外增加///////// /*********************批量写入数据********************************/ // 参数1:dat 指向要写入的数据指针 // 参数2:addr写入到M24M01存储器的地址 // 参数3:len写入数据的长度(字节单位) // 返回:1:成功 //////////////////////////////////////////////////////////////// uint8_t eeprom_MassWrite(uint8_t *dat, uint32_t addr, uint16_t len) // { if (addr > EEPROM_MAX_SIZE - 1 || addr + len > EEPROM_MAX_SIZE) return 1; // 已经跨页 // uint8_t num=(uint8_t)(EEPROM_PAGE_SIZE/EEPROM_MAX_WRITE_SIZE+1);//每页需写入的次数 // LOG("len= %d",len); uint32_t add_end = addr + len; // 地址末尾 uint16_t w_size; // 写入数据大小 uint8_t ret = 0; while (addr < add_end) { if (addr / EEPROM_PAGE_SIZE == (addr + EEPROM_MAX_WRITE_SIZE - 1) / EEPROM_PAGE_SIZE) { // 还在本页内 w_size = EEPROM_MAX_WRITE_SIZE; // 取写入长度为最大长度 } else { // 最大写入长度已经超过本页 w_size = EEPROM_PAGE_SIZE - (addr % EEPROM_PAGE_SIZE); // 取写入长度刚好到页边界 } if (addr + w_size > add_end) { // 写入长度已超出实际写入边界 w_size = add_end - addr; // 取实际写入长度 } ret |= write_eeprom_para(addr, dat, w_size); // 这里要改,write_eeprom是专门为写记录准备的,最后一个字节是校验位 addr += w_size; // 地址增加 dat += w_size; // 写数据指针增加 // LOG("addr= %d,size= %d\r\n",addr,w_size); } //} return ret; } /*******************批量读取数据********************************/ // 参数1 dat:读取数据保存指针 // 参数2 addr 读取数据eeprom起始地址 // 参数3 len:读取长度 // 返回:1:成功 /***************************************************************/ uint8_t eeprom_MassRead(uint8_t *dat, uint32_t addr, uint16_t len) { memset(dat, 0, len); uint16_t i; uint8_t ret = 0; if (addr > EEPROM_MAX_SIZE - 1 || addr + len > EEPROM_MAX_SIZE) { // 写入空间超过了最大地址范围 return 1; } if (len > EEPROM_MAX_READ_SIZE) { // 超过最大写读取长度 for (i = 0; i < len / EEPROM_MAX_READ_SIZE; i++) { // 每次读取最大长度 ret |= read_eeprom_para(addr + i * EEPROM_MAX_READ_SIZE, dat + i * EEPROM_MAX_READ_SIZE, EEPROM_MAX_READ_SIZE); // 要改 } if ((len % EEPROM_MAX_READ_SIZE) != 0) { // 整长度读完后,读取可能剩余的部分 ret |= read_eeprom_para(addr + i * EEPROM_MAX_READ_SIZE, dat + i * EEPROM_MAX_READ_SIZE, len % EEPROM_MAX_READ_SIZE); } } else { // 未超过最大长度 ret |= read_eeprom_para(addr, dat, len); } return ret; } #define DEFAULT_PATH_SKEW 0 // 超声波通道偏差修正系数 #define DEFAULT_PIPE_COE 0.1168745 // COE系数 #define DEFAULT_IMEI "000000000000000" // 默认IMEI #define DEFAULT_CCID "00000000000000000000" // 默认IMEI // 01234567890123456789 #define DEFAULT_MUX_CH_NO1 0 #define DEFAULT_MUX_CH_NO2 1 #define DEFAULT_MUX_CH_NO3 2 #define DEFAULT_MUX_CH_NO4 3 void LoadDefaultParam(void) // 加载默认参数值 { memset((uint8_t *)&Paramx, 0, sizeof(Param_t)); Paramx.path_skew[0] = DEFAULT_PATH_SKEW; Paramx.path_skew[1] = DEFAULT_PATH_SKEW; Paramx.path_skew[2] = DEFAULT_PATH_SKEW; Paramx.path_skew[3] = DEFAULT_PATH_SKEW; // 通道修正系数 Paramx.pipe_coe = DEFAULT_PIPE_COE; // coe系数 // strcpy(Paramx.imei, DEFAULT_IMEI); // imei号 // Paramx.imei[sizeof(DEFAULT_IMEI) - 1] = 0; // strcpy(Paramx.ccid, DEFAULT_CCID); // CCID号 // Paramx.ccid[sizeof(DEFAULT_CCID) - 1] = 0; Paramx.mux_ch_no[0] = DEFAULT_MUX_CH_NO1; Paramx.mux_ch_no[1] = DEFAULT_MUX_CH_NO2; Paramx.mux_ch_no[2] = DEFAULT_MUX_CH_NO3; Paramx.mux_ch_no[3] = DEFAULT_MUX_CH_NO4; // 多路开关地址 // Paramx.dev_id[0] = 0x00; //a6 Paramx.dev_id[0] = 0x80; // bcd //a5 Paramx.dev_id[1] = 0x55; // bcd Paramx.dev_id[2] = 0x60; // bcd Paramx.dev_id[3] = 0x10; // bcd Paramx.dev_id[4] = 0x00; // bcd Paramx.dev_id[5] = 0x03; // bcd//设备地址 A0 Paramx.chan_num = 1; // 超声波通道数量 Paramx.diameter = 25; // 直径 Paramx.pressure_sensor = 2; // without pressure sensor 是否带压力传感器,默认不带 Paramx.settlement_day = 0; // 结算日,默认 每月最后一天 Paramx.report_base_hour = 0; Paramx.report_base_min = 0; Paramx.report_base_sec = 0; // 上报时间点 Paramx.report_interval = 24 * 60; // 上报间隔 1天 Paramx.high_temp_alarm_thres = 32767; // 高温报警阈值 Paramx.low_temp_alarm_thres = -32768; // 低温报警阈值 Paramx.large_flow_alarm_thres = 0; // 大流量报警阈值 Paramx.large_flow_cnti_moni_time = 0; // 大流量持续时间 Paramx.cnti_flow_cnti_moni_time = 0; // 持续流量报警时间 Paramx.leak_flow_alarm_thres = 0; // 渗漏报警阈值 Paramx.leak_flow_cnti_moni_time = 0; // 渗漏报警持续时间 Paramx.high_press_alarm_thres = 0; // 高压力报警阈值 Paramx.low_press_alarm_thres = 0; // 低压力报警阈值 Paramx.main_server_ip[0] = 221; Paramx.main_server_ip[1] = 229; Paramx.main_server_ip[2] = 214; Paramx.main_server_ip[3] = 202; // 主服务器ip地址 Paramx.main_server_port = 5683; // 主服务器端口号 Paramx.sub_server_ip[0] = 221; // 备用服务器ip地址 Paramx.sub_server_ip[1] = 229; Paramx.sub_server_ip[2] = 214; Paramx.sub_server_ip[3] = 202; Paramx.sub_server_port = 5683; // 备用服务器端口号 Paramx.dma_report_start[0] = 0; Paramx.dma_report_start[1] = 0; Paramx.dma_report_start[2] = 0; // dma上报起始时间 时分秒 Paramx.dma_report_end[0] = 0; Paramx.dma_report_end[1] = 0; Paramx.dma_report_end[2] = 0; // dma上报结束时间 时分秒 Paramx.dma_interval = 0; // dma上报间隔 分钟单位 Paramx.total_positive_water[0] = 0; Paramx.total_positive_water[1] = 0; Paramx.total_positive_water[2] = 0; Paramx.total_positive_water[3] = 0; // 表底读数 uint16_t i, j; for (i = 0; i < CHECK_TEMP_POINT; i++) { // 8个流量等级 ,3个温度等级下的修正流速,用于查找当前流速落入哪个区间 Paramx.check_spdLvl_c[i].spd_lvl[0] = GetSpdLvl(Q1); Paramx.check_spdLvl_c[i].spd_lvl[1] = GetSpdLvl(1.6 * Q1); Paramx.check_spdLvl_c[i].spd_lvl[2] = GetSpdLvl(3.2 * Q1); Paramx.check_spdLvl_c[i].spd_lvl[3] = GetSpdLvl(8 * Q1); Paramx.check_spdLvl_c[i].spd_lvl[4] = GetSpdLvl(0.1 * Q3); Paramx.check_spdLvl_c[i].spd_lvl[5] = GetSpdLvl(0.2 * Q3); Paramx.check_spdLvl_c[i].spd_lvl[6] = GetSpdLvl(0.35 * (1.6 * Q1 + Q3)); Paramx.check_spdLvl_c[i].spd_lvl[7] = GetSpdLvl(Q3); // init c value is 1340 for (j = 0; j < CHECK_POINT_NUM; j++) { // 8个流量等级 ,3个温度等级下的基础声速,修正声速 Paramx.check_spdLvl_c[i].base_c[j] = CHECK_C_VALUE; Paramx.check_spdLvl_c[i].cur_c[j] = CHECK_C_VALUE; } } // float *fp = (float *)Quad_Cell_Ary; // for (i = 0; i < Quad_Cell_NUM * 5; i++) // { // *fp = 1; // fp++; // } Paramx.q_value = CHECK_Q_VALUE; // 默认Q值,影响check脉冲计算 Paramx.para_len = sizeof(Paramx) - 8; Paramx.para_crc32 = com_crc32L_next(0xFFFFFFFFu, (uint8_t *)(&Paramx) + 8, sizeof(Param_t) - 8, 0xEDB88320); // 计算crc校验值 // eeprom_MassWrite((uint8_t *)&Paramx,PARAMX_START_ADDR,sizeof(Param_t)); // eeprom_MassRead((uint8_t *)&Paramx,PARAMX_START_ADDR,sizeof(Param_t)); Paramx.b = 63; Paramx.n = 45; Paramx.q = 32; Paramx.r = 500; rewrite_para_flag |= 0x1; } void get_para_to_tdcitf(void) // 获取参数给tdcItf { uint8_t i; for (i = 0; i < MUX_CH_NUM; i++) TdcItf.path_skew[i] = Paramx.path_skew[i]; TdcItf.pipe_coe = Paramx.pipe_coe; TdcItf.ch_num = Paramx.chan_num; if (TdcItf.ch_num == 1) { TdcItf.ch_bit_map = 1; } else if (TdcItf.ch_num == 2) { TdcItf.ch_bit_map = 3; } else if (TdcItf.ch_num == 4) { TdcItf.ch_bit_map = 0x0F; } for (i = 0; i < MUX_CH_NUM; i++) TdcItf.mux_ch_no[i] = Paramx.mux_ch_no[i]; } // #define DEBUG_PARAM // #ifdef DEBUG_PARAM // uint8_t p1data[sizeof(Param_t)]; // uint8_t p2data[sizeof(Param_t)]; // uint8_t err_pos=0; // #endif void ParamInit(void) // 上电初始化 { uint8_t i; if (init_eeprom_mutx_flag == 0) { eeprom_mutx_init(); // 创建eeprom操作的互斥信号量,未进入调度前,不能用 } eeprom_MassRead((unsigned char *)&Paramx, PARAMX_START_ADDR, sizeof(Param_t)); // 读取参数 // memcpy(p1data,&Paramx,sizeof(Param_t)); uint32_t crc = com_crc32L_next(0xFFFFFFFFu, (uint8_t *)(&Paramx) + 8, sizeof(Param_t) - 8, 0xEDB88320); if (crc != Paramx.para_crc32) // wxl2026-5-8 校验错误时记录日志 rewrite_para_flag |= 4; if (Paramx.para_len != sizeof(Paramx) - 8) { LoadDefaultParam(); // 加载默认参数 } // Nb_Run_t *nb_run; // GetNbRun(&nb_run); // Paramx.imei[15] = 0; // 限制长度,防止溢出 // strcpy(nb_run->imei, Paramx.imei); // Paramx.ccid[31] = 0; // 限制长度,防止溢出 // strcpy(nb_run->ccid, Paramx.ccid); // Paramx.ver_type = VER_TYPE; // type, 1: ultrasound flow meter; // Paramx.ver_operator = VER_OPERATOR; // operator, 1: China Telecom; // Paramx.ver_module = VER_MODULE; // module, 1: HC32_BC28; // Paramx.ver_firmware = VER_FIRMWARE; // firmware version;//设备版本信息 if (Paramx.chan_num != 1) { Paramx.chan_num = 1; rewrite_para_flag |= 1; } get_para_to_tdcitf(); // 获取参数到tdcitf结构体 memset((uint8_t *)&PatchInfo, 0, sizeof(PATCH_INFO_DEF)); if (Check_quad_cell_data_valid(QUAD_CELL_START_ADDR) == 0) { // wxl2026-4-16 get_quad_cell_data(Quad_Cell_Ary); // wxl2026-4-14 } else { rewrite_para_flag |= 0x2; } alarm_init(); bill_init(); init_eeprom_mutx_flag = 1; } ////////////检查参数时,发现参数不合法,加载默认参数后重新eeprom///////////////// void check_rewrite_para(void) { if (StartUp != 2) // 在第二秒开始判断参数正确性 return; if (rewrite_para_flag & 0x1) { // 保存主参数 eeprom_MassWrite((uint8_t *)&Paramx, PARAMX_START_ADDR, sizeof(Param_t)); rewrite_para_flag |= 0x4; } if (rewrite_para_flag & 0x2) { // 保存主参数 set_diameter_and_quad_cell_data(0, &Quad_Cell_Ary); } // crc校验错误写日志 if (rewrite_para_flag & 0x4) // wxl2026-5-8 { write_alarm(LOG_PARA_CRC_ERROR, 1, 5555); // memcpy(p2data,&Paramx,sizeof(Param_t)); set_crc_check_para(); // int i; /// for(i=0;i= 40.0) { return 2; } if (temp >= 10.0) { return 1; } return 0; } */ // 获取温度等级索引号,only use 0 uint16_t CheckTempRangeIndex(float temp) { return 0; } // modified by yuewei 20260316 only use check_spdLvl_c[0] end /**************************************************/ // 获取流速等级索引号,用于获取校正系数 /***************************************************/ #if 0 uint8_t CheckCValueIndex(uint16_t temp_idx, uint16_t spdLvl) { uint8_t i; uint16_t mid; CheckSpdLvlC_t *p = &Paramx.check_spdLvl_c[temp_idx]; if (spdLvl <= p->spd_lvl[0]) { return 0; } for (i = 0; i < CHECK_POINT_NUM - 1; i++) { mid = (p->spd_lvl[i + 1] + p->spd_lvl[i]) / 2; if (spdLvl >= p->spd_lvl[i] && spdLvl < mid) { return i; } else if (spdLvl >= mid && spdLvl < p->spd_lvl[i + 1]) { return i + 1; } } return CHECK_POINT_NUM - 1; } #endif #if 0 uint8_t CheckCValueIndex(uint16_t temp_idx, uint16_t spdLvl) { // 1. Boundary safety limit, only 3 temperature intervals, actually use index 0 only if (temp_idx >= 3) { return 0; } CheckSpdLvlC_t *p = &Paramx.check_spdLvl_c[temp_idx]; uint8_t i; // 2. Less than minimum speed level -> fixed return 0 if (spdLvl <= p->spd_lvl[0]) { return 0; } // 3. Direct interval judgment (hysteresis canceled) for (i = 0; i < CHECK_POINT_NUM - 1; i++) { // Directly match speed level interval if (spdLvl <= p->spd_lvl[i + 1]) { return i; } } // 4. Greater than maximum speed level -> return last index return CHECK_POINT_NUM - 1; } #endif uint8_t CheckCValueIndex(uint16_t temp_idx, uint16_t spdLvl) { // 1. 温度区间安全限界,仅0/1/2有效 if (temp_idx >= 3) { return 0; } CheckSpdLvlC_t *p = &Paramx.check_spdLvl_c[temp_idx]; uint8_t i; uint8_t best_idx = 0; uint16_t min_diff; uint16_t curr_diff; spdLvl = spdLvl - 5;//add by yuewei 20260629 // 初始化最小差值为第一个点差值 if (spdLvl >= p->spd_lvl[0]) min_diff = spdLvl - p->spd_lvl[0]; else min_diff = p->spd_lvl[0] - spdLvl; // 遍历所有速度点,寻找差值最小的下标 for (i = 1; i < CHECK_POINT_NUM; i++) { if (spdLvl >= p->spd_lvl[i]) curr_diff = spdLvl - p->spd_lvl[i]; else curr_diff = p->spd_lvl[i] - spdLvl; // 更新最小差值与最优索引 if (curr_diff < min_diff) { min_diff = curr_diff; best_idx = i; } } return best_idx; } // 获取在不同温度和流速等级下的,声速修正系数 float CheckFlowCorrectFactor(float temp, uint16_t spdLvl) { uint16_t temp_idx = CheckTempRangeIndex(temp); uint8_t c_idx = CheckCValueIndex(temp_idx, spdLvl); CheckSpdLvlC_t *p = &Paramx.check_spdLvl_c[temp_idx]; uint16_t curr_c = p->cur_c[c_idx]; uint16_t base_c = p->base_c[c_idx]; LOG("spdLvl=%d,curr_c = %d,c_idx=%d\n", spdLvl, curr_c, c_idx); return ((float)curr_c * 1.0f) / (float)base_c; } uint32_t BillTime(uint8_t year, uint8_t month) // 确定结算日的上报时间(取) { uint8_t last_day = last_day_of_month(year, month); uint8_t day; if (Paramx.settlement_day == 0) { return 0; } if (Paramx.settlement_day > 0 && Paramx.settlement_day <= last_day) { day = Paramx.settlement_day; } else { day = last_day; } return ((day << 24) | (Paramx.report_base_hour << 16) | (Paramx.report_base_min << 8) | Paramx.report_base_sec); // 上报时分秒 } /////////////////获取参数结构体指针////////////////////// // 输入参数:指向结构体指针的指针 // 返回参数:无 ////////////////////////////////////// void GetParam(Param_t **para) { *para = &Paramx; } ///////////////////////保存所有参数////////////////// // 输入参数:无 // 返回参数:无 //////将内存中的param参数全部写入到eeprom中////////// void SaveParam(void) { uint32_t param_len = 0; uint32_t param_crc; uint8_t *data; uint8_t i; Nb_Run_t *nb_run; GetNbRun(&nb_run); Paramx.path_skew[0] = TdcItf.path_skew[0]; Paramx.path_skew[1] = TdcItf.path_skew[1]; Paramx.path_skew[2] = TdcItf.path_skew[2]; Paramx.path_skew[3] = TdcItf.path_skew[3]; Paramx.pipe_coe = TdcItf.pipe_coe; // memcpy(Paramx.imei, nb_run->imei, 16); // imei号保存 // memcpy(Paramx.ccid, nb_run->ccid, 32); // ccid号保存 memcpy(Paramx.mux_ch_no, TdcItf.mux_ch_no, sizeof(TdcItf.mux_ch_no)); // 多路开关地址 Paramx.para_crc32 = com_crc32L_next(0xFFFFFFFFu, (uint8_t *)(&Paramx) + 8, sizeof(Param_t) - 8, 0xEDB88320); Paramx.para_len = sizeof(Paramx) - 8; eeprom_MassWrite((uint8_t *)&Paramx, PARAMX_START_ADDR, sizeof(Paramx)); } /////////////////设置版本号 保存到eeprom中/////////////////////// // 参数:版本号uchar // 返回:无 /////////////////////////////// // void set_param_firmware_ver(uint8_t ver) //{ // 设置版本号 // Paramx.ver_firmware = ver; // uint32_t addr = offsetof(Param_t, ver_firmware) + PARAMX_START_ADDR; // write_eeprom_para(addr, (uint8_t *)&ver, sizeof(ver)); // set_crc_check_para(); // } ////////////获取版本号////////////// // 参数:无 // 返回:版本号数值uchar //////////////////////// uint8_t get_param_firmware_ver(void) { // 读取版本号 return VER_FIRMWARE; } /////////////////获取超声波通道数///////////////////// /// 参数:无 // 返回:超声波通道数 ///////////////////////////////////// uint8_t GetChNum(void) { uint8_t numb; get_channel_numb(&numb); return numb; } int8_t get_channel_numb(uint8_t *numb) { // 获取通道数 *numb = Paramx.chan_num; return 0; } /////////////////////////////与红外通讯相关的设置和读取/////////////////////////////////////////// //////////////////////////////////对paramx参数进行crc校验,并保存crc校验值//////////////////////////////////////////////////////////////// // 参数:无 // 返回0:正常 1:异常 uint8_t set_crc_check_para(void) { Paramx.para_crc32 = com_crc32L_next(0xFFFFFFFFu, (uint8_t *)(&Paramx) + 8, sizeof(Param_t) - 8, 0xEDB88320); return write_eeprom_para(PARAMX_START_ADDR + offsetof(Param_t, para_crc32), (uint8_t *)&Paramx.para_crc32, sizeof(Paramx.para_crc32)); // 校验值长度 return 0; } /////////////////////////检查新参数是否改变,如果改变则保存///////////////////////////////////////////////// // 参数1: new_para新参数值 // 参数2: old_para原参数值 // 参数3: len参数长度 // 参数5:flag保存是否修改的标志1:修改了,0,未修改 // 返回:0:保存成功 1:失败 ///////////////////////////////////////////////////////////////////////////// uint8_t set_para_if_nessary(void *new_para, void *old_para, uint8_t len, uint8_t *flag) { uint8_t *dat1 = new_para; // 转换为char型指针 uint8_t *dat2 = old_para; // 转换为char型指针 // if(memcmp(dat1,dat2,len)==0){//检查新数据和老数据是否一致 // return 0; // } memcpy(dat2, dat1, len); // 更新参数值,在paramx中 *flag = 1; // 参数更改标志,用于crc校验参考 uint32_t addr = PARAMX_START_ADDR + (uint32_t)old_para - (uint32_t)&Paramx; // 取改参数在eeprom中的地址 uint8_t ret; ret = write_eeprom_para(addr, dat1, len); // 返回写入是否成功标志 0:成功,1失败 ret |= read_eeprom_para(addr, old_para, len); // 写入后读回来 get_para_to_tdcitf(); return 0; } //////////////////设置硬件参数参数//////////////////////////// /// 参数:HardWarePara 包含 是否带压力传感器指示 pressure_sensor // // 关断参数:pipe_coe // 返回: 成功返回1, 失败返回2 /////////////////////////////////// int8_t set_hardware_para(HardWareParaDef *HardWarePara) { uint8_t flag = 0, ret = 0; if (HardWarePara->pipe_coe <= 0) return 2; // 数据不合法 if (HardWarePara->pressure_sensor != 1 && HardWarePara->pressure_sensor != 2) return 2; ret |= set_para_if_nessary(&HardWarePara->pipe_coe, &Paramx.pipe_coe, sizeof(Paramx.pipe_coe), &flag); ret |= set_para_if_nessary(&HardWarePara->pressure_sensor, &Paramx.pressure_sensor, sizeof(Paramx.pressure_sensor), &flag); if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); // write_eeprom_para在成功写入后返回0,否则返回非零值 } ////////////////////////读取硬件参数/////////////////////////////////// // 参数:硬件参数指针HardWareParaDef // 返回:0:读取不成功, 1:读取成功 /////////////////////////////////////////////////////////////////////// int8_t get_hardware_para(HardWareParaDef *HardWarePara) { HardWarePara->pipe_coe = Paramx.pipe_coe; HardWarePara->pressure_sensor = Paramx.pressure_sensor; return 0; } PATCH_INFO_DEF PatchInfo; ///////////////////设置表端开始升级信息////////////////////////////////////////// // 参数patch_info:表端升级相关信息指针 // 返回:1:设置成功 2:版本号不匹配 3:升级包长度值超过允许长度 ///////////////////////////////////////////////////////// int8_t set_patch_info(PATCH_INFO_DEF *patch_info) { memset((uint8_t *)&PatchInfo, 0, sizeof(PATCH_INFO_DEF)); if (patch_info->new_ver[4] <= patch_info->cur_ver[4] || patch_info->new_ver[0] != patch_info->cur_ver[0] || patch_info->new_ver[1] != patch_info->cur_ver[1] || patch_info->new_ver[2] != patch_info->cur_ver[2] || patch_info->new_ver[3] != patch_info->cur_ver[3]) { // 新版本信息不合法 return 2; } if (patch_info->cur_ver[0] != 'V') return 2; if (patch_info->cur_ver[1] != VER_TYPE) return 2; if (patch_info->cur_ver[2] != VER_OPERATOR) return 2; if (patch_info->cur_ver[3] != VER_MODULE) return 2; if (patch_info->cur_ver[4] != VER_FIRMWARE) return 2; // 差分包当前版本和正在运行的版本不一致 if (patch_info->patch_len > DIFF_PATCH_PACKET_SIZE) return 3; // 长度超过写入最大长度 memcpy(PatchInfo.cur_ver, patch_info->cur_ver, sizeof(patch_info->cur_ver)); // 保存当前版本号 memcpy(PatchInfo.new_ver, patch_info->new_ver, sizeof(patch_info->new_ver)); // 保存新版本号 PatchInfo.packet_crc = patch_info->packet_crc; // 数据的总校验值 PatchInfo.patch_len = patch_info->patch_len; // 数据总长度 PatchInfo.per_packet_len = patch_info->per_packet_len; // 固定512字节 // PatchInfo.per_packet_len=patch_info->per_packet_len;//每包数据长度 PatchInfo.update_status = 0x1; // 开始升级标志 PatchInfo.cur_num = 1; // 当前升级包序号,初始0,第一包值=1 PatchInfo.res_crc16 = 0; // 累计crc,没来一包数据校验一次,最后检查和中校验值是否相等 PatchInfo.flash_addr_ptr = 0; // 当前flash写入地址 LOG("packet num =%d,total size = %d\r\n", (patch_info->patch_len + PatchInfo.per_packet_len - 1) / PatchInfo.per_packet_len, patch_info->patch_len); return 1; } ////////////////////////////向flash写入差分包///////////////////////////////////////// ////DIFF_PACKET_DEF参数1 numb:包序号,从1开始 // DIFF_PACKET_DEF参数2: len:差分包长度,目前默认是512byte // DIFF_PACKET_DEF参数3:data指针 指向要写入的差分包数据 // 返回:1:成功写入 2:crc失败 6:未收到开始包 7:数据包序号不对 ////////////////////////////////////////////////////////////////////////////// // const uint8_t app_addr=0; uint8_t write_diff_patch_to_flash(DIFF_PACKET_DEF *Packet) { McuFlash_WP_t WP; uint8_t last_dat = 0; uint16_t write_len; if (PatchInfo.update_status != 1) return 0x6; if (Packet->packet_numb != PatchInfo.cur_num) return 6; // 不是下一个包 //////////////////确定最后一帧及写入长度/////////////////// Packet->packet_size = Packet->packet_size; // DIFF_PER_PACKET_SIZE;//固定 if (Packet->packet_numb * PatchInfo.per_packet_len >= PatchInfo.patch_len) { last_dat = 1; write_len = (uint16_t)(PatchInfo.patch_len % PatchInfo.per_packet_len); } else { last_dat = 0; // 中间帧数据 write_len = Packet->packet_size; } // write_len = Packet->packet_size; PatchInfo.res_crc16 = pack_crc_check(PatchInfo.res_crc16, Packet->data, write_len); if (Packet->packet_numb == 1) { // 第一包数据,先擦除 #ifndef DEBUG_PATCH rt_enter_critical(); // McuFlash_Unlock(); McuFlash_EraseSegment(DIFF_PATCH_PACKET_START, DIFF_PATCH_PACKET_SIZE); // McuFlash_Lock(); rt_exit_critical(); #endif // erase_flash_memory(DIFF_PATCH_PACKET_START, DIFF_PATCH_PACKET_SIZE); } #ifndef DEBUG_PATCH write_flash_memory(Packet->data, DIFF_PATCH_PACKET_START + PatchInfo.flash_addr_ptr, write_len); #endif #ifdef APP_DEBUG_PRINT LOG("write patch num= %d\r\n", PatchInfo.cur_num); #endif PatchInfo.cur_num++; PatchInfo.flash_addr_ptr += write_len; if (last_dat == 1) { // 最后一包 if (PatchInfo.res_crc16 != PatchInfo.packet_crc) { LOG("update data crc err pack:%d\r\n", Packet->packet_numb); return 2; } else { PatchInfo.update_status = 2; // 升级包写入结束,bootloader用该位判断是否需要升级,升级完成后将该位清零 // 未升级的情况下,bootloader根据代码的完整程度和版本的新旧情况运行app // 写差分升级包升级的信息,供bootloader使用 PatchInfo.run_app_ver_addr = (uint32_t)SOFT_VER; #ifndef DEBUG_PATCH erase_flash_memory(DIFF_UPDATE_INFO_START, DIFF_UPDATE_INFO_SIZE); // 写flash write_flash_memory((uint8_t *)&PatchInfo, DIFF_UPDATE_INFO_START, sizeof(PATCH_INFO_DEF)); #endif LOG("last packet ok\r\n"); NbRun.reset_mcu_cnt = 5; return 1; // 最后一包 } } return 1; // 0x10;//中间包 } //************设置主服务器和从服务器的ip地址和端口号******************************/ // 参数dat:指向SERVER_IP_PORT_DEF结构体 // 返回:成功返回1,失败返回2 ///////////////////////////////////////////////////////////////////////////////// int8_t set_server_ip_port_para(SERVER_IP_PORT_DEF *server_ip_para) { uint8_t flag = 0, ret = 0; // uint32_t addr; ret |= set_para_if_nessary(server_ip_para->main_server_ip, Paramx.main_server_ip, sizeof(Paramx.main_server_ip), &flag); ret |= set_para_if_nessary(&server_ip_para->main_server_port, &Paramx.main_server_port, sizeof(Paramx.main_server_port), &flag); ret |= set_para_if_nessary(server_ip_para->sub_server_ip, Paramx.sub_server_ip, sizeof(Paramx.sub_server_ip), &flag); ret |= set_para_if_nessary(&server_ip_para->sub_server_port, &Paramx.sub_server_port, sizeof(Paramx.sub_server_port), &flag); if (flag == 1) { // 保存crc校验 // Paramx.para_crc32 = com_crc32L_next(0xFFFFFFFFu, (uint8_t *)(&Paramx+8), sizeof(Param_t)-8, 0xEDB88320); // ret|=write_eeprom_para(PARAMX_START_ADDR+offsetof(Param_t,para_crc32),(uint8_t *)&Paramx.para_crc32,sizeof(Paramx.para_crc32));//校验值长度 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); // write_eeprom_para在成功写入后返回0,否则返回非零值 } ////////////////////////////读取主服务器和备用服务器ip地址和端口号///////////////////////////// // 参数server_ip_dat :SERVER_IP_PORT_DEF类型指针,用于保存主服务器和备用服务器的端口和ip地址信息 ////////////////////////////////////////////////////////// int8_t get_server_ip_port_para(SERVER_IP_PORT_DEF *server_ip_para) { memcpy(server_ip_para->main_server_ip, Paramx.main_server_ip, sizeof(Paramx.main_server_ip)); server_ip_para->main_server_port = Paramx.main_server_port; memcpy(server_ip_para->sub_server_ip, Paramx.sub_server_ip, sizeof(Paramx.sub_server_ip)); server_ip_para->sub_server_port = Paramx.sub_server_port; return 0; } ////////////////////////////设置定时上报周期和时间间隔///////////////////////////////////////////////////// // 参数:regular_report_para定时上报数据结构体,包含起始基准时间,间隔时间u\ //返回:1:成功2:失败 ///////////////////////////////////////////////////////////////////////////////////////////////////////// int8_t set_regular_report_para(REGULAR_REPORT_DEF *regular_report_para) { uint8_t flag = 0, ret = 0; if (regular_report_para->report_base_hour > 23 || regular_report_para->report_base_min > 59 || regular_report_para->report_base_sec > 59) return 2; ret |= set_para_if_nessary(®ular_report_para->report_base_hour, &Paramx.report_base_hour, 3, &flag); // 年月日参数 ret |= set_para_if_nessary(®ular_report_para->report_interval, &Paramx.report_interval, sizeof(Paramx.report_interval), &flag); // 时间间隔 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } /////////////////////////////读取定时上报周期和时间间隔信息////////////////////////////////////////////// // 参数regular_report_para保存定时上报数据结构体 // 返回:1成功读取 //////////////////////////////////////////// int8_t get_regular_report_para(REGULAR_REPORT_DEF *regular_report_para) { regular_report_para->report_base_hour = Paramx.report_base_hour; regular_report_para->report_base_min = Paramx.report_base_min; regular_report_para->report_base_sec = Paramx.report_base_sec; regular_report_para->report_interval = Paramx.report_interval; return 0; } ///////////////////////////////////设置DMA定时上报周期参数//////////////////////////////////////////////// // 参数:dma_report_para dma参数结构体,包含dma起始时间结束时间和时间间隔参数 // 返回:1:成功 2:失败 //////////////////////////////////////////////////////////////////////////////////// int8_t set_dma_regular_report_para(DMA_REGULAR_REPORT_DEF *dma_report_para) { uint8_t flag = 0, ret = 0; // 判断数据是否在正常范围内 if (dma_report_para->dma_report_start_hour > 23 || dma_report_para->dma_report_start_min > 59 || dma_report_para->dma_report_start_sec > 59) return 2; if (dma_report_para->dma_report_end_hour > 23 || dma_report_para->dma_report_end_min > 59 || dma_report_para->dma_report_end_sec > 59) return 2; ret |= set_para_if_nessary(dma_report_para, Paramx.dma_report_start, 8, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } //////////////////////////////读取DMA定时上报参数/////////////////////////////////////////////////////// // 参数: dma_report_para 参数保存结构体 // 返回1:获取成功2:失败 //////////////////////////////////////////////////////////////////////////////////////// int8_t get_dma_regular_report_para(DMA_REGULAR_REPORT_DEF *dma_report_para) { dma_report_para->dma_report_start_hour = Paramx.dma_report_start[0]; dma_report_para->dma_report_start_min = Paramx.dma_report_start[1]; dma_report_para->dma_report_start_sec = Paramx.dma_report_start[2]; dma_report_para->dma_report_end_hour = Paramx.dma_report_end[0]; dma_report_para->dma_report_end_min = Paramx.dma_report_end[1]; dma_report_para->dma_report_end_sec = Paramx.dma_report_end[2]; dma_report_para->dma_interval = Paramx.dma_interval; return 0; } ////////////////////////设置流量告警参数////////////////////////////////////// // 参数:flow_alarm_para流量告警参数结构体 // 返回:1:设置成功 2:失败 int8_t set_flow_alarm_para(FLOW_ALARM_PARA_DEF *flow_alarm_para) { uint8_t flag = 0, ret = 0; // 判断数据是否在正常范围内 ret = set_para_if_nessary(flow_alarm_para, &Paramx.large_flow_alarm_thres, 14, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ?: -1); } ////////////////////////获取流量告警参数值///////////////////////////////////////// // 参数flow_alarm_para 保存流量参数 // 返回:1:成功 2:失败 ////////////////////////////////////////////////////// int8_t get_flow_alarm_para(FLOW_ALARM_PARA_DEF *flow_alarm_para) { flow_alarm_para->large_flow_alarm_thres = Paramx.large_flow_alarm_thres; flow_alarm_para->large_flow_cnti_moni_time = Paramx.large_flow_cnti_moni_time; flow_alarm_para->cnti_flow_cnti_moni_time = Paramx.cnti_flow_cnti_moni_time; flow_alarm_para->leak_flow_alarm_thres = Paramx.leak_flow_alarm_thres; flow_alarm_para->leak_flow_cnti_moni_time = Paramx.leak_flow_cnti_moni_time; return 0; } /////////////////////设置压力表告警参数/////////////////////////////////////////////////// // 参数:press_alarm_para 要设置的压力表报警参数数据指针 // 返回:1:成功,2:失败 /////////////////////////////////////////////////////////////////////////////// int8_t set_press_alarm_para(PRESS_ALARM_PARA_DEF *press_alarm_para) { uint8_t flag = 0, ret = 0; uint8_t dat[4]; memset(dat, 0, 4); dat[0] = press_alarm_para->high_press_alarm_thres; dat[2] = press_alarm_para->low_press_alarm_thres; if (dat[0] < dat[2]) return 3; // 判断数据是否在正常范围内 ret = set_para_if_nessary(dat, &Paramx.high_press_alarm_thres, 4, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } ////////////////////////获取压力报警参数////////////////////////////////////////////////// ////参数 press_alarm_para 存储压力报警参数值,包括高压力和低压力报警阈值 // 返回 1:读取成功 //////////////////////////////////////////////////////////////////////////////////////////// int8_t get_press_alarm_para(PRESS_ALARM_PARA_DEF *press_alarm_para) { press_alarm_para->high_press_alarm_thres = Paramx.high_press_alarm_thres; press_alarm_para->low_press_alarm_thres = Paramx.low_press_alarm_thres; return 0; } /////////////////////////设置温度告警参数////////////////////////////////////// /////参数: temp_alarm_para 指针包含要存储的温度报警参数 包括高温和低温报警阈值 // 返回 1:成功 2:失败 //////////////////////////////////////////////////////////////////////////// int8_t set_temp_alarm_para(TEMP_ALARM_PARA_DEF *temp_alarm_para) { uint8_t flag = 0, ret = 0; if (temp_alarm_para->high_temp_alarm_thres < temp_alarm_para->low_temp_alarm_thres) return 2; ret = set_para_if_nessary(temp_alarm_para, &Paramx.high_temp_alarm_thres, 4, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } /////////////////////////获取温度告警参数////////////////////////////////////// /////参数: temp_alarm_para 指针,保存读出的参数 // 返回 1:成功 2:失败 //////////////////////////////////////////////////////////////////////////// int8_t get_temp_alarm_para(TEMP_ALARM_PARA_DEF *temp_alarm_para) { temp_alarm_para->high_temp_alarm_thres = Paramx.high_temp_alarm_thres; temp_alarm_para->low_temp_alarm_thres = Paramx.low_temp_alarm_thres; return 0; } ////////////////////////////设置表端结算日参数///////////////////////////////////// // 参数:要写入的表端结算日数据 // 返回 1:成功 2:失败 //////////////////////////////////////////////////////////////////////////////////// int8_t set_settlement_day_para(SETTLEMENT_DAY_DEF *settlement_day_para) { uint8_t flag = 0, ret = 0; if (settlement_day_para->settlement_day > 31) return 2; ret = set_para_if_nessary(settlement_day_para, &Paramx.settlement_day, 1, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } ////////////////////////////读取表端结算日参数///////////////////////////////////// // 参数:要写入的表端结算日数据 // 返回 1:成功 2:失败 //////////////////////////////////////////////////////////////////////////////////// int8_t get_settlement_day_para(SETTLEMENT_DAY_DEF *settlement_day_para) { settlement_day_para->settlement_day = Paramx.settlement_day; return 0; } ///////////////////////////////设置表端底读/////////////////////////////////////////////////// // 参数:指向表端底读数据指针 // 返回:1:成功 2:失败 ////////////////////////////////////////////////////////////////////////////////////////////// int8_t set_meter_bottom_data(METER_BOTTOM_DEF *bottom_data) { uint8_t flag = 0, ret = 0; double base_read = (double)(uint32_t)((bottom_data->fw[3] << 24) | (bottom_data->fw[2] << 16) | (bottom_data->fw[1] << 8) | bottom_data->fw[0]); base_read *= 10000; // 0.01->0.000001//转换为0.000001单位 FwCumFlow = base_read; // 更新当前流量和正向流量 base_read = (double)(uint32_t)((bottom_data->bw[3] << 24) | (bottom_data->bw[2] << 16) | (bottom_data->bw[1] << 8) | bottom_data->bw[0]); base_read *= 10000; // 0.01->0.000001//转换为0.000001单位 BwCumFlow = base_read; CumFlow = FwCumFlow - BwCumFlow; SetBillCumFlow(CumFlow, FwCumFlow, BwCumFlow); // 设置当前流量数据 save_cur_flow_flag = 1; // 保存到当前值,循环存储 return 0; } ////////////////////////读取前18个月月结数据////////////////////////////////////////// // 参数:月结数据指针,空间大小18*11 // 成功返回1,失败返回2 ///////////////////////////////////////////////////////////////////////////////////// int8_t get_18month_record_data(uint8_t *data) { uint8_t i, ret = 0; bill_t bill; rtc_run_t *rr; get_rtc_run(&rr); // 当前时间 uint8_t flag, j; uint32_t date = ((rr->year << 16) | (rr->month << 8)); for (i = 0; i < 18; i++) { date = prev_month(date); flag = get_month_used_bill((uint8_t)(date >> 16), (uint8_t)(date >> 8), &bill); ret |= flag; if (flag != 0) { for (j = 0; j < 11; j++) data[i * 11 + j] = 0; continue; } data[i * 11] = 0x14; data[i * 11 + 1] = bill.date[0]; data[i * 11 + 2] = bill.date[1]; // data[i * 11 + 3] = (unsigned char)(bill.fw_used); data[i * 11 + 4] = (unsigned char)(bill.fw_used >> 8); data[i * 11 + 5] = (unsigned char)(bill.fw_used >> 16); data[i * 11 + 6] = (unsigned char)(bill.fw_used >> 24); // 正向流量 data[i * 11 + 7] = (unsigned char)(bill.bw_used); data[i * 11 + 8] = (unsigned char)(bill.bw_used >> 8); data[i * 11 + 9] = (unsigned char)(bill.bw_used >> 16); data[i * 11 + 10] = (unsigned char)(bill.bw_used >> 24); // 反向流量 } if (ret != 0) return -1; else return 0; } ////////////////////////读取前30日日结数据////////////////////////////////////////// // 参数:日结数据指针,空间大小30*12 // 成功返回1,失败返回2 ///////////////////////////////////////////////////////////////////////////////////// int8_t get_30day_record_data(uint8_t *data) { uint8_t i, ret = 0; bill_t bill; rtc_run_t *rr; get_rtc_run(&rr); // 当前时间 uint32_t date = ((rr->year << 16) | (rr->month << 8) | rr->day); uint8_t flag, j; for (i = 0; i < 30; i++) { date = prev_day(date); flag = get_day_used_bill((uint8_t)(date >> 8), (uint8_t)date, &bill); ret |= flag; if ((uint8_t)(date >> 16) != bill.date[0] || (uint8_t)(date >> 8) != bill.date[1] || (uint8_t)(date & 0xff) != bill.date[2] // rec time is not intend to read || flag != 0) { ret |= 1; for (j = 0; j < 12; j++) data[i * 12 + j] = 0; continue; } data[i * 12] = 0x14; // 20 data[i * 12 + 1] = bill.date[0]; // 年 data[i * 12 + 2] = bill.date[1]; // 月 data[i * 12 + 3] = bill.date[2]; // 日 data[i * 12 + 4] = (unsigned char)(bill.fw_used); data[i * 12 + 5] = (unsigned char)(bill.fw_used >> 8); data[i * 12 + 6] = (unsigned char)(bill.fw_used >> 16); data[i * 12 + 7] = (unsigned char)(bill.fw_used >> 24); data[i * 12 + 8] = (unsigned char)(bill.bw_used); data[i * 12 + 9] = (unsigned char)(bill.bw_used >> 8); data[i * 12 + 10] = (unsigned char)(bill.bw_used >> 16); data[i * 12 + 11] = (unsigned char)(bill.bw_used >> 24); } if (ret != 0) return -1; else return 0; } ////////////////////////读取结数据////////////////////////////////////////// // 参数1:读取的年月日 ///////////////////////////////////////////////////////////////////////////////////// int8_t get_hour_record_data(uint8_t year, uint8_t month, uint8_t day, uint8_t *data) { uint8_t i; hour_bill_t bill; int32_t days; // 1. 默认填零 memset(data, 0, 244); // 2. 检查时间有效性 days = get_days_between_now(year, month, day); // 假设:有效范围为过去30天内(含今天,即0~30天) // 如果 get_days_between_now 返回负数表示未来,正数表示过去 if (days < 0 || days > 30) { return 0; // 不在30天内,返回全零(已memset) } // 3. 读取数据 // 注意:请确认 get_hour_used_bill 的正确用法。 // 如果原代码 (uint8_t)date 是bug,这里应该传入正确的参数。 // 假设 get_hour_used_bill 需要 year, month, day, hour // 由于我不知道 get_hour_used_bill 的确切签名,以下代码假设它需要修正以支持年月日 // 如果原系统确实只传 day 就能工作(例如底层驱动自动处理环形缓冲或当前月),则保留原调用方式但需注意局限性。 for (i = 0; i < 24; i++) { // 【重要】原代码这里强转 uint8_t 丢失了年月信息,极大概率是Bug。 // 如果 get_hour_used_bill 只能接受 uint8_t day,请确认它是否能正确读取指定年月的数据。 // 如果不能,你需要修改 get_hour_used_bill 或在此处提供完整日期上下文。 // 暂时沿用原调用方式,但加上注释警示 if (get_hour_used_bill(day, i, &bill) == 0) { if (bill.date[0] != year || bill.date[1] != month || bill.date[2] != day || bill.date[3] != i) // if rec time is not valid drop it continue; data[0] = 0x14; // 标记有效? 原代码在循环外设置 data[0]=0x14, 这里如果部分失败怎么处理? // 原代码逻辑:只要进入else,data[0]就被设为0x14。 // 如果某个小时失败,bill是全零,拷贝进去也是零。 memcpy(&data[4 + i * 3], bill.fw_used, 3); memcpy(&data[4 + 72 + i * 3], bill.bw_used, 3); data[4 + 144 + i] = bill.pressure; memcpy(&data[4 + 168 + i * 3], bill.flow_rate, 3); } // 如果读取失败,data对应位置保持为0(因为开头memset了) } // 设置头部信息,表示这是一条有效的记录结构(即使内容为0) data[0] = 0x14; data[1] = year; data[2] = month; data[3] = day; return 0; } ////////////////////////////////比较两个时间的大小/////////////////////////////////////////// // 参数1,2,用于比较的两个时间结构体 // 返回:date1>date2时返回1,相等时返回0,date1 date2.year) return 1; else if (date1.year < date2.year) return -1; if (date1.month > date2.month) return 1; else if (date1.month < date2.month) return -1; if (date1.day > date2.day) return 1; else if (date1.day < date2.day) return -1; if (date1.hour > date2.hour) return 1; else if (date1.hour < date2.hour) return -1; if (date1.min > date2.min) return 1; else if (date1.min < date2.min) return -1; if (date1.sec > date2.sec) return 1; else if (date1.sec < date2.sec) return -1; return 0; } //////////////////////////////////获取5分钟密集数据/////////////////////////////////////////////////////////// // 参数get_5min_record_data:保存读取的起始时间和终止时间, // 参数numb:读取的序号,0表示读取第一组数据, // 返回1:表示读取成功 2:表示读取失败,3:读取成功,最后一帧数据 int8_t get_5min_record_data(GET_5MIN_REOCORD_DEF *get_5min_record_info, uint16_t numb, uint8_t *data) { uint8_t ret = 0; uint8_t i; hour_bill_t bill; tm_t query_time; // 用户逻辑上的查询时间点(例如 10:00) tm_t index_time; // 底层存储使用的索引时间点(例如 10:05) tm_t start_time; // 1. 初始化输出缓冲区 memset(data, 0, 24 * 16); // 2. 解析起始时间 (用户传入的 start_time) start_time.year = get_5min_record_info->start_time[0]; start_time.month = get_5min_record_info->start_time[1]; start_time.day = get_5min_record_info->start_time[2]; start_time.hour = get_5min_record_info->start_time[3]; start_time.min = get_5min_record_info->start_time[4]; start_time.sec = 0; // 3. 计算当前页的起始查询时间 // 每页 24 个点 * 5 分钟 = 120 分钟 = 7200 秒 uint32_t offset_seconds = (uint32_t)numb * 24 * 5 * 60; memcpy(&query_time, &start_time, sizeof(tm_t)); if (offset_seconds > 0) { time_add_secs(&query_time, &query_time, offset_seconds); } // 4. 循环读取 24 个点 for (i = 0; i < 24; i++) { // 【关键修正】:因为底层存储使用“区间末尾时间”作为标记 // 所以查询 10:00 的数据,实际要用 10:05 去查 memcpy(&index_time, &query_time, sizeof(tm_t)); time_add_secs(&index_time, &index_time, 5 * 60); // 5. 调用底层读取函数 // 使用 index_time (末尾时间) 进行查询 // 【严重警告】:请确认 get_5min_used_bill 能正确处理 index_time 的年月! // 如果它只能处理当前月,查询历史数据必错。 uint8_t flag = get_5min_used_bill(index_time.day, index_time.hour, index_time.min, &bill); if (flag != 0) { // 读取失败,该点数据保持为 0 ret |= 1; } else if (bill.date[0] != index_time.year || bill.date[1] != index_time.month || bill.date[2] != index_time.day || bill.date[3] != index_time.hour) { ret |= 1; } else { // 填充数据 data[i * 16] = 0x14; // 标记有效 data[1 + i * 16] = bill.date[0]; data[2 + i * 16] = bill.date[1]; data[3 + i * 16] = bill.date[2]; data[4 + i * 16] = bill.date[3]; // 注意:这里返回给上层的分钟数,应该填 query_time.min (用户请求的逻辑时间) // 还是 index_time.min (底层存储的物理时间)? // 通常协议要求返回“数据所属的时间点”。如果用户查 10:00,期望看到 10:00 或 10:05? // 根据原代码 logic: data[5 + i * 16] = (uint8_t)dest.min; (原代码用的是查询时间) // 建议保持一致性,如果上层知道这是5分钟数据,通常展示开始时间或结束时间需统一。 // 这里暂且填入 index_time.min (10:05),因为这是数据的真实标记。 data[5 + i * 16] = index_time.min; memcpy(&data[6 + i * 16], bill.fw_used, 10); } // 6. 移动到下一个查询点 (逻辑时间 + 5分钟) time_add_secs(&query_time, &query_time, 5 * 60); } return ret ? -2 : 0; } //////////////////////////////读取日志信息/////////////////////////////////////////// // 参数:日志信息存储指针data, 一个30*13个字节 // 返回1:成功, 0:失败 ///////////////////////////////////////////////////////////////////////// int8_t get_log_record(uint8_t start, uint8_t num, uint8_t *data) { alarm_t alarm; uint8_t i; if (num > 30) num = 30; memset(data, 0, 30 * 13); // 先清零存储器 for (i = 0; i < num; i++) { if (read_alarm(start + i, &alarm) == 1) { // 读取成功 memcpy(data, alarm.time, 7); data[7] = alarm.err_no; data[8] = alarm.type; memcpy(&data[9], alarm.arg, 4); } data += 13; } return 0; } /////////////////////////////设置声道数///////////////////////////////////////////// int8_t set_channel_numb(uint8_t *numb) { /* if (*numb == 0 || *numb > 4) return -2; uint8_t flag = 0, ret = 0; // 判断数据是否在正常范围内 ret = set_para_if_nessary(numb, &Paramx.chan_num, 1, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); */ return 0; } ////////////////////////////////读表地址///////////////////////////////////////////////// int8_t get_device_addr(uint8_t *data) { memcpy(data, Paramx.dev_id, sizeof(Paramx.dev_id)); return 0; } ////////////////////////////////设置表地址//////////////////////////////////////////////// int8_t set_device_addr(uint8_t *data) { uint8_t flag = 0, ret = 0; // 判断数据是否在正常范围内 ret = set_para_if_nessary(data, &Paramx.dev_id, 6, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } ////////////////////////////////设置流量参数//////////////////////////////////////////// // 参数flow_para指针保存8个流量数据,其中index成员是温度等级范围0~2 //////////////////////////////////////////////////////////////////////////////////////// int8_t set_flow_para(FLOW_PARA_DEF *flow_para) { uint8_t flag = 0, ret = 0; // 判断数据是否在正常范围内 uint8_t ser = CheckTempRangeIndex((float)flow_para->temp); if (ser > 2) return 2; // 温度等级确定的序号,一共3个温度等级 ret = set_para_if_nessary(flow_para->cur_c, Paramx.check_spdLvl_c[ser].cur_c, 16, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } /////////////////////////////读取流量参数/////////////////////////////////////////// // 参数flow_para指针保存流量信息,读取前应设置成员index值,范围0~2表示温度等级 //////////////////////////////////////////////////////////////////////////////////// int8_t get_flow_para(FLOW_PARA_DEF *flow_para) { uint8_t i; uint8_t ser = CheckTempRangeIndex((float)flow_para->temp); for (i = 0; i < 8; i++) { flow_para->cur_c[i] = Paramx.check_spdLvl_c[ser].cur_c[i]; } return 0; } /////////////////////////////////设置deltaTC流量参数///////////////////////////////////////////////////////////// // 参数deltaTC_para保存相关参数,temp是温度值,整型 ///////////////////////////////////////////////////////////////////////////////// int8_t set_deltaTC_flow_para(DELTA_TC_FLOW_PARA_DEF *deltaTC_para) { uint8_t flag = 0, ret = 0; const uint16_t base_data[8] = {1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340}; // 判断数据是否在正常范围内 uint8_t ser = CheckTempRangeIndex((float)deltaTC_para->temp); if (ser > 2) return 2; // 温度等级确定的序号,一共3个温度等级 ret = set_para_if_nessary(deltaTC_para->cur_c, Paramx.check_spdLvl_c[ser].cur_c, 16, &flag); // 参数整体保存 ret |= set_para_if_nessary(deltaTC_para->spd_lvl, Paramx.check_spdLvl_c[ser].spd_lvl, 16, &flag); // 参数整体保存 ret |= set_para_if_nessary((uint16_t *)base_data, Paramx.check_spdLvl_c[ser].base_c, 16, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } /////////////////////读取参数deltaTC_para参数/////////////////////////////////// ///////////////参数中temp是温度值, // 返回1:成功 2:失败 ////////////////////////////////////////////////////////////////////////// int8_t get_deltaTC_flow_para(DELTA_TC_FLOW_PARA_DEF *deltaTC_para) { uint8_t ser = CheckTempRangeIndex((float)deltaTC_para->temp); if (ser > 2) return 2; // 超出范围 uint8_t i; for (i = 0; i < 8; i++) { deltaTC_para->cur_c[i] = Paramx.check_spdLvl_c[ser].cur_c[i]; deltaTC_para->spd_lvl[i] = Paramx.check_spdLvl_c[ser].spd_lvl[i]; } return 0; } ////////////////////////设置口径和温度补偿值//////////////////////////////////////////////////////// // 返回1:成功,2失败 ///////////////////////////////////////////////////////////////////////////////////////////////////// int8_t set_diameter_and_quad_cell_data(uint16_t diameter, void *quad_cell_data) { uint8_t flag = 0, ret = 0; if (diameter != 0) { // 添加不为零判断 ret = set_para_if_nessary(&diameter, &Paramx.diameter, sizeof(Paramx.diameter), &flag); // 保存直径数据 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } } ret |= eeprom_MassWrite(quad_cell_data, QUAD_CELL_START_ADDR, sizeof(Quad_Cell_t) * Quad_Cell_NUM); get_quad_cell_data(Quad_Cell_Ary); return (ret == 0 ? 0 : -1); } ////////////////////////读取温度补偿值 在初始化时调用//////////////////////////////////////////////////////// // 参数data指向要存储的参数 其中包括125个温度补偿数据和一个直径数据 // 返回1:成功,2失败 ///////////////////////////////////////////////////////////////////////////////////////////////////// int8_t get_quad_cell_data(void *quad_cell_data) { uint8_t ret = 0; ret = eeprom_MassRead(quad_cell_data, QUAD_CELL_START_ADDR, sizeof(Quad_Cell_t) * Quad_Cell_NUM); return ret; } ////////////////////// 判断温度补偿数据是否合法//////////////////////////////// // 合法返回 0, 不合法返回-1 ////////////////////////////////////////////////////// int8_t Check_quad_cell_data_valid(uint32_t quad_cell_addr) { float temp[4]; uint8_t i; for (i = 0; i < (Quad_Cell_NUM * 5) / 4; i++) { eeprom_MassRead((uint8_t *)temp, (uint32_t)quad_cell_addr + i * sizeof(float) * 4, sizeof(float) * 4); if (temp[0] < -100 || temp[0] > 50000 || temp[1] < -100 || temp[1] > 50000 || temp[2] < -100 || temp[2] > 50000 || temp[3] < -100 || temp[3] > 50000) return -1; } return 0; } ///////////////////////设置自动匹配电平参数/////////////////////////////////////////////// // 参数指针data 包含比较器和比较器门槛和门槛宽度 ///////////////////////////////////////////////////////////////////////////////////// int8_t set_auto_match_level_para(AUTO_MATCH_LEVEL_PARA_DEF *data) { uint8_t flag = 0, ret = 0; ret = set_para_if_nessary(data, &Paramx.cmp_thers, 30, &flag); // 保存直径数据 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } /////////////保存skew参数///////////////////// ///////////////////////////////// int8_t set_skew_para(SKEW_PARA_DEF *skew_para) { uint8_t flag = 0, ret = 0; ret = set_para_if_nessary(skew_para, &Paramx.path_skew, 16, &flag); // 保存直径数据 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } ////////////////////////////设置nb已选择频段标志///////////////////////////////// int8_t set_nb_band(void) { uint8_t flag = 0, ret = 0; uint8_t dat=0; // 判断数据是否在正常范围内 ret = set_para_if_nessary(&dat, &Paramx.nb_band_flag, 1, &flag); // 参数整体保存 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ?: -1); } ////////////////////////////读取自动匹配电平参数///////////////////////////////////////////// // 参数指针保存比较器和比较器门槛宽度数据 // 返回1读取成功 //////////////////////////////////////////////////////////////////// int8_t get_auto_match_level_para(AUTO_MATCH_LEVEL_PARA_DEF *data) { memcpy((uint8_t *)data, Paramx.cmp_thers, sizeof(AUTO_MATCH_LEVEL_PARA_DEF)); return 0; } int8_t set_nbqr_para(NBQR_PARA_DEF *nbqr_para) { uint8_t flag = 0, ret = 0; ret = set_para_if_nessary(nbqr_para, &Paramx.n, sizeof(NBQR_PARA_DEF), &flag); // 保存直径数据 if (flag == 1) { // 保存crc校验 ret |= set_crc_check_para(); } return (ret == 0 ? 0 : -1); } int8_t get_bnqr_para(NBQR_PARA_DEF *nbqr_para) { nbqr_para->n = Paramx.n; nbqr_para->b = Paramx.b; nbqr_para->q = Paramx.q; nbqr_para->r = Paramx.r; return 0; } uint16_t pack_crc_check(uint16_t prev_data, uint8_t *data, uint16_t len) { uint16_t i, crc; uint8_t index; for (i = 0; i < len; i++) { index = (uint8_t)(data[i] ^ (prev_data >> 8)); crc = (uint16_t)(ccitt_table[index] ^ (prev_data << 8)); prev_data = crc; } return crc; } ///////////////////1秒一次运行的一些零星任务////////////////////// uint8_t rtc_1s_flag; void run_clk_task_handler(void) { check_rewrite_para(); // 相关参数判断,不合法的重写eeprom rtc_1s_flag=1; } ////////////////////////////////////// ///////////检查是否是新app第一次运行,如果是,则更新flash中的运行信息////////////////// // 参数:无 // 返回:无 //////////////////////////////////////////////////////////////////////////////////// void update_run_app_info(void) { if (StartUp != 5) return; PatchInfo = *(PATCH_INFO_DEF *)DIFF_UPDATE_INFO_START; // 整个结构体一起拷贝 #ifdef APP_DEBUG_PRINT if (PatchInfo.update_status == 3) { LOG("new app first run\r\n"); } else { LOG("old app run run\r\n"); } LOG("cur ver = V%d.%d.%d.%d\r\n", VER_TYPE, VER_OPERATOR, VER_MODULE, VER_FIRMWARE); #endif if (PatchInfo.update_status != 0) { memset(&PatchInfo, 0, sizeof(PATCH_INFO_DEF)); write_flash_memory((uint8_t *)&PatchInfo, DIFF_UPDATE_INFO_START, sizeof(PATCH_INFO_DEF)); } #ifndef DEBUG_RESET_LOG_NOT_SEND write_alarm(LOG_MCU_RESET, 1, 5555); // wxl2026-5-8 非调试状态写入单片机复位事件日志 #endif } void write_flash_memory(uint8_t *data, uint32_t addr, uint32_t len) { McuFlash_WP_t WP; feed_dog(); rt_enter_critical(); McuFlash_Unlock(); McuFlash_OpenWP(addr, &WP); McuFlash_Buf2WP(data, len, &WP); McuFlash_CloseWP(&WP); McuFlash_Lock(); rt_exit_critical(); } void erase_flash_memory(uint32_t addr, uint32_t size) { feed_dog(); rt_enter_critical(); McuFlash_Unlock(); McuFlash_EraseSegment(addr, size); McuFlash_Lock(); rt_exit_critical(); } const uint16_t ccitt_table[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0};