Initial commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Erki 2021-09-19 15:42:37 +03:00
parent 3b2b535ad5
commit 82f62d1650
11 changed files with 2966 additions and 1 deletions

View File

@ -15,6 +15,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/)
set(CMAKE_CXX_STANDARD 17)
list(APPEND CMAKE_CXX_FLAGS "-Wall -Wextra")
option(WITH_DOCS "Enable building docs." OFF)
option(WITH_TESTS "Enable unit testing." OFF)
option(WITH_HAL "Enable the compiling and deployment of the HAL dependent sections." OFF)
@ -29,6 +30,10 @@ if(WITH_TESTS)
add_subdirectory(Tests)
endif()
if(WITH_DOCS)
add_subdirectory(docs)
endif()
## Install
configure_file(skullc-config.cmake

View File

@ -20,6 +20,17 @@ enum class ButtonPress : std::uint32_t
LONG_PRESS
};
/**
* A class to abstract a button!
*
* Usage example:
* @code
* int a = 4;
* @endcode
*
* @tparam G The class to use for GPIO. Must have members @c set(bool).
* @tparam H The HAL class to use.
*/
template<typename G, typename H>
class Button
{
@ -27,7 +38,7 @@ public:
using gpio = G;
using hal = H;
gpio sw;
gpio sw; ///< A switch instance.
static constexpr std::uint32_t TIMEOUT_SHORT_PRESS = 50;
static constexpr std::uint32_t TIMEOUT_LONG_PRESS = 500;
@ -37,6 +48,9 @@ public:
: sw(sw)
{}
/**
* Does the do.
*/
void update()
{
const bool is_pressed = sw.read();

View File

@ -0,0 +1,40 @@
/*
* utility_assert.hpp
*
* Created on: Jun 25, 2021
* Author: erki
*/
#ifndef UTILITY_INC_UTILITY_ASSERT_HPP_
#define UTILITY_INC_UTILITY_ASSERT_HPP_
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((weak)) void SkullC_AssertHandler(const char* file, int line, const char* func, const char* failed_expr)
{
}
void __assert_func(const char* file, int line, const char* func, const char* failed_expr)
{
#ifdef SKULLC_UTILITY_ASSERT_TO_ERRORHANDLER
Error_Handler();
#else
__disable_irq();
__asm("bkpt");
#ifdef SKULLC_UTILITY_ASSERT_CUSTOM_HANDLER
SkullC_AssertHandler(file, line, func, failed_expr);
#endif
while (1)
;
#endif
}
#ifdef __cplusplus
}
#endif
#endif /* UTILITY_INC_UTILITY_ASSERT_HPP_ */

7
cmake/FindSphinx.cmake Normal file
View File

@ -0,0 +1,7 @@
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
find_package_handle_Standard_args(Sphinx "Failed to find sphinx-build executable" SPHINX_EXECUTABLE)

44
docs/CMakeLists.txt Normal file
View File

@ -0,0 +1,44 @@
find_package(Doxygen REQUIRED)
find_package(Sphinx REQUIRED)
file(GLOB_RECURSE PERIPHERALS_HEADERS ${CMAKE_SOURCE_DIR}/Peripherals/Inc/*.hpp)
set(DOXYGEN_INPUT_DIR "${PROJECT_SOURCE_DIR}/Threads/Inc ${PROJECT_SOURCE_DIR}/Peripherals/Inc ${PROJECT_SOURCE_DIR}/Utility/Inc")
set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/docs/doxygen)
set(DOXYGEN_INDEX_FILE ${CMAKE_CURRENT_SOURCE_DIR}/html/index.html)
set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
#Replace variables inside @@ with the current values
configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY)
file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR}) #Doxygen won't create this for us
add_custom_command(OUTPUT ${DOXYGEN_INDEX_FILE}
DEPENDS ${PERIPHERALS_HEADERS}
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT}
MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN}
COMMENT "Generating docs")
add_custom_target(Doxygen ALL DEPENDS ${DOXYGEN_INDEX_FILE})
set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs/sphinx)
set(SPHINX_INDEX_FILE ${SPHINX_BUILD}/index.html)
set(SPHINX_SOURCE_LIST
${CMAKE_CURRENT_SOURCE_DIR}/index.rst
${CMAKE_CURRENT_SOURCE_DIR}/peripherals.rst
${CMAKE_CURRENT_SOURCE_DIR}/utility.rst
${CMAKE_CURRENT_SOURCE_DIR}/threads.rst
)
add_custom_command(OUTPUT ${SPHINX_INDEX_FILE}
COMMAND
${SPHINX_EXECUTABLE} -b html
-Dbreathe_projects.SkullC=${DOXYGEN_OUTPUT_DIR}/xml
${SPHINX_SOURCE} ${SPHINX_BUILD}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${SPHINX_SOURCE_LIST} ${PERIPHERALS_HEADERS}
MAIN_DEPENDENCY ${SPHINX_SOURCE}/conf.py
COMMENT "Generating documentation with Sphinx")
add_custom_target(Sphinx ALL DEPENDS ${SPHINX_INDEX_FILE})

2579
docs/Doxyfile.in Normal file

File diff suppressed because it is too large Load Diff

57
docs/conf.py Normal file
View File

@ -0,0 +1,57 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'SkullC'
copyright = '2021, Erki (Skull132)'
author = 'Erki (Skull132)'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"breathe"
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for breathe plugin -----------------------------------------------
breathe_default_project = "SkullC"

50
docs/index.rst Normal file
View File

@ -0,0 +1,50 @@
.. SkullC documentation master file, created by
sphinx-quickstart on Wed Jul 7 13:46:52 2021.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to SkullC's documentation!
==================================
**SkullC** is a C++17 library, composed of various modules, meant for **embedded software** development. The code is presently
licensed under MIT and can be freely found in `the Git repository here <https://git.skullnet.me/erki/skullc-peripherals>`_.
The library contains abstractions for commonly used peripheral devices, general utility functions, and an abstraction over
FreeRTOS for RTOS based programming. A lot of it is developed as I go, with some components having more questionable design
as others (since some are implemented as thought-exercises at first). This also speaks about API stability: things may change.
Most of this library has been tested and built for the STM32 boards. Though in theory, the code is not reliant on any specific
HAL and can be expanded to work on any system for which you implement the required interfaces.
Contents
--------
You will find 3 large modules here.
The :doc:`peripherals` contains both abstractions over the ST HAL and various other peripheral devices, such as IMUs,
displays, etc.
The :doc:`utility` contains various "software" helpers, abstractions, etcetera, that don't necessarily interface with
hardware, but in general provide helpful tools for embedded development. This includes logging functionality, a drawing
abstraction, and so forth.
The :doc:`threads` is a set of various C++ abstractions over FreeRTOS. With the key highlight being an implementation of
a signalling system, and the actor model of asynchronous processing.
Usage
-----
The library is made to be as build-tool agnostic as possible. All modules can simply be dropped into whatever IDE/project
structure you have, and then used. No major configuration is necessary. Most modules are header-only, some require additional
source files to be compiled and linked.
For development use, cmake is used to enable unit testing (and should also function for integration with cmake projects).
Conan and catch2 are additional dependencies for unit testing and CI work.
.. toctree::
:maxdepth: 2
:caption: Contents:
peripherals.rst
utility.rst
threads.rst

161
docs/peripherals.rst Normal file
View File

@ -0,0 +1,161 @@
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:

4
docs/threads.rst Normal file
View File

@ -0,0 +1,4 @@
Threads Module
==============
asdasd

4
docs/utility.rst Normal file
View File

@ -0,0 +1,4 @@
Utility Module
==============
szdf