Giter Site home page Giter Site logo

stm32f3xx-hal's Introduction

stm32f3xx-hal

Build Status Crate Docs Crates.io Minimum Supported Rust Version

stm32f3xx-hal contains a multi device hardware abstraction on top of the peripheral access API for the STMicro STM32F3 series microcontrollers. The selection of the MCU is done by feature gates, typically specified by board support crates. An excerpt of supported chip variants:

  • stm32f301
  • stm32f318
  • stm32f302
  • stm32f303
  • stm32f373
  • stm32f378
  • stm32f334
  • stm32f328
  • stm32f358
  • stm32f398

The idea behind this crate is to gloss over the slight differences in the various peripherals available on those MCUs so a HAL can be written for all chips in that same family without having to cut and paste crates for every single model.

Collaboration on this crate is highly welcome as are pull requests!

This crate relies on Adam Greigs fantastic stm32f3 crate to provide appropriate register definitions and implements a partial set of the embedded-hal traits.

Almost all of the implementation was shamelessly adapted from the stm32f30x-hal crate by Jorge Aparicio.

Getting Started

Adding stm32f3xx-hal and other dependencies

Cargo.toml:

[dependencies]
# Only set the critical section feature, if you are using a bare-metal platform
# without any RTOS
# See https://github.com/rust-embedded/critical-section for further details.
cortex-m = { version = "0.7.4", features = ["critical-section-single-core"]}
cortex-m-rt = { version = "0.7.3", features = ["device"] }
# Panic behavior, see https://crates.io/keywords/panic-impl for alternatives
panic-halt = "0.2.0"
# Replace stm32f303xc with your target chip, see next section for more info
stm32f3xx-hal = { version = "0.10.0", features = ["ld", "rt", "stm32f303xc"] }

We also need to tell Rust about target architecture and how to link our executable by creating .cargo/config.

.cargo/config:

[target.thumbv7em-none-eabihf]
rustflags = [
  "-C", "link-arg=-Tlink.x",
]

[build]
target = "thumbv7em-none-eabihf"

Selecting the right chip

This crate requires you to specify your target chip as a feature.

Example: The STM32F3Discovery board has a STM32F303VCT6 chip according to the user manual. So you need to specify stm32f303xc in your Cargo.toml (note that VC → xc).

All possible chip variants are selectable via cargo features. You can find a list here, in the docs.

Note

  1. This features are mutually exclusive. Only one feature / chip variant can be chosen.
  2. You have to choose exactly one feature to build this crate at all.

Background

For some of the stm32f3xx chips there are sub-variants that differ in functionality, peripheral use and hence 'under the hood' implementation. To allow the full use of all peripherals on certain sub-variants without allowing for code that just doesn't run on other sub-variants, they are distinct features that need to be specified.

Basic Usage

#![no_std]
#![no_main]

use cortex_m::asm;
use cortex_m_rt::entry;
use panic_halt as _;
use stm32f3xx_hal::{self as hal, pac, prelude::*};

#[entry]
fn main() -> ! {
      let dp = pac::Peripherals::take().unwrap();

      let mut rcc = dp.RCC.constrain();
      let mut gpioe = dp.GPIOE.split(&mut rcc.ahb);

      let mut led = gpioe
            .pe13
            .into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper);

      loop {
            led.toggle().unwrap();
            asm::delay(8_000_000);
      }
}

See the examples folder for more example programs.

Minimum Supported Rust Version (MSRV)

This crate is guaranteed to compile on stable Rust 1.60.0 and up. It might compile with older versions but that may change in any new patch release.

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

stm32f3xx-hal's People

Contributors

agalakhov avatar arlosi avatar barafael avatar brain113 avatar burrbull avatar danielgallagher0 avatar david-oconnor avatar dfrankland avatar disasm avatar dpralas avatar iamfromspace avatar joestarwalker avatar jr-oss avatar mirabellensaft avatar piroro-hs avatar rahix avatar richard7770 avatar robamu avatar russellmcc avatar seanybaggins avatar sh3rm4n avatar sirhcel avatar sprhawk avatar strom-und-spiele avatar teskje avatar thezoq2 avatar tstellanova avatar wafflelapkin avatar wbuck avatar zklapow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stm32f3xx-hal's Issues

Thoughts on moving from macros to enums

The macros this crate uses extensively to apply methods to pins makes for obfuscated source code and docs. Thoughts on switching to an enum-based approach instead?

If I ever figure out how timers and PWM works, I'll start with a new PWM module.

edit: This may not be as feasible as I originally though. I need to try this first.

panic attempting to setup PWM

I tried to use the new PWM functionality (by @IamfromSpace I think) and so far failed.

Using a stm32f303, I have the following code:
stm32f3xx_hal::pwm::tim3(device.TIM3, 16000, Hertz(500), &clocks);

