|
|
@ -10,12 +10,12 @@ For an 8-bit register at memory address `0x0021`, you would write:
|
|
|
|
using PORTA_OUT = fr::MemRegister<uint8_t, 0x0021>;
|
|
|
|
using PORTA_OUT = fr::MemRegister<uint8_t, 0x0021>;
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`fr::MemRegister` is an `fr::Register` that uses `MemoryIO` access. Registers
|
|
|
|
`MemRegister` is an `Register` that uses `MemoryIO` access. Registers
|
|
|
|
have static functions for interacting with their contents; for example, we
|
|
|
|
have static functions for interacting with their contents; for example, we
|
|
|
|
could now do `PORTA_OUT::write(0x10)` or `auto state = PORTA::read()`.
|
|
|
|
could now do `PORTA_OUT::write(0x10)` or `auto state = PORTA::read()`.
|
|
|
|
|
|
|
|
|
|
|
|
A lot more can be done with registers once we define some register masks. A
|
|
|
|
A lot more can be done with registers once we define some register masks. A
|
|
|
|
`fr::RegisterMask` lets us name one or more bits within a register.
|
|
|
|
`RegisterMask` lets us name one or more bits within a register.
|
|
|
|
|
|
|
|
|
|
|
|
To name our two LEDs, which are only controlled by single bits, we write:
|
|
|
|
To name our two LEDs, which are only controlled by single bits, we write:
|
|
|
|
|
|
|
|
|
|
|
@ -40,15 +40,16 @@ These calls can also be made through the register using template parameters:
|
|
|
|
PORTA_OUT::set<LED_2>();
|
|
|
|
PORTA_OUT::set<LED_2>();
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Registers can take multiple masks at once:
|
|
|
|
Registers can take multiple masks at once too. The masks will be merged so that
|
|
|
|
|
|
|
|
the register is only read and written once:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
```cpp
|
|
|
|
PORTA_OUT::toggle<LED_1, LED_2>();
|
|
|
|
PORTA_OUT::toggle<LED_1, LED_2>();
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
They also support a `modify` function, which takes a list of mask operations as
|
|
|
|
A `modify` function is also supported, which takes a list of mask operations as
|
|
|
|
shown below. Through `modify`, the register is only read and written once,
|
|
|
|
shown below. This allows the different operations to be carried out together,
|
|
|
|
minimizing I/O.
|
|
|
|
still keeping to a single register read and write:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
```cpp
|
|
|
|
PORTA_OUT::modify<LED_1::set, LED_2::clear>(); // Only have LED_1 turned on
|
|
|
|
PORTA_OUT::modify<LED_1::set, LED_2::clear>(); // Only have LED_1 turned on
|
|
|
@ -57,7 +58,7 @@ PORTA_OUT::modify<LED_1::set, LED_2::clear>(); // Only have LED_1 turned on
|
|
|
|
What if we need to add a third LED? And what if that LED is on a different
|
|
|
|
What if we need to add a third LED? And what if that LED is on a different
|
|
|
|
register, PORTB?
|
|
|
|
register, PORTB?
|
|
|
|
|
|
|
|
|
|
|
|
This is what you could do:
|
|
|
|
This is where `RegisterGroup` comes in handy:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
```cpp
|
|
|
|
using PORTB_OUT = fr::MemRegister<uint8_t, 0x0041>;
|
|
|
|
using PORTB_OUT = fr::MemRegister<uint8_t, 0x0041>;
|
|
|
@ -68,10 +69,9 @@ using LED_3 = fr::RegisterMask<PORTB_OUT, (1 << 5)>;
|
|
|
|
using LEDS = fr::RegisterGroup<PORTA_OUT, PORTB_OUT>;
|
|
|
|
using LEDS = fr::RegisterGroup<PORTA_OUT, PORTB_OUT>;
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
By defining a `RegisterGroup`, we can make the same calls to modify the LEDs as
|
|
|
|
By grouping the two registers, we can carry out our modification calls without
|
|
|
|
we would with the single register. `RegisterGroup` will direct masks to their
|
|
|
|
worrying about which mask is for what register. The `RegisterGroup` will take
|
|
|
|
appropriate registers, while merging operations on the same register to
|
|
|
|
of that, while still merging operations when possible to maintain minimal I/O:
|
|
|
|
maintain that minimal I/O:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
```cpp
|
|
|
|
LEDS::clear<LED_1, LED_2, LED_3>();
|
|
|
|
LEDS::clear<LED_1, LED_2, LED_3>();
|
|
|
@ -98,8 +98,8 @@ CLOCK_DIV::write<0x03>();
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
This will read the register's current value, clear all bits selected by the
|
|
|
|
This will read the register's current value, clear all bits selected by the
|
|
|
|
mask, bitwise-OR the value `0x03` to the mask's location, then write the new
|
|
|
|
mask, set the new value `0x03` in the mask's location, then update the
|
|
|
|
value to the register.
|
|
|
|
register.
|
|
|
|
|
|
|
|
|
|
|
|
`write` can also be included in `modify` chains:
|
|
|
|
`write` can also be included in `modify` chains:
|
|
|
|
|
|
|
|
|
|
|
@ -107,13 +107,13 @@ value to the register.
|
|
|
|
CLOCK_CONTROL::modify<CLOCK_DIV::write<0x03>, CLOCK_ENABLE::set>();
|
|
|
|
CLOCK_CONTROL::modify<CLOCK_DIV::write<0x03>, CLOCK_ENABLE::set>();
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
You may also define a `RegisterMaskValue` to name a specific value:
|
|
|
|
A `RegisterMaskValue` can also be defined to identify specific values:
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
```cpp
|
|
|
|
using CLOCK_DIV4 = fr::RegisterMaskValue<CLOCK_DIV, 0x03>;
|
|
|
|
using CLOCK_DIV4 = fr::RegisterMaskValue<CLOCK_DIV, 0x03>;
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Three functions are supported for `RegisterMaskValue`: `set`, which would call
|
|
|
|
`RegisterMaskValue` supports three functions: `set`, which would call
|
|
|
|
`CLOCK_DIV::write<0x03>()`; `clear`, which clears the masked bits; and `test`,
|
|
|
|
`CLOCK_DIV::write<0x03>()`; `clear`, which clears the masked bits; and `test`,
|
|
|
|
which would confirm that the register contains the value `0x03` in the masked
|
|
|
|
which would confirm that the register contains the value `0x03` in the masked
|
|
|
|
bits' location.
|
|
|
|
bits' location.
|
|
|
@ -122,7 +122,7 @@ bits' location.
|
|
|
|
|
|
|
|
|
|
|
|
"External" registers are registers that are not memory-mapped. These are also
|
|
|
|
"External" registers are registers that are not memory-mapped. These are also
|
|
|
|
supported in *funreg*, and can even be placed in `RegisterGroup`s with
|
|
|
|
supported in *funreg*, and can even be placed in `RegisterGroup`s with
|
|
|
|
memory-mapped or other register types.
|
|
|
|
other register types.
|
|
|
|
|
|
|
|
|
|
|
|
An "access type" must be defined to specify how the register is accessed. Here
|
|
|
|
An "access type" must be defined to specify how the register is accessed. Here
|
|
|
|
is the definition of `MemoryIO`, which is used for memory-mapped registers:
|
|
|
|
is the definition of `MemoryIO`, which is used for memory-mapped registers:
|
|
|
|