stm32-rs / stm32f4xx-hal

A Rust embedded-hal HAL for all MCUs in the STM32 F4 family
BSD Zero Clause License
550 stars 208 forks source link

implement `embedded-io` traits #721

Open rursprung opened 9 months ago

rursprung commented 9 months ago

with embedded-hal v1 the UART related traits have been removed in favour of embedded-io. thus these traits should be implemented by this HAL so that the replacement for the old e-h 0.2 traits is available.

for avr-hal i've done an attempt at this, maybe that helps as a reference: https://github.com/Rahix/avr-hal/pull/484 (i don't have an STM32 at hand, so i can't provide it here, sorry)

pdgilbert commented 8 months ago

I am working on an example that reads from one serial port and writes to another using Read and Write traits from embedded-io-0.6.1 (and using embedded-hal-1.0.0). The code further below compiles using @techmccat 's fork of stm32g4xx-hal and branch eh-v1.0 of stm32h7xx-hal. Compiling with stm32f4xx-hal gives these errors:

Click to expand compile errors ``` $ cargo build --no-default-features --target thumbv7em-none-eabihf --features stm32f411,stm32f4xx --example echo_by_char Updating crates.io index Updating git repository `https://github.com/techmccat/stm32g4xx-hal` Updating git repository `https://github.com/stm32-rs/stm32h7xx-hal` Compiling proc-macro2 v1.0.78 Compiling unicode-ident v1.0.12 Compiling semver-parser v0.7.0 Compiling cortex-m v0.7.7 Compiling nb v1.1.0 Compiling semver v1.0.21 Compiling vcell v0.1.3 Compiling cortex-m-rt v0.7.3 Compiling syn v1.0.109 Compiling nb v0.1.3 Compiling void v1.0.2 Compiling volatile-register v0.2.2 Compiling embedded-hal v0.2.7 Compiling critical-section v1.1.2 Compiling semver v0.9.0 Compiling autocfg v1.1.0 Compiling bitfield v0.13.2 Compiling rustc_version v0.2.3 Compiling byteorder v1.5.0 Compiling az v1.2.1 Compiling gcd v2.3.0 Compiling stm32f4 v0.15.1 Compiling rustc_version v0.4.0 Compiling bare-metal v0.2.5 Compiling stable_deref_trait v1.2.0 Compiling powerfmt v0.2.0 Compiling num-traits v0.2.17 Compiling cortex-m-semihosting v0.5.0 Compiling deranged v0.3.11 Compiling quote v1.0.35 Compiling syn v2.0.48 Compiling heapless v0.7.17 Compiling fugit v0.3.7 Compiling stm32f4xx-hal v0.20.0 Compiling num-conv v0.1.0 Compiling time-core v0.1.2 Compiling embedded-hal v1.0.0 Compiling litrs v0.4.1 Compiling bare-metal v1.0.0 Compiling embedded-hal-nb v1.0.0 Compiling time v0.3.34 Compiling fugit-timer v0.1.3 Compiling embedded-dma v0.2.0 Compiling hash32 v0.2.1 Compiling rand_core v0.6.4 Compiling float-cmp v0.9.0 Compiling embedded-graphics-core v0.4.0 Compiling micromath v2.1.0 Compiling embedded-storage v0.3.1 Compiling panic-semihosting v0.6.0 Compiling panic-halt v0.2.0 Compiling embedded-io v0.6.1 Compiling document-features v0.2.8 Compiling embedded-graphics v0.8.1 Compiling enumflags2_derive v0.7.8 Compiling cortex-m-rt-macros v0.7.0 Compiling enumflags2 v0.7.8 Compiling echo v0.0.1 (echo) error[E0308]: mismatched types --> examples/echo_by_char.rs:131:15 | 131 | tx1.write(b"\r\nconsole connect check.\r\n").ok(); | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `&[u8; 26]` | | | arguments to this method are incorrect | note: method defined here --> .cargo/registry/src/index.crates.io-6f17d22bba15001f/embedded-hal-nb-1.0.0/src/serial.rs:98:8 | 98 | fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; | ^^^^^ error[E0308]: mismatched types --> examples/echo_by_char.rs:135:15 | 135 | tx1.write(b"test read and write by char. Please type into the console ...").ok(); | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `&[u8; 61]` | | | arguments to this method are incorrect | note: method defined here --> .cargo/registry/src/index.crates.io-6f17d22bba15001f/embedded-hal-nb-1.0.0/src/serial.rs:98:8 | 98 | fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; | ^^^^^ error[E0061]: this method takes 0 arguments but 1 argument was supplied --> examples/echo_by_char.rs:141:24 | 141 | let _len = rx1.read(&mut buffer); | ^^^^ ----------- | | | unexpected argument of type `&mut [u8; 5]` | help: remove the extra argument | note: method defined here --> .cargo/registry/src/index.crates.io-6f17d22bba15001f/embedded-hal-nb-1.0.0/src/serial.rs:85:8 | 85 | fn read(&mut self) -> nb::Result; | ^^^^ error[E0308]: mismatched types --> examples/echo_by_char.rs:145:19 | 145 | tx1.write(&buffer).ok(); | ----- ^^^^^^^ expected `u8`, found `&[u8; 5]` | | | arguments to this method are incorrect | note: method defined here --> .cargo/registry/src/index.crates.io-6f17d22bba15001f/embedded-hal-nb-1.0.0/src/serial.rs:98:8 | 98 | fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; | ^^^^^ warning: unused import: `Write` --> examples/echo_by_char.rs:23:25 | 23 | use embedded_io::{Read, Write}; | ^^^^^ | = note: `#[warn(unused_imports)]` on by default warning: unused import: `Read` --> examples/echo_by_char.rs:23:19 | 23 | use embedded_io::{Read, Write}; | ^^^^ Some errors have detailed explanations: E0061, E0308. For more information about an error, try `rustc --explain E0061`. warning: `echo` (example "echo_by_char") generated 2 warnings error: could not compile `echo` (example "echo_by_char") due to 4 previous errors; 2 warnings emitted $ cargo build --no-default-features --target thumbv7em-none-eabihf --features stm32g474,stm32g4xx --example echo_by_char Compiling paste v1.0.14 Compiling cast v0.2.7 Compiling stm32g4 v0.15.1 Compiling bitflags v1.3.2 Compiling static_assertions v1.1.0 Compiling embedded-dma v0.1.2 Compiling fdcan v0.1.2 Compiling stm32g4xx-hal v0.0.2 (https://github.com/techmccat/stm32g4xx-hal?branch=hal-1#bf979a6b) Compiling echo v0.0.1 (echo) Finished dev [unoptimized + debuginfo] target(s) in 21.62s $ cargo build --no-default-features --target thumbv7em-none-eabihf --features stm32h742,stm32h7xx --example echo_by_char Compiling nb v0.1.3 Compiling stm32h7 v0.15.1 Compiling cast v0.3.0 Compiling embedded-hal v0.2.7 Compiling cortex-m v0.7.7 Compiling cortex-m-semihosting v0.5.0 Compiling panic-semihosting v0.6.0 Compiling stm32h7xx-hal v0.15.1 (https://github.com/stm32-rs/stm32h7xx-hal?branch=eh-v1.0#24dcd419) Compiling echo v0.0.1 (echo) Finished dev [unoptimized + debuginfo] target(s) in 24.69s ```

