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
37 changes: 23 additions & 14 deletions Documents/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,19 +226,23 @@ PID 接口是旧版控制器工具。Motor 新 controller 不依赖该公共 API

头文件:`include/ares/interface/ares_interface.h`

`struct AresInterfaceAPI` 是传输层驱动表:
`struct AresInterfaceAPI` 是传输层能力表。不是每个接口都会实现所有回调,协议层调用前应检查函数
指针是否为空。`send()` 与 `send_with_callback()` 调用后,`net_buf` 所有权交给接口;接口会在发送
完成、发送中止或同步入队失败时释放缓冲。

| 回调 | 说明 |
| --- | --- |
| `send(interface, buf)` | 发送一个 `net_buf`。 |
| `send_with_lock(interface, buf, mutex)` | 在调用者给出的锁保护下发送。 |
| `send_raw(interface, data, len)` | 发送原始字节。 |
| `connect(interface)` | 建立连接。 |
| `disconnect(interface)` | 断开连接。 |
| `is_connected(interface)` | 查询连接状态。 |
| `alloc_buf(interface)` | 分配发送缓冲。 |
| `alloc_buf_with_data(interface, data, size)` | 分配并填充发送缓冲。 |
| `init(interface)` | 初始化接口对象。 |
| 回调 | 说明 | 当前实现 |
| --- | --- | --- |
| `send(interface, buf)` | 发送一个 `net_buf`,不需要完成通知时使用。 | UART、USB bulk |
| `send_with_callback(interface, buf, cb, user_data)` | 发送 `net_buf` 并在 TX 完成、abort 或同步失败时调用 `ares_interface_tx_done_cb_t`。 | UART、USB bulk |
| `send_raw(interface, data, len)` | 发送原始字节,不使用 `net_buf`。 | UART |
| `connect(interface)` | 建立连接。 | 预留,当前 UART/USB 未填 |
| `disconnect(interface)` | 断开连接。 | 预留,当前 UART/USB 未填 |
| `is_connected(interface)` | 查询连接状态。 | 预留,当前 UART/USB 未填 |
| `caps(interface)` | 返回 `AresInterfaceCaps` 能力位。 | UART、USB bulk |
| `mtu(interface)` | 返回接口建议帧长上限。 | UART、USB bulk |
| `alloc_buf(interface)` | 分配发送缓冲。 | UART、USB bulk |
| `alloc_buf_with_data(interface, data, size)` | 分配并携带现有数据块的发送缓冲。 | USB bulk |
| `init(interface)` | 初始化接口对象。 | UART、USB bulk |

`struct AresInterface` 包含名称、API、绑定的协议和传输私有数据。

Expand All @@ -250,8 +254,11 @@ PID 接口是旧版控制器工具。Motor 新 controller 不依赖该公共 API
| --- | --- |
| `ares_uart_init(interface)` | 初始化 UART 接口。 |
| `ares_uart_send(interface, buf)` | 发送 `net_buf`。 |
| `ares_uart_send_with_callback(interface, buf, cb, user_data)` | 发送 `net_buf` 并在 UART TX 完成或失败时通知调用者。 |
| `ares_uart_send_raw(interface, data, len)` | 发送原始字节。 |
| `ares_uart_interface_alloc_buf(interface)` | 分配 UART 发送缓冲。 |
| `ares_uart_caps(interface)` | 返回 UART 接口能力位。 |
| `ares_uart_mtu(interface)` | 返回 UART 建议帧长上限。 |
| `ares_uart_init_dev(interface, uart_dev)` | 绑定 Zephyr UART 设备。 |
| `ARES_UART_INTERFACE_DEFINE(name)` | 定义 UART 接口实例。 |

Expand All @@ -263,9 +270,11 @@ PID 接口是旧版控制器工具。Motor 新 controller 不依赖该公共 API
| --- | --- |
| `ares_usbd_init(interface)` | 初始化 USB bulk 接口。 |
| `ares_usbd_write(interface, buf)` | 发送 `net_buf`。 |
| `ares_usbd_write_with_lock(interface, buf, mutex)` | 带外部锁发送。 |
| `ares_usbd_write_with_callback(interface, buf, cb, user_data)` | 发送 `net_buf` 并在 USB IN 传输完成或失败时通知调用者。 |
| `ares_interface_alloc_buf(interface)` | 分配发送缓冲。 |
| `ares_interface_alloc_buf_with_data(interface, data, len)` | 分配并填充发送缓冲。 |
| `ares_interface_alloc_buf_with_data(interface, data, len)` | 分配并携带现有数据块的发送缓冲。 |
| `ares_usbd_caps(interface)` | 返回 USB bulk 接口能力位。 |
| `ares_usbd_mtu(interface)` | 返回当前 USB bulk 端点最大包长,未初始化时返回 64。 |
| `ARES_BULK_INTERFACE_DEFINE(name)` | 定义 USB bulk 接口实例。 |

