How to organize embedded Rust modules

I started an embedded Rust project using the stm32f1xx_hal. I looked at the many examples. Most of these examples are very simple and realized in one module or file.
I wanted to implement a fan control with a PWM output and a frequency counter to check the fan speed. So I started with some code like this in my main.rs:

// Some peripheral unwrapping - cut out
let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);

let pins = (
    gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl),
    gpioa.pa1.into_alternate_push_pull(&mut gpioa.crl),
);
let mut pwm_out = Timer::tim2(dp.TIM2, &clocks, &mut rcc.apb1)
    .pwm::<Tim2NoRemap, _, _, _>(pins, &mut afio.mapr, 25.khz()).0;

let pwm_in_pins= (gpioa.pa6, gpioa.pa7);
let t3 = Timer::tim3(dp.TIM3, &clocks, &mut rcc.apb1);
let pwm_in = t3.pwm_input(pwm_in_pins, &mut afio.mapr, &mut dbg,
                Configuration::Frequency(10.khz()));

Now I can use the pwm_in and the pwn_out to control the fan.

But I don't want to do this in the main file. I would like to have a module with functions like fan.set_power() and fan.get_speed(). There are some issues where I can't get further.

  • The timer and the pwm functions have so many dependencies. The "new" function becomes horrible with a lots of parameters. There are: clocks, rcc, afio, gpio, ...
  • If I create a new struct in a new file which contains the pwm_out and the pwm_in I need the type and the type arguments. For pwm_in my IntelliJ helps me and gives the type: PwmInput<TIM3, Tim3NoRemap, <PA6<Input<Floating_>>, PA7<Input<Floating_>>)> :exploding_head: For pwm_out IntelliJ gives up. It will not show the type.
  • There are so many macros in the hal to create all the timer and pwm functions. It's hard to understand. My feeling is that I have to understand the implementation details before I can use it.

I don't know how to modularize the source code. Which data should be passed to a module to ?

Is there a sample project which shows some of these organization.

This can indeed be a problem and perhaps something we should document better.

I have a project here: https://github.com/TheZoq2/weather/tree/master/stmhardware/src which might work an example.

In general, I prefer to have my peripheral setup (configuring pins, timers spi etc.) stuff in main, then move those things into structs. As you said, the types become quite horrible if doing that, and to fix this I think there are at least 2 options.

  1. Use generics: Make the Pwm struct take generic parameters for the pins and peripherals. The embedded_hal traits come in handy here as they are implemented by most of the stm32f1xx_hal stuff. Additionally, this makes your code portable as most things in the embedded ecosystem implement those traits.
  2. Use newtypes. This is less portable but works well if there are no traits for you to use with the genrics. You can see that done in the dhtxx module in the linked repo
1 Like

For an example on how to use generics, you can have a look on my simple midi driver, using uart:

Check both src and examples folders.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.