工程提交

This commit is contained in:
2026-03-31 15:46:04 +08:00
parent 75f512a5b4
commit da4e944bca
2841 changed files with 4822938 additions and 1 deletions

View File

@@ -0,0 +1,235 @@
#include "ChargerTask.h"
static struct netconn *datalink_conn; // 数据链路句柄
#define LINK_SERVER_PORT 6001 // 网关UDP服务端口
#define LINK_STAKE_PORT 6001 // 桩通讯端口
/**
* @brief 桩IP地址
* @note 桩IP地址从10.12.19.101开始递增
*/
ip4_addr_t stake_ip_1 = IPADDR4_INIT_BYTES(10, 12, 19, 101); // 桩1IP地址
ip4_addr_t stake_ip_2 = IPADDR4_INIT_BYTES(10, 12, 19, 102); // 桩2IP地址
ip4_addr_t stake_ip_3 = IPADDR4_INIT_BYTES(10, 12, 19, 103); // 桩3IP地址
ip4_addr_t stake_ip_4 = IPADDR4_INIT_BYTES(10, 12, 19, 104); // 桩4IP地址
ip4_addr_t stake_ip_5 = IPADDR4_INIT_BYTES(10, 12, 19, 105); // 桩5IP地址
ip4_addr_t stake_ip_6 = IPADDR4_INIT_BYTES(10, 12, 19, 106); // 桩6IP地址
/**
* @brief UDP发送
* @note 发送数据到指定桩
* @param stake_index 目标桩ID
* @param data 数据指针
* @param len 数据长度
* @return err_t 错误码
*/
err_t udp_send_response(uint8_t stake_index, uint8_t *data, u16_t len)
{
struct netbuf *buf = netbuf_new();
if (!buf)
{
printf("[%s] %d\n", __func__, __LINE__);
return ERR_MEM;
}
ip4_addr_t *dst_ip = NULL;
switch (stake_index)
{
case 1:
dst_ip = &stake_ip_1;
break;
case 2:
dst_ip = &stake_ip_2;
break;
case 3:
dst_ip = &stake_ip_3;
break;
case 4:
dst_ip = &stake_ip_4;
break;
case 5:
dst_ip = &stake_ip_5;
break;
case 6:
dst_ip = &stake_ip_6;
break;
default:
printf("Invalid stake index\r\n");
return;
}
netbuf_ref(buf, data, len);
err_t err = netconn_sendto(datalink_conn, buf, dst_ip, LINK_STAKE_PORT);
netbuf_delete(buf);
return err;
}
void UDP_ParseTask_Function(void const *argument)
{
UdpMsg_t msg;
cJSON *root = NULL, *cmd = NULL, *id = NULL;
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); /* 等待桩通讯协议层完成*/
while (1)
{
if (xQueueReceive(UDP_Message_Queue, &msg, portMAX_DELAY) == pdPASS)
{
root = cJSON_Parse((const char *)msg.data);
if (root == NULL)
{
printf("JSON Parse Failed: %s\r\n", msg.data);
vPortFree(msg.data);
continue;
}
id = cJSON_GetObjectItem(root, "id");
cmd = cJSON_GetObjectItem(root, "cmd");
if (cmd != NULL && id != NULL)
{
const char *cmd_str = cmd->valuestring;
handle_udp_downlink((uint8_t)id->valueint, cmd_str, root);
cJSON_Delete(root);
}
else
{
printf("Missing 'code' field\r\n");
cJSON_Delete(root);
}
vPortFree(msg.data);
msg.data = NULL;
}
}
}
/**
* @brief UPLinkTask_Function桩通讯任务
*
* @note none
*
* @param taskID : 任务ID
*
* @retval runtime : 任务周期
*/
void UDPTask_Function(void const *argument)
{
err_t recv_err;
struct netbuf *datalink_buf = NULL;
datalink_conn = netconn_new(NETCONN_UDP);
netconn_bind(datalink_conn, IP_ADDR_ANY, LINK_SERVER_PORT);
UDP_Message_Queue_Init(); // 初始化UDP接收队列
/*桩UDP通讯初始化完成 发送云快充任务通知*/
xTaskNotifyGive(YkcTaskHandle);
xTaskNotifyGive(DownLinkTaskHandle);
xTaskNotifyGive(UDP_ParseTaskHandle);
while (1)
{
/*获取任务运行状态*/
TaskRunTimeStat.UPLinkTask.threads_runtime = GetTask_RunTime(UPLinkTaskID);
TaskRunTimeStat.UPLinkTask.threads_counter = GetTask_Beatcnt(UPLinkTaskID);
TaskRunTimeStat.UPLinkTask.threads_freestack = Get_Free_Stack(UPLinkTaskID);
recv_err = netconn_recv(datalink_conn, &datalink_buf);
if (recv_err == ERR_OK && datalink_buf != NULL)
{
uint8_t *playload;
uint16_t playload_len;
netbuf_data(datalink_buf, (void *)&playload, &playload_len);
if (playload_len > 0)
{
UdpMsg_t msg;
ip_addr_copy(msg.src_ip, *netbuf_fromaddr(datalink_buf)); // 获取UDP源IP
msg.src_port = netbuf_fromport(datalink_buf); // 获取UDP源端口
msg.len = playload_len;
msg.data = (char *)pvPortMalloc(playload_len + 1);
if (msg.data != NULL)
{
memcpy(msg.data, playload, playload_len);
msg.data[playload_len] = '\0';
if (xQueueSend(UDP_Message_Queue, &msg, 0) != pdPASS)
{
vPortFree(msg.data);
}
}
}
netbuf_delete(datalink_buf); // 释放UDP网络缓冲区
}
else
{
if (recv_err != ERR_TIMEOUT && recv_err != ERR_WOULDBLOCK)
{
printf("datalink netconn_recv err: %d\r\n", recv_err);
}
}
}
}
/**
* @brief 解析充电桩上电指令
* @note 设置桩为本地在线状态,发送上电应答
* @param stake_index 桩索引
* @param json_pack json数据包
*/
void local_on_cmd_callback_power_on(uint8_t stake_index, cJSON *json_pack)
{
if (stake_index > 6)
{
return;
}
g_charger_manager.charger_piles[stake_index - 1].is_udp_online = true; // 设置桩为本地在线状态
cJSON *root = NULL;
char *str = NULL;
root = cJSON_CreateObject();
if (root == NULL)
{
printf("Failed to create JSON object for stake %d\r\n", stake_index);
return;
}
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(root, "id", stake_index);
cJSON_AddStringToObject(root, "cmd", "power_on");
cJSON_AddNumberToObject(root, "code", 1);
str = cJSON_Print(root);
udp_send_response(stake_index, str, strlen(str));
free(str);
cJSON_Delete(root);
printf("电桩 %d 上电报文\r\n", stake_index);
}
/**
* @brief 解析充电桩计费模型请求指令
* @note 获取云快充下发的计费模型,回复给充电桩
* @param stake_index 桩索引
* @param json_pack json数据包
*/
void local_on_cmd_callback_get_billing_model(uint8_t stake_index, cJSON *json_pack)
{
if (stake_index > 6)
{
return;
}
}
void handle_udp_downlink(uint8_t id, const char *cmd, cJSON *json_pack)
{
if (cmd == NULL)
return;
if (strcmp(cmd, "power_on") == 0)
{
local_on_cmd_callback_power_on(id, json_pack);
}
else if (strcmp(cmd, "get_billing_model") == 0)
{
local_on_cmd_callback_get_billing_model(id, json_pack);
}
else
{
printf("Unknown CMD: '%s' from ID %d\r\n", cmd, id);
}
}

