zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.52k stars 6.44k forks source link

lptim pwm support for stm32 #57316

Open turbogubbu opened 1 year ago

turbogubbu commented 1 year ago

Is your feature request related to a problem? Please describe. Currently it is not possible to use the lptimers of a stm32 as a pwm source.

Describe the solution you'd like Using lptimers the same way as normal timers for pwm generation.

Describe alternatives you've considered Using the HAL.

Additional context The devictree binding "st,stm32-lptim" has the st,prescaler in it's property blocklist. I think this value is required by the pwm interface. Furthermore the lptim2 is not defined in the devicetree of the stm32u5xx platform.

github-actions[bot] commented 1 year ago

Hi @turbogubbu! We appreciate you submitting your first issue for our open-source project. 🌟

Even though I'm a bot, I can assure you that the whole community is genuinely grateful for your time and effort. 🤖💙

FRASTM commented 1 year ago

When the LPTIM node of the stm32 device is defined with compatible = "st,stm32-timers"; instead of "st,stm32-lptim, then the LPTIM instance is able to use the PWM mode, like any timer. The lptim is no more considered as a kernel timer running in low power stop mode. In case of stm32u5 serie, a static-prescaler exists and must be added in the timer bindings. The stm32 device DSTI must also have the lptimx_chy_pin definition.

See the branch https://github.com/FRASTM/zephyr/tree/lptim_pwm for the stm32u585 disco kit, also add the

            /omit-if-no-ref/ lptim3_ch1_pb0: lptim3_ch1_pb0 {
                pinmux = <STM32_PINMUX('B', 0, AF4)>;
            };

            /omit-if-no-ref/ lptim3_ch2_pb1: lptim3_ch2_pb1 {
                pinmux = <STM32_PINMUX('B', 1, AF4)>;
            };

to the ../modules/hal/stm32/dts/st/u5/stm32u585aiixq-pinctrl.dtsi>

turbogubbu commented 1 year ago

Hey, thanks for the quick response. I tried to implement the steps you mentioned for the lptim2 of the stm32u585ziq. Unfortunately it does not work. I added these lines to the devicetree:

lptim2: timers@40009400 {
      compatible = "st,stm32-timers";
      #address-cells = <1>;
      #size-cells = <0>;
      reg = <0x40009400 0x400>;
      clocks = <&rcc STM32_CLOCK_BUS_APB1_2 0x00000200>;
      resets = <&rctl STM32_RESET(APB1H, 5U)>;
      interrupts = <68 0>;
      interrupt-names = "global";

      st,static-prescaler;

      pwm {
        compatible = "st,stm32-pwm";
        status = "disabled";
        #pwm-cells = <3>;
      };

      status = "disabled";
};

&lptim2 {
  status = "okay";
  clocks = <&rcc STM32_CLOCK_BUS_APB1_2 0x00000200>,
             <&rcc STM32_SRC_LSE LPTIM2_SEL(3)>;

  st,prescaler = <255>;

  lptim2_pwms: pwm {
    status = "okay";
    pinctrl-0 = <&lptim2_ch1_pa8>;
    pinctrl-names = "default";
  };
}

&pinctrl {
  /omit-if-no-ref/ lptim2_ch1_pa8: lptim2_ch1_pa8 {
    pinmux = <STM32_PINMUX('A', 8, AF4)>;
  };
};

I also added the st,static-prescaler to the timer binding.

Is there something wrong in the devicetree?

I implemented the lptim based on the branch you provided. One more question: Why are two clocks provided for the lptim?

turbogubbu commented 1 year ago

ping @FRASTM

Regarding the device tree lptim2 node, the bit 5 is of the APB1_ register if for the LPTIM2 instance:

clocks = <&rcc STM32_CLOCK_BUS_APB1_2 0x00000020>;

