Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion Documents/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ PID 接口是旧版控制器工具。Motor 新 controller 不依赖该公共 API
| `motor_can_sched_register_can(can_dev)` | 注册一个 CAN 设备。 | `0` 成功,负 errno 失败。 |
| `motor_can_sched_send(can_dev, frame, param, handle_out)` | 通用发送入口,可配置周期、应答追踪与优先级。 | `0` 成功,负 errno 失败。 |
| `motor_can_sched_send_with_priority(can_dev, frame, priority, tag)` | 按明确优先级发送单帧。 | `0` 成功,负 errno 失败。 |
| `motor_can_sched_send_prio(can_dev, frame, high_priority, tag)` | 发送单帧,高优先级布尔入口。 | `0` 成功,负 errno 失败。 |
| `motor_can_sched_send_reply(can_dev, frame, reply_id, reply_mask, timeout_ms, tag)` | 发送请求回复帧。 | `0` 成功,负 errno 失败。 |
| `motor_can_sched_update(handle, frame)` | 更新周期帧内容。 | `0` 成功,负 errno 失败。 |
| `motor_can_sched_remove(handle)` | 删除周期帧。 | `0` 成功,负 errno 失败。 |
Expand Down Expand Up @@ -306,6 +305,29 @@ PID 接口是旧版控制器工具。Motor 新 controller 不依赖该公共 API

Dual Protocol 支持函数帧、同步帧、错误帧与回复帧。协议常量定义在同一头文件中。

### MQTT-like 协议

头文件:`include/ares/protocol/mqttlite/mqttlite_protocol.h`

| 接口 | 说明 |
| --- | --- |
| `ares_mqttlite_register_topic(protocol, topic_id, topic)` | 注册本地 topic id 映射。 |
| `ares_mqttlite_subscribe(protocol, topic_filter, cb, user_data)` | 注册本地 topic 订阅回调。 |
| `ares_mqttlite_subscribe_id(protocol, topic_id, cb, user_data)` | 按已约定 topic id 注册本地订阅回调。 |
| `ares_mqttlite_unsubscribe(protocol, topic_filter)` | 移除本地 topic 订阅。 |
| `ares_mqttlite_unsubscribe_id(protocol, topic_id)` | 移除本地 topic id 订阅。 |
| `ares_mqttlite_publish(protocol, topic, payload, payload_len, qos, cb, user_data)` | 发布消息,QoS1/QoS2 成功时返回 packet id。 |
| `ares_mqttlite_publish_id(protocol, topic_id, payload, payload_len, qos, cb, user_data)` | 使用 2 字节 topic id 发布消息。 |
| `ares_mqttlite_publish_prepare_id(protocol, topic_id, payload_len, pub)` | 准备 QoS0 topic-id 零拷贝发布缓冲。 |
| `ares_mqttlite_publish_commit(protocol, pub)` | 发送已填充的零拷贝发布缓冲。 |
| `ares_mqttlite_publish_abort(pub)` | 释放未提交的零拷贝发布缓冲。 |
| `ares_mqttlite_ping(protocol)` | 发送 PING 帧。 |
| `ARES_MQTTLITE_PROTOCOL_DEFINE(name)` | 定义 MQTT-like 协议实例。 |

协议支持 `ARES_MQTTLITE_QOS0`、`ARES_MQTTLITE_QOS1` 与 `ARES_MQTTLITE_QOS2`。QoS1 使用
`PUBLISH/PUBACK`,QoS2 使用 `PUBLISH/PUBREC/PUBREL/PUBCOMP`。payload 是变长字段,长度由每帧
header 携带,并受 `CONFIG_ARES_MQTTLITE_MAX_PAYLOAD_SIZE` 限制。

### Plotter 协议

头文件:`include/ares/protocol/plotter/aresplot_protocol.h`
Expand Down
23 changes: 15 additions & 8 deletions Documents/communication/interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ UART 异步回调收到 `UART_RX_RDY` 后:
这一设计保证了单通道串口发送有统一仲裁点,避免多个上下文直接打到底层驱动。

`ares_uart_send_raw()` 则是例外,它直接调用 `uart_tx()`,更适合 plotter/VOFA 这种自带帧组装、
不依赖 `net_buf` 的快速路径。
不依赖 `net_buf` 的快速路径。UART 接口没有实现 `alloc_buf_with_data()`。

### 配置项

Expand Down Expand Up @@ -200,7 +200,10 @@ USB bulk 接口适合:

