功能:集成SU-03T语音模块,完善UI代码文档
- 在CMakeLists.txt中添加SU-03T语音模块依赖。 - 在main.cpp中实现SU-03T接收回调函数,处理接收消息。 - 完善各UI源文件文档,包括动作、屏幕和字体,明确模块作用与数据流向。 - 更新主应用逻辑,初始化并启动SU-03T接收器。 - 修改过程中确保兼容性,保留原有接口。
This commit is contained in:
3
components/su-03t/CMakeLists.txt
Normal file
3
components/su-03t/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "su-03t.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES driver)
|
||||
113
components/su-03t/include/su-03t.h
Normal file
113
components/su-03t/include/su-03t.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 文件: components/su-03t/include/su-03t.h
|
||||
* 角色: SU-03T 语音模块串口协议适配
|
||||
* 说明:
|
||||
* - 本文件用于实现当前模块的核心功能或接口定义。
|
||||
* - 修改前请先确认该模块与其它任务/外设之间的数据流关系。
|
||||
* - 涉及协议与硬件时,优先保持现有接口兼容,避免联调回归。
|
||||
*/
|
||||
|
||||
/*
|
||||
* SU-03T 语音模块接口声明
|
||||
*
|
||||
* 说明:
|
||||
* 1) 固定使用 UART2,默认引脚 RX=IO41 / TX=IO42,波特率 115200。
|
||||
* 2) 提供固定帧协议封装与原始十六进制发送两种方式。
|
||||
* 3) 提供同步收帧与后台异步回调,便于语音事件实时上报。
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SU03T_UART_PORT UART_NUM_2
|
||||
#define SU03T_UART_BAUD 115200
|
||||
#define SU03T_UART_TX_IO GPIO_NUM_42
|
||||
#define SU03T_UART_RX_IO GPIO_NUM_41
|
||||
|
||||
#define SU03T_DEFAULT_HEAD_H 0xAA
|
||||
#define SU03T_DEFAULT_HEAD_L 0x55
|
||||
#define SU03T_DEFAULT_TAIL_H 0x55
|
||||
#define SU03T_DEFAULT_TAIL_L 0xAA
|
||||
|
||||
#define SU03T_MAX_PARAM_LEN 256
|
||||
|
||||
typedef struct {
|
||||
uint8_t head_h;
|
||||
uint8_t head_l;
|
||||
uint8_t tail_h;
|
||||
uint8_t tail_l;
|
||||
} su03t_frame_format_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t msgno;
|
||||
uint8_t params[SU03T_MAX_PARAM_LEN];
|
||||
size_t params_len;
|
||||
} su03t_frame_t;
|
||||
|
||||
/* 异步接收回调: 每收到一帧完整数据触发一次。 */
|
||||
typedef void (*su03t_rx_callback_t)(const su03t_frame_t *frame, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief 初始化 SU-03T 串口驱动
|
||||
* @return ESP_OK 成功;其它为驱动安装或串口配置错误
|
||||
*/
|
||||
esp_err_t su03t_init(void);
|
||||
|
||||
/**
|
||||
* @brief 配置帧头帧尾(默认 AA55 ... 55AA)
|
||||
*/
|
||||
esp_err_t su03t_set_frame_format(const su03t_frame_format_t *fmt);
|
||||
|
||||
/**
|
||||
* @brief 获取当前帧格式配置
|
||||
*/
|
||||
esp_err_t su03t_get_frame_format(su03t_frame_format_t *fmt_out);
|
||||
|
||||
/**
|
||||
* @brief 发送固定格式消息
|
||||
* @param msgno 消息编号(1字节)
|
||||
* @param params 参数区首地址,可为 NULL(当 params_len 为 0)
|
||||
* @param params_len 参数区长度
|
||||
*/
|
||||
esp_err_t su03t_send_frame(uint8_t msgno, const uint8_t *params, size_t params_len);
|
||||
|
||||
/**
|
||||
* @brief 发送十六进制字符串(支持空格分隔)
|
||||
* @param hex_string 十六进制文本,例如 "AA 55 01 11 22 55 AA"
|
||||
*/
|
||||
esp_err_t su03t_send_hex_string(const char *hex_string);
|
||||
|
||||
/**
|
||||
* @brief 同步接收并解析一帧消息
|
||||
* @param out_frame 输出帧
|
||||
* @param timeout_ms 超时时间(毫秒)
|
||||
*/
|
||||
esp_err_t su03t_recv_frame(su03t_frame_t *out_frame, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief 启动后台接收任务,持续解析并回调
|
||||
* @param callback 收到完整帧时的回调函数
|
||||
* @param user_ctx 回调透传上下文指针
|
||||
* @param task_stack_size 接收任务栈大小,传 0 使用默认值
|
||||
* @param task_priority 接收任务优先级,传 0 使用默认值
|
||||
*/
|
||||
esp_err_t su03t_start_receiver(su03t_rx_callback_t callback, void *user_ctx, uint32_t task_stack_size, UBaseType_t task_priority);
|
||||
|
||||
/**
|
||||
* @brief 停止后台接收任务
|
||||
*/
|
||||
esp_err_t su03t_stop_receiver(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
397
components/su-03t/su-03t.c
Normal file
397
components/su-03t/su-03t.c
Normal file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* 文件: components/su-03t/su-03t.c
|
||||
* 角色: SU-03T 语音模块串口协议适配
|
||||
* 说明:
|
||||
* - 本文件用于实现当前模块的核心功能或接口定义。
|
||||
* - 修改前请先确认该模块与其它任务/外设之间的数据流关系。
|
||||
* - 涉及协议与硬件时,优先保持现有接口兼容,避免联调回归。
|
||||
*/
|
||||
|
||||
/*
|
||||
* SU-03T 语音模块串口适配层
|
||||
*
|
||||
* 设计目标:
|
||||
* 1) 提供统一的 UART2 初始化接口(RX=IO41, TX=IO42, 115200)。
|
||||
* 2) 支持两类发送方式:
|
||||
* - 结构化帧发送: 头(2) + msgno(1) + params(N) + 尾(2)
|
||||
* - 原始十六进制串发送: 方便调试工具或协议快速验证。
|
||||
* 3) 支持同步收帧与后台异步收帧回调,满足“主控主动发、模块随时上报”的场景。
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_log.h"
|
||||
#include "su-03t.h"
|
||||
|
||||
static const char *TAG = "su03t";
|
||||
|
||||
static bool s_inited = false;
|
||||
static TaskHandle_t s_rx_task = NULL;
|
||||
static volatile bool s_rx_running = false;
|
||||
static su03t_rx_callback_t s_rx_callback = NULL;
|
||||
static void *s_rx_user_ctx = NULL;
|
||||
static su03t_frame_format_t s_fmt = {
|
||||
.head_h = SU03T_DEFAULT_HEAD_H,
|
||||
.head_l = SU03T_DEFAULT_HEAD_L,
|
||||
.tail_h = SU03T_DEFAULT_TAIL_H,
|
||||
.tail_l = SU03T_DEFAULT_TAIL_L,
|
||||
};
|
||||
|
||||
/*
|
||||
* 将单个十六进制字符转换为 4bit 数值。
|
||||
* 返回值:
|
||||
* - 0~15: 有效十六进制字符
|
||||
* - -1 : 非法字符
|
||||
*/
|
||||
/* 函数: hex_to_nibble
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
static int hex_to_nibble(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SU-03T 后台接收任务。
|
||||
* 逻辑:
|
||||
* 1) 循环调用 su03t_recv_frame() 解析帧。
|
||||
* 2) 解析成功后,回调上层业务。
|
||||
* 3) 超时属于正常空闲,不打印告警;仅对非超时错误告警。
|
||||
*/
|
||||
/* 函数: su03t_rx_task
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
static void su03t_rx_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
su03t_frame_t frame;
|
||||
while (s_rx_running) {
|
||||
esp_err_t ret = su03t_recv_frame(&frame, 200);
|
||||
if (ret == ESP_OK) {
|
||||
if (s_rx_callback != NULL) {
|
||||
s_rx_callback(&frame, s_rx_user_ctx);
|
||||
}
|
||||
} else if (ret != ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGW(TAG, "recv frame failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
|
||||
s_rx_task = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* 初始化 UART2 驱动并配置 SU-03T 引脚。
|
||||
* 注意: 若驱动已安装,uart_driver_install 可能返回 ESP_ERR_INVALID_STATE,
|
||||
* 这里将其视为“可继续配置”的情况。
|
||||
*/
|
||||
/* 函数: su03t_init
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
esp_err_t su03t_init(void)
|
||||
{
|
||||
const uart_config_t uart_cfg = {
|
||||
.baud_rate = SU03T_UART_BAUD,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
};
|
||||
|
||||
esp_err_t ret = uart_driver_install(SU03T_UART_PORT, 1024, 1024, 0, NULL, 0);
|
||||
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = uart_param_config(SU03T_UART_PORT, &uart_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = uart_set_pin(SU03T_UART_PORT, SU03T_UART_TX_IO, SU03T_UART_RX_IO,
|
||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
s_inited = true;
|
||||
ESP_LOGI(TAG, "init UART2 done, TX=%d RX=%d baud=%d",
|
||||
SU03T_UART_TX_IO, SU03T_UART_RX_IO, SU03T_UART_BAUD);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* 设置当前帧头帧尾格式,后续发送与解析都按该格式执行。 */
|
||||
/* 函数: su03t_set_frame_format
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
esp_err_t su03t_set_frame_format(const su03t_frame_format_t *fmt)
|
||||
{
|
||||
if (fmt == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
s_fmt = *fmt;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* 获取当前帧格式,便于业务层调试或回显配置。 */
|
||||
/* 函数: su03t_get_frame_format
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
esp_err_t su03t_get_frame_format(su03t_frame_format_t *fmt_out)
|
||||
{
|
||||
if (fmt_out == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
*fmt_out = s_fmt;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 按协议帧格式发送:
|
||||
* [head_h head_l msgno params... tail_h tail_l]
|
||||
*/
|
||||
/* 函数: su03t_send_frame
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
esp_err_t su03t_send_frame(uint8_t msgno, const uint8_t *params, size_t params_len)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (params_len > SU03T_MAX_PARAM_LEN) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
if (params_len > 0 && params == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint8_t tx_buf[2 + 1 + SU03T_MAX_PARAM_LEN + 2];
|
||||
size_t idx = 0;
|
||||
tx_buf[idx++] = s_fmt.head_h;
|
||||
tx_buf[idx++] = s_fmt.head_l;
|
||||
tx_buf[idx++] = msgno;
|
||||
if (params_len > 0) {
|
||||
memcpy(&tx_buf[idx], params, params_len);
|
||||
idx += params_len;
|
||||
}
|
||||
tx_buf[idx++] = s_fmt.tail_h;
|
||||
tx_buf[idx++] = s_fmt.tail_l;
|
||||
|
||||
int written = uart_write_bytes(SU03T_UART_PORT, (const char *)tx_buf, idx);
|
||||
if (written < 0 || (size_t)written != idx) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return uart_wait_tx_done(SU03T_UART_PORT, pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
/*
|
||||
* 发送十六进制字符串。
|
||||
* 输入示例: "AA 55 01 11 22 55 AA"
|
||||
* 规则:
|
||||
* 1) 允许空白字符。
|
||||
* 2) 必须是偶数个十六进制字符。
|
||||
* 3) 遇到非法字符直接返回参数错误。
|
||||
*/
|
||||
/* 函数: su03t_send_hex_string
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
esp_err_t su03t_send_hex_string(const char *hex_string)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (hex_string == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint8_t tx_buf[2 + 1 + SU03T_MAX_PARAM_LEN + 2];
|
||||
size_t out_len = 0;
|
||||
int high = -1;
|
||||
|
||||
for (const char *p = hex_string; *p != '\0'; ++p) {
|
||||
if (isspace((unsigned char)*p)) {
|
||||
continue;
|
||||
}
|
||||
int n = hex_to_nibble(*p);
|
||||
if (n < 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (high < 0) {
|
||||
high = n;
|
||||
} else {
|
||||
if (out_len >= sizeof(tx_buf)) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
tx_buf[out_len++] = (uint8_t)((high << 4) | n);
|
||||
high = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (high >= 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (out_len == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int written = uart_write_bytes(SU03T_UART_PORT, (const char *)tx_buf, out_len);
|
||||
if (written < 0 || (size_t)written != out_len) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return uart_wait_tx_done(SU03T_UART_PORT, pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
/*
|
||||
* 在超时时间内解析一帧。
|
||||
* 协议格式:
|
||||
* head_h head_l msgno param... tail_h tail_l
|
||||
* 返回:
|
||||
* - ESP_OK: 成功拿到完整帧
|
||||
* - ESP_ERR_TIMEOUT: 超时未收到完整帧
|
||||
* - 其它: 参数或协议错误
|
||||
*/
|
||||
/* 函数: su03t_recv_frame
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
esp_err_t su03t_recv_frame(su03t_frame_t *out_frame, uint32_t timeout_ms)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (out_frame == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
memset(out_frame, 0, sizeof(*out_frame));
|
||||
|
||||
const TickType_t timeout_ticks = pdMS_TO_TICKS(timeout_ms);
|
||||
const TickType_t start_tick = xTaskGetTickCount();
|
||||
uint8_t byte = 0;
|
||||
uint8_t payload[1 + SU03T_MAX_PARAM_LEN];
|
||||
size_t payload_len = 0;
|
||||
bool head_found = false;
|
||||
|
||||
while ((xTaskGetTickCount() - start_tick) < timeout_ticks) {
|
||||
int n = uart_read_bytes(SU03T_UART_PORT, &byte, 1, pdMS_TO_TICKS(20));
|
||||
if (n <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!head_found) {
|
||||
if (byte == s_fmt.head_h) {
|
||||
int n2 = uart_read_bytes(SU03T_UART_PORT, &byte, 1, pdMS_TO_TICKS(20));
|
||||
if (n2 > 0 && byte == s_fmt.head_l) {
|
||||
head_found = true;
|
||||
payload_len = 0;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (payload_len >= sizeof(payload)) {
|
||||
head_found = false;
|
||||
payload_len = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
payload[payload_len++] = byte;
|
||||
|
||||
if (payload_len >= 3 &&
|
||||
payload[payload_len - 2] == s_fmt.tail_h &&
|
||||
payload[payload_len - 1] == s_fmt.tail_l) {
|
||||
|
||||
size_t body_len = payload_len - 2;
|
||||
if (body_len < 1) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
out_frame->msgno = payload[0];
|
||||
out_frame->params_len = body_len - 1;
|
||||
if (out_frame->params_len > 0) {
|
||||
memcpy(out_frame->params, &payload[1], out_frame->params_len);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* 启动异步接收:
|
||||
* - callback: 每收到一帧就回调一次
|
||||
* - task_stack_size/task_priority: 允许上层按系统负载调优
|
||||
*/
|
||||
/* 函数: su03t_start_receiver
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
esp_err_t su03t_start_receiver(su03t_rx_callback_t callback, void *user_ctx, uint32_t task_stack_size, UBaseType_t task_priority)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (callback == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_rx_task != NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (task_stack_size == 0) {
|
||||
task_stack_size = 4096;
|
||||
}
|
||||
if (task_priority == 0) {
|
||||
task_priority = 5;
|
||||
}
|
||||
|
||||
s_rx_callback = callback;
|
||||
s_rx_user_ctx = user_ctx;
|
||||
s_rx_running = true;
|
||||
|
||||
BaseType_t ok = xTaskCreate(su03t_rx_task, "su03t_rx", task_stack_size, NULL, task_priority, &s_rx_task);
|
||||
if (ok != pdPASS) {
|
||||
s_rx_running = false;
|
||||
s_rx_callback = NULL;
|
||||
s_rx_user_ctx = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 停止异步接收。
|
||||
* 该实现通过标志位让接收任务自行退出,避免跨任务强制删除导致资源状态不一致。
|
||||
*/
|
||||
/* 函数: su03t_stop_receiver
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
*/
|
||||
esp_err_t su03t_stop_receiver(void)
|
||||
{
|
||||
if (s_rx_task == NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
s_rx_running = false;
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user