Dushara
Dushara

Reputation: 668

Re-configuring peripheral as GPIO at runtime in embedded rust

I'm working on a board using the stm32h7xx hal and need to solve a particularly hairy problem. The board comprises of several components that are controlled via an SPI peripheral. However, there is also another device that needs to be driven by a GPIO connected to the MOSI line of the SPI peripheral.

I've done the basic skeleton of the driver in question. The sections marked TODO are the bits I'm struggling with.

use cortex_m::prelude::_embedded_hal_blocking_spi_Write;
use stm32h7xx_hal::{spi::Enabled, gpio::{Output, Pin, PushPull}, pac::SPI3, spi::Spi};

pub struct ShiftRegSelector {
    up_ciod_sh_reg_sel0: Pin<'D', 10, Output<PushPull>>,
    up_ciod_sh_reg_sel1: Pin<'D', 11, Output<PushPull>>,
    up_ciod_sh_reg_n_latch: Pin<'E', 10, Output<PushPull>>,
    up_ciod_sh_reg_n_rst: Pin<'E', 15, Output<PushPull>>,
    spi: Spi<SPI3, Enabled>,
}

impl ShiftRegSelector {
    pub fn new(
        up_ciod_sh_reg_sel0: Pin<'D', 10, Output<PushPull>>,
        up_ciod_sh_reg_sel1: Pin<'D', 11, Output<PushPull>>,
        up_ciod_sh_reg_n_latch: Pin<'E', 10, Output<PushPull>>,
        up_ciod_sh_reg_n_rst: Pin<'E', 15, Output<PushPull>>,
        spi: Spi<SPI3, Enabled>,
    ) -> Self {
        Self {
            up_ciod_sh_reg_sel0,
            up_ciod_sh_reg_sel1,
            up_ciod_sh_reg_n_latch,
            up_ciod_sh_reg_n_rst,
            spi,
        }
    }

    pub fn psu_13v8_ctrl(&mut self, _enable: bool) {

        // TODO: Reconfigure spi.mosi as GPIO output
        self.reset_mux();

        if _enable {
            // TODO: Drive GPIO line high"
        } else {
            // TODO: Drive GPIO line low
        }
        
        self.latch_mux();
        self.reset_mux();

        // TODO: Restore SPI configuration
    }

    fn latch_mux(&mut self) {
        self.up_ciod_sh_reg_sel0.set_high();
        self.up_ciod_sh_reg_sel1.set_high();
        self.up_ciod_sh_reg_n_latch.set_low();
    }
    
    fn reset_mux(&mut self) {
        self.up_ciod_sh_reg_n_rst.set_high();
        self.up_ciod_sh_reg_n_latch.set_high();
    }
    
    pub fn something(&mut self) {
        self.spi.write(&[0x11u8, 0x22, 0x33]).unwrap();
    }
}

How should the IO/peripheral re-configration be done in psu_13v8_ctrl?

Upvotes: 1

Views: 63

Answers (1)

joosts
joosts

Reputation: 164

In your question you don't mention the exact microcontroller you are using, so I am assuming you are using a STM32H742, the answer might depend on the exact microcontroller you are using.

Also I hope you understand that when you reconfigure the MOSI-pin to be used as a GPIO port, during that time you won't be able to control the external SPI component on the board (as the Master-Out Slave-In-line is required to send data to the external component). In order to prevent malfunctioning of the external component, you should pull up the chip select (SS or CS) line to make sure the external component doesn't react to your MOSI line.

When I need to reconfigure a microcontroller peripheral, I usually look up the specification of the microcontroller. For STM32's the manufacturer calls these specification Reference Manuals, so I with my search engine I found Reference manual STM32H742, STM32H743/753 and STM32H750 Value line advanced Arm(r)-based 32-bit MCUs - RM0433.

In the reference manual I found that STM32H7's have both a QUADSPI (Ch. 23) and regular SPI (Ch. 50) peripheral. I am assuming you want a pin connected to the regular SPI peripheral. Page 2160 of the reference manual gives a block description of the SPI device.

According to your code you want the use SPI device 3. According to page 2173 of the reference manual, control of MISO, MOSI, SCLK and SS pins is enabled and disabled using the GPIO alternate functions register. Op page 544 to 545 of the reference manual you can find the register map for the GPIO alternate functions registers. To find out a) what pin your MOSI is connected to and b) what alternative function you need to select, you need to make a side-step to the Datasheet of you microcontroller. Should you have a STM32H742xI/G, you would need the datasheet STM32H742xI/G STM32H743xI/G DS12110. In chapter 5 of the datasheet you would find the pin mappings, and on pages 88-101 of the datasheet you find all pin functions enumerated. Alternative function MOSI of SPI3 would be SPI3_MOSI and therefore either AF7 of pin PB5 or AF6 of pin PC12. So you would need to unset the alternative function of PB5 or PC12. You do this with GPIOx_MODER (so either GPIOB_MODER or GPIOC_MODER) (page 540 of the reference manual), by setting the MODERx (so either MODER5 or MODER12) to 00 for input or 01 for output.

According to the stm32h7xx_hal crate (looking at https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/blinky.rs) you should be able to set the alternative function of a pin with code that vaguely looks a follows (caveat emptor I didn't check this code, so I'm pretty sure there are multiple errors, but it should give you an idea):

use stm32h7xx_hal::{pac, prelude::*};

# assuming PB5 is used in AF7 mode for SPI3 MOSI
let dp = pac::Peripherals::take().unwrap();
let mut gpiob = dp.GPIOB.split();

# Set PB5 pin into push pull output mode
let mut shiftreg = gpiob.pb5.into_push_pull_output();

# Restore PB5 pin into alternative mode AF7
let mut mosi = shiftreg.into_alternate::<7>()

In addition to disabling control of the MOSI pin, you might want to consider disabling the clock to your SPI peripheral on your microcontroller, to save some energy. If you have enabled the SPI peripheral the shutdown process is explained in page 2179 of the reference manual. If haven't enabled the SPI peripheral, you can 'just' disable the clock with the RCC peripheral. The clock lines to SPI are explained on page 358 and the full register map is found on page 461-463 of the reference manual (which explains that you should write to the SPI3EN bit of the RCC_APB1LENR register). Module stm32h7xx_hal::rcc::rec gives access to these registers, c.f. stm32h7xx_hal::rcc::rec.

Upvotes: 1

Related Questions