From the unused import messages I think the stm32f4xx-hal compile is not finding the traits properly. Is there a work-around or fix for this? The code and Cargo.toml` are

Click to expand code ``` //! Echo console input back to console //! //! On blackpill: //! Connect the Tx pin pa9 to the Rx pin of usb-ttl converter //! Connect the Rx pin pa10 to the Tx pin of usb-ttl converter //! Set up the serial console (e.g. minicom) with the same settings used here. //! (Using 9600bps, could be higher but needs serial console to be the same.) #![deny(unsafe_code)] #![no_main] #![no_std] #[cfg(debug_assertions)] use panic_semihosting as _; #[cfg(not(debug_assertions))] use panic_halt as _; use cortex_m_semihosting::hprintln; use cortex_m_rt::entry; use embedded_io::{Read, Write}; //use core::str::from_utf8; #[cfg(feature = "stm32f4xx")] use stm32f4xx_hal as hal; #[cfg(feature = "stm32g4xx")] use stm32g4xx_hal as hal; #[cfg(feature = "stm32h7xx")] use stm32h7xx_hal as hal; use hal::{ // for common locations, differences below // pac::Peripherals, //pac::USART1, prelude::*, serial::{Rx, Tx}, }; // setup() does hal/MCU specific setup and returns generic (tx, rx) for use in main code. #[cfg(feature = "stm32f4xx")] use stm32f4xx_hal::{ pac::Peripherals, pac::USART1, serial::{config::Config, Serial}, }; #[cfg(feature = "stm32f4xx")] fn setup() -> (Tx, Rx) { let dp = Peripherals::take().unwrap(); let rcc = dp.RCC.constrain(); let clocks = rcc.cfgr.freeze(); let gpioa = dp.GPIOA.split(); Serial::new( dp.USART1, (gpioa.pa9.into_alternate(), gpioa.pa10.into_alternate()), Config::default().baudrate(9600.bps()), &clocks, ).unwrap().split() } #[cfg(feature = "stm32g4xx")] use stm32g4xx_hal::{ stm32::Peripherals, // is there a convention that pac should be used or an alias? stm32::USART1, // is there a convention that pac should be used or an alias? serial::{FullConfig, NoDMA}, gpio::{Alternate, gpioa::{PA9, PA10}}, // why Tx not Tx ? }; #[cfg(feature = "stm32g4xx")] fn setup() -> (Tx>, NoDMA>, Rx>, NoDMA>) { let dp = Peripherals::take().unwrap(); let mut rcc = dp.RCC.constrain(); let gpioa = dp.GPIOA.split(&mut rcc); dp.USART1.usart( gpioa.pa9.into_alternate(), gpioa.pa10.into_alternate(), FullConfig::default().baudrate(9600.bps()), &mut rcc).unwrap().split() } #[cfg(feature = "stm32h7xx")] use stm32h7xx_hal::{ pac::Peripherals, pac::USART1, }; #[cfg(feature = "stm32h7xx")] fn setup() -> (Tx, Rx) { let dp = Peripherals::take().unwrap(); let pwr = dp.PWR.constrain(); let vos = pwr.freeze(); let rcc = dp.RCC.constrain(); let ccdr = rcc.sys_ck(160.MHz()).freeze(vos, &dp.SYSCFG); let clocks = ccdr.clocks; let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); dp.USART1.serial( ( gpioa.pa9.into_alternate(), //tx gpioa.pa10.into_alternate(), //rx ), 9600.bps(), ccdr.peripheral.USART1, &clocks, ).unwrap().split() } // End of hal/MCU specific setup. Following should be generic code. #[entry] fn main() -> ! { let (mut tx1, mut rx1) = setup(); hprintln!("test write to console ..."); tx1.write(b"\r\nconsole connect check.\r\n").ok(); hprintln!("test read and write by char. Please type into the console ..."); tx1.write(b"test read and write by char. Please type into the console ...").ok(); let mut buffer: [u8; 5] = [0; 5]; // could be length 1 for a byte loop { // Read a byte and write let _len = rx1.read(&mut buffer); //hprintln!("received"); // for debugging tx1.write(&buffer).ok(); hprintln!("{:?}", &buffer); // for debugging } } ```
Click to expand Cargo.toml ``` [package] authors = ["pdGilbert"] categories = ["embedded", "no-std"] description = "echo between serial interfaces, embedded-io example" keywords = ["serial", "embedded-io", "example"] license = "MIT OR Apache-2.0" name = "echo" version = "0.0.1" edition = "2021" [dependencies] stm32f4xx-hal = { version = "0.20.0", optional = true } stm32g4xx-hal = { git = "https://github.com/techmccat/stm32g4xx-hal", optional = true, branch = "hal-1" } stm32h7xx-hal = { git = "https://github.com/stm32-rs/stm32h7xx-hal", optional = true, branch = "eh-v1.0"} embedded-hal = "1.0" embedded-io = "0.6.1" embedded-graphics = ">=0.7" heapless = "0.7" cortex-m-rt = ">=0.7.0" cortex-m-semihosting = { version = "0.5.0" } panic-halt = { version = ">=0.2.0" } panic-semihosting = { version = ">=0.6.0" } [features] stm32f4xx = ["stm32f4xx-hal" ] stm32g4xx = ["stm32g4xx-hal" ] stm32h7xx = ["stm32h7xx-hal" ] stm32f401 = ["stm32f4xx-hal/stm32f401" ] stm32f411 = ["stm32f4xx-hal/stm32f411" ] stm32g473 = ["stm32g4xx-hal/stm32g473" ] stm32g474 = ["stm32g4xx-hal/stm32g474" ] stm32h742 = ["stm32h7xx-hal/stm32h742" ] ```

