stm32-rs / stm32l0xx-hal

A hardware abstraction layer (HAL) for the STM32L0 series microcontrollers written in Rust
BSD Zero Clause License
96 stars 60 forks source link

PWM Example compile issues #219

Closed NiklasC closed 1 year ago

NiklasC commented 2 years ago

Hello! I'm a beginner with rust and am currently working with a STM32L011K4T6. I wanted to try out the PWM Example for my project, however, I'm running into some issues that I'm not sure how to fix.

I've copied the example into my project and changed the PWM pin to PB3 and use the feature stm32l0x2.

When compiling, I get the following error messages:

user@MacBook-Air l011-blinky % cargo build --release
   Compiling l011-blinky v0.1.0 (/Users/user/Documents/Projekte/l011-blinky)
error[E0599]: no method named `get_max_duty` found for struct `stm32l0xx_hal::pwm::Timer` in the current scope
  --> src/main.rs:33:19
   |
33 |     let max = pwm.get_max_duty();
   |                   ^^^^^^^^^^^^ method not found in `stm32l0xx_hal::pwm::Timer<stm32l0xx_hal::pac::TIM2>`

error[E0599]: no method named `enable` found for struct `stm32l0xx_hal::pwm::Timer` in the current scope
  --> src/main.rs:35:9
   |
35 |     pwm.enable();
   |         ^^^^^^ method not found in `stm32l0xx_hal::pwm::Timer<stm32l0xx_hal::pac::TIM2>`

error[E0599]: no method named `set_duty` found for struct `stm32l0xx_hal::pwm::Timer` in the current scope
  --> src/main.rs:38:13
   |
38 |         pwm.set_duty(max);
   |             ^^^^^^^^ method not found in `stm32l0xx_hal::pwm::Timer<stm32l0xx_hal::pac::TIM2>`

error[E0599]: no method named `set_duty` found for struct `stm32l0xx_hal::pwm::Timer` in the current scope
  --> src/main.rs:41:13
   |
41 |         pwm.set_duty(max / 2);
   |             ^^^^^^^^ method not found in `stm32l0xx_hal::pwm::Timer<stm32l0xx_hal::pac::TIM2>`

error[E0599]: no method named `set_duty` found for struct `stm32l0xx_hal::pwm::Timer` in the current scope
  --> src/main.rs:44:13
   |
44 |         pwm.set_duty(max / 4);
   |             ^^^^^^^^ method not found in `stm32l0xx_hal::pwm::Timer<stm32l0xx_hal::pac::TIM2>`

error[E0599]: no method named `set_duty` found for struct `stm32l0xx_hal::pwm::Timer` in the current scope
  --> src/main.rs:47:13
   |
47 |         pwm.set_duty(max / 8);
   |             ^^^^^^^^ method not found in `stm32l0xx_hal::pwm::Timer<stm32l0xx_hal::pac::TIM2>`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `l011-blinky` due to 6 previous errors

I've checked the documentation, and my understanding is that the required features are implemented at the PWM struct and not the Timer struct. However, due to my limited experience in Rust I'm not sure how to use instantiate the PWM peripheral correctly. Would it be possible to update the example?

jamwaffles commented 2 years ago

Hey, welcome to embedded Rust! A couple of things:

By the looks of it, shouldn't you be using the stm32l0x1 feature instead of stm32l0x2? Better yet, consider using ONLY the mcu-STM32L011K4Tx feature - it will enable the other correct features for your MCU which is nice.

You also need to be careful around the combination of timer/channel/pin you use. If you can build the docs locally with cargo doc --open --features mcu-STM32L011K4Tx and search for the pwm::Pin trait, you'll see that it's only implemented for PA0, PA1, PA2 and PA3 for the STM32L011 chip you're using. There's a chain of trait bounds that are then not satisfied for PB3, leading to the compile errors you're seeing - those methods come from the PwmPin trait.

Note that if you check the online docs here, you'll see the different combinations that can be used with different/bigger chips in the L0 series. This is because the build runs with the mcu-STM32L071KBTx feature turned on.

I appreciate this is confusing for someone just getting started, but if we did our work right in this crate, the pins enabled for your MCU should match the datasheet which is a good second reference. I haven't checked myself, so please confirm if your MCU really does/doesn't have PB3 available as a PWM output. If we got something wrong here, there's a bug to fix :)

NiklasC commented 2 years ago

Thank you for your quick reply! I've updated the feature as you suggested, however, I still get the same errors as before. To be honest I did not check the docs regarding the PWMpin itself, only the peripheral.

According to the datasheet on page 39, PB3 has the TIM2 Channel 2 alternate function listed, which is also why I selected the stm32l0x2 originally, since it does support PB3 as PWM output as specified in the pwm.rs file.

jamwaffles commented 2 years ago

Can you paste your code here if it's not too long?

According to the datasheet on page 39, PB3 has the TIM2 Channel 2 alternate function listed, which is also why I selected the stm32l0x2 originally, since it does support PB3 as PWM output as specified in the pwm.rs file.

Ah ok, maybe there's a bug in this crate's feature selection then. I can try to take a look when I have a spare minute.

NiklasC commented 1 year ago

Hey! Sorry, it's been some time, and I've only recently come back to this project. My code is the same as in the example pwm.rs:

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

extern crate panic_halt;