In a debug build, this panics with "attempt to subtract with overflow" in the following line in pwm.rs (using current git master, 609af0a:

tim.psc.write(|w| w.psc().bits(prescale_factor as u16 - 1));

Indeed, debugging this a bit, I see that prescale_factor is set to 0. (Going into the logic where this is set, clock_freq is set to 4000000, res to 16000, and freq.0 to 500. These values thus cause prescale_factor to be set to 0.)

The thing is, I think this resolution and rate do work on this hardware since I am updating some old code where this works.

Improve race free register access ergonomics

Summary

The current way we go about this is both un-ergonomic and unsound.

Current Usage

Today, the strategy for handling registers like these is to allow only the HAL to mutate them (via pub(crate)), and then assure that they are available for mutation from the user.

For example if we want to put PA4 into an alternate function mode 2:

...
let mut gpioa = dp.GPIOA.split(&mut rcc.ahb);
gpioa.pa4.into_af2(&mut gpioa.moder, &mut gpioa.afrl);

The AHB, MODER, and AFRL have no public API, so they cannot be mutated directly, but they must be (mutably) passed into the split or into_af2 so the HAL can modify them and correctly configure the pin.

The Problem

Originally, my thought was a recommendation to improve ergonomics and hide more internal details from users. However, I realized that this strategy is not actually sound. The former leads to the latter, so we'll explore them in that order.

Ergonomics

If we do not return or require these registers it greatly simplifies the API for users. New users who are less familiar to MCU will be able to work in more concrete topics like peripherals, rather than registers.

Also, composition becomes much simpler. Now, peripherals that accept pins must either a) require the pin in an already correctly configured state b) require registers that are not related its own function so it can configure the pin.

The former requires the user know extra details, and the latter is a break of the Law of Demeter (reaching into a dependency).

If instead moving a pin into alternate function 2 was as simple as:

let mut gpioa = dp.GPIOA.split();
gpioa.pa4.into_af2();

Then a peripheral could simply accept an unconfigured pin, and the user would never even know that an alternate function was required, further hiding details and simplifying user's code. HAL's across multiple chips and manufactures would look more similar and wouldn't require re-learning the nuances of each chip.

The two reasons that this hasn't been done before is likely not wanting to mask these details and the number of resulting unsafe blocks.

It is not the job of a HAL to teach a user how their chip works. There should absolutely be resources to enable the curious! But an abstraction layer is about hiding. And the resulting unsafe blocks are not any less safe than today, due to soundness issues.

Unsoundness

This strategy has been built on the premise that: If HAL users do not write unsafe code, and HAL contributors do not write unsafe code, then there will not be "unsafe" results. This is not true.

As a contributor I can write the following "safe" function:

pub fn evil(afrl: &mut AFRL) {
  afrl.reset();
}

