feat(wifi_ota): 添加CAN帧转发功能

添加了HTTP接口用于转发CAN帧,包括/can/send POST接口用于发送CAN帧,
以及/can/status GET接口用于查询CAN通道状态。同时集成了SPI master驱动
以支持与GD32之间的CANFD通信,并定义了完整的CAN帧格式和通道管理机制。

BREAKING CHANGE: 移除了OTA上位机工具脚本
This commit is contained in:
小龙虾
2026-04-27 03:22:49 +08:00
parent df27ef3a98
commit 47ffe1bd54
5 changed files with 358 additions and 660 deletions
+85
View File
@@ -2,12 +2,15 @@
* spi_master.c - SPI Master 驱动实现
* T1.1 - T1.4
* ESP-IDF 6.0 适配版本
* CANFD 3通道发送接口
*/
#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "soc/spi_struct.h"
#include "esp_err.h"
#include "spi_master.h"
#include "monitor/debug_log.h"
@@ -99,6 +102,88 @@ esp_err_t spi_recv(uint8_t cmd, uint8_t *recv_buf, size_t len, uint32_t timeout_
return spi_sync_transfer(cmd, dummy, len, recv_buf, len, timeout_ms);
}
// CANFD 帧发送
// 帧格式: [通道(1B)] [扩展帧ID(4B BE)] [数据(1~64B)]
static int can_frame_send(can_channel_t channel, uint32_t ext_id,
const uint8_t *data, uint8_t dlc,
uint8_t *recv_buf, uint8_t recv_max,
uint32_t timeout_ms)
{
if (g_spi_device == NULL) {
return -1;
}
if (dlc == 0 || dlc > CAN_MAX_DLC) {
DEBUG_ERR("SPI", "DLC 无效: %d", dlc);
return -2;
}
if (channel >= CAN_CHANNEL_MAX) {
DEBUG_ERR("SPI", "通道号无效: %d", channel);
return -3;
}
// 构建 CANFD 帧: [通道(1B)] [EXT_ID(4B)] [DATA(dlc B)]
uint8_t tx_buf[1 + 4 + CAN_MAX_DLC] = {0};
tx_buf[0] = (uint8_t)channel;
tx_buf[1] = (uint8_t)((ext_id >> 24) & 0xFF);
tx_buf[2] = (uint8_t)((ext_id >> 16) & 0xFF);
tx_buf[3] = (uint8_t)((ext_id >> 8) & 0xFF);
tx_buf[4] = (uint8_t)(ext_id & 0xFF);
memcpy(&tx_buf[5], data, dlc);
size_t tx_len = 5 + dlc;
uint8_t rx_buf[5 + CAN_MAX_DLC] = {0};
spi_transaction_t t = {};
t.tx_buffer = tx_buf;
t.rx_buffer = rx_buf;
t.length = tx_len * 8;
t.rxlength = (recv_max > 0) ? (5 + recv_max) * 8 : 0;
esp_err_t ret = spi_device_transmit(g_spi_device, &t);
if (ret != ESP_OK) {
DEBUG_ERR("SPI", "CAN[ch%d] ID=0x%08X 发送失败: %s",
channel, ext_id, esp_err_to_name(ret));
return -4;
}
if (recv_buf && recv_max > 0) {
// 解析 GD32 响应帧
uint8_t resp_channel = rx_buf[0];
uint32_t resp_id = (rx_buf[1] << 24) | (rx_buf[2] << 16)
| (rx_buf[3] << 8) | rx_buf[4];
uint8_t resp_dlc = resp_id >> 24; // 复用了 ID 字段低字节作为 DLC
resp_id &= 0x00FFFFFF;
uint8_t copy_len = (resp_dlc < recv_max) ? resp_dlc : recv_max;
memcpy(recv_buf, &rx_buf[5], copy_len);
DEBUG_INFO("SPI", "CAN[ch%d] ID=0x%08X 响应 DLC=%d",
resp_channel, resp_id, copy_len);
return copy_len;
}
return 0;
}
int gd32_can_send(can_channel_t channel, uint32_t ext_id,
const uint8_t *data, uint8_t dlc,
uint8_t *recv_buf, uint8_t recv_max,
uint32_t timeout_ms)
{
if (dlc == 0) {
dlc = (data != NULL) ? 64 : 0; // 避免 dlc=0 的情况
}
return can_frame_send(channel, ext_id, data, dlc, recv_buf, recv_max, timeout_ms);
}
int gd32_can_send_no_reply(can_channel_t channel, uint32_t ext_id,
const uint8_t *data, uint8_t dlc)
{
return can_frame_send(channel, ext_id, data, dlc, NULL, 0, 0);
}
// 带重试的 SPI 传输
esp_err_t spi_transfer_with_retry(uint8_t cmd, const uint8_t *payload,
size_t payload_len, uint8_t *recv_buf,