freertos.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052
  1. /* USER CODE BEGIN Header */
  2. /**
  3. ******************************************************************************
  4. * File Name : freertos.c
  5. * Description : Code for freertos applications
  6. ******************************************************************************
  7. * @attention
  8. *
  9. * Copyright (c) 2024 STMicroelectronics.
  10. * All rights reserved.
  11. *
  12. * This software is licensed under terms that can be found in the LICENSE file
  13. * in the root directory of this software component.
  14. * If no LICENSE file comes with this software, it is provided AS-IS.
  15. *
  16. ******************************************************************************
  17. */
  18. /* USER CODE END Header */
  19. /* Includes ------------------------------------------------------------------*/
  20. #include "FreeRTOS.h"
  21. #include "task.h"
  22. #include "main.h"
  23. #include "cmsis_os.h"
  24. /* Private includes ----------------------------------------------------------*/
  25. /* USER CODE BEGIN Includes */
  26. #include "semphr.h"
  27. #include "i2c.h"
  28. #include "can.h"
  29. #include "usart.h"
  30. #include "gpio.h"
  31. #include "rtc.h"
  32. #include "iwdg.h"
  33. /* USER CODE END Includes */
  34. /* Private typedef -----------------------------------------------------------*/
  35. /* USER CODE BEGIN PTD */
  36. /* USER CODE END PTD */
  37. /* Private define ------------------------------------------------------------*/
  38. /* USER CODE BEGIN PD */
  39. #define MAX_RETRY_COUNT 3
  40. #define ROWS 24 // 温度点矩阵的行数
  41. #define COLS 32 // 温度点矩阵的列数
  42. #define MAX_CAN_MSG_LEN 8
  43. #define NUM_TEMP_DATA 18 // 9 个温度点,每个点两条数据
  44. volatile uint8_t Ambient_temperature;
  45. volatile uint8_t systemlevel = 0;
  46. volatile uint8_t dataReadyFlag = 0;// 标志位或状态
  47. uint8_t canMsgBuffer1[MAX_CAN_MSG_LEN] = {0}; // 第一个 CAN 消息缓冲区
  48. uint8_t canMsgBuffer2[MAX_CAN_MSG_LEN] = {0}; // 第二个 CAN 消息缓冲区
  49. uint8_t summaryBuffer[MAX_CAN_MSG_LEN] = {0}; // 存放最大值、最小值和平均值的缓冲区
  50. uint8_t canMsgBuffer1_tmp[MAX_CAN_MSG_LEN] = {0}; // 第一个 CAN 消息缓冲区
  51. uint8_t canMsgBuffer2_tmp[MAX_CAN_MSG_LEN] = {0}; // 第二个 CAN 消息缓冲区
  52. uint8_t summaryBuffer_tmp[MAX_CAN_MSG_LEN] = {0}; // 存放最大值、最小值和平均值的缓冲区
  53. uint8_t debugBuffer1_tmp[MAX_CAN_MSG_LEN] = {0}; // 包含预警等级加温度的全部数据缓冲区
  54. uint8_t debugBuffer2_tmp[MAX_CAN_MSG_LEN] = {0}; // 包含预警等级加温度的全部数据缓冲区
  55. #define DATA_MOVING (0)
  56. #define DATA_READY (1)
  57. #define STOP2_READY (2)
  58. uint8_t data_Flag = DATA_MOVING;
  59. volatile bool taskEnabled = true; // 任务状态,初始为启动状态
  60. /**
  61. * @breaf 内部flash参数定义
  62. */
  63. #define FLASH_LAST_PAGE_ADDR 0x0801FC00 // STM32L431CCT6 内部Flash第127页起始地址
  64. #define FLASH_SECOND_LAST_PAGE_ADDR 0x08018800 // STM32L431CCT6 内部Flash第126页起始地址
  65. uint16_t NODE_ID; // 初始节点ID 01 //温度传感器ID从 16 开始,16 代表 01 号小板
  66. FlashData flash_data = {
  67. .first_threshold = 60, // 一级阈值 60
  68. .second_threshold = 100, // 二级阈值 100
  69. .delay_receivedValue = 1, // 初始上报频率 2s
  70. .Emissivity = 95, // 初始发射率 0.95(后续除100)
  71. };
  72. extern uint16_t eeMLX90640[832];
  73. extern int status;
  74. bool MLX_powered = false;
  75. int failCount = 0; // 新增失败计数器
  76. extern uint32_t start_tick;
  77. uint32_t end_tick = 0;
  78. /* USER CODE END PD */
  79. /* Private macro -------------------------------------------------------------*/
  80. /* USER CODE BEGIN PM */
  81. volatile uint8_t stop2WakeupFlag = 0;// STOP2 唤醒标志
  82. void Configure_GPIOs_For_LowPower(void);
  83. void Enter_LPStop2Mode(void);
  84. void Wakeup_LPStop2Mode(void);
  85. void Enable_RTC_Wakeup(uint16_t delay_time);
  86. void control_MLX90640_power(bool enable) ;
  87. void printCurrentRTCTime(void);
  88. /* USER CODE END PM */
  89. /* Private variables ---------------------------------------------------------*/
  90. /* USER CODE BEGIN Variables */
  91. extern paramsMLX90640 mlx90640;
  92. extern float mlx90640To[768];
  93. extern uint16_t frame[834];
  94. extern uint16_t blockNumber;
  95. /* USER CODE END Variables */
  96. /* Definitions for readTempTask */
  97. osThreadId_t readTempTaskHandle;
  98. const osThreadAttr_t readTempTask_attributes = {
  99. .name = "readTempTask",
  100. .stack_size = 256 * 4,
  101. .priority = (osPriority_t) osPriorityNormal,
  102. };
  103. /* Definitions for sendTempTask */
  104. osThreadId_t sendTempTaskHandle;
  105. const osThreadAttr_t sendTempTask_attributes = {
  106. .name = "sendTempTask",
  107. .stack_size = 256 * 4,
  108. .priority = (osPriority_t) osPriorityHigh,
  109. };
  110. /* Definitions for Common */
  111. osThreadId_t CommonHandle;
  112. const osThreadAttr_t Common_attributes = {
  113. .name = "Common",
  114. .stack_size = 128 * 4,
  115. .priority = (osPriority_t) osPriorityRealtime,
  116. };
  117. /* Definitions for dataMutex */
  118. osMutexId_t dataMutexHandle;
  119. const osMutexAttr_t dataMutex_attributes = {
  120. .name = "dataMutex"
  121. };
  122. /* Definitions for dataReadySem */
  123. osSemaphoreId_t dataReadySemHandle;
  124. const osSemaphoreAttr_t dataReadySem_attributes = {
  125. .name = "dataReadySem"
  126. };
  127. /* Private function prototypes -----------------------------------------------*/
  128. /* USER CODE BEGIN FunctionPrototypes */
  129. void lowpower_wakeup(void);
  130. void lowpower_sleep(void);
  131. void CAN_ErrorHandler(TickType_t *pxPreviousWakeTime, uint32_t *send_interval);
  132. void CAN_TemperatureSend(uint8_t *sequence, uint32_t *send_interval);
  133. int MLX90640_ReadFrameData(void);
  134. int ProcessTemperatureData(void);
  135. void Read_NODE_ID_From_Flash(void);
  136. void Read_parameter_From_Flash(void);
  137. void Save_NODEID_To_Flash(uint8_t NODE_ID) ;
  138. void Save_parameter_To_Flash(uint8_t first_threshold ,uint8_t second_threshold ,uint8_t delay_receivedValue ,uint8_t Emissivity);
  139. void CAN_Send_Msg(uint8_t *msg,uint8_t len,uint32_t node_id, uint8_t sequence, uint8_t slice_num, uint8_t slice_id);
  140. void CAN_Send_extId(uint16_t *data, uint8_t len, uint32_t node_id, uint8_t sequence, uint8_t slice_num, uint8_t slice_id);
  141. void CAN_Send_Parameter(uint32_t node_id, uint8_t len, FlashData *flash_data, uint8_t sequence, uint8_t slice_num, uint8_t slice_id) ;
  142. /* USER CODE END FunctionPrototypes */
  143. void ReadTemperatureTask(void *argument);
  144. void SendTemperatureTask(void *argument);
  145. void CommonTask(void *argument);
  146. void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
  147. /**
  148. * @brief FreeRTOS initialization
  149. * @param None
  150. * @retval None
  151. */
  152. void MX_FREERTOS_Init(void) {
  153. /* USER CODE BEGIN Init */
  154. Read_parameter_From_Flash();
  155. Read_NODE_ID_From_Flash();
  156. /* USER CODE END Init */
  157. /* Create the mutex(es) */
  158. /* creation of dataMutex */
  159. dataMutexHandle = osMutexNew(&dataMutex_attributes);
  160. /* USER CODE BEGIN RTOS_MUTEX */
  161. /* add mutexes, ... */
  162. /* USER CODE END RTOS_MUTEX */
  163. /* Create the semaphores(s) */
  164. /* creation of dataReadySem */
  165. dataReadySemHandle = osSemaphoreNew(1, 1, &dataReadySem_attributes);
  166. /* USER CODE BEGIN RTOS_SEMAPHORES */
  167. /* add semaphores, ... */
  168. /* USER CODE END RTOS_SEMAPHORES */
  169. /* USER CODE BEGIN RTOS_TIMERS */
  170. /* start timers, add new ones, ... */
  171. /* USER CODE END RTOS_TIMERS */
  172. /* USER CODE BEGIN RTOS_QUEUES */
  173. /* add queues, ... */
  174. /* USER CODE END RTOS_QUEUES */
  175. /* Create the thread(s) */
  176. /* creation of readTempTask */
  177. readTempTaskHandle = osThreadNew(ReadTemperatureTask, NULL, &readTempTask_attributes);
  178. /* creation of sendTempTask */
  179. sendTempTaskHandle = osThreadNew(SendTemperatureTask, NULL, &sendTempTask_attributes);
  180. /* creation of Common */
  181. CommonHandle = osThreadNew(CommonTask, NULL, &Common_attributes);
  182. /* USER CODE BEGIN RTOS_THREADS */
  183. /* add threads, ... */
  184. /* USER CODE END RTOS_THREADS */
  185. /* USER CODE BEGIN RTOS_EVENTS */
  186. /* add events, ... */
  187. /* USER CODE END RTOS_EVENTS */
  188. }
  189. /* USER CODE BEGIN Header_ReadTemperatureTask */
  190. /**
  191. * @brief Function implementing the readTempTask thread.
  192. * @param argument: Not used
  193. * @retval None
  194. */
  195. /* USER CODE END Header_ReadTemperatureTask */
  196. void ReadTemperatureTask(void *argument)
  197. {
  198. /* USER CODE BEGIN ReadTemperatureTask */
  199. TickType_t pxPreviousWakeTime = xTaskGetTickCount();
  200. uint8_t sequence =0;
  201. uint32_t send_interval = 0;
  202. for(;;)
  203. {
  204. // 读取并校验有效帧
  205. if (MLX90640_ReadFrameData())
  206. {
  207. // control_MLX90640_power(false);
  208. int validData = ProcessTemperatureData();
  209. if (validData == 1)
  210. {
  211. // 1. CAN 错误处理
  212. CAN_ErrorHandler(&pxPreviousWakeTime, &send_interval);
  213. // 如果仍然处于 Bus-Off 等待状态,就跳过发送流程
  214. if (hcan1.Instance->ESR & CAN_ESR_BOFF)
  215. {
  216. continue;
  217. }
  218. CAN_TemperatureSend(&sequence, &send_interval);
  219. send_interval++;
  220. Enter_LPStop2Mode();
  221. }
  222. }
  223. osDelay(10);
  224. }
  225. /* USER CODE END ReadTemperatureTask */
  226. }
  227. /* USER CODE BEGIN Header_SendTemperatureTask */
  228. /**
  229. * @brief Function implementing the sendTempTask thread.
  230. * @param argument: Not used
  231. * @retval None
  232. */
  233. /* USER CODE END Header_SendTemperatureTask */
  234. void SendTemperatureTask(void *argument)
  235. {
  236. /* USER CODE BEGIN SendTemperatureTask */
  237. // TickType_t pxPreviousWakeTime = xTaskGetTickCount();
  238. //
  239. // uint8_t sequence =0;
  240. // uint32_t send_interval = 0;
  241. for (;;)
  242. {
  243. // // 1. CAN 错误处理
  244. // CAN_ErrorHandler(&pxPreviousWakeTime, &send_interval);
  245. // // 如果仍然处于 Bus-Off 等待状态,就跳过发送流程
  246. // if (hcan1.Instance->ESR & CAN_ESR_BOFF)
  247. // {
  248. // continue;
  249. // }
  250. osDelay( 100 );
  251. }
  252. /* USER CODE END SendTemperatureTask */
  253. }
  254. /* USER CODE BEGIN Header_CommonTask */
  255. /**
  256. * @brief Function implementing the Common thread.
  257. * @param argument: Not used
  258. * @retval None
  259. */
  260. /* USER CODE END Header_CommonTask */
  261. void CommonTask(void *argument)
  262. {
  263. /* USER CODE BEGIN CommonTask */
  264. /* Infinite loop */
  265. // uint16_t counter = 0; // LED 闪烁计数器
  266. // uint16_t led_flash_threshold = 50; // 默认慢闪
  267. for(;;)
  268. {
  269. osDelay(10);
  270. // counter++;
  271. // // 1. 获取当前 CAN 错误状态
  272. // uint32_t can_esr = hcan1.Instance->ESR;
  273. // uint8_t tec = (can_esr >> 16) & 0xFF;
  274. // uint8_t rec = (can_esr >> 24) & 0xFF;
  275. // // 2. 判断当前状态并设置 LED 闪烁速度
  276. // if (can_esr & CAN_ESR_BOFF)
  277. // {
  278. // // Bus-Off 快闪 50ms (10ms * 5)
  279. // led_flash_threshold = 5;
  280. // }
  281. // else if (tec >= 128 || rec >= 128)
  282. // {
  283. // // Error Passive 中闪 200ms (10ms * 20)
  284. // led_flash_threshold = 20;
  285. // }
  286. // else
  287. // {
  288. // // 正常 慢闪 500ms (10ms * 50)
  289. // led_flash_threshold = 50;
  290. // }
  291. // // 3. 按照不同状态执行 LED 闪烁
  292. // if (counter >= led_flash_threshold)
  293. // {
  294. // HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4); // 切换 LED 状态
  295. // counter = 0; // 重置计数器
  296. // }
  297. // // 4. 喂狗
  298. // HAL_IWDG_Refresh(&hiwdg);
  299. }
  300. /* USER CODE END CommonTask */
  301. }
  302. /* Private application code --------------------------------------------------*/
  303. /* USER CODE BEGIN Application */
  304. void lowpower_wakeup(void)
  305. {
  306. DEBUG_PRINTF("1\r\n");
  307. HAL_I2C_DeInit(&hi2c1);
  308. MX_I2C1_Init();
  309. HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9|GPIO_PIN_10, GPIO_PIN_SET);
  310. control_MLX90640_power(true);
  311. HAL_Delay(50);
  312. MLX90640_SetRefreshRate(MLX90640_ADDR, RefreshRate8HZ); // 设置帧率�??0-7对应0.5,1,2,4,8,16,32,64HZ�??
  313. status = MLX90640_DumpEE(MLX90640_ADDR, eeMLX90640); // 读取像素校正参数
  314. status = MLX90640_ExtractParameters(eeMLX90640, &mlx90640); // 解析校正参数
  315. }
  316. void lowpower_sleep(void)
  317. {
  318. control_MLX90640_power(false);
  319. Configure_GPIOs_For_LowPower();
  320. HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9|GPIO_PIN_10, GPIO_PIN_RESET);
  321. }
  322. // CAN 错误状态处理函数
  323. void CAN_ErrorHandler(TickType_t *pxPreviousWakeTime, uint32_t *send_interval)
  324. {
  325. static uint32_t last_busoff_time = 0; // 上次 Bus-Off 恢复时间
  326. static uint8_t bus_off_flag = 0; // Bus-Off 状态标志
  327. uint32_t can_esr = hcan1.Instance->ESR;
  328. uint8_t tec = (can_esr >> 16) & 0xFF;
  329. uint8_t rec = (can_esr >> 24) & 0xFF;
  330. // 1. Bus-Off 状态处理
  331. if (can_esr & CAN_ESR_BOFF)
  332. {
  333. if (bus_off_flag == 0)
  334. {
  335. DEBUG_PRINTF(" [CAN ERROR] Bus-Off detected! TEC = %d, REC = %d\n", tec, rec);
  336. bus_off_flag = 1;
  337. last_busoff_time = HAL_GetTick();
  338. }
  339. // 等待1秒后重启 CAN
  340. if (HAL_GetTick() - last_busoff_time > 1000)
  341. {
  342. DEBUG_PRINTF("[CAN] Restarting CAN after Bus-Off...\n");
  343. HAL_CAN_Stop(&hcan1);
  344. HAL_CAN_DeInit(&hcan1);
  345. MX_CAN1_Init();
  346. HAL_CAN_Start(&hcan1);
  347. // 激活中断
  348. HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_BUSOFF);
  349. DEBUG_PRINTF("[CAN] CAN restarted after Bus-Off\n");
  350. bus_off_flag = 0;
  351. }
  352. // 跳过本次发送,等待恢复
  353. vTaskDelayUntil(pxPreviousWakeTime, pdMS_TO_TICKS(10));
  354. (*send_interval)++;
  355. }
  356. }
  357. // CAN 温度数据发送函数
  358. void CAN_TemperatureSend(uint8_t *sequence, uint32_t *send_interval)
  359. {
  360. // if (*send_interval >= 100 * flash_data.delay_receivedValue)
  361. // {
  362. CAN_Send_Msg(canMsgBuffer1, 8, NODE_ID, *sequence, 2, 1);
  363. CAN_Send_Msg(canMsgBuffer2, 8, NODE_ID, *sequence, 2, 2);
  364. control_MLX90640_power(false);
  365. (*sequence)++;
  366. if (*sequence > 0xFF)
  367. {
  368. *sequence = 0;
  369. }
  370. *send_interval = 0;
  371. // DEBUG_PRINTF("Send Temperature Data - Sequence: %d\n", *sequence);
  372. // printCurrentRTCTime();
  373. // }
  374. }
  375. // MLX90640读取温度函数
  376. int MLX90640_ReadFrameData(void)
  377. {
  378. int validFrame = 0;
  379. while (!validFrame)
  380. {
  381. DEBUG_PRINTF("[MLX] Start reading frame data\r\n");
  382. // control_MLX90640_power(true);
  383. int status = MLX90640_GetFrameData(MLX90640_ADDR, frame);
  384. if (status < 0)
  385. {
  386. DEBUG_PRINTF("[MLX] Failed to get frame data, status=%d, retrying...\r\n", status);
  387. MX_I2C1_Init();
  388. continue;
  389. }
  390. float vdd = MLX90640_GetVdd(frame, &mlx90640);
  391. float Ta = MLX90640_GetTa(frame, &mlx90640);
  392. Ambient_temperature = (uint8_t)Ta;
  393. DEBUG_PRINTF("vdd %.1f Ta %.1f\r\n", vdd, Ta);
  394. if (vdd < 2.5f || vdd > 5.5f || Ta < -40 || Ta > 85)
  395. {
  396. DEBUG_PRINTF("Failed vdd %.1f Ta %.1f\r\n", vdd, Ta);
  397. failCount++;
  398. if (failCount >= 5)
  399. {
  400. DEBUG_PRINTF("[MLX] Failed 5 times, system resetting\r\n");
  401. NVIC_SystemReset(); // 软复位
  402. }
  403. continue;
  404. }
  405. float tr = Ta - TA_SHIFT;
  406. float emissivity = flash_data.Emissivity / 100.0f;
  407. MLX90640_CalculateTo(frame, &mlx90640, emissivity, tr, mlx90640To);
  408. MLX90640_BadPixelsCorrection(mlx90640.brokenPixels, mlx90640To, 1, &mlx90640);
  409. MLX90640_BadPixelsCorrection(mlx90640.outlierPixels, mlx90640To, 1, &mlx90640);
  410. // 数据有效性检查
  411. int oddSum = 0, evenSum = 0;
  412. for (int i = 0; i < 768; i++)
  413. {
  414. if (mlx90640To[i] < -40 || mlx90640To[i] > 255)
  415. {
  416. DEBUG_PRINTF("[MLX] Temperature point[%d] invalid, value=%.1f, retrying...\r\n", i, mlx90640To[i]);
  417. validFrame = 0;
  418. break;
  419. }
  420. if (i % 2 == 0)
  421. evenSum += mlx90640To[i];
  422. else
  423. oddSum += mlx90640To[i];
  424. }
  425. if (oddSum == 0 || evenSum == 0)
  426. {
  427. DEBUG_PRINTF("[MLX] Odd or even sum is zero, invalid data, retrying...\r\n");
  428. validFrame = 0;
  429. continue;
  430. }
  431. validFrame = 1;
  432. control_MLX90640_power(false);
  433. end_tick = osKernelGetTickCount();
  434. DEBUG_PRINTF("MLX_end_tick %lu\r\n",end_tick);
  435. DEBUG_PRINTF("Elapsed time: %lu ms\r\n", end_tick - start_tick);
  436. }
  437. return validFrame;
  438. }
  439. // 处理MLX90640读取到的温度数据
  440. int ProcessTemperatureData(void)
  441. {
  442. DEBUG_PRINTF("[Process] Start processing temperature data\r\n");
  443. // 初始化最大值、最小值、平均值、最大值坐标
  444. float maxTemp = mlx90640To[0];
  445. float minTemp = mlx90640To[0];
  446. float sumTemp = 0;
  447. int maxIndex = 0;
  448. int minIndex = 0;
  449. for (int i = 0; i < 768; i++)
  450. {
  451. if (mlx90640To[i] > maxTemp)
  452. {
  453. maxTemp = mlx90640To[i];
  454. maxIndex = i;
  455. }
  456. else if (mlx90640To[i] == maxTemp && i < maxIndex)
  457. {
  458. maxIndex = i; // 选择第一个最大值索引
  459. }
  460. if (mlx90640To[i] < minTemp)
  461. {
  462. minTemp = mlx90640To[i];
  463. minIndex = i;
  464. }
  465. sumTemp += mlx90640To[i];
  466. }
  467. DEBUG_PRINTF("[Process] MaxTemp=%.1f maxIndex=%d, MinTemp=%.1f minIndex=%d\r\n", maxTemp, maxIndex, minTemp, minIndex);
  468. float avgTemp = sumTemp / 768;
  469. // 判断系统预警等级
  470. if ((uint8_t)maxTemp <= flash_data.first_threshold)
  471. {
  472. systemlevel = 0;
  473. }
  474. else if ((uint8_t)maxTemp > flash_data.first_threshold && (uint8_t)maxTemp <= flash_data.second_threshold)
  475. {
  476. systemlevel = 1;
  477. }
  478. else
  479. {
  480. systemlevel = 2;
  481. }
  482. // 填充数据
  483. debugBuffer1_tmp[0] = (uint8_t)systemlevel;
  484. debugBuffer1_tmp[1] = (uint8_t)maxTemp;
  485. debugBuffer1_tmp[2] = (uint8_t)minTemp;
  486. debugBuffer1_tmp[3] = (uint8_t)avgTemp;
  487. debugBuffer1_tmp[4] = Ambient_temperature;
  488. // 最大值点坐标
  489. int maxRow = maxIndex / COLS;
  490. int maxCol = maxIndex % COLS;
  491. // 确定九宫格范围
  492. int startRow = (maxRow > 0) ? maxRow - 1 : 0;
  493. int endRow = (maxRow < ROWS - 1) ? maxRow + 1 : ROWS - 1;
  494. int startCol = (maxCol > 0) ? maxCol - 1 : 0;
  495. int endCol = (maxCol < COLS - 1) ? maxCol + 1 : COLS - 1;
  496. // 填充九宫格数据
  497. int dataIndex = 0;
  498. memset(debugBuffer2_tmp, 0, sizeof(debugBuffer2_tmp));
  499. for (int r = startRow; r <= endRow; r++)
  500. {
  501. for (int c = startCol; c <= endCol; c++)
  502. {
  503. int temp = (int)mlx90640To[r * COLS + c]; // 舍去小数部分
  504. if (dataIndex < 1)
  505. {
  506. debugBuffer1_tmp[dataIndex + 7] = (uint8_t)temp;
  507. }
  508. else
  509. {
  510. debugBuffer2_tmp[dataIndex - 1] = (uint8_t)temp;
  511. }
  512. dataIndex++;
  513. }
  514. }
  515. // 填充最大值点坐标
  516. debugBuffer1_tmp[5] = (uint8_t)maxCol;
  517. debugBuffer1_tmp[6] = (uint8_t)maxRow;
  518. uint8_t zeroCount = 0;
  519. // 统计 debugBuffer2_tmp 中 0 的数量
  520. for (int i = 0; i < sizeof(debugBuffer2_tmp); i++)
  521. {
  522. if (debugBuffer2_tmp[i] == 0)
  523. {
  524. zeroCount++;
  525. }
  526. }
  527. // 如果 0 的数量在 3 到 6 之间(包含 3 和 6),直接返回 0
  528. if (zeroCount >= 3 && zeroCount <= 6)
  529. {
  530. return 0;
  531. }
  532. if( (debugBuffer1_tmp[0] == 0 && debugBuffer1_tmp[1] <= flash_data.first_threshold)
  533. ||(debugBuffer1_tmp[0] == 1 && (flash_data.first_threshold < debugBuffer1_tmp[1] && debugBuffer1_tmp[1] <= flash_data.second_threshold))
  534. ||(debugBuffer1_tmp[0] == 2 && debugBuffer1_tmp[1] > flash_data.second_threshold) )
  535. {
  536. taskENTER_CRITICAL();
  537. // CAN 消息打包
  538. memcpy(canMsgBuffer1, debugBuffer1_tmp, 8);
  539. memcpy(canMsgBuffer2, debugBuffer2_tmp, 8);
  540. taskEXIT_CRITICAL();
  541. return 1;
  542. }
  543. return 0;
  544. // DEBUG_PRINTF("ProcessTemperatureData() complete. MaxCol: %d, MaxRow: %d\r\n", maxCol, maxRow);
  545. }
  546. /**
  547. * @breaf 内部flash相关函数
  548. */
  549. void Read_NODE_ID_From_Flash(void) // 从 Flash 读取 NODE_ID
  550. {
  551. NODE_ID = *((uint8_t *)FLASH_SECOND_LAST_PAGE_ADDR);
  552. if (NODE_ID == 0xFF)
  553. {
  554. NODE_ID = 15 + 30 ;
  555. }else{
  556. NODE_ID = NODE_ID;
  557. }
  558. DEBUG_PRINTF("Loaded data: NODE_ID=%u\n",NODE_ID);
  559. }
  560. void Read_parameter_From_Flash(void) // 从 Flash 读取 parameter
  561. {
  562. uint8_t *flash_ptr = (uint8_t *)FLASH_LAST_PAGE_ADDR;
  563. if (flash_ptr[0] == 0xFF) {
  564. flash_data.first_threshold = 60;
  565. }else{
  566. flash_data.first_threshold = flash_ptr[0];
  567. }
  568. if (flash_ptr[1] == 0xFF) {
  569. flash_data.second_threshold = 100;
  570. }else{
  571. flash_data.second_threshold = flash_ptr[1];
  572. }
  573. if (flash_ptr[2] == 0xFF) {
  574. flash_data.delay_receivedValue = 30;
  575. }else{
  576. flash_data.delay_receivedValue = flash_ptr[2];
  577. }
  578. if (flash_ptr[3] == 0xFF) {
  579. flash_data.Emissivity = 95;
  580. }else{
  581. flash_data.Emissivity = flash_ptr[3];
  582. }
  583. DEBUG_PRINTF("Loaded data: first_threshold=%u, second_threshold=%u , delay_receivedValue=%u ,Emissivity=%u\n",
  584. flash_data.first_threshold, flash_data.second_threshold, flash_data.delay_receivedValue, flash_data.Emissivity);
  585. }
  586. void Save_NODEID_To_Flash(uint8_t NODE_ID) // 向 Flash 写入 NODE_ID
  587. {
  588. uint8_t nodeIDData[4] = { NODE_ID, 0, 0, 0 };
  589. uint8_t readnodeIDData[4];
  590. if (Flash_WriteRead(FLASH_SECOND_LAST_PAGE_ADDR, nodeIDData, sizeof(nodeIDData), readnodeIDData) != SUCCESS) {
  591. DEBUG_PRINTF("Failed to save NODE_ID to flash\n");
  592. } else {
  593. DEBUG_PRINTF("NODE_ID saved to flash: %u\n", nodeIDData[0]);
  594. }
  595. }
  596. void Save_parameter_To_Flash(uint8_t first_threshold ,uint8_t second_threshold ,uint8_t delay_receivedValue ,uint8_t Emissivity) // 向 Flash 写入 parameter
  597. {
  598. uint8_t parameter[4] = { first_threshold, second_threshold, delay_receivedValue, Emissivity};
  599. uint8_t readparameterData[4];
  600. if (Flash_WriteRead(FLASH_LAST_PAGE_ADDR, parameter, sizeof(parameter), readparameterData) != SUCCESS) {
  601. DEBUG_PRINTF("Failed to save Parameter to flash\n");
  602. } else {
  603. DEBUG_PRINTF("Parameter saved to flash: [%u] [%u] [%u] [%u]\n", parameter[0], parameter[1], parameter[2], parameter[3]);
  604. }
  605. }
  606. /**
  607. * @breaf CAN相关函数
  608. */
  609. //主板ID 0x00100000
  610. HAL_StatusTypeDef CAN_FilterInit(void)// 初始化CAN过滤器
  611. {
  612. CAN_FilterTypeDef CAN_FilterInitStructure;
  613. // 选择要初始化的过滤器
  614. CAN_FilterInitStructure.FilterBank = 0; // 选择过滤器的过滤库,CAN总线支持多个过滤器
  615. CAN_FilterInitStructure.FilterMode = CAN_FILTERMODE_IDMASK; // 设置过滤模式为ID掩码模式,即根据ID和掩码进行过滤
  616. CAN_FilterInitStructure.FilterScale = CAN_FILTERSCALE_32BIT; // 选择32位的过滤器(过滤器ID + 掩码总共32位)
  617. CAN_FilterInitStructure.FilterFIFOAssignment = CAN_FILTER_FIFO0; // 指定过滤器分配给FIFO0,用于接收来自CAN总线的消息
  618. // 配置第一个过滤器接收 0x00100000
  619. CAN_FilterInitStructure.FilterIdHigh = (0x00100000 >> 13) & 0xFFFF; // 高 16 位
  620. CAN_FilterInitStructure.FilterIdLow = ((0x00100000 << 3) & 0xFFFF) | (1 << 2); // 低 16 位 + IDE 位
  621. CAN_FilterInitStructure.FilterMaskIdHigh = 0xFFFF; // 掩码的高16位设置为全1,表示不对该部分进行匹配过滤
  622. CAN_FilterInitStructure.FilterMaskIdLow = 0xFFFF;
  623. CAN_FilterInitStructure.FilterActivation = CAN_FILTER_ENABLE; // 启用该过滤器,只有符合条件的消息才会通过
  624. if (HAL_CAN_ConfigFilter(&hcan1, &CAN_FilterInitStructure) != HAL_OK)
  625. {
  626. Error_Handler();
  627. return HAL_ERROR;
  628. }
  629. // 配置第二个过滤器接收 0x00110000
  630. CAN_FilterInitStructure.FilterBank = 1; // 使用下一个过滤器
  631. CAN_FilterInitStructure.FilterIdHigh = (0x00110000 >> 13) & 0xFFFF;
  632. CAN_FilterInitStructure.FilterIdLow = ((0x00110000 << 3) & 0xFFFF) | (1 << 2);
  633. if (HAL_CAN_ConfigFilter(&hcan1, &CAN_FilterInitStructure) != HAL_OK)
  634. {
  635. Error_Handler();
  636. return HAL_ERROR;
  637. }
  638. if (HAL_CAN_Init(&hcan1) != HAL_OK)// 初始化CAN外设
  639. {
  640. // 如果CAN初始化失败,打印错误信息并进入错误处理
  641. uint32_t can_error = HAL_CAN_GetError(&hcan1);
  642. DEBUG_PRINTF("CAN Initialization Error: %ld\n", can_error);
  643. Error_Handler();
  644. return HAL_ERROR;
  645. }
  646. return HAL_OK;
  647. }
  648. void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
  649. {
  650. CAN_RxHeaderTypeDef RX_Header;
  651. uint8_t rxData[8];
  652. if (HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RX_Header, rxData) == HAL_OK)
  653. {
  654. if (RX_Header.ExtId == 0x00100000 && RX_Header.IDE == CAN_ID_EXT)
  655. {
  656. DEBUG_PRINTF("CANID received: 0x%08X\n", RX_Header.ExtId);
  657. if (rxData[1] == 0x00)
  658. {
  659. CAN_Send_Parameter(NODE_ID, 0, &flash_data, 0xFF, 0xFF, 0xFF);
  660. return;
  661. }
  662. // 更新 NODE_ID
  663. if (rxData[0] == 0xFF || rxData[0] == (uint8_t)(NODE_ID & 0xFF))
  664. {
  665. NODE_ID = rxData[1];
  666. // 保存到 Flash
  667. Save_NODEID_To_Flash(NODE_ID);
  668. // 应答
  669. CAN_Send_extId(&NODE_ID, 1, NODE_ID, 0xFF, 0xFF, 0xFF);
  670. }
  671. else if (rxData[0] != (uint8_t)(NODE_ID & 0xFF))
  672. {
  673. // 如果第一个字节和当前全局变量 NODE_ID 不一致,则不执行
  674. return;
  675. }
  676. }
  677. else if(RX_Header.ExtId == 0x00110000 && RX_Header.IDE == CAN_ID_EXT)
  678. {
  679. DEBUG_PRINTF("CANID received: 0x%08X\n", RX_Header.ExtId);
  680. if(rxData[0]==0 || rxData[1]==0 || rxData[2]==0 || rxData[3]==0 )
  681. {
  682. CAN_Send_Parameter(NODE_ID, 0, &flash_data, 0xFF, 0xFF, 0xFF);
  683. return;
  684. }
  685. // 更新 first_threshold 和 delay_receivedValue 和 Emissivity
  686. flash_data.first_threshold = rxData[0];
  687. flash_data.second_threshold = rxData[1];
  688. flash_data.delay_receivedValue = rxData[2];
  689. flash_data.Emissivity = rxData[3];
  690. // 保存到 Flash
  691. Save_parameter_To_Flash(flash_data.first_threshold ,flash_data.second_threshold ,flash_data.delay_receivedValue ,flash_data.Emissivity);
  692. // 应答
  693. CAN_Send_Parameter(NODE_ID, 4, &flash_data, 0xFF, 0xFF, 0xFF);
  694. }
  695. else
  696. {
  697. DEBUG_PRINTF("CANID mismatched!\n");
  698. }
  699. }
  700. else
  701. {
  702. DEBUG_PRINTF("CAN receive failed!Error code: 0x%08lX\n", HAL_CAN_GetError(&hcan1));
  703. }
  704. }
  705. void CAN_Send_Msg(uint8_t *msg,uint8_t len,uint32_t node_id, uint8_t sequence, uint8_t slice_num, uint8_t slice_id)
  706. {
  707. CAN_TxHeaderTypeDef Tx_Header;
  708. uint32_t TxMailBox;
  709. uint32_t ret_status = 0;
  710. // 0000 0000 0011 0000 0000 0000 0000 0000
  711. Tx_Header.ExtId = (1U << 28) // 数据标志位 1
  712. | ((node_id & 0xFF) << 20) // 节点 ID,占 8 位
  713. | ((sequence & 0xFF) << 12) // 序列号,占 8 位
  714. | ((slice_num & 0xF) << 8) // 分片总数,占 4 位
  715. | ((slice_id & 0xF) << 4); // 当前分片 ID,占 4 位
  716. Tx_Header.IDE = CAN_ID_EXT;
  717. Tx_Header.RTR = CAN_RTR_DATA;
  718. Tx_Header.DLC = len;
  719. ret_status = HAL_CAN_AddTxMessage(&hcan1,&Tx_Header,msg,&TxMailBox);
  720. if(0 != ret_status){
  721. DEBUG_PRINTF("HAL_CAN_AddTxMessage failed , ret_status:0x%x \r\n", ret_status);
  722. DEBUG_PRINTF("hcan->ErrorCode: 0x%x \r\n", hcan1.ErrorCode);
  723. }
  724. }
  725. void CAN_Send_extId(uint16_t *data, uint8_t len, uint32_t node_id, uint8_t sequence, uint8_t slice_num, uint8_t slice_id)
  726. {
  727. // 配置 CAN 发送消息结构体
  728. CAN_TxHeaderTypeDef Tx_Header;
  729. uint32_t txMailbox;
  730. Tx_Header.ExtId = (0U << 28) // 指令标志位 0
  731. | ((node_id & 0xFF) << 20) // 节点 ID,占 8 位  
  732. | ((sequence & 0xFF) << 12) // 序列号,占 8 位  
  733. | ((slice_num & 0xF) << 8) // 分片总数,占 4 位  
  734. | ((slice_id & 0xF) << 4); // 当前分片 ID,占 4 位
  735. Tx_Header.IDE = CAN_ID_EXT; // 扩展帧
  736. Tx_Header.RTR = CAN_RTR_DATA; // 数据帧
  737. Tx_Header.DLC = len; // 数据长度
  738. if (HAL_CAN_AddTxMessage(&hcan1, &Tx_Header, (uint8_t *)data, &txMailbox) != HAL_OK) {
  739. DEBUG_PRINTF("CAN message send failed\n");
  740. } else {
  741. // 将数据转换为十六进制字符串形式
  742. char dataStr[len * 3]; // 每字节 3 个字符 (2个字符 + 空格)
  743. for (uint8_t i = 0; i < len; i++) {
  744. sprintf(&dataStr[i * 3], "%02X ", data[i]);
  745. }
  746. DEBUG_PRINTF("CAN message sent: ID=0x%08X, Data=%s\n", Tx_Header.ExtId, dataStr);
  747. }
  748. }
  749. void CAN_Send_Parameter(uint32_t node_id, uint8_t len, FlashData *flash_data, uint8_t sequence, uint8_t slice_num, uint8_t slice_id)
  750. {
  751. CAN_TxHeaderTypeDef Tx_Header;
  752. uint32_t TxMailBox;
  753. uint32_t ret_status = 0;
  754. uint8_t msg[8] = {0};
  755. msg[0] = flash_data->first_threshold;
  756. msg[1] = flash_data->second_threshold;
  757. msg[2] = flash_data->delay_receivedValue;
  758. msg[3] = flash_data->Emissivity;
  759. msg[4] = 0;
  760. msg[5] = 0;
  761. msg[6] = 0;
  762. msg[7] = 0;
  763. Tx_Header.ExtId = (0U << 28) // 指令标志位 0
  764. | ((node_id & 0xFF) << 20) // 节点 ID,占 8 位  
  765. | ((sequence & 0xFF) << 12) // 序列号,占 8 位  
  766. | ((slice_num & 0xF) << 8) // 分片总数,占 4 位  
  767. | ((slice_id & 0xF) << 4); // 当前分片 ID,占 4 位
  768. Tx_Header.IDE = CAN_ID_EXT;
  769. Tx_Header.RTR = CAN_RTR_DATA;
  770. Tx_Header.DLC = len;
  771. ret_status = HAL_CAN_AddTxMessage(&hcan1,&Tx_Header,msg,&TxMailBox);
  772. if(0 != ret_status){
  773. DEBUG_PRINTF("HAL_CAN_AddTxMessage failed , ret_status:0x%x \r\n", ret_status);
  774. DEBUG_PRINTF("hcan->ErrorCode: 0x%x \r\n", hcan1.ErrorCode);
  775. }
  776. }
  777. /**
  778. * @breaf 低功耗相关
  779. */
  780. void Configure_GPIOs_For_LowPower(void)
  781. {
  782. GPIO_InitTypeDef GPIO_InitStruct = {0};
  783. // 启用承朿 GPIO 端口的时钿
  784. __HAL_RCC_GPIOA_CLK_ENABLE();
  785. __HAL_RCC_GPIOB_CLK_ENABLE();
  786. __HAL_RCC_RTC_ENABLE();
  787. __HAL_RCC_USART2_CLK_ENABLE();
  788. GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;// 配置为模拟模式,禁用上拉/下拉
  789. GPIO_InitStruct.Pull = GPIO_NOPULL;
  790. GPIO_InitStruct.Pin = GPIO_PIN_All;
  791. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 配置 GPIOA
  792. GPIO_InitStruct.Pin = GPIO_PIN_All& ~(GPIO_PIN_15);// 配置 GPIOB
  793. HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  794. // 9 10 是IIC,11 12是CAN
  795. HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
  796. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_15, GPIO_PIN_SET);
  797. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);
  798. __HAL_RCC_GPIOA_CLK_DISABLE();
  799. __HAL_RCC_GPIOB_CLK_DISABLE();
  800. // 关闭外设时钟
  801. __HAL_RCC_USART2_CLK_DISABLE();
  802. __HAL_RCC_I2C1_CLK_DISABLE();
  803. __HAL_RCC_CAN1_CLK_DISABLE();
  804. }
  805. void Enter_LPStop2Mode(void)
  806. {
  807. DEBUG_PRINTF("Entering Stop2\n");
  808. Configure_GPIOs_For_LowPower();
  809. // 使能 RTC 唤醒
  810. Enable_RTC_Wakeup(flash_data.delay_receivedValue);
  811. HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE2); // 配置调压器为低功耗模式
  812. HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);// 进入 STOP2 低功耗模式
  813. // // 重新初始化 CAN
  814. // MX_CAN1_Init();
  815. // // 重新启动 CAN
  816. // HAL_CAN_Start(&hcan1);
  817. // HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
  818. // HAL_Delay(50);
  819. // SystemClock_Config();
  820. // HAL_I2C_DeInit(&hi2c1);
  821. // MX_I2C1_Init();
  822. // MX_CAN1_Init(); // 重新初始化 CAN
  823. // HAL_CAN_Start(&hcan1);
  824. // HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
  825. // DEBUG_PRINTF("Stop2 wakeup\n");
  826. }
  827. void Wakeup_LPStop2Mode(void)
  828. {
  829. // 重新初始化系统时钟
  830. SystemClock_Config();
  831. NVIC_SystemReset(); // 直接执行软件复位
  832. // // 重新初始化 CAN
  833. // MX_CAN1_Init();
  834. // // 重新启动 CAN
  835. // HAL_CAN_Start(&hcan1);
  836. // HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
  837. // HAL_Delay(50);
  838. // SystemClock_Config();
  839. // HAL_I2C_DeInit(&hi2c1);
  840. // MX_I2C1_Init();
  841. // MX_CAN1_Init(); // 重新初始化 CAN
  842. // HAL_CAN_Start(&hcan1);
  843. // HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
  844. }
  845. void Enable_RTC_Wakeup(uint16_t delay_time)
  846. {
  847. HAL_RTCEx_DeactivateWakeUpTimer(&hrtc); // 先关闭 RTC 唤醒,防止冲突
  848. // 计算唤醒时间
  849. uint32_t wakeup_counter = delay_time - 9;
  850. // 设置 RTC 唤醒
  851. HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, delay_time, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
  852. DEBUG_PRINTF("RTC Wakeup Timer set for %d s\n", wakeup_counter);
  853. }
  854. void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
  855. {
  856. DEBUG_PRINTF("RTC callback\n");
  857. control_MLX90640_power(true);
  858. HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); // 配置调压器为正常模式
  859. NVIC_SystemReset(); // RTC 唤醒后执行软件复位
  860. }
  861. void control_MLX90640_power(bool enable) // 三极管 徐
  862. {
  863. // 低电平 供电,高电平 断电。
  864. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, enable ? GPIO_PIN_RESET : GPIO_PIN_SET);
  865. }
  866. //void control_MLX90640_power(bool enable) // 跳线 邢
  867. //{
  868. // // 高电平 供电,低电平 断电。
  869. // HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, enable ? GPIO_PIN_SET : GPIO_PIN_RESET);
  870. //}
  871. void printCurrentRTCTime(void)
  872. {
  873. RTC_TimeTypeDef sTime = {0};
  874. RTC_DateTypeDef sDate = {0};
  875. char timeString[20]; // 用于存储格式化的时间字符串
  876. char dataString[20]; // 用于存储格式化的时间字符串
  877. // 获取当前时间
  878. HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
  879. // 获取当前日期
  880. HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
  881. // 格式化时间为字符串 "HH:MM:SS"
  882. snprintf(timeString, sizeof(timeString), "%02d:%02d:%02d",
  883. sTime.Hours, sTime.Minutes, sTime.Seconds);
  884. // 格式化日期为字符串 "YYYY/MM/DD"
  885. snprintf(dataString, sizeof(dataString), "20%02d/%02d/%02d",
  886. sDate.Year, sDate.Month, sDate.Date);
  887. // 打印日期
  888. DEBUG_PRINTF("CurrentBeijingDateTime: %s-%s\n", dataString, timeString);
  889. }
  890. /* USER CODE END Application */