Raul Alimbekov
Raul Alimbekov

Reputation: 11

Rust Embassy SimplePwm waveform functions don't work as intended

Duty-cycle behaves strangely at high frequencies. While at 20 KHz the error is barely noticeable PWM of 20 KHz At 6 MHz it becomes obvious PWM of 6 MHz

I use the STM32F11ce6 microcontroller.

Clock settings:

    {
        use embassy_stm32::rcc::*;
        use embassy_stm32::time::*;
        device_config.enable_debug_during_sleep = true;
        device_config.rcc.hse = Some(Hse {
            freq: mhz(25),
            mode: HseMode::Oscillator,
        });
        device_config.rcc.hsi = true;
        device_config.rcc.pll_src = PllSource::HSE;
        device_config.rcc.pll = Some(Pll {
            prediv: PllPreDiv::DIV12,
            mul: PllMul::MUL96,
            divp: Some(PllPDiv::DIV2),
            divq: None,
            divr: None,
        });
        device_config.rcc.sys = Sysclk::PLL1_P;
        device_config.rcc.ahb_pre = AHBPrescaler::DIV1;
        device_config.rcc.apb1_pre = APBPrescaler::DIV2;
        device_config.rcc.apb2_pre = APBPrescaler::DIV1;
    }

Initialization of SimplePwm:

let mut ws2812_pwm = SimplePwm::new(
        dp.TIM3,
        Some(PwmPin::new_ch1(dp.PA6, OutputType::PushPull)),
        None,
        None,
        None,
        khz(6000), // data rate 
        CountingMode::EdgeAlignedUp,
    );

Generating a PWM signal sequence:

let waveform = [max_duty / 3 * 2, max_duty / 3, max_duty / 3 * 2, 0];
loop {
        ws2812_pwm
            .waveform_up(&mut dp.DMA1_CH2, pwm_channel, &waveform)
            .await;
    }

Upvotes: 0

Views: 132

Answers (1)

bored_beaver
bored_beaver

Reputation: 33

From your description and the waveforms provided, it seems that the duty-cycle behavior of your PWM signals is inconsistent at higher frequencies. Based on the images provided in the links from your question(As mentioned in the comments, kindly add description of images instead of leaving the filler text),

  • From image 1, The first waveform(@30 kHz), has the duty cycle which is approximately correct, with only minor discrepancies.
  • In image 2, the second waveform(@6 MHz), there's a significant discrepancy in the duty cycle and overall shape of the PWM

Based on my experience, this situation can be caused by various reasons. Main culprit is likely to be the timer resolution and frequency limitations of your system. At high frequencies like 6 MHz, the timer resolution becomes critical. The APB clock must be high enough to achieve the desired timer frequency accurately. It looks like your APB1 frequency is divided by 2 (APB1Prescaler::DIV2), which could be limiting the timer frequency that can be achieved. Considering your PLL setup and prescalers, the effective clock may not be sufficient to produce a stable 6 MHz PWM.

DMA timing issue will also affect your PWM signal. The STM32F1 family is known to face limitations when using DMA to update the PWM registers at high frequencies. The DMA request can not be able to keep up with the timer at 6 MHz, causing distortions in the waveform. Since the DMA controller can have a latency that increases with higher frequencies, there can be gaps or incorrect timing when updating the registers, resulting in an incorrect duty cycle. Bus arbitration and bus contention with the CPU or other peripherals also introduce delays. This will cause the PWM signal to become distorted, as the timing of the updates is no longer precise. For more details, refer to the chapter five of the STM32 documentation, which explains the DMA latency.

Regarding the clock configuration, your clock configuration sets the PLL with HSE (25 MHz) and PLL multiplication factor of 96. After various divisions (DIV12 for the HSE and DIV2 for the PLL), make sure that the final clock settings match the expected output for the system and peripheral clock speeds. Check if the resulting AHB and APB clock frequencies provide adequate precision for high-frequency PWM.

Here is the breakdown of your configuration:

  • HSE = 25 MHz
  • Pll Prediv = DIV12 -> HSE/12 = 25 MHz / 12 ≈ 2.08 MHz
  • Pll Mul = MUL96 -> 2.08 MHz * 96 ≈ 200 MHz
  • PllP Div = DIV2 -> 200 MHz / 2 ≈ 100 MHz
  • The sysclk becomes 100 MHz

After this, you apply:

  • AHBPrescaler::DIV1 -> AHB remains at 100 MHz
  • APB1Prescaler::DIV2 -> APB1 runs at 50 MHz
  • APB2Prescaler::DIV1 -> APB2 runs at 100 MHz

The TIM3 timer in APB1 runs at 2 * 50 MHz = 100 MHz (due to the timer clock doubling effect when APB prescaler is not 1). At 6 MHz, this timer can struggle to produce a precise waveform.

As for possible solutions, you can try increasing the APB1 clock frequency. Change the APB1 prescaler to DIV1 if possible. This way, the timers in APB1 will operate at the maximum clock speed available, which should help maintain precision at high frequencies.

device_config.rcc.apb1_pre = APBPrescaler::DIV1; // Set APB1 to full speed

Regarding the PWM frequency, If 6 MHz is not a hard requirement, lowering it will help reduce the stress on the DMA and improve stability. Consider lowering the PWM frequency to something more manageable by the DMA and the timer hardware if possible.

Ensure that DMA is prioritized appropriately. Bridge synchronization between the AHB and APB buses further increases the delay when accessing APB peripherals. If you are facing issues with DMA latency, adjusting the priority of the DMA channel or using burst mode will help.

You can also try manually configuring timer parameters. Configuring the timer registers directly to have finer control over the timing parameters, prescalers, and auto-reload values. This can help achieve more accurate high-frequency PWM signals.

In summary, the discrepancies at higher frequencies are more likely due to clock settings, DMA timing, and hardware limitations. Increasing the APB1 clock to full speed and optimizing DMA priority usually improves performance. Consider lowering the PWM frequency(if 6 Mhz is not a hard requirement) or using direct timer register configurations for better control.

If your design permits, another possible solution is to consider using a dedicated PWM generator(an external chip like LT8500) rather than relying on the microcontroller, especially if the current timer/DMA combination struggles at high frequencies.

Upvotes: 0

Related Questions