stm32-rs / stm32f1xx-hal

A Rust embedded-hal HAL impl for the STM32F1 family based on japarics stm32f103xx-hal
Apache License 2.0
550 stars 173 forks source link

setup dp parts #457

Closed pdgilbert closed 3 months ago

pdgilbert commented 1 year ago

(Somewhat related to #24, #305, and #313.)

While learning Rust and embedded programming I have accumulated a set of examples that I am trying to keep working with new HAL releases. I have been using a setup() function to specify pins and parameters for the settings of peripherals. This works but does not scale well as the number of examples grows. I would like to encapsulate parts of the setup in different functions so I can use some pieces for one example and mix those with other pieces in other examples. The code below illustrates the problem. In this example I need both i2c1 and i2c2 while in many other examples I use only i2c1. I have functions setup_i2c1 and setup_i2c2 but I cannot get around the move problem.

The code does not compile:

move occurs because `gpiob` has type `stm32f1xx_hal::gpio::gpiob::Parts`, which does not implement the `Copy` trait

In the function setup_i2c1_i2c2_led_delay_using_dp if I replace calls to setup_i2c1 and setup_i2c2 with the code in those functions (commented out) then it does compile. But then I have to repeat all that code in several examples.

I have also tried passing the whole device peripheral as &mut dp to each setup and then using * de-referencing in the function. This approach I can make work with simpler objects but in the case of dp I only get to

cannot move out of `gpiob.pb8` which is behind a mutable reference

Any suggestions for how to achieve this subdivision of the setup would be appreciated.

Click to expand #! Example using two i2c buses on bluepill #![deny(unsafe_code)] #![no_std] #![no_main] use aht10::AHT10; #[cfg(debug_assertions)] use panic_semihosting as _; #[cfg(not(debug_assertions))] use panic_halt as _; use cortex_m_rt::entry; use core::fmt::Write; //use cortex_m_semihosting::hprintln; use embedded_graphics::{ mono_font::{ascii::FONT_5X8 as FONT, MonoTextStyleBuilder}, pixelcolor::BinaryColor, prelude::*, text::{Baseline, Text}, }; use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; use stm32f1xx_hal::{ timer::Delay, i2c::{BlockingI2c, DutyCycle, Mode}, pac::{Peripherals, TIM2, I2C1, I2C2}, gpio::{gpiob::{PB8, PB9, PB10, PB11, Parts}, gpioc::{PC13, Parts as PartsC}, Alternate, OpenDrain, Output, PushPull}, afio::Parts as afioParts, rcc::Clocks, prelude::* }; pub type I2c1Type = BlockingI2c>, PB9>)>; pub type I2c2Type = BlockingI2c>, PB11>)>; pub type DelayType = Delay; pub type LedType = PC13>; pub trait LED { fn on(&mut self) -> (); fn off(&mut self) -> (); fn blink(&mut self, time: u16, delay: &mut DelayType) -> () { self.on(); delay.delay_ms(time); self.off(); delay.delay_ms(time); //consider delay.delay_ms(500u16); } } pub fn setup_led(mut gpioc: PartsC) -> LedType { let led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); impl LED for LedType { fn on(&mut self) -> () { self.set_low() } fn off(&mut self) -> () { self.set_high() } } led } pub fn setup_i2c1(i2c1: I2C1, mut gpiob: Parts, afio: &mut afioParts, &clocks: &Clocks) -> I2c1Type { let scl = gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh); let sda = gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh); BlockingI2c::i2c1( i2c1, (scl, sda), &mut afio.mapr, Mode::Fast { frequency: 100_000_u32.Hz(), duty_cycle: DutyCycle::Ratio2to1, }, clocks, 1000, 10, 1000, 1000, ) } pub fn setup_i2c2(i2c2: I2C2 , mut gpiob: Parts, &clocks: &Clocks) -> I2c2Type { BlockingI2c::i2c2( i2c2, ( gpiob.pb10.into_alternate_open_drain(&mut gpiob.crh), gpiob.pb11.into_alternate_open_drain(&mut gpiob.crh), ), //&mut afio.mapr needed for i2c1 but not for i2c2 Mode::Fast { frequency: 400_000_u32.Hz(), duty_cycle: DutyCycle::Ratio2to1, }, clocks, 1000, 10, 1000, 1000, ) } pub const MONOCLOCK: u32 = 8_000_000; pub fn setup_i2c1_i2c2_led_delay_using_dp(dp: Peripherals) -> (I2c1Type, I2c2Type, LedType, DelayType) { let rcc = dp.RCC.constrain(); let mut afio = dp.AFIO.constrain(); let clocks = rcc.cfgr.freeze(&mut dp.FLASH.constrain().acr); let mut gpiob = dp.GPIOB.split(); // Next does not work because of value move problems. Using &mut argument and * deref only gets to // cannot move out of `gpiob.pb8` which is behind a mutable reference. let i2c1 = setup_i2c1(dp.I2C1, gpiob, &mut afio, &clocks); let i2c2 = setup_i2c2(dp.I2C2, gpiob, &clocks); // Fixed by putting all i2c setup code here. // let i2c1 = BlockingI2c::i2c1( // dp.I2C1, // (gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh), // gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh) // ), // &mut afio.mapr, // Mode::Fast { // frequency: 100_000_u32.Hz(), // duty_cycle: DutyCycle::Ratio2to1, // }, // clocks, // 1000, // 10, // 1000, // 1000, // ); // // let i2c2 = BlockingI2c::i2c2( // dp.I2C2, // (gpiob.pb10.into_alternate_open_drain(&mut gpiob.crh), // gpiob.pb11.into_alternate_open_drain(&mut gpiob.crh), // ), // //&mut afio.mapr, need this for i2c1 (PB8, PB9) but //NOT i2c2 // Mode::Fast { // frequency: 400_000_u32.Hz(), // duty_cycle: DutyCycle::Ratio2to1, // }, // clocks, // 1000, // 10, // 1000, // 1000, // ); let mut led = setup_led(dp.GPIOC.split()); led.off(); let delay = dp.TIM2.delay_us(&clocks); (i2c1, i2c2, led, delay) } #[entry] fn main() -> ! { let dp = Peripherals::take().unwrap(); let (i2c1, i2c2, mut led, mut delay) = setup_i2c1_i2c2_led_delay_using_dp(dp); led.blink(2000_u16, &mut delay); // Blink LED to indicate setup finished. let manager = shared_bus::BusManagerSimple::new(i2c1); let interface = I2CDisplayInterface::new(manager.acquire_i2c()); let mut display = Ssd1306::new(interface, DisplaySize128x32, DisplayRotation::Rotate0) .into_buffered_graphics_mode(); display.init().unwrap(); display.flush().unwrap(); let text_style = MonoTextStyleBuilder::new().font(&FONT).text_color(BinaryColor::On).build(); let mut lines: [heapless::String<32>; 2] = [heapless::String::new(), heapless::String::new()]; led.blink(500_u16, &mut delay); // Blink LED to indicate Ssd1306 initialized. // need separate delay to use blink after this let manager2 = shared_bus::BusManagerSimple::new(i2c2); let mut device = AHT10::new(manager2.acquire_i2c(), delay).expect("device failed"); loop { let z = device.read(); lines[0].clear(); lines[1].clear(); // next recovers from sda disconnect/reconnect but not scl disconnect/reconnect match z { Ok((h,t)) => {//hprintln!("{} deg C, {}% RH", t.celsius(), h.rh()).unwrap(); write!(lines[0], "temperature: {}C", t.celsius()).unwrap(); write!(lines[1], "relative humidity: {0}%", h.rh()).unwrap(); }, Err(e) => {//hprintln!("Error {:?}", e).unwrap(); write!(lines[0], "device read error. Resetting.").unwrap(); write!(lines[1], "code {:?}", e).unwrap(); device.reset().unwrap(); } } //hprintln!("h.rh(): {}", h.rh()).unwrap(); //hprintln!("t.celsius(): {}", t.celsius()).unwrap(); display.clear(); for (i, line) in lines.iter().enumerate() { Text::with_baseline( line, Point::new(0, i as i32 * 16), text_style, Baseline::Top, ) .draw(&mut display) .unwrap(); } display.flush().unwrap(); } }
pdgilbert commented 3 months ago

The general problem still exists in some situations, but the example is too dated to be useful.