mciantyre / teensy4-rs

Rust support for the Teensy 4
Apache License 2.0
271 stars 31 forks source link

Different PWM example? #121

Closed brandonros closed 1 year ago

brandonros commented 2 years ago

A more common (in my opinion) PWM example is 1 pin, but the example uses https://docs.rs/imxrt-hal/latest/imxrt_hal/pwm/struct.Submodule.html which calls for 2 pins.

Converts two pins into PWM outputs. Returns a Pins type that wraps the underlying pins.

Can the same pin be pin_a and pin_b?

brandonros commented 2 years ago
   Compiling pwm v0.1.0 (/Users/brandonros/Desktop/pwm)
error[E0271]: type mismatch resolving `<Pad<imxrt106x::bases::B0, typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>, typenum::bit::B1>, typenum::bit::B0>> as teensy4_bsp::teensy4_pins::imxrt_iomuxc::pwm::Pin>::Output == teensy4_bsp::teensy4_pins::imxrt_iomuxc::pwm::B`
   --> src/main.rs:73:10
    |
73  |         .outputs(
    |          ^^^^^^^ expected enum `teensy4_bsp::teensy4_pins::imxrt_iomuxc::pwm::B`, found enum `teensy4_bsp::teensy4_pins::imxrt_iomuxc::pwm::A`
    |
note: required by a bound in `Submodule::<M, typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>>::outputs`
   --> /Users/brandonros/.cargo/registry/src/github.com-1ecc6299db9ec823/imxrt-hal-0.4.5/src/pwm.rs:224:1
    |
224 | / submodule_outputs!(
225 | |     U2, SMCTRL22, SMCTRL2, SMOCTRL2, SMDTCNT02, SMINIT2, SMVAL02, SMVAL12, SMVAL22, SMVAL32,
226 | |     SMVAL42, SMVAL52
227 | | );
    | |_^ required by this bound in `Submodule::<M, typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>>::outputs`
    = note: this error originates in the macro `submodule_outputs` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0271`.

No, they can not. How do to 1-pin PWM?

brandonros commented 2 years ago

On top of no support for single-pin PWM, I can't seem to get anything other than pwm2.submodule2 to work? (which doesn't correctly receive PWM from Channel1 of FLYSKY FS-IA6B in PWM mode @ 50hz to pin 6 of Teensy4)

   Compiling pwm v0.1.0 (/Users/brandonros/Desktop/pwm)
error[E0271]: type mismatch resolving `<Pad<imxrt106x::bases::B0, typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>, typenum::bit::B1>, typenum::bit::B0>> as teensy4_bsp::teensy4_pins::imxrt_iomuxc::pwm::Pin>::Module == typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>`
   --> src/main.rs:61:10
    |
61  |         .outputs(
    |          ^^^^^^^ expected struct `typenum::uint::UTerm`, found struct `typenum::uint::UInt`
    |
    = note: expected struct `typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>`
               found struct `typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>`
note: required by a bound in `Submodule::<M, typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>>::outputs`
   --> /Users/brandonros/.cargo/registry/src/github.com-1ecc6299db9ec823/imxrt-hal-0.4.5/src/pwm.rs:220:1
    |
220 | / submodule_outputs!(
221 | |     U1, SMCTRL21, SMCTRL1, SMOCTRL1, SMDTCNT01, SMINIT1, SMVAL01, SMVAL11, SMVAL21, SMVAL31,
222 | |     SMVAL41, SMVAL51
223 | | );
    | |_^ required by this bound in `Submodule::<M, typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>>::outputs`
    = note: this error originates in the macro `submodule_outputs` (in Nightly builds, run with -Z macro-backtrace for more info)
#![no_std]
#![no_main]

use teensy4_bsp as bsp;
use teensy4_panic as _;
use embedded_hal::Pwm;

mod logging;

mod systick {
    use cortex_m::{
        delay::Delay,
        peripheral::{syst::SystClkSource, SYST},
    };

    /// Re-export of the SYSTICK type for these examples.
    pub type SysTick = Delay;

    /// Create a blocking delay over SYSTICK.
    pub fn new(syst: SYST) -> SysTick {
        Delay::with_source(syst, teensy4_bsp::EXT_SYSTICK_HZ, SystClkSource::External)
    }
}

/// Helper function to represent a duty cycle as a percent
fn percent(duty: u16) -> f32 {
    ((duty as f32) * 100.0f32) / (core::u16::MAX as f32)
}

#[cortex_m_rt::entry]
fn main() -> ! {
    let mut periphs = bsp::Peripherals::take().unwrap();

    // Reduce the number of pins to those specific
    // to the Teensy 4.0.
    let pins = bsp::pins::t40::from_pads(periphs.iomuxc);
    // Prepare the LED, and turn it on!
    // (If it never turns off, something
    // bad happened.)
    let mut led = bsp::configure_led(pins.p13);
    led.set();

    // Prepare the ARM clock to run at ARM_HZ.
    let (_, ipg_hz) = periphs.ccm.pll1.set_arm_clock(
        bsp::hal::ccm::PLL1::ARM_HZ,
        &mut periphs.ccm.handle,
        &mut periphs.dcdc,
    );

    // See the `logging` module docs for more info.
    assert!(logging::init().is_ok());

    let mut systick = systick::new(cortex_m::Peripherals::take().unwrap().SYST);

    // Enable the clocks for the PWM1 module
    let mut pwm1 = periphs.pwm1.clock(&mut periphs.ccm.handle);

    // Get the outputs from the PWM2 module, submodule 2.
    let mut sm1 = pwm1
        .sm1
        .outputs(
            &mut pwm1.handle,
            pins.p6,
            pins.p9,
            bsp::hal::pwm::Timing {
                clock_select: bsp::hal::ccm::pwm::ClockSelect::IPG(ipg_hz),
                prescalar: bsp::hal::ccm::pwm::Prescalar::PRSC_6,
                switching_period: core::time::Duration::from_millis(2),
            },
        )
        .unwrap();

    // Two different duty cycles that will be swapped to show
    // different duty cycles on the same PWM pins
    let mut ctrl = sm1.control(&mut pwm1.handle);

    // Enable PWM channels
    ctrl.enable(bsp::hal::pwm::Channel::A);
    ctrl.enable(bsp::hal::pwm::Channel::B);

    // Start
    loop {
        // get duty cycle
        log::info!("Current duty cycle A: {}", ctrl.get_duty(bsp::hal::pwm::Channel::A));
        log::info!("Current duty cycle B: {}", ctrl.get_duty(bsp::hal::pwm::Channel::B));
        // toggle LED
        led.toggle();
        // log
        log::info!("Hello world");
        // sleep?
        systick.delay_ms(100);
    }
}
mciantyre commented 2 years ago

Can the same pin be pin_a and pin_b? [...] How do to 1-pin PWM?

Pin A and pin B need to be distinct. The HAL doesn't support 1-pin PWM right now. We could change that.


I can't seem to get anything other than pwm2.submodule2 to work?

Thanks for the example code. It looks like you're configuring PWM 1 with pins 6 and 9. Pin 6 and pin 9 only support PWM 2 functions (in the "Alt2" column). If you must use the PWM 1 peripheral, try using pins 7 and 8 with the third submodule, sm3. On the other hand, if you must use pins 6 and 9, you must use PWM 2.

Here's a snippet to demonstrate PWM 1 with pins 7 / 8. Comments show what should change from the above example code.

    let mut pwm1 = periphs.pwm1.clock(&mut periphs.ccm.handle);

    // Previously called 'sm1'.
    let mut sm3 = pwm1
        .sm3 // sm1
        .outputs(
            &mut pwm1.handle,
            pins.p8, // pins.p6,
            pins.p7, // pins.p9,
            bsp::hal::pwm::Timing {
                clock_select: bsp::hal::ccm::pwm::ClockSelect::IPG(ipg_hz),
                prescalar: bsp::hal::ccm::pwm::Prescalar::PRSC_6,
                switching_period: core::time::Duration::from_millis(2),
            },
        )
        .unwrap();

which doesn't correctly receive PWM from Channel1 of FLYSKY FS-IA6B

Emphasis on receive. Are you looking for a PWM input? Today's PWM pins are output-only.

brandonros commented 2 years ago

That’s exactly it! I needed PWM input.

PWM input on your roadmap? Would it be difficult?

stappersg commented 2 years ago

That’s exactly it! I needed PWM input.

PWM input on your roadmap? Would it be difficult?

My first impression was that screams for more research.

So today I did learn that there is indeed PWM input. #TIL

mciantyre commented 2 years ago

I personally don't plan to add PWM inputs into today's HAL. But I'm happy to mentor you, or anyone who wants to contribute the feature.

PWM inputs are described by "E-Capture" mentions in the i.MX RT reference manual (RM). These mentions are scattered throughout the eFlexPWM section (chapter 55 in the 1060 RM). I have a basic understanding of how the feature works, but not enough to know the challenges.

Open an issue in the HAL repo to signal interest.

mciantyre commented 1 year ago

The latest teensy4-bsp release (0.4) uses imxrt-hal 0.5, which includes support for single-pin PWM output. There's still no support for PWM inputs. Check out the imxrt-hal repo for board configurations and hardware examples.

Now that the HAL supports more i.MX RT chips, it's more important to direct driver discussions there, so that all HAL users can benefit.

brandonros commented 1 year ago

Would you suggest I open a tracking ticket in https://github.com/imxrt-rs/imxrt-hal for PWM inputs?

mciantyre commented 1 year ago

Yes please, especially if you'll use it or want to contribute it. PWM inputs are still off my radar, but if you and I don't get around to it, someone else might.

If you have time to explain your use-case and setup in that issue, we could discuss other approaches. Maybe there's another hardware timer that could do the trick, or could be easier to implement and use. If you've already done this research, it'll be helpful to share those thoughts too.

brandonros commented 1 year ago

What’s your opinion on if this use case is worthwhile: Flysky FS-i6X 6-10(Default 6)CH 2.4GHz AFHDS RC Transmitter w/ FS-iA6B Receiver https://a.co/d/5bsRb5J

I think I was just reading the joystick transmissions from this playing around.