Giter VIP home page Giter VIP logo

stm32f0xx-hal's Introduction

STM32 Peripheral Access Crates

CI crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io Matrix

This repository provides Rust device support crates for all STM32 microcontrollers, providing a safe API to that device's peripherals using svd2rust and a community-built collection of patches to the basic SVD files. There is one crate per device family, and each supported device is a feature-gated module in that crate. These crates are commonly known as peripheral access crates or "PACs".

To view the generated code that makes up each crate, visit the stm32-rs-nightlies repository, which is automatically rebuilt on every commit to stm32-rs master. The stm32-rs repository contains the patches to the underlying SVD files and the tooling to generate the crates.

While these crates are widely used, not every register of every device will have been tested on hardware, and so errors or omissions may remain. We can't make any guarantee of correctness. Please report any bugs you find!

You can see current coverage status for each chip here. Coverage means that individual fields are documented with possible values, but even devices with low coverage should have every register and field available in the API. That page also allows you to drill down into each field on each register on each peripheral.

Using Device Crates In Your Own Project

In your own project's Cargo.toml:

[dependencies.stm32f4]
version = "0.15.1"
features = ["stm32f405", "rt"]

The rt feature is optional but helpful. See svd2rust for details.

Then, in your code:

use stm32f4::stm32f405;

let mut peripherals = stm32f405::Peripherals::take().unwrap();

Refer to svd2rust documentation for further usage.

Replace stm32f4 and stm32f405 with your own device; see the individual crate READMEs for the complete list of supported devices. All current STM32 devices should be supported to some level.

Using Latest "Nightly" Builds

Whenever the master branch of this repository is updated, all device crates are built and deployed to the stm32-rs-nightlies repository. You can use this in your Cargo.toml:

[dependencies.stm32f4]
git = "https://github.com/stm32-rs/stm32-rs-nightlies"
features = ["stm32f405", "rt"]

The nightlies should always build and be as stable as the latest release, but contain the latest patches and updates.

Generating Device Crates / Building Locally

  • Install svd2rust, svdtools, and form:
    • On x86-64 Linux, run make install to download pre-built binaries at the current version used by stm32-rs
    • Otherwise, build using cargo (double check versions against scripts/tool_install.sh):
      • cargo install form --version 0.12.1
      • cargo install svdtools --version 0.3.17
      • cargo install svd2rust --version 0.33.4
  • Install rustfmt: rustup component add rustfmt
  • Generate patched SVD files: make patch (you probably want -j for all make invocations)
    • Alternatively you could install cargo-make runner and then use it instead of make. Works on MS Windows natively:
      • cargo install cargo-make
      • cargo make patch
  • Generate svd2rust device crates: make svd2rust
  • Optional: Format device crates: make form

Motivation and Objectives

This project serves two purposes:

  • Create a source of high-quality STM32 SVD files, with manufacturer errors and inconsistencies fixed. These files could be used with svd2rust or other tools, or in other projects. They should hopefully be useful in their own right.
  • Create and publish svd2rust-generated crates covering all STM32s, using the SVD files.

When this project began, many individual crates existed for specific STM32 devices, typically maintained separately with hand-edited updates to the SVD files. This project hopes to reduce that duplication of effort and centralise the community's STM32 device support in one place.

Helping

This project is still young and there's a lot to do!

  • More peripheral patches need to be written, most of all. See what we've got in peripherals/ and grab a reference manual!
  • Also everything needs testing, and you can't so easily automate finding bugs in the SVD files...

Supported Device Families

crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io

Please see the individual crate READMEs for the full list of devices each crate supports. All SVDs released by ST for STM32 devices are covered, so probably your device is supported to some extent!

Devices that are nearly identical, like the STM32F405/F415, are supported by ST under a single SVD file STM32F405, so if you can't find your exact device check if its sibling is supported instead. The crate READMEs make this clear.

Many peripherals are not yet patched to provide the type-safe friendly-name interface (enumerated values); please consider helping out with this!

Check out the full list of supported devices here.

Adding New Devices

  • Update SVD zips in svd/vendor to include new SVDs.
  • Run make extract to extract the new zip files.
  • Add new YAML file in devices/ with the new SVD path and include any required SVD patches for this device, such as renaming or merging fields.
  • Add the new devices to stm32_part_table.yaml.
  • Add the new devices to scripts/makecrates.py.
  • You can run scripts/matchperipherals.py script to find out what existing peripherals could be cleanly applied to this new SVD. If they look sensible, you can include them in your device YAML. This requires a Python environment with the pyyaml and svdtools dependencies. Example command: python scripts/matchperipherals.py peripherals/rcc devices/stm32h562.yaml
  • Re-run scripts/makecrates.py devices/ to update the crates with the new devices.
  • Run make to rebuild, which will make a patched SVD and then run svd2rust on it to generate the final library.

If adding a new STM32 family (not just a new device to an existing family), complete these steps as well:

  • Add the new devices to the CRATES field in Makefile.
  • Update this Readme to include the new devices.
  • Add the devices to workflows/ci.yaml and workflows/nightlies.yaml.

Updating Existing Devices/Peripherals

  • Using Linux, run make extract at least once to pull the SVDs out.
  • Edit the device or peripheral YAML (see below for format).
  • Using Linux, run make to rebuild all the crates using svd patch and svd2rust.
  • Test your new stuff compiles: cd stm32f4; cargo build --features stm32f405

If you've added a new peripheral, consider using the matchperipherals.py script to see which devices it would cleanly apply to.

To generate a new peripheral file from scratch, consider using periphtemplate.py, which creates an empty peripheral file based on a single SVD file, with registers and fields ready to be populated. For single bit wide fields with names ending in 'E' or 'D' it additionally generates sample "Enabled"/"Disabled" entries to save time.

Device and Peripheral YAML Format

Please see the svdtools documentation for full details of the patch file format.

Style Guide

  • Enumerated values should be named in the past tense ("enabled", "masked", etc).
  • Descriptions should start with capital letters but do not end with a period

Releasing

Notes for maintainers:

  1. Create PR preparing for new release:
    • Update CHANGELOG.md with changes since last release and new contributors
    • Update README.md to bump version number in example snippet
    • Update scripts/makecrates.py to update version number for generated PACs
  2. Merge PR once CI passes, pull master locally.
  3. make clean
  4. make -j16 form
  5. for f in stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32h7 stm32l0 stm32l1 stm32l4 stm32l5 stm32g0 stm32g4 stm32mp1 stm32wl stm32wb; cd $f; pwd; cargo publish --allow-dirty --no-default-features; cd ..; end
  6. git tag -a vX.X.X -m vX.X.X
  7. git push vX.X.X