### 协议层
Expand Down
39 changes: 28 additions & 11 deletions Documents/communication/interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,29 @@ ARES 接口层统一承载底层链路,向协议层暴露稳定的发送、缓

## `AresInterfaceAPI`

接口实现可以提供以下能力:
接口实现可以提供以下能力。这个结构体是能力表,不是强制每个接口完整实现的 vtable

- `send()`: 发送 `net_buf`。
- `send_with_lock()`: 带互斥语义的发送。
- `send_raw()`: 发送裸字节缓冲。
- `connect()` / `disconnect()` / `is_connected()`: 连接状态接口。
- `send_with_callback()`: 发送 `net_buf` 并在完成、abort 或同步失败时通知调用者。
- `send_raw()`: 发送裸字节缓冲,当前由 UART 实现。
- `connect()` / `disconnect()` / `is_connected()`: 连接状态接口,当前 UART/USB 宏未填,属于预留能力。
- `caps()`: 返回 `AresInterfaceCaps` 能力位。
- `mtu()`: 返回接口建议帧长上限。
- `alloc_buf()`: 分配发送缓冲。
- `alloc_buf_with_data()`: 用现有数据块包装发送缓冲
- `alloc_buf_with_data()`: 用现有数据块创建发送缓冲,当前由 USB bulk 实现
- `init()`: 初始化接口实例。

并非所有接口都会实现全部入口。协议层在调用前应按空指针能力协商。
当前宏填充情况:

| 接口宏 | 已填回调 |
| --- | --- |
| `ARES_UART_INTERFACE_DEFINE()` | `init`、`send`、`send_with_callback`、`send_raw`、`caps`、`mtu`、`alloc_buf` |
| `ARES_BULK_INTERFACE_DEFINE()` | `init`、`send`、`send_with_callback`、`caps`、`mtu`、`alloc_buf`、`alloc_buf_with_data` |

协议层在调用可选回调前必须按空指针能力协商。调用 `send()` 或 `send_with_callback()` 后,
`net_buf` 所有权交给接口;当前 UART/USB 实现会在发送完成、发送中止或同步入队失败时释放传入
缓冲,调用者不应再次 `net_buf_unref()`。需要等待 TX 完成的协议应使用 `send_with_callback()`,
并在 `ares_interface_tx_done_cb_t` 里释放自己的在飞状态。

## 绑定顺序

Expand Down Expand Up @@ -62,8 +74,12 @@ UART 接口适合:

- `ares_uart_init(struct AresInterface *interface)`
- `ares_uart_send(struct AresInterface *interface, struct net_buf *buf)`
- `ares_uart_send_with_callback(struct AresInterface *interface, struct net_buf *buf,
ares_interface_tx_done_cb_t cb, void *user_data)`
- `ares_uart_send_raw(struct AresInterface *interface, uint8_t *data, uint16_t len)`
- `ares_uart_interface_alloc_buf(struct AresInterface *interface)`
- `ares_uart_caps(struct AresInterface *interface)`
- `ares_uart_mtu(struct AresInterface *interface)`
- `ares_uart_init_dev(struct AresInterface *interface, const struct device *uart_dev)`

以及实例定义宏:
Expand Down Expand Up @@ -125,7 +141,8 @@ UART 异步回调收到 `UART_RX_RDY` 后:

1. 把 `net_buf *` 放入 TX 队列。
2. 由专用 TX 线程串行发送。
3. 依靠 `UART_TX_DONE` / `UART_TX_ABORTED` 回调释放在飞缓冲。
3. 依靠 `UART_TX_DONE` / `UART_TX_ABORTED` 回调触发 `send_with_callback()` 的完成通知。
4. 完成通知返回后释放在飞缓冲。

这一设计保证了单通道串口发送有统一仲裁点,避免多个上下文直接打到底层驱动。

Expand All @@ -147,13 +164,13 @@ UART 异步回调收到 `UART_RX_RDY` 后:

UART 接口常见失败点如下:

- TX 队列满:`ares_uart_send()` 返回 `-ENOMEM`,并释放缓冲
- TX 队列满:`ares_uart_send()` 返回 `-ENOMEM`,触发完成回调并释放缓冲
- 设备未 ready:`ares_uart_init()` 返回 `-ENODEV`。
- `uart_callback_set()` 失败:初始化直接返回底层错误码。
- `uart_tx()` 失败:发送线程记录错误并释放当前缓冲
- `uart_tx()` 失败:发送线程记录错误,触发完成回调并释放当前缓冲

