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
+440
View File
@@ -0,0 +1,440 @@
#include "mylog/mylog.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "usart.h"
#include "app_rtc/app_rtc.h"
#include "app_init/app_init.h"
#include "app_init/app_test.h"
#include "app_init/task_lock.h"
#include "publicdata/public_define.h"
#include "publicdata/publicdata.h"
#include "wdt_task/wdt_task.h"
#if FATFS_EN == 1
#include "app_fatfs/fatfs_init.h"
#endif
#if SD_CARD_EN
#include "sdmmc/app_sdmmc.h"
#endif
/* 暂时屏蔽 mylog UDP(保留代码,不删除) */
#ifndef MYLOG_UDP_EN
#define MYLOG_UDP_EN 1
#endif
#if (MYLOG_UDP_EN)
#include "lwip/sockets.h"
#include "lwip/inet.h"
#endif
/* mylog UDP 目标地址配置(模块内定义) */
#define MYLOG_UDP_DST_IP0 192
#define MYLOG_UDP_DST_IP1 168
#define MYLOG_UDP_DST_IP2 1
#define MYLOG_UDP_DST_IP3 238
#define MYLOG_UDP_DST_PORT 18487 /* UDP日志发送的目标端口(上位机监听端口) */
#define MYLOG_UDP_SRC_PORT 8088 /* 本机UDP发送socket绑定的源端口(抓包显示的本地端口) */
#define MYLOG_UDP_RX_PORT 8088 /* 本机UDP接收socket监听端口(用于接收并回显测试数据) */
/* 日志头格式选择:
* 1: [task] file:line func - msg
* 0: [task][sec=<当前秒>] - msg
*/
#ifndef MYLOG_PRINT_WITH_FILE_LINE_FUNC
#define MYLOG_PRINT_WITH_FILE_LINE_FUNC (0)
#endif
/* 本任务ID定义:用于统一索引任务表、日志与锁资源。 */
#define MYLOG_TASK_ID (TASK_ID_MyLog)
/* 获取 MyLog 任务睡眠时间(ms)。 */
#define MYLOG_SLEEP_TIME_MS() (MY_GET_SLEEP_TIME(MYLOG_TASK_ID))
/* MyLog 任务锁操作宏:简化锁接口调用。 */
#define MYLOG_LOCK_INIT() (u8_task_lock_ctrl(MYLOG_TASK_ID, TASK_LOCK_OP_INIT))
#define MYLOG_LOCK_TAKE() (u8_task_lock_ctrl(MYLOG_TASK_ID, TASK_LOCK_OP_TAKE))
#define MYLOG_LOCK_GIVE() (u8_task_lock_ctrl(MYLOG_TASK_ID, TASK_LOCK_OP_GIVE))
/* MyLog 任务喂狗宏:标记本任务周期正常运行。 */
#define MYLOG_FEED_DOG() (v_wdt_setFlag(MYLOG_TASK_ID))
/* MyLog 自身打印接口:对齐参考工程写法。 */
#define MYLOG_LOG(fmt, ...) MYLOG_MSG(MYLOG_TASK_ID, (fmt), ##__VA_ARGS__)
/* 任务日志开关表:默认仅 MyLog 任务允许打印,其它任务默认关闭。 */
/* 索引与 TASK_ID 一一对应,值 1 表示允许该任务打印,0 表示禁止。 */
uint8_t u8_task_log_flag[MY_TASK_NUM] =
{
[TASK_ID_EthIf] = 0U,
[TASK_ID_TCPIP] = 0U,
[TASK_ID_BmsComm] = 0U,
[TASK_ID_DataColl] = 0U,
[TASK_ID_FlowCtrl] = 1U,
[TASK_ID_FaultCheck] = 1U,
[TASK_ID_MduComm] = 0U,
[TASK_ID_SubComm] = 0U,
[TASK_ID_PlatComm] = 1U,
#if (MY_SHELL_EN)
[TASK_ID_MyShell] = 0U,
#endif
[TASK_ID_UI] = 0U,
[TASK_ID_WATCHDOG] = 0U,
[TASK_ID_MeterColl] = 0U,
[TASK_ID_Meterfee] = 0U,
[TASK_ID_MyLog] = 1U,
#if (TCP_DEBUG_EN)
[TASK_ID_TcpSer] = 1U,
#endif
[TASK_ID_EthLink] = 0U
};
/* UDP 套接字句柄:<0 表示未创建。 */
#if (MYLOG_UDP_EN)
static int g_mylog_udp_sock = -1;
/* UDP 接收套接字句柄:<0 表示未创建。 */
static int g_mylog_udp_rx_sock = -1;
/* UDP 目的地址结构体。 */
static struct sockaddr_in g_mylog_udp_addr;
#endif
/**
* @brief 初始化 mylog 的 UDP 发送通道。
*
* @details
* - 首次调用时创建 UDP socket
* - 配置固定的目的 IP 和端口;
* - 若 socket 创建失败,则保持未初始化状态,后续发送时可重试。
*/
static void v_mylog_udp_init(void)
{
#if (MYLOG_UDP_EN)
struct sockaddr_in local_addr;
if (g_mylog_udp_sock >= 0) {
return;
}
g_mylog_udp_sock = lwip_socket(AF_INET, SOCK_DGRAM, 0);
if (g_mylog_udp_sock < 0) {
return;
}
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = PP_HTONS(MYLOG_UDP_SRC_PORT);
local_addr.sin_addr.s_addr = PP_HTONL(INADDR_ANY);
if (lwip_bind(g_mylog_udp_sock, (const struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) {
lwip_close(g_mylog_udp_sock);
g_mylog_udp_sock = -1;
return;
}
memset(&g_mylog_udp_addr, 0, sizeof(g_mylog_udp_addr));
g_mylog_udp_addr.sin_family = AF_INET;
g_mylog_udp_addr.sin_port = PP_HTONS(MYLOG_UDP_DST_PORT);
g_mylog_udp_addr.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(MYLOG_UDP_DST_IP0, MYLOG_UDP_DST_IP1, MYLOG_UDP_DST_IP2, MYLOG_UDP_DST_IP3));
#else
/* UDP disabled */
#endif
}
/**
* @brief 初始化 mylog 的 UDP 接收通道。
*
* @details
* - 创建 UDP socket 并绑定到 `MYLOG_UDP_RX_PORT`
* - 设置为非阻塞模式,避免阻塞任务主循环。
*/
static void v_mylog_udp_rx_init(void)
{
#if (MYLOG_UDP_EN)
if (g_mylog_udp_rx_sock >= 0) {
return;
}
#if (MYLOG_UDP_RX_PORT == MYLOG_UDP_SRC_PORT)
if (g_mylog_udp_sock < 0) {
v_mylog_udp_init();
}
g_mylog_udp_rx_sock = g_mylog_udp_sock;
return;
#endif
#if (MYLOG_UDP_RX_PORT != MYLOG_UDP_SRC_PORT)
struct sockaddr_in local_addr;
g_mylog_udp_rx_sock = lwip_socket(AF_INET, SOCK_DGRAM, 0);
if (g_mylog_udp_rx_sock < 0) {
return;
}
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = PP_HTONS(MYLOG_UDP_RX_PORT);
local_addr.sin_addr.s_addr = PP_HTONL(INADDR_ANY);
if (lwip_bind(g_mylog_udp_rx_sock, (const struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) {
lwip_close(g_mylog_udp_rx_sock);
g_mylog_udp_rx_sock = -1;
return;
}
#endif
#else
/* UDP disabled */
#endif
}
/**
* @brief 发送 UDP 日志数据。
*
* @param data 待发送数据指针。
* @param len 待发送数据长度(字节)。
*
* @details
* - 入参非法时直接返回;
* - socket 未初始化时先尝试初始化;
* - 发送失败不阻塞业务流程(静默处理)。
*/
void v_mylog_udp_send_data(const uint8_t *data, uint16_t len)
{
#if (MYLOG_UDP_EN)
if (data == NULL || len == 0U) {
return;
}
if (g_mylog_udp_sock < 0) {
v_mylog_udp_init();
}
if (g_mylog_udp_sock >= 0) {
(void)lwip_sendto(g_mylog_udp_sock,
data,
len,
0,
(const struct sockaddr *)&g_mylog_udp_addr,
sizeof(g_mylog_udp_addr));
}
#else
(void)data;
(void)len;
#endif
}
/**
* @brief 非阻塞接收 UDP 数据并原样回显给发送端。
*/
static void v_mylog_udp_recv_echo(void)
{
#if (MYLOG_UDP_EN)
uint8_t rx_buf[256];
int recv_len;
socklen_t peer_len;
struct sockaddr_in peer_addr;
if (g_mylog_udp_rx_sock < 0) {
v_mylog_udp_rx_init();
}
if (g_mylog_udp_rx_sock < 0) {
return;
}
peer_len = (socklen_t)sizeof(peer_addr);
recv_len = lwip_recvfrom(g_mylog_udp_rx_sock,
rx_buf,
sizeof(rx_buf),
MSG_DONTWAIT,
(struct sockaddr *)&peer_addr,
&peer_len);
if (recv_len > 0) {
(void)lwip_sendto(g_mylog_udp_rx_sock,
rx_buf,
(size_t)recv_len,
0,
(const struct sockaddr *)&peer_addr,
peer_len);
}
#else
/* UDP disabled */
#endif
}
/**
* @brief 统一格式化并输出日志(UDP + 串口)。
*
* @param id 任务ID(用于显示任务名)。
* @param file 文件名(建议传 BASENAME(__FILE__))。
* @param line 行号。
* @param func 函数名。
* @param fmt 可变参数格式字符串。
*
* @details
* - 通过 MYLOG_TASK_ID 对应锁串行化输出,避免多任务并发打乱日志;
* - 输出格式:`[task] file:line func - message`
* - 同时发 UDP 和串口 printf。
*/
void v_format_logMsg(int id, const char *file, int line, const char *func, const char *fmt, ...)
{
va_list args;
char log_buf[256];
int written = 0;
if (fmt == NULL) {
return;
}
if (MYLOG_LOCK_TAKE() != 1U) {
return;
}
#if (MYLOG_PRINT_WITH_FILE_LINE_FUNC)
written += snprintf(log_buf + written,
(int)sizeof(log_buf) - written,
"[%s] %s:%d %s - ",
MY_GET_TASK_NAME(id),
(file != NULL) ? file : "unknown",
line,
(func != NULL) ? func : "unknown");
#else
Comm_Time now_time;
(void)memset(&now_time, 0, sizeof(now_time));
GetCurrentTime(&now_time);
written += snprintf(log_buf + written,
(int)sizeof(log_buf) - written,
"[%s][%02u%02u%02u] - ",
MY_GET_TASK_NAME(id),
(unsigned int)now_time.ucHour,
(unsigned int)now_time.ucMin,
(unsigned int)now_time.ucSec);
#endif
if (written < (int)sizeof(log_buf)) {
va_start(args, fmt);
written += vsnprintf(log_buf + written, (int)sizeof(log_buf) - written, fmt, args);
va_end(args);
}
if (written < 0) {
(void)MYLOG_LOCK_GIVE();
return;
}
if (written >= (int)sizeof(log_buf)) {
written = (int)sizeof(log_buf) - 1;
}
// v_mylog_udp_send_data((const uint8_t *)log_buf, (uint16_t)written);
printf("%s\r\n", log_buf);
// 同时写入文件系统日志
if(
// id == TASK_ID_BMS ||
// id == TASK_ID_FlowCtrl ||
// id == TASK_ID_FaultCheck ||
id == TASK_ID_PlatComm )
{
#define LOG_BUFFER_SIZE 512
char logBuffer[LOG_BUFFER_SIZE];
memset(logBuffer , 0 , LOG_BUFFER_SIZE);
written = 0;
// written += snprintf(logBuffer + written, LOG_BUFFER_SIZE - written,
// "[%s]", // 任务名称
// MY_GET_TASK_NAME(id));
// 处理内容
va_start(args, fmt);
written += vsnprintf(logBuffer + written, LOG_BUFFER_SIZE - written, fmt, args);
va_end(args);
/* LOG_INFO("%s", logBuffer); -- FS log disabled */
}
(void)MYLOG_LOCK_GIVE();
}
/**
* ************************************************************************
* @brief - RTC时间更新,
* (由于RCT无外部时钟,内部时钟精度较低,定时20分钟使用rx8025对RTC进行校时处理)
*
*
*
* @author YZG
* @date 2025-11-04
* ************************************************************************
*/
#if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D
void v_current_time_updata()
{
static uint32_t current_time_last = 0;
Comm_Time comm_time;
memset(&comm_time,0,sizeof(Comm_Time));
if(u32_safe_seconds_since(current_time_last) > NUM_60 *20) //运行10分钟
{
GetCurrentTime(&comm_time);
MYLOG_LOG("RTC time:%d-%d-%d %d:%d:%d \n",comm_time.iYear,comm_time.ucMonth,comm_time.ucDay,
comm_time.ucHour,comm_time.ucMin,comm_time.ucSec);
v_get_rx8025_set_rtc();
GetCurrentTime(&comm_time);
MYLOG_LOG("rx8025 time:%d-%d-%d %d:%d:%d \n",comm_time.iYear,comm_time.ucMonth,comm_time.ucDay,
comm_time.ucHour,comm_time.ucMin,comm_time.ucSec);
current_time_last = get_current_seconds();
}
}
#endif
/**
* @brief MyLog 任务主函数。
*
* @param argument 任务参数,当前未使用。
*
* @details
* - 初始化 MyLog 对应锁;
* - 按任务周期执行;
* - 在 `TASK_RUN_PRINT_TIME` 周期点输出“任务运行中”日志,便于在线确认任务活性。
*/
void v_mylog_task(void *argument)
{
uint16_t u16_sleep_time;
uint32_t last_send_time;
uint32_t current_time;
(void)argument;
/* 初始化本任务专属互斥锁,保护日志格式化与输出过程。 */
(void)MYLOG_LOCK_INIT();
/* 读取任务表中的 MyLog 周期配置,后续循环统一使用该周期。 */
u16_sleep_time = MYLOG_SLEEP_TIME_MS();
/* 记录周期打印基准时间(秒),用于 TASK_RUN_PRINT_TIME 节拍判断。 */
last_send_time = get_current_seconds();
for (;;) {
/* 每个循环先喂狗,标记 MyLog 任务仍在正常执行。 */
MYLOG_FEED_DOG();
/* 使用全局 TASK_RUN_PRINT_TIME 判断是否到达周期打印时刻。 */
current_time = get_current_seconds();
if ((TASK_RUN_PRINT_TIME != 0U) && (u32_safe_seconds_since(last_send_time) >= TASK_RUN_PRINT_TIME)) {
MYLOG_LOG("mylog is runing, sleep time is %ums", u16_sleep_time);
last_send_time = current_time;
}
/* 轮询 UDP 接收并回显收到的数据(可通过 MYLOG_UDP_EN 屏蔽)。 */
v_mylog_udp_recv_echo();
#if FATFS_EN == 1
fatfs_action(); //文件系统周期执行函数
#endif
#if SD_CARD_EN
app_sdmmc_periodic();
#endif
#if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D
v_current_time_updata(); //crt时间周期更新
#endif
/* 按任务配置周期休眠,避免忙轮询占用 CPU。 */
mSleep(u16_sleep_time);
}
}
+31
View File
@@ -0,0 +1,31 @@
#ifndef MYLOG_H
#define MYLOG_H
#include <stdint.h>
#include <string.h>
#include "app_init/app_init.h"
/* 提取文件名(去掉路径),用于日志输出。 */
#define BASENAME(file) \
(strrchr((file), '/') ? (strrchr((file), '/') + 1) : \
(strrchr((file), '\\') ? (strrchr((file), '\\') + 1) : (file)))
/* 每个任务的日志开关:1-允许打印,0-禁止打印。 */
extern uint8_t u8_task_log_flag[MY_TASK_NUM];
void v_mylog_task(void *argument);
void v_format_logMsg(int id, const char *file, int line, const char *func, const char *fmt, ...);
void v_mylog_udp_send_data(const uint8_t *data, uint16_t len);
#define MYLOG_ID_FORCE_OCPP 0xFFU
#define MYLOG_ID_FORCE_IES 0xFEU
/* id=0xFFU 表示强制打印;其余 id 需在范围内且开关为1才打印。 */
#define MYLOG_MSG(id, fmt, ...) \
do { \
if (((id) == 0xFFU) || (((uint32_t)(id) < (uint32_t)MY_TASK_NUM) && (u8_task_log_flag[(uint32_t)(id)] == 1U))) { \
v_format_logMsg((id), BASENAME(__FILE__), __LINE__, __func__, (fmt), ##__VA_ARGS__); \
} \
} while (0)
#endif
+44
View File
@@ -0,0 +1,44 @@
# mylog 模块说明
> 路径:`app/mylog/`
> 返回主说明:[README](../../README.md)
## 1. 模块职责
- 提供统一日志输出入口;
- 管理日志等级、模块输出开关;
- 对接串口/网络日志通道(按工程配置启用)。
## 2. 关键文件
- `mylog.c`
- `mylog.h`
## 3. 功能说明
- 提供标准日志打印宏/函数,便于各任务统一格式输出;
- 支持运行时日志开关控制,便于现场定位问题;
- 与测试模块联动时可输出周期状态日志。
## 4. 调度与对外接口
该模块由 FreeRTOS 任务 `MyLog` 周期运行(单位:ms),周期来自 `app/app_init/app_init.c`
- `MyLog``200ms`(任务入口:`v_mylog_task(void *argument)`
对外接口(来自 `mylog.h`):
- `void v_mylog_task(void *argument)`:日志任务入口
- `void v_format_logMsg(int id, const char *file, int line, const char *func, const char *fmt, ...)`:内部格式化/输出实现
- `void v_mylog_udp_send_data(const uint8_t *data, uint16_t len)`UDP 发送接口(若工程配置启用)
- `MYLOG_MSG(id, fmt, ...)`:日志宏
- `extern uint8_t u8_task_log_flag[MY_TASK_NUM]`:运行时控制各任务日志开关(`id` 对应任务 ID
## 5. 维护建议
- 业务模块不要直接依赖底层串口细节,统一走 `mylog`
- 高频日志建议加节流,避免影响实时任务。
## 6. 关联文档
- 主索引:[README](../../README.md)