skullc-peripherals/docs/peripherals.rst
Erki 82f62d1650
All checks were successful
continuous-integration/drone/push Build is passing
Initial commit
2021-09-19 15:42:37 +03:00

162 lines
4.9 KiB
ReStructuredText

Peripherals Module
==================
The peripherals module contains various external devices for which (hopefully) reusable abstractions have been created.
All interfacing with hardware is done through static dispatch: hardware wrappers are to be passed in as copy-constructible
handle objects, with the objects themselves being specified as template parameters. This includes a static HAL object,
should the class need generic HAL features, like a delay function.
HAL Abstraction
---------------
As mentioned earlier, the HAL gets abstracted with simple wrapper classes that are passed into the consuming object as
template parameters. There's a special class to encapsulate more static HAL functions, such as delays, which is referred
to as the "static HAL" struct.
An example implementation of mildly fleshed out HAL abstraction can be found in the "Peripherals::Hal::St" namespace.
To make peripheral devices reusable and also configurable, the library composes all HAL-dependencies as templated members.
A few common interfaces have come from this, specifically the GPIO and Serial interfaces. Along with the static HAL itself.
Requirements for these common classes can be outlined as follows:
.. code-block:: c++
// A class for statically composing "static" features of a HAL.
struct StaticHal
{
static std::uint32_t getMillis();
static void delay(const std::uint32_t milliseconds);
static void delayUs(const std::uint32_t micros);
static void enableInterrupts();
static void disableInterrupts();
};
.. code-block:: c++
// A common serial interface, for sending and receiving data.
struct SerialInterface
{
bool transmit(std::uint8_t* data, const std::uint32_t data_len);
template<typename Td, std::size_t N>
bool transmit(std::array<Td, N>& array);
bool receive(std::uint8_t* data, const std::uint32_t data_len);
template<typename Td, std::size_t N>
bool receive(std::array<Td, N>& array);
};
.. code-block:: c++
// A common GPIO interface.
struct Gpio
{
void set(const bool& state);
void toggle();
bool read() const;
};
Usage Notes
-----------
It would be recommended to type-def whatever configurations you use, and go from there. The peripherals themselves should
be passed around as pointers or references -- they don't typically play well with copy constructors. And really, one object
per physical peripheral should exist.
It should also be noted that **constructors for peripherals can be non-trivial**. (Peripherals in this case being the non-HAL
ones.) They will do initialization in there, so that the object is usable right after successful construction. This means
you have to ensure that HAL setup is completed *before* the instances are constructed. Having them as global instances
will be affected by this, a quick fix is to just dynamically allocate them. Or use other means of deferred construction.
A few examples follow, based on the STM32 HAL.
Using a simple main, where the constructor is placed after all of the init functions:
.. code-block:: c++
#include <peripherals_button.hpp>
#include <peripherals_hal_st.hpp>
using Button
= Peripherals::Button<Peripherals::Hal::St::Gpio, Peripherals::Hal::St::StaticHal>;
void useButton(Button& b);
int main() {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
Button b1({ BTN_GPIO_Port, BTN_Pin, false });
while (true) {
useButton(b1);
}
}
Using a scenario where the class instance is kept as a global. In this case, dynamic allocation is used to achieve proper
constructor order:
.. code-block:: c++
#include <peripherals_button.hpp>
#include <peripherals_hal_st.hpp>
using Button
= Peripherals::Button<Peripherals::Hal::St::Gpio, Peripherals::Hal::St::StaticHal>;
Button* b1;
void useButton(Button& b);
int main() {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
b1 = new Button({ BTN_GPIO_Port, BTN_Pin, false });
while (true) {
useButton(*b1);
}
}
Using the StaticPointer class from the Utility module of this library is also a way of deferring the construction, while
maintaining complete static allocation:
.. code-block:: c++
#include <peripherals_button.hpp>
#include <peripherals_hal_st.hpp>
#include <utility_staticpointer.hpp>
using Button
= Peripherals::Button<Peripherals::Hal::St::Gpio, Peripherals::Hal::St::StaticHal>;
Utility::StaticPointer<Button> b1;
void useButton(Button& b);
int main() {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
b1.setup({ BTN_GPIO_Port, BTN_Pin, false });
while (true) {
useButton(*b1);
}
}
Public Members
--------------
.. doxygennamespace:: Peripherals
:content-only:
:undoc-members:
:members: