ryankurte / embedded-hal-compat

Rust embedded-hal inter-version compatibility layer
MIT License
15 stars 11 forks source link

usage question #3

Closed pdgilbert closed 3 years ago

pdgilbert commented 3 years ago

I am trying to use embedded-hal-compat with a fork of rust-radio-sx127x in which I have put some examples. I am testing with stm32f1xx_hal. In Cargo.toml I have added

[dependencies]
#embedded-hal-compat = "0.1.2"
embedded-hal-compat = { git = "https://github.com/ryankurte/embedded-hal-compat.git", branch = "main"}

In the example code I have added


$ cargo build  --no-default-features  --target $TARGET --features=$HAL,$MCU  --example lora_spi_send
warning: Patch `radio v0.8.1 (https://github.com/ryankurte/rust-radio.git?branch=master#b2383d55)` was not used in the crate graph.
Check that the patched package version and available features are compatible
with the dependency requirements. If the patch has a different version from
what is locked in the Cargo.lock file, run `cargo update` to use the new
version. This may also occur with an optional dependency that is not enabled.
   Compiling semver-parser v0.7.0
   Compiling nb v1.0.0
   Compiling typenum v1.12.0
   Compiling proc-macro2 v1.0.24
   Compiling void v1.0.2
   Compiling unicode-xid v0.2.1
   Compiling version_check v0.9.2
   Compiling cortex-m v0.7.1
   Compiling syn v1.0.61
   Compiling vcell v0.1.3
   Compiling stable_deref_trait v1.2.0
   Compiling bitfield v0.13.2
   Compiling log v0.4.14
   Compiling cfg-if v1.0.0
   Compiling cortex-m-rt v0.6.13
   Compiling cortex-m v0.6.7
   Compiling async-trait v0.1.45
   Compiling stm32f1 v0.11.0
   Compiling r0 v0.2.2
   Compiling libc v0.2.87
   Compiling cortex-m-semihosting v0.3.7
   Compiling bitflags v1.2.1
   Compiling byteorder v1.4.2
   Compiling radio-sx127x v0.10.1 (/home/paul/githubClones/rust-radio-sx127x)
   Compiling heapless v0.6.1
   Compiling panic-halt v0.2.0
   Compiling nb v0.1.3
   Compiling embedded-hal v1.0.0-alpha.4
   Compiling volatile-register v0.2.0
   Compiling semver v0.9.0
   Compiling embedded-dma v0.1.2
   Compiling generic-array v0.14.4
   Compiling embedded-hal v0.2.4
   Compiling hash32 v0.1.1
   Compiling rustc_version v0.2.3
   Compiling embedded-hal-compat v0.1.2 (https://github.com/ryankurte/embedded-hal-compat.git?branch=main#4e6f7e3b)
   Compiling driver-pal v0.8.0-alpha.2
   Compiling embedded-spi v0.6.2
   Compiling bare-metal v0.2.5
   Compiling cast v0.2.3
   Compiling quote v1.0.9
   Compiling generic-array v0.12.4
   Compiling generic-array v0.13.3
   Compiling panic-semihosting v0.5.6
   Compiling as-slice v0.1.5
   Compiling aligned v0.3.4
   Compiling cortex-m-rt-macros v0.1.8
   Compiling radio v0.7.0
   Compiling stm32f1xx-hal v0.7.0
error[E0107]: wrong number of type arguments: expected 3, found 2
   --> examples/lora_spi_send.rs:196:55
    |
196 |     fn setup() ->  impl DelayMs<u32> + Transmit<Error=sx127xError<Error, core::convert::Infallible>> {
    |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 3 type arguments

error[E0308]: mismatched types
   --> examples/lora_spi_send.rs:220:12
    |
220 |            MODE,
    |            ^^^^ expected struct `stm32f1xx_hal::spi::Mode`, found struct `embedded_hal::spi::Mode`
    |
    = note: perhaps two different versions of crate `embedded_hal` are being used?

error[E0277]: the trait bound `Compat<Spi<stm32f1xx_hal::pac::SPI1, Spi1NoRemap, (PA5<Alternate<PushPull>>, PA6<Input<Floating>>, PA7<Alternate<PushPull>>), u8>>: embedded_hal::blocking::spi::transactional::Default<u8>` is not satisfied
   --> examples/lora_spi_send.rs:231:19
    |
231 |        let lora = Sx127x::spi(
    |                   ^^^^^^^^^^^ the trait `embedded_hal::blocking::spi::transactional::Default<u8>` is not implemented for `Compat<Spi<stm32f1xx_hal::pac::SPI1, Spi1NoRemap, (PA5<Alternate<PushPull>>, PA6<Input<Floating>>, PA7<Alternate<PushPull>>), u8>>`
    |
    = note: required because of the requirements on the impl of `embedded_hal::blocking::spi::Transactional<u8>` for `Compat<Spi<stm32f1xx_hal::pac::SPI1, Spi1NoRemap, (PA5<Alternate<PushPull>>, PA6<Input<Floating>>, PA7<Alternate<PushPull>>), u8>>`
    = note: required by `radio_sx127x::Sx127x::<driver_pal::wrapper::Wrapper<Spi, SpiError, CsPin, BusyPin, ReadyPin, ResetPin, PinError, Delay, DelayError>, SpiError, PinError, DelayError>::spi`

error[E0277]: the trait bound `PA1<Output<PushPull>>: embedded_hal::digital::OutputPin` is not satisfied
   --> examples/lora_spi_send.rs:231:19
    |
231 |        let lora = Sx127x::spi(
    |                   ^^^^^^^^^^^ the trait `embedded_hal::digital::OutputPin` is not implemented for `PA1<Output<PushPull>>`
    |
    = note: required by `radio_sx127x::Sx127x::<driver_pal::wrapper::Wrapper<Spi, SpiError, CsPin, BusyPin, ReadyPin, ResetPin, PinError, Delay, DelayError>, SpiError, PinError, DelayError>::spi`

error[E0277]: the trait bound `PB8<Input<Floating>>: embedded_hal::digital::InputPin` is not satisfied
   --> examples/lora_spi_send.rs:231:19
    |
231 |        let lora = Sx127x::spi(
    |                   ^^^^^^^^^^^ the trait `embedded_hal::digital::InputPin` is not implemented for `PB8<Input<Floating>>`
    |
    = note: required by `radio_sx127x::Sx127x::<driver_pal::wrapper::Wrapper<Spi, SpiError, CsPin, BusyPin, ReadyPin, ResetPin, PinError, Delay, DelayError>, SpiError, PinError, DelayError>::spi`

error[E0277]: the trait bound `PB9<Input<Floating>>: embedded_hal::digital::InputPin` is not satisfied
   --> examples/lora_spi_send.rs:231:19
    |
231 |        let lora = Sx127x::spi(
    |                   ^^^^^^^^^^^ the trait `embedded_hal::digital::InputPin` is not implemented for `PB9<Input<Floating>>`
    |
    = note: required by `radio_sx127x::Sx127x::<driver_pal::wrapper::Wrapper<Spi, SpiError, CsPin, BusyPin, ReadyPin, ResetPin, PinError, Delay, DelayError>, SpiError, PinError, DelayError>::spi`

error[E0277]: the trait bound `PA0<Output<PushPull>>: embedded_hal::digital::OutputPin` is not satisfied
   --> examples/lora_spi_send.rs:231:19
    |
231 |        let lora = Sx127x::spi(
    |                   ^^^^^^^^^^^ the trait `embedded_hal::digital::OutputPin` is not implemented for `PA0<Output<PushPull>>`
    |
    = note: required by `radio_sx127x::Sx127x::<driver_pal::wrapper::Wrapper<Spi, SpiError, CsPin, BusyPin, ReadyPin, ResetPin, PinError, Delay, DelayError>, SpiError, PinError, DelayError>::spi`

warning: unused import: `DelayMs`
  --> examples/lora_spi_send.rs:71:51
   |
71 | use embedded_hal_compat::eh1_0::blocking::delay::{DelayMs as _};
   |                                                   ^^^^^^^
   |
   = note: `#[warn(unused_imports)]` on by default

error: aborting due to 7 previous errors; 1 warning emitted

Some errors have detailed explanations: E0107, E0277, E0308.
For more information about an error, try `rustc --explain E0107`.
error: could not compile `radio-sx127x`

I thought the OutputPin was fixed in the last PR merge so I suppose I am doing something wrong? A more complete subset of the code is:

Click to expand ``` #![no_std] #![no_main] use embedded_hal_compat::IntoCompat; use embedded_hal_compat::eh1_0::blocking::delay::{DelayMs as _}; #[cfg(debug_assertions)] extern crate panic_semihosting; #[cfg(not(debug_assertions))] extern crate panic_halt; // use nb::block; use cortex_m_rt::entry; use cortex_m_semihosting::*; use embedded_hal::{blocking::delay::DelayMs, spi::{Mode, Phase, Polarity}, }; //use asm_delay::{ AsmDelay, bitrate, }; //use cortex_m::asm; //for breakpoint use radio_sx127x::Error as sx127xError; // Error name conflict with hals use radio_sx127x::{prelude::*, // prelude has Sx127x, device::{Modem, Channel, PaConfig, PaSelect,}, device::lora::{LoRaConfig, LoRaChannel, Bandwidth, SpreadingFactor, CodingRate, PayloadLength, PayloadCrc, FrequencyHopping, }, }; //use radio::{Receive, Transmit}; use radio::{Transmit}; // trait needs to be in scope to find methods start_transmit and check_transmit. // lora and radio parameters pub const MODE: Mode = Mode { // SPI mode for radio phase: Phase::CaptureOnSecondTransition, polarity: Polarity::IdleHigh, }; const FREQUENCY: u32 = 907_400_000; // frequency in hertz ch_12: 915_000_000, ch_2: 907_400_000 const CONFIG_CH: LoRaChannel = LoRaChannel { freq: FREQUENCY as u32, // frequency in hertz bw: Bandwidth::Bw125kHz, sf: SpreadingFactor::Sf7, cr: CodingRate::Cr4_8, }; const CONFIG_LORA: LoRaConfig = LoRaConfig { preamble_len: 0x8, symbol_timeout: 0x64, payload_len: PayloadLength::Variable, payload_crc: PayloadCrc::Enabled, frequency_hop: FrequencyHopping::Disabled, invert_iq: false, }; const CONFIG_PA: PaConfig = PaConfig {output: PaSelect::Boost, power: 10, }; //let CONFIG_RADIO = Config::default() ; const CONFIG_RADIO: radio_sx127x::device::Config = radio_sx127x::device::Config { modem: Modem::LoRa(CONFIG_LORA), channel: Channel::LoRa(CONFIG_CH), pa_config: CONFIG_PA, xtal_freq: 32000000, // CHECK timeout_ms: 100, }; // setup() does all hal/MCU specific setup and returns generic object for use in main code. #[cfg(feature = "stm32f1xx")] // eg blue pill stm32f103 use stm32f1xx_hal::{prelude::*, pac::Peripherals, spi::{Spi, Error,}, delay::Delay, }; #[cfg(feature = "stm32f1xx")] fn setup() -> impl DelayMs + Transmit> { //fn setup() -> Sx127x>, PA6>, PA7>), u8>, Error, // PA1>, PB8>, PB9>, PA0>, // core::convert::Infallible, Delay>, Error, core::convert::Infallible> { let cp = cortex_m::Peripherals::take().unwrap(); let p = Peripherals::take().unwrap(); let mut rcc = p.RCC.constrain(); let clocks = rcc.cfgr.sysclk(64.mhz()).pclk1(32.mhz()).freeze(&mut p.FLASH.constrain().acr); let mut afio = p.AFIO.constrain(&mut rcc.apb2); let mut gpioa = p.GPIOA.split(&mut rcc.apb2); let mut gpiob = p.GPIOB.split(&mut rcc.apb2); let spi = Spi::spi1( p.SPI1, (gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl), // sck on PA5 gpioa.pa6.into_floating_input(&mut gpioa.crl), // miso on PA6 gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl) // mosi on PA7 ), &mut afio.mapr, MODE, 8.mhz(), clocks, &mut rcc.apb2, ); let delay = Delay::new(cp.SYST, clocks); // Create lora radio instance let lora = Sx127x::spi( spi.compat(), //Spi gpioa.pa1.into_push_pull_output(&mut gpioa.crl), //CsPin on PA1 gpiob.pb8.into_floating_input(&mut gpiob.crh), //BusyPin DIO0 on PB8 gpiob.pb9.into_floating_input(&mut gpiob.crh), //ReadyPin DIO1 on PB9 gpioa.pa0.into_push_pull_output(&mut gpioa.crl), //ResetPin on PA0 delay.compat(), //Delay &CONFIG_RADIO, //&Config ).unwrap(); // should handle error lora } // End of hal/MCU specific setup. Following should be generic code. #[entry] fn main() -> !{ let mut lora = setup(); //delay is available in lora let message = b"Hello, LoRa!"; loop { lora.start_transmit(message).unwrap(); // should handle error match lora.check_transmit() { Ok(b) => if b {hprintln!("TX complete").unwrap()} else {hprintln!("TX not complete").unwrap()}, Err(_err) => hprintln!("Error in lora.check_transmit(). Should return True or False.").unwrap(), }; lora.try_delay_ms(5000u32); }; } ```

Output from the workflow is at https://github.com/pdgilbert/rust-radio-sx127x/runs/2041700780?check_suite_focus=true

ryankurte commented 3 years ago

ahh the downside of the compat layer is some nasty errors :-/

for the GPIOs think you just need to be calling .compat() on them so they get wrapped (same as the spi and delay objects)? and transactional will require a polyfill per https://github.com/ryankurte/embedded-hal-compat/issues/2 that i forgot to add

pdgilbert commented 3 years ago

Ok, I'm getting closer. But I'm confused about the transactional polyfill. Do I define something like fn spi_exec in my example code and call it? Or is it something I should try to get the crate maintainer to put in lib.rs? Maybe somewhere near

impl<Spi, SpiError, CsPin, BusyPin, ReadyPin, ResetPin, PinError, Delay, DelayError>
    Sx127x<SpiWrapper<Spi, SpiError, CsPin, BusyPin, ReadyPin, ResetPin, PinError, Delay, DelayError>, SpiError, PinError, DelayError>
where
    Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError> + Transactional<u8, Error = SpiError>,

(If I were the maintainer I would not trust me to do it, because it would be more work figuring out the mess I made.)

ryankurte commented 3 years ago

because of the orphan rules you can only implement traits in the location that either the trait or the object is defined, for the purposes of this it'd be here because the hal implementation you're using isn't running 1.0.0-alpha.X yet (or this wouldn't be a problem).

added (but completely untested) in #4, see how that goes?

pdgilbert commented 3 years ago

Ok, my send examples are building but I am having trouble with receive:

Click to expand ``` $ cargo build --no-default-features --target $TARGET --features=$HAL,$MCU --example lora_spi_receive --release warning: Patch `radio v0.8.1 (https://github.com/ryankurte/rust-radio.git?branch=master#b2383d55)` was not used in the crate graph. Check that the patched package version and available features are compatible with the dependency requirements. If the patch has a different version from what is locked in the Cargo.lock file, run `cargo update` to use the new version. This may also occur with an optional dependency that is not enabled. Compiling radio-sx127x v0.10.1 (/home/paul/githubClones/rust-radio-sx127x) warning: unused import: `embedded_spi::wrapper::Wrapper` --> examples/lora_spi_receive.rs:78:5 | 78 | use embedded_spi::wrapper::Wrapper; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default error[E0308]: mismatched types --> examples/lora_spi_receive.rs:676:53 | 184 | fn setup() -> impl DelayMs + Receive> { | ----------------------------------------------------------------------------------------------------------- the expected opaque type ... 676 | Ok(v) if v => {n = lora.get_received(&mut info, &mut buff).unwrap(); | ^^^^^^^^^ expected associated type, found struct `radio_sx127x::device::PacketInfo` | = note: expected mutable reference `&mut +radio::Receive as radio::Receive>::Info` found mutable reference `&mut radio_sx127x::device::PacketInfo` help: consider constraining the associated type `+radio::Receive as radio::Receive>::Info` to `radio_sx127x::device::PacketInfo` | 184 | fn setup() -> impl DelayMs + Receive, Info = radio_sx127x::device::PacketInfo> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused import: `DelayMs` --> examples/lora_spi_receive.rs:38:51 | 38 | use embedded_hal_compat::eh1_0::blocking::delay::{DelayMs as _}; | ^^^^^^^ error: aborting due to previous error; 2 warnings emitted ```

I've tried .compat() a few places with no luck. Also the warning: unused import: embedded_spi::wrapper::Wrapper does not happen in examples that work.

ryankurte commented 3 years ago

I think there are two problems, one is that radio should be 0.8.1 and the other is that when you call .compat() it wraps the types, so the return types from the setup functions that use static types become invalid... this patch gets the send and receive examples building for the STM32F411 (cargo build --example lora_spi_receive --target thumbv7em-none-eabihf --no-default-features --features=stm32f4xx,stm32f401)

diff --git a/Cargo.toml b/Cargo.toml
index 02797e2..0367ab7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -71,7 +71,7 @@ stm32l1xx-hal = {version = "^0.1.0",   optional = true,  default-features = fals
 stm32l4xx-hal = {version = "^0.6.0", optional = true}

 [dependencies.radio]
-version = "0.7.0"
+version = "0.8.1"

 [dependencies.embedded-hal]
 version = "1.0.0-alpha.4"
diff --git a/examples/lora_spi_receive.rs b/examples/lora_spi_receive.rs
index 2297d21..37fe0fe 100644
--- a/examples/lora_spi_receive.rs
+++ b/examples/lora_spi_receive.rs
@@ -68,7 +68,7 @@ use stm32l4xx_hal::{spi::{Mode,Phase, Polarity}};

 use radio_sx127x::Error as sx127xError;                           // Error name conflict with hals
 use radio_sx127x::{prelude::*,                                     // prelude has Sx127x,
-          device::{Modem, Channel, PaConfig, PaSelect,},
+          device::{Modem, Channel, PaConfig, PaSelect, PacketInfo},
                    device::lora::{LoRaConfig, LoRaChannel, Bandwidth, SpreadingFactor, CodingRate,
                                   PayloadLength, PayloadCrc, FrequencyHopping, },
           };
@@ -310,10 +310,7 @@ use stm32f4xx_hal::{prelude::*,

     #[cfg(feature = "stm32f4xx")]
-    fn setup() ->  Sx127x<Wrapper<Spi<SPI1, 
-                           (PA5<Alternate<AF5>>,    PA6<Alternate<AF5>>,   PA7<Alternate<AF5>>)>,  Error, 
-                   PA1<Output<PushPull>>,  PB8<Input<Floating>>,  PB9<Input<Floating>>,  PA0<Output<PushPull>>, 
-                   Infallible,  Delay>,  Error, Infallible, Infallible> {
+    fn setup() -> impl DelayMs<u32> + Receive<Info=PacketInfo, Error=sx127xError<Error, Infallible, Infallible>>  {

        let cp = cortex_m::Peripherals::take().unwrap();
        let p  = Peripherals::take().unwrap();
pdgilbert commented 3 years ago

I now have my examples building for MCUs stm32f103, stm32f100, stm32f101, stm32f303xc, stm32f401, stm32f411, stm32h742, stm32l100, and stm32l151. These use crates stm32f1xx-hal,stm32f3xx-hal, stm32f4xx-hal, stm32f7xx-hal, stm32h7xx-hal,stm32l0xx-hal, and stm32l1xx-hal that are based on embedded-hal v0.2.4. The examples use crate radio-sx127x based onembedded-halv1.0.0-alpha.4.

Just to summarize usage for anyone reading:

       let lora = Sx127x::spi(
            spi.compat(),                                //Spi
            gpioa.pa1.into_push_pull_output(&mut gpioa.crl).compat(),        //CsPin         on PA1
            gpiob.pb8.into_floating_input(&mut gpiob.crh).compat(),          //BusyPin  DIO0 on PB8
            gpiob.pb9.into_floating_input(&mut gpiob.crh).compat(),          //ReadyPin DIO1 on PB9
            gpioa.pa0.into_push_pull_output(&mut gpioa.crl).compat(),        //ResetPin      on PA0
            delay.compat(),                              //Delay
            &CONFIG_RADIO,                       //&Config
            ).unwrap();      // should handle error

use embedded_hal::{spi::{Mode, Phase, Polarity}, };

with

Click to expand ``` #[cfg(feature = "stm32f0xx")] // eg stm32f030xc use stm32f0xx_hal::{spi::{Mode,Phase, Polarity}}; #[cfg(feature = "stm32f1xx")] // eg blue pill stm32f103 use stm32f1xx_hal::{spi::{Mode,Phase, Polarity}}; #[cfg(feature = "stm32f3xx")] // eg Discovery-stm32f303 use stm32f3xx_hal::{spi::{Mode,Phase, Polarity}}; #[cfg(feature = "stm32f4xx")] // eg Nucleo-64 stm32f411, blackpill stm32f411, blackpill stm32f401 use stm32f4xx_hal::{spi::{Mode,Phase, Polarity}}; #[cfg(feature = "stm32f7xx")] use stm32f7xx_hal::{spi::{Mode,Phase, Polarity}}; #[cfg(feature = "stm32h7xx")] use stm32h7xx_hal::{spi::{Mode,Phase, Polarity}}; #[cfg(feature = "stm32l0xx")] use stm32l0xx_hal::{spi::{Mode,Phase, Polarity}}; #[cfg(feature = "stm32l1xx") ] // eg Discovery kit stm32l100 and Heltec lora_node STM32L151CCU6 use stm32l1xx_hal::{spi::{Mode,Phase, Polarity}}; #[cfg(feature = "stm32l4xx")] use stm32l4xx_hal::{spi::{Mode,Phase, Polarity}}; ```
ryankurte commented 3 years ago

There can be other needs to account for the fact that the MCU hals are not using the same embedded-hal

ahh, you could probably access this as embedded_hal_compat::eh0_2::spi::... (or explicitly import the v0.2.x hal) rather than having to switch for each device type

pdgilbert commented 3 years ago

Rather than switch for each device type I added dependency

old-e-h = {version = "0.2.4", package = "embedded-hal" }

to Cargo.toml and in the code replaced all the cfg feature lines above with

use old_e_h::{spi::{Mode,Phase, Polarity}}; 

This also solved the problem that one hal crate was not re-exporting{Mode,Phase, Polarity}.