View File

@@ -0,0 +1,30 @@
#ifndef __CHARGERTASK_H
#define __CHARGERTASK_H
/* includes ----------------------------------------------------------------------------------------------*/
#include "global.h"
#include "lwip/opt.h"
#include "lwip/api.h"
#include "lwip/sys.h"
/* macro ------------------------------------------------------------------------------------------------*/
/* struct ------------------------------------------------------------------------------------------------*/
// 定义UDP消息体
typedef struct {
ip4_addr_t src_ip; // 来源IP
uint16_t src_port; // 来源端口
uint16_t len; // 数据长度
char *data; // 数据指针(动态分配)
} UdpMsg_t;
//void Charger_Task_Init(void);
void handle_udp_downlink(uint8_t id, const char *cmd, cJSON *json_pack);
#endif /* __CHARGERTASK_H */

View File

@@ -0,0 +1,104 @@
/**
******************************************************************************
* @file user\task\DatalinkTask.c
* @author luhuaishuai
* @version v0.1
* @date 2026-1-12
* @brief 数据链路任务,负责处理与云快充的通信
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "DataLinkTask.h"
#include "YkcTask.h"
#include "lwip/opt.h"
#include "lwip/api.h"
#include "lwip/sys.h"
#include "httpd.h"
/* typedef --------------------------------------------------------------------*/
/* variables ------------------------------------------------------------------*/
/* code -----------------------------------------------------------------------*/
/**
* @brief DownLinkTask_Function
*
* @note none
*
* @param taskID : 任务ID
*
* @retval runtime : 任务周期
*/
void DownLinkTask_Function(void const *argument)
{
uint8_t air724_rx_msg[UART1_RX_BUFFER_SIZE];
uint8_t rs485_rx_msg[UART3_RX_BUFFER_SIZE];
uint8_t *ykc_downlink_frame = NULL;
/* 等待 datalink_conn 初始化完成 */
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
/**/
while (1)
{
/*---------------------------------------------------获取任务运行状态---------------------------------------------------*/
TaskRunTimeStat.DownLinkTask.threads_runtime = GetTask_RunTime(DownLinkTaskID);
TaskRunTimeStat.DownLinkTask.threads_counter = GetTask_Beatcnt(DownLinkTaskID);
TaskRunTimeStat.DownLinkTask.threads_freestack = Get_Free_Stack(DownLinkTaskID);
if (xQueueReceive(Air724_Message_Queue, &air724_rx_msg, 10) == pdPASS)
{
if (air724_rx_msg[0] == 0x55 && air724_rx_msg[1] == 0xAA)
{
// 主指令解析
switch (air724_rx_msg[2])
{
// 云快冲下行解析
case 0x01:
{
uint8_t charger_index = air724_rx_msg[3];
uint8_t ykc_downlink_frame_len = air724_rx_msg[4];
memcpy(ykc_downlink_frame, &air724_rx_msg[5], ykc_downlink_frame_len);
SERVER_PACK frame;
frame.start_flag = ykc_downlink_frame[0];
frame.len = ykc_downlink_frame[1];
frame.serial = (ykc_downlink_frame[2] << 8) | ykc_downlink_frame[3];
frame.encrypt_flag = ykc_downlink_frame[4];
frame.frame_type = ykc_downlink_frame[5];
frame.data = (uint8_t *)pvPortMalloc(frame.len - 4);
memcpy(frame.data, ykc_downlink_frame + HEADER_LENGTH, frame.len - 4);
frame.crc = (ykc_downlink_frame[HEADER_LENGTH + frame.len] << 8) | ykc_downlink_frame[HEADER_LENGTH + frame.len + 1];
printf("Raw Bytes: ");
for (int i = 0; i < ykc_downlink_frame_len; i++)
{
printf("%02X ", ykc_downlink_frame[i]);
if ((i + 1) % 16 == 0) // 每16字节换行
{
printf("\r");
}
}
printf("\r");
handle_ykc_downlink(charger_index, &frame);
vPortFree(frame.data);
}
break;
}
}
}
if (xQueueReceive(RS485_Message_Queue, &rs485_rx_msg, 10) == pdPASS)
{
#ifdef DEBUG
printf("rs-485_rx_msg: %s", rs485_rx_msg);
#endif
}
}
}