use cortex_m_rt::entry;
use stm32l0xx_hal::{pac, prelude::*, pwm, rcc::Config};

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

    // Configure the clock.
    let mut rcc = dp.RCC.freeze(Config::hsi16());

    // Get the delay provider.
    let mut delay = cp.SYST.delay(rcc.clocks);

    // Acquire the GPIOA peripheral. This also enables the clock for GPIOA in
    // the RCC register.
    let gpioa = dp.GPIOA.split(&mut rcc);

    // Initialize TIM2 for PWM
    let pwm = pwm::Timer::new(dp.TIM2, 10_000.Hz(), &mut rcc);

    #[cfg(feature = "stm32l0x1")]
    let mut pwm = pwm.channel2.assign(gpioa.pa1);

    // This is LD2 on ST's B-L072Z-LRWAN1 development board.
    #[cfg(any(feature = "stm32l0x2", feature = "stm32l0x3"))]
    let mut pwm = pwm.channel1.assign(gpioa.pa5);

    let max = pwm.get_max_duty();

    pwm.enable();

    loop {
        pwm.set_duty(max);
        delay.delay_ms(500_u16);

        pwm.set_duty(max / 2);
        delay.delay_ms(500_u16);

        pwm.set_duty(max / 4);
        delay.delay_ms(500_u16);

        pwm.set_duty(max / 8);
        delay.delay_ms(500_u16);
    }
}

I've tried to change the PWM output to PB3 as it is on my hardware as well, but that does not work either.

jamwaffles commented 1 year ago

Hey, your comment spurred me to look into this a bit deeper and I think this crate has the pin config incorrect. Looking through the L011 datasheet and some other spot checks, it looks like TIM2 can output on a few pins for all L0 devices which we were only enabling for L0x2 and L0x3 devices. I've opened a PR in #224 which fixes this.

Would you mind giving it a test though? e.g. put this in your Cargo.toml:

stm32l0xx-hal = { git = "https://github.com/stm32-rs/stm32l0xx-hal.git", branch = "correct-tim2-pins" }
NiklasC commented 1 year ago

Thank you very much :) I've tried your PR, but sadly I still get the same result as before. My Cargo.toml is looking like this:

[package]
name = "l011_blinky"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
cortex-m = "0.7.6"
cortex-m-rt = "0.7.1"
embedded-hal = "0.2.7"
panic-halt = "^0.2.0"
stm32l0 = "0.15.1"
stm32l0xx-hal = {git = "https://github.com/stm32-rs/stm32l0xx-hal.git", branch = "correct-tim2-pins", features = ["mcu-STM32L011K4Tx", "rt"]}

I've also tried using TIM21 instead of TIM2 for PWM, but it seems like this is not implemented? I get the additional error message:

error[E0277]: the trait bound `stm32l0xx_hal::pac::TIM21: stm32l0xx_hal::pwm::Instance` is not satisfied
  --> src/main.rs:27:31
   |
27 |     let pwm = pwm::Timer::new(dp.TIM21, 10_000.Hz(), &mut rcc);
   |               --------------- ^^^^^^^^ the trait `stm32l0xx_hal::pwm::Instance` is not implemented for `stm32l0xx_hal::pac::TIM21`
   |               |
   |               required by a bound introduced by this call
   |
   = help: the following other types implement trait `stm32l0xx_hal::pwm::Instance`:
             stm32l0xx_hal::pac::TIM2
             stm32l0xx_hal::pac::TIM3

Is it not intended to use TIM21 for PWM? What about the Low Power Timers?

jamwaffles commented 1 year ago

Ah, I only fixed TIM2 in the PR because that's what your code example uses. I'll check some datasheets but I think TIM21 has the same features across the L0 family so I can fix that in #224 as well.

Either way, according to the datasheet, Table 14 PA5 is only supported on TIM2 so you might want to use TIM2 instead.

NiklasC commented 1 year ago

Oh, you're right. I want to use TIM2 channel 2 on PB3, as that's the LED output on the Nucleo Board.

jamwaffles commented 1 year ago

I've just pushed some changes to the PR branch - please try commit 871919380f8ea1e65d4e5b752cb20640ecb766a2. The old commit might not work any more as I did a force push.

I'll check some datasheets but I think TIM21 has the same features across the L0 family so I can fix that in #224 as well.

Sigh, scratch that. I'm afraid I don't have the willpower to go through all the datasheets and fix the code for TIM21 😓 sorry about that.

NiklasC commented 1 year ago

It works! Thank you very much :) I also had to remove the #[cfg(feature = "stm32l0x1")] from the example, and only set the "mcu-STM32L011K4Tx" in the Cargo.toml

Sigh, scratch that. I'm afraid I don't have the willpower to go through all the datasheets and fix the code for TIM21 😓 sorry about that.

Don't worry, thank you for your help! Once I get more familiar with embedded rust I'd love to help out by implementing stuff like that myself

jamwaffles commented 1 year ago

Great to hear! Thanks for helping me test this. I'll merge #224 which will close this issue but please either reopen or make a new issue if you run into more problems.

NiklasC commented 1 year ago

Hey, just a quick question regarding the merge - I've changed the Cargo.toml back to stm32l0xx-hal = {version = "0.10.0", features = ["mcu-STM32L011K4Tx", "rt"]} as the branch is now closed, but I get error again. Do I need to specify a different version for that?

jamwaffles commented 1 year ago

I haven't released a new version yet, so you'll have to use it from Github for now. Try this:

stm32l0xx-hal = {git = "https://github.com/stm32-rs/stm32l0xx-hal.git", rev = "e6031a3798dae9658f2cd2559891f4c19aaeabe0", features = ["mcu-STM32L011K4Tx", "rt"]}

That will point to the latest master commit at time of writing.

NiklasC commented 1 year ago

Thank you!