
嵌入式物联网异步编程【免费下载链接】embassyModern embedded framework, using Rust and async.项目地址https://gitcode.com/gh_mirrors/em/embassy点击查看免费下载导读本文以 embassy-nxp/CHANGELOG.md 为骨架逐条解读 embassy-nxp 首个带 changelog 的版本0.1.0中针对 NXP LPC55 系列芯片落地的一系列 HAL 能力阻塞版 SPI 驱动、基于 DMA 的异步 USART、简单 PWM 输出、以及底层从lpc55-pac迁移到统一的nxp-pac元数据代码生成机制。通过结合 embassy-nxp/src 下的真实实现你将掌握 LPC55S69 等芯片上 Flexcomm 外设USART/SPI的驱动结构、DMA 异步收发的底层调用链以及 embassy 系 HAL 如何借助nxp-pac元数据在构建期自动生成外设与引脚代码。一、版本与变更背景embassy-nxp 是 Embassy 异步嵌入式框架中面向 NXP 芯片的 HAL crate当前版本为0.1.0见 embassy-nxp/Cargo.toml许可证为 MIT OR Apache-2.0publish false属于仓库内的工作区 crate。它通过package.metadata.embassy定义了针对thumbv8m.main-none-eabihfLPC55 系列与thumbv7em-none-eabihfi.MX RT 系列的构建矩阵[package.metadata.embassy] build [ {target thumbv8m.main-none-eabihf, features [defmt, lpc55-core0]}, {target thumbv8m.main-none-eabihf, features [defmt, lpc55s16]}, {target thumbv7em-none-eabihf, features [defmt, mimxrt1011, rt, time-driver-pit]}, {target thumbv7em-none-eabihf, features [defmt, mimxrt1062, rt, time-driver-pit]}, ]从 changelog 的条目来看这一版本的核心工作集中在 LPC55 平台补齐阻塞 SPI、引入 DMA 与异步 USART、落地简单 PWM并把整个 PAC 依赖切换为统一的nxp-pac。以下逐条展开。二、LPC55阻塞版 SPI 驱动变更条目LPC55: blocking version of SPI在 embassy-nxp/src/spi.rs 中SPI 模块通过#[cfg_attr(lpc55, path ./spi/lpc55.rs)]按芯片选择实现文件LPC55 的实际实现位于 embassy-nxp/src/spi/lpc55.rs。2.1 配置结构驱动提供Config结构体包含频率、相位、极性、数据位序四项pub struct Config { pub frequency: u32, // 时钟频率默认 1_000_0001 MHz pub phase: Phase, // 默认 CaptureOnFirstTransition pub polarity: Polarity, // 默认 IdleLow pub data_format: DataFormat, // MsbFirst / LsbFirst默认 MsbFirst }phase与polarity直接复用embedded_hal_02::spi的Phase、Polarity枚举DataFormat则由 HAL 自行定义最终映射到 SPI 寄存器的LSBF位spi::vals::Lsbf::Reverse/Standard。2.2 阻塞 API 集合阻塞模式通过Spid, Blocking提供四组核心方法blocking_write只写逐字节等待txnotfull写入fifowr寄存器后flushblocking_read只读写入哑数据txdata 0同时读取fifordblocking_transfer半双工读写以read.len().max(write.len())为循环上界blocking_transfer_in_place原地读写。所有方法都会在写入 FIFO 前轮询txnotfull并在返回前检查txerr/rxerr出错时返回Error::Overrun。此外还实现了embedded_hal_02::blocking::spi::Transferu8与Writeu8trait保证与既有 embedded-hal 生态驱动兼容。构造函数方面提供三种模式new_blocking全双工SCK MOSI MISOnew_blocking_txonly仅发送SCK MOSInew_blocking_rxonly仅接收SCK MISO。若 MOSI 与 MISO 均未提供构造函数会返回ConstructorError::NoTransferringPinsError频率超出可配置范围时返回IncompatibleFrequencyError。2.3 底层时钟链整数分频 分数分频两级雕琢SPI 的时钟配置是这段代码中比较有代表性的实现。LPC55 的 Flexcomm 接口函数时钟公式为FCLK (FCCLKSEL 选择的时钟) / (1 MULT / DIV) 最终频率 FCLK / (DIVVAL 1)源码embassy-nxp/src/spi/lpc55.rs 的configure_clock固定选择 96 MHz 作为源时钟FcclkselSel::Enum0x3先计算整数分频div_val (96_000_000 / frequency).min(0xFFFF)再计算分数分频mult_val ((raw_clock * 256 / frequency) - 256).min(255)其中DIV固定写0xFF即分母 256。注释明确给出了理论可达范围最低约 732 Hz96 MHz / 131_072。这种先整数分频、再分数微调的两级设计是为了让任意目标频率都能被较高精度的MULT/DIV分数发生器雕琢出来。2.4 Flexcomm 外设选择与引脚绑定LPC55 的 Flexcomm 外设是多面手同一 IP 可通过PERSEL位选择工作为 USART、SPI 或 I2C。configure_flexcomm完成三步使能SYSCON中 IOCON 与对应 Flexcomm 实例的 AHB 时钟通过presetctrl1对外设做一次断言复位再释放将pselid.persel置为Spi并set_lock(true)——锁定位一旦置位直到整板复位前都无法再更改外设模式。引脚绑定采用实例 trait 引脚 trait的双重约束Spid, Blocking的构造函数要求sck: impl SckPinT、mosi: impl MosiPinT、miso: impl MisoPinT。这些 trait 由impl_spi_sck_pin!/impl_spi_mosi_pin!/impl_spi_miso_pin!三个宏在芯片级代码中批量实现每个引脚会带出对应的PioFuncIOCON 复用功能编号。三、Codegen基于 nxp-pac 元数据的外设代码生成变更条目Codegen using nxp-pac metadata这是本次版本最底层的变化不再为每个芯片手写外设枚举而是由构建脚本从nxp-pac的METADATA元数据中自动生成。3.1 构建脚本整体流程embassy-nxp/build.rs 的main函数执行以下步骤通过环境变量CARGO_FEATURE_*检测启用了哪个芯片 featuremimxrt1011/mimxrt1062/lpc55s16/lpc55-core0未启用或启用多个都会 panic定义两组 cfg 别名cfg_aliases! { rt1xxx: { any(feature mimxrt1011, feature mimxrt1062) }, } cfg_aliases! { lpc55: { any(feature lpc55s16, feature lpc55-core0) }, }调用generate_code生成外设单例peripheral singleton与引脚实现输出到OUT_DIR/_generated.rs。3.2 单例生成策略singletons函数遍历metadata::METADATA.peripherals按名字前缀分流跳过GPIO*与DMA*它们需要在第二轮处理GPIO{n}为每个引脚生成PIO{n}_{pin}单例并根据引脚号是否大于 15 启用gpio{n}_hicfgDMA{n}为每个DMA{n}_CH{channel}生成通道单例SCT{n}只取名字以OUT开头的信号生成SCT{n}_OUT{m}输出单例。最终这些单例在 embassy-nxp/src/chips/lpc55.rs 通过include!(concat!(env!(OUT_DIR), /_generated.rs))汇入并由 embassy-nxp/src/lib.rs 的pub use chip::{Peripherals, interrupt, peripherals}对外暴露。对于 i.MX RT 系列_rt1xxxgenerate_iomuxc还会从元数据的pins中筛选带有iomuxc.mux定义的引脚生成iomuxc_pad/iomuxc_mux查询函数供 embassy-nxp/src/iomuxc.rs 使用。这意味着引脚复用配置也全部由元数据驱动消除了手写 match 表。3.3 对开发者的影响新增芯片时只需要在nxp-pac中补充元数据并给Cargo.toml增加一个芯片 feature无需手写大量重复的引脚实现外设单例命名如peripherals::USART0、peripherals::PIO1_5全部与 PAC 元数据保持一致降低了出错概率代价是构建依赖nxp-pacgit 依赖固定 rev 为98b09d2eae1f073804d6ded639c8dab583f614b0与proc-macro2、quote构建期更长。四、LPC55简单 PWM变更条目LPC55: PWM simplePWM 实现位于 embassy-nxp/src/pwm/lpc55.rs底层使用 LPC55 的 SCTimer/PWMSCT0统一计数器。4.1 配置模型与周期公式pub struct Config { pub invert: bool, // 是否反相输出 pub phase_correct: bool, // 相位校正模式开启后输出频率减半 pub enable: bool, // 是否启动输出 pub divider: u8, // SYSCON 时钟分频实际除以 divider 1 pub prescale_factor: u8, // SCT 预分频实际除以 prescale_factor 1 pub compare: u32, // 比较值输出电平翻转点 pub top: u32, // 计数上限决定周期 }文档注释给出了输出周期计算公式周期时钟周期数 (top 1) * (phase_correct ? 1 : 2) * divider * prescale_factor默认 SCT 时钟为 96 MHz。Config::new(compare, top)提供便捷构造函数默认divider 255、prescale_factor 255。4.2 实现要点共享计数器SCT0 是统一计数器TOP_VALUE以AtomicU32静态保存。源码通过assert!(config.compare config.top)强制比较值不超过计数上限否则计数器永远达不到匹配事件TOP_VALUE一旦在第一个实例初始化时写入之后再次修改会直接panic!(The top value cannot be changed after the initialization.)。这与注释中counter is shared的设计一致——周期由第一个通道决定后续通道只能改占空比。事件与输出映射match_(0)保存top值match_(output_number 1)保存compare值ev(0)与ev(output_number 1)两个事件配合out_set/out_clr决定输出拉高/拉低的时机。invert true时二者互换。生命周期Pwm实现Drop用REF_COUNTAtomicU8跟踪活跃实例最后一个实例销毁时重置TOP_VALUE允许后续重新配置周期。初始化钩子Pwm::reset()在init阶段见 embassy-nxp/src/lib.rs 的pwm::Pwm::reset()通过presetctrl1.sct_rst断言/释放复位保证计数器从确定状态开始。4.3 使用方式let mut pwm Pwm::new_output( p.SCT0_OUT0, // SCT 输出通道单例 p.PIO1_5, // 输出引脚 Config::new(compare, top), // 直接指定 compare 与 top ); // 运行时改占空比 pwm.set_config(Config { compare: new_val, ..config }); let counter pwm.counter(); // 读取当前计数值五、LPC55USART 的 ALT 定义迁移与内部宏清理变更条目LPC55: Move ALT definitions for USART to TX/RX pin impls.LPC55: Remove internal match_iocon macro这两条属于内部重构但影响了公共 API 的形态。在 embassy-nxp/src/usart/lpc55.rs 中可以看到每个impl_usart_txd_pin!/impl_usart_rxd_pin!宏展开即为引脚 USART 实例 IOCON 复用功能号的三元绑定impl_usart_txd_pin!(PIO0_29, USART0, Func4); impl_usart_rxd_pin!(PIO0_0, USART0, Func3);即把该引脚作为某 USART 的 TX/RX 时应该配置成哪个 ALT 功能直接收进引脚实现里取代了原先集中式match_iocon宏根据 (pin, peripheral) 查表的分发逻辑。对用户而言编译器在Usart::new(usart, tx_pin, rx_pin, ...)时通过impl TxPinT/impl RxPinTtrait 约束直接校验引脚与 USART 实例是否匹配错误在编译期暴露而不是运行期查表失败。六、LPC55DMA 控制器与异步 USART变更条目LPC55: DMA Controller and asynchronous version of USART这是本版本功能量最大的条目DMA 驱动落地并为 USART 提供异步收发能力。DMA 模块入口在 embassy-nxp/src/dma.rs实际实现位于 embassy-nxp/src/dma/lpc55.rs异步 USART 与 DMA 的配合逻辑集中在 embassy-nxp/src/usart/lpc55.rs。6.1 模式系统与中断绑定USART 驱动使用Mode泛型区分阻塞/异步两种模式embassy-nxp/src/lib.rspub trait Mode: SealedMode {} pub struct Blocking; // 阻塞模式 pub struct Async; // 异步模式Usartd, M: Mode内部持有UsartTxd, M与UsartRxd, M两个半部可调用split()/split_ref()拆分给不同任务使用。异步模式依赖中断绑定。lib.rs导出的bind_interrupts!宏将 IRQ 与InterruptHandler关联bind_interrupts!( struct Irqs { FLEXCOMM0 usart::InterruptHandlerperipherals::USART0; } );InterruptHandler::on_interrupt的实现值得注意当收到 RX 错误中断时它不清中断标志而是置位dma_state.rx_errAtomicBool并唤醒rx_err_wakerAtomicWaker。注释解释了原因清标志会让 DMA 传输继续可能在我们检查传输期间发生的错误之前就发出完成信号——因此必须让 DMA 先停下来再统一判定错误类型。6.2 异步 TXDMA 写pub async fn write(mut self, buffer: [u8]) - Result(), Error { let ch self.tx_dma.as_mut().unwrap().reborrow(); let transfer unsafe { self.info.usart_reg.fifocfg().modify(|w| w.set_dmatx(true)); crate::dma::write(ch, buffer, self.info.usart_reg.fifowr().as_ptr() as *mut _) }; transfer.await; Ok(()) }流程是开启dmatx位让 DMA 按 FIFO 水位节拍搬运数据 → 构造 DMA 传输 future →await等待完成。源码特意将 future 绑定到变量transfer上再 await注释指出若不这样做数据寄存器指针会跨 await 被持有使 future 变为非 Send。6.3 异步 RXFIFO 预读 DMA 错误竞争read的实现比 TX 复杂得多核心思想是错误字节也会进 FIFO先清错误标志并同步读取至多 16 字节FIFO 深度——drain_fifo逐个检查rxerrOverrun、parityerr、framerr、rxnoise、deltarxbrk若 FIFO 预读已满足请求长度直接返回否则使能错误中断framerren/parityerren/rxnoiseen/rxerr与dmarx发起 DMA 读用embassy_futures::select::select同时等待传输完成与错误唤醒两个 future若 DMA 先完成仍要通过rx_err.swap(false)检查最后一字节是否携带错误Either::First分支的注释错误可能发生在最后一个字节上判定具体错误类型按framerrint→parityerrint→rxnoiseint→ FIFOrxerr的顺序检查中断状态寄存器。6.4 阻塞与异步的 API 对照能力阻塞Blocking异步Async发送blocking_write/blocking_flushwrite().awaitDMA接收blocking_read轮询 FIFOread().awaitFIFO 预读 DMA状态tx_busy()同上构造new_blocking无需 DMA 通道new需要 IRQ 绑定 TX/RX DMA 通道UsartRx::new_inner中有一处debug_assert_eq!(has_irq, rx_dma.is_some())即有中断处理则必须有 DMA 通道保证错误中断路径与 DMA 路径配对出现。异步构造的UsartRx在new_inner中会先unpend()再enable()对应 NVIC 中断。七、从 lpc55-pac 迁移到 nxp-pac变更条目Moved NXP LPC55S69 from lpc55-pac to nxp-pac这是版本中最具方向性的架构决策LPC55S69 的 PAC 依赖从独立的lpc55-pac统一迁移到nxp-pac。从 embassy-nxp/Cargo.toml 可以看到nxp-pac同时是普通依赖可选rev 98b09d2eae1f073804d6ded639c8dab583f614b0与构建依赖default-features false, features [metadata]芯片选择 feature 直接透传 PAC 的芯片 featurelpc55-core0 [nxp-pac/lpc55s69_cm33_core0, _lpc55] lpc55s16 [nxp-pac/lpc55s16, _lpc55] mimxrt1011 [nxp-pac/mimxrt1011, _rt1xxx, dep:imxrt-rt] mimxrt1062 [nxp-pac/mimxrt1062, _rt1xxx, dep:imxrt-rt]PAC 通过unstable-pacfeature 在embassy_nxp::pac重导出不启用时仅pub(crate)可见。Cargo.toml注释解释了为何这个重导出永远不稳定embassy-nxp 的 semver-minor非破坏性发布可能对 PAC 做 major-bump破坏性升级官方建议需要固定 PAC 版本的用户直接依赖固定版本的 PAC且没有计划让这一 feature 稳定。统一到nxp-pac后SPI/USART 的寄存器访问都变成crate::pac::spi::Spi、crate::pac::usart::Usart、crate::pac::flexcomm::Flexcomm、crate::pac::sct0等统一命名空间配合第三节的元数据 codegen驱动代码不再感知具体芯片的 PAC crate 差异同一套驱动代码可同时覆盖 LPC55 与 i.MX RT 两条产品线。八、版本演进脉络小结将各条目按依赖关系串联可以还原 0.1.0 的开发顺序与设计意图基础设施先行从lpc55-pac迁移到nxp-pac并引入基于METADATA的构建期 codegen为多芯片支持打底外设驱动按阻塞 → 异步梯度补齐先有阻塞版 SPISpid, Blocking embedded-hal 0.2 trait 实现随后 DMA 控制器就绪USART 才获得异步收发能力重构改善可维护性把 USART 的 ALT 定义下沉到 TX/RX 引脚 impl移除内部match_iocon宏让引脚-外设匹配关系在类型层面表达PWM 提供基础输出能力基于 SCT0 统一计数器先做简单版本单一周期、多通道占空比。对于想深入代码的读者推荐按以下路径阅读入口与初始化embassy-nxp/src/lib.rsinit()、bind_interrupts!、Mode类型系统阻塞 SPI 完整实现embassy-nxp/src/spi/lpc55.rs异步 USART DMA 竞争逻辑embassy-nxp/src/usart/lpc55.rs构建期 codegenembassy-nxp/build.rs 与 embassy-nxp/src/chips/lpc55.rsPWM 实现embassy-nxp/src/pwm/lpc55.rs。需要注意的是本 crate 仍处于 0.1.0 早期阶段unstable-pac明确不稳定SPI 的 DMA 版本在configure_spi中仍以注释// DMA is going to be disabled until the async version is implemented标记为未完成LPC55 USART 的 CTS/RTS/SCK 引脚 trait 也带有TODO(wt): This needs to be wired up的未接线注释——这些都可以视为下一步版本的功能预告引用该版本能力时应以这些实际代码状态为准。赞分享嵌入式物联网异步编程【免费下载链接】embassyModern embedded framework, using Rust and async.项目地址https://gitcode.com/gh_mirrors/em/embassy点击查看免费下载相关推荐MaterialStyledDialogs Builder API详解轻松定制专属对话框MaterialStyledDialogs Builder API详解轻松定制专属对话框 MaterialStyledDialogs是一款专为Android开Apache OpenDAL™ Operator 完全指南异步与阻塞操作深度解析Apache OpenDAL™ Operator 完全指南异步与阻塞操作深度解析 Apache OpenDAL™ 是一个革命性的数据访问层旨在为开发者提供统数据存储后端screenshot-to-code 的提交历史与非阻塞多变体生成机制深度解析screenshot to code 的提交历史与非阻塞多变体生成机制深度解析 screenshot to code 将一次代码生成结果组织为可回溯的“提交C人工智能大模型AI 应用代码生成上一篇在 Angular 中集成 Tailwind CSS 与 daisyUI让模板摆脱工具类噪声的组件化方案下一篇nvm-desktop高级技巧自定义配置与镜像源设置指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考