Any user who setups up a peripheral, and then calls evil in their code (remember, they'll still be able to pass a mutable reference at this point) all pins that require an alternate function are now set incorrectly and no longer connected to their previous peripheral.

The fact that maintainers would never allow this into the HAL is irrelevant. The important piece is that the borrow checker didn't not prevent this function from mutating pins it didn't own or have mutable access to.

Addressing this Issue

EDIT: The following has soundness issues, and is not a viable solution! Please see my next comment further down for a summary of the issue and safe ways to address it.

Going forward these registers should not be exposed or accepted. Instead unsafe blocks should be used, where ownership/mutable reference of the pin/peripheral is proof of safe access to particular bits within a register with "global" bits. Other mechanisms ensure that there is never more than one pin/peripheral/etc:

impl<MODE> PA4<MODE> {
  pub fn into_af2(self) -> PA4<AF2> {
    unsafe {
      (*$GPIOX::ptr()).moder.modify(|_, w| {
        w.moder4().alternate()
      }
    }
 
    unsafe {
      (*$GPIOX::ptr()).afrl.modify(|_, w| {
        w.modify().afrl4().af2()
      }
    }
 
    PA4 { _mode: PhantomData }
}

The presence of the unsafe keyword is not a "bad thing." Instead it is a signal that this is a delicate area of the code that should be used with caution.

The way that the stm32f3xx chips are designed simply doesn't allow for svd2rust to ensure safety at the peripheral layer. This is not anyone's "failing" it's simply a that we need to manually validate safety in certain places--it cannot be deferred to the borrow checker.

I2C can run into deadlock / has no timeout

I struggle to use this crate to execute basic I2C requests with timeouts.

Can you provide an example on how to use the I2C abstraction with timeouts to ie scan the I2C bus for available addresses?

I'm using v0.4.3.

Thanks

Serial Communication Issue with crate

Hey!
Basically i was building a project which uses pwm and serial communication "usart1". So the Issue i was facing is that if I use the Serial Communication from Discovery Book, it works fine but if i write the same code with stm32f3xx_hal crate , it does work but it sends data in some other encoding, for example, from discovery book example, If i press "enter" it sends "13" but by using this crate "ENTER" = "128". Also no matter what i send i receive two integers. I am beginner so i am not very sure how to deal with this.

Problem matching STCubeMX RCC configuration

I've having trouble migrating my STCubeMX RCC configuration to a working configuration with stm32f3xx-hal.

I'm using a STM32F303CCT6 with 16 MHz HSE and USB. The clock configuration works fine for USB-Serial when generating a project via STCubeMX:

Screenshot 2020-07-02 at 19 31 17

Now I'm trying to convert attached clock configuration, but this fails the usb validation it seems:

let clocks = rcc
        .cfgr
        .use_hse(16.mhz())
        .hclk(72.mhz())
        .sysclk(72.mhz())
        .pclk1(36.mhz())
        .pclk2(72.mhz())
        .freeze(&mut flash.acr);
assert!(clocks.usbclk_valid());

when I switch to 48 mhz it works fine:

let clocks = rcc
        .cfgr
        .use_hse(16.mhz())
        .sysclk(48.mhz())
        .pclk1(24.mhz())
        .freeze(&mut flash.acr);
    assert!(clocks.usbclk_valid());

Is this a problem with the dynamic pll_config calculation, or am I missing something?

Interrupt support

I'd like to see this library include high-level code to handle interrupts, and am curious how y'all would like to approach this. @jkboyce , who posted this issue has been immensely helpful, as have several people on the stm32 Matrix chat, especially adamgreig.

I've been working off a standalone module. Here are possible approaches:
1 - Achieve API and implementation parity with other stm32 hal crates. I haven't done my research on this, although understanding how this API is set up isn't trivial due to heavy use of macros and traits. API parity has obvious advantages - most notably the main HAL virtue of transparent switching between MCUs, so this is the ideal solution.

2 - Add a standalone module to this crate using hand-written, repetitive fns

3 - Same as above, but using macros to make it cleaner to maintain. I'm not sure how to do this, as my trivial attempts have failed. Might need to use the paste crate or proc macros.

4 - As a standalone module released on crates.io. This is the backup approach. I would never have been able to get interrupts working in my crate without the help of the two people listed at the top of this post, and want this to be a solved problem going forward.

Here are the basics for GPIO interrupts, using option 2. It works, but suffers from DRY. The remaining interrupt functionality (The 4 types of RTC interrupts, I2c interrupts etc) should be added as well, either in the initial PR, or later.

/// Reference the STM32F303 reference manual, section 14.2.5 for a `Functional description`
/// of the steps we accomplish here.
/// 0 - 15.
pub fn setup_line(
    exti: &mut EXTI,
    line: u8,
    task: Task,
    edge: Edge,
) {
    // Configure the Trigger Selection bits of the Interrupt line (EXTI_RTSR and EXTI_FTSR)
    let rise_trigger = match edge {
        Edge::Rising => {
            // configure EXTI line to trigger on rising edge, disable trigger on falling edge.
            true
        }
        Edge::Falling => {
            // configure EXTI line to trigger on falling edge, disable trigger on rising edge
           false
        }
    };

    match line {
        0 => {
            // Configure the corresponding mask bit in the EXTI_IMR register.
            exti.imr1.modify(|_, w| w.mr0().unmasked());
            exti.rtsr1.modify(|_, w| w.tr0().bit(rise_trigger));
            exti.ftsr1.modify(|_, w| w.tr0().bit(!rise_trigger));

            // Configure the enable and mask bits that control the NVIC IRQ channel mapped to the
            // EXTI so that an interrupt coming from one of the EXTI line can be correctly
            // acknowledged.
            unsafe { NVIC::unmask(interrupt::EXTI0) };
        }
        // ....
        5 => {
            exti.imr1.modify(|_, w| w.mr5().unmasked());
            exti.rtsr1.modify(|_, w| w.tr5().bit(rise_trigger));
            exti.ftsr1.modify(|_, w| w.tr5().bit(!rise_trigger));
            unsafe { NVIC::unmask(interrupt::EXTI9_5) };
        }
        // ...
    }
}

/// Configure a GPIO to work with an interrupt line.
/// The `line` parameter corresponds to both the interrupt line, and the
/// GPIO number. Eg, `3` means `line3`, and `px3`.
/// Reference section 12.1.3 of the datasheet for a breakdown by CR
/// and line.
pub fn setup_gpio(syscfg: &mut SYSCFG, gpio_reg: GpioReg, line: u8) {
    match line {
        0 => syscfg
            .exticr1  // Each exticr is used for 4 lines.
            .modify(|_, w| unsafe { w.exti0().bits(gpio_reg.cr_val()) }),
        1 => syscfg
    // ...
    }
}


/// Set EWUP bit in PWR_CSR register; set PWREN bit to enable power interface clock.
/// This is required to enable use of the WKUP pin, eg to wake up from standby.
/// https://vasiliev.me/blog/sending-stm32f1-to-deep-sleep-with-rust/#fn:3
/// We use our custom stm32f3xx hal branch to allow access to the (normally private) `enr`.
pub fn setup_wakeup(apb1: &mut APB1, pwr: &mut PWR) {
    // todo: We must somehow call this too:
    // enable SYSCFG clock to enable external interrupts; must come before RCC.constrain()
    // dp.RCC.apb2enr.write(|w| w.syscfgen().enabled());

    apb1.enr().modify(|_r, w| w.pwren().set_bit());
    pwr.csr.modify(|_, w| w.ewup1().set_bit());
}

/// These functions are called when their appropriate interrupt line
/// is triggered.
/// Eg:
///     make_interrupt_handler!(EXTI3);
///     make_interrupt_handler!(EXTI9_5);
///     make_interrupt_handler!(EXTI15_10);
macro_rules! make_interrupt_handler {
    ($line:ident) => {
        #[interrupt]
        fn $line() {
            free(|cs| {
                // Reset pending bit for interrupt line
                // todo: pr1 for all?
                unsafe { (*EXTI::ptr()).pr1.modify(|_, w| w.pr1().bit(true)) };
                // hprintln!("In exti $line").ok();
            });
        }
    };
}

Example use:

   // Setup an interrupt that activates when `Pb3` is pulled low.
    interrupt_::setup_line(
        &mut dp.EXTI,
        3,
        interrupt_::Task::Enable,
        interrupt_::Edge::Falling,
    );
    interrupt_::setup_gpio(&mut dp.SYSCFG, interrupt_::GpioReg::B, 3);
    interrupt_::setup_wakeup(&mut rcc.apb1, &mut dp.PWR);
    make_interrupt_handler!(EXTI3);

Uart Flow Control

I have been looking at the serial implementation for some time now, and I wonder is there a plan to expand it to allow Hardware flow control? I am a rust novice, and have no experience working on the implementation side of an HAL.

In my mind, I could use the PAC to activate hardware flow control, and continue using the rest of the crate serial library provided normally. I am unsure if I might be missing something here or if this would cause other issues.

Alternativey, is someone already working on this, or does anyone have advice as to where to get started adding it to the HAL. I wouldn't want to go against the design principles, but the only example I found was in https://github.com/stm32-rs/stm32l4xx-hal. It has quite a few differences in structure, and would probably require refactoring throughout the rest of the implementation. Is there a way around this?

Sorry for the rambling nature of the issue, but I am not sure where to start.

Edit: I took a look at how the initialization of the function works, and I believe that if I set the Hardware Flow control in the relevant UART before starting the rest it will work correctly. I will edit again when I've confirmed this.

Pwm input mode in timers

Hey! I am working on something where i needed the timer pwm input mode and i didn't find anything in the hal to implement this as in this crate there is a way to implementation pwm but pwm input is not available. I want to get the duty cycle and frequency of a pwm signal being input to the board and i also it needs to be 4 channels per timer. I am a newbie to rust and embedded programming so i am not very sure how this could be done using the pac crates but can anyone help regarding this?

`downgrade()` results in hardfault if resulting array is used with non const index

This is a nifty bug and I'm not where it originates from, however, these were my observations so far:

let ld1 = gpiob.pb0.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);
let ld2 = gpiob.pb7.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);
let mut leds = [ld1.downgrade(), ld2.downgrade()];

The downgrade() call results in an hardfault, if and only if somewhere in the program there is an access to leds, where the index is not const/known at compile time.
No problem:

leds[0].set_high().unwrap();
for i in 0..2 {
  leds[1].set_high().unwrap();
}

Sure a problem (note: the hardfault does not occur on the .set_high() but on the .downgrade() above):

for i in 0..2 {
  leds[i].set_high().unwrap();
}

The same holds true for for led in leds.iter_mut() or let mut i=0; loop {leds[i%2] ...; i+=1;}

At this point, I have no clue how to debug any further. Any ideas?

Justify `.write` use

After following a few discussions on issues, it occurred to me that using .write reliably produced unwanted behavior.
The only way I know how to solve this is to grep for .write and justify it's uses by adding comments or correct the wrong usages. This is some what ugly and requires future commits to hold to this practice, but it'd emphasize the importance of paying attention here.

I'd be happy to hear a cleaner solution and am wiling to implement it ( in the long run though - It seems like my life wants to be busy right now ;) )
I guess it won't be too much work at the current state of the repo ($ rg "\.write" src/* | wc -l → 26)

writeln support

It would be nice to have support for writeln! ( which comes with "use core::fmt::Write;" ). I think this is done in stm32f1xx-hal and stm32f4xx-hal by implementing a trait. I'm a newbie, so probably wrong about how it is done, but I do know it works with those two hals and not with stmf3xx-hal.

Support DMA

Direct Memory Access allows peripherals to write to memory directly, without needing buffers. This saves memory and is a handy feature.

Point external documentation to this crate / stm32-rs

We should likely update awesome-embedded-rust and also stm32f30x-hal to point to this crate since this crate is actively maintained. Related issue: japaric/stm32f30x-hal#36

OneWire support

OneWire is a digital protocol used by a very popular temperature sensor, the DS1820B. I'm unable to get it working with this crate, and am wonder what you think the best approach is.

I'm suspicious this recent issue is related.

I think the issue is that the GPIO pins this library uses are generally input or output only, and don't implement both. Is this correct? The specific issue I'm running into with this OneWire crate is that it expects the pin to implement this:

pub trait OpenDrainOutput<E>: OutputPin<Error = E> + InputPin<Error = E> {}

Is this an issue of implementing both InputPin and OutputPin for gpio pins configured as open drain? I looked through this source file to try to find a solution, either robust, or a hack to make OneWire work, but am not sure how to deal with the macros. Is it just an issue of making a way to configure a pin with both the InputPin and OutputPin methods? Ie set_low() and is_low()? Ie a copy+paste job.

Thoughts? Is this the same thing as the issue I linked near the top?

Wrong alternate function mappings for GPIO banks

As discussed in #116 (comment), the alternate function mapping for the gpios are not 100 % according to the device datasheets (p.43) , in which the alternate function table is listed.

stm32f3xx-hal/src/gpio.rs

Lines 921 to 930 in 2ea16b4

PB0: (pb0, 0, Input<Floating>, moder0, AFRL, afrl0, bs0, br0, odr0, idr0, pupdr0, ot0, [
AF3: (into_af3, af3,),
AF15: (into_af15, af15,),
], [
AF2: (into_af2, af2, ["stm32f302", "stm32f303", "stm32f334", "stm32f328", "stm32f358", "stm32f378", "stm32f398",],),
AF4: (into_af4, af4, ["stm32f303xb", "stm32f303xc", "stm32f303xd", "stm32f303xe", "stm32f358", "stm32f398",],),
AF5: (into_af5, af5, ["stm32f378",],),
AF6: (into_af6, af6, ["stm32f301", "stm32f318", "stm32f302", "stm32f303", "stm32f334", "stm32f328", "stm32f358", "stm32f398",],),
AF10: (into_af10, af10, ["stm32f378",],),
]),

How do you use Delay in multiple places

Let's say you have something that consumes the delay:

    let mut cp = cortex_m::Peripherals::take().unwrap();
    let dp = stm32::Peripherals::take().unwrap();
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    let mut delay = Delay::new(cp.SYST, clocks);
    let mut lcd = HD44780::new_i2c(i2c, LCD_I2C_ADDRESS, delay);

How do you then use a delay in the loop? It looks like the Delay::free method may be along the right tracks, but I'm not sure. Tried re-initializing cp and delay with no luck, and it appears you can't clone either, or use as a ref.

Move CI to GitHub Actions

GitHub Actions is the new blessed way to do CI on GitHub. It's already widely used in by other projects in the embedded Rust ecosystem, so we should switch for consistency alone. But also GHA is said to have advantages over Travis, such as easier setup, faster build times, and more features.

Other HAL projects already use GHA. We can simply copy what they are doing:

HardFault with semihosting hprintln

I was trying to print some text out but with the code below I have a Breakpoint 2, 0x08002690 in HardFault_ () error

#![no_main]
#![no_std]

extern crate cortex_m;
extern crate cortex_m_rt as rt;
extern crate stm32f3xx_hal as hal;
use core::panic::PanicInfo;
use cortex_m_semihosting::hprintln;
use hal::stm32;

#[entry]
fn main() -> ! {
    hprintln!("Hello, world!").unwrap();
    loop {}
}

I'm using a STM32F303K8 board with these dependencies:

[dependencies]
cortex-m = "0.6.1"
cortex-m-rtfm = "0.4.3"
panic-halt = "0.2.0"
alloc-cortex-m = "0.3.5"
embedded-hal = "0.2.3"
generic-array = "0.13.2"
nb = "0.1.0"
cortex-m-semihosting = "0.3.5"

[dependencies.cortex-m-rt]
version = "0.6.11"
features = ["device"]

[dependencies.stm32f3xx-hal]
version = "0.3.0"
features = ["stm32f303", "device-selected"]

Missing code completion in IntelliJ

I know this may be resolved by an IDE plugin update in the future, but can we do something about this in the mean time?
image

It seams to me like the split function is not auto completed, because the IDE doesn´t find the macro generated struct stm32f3xx_hal::gpio::gpioa::Parts. Am I correct about this?

Can't clear a UART read error

If I get a UART Error::Overrun, that bit seems to be sticky and won't go away. Could we add a clear function so we can clear that bit? Or could we clear the bit automatically when we return the error?

SPI Clock/Signal not right

Hi,

I switched from alt-stm32f30x-hal to this hal because the code looked easier for me to contribute to. But after switching I have a problem which I am sure what it is cause by, probalby my stupidity.

I am using the same SPI Code as with the other hal except of course the necessary adaptions in parameters for the functions etc. but I can't get a valid SPI Signal out of my STM anymore.
Instead it looks like this:
image

I am obviously missing 3 clock cycles for a single byte.
I was tryting to figure out what is different but I can't see any meaningful differences between this library and the alt library in terms of initialization, so it must be something in my code I guess, so here is my initialization of the SPI, can you spot something wront?


    let stm = hal::stm32::Peripherals::take().unwrap();
    let core = cortex_m::Peripherals::take().unwrap();
    //This is done for the peripherals to play nice I think!
    let mut flash = stm.FLASH.constrain();
    let mut rcc = stm.RCC.constrain();

    //clock configuration
    let clocks = rcc.cfgr
        .sysclk(64.mhz())
        .pclk1(32.mhz())
        .pclk2(32.mhz())
        .freeze(&mut flash.acr);

    let mut delay_timer = Delay::new(core.SYST, clocks);

    //spi
    let mut gpioa = stm.GPIOA.split(&mut rcc.ahb);
    let mosi = gpioa.pa7.into_af5(&mut gpioa.moder, &mut gpioa.afrl);
    let miso = gpioa.pa6.into_af5(&mut gpioa.moder, &mut gpioa.afrl);
    let sck = gpioa.pa5.into_af5(&mut gpioa.moder, &mut gpioa.afrl);
    let cs = gpioa.pa4.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);
    let mut spi = hal::spi::Spi::spi1(stm.SPI1, (sck, miso, mosi), MODE_0, 1.mhz(), clocks, &mut rcc.apb2);
    spi.send(0b101010);

Apoint more maintainers

Lately this project seems to be quite undermaintained. There are a couple of PRs open currently that would be useful to have merged, but they don't get reviewed.

I think that is a problem, since the stm32f3xx-hal is usually how beginners in embedded Rust (like me) have their first contact with the ecosystem (due to the great Discovery book). Bugs and missing features of this crate can lead to needlessly steep learning curves, so it would be good if we could fix those in a timely manner.

I am sure the current maintainers have a busy life and I certainly don't want to blame anyone! But I think we need to find a solution to this problem. The most obvious one would be giving maintainer rights to more people, to share the load of getting PRs reviewed and merged.

Fix support for 301 family

This crate has features for stm32f301xa, xb, xc, and xd devices, but ST does not have those in its product line. It does have 301x6 and 301x8 devices.

I propose adding features for 301x6 and 301x8 (and implementing those devices), and marking the other 301 features as deprecated. The 301xa/b/c/d support can then be removed at some point in the future.

stm32f3xx_hal::rcc::CFGR::freeze() gives wrong default clocks: it gives half of the real clocks

Today when I'm doing a serial project with the usart1 hardware on my f3discovery board, to get the default clock configuration by the following code:

let clocks = rcc.cfgr.freeze(&mut flash.acr);

I can't get anything over serial line, after I debuged the hardward by a logic analyzer, I found out the actural bps of the usart1 hardware is twice as much as the intended bps value.

gdb give this clue, showing the hardware is runing on 4MHz sysclk:

(gdb) print clocks
$1 = stm32f3xx_hal::rcc::Clocks {hclk: stm32f3xx_hal::time::Hertz (4000000), pclk1: stm32f3xx_hal::time::Hertz (4000000), pclk2: stm32f3xx_hal::time::Hertz (4000000), ppre1: 1, ppre2: 1, sysclk: stm32f3xx_hal::time::Hertz (4000000), usbclk_valid: false}

which is curious, because I was told the default sysclk is 8MHz. If the default clock configuration gives half of the real clocks, it can explain the double of bps in usart1.

Freeze the clock configuration with 8MHz sysclk manually:

let clocks = rcc.cfgr.sysclk(8.mhz()).freeze(&mut flash.acr);

everythings works fine.

Switching to stm32f30x-hal crate and freeze with default clock configuration again, everything works fine too. And gdb gives me 8MHz sysclk.

The freeze() code in rcc.rs is quite complex, I haven't figure it out yet. but I think there's some thing wrong with PLLMUL setting, in the reference manual, the PLL multiplication factor can never be 1x.

Implement InputPin trait for PA1<Output<OpenDrain>>

I'm trying to read a dht11 humidity sensor with a stm32f303 discovery board

the example in the dht11 crate is for stmf0xx boards:
https://github.com/michaelbeaumont/dht-sensor/blob/master/examples/stm32f042.rs#L30

Rather than give in and just buy an stm32f0xx board I'd like to have a crack at adding this trait. If anyone knows that it is futile (eg Output<OpenDrain> is not both InputPin and OutputPin for the f3) or not useful in anyway please say so.

Part-converted example: https://gist.github.com/peter-wilkins/b5c914b1f8f5b4098b198133b4280472

I'm just starting out with embedded but will attempt to put together an PR, it may take some time. Any advice appreciated.

Increase readability and consistency in src/gpio.rs

Currently the gpio macro in src/gpio.rs has a lot of copy paste and future improvements will probably require even more copy paste.

For example the implementations for the embedded-hal traits currently consist of a large set of copy paste code.

As moving back from an erased type state to a non erased type state is a pain (and currently not even supported for the gpio pins (as I understand it), it seems like it would be beneficial to allow the pin states to be changed regardless if they are erased or not. But with current rust that would require a lot of copy-paste code as, so these implementations should be extracted into separated macros used by the top gpio macro. See e.g. stm32l0xx-hal for the amount of copy paste needed.

If we instead replaced the copy paste of code in the gpio macro with sub macros the code will be much easier to read and understand.

I do not know if we are allowed to use the paste crate as it as I understand require quite a new rust compiler (but at least works with a stable compiler). Using paste we could simplify the parameters to the gpio macro as many parameters are actually identifiers which could be constructed using the paste crate from one single identifier.

Collapse Input<OpenDrain / PullUp / PullDown) into Input

Rethink Pin Type States in Regard of Pull Up/Down

Currently Input<> has three different states (Floating, PullUp, PullDown) which expose the pull-up and pull-down settings.
But Output / AF has no such distinction, instead there is a method internal_pull_up(bool) available for AF and Output, which make it possible to switch between floating of pull-up.
I see a couple of problems with this model:

  1. it is not consistent
  2. it limits the available settings more than the MCU really does, which might be a problem in some situations.
  3. I would call the current implementation as being buggy, as it currently allows one to fool it. You can move a pin into Input then move it to Output or even Output and as neither of the later moves resets the pull up/down states you enter a state which should have been "blocked".

My suggestion is that we collapse the Input<*> states into just Input.

One other solution would be to actually add more type states, but I think that would be a bad idea as the pull up/down is more of a hardware choice to have in the MCU and not external on the board.

Support PWM Output

Supporting PWM output across devices/timers would allow us to support peripherals like servos.

The f3 supports multiple output channels (same period, possibly different duty cycle), and it supports multiple pin-out, making the PwmPin trait from embedded-hal a good way to go. The stm32f1xx-hal has done a lot of good work here, however I propose a slightly different approach, where pin configuration is left to the channels that are returned. This has the benefit of not causing an explosion combinatorics, makes for a flatter (and more easily supported macro), supports channels that can output to multiple pins, and the resulting interface is fairly intuitive to use. We can use type states to ensure that only channels that have pins can be enabled.

I have a WIP branch here, with most of the macros working.

The resulting usage would look like:

// other use statements omitted
use stm32f3xx_hal::pwm::tim8;
use embedded_hal::PwmPin;

#[entry]
fn main() -> ! {
    let dp = stm32f303::Peripherals::take().unwrap();

    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);

    let mut gpioc = dp.GPIOC.split(&mut rcc.ahb);
    let pc8 = gpioc.pc8.into_af4(&mut gpioc.moder, &mut gpioc.afrh);

    let mut gpiob = dp.GPIOB.split(&mut rcc.ahb);
    let pb9 = gpiob.pb9.into_af10(&mut gpiob.moder, &mut gpiob.afrh);

    let (_, _, ch3_without_pins, _) = tim8(
        dp.TIM8,
        256, // resolution of duty cycle
        50, // frequency of period
        &clocks, // To get the timer's clock speed
    );

    // None of these lines compile due to type states
    // let max_duty = ch3_without_pins.get_max_duty();
    // ch3_without_pins.set_duty(max_duty / 4);
    // ch3_without_pins.enable();

    let mut ch3 = ch3_without_pins
        .output_to_pb9(pb9)
        .output_to_pc8(pc8);

    let max_duty = ch3.get_max_duty();
    ch3.set_duty(max_duty / 4);
    ch3.enable();

    loop {}
}

