embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.52k stars 768 forks source link

STM32F103 : PWM on TIM2 messes with Timer #1316

Closed sjames closed 1 year ago

sjames commented 1 year ago

Hi,

I'm trying to get PWM working on TIM2 with an output on PA15 on the BluePill (STM32F103). I find that the Timer::after(..) no longer works after the SimplePWM instance is constructed.

Here is the minimal code to replicate it.

Behaviour

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::{Config, gpio::{Level, Output, Speed}, pwm::simple_pwm::{PwmPin, SimplePwm}, time::hz, time::{mhz, Hertz}};
use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _};

#[embassy_executor::main]
async fn main(spawner: Spawner) {

    let mut config = Config::default();
    config.rcc.hse = Some(mhz(8));
    config.rcc.sys_ck = Some(Hertz(48_000_000));
    //config.rcc.hclk = Some(mhz(64));
    config.rcc.pclk1 = Some(Hertz(24_000_000));
    //config.rcc.pclk2 = Some(mhz(64));

    let p = embassy_stm32::init(config);
    info!("Hello World!");

    let mut led = Output::new(p.PC13, Level::High, Speed::Low);

    let pwm_pin = PwmPin::new_ch1(p.PA15);

    let mut pwm = SimplePwm::new(p.TIM2, Some(pwm_pin), None, None, None, hz(1000));

    loop {
       info!("high");
        led.set_high();
        Timer::after(Duration::from_millis(1000)).await;

        info!("low");
        led.set_low();
        Timer::after(Duration::from_millis(1000)).await;
    }

}

This is my first attempt at embassy and would really appreciate some pointers to debug further.

rand12345 commented 1 year ago

Hi,

I also had this issue using the F105 and PA15. Here's the remapping I used to ensure the timer worked fine, not 100% sure what is going on under the hood but it worked.

Insert the following below between your clocks and embassy_stm32::init(config)

unsafe {
        use embassy_stm32::pac;
        pac::RCC.apb2enr().modify(|w| w.set_afioen(true));
        pac::AFIO.mapr().modify(|w| w.set_tim2_remap(0b11)); // enable TIM2 for PA15
        pac::AFIO.mapr().modify(|w| w.set_swj_cfg(0b010)); // disables JTAG, enables PA15
    }
sjames commented 1 year ago

Thanks for the hint @rand12345 , this unfortunately does not work on the STM32F103.

sjames commented 1 year ago

On further digging, I found that the time driver uses the General purpose timers of the STM32. You can configure which timer you want to use by setting the feature.

The default is "time-driver-any" which I supposed uses TIM2 by default. You can select time-driver-tim3 to free up TIM2.

embassy-stm32 = { version = "0.1.0", git = "https://github.com/embassy-rs/embassy.git", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-tim3"]  }
sjames commented 1 year ago

The issue is resolved by selecting time-driver-tim3 feature in embassy-stm32

Dirbaio commented 1 year ago

Yes, this is a known issue. Enabling time-driver-xxx should remove the corresponding TIMxx singleton so that you can't accidentally try to use it from user code. This way this mistake would be prevented at compile time.