stm32-rs / stm32f4xx-hal

A Rust embedded-hal HAL for all MCUs in the STM32 F4 family

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

implement `embedded-io` traits

rursprung opened this issue · comments

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: Rahix/avr-hal#484 (i don't have an STM32 at hand, so i can't provide it here, sorry)

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<Word, Self::Error>;
    |        ^^^^

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<USART1>, Rx<USART1>) {
    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<USART1, pin, pin> not Tx<USART1> ?
};

#[cfg(feature = "stm32g4xx")]
fn setup() -> (Tx<USART1, PA9<Alternate<7_u8>>, NoDMA>, Rx<USART1, PA10<Alternate<7_u8>>, 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<USART1>, Rx<USART1>) {
    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 stm32-rs/stm32h7xx-hal#474 (comment) ), 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.

Also tried with the stm32f4xx_hal io branch for @burrbull 's pull request #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<stm32f4xx_hal::pac::USART1>: 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<stm32f4xx_hal::pac::USART1>`
    |                    |
    |                    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