- `ares_usbd_init(struct AresInterface *interface)`
- `ares_usbd_write(struct AresInterface *interface, struct net_buf *buf)`
- `ares_usbd_write_with_lock(struct AresInterface *interface, struct net_buf *buf, struct k_mutex *mutex)`
- `ares_usbd_write_with_callback(struct AresInterface *interface, struct net_buf *buf,
ares_interface_tx_done_cb_t cb, void *user_data)`
- `ares_usbd_caps(struct AresInterface *interface)`
- `ares_usbd_mtu(struct AresInterface *interface)`
- `ares_interface_alloc_buf(struct AresInterface *interface)`
- `ares_interface_alloc_buf_with_data(struct AresInterface *interface, void *data, size_t len)`

Expand Down Expand Up @@ -248,20 +251,24 @@ OUT 端点启用后会持续预投递读请求。收到数据后:

#### 发送

发送调用 `ares_usbd_write()` 或 `ares_usbd_write_with_lock()`:
发送调用 `ares_usbd_write()` 或 `ares_usbd_write_with_callback()`:

1. 检查 USB class 和接口状态。
2. 检查 IN 端点是否已有在飞事务。
3. 在 `net_buf` 用户区写入端点与互斥信息
3. 在 `net_buf` 用户区写入端点与完成回调信息
4. 调用 `usbd_ep_enqueue()`。

发送完成后,request handler 清除 IN engaged 状态,并通过 buffer 回调释放 `net_buf`;
若附带互斥锁,还会在缓冲释放回调中解锁。
发送完成后,request handler 清除 IN engaged 状态,调用发送完成回调并释放 `net_buf`。同步入队失败时,
USB 层也会调用完成回调并释放缓冲。

USB bulk 接口没有实现 `send_raw()` 与 `connect()` / `disconnect()` / `is_connected()`。连接状态通过
USBD 消息转换为协议事件,而不是通过 `AresInterfaceAPI` 查询。

### `alloc_buf_with_data()` 的作用

双向协议会优先使用 `alloc_buf_with_data()`。对 USB 来说,这允许直接把现成帧数据包装成
发送缓冲,减少一次显式拷贝。维护者修改协议发送路径时,应保留这种能力协商。
双向协议会优先使用 `alloc_buf_with_data()`。对 USB 来说,这允许用现成帧数据创建发送缓冲,减少协议层
显式拷贝。维护者修改协议发送路径时,应保留这种能力协商,并为没有该能力的接口保留
`alloc_buf()` + `net_buf_add_mem()` fallback。

### 协议事件

Expand Down
120 changes: 119 additions & 1 deletion Documents/communication/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ SYNC 帧长度不能仅靠帧头得知,必须在收到 ID 后查同步表长

双向协议的发送始终通过已绑定接口:

- 优先使用 `alloc_buf_with_data()` 包装现成帧
- 优先使用 `alloc_buf_with_data()` 用现成帧数据创建发送缓冲
- 否则分配 `net_buf` 后复制数据
- 需要等待 TX 完成的 FUNC/同步帧使用 `send_with_callback()` 清理在飞状态