These are also available in a package structure at https://github.com/pdgilbert/echo .

This example is derived from an example that worked with embedded-hal-0.2.7 by reading and writing individual bytes. As pointed out by @richardion (see https://github.com/stm32-rs/stm32h7xx-hal/issues/474#issuecomment-1883838622 ), there are no methods for byte wise serial operations in embedded-io. Thus the read/write buffer in the example can now be more than a single byte. I am not sure the example code will accomplish this properly, so comments on the code would also be appreciated.

pdgilbert commented 8 months ago

Also tried with the stm32f4xx_hal io branch for @burrbull 's pull request https://github.com/stm32-rs/stm32f4xx-hal/pull/725. The error message suggests to disambiguate the method. So changing, for example, tx1.write(b"\r\nconsole connect check.\r\n").ok(); to embedded_io::Write::write(&mut tx1, b"\r\nconsole connect check.\r\n").ok(); the writes do not cause errors. It does not address Read. Attempting read with let _len = embedded_io::Read::read(&mut rx1, &mut buffer).ok(); to disambiguate gives

Click to expand compile errors ``` $ cargo build --no-default-features --target $TARGET --features $MCU,$HAL --example echo_by_char Compiling echo v0.0.1 (echo) warning: unused imports: `Read`, `Write` --> examples/echo_by_char.rs:23:19 | 23 | use embedded_io::{Read, Write}; | ^^^^ ^^^^^ | = note: `#[warn(unused_imports)]` on by default error[E0277]: the trait bound `stm32f4xx_hal::serial::Rx: embedded_io::Read` is not satisfied --> examples/echo_by_char.rs:144:44 | 144 | let _len = embedded_io::Read::read(&mut rx1, &mut buffer).ok(); | ----------------------- ^^^^^^^^ the trait `embedded_io::Read` is not implemented for `stm32f4xx_hal::serial::Rx` | | | required by a bound introduced by this call | = help: the following other types implement trait `embedded_io::Read`: &[u8] &mut T For more information about this error, try `rustc --explain E0277`. warning: `echo` (example "echo_by_char") generated 1 warning error: could not compile `echo` (example "echo_by_char") due to 1 previous error; 1 warning emitted ```