docs(ota_mutex): 添加函数注释说明

This commit is contained in:
小龙虾
2026-06-15 01:14:29 +08:00
parent 929faf1d38
commit 2875ef2145
17 changed files with 1026 additions and 0 deletions
+7
View File
@@ -20,12 +20,19 @@
// ============================================================
// Protocol 单例
// ============================================================
/**
* @brief 获取 Protocol 单例实例。
* @return Protocol 单例引用。
*/
Protocol& Protocol::instance()
{
static Protocol inst;
return inst;
}
/**
* @brief 初始化协议层,打印初始化信息并设置初始化标志。
*/
void Protocol::init()
{
if (m_initialized) return;
+24
View File
@@ -37,13 +37,37 @@ typedef struct {
*/
class Protocol {
public:
/**
* @brief 获取 Protocol 单例实例。
* @return Protocol 单例引用。
*/
static Protocol& instance();
/**
* @brief 初始化协议层。
*/
void init();
/**
* @brief 生成基于时间戳、地址和随机种子的扰码 nonce。
* @param timestamp 时间戳。
* @param mem_addr 内存地址。
* @param rand_seed 随机种子。
* @param nonce_out 输出的 16 字节 nonce 缓冲区。
*/
static void scrambleGenerate(uint32_t timestamp, uint32_t mem_addr,
uint32_t rand_seed, uint8_t *nonce_out);
/**
* @brief 会话密钥初始化,将密钥拷贝到会话上下文。
* @param sess 会话上下文指针。
* @param k_session 会话密钥数据。
* @return 成功返回 0,参数无效返回 -1。
*/
static int sessionKeyInit(session_ctx_t *sess, const uint8_t *k_session);
/**
* @brief 清除会话上下文的密钥和 IV 数据。
* @param sess 会话上下文指针。
*/
static void sessionKeyClear(session_ctx_t *sess);
private:
+173
View File
@@ -23,6 +23,10 @@ static constexpr size_t kSpiTransferMaxBytes = 256U;
// ============================================================
// 单例
// ============================================================
/**
* @brief 获取 SpiMaster 单例实例(单例模式)。
* @return SpiMaster 单例引用。
*/
SpiMaster& SpiMaster::instance()
{
static SpiMaster inst;
@@ -32,6 +36,15 @@ SpiMaster& SpiMaster::instance()
// ============================================================
// 内部 SPI 传输
// ============================================================
/**
* @brief 执行底层 SPI 时钟同步传输。
* @param tx_buf 发送数据缓冲区,可为 NULL。
* @param tx_len 发送数据长度。
* @param rx_buf 接收数据缓冲区,可为 NULL。
* @param rx_len 接收数据长度。
* @param timeout_ms 超时时间(毫秒),0 表示无限等待。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::transferClocked(const uint8_t *tx_buf, size_t tx_len,
uint8_t *rx_buf, size_t rx_len,
uint32_t timeout_ms)
@@ -100,6 +113,16 @@ esp_err_t SpiMaster::transferClocked(const uint8_t *tx_buf, size_t tx_len,
return ESP_OK;
}
/**
* @brief 执行 SPI 请求-响应组合传输。
* @param cmd 命令字节(0 表示无命令)。
* @param payload 负载数据缓冲区。
* @param payload_len 负载数据长度。
* @param rx_buf 接收数据缓冲区。
* @param rx_len 期望接收的数据长度。
* @param timeout_ms 超时时间(毫秒),0 表示无限等待。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::transferRequestResponse(uint8_t cmd,
const uint8_t *payload, size_t payload_len,
uint8_t *rx_buf, size_t rx_len,
@@ -146,6 +169,12 @@ esp_err_t SpiMaster::transferRequestResponse(uint8_t cmd,
// ============================================================
// OTA 帧工具
// ============================================================
/**
* @brief 计算 OTA 帧的 8 位 CRC 校验和。
* @param data 数据缓冲区。
* @param len 数据长度。
* @return 计算得到的 CRC 值。
*/
uint8_t SpiMaster::otaCrc8(const uint8_t *data, size_t len)
{
uint8_t crc = 0x00;
@@ -155,6 +184,15 @@ uint8_t SpiMaster::otaCrc8(const uint8_t *data, size_t len)
return crc;
}
/**
* @brief 构建 OTA 协议帧。
* @param type 帧类型。
* @param payload 负载数据缓冲区。
* @param payload_len 负载数据长度。
* @param out 输出帧缓冲区。
* @param out_len 输出实际帧长度。
* @return true 表示构建成功,false 表示失败。
*/
bool SpiMaster::otaBuildFrame(uint8_t type, const uint8_t *payload, size_t payload_len,
uint8_t *out, size_t *out_len)
{
@@ -173,6 +211,15 @@ bool SpiMaster::otaBuildFrame(uint8_t type, const uint8_t *payload, size_t paylo
return true;
}
/**
* @brief 解析 OTA 协议帧。
* @param frame 帧数据缓冲区。
* @param frame_len 帧数据长度。
* @param type 输出解析得到的帧类型。
* @param payload 输出指向负载数据的指针。
* @param payload_len 输出负载数据长度。
* @return true 表示解析成功,false 表示失败。
*/
bool SpiMaster::otaParseFrame(const uint8_t *frame, size_t frame_len,
uint8_t *type, const uint8_t **payload, size_t *payload_len)
{
@@ -192,6 +239,16 @@ bool SpiMaster::otaParseFrame(const uint8_t *frame, size_t frame_len,
return true;
}
/**
* @brief 在数据缓冲区中搜索并解析 OTA 帧。
* @param buf 数据缓冲区。
* @param buf_len 数据缓冲区长度。
* @param type 输出帧类型。
* @param payload 输出指向负载数据的指针。
* @param payload_len 输出负载数据长度。
* @param frame_offset 输出帧在缓冲区中的偏移位置。
* @return true 表示找到并成功解析,false 表示未找到。
*/
bool SpiMaster::otaFindFrame(const uint8_t *buf, size_t buf_len,
uint8_t *type, const uint8_t **payload, size_t *payload_len,
size_t *frame_offset)
@@ -222,6 +279,10 @@ bool SpiMaster::otaFindFrame(const uint8_t *buf, size_t buf_len,
// ============================================================
// 初始化
// ============================================================
/**
* @brief 初始化 SPI2 总线与目标从设备。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::init()
{
LockGuard lock(m_mutex);
@@ -274,6 +335,16 @@ esp_err_t SpiMaster::init()
// ============================================================
// 公开 SPI 传输接口
// ============================================================
/**
* @brief 执行一次同步 SPI 事务,先发送请求,再读取响应字节。
* @param cmd 命令字节。
* @param payload 负载数据缓冲区。
* @param payload_len 负载数据长度。
* @param recv_buf 接收数据缓冲区。
* @param recv_len 接收数据长度。
* @param timeout_ms 超时时间(毫秒)。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::syncTransfer(uint8_t cmd, const uint8_t *payload,
size_t payload_len, uint8_t *recv_buf,
size_t recv_len, uint32_t timeout_ms)
@@ -282,12 +353,27 @@ esp_err_t SpiMaster::syncTransfer(uint8_t cmd, const uint8_t *payload,
return transferRequestResponse(cmd, payload, payload_len, recv_buf, recv_len, timeout_ms);
}
/**
* @brief 仅发送 SPI 请求数据,不关心返回内容。
* @param cmd 命令字节。
* @param payload 负载数据缓冲区。
* @param len 负载数据长度。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::send(uint8_t cmd, const uint8_t *payload, size_t len)
{
LockGuard lock(m_mutex);
return transferRequestResponse(cmd, payload, len, NULL, 0, 0U);
}
/**
* @brief 仅接收 SPI 数据,必要时先发送命令字节。
* @param cmd 命令字节。
* @param recv_buf 接收数据缓冲区。
* @param len 接收数据长度。
* @param timeout_ms 超时时间(毫秒)。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::recv(uint8_t cmd, uint8_t *recv_buf, size_t len, uint32_t timeout_ms)
{
LockGuard lock(m_mutex);
@@ -297,6 +383,18 @@ esp_err_t SpiMaster::recv(uint8_t cmd, uint8_t *recv_buf, size_t len, uint32_t t
// ============================================================
// SPI OTA 协议
// ============================================================
/**
* @brief 按 GD32 SPI OTA 最小协议执行一次请求加响应读取。
* @param type 请求帧类型。
* @param payload 负载数据缓冲区。
* @param payload_len 负载数据长度。
* @param resp_type 输出响应类型。
* @param resp_payload 输出响应负载缓冲区。
* @param resp_max 响应负载缓冲区最大长度。
* @param resp_len 输出实际响应长度。
* @param timeout_ms 超时时间(毫秒)。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::otaTransfer(uint8_t type, const uint8_t *payload, size_t payload_len,
uint8_t *resp_type, uint8_t *resp_payload,
size_t resp_max, size_t *resp_len, uint32_t timeout_ms)
@@ -319,6 +417,15 @@ esp_err_t SpiMaster::otaTransfer(uint8_t type, const uint8_t *payload, size_t pa
return otaPollResponseUnlocked(resp_type, resp_payload, resp_max, resp_len, timeout_ms);
}
/**
* @brief 仅执行 SPI OTA 响应读取事务,不重复发送请求帧。
* @param resp_type 输出响应类型。
* @param resp_payload 输出响应负载缓冲区。
* @param resp_max 响应负载缓冲区最大长度。
* @param resp_len 输出实际响应长度。
* @param timeout_ms 超时时间(毫秒)。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::otaPollResponse(uint8_t *resp_type, uint8_t *resp_payload,
size_t resp_max, size_t *resp_len, uint32_t timeout_ms)
{
@@ -326,6 +433,15 @@ esp_err_t SpiMaster::otaPollResponse(uint8_t *resp_type, uint8_t *resp_payload,
return otaPollResponseUnlocked(resp_type, resp_payload, resp_max, resp_len, timeout_ms);
}
/**
* @brief 无锁版本的 OTA 响应轮询实现。
* @param resp_type 输出响应类型。
* @param resp_payload 输出响应负载缓冲区。
* @param resp_max 响应负载缓冲区最大长度。
* @param resp_len 输出实际响应长度。
* @param timeout_ms 超时时间(毫秒)。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::otaPollResponseUnlocked(uint8_t *resp_type, uint8_t *resp_payload,
size_t resp_max, size_t *resp_len, uint32_t timeout_ms)
{
@@ -390,6 +506,10 @@ esp_err_t SpiMaster::otaPollResponseUnlocked(uint8_t *resp_type, uint8_t *resp_p
return ESP_ERR_TIMEOUT;
}
/**
* @brief 通知 GD32 DCU_APP 设置 OTA 标志并软复位进入 Bootloader。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::appRequestBootloader()
{
LockGuard lock(m_mutex);
@@ -429,6 +549,13 @@ esp_err_t SpiMaster::appRequestBootloader()
return ESP_ERR_TIMEOUT;
}
/**
* @brief 向 GD32 APP 发送存活探测命令,等待 APP 回复 ALIVE ACK。
* @param timeout_ms 超时时间(毫秒)。
* @param uptime_sec 输出设备运行时间(秒)。
* @param version 输出 GD32 版本信息(可选)。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::appPing(uint32_t timeout_ms, uint32_t *uptime_sec, gd32_version_t *version)
{
LockGuard lock(m_mutex);
@@ -546,6 +673,16 @@ esp_err_t SpiMaster::appPing(uint32_t timeout_ms, uint32_t *uptime_sec, gd32_ver
return ESP_ERR_TIMEOUT;
}
/**
* @brief 执行带重试的 SPI 同步传输。
* @param cmd 命令字节。
* @param payload 负载数据缓冲区。
* @param payload_len 负载数据长度。
* @param recv_buf 接收数据缓冲区。
* @param recv_len 接收数据长度。
* @param max_retries 最大重试次数。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::transferWithRetry(uint8_t cmd, const uint8_t *payload,
size_t payload_len, uint8_t *recv_buf,
size_t recv_len, int max_retries)
@@ -579,6 +716,17 @@ esp_err_t SpiMaster::transferWithRetry(uint8_t cmd, const uint8_t *payload,
// ============================================================
// CANFD 接口
// ============================================================
/**
* @brief 发送一帧 CANFD 数据,可选等待并读取响应内容。
* @param channel CAN 通道编号(0~2)。
* @param ext_id 扩展帧 ID(29 位)。
* @param data CAN 数据负载。
* @param dlc 数据长度。
* @param recv_buf 接收数据缓冲区。
* @param recv_max 接收缓冲区最大长度。
* @param timeout_ms 超时时间(毫秒)。
* @return 成功时返回接收数据长度,失败返回 -1。
*/
int SpiMaster::canSend(can_channel_t channel, uint32_t ext_id,
const uint8_t *data, uint8_t dlc,
uint8_t *recv_buf, uint8_t recv_max,
@@ -608,6 +756,14 @@ int SpiMaster::canSend(can_channel_t channel, uint32_t ext_id,
return (int)recv_max;
}
/**
* @brief 发送一帧无需响应的 CANFD 数据。
* @param channel CAN 通道编号(0~2)。
* @param ext_id 扩展帧 ID(29 位)。
* @param data CAN 数据负载。
* @param dlc 数据长度。
* @return 成功返回 0,失败返回 -1。
*/
int SpiMaster::canSendNoReply(can_channel_t channel, uint32_t ext_id,
const uint8_t *data, uint8_t dlc)
{
@@ -638,6 +794,16 @@ int SpiMaster::canSendNoReply(can_channel_t channel, uint32_t ext_id,
// ============================================================
// 无锁内部版本实现
// ============================================================
/**
* @brief 无锁版本的同步 SPI 传输实现。
* @param cmd 命令字节。
* @param payload 负载数据缓冲区。
* @param payload_len 负载数据长度。
* @param recv_buf 接收数据缓冲区。
* @param recv_len 接收数据长度。
* @param timeout_ms 超时时间(毫秒)。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::syncTransferUnlocked(uint8_t cmd, const uint8_t *payload,
size_t payload_len, uint8_t *recv_buf,
size_t recv_len, uint32_t timeout_ms)
@@ -645,6 +811,13 @@ esp_err_t SpiMaster::syncTransferUnlocked(uint8_t cmd, const uint8_t *payload,
return transferRequestResponse(cmd, payload, payload_len, recv_buf, recv_len, timeout_ms);
}
/**
* @brief 无锁版本的 SPI 发送实现。
* @param cmd 命令字节。
* @param payload 负载数据缓冲区。
* @param len 负载数据长度。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t SpiMaster::sendUnlocked(uint8_t cmd, const uint8_t *payload, size_t len)
{
return transferRequestResponse(cmd, payload, len, NULL, 0, 0U);
+108
View File
@@ -74,6 +74,10 @@ typedef struct {
*/
class SpiMaster {
public:
/**
* @brief 获取 SpiMaster 单例实例。
* @return SpiMaster 单例引用。
*/
static SpiMaster& instance();
/** @brief 初始化 SPI2 总线与目标从设备。 */
@@ -136,9 +140,21 @@ public:
spi_device_handle_t deviceHandle() const { return m_spiDevice; }
private:
/**
* @brief 私有默认构造函数。
*/
SpiMaster() = default;
/**
* @brief 私有析构函数。
*/
~SpiMaster() = default;
/**
* @brief 禁止复制构造。
*/
SpiMaster(const SpiMaster&) = delete;
/**
* @brief 禁止复制赋值。
*/
SpiMaster& operator=(const SpiMaster&) = delete;
spi_device_handle_t m_spiDevice = NULL;
@@ -147,40 +163,132 @@ private:
// RAII 锁守卫
class LockGuard {
public:
/**
* @brief 构造加锁守卫,获取互斥锁。
* @param mutex 互斥锁句柄。
*/
explicit LockGuard(SemaphoreHandle_t mutex) : m_lock(mutex) {
if (m_lock) { xSemaphoreTake(m_lock, portMAX_DELAY); }
}
/**
* @brief 析构解锁守卫,释放互斥锁。
*/
~LockGuard() {
if (m_lock) { xSemaphoreGive(m_lock); }
}
/**
* @brief 禁止复制构造。
*/
LockGuard(const LockGuard&) = delete;
/**
* @brief 禁止复制赋值。
*/
LockGuard& operator=(const LockGuard&) = delete;
private:
SemaphoreHandle_t m_lock;
};
// 内部 SPI 传输
/**
* @brief 执行底层 SPI 时钟同步传输。
* @param tx_buf 发送数据缓冲区。
* @param tx_len 发送数据长度。
* @param rx_buf 接收数据缓冲区。
* @param rx_len 接收数据长度。
* @param timeout_ms 超时时间(毫秒),0 表示无限等待。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t transferClocked(const uint8_t *tx_buf, size_t tx_len,
uint8_t *rx_buf, size_t rx_len,
uint32_t timeout_ms = 0U);
/**
* @brief 执行 SPI 请求-响应组合传输。
* @param cmd 命令字节(0 表示无命令)。
* @param payload 负载数据缓冲区。
* @param payload_len 负载数据长度。
* @param rx_buf 接收数据缓冲区。
* @param rx_len 期望接收的数据长度。
* @param timeout_ms 超时时间(毫秒),0 表示无限等待。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t transferRequestResponse(uint8_t cmd, const uint8_t *payload, size_t payload_len,
uint8_t *rx_buf, size_t rx_len,
uint32_t timeout_ms = 0U);
// 无锁内部版本(供已持锁的公共方法调用,避免递归死锁)
/**
* @brief 无锁版本的同步 SPI 传输(调用者须已持有锁)。
* @param cmd 命令字节。
* @param payload 负载数据缓冲区。
* @param payload_len 负载数据长度。
* @param recv_buf 接收数据缓冲区。
* @param recv_len 接收数据长度。
* @param timeout_ms 超时时间(毫秒)。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t syncTransferUnlocked(uint8_t cmd, const uint8_t *payload,
size_t payload_len, uint8_t *recv_buf,
size_t recv_len, uint32_t timeout_ms);
/**
* @brief 无锁版本的 SPI 发送(调用者须已持有锁)。
* @param cmd 命令字节。
* @param payload 负载数据缓冲区。
* @param len 负载数据长度。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t sendUnlocked(uint8_t cmd, const uint8_t *payload, size_t len);
/**
* @brief 无锁版本的 OTA 响应轮询(调用者须已持有锁)。
* @param resp_type 输出响应类型。
* @param resp_payload 输出响应负载缓冲区。
* @param resp_max 响应负载缓冲区最大长度。
* @param resp_len 输出实际响应长度。
* @param timeout_ms 超时时间(毫秒)。
* @return ESP_OK 表示成功,否则返回错误码。
*/
esp_err_t otaPollResponseUnlocked(uint8_t *resp_type, uint8_t *resp_payload,
size_t resp_max, size_t *resp_len, uint32_t timeout_ms);
// OTA 帧工具(静态,无状态)
/**
* @brief 计算 OTA 帧的 8 位 CRC 校验和。
* @param data 数据缓冲区。
* @param len 数据长度。
* @return 计算得到的 CRC 值。
*/
static uint8_t otaCrc8(const uint8_t *data, size_t len);
/**
* @brief 构建 OTA 协议帧。
* @param type 帧类型。
* @param payload 负载数据缓冲区。
* @param payload_len 负载数据长度。
* @param out 输出帧缓冲区。
* @param out_len 输出实际帧长度。
* @return true 表示构建成功,false 表示失败。
*/
static bool otaBuildFrame(uint8_t type, const uint8_t *payload, size_t payload_len,
uint8_t *out, size_t *out_len);
/**
* @brief 解析 OTA 协议帧。
* @param frame 帧数据缓冲区。
* @param frame_len 帧数据长度。
* @param type 输出解析得到的帧类型。
* @param payload 输出指向负载数据的指针。
* @param payload_len 输出负载数据长度。
* @return true 表示解析成功,false 表示失败。
*/
static bool otaParseFrame(const uint8_t *frame, size_t frame_len,
uint8_t *type, const uint8_t **payload, size_t *payload_len);
/**
* @brief 在数据缓冲区中搜索并解析 OTA 帧。
* @param buf 数据缓冲区。
* @param buf_len 数据缓冲区长度。
* @param type 输出帧类型。
* @param payload 输出指向负载数据的指针。
* @param payload_len 输出负载数据长度。
* @param frame_offset 输出帧在缓冲区中的偏移位置。
* @return true 表示找到并成功解析,false 表示未找到。
*/
static bool otaFindFrame(const uint8_t *buf, size_t buf_len,
uint8_t *type, const uint8_t **payload, size_t *payload_len,
size_t *frame_offset);