Support for HSE oscillator?

Hi there,

I was wondering if support for the HSE oscillator in RCC for STM32F3 (F303 in my particular case) was planned. Technically, the LSE oscillator isn't fit to support USB, so an external oscillator is required for usb.

I'm hoping to create an application that uses USB so this would be a blocking item.

My current idea for a work around is to configure in STMCube and then just link in and call the clock config functions.

Let me know if this isn't the right place for a question like this.

Low-power modes

The STM32f3xx supports 3 low-power modes: Sleep, Stop, and Standby, in order of high-low power. How can I activate these? Searching the docs for these terms doesn't produce results. Do we need to manually modify registers? Thank you.

Something like this?

stm32::pwr::csr.modify(|_, w| w.sbf().set_bit(1));

Support for ADCs

Hi,

I want to use the analog-to-digital converters on STM32F3. Since I need to implement this anyway, I think this HAL crate is the best place to add support.

  • Can I just go ahead and submit pull requests when I have any?
  • Otherwise, if someone is already working on this, is there any way I can help?
  • I am assuming we need to implement Channel and OneShot from embedded_hal::adc. Is this correct?
  • If not, are there any known problems with those traits that make them unsuitable for the SAR and ΣΔ converters available on STM32F3 chips?

My primary goal is using the ΣΔ ADCs on STM32F373, but I think implementing SAR and other STM32F3 chips may be reasonable because of a large amount of overlap. I would also love to hear any other thoughts about this.