Expand Down Expand Up @@ -227,6 +227,124 @@ dual_func_add(&link_proto, 0x10, my_rpc);
dual_sync_add(&link_proto, 0x20, sync_buf, sizeof(sync_buf), sync_cb);
```

## MQTT-like 协议

### 角色

MQTT-like 协议面向 USB bulk 这类可靠包传输接口上的轻量发布/订阅。它复用
`AresInterface`,不直接依赖 USB 类实现,因此接口层仍负责收发 `net_buf`,协议层只处理 topic、
QoS 握手和消息分发。

实现位于:

- 头文件:`include/ares/protocol/mqttlite/mqttlite_protocol.h`
- 实现:`lib/ares/protocol/mqttlite/mqttlite_protocol.c`

### 帧类型

协议帧包含固定 12 字节头:

- magic
- version
- frame type
- flags
- packet id
- topic length
- payload length

当前定义的帧类型:

- `PUBLISH`
- `PUBACK`
- `PUBREC`
- `PUBREL`
- `PUBCOMP`
- `PING`
- `PONG`

topic 与 payload 紧跟在固定头之后。payload 是变长字段,长度由 frame header 携带,不是定长。
最大 topic、payload、frame、订阅数量、topic id 注册数量和在飞 QoS 包数量由
`CONFIG_ARES_MQTTLITE_*` 控制。

### Topic ID

普通 `PUBLISH` 帧携带完整 topic 字符串。性能敏感路径可以先用
`ares_mqttlite_register_topic()` 在本地注册 `uint16_t topic_id -> topic` 映射,再使用:

- `ares_mqttlite_subscribe_id()`
- `ares_mqttlite_unsubscribe_id()`
- `ares_mqttlite_publish_id()`

topic-id 帧会设置 frame flags 中的 topic-id 位,并把 topic 字段压缩为 2 字节 little-endian id。
接收端若注册了对应 id,会把 id 反查为字符串传给原有消息回调;未注册时 id 订阅仍可匹配,但回调中的
topic 字符串为空。

### QoS

协议支持三档 QoS:

- `ARES_MQTTLITE_QOS0`:最多一次,发送后不等待确认。
- `ARES_MQTTLITE_QOS1`:至少一次,发送端等待 `PUBACK`,超时重发 `PUBLISH`。
- `ARES_MQTTLITE_QOS2`:恰好一次语义,使用 `PUBLISH/PUBREC/PUBREL/PUBCOMP`,接收端在
`PUBREL` 阶段交付。

QoS1/QoS2 的发送端会记录在飞包,按 `CONFIG_ARES_MQTTLITE_RETRY_INTERVAL_MS` 重试,超过
`CONFIG_ARES_MQTTLITE_MAX_RETRIES` 后通过发布回调返回超时。

### Topic 匹配

订阅使用 `ares_mqttlite_subscribe()` 注册本地回调。topic filter 支持:

- 精确匹配
- 单层通配符 `+`
- 末尾多层通配符 `#`

协议当前不向远端发送 SUBSCRIBE 控制帧;订阅表是本地分发表。两端需要按应用契约分别注册各自要接收的
topic。

topic-id 订阅不做通配符匹配,只比较 `uint16_t topic_id`。这条路径适合高频 telemetry 和固定控制
topic。

### 发送路径

发布消息使用:

```c
ARES_MQTTLITE_PROTOCOL_DEFINE(link_proto);

ares_bind_interface(&usb_bulk_interface, &link_proto);
ares_mqttlite_subscribe(&link_proto, "robot/+/state", state_cb, NULL);
ares_mqttlite_publish(&link_proto, "robot/chassis/state", payload, payload_len,
ARES_MQTTLITE_QOS1, publish_cb, NULL);
```

协议通过已绑定接口分配 `net_buf` 并调用 `send()`。因此它可以跑在 USB bulk 上,也可以跑在其他实现了
`AresInterfaceAPI` 的块式接口上。

QoS0 的 topic-id 快速路径可以避免 payload 复制:

```c
struct ares_mqttlite_publish_buffer pub;

ares_mqttlite_register_topic(&link_proto, 1, "robot/chassis/state");
ares_mqttlite_publish_prepare_id(&link_proto, 1, payload_len, &pub);
memcpy(pub.payload, payload, payload_len);
ares_mqttlite_publish_commit(&link_proto, &pub);
```

这条路径直接让调用者写入最终 `net_buf` 的 payload 区域。QoS1/QoS2 仍会保留 payload 副本用于重试。

### 错误边界

MQTT-like 协议的主要错误边界:

- topic 或 payload 超过配置上限时,发布返回 `-EINVAL`。
- frame 超过接收缓冲上限时,解析器丢弃该帧。
- topic id 为 0 或未按应用契约双边约定时,远端无法按预期分发消息。
- QoS 在飞表满时,QoS1/QoS2 发布返回 `-ENOMEM`。
- QoS 重试耗尽时,发布回调收到 `ARES_MQTTLITE_PUBLISH_TIMEOUT`。
- 断连事件会清空在飞 QoS 状态和已暂存的 QoS2 接收状态。

## 绘图协议

### 角色
Expand Down
2 changes: 1 addition & 1 deletion Documents/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ Graph 当前承载 INSLink 相关代码。该目录并非通用图形库,维
- `include/ares/protocol/`
- `lib/ares/protocol/`

协议层是帧解析和业务分发层,目前包含 Dual Protocol 与 Plotter Protocol。
协议层是帧解析和业务分发层,目前包含 Dual Protocol、MQTT-like Protocol 与 Plotter Protocol。

### ARES 通信(ARES Comm)