View File

@@ -0,0 +1,18 @@
#ifndef __DATALINKTASK_H
#define __DATALINKTASK_H
/* includes ----------------------------------------------------------------------------------------------*/
#include "global.h"
/* macro ------------------------------------------------------------------------------------------------*/
/* struct ------------------------------------------------------------------------------------------------*/
void DataLink_Task_Init(void);
#endif /* __DATALINKTASK_H */

View File

@@ -0,0 +1,96 @@
/**
******************************************************************************
* @file user\task\HeartBeatTask.c
* @author luhuaishuai
* @version v0.1
* @date 2026-1-12
* @brief 心跳任务,负责发送心跳包和处理云快充的响应
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "HeartBeatTask.h"
void HeartBeat_Sign(void);
/**
* @brief 发送显示数据
* @note 发送显示数据,包括充电桩的连接状态和工作状态
*/
void Send_Display_Data(void)
{
#ifdef DEBUG
char str[128];
sprintf(str, "t1.bco=%s\xff\xff\xfft2.bco=%s\xff\xff\xfft3.bco=%s\xff\xff\xfft4.bco=%s\xff\xff\xfft5.bco=%s\xff\xff\xfft6.bco=%s\xff\xff\xff",
g_charger_manager.charger_piles[0].is_udp_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[1].is_udp_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[2].is_udp_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[3].is_udp_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[4].is_udp_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[5].is_udp_online ? "GREEN" : "RED");
Rs485_Message_Send(str, strlen(str));
sprintf(str, "t7.bco=%s\xff\xff\xfft8.bco=%s\xff\xff\xfft9.bco=%s\xff\xff\xfft10.bco=%s\xff\xff\xfft11.bco=%s\xff\xff\xfft12.bco=%s\xff\xff\xff",
g_charger_manager.charger_piles[0].is_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[1].is_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[2].is_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[3].is_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[4].is_online ? "GREEN" : "RED",
g_charger_manager.charger_piles[5].is_online ? "GREEN" : "RED");
Rs485_Message_Send(str, strlen(str));
#endif
}
/**
* @funNm : HeartbeatTask_Function
* @brief : 心跳
* @param : argument
* @retval: void
*/
void HeartbeatTask_Function(void const *argument)
{
while (1)
{
TaskRunTimeStat.HeartBeatTask.threads_runtime = GetTask_RunTime(HeartBeatTaskID);
TaskRunTimeStat.HeartBeatTask.threads_counter = GetTask_Beatcnt(HeartBeatTaskID);
TaskRunTimeStat.HeartBeatTask.threads_freestack = Get_Free_Stack(HeartBeatTaskID);
HeartBeat_Sign(); // 指示灯、蜂鸣器
// HAL_IWDG_Refresh(&hiwdg); //喂狗
osDelay(50);
}
}
/**
* @funNm : HeartBeat_Sign
* @brief : 心跳指示灯、蜂鸣器 云快充心跳
* @param : none
* @retval: void
*/
void HeartBeat_Sign(void)
{
if (g_charger_manager.charger_piles[0].is_online)
{
RUN_EVERY(8000, tick_A, {
charger_to_server_0X03(1, 1, 0);
});
RUN_EVERY(100, tick_B, {
System_Mode_Led_Toggle();
});
}
else
{
RUN_EVERY(500, tick_C, {
System_Mode_Led_Toggle();
});
}
RUN_EVERY(1000, tick_D, {
System_Run_Led_Toggle();
});
RUN_EVERY(500, tick_E, {
Send_Display_Data();
});
}

