Initial commit: CCU621_M firmware project with BLE debug link support.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 17:28:36 +08:00
commit 9ceb218f80
1597 changed files with 724159 additions and 0 deletions
+195
View File
@@ -0,0 +1,195 @@
#include "app_init.h"
#include "app_init/task_lock.h"
#include "mylog/mylog.h"
#include "bms/bms_task.h"
#include "collect_ctrl/collect_task.h"
#include "flowctrl/flowctrl_task.h"
#include "fault_cheak/faultcheck_task.h"
#include "mdu_comm/mdu_comm_task.h"
#include "plat_comm/plat_comm_task.h"
#include "ui/ui_task.h"
#include "wdt_task/wdt_task.h"
#include "meter_calculate/meter_coll_task.h"
#include "meter_calculate/calculate_task.h"
#include "tcp_ser/tcp_ser_task.h"
#include "sys_drv_init.h"
#include "publicdata/publicdata.h"
#include "bsp_include.h"
#if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D
extern struct netif gnetif;
#include "adc/app_adc.h"
#include "app_fatfs/fatfs_init.h"
#include "net_lwip/ethernetif.h"
#elif SYSTEM_TYPE == SYSTEM_TYPE_CCU621_M
#include "netconf.h"
#include "net_init.h"
#include "eth_link.h"
#include "sub_comm/sub_comm_task.h"
#else
#endif
#if SD_CARD_EN
#include "sdmmc/sdmmc_sd_drv.h"
#include "sdmmc/app_sdmmc.h"
#endif
#if(MY_SHELL_EN) //Shell 使能
#include "letter_shell/shell_port.h"
#endif
void v_fault_blink_code_loop(uint32_t blink_count)
{
uint32_t i;
if (blink_count == 0U) {
blink_count = 1U;
}
for (;;) {
for (i = 0U; i < blink_count; i++) {
RUN_LED_ON();
delay_ms(200U);
RUN_LED_OFF();
delay_ms(200U);
}
/* 码间隔:灭 500ms,便于快速识别状态 */
RUN_LED_OFF();
delay_ms(1000U);
}
}
void delay_ms(uint32_t ms)
{
uint32_t start;
uint32_t ticks;
/* 基于 DWT CYCCNT 的精确延时:按 SystemCoreClock 计时 */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
start = DWT->CYCCNT;
ticks = (SystemCoreClock / 1000U) * ms;
while ((DWT->CYCCNT - start) < ticks) {
__NOP();
}
}
/*
### CMSIS-RTOS2 优先级与任务周期匹配表
| 优先级范围 | 适用任务类型 | 典型执行周期 | 说明 |
| **osPriorityRealtime**<br>(48~55) | 硬件中断延迟处理、安全关键任务 | **<1ms** | 最高优先级,用于不能容忍任何延迟的任务(如电机紧急停止) |
| **osPriorityHigh**<br>(40~47) | 实时控制、高频传感器处理 | **1~10ms** | 要求严格定时执行的任务(如PID控制、IMU数据融合) |
| **osPriorityAboveNormal**<br>(32~39) | 通信协议栈、用户输入响应 | **10~50ms** | 需要快速响应但允许轻微抖动的任务(如UART命令解析、触摸屏响应) |
| **osPriorityNormal**<br>(24~31) | 主要业务逻辑、中等频率任务 | **50~200ms** | 常规任务(如状态机更新、中等频率数据采集) |
| **osPriorityBelowNormal**<br>(16~23) | 后台处理、低优先级服务 | **200ms~1s** | 允许延迟的任务(如日志写入、非实时数据分析) |
| **osPriorityLow**<br>(8~15) | 维护性任务 | **1s~10s** | 对时间不敏感的任务(如LED心跳灯、内存碎片整理) |
| **osPriorityIdle**<br>(1) | 空闲任务 | **无固定周期** | 仅当系统空闲时运行(如低功耗模式准备) |
#### 配置原则
1. **周期越短 → 优先级越高**
- 例:100us级电机控制 → `osPriorityRealtime7` (55)
2. **关键性补偿**
- 重要但低频的任务(如看门狗)可提高1-2级优先级
3. **避免优先级反转**
- 共享资源的任务间优先级差建议≤8级
*/
const MY_TASK_DATA my_task_data[MY_TASK_NUM] =
{
/* 格式:{名称, 优先级, 栈大小(字节), 执行周期(ms), 任务入口, 任务参数} */
/* FreeRTOS 优先级规则(重要):
* - 数值越大,优先级越高;0 为最低优先级(通常是 IDLE)。
* - 当前工程 configMAX_PRIORITIES=32,可用范围 0~31。
* - 现场要求任务优先级不重复,以下按周期和关键性分配唯一优先级。 */
{"EthIf", (UBaseType_t)(tskIDLE_PRIORITY + 30U), TASK_SIZE_SIZE_2048, TASK_SLEEP_MS_1, NULL, NULL},
{"TCP/IP", (UBaseType_t)(tskIDLE_PRIORITY + 29U), TASK_SIZE_SIZE_8k, TASK_SLEEP_MS_1, NULL, NULL},
{"BmsComm", (UBaseType_t)(tskIDLE_PRIORITY + 27U), TASK_SIZE_SIZE_4k, TASK_SLEEP_MS_5, v_bms_task, NULL},
{"DataColl",(UBaseType_t)(tskIDLE_PRIORITY + 26U), TASK_SIZE_SIZE_2048, TASK_SLEEP_MS_10, v_collect_task, NULL},
{"FlowCtrl",(UBaseType_t)(tskIDLE_PRIORITY + 25U), TASK_SIZE_SIZE_4k, TASK_SLEEP_MS_20, v_flowctrl_task, NULL},
{"FaultCheck",(UBaseType_t)(tskIDLE_PRIORITY + 24U), TASK_SIZE_SIZE_6k, TASK_SLEEP_MS_50, v_fault_task, NULL},
{"MduComm", (UBaseType_t)(tskIDLE_PRIORITY + 23U), TASK_SIZE_SIZE_2048, TASK_SLEEP_MS_50, v_Mdu_Comm_task, NULL},
{"SubComm", (UBaseType_t)(tskIDLE_PRIORITY + 22U), TASK_SIZE_SIZE_2048, TASK_SLEEP_MS_50, v_sub_comm_task, NULL},
{"PlatComm",(UBaseType_t)(tskIDLE_PRIORITY + 21U), TASK_SIZE_SIZE_8k*2, TASK_SLEEP_MS_100, v_plat_comm_task, NULL},
#if (MY_SHELL_EN)
{"MyShell", (UBaseType_t)(tskIDLE_PRIORITY + 20U), TASK_SIZE_SIZE_4k, TASK_SLEEP_MS_100, v_myshell_task, &shell},
#endif
{"UiComm", (UBaseType_t)(tskIDLE_PRIORITY + 19U), TASK_SIZE_SIZE_3k, TASK_SLEEP_MS_100, v_ui_task, NULL},
/* WatchDog 放在 EthIf/TCP-IP 之后,保留较高优先级 */
{"WatchDog",(UBaseType_t)(tskIDLE_PRIORITY + 28U), TASK_SIZE_SIZE_1024, TASK_SLEEP_MS_150, v_wdt_task, NULL},
{"MeterColl",(UBaseType_t)(tskIDLE_PRIORITY + 18U), TASK_SIZE_SIZE_2048, TASK_SLEEP_MS_200, v_Meter_Coll_task, NULL},
{"Meterfee",(UBaseType_t)(tskIDLE_PRIORITY + 17U), TASK_SIZE_SIZE_3k, TASK_SLEEP_MS_200, v_meterfee_task, NULL},
{"MyLog", (UBaseType_t)(tskIDLE_PRIORITY + 16U), TASK_SIZE_SIZE_8k*2, TASK_SLEEP_MS_200, v_mylog_task, NULL},
#if TCP_DEBUG_EN
{"TcpSer", (UBaseType_t)(tskIDLE_PRIORITY + 15U), TASK_SIZE_SIZE_8k*2, TASK_SLEEP_MS_200, v_tcp_ser_task, NULL},
#endif
{"EthLink", (UBaseType_t)(tskIDLE_PRIORITY + 1U), TASK_SIZE_SIZE_1024, TASK_SLEEP_MS_1000, ethernet_link_thread, NETCONF_ETH_NETIF}
};
void app_os_init(void)
{
BaseType_t create_ret;
v_public_cfg_data_init(); //参数初始化
#if (MY_SHELL_EN)
userShellInit();
#endif
#if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D
v_adc_data_init();
extern void Netif_Config_init(void);
Netif_Config_init(); //网络及lwip 初始化 内部创建3个线程 分别是EthIf TCP/IP EthLink
CANSPI_Initialize(); //SPI CAN 配置
#elif SYSTEM_TYPE == SYSTEM_TYPE_CCU621_M
create_ret = net_init_task_create();
if (create_ret != pdPASS) {
MYLOG_MSG(0xFFU, "NetInit create failed, ret=%ld", (long)create_ret);
} else {
MYLOG_MSG(0xFFU, "NetInit create ok");
}
#else
#endif
for (uint32_t i = 0U; i < (uint32_t)MY_TASK_NUM; ++i) {
if (my_task_data[i].task_entry == NULL) {
continue;
}
create_ret = xTaskCreate(my_task_data[i].task_entry,
my_task_data[i].task_name,
(uint16_t)(my_task_data[i].task_size / sizeof(StackType_t)),
my_task_data[i].task_arg,
my_task_data[i].task_prio,
NULL);
if (create_ret != pdPASS) {
MYLOG_MSG(0xFFU, "Task[%s] create FAILED! ret=%ld", my_task_data[i].task_name, (long)create_ret);
} else {
MYLOG_MSG(0xFFU, "Task[%s] create ok", my_task_data[i].task_name);
}
}
#if SD_CARD_EN
sdmmc_sd_drv_init();
app_sdmmc_init();
#endif
#if SD_CARD_EN
fatfs_init();
#endif
}
+97
View File
@@ -0,0 +1,97 @@
#ifndef APP_INIT_H
#define APP_INIT_H
#include "FreeRTOS.h"
#include "task.h"
#include "publicdata/public_define.h"
//任务最小堆栈空间大小 --注意单位是字节!!!!
#define TASK_SIZE_SIZE_512 (configMINIMAL_STACK_SIZE*4) //512字节
#define TASK_SIZE_SIZE_1024 (configMINIMAL_STACK_SIZE*8) //1024字节=1k
#define TASK_SIZE_SIZE_2048 (TASK_SIZE_SIZE_1024*2) //2048字节=2k
#define TASK_SIZE_SIZE_3k (TASK_SIZE_SIZE_1024*3) //3k
#define TASK_SIZE_SIZE_4k (TASK_SIZE_SIZE_1024*4) //4k
#define TASK_SIZE_SIZE_5k (TASK_SIZE_SIZE_1024*5) //5k
#define TASK_SIZE_SIZE_6k (TASK_SIZE_SIZE_1024*6) //6k
#define TASK_SIZE_SIZE_7k (TASK_SIZE_SIZE_1024*7) //7k
#define TASK_SIZE_SIZE_8k (TASK_SIZE_SIZE_1024*8) //8k
//任务延时周期
#define TASK_SLEEP_MS_1000 (1000U)
#define TASK_SLEEP_MS_500 (500U)
#define TASK_SLEEP_MS_300 (300U)
#define TASK_SLEEP_MS_200 (200U)
#define TASK_SLEEP_MS_150 (150U)
#define TASK_SLEEP_MS_100 (100U)
#define TASK_SLEEP_MS_50 (50U)
#define TASK_SLEEP_MS_30 (30U)
#define TASK_SLEEP_MS_20 (20U)
#define TASK_SLEEP_MS_10 (10U)
#define TASK_SLEEP_MS_5 (5U)
#define TASK_SLEEP_MS_1 (1U)
typedef struct
{
char task_name[16];
UBaseType_t task_prio;
uint16_t task_size;
uint16_t task_timer;
TaskFunction_t task_entry;
void *task_arg;
} MY_TASK_DATA;
typedef enum
{
TASK_ID_EthIf = 0,
TASK_ID_TCPIP,
TASK_ID_BmsComm,
TASK_ID_DataColl,
TASK_ID_FlowCtrl,
TASK_ID_FaultCheck,
TASK_ID_MduComm,
TASK_ID_SubComm,
TASK_ID_PlatComm,
#if (MY_SHELL_EN)
TASK_ID_MyShell,
#endif
TASK_ID_UI,
TASK_ID_WATCHDOG,
TASK_ID_MeterColl,
TASK_ID_Meterfee,
TASK_ID_MyLog,
#if (TCP_DEBUG_EN)
TASK_ID_TcpSer,
#endif
TASK_ID_EthLink,
MY_TASK_NUM
} TASK_ID;
extern const MY_TASK_DATA my_task_data[MY_TASK_NUM];
/* lwIP TCP/IP 线程:任务表与 lwipopts.h 中 TCPIP_THREAD_* 须一致(lwipopts 不可再 include 本头文件) */
#define MY_TCPIP_NAME (my_task_data[TASK_ID_TCPIP].task_name)
#define MY_TCPIP_PRIO (my_task_data[TASK_ID_TCPIP].task_prio)
#define MY_TCPIP_SIZE (my_task_data[TASK_ID_TCPIP].task_size)
/* 兼容参考工程的任务访问宏 */
#ifndef mSleep
#define mSleep(x) (vTaskDelay(pdMS_TO_TICKS((x))))
#endif
#define MY_TASK_NEW(task_id, func, para) \
xTaskCreate((func), \
my_task_data[(task_id)].task_name, \
(uint16_t)(my_task_data[(task_id)].task_size / sizeof(StackType_t)), \
(para), \
my_task_data[(task_id)].task_prio, \
NULL)
#define MY_SLEEP_TIME(task_id) mSleep(my_task_data[(task_id)].task_timer)
#define MY_GET_SLEEP_TIME(task_id) (my_task_data[(task_id)].task_timer)
#define MY_GET_TASK_NAME(task_id) (my_task_data[(task_id)].task_name)
void app_os_init(void);
void delay_ms(uint32_t ms);
void v_fault_blink_code_loop(uint32_t blink_count);
#endif
+129
View File
@@ -0,0 +1,129 @@
# app_init 模块说明(app_init.c / app_init.h
> 本文档描述 `CCU621_M` 工程中 `app_init.c` 与 `app_init.h` 的职责、接口与使用约束。
[返回主说明](./../../README.md)
## 快速跳转
- [1. 模块职责](#1-模块职责)
- [2. 涉及文件](#2-涉及文件)
- [3. 对外接口](#3-对外接口)
- [4. app_os_init 初始化流程](#4-app_os_init-初始化流程)
- [5. 任务表说明(当前版本)](#5-任务表说明当前版本)
- [6. 依赖关系](#6-依赖关系)
- [7. 维护注意事项](#7-维护注意事项)
- [返回主说明 README](./../../README.md)
---
## 1. 模块职责
`app_init` 是应用层任务初始化总入口,负责:
- 维护统一任务表 `my_task_data[]`(名称/优先级/栈/周期/入口函数/参数);
-`app_os_init()` 中统一创建任务(`xTaskCreate`);
- 维护任务ID枚举 `TASK_ID` 与任务表下标一一对应;
- 提供任务周期、任务名称等统一访问宏;
- 为 lwIP 提供 `MY_TCPIP_NAME/MY_TCPIP_PRIO/MY_TCPIP_SIZE` 参数来源。
> 说明:当前工程是“集中创建任务”模式,不同于 `CCU601E_D` 的“各模块内部 `*_init()` 分散创建”模式。
---
## 2. 涉及文件
| 文件 | 说明 |
|---|---|
| `app/app_init/app_init.c` | 任务表定义 + `app_os_init()` 创建逻辑 |
| `app/app_init/app_init.h` | 栈/周期宏、`TASK_ID`、任务结构体、访问宏 |
| `app/app_init/task_lock.c/h` | 任务锁统一接口(每任务一个锁句柄) |
---
## 3. 对外接口
### 3.1 函数
| 符号 | 说明 |
|---|---|
| `void app_os_init(void)` | 应用任务初始化总入口(由 `main.c` 调用) |
### 3.2 全局数据
| 符号 | 说明 |
|---|---|
| `const MY_TASK_DATA my_task_data[MY_TASK_NUM]` | 全部任务配置表 |
### 3.3 关键类型与宏
- `MY_TASK_DATA`:任务名称、优先级、栈(字节)、周期(ms)、入口函数、入口参数;
- `TASK_ID`:任务ID**必须与 `my_task_data[]` 顺序一致**
- `MY_TASK_NUM`:任务数量(由 `TASK_ID` 枚举尾项决定);
- `mSleep(x)``vTaskDelay(pdMS_TO_TICKS(x))` 封装;
- `MY_GET_SLEEP_TIME(task_id)`:取任务周期;
- `MY_GET_TASK_NAME(task_id)`:取任务名称;
- `MY_TCPIP_NAME/MY_TCPIP_PRIO/MY_TCPIP_SIZE`:由 `my_task_data[TASK_ID_TCPIP]` 映射得出,供 lwIP 配置使用。
---
## 4. `app_os_init()` 初始化流程
当前流程(以 `app_init.c` 为准):
1.`MY_SHELL_EN=1`:执行 `userShellInit()`
2. 遍历 `my_task_data[]`
3.`task_entry != NULL` 的任务执行 `xTaskCreate(...)`
4. 忽略 `task_entry == NULL` 的条目(如 `EthIf``TCP/IP` 由 lwIP 内部创建)。
---
## 5. 任务表说明(当前版本)
`my_task_data[]` 当前主要任务:
- `EthIf`(占位,lwIP线程)
- `TCP/IP`(占位,lwIP线程)
- `DataColl`
- `FaultCheck`
- `MduComm`
- `MyShell`(条件编译)
- `UiComm`
- `WatchDog`
- `MeterColl`
- `MyLog`
- `EthLink`
> `EthIf`、`TCP/IP` 仅用于参数对齐与状态展示,不由 `app_os_init` 直接创建。
---
## 6. 依赖关系
-`main.c` 调用:`app_os_init()`
- 依赖模块:
- `mylog/collect_ctrl/fault_cheak/mdu_comm/ui/meter_calculate/wdt_task`
- `BSP/net_lwip``eth_link_thread`
- `BSP/letter_shell`(条件编译)
---
## 7. 维护注意事项
1. 新增任务时必须同步修改:
- `TASK_ID` 枚举;
- `my_task_data[]` 表项;
- 相关任务源文件中的任务ID宏(若使用模板化风格)。
2. `my_task_data[]``TASK_ID` 顺序不一致会导致:
- 周期读取错误;
- 日志任务名错误;
- 看门狗计数映射异常。
3. 若调整 `TCP/IP` 参数,建议只改任务表对应项,不在其他位置重复写常量。
4. `MY_SHELL_EN` 会影响任务数量与任务ID布局(`TASK_ID_MyShell` 条件编译)。
---
## 关联文档
- 主索引:[`CCU621_M/README.md`](./../../README.md)
- 本文位置:`app/app_init/app_init模块说明.md`
+905
View File
@@ -0,0 +1,905 @@
#include "app_init/app_test.h"
#include "app_init/app_init.h"
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include "FreeRTOS.h"
#include "portable.h"
#include "flash_external_data.h"
#include "fm24cl16.h"
#include "app_rtc/app_rtc.h"
#include "usart.h"
#include "can.h"
#include "publicdata/publicdata.h"
#include "mylog/mylog.h"
#include "collect_ctrl/collect_task.h"
#include "sys_drv_init.h"
#define APP_TEST_LOG(fmt, ...) MYLOG_MSG(TASK_ID_MyLog, (fmt), ##__VA_ARGS__)
#ifndef APP_TEST_ENABLE
#define APP_TEST_ENABLE (0U)
#endif
#define APP_TEST_FLASH_ADDR (0x00010000U)
#define APP_TEST_FLASH_RW_SIZE (1024U)
#define APP_TEST_FLASH_PERIOD_S (10U)
#define APP_TEST_MIN_TEST_MS (1U)
#define APP_TEST_ALIGN (32U)
#define APP_TEST_GUARD_SIZE (32U)
#define APP_TEST_FLASH_TOTAL_32M (0x02000000U)
#define APP_TEST_FLASH_STEP_1M (0x00100000U)
#define APP_TEST_ENABLE_FLASH_SPEED (0U)
#define APP_TEST_EEPROM_ADDR (0x0600U)
#define APP_TEST_EEPROM_SIZE (32U)
#define APP_TEST_EEPROM_MULTI_SIZE (64U)
#define APP_TEST_EEPROM_SPEED_SIZE (64U)
#define APP_TEST_EEPROM_SPEED_LOOP (10U)
#define APP_TEST_EEPROM_SPEED_RETRY (3U)
/* Use dedicated test window, avoid business hot partitions (e.g. 0x0100/0x0400). */
#define APP_TEST_EEPROM_TEST_ADDR_BASE (0x0600U)
typedef enum
{
APP_TEST_STEP_RTC = 0,
APP_TEST_STEP_EEPROM_BASIC,
APP_TEST_STEP_EEPROM_MULTI,
APP_TEST_STEP_EEPROM_SPEED,
APP_TEST_STEP_CAN,
APP_TEST_STEP_UART,
#if (APP_TEST_ENABLE_FLASH_SPEED)
APP_TEST_STEP_FLASH_SPEED,
#endif
APP_TEST_STEP_FLASH_STRIDE,
APP_TEST_STEP_DONE
} APP_TEST_STEP_T;
#if (APP_TEST_ENABLE)
/**
* @brief 将指针向上对齐到指定字节边界。
* @param p 原始指针。
* @param align 对齐值(2 的幂)。
* @return 对齐后的指针。
*/
static uint8_t *align_up_u8(uint8_t *p, uint32_t align)
{
uintptr_t v = (uintptr_t)p;
v = (v + (align - 1U)) & ~(uintptr_t)(align - 1U);
return (uint8_t *)v;
}
/**
* @brief 为 Flash DMA 测试申请对齐缓冲。
* @param tx_raw 输出:TX 原始指针(用于释放)。
* @param rx_raw 输出:RX 原始指针(用于释放)。
* @param tx_buf 输出:对齐后的 TX 指针(用于读写)。
* @param rx_buf 输出:对齐后的 RX 指针(用于读写)。
* @return 1 成功,0 失败。
*/
static uint8_t app_test_alloc_aligned_buffers(uint8_t **tx_raw, uint8_t **rx_raw, uint8_t **tx_buf, uint8_t **rx_buf)
{
uint32_t alloc_size = APP_TEST_FLASH_RW_SIZE + APP_TEST_ALIGN + APP_TEST_GUARD_SIZE;
*tx_raw = (uint8_t *)pvPortMalloc(alloc_size);
*rx_raw = (uint8_t *)pvPortMalloc(alloc_size);
if ((*tx_raw == NULL) || (*rx_raw == NULL)) {
if (*tx_raw != NULL) {
vPortFree(*tx_raw);
}
if (*rx_raw != NULL) {
vPortFree(*rx_raw);
}
*tx_raw = NULL;
*rx_raw = NULL;
*tx_buf = NULL;
*rx_buf = NULL;
return 0U;
}
*tx_buf = align_up_u8(*tx_raw, APP_TEST_ALIGN);
*rx_buf = align_up_u8(*rx_raw, APP_TEST_ALIGN);
return 1U;
}
/**
* @brief 释放由 app_test_alloc_aligned_buffers 申请的原始缓冲。
* @param tx_raw TX 原始指针。
* @param rx_raw RX 原始指针。
*/
static void app_test_free_aligned_buffers(uint8_t *tx_raw, uint8_t *rx_raw)
{
if (tx_raw != NULL) {
vPortFree(tx_raw);
}
if (rx_raw != NULL) {
vPortFree(rx_raw);
}
}
#if (APP_TEST_ENABLE_FLASH_SPEED)
/**
* @brief 单次 Flash 擦写读回测速与校验。
* @details
* - 使用 1KB 数据块在固定地址做擦除/写入/读取;
* - 打印 erase/write/read 耗时与吞吐;
* - 读回校验失败会打印 seed 便于复现。
*/
static void v_app_test_flash_rw_speed_once(void)
{
uint32_t i;
uint64_t t0;
uint64_t t1;
uint32_t seed;
uint32_t erase_ms;
uint32_t write_ms;
uint32_t read_ms;
uint32_t write_kbs;
uint32_t read_kbs;
uint8_t *tx_raw;
uint8_t *rx_raw;
uint8_t *tx_buf;
uint8_t *rx_buf;
if (app_test_alloc_aligned_buffers(&tx_raw, &rx_raw, &tx_buf, &rx_buf) == 0U) {
APP_TEST_LOG("[APP_TEST] exflash malloc fail, size=%u\r\n", (unsigned int)APP_TEST_FLASH_RW_SIZE);
return;
}
seed = (uint32_t)u64_get_current_millis();
for (i = 0U; i < APP_TEST_FLASH_RW_SIZE; i++) {
tx_buf[i] = (uint8_t)((seed + i) & 0xFFU);
}
(void)memset(rx_buf, 0, APP_TEST_FLASH_RW_SIZE);
t0 = u64_get_current_millis();
(void)s32_flash_dataflash_erase_sector(APP_TEST_FLASH_ADDR);
t1 = u64_get_current_millis();
erase_ms = (uint32_t)(t1 - t0);
t0 = u64_get_current_millis();
(void)s32_flash_dataflash_write(APP_TEST_FLASH_ADDR, tx_buf, APP_TEST_FLASH_RW_SIZE);
t1 = u64_get_current_millis();
write_ms = (uint32_t)(t1 - t0);
if (write_ms < APP_TEST_MIN_TEST_MS) {
write_ms = APP_TEST_MIN_TEST_MS;
}
t0 = u64_get_current_millis();
(void)s32_flash_dataflash_read(APP_TEST_FLASH_ADDR, rx_buf, APP_TEST_FLASH_RW_SIZE);
t1 = u64_get_current_millis();
read_ms = (uint32_t)(t1 - t0);
if (read_ms < APP_TEST_MIN_TEST_MS) {
read_ms = APP_TEST_MIN_TEST_MS;
}
if (memcmp(tx_buf, rx_buf, APP_TEST_FLASH_RW_SIZE) != 0) {
APP_TEST_LOG("[APP_TEST] exflash rw verify fail, seed=%lu\r\n", (unsigned long)seed);
app_test_free_aligned_buffers(tx_raw, rx_raw);
return;
}
write_kbs = (APP_TEST_FLASH_RW_SIZE * 1000U) / (write_ms * 1024U);
read_kbs = (APP_TEST_FLASH_RW_SIZE * 1000U) / (read_ms * 1024U);
APP_TEST_LOG("[APP_TEST] exflash speed seed=%lu, erase=%lums, write=%lums(%luKB/s), read=%lums(%luKB/s)\r\n",
(unsigned long)seed,
(unsigned long)erase_ms,
(unsigned long)write_ms,
(unsigned long)write_kbs,
(unsigned long)read_ms,
(unsigned long)read_kbs);
app_test_free_aligned_buffers(tx_raw, rx_raw);
}
#endif
void v_app_test_rtc_rw_once(void)
{
Comm_Time backup_time;
Comm_Time write_time;
Comm_Time read_time;
GetCurrentTime(&backup_time);
write_time = backup_time;
write_time.ucSec = (uint8_t)((write_time.ucSec + 1U) % 60U);
if ((write_time.ucSec == 0U) && (write_time.ucMin < 59U)) {
write_time.ucMin++;
}
v_rtc_set_time(&write_time);
GetCurrentTime(&read_time);
APP_TEST_LOG("[APP_TEST] rtc rw test, write=%04u-%02u-%02u %02u:%02u:%02u, read=%04u-%02u-%02u %02u:%02u:%02u\r\n",
write_time.iYear, write_time.ucMonth, write_time.ucDay,
write_time.ucHour, write_time.ucMin, write_time.ucSec,
read_time.iYear, read_time.ucMonth, read_time.ucDay,
read_time.ucHour, read_time.ucMin, read_time.ucSec);
v_rtc_set_time(&backup_time);
}
#if defined(__CC_ARM)
#pragma diag_suppress 177
#endif
/**
* @brief EEPROM(FM24CL16) 单次读写校验测试。
* @return 1 通过,0 失败。
*/
uint8_t u8_app_test_eeprom_rw_once(void)
{
uint8_t tx[APP_TEST_EEPROM_SIZE];
uint8_t rx[APP_TEST_EEPROM_SIZE];
uint32_t i;
int32_t first_mismatch = -1;
uint8_t probe_ret;
uint32_t err_after_write;
uint32_t err_after_read;
uint8_t pr;
APP_TEST_LOG("[APP_TEST] eeprom rw test start, addr=0x%02X size=%u\r\n",
(unsigned int)APP_TEST_EEPROM_ADDR,
(unsigned int)APP_TEST_EEPROM_SIZE);
for (i = 0U; i < APP_TEST_EEPROM_SIZE; i++) {
tx[i] = (uint8_t)(0xA0U + i);
}
(void)memset(rx, 0, sizeof(rx));
probe_ret = eeprom_probe((uint16_t)APP_TEST_EEPROM_ADDR);
APP_TEST_LOG("[APP_TEST] eeprom probe ret=%u last_err=%lu\r\n",
(unsigned int)probe_ret,
(unsigned long)eeprom_get_last_error());
/* quick probe different blocks to verify dev-addr mapping */
pr = eeprom_probe(0x0000U);
APP_TEST_LOG("[APP_TEST] eeprom probe @0x0000 ret=%u err=%lu\r\n",
(unsigned int)pr, (unsigned long)eeprom_get_last_error());
pr = eeprom_probe(0x0100U);
APP_TEST_LOG("[APP_TEST] eeprom probe @0x0100 ret=%u err=%lu\r\n",
(unsigned int)pr, (unsigned long)eeprom_get_last_error());
pr = eeprom_probe(0x0200U);
APP_TEST_LOG("[APP_TEST] eeprom probe @0x0200 ret=%u err=%lu\r\n",
(unsigned int)pr, (unsigned long)eeprom_get_last_error());
pr = eeprom_probe(0x0700U);
APP_TEST_LOG("[APP_TEST] eeprom probe @0x0700 ret=%u err=%lu\r\n",
(unsigned int)pr, (unsigned long)eeprom_get_last_error());
eeprom_buffer_write(tx, (uint16_t)APP_TEST_EEPROM_ADDR, (uint16_t)APP_TEST_EEPROM_SIZE);
err_after_write = eeprom_get_last_error();
eeprom_buffer_read(rx, (uint16_t)APP_TEST_EEPROM_ADDR, (uint16_t)APP_TEST_EEPROM_SIZE);
err_after_read = eeprom_get_last_error();
if (memcmp(tx, rx, APP_TEST_EEPROM_SIZE) != 0) {
for (i = 0U; i < APP_TEST_EEPROM_SIZE; i++) {
if (tx[i] != rx[i]) {
first_mismatch = (int32_t)i;
break;
}
}
APP_TEST_LOG("[APP_TEST] eeprom rw verify fail, addr=0x%02X size=%u\r\n",
(unsigned int)APP_TEST_EEPROM_ADDR,
(unsigned int)APP_TEST_EEPROM_SIZE);
APP_TEST_LOG("[APP_TEST] eeprom err write=%lu read=%lu\r\n",
(unsigned long)err_after_write,
(unsigned long)err_after_read);
APP_TEST_LOG("[APP_TEST] eeprom dump first16 @0x%02X:\r\n",
(unsigned int)APP_TEST_EEPROM_ADDR);
for (i = 0U; i < 16U; i++) {
APP_TEST_LOG(" [%02u] wr=0x%02X rd=0x%02X\r\n",
(unsigned int)i,
(unsigned int)tx[i],
(unsigned int)rx[i]);
}
if (first_mismatch >= 0) {
APP_TEST_LOG("[APP_TEST] eeprom mismatch idx=%ld wr=0x%02X rd=0x%02X\r\n",
(long)first_mismatch,
(unsigned int)tx[(uint32_t)first_mismatch],
(unsigned int)rx[(uint32_t)first_mismatch]);
}
return 0U;
}
APP_TEST_LOG("[APP_TEST] eeprom rw verify pass, addr=0x%02X size=%u\r\n",
(unsigned int)APP_TEST_EEPROM_ADDR,
(unsigned int)APP_TEST_EEPROM_SIZE);
return 1U;
}
/**
* @brief EEPROM 多地址点读写校验(覆盖关键分区起点与页边界)。
* @return 1 通过,0 失败。
*/
uint8_t u8_app_test_eeprom_multi_addr_rw_once(void)
{
static const uint16_t rw_addrs[] = {
0x0000U,
0x0040U,
0x0070U,
0x0080U,
0x00C0U,
0x00E0U,
0x00F0U,
0x0580U,
0x0600U,
0x0680U,
0x07C0U
};
static const uint16_t observe_addrs[] = {
(uint16_t)EEPROM_ADDR_HISALARM_MNG,
(uint16_t)EEPROM_ADDR_CARD_MNG,
(uint16_t)EEPROM_ADDR_A_LOG_DATA,
(uint16_t)EEPROM_ADDR_B_LOG_DATA
};
uint8_t tx[APP_TEST_EEPROM_MULTI_SIZE];
uint8_t rx[APP_TEST_EEPROM_MULTI_SIZE];
uint8_t bk[APP_TEST_EEPROM_MULTI_SIZE];
uint32_t i;
uint32_t a;
uint16_t addr;
for (i = 0U; i < APP_TEST_EEPROM_MULTI_SIZE; i++) {
tx[i] = (uint8_t)(0x55U ^ (uint8_t)i);
}
APP_TEST_LOG("[APP_TEST] eeprom multi-addr rw test start, size=%u, points=%u\r\n",
(unsigned int)APP_TEST_EEPROM_MULTI_SIZE,
(unsigned int)(sizeof(rw_addrs) / sizeof(rw_addrs[0])));
for (a = 0U; a < (uint32_t)(sizeof(rw_addrs) / sizeof(rw_addrs[0])); a++) {
addr = rw_addrs[a];
if (!EEPROM_ADDR_IN_RANGE(addr) || !EEPROM_ADDR_IN_RANGE((uint16_t)(addr + APP_TEST_EEPROM_MULTI_SIZE - 1U))) {
APP_TEST_LOG("[APP_TEST] eeprom multi-addr skip out-of-range addr=0x%04X\r\n",
(unsigned int)addr);
continue;
}
(void)memset(rx, 0, sizeof(rx));
(void)memset(bk, 0, sizeof(bk));
/* backup original content to avoid corrupting real partitions */
eeprom_buffer_read(bk, addr, (uint16_t)APP_TEST_EEPROM_MULTI_SIZE);
if (eeprom_get_last_error() != 0U) {
APP_TEST_LOG("[APP_TEST] eeprom multi-addr backup read fail addr=0x%04X err=%lu\r\n",
(unsigned int)addr, (unsigned long)eeprom_get_last_error());
return 0U;
}
eeprom_buffer_write(tx, addr, (uint16_t)APP_TEST_EEPROM_MULTI_SIZE);
if (eeprom_get_last_error() != 0U) {
APP_TEST_LOG("[APP_TEST] eeprom multi-addr write fail addr=0x%04X err=%lu\r\n",
(unsigned int)addr, (unsigned long)eeprom_get_last_error());
/* best-effort restore */
eeprom_buffer_write(bk, addr, (uint16_t)APP_TEST_EEPROM_MULTI_SIZE);
return 0U;
}
eeprom_buffer_read(rx, addr, (uint16_t)APP_TEST_EEPROM_MULTI_SIZE);
if (eeprom_get_last_error() != 0U) {
APP_TEST_LOG("[APP_TEST] eeprom multi-addr read fail addr=0x%04X err=%lu\r\n",
(unsigned int)addr, (unsigned long)eeprom_get_last_error());
/* best-effort restore */
eeprom_buffer_write(bk, addr, (uint16_t)APP_TEST_EEPROM_MULTI_SIZE);
return 0U;
}
if (memcmp(tx, rx, APP_TEST_EEPROM_MULTI_SIZE) != 0) {
for (i = 0U; i < APP_TEST_EEPROM_MULTI_SIZE; i++) {
if (tx[i] != rx[i]) {
APP_TEST_LOG("[APP_TEST] eeprom multi-addr mismatch addr=0x%04X idx=%lu wr=0x%02X rd=0x%02X\r\n",
(unsigned int)addr,
(unsigned long)i,
(unsigned int)tx[i],
(unsigned int)rx[i]);
break;
}
}
/* restore original before returning */
eeprom_buffer_write(bk, addr, (uint16_t)APP_TEST_EEPROM_MULTI_SIZE);
return 0U;
}
APP_TEST_LOG("[APP_TEST] eeprom multi-addr pass addr=0x%04X\r\n", (unsigned int)addr);
/* restore original content */
eeprom_buffer_write(bk, addr, (uint16_t)APP_TEST_EEPROM_MULTI_SIZE);
if (eeprom_get_last_error() != 0U) {
APP_TEST_LOG("[APP_TEST] eeprom multi-addr restore fail addr=0x%04X err=%lu\r\n",
(unsigned int)addr, (unsigned long)eeprom_get_last_error());
return 0U;
}
}
/* Observe-only check for business hot partitions (no write/restore). */
for (a = 0U; a < (uint32_t)(sizeof(observe_addrs) / sizeof(observe_addrs[0])); a++) {
addr = observe_addrs[a];
if (!EEPROM_ADDR_IN_RANGE(addr) || !EEPROM_ADDR_IN_RANGE((uint16_t)(addr + APP_TEST_EEPROM_MULTI_SIZE - 1U))) {
continue;
}
(void)memset(bk, 0, sizeof(bk));
eeprom_buffer_read(bk, addr, (uint16_t)APP_TEST_EEPROM_MULTI_SIZE);
if (eeprom_get_last_error() != 0U) {
APP_TEST_LOG("[APP_TEST] eeprom multi-addr observe fail addr=0x%04X err=%lu\r\n",
(unsigned int)addr, (unsigned long)eeprom_get_last_error());
return 0U;
}
APP_TEST_LOG("[APP_TEST] eeprom multi-addr observe pass addr=0x%04X\r\n", (unsigned int)addr);
}
APP_TEST_LOG("[APP_TEST] eeprom multi-addr rw test pass\r\n");
return 1U;
}
/**
* @brief EEPROM 读写速度测试(循环读写固定大小并统计吞吐)。
* @return 1 通过,0 失败。
*/
uint8_t u8_app_test_eeprom_speed_once(void)
{
uint8_t tx[APP_TEST_EEPROM_SPEED_SIZE];
uint8_t rx[APP_TEST_EEPROM_SPEED_SIZE];
uint8_t bk[APP_TEST_EEPROM_SPEED_SIZE];
uint32_t i;
uint32_t j;
uint32_t r;
int32_t first_mismatch;
uint64_t t0;
uint64_t t1;
uint64_t write_ms;
uint64_t read_ms;
uint32_t write_kbs;
uint32_t read_kbs;
uint32_t loops = APP_TEST_EEPROM_SPEED_LOOP;
uint16_t addr = (uint16_t)APP_TEST_EEPROM_TEST_ADDR_BASE;
if (!EEPROM_ADDR_IN_RANGE(addr) || !EEPROM_ADDR_IN_RANGE((uint16_t)(addr + APP_TEST_EEPROM_SPEED_SIZE - 1U))) {
APP_TEST_LOG("[APP_TEST] eeprom speed addr out-of-range\r\n");
return 0U;
}
for (i = 0U; i < APP_TEST_EEPROM_SPEED_SIZE; i++) {
tx[i] = (uint8_t)(i & 0xFFU);
}
APP_TEST_LOG("[APP_TEST] eeprom speed test start, addr=0x%04X size=%u loop=%u\r\n",
(unsigned int)addr,
(unsigned int)APP_TEST_EEPROM_SPEED_SIZE,
(unsigned int)loops);
/* backup original test window and restore at the end */
(void)memset(bk, 0, sizeof(bk));
eeprom_buffer_read(bk, addr, (uint16_t)APP_TEST_EEPROM_SPEED_SIZE);
if (eeprom_get_last_error() != 0U) {
APP_TEST_LOG("[APP_TEST] eeprom speed backup read fail err=%lu\r\n",
(unsigned long)eeprom_get_last_error());
return 0U;
}
t0 = u64_get_current_millis();
for (i = 0U; i < loops; i++) {
for (r = 0U; r < APP_TEST_EEPROM_SPEED_RETRY; r++) {
eeprom_buffer_write(tx, addr, (uint16_t)APP_TEST_EEPROM_SPEED_SIZE);
if (eeprom_get_last_error() == 0U) {
break;
}
vTaskDelay(pdMS_TO_TICKS(2U));
}
if (eeprom_get_last_error() != 0U) {
APP_TEST_LOG("[APP_TEST] eeprom speed write fail loop=%lu retry=%lu err=%lu\r\n",
(unsigned long)i,
(unsigned long)r,
(unsigned long)eeprom_get_last_error());
eeprom_buffer_write(bk, addr, (uint16_t)APP_TEST_EEPROM_SPEED_SIZE);
return 0U;
}
/* Allow write cycle settle time under stress loops. */
vTaskDelay(pdMS_TO_TICKS(2U));
}
t1 = u64_get_current_millis();
write_ms = (t1 >= t0) ? (t1 - t0) : 0U;
if (write_ms < APP_TEST_MIN_TEST_MS) {
write_ms = APP_TEST_MIN_TEST_MS;
}
t0 = u64_get_current_millis();
for (i = 0U; i < loops; i++) {
for (r = 0U; r < APP_TEST_EEPROM_SPEED_RETRY; r++) {
(void)memset(rx, 0, sizeof(rx));
eeprom_buffer_read(rx, addr, (uint16_t)APP_TEST_EEPROM_SPEED_SIZE);
if ((eeprom_get_last_error() == 0U) &&
(memcmp(tx, rx, APP_TEST_EEPROM_SPEED_SIZE) == 0)) {
break;
}
vTaskDelay(pdMS_TO_TICKS(2U));
}
if ((eeprom_get_last_error() != 0U) || (memcmp(tx, rx, APP_TEST_EEPROM_SPEED_SIZE) != 0)) {
first_mismatch = -1;
for (j = 0U; j < APP_TEST_EEPROM_SPEED_SIZE; j++) {
if (tx[j] != rx[j]) {
first_mismatch = (int32_t)j;
break;
}
}
if (first_mismatch >= 0) {
APP_TEST_LOG("[APP_TEST] eeprom speed verify fail loop=%lu retry=%lu idx=%ld wr=0x%02X rd=0x%02X err=%lu\r\n",
(unsigned long)i,
(unsigned long)r,
(long)first_mismatch,
(unsigned int)tx[(uint32_t)first_mismatch],
(unsigned int)rx[(uint32_t)first_mismatch],
(unsigned long)eeprom_get_last_error());
} else {
APP_TEST_LOG("[APP_TEST] eeprom speed verify fail loop=%lu retry=%lu err=%lu\r\n",
(unsigned long)i,
(unsigned long)r,
(unsigned long)eeprom_get_last_error());
}
eeprom_buffer_write(bk, addr, (uint16_t)APP_TEST_EEPROM_SPEED_SIZE);
return 0U;
}
}
t1 = u64_get_current_millis();
read_ms = (t1 >= t0) ? (t1 - t0) : 0U;
if (read_ms < APP_TEST_MIN_TEST_MS) {
read_ms = APP_TEST_MIN_TEST_MS;
}
write_kbs = (uint32_t)(((uint64_t)APP_TEST_EEPROM_SPEED_SIZE * (uint64_t)loops * 1000ULL) / (write_ms * 1024ULL));
read_kbs = (uint32_t)(((uint64_t)APP_TEST_EEPROM_SPEED_SIZE * (uint64_t)loops * 1000ULL) / (read_ms * 1024ULL));
APP_TEST_LOG("[APP_TEST] eeprom speed result: write=%llums(%luKB/s) read=%llums(%luKB/s)\r\n",
(unsigned long long)write_ms,
(unsigned long)write_kbs,
(unsigned long long)read_ms,
(unsigned long)read_kbs);
eeprom_buffer_write(bk, addr, (uint16_t)APP_TEST_EEPROM_SPEED_SIZE);
return 1U;
}
#define APP_TEST_CAN_TX_PERIOD_MS (1000ULL)
#define APP_TEST_CAN_EXT_ID_BASE (0x18FEE000U)
/* ================= USART 单路测试(可改参数) =================
* 简化版:每次执行 UART step 时
* - 发送一次(固定数据,可改)
* - 接收一次并打印(如有数据)
* 每次仅测试一个串口。
*/
static E_USART_ID s_uart_test_port = E_USART_3; /* 可改:E_USART_0/2/3/4/5/6/7 */
static uint8_t s_uart_test_tx_data[16] = { 0x55U, 0xAAU, 0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U };
static uint16_t s_uart_test_tx_len = 8U;
static void v_app_test_uart_poll(void)
{
uint8_t rx_buf[128];
uint16_t rx_len;
uint16_t send_len = s_uart_test_tx_len;
if (send_len > (uint16_t)sizeof(s_uart_test_tx_data)) {
send_len = (uint16_t)sizeof(s_uart_test_tx_data);
}
if (send_len > 0U) {
(void)u16_usart_send(s_uart_test_port, s_uart_test_tx_data, send_len);
APP_TEST_LOG("[APP_TEST] uart tx port=%u len=%u\r\n",
(unsigned int)s_uart_test_port,
(unsigned int)send_len);
}
rx_len = u16_usart_recv(s_uart_test_port, rx_buf, (uint16_t)sizeof(rx_buf));
if (rx_len > 0U) {
char hex[3U * sizeof(rx_buf) + 4U];
uint32_t pos = 0U;
uint16_t i;
hex[0] = '\0';
for (i = 0U; i < rx_len && pos + 3U < sizeof(hex); i++) {
(void)snprintf(&hex[pos], sizeof(hex) - pos, "%02X ", (unsigned int)rx_buf[i]);
pos = (uint32_t)strlen(hex);
}
APP_TEST_LOG("[APP_TEST] uart rx port=%u len=%u %s\r\n",
(unsigned int)s_uart_test_port,
(unsigned int)rx_len,
hex);
}
}
/**
* @brief 读空三路 CAN 软件缓存并打印(扩展帧由驱动层标记)。
*/
static void v_app_test_can_rx_drain(void)
{
uint8_t port;
CAN_DATA rx[CAN_RX_BUFFER_SIZE];
uint8_t rx_cnt;
uint8_t i;
uint8_t k;
for (port = 0U; port < 3U; port++) {
rx_cnt = u8_can_rx((E_CAN_ID)port, &rx[0]);
for (k = 0U; k < rx_cnt; k++) {
char hex[3U * 8U + 4U] = {0};
uint32_t pos = 0U;
for (i = 0U; i < rx[k].Len && i < 8U && pos + 3U < sizeof(hex); i++) {
(void)snprintf(&hex[pos], sizeof(hex) - pos, "%02X ", (unsigned int)rx[k].Data[i]);
pos = (uint32_t)strlen(hex);
}
APP_TEST_LOG("[APP_TEST] can rx port=%u id=0x%08lX dlc=%u %s\r\n",
(unsigned int)port, (unsigned long)rx[k].ID, (unsigned int)rx[k].Len, hex);
}
}
}
/**
* @brief 三路 CAN 各发一帧扩展数据帧(8 字节,前 4 字节为递增序号)。
*/
static void v_app_test_can_tx_all_ports(uint32_t seq)
{
uint8_t port;
CAN_DATA tx;
for (port = 0U; port < 3U; port++) {
(void)memset(&tx, 0, sizeof(tx));
tx.ID = APP_TEST_CAN_EXT_ID_BASE + (uint32_t)port;
tx.Len = 8U;
tx.Data[0] = (uint8_t)(seq & 0xFFU);
tx.Data[1] = (uint8_t)((seq >> 8) & 0xFFU);
tx.Data[2] = (uint8_t)((seq >> 16) & 0xFFU);
tx.Data[3] = (uint8_t)((seq >> 24) & 0xFFU);
tx.Data[4] = port;
tx.Data[5] = 0xC0U;
tx.Data[6] = 0xAAU;
tx.Data[7] = 0x55U;
v_can_tx((E_CAN_ID)port, &tx);
}
APP_TEST_LOG("[APP_TEST] can tx 3ch ext seq=%lu base_id=0x%08lX\r\n",
(unsigned long)seq, (unsigned long)APP_TEST_CAN_EXT_ID_BASE);
}
/**
* @brief CAN 周期任务:每 1s 三路各发一帧扩展帧;每次调用先收完并打印。
* @note 挂在 v_app_test_periodic 入口,随 MyLog 任务节拍轮询(不受 10s 自检节流影响)。
*/
static void v_app_test_can_poll(void)
{
static uint64_t s_next_tx_ms = 0ULL;
static uint32_t s_tx_seq = 0U;
uint64_t now_ms;
v_app_test_can_rx_drain();
if (u8_can_is_initialized() == 0U) {
v_can_interface_init();
}
now_ms = u64_get_current_millis();
if (s_next_tx_ms == 0ULL) {
s_next_tx_ms = now_ms;
}
if (now_ms < s_next_tx_ms) {
return;
}
s_next_tx_ms = now_ms + APP_TEST_CAN_TX_PERIOD_MS;
v_app_test_can_tx_all_ports(s_tx_seq);
s_tx_seq++;
}
/**
* @brief 通过 u8_get_yxyc_data 读取整桩/枪实时采集结构并打印(与 collect_task 内数据源一致)。
*/
static void v_app_test_collect_yxyc_snapshot_print(void)
{
SYS_RT_DATA pile;
GUN_RT_DATA gun;
U8_T gun_cnt;
U8_T i;
if (u8_get_yxyc_data(&pile, NULL, 0U) == 0U) {
APP_TEST_LOG("[APP_TEST] coll get sys fail (lock/busy)\r\n");
return;
}
APP_TEST_LOG("[APP_TEST] ****************************************************** start ******************************************************\r\n");
APP_TEST_LOG("[APP_TEST] coll SYS yx_raw=0x%08lX\r\n",
(unsigned long)pile.yx_sys_data.u32_yx_sys_data);
APP_TEST_LOG("[APP_TEST] SYS_yx estp=%u door=%u water=%u smoke=%u busKM=%u spd=%u acKM=%u qf=%u\r\n",
(unsigned int)pile.yx_sys_data.bit_yx_sys_data.yx_emergency,
(unsigned int)pile.yx_sys_data.bit_yx_sys_data.yx_door,
(unsigned int)pile.yx_sys_data.bit_yx_sys_data.yx_water,
(unsigned int)pile.yx_sys_data.bit_yx_sys_data.yx_smoke,
(unsigned int)pile.yx_sys_data.bit_yx_sys_data.yx_bus_KM,
(unsigned int)pile.yx_sys_data.bit_yx_sys_data.yx_spd,
(unsigned int)pile.yx_sys_data.bit_yx_sys_data.yx_KM,
(unsigned int)pile.yx_sys_data.bit_yx_sys_data.yx_QF);
APP_TEST_LOG("[APP_TEST] SYS_yc temp1_x1000=%ld temp2_x1000=%ld\r\n",
(long)(pile.f32_yc_sys_temp1 * 1000.0f),
(long)(pile.f32_yc_sys_temp2 * 1000.0f));
APP_TEST_LOG("[APP_TEST] hw_DOgpio(1=H 0=L) K1-K5 : %u %u %u %u %u\r\n",
(unsigned int)DO1_READ(), (unsigned int)DO2_READ(), (unsigned int)DO3_READ(),
(unsigned int)DO4_READ(), (unsigned int)DO5_READ());
APP_TEST_LOG("[APP_TEST] hw_DOgpio(1=H 0=L) K6-K10: %u %u %u %u %u | ELOCK_A=%u ELOCK_B=%u\r\n",
(unsigned int)DO6_READ(), (unsigned int)DO7_READ(), (unsigned int)DO8_READ(),
(unsigned int)DO9_READ(), (unsigned int)DO10_READ(),
(unsigned int)ELOCK_A_READ(), (unsigned int)ELOCK_B_READ());
gun_cnt = (U8_T)GUN_MAX_CNT;
for (i = 0U; i < gun_cnt; i++) {
if (u8_get_yxyc_data(NULL, &gun, i) == 0U) {
APP_TEST_LOG("[APP_TEST] coll get gun%u fail\r\n", (unsigned int)i);
continue;
}
APP_TEST_LOG("[APP_TEST] coll GUN%u link=%u\r\n",
(unsigned int)i,
(unsigned int)gun.u8_gunlink_state);
APP_TEST_LOG("[APP_TEST] GUN_yx raw=0x%08lX lock=%u dcP=%u dcN=%u rlf=%u fuse=%u apw=%u home=%u\r\n",
(unsigned long)gun.yx_gun_data.u32_yx_gun_data,
(unsigned int)gun.yx_gun_data.bit_yx_gun_data.yx_gun_lock,
(unsigned int)gun.yx_gun_data.bit_yx_gun_data.yx_switch_pos,
(unsigned int)gun.yx_gun_data.bit_yx_gun_data.yx_switch_neg,
(unsigned int)gun.yx_gun_data.bit_yx_gun_data.yx_relief_swich,
(unsigned int)gun.yx_gun_data.bit_yx_gun_data.yx_fuse,
(unsigned int)gun.yx_gun_data.bit_yx_gun_data.yx_gun_power,
(unsigned int)gun.yx_gun_data.bit_yx_gun_data.yx_gun_homing);
APP_TEST_LOG("[APP_TEST] GUN_yc bus_mV V+=%ld V-=%ld I_mA=%ld\r\n",
(long)(gun.f32_yc_gun_vol_pos * 1000.0f),
(long)(gun.f32_yc_gun_vol_neg * 1000.0f),
(long)(gun.f32_yc_gun_curr * 1000.0f));
APP_TEST_LOG("[APP_TEST] GUN_yc bat_mV V+=%ld V-=%ld | gunTemp_x1000 Tpos=%ld Tneg=%ld\r\n",
(long)(gun.f32_yc_bat_vol_pos * 1000.0f),
(long)(gun.f32_yc_bat_vol_neg * 1000.0f),
(long)(gun.f32_yc_gun_temp_pos * 1000.0f),
(long)(gun.f32_yc_gun_temp_neg * 1000.0f));
}
APP_TEST_LOG("[APP_TEST] ****************************************************** end ******************************************************\r\n");
}
/**
* @brief 对外:以 1MB 步长扫描 32MB 地址空间读写校验。
* @details
* - 每个步长点执行:扇区擦除 -> 写入 1KB 模式数据 -> 读回校验;
* - 任一点失败立即返回并打印失败地址;
* - 全部通过后打印总耗时。
* @return 1 通过,0 失败。
*/
uint8_t u8_app_test_flash_32m_stride_rw(void)
{
uint32_t addr;
uint32_t i;
uint32_t seed;
uint64_t t0;
uint64_t t1;
uint64_t elapsed_ms;
uint8_t *tx_raw;
uint8_t *rx_raw;
uint8_t *tx_buf;
uint8_t *rx_buf;
if (app_test_alloc_aligned_buffers(&tx_raw, &rx_raw, &tx_buf, &rx_buf) == 0U) {
APP_TEST_LOG("[APP_TEST] 32M stride malloc fail\r\n");
return 0U;
}
APP_TEST_LOG("[APP_TEST] 32M stride test start, step=0x%08X, size=%u\r\n",
(unsigned int)APP_TEST_FLASH_STEP_1M,
(unsigned int)APP_TEST_FLASH_RW_SIZE);
t0 = u64_get_current_millis();
for (addr = 0U; addr < APP_TEST_FLASH_TOTAL_32M; addr += APP_TEST_FLASH_STEP_1M) {
seed = (uint32_t)(addr ^ 0x5A5A0000U);
for (i = 0U; i < APP_TEST_FLASH_RW_SIZE; i++) {
tx_buf[i] = (uint8_t)((seed + i) & 0xFFU);
}
(void)memset(rx_buf, 0, APP_TEST_FLASH_RW_SIZE);
(void)s32_flash_dataflash_erase_sector(addr);
(void)s32_flash_dataflash_write(addr, tx_buf, APP_TEST_FLASH_RW_SIZE);
(void)s32_flash_dataflash_read(addr, rx_buf, APP_TEST_FLASH_RW_SIZE);
if (memcmp(tx_buf, rx_buf, APP_TEST_FLASH_RW_SIZE) != 0) {
APP_TEST_LOG("[APP_TEST] 32M stride verify fail, addr=0x%08lX, seed=0x%08lX\r\n",
(unsigned long)addr,
(unsigned long)seed);
app_test_free_aligned_buffers(tx_raw, rx_raw);
return 0U;
}
}
t1 = u64_get_current_millis();
elapsed_ms = t1 - t0;
APP_TEST_LOG("[APP_TEST] 32M stride test pass, range=0x00000000~0x01FFFFFF, elapsed=%llums\r\n", elapsed_ms);
app_test_free_aligned_buffers(tx_raw, rx_raw);
return 1U;
}
/**
* @brief 周期触发综合测试(RTC/EEPROM/Flash)。
* @details 首次调用仅记录起始时间,不立即执行测试。
*/
void v_app_test_periodic(void)
{
static uint32_t s_last_test_sec = 0U;
static uint8_t s_test_step = (uint8_t)APP_TEST_STEP_RTC;
uint32_t now_sec;
if (s_last_test_sec == 0U) {
s_last_test_sec = get_current_seconds();
return;
}
now_sec = get_current_seconds();
/* 改为 1 秒节拍执行一次 step(按你的要求)。 */
if (u32_safe_seconds_since(s_last_test_sec) < 1U) {
return;
}
// APP_TEST_LOG("[APP_TEST] app test periodic\r\n");
/* 改用“数字递增 step”的方式,避免 bitmask 维护成本。 */
switch ((APP_TEST_STEP_T)s_test_step) {
case APP_TEST_STEP_RTC:
// v_app_test_rtc_rw_once();
test_rtc(); //测试RTC
s_test_step++;
break;
case APP_TEST_STEP_EEPROM_BASIC:
// (void)u8_app_test_eeprom_rw_once();
s_test_step++;
break;
case APP_TEST_STEP_EEPROM_MULTI:
// (void)u8_app_test_eeprom_multi_addr_rw_once();
s_test_step++;
break;
case APP_TEST_STEP_EEPROM_SPEED:
// (void)u8_app_test_eeprom_speed_once();
s_test_step++;
break;
case APP_TEST_STEP_CAN:
/* 每秒发送一次 + 周期接收打印(函数内部有 1s 节拍控制)。 */
// v_app_test_can_poll();
s_test_step++;
break;
case APP_TEST_STEP_UART:
// v_app_test_uart_poll();
v_app_test_collect_yxyc_snapshot_print();
s_test_step++;
break;
#if (APP_TEST_ENABLE_FLASH_SPEED)
case APP_TEST_STEP_FLASH_SPEED:
// v_app_test_flash_rw_speed_once();
s_test_step++;
break;
#endif
case APP_TEST_STEP_FLASH_STRIDE:
// if (u8_app_test_flash_32m_stride_rw() == 0U) {
// APP_TEST_LOG("[APP_TEST] 32M stride test failed in periodic flow\r\n");
// }
s_test_step++;
break;
case APP_TEST_STEP_DONE:
s_test_step = (uint8_t)APP_TEST_STEP_RTC;
break;
default:
break;
}
s_last_test_sec = now_sec;
}
#else
void v_app_test_periodic(void)
{
/* test disabled by APP_TEST_ENABLE */
}
#endif /* APP_TEST_ENABLE */
+8
View File
@@ -0,0 +1,8 @@
#ifndef APP_TEST_H
#define APP_TEST_H
#include <stdint.h>
void v_app_test_periodic(void);
#endif
+102
View File
@@ -0,0 +1,102 @@
#include "app_init/task_lock.h"
/**
* @brief 每任务锁句柄数组(外部可见)。
*
* @details
* - 下标与 `TASK_ID` 一一对应;
* - 每个任务默认预留一个 mutex;
* - 具体何时初始化由各任务自行调用 `u8_task_lock_ctrl(..., TASK_LOCK_OP_INIT)`。
*/
static SemaphoreHandle_t g_task_lock_handles[MY_TASK_NUM] = {0};
/**
* @brief 自定义锁句柄控制接口。
*
* @details
* 通过外部传入的 `SemaphoreHandle_t` 句柄指针执行统一锁操作:
* - INIT:若句柄为空则创建 mutex;
* - TAKE:阻塞获取 mutex
* - GIVE:释放 mutex
* - DELETE:删除 mutex 并清空句柄。
*
* @param p_handle 外部锁句柄地址。
* @param op 锁操作动作(初始化、上锁、解锁、删除)。
* @return uint8_t 1-成功,0-失败(指针非法/句柄为空/RTOS调用失败)。
*/
uint8_t u8_task_lock_handle_ctrl(SemaphoreHandle_t *p_handle, TASK_LOCK_OP_E op)
{
if (p_handle == NULL) {
return 0U;
}
switch (op) {
case TASK_LOCK_OP_INIT:
if (*p_handle == NULL) {
*p_handle = xSemaphoreCreateMutex();
}
return (*p_handle != NULL) ? 1U : 0U;
case TASK_LOCK_OP_TAKE:
if (*p_handle == NULL) {
return 0U;
}
return (xSemaphoreTake(*p_handle, portMAX_DELAY) == pdPASS) ? 1U : 0U;
case TASK_LOCK_OP_GIVE:
if (*p_handle == NULL) {
return 0U;
}
return (xSemaphoreGive(*p_handle) == pdTRUE) ? 1U : 0U;
case TASK_LOCK_OP_DELETE:
if (*p_handle != NULL) {
vSemaphoreDelete(*p_handle);
*p_handle = NULL;
}
return 1U;
default:
return 0U;
}
}
/**
* @brief 统一线程锁控制接口。
*
* @details
* 根据任务ID选择对应互斥锁句柄,并执行指定动作:
* - INIT:若未创建则创建 mutex
* - TAKE:阻塞获取 mutex
* - GIVE:释放 mutex
* - DELETE:删除 mutex 并清空句柄。
*
* @param task_id 任务ID`TASK_ID` 枚举,作为锁数组下标)。
* @param op 锁操作动作(初始化、上锁、解锁、删除)。
* @return uint8_t 1-成功,0-失败(ID非法/句柄为空/RTOS调用失败)。
*/
uint8_t u8_task_lock_ctrl(TASK_ID task_id, TASK_LOCK_OP_E op)
{
uint8_t id = (uint8_t)task_id;
if (id >= (uint8_t)MY_TASK_NUM) {
return 0U;
}
switch (op) {
case TASK_LOCK_OP_INIT:
return u8_task_lock_handle_ctrl(&g_task_lock_handles[id], TASK_LOCK_OP_INIT);
case TASK_LOCK_OP_TAKE:
return u8_task_lock_handle_ctrl(&g_task_lock_handles[id], TASK_LOCK_OP_TAKE);
case TASK_LOCK_OP_GIVE:
return u8_task_lock_handle_ctrl(&g_task_lock_handles[id], TASK_LOCK_OP_GIVE);
case TASK_LOCK_OP_DELETE:
return u8_task_lock_handle_ctrl(&g_task_lock_handles[id], TASK_LOCK_OP_DELETE);
default:
return 0U;
}
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef TASK_LOCK_H
#define TASK_LOCK_H
#include "app_init/app_init.h"
#include "semphr.h"
typedef enum
{
TASK_LOCK_OP_INIT = 0,
TASK_LOCK_OP_TAKE,
TASK_LOCK_OP_GIVE,
TASK_LOCK_OP_DELETE
} TASK_LOCK_OP_E;
uint8_t u8_task_lock_ctrl(TASK_ID task_id, TASK_LOCK_OP_E op);
uint8_t u8_task_lock_handle_ctrl(SemaphoreHandle_t *p_handle, TASK_LOCK_OP_E op);
#endif