License

Licensed under either of

at your option.

Contribution

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.

stm32f0xx-hal's People

Contributors

adinack avatar bentwire avatar bors[bot] avatar brainstorm avatar cillian64 avatar david-sawatzke avatar dimpolo avatar disasm avatar evils avatar gekkio avatar harkonenbade avatar jamesmunns avatar janekgraff avatar jessebraham avatar jonas-schievink avatar jonfin avatar maldus512 avatar matoushybl avatar newam avatar oll3 avatar owez avatar pagten avatar pigrew avatar revyakin avatar richardeoin avatar shellixyz avatar therealprof avatar torkeldanielsson avatar yatekii 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  avatar  avatar

stm32f0xx-hal's Issues

I2C, SPI, and other peripherals should require their respective DMA channels

Currently, it's possible to configure the SPI1 and I2C1 peripherals like this:

            let sda = gpiob.pb7.into_alternate_af1(cs);
            let scl = gpiob.pb6.into_alternate_af1(cs);

            let i2c = hal::i2c::I2c::i2c1(p.I2C1, (scl, sda), 100.khz(), &mut rcc);

            let sck = gpioa.pa5.into_alternate_af0(cs);
            let miso = gpioa.pa6.into_alternate_af0(cs);
            let mosi = gpioa.pa7.into_alternate_af0(cs);

            let spi = hal::spi::Spi::spi1(
                p.SPI1,
                (sck, miso, mosi),
                ws2812_spi::MODE,
                3_000.khz(),
                &mut rcc,
            );

However, this configuration doesn't actually work since on most STM32F0xx parts, SPI1 and I2C1 share DMA channels 2 (TX) and 3 (RX). Even if you don't use the DMA features, the attempt to share resources puts both peripherals into a bad state. On STM32F07x parts we could use the I2C1_DMA_RMP or SPI1_DMA_RMP bits in SYSCFG_CFGR1 to remap one of the two peripherals to use channels 6&7, so any API for this will need to support that somehow.

This also raises a related concern, which is that there doesn't seem to be a safe way to disable peripherals. My expectation would be that the release() methods would disable them, but looking at the implementations that doesn't seem to be the case. To support it, we'd probably have to pass the RCC into release().

Cannot acknowledge timer interrupt flag

I'm attempting to use a periodic timer using the HAL Timer driver, on an stm32f031, and have hit a limitation.

I can instantiate it and use the driver to setup the desired overflow rate, and then turn on the interrrupt -- so that's easy enough:

let timer = hal::timers::Timer::<pac::TIM16>::tim16(cx.device.TIM16, 1.khz(), &mut rcc);
timer.listen(hal::timers::Event::TimeOut);

I can then create a handler for the TIM16 vector... but now I need to clear the UIF flag, and the only way I can come up with to do it is to steal another copy of the TIM16 register block. One solution is that probably the driver should provide a way to clear the interrupt flags, but it seems to me that a more general solution is to make the tim field (the reg block) public so that if I have a mutable Timer I can manipulate it's registers as well -- this allows for more general extensions of the driver as well as simply clearing the interrupt flag.

Perhaps I am missing something about the intended use here. I'm fairly new to Rust still -- still trying to adapt from C++.

How to use SysTick timer to count ticks from a rotary encoder?

I have seen a couple of examples that use the SysTick timer to count the rotations on a rotary encoder, like this example for the blue pill .

I was able to port that example to ESP32 (based on ESP32-Arduino code) as:

 static void IRAM_ATTR pcnt_example_intr_handler(void *arg) {
	uint32_t intr_status = PCNT.int_st.val;
	int i = 0;

	for (i = 0; i < PCNT_UNIT_MAX; i++) {
		if (intr_status & (BIT(i))) {
			/* Save the PCNT event type that caused an interrupt
			 to pass it to the main program */

			int64_t status=0;
			if(PCNT.status_unit[i].COUNTER_H_LIM){
				status = r_enc_config.counter_h_lim;
			}
			if(PCNT.status_unit[i].COUNTER_L_LIM){
				status = r_enc_config.counter_l_lim;
			}
			//pcnt_counter_clear(ptr->unit);
			PCNT.int_clr.val = BIT(i); // clear the interrupt
			count = status + count;
		}
	}
}

I saw there's a systick timer in this repo and a flash example based on it, but I can't reason around how I would use it for the rotary encoder.

Any help with this would be highly appreciated!

cannot find value `RCC_PLLSRC_PREDIV1_SUPPORT`

With my own code and also with a copy of examples/blinky_delay.rs, using git master and staging, I am getting