Expand Down
1 change: 0 additions & 1 deletion Documents/motor/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,6 @@ stateDiagram-v2
| 接口 | 用途 |
| --- | --- |
| `motor_can_sched_register_can()` | 注册 CAN 设备。 |
| `motor_can_sched_send_prio()` | 发送普通帧,可选高优先级。 |
| `motor_can_sched_send_with_priority()` | 以明确优先级发送单帧。 |
| `motor_can_sched_send_reply()` | 发送会触发回复的帧,并跟踪回复超时。 |
| `motor_can_sched_send()` | 通用入口,支持周期帧和回复跟踪。 |
Expand Down
2 changes: 1 addition & 1 deletion Documents/motor/can-scheduler.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ int motor_can_sched_register_can(const struct device *can_dev);
- 周期控制帧默认使用 `NORMAL`。
- 不要把所有帧都提升到高优先级,否则调度器失去意义。

`motor_can_sched_send_prio()` 是较窄的便捷接口,只暴露“普通”与“高优先级”的常用路径。需要更细粒度控制时,使用 `motor_can_sched_send_with_priority()` 或通用 `motor_can_sched_send()`。
单帧发送应使用 `motor_can_sched_send_with_priority()` 明确声明优先级;需要周期帧或回复跟踪时,使用通用 `motor_can_sched_send()`。

## 发送模型

Expand Down
19 changes: 17 additions & 2 deletions Documents/samples/communication.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Communication 样例

通信样例分为 `ARES 通信` 与 `Plotter 协议` 两条线,覆盖 USB Bulk、UART 接口和日志输出行为。
通信样例分为 `ARES 通信`、`MQTT-like USB` 与 `Plotter 协议` 几条线,覆盖 USB Bulk、UART
接口和日志输出行为。

## 共同边界

- `CONFIG_UART_INTERFACE` 与 `CONFIG_USB_BULK_INTERFACE` 在各样例中的组合不同,须按 `boards/*` 与 `prj.conf` 一起确认。
- 示例流程以 `CONFIG_DUAL_PROPOSE_PROTOCOL` 或 `CONFIG_PLOTTER` 启用为入口,接口初始化失败时通常会跳过失败通道并继续运行。
- 示例流程以 `CONFIG_DUAL_PROPOSE_PROTOCOL`、`CONFIG_ARES_MQTTLITE_PROTOCOL` 或 `CONFIG_PLOTTER` 启用为入口,接口初始化失败时通常会跳过失败通道并继续运行。
- 上位机联调前先确认 `usart6` 与 USB OTG 物理连接。

## samples/communication/ares_communication
Expand All @@ -24,6 +25,20 @@
- 若新增板适配,先确认 `boards/<board>.conf` 是否覆盖了 `CONFIG_UART_INTERFACE`。
- 回调函数语义(`func_cb` / `sync_cb` / `func_ret_cb`)变化需同步示例文档与接收端联调脚本。

## samples/communication/mqttlite_usb

- 用途:MQTT-like 协议的 USB Bulk 发布/订阅演示,周期通过 topic-id 零拷贝路径发布 QoS0 heartbeat,并按 id 订阅 `host/command`。
- 构建:
- `west build -b dm_mc02 samples/communication/mqttlite_usb --pristine`
- `west flash`
- 适用 board:`dm_mc02`
- 硬件依赖:
- USB 设备能力
- 兼容 MQTT-like 帧格式的上位机
- 维护规则:
- QoS 行为变化需同步 `include/ares/protocol/mqttlite/mqttlite_protocol.h` 和协议文档。
- topic 名称变化需同步上位机接收端。

## samples/communication/plotter_auto

- 用途:自动上报变量的 Plotter 示例。
Expand Down
2 changes: 2 additions & 0 deletions Documents/zephyr/kconfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ Motor 子系统的调度和节拍选项:
| `CONFIG_ARES` | 启用 ARES 库菜单。 |
| `CONFIG_UART_INTERFACE` | 启用 UART 接口。 |
| `CONFIG_USB_BULK_INTERFACE` | 启用 USB Bulk 接口。 |
| `CONFIG_ARES_MQTTLITE_PROTOCOL` | 启用 MQTT-like 发布/订阅协议。 |
| `CONFIG_ARES_MQTTLITE_MAX_TOPICS` | 设置 MQTT-like topic id 注册数量上限。 |
| `CONFIG_ARES_BOARD_STATUS_LED` | 启用板级状态 LED 服务。 |
| `CONFIG_IMU_PWM_TEMP_CTRL` | 启用 IMU PWM 温控。 |
| `CONFIG_AUTO_PROBE_GYRO_BIAS` | 启用陀螺偏置自动探测。 |
Expand Down
11 changes: 0 additions & 11 deletions drivers/motor/common/motor_can_sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -1010,17 +1010,6 @@ int motor_can_sched_send(const struct device *can_dev, const struct can_frame *f
return 0;
}

