STM32 embedded raw-pointer to SPI peripheral

Hi,
I'm trying to hack together changing the SPI mode on an STM32 microprocessor as the SPI hal does not make it convenient or obvious how to access the mode bits. I can setup a raw pointer for reading and have confirmed it reads correctly, however, it does not let me write to the register CFG2 (I have confirmed that the register is read/write). I also confirmed that I can read/write to GPIO registers. Does anyone know why it is not letting me write to this SPI1 CFG2 register? I'm more or less following the syntax from page 420 of "the Rust Programming Language" book.

Here is a code snippet.

// initialize SPI        
        #[allow(unused_mut)]
        let mut spi1 = ctx.device.SPI1.spi( 
            (spi1_sclk, spi1_miso, spi1_mosi),
            spi::MODE_0,                                // DAC and ADC uses MODE_1, DDS uses MODE_0
            3.mhz(),
            ccdr.peripheral.SPI1,
            &ccdr.clocks,
        );        

        // raw pointer Hack, as SPI HAL does not give convenient or obvious access to mode
        unsafe {
            //stm32h7xx
            let spi1_cfg2_address = 0x4001300c;                 //spi1 peripheral address        
            let cfg2_val = spi1_cfg2_address as *mut u32;            
            let mut _cfg2_val_read = 0u32;

            _cfg2_val_read = *cfg2_val;             // read is correct
            *cfg2_val = 0x0101abcd;                 // write does not work. why? 
            _cfg2_val_read = *cfg2_val;
             
        }

Thanks,
Nathan

ps

I'm not familiar with the hardware you're targeting, but at first glance I'd say you should try reading from and writing to memory-mapped registers with core::ptr::read_volatile and core::ptr::write_volatile. The Embedded Rust Book has some explanation on why you need volatile accesses. Something like this:

use core::ptr;

unsafe {
    let cfg2_ptr = 0x4001_300c as *mut u32;            
    let _cfg2_val_read: u32 = ptr::read_volatile(cfg2_ptr);
    ptr::write_volatile(cfg2_ptr, 0x0101_abcd);
}

You can make the volatile reads and writes a bit more ergonomic with the crate volatile_register, as the embedded book also suggests. Something like this:

use volatile_register::RW;

let cfg2_reg: &mut RW<u32> = unsafe { &mut *(0x400_1300c as *mut RW<u32>) };            
let _cfg2_val_read: u32 = cfg2_reg.read();
unsafe { cfg2_reg.write(0x0101_abcd) };

Thanks for the feedback. I'm using the STM32H7xx. I tried both of these volatile methods you suggested and they both behave the same way where it does not let me write to the SPI CFG2 register. It does allow me to write to the GPIO related registers. I've also tried using optimization level 0 in the cargo.toml file but I get the same result.

The GPIO is on the AHB4 bus and the SPI1 I'm using here is on the ABP2 bus but I'm not sure if that makes a difference with regard to writablility to peripherals.

It was not a bus issue. I found out there there was an IOLOCK in the SPI2S_CR1 register and that you have to disable the SPI peripheral (SPE bit of SPI2S_CR1 register) in order to be able to change the SPI mode in the CFG2 register.

1 Like