The second clock is for setting another clock source than the default one for the LPTIM2 : Checking the RCC peripheral independent clock config register (CCPIR1), you can select the LPTIM2 clock source with bit[19:18], typically LSE when <&rcc STM32_SRC_LSE LPTIM2_SEL(3)>

turbogubbu commented 1 year ago

I have tried it again with bit 5 in the in the clock configuration, sadly it still does not work using lptim2. I used the pwm_dt_spec api in the program itself. No errors/warning when compiling/running the program. Going to move on using the HAL, but thanks for the help (:

FRASTM commented 1 year ago

@turbogubbu , do you have a better result with LPtimer 3. Did you check the pinning for LPTIMx channel PWM pins on your hardware

I agree that the LPTIM 2 has clocks = <&rcc STM32_CLOCK_BUS_APB1_2 0x00000020

mkaranki commented 1 year ago

When the LPTIM node of the stm32 device is defined with compatible = "st,stm32-timers"; instead of "st,stm32-lptim, then the LPTIM instance is able to use the PWM mode, like any timer.

I also tried with both lptim2 and lptim3. No success. LPTIM peripherals do not even get enabled on the MCU.

I don't see how pwm_stm32.c driver could support LPTIMs only with device tree additions. For instance, the device initialization here: https://github.com/zephyrproject-rtos/zephyr/blob/bbec614b78b3a083fc7a559ae3ac00a5af24973d/drivers/pwm/pwm_stm32.c#L696 calls HAL function LL_TIM_Init, which should not pass even assert_param(IS_TIM_INSTANCE(TIMx)); assert if the assertions would be enabled (well, they aren't). LL_LPTIM_Init should be used instead to initialize the peripheral, right?

Also the layout of LPTIM_TypeDef and TIM_TypeDef and thus the register layouts are also very different:

typedef struct
{
  __IO uint32_t ISR;            /*!< LPTIM Interrupt and Status register,    Address offset: 0x00 */
  __IO uint32_t ICR;            /*!< LPTIM Interrupt Clear register,         Address offset: 0x04 */
  __IO uint32_t DIER;           /*!< LPTIM Interrupt Enable register,        Address offset: 0x08 */
  __IO uint32_t CFGR;           /*!< LPTIM Configuration register,           Address offset: 0x0C */
  __IO uint32_t CR;             /*!< LPTIM Control register,                 Address offset: 0x10 */
  __IO uint32_t CCR1;           /*!< LPTIM Capture/Compare register 1,       Address offset: 0x14 */
  __IO uint32_t ARR;            /*!< LPTIM Autoreload register,              Address offset: 0x18 */
  __IO uint32_t CNT;            /*!< LPTIM Counter register,                 Address offset: 0x1C */
  __IO uint32_t RESERVED0;      /*!< Reserved,                               Address offset: 0x20 */
  __IO uint32_t CFGR2;          /*!< LPTIM Configuration register 2,         Address offset: 0x24 */
  __IO uint32_t RCR;            /*!< LPTIM Repetition register,              Address offset: 0x28 */
  __IO uint32_t CCMR1;          /*!< LPTIM Capture/Compare mode register,    Address offset: 0x2C */
  __IO uint32_t RESERVED1;      /*!< Reserved,                               Address offset: 0x30 */
  __IO uint32_t CCR2;           /*!< LPTIM Capture/Compare register 2,       Address offset: 0x34 */
} LPTIM_TypeDef;

vs.