Compiling stm32f0xx-hal v0.17.1 (https://github.com/stm32-rs/stm32f0xx-hal#e679ab19)
error[E0425]: cannot find value `RCC_PLLSRC_PREDIV1_SUPPORT` in module `self::inner`
   --> /../.cargo/git/checkouts/stm32f0xx-hal-3a41196e5b12fb2e/e679ab1/src/rcc.rs:457:66
    |
457 |             let pllprediv = match (&self.clock_src, self::inner::RCC_PLLSRC_PREDIV1_SUPPORT) {
    |                                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `self::inner`

Am I missing something simple, or is this a bug?

SPI on 0.17.1 is broken

Absolutely the same code is working fine on 0.16 and doesn't on 0.17. I believe this is due to some performance updates merged lately. There is simply nothing on SCK or MOSI pins. Initialisation code is stanard:

const MODE: Mode = Mode {
        polarity: Polarity::IdleLow,
        phase: Phase::CaptureOnFirstTransition,
};
let spi = Spi::spi1(p.SPI1, (mcp_sck, mcp_miso, mcp_mosi), MODE, spi_freq, &mut rcc);

Usage is also standard, write and transfer methods are used.

image
image

First write on I2C bus never NACK's

Problem:

When performing the first write transaction of the i2c scanner example, there is never a NACK, even if no device is attached on the bus.

Hardware required:

1x STM32F072 Nucleo

2x appropriate (for example 4.7KOhm) pullup resistor (alternatively, use any known OK I2C breakout board with a known I2C address which already includes pullups)

1x https://github.com/stm32-rs/stm32f0xx-hal/blob/master/examples/i2c_find_address.rs (for easier pin access, you may want to change the SDA pin to be PB9 instead of PB7)

Steps to reproduce:

  1. Wire up the pull up resistors from SCL/SDA to 3v3.

  2. Run the i2c scanner example: https://github.com/stm32-rs/stm32f0xx-hal/blob/master/examples/i2c_find_address.rs

  • Expected: _devices == 0
  • Actual: _devices == 1, ACK at address 0
  • If you see a lot of ACKs, your pull-ups could be wrong
  1. Change start address in line 32:
    for add in 0..=127 {
    to e.g. 6 and re-run the scan.
  • Expected: _devices == 0
  • Actual: _devices == 1, ACK at address e.g. 6
  1. Before the scan loop, add this line: let _ = i2c.write(0x4, &[]); and re-run the scan.
  • Expected: _devices == 0
  • Actual: _devices == 0 (yay!)
  1. (Optional) Remove the line added in step 4. Attach an I2C sensor with known address to the bus and re-run the scan.
  • Expected: _devices == 1
  • Actual: _devices == 2

USB: STM32F070: USB Clock source selection

On the STM32F070RB, USB can only be used with the PLL source, not HSI48 (which doesn't exist). A method to select RCC.CFGR3.USBSW=1 should be provided.

Some implementation points to be considered (with my tentative answers in parenthesis):

  1. What should the default be? (always set to HSI48?)
  2. Should there be a debug assertion in freeze() that the PLL is enabled if the PLL source is selected for USB? (Yes)
  3. Should there be a debug assertion in the USB HAL enable() that the selected clock's speed is 48 MHz? (Maybe not since we'd need to start also adding Rcc to the USB peripheral struct)
  4. What should the usage be?

Proposed usage:

    let mut rcc = dp.RCC
        .configure()
        .usbclksrc(stm32f0xx_hal::rcc::USBClkSource::HSI48)
        .sysclk(48.mhz())
        .hclk(48.mhz())
        .pclk(48.mhz())
        .freeze(&mut dp.FLASH);

ADDENDUM: The STM32F1xx HAL has some logic related assertions about the USB clock, so my plan is to duplicate its API as much as possible.

Improve API to allow having an array of Analog pins

It would be great to be able to store analog pins in the array and process them sequentially.
If one needs to have multiple analog pins in the array it's not possible -- types are different and Channel trait has type-associated function.

Currently, Adc implements OneShot trait which accepts pin.

impl<WORD, PIN> OneShot<Adc, WORD, PIN> for Adc

The pin argument itself is not used, but the associated function of its type channel (which is the channel id) is used instead.

Perhaps we can change the function to fn channel(&self) but it might break some assumptions. Would be great to hear some input

Support remaining stm32f0xx devices

Each of the stm32f0x0, stm32f0x1, and stm32f0x2 lines have had support added at this time. The only remaining devices belong to the stm32f0x8 line, and are as follows:

  • STM32F038
  • STM32F048
  • STM32F058
  • STM32F078
  • STM32F098

If nobody else feels like hopping on these I will likely start picking away at them shortly, but just wanted to document the progress made thus far.

Unable to manage overrun for USART

I'm trying to use the USART2 peripheral on my stm32f030RC. I'm not using interrupts, so from time to time an overrun interrupt is to be expected (and is acceptable); however I am unable to manage the error with the tools provided by the API.
When an overrun occurs the serial.read method returns an error accordingly, but:

  • the Peripheral.USART2 reference has been passed to the Serial object, so I cannot access its ICF register to clear the ORE bit.
  • I cannot set the OVRDIS bit and ignore every overrun because the Serial constructor resets the CR3 register (https://docs.rs/stm32f0xx-hal/0.14.1/src/stm32f0xx_hal/serial.rs.html#350); I cannot change it afterwards because the USART2 reference is gone and that bit cannot be changed while the peripheral is on.

How do I manage overrun errors? Either clearing the flag or disabling it altogether would be fine for me.

Edit: this problem would be resolved by PR #63

Flags are never cleared when there's a UART error

If there's a UART error (for example, it receives a byte when there's already a byte in the Receive Data Register), a sticky flag gets set. This flag causes read() to return an error, but the flag doesn't get cleared. So every subsequent read() also returns an error.

I propose you clear the flag by writing to the appropriate bit in icf just before the error is returned.

rcc error

error[E0425]: cannot find value RCC_PLLSRC_PREDIV1_SUPPORT in module self::inner
--> C:\Users\xxxxx.cargo\git\checkouts\stm32f0xx-hal-3a41196e5b12fb2e\917c181\src\rcc.rs:460:66
|
460 | let pllprediv = match (&self.clock_src, self::inner::RCC_PLLSRC_PREDIV1_SUPPORT) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in self::inner

For more information about this error, try rustc --explain E0425.
error: could not compile stm32f0xx-hal due to previous error

More precise units for timers.

Timers are currently implemented with type Time = Hertz. Besides the obvious oddity of using frequency as the unit for timeout times, the Hertz wrapper type is also limited to integer values, which severely restricts the possible periods/frequencies that can be set for a timer.

This isn't a problem at the kHz or MHz scale, but at low audible frequencies (like the low end of the range of MIDI music notes), it's just not enough precision. At the very lowest values, the differences between frequencies are <1Hz, and above those, the frequencies are still noticeably out of tune until they reach a scale where integers provide enough precision.

'Blink' example not working

The "blink" project in the example directory is not working. It can compile fine, but don't run on my nucleo F091RC board.
The led is supposed to blink, instead the led light up and that's all.
Tried to debug step by step, but can't go after the critical section call.

Btw, is there an example to blink a led without using a critical section ?

USB driver release

In the current CHANGELOG, we see Added USB driver under 0.15.2, but this feature is missing from that version. As far as I can tell, no released version contains the stm32-usbd feature.

Took me a little while to realize it wasn't actually released, because the newest release of stm32-usbd no longer has the board-specific impls but I couldn't activate the stm32-usbd feature in this one even though it's in the CHANGELOG.

Thanks!

FEATURE: Allow HSE bypass clock source for F070

An ability to set a "bypass" clock source should be added. Currently, HSE always is in the non-bypassed mode when using this HAL.

I started to code this (it's fairly simple), but don't know what the desired Rcc API would be.

My first choice would be to modify the existing fn to be pub fn hse<F>(mut self, freq: F, bypassed: bool). The disadvantage is that this breaks existing code.

Another implementation would be to create pub fn hse_bypass<F>(mut self, bypassed: bool), with a default to use a XTAL (non-bypassed) source.

This feature is needed for most of the Nucleo boards, where the clock source is an external oscillator (the STLink). I can attempt to implement either API (though I'm very new to Rust).

PWM on Special Channels?

Hello, I am using an STM32F031K6 and I am trying to modify the pwm example to output complementary pwm signals on PA7 and PA8 (since PA8 is on TIM1_CH1 and PA7 is on TIM1_CH1N).

I cannot figure out how to do this, when I try to add PA7 to the channels declaration the pwm::tim1 function yells at me.

I have determined any pin on CH1, 2, 3, or 4 are accepted, but any on BKIN, ETR or CH1N are not.

Am I going about this the wrong way? Is there a different way to configure pins to use these special channels?

Thanks!

USART: Support half-duplex mode

The STM32G0x0 USART peripherals support a 1-wire half-duplex mode. From the reference manual:

26.5.15 USART single-wire Half-duplex communication

Single-wire Half-duplex mode is selected by setting the HDSEL bit in the USART_CR3 register. In this mode, the following bits must be kept cleared:

  • LINEN and CLKEN bits in the USART_CR2 register,
  • SCEN and IREN bits in the USART_CR3 register.
    The USART can be configured to follow a Single-wire Half-duplex protocol where the TX and RX lines are internally connected. The selection between half- and Full-duplex communication is made with a control bit HDSEL in USART_CR3.
    As soon as HDSEL is written to โ€˜1โ€™:
  • The TX and RX lines are internally connected.
  • The RX pin is no longer used.
  • The TX pin is always released when no data is transmitted. Thus, it acts as a standard I/O in idle or in reception. It means that the I/O must be configured so that TX is configured as alternate function open-drain with an external pull-up.
    Apart from this, the communication protocol is similar to normal USART mode. Any conflict on the line must be managed by software (for instance by using a centralized arbiter). In particular, the transmission is never blocked by hardware and continues as soon as data are written in the data register while the TE bit is set.

The only other rust implementation I've been able to find is from the stm32l4xx-hal crate, but it looks like that is using a different style of configuration, so it wouldn't be a straight port.

I'd be happy to try and contribute this, but I'll need a couple of pointers (still pretty new to embedded rust).

Disabling GPIO clock

A common way to lower the power consumption of the stm32f0xx (maybe all stm32's) seems to be to enable GPIOs (clock them), set them to analog mode and then disable the GPIO clocks. And seems like the clock signal for a port is enabled when splitting it (eg x.GPIOX.split()) but couldn't find any way to later revert the operation and disable the clock for the same port.

So wondering where this kind of logic would belong, is there any other stm32-rs hal which already has this implemented (I couldn't find one)?

Confused by features on f03x

If I compile my code with the stm32f031 feature it runs fine on an stm32f030k6t6. But if I enable the stm32f030x6 feature, the code doesn't compile, telling me the timer I'm using doesn't exist. But it does because the code compiled for f031 works on an f030?

This is the firmware and you can see the alternate line in Cargo.toml.

https://github.com/Neotron-Compute/Neotron-BMC/tree/4ae6a7fd535d41ff2f62493895873731059b5001/neotron-bmc-pico

I'm probably doing something dumb.

Note - be sure to only look at the commit as I might change stuff.

SPI blocking transfers do not wait for completion.

Problem

When doing a SPI transfer with the blocking traits the CS line goes high before the transfer is completed.

    let mut buf: [u8; 1] = [0];
    w5500_cs.set_low().unwrap();
    spi.write(&[0x00, 0x39, 0x00]).unwrap();
    spi.transfer(&mut buf).unwrap();
    w5500_cs.set_high().unwrap();

image

Expected Behavior

When I add some hacky polling for the BSY bit (0x80) in the SPI status register to that code, the transfer works as expected, with the CS line going high only after completion of the SPI bus activity.

    let mut buf: [u8; 1] = [0];
    w5500_cs.set_low().unwrap();
    spi.write(&[0x00, 0x39, 0x00]).unwrap();
    let mut spi1_sr: u32 = unsafe { core::ptr::read_volatile(0x4001_3008 as *const u32) };
    while spi1_sr & 0x80 != 0x00 {
        spi1_sr = unsafe { core::ptr::read_volatile(0x4001_3008 as *const u32) };
    }
    spi.transfer(&mut buf).unwrap();
    let mut spi1_sr: u32 = unsafe { core::ptr::read_volatile(0x4001_3008 as *const u32) };
    while spi1_sr & 0x80 != 0x00 {
        spi1_sr = unsafe { core::ptr::read_volatile(0x4001_3008 as *const u32) };
    }
    w5500_cs.set_high().unwrap();

image

This seems to be a case of under-specified behavior in the embedded-hal (which is fixed in 1.0.0), the docs do not specify if the blocking part is just blocking for buffer space, after which the function returns, or blocking for buffer space, and polling for completion.

I think this is a bug though, because despite the under-specification other STM32 embedded HAL traits do block for completion (such as the stm32f1 which I also tested), this appears to be the odd one out.

Other Information

I am using a STM32F070CBTx.

Relevant Cargo.toml:

[dependencies]
cortex-m = "0.6.4"
cortex-m-rt = { version = "0.6.13", features = ["device"] }
embedded-hal = "0.2.4"
stm32f0xx-hal = { version = "0.17.1", features = ["stm32f070xb"] }
rtt-target = "0.3.0"
$ cargo -V             
cargo 1.50.0-nightly (a3c2627fb 2020-12-14)

Potential fault in delay.rs

I was poking clippy to see if it had any useful insights and it threw up this:

warning: digits grouped inconsistently by underscores
  --> src/delay.rs:85:19
   |
85 |             us / (1_000_00 / self.clocks.sysclk().0)
   |                   ^^^^^^^^ help: consider: `100_000`
   |
   = note: #[warn(clippy::inconsistent_digit_grouping)] on by default
   = help: for further information visit https://rust-lang-nursery.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping

Is this constant supposed to be 100,000 or should it but 1,000,000?

Conflicting types for 'assert_failed'

When USE_FULL_ASSERT is enabled, STM32CubeMX generates this function in main.c:

void assert_failed(char *file, uint32_t line)

however the prototype in stm32f0xx_hal_conf.h is:

void assert_failed(uint8_t* file, uint32_t line);

This causes a compile error from GCC "error: conflicting types for 'assert_failed'". It looks like someone updated the prototype but forgot to update the body. The funny thing is uint8_t* is not correct anyway. I think it should be changed to const char*, or at least keep it at char*

[META] Clarification needed on adding additional devices

I'm looking to start adding support for a couple of devices (STM32F070 and STM32F091), and I'm just looking for some clarification on something before I get started. I'm still relatively new to Rust so hopefully I'm not missing something simple ๐Ÿ˜„

Initial support for the STM32F030, which is quite similar to the STM32F070, was recently added to the repository, so I planned on basing my patch(es) off of the work already done. Most of the changes made so far make sense to me; at this time the major point of confusion for me lies in Cargo.toml.

This all makes perfect sense, and is exactly how I would expect it to look:

[features]
rt = ["stm32f0/rt"]
stm32f042 = ["stm32f0/stm32f0x2"]
stm32f030 = ["stm32f0/stm32f0x0"]

The remainder, however, has me a bit confused:

stm32f030x4 = ["stm32f030x6"]
stm32f030x6 = ["stm32f030"]
stm32f030x8 = ["stm32f030", "stm32f0/stm32f0x0"]
stm32f030xc = ["stm32f030", "stm32f0/stm32f0x0"]

My first question is, why does the STM32F030 have a feature for the base model as well as the four available variants, while the STM32F042 only has the base model listed as a feature, ignoring its variants?

Second, I'm not quite understanding the dependencies listed for the STM32F030 variants; I'm not sure if an error has been made here or if I'm just failing to understand it. In my mind the stm32f030 = ["stm32f0/stm32f0x0"] is the only line that should be required, and the remainders seems redundant, so hopefully somebody call fill in the blanks on what's going on here.

Thanks in advance, keep up the great work!

Clarification on Embedded Rust programming philosophy

Hello,
I'm starting a new project with an stm32f0 device which I specifically chose to be able to develop Rust firmware. I made a few experiments with just registers and the cortex_m crate following along the Embedded Rust book's teaching examples to really understand how the architecture works, then started approaching this library for ease of use.

I have a few years of experience as an embedded C developer, a few months of tutorials and experiments with Rust and overall good comprehension of modern programming language features. I'm having a few difficulties in understanding what is the proper way to do things in Rust and I could use some clarifications.

I took the blinky example and started working from that: I wanted to add a systick interrupt to keep track of milliseconds elapsed since the start:

#[exception]
fn SysTick() -> ! {
    unsafe {
        MILLIS += 1;
    }
}

and then read the shared variable MILLIS to manage various temporized functions in the main cycle. When I tried this approach however I realized that it didn't work because the main cycle (following the blinky example) was wrapped in a CriticalSection context, disabling all interrupts by default.
When I tried moving the operations outside of the critical section (i.e. blinking the LED every 1000 milliseconds) the compiler meticulously pointed out that I had no way of accessing the PA0<Output<PushPull>> reference I was using to toggle the LED. Plus, I can't even create one without a critical section token.

In fact, there is no example in the repository that has concurrent main task and interrupts: it's either just the main cycle or just the interrupt, with the main cycle stuck in an empty loop (thinking about led_hal_button_irq.rs). So, my first question is: how would I proceed to obtain a main cycle that allows interruptions but has obtained a reference to the peripherals? The interrupt::free context moves the values so I can't use them afterwards; I know I can populate a Mutex or RefCell, but those are data structures used to share and sync references between threads, and I have no need for sharing: only my main thread will access those, with the interrupts occasionally setting flags and such.

While I was looking for a solution I also dropped the idea of keeping track of time via interrupt and instead I'm using a free counter. Nonetheless I expect to hit this obstacle again soon enough.
This is the code I've written so far, just to explain what I was trying to accomplish.

#![no_main]
#![no_std]

extern crate panic_semihosting;

use stm32f0xx_hal as hal;

use hal::{prelude::*, stm32};

use cast;
use cortex_m::peripheral::Peripherals;
use cortex_m_rt::entry;
use cortex_m_rt::exception;

#[entry]
fn main() -> ! {
    let mut p = stm32::Peripherals::take().unwrap();
    let cp = Peripherals::take().unwrap();

    cortex_m::interrupt::free(move |cs| {
        p.RCC.apb1enr.modify(|_, w| w.tim6en().set_bit());
        p.RCC.apb1rstr.modify(|_, w| w.tim6rst().set_bit());
        p.RCC.apb1rstr.modify(|_, w| w.tim6rst().clear_bit());
        let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut p.FLASH);

        let gpioa = p.GPIOA.split(&mut rcc);

        // (Re-)configure PA1 as output
        let mut led = gpioa.pa12.into_push_pull_output(cs);
        let tim = p.TIM6;

        // pause
        tim.cr1.modify(|_, w| w.cen().clear_bit());
        // restart counter
        tim.cnt.reset();

        let frequency = 1000;
        let ticks = rcc.clocks.pclk().0 / frequency;

        let psc = cast::u16(ticks - 1).unwrap(); // / (1 << 16)).unwrap();
        tim.psc.write(|w| unsafe { w.psc().bits(psc) });

        // start counter
        tim.cr1.modify(|_, w| w.cen().set_bit());

        let mut ts: u32 = 0;


        /* The main loop should be outside of the interrupt-free context */
        loop {
            if is_expired(ts, get_millis(&tim), 1000) {
                led.toggle();
                ts = get_millis(&tim);
            }
            check_overflow(&tim);
        }
    });

    loop {
        continue;
    }
}

static mut MILLIS: u32 = 0;

pub fn is_expired(start : u32, current : u32, delay : u32) -> bool {
    let finish = start.wrapping_add(delay);
    (finish.wrapping_sub(current) as i32) < 0
}

pub fn check_overflow(timer: &hal::stm32::TIM6) {
    cortex_m::interrupt::free(|_| {
        if timer.sr.read().uif().is_update_pending() {
            unsafe { MILLIS += 0xFFFF }
            timer.sr.write(|w| w.uif().clear());
        }
    });
}

pub fn get_millis(timer: &hal::stm32::TIM6) -> u32 {
    let mut result: u32 = 0;
    cortex_m::interrupt::free(|_| unsafe { result = MILLIS + timer.cnt.read().bits() });
    result
}

Please be patient if I'm missing a key aspect of Rust programming, I'm new to it and I promise I did my research before asking.

Problem when using external clock source

Usually I clock my precision devices from external 8MHz crystal and it fails to work here.
There is my setup:

            // Setup clock from 8 MHz external crystal and 48 MHz PLL
            let mut rcc = p.RCC
                .configure()
                .hse(8.mhz())
                .sysclk(48.mhz())
                .freeze(&mut p.FLASH);

There infinite busy-loop occurs on PLL ready flag awaiting.
To exclude hardware problems I tried to load code from other project in C with similar setup and it works as expected.
It seems PLL multiplier calculation is wrong because in case of using HSE as a clock source according to datasheet the input frequency does not divided by two alike a case when HSI is used.
I may mistake but that my debugger says:

// before enable_pll()
(gdb) svd RCC CR
Fields in RCC CR:
        HSION:     1  Internal High Speed clock enable
        HSIRDY:    1  Internal High Speed clock ready flag
        HSITRIM:  16  Internal High Speed clock trimming
        HSICAL:   93  Internal High Speed clock Calibration
        HSEON:     1  External High Speed clock enable
        HSERDY:    1  External High Speed clock ready flag
        HSEBYP:    0  External High Speed clock Bypass
        CSSON:     1  Clock Security System enable
        PLLON:     0  PLL enable
        PLLRDY:    0  PLL clock ready flag
(gdb) s
stm32f0xx_hal::rcc::inner::enable_pll (rcc=<optimized out>, c_src=0x20000fe0, ppre_bits=<optimized out>,
    hpre_bits=<optimized out>, pllmul_bits=<optimized out>)
// after enable_pll()
(gdb) svd RCC CR
Fields in RCC CR:
        HSION:     1  Internal High Speed clock enable
        HSIRDY:    1  Internal High Speed clock ready flag
        HSITRIM:  16  Internal High Speed clock trimming
        HSICAL:   93  Internal High Speed clock Calibration
        HSEON:     0  External High Speed clock enable
        HSERDY:    0  External High Speed clock ready flag
        HSEBYP:    0  External High Speed clock Bypass
        CSSON:     0  Clock Security System enable
        PLLON:     1  PLL enable
        PLLRDY:    0  PLL clock ready flag
(gdb) svd RCC CFGR
Fields in RCC CFGR:
        SW:         0  System clock Switch
        SWS:        0  System Clock Switch Status
        HPRE:       0  AHB prescaler
        PPRE:       0  APB Low speed prescaler (APB1)
        ADCPRE:     0  ADC prescaler
        PLLSRC:     2  PLL input clock source
        PLLXTPRE:   0  HSE divider for PLL entry
        PLLMUL:    10  PLL Multiplication Factor
        MCO:        0  Microcontroller clock output
        MCOPRE:     0  Microcontroller Clock Output Prescaler
        PLLNODIV:   0  PLL clock not divided for MCO

Unexpectedly HSE turned off and the PLLMUL is 10 (x12 multiplier) when 4 (x6) is expected.

How to handle race conditions with shared peripherals?

Addresses #15 (comment). We have a couple race conditions:

  1. The $PXi types are send, but setting the alternate mode does a RMW to a shared register

    I'd add a GPIO specific GpioShared struct which is in parts and needs to be passed as a &mut to the mode settings (af, pp, hs, od) and pull-up/down config.

  2. The register modification to enable peripherals also suffer from race conditions.

    Something like a token returned in the Rcc struct that has to be passed to the new methods, similar to the Gpio thing.

That makes the api a bit more annoying but safe. If can't think of better ways, but there are probably some

Figure out what to do with the examples

I'd really like to keep them in there. They seem like good general representations and easily give an overview about how stuff is supposed to work. I'd remove the more specific ones (all the i2c & spi ones) and replace them with generalized versions (e.g. for spi a loopback example).

They provide quick example code which (even if they exist) are only partly given by doc tests. Especially for something like interrupts. They also serve as a way to have sample programs to compare binaries.

If we use a linker file with the minimum amount of memory and keep them in the CI system, I see little potential for breakage.

It also enables people that already have some chip, but not a specific board, get started, especially if they already have some experience in embedded c.

As we currently can't use doc tests, they also server as some form of build test.

Why does configuring a GPIO require a critical section in this hal?

I'm using an stm32f042 and come from using the stm32f107. Getting started with this hal there is a different approach here in how gpio:s are acquired. Functions such as into_push_pull_output take a critical section here, whereas in other hal:s they take one or more configuration registers.

Is the current design of this hal by intention?

I was surprised to find that the example for how to blink more than one LED wraps the whole main loop in a critical section. If I am reading things correctly then the main reason for that is in how gpio:s are acquired.

I was looking a bit at restructuring this. It seems f1xx uses a different set of registers for setting up the gpio:s. But f3xx and f4xx seems more reasonably similar and could maybe be used as reference (i.e. place to shamelessly copy code from...). It seems like it would be a bit of work though, and it would cause breaking changes for users of this HAL. And maybe I'm missing an obvious/good reason for it being the way it is? So I thought it best to check here first before putting more effort into it :)

edit: Actually, after writing this and then letting go of trying to get rid of the critical section by changing the hal I came up with the following to not have the main loop inside the critical section while having more than one led:

    let (mut led_yellow, mut led_blue, mut led_red) = cortex_m::interrupt::free(move |cs| {
        let mut led_yellow = gpiob.pb3.into_push_pull_output(cs);
        led_yellow.set_high().ok();
        let mut led_blue = gpiob.pb4.into_push_pull_output(cs);
        led_blue.set_high().ok();
        let mut led_red = gpiob.pb5.into_push_pull_output(cs);
        led_red.set_high().ok();

        (led_yellow, led_blue, led_red)
    });

This does not feel that bad. So maybe I should make a PR changing the multiple-led example to this instead of changing the whole hal?

Using timer1 channel 4 for ADC triggering

I need to configure the 4th channel to trigger timer1 3 complementary output and adc.
macro! pwm_4_channels_with_3_complementary_outputs() macro, the point I'm stuck on is that I don't know how to access the 4th channel.
I don't want to connect to the GPIO pin to which the 4th channel is connected

#![deny(unsafe_code)]
#![no_main]
#![no_std]

// Halt on panic
use panic_halt as _;

use cortex_m;
use cortex_m_rt::entry;

use stm32f0xx_hal as hal;

use hal::{delay::Delay, pac::{self, TIM1}, prelude::*, pwm, adc};

#[entry]
fn main() -> ! {
if let Some(mut dp) = pac::Peripherals::take() {
// Set up the system clock.
let mut rcc = dp.RCC.configure().sysclk(8.mhz()).freeze(&mut dp.FLASH);

    let gpioa = dp.GPIOA.split(&mut rcc);
    let gpiob = dp.GPIOB.split(&mut rcc);
    let channels = cortex_m::interrupt::free(move |cs| {
        (
            gpioa.pa8.into_alternate_af2(cs),  // on TIM1_CH1
            gpiob.pb13.into_alternate_af2(cs), // on TIM1_CH1N
            gpioa.pa9.into_alternate_af2(cs),  // on TIM1_CH2
            gpiob.pb14.into_alternate_af2(cs), // on TIM1_CH2N
            gpioa.pa10.into_alternate_af2(cs), // on TIM1_CH3
            gpiob.pb15.into_alternate_af2(cs), // on TIM1_CH3N
        )
    });

    let pwm = pwm::tim1(dp.TIM1, channels, &mut rcc, 20u32.khz());
    let (mut pwm_c_h, mut pwm_c_l, mut pwm_b_h, mut pwm_b_l, mut pwm_a_h, mut pwm_a_l) = pwm;



    let max_duty = pwm_c_h.get_max_duty();
    pwm_c_h.set_duty(max_duty / 2);
    pwm_c_h.enable();
    pwm_c_l.enable();
    pwm_b_h.set_duty(max_duty / 2);
    pwm_b_h.enable();
    pwm_b_l.enable();
    pwm_a_h.set_duty(max_duty / 2);
    pwm_a_h.enable();
    pwm_a_l.enable();

    // simple duty sweep
    if let Some(cp) = cortex_m::Peripherals::take() {
        let mut delay = Delay::new(cp.SYST, &rcc);

        let steps = 100;

        loop {
            for i in 0..steps {
                pwm_c_h.set_duty(max_duty / steps * i);
                pwm_b_h.set_duty(max_duty / steps * i);
                pwm_a_h.set_duty(max_duty / steps * i);
                delay.delay_ms(30u16);
            }

            for i in (1..steps).rev() {
                pwm_c_h.set_duty(max_duty / steps * i);
                pwm_b_h.set_duty(max_duty / steps * i);
                pwm_a_h.set_duty(max_duty / steps * i);
                delay.delay_ms(30u16);
            }
        }
    }
}

// something went wrong when acquiring peripheral access
loop {
    cortex_m::asm::nop();
}

}

Quadrature Encoder Interface

I am using STM32F072RB, and its datasheet specifically mentions handling encoder signals with TIM2 and TIM3. I believe the interface implementation should be very similar to stm32f1xx-hal. Is there a reason why it was omitted from this crate?

Loop {} in main() seems to prevent interrupts from firing.

Not sure if that's an issue or something is missing in the interrupts setup:

I tried using the blinky-irq example to do something else, e.g. display something on SSD1306, while the interrupts are updating values, toggling LEDs, etc.
As it didn't work, I tried adding single pieces one by one. The conclusion is: as soon as I put a loop {} in main () in this code https://github.com/nebelgrau77/STM32F0_blinky_IRQ
the LEDs stop blinking. It doesn't matter if it's an empty loop, or with a NOP, or with any actual stuff being done.

Side effects from using *const addresses

I just noticed that using *const means we can't share peripherals between interrupt/exception handlers and main anymore:

error[E0277]: `*const stm32f0::stm32f0x2::USART2` cannot be sent between threads safely
  --> examples/i2c_hal_ssd1306alphabeter.rs:23:1
   |
23 | / static PANIC_SERIAL: Mutex<RefCell<Option<hal::serial::Tx<USART2>>>> =
24 | |     Mutex::new(RefCell::new(None));
   | |___________________________________^ `*const stm32f0::stm32f0x2::USART2` cannot be sent between threads safely

cc @HarkonenBade @david-sawatzke

PWM support

I started implementing PWM for this crate in two slightly different ways:

Approach one

https://github.com/stm32-rs/stm32f0xx-hal/tree/features/pwm

This uses the typical approach of initialising the PWM peripheral with the timer and pins to use for PWM. The code is loosely based on https://github.com/stm32-rs/stm32f1xx-hal/blob/master/src/pwm.rs and I can see a number of advantages and disadvantages with this.

Advantages:

  • Prevents misconfigurations of timer, channel and GPIO combinations

Disadvantages:

  • Does not allow using one channel for multiple GPIOs at once
  • Earth shattering number of timer, channel and GPIO combinations need to be encoded
  • Someone might want to use PWM without assigned GPIO which is not possible in the current design

Approach two

https://github.com/stm32-rs/stm32f0xx-hal/tree/features/pwm-alternate

This approach only binds the timer to provide 4 PWM channels without any association to a GPIO.

Advantages:

  • Allows for much more flexible configuration of zero or more GPIOs per channel

Disadvantages:

  • No sanity checking for correctly configured GPIO pins and channel relations

I'm a bit on the fence which variant we should go for. Approach one is rather limiting, especially if we want to support advanced uses of PWM (and maybe other nice timer features). It is also very complex to implement for all chips and all possible combinations at once. On the other hand it would be great to have a way of telling a user that some timer/channel/GPIO combination will not work or is incorrectly configured.

Ideas?

CC @zklapow

Unable to compile examples

Hello,
I'm trying to run the blinky-delay example on an stm32f030rc. At first I have tried copying it in a new project and adding the crate as a dependency, by removing the crate self reference and adding version 0.14.1 to Cargo.toml dependencies:

#![no_main]
#![no_std]

#[allow(unused)]
use panic_halt;

use stm32f0xx_hal as hal;

use hal::{delay::Delay, prelude::*, stm32};

use cortex_m::peripheral::Peripherals;
use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
    if let (Some(mut p), Some(cp)) = (stm32::Peripherals::take(), Peripherals::take()) {
        cortex_m::interrupt::free(move |cs| {
            let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut p.FLASH);

            let gpioa = p.GPIOA.split(&mut rcc);

            // (Re-)configure PA1 as output
            let mut led = gpioa.pa1.into_push_pull_output(cs);

            // Get delay provider
            let mut delay = Delay::new(cp.SYST, &rcc);

            loop {
                led.toggle();
                delay.delay_ms(1_000_u16);
            }
        });
    }

    loop {
        continue;
    }
}

Unfortunately this leads to the compiler complaining about unresolved imports for all modules used:

   Updating crates.io index
   Compiling stm32f0 v0.7.1
   Compiling stm32f0xx-hal v0.14.1
   Compiling app v0.1.0 (/home/maldus/Projects/app)
error[E0432]: unresolved imports `hal::delay`, `hal::prelude`, `hal::stm32`
  --> src/main.rs:10:11
   |
10 | use hal::{delay::Delay, prelude::*, stm32};
   |           ^^^^^         ^^^^^^^     ^^^^^ no `stm32` in the root
   |           |             |
   |           |             could not find `prelude` in `hal`
   |           could not find `delay` in `hal`

warning: unused import: `prelude::*`
  --> src/main.rs:10:25
   |
10 | use hal::{delay::Delay, prelude::*, stm32};
   |                         ^^^^^^^^^^
   |
   = note: #[warn(unused_imports)] on by default

error[E0599]: no method named `mhz` found for type `{integer}` in the current scope
  --> src/main.rs:19:54
   |
19 |             let mut rcc = p.RCC.configure().sysclk(8.mhz()).freeze(&mut p.FLASH);
   |                                                      ^^^

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0432, E0599.
For more information about an error, try `rustc --explain E0432`.
error: Could not compile `app`.

I have since tried to compile examples directly from the repository itself, but I encountered similar issues. I'm working on stable, do I need a particular toolchain version to compile this?

$ rustc -V
rustc 1.36.0 (a53f9df32 2019-07-03)

Timer<TIM7> fires twice as fast as expected

Hey, using the STM32F072, a periodic 1khz timer seems to actually be running at 2khz.

My RCC looks like this:

let mut rcc = rcc
    .configure()
    .hsi48()
    .enable_crs(crs)
    .sysclk(48.mhz())
    .pclk(24.mhz())
    .freeze(&mut flash);

My Timer setup looks like this:

let mut ms_timer = Timer::tim7(tim7, 1.khz(), &mut rcc);
ms_timer.listen(Event::TimeOut);

And my timer interrupt looks like this:

#[task(binds = TIM7, resources = [led, ms_timer])]
fn clock(cx: clock::Context) {
    static mut TOGGLE: bool = false;
    static mut STEPDOWN: usize = 0;

    // Only way to clear the interrupt flag
    cx.resources.ms_timer.wait().ok();

    *STEPDOWN += 1;

    if *STEPDOWN >= 1000 {
        *STEPDOWN = 0;
    } else {
        return;
    }

    if *TOGGLE {
        cx.resources.led.set_low().ok();
    } else {
        cx.resources.led.set_high().ok();
    }
    *TOGGLE = !*TOGGLE
}

With this, I would expect that the LED blinks at approximately 0.5Hz (it toggles at 1Hz), however it seems to be blinking at 1Hz (toggling at 2Hz). I'm not sure where the timer clock source is set (the code calculation uses pclk(), the reference manual mentions "CK_CNT", which I can't find in the HAL code. I have a suspicion that it is using the core clock (at 48MHz), but using the peripheral clock (at 24MHz) to calculate the counter value.

How do I run an SPI controller in slave mode?

I am currently working on a design involving an STM32F042 microcontroller that involves the use of both SPI controllers, one operating as a master and one operating as a slave. While this HAL does offer facilities for driving an SPI controller in master mode, I do not see any code for abstracting over a slave controller. How would I go about that, if I were to use this library to handle other aspects of the microcontroller?

Flash Driver

Hi I'm looking for a flash driver for stm32f091, can you help me with that?

0.16.0 docs build is failing

docs.rs failed to build 0.16.0: https://docs.rs/crate/stm32f0xx-hal/0.16.0

logs

[INFO] [stderr] WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
[INFO] [stdout] add606e904d8d00e9e83ceb3d5cf36d06acbc0cfc0cd35183e9f98ced61ac806
[INFO] running `"docker" "start" "-a" "add606e904d8d00e9e83ceb3d5cf36d06acbc0cfc0cd35183e9f98ced61ac806"`
[INFO] [stderr]    Compiling semver-parser v0.7.0
[INFO] [stderr]    Compiling typenum v1.11.2
[INFO] [stderr]    Compiling proc-macro2 v1.0.8
[INFO] [stderr]    Compiling unicode-xid v0.2.0
[INFO] [stderr]    Compiling cortex-m v0.6.2
[INFO] [stderr]     Checking stable_deref_trait v1.1.1
[INFO] [stderr] error[E0463]: can't find crate for `core`
[INFO] [stderr]   |
[INFO] [stderr]   = note: the `thumbv6m-none-eabi` target may not be installed
[INFO] [stderr] 
[INFO] [stderr] error: aborting due to previous error
[INFO] [stderr] 
[INFO] [stderr] For more information about this error, try `rustc --explain E0463`.
[INFO] [stderr] error: could not compile `stable_deref_trait`.
[INFO] [stderr] warning: build failed, waiting for other jobs to finish...
[INFO] [stderr] error: build failed
[INFO] running `"docker" "inspect" "add606e904d8d00e9e83ceb3d5cf36d06acbc0cfc0cd35183e9f98ced61ac806"`
[INFO] running `"docker" "rm" "-f" "add606e904d8d00e9e83ceb3d5cf36d06acbc0cfc0cd35183e9f98ced61ac806"`
[INFO] [stdout] add606e904d8d00e9e83ceb3d5cf36d06acbc0cfc0cd35183e9f98ced61ac806

Have PA9/10/11/12 USB pins automatically remap?

I was going a bit nuts because my board was not USB-enumerating (for a seemingly simple RTIC example) and it turned out to be that I didn't uncomment those:

        /* Uncomment the following lines if you have a chip in TSSOP20 (STM32F042F)
        or UFQFPN28 (STM32F042G) package
        This code enables clock for SYSCFG and remaps USB pins to PA9 and PA10.
        */
        dp.RCC.apb2enr.modify(|_, w| w.syscfgen().set_bit());
        dp.SYSCFG.cfgr1.modify(|_, w| w.pa11_pa12_rmp().remapped());

Fortunately, @mvirkkunen and other very helpful folks at rust-embedded discord channel pointed that out...

Now, would it be at all possible to detect those different variants automatically, somehow?

Wrong I2C pin definitions for stm32f030.

Stm32f030 datasheet defines pinsPA11 and PA12 alternate function 5 as "SCL" and "SDA" instead of "I2Cx_SCL" and "I2Cx_SDA". Current implementation assumes that these pins are used by the I2C1 interface. This is however seems to not be true for the STM32F030 as the I2C1 interface doesn't work on these pins.

Does anyone have any further information on this?

I may try using these pins with I2C2 in the future and then open a PR with the fix.

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.