Macro peripherals::periph[][src]

macro_rules! periph {
    (
        $(#[$periph_attr:meta])*
        $periph:ident;
        $($($reg_desc:literal)? $(#[$reg_attr:meta])*
        $rw:ident $reg:ident @ $offset:literal : $type:ty = $reset:literal $fields:tt)*
    ) => { ... };
    (@impl_reg base $reg:ident $type:ty) => { ... };
    (@impl_reg rw $value:ident $bits:ident) => { ... };
    (@impl_reg r $value:ident $bits:ident) => { ... };
    (@impl_reg w $value:ident $bits:ident) => { ... };
    (@impl_value $(#[$attr:meta])* $reg:ident $value:ident $bits:ident: $type:ty = $reset:literal {$(
        $(#[$field_attr:meta])*
        $field:ident : $start:literal $(.. $end:literal)? = $kind:ident $field_type:ident $({ $($enum:tt)* })? $(($($struct:tt)*))? $(;)?
    )*}) => { ... };
    (@impl_bits $(#[$attr:meta])* $reg:ident $bits:ident: $type:ty) => { ... };
    (@fields $bits:ident: $type:ty {$(
        $(#[$field_attr:meta])*
        $field:ident : $start:literal $(.. $end:literal)? = $kind:ident $field_type:ident $({ $($enum:tt)* })? $(($($struct:tt)*))? $(;)?
    )*}) => { ... };
    (@field_type $bits:ident $type:ty :
        $(#[$attr:meta])*
        $start:literal = enum $name:ident {
            $(#[$variant1_attr:meta])*
            $variant1:ident = $value1:literal,
            $(#[$variant2_attr:meta])*
            $variant2:ident = $value2:literal $(,)?
        }
    ) => { ... };
    (@field_type $bits:ident $type:ty :
        $(#[$attr:meta])*
        $start:literal $(.. $end:literal)? = enum $name:ident {$(
            $(#[$variant_attr:meta])*
            $variant:ident = $value:literal
        ),*$(,)?}
    ) => { ... };
    (@field_type $bits:ident $type:ty :
        $(#[$attr:meta])*
        $start:literal = struct $name:ident (bool)
    ) => { ... };
    (@field_type $bits:ident $type:ty :
        $(#[$attr:meta])*
        $start:literal $(.. $end:literal)? = struct $name:ident ($inner:ty)
    ) => { ... };
    (@field_type $bits:ident $type:ty :
        $(#[$attr:meta])* $start:literal $(.. $end:literal)? = extern $name:ident
    ) => { ... };
    (@impl_field $bits:ident $name:ident $value:ident $type:ty: $start:literal $(.. $end:literal)?;
        $to_type:expr; $from_type:expr;
    ) => { ... };
}

Define a peripheral and all its associated registers, fields and values

It is recommended to have one module per peripheral and thus to invoke this macros in its own module. For an example of the generated api, see the example module.

Usage

The macro begins with the peripheral name. Note that you can’t actually define an empty peripheral.

peripherals::periph!{
    /// Optional documentation
    MyPeripheral;
}

Then each register is described as follow:

peripherals::periph!{
    MyPeripheral;
    // access name     offset size  reset value
       rw     MY_REG @ 0x00:  u16 = 0x1234      {

        // fields go here
    }
}
  • The access can be r, w or rw for read-only, write-only or read-write.
  • The name of the register is generaly written in uppercase. It is used to name all types related to this register, as well as the field (in lowercase) in the peripheral struct.
  • The offset (here 0x00) is the offset of the register in the register block / peripheral. This allows the code to be generic over the peripheral instance.
  • The size describes the width of the register (8, 16, 32, or more bits) as well as the type used for all accesses.
  • The reset value (here 0x1234) is the “default” value of the register, i.e. the one after a reset of the microcontroller.

There can also be some documentation: a short description of the resister, used on the register struct and the register field of the peripheral struct, and a long description, used on everything related to this register

peripherals::periph!{
    MyPeripheral;
    "Short description"
    /// Detailed description of my register
    rw MY_REG @ 0x00: u16 = 0x1234 {
        // fields go here
    }
}

Each field has a name, used (in lowercase) for the access function on read values. It also has a position in the register, and a type. The position is either an inclusive range or a single bit.

// name     position
SOME_FIELD: 0..1     = // type
SINGLE_BIT: 2        = // type

The type can be:

  • A unit struct over an other type, which must implement Into and TryFrom for the register type (e.g. u16)
  • An enum over all possible values, which is expected to be exhaustive
  • A type defined somewere else for more flexibility. This allows to give two names to a field, to share types between registers and to define and use more complex types.
A_SRTUCT: 0..4 = struct Struct(u8);
BOOL_STRUCT: 5 = struct Bool(bool);
AN_ENUM: 6..7 = enum Enum {
     A = 0,
     B = 1,
     C = 2,
     D = 3,
}
SINGLE_BIT_ENUM: 8 = enum SingleBit {
     Off = 0,
     On = 1,
}
ALIAS: 8 = extern SingleBit;

Everything can of course also have doc comments