/* includes ----------------------------------------------------------*/ #include "global.h" /* typedef -----------------------------------------------------------*/ /* define ------------------------------------------------------------*/ /* macro -------------------------------------------------------------*/ /* variables ---------------------------------------------------------*/ global_par s_global_par; /* function prototypes -----------------------------------------------*/ /** * @brief 绑定车辆比对 * @param NONE * @note NONE * @retval 绑定车辆比对,查看车辆VIN是否相同,若相同则不做处理,更换车辆则备份新的车辆数据 */ void VehicleCompare(void){ param_boot *p_param_boot = &s_param_boot; rs485RecDate *p_rs485RecDate = &s_rs485RecDate; global_par *p_global_par = &s_global_par; if(s_comData.vinRecSuccess == 1) { // 成功获取到车辆数据 // 检查指针是否有效 if(p_param_boot != NULL && p_rs485RecDate != NULL && p_global_par != NULL) { // 只有在VIN码不同时才执行更新操作 if(memcmp(p_param_boot->vin, p_rs485RecDate->VIN, 26) != 0) { // 比对不成功 // 备份新的VIN码 memcpy(p_param_boot->vin, p_rs485RecDate->VIN, 26); // 备份新的车辆数据 p_param_boot->carTotalMileage = ((uint32_t)s_rs485RecDate.accTotalMileage_h << 16) | s_rs485RecDate.accTotalMileage_l; // 写入参数区 Write_paramArea(); // 置位中控板迁移事件 BIT_SET(p_global_par->ctrEvent, CtrlPanelMigration); // 更新围栏数据 p_global_par->fence_update = 1; } } } } /** * @brief 中控板初始化 * @param p_param_boot : 参数区指针 * @param p_rs485RecDate:485接收数据区的指针 * @note NONE * @retval 无 */ void centralCtrSys_Init(param_boot *p_param_boot){ uint8_t recBack = 0; rs485RecDate *p_rs485RecDate = &s_rs485RecDate; // 1. 从flash中读出存储参数 Read_ParamArea(); // 读出参数区的所有数据 // 3. 从备份数据中拿出初始VIN memcpy(p_rs485RecDate->VIN, p_param_boot->vin, 26); s_comData.vinRecSuccess = 1; // 4. 初始化4g模块 4g模块在这里不好处理 放在任务中,围栏数据也一样 // 5. 读取96位(或者说是3个32位的字)的唯一ID GetUniqueID(s_messageDate.devId); // 6. 陀螺仪的初始化 recBack = atk_ms6050_init(); if(recBack != 0){ // printf("ATK-MS6050 init failed!\r\n"); BIT_SET(s_comData.Malfunction, gyroscope); // 置位陀螺仪数据异常故障 } // printf("ATK-MS6050 init\r\n"); recBack = atk_ms6050_dmp_init(); if(recBack != 0){ // printf("ATK-MS6050 DMP init failed!\r\n"); BIT_SET(s_comData.Malfunction, gyroscope); // 置位陀螺仪数据异常故障 } // printf("ATK-MS6050 DMP init!\r\n"); // 7. 外部flash初始化 norflash_init(); } // 车本体故障判断 void fault_car(rs485RecDate *p_rs485RecDate, comData *p_comData){ uint16_t errorLevel = 0; /* 尝试获取互斥量,等待无限长时间 */ if(osMutexAcquire(mutex_rs485RecDateHandle, 1000) == osOK) { errorLevel = p_rs485RecDate->alarmLevel; /* 访问完成,释放互斥量 */ osMutexRelease(mutex_rs485RecDateHandle); } // 存在车故障 if(errorLevel != 0){ BIT_SET(s_comData.Malfunction, carError); // 置位车故障 }else{ BIT_CLEAR(s_comData.Malfunction, carError); // 清除车故障 } } /** * @brief 中控板故障检测 * @param p_global_par:global_par的全局参数指针 * @note 周期为1s1次 清除除电池故障的其他故障 * @retval 无 */ void faultDetection(global_par *p_global_par){ // 参数定义 static uint16_t timesCnt_gps = 0; // gps记时 static uint8_t errorCnt_gyro = 0; // 陀螺仪数据故障滤波次数 static uint16_t timesOut_ota = 0; // OTA超时计数 static uint16_t timesCnt_runFence; // 超运行围栏计数 static uint16_t timesCnt_outSpeed; // 超速计时器 comData *p_comData = &s_comData; param_boot *p_param_boot = &s_param_boot; // 开启超级权限 不判断所有故障 if(p_global_par->superUser == 1){ p_comData->Malfunction = 0; // 清除所有故障 return; } // 1. 车本体故障判断 fault_car(&s_rs485RecDate, &s_comData); // 车故障判断 // 2. 超出围栏故障判断 if(p_comData->fenceStatus == 1){ if(timesCnt_runFence < p_param_boot->fenceBreach_Timeout){ timesCnt_runFence++; }else{ timesCnt_runFence = p_param_boot->fenceBreach_Timeout; BIT_SET(s_comData.Malfunction, fence); } }else{ timesCnt_runFence = 0; BIT_CLEAR(s_comData.Malfunction, fence); } // 3. GPS信号丢失 // 获取gps失败,但陀螺仪数据正常的情况下,一分钟之后置位gps故障 // 获取gps失败,陀螺仪数据异常,直接置位gps故障 if((p_global_par->positionErrorCnt >= 3) && (BIT_CHECK(s_comData.Malfunction, fence) == 0)){ if(timesCnt_gps < FILTER_TIME_GPS){ // 4分钟滤波时间 timesCnt_gps++; }else{ timesCnt_gps = FILTER_TIME_GPS; BIT_SET(s_comData.Malfunction, positionError); } }else if((p_global_par->positionErrorCnt >= 3) && (BIT_CHECK(s_comData.Malfunction, fence) == 1)){ timesCnt_gps = 0; BIT_SET(s_comData.Malfunction, positionError); }else{ timesCnt_gps = 0; } if(p_global_par->positionErrorCnt == 0){ // 清除gps故障 BIT_CLEAR(s_comData.Malfunction, positionError); } // 4. MQTT通信异常 if(p_global_par->mqttTimeoutCnt >= PUBLISH_TIME_MS){ BIT_SET(s_comData.Malfunction, errorMqtt); // 置位MQTT故障 } if(p_global_par->mqttTimeoutCnt == 0){ // 清除MQTT故障 BIT_CLEAR(s_comData.Malfunction, errorMqtt); } // 5. 4G模块初始化故障 if(p_global_par->InitFaultFlag_4G == 1){ BIT_SET(s_comData.Malfunction, init_4G_error); // 置位4g模块初始化故障 }else{ BIT_CLEAR(s_comData.Malfunction, init_4G_error); // 清除4g模块初始化故障 } // 6. 485通信故障 if(p_global_par->timeoutCnt_485 >= TIMEOUT_485){ BIT_SET(s_comData.Malfunction, com485); // 置位485通信故障 } if(p_global_par->timeoutCnt_485 == 0){ BIT_CLEAR(s_comData.Malfunction, com485); // 置位485通信故障 } // 7. 陀螺仪数据故障-- 获取的姿态角数据全部为0 if(p_global_par->gyroDataFaultFlag == 1){ if(errorCnt_gyro < FILTER_TIME_gyro){ errorCnt_gyro++; }else{ errorCnt_gyro = FILTER_TIME_gyro; BIT_SET(s_comData.Malfunction, gyroscope); // 置位陀螺仪数据异常故障 } }else{ errorCnt_gyro = 0; BIT_CLEAR(s_comData.Malfunction, gyroscope); } // 8. 超速故障 if(s_rs485RecDate.vehicleSpeed > s_param_boot.speed_limit){ if(timesCnt_outSpeed < s_param_boot.overspeed_Timeout){ timesCnt_outSpeed++; }else{ timesCnt_outSpeed = s_param_boot.overspeed_Timeout; BIT_SET(s_comData.Malfunction, outSpeed); // 置位超速故障 } }else{ timesCnt_outSpeed = 0; BIT_CLEAR(s_comData.Malfunction, outSpeed); // 清零超速故障 } // 9. 升级故障--开始升级后,开始记时,2分钟还未升级成功的话,置位升级故障, // 退出升级,此次升级失败,旧版本运行,清除升级状态变量、标志、数据,重启释放 // OTA开始后任务周期为200ms if(p_global_par->otaUpgradeStartFlag == 1){ if(timesOut_ota < TIME_OUT_OTA){ timesOut_ota++; }else{ timesOut_ota = TIME_OUT_OTA; BIT_SET(s_comData.Malfunction, OTA_fault); // 置位OTA异常故障 } } // 10. 订阅主题失败 if(p_global_par->subscribe_fail == 1){ BIT_SET(s_comData.Malfunction, subscribeFail); // 置位订阅主题失败故障 }else{ BIT_CLEAR(s_comData.Malfunction, subscribeFail); // 清除订阅主题失败故障 } // 11. 发布消息失败 if((p_global_par->publish_fail == 1) || (s_global_par.reconnect_server_flag == 1)){ BIT_SET(s_comData.Malfunction, publishFail); // 置位发布消息失败故障 }else{ BIT_CLEAR(s_comData.Malfunction, publishFail); // 清除发布消息失败故障 } // 12. 获取定位信息失败 if(p_global_par->get_location_error == 1){ BIT_SET(s_comData.Malfunction, getLocationError); // 置位获取定位信息失败故障 }else{ BIT_CLEAR(s_comData.Malfunction, getLocationError); // 清除获取定位信息失败故障 } } /** * @brief 控制蜂鸣器响停 * @param count 当前计数 * @param on_threshold 响的阈值 * @param off_threshold 停的阈值 * @retval 无 */ void beep_control(uint8_t count, uint8_t on_threshold, uint8_t off_threshold) { if (count < on_threshold) { GPIO_BEEP(GPIO_PIN_SET); } else if (count < off_threshold) { GPIO_BEEP(GPIO_PIN_RESET); } } /** * @brief 控制蜂鸣器响应 * @note 根据不同条件控制蜂鸣器的响停 进入周期为100ms * @param 无 * @retval 无 */ void control_beep_response(void) { global_par *p_global_par = &s_global_par; // 蜂鸣器计数器 static uint8_t beep_count_fence = 0; // static uint8_t beep_count_fault = 0; static uint8_t beep_count_ota = 0; // 超出围栏 500ms频率响应 if (BIT_CHECK(s_comData.Malfunction, fence)) { beep_control(beep_count_fence, 5, 10); beep_count_fence = (beep_count_fence >= 10) ? 0 : beep_count_fence + 1; } // OTA升级 2s的响应频率 else if (p_global_par->otaUpgradeStartFlag == 1) { beep_control(beep_count_ota, 20, 40); beep_count_ota = (beep_count_ota >= 40) ? 0 : beep_count_ota + 1; } // 不存在蜂鸣器响应,蜂鸣器应关闭 else{ GPIO_BEEP(GPIO_PIN_RESET); } } // 格式转换函数 void formatDateTimeAndMalfunction(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t sec, uint32_t malfunction, char *output) { // 确保提供的output数组足够大 // 格式化字符串为 YYYYMMDD-HHMM:0xXXXXXXXX 格式 sprintf(output, "%04u%02u%02u-%02u%02u%02u:0x%08X,", year, month, day, hour, minute, sec, malfunction); output[29] = '\0'; // 确保字符串以空字符结尾 } /** * @brief 历史故障存储 * @note 此函数1s1次 * @param 无 * @retval 无 */ void storeFaultRecord(void) { uint16_t id = 0; char storeFaultData[30] = {0}; static uint32_t fault_log = 0; // 用来同当前故障数据对比,看是否产生故障变化 static uint16_t timeCnt_delay = 0; // 存在故障 id = norflash_read_id(); /* 读取FLASH ID */ if((id == 0) || (id == 0XFFFF)) { // 检测不到FLASH芯片 return; } // 检查是否存在故障 if (s_comData.Malfunction == 0) { return; // 如果没有故障,直接返回 } // 检查是否产生故障变化或者到达计时时间 if((fault_log == s_comData.Malfunction) && (timeCnt_delay < FAULT_RECORD_TIME)){ timeCnt_delay++; return; // 如果没有故障变化或者未到达计时时间,直接返回 }else{ // 更新计数值和故障log timeCnt_delay = 0; fault_log = s_comData.Malfunction; } // 存储格式转换成 这样的20240513-172203:0x0000000f,字符串数据 formatDateTimeAndMalfunction(s_nmea_utc_time.year,s_nmea_utc_time.month,s_nmea_utc_time.date, s_nmea_utc_time.hour,s_nmea_utc_time.min,s_nmea_utc_time.sec,s_comData.Malfunction, storeFaultData); // 写入数据 norflash_write((uint8_t *)storeFaultData, s_param_boot.nextFaultAddr, strlen(storeFaultData)); // 更新索引 s_param_boot.faultRecordIndex = (s_param_boot.faultRecordIndex < MAX_RECORDS) ? (s_param_boot.faultRecordIndex + 1) : 0 ; // 更新地址 s_param_boot.nextFaultAddr += strlen(storeFaultData); if(s_param_boot.faultRecordIndex == 0){ s_param_boot.nextFaultAddr = 0; } // 保存最新的索引和地址数据 Write_paramArea(); } /** * @brief 更新保存的时间信息 * @note 此函数1s1次 * @param 无 * @retval 无 */ void refreshSavedTime(void){ static uint8_t timesCnt = 0; // 未获取到时间数据,则不保存 if(s_global_par.time_stamp_flag != 1){ return; } s_param_boot.year = s_nmea_utc_time.year; s_param_boot.month = s_nmea_utc_time.month; s_param_boot.date = s_nmea_utc_time.date; s_param_boot.hour = s_nmea_utc_time.hour; s_param_boot.min = s_nmea_utc_time.min; s_param_boot.sec = s_nmea_utc_time.sec; // 10s保存一次时间数据,发生故障时保存一次,固件更新时保存一次 if(timesCnt < times_10s){ timesCnt++; }else{ timesCnt = 0; Write_paramArea(); } } // 车辆行驶里程计算 // 参数:day // 参数2:累积行驶总里程 485下发的总里程 void calculate_driving_distance(uint32_t totalMileage){ param_boot *p_param_boot = &s_param_boot; // 问询的车辆总里程没有备份的大,则不做操作 if(totalMileage <= p_param_boot->carTotalMileage){ return; } // 1. 计算当日行驶里程 p_param_boot->dailyMileage += totalMileage - p_param_boot->carTotalMileage; // 2. 备份总里程信息 p_param_boot->carTotalMileage = totalMileage; } // 车辆行驶时长计算 // 参数1 :单次行驶时长 // 参数2 :当日行驶时长 // 参数3 :累积行驶时长 // 周期为1s1次 void calculateDriveTime(void){ static uint16_t run_sec = 0; param_boot *p_param_boot = &s_param_boot; // 数据累加 按s累加 run_sec++; p_param_boot->dailyDriveTime++; p_param_boot->totalDriveTime++; // 计算本次行驶时长 int hours = (run_sec % 86400) / 3600; // 计算剩余秒数对应的小时数 int minutes = (run_sec % 3600) / 60; // 计算剩余秒数对应的分钟数 s_rs485RecDate.runTime = (uint16_t)hours << 8 | minutes; } // 车辆状态参数数据迁移vehicle state data migration void vehStateDataMig(uint8_t timeDate){ param_boot *p_param_boot = &s_param_boot; rs485RecDate *p_rs485RecDate = &s_rs485RecDate; // 判断没有在当日 跨天则当日行驶里程清零 if(timeDate != p_param_boot->dailyDate){ p_param_boot->dailyDriveTime = 0; p_param_boot->dailyMileage = 0; p_param_boot->dailyDate = timeDate; // 更新时间信息 } p_rs485RecDate->dailyDrivMileage = p_param_boot->dailyMileage * 10; // 单位转换 int hours = (p_param_boot->dailyDriveTime % 86400) / 3600; // 计算剩余秒数对应的小时数 int minutes = (p_param_boot->dailyDriveTime % 3600) / 60; // 计算剩余秒数对应的分钟数 p_rs485RecDate->dailyDrivTime = (uint16_t)hours << 8 | minutes; int days = p_param_boot->totalDriveTime / 86400; // 计算天数 hours = (p_param_boot->totalDriveTime % 86400) / 3600; // 计算剩余秒数对应的小时数 minutes = (p_param_boot->totalDriveTime % 3600) / 60; // 计算剩余秒数对应的分钟数 p_rs485RecDate->accTotalDrivTime_day = days; p_rs485RecDate->accTotalDrivTime_h_min = (uint16_t)hours << 8 | minutes; } // 测试使用 void fun(void){ static uint8_t cnt = 0; uint32_t accTotalMileage = 0; accTotalMileage = (uint32_t)s_rs485RecDate.accTotalMileage_h << 16 | s_rs485RecDate.accTotalMileage_l; if(cnt < times_5s){ cnt++; return; }else{ cnt = 0; accTotalMileage++; } s_rs485RecDate.accTotalMileage_h = accTotalMileage >> 16; s_rs485RecDate.accTotalMileage_l = accTotalMileage & 0x0000FFFF; // 使用位与操作符 }