typedef struct
{
  __IO uint32_t CR1;         /*!< TIM control register 1,                   Address offset: 0x00 */
  __IO uint32_t CR2;         /*!< TIM control register 2,                   Address offset: 0x04 */
  __IO uint32_t SMCR;        /*!< TIM slave mode control register,          Address offset: 0x08 */
  __IO uint32_t DIER;        /*!< TIM DMA/interrupt enable register,        Address offset: 0x0C */
  __IO uint32_t SR;          /*!< TIM status register,                      Address offset: 0x10 */
  __IO uint32_t EGR;         /*!< TIM event generation register,            Address offset: 0x14 */
  __IO uint32_t CCMR1;       /*!< TIM capture/compare mode register 1,      Address offset: 0x18 */
  __IO uint32_t CCMR2;       /*!< TIM capture/compare mode register 2,      Address offset: 0x1C */
  __IO uint32_t CCER;        /*!< TIM capture/compare enable register,      Address offset: 0x20 */
  __IO uint32_t CNT;         /*!< TIM counter register,                     Address offset: 0x24 */
  __IO uint32_t PSC;         /*!< TIM prescaler,                            Address offset: 0x28 */
  __IO uint32_t ARR;         /*!< TIM auto-reload register,                 Address offset: 0x2C */
  __IO uint32_t RCR;         /*!< TIM repetition counter register,          Address offset: 0x30 */
  __IO uint32_t CCR1;        /*!< TIM capture/compare register 1,           Address offset: 0x34 */
  __IO uint32_t CCR2;        /*!< TIM capture/compare register 2,           Address offset: 0x38 */
  __IO uint32_t CCR3;        /*!< TIM capture/compare register 3,           Address offset: 0x3C */
  __IO uint32_t CCR4;        /*!< TIM capture/compare register 4,           Address offset: 0x40 */
  __IO uint32_t BDTR;        /*!< TIM break and dead-time register,         Address offset: 0x44 */
  __IO uint32_t CCR5;        /*!< TIM capture/compare register 5,           Address offset: 0x48 */
  __IO uint32_t CCR6;        /*!< TIM capture/compare register 6,           Address offset: 0x4C */
  __IO uint32_t CCMR3;       /*!< TIM capture/compare mode register 3,      Address offset: 0x50 */
  __IO uint32_t DTR2;        /*!< TIM deadtime register 2,                  Address offset: 0x54 */
  __IO uint32_t ECR;         /*!< TIM encoder control register,             Address offset: 0x58 */
  __IO uint32_t TISEL;       /*!< TIM Input Selection register,             Address offset: 0x5C */
  __IO uint32_t AF1;         /*!< TIM alternate function option register 1, Address offset: 0x60 */
  __IO uint32_t AF2;         /*!< TIM alternate function option register 2, Address offset: 0x64 */
  __IO uint32_t OR1 ;        /*!< TIM option register,                      Address offset: 0x68 */
       uint32_t RESERVED0[220];/*!< Reserved,                               Address offset: 0x6C */
  __IO uint32_t DCR;         /*!< TIM DMA control register,                 Address offset: 0x3DC */
  __IO uint32_t DMAR;        /*!< TIM DMA address for full transfer,        Address offset: 0x3E0 */
} TIM_TypeDef;

... so configuring LPTIM using TIM_TypeDef is quite expected to not to work.

wysman commented 1 year ago

Hi,

We have a the issue / request for stm32l4 ! Does someone have works on a patch for the stm32 pwm timer ?

FRASTM commented 1 year ago

For the stm32u585 the Datasheet gives : &pinctrl { /omit-if-no-ref/ lptim2_ch1_pa8: lptim2_ch1_pa8 { pinmux = <STM32_PINMUX('A', 8, AF14)>; }; };

FRASTM commented 1 year ago

Hi,

We have a the issue / request for stm32l4 ! Does someone have works on a patch for the stm32 pwm timer ?

Not yet as said, The stm32_pwm driver has to be modified to address the LL_LPTIM instead of LL_TIM Detecting a LPTIM instead of TIM through the device tree is not immediate. With such a of LPTIM "pwm-timer-counter" compatible, the stm32_pwm driver could translate and route LL function / instance to the LPTIM else this is a new _stm32_lptimpwm driver to implement. Note: lptim are 16-bit counters.

wysman commented 1 year ago

@FRASTM Have you any preferred solution (new driver vs hack actual) ?