Greetings,
Môshe van der Sterre

Improve race free register access ergonomics

I have played around and trying to figure out if there could be any ergonomic way to ensure race free access without any downsides, I do not think there is any perfect solution, but I think we could come a long way by replacing the current model where we pass one or more &mut parameters to the operations. We could instead implement a type state model where the user could join and split registers and set of registers, with one trait created for accessing each register. This should ensure that max one &mut parameter would be needed for each operation.

E.g. for the gpio the Parts struct returned by the GpioExt::split() operation would return a container struct containing all the gpio registers. Then it would be possible for the user to call split() on that struct to get individual registers ready to be merged again into more or less fine grained containers. I suppose the container structs should be created by the user based on need to not create type state explosion.

With this model it is then up to the user to select how find grained split they need and how they would like to ensure race free access. If the user selects a very coarse grained split, the APIs should be more ergonomic than today.
I also suppose that in most cases the user would not need to do any register splitting as the configuration is done only once start of the system.

SPI initialization should set CR1.LSBFIRST bit to 'MSB first' by default

Many SPI protocol use 'MSB first' in MOSI/MISO line when transfer bits, so 'MSB first' should be set in the SPI_CR1.LSBFIRST bit by default.

I see https://github.com/stm32-rs/stm32f3xx-hal/blob/master/src/spi.rs#L152 try to do the right thing:

// LSBFIRST: MSB first

but https://github.com/stm32-rs/stm32f3xx-hal/blob/master/src/spi.rs#L185 acturally set is to 'LSB first'

                            .lsbfirst()    // line 184, return stm32f3::stm32f303::spi1::cr1::LSBFIRST_W<'_>
                            .lsbfirst()    // line 185, call method on struct returned from line 184

So line 185 should be changed to .msbfirst().

I have successful communicated to my SSD1306 OLED display module from a STM32F3Discovery board after making the this change.

Note: for the SPI to work, the master branch should be used, as another SPI initialization bug fixed in #35 haven't landed on '0.4.0' release yet.

Initialization of real time clock on stm32f3 Discovery board

Hello!

I have not been able to find a way to initialize the rtc on the stm32f3 Discovery board.
For the STM32F103C8, initialization would go something like this (using cortex-m-rtfm crate):

use stm32f1xx_hal::{delay, gpio, i2c, rtc, spi, stm32, timer};

#[app(device = stm32f1xx_hal::stm32, peripherals = true)]
const APP: () = {
   
       #[init(spawn = [msg])]
       fn init(mut c: init::Context) -> init::LateResources {
           let mut rcc = c.device.RCC.constrain();
           let mut backup_domain = rcc
                .bkp
                .constrain(c.device.BKP, &mut rcc.apb1, &mut c.device.PWR);
           let mut rtc_dev = rtc::Rtc::rtc(c.device.RTC, &mut backup_domain); // initialization of rtc
           // rest is omitted
       }
}