View File

@@ -0,0 +1,22 @@
#ifndef __HEATBEATTASK_H
#define __HEATBEATTASK_H
/* includes ----------------------------------------------------------------------------------------------*/
#include "global.h"
#include "cmsis_os.h" // 或使用 HAL_GetTick() 等
// 获取当前系统时间(毫秒)
//#define GET_TICK() (osKernelSysTick()) // FreeRTOS/CMSIS-RTOS
#define GET_TICK() (HAL_GetTick()) // STM32 HAL 用户可换这行
// 非阻塞延时宏:每 interval_ms 执行一次 {code}
#define RUN_EVERY(interval_ms, static_tick_var, code) \
do { \
static uint32_t static_tick_var = 0; \
if ((GET_TICK() - static_tick_var) >= (interval_ms)) { \
static_tick_var = GET_TICK(); \
code; \
} \
} while(0)
#endif /* __HEATBEATTASK_H */

101
Core/User/Task/YkcTask.c Normal file
View File

@@ -0,0 +1,101 @@
/**
******************************************************************************
* @file user\task\HeartBeatTask.c
* @author luhuaishuai
* @version v0.1
* @date 2026-1-12
* @brief Briefly describe the function of your function
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "YkcTask.h"
#include "lwip/opt.h"
#include "lwip/api.h"
#include "lwip/sys.h"
/**
* @funNm : YkcTask_Function
* @brief : 云快充平台交互任务
* @param : argument
* @retval: void
*/
uint8_t SETP = 0;
void YkcTask_Function(void const *argument)
{
init_chargers(); /* 初始化桩结构体*/
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); /* 等待桩通讯协议层完成*/
while (1)
{
TaskRunTimeStat.YkcTask.threads_runtime = GetTask_RunTime(YkcTaskID);
TaskRunTimeStat.YkcTask.threads_counter = GetTask_Beatcnt(YkcTaskID);
TaskRunTimeStat.YkcTask.threads_freestack = Get_Free_Stack(YkcTaskID);
switch (SETP)
{
case 0:
{
if (!g_charger_manager.charger_piles[1 - 1].is_online)
{
charger_to_server_0X01(1);
}
else
{
SETP = 1;
}
}
break;
case 1:
{
if (!g_charger_manager.charger_piles[1 - 1].get_model)
charger_to_server_0X09(1); // 桩1计费模型请求
else
SETP = 2;
}
break;
case 2:
{
charger_to_server_0X13(1, 1); // 上传状态
}
break;
default:
break;
}
osDelay(2000);
}
}
/**
* @brief 解析云快充平台下行数据
* @note 解析云快充平台下行数据,根据不同的帧类型调用相应的处理函数
* @param stake_mark 桩编号
* @param pack 指向SERVER_PACK结构体的指针包含下行数据
*/
void handle_ykc_downlink(uint8_t stake_mark, SERVER_PACK *pack)
{
switch (pack->frame_type)
{
case FRAME_TYPE_0X02:
on_cmd_frame_type_0X02(stake_mark, pack);
break;
case FRAME_TYPE_0X04:
on_cmd_frame_type_0X04(stake_mark, pack);
break;
case FRAME_TYPE_0X06:
on_cmd_frame_type_0X06(stake_mark, pack);
break;
case FRAME_TYPE_0X0A:
on_cmd_frame_type_0X0A(stake_mark, pack);
break;
case FRAME_TYPE_0X58:
on_cmd_frame_type_0X58(stake_mark, pack);
break;
case FRAME_TYPE_0X34:
on_cmd_frame_type_0X34(stake_mark, pack);
break;
}
}

11
Core/User/Task/YkcTask.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef __YKCTASK_H
#define __YKCTASK_H
/* includes ----------------------------------------------------------------------------------------------*/
#include "global.h"
void handle_ykc_downlink(uint8_t stake_mark, SERVER_PACK *pack);
#endif /* __YKCTASK_H */

View File