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
orrw
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
andTryFrom
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