Embassy+STM32: Enabling pull-up on a QEI peripheral

I have a rotary encoder hooked up to a STM32G030 microcontroller. I am trying to use the Quadrature Encoder Interface (QEI) from Embassy like this:

    let ch1_pin = QeiPin::new_ch1(p.PA8);
    let ch2_pin = QeiPin::new_ch2(p.PA9);
    let mut qei = Qei::new(p.TIM1, ch1_pin, ch2_pin);

However, my channel 1 and 2 pins do not have an external pull up resistor. According to the datasheet of STM32G030, I should be able to configure the internal pull up resistor.

I have tried to look around at how this should be done with Embassy, but it seems that all avenues that I could see are not available, since each time the function that seems to be able to configure the pin to have an internal pull up register is not public.

In what way can I best enable that pull up register?

I just ran into the same issue, were you able to find a solution?

I'm not very familiar with the embassy hal, but I tried to dig a little bit: it seems to be hardcoded as floating input (i.e. Pull::None):

generally, you allowed to change the pin states when they are configured as GPIO (the Flex driver, I think),but NOT when pins are configured for alternate functions.

so, I think you can try to this very hacky workaround where you (ab-)use the Flex driver with a steal-ed pin to enable the internal pull up mode, something like:

// SAFETY: ??? 
let pa8 = unsafe { PA8::steal() };
let af_num = pa8.af_num();
let pa8 = Flex::new(pa8);
// this is not `unsafe` because peripheral IO is outside the scope of rust's memory model
// but this will introduce logic error to the application if used wrong
pa8.set_as_af_unchecked(af_num, AfType::input(Pull::Up));
// note: `Flex` will disconnect the pin on drop, so `forget()` is used
// alternative is to wrap it in `ManuallyDrop`
core::mem::forget(pa8);

disclaimer: I feel the use of steal() is technically incorrect, but it's the only "hack" I can think of with the current API.

although this hacky "solution" would probably work in practice, I suggest to make a local patch to the embassy-stm32 crate to add a new API, let's call it QeiPin::new_ch1_with_pull_mode(pin: xxx, pull: Pull)), to "properly" support your use case. (you can also make a feature request to embassy if you want).

here's what the patched code looks like

    pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
        Self::$new_chx_with_pull_mode(pin, Pull::None)
    }
    pub fn $new_chx_with_pull_mode(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, pull: Pull) -> Self {
        into_ref!(pin);
        critical_section::with(|_| {
            pin.set_low();
            pin.set_as_af(pin.af_num(), AfType::input(pull));
        });
        QeiPin {
            _pin: pin.map_into(),
            phantom: PhantomData,
        }
    }