the trait bound PA0: RxPin<USART1> is not satisfied
主线位置:中断主线第 1 步之前,构造驱动时。
build.rs怎样从stm32-metapac的芯片数据生成引脚表,见芯片数据与代码生成。
flowchart TB
data["stm32-metapac 芯片数据"] --> gen["build.rs"]
gen --> impl["pin_trait_impl! 每行一个"]
impl --> tr["impl RxPin USART1 for PB7"]
ctor["BufferedUart::new"] --> need["rx: impl RxPin T"]
need --> q{"PA0 有这行吗"}
q -->|没有| err["E0277 列出 PA10 PB7"]
实验 crate exp-embassy/pitb-qemu(thumbv7em-none-eabihf,stm32l475vg,release,只编译)。embassy-stm32 经 [patch.crates-io] 指向本目录里 hal-qemu-patched 的副本(0.6.0 加 QEMU 补丁,macros.rs 和 build.rs 没有改动),所以报错路径里是 hal-qemu-patched/...。报错文件在 pitb-qemu/errs/。
现象
原理图上写「UART RX:PA0」,代码照抄(errs/e22_pa0.txt):
// board schematic: "UART RX on PA0" -- but PA0 is UART4_TX / USART2_CTS
let _uart = BufferedUart::new(p.USART1, p.PA0, p.PB6, TX.init([0; 32]), RX.init([0; 32]), Irqs, Config::default());
error[E0277]: the trait bound `PA0: embassy_stm32::usart::RxPin<embassy_stm32::peripherals::USART1>` is not satisfied
--> src/bin/e22_pa0.rs:21:43
|
21 | let _uart = BufferedUart::new(p.USART1, p.PA0, p.PB6, TX.init([0; 32]), RX.init([0; 32]), Irqs, Config::default());
| ----------------- ^^^^^ the trait `embassy_stm32::usart::RxPin<embassy_stm32::peripherals::USART1>` is not implemented for `PA0`
| |
| required by a bound introduced by this call
|
help: the following other types implement trait `embassy_stm32::usart::RxPin<T>`
--> hal-qemu-patched/embassy-stm32/src/macros.rs:68:9
|
68 | impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$pin {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| `PA10`
| `PB7`
|
::: ~/exp-embassy/target-pitb-qemu/thumbv7em-none-eabihf/release/build/embassy-stm32-ced6d4165388e73f/out/_generated.rs:3421:1
|
3421 | pin_trait_impl!(crate::usart::RxPin, USART1, PA10, 7u8, crate::gpio::AfioRemapNotApplicable);
| -------------------------------------------------------------------------------------------- in this macro invocation
...
3432 | pin_trait_impl!(crate::usart::RxPin, USART1, PB7, 7u8, crate::gpio::AfioRemapNotApplicable);
| ------------------------------------------------------------------------------------------- in this macro invocation
...
更常见的是 TX、RX 填反。构造函数的参数顺序是 (peri, rx, tx, ..),写成 p.PB6, p.PB7(errs/e22_swap.txt),得到两条 E0277:
error[E0277]: the trait bound `PB6: embassy_stm32::usart::RxPin<embassy_stm32::peripherals::USART1>` is not satisfied
...
| `PA10`
| `PB7`
...
error[E0277]: the trait bound `PB7: embassy_stm32::usart::TxPin<embassy_stm32::peripherals::USART1>` is not satisfied
...
| `PA9`
| `PB6`
...
error: could not compile `pitb-qemu` (bin "e22_swap") due to 2 previous errors
出处
BufferedUart::new 的引脚参数是 rx: Peri<'d, if_afio!(impl RxPin<T, A>)>、tx: Peri<'d, if_afio!(impl TxPin<T, A>)>(L475 没有 F1 的 AFIO 重映射,if_afio! 退化成 impl RxPin<T>)。RxPin / TxPin 由 pin_trait! 定义(usart/mod.rs 第 2151 行),实现全部来自 build.rs 生成的 _generated.rs,每个「引脚 × 外设 × 信号」一行 pin_trait_impl!,最后一个数字是复用功能号(AF):
3409:pin_trait_impl!(crate::usart::TxPin, UART4, PA0, 8u8, crate::gpio::AfioRemapNotApplicable);
3421:pin_trait_impl!(crate::usart::RxPin, USART1, PA10, 7u8, crate::gpio::AfioRemapNotApplicable);
3432:pin_trait_impl!(crate::usart::RxPin, USART1, PB7, 7u8, crate::gpio::AfioRemapNotApplicable);
3433:pin_trait_impl!(crate::usart::CtsPin, USART2, PA0, 7u8, crate::gpio::AfioRemapNotApplicable);
(grep -n 'PA0,\|RxPin, USART1' _generated.rs 的一部分。)PA0 在这颗芯片上只能当 UART4 的 TX(AF8)或 USART2 的 CTS(AF7),没有任何一行让它成为 USART1 的 RX。驱动在构造时用 new_pin! 从 trait 拿 af_num() 配置引脚,所以「哪个 AF」这件事用户不用写,写错了也不可能编译通过。
修法
按 help 列出的换引脚,或者换外设:
// USART1: RX = PB7 (or PA10), TX = PB6 (or PA9)
let uart = BufferedUart::new(p.USART1, p.PB7, p.PB6, TX.init([0; 32]), RX.init([0; 32]), Irqs, Config::default()).unwrap();
这一行就是 BufferedUart 第二次 write 卡住 里在 QEMU 上跑通的那一行。硬件已经定了、RX 真的接在 PA0 上,就要找一个能把 PA0 当 RX 的外设:上面的表里没有,PA0 只能当 TX 或 CTS,那是原理图的问题,代码修不了;E0277 让它在编译时就暴露出来。
怎么确认没中招
- 报错的 trait 是
RxPin<..>/TxPin<..>/SckPin<..>这一类:看help下面的反引号列表,那就是全部合法的引脚。 - 反查某个引脚能做什么:
grep -n ', PA0,' target/.../build/embassy-stm32-*/out/_generated.rs(OUT_DIR的位置见芯片数据与代码生成)。 - 两条 E0277 同时出现、一个说 RX 一个说 TX,并且各自的合法列表里正好有对方,就是填反了。
小结
- 引脚和外设信号的对应关系由
build.rs生成的pin_trait_impl!决定,驱动参数是impl RxPin<T>这样的约束,选错就是 E0277。 - rustc 的
help列出所有合法引脚,并指向_generated.rs里对应的行;TX、RX 填反会出两条。 - 复用功能号不用手写,也写不错;原理图上的引脚根本不支持这个信号时,只能换外设或改板子。