diff --git a/GUIDE.md b/GUIDE.md index 6d3b702..80abfb6 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -10,12 +10,12 @@ For an 8-bit register at memory address `0x0021`, you would write: using PORTA_OUT = fr::MemRegister; ``` -`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 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 -`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: @@ -40,15 +40,16 @@ These calls can also be made through the register using template parameters: PORTA_OUT::set(); ``` -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 PORTA_OUT::toggle(); ``` -They also support a `modify` function, which takes a list of mask operations as -shown below. Through `modify`, the register is only read and written once, -minimizing I/O. +A `modify` function is also supported, which takes a list of mask operations as +shown below. This allows the different operations to be carried out together, +still keeping to a single register read and write: ```cpp PORTA_OUT::modify(); // Only have LED_1 turned on @@ -57,7 +58,7 @@ PORTA_OUT::modify(); // Only have LED_1 turned on What if we need to add a third LED? And what if that LED is on a different register, PORTB? -This is what you could do: +This is where `RegisterGroup` comes in handy: ```cpp using PORTB_OUT = fr::MemRegister; @@ -68,10 +69,9 @@ using LED_3 = fr::RegisterMask; using LEDS = fr::RegisterGroup; ``` -By defining a `RegisterGroup`, we can make the same calls to modify the LEDs as -we would with the single register. `RegisterGroup` will direct masks to their -appropriate registers, while merging operations on the same register to -maintain that minimal I/O: +By grouping the two registers, we can carry out our modification calls without +worrying about which mask is for what register. The `RegisterGroup` will take +of that, while still merging operations when possible to maintain minimal I/O: ```cpp LEDS::clear(); @@ -98,8 +98,8 @@ CLOCK_DIV::write<0x03>(); ``` 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 -value to the register. +mask, set the new value `0x03` in the mask's location, then update the +register. `write` can also be included in `modify` chains: @@ -107,13 +107,13 @@ value to the register. CLOCK_CONTROL::modify, 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 using CLOCK_DIV4 = fr::RegisterMaskValue; ``` -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`, which would confirm that the register contains the value `0x03` in the masked bits' location. @@ -122,7 +122,7 @@ bits' location. "External" registers are registers that are not memory-mapped. These are also 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 is the definition of `MemoryIO`, which is used for memory-mapped registers: diff --git a/README.md b/README.md index c46b295..7ca829c 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,34 @@ -# funreg: Functional Memory-mapped Register I/O +# funreg: Functional Register I/O using modern C++ -*funreg* provides a functional approach to operating on memory-mapped registers -with zero overhead. This library primarily targets embedded firmware, where -these types of operations are frequently encountered. +*funreg* provides a functional approach to interacting with registers. +The library includes support for memory-mapped registers; however, other types +of registers can be supported through creating a simple access interface. -What makes this library unique is its ability to carry out multiple register -operations with a single function call, reducing this to a single register read -and write. Further, registers can be organized into "groups": these groups can -receive a list of operations for any of the contained registers, and will -optimize down to a single read and write for each register. +A unique feature of this library is its ability to handle multiple register +operations with a single function call; these operations will be merged +together so that the register is only read and written once. -A tutorial or guide will be added soon. +Registers may also be organized into groups. These groups can similarly receive +a list of operations, which will be directed the to the appropriate registers +for the same single-read-single-write process. + +For example, LEDs can be controlled by a microcontroller with a single call: + +```cpp +LEDS::modify(); +``` + +...no matter if the LEDs use different registers, or if any of them are +controlled by an external circuit rather than a built-in IO peripheral. + +See `GUIDE.md` for a walk-through of the available functionality. + +## Feature overview + +* Define registers of any size, at any address, with optional custom access interface +* Define register masks to name the bits of registers +* Define register groups so ease programming (e.g. define an `RTC` group to work with all real-time clock registers at once) +* Make modifications through groups, masks, or the registers directly ## Requirements