Is there a way to initialize the real time clock currently using the stm32f3xx-hal?

STM32F303 - GPIOE.split() missing

I'm trying to get something running on the STM32F3Discovery board, which has an STM32F303 processor.

I've hit a problem where cargo build reports:

error[E0599]: no method named `split` found for type `stm32f3::stm32f303::GPIOE` in the current scope
  --> src/main.rs:49:38
   |
49 |         let mut gpioe = device.GPIOE.split(&mut rcc.ahb);
   |                                      ^^^^^

What's odd about this is that if I change the port to any of GPIOA-GPIOF other than GPIOE, there's no problem. And if I change the hal feature to "stm32f373" instead of "stm32f303" there's also no problem.

Any ideas?

Tracking issue for refactoring

Let's use this issue to track the state of any larger refactors we are planning to do.

This is also the place for discussing new ideas on a higher level. More specific discussions related to the individual refactors should be taken into the respective sub-tickets.

  • Refactor use of features for conditional compilation
  • Refactor the RCC module (WIP #153)
    • Discussed in #157
    • Evaluation of approaches #156
    • Previous attempt #159
  • GPIO
    • Refactor the GPIO module (done in #129)
    • Input Pin Type consistency
  • Refactor CAN (WIP #178)
  • Refactor ADC (WIP #175, #281)
    • breaking changes are needed to extend the unfinished feature set.
      At least make it robust of breaking changes and future proof
    • Get inspired by the stm32f4xx-hal adc implementation
    • Allow more devices
  • Refactor RTC (WIP #174)
    • Add RTC example to prove rtc is working. See #87
  • Refactor peripheral access
  • Serial
    • UART Flow Control #128
    • Parity configuration #231
  • Incorporate alt-stm32f30x-hal

Whenever we refactor anything, we should be aware of the current state of the art. That means looking at the other HALs under stm32-rs and understanding the design decisions and trade-offs they made. This should help us come up with designs that are future-proof and maintainable.

Update embedded-hal to v0.2.3

Hi,

are you planning on updating to embedded-hal v0.2.3? Especially implement the new digital::v2 traits?
Or would you be willing to accept a pull request for it?

External interrupts (example, question)

I wrote a simple example of GPIO-driven interrupt handling, available here. Compile with cargo build --example gpio_interrupt --features "rt stm32f303xc" On the F3 Discovery board it toggles an led when you push the user button.

I'm curious if anyone's working on–or has thoughts about–adding better support for interrupts. The gpio.rs in stm32f4xx-hal has some nice methods based on the ExtiPin trait, making it easy to set up interrupts without direct register access. For example:

let gpioc = dp.GPIOC.split();
let mut board_btn = gpioc.pc13.into_floating_input();
board_btn.make_interrupt_source(&mut dp.SYSCFG);
board_btn.enable_interrupt(&mut dp.EXTI);
board_btn.trigger_on_edge(&mut dp.EXTI, Edge::RISING);

It would be nice to get the f3/f4 HALs into closer alignment but I wanted to gauge others' thoughts before jumping in.

Tim2: Trick to setting fine resolutions?

Hi. I noticed after testing with an oscillosope, I'm having trouble setting fine resolutions using TIM2. Code:

    let tim2_channels = tim2(
        dp.TIM2,
        160_000,  // resolution of duty cycle
        94.hz(), // frequency of period
        &clocks, // To get the timer's clock speed
    );

    let mut pwm0 = tim2_channels.0.output_to_pa0(pwm0_pin);
    pwm0.set_duty(pwm0.get_max_duty() / 2); // 50% duty cycle
    pwm0.enable();

The scope reports a frequency of 100Hz. If set anywhere between 100 and 60, it outputs 100Hz. If set to 50, it outputs 50Hz. Above 100, and I don't see any output. Are there any gotchas here? Thank you.

Unable to initialize RCC

Currently I am unable to initialize RCC on my MCU because of some problems with the HAL. The MCU in question is STM32F303CCT6 and it has a 8 MHz HSE attached, I am initializing it with

    let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(72.mhz())

The problem code in the hal library I've identified is this snippet from rcc.rs

        let pllsrcclk = self.hse.unwrap_or(HSI / 2);

        let pllmul = (2 * self.sysclk.unwrap_or(pllsrcclk)) / pllsrcclk;
        let pllmul = cmp::min(cmp::max(pllmul, 2), 16);
        let pllmul_bits = if pllmul == 2 {
            None
        } else {
            Some(pllmul as u8 - 2)
        };

        let sysclk = pllmul * self.hse.unwrap_or(HSI) / 2;

My current fix is to remove the 2* and /2 from line 2 and the last line by modifying rcc.rs

        let pllsrcclk = self.hse.unwrap_or(HSI / 2);

        let pllmul =  self.sysclk.unwrap_or(pllsrcclk) / pllsrcclk;
        let pllmul = cmp::min(cmp::max(pllmul, 2), 16);
        let pllmul_bits = if pllmul == 2 {
            None
        } else {
            Some(pllmul as u8 - 2)
        };

        let sysclk = pllmul * self.hse.unwrap_or(HSI);

I'd like to ask why the multiplication and division were there in the first place, if they aren't needed I can put up a pull request to include the fix.

The problem is that pllmul becomes 18 which ends up getting coerced to 16, so the value of sysclk and hclk ends up being 64MHz which is obviously not what we want. This is not the problem in itself as the MCU in question is fully capable of running those peripherals at that speed but down the line pclk1 ends up becoming 64MHz as well. Which then fails the assert

        let ppre1 = 1 << (ppre1_bits - 0b011);
        let pclk1 = hclk / u32(ppre1);

        assert!(pclk1 <= 36_000_000);

Which is also expected behaviour since pclk1 does not support more than 36MHz so in the end the problem lies upstream.

Module gpioe is not found

#![no_main]
#![no_std]

use embedded_hal::digital::v1_compat::{OldInputPin, OldOutputPin};
use rtfm::app;
use portable::button;
use stm32f3xx_hal::prelude::*;
use stm32f3xx_hal::{delay, gpio, i2c ,spi, stm32, timer};

#[cfg(not(test))]
extern crate panic_semihosting;

type Button0Pin = gpio::gpioa::PA6<gpio::Input<gpio::PullUp>>;
type BlueLED = gpio::gpioe::PE8<gpio::Output<gpio::PushPull>>;

There is an error in type BlueLED. The module gpioe cannot be found within the gpio module. May someone please advise?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.