-
Notifications
You must be signed in to change notification settings - Fork 49
Question: can I use just the complementary PWM of a timer/channel? #195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Just as context, I got it working by falling back to direct register manipulation. So if special cases are out-of-scope for the nice api, then do feel free to close the issue. (code was ai generated) let tim8 = dp.TIM8;
// Configure TIM8 from scratch using registers
unsafe {
// Enable TIM8 clock
(*stm32g4xx_hal::pac::RCC::ptr())
.apb2enr()
.modify(|_, w| w.tim8en().set_bit());
// Reset timer
tim8.cr1().write(|w| w.bits(0));
// Set prescaler (adjust frequency as needed)
tim8.psc().write(|w| {
w.psc()
.bits((rcc.clocks.sys_clk.to_Hz() / 1_000_000 - 1) as u16)
});
// Set auto-reload (period) for 1kHz
tim8.arr().write(|w| w.arr().bits(1000));
// Configure channel 2 as PWM mode 1
tim8.ccmr1_output().modify(
|_, w| {
w.oc2m()
.bits(0b110) // PWM mode 1
.oc2pe()
.set_bit()
}, // Preload enable
);
// Set output compare register (duty cycle)
tim8.ccr2().write(|w| w.ccr().bits(0)); // Start with 0% duty
// Enable complementary output and configure polarity
tim8.ccer().modify(
|_, w| {
w.cc2e()
.clear_bit() // Disable main output
// note: complementary output is enabled later - here the PWM is just configured but not yet started
// .cc2ne()
// .set_bit() // Enable complementary output
.cc2p()
.clear_bit() // Main output polarity (not used)
.cc2np()
.clear_bit()
}, // Complementary output polarity: active high (reversed)
);
// Configure break and dead-time register
tim8.bdtr().modify(
|_, w| {
w.moe()
.set_bit() // Main output enable
.aoe()
.set_bit() // Automatic output enable
.ossr()
.set_bit() // Off-state selection for Run mode
.ossi()
.set_bit() // Off-state selection for Idle mode
.dtg()
.bits(0)
}, // No dead time
);
// Generate update event to load registers
tim8.egr().write(|w| w.ug().set_bit());
// Enable counter
tim8.cr1().modify(|_, w| w.cen().set_bit());
}
unsafe {
set_tim8_ch2n_duty(&tim8, 900);
enable_tim8_ch2n_pwm(&tim8);
}
// -----------
unsafe fn set_tim8_ch2n_duty(tim8: &stm32g4xx_hal::stm32::TIM8, duty: u16) {
let duty = duty.min(1000);
tim8.ccr2().write(|w| w.ccr().bits(duty as u32));
}
/// Enables PWM output on TIM8 CH2N
unsafe fn enable_tim8_ch2n_pwm(tim8: &stm32g4xx_hal::stm32::TIM8) {
// Enable complementary output for channel 2
tim8.ccer().modify(|_, w| w.cc2ne().set_bit());
// Ensure main output is enabled in the BDTR register
// (This is a global enable for all channels)
tim8.bdtr().modify(|_, w| w.moe().set_bit());
}
/// Disables PWM output on TIM8 CH2N
unsafe fn disable_tim8_ch2n_pwm(tim8: &stm32g4xx_hal::stm32::TIM8) {
// Disable complementary output for channel 2
tim8.ccer().modify(|_, w| w.cc2ne().clear_bit());
} |
Quick thought (without having actually looked at the timer code in a while), perhaps we could implement Pin for |
Yeah that would make sense, you’d need to also be able to specify which channel to use. |
Slightly modifying examples/pwm.rs: #[macro_use]
mod utils;
+struct NoPin<CH>{
+ _ch: PhantomData<CH>
+}
+
+impl<CH> NoPin<CH> {
+ fn new() -> Self {
+ Self { _ch: PhantomData }
+ }
+}
+
+impl<CH, TIM> Pins<TIM, CH, ComplementaryDisabled> for NoPin<CH> {
+ type Channel = Pwm<TIM, CH, ComplementaryDisabled, ActiveHigh, ActiveHigh>;
+}
+
#[entry]
fn main() -> ! {
utils::logger::init();
@@ -23,9 +41,10 @@ fn main() -> ! {
let dp = stm32::Peripherals::take().expect("cannot take peripherals");
let mut rcc = dp.RCC.constrain();
let gpioa = dp.GPIOA.split(&mut rcc);
- let pin: PA8<Alternate<AF6>> = gpioa.pa8.into_alternate();
+ let pa7 = gpioa.pa7.into_alternate(); // tim1_ch1n/tim8_ch1n
- let mut pwm = dp.TIM1.pwm(pin, 100.Hz(), &mut rcc);
+ let mut pwm = dp.TIM1.pwm(NoPin::<C1>::new(), 100.Hz(), &mut rcc).into_complementary(pa7);
let _ = pwm.set_duty_cycle_percent(50);
pwm.enable(); I have not tested this on real hardware |
Thanks! That works perfectly, and together with |
I have an existing board with a
STM32G431Rx
. I need to drive a PWM at pinC11
. C11 is the complementary output of Timer8_Channel2.Additionally, I'd like to drive a PWM at pin
B6
. B6 is Timer8_Channel1.Driving only B6 should be easy:
I'm struggling to drive just C11.
It compiles when I use one of the primary pins for Tim8Ch2:
As I understand it, to use multiple channels of the same timer, I'll need to use tuple syntax. The following compiles (I haven't yet tested any of this on hardware):
Unfortunately I actually do need both possible primary pins of timer 8 channel 2 (C7 or A14) for other functionality so I can't just leave them floating and run a dummy PWM on them.
So is it possible to run just the complementary signal, without having to configure the primary pin?
The text was updated successfully, but these errors were encountered: