the name TICK_HZ is defined multiple times
主线位置:定时器主线的起点,
Timer::after(100ms)先把 100 ms 换算成 tick。tick 有多长由编译期常量TICK_HZ决定,它怎么从 feature 里选出来,见 Instant 与 Duration 的「TICK_HZ 从哪来」。
flowchart TB
bin["二进制 tick-hz-32_768"] --> drv["embassy-time-driver"]
std["std 带 1 MHz"] --> drv
lib["库 tick-hz-1_000"] --> drv
drv --> n{"打开几个"}
n -->|0| def["默认 1 MHz"]
n -->|1| one["用那一个"]
n -->|2 个以上| err["E0428"]
实验 crate exp-embassy/pita-tick、pita-tick2、pita-tick2-fixed、pita-tick3(std,debug);M2 Max、macOS 27、rustc 1.99.0。
现象
照抄了芯片例子的频率。 仓库里大多数 stm32 例子给 embassy-time 开 tick-hz-32_768。把这一行搬进一个用 std 驱动的程序(pita-tick):
embassy-time = { version = "=0.5.1", features = ["std", "tick-hz-32_768"] }
error[E0428]: the name `TICK_HZ` is defined multiple times
--> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/embassy-time-driver-0.2.2/src/tick.rs:86:1
|
54 | pub const TICK_HZ: u64 = 32_768;
| -------------------------------- previous definition of the value `TICK_HZ` here
...
86 | pub const TICK_HZ: u64 = 1_000_000;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `TICK_HZ` redefined here
|
= note: `TICK_HZ` must be defined only once in the value namespace of this module
For more information about this error, try `rustc --explain E0428`.
error: could not compile `embassy-time-driver` (lib) due to 1 previous error
Cargo.toml 里只写了一个频率,另一个是 std 带进来的。
一个库选了频率。 pita-tick2 的二进制只开 std,它依赖的本地库 sensor 写了:
[dependencies]
# a library that picks a tick rate itself
embassy-time = { version = "=0.5.1", features = ["tick-hz-1_000"] }
报错是同一条,只是 previous definition 那一行换成了 28 | pub const TICK_HZ: u64 = 1_000;。错误出在 embassy-time-driver 里,不提是谁开的 feature。
库选了频率,二进制没选。 这时没有任何报错,pita-tick3 是证据。二进制自带一个驱动、不开 tick-hz-*(embassy-stm32 的 TIM 驱动就是这样,按 TICK_HZ 算预分频),依赖同一个 sensor:
TICK_HZ = 1000, from_micros(1500) = Duration { ticks: 2 } = 2000 us
整个程序的时间单位变成了 1 ms,from_micros(1500) 向上取整成 2 ms。库作者在自己的测试里不会发现,因为对库来说 1 kHz 就是它想要的。
出处
TICK_HZ 转手取自 tick.rs,每个频率一行 #[cfg(feature = "tick-hz-X")] pub const TICK_HZ。cargo 把整个依赖图里对 embassy-time-driver 打开的 feature 合并成一个集合,开了两个不同的 tick-hz-*,tick.rs 里就有两行 pub const 同时生效,rustc 报 E0428。开两次同一个频率只留下一行,不报错。
embassy-time 的 Cargo.toml 里 std = ["tick-hz-1_000_000", ...],mock-driver、wasm 也一样,所以用这三个驱动时不能再选。同一个文件的 feature 说明写了规则:
#! At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used.
#!
#! If the time driver in use supports using arbitrary tick rates, you can enable one `tick-*`
#! feature from your binary crate to set the tick rate. The driver will use configured tick rate.
#! If the time driver supports a fixed tick rate, it will enable one feature itself, so you should
#! not enable one. Check the time driver documentation for details.
「from your binary crate」是关键:频率是全程序的决定,只有二进制和固定频率的驱动有资格做。
修法
先找出是谁开的。pita-tick2 里:
$ cargo tree -e features -i embassy-time-driver
embassy-time-driver v0.2.2
├── embassy-time-driver feature "default"
│ └── embassy-time v0.5.1
...
│ ├── embassy-time feature "std"
│ │ └── pita-tick2 v0.1.0 (~/exp-embassy/pita-tick2)
│ ├── embassy-time feature "tick-hz-1_000"
│ │ └── sensor v0.1.0 (~/exp-embassy/pita-tick2/sensor)
│ └── embassy-time feature "tick-hz-1_000_000"
│ └── embassy-time feature "std" (*)
├── embassy-time-driver feature "tick-hz-1_000"
│ └── embassy-time feature "tick-hz-1_000" (*)
└── embassy-time-driver feature "tick-hz-1_000_000"
└── embassy-time feature "tick-hz-1_000_000" (*)
最后两条分支就是两个频率,往上追:tick-hz-1_000 来自 sensor,tick-hz-1_000_000 来自 std。库里删掉 feature(pita-tick2-fixed):
[dependencies]
# a library leaves the tick rate to the binary
embassy-time = "=0.5.1"
TICK_HZ = 1000000, sensor period = Duration { ticks: 10000 }
库里的 Duration::from_millis(10) 跟着全程序的 1 MHz 变成 10000 tick,它本来就不该关心单位。第一种情况(照抄芯片例子)删掉 tick-hz-32_768:std 驱动只能是 1 MHz。
怎么确认没中招
- 库 crate 的
Cargo.toml里不应出现tick-hz-:grep -rn 'tick-hz' --include=Cargo.toml .,只允许二进制 crate 有,并且用固定频率驱动(std、mock-driver、wasm)时连二进制也不该有。 - 程序启动时打印一次
embassy_time::TICK_HZ,和驱动文档里的频率比一比。没有报错不代表频率是你选的。 - 想让一个库在不同频率下都对,它的测试至少在两个频率下各跑一遍;换算的取整方向见 Instant 与 Duration。
小结
TICK_HZ是tick.rs里按 feature 选中的一行常量;整个依赖图开了两个不同的tick-hz-*,就是E0428,报错指向embassy-time-driver。std/mock-driver/wasm自带 1 MHz;常见的冲突是照抄芯片例子的tick-hz-32_768,或一个库自己选了频率。- 库选频率、二进制没选时不报错,全程序的分辨率被改掉;用
cargo tree -e features -i embassy-time-driver找出是谁开的。