Closed dkotTech closed 1 month ago
Is it possible that the issue is with measuring the duty cycle rather than generating the signal? In case it's useful, I have a working example of PWM DMA burst tested on a STM32H747I-DISCO. I wasn't able to get examples/timer.rs
to compile.
#![no_main]
#![no_std]
use cortex_m::delay::Delay;
use hal::{
clocks::Clocks,
dma::{self, Dma, DmaChannel, DmaInput, DmaPeriph},
gpio::{Pin, PinMode, Port},
pac,
timer::{OutputCompare, TimChannel, Timer, TimerConfig, TimerInterrupt},
};
use defmt_rtt as _;
use panic_probe as _;
// Calculate the DMA offset by taking the Adddress Offset for
// the associated CCR channel in the RM register table, and dividing by 4.
const TIMX_CCR1_OFFSET: u8 = 0x34;
// 10 duty cycles from 10% to 80% assuming max duty cycle is 5332
const DUTY_CYCLES: [u16; 8] = [533, 1066, 1600, 2133, 2666, 3199, 3732, 4266];
#[cortex_m_rt::entry]
fn main() -> ! {
let cp = cortex_m::Peripherals::take().unwrap();
let dp = pac::Peripherals::take().unwrap();
let clock_cfg = Clocks::default();
clock_cfg.setup().unwrap();
let mut delay = Delay::new(cp.SYST, clock_cfg.systick());
// Generate a PWM signal with 50% duty cycle and 2400 Hz frequency on PC6 (TIM3_CH1)
// TIM3_CH1 is connected to PC6 as alternate function 2
let _pwm_pin = Pin::new(Port::C, 6, PinMode::Alt(2));
let mut pwm_timer = Timer::new_tim3(
dp.TIM3,
37_500.,
TimerConfig::default(),
&clock_cfg,
);
pwm_timer.enable_pwm_output(TimChannel::C1, OutputCompare::Pwm1, 0.5);
let _dma = Dma::new(dp.DMA1);
dma::mux(DmaPeriph::Dma1, DmaChannel::C1, DmaInput::Tim3Up);
pwm_timer.enable_interrupt(TimerInterrupt::UpdateDma);
loop {
delay.delay_ms(2000);
pwm_timer.enable();
defmt::info!("Generating 50% duty cycle PWM signal");
delay.delay_ms(5);
// Set duty cycle using DMA
unsafe {
pwm_timer.write_dma_burst(
&DUTY_CYCLES,
TIMX_CCR1_OFFSET / 4,
1,
DmaChannel::C1,
dma::ChannelCfg {
circular: dma::Circular::Enabled,
..Default::default()
},
false,
DmaPeriph::Dma1,
);
}
defmt::info!("Generating 10% to 80% duty cycle PWM signal");
}
}
Hey! I don't know what's going on. That's possible. Timer shenanigans on STM32 are tricky! LMK if you come up with anything; would love to get this fixed.
@akhilles Hello, I tried your example, but it's not working for me either. Some parts of the code differ between the G4 and H7 chips. Anyway, I tried to set up the project in CubeIDE.
The HAL_TIM_DMABurst_WriteStart
function works strangely—some values are transferred from DMA to the timer registers, but some are not. After several attempts, I still can't find the correct way to use this function.
The HAL_TIM_PWM_Start_DMA
function works as expected. I can set a buffer for DMA, and DMA sends this buffer to CCRx in a circular manner.
LMK if you come up with anything. I've used the burst DMA on G4 only; haven't tried on other MCUs. (And only for one specific use case that may not generalize) I suspect the fix is something simple, but I'm not sure what it is.
What I would do: See if you can get it working using the Reference manual and PAC directly, then post the results here, and/or compare to what the HAL functions are doing.
Hi! After a few hours of searching, I finally managed to achieve what I wanted, and even more. Now, I know two ways to solve the problem.
I’ll clean up the code and post it later.
So I tried to figure out what was wrong with the HAL library code but couldn't find anything suspicious. Everything looks correct, even the parts I had doubts about turned out fine.
In the end, just one line placed in the right spot fixed everything:
dma::enable_mux1();
I’m not sure why the mux doesn’t need to be enabled for the H7 chip; it seems to work differently. Also, note that the mux must be enabled before specifying the routing.
Nice find! It might be a good idea to enable the DMA mux RCC clock for G4 targets automatically when calling dma::mux
? It's just 1 register write, and setting up dma muxing if fairly infrequent.
Nice find! I agree that it would be nice to enable it on G4 in dma::mux
. Done; in the latest commit.
I also wish the docs could be easily hosted/built for all targets: The enable_mux
fn is not in the published docs because it's only on G4.
Hi, thanks for the help.
If anyone has encountered a problem working with DMA, you can check out example with a deep dive into registers. https://github.com/dkotTech/stm32g431-exp/blob/main/dma_pwm_pac/src/main.rs
Hi, I am working on an example that demonstrates the usage of a DRV8844 motor driver with PWM and DMA.
However, something is not working as expected. The duty cycle does not change after a DMA write request.
full src link
code:
cargo:
output: