imxrt_ral

Macro modify_reg

macro_rules! modify_reg {
    ( $periph:path, $instance:expr, $reg:ident $([$offset:expr])*, $( $field:ident : $value:expr ),+ $(,)? ) => { ... };
    ( $periph:path, $instance:expr, $reg:ident $([$offset:expr])*, $fn:expr ) => { ... };
}
Expand description

Modify a RWRegister or UnsafeRWRegister.

§Examples

// Safely acquire the peripheral instance (will panic if already acquired)
let gpioa = stm32ral::gpio::GPIOA::take().unwrap();

// Update the register to ensure bit 3 is set.
modify_reg!(stm32ral::gpio, gpioa, ODR, |reg| reg | (1<<3));

// Write values to specific fields. Unspecified fields are left unchanged.
modify_reg!(stm32ral::gpio, gpioa, MODER, MODER3: Output, MODER4: Analog);

// Unsafe access without requiring you to first `take()` the instance
unsafe { modify_reg!(stm32ral::gpio, GPIOA, MODER, MODER3: Output, MODER4: Analog) };

To support register arrays, each macro form also supports one or more array indices after the register. For example, modify_reg!(stm32ral::gpio, gpioa, ODR[2], |reg| reg | (1<<3)); sets a high bit in the third register of an ODR register array.

§Usage

Like write_reg!, this macro can be used in two ways, either with a modification of the entire register, or by specifying which fields to change and what value to change them to.

In both cases, the first arguments are:

  • the path to the peripheral module: stm32ral::gpio,
  • a reference to the instance of that peripheral: ‘gpioa’ (anything which dereferences to RegisterBlock, such as Instance, &Instance, &RegisterBlock, or *const RegisterBlock),
  • the register (and offset, for arrays) you wish you access: MODER (a field on the RegisterBlock).

In the whole-register usage, the final argument is a closure that accepts the current value of the register and returns the new value to write:

// Turn on PA3 without affecting anything else.
modify_reg!(stm32ral::gpio, gpioa, ODR, |reg| reg | (1<<3));

Otherwise, the remaining arguments are Field: Value pairs:

// Set PA3 to Output, PA4 to Analog, and leave everything else unchanged.
modify_reg!(stm32ral::gpio, gpioa, MODER, MODER3: 0b01, MODER4: 0b11);

For fields with annotated values, you can also specify a named value:

// As above, but with named values.
modify_reg!(stm32ral::gpio, gpioa, MODER, MODER3: Output, MODER4: Analog);

This macro expands to calling (*instance).register.write(value). When called with a closure, (*instance).register.read() is called, the result passed in to the closure, and the return value of the closure is used for value. When called with Field: Value arguments, the current value is read and then masked according to the specified fields, and then ORd with the OR of each field value, each masked and shifted appropriately for the field. The named values are brought into scope by use peripheral::register::field::* for each field. The same constants could just be specified manually:

// As above, but being explicit about named values.
modify_reg!(stm32ral::gpio, gpioa, MODER, MODER3: stm32ral::gpio::MODER::MODER3::RW::Output,
                                          MODER4: stm32ral::gpio::MODER::MODER4::RW::Analog);

The fully expanded form is equivalent to:

// As above, but expanded.
(*gpioa).MODER.write(
    (
        // First read the current value...
        (*gpioa).MODER.read()
        // Then AND it with an appropriate mask...
        &
        !( stm32ral::gpio::MODER::MODER3::mask | stm32ral::gpio::MODER::MODER4::mask )
    )
    // Then OR with each field value.
    |
        ((stm32ral::gpio::MODER::MODER3::RW::Output << stm32ral::gpio::MODER::MODER3::offset)
         & stm32ral::gpio::MODER::MODER3::mask)
    |
        ((stm32ral::gpio::MODER::MODER4::RW::Analog << stm32ral::gpio::MODER::MODER3::offset)
         & stm32ral::gpio::MODER::MODER3::mask)
);

§Safety

This macro will require an unsafe function or block when used with an UnsafeRWRegister, but not if used with RWRegister.

When run in an unsafe context, peripheral instances are directly accessible without requiring having called take() beforehand:

unsafe { modify_reg!(stm32ral::gpio, GPIOA, MODER, MODER3: Output, MODER4: Analog) };

This works because GPIOA is a *const RegisterBlock in the stm32ral::gpio module; and the macro brings such constants into scope and then dereferences the provided reference.