int motor_can_sched_send_prio(const struct device *can_dev, const struct can_frame *frame,
bool high_priority, const char *tag)
{
const struct motor_can_sched_tx_param param = {
.high_priority = high_priority,
.tag = tag,
};

return motor_can_sched_send(can_dev, frame, &param, NULL);
}

int motor_can_sched_send_with_priority(const struct device *can_dev, const struct can_frame *frame,
enum motor_can_sched_prio priority, const char *tag)
{
Expand Down
2 changes: 0 additions & 2 deletions drivers/motor/common/motor_can_sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,6 @@ int motor_can_sched_send(const struct device *can_dev, const struct can_frame *f
motor_can_sched_handle_t *handle_out);
int motor_can_sched_send_with_priority(const struct device *can_dev, const struct can_frame *frame,
enum motor_can_sched_prio priority, const char *tag);
int motor_can_sched_send_prio(const struct device *can_dev, const struct can_frame *frame,
bool high_priority, const char *tag);
int motor_can_sched_send_reply(const struct device *can_dev, const struct can_frame *frame,
uint32_t reply_id, uint32_t reply_mask, uint16_t timeout_ms,
const char *tag);
Expand Down
13 changes: 9 additions & 4 deletions drivers/motor/dji/motor_dji.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,9 @@ void dji_control(const struct device *dev, enum motor_cmd cmd)
frame.data[1] = (cfg->common.rx_id - 0x200) >> 8;
frame.data[2] = 0x55;
frame.data[3] = 0x3C;
motor_can_sched_send_prio(cfg->common.phy, &frame, true, "dji-set-zero");
motor_can_sched_send_with_priority(cfg->common.phy, &frame,
MOTOR_CAN_SCHED_PRIO_CRITICAL,
"dji-set-zero");
}
break;
case CLEAR_CONTROLLER:
Expand All @@ -357,7 +359,9 @@ void dji_control(const struct device *dev, enum motor_cmd cmd)
frame.data[1] = (cfg->common.rx_id - 0x200) >> 8;
frame.data[2] = 0x55;
frame.data[3] = 0x50;
motor_can_sched_send_prio(cfg->common.phy, &frame, true, "dji-clear-error");
motor_can_sched_send_with_priority(cfg->common.phy, &frame,
MOTOR_CAN_SCHED_PRIO_CRITICAL,
"dji-clear-error");
}
break;
}
Expand Down Expand Up @@ -790,8 +794,9 @@ void dji_tx_handler(struct k_work *work)
txframe.dlc = 8;
txframe.flags = 0;
const struct device *can_dev = ctrl_struct->can_dev;
motor_can_sched_send_prio(can_dev, &txframe, true,
"dji-feedback-control");
motor_can_sched_send_with_priority(can_dev, &txframe,
MOTOR_CAN_SCHED_PRIO_CRITICAL,
"dji-feedback-control");
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions drivers/motor/dm/motor_dm.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ static int dm_send_cmd_frame(const struct device *dev, const uint8_t data[8], co
};

memcpy(frame.data, data, 8);
return motor_can_sched_send_prio(cfg->common.phy, &frame, true, tag);
return motor_can_sched_send_with_priority(cfg->common.phy, &frame,
MOTOR_CAN_SCHED_PRIO_CRITICAL, tag);
}

static int dm_send_cmd_frame_reply(const struct device *dev, const uint8_t data[8], const char *tag)
Expand Down Expand Up @@ -301,7 +302,7 @@ static void dm_edit_reg_value(const struct device *dev, uint16_t can_id, uint8_t
frame.data[5] = reg_value >> 8;
frame.data[6] = reg_value >> 16;
frame.data[7] = reg_value >> 24;
motor_can_sched_send_prio(dev, &frame, true, "dm-reg");
motor_can_sched_send_with_priority(dev, &frame, MOTOR_CAN_SCHED_PRIO_CRITICAL, "dm-reg");
}

static void dm_edit_reg_float(const struct device *dev, uint16_t can_id, uint8_t reg_addr,
Expand Down
Loading
Loading