Rust in Everything
基于 embassy-stm32 0.6.0

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 填反会出两条。
  • 复用功能号不用手写,也写不错;原理图上的引脚根本不支持这个信号时,只能换外设或改板子。

💬 关联讨论

发起讨论
加载中...