调用者应将 `send()` 返回值视为“接口是否接管了缓冲”的边界;失败时不要再自行释放已经交给接口的
缓冲,避免双重 `unref`
调用者应将 `send()` 或 `send_with_callback()` 调用视为缓冲所有权转移边界;即使发送入口返回错误,
当前实现也已处理完成回调与缓冲释放,调用者不要再自行释放

### 最小入口

Expand Down
5 changes: 3 additions & 2 deletions Documents/communication/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,10 @@ SYNC 帧长度不能仅靠帧头得知,必须在收到 ID 后查同步表长

- 优先使用 `alloc_buf_with_data()` 包装现成帧
- 否则分配 `net_buf` 后复制数据
- 若接口支持 `send_with_lock()`,则利用互斥保护在飞帧
- 需要等待 TX 完成的 FUNC/同步帧使用 `send_with_callback()` 清理在飞状态

这解释了为什么 USB 实现提供了 `send_with_lock()`,而 UART 没有。
协议层不再依赖 USB 专用锁语义。UART 和 USB bulk 都通过 `ares_interface_tx_done_cb_t`
报告发送完成、abort 或同步入队失败。

### CRC

Expand Down
159 changes: 159 additions & 0 deletions Documents/updates/ares_uart_usart1_921600_update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<!--
SPDX-License-Identifier: Apache-2.0
-->

# ARES UART USART1 921600 Update

Date: 2026-07-01
Board: Damiao MC-02, STM32H723
Test UART: USART1 on PA9/PA10
Log UART: USART10 at 115200
Host UART device: `/dev/cu.usbmodem00000000050C1`

## Summary

This update brings up the ARES UART interface on MC-02 USART1 and validates it
with real ARES dual-protocol FUNC/REPL frames at 921600 baud. The test path does
not bypass the protocol stack: the host sends FUNC frames, the board runs the
sample `func_cb`, and the board sends REPL frames back through the UART
interface.

## Bugs Found

- The communication sample hard-coded `DT_NODELABEL(usart6)`, while MC-02 test
hardware is wired to USART1.
- `CONFIG_UART_ASYNC_API` was not enabled in the UART test build, causing
`uart_callback_set()` to fail with `-ENOSYS`.
- STM32 async UART requires DMA. Without `dmas`/`dma-names`, `uart_tx()` failed
with `-ENODEV`.
- STM32H7 async UART DMA rejects cached buffers. RX and TX initially failed with
`Rx/Tx buffer should be placed in a nocache memory region` and `-EFAULT`.
- The UART driver created threads with `K_NO_WAIT` and then called
`k_thread_start()` again, which can restart an already started thread and led
to an MPU fault during bring-up.
- UART thread stacks were declared as raw `k_thread_stack_t[]` members instead
of Zephyr kernel stack members.

## Changes

- The sample now honors `chosen { ares,uart = ...; }` before falling back to
`usart6`, so board-specific UART selection can be done in an overlay.
- Added `samples/communication/ares_communication/boards/dm_mc02_usart1_921600.overlay`
for USART1 PA9/PA10 at 921600 baud with STM32H7 DMAMUX requests 42/41.
- UART RX slab is placed in `__nocache`.
- UART TX copies each outgoing `net_buf` into a dedicated `__nocache` DMA buffer
before calling `uart_tx()`.
- UART exposes TX-complete callback support and interface caps/MTU so the dual
protocol can clear in-flight state when real UART TX completes.
- Removed redundant `k_thread_start()` calls after `k_thread_create(..., K_NO_WAIT)`.
- Changed UART stack members to `K_KERNEL_STACK_MEMBER`.

## Build Commands

Functional/log-enabled test:

```sh
west build -p always -b dm_mc02/stm32h723xx \
samples/communication/ares_communication \
-d build_uart_test -- \
-DDTC_OVERLAY_FILE="samples/communication/ares_communication/boards/dm_mc02.overlay;samples/communication/ares_communication/boards/dm_mc02_usart1_921600.overlay" \
-DCONFIG_UART_INTERFACE=y \
-DCONFIG_UART_ASYNC_API=y \
-DCONFIG_NOCACHE_MEMORY=y
```

Transport benchmark without application logging:

```sh
west build -p always -b dm_mc02/stm32h723xx \
samples/communication/ares_communication \
-d build_uart_test -- \
-DDTC_OVERLAY_FILE="samples/communication/ares_communication/boards/dm_mc02.overlay;samples/communication/ares_communication/boards/dm_mc02_usart1_921600.overlay" \
-DCONFIG_UART_INTERFACE=y \
-DCONFIG_UART_ASYNC_API=y \
-DCONFIG_NOCACHE_MEMORY=y \
-DCONFIG_LOG=n
```

Flash:

```sh
west flash -d build_uart_test --no-rebuild --runner openocd \
--config boards/damiao/dm_mc02/support/openocd.cfg
```

## Functional Validation

- Board initialized `uart_protocol` successfully on USART1.
- Board transmitted ARES heartbeat frames on USART1.
- Host heartbeat frames kept `uart_protocol` online.
- Host FUNC frame:

```text
ca fe 01 00 11 00 00 00 22 00 00 00 33 00 00 00 34 12
```

Board log confirmed:

```text
func_cb
params: 11,22,33
```

## Performance

All rows use 921600 baud, FUNC requests are 18 bytes, REPL replies are 10 bytes.
RTT is measured at the host from FUNC write to matching REPL read. At overloaded
rates, REPL request IDs wrap at 8 bits in the current protocol, so tail RTT is
best read as queue/overload indication rather than precise per-frame latency.

### Log Enabled

The sample's `func_cb` logs two lines per incoming FUNC. USART10 logging at
115200 saturated around 9.4 KiB/s and became a system-level bottleneck.

| Target FPS | Sent FPS | Reply FPS | Success | Avg RTT ms | P95 RTT ms | P99 RTT ms | Max RTT ms |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 500 | 500.0 | 500.0 | 100.00% | 1.03 | 1.15 | 1.72 | 3.34 |
| 1000 | 1000.0 | 1000.0 | 100.00% | 1.03 | 1.13 | 1.33 | 3.65 |
| 2000 | 1998.7 | 1989.0 | 99.52% | 8.21 | 128.78 | 129.20 | 131.88 |
| 3000 | 2818.0 | 2777.3 | 98.56% | 16.07 | 86.65 | 170.87 | 343.31 |
| 4000 | 2759.0 | 2721.0 | 98.62% | 23.31 | 103.72 | 198.97 | 331.67 |
| 5000 | 2767.0 | 2733.7 | 98.80% | 17.27 | 97.50 | 169.64 | 271.34 |

Practical log-enabled operating point: 1000 FUNC/REPL fps with low latency and
no observed loss in the 3 s run. Higher rates still return many frames, but the
log backend is saturated and tail latency rises sharply.

### Log Disabled

This removes application log traffic while keeping the real ARES UART
FUNC/REPL path. The wire-rate request-side limit for 18-byte frames at 921600
8N1 is about 5120 FUNC fps.

| Target FPS | Sent FPS | Reply FPS | Success | Avg RTT ms | P50 RTT ms | P95 RTT ms | P99 RTT ms | Max RTT ms |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 1000 | 979.7 | 969.3 | 98.95% | 22.93 | 0.93 | 256.85 | 257.17 | 573.82 |
| 2000 | 2000.0 | 1977.3 | 98.87% | 19.65 | 0.89 | 128.93 | 256.88 | 384.85 |
| 3000 | 3000.0 | 2824.0 | 94.13% | 113.50 | 86.08 | 341.76 | 342.50 | 512.89 |
| 4000 | 4000.0 | 2948.0 | 73.70% | 386.33 | 384.53 | 896.55 | 1088.59 | 1345.09 |
| 5000 | 5000.0 | 1898.3 | 37.97% | 952.40 | 914.41 | 1817.98 | 2021.38 | 2328.66 |
| 6000 | 5996.3 | 1464.0 | 24.41% | 997.69 | 928.22 | 2254.06 | 2461.94 | 2803.09 |

Practical log-disabled operating point: around 2000 FUNC/REPL fps for sustained
testing with about 98.9% replies in this run. The maximum observed reply
throughput was about 2948 REPL fps at a 4000 fps request target, but that mode
is overloaded and drops roughly 26% of replies.

The stable 2000 fps point corresponds to approximately 36.0 KiB/s of request
frame bytes host-to-board and 19.3 KiB/s of reply frame bytes board-to-host,
or about 55.3 KiB/s total protocol frame bytes across the full-duplex UART.

## Remaining Bottleneck

The UART transport is now functional at 921600, but the current dual-protocol
FUNC reply path has only one in-flight reply buffer per function mapping. When
FUNC frames arrive faster than UART TX completion can clear that in-flight bit,
extra replies are intentionally skipped. Raising the loss-free ceiling will
require queuing REPL frames or giving each pending reply its own buffer and
request ID storage.
Loading
Loading