Compare commits
No commits in common. "master" and "0762d5c9cd78ff7c34a50da7c18d09b003f4223b" have entirely different histories.
master
...
0762d5c9cd
@ -17,7 +17,7 @@ AlwaysBreakAfterReturnType: None
|
|||||||
AlwaysBreakTemplateDeclarations: Yes
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
BreakBeforeBraces: Custom
|
BreakBeforeBraces: Custom
|
||||||
BraceWrapping:
|
BraceWrapping:
|
||||||
AfterCaseLabel: true
|
AfterCaseLabel: false
|
||||||
AfterClass: true
|
AfterClass: true
|
||||||
AfterControlStatement: Always
|
AfterControlStatement: Always
|
||||||
AfterEnum: true
|
AfterEnum: true
|
||||||
@ -25,8 +25,8 @@ BraceWrapping:
|
|||||||
AfterNamespace: true
|
AfterNamespace: true
|
||||||
AfterUnion: true
|
AfterUnion: true
|
||||||
AfterStruct: true
|
AfterStruct: true
|
||||||
BeforeCatch: true
|
BeforeCatch: false
|
||||||
BeforeElse: true
|
BeforeElse: false
|
||||||
IndentBraces: false
|
IndentBraces: false
|
||||||
SplitEmptyFunction: false
|
SplitEmptyFunction: false
|
||||||
SplitEmptyRecord: true
|
SplitEmptyRecord: true
|
||||||
|
|||||||
29
.drone.yml
Normal file
29
.drone.yml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: default
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build and test
|
||||||
|
pull: never
|
||||||
|
image: erki/cpp-ubuntu:latest
|
||||||
|
commands:
|
||||||
|
- mkdir -p build
|
||||||
|
- cd build
|
||||||
|
- conan install .. --build=missing
|
||||||
|
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=ON
|
||||||
|
- ninja
|
||||||
|
- ctest . --output-on-failure
|
||||||
|
- name: notify
|
||||||
|
image: plugins/matrix
|
||||||
|
settings:
|
||||||
|
homeserver:
|
||||||
|
from_secret: matrix_homeserver
|
||||||
|
roomid:
|
||||||
|
from_secret: matrix_roomid
|
||||||
|
username:
|
||||||
|
from_secret: matrix_username
|
||||||
|
password:
|
||||||
|
from_secret: matrix_password
|
||||||
|
when:
|
||||||
|
status:
|
||||||
|
- failure
|
||||||
@ -1,41 +0,0 @@
|
|||||||
name: CI & Unit Tests
|
|
||||||
run-name: CI & Unit Tests
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Unit-Tests:
|
|
||||||
runs-on: fedora-cpp
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Configure build
|
|
||||||
working-directory: ${{runner.workspace}}
|
|
||||||
run: |
|
|
||||||
cmake . -B${{runner.workspace}}/build \
|
|
||||||
-G"Ninja" \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DSKULLC_WITH_TESTS=ON
|
|
||||||
|
|
||||||
- name: Build tests + lib
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: ninja
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: ctest . --output-on-failure
|
|
||||||
Docs:
|
|
||||||
runs-on: fedora-cpp
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Configure build
|
|
||||||
working-directory: ${{runner.workspace}}
|
|
||||||
run: |
|
|
||||||
cmake . -B${{runner.workspace}}/build \
|
|
||||||
-G"Ninja" \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DSKULLC_WITH_DOCS=ON
|
|
||||||
|
|
||||||
- name: Build docs
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: ninja Sphinx
|
|
||||||
@ -1,22 +1,22 @@
|
|||||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||||
|
|
||||||
set(SKULLC_VERSION 0.1.0)
|
set(version 0.1.0)
|
||||||
|
|
||||||
project(skullc
|
project(skullc
|
||||||
VERSION ${SKULLC_VERSION}
|
VERSION ${version}
|
||||||
LANGUAGES
|
LANGUAGES
|
||||||
C
|
C
|
||||||
CXX
|
CXX
|
||||||
)
|
)
|
||||||
|
|
||||||
#list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
|
list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
|
||||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/)
|
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/)
|
||||||
|
|
||||||
option(SKULLC_WITH_TESTS "Enable unit testing." OFF)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
option(SKULLC_WITH_HAL "Enable the compiling and deployment of the HAL dependent sections." OFF)
|
list(APPEND CMAKE_CXX_FLAGS "-Wall -Wextra")
|
||||||
option(SKULLC_USE_HAL_ST "Enable the ST HAl when SKULLC_WITH_HAL is enabled." OFF)
|
|
||||||
option(SKULLC_USE_HAL_ESP "Enable the ESP HAL when SKULLC_WITH_HAL is enabled." OFF)
|
option(WITH_TESTS "Enable unit testing." OFF)
|
||||||
option(SKULLC_WITH_DOCS "Enable documentation building." OFF)
|
option(WITH_HAL "Enable the compiling and deployment of the HAL dependent sections." OFF)
|
||||||
|
|
||||||
include(skullc-install)
|
include(skullc-install)
|
||||||
|
|
||||||
@ -24,15 +24,11 @@ add_subdirectory(Peripherals)
|
|||||||
add_subdirectory(Utility)
|
add_subdirectory(Utility)
|
||||||
add_subdirectory(Messaging)
|
add_subdirectory(Messaging)
|
||||||
|
|
||||||
if(SKULLC_WITH_TESTS)
|
if(WITH_TESTS)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory(Tests)
|
add_subdirectory(Tests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(SKULLC_WITH_DOCS)
|
|
||||||
add_subdirectory(Docs)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
configure_file(skullc-config.cmake
|
configure_file(skullc-config.cmake
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
find_package(Doxygen REQUIRED)
|
|
||||||
find_package(Sphinx REQUIRED)
|
|
||||||
|
|
||||||
file(GLOB_RECURSE SKULLC_PUBLIC_HEADERS
|
|
||||||
${PROJECT_SOURCE_DIR}/Messaging/Inc/*
|
|
||||||
${PROJECT_SOURCE_DIR}/Threads/Inc/*
|
|
||||||
${PROJECT_SOURCE_DIR}/Utility/Inc/*
|
|
||||||
${PROJECT_SOURCE_DIR}/Peripherals/Inc/*
|
|
||||||
)
|
|
||||||
|
|
||||||
set(DOXYGEN_INPUT_DIRS "${PROJECT_SOURCE_DIR}/Messaging/Inc ${PROJECT_SOURCE_DIR}/Threads/Inc ${PROJECT_SOURCE_DIR}/Utility/Inc ${PROJECT_SOURCE_DIR}/Peripherals/Inc")
|
|
||||||
set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/docs/doxygen)
|
|
||||||
set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/xml/index.xml)
|
|
||||||
set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
|
|
||||||
set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
|
|
||||||
|
|
||||||
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 ${SKULLC_PUBLIC_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}/sphinx)
|
|
||||||
set(SPHINX_INDEX_FILE ${SPHINX_BUILD}/index.html)
|
|
||||||
file(GLOB SPHINX_RST_FILES ${SPHINX_SOURCE}/*.rst)
|
|
||||||
|
|
||||||
# Only regenerate Sphinx when:
|
|
||||||
# - Doxygen has rerun
|
|
||||||
# - Our doc files have been updated
|
|
||||||
# - The Sphinx config has been updated
|
|
||||||
add_custom_command(OUTPUT ${SPHINX_INDEX_FILE}
|
|
||||||
COMMAND
|
|
||||||
${SPHINX_EXECUTABLE} -b html
|
|
||||||
# Tell Breathe where to find the Doxygen output
|
|
||||||
-Dbreathe_projects.SkullCPeripheralsLibrary=${DOXYGEN_OUTPUT_DIR}/xml
|
|
||||||
${SPHINX_SOURCE} ${SPHINX_BUILD}
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
|
||||||
DEPENDS
|
|
||||||
# Other docs files you want to track should go here (or in some variable)
|
|
||||||
${SPHINX_RST_FILES}
|
|
||||||
${DOXYGEN_INDEX_FILE}
|
|
||||||
MAIN_DEPENDENCY ${SPHINX_SOURCE}/conf.py
|
|
||||||
COMMENT "Generating documentation with Sphinx")
|
|
||||||
|
|
||||||
# Nice named target so we can run the job easily
|
|
||||||
add_custom_target(Sphinx ALL DEPENDS ${SPHINX_INDEX_FILE})
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
// Every other setting ends up being default.
|
|
||||||
|
|
||||||
GENERATE_XML = YES
|
|
||||||
GENERATE_HTML = YES
|
|
||||||
|
|
||||||
JAVADOC_AUTOBRIEF = YES
|
|
||||||
|
|
||||||
INPUT = @DOXYGEN_INPUT_DIRS@
|
|
||||||
RECURSIVE = YES
|
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@"
|
|
||||||
31
Docs/conf.py
31
Docs/conf.py
@ -1,31 +0,0 @@
|
|||||||
# Configuration file for the Sphinx documentation builder.
|
|
||||||
#
|
|
||||||
# For the full list of built-in configuration values, see the documentation:
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
||||||
|
|
||||||
project = 'SkullC Peripherals Library'
|
|
||||||
copyright = '2023, Rusted Skull'
|
|
||||||
author = 'Rusted Skull'
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
||||||
|
|
||||||
extensions = [ "breathe" ]
|
|
||||||
|
|
||||||
templates_path = ['_templates']
|
|
||||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
||||||
|
|
||||||
html_theme = 'classic'
|
|
||||||
html_static_path = ['_static']
|
|
||||||
|
|
||||||
# -- Breathe Configuration"
|
|
||||||
breathe_default_project = "SkullCPeripheralsLibrary"
|
|
||||||
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
.. SkullC Peripherals Library documentation master file, created by
|
|
||||||
sphinx-quickstart on Thu Dec 28 23:16:21 2023.
|
|
||||||
You can adapt this file completely to your liking, but it should at least
|
|
||||||
contain the root `toctree` directive.
|
|
||||||
|
|
||||||
Welcome to SkullC Peripherals Library's documentation!
|
|
||||||
======================================================
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
:caption: Contents:
|
|
||||||
|
|
||||||
utility
|
|
||||||
peripherals
|
|
||||||
threads
|
|
||||||
messaging
|
|
||||||
|
|
||||||
Indices and tables
|
|
||||||
==================
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`modindex`
|
|
||||||
* :ref:`search`
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
Messaging Library
|
|
||||||
=================
|
|
||||||
|
|
||||||
A small Packet and Parser implementation for framing random message structures on the wire.
|
|
||||||
|
|
||||||
Use the :cpp:struct:`Packet` to copy in various data. Then send the serialized version of the packet
|
|
||||||
onto the wire.
|
|
||||||
|
|
||||||
:cpp:class:`Messaging::Parser` will help you put bytes coming in from the wire into coherent packets, and will serve you
|
|
||||||
these packets as you're ready for them.
|
|
||||||
|
|
||||||
.. doxygennamespace:: Messaging
|
|
||||||
:project: SkullCPeripheralsLibrary
|
|
||||||
:content-only:
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
Peripherals Library
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. doxygennamespace:: Peripherals
|
|
||||||
:project: SkullCPeripheralsLibrary
|
|
||||||
:content-only:
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
Threads Library
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. doxygennamespace:: Threads
|
|
||||||
:project: SkullCPeripheralsLibrary
|
|
||||||
:content-only:
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
Utility Library
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. doxygennamespace:: Utility
|
|
||||||
:project: SkullCPeripheralsLibrary
|
|
||||||
:content-only:
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
@ -10,4 +10,4 @@ target_include_directories(messaging
|
|||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
)
|
)
|
||||||
|
|
||||||
skullc_install_packages(skullc messaging ${SKULLC_VERSION})
|
skullc_install_packages(skullc messaging ${version})
|
||||||
|
|||||||
@ -11,4 +11,4 @@ target_include_directories(peripherals
|
|||||||
)
|
)
|
||||||
|
|
||||||
## INSTALL
|
## INSTALL
|
||||||
skullc_install_packages(skullc peripherals ${SKULLC_VERSION})
|
skullc_install_packages(skullc peripherals ${version})
|
||||||
|
|||||||
@ -42,24 +42,19 @@ public:
|
|||||||
const bool is_pressed = sw.read();
|
const bool is_pressed = sw.read();
|
||||||
ButtonPress new_state = ButtonPress::NOT_PRESSED;
|
ButtonPress new_state = ButtonPress::NOT_PRESSED;
|
||||||
|
|
||||||
const std::uint32_t time_held = hal::getMillis() - time_pressed_down_;
|
|
||||||
|
|
||||||
if (is_pressed && !was_pressed_)
|
if (is_pressed && !was_pressed_)
|
||||||
{
|
{
|
||||||
time_pressed_down_ = hal::getMillis();
|
time_pressed_down_ = hal::getMillis();
|
||||||
}
|
} else if (!is_pressed && was_pressed_)
|
||||||
else if (!is_pressed && was_pressed_)
|
|
||||||
{
|
{
|
||||||
if (time_held > TIMEOUT_SHORT_PRESS && time_held <= TIMEOUT_LONG_PRESS)
|
const std::uint32_t time_held = hal::getMillis() - time_pressed_down_;
|
||||||
|
if (time_held > TIMEOUT_LONG_PRESS)
|
||||||
|
new_state = ButtonPress::LONG_PRESS;
|
||||||
|
else if (time_held > TIMEOUT_SHORT_PRESS)
|
||||||
new_state = ButtonPress::SHORT_PRESS;
|
new_state = ButtonPress::SHORT_PRESS;
|
||||||
|
|
||||||
time_pressed_down_ = 0;
|
time_pressed_down_ = 0;
|
||||||
}
|
}
|
||||||
else if (is_pressed && was_pressed_)
|
|
||||||
{
|
|
||||||
if (current_state_ == ButtonPress::NOT_PRESSED && time_held > TIMEOUT_LONG_PRESS)
|
|
||||||
new_state = ButtonPress::LONG_PRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
was_pressed_ = is_pressed;
|
was_pressed_ = is_pressed;
|
||||||
current_state_ = new_state;
|
current_state_ = new_state;
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 11/02/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <concepts>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace Peripherals::Hal
|
|
||||||
{
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept Gpio = requires (T t, const T ct) {
|
|
||||||
t.set(true);
|
|
||||||
t.toggle();
|
|
||||||
{ ct.read() } -> std::same_as<bool>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept SerialInterface = requires (T t, std::uint8_t* data, const std::uint32_t len) {
|
|
||||||
{ t.transmit(data, len) } -> std::same_as<bool>;
|
|
||||||
{ t.transmit(std::array<std::uint8_t, 10>{}) } -> std::same_as<bool>;
|
|
||||||
{ t.receive(data, len) } -> std::same_as<bool>;
|
|
||||||
{ t.receive(std::array<std::uint8_t, 10>{}) } -> std::same_as<bool>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, typename RegName>
|
|
||||||
concept RegisterInterface = requires (T t, RegName reg, std::uint8_t* data, const std::uint32_t len) {
|
|
||||||
t.writerRegister(reg, std::uint8_t{});
|
|
||||||
t.writeRegisterMultibyte(reg, data, len);
|
|
||||||
{ t.readRegister(reg, std::uint32_t{}) } -> std::same_as<std::uint8_t>;
|
|
||||||
t.readRegisterMultibyte(reg, data, len, std::uint32_t{});
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 11/02/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef SKULLC_USE_HAL_ESP
|
|
||||||
|
|
||||||
#include <peripherals_hal_concepts.hpp>
|
|
||||||
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
|
|
||||||
namespace Peripherals::Hal::Esp
|
|
||||||
{
|
|
||||||
|
|
||||||
struct Gpio
|
|
||||||
{
|
|
||||||
gpio_num_t gpio;
|
|
||||||
const bool inverted = false;
|
|
||||||
|
|
||||||
Gpio() = delete;
|
|
||||||
explicit Gpio(gpio_num_t gpio, const bool inverted)
|
|
||||||
: gpio(gpio), inverted(inverted)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void set(const bool& state)
|
|
||||||
{
|
|
||||||
gpio_set_level(gpio, inverted ? !state : state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggle() { gpio_set_level(gpio, !gpio_get_level(gpio)); }
|
|
||||||
|
|
||||||
bool read() const
|
|
||||||
{
|
|
||||||
return inverted ? !bool(gpio_get_level(gpio)) : bool(gpio_get_level(gpio));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(Peripherals::Hal::Gpio<Gpio>);
|
|
||||||
|
|
||||||
#define CREATE_GPIO(name) \
|
|
||||||
Peripherals::Hal::Esp::Gpio{name, false}
|
|
||||||
|
|
||||||
#define CREATE_INV_GPIO(name) \
|
|
||||||
Peripherals::Hal::Esp::Gpio{name, true}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
# warning "ESP HAL included without SKULLC_USE_HAL_ESP being defined."
|
|
||||||
#endif /* SKULLC_USE_HAL_ESP */
|
|
||||||
@ -8,10 +8,10 @@
|
|||||||
#ifndef SKULLC_PERIPHERALS_HAL_ST_HPP_
|
#ifndef SKULLC_PERIPHERALS_HAL_ST_HPP_
|
||||||
#define SKULLC_PERIPHERALS_HAL_ST_HPP_
|
#define SKULLC_PERIPHERALS_HAL_ST_HPP_
|
||||||
|
|
||||||
#ifdef SKULLC_USE_HAL_ST
|
|
||||||
|
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
|
|
||||||
|
#include "peripherals_utility.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#define USE_DELAY_US
|
#define USE_DELAY_US
|
||||||
@ -96,15 +96,9 @@ struct Gpio
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define CREATE_GPIO(name) \
|
#define CREATE_GPIO(name) \
|
||||||
Peripherals::Hal::St::Gpio \
|
Peripherals::Hal::St::Gpio { name##_GPIO_Port, name##_Pin, false }
|
||||||
{ \
|
|
||||||
name##_GPIO_Port, name##_Pin, false \
|
|
||||||
}
|
|
||||||
#define CREATE_INV_GPIO(name) \
|
#define CREATE_INV_GPIO(name) \
|
||||||
Peripherals::Hal::St::Gpio \
|
Peripherals::Hal::St::Gpio { name##_GPIO_Port, name##_Pin, true }
|
||||||
{ \
|
|
||||||
name##_GPIO_Port, name##_Pin, true \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif// HAL_GPIO_MODULE_ENABLED
|
#endif// HAL_GPIO_MODULE_ENABLED
|
||||||
|
|
||||||
@ -268,30 +262,10 @@ struct SpiRegisters
|
|||||||
|
|
||||||
#ifdef HAL_UART_MODULE_ENABLED
|
#ifdef HAL_UART_MODULE_ENABLED
|
||||||
|
|
||||||
namespace _Details
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hack: Temporary workarounds to make the HAL compile for FW1.27. ST made it so that only UART libraries
|
|
||||||
* are const-correct. The others remain unconst. So these wrappers will exist until we're no longer partially
|
|
||||||
* const-correct.
|
|
||||||
*/
|
|
||||||
inline HAL_StatusTypeDef uartTransmit(UART_HandleTypeDef* huart, std::uint8_t* data, const std::uint16_t size, const std::uint32_t timeout)
|
|
||||||
{
|
|
||||||
return HAL_UART_Transmit(huart, data, size, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline HAL_StatusTypeDef uartTransmitDma(UART_HandleTypeDef* huart, std::uint8_t* data, const std::uint16_t size)
|
|
||||||
{
|
|
||||||
return HAL_UART_Transmit_DMA(huart, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace _Details
|
|
||||||
|
|
||||||
using UartInterface =
|
using UartInterface =
|
||||||
SerialInterface<UART_HandleTypeDef, _Details::uartTransmit, HAL_UART_Receive>;
|
SerialInterface<UART_HandleTypeDef, HAL_UART_Transmit, HAL_UART_Receive>;
|
||||||
using UartInterfaceDMA =
|
using UartInterfaceDMA =
|
||||||
SerialInterfaceAsync<UART_HandleTypeDef, _Details::uartTransmitDma,
|
SerialInterfaceAsync<UART_HandleTypeDef, HAL_UART_Transmit_DMA,
|
||||||
HAL_UART_Receive_DMA>;
|
HAL_UART_Receive_DMA>;
|
||||||
|
|
||||||
#endif// HAL_UART_MODULE_ENABLED
|
#endif// HAL_UART_MODULE_ENABLED
|
||||||
@ -345,8 +319,4 @@ struct ItmSerialInterface
|
|||||||
}// namespace Hal
|
}// namespace Hal
|
||||||
}// namespace Peripherals
|
}// namespace Peripherals
|
||||||
|
|
||||||
#else
|
|
||||||
# warning "ESP HAL included without SKULLC_USE_HAL_ESP being defined."
|
|
||||||
#endif /* SKULLC_USE_HAL_ST */
|
|
||||||
|
|
||||||
#endif /* SKULLC_PERIPHERALS_HAL_ST_HPP_ */
|
#endif /* SKULLC_PERIPHERALS_HAL_ST_HPP_ */
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include "peripherals_imu.hpp"
|
#include "peripherals_imu.hpp"
|
||||||
|
#include "peripherals_utility.hpp"
|
||||||
|
|
||||||
namespace Peripherals
|
namespace Peripherals
|
||||||
{
|
{
|
||||||
@ -79,6 +80,10 @@ public:
|
|||||||
registers.writeRegister(Registers_::ACCEL_CONFIG & Registers_::WRITE_MASK,
|
registers.writeRegister(Registers_::ACCEL_CONFIG & Registers_::WRITE_MASK,
|
||||||
new_accel_config);
|
new_accel_config);
|
||||||
|
|
||||||
|
// setGyroscopeScale(scale_gyro_);
|
||||||
|
//
|
||||||
|
// setAccelerometerScale(scale_accel_);
|
||||||
|
|
||||||
// ACCEL_FCHOICE_B = 0, A_DLPF_CFG = 3 filter=44.8/61.5 rate=1KHz
|
// ACCEL_FCHOICE_B = 0, A_DLPF_CFG = 3 filter=44.8/61.5 rate=1KHz
|
||||||
registers.writeRegister(Registers_::ACCEL_CONFIG2 & Registers_::WRITE_MASK,
|
registers.writeRegister(Registers_::ACCEL_CONFIG2 & Registers_::WRITE_MASK,
|
||||||
0x03);
|
0x03);
|
||||||
@ -124,16 +129,16 @@ public:
|
|||||||
for (std::uint32_t i = 0; i < samples; i++)
|
for (std::uint32_t i = 0; i < samples; i++)
|
||||||
{
|
{
|
||||||
std::array<std::int16_t, 3> raw;
|
std::array<std::int16_t, 3> raw;
|
||||||
|
auto add_to_avg = [&raw](std::array<std::int32_t, 3>& out) {
|
||||||
|
for (std::uint32_t j = 0; j < 3; j++)
|
||||||
|
out[j] += raw[j];
|
||||||
|
};
|
||||||
|
|
||||||
readGyroRaw(raw.data());
|
readGyroRaw(raw.data());
|
||||||
for (std::uint32_t j = 0; j < 3; j++)
|
add_to_avg(avg_gyro);
|
||||||
avg_gyro[j] += raw[j];
|
|
||||||
|
|
||||||
readAccelerometerRaw(raw.data());
|
readAccelerometerRaw(raw.data());
|
||||||
for (std::uint32_t j = 0; j < 3; j++)
|
add_to_avg(avg_accel);
|
||||||
avg_accel[j] += raw[j];
|
|
||||||
|
|
||||||
hal::delay(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::uint32_t i = 0; i < 3; i++)
|
for (std::uint32_t i = 0; i < 3; i++)
|
||||||
@ -243,6 +248,9 @@ private:
|
|||||||
GyroScale scale_gyro_ = GyroScale::DPS_2000;
|
GyroScale scale_gyro_ = GyroScale::DPS_2000;
|
||||||
AccelerometerScale scale_accel_ = AccelerometerScale::G16;
|
AccelerometerScale scale_accel_ = AccelerometerScale::G16;
|
||||||
|
|
||||||
|
std::array<std::int16_t, 3> bias_gyro_;
|
||||||
|
std::array<std::int16_t, 3> bias_accel_;
|
||||||
|
|
||||||
static constexpr float accel_fs_to_bit_constants_[4] = {
|
static constexpr float accel_fs_to_bit_constants_[4] = {
|
||||||
(2.0f / 32768.0f), (4.0f / 32768.0f), (8.0f / 32768.0f),
|
(2.0f / 32768.0f), (4.0f / 32768.0f), (8.0f / 32768.0f),
|
||||||
(16.0f / 32768.0f)};
|
(16.0f / 32768.0f)};
|
||||||
@ -251,9 +259,6 @@ private:
|
|||||||
(250.0f / 32768.0f), (500.0f / 32768.0f), (1000.0f / 32768.0f),
|
(250.0f / 32768.0f), (500.0f / 32768.0f), (1000.0f / 32768.0f),
|
||||||
(2000.0f / 32768.0f)};
|
(2000.0f / 32768.0f)};
|
||||||
|
|
||||||
std::array<std::int16_t, 3> bias_gyro_;
|
|
||||||
std::array<std::int16_t, 3> bias_accel_;
|
|
||||||
|
|
||||||
struct Registers_
|
struct Registers_
|
||||||
{
|
{
|
||||||
static constexpr std::uint32_t ICM20689_ID = 0x98;
|
static constexpr std::uint32_t ICM20689_ID = 0x98;
|
||||||
|
|||||||
@ -54,8 +54,7 @@ public:
|
|||||||
{
|
{
|
||||||
left_.forward.setCompare(left);
|
left_.forward.setCompare(left);
|
||||||
left_.backward.setCompare(0);
|
left_.backward.setCompare(0);
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
left_.forward.setCompare(0);
|
left_.forward.setCompare(0);
|
||||||
left_.backward.setCompare(-1 * left);
|
left_.backward.setCompare(-1 * left);
|
||||||
@ -65,8 +64,7 @@ public:
|
|||||||
{
|
{
|
||||||
right_.forward.setCompare(right);
|
right_.forward.setCompare(right);
|
||||||
right_.backward.setCompare(0);
|
right_.backward.setCompare(0);
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
right_.forward.setCompare(0);
|
right_.forward.setCompare(0);
|
||||||
right_.backward.setCompare(-1 * right);
|
right_.backward.setCompare(-1 * right);
|
||||||
|
|||||||
72
Peripherals/Inc/peripherals_utility.hpp
Normal file
72
Peripherals/Inc/peripherals_utility.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* peripherals_utility.hpp
|
||||||
|
*
|
||||||
|
* Created on: Feb 24, 2021
|
||||||
|
* Author: erki
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SKULLC_PERIPHERALS_UTILITY_HPP_
|
||||||
|
#define SKULLC_PERIPHERALS_UTILITY_HPP_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace Peripherals
|
||||||
|
{
|
||||||
|
|
||||||
|
#define SKULLC_CONCAT_IMPL(x, y) x##y
|
||||||
|
#define SKULLC_CONCAT(x, y) SKULLC_CONCAT_IMPL(x, y)
|
||||||
|
#define SKULLC_TAG struct SKULLC_CONCAT(SkullCTag_, __COUNTER__)
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr const T& clamp(const T& v, const T& lo, const T& hi)
|
||||||
|
{
|
||||||
|
if (v > hi)
|
||||||
|
return hi;
|
||||||
|
else if (v < lo)
|
||||||
|
return lo;
|
||||||
|
else
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, std::size_t N>
|
||||||
|
T byteToTypeBE(const std::uint8_t a[N])
|
||||||
|
{
|
||||||
|
T t(0);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
t |= a[i] << (i * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, std::size_t N>
|
||||||
|
T byteToTypeLE(const std::uint8_t a[N])
|
||||||
|
{
|
||||||
|
T t(0);
|
||||||
|
|
||||||
|
for (std::size_t i = N; i >= 0; i--)
|
||||||
|
{
|
||||||
|
t |= a[i] << ((i - 1) * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T zeroInitialized()
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
||||||
|
|
||||||
|
T t;
|
||||||
|
std::memset(&t, 0, sizeof(T));
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace Peripherals
|
||||||
|
|
||||||
|
#endif /* SKULLC_PERIPHERALS_UTILITY_HPP_ */
|
||||||
@ -1,16 +1,10 @@
|
|||||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||||
|
|
||||||
option(SKULLC_TESTS_WITH_SANITIZERS "Enable sanitizers for tests." ON)
|
option(TESTS_WITH_SANITIZERS "Enable sanitizers for tests." ON)
|
||||||
|
|
||||||
include(FetchContent)
|
find_package(Catch2 REQUIRED)
|
||||||
|
|
||||||
FetchContent_Declare(Catch2
|
if(TESTS_WITH_SANITIZERS)
|
||||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
|
||||||
GIT_TAG v2.13.8
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(Catch2)
|
|
||||||
|
|
||||||
if(SKULLC_TESTS_WITH_SANITIZERS)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fno-sanitize-recover")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fno-sanitize-recover")
|
||||||
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fno-sanitize-recover")
|
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fno-sanitize-recover")
|
||||||
endif()
|
endif()
|
||||||
@ -24,14 +18,7 @@ add_executable(tests
|
|||||||
rand.cpp
|
rand.cpp
|
||||||
fixedpoint.cpp
|
fixedpoint.cpp
|
||||||
pixelbuffer.cpp
|
pixelbuffer.cpp
|
||||||
pixelbuffer_effects.cpp
|
pixelbuffer_effects.cpp function.cpp)
|
||||||
function.cpp
|
|
||||||
assert_ndebug.cpp
|
|
||||||
assert.cpp
|
|
||||||
enum_helpers.cpp
|
|
||||||
bytes.cpp
|
|
||||||
filters.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(tests
|
target_link_libraries(tests
|
||||||
PUBLIC
|
PUBLIC
|
||||||
@ -41,12 +28,6 @@ target_link_libraries(tests
|
|||||||
Catch2::Catch2
|
Catch2::Catch2
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(tests
|
|
||||||
PROPERTIES
|
|
||||||
CXX_STANDARD 17
|
|
||||||
)
|
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib)
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
include(Catch)
|
include(Catch)
|
||||||
catch_discover_tests(tests)
|
catch_discover_tests(tests)
|
||||||
@ -1,68 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 29.06.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
|
|
||||||
#ifdef NDEBUG
|
|
||||||
#undef NDEBUG
|
|
||||||
#define NDEBUG_WAS_SET
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "utility_assert.hpp"
|
|
||||||
|
|
||||||
#ifdef NDEBUG_WAS_SET
|
|
||||||
#define NDEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
bool assert_flag = false;
|
|
||||||
|
|
||||||
void assertCallback(const char*, const char*, const int)
|
|
||||||
{
|
|
||||||
assert_flag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace
|
|
||||||
|
|
||||||
TEST_CASE("Assert debug without NDEBUG gets triggered if check is false.", "[utility],[assert]")
|
|
||||||
{
|
|
||||||
assert_flag = false;
|
|
||||||
Utility::Assert::setHandler(assertCallback);
|
|
||||||
|
|
||||||
SKULLC_ASSERT_DEBUG(false);
|
|
||||||
|
|
||||||
CHECK(assert_flag == true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Assert debug without NDEBUG doesn't get triggered if check is true.", "[utility],[assert]")
|
|
||||||
{
|
|
||||||
assert_flag = false;
|
|
||||||
Utility::Assert::setHandler(assertCallback);
|
|
||||||
|
|
||||||
SKULLC_ASSERT_DEBUG(true);
|
|
||||||
|
|
||||||
CHECK(assert_flag == false);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Assert safe without NDEBUG gets triggered if check is false.", "[utility],[assert]")
|
|
||||||
{
|
|
||||||
assert_flag = false;
|
|
||||||
Utility::Assert::setHandler(assertCallback);
|
|
||||||
|
|
||||||
SKULLC_ASSERT_SAFE(false);
|
|
||||||
|
|
||||||
CHECK(assert_flag == true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Assert safe without NDEBUG doesn't get triggered if check is true.", "[utility],[assert]")
|
|
||||||
{
|
|
||||||
assert_flag = false;
|
|
||||||
Utility::Assert::setHandler(assertCallback);
|
|
||||||
|
|
||||||
SKULLC_ASSERT_SAFE(true);
|
|
||||||
|
|
||||||
CHECK(assert_flag == false);
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 29.06.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
#define NDEBUG
|
|
||||||
#define NDEBUG_WAS_NOT_DEFINED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "utility_assert.hpp"
|
|
||||||
|
|
||||||
#ifdef NDEBUG_WAS_NOT_DEFINED
|
|
||||||
#undef NDEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
bool assert_flag = false;
|
|
||||||
|
|
||||||
void assertCallback(const char*, const char*, const int)
|
|
||||||
{
|
|
||||||
assert_flag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace
|
|
||||||
|
|
||||||
TEST_CASE("Assert debug with NDEBUG is a null-opt.", "[utility],[assert]")
|
|
||||||
{
|
|
||||||
assert_flag = false;
|
|
||||||
Utility::Assert::setHandler(assertCallback);
|
|
||||||
|
|
||||||
int check_number = 0;
|
|
||||||
SKULLC_ASSERT_DEBUG(check_number++ == 0);
|
|
||||||
|
|
||||||
CHECK(check_number == 0);
|
|
||||||
CHECK(assert_flag == false);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Assert safe with NDEBUG gets triggered if check is false.", "[utility],[assert]")
|
|
||||||
{
|
|
||||||
assert_flag = false;
|
|
||||||
Utility::Assert::setHandler(assertCallback);
|
|
||||||
|
|
||||||
int check_number = 0;
|
|
||||||
SKULLC_ASSERT_SAFE(check_number++ == 1);
|
|
||||||
|
|
||||||
CHECK(check_number == 1);
|
|
||||||
CHECK(assert_flag == true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Assert safe with NDEBUG doesn't get triggered if check is true.", "[utility],[assert]")
|
|
||||||
{
|
|
||||||
assert_flag = false;
|
|
||||||
Utility::Assert::setHandler(assertCallback);
|
|
||||||
|
|
||||||
SKULLC_ASSERT_SAFE(true);
|
|
||||||
|
|
||||||
CHECK(assert_flag == false);
|
|
||||||
}
|
|
||||||
@ -109,6 +109,7 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]")
|
|||||||
SECTION("Long press is identified properly.")
|
SECTION("Long press is identified properly.")
|
||||||
{
|
{
|
||||||
HAL::millis = Button::TIMEOUT_LONG_PRESS + 2;
|
HAL::millis = Button::TIMEOUT_LONG_PRESS + 2;
|
||||||
|
Gpio::set = false;
|
||||||
|
|
||||||
button.update();
|
button.update();
|
||||||
|
|
||||||
@ -120,16 +121,6 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]")
|
|||||||
button.update();
|
button.update();
|
||||||
|
|
||||||
REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED);
|
REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED);
|
||||||
|
|
||||||
SECTION("Releasing keeps NOT_PRESSED state.")
|
|
||||||
{
|
|
||||||
HAL::millis += 1;
|
|
||||||
Gpio::set = false;
|
|
||||||
|
|
||||||
button.update();
|
|
||||||
|
|
||||||
REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,68 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 10/12/22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
|
|
||||||
#include "utility_bytes.hpp"
|
|
||||||
|
|
||||||
TEST_CASE("zeroInitialized creates an appropriate struct.")
|
|
||||||
{
|
|
||||||
struct TestStruct
|
|
||||||
{
|
|
||||||
int a;
|
|
||||||
char b;
|
|
||||||
int c;
|
|
||||||
};
|
|
||||||
|
|
||||||
SECTION("Return by value variant returns a struct where all members are 0.")
|
|
||||||
{
|
|
||||||
const TestStruct test = Utility::zeroInitialized<TestStruct>();
|
|
||||||
REQUIRE(test.a == 0);
|
|
||||||
REQUIRE(test.b == 0);
|
|
||||||
REQUIRE(test.c == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Pass by reference variant initializes the struct to all 0's.")
|
|
||||||
{
|
|
||||||
TestStruct test;
|
|
||||||
Utility::zeroInitialized(test);
|
|
||||||
|
|
||||||
REQUIRE(test.a == 0);
|
|
||||||
REQUIRE(test.b == 0);
|
|
||||||
REQUIRE(test.c == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Pass by pointer variant initializes the struct to all 0's.")
|
|
||||||
{
|
|
||||||
TestStruct test;
|
|
||||||
Utility::zeroInitialized(&test);
|
|
||||||
|
|
||||||
REQUIRE(test.a == 0);
|
|
||||||
REQUIRE(test.b == 0);
|
|
||||||
REQUIRE(test.c == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("arrayToType constructs a type appropriately.")
|
|
||||||
{
|
|
||||||
const std::uint32_t initial_value = 0x12345678;
|
|
||||||
|
|
||||||
SECTION("Using C-arrays the operation works appropriately.")
|
|
||||||
{
|
|
||||||
std::uint8_t raw_data[4] = {0};
|
|
||||||
std::memcpy(raw_data, &initial_value, 4);
|
|
||||||
|
|
||||||
const std::uint32_t output = Utility::arrayToType<std::uint32_t, 4>(raw_data);
|
|
||||||
REQUIRE(output == initial_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Using C-arrays the operation works appropriately.")
|
|
||||||
{
|
|
||||||
std::array<std::uint8_t, 4> raw_data = {0, 0, 0, 0};
|
|
||||||
std::memcpy(raw_data.data(), &initial_value, 4);
|
|
||||||
|
|
||||||
const std::uint32_t output = Utility::arrayToType<std::uint32_t>(raw_data);
|
|
||||||
REQUIRE(output == initial_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 15.07.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
|
|
||||||
#include "utility_enum_helpers.hpp"
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
enum class TestEnum : unsigned
|
|
||||||
{
|
|
||||||
FLAG_0 = 1,
|
|
||||||
FLAG_1 = 2,
|
|
||||||
FLAG_2 = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
SKULLC_ENUM_DECLARE_BITFLAG_OPERATORS(TestEnum)
|
|
||||||
|
|
||||||
}// namespace
|
|
||||||
|
|
||||||
TEST_CASE("OR operator works as expected.", "[utility],[enum_helpers]")
|
|
||||||
{
|
|
||||||
const TestEnum a = TestEnum::FLAG_0;
|
|
||||||
const TestEnum b = TestEnum::FLAG_1;
|
|
||||||
|
|
||||||
const TestEnum sum = a | b;
|
|
||||||
CHECK(unsigned(sum) == 3u);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("AND operator works as expected.", "[utility],[enum_helpers]")
|
|
||||||
{
|
|
||||||
const TestEnum a = TestEnum::FLAG_0 | TestEnum::FLAG_1;
|
|
||||||
|
|
||||||
const TestEnum masked_1 = a & TestEnum::FLAG_0;
|
|
||||||
CHECK(masked_1 == TestEnum::FLAG_0);
|
|
||||||
|
|
||||||
const TestEnum masked_2 = a & TestEnum::FLAG_2;
|
|
||||||
CHECK(masked_2 != TestEnum::FLAG_2);
|
|
||||||
}
|
|
||||||
@ -1,191 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 12/11/22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
|
|
||||||
#include "utility_filters.hpp"
|
|
||||||
|
|
||||||
TEST_CASE("LowPassFilter works as expected.")
|
|
||||||
{
|
|
||||||
const int threshold = 50;
|
|
||||||
Utility::LowPassFilter<int> filter{threshold};
|
|
||||||
|
|
||||||
SECTION("Initial value is 0.")
|
|
||||||
{
|
|
||||||
REQUIRE(filter.currentValue() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Passing a number higher than threshold gets filtered.")
|
|
||||||
{
|
|
||||||
filter.update(60);
|
|
||||||
REQUIRE(filter.currentValue() == 0);
|
|
||||||
|
|
||||||
SECTION("Filter updates state as expected.")
|
|
||||||
{
|
|
||||||
filter.update(40);
|
|
||||||
REQUIRE(filter.currentValue() == 40);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Passing a number lower than threshold passes filter.")
|
|
||||||
{
|
|
||||||
filter.update(40);
|
|
||||||
REQUIRE(filter.currentValue() == 40);
|
|
||||||
|
|
||||||
SECTION("Filter retains state as expected.")
|
|
||||||
{
|
|
||||||
filter.update(60);
|
|
||||||
REQUIRE(filter.currentValue() == 40);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("HighPassFilter works as expected.")
|
|
||||||
{
|
|
||||||
const int threshold = 50;
|
|
||||||
Utility::HighPassFilter<int> filter{threshold};
|
|
||||||
|
|
||||||
SECTION("Initial value is 0.")
|
|
||||||
{
|
|
||||||
REQUIRE(filter.currentValue() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Passing a number higher than threshold passes filter.")
|
|
||||||
{
|
|
||||||
filter.update(60);
|
|
||||||
REQUIRE(filter.currentValue() == 60);
|
|
||||||
|
|
||||||
SECTION("Filter retains state as expected.")
|
|
||||||
{
|
|
||||||
filter.update(40);
|
|
||||||
REQUIRE(filter.currentValue() == 60);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Passing a number lower than threshold gets filtered.")
|
|
||||||
{
|
|
||||||
filter.update(40);
|
|
||||||
REQUIRE(filter.currentValue() == 0);
|
|
||||||
|
|
||||||
SECTION("Filter updates state as expected.")
|
|
||||||
{
|
|
||||||
filter.update(60);
|
|
||||||
REQUIRE(filter.currentValue() == 60);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Two filters can be linked together.")
|
|
||||||
{
|
|
||||||
const int threshold_low = 40;
|
|
||||||
const int threshold_high = 50;
|
|
||||||
|
|
||||||
Utility::LowPassFilter<int> low_pass{threshold_high};
|
|
||||||
Utility::HighPassFilter<int> high_pass{threshold_low};
|
|
||||||
|
|
||||||
high_pass.assignPrecedingFilter(low_pass);
|
|
||||||
|
|
||||||
SECTION("Initial value is 0.")
|
|
||||||
{
|
|
||||||
REQUIRE(high_pass.currentValue() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Values outside of band are filtered out.")
|
|
||||||
{
|
|
||||||
high_pass.update(30);
|
|
||||||
REQUIRE(high_pass.currentValue() == 0);
|
|
||||||
|
|
||||||
high_pass.update(60);
|
|
||||||
REQUIRE(high_pass.currentValue() == 0);
|
|
||||||
|
|
||||||
SECTION("Filter updates with in-band value.")
|
|
||||||
{
|
|
||||||
high_pass.update(45);
|
|
||||||
REQUIRE(high_pass.currentValue() == 45);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Values inside of band are passed through.")
|
|
||||||
{
|
|
||||||
high_pass.update(44);
|
|
||||||
REQUIRE(high_pass.currentValue() == 44);
|
|
||||||
|
|
||||||
high_pass.update(46);
|
|
||||||
REQUIRE(high_pass.currentValue() == 46);
|
|
||||||
|
|
||||||
SECTION("Out of band values are filtered out.")
|
|
||||||
{
|
|
||||||
high_pass.update(30);
|
|
||||||
REQUIRE(high_pass.currentValue() == 46);
|
|
||||||
|
|
||||||
high_pass.update(60);
|
|
||||||
REQUIRE(high_pass.currentValue() == 46);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Decoupling filters works as expected.")
|
|
||||||
{
|
|
||||||
high_pass.update(60);
|
|
||||||
REQUIRE(high_pass.currentValue() == 0);
|
|
||||||
|
|
||||||
high_pass.clearPrecedingFilter();
|
|
||||||
high_pass.update(60);
|
|
||||||
REQUIRE(high_pass.currentValue() == 60);
|
|
||||||
|
|
||||||
high_pass.update(30);
|
|
||||||
REQUIRE(high_pass.currentValue() == 60);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Median filter works as expected.")
|
|
||||||
{
|
|
||||||
Utility::MedianFilter<int, 5> filter;
|
|
||||||
|
|
||||||
SECTION("Initial value is 0.")
|
|
||||||
{
|
|
||||||
REQUIRE(filter.currentValue() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("The median is filtered appropriately.")
|
|
||||||
{
|
|
||||||
std::array<int, 5> data = {5, 1, 4, 3, 2};
|
|
||||||
std::array<int, 5> expected_median = {0, 0, 1, 3, 3};
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
const int x = data[i];
|
|
||||||
filter.update(x);
|
|
||||||
REQUIRE(filter.currentValue() == expected_median[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Rolling average filter works as expected.")
|
|
||||||
{
|
|
||||||
Utility::RollingAverageFilter<int> filter{5};
|
|
||||||
|
|
||||||
SECTION("Initial value is 0.")
|
|
||||||
{
|
|
||||||
REQUIRE(filter.currentValue() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Value below step size is filtered out for int filter.")
|
|
||||||
{
|
|
||||||
filter.update(4);
|
|
||||||
REQUIRE(filter.currentValue() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Value is updated appropriately.")
|
|
||||||
{
|
|
||||||
filter.update(20);
|
|
||||||
REQUIRE(filter.currentValue() == 20 / 5);
|
|
||||||
|
|
||||||
SECTION("Saturation is reached appropriately")
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 5 * 2 - 1; i++)
|
|
||||||
filter.update(20);
|
|
||||||
|
|
||||||
REQUIRE(filter.currentValue() == 20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "utility_fixedpoint.hpp"
|
#include "utility_fixedpoint.hpp"
|
||||||
|
|
||||||
using FP15 = Utility::FixedPoint<std::uint32_t, 15>;
|
using FP15 = Peripherals::FixedPoint<std::uint32_t, 15>;
|
||||||
|
|
||||||
TEST_CASE("FP constructors work appropriately.", "[utility],[fixed point]")
|
TEST_CASE("FP constructors work appropriately.", "[utility],[fixed point]")
|
||||||
{
|
{
|
||||||
@ -172,7 +172,7 @@ TEST_CASE("FP arithmetic operators work appropriately.", "[utility],[fixed point
|
|||||||
TEMPLATE_TEST_CASE("FP converting back to integral values works appropriately.", "[utility],[fixed point]",
|
TEMPLATE_TEST_CASE("FP converting back to integral values works appropriately.", "[utility],[fixed point]",
|
||||||
std::int8_t, std::int16_t, std::int32_t)
|
std::int8_t, std::int16_t, std::int32_t)
|
||||||
{
|
{
|
||||||
using FP = Utility::FixedPoint<TestType, 4>;
|
using FP = Peripherals::FixedPoint<TestType, 4>;
|
||||||
|
|
||||||
FP fp(4);
|
FP fp(4);
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
#include <catch2/catch.hpp>
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
#include <utility_function.hpp>
|
#include <utility_function.hpp>
|
||||||
#include <utility_tag.hpp>
|
|
||||||
|
|
||||||
TEST_CASE("Function calls function appropriately.", "[utility],[function]")
|
TEST_CASE("Function calls function appropriately.", "[utility],[function]")
|
||||||
{
|
{
|
||||||
@ -39,26 +38,6 @@ TEST_CASE("Function passes arguments appropriately.", "[utility],[function]")
|
|||||||
REQUIRE(bool_to_set == true);
|
REQUIRE(bool_to_set == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Function works with toStaticFunction.", "[utility],[function]")
|
|
||||||
{
|
|
||||||
static int int_to_set = 0;
|
|
||||||
static bool bool_to_set = false;
|
|
||||||
|
|
||||||
auto func_to_call = [](const int i, const bool b) {
|
|
||||||
int_to_set = i;
|
|
||||||
bool_to_set = b;
|
|
||||||
};
|
|
||||||
|
|
||||||
Utility::Function<void(int, bool)> function(func_to_call);
|
|
||||||
using signature = void (*)(int, bool);
|
|
||||||
|
|
||||||
signature func_pointer = function.toStaticFunction<SKULLC_TAG>();
|
|
||||||
func_pointer(10, true);
|
|
||||||
|
|
||||||
REQUIRE(int_to_set == 10);
|
|
||||||
REQUIRE(bool_to_set == true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("FunctionOwned calls function appropriately.", "[utility],[function]")
|
TEST_CASE("FunctionOwned calls function appropriately.", "[utility],[function]")
|
||||||
{
|
{
|
||||||
struct S
|
struct S
|
||||||
@ -103,29 +82,3 @@ TEST_CASE("FunctionOwned passes arguments appropriately.", "[utility],[function]
|
|||||||
REQUIRE(subject.int_to_set == 10);
|
REQUIRE(subject.int_to_set == 10);
|
||||||
REQUIRE(subject.bool_to_set == false);
|
REQUIRE(subject.bool_to_set == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("FunctionOwned works with toStaticFunction.", "[utility],[function]")
|
|
||||||
{
|
|
||||||
struct S
|
|
||||||
{
|
|
||||||
int int_to_set = 0;
|
|
||||||
bool bool_to_set = true;
|
|
||||||
|
|
||||||
void toCall(const int i, const bool b)
|
|
||||||
{
|
|
||||||
int_to_set = i;
|
|
||||||
bool_to_set = b;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
S subject;
|
|
||||||
|
|
||||||
Utility::FunctionOwned<S, void(int, bool)> function(subject, &S::toCall);
|
|
||||||
using signature = void (*)(int, bool);
|
|
||||||
|
|
||||||
signature func_pointer = function.toStaticFunction<SKULLC_TAG>();
|
|
||||||
func_pointer(10, true);
|
|
||||||
|
|
||||||
REQUIRE(subject.int_to_set == 10);
|
|
||||||
REQUIRE(subject.bool_to_set == true);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -15,15 +15,15 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include <threads_actor_thread.hpp>
|
#include "threads_primitivethread.hpp"
|
||||||
#include <threads_iactor.hpp>
|
#include "threads_signal.hpp"
|
||||||
#include <threads_signal.hpp>
|
#include "threads_timer.hpp"
|
||||||
|
|
||||||
namespace Threads
|
namespace Threads
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename CRTP, typename... Ts>
|
template<typename CRTP, typename... Ts>
|
||||||
class Actor : public IActor
|
class Actor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = std::variant<Ts...>;
|
using value_type = std::variant<Ts...>;
|
||||||
@ -31,11 +31,14 @@ public:
|
|||||||
static_assert(std::is_default_constructible_v<value_type>, "Ts must be default constructible.");
|
static_assert(std::is_default_constructible_v<value_type>, "Ts must be default constructible.");
|
||||||
static_assert(std::is_trivially_copyable_v<value_type>, "Ts must be trivially copyable.");
|
static_assert(std::is_trivially_copyable_v<value_type>, "Ts must be trivially copyable.");
|
||||||
|
|
||||||
Actor() : IActor(sizeof(value_type)),
|
Actor(const std::uint32_t queue_size, const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
||||||
signal_(this)
|
: thread_(&Actor<CRTP, Ts...>::threadHandler_, this, name, priority, stack_size),
|
||||||
{}
|
msg_queue_(xQueueCreate(queue_size, sizeof(value_type))), signal_(this)
|
||||||
|
{
|
||||||
|
assert(msg_queue_);
|
||||||
|
}
|
||||||
|
|
||||||
~Actor() override {}
|
virtual ~Actor() {}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct Signal : Signallable<T>
|
struct Signal : Signallable<T>
|
||||||
@ -48,21 +51,20 @@ public:
|
|||||||
: q_(q)
|
: q_(q)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Signal(const Signal&) = default;
|
|
||||||
Signal(Signal&&) = default;
|
|
||||||
Signal& operator=(const Signal&) = default;
|
|
||||||
Signal& operator=(Signal&&) = default;
|
|
||||||
|
|
||||||
void emit(const T& data) override
|
void emit(const T& data) override
|
||||||
{
|
{
|
||||||
parent::value_type to_send = data;
|
parent::value_type to_send = data;
|
||||||
q_->dispatchEvent(to_send);
|
xQueueSend(q_->msg_queue_, &to_send, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseType_t emitFromIsr(const T& data) override
|
BaseType_t emitFromIsr(const T& data) override
|
||||||
{
|
{
|
||||||
parent::value_type to_send = data;
|
parent::value_type to_send = data;
|
||||||
return q_->dispatchEventFromIsr(to_send);
|
BaseType_t was_awoken = pdFALSE;
|
||||||
|
|
||||||
|
xQueueSendFromISR(q_->msg_queue_, &to_send, &was_awoken);
|
||||||
|
|
||||||
|
return was_awoken;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -77,25 +79,18 @@ public:
|
|||||||
return Signal<U>(this);
|
return Signal<U>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename U>
|
|
||||||
Signal<U>* getAllocatedSignal()
|
|
||||||
{
|
|
||||||
static_assert(std::is_convertible_v<U, value_type>, "value_type cannot be constructed from U.");
|
|
||||||
|
|
||||||
return new Signal<U>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Signallable<value_type>* getStaticSignal()
|
Signallable<value_type>* getStaticSignal()
|
||||||
{
|
{
|
||||||
return &signal_;
|
return &signal_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispatchSignal(const char* data) final
|
protected:
|
||||||
{
|
virtual void init() = 0;
|
||||||
dispatchImpl_(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
PrimitiveThread thread_;
|
||||||
|
QueueHandle_t msg_queue_;
|
||||||
|
|
||||||
struct Visitor_
|
struct Visitor_
|
||||||
{
|
{
|
||||||
using parent = Actor<CRTP, Ts...>;
|
using parent = Actor<CRTP, Ts...>;
|
||||||
@ -113,15 +108,113 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void dispatchImpl_(const char* data)
|
void operator()()
|
||||||
{
|
{
|
||||||
const value_type* signal_data = reinterpret_cast<const value_type*>(data);
|
init();
|
||||||
std::visit(Visitor_{this}, *signal_data);
|
|
||||||
|
value_type data{};
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const BaseType_t got = xQueueReceive(msg_queue_, &data, portMAX_DELAY);
|
||||||
|
|
||||||
|
if (got == pdTRUE)
|
||||||
|
{
|
||||||
|
std::visit(Visitor_{this}, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void threadHandler_(void* d)
|
||||||
|
{
|
||||||
|
auto* dd = static_cast<Actor<CRTP, Ts...>*>(d);
|
||||||
|
(*dd)();
|
||||||
}
|
}
|
||||||
|
|
||||||
Signal<value_type> signal_;
|
Signal<value_type> signal_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename CRTP>
|
||||||
|
class Actor<CRTP, void>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = void;
|
||||||
|
|
||||||
|
Actor(const std::uint32_t queue_size, const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
||||||
|
: thread_([](void* d) {
|
||||||
|
auto* dd = static_cast<Actor<CRTP, value_type>*>(d);
|
||||||
|
(*dd)();
|
||||||
|
},
|
||||||
|
this, name, priority, stack_size),
|
||||||
|
msg_queue_(xQueueCreate(queue_size, sizeof(int))), signal_(*this)
|
||||||
|
{
|
||||||
|
assert(msg_queue_);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Actor() {}
|
||||||
|
|
||||||
|
Signallable<value_type>* getSignal()
|
||||||
|
{
|
||||||
|
return &signal_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void init() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PrimitiveThread thread_;
|
||||||
|
QueueHandle_t msg_queue_;
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
|
||||||
|
int data;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const BaseType_t got = xQueueReceive(msg_queue_, &data, portMAX_DELAY);
|
||||||
|
|
||||||
|
if (got == pdTRUE)
|
||||||
|
{
|
||||||
|
auto* crtp = static_cast<CRTP*>(this);
|
||||||
|
crtp->onSignal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend struct Signal_;
|
||||||
|
|
||||||
|
struct Signal_ : Signallable<value_type>
|
||||||
|
{
|
||||||
|
using parent = Actor<CRTP, void>;
|
||||||
|
parent& q;
|
||||||
|
|
||||||
|
explicit Signal_(parent& q)
|
||||||
|
: q(q)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void emit() override
|
||||||
|
{
|
||||||
|
const int data = 0;
|
||||||
|
|
||||||
|
xQueueSend(q.msg_queue_, &data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseType_t emitFromIsr() override
|
||||||
|
{
|
||||||
|
const int data = 0;
|
||||||
|
|
||||||
|
BaseType_t was_awoken = pdFALSE;
|
||||||
|
xQueueSendFromISR(q.msg_queue_, &data, &was_awoken);
|
||||||
|
|
||||||
|
return was_awoken;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Signal_ signal_;
|
||||||
|
};
|
||||||
|
|
||||||
}// namespace Threads
|
}// namespace Threads
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* threads_actor_output.hpp
|
|
||||||
*
|
|
||||||
* Created on: Oct 24, 2021
|
|
||||||
* Author: erki
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SKULLC_THREADS_ACTOR_OUTPUT_HPP_
|
|
||||||
#define SKULLC_THREADS_ACTOR_OUTPUT_HPP_
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <optional>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
#include <threads_signal.hpp>
|
|
||||||
|
|
||||||
namespace Threads
|
|
||||||
{
|
|
||||||
|
|
||||||
template<std::size_t N, typename... Ts>
|
|
||||||
class ActorOutput
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
using signal_variant = std::variant<std::monostate, Signallable<Ts>*...>;
|
|
||||||
std::array<signal_variant, N> stored_signals_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ActorOutput() = default;
|
|
||||||
|
|
||||||
ActorOutput(const ActorOutput&) = delete;
|
|
||||||
ActorOutput(ActorOutput&&) = delete;
|
|
||||||
ActorOutput& operator=(const ActorOutput&) = delete;
|
|
||||||
ActorOutput& operator=(ActorOutput&&) = delete;
|
|
||||||
|
|
||||||
using connection_type = std::optional<typename decltype(stored_signals_)::iterator>;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
connection_type addConnection(Signallable<T>* signal)
|
|
||||||
{
|
|
||||||
static_assert(std::disjunction_v<std::is_same<T, Ts>...>, "T is not a member of Ts.");
|
|
||||||
|
|
||||||
for (auto it = stored_signals_.begin(); it != stored_signals_.end(); ++it)
|
|
||||||
{
|
|
||||||
auto& storage = *it;
|
|
||||||
|
|
||||||
if (std::get_if<std::monostate>(&storage))
|
|
||||||
{
|
|
||||||
storage = signal_variant{signal};
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool removeConnection(connection_type& connection)
|
|
||||||
{
|
|
||||||
if (!connection)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for (auto it = stored_signals_.begin(); it != stored_signals_.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it == *connection)
|
|
||||||
{
|
|
||||||
delete *it;
|
|
||||||
|
|
||||||
*it = std::monostate{};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
template<typename T>
|
|
||||||
void emitOutput(const T& data)
|
|
||||||
{
|
|
||||||
static_assert(std::disjunction_v<std::is_same<T, Ts>...>, "T is not a member of Ts.");
|
|
||||||
|
|
||||||
for (auto& s : stored_signals_)
|
|
||||||
{
|
|
||||||
if (auto** signal = std::get_if<Signallable<T>*>(&s))
|
|
||||||
(*signal)->emit(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void emitOutputFromIsr(const T& data)
|
|
||||||
{
|
|
||||||
static_assert(std::disjunction_v<std::is_same<T, Ts>...>, "T is not a member of Ts.");
|
|
||||||
|
|
||||||
for (auto& s : stored_signals_)
|
|
||||||
{
|
|
||||||
if (auto** signal = std::get_if<Signallable<T>*>(&s))
|
|
||||||
(*signal)->emitFromIsr(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, typename Sender, typename Receiver>
|
|
||||||
auto connectActors(Sender* sender, Receiver* receiver)
|
|
||||||
{
|
|
||||||
auto* signal = receiver->template getAllocatedSignal<T>();
|
|
||||||
return sender->addConnection(signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename Sender, typename Receiver>
|
|
||||||
auto connectActors(Sender& sender, Receiver& receiver)
|
|
||||||
{
|
|
||||||
auto* signal = receiver.template getAllocatedSignal<T>();
|
|
||||||
return sender.addConnection(signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace Threads
|
|
||||||
|
|
||||||
#endif// SKULLC_THREADS_ACTOR_OUTPUT_HPP_
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* threads_actor_thread.hpp
|
|
||||||
*
|
|
||||||
* Created on: Nov 13, 2021
|
|
||||||
* Author: erki
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SKULLC_THREADS_ACTOR_THREAD_HPP_
|
|
||||||
#define SKULLC_THREADS_ACTOR_THREAD_HPP_
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <freertos_os2.h>
|
|
||||||
#include <queue.h>
|
|
||||||
|
|
||||||
#include <threads_thread.hpp>
|
|
||||||
|
|
||||||
namespace Threads
|
|
||||||
{
|
|
||||||
|
|
||||||
class IActor;
|
|
||||||
|
|
||||||
class ActorThread : public Thread<ActorThread>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ActorThread(const std::size_t queue_length, const char* name, const osPriority_t priority, const std::uint32_t stack_size);
|
|
||||||
|
|
||||||
void operator()();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init_();
|
|
||||||
void doMessageLoop_();
|
|
||||||
|
|
||||||
friend class IActor;
|
|
||||||
std::size_t acceptActor_(IActor* actor);
|
|
||||||
|
|
||||||
std::size_t queue_length_;
|
|
||||||
std::size_t queue_item_length_ = 0;
|
|
||||||
|
|
||||||
QueueHandle_t queue_ = nullptr;
|
|
||||||
char* rx_buffer_ = nullptr;
|
|
||||||
char* tx_buffer_ = nullptr;
|
|
||||||
std::array<IActor*, 32> actors_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace Threads
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* SKULLC_THREADS_ACTOR_THREAD_HPP_ */
|
|
||||||
@ -65,8 +65,7 @@ public:
|
|||||||
if (!notified)
|
if (!notified)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return current_value_;
|
return current_value_;
|
||||||
}
|
}
|
||||||
@ -79,8 +78,7 @@ public:
|
|||||||
if (!waiting_thread_)
|
if (!waiting_thread_)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return waiting_thread_->notify(0, eNoAction);
|
return waiting_thread_->notify(0, eNoAction);
|
||||||
}
|
}
|
||||||
@ -93,8 +91,7 @@ public:
|
|||||||
if (!waiting_thread_)
|
if (!waiting_thread_)
|
||||||
{
|
{
|
||||||
return {false, pdFALSE};
|
return {false, pdFALSE};
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
|
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
|
||||||
(void) discard;
|
(void) discard;
|
||||||
@ -161,8 +158,7 @@ struct ExclusiveSignal<void> : public Signallable<void>
|
|||||||
if (!waiting_thread_)
|
if (!waiting_thread_)
|
||||||
{
|
{
|
||||||
return pdFALSE;
|
return pdFALSE;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
|
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
|
||||||
(void) discard;
|
(void) discard;
|
||||||
|
|||||||
@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* threads_iactor.hpp
|
|
||||||
*
|
|
||||||
* Created on: Nov 14, 2021
|
|
||||||
* Author: erki
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SKULLC_THREADS_IACTOR_HPP_
|
|
||||||
#define SKULLC_THREADS_IACTOR_HPP_
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <freertos_os2.h>
|
|
||||||
|
|
||||||
namespace Threads
|
|
||||||
{
|
|
||||||
|
|
||||||
class ActorThread;
|
|
||||||
|
|
||||||
class IActor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IActor(const std::size_t signal_size)
|
|
||||||
: signal_size_(signal_size)
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual ~IActor() {}
|
|
||||||
|
|
||||||
virtual void init() = 0;
|
|
||||||
virtual void dispatchSignal(const char* data) = 0;
|
|
||||||
|
|
||||||
void moveToThread(ActorThread* thread);
|
|
||||||
|
|
||||||
std::size_t actorIndex() const
|
|
||||||
{
|
|
||||||
return actor_index_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t signalSize() const
|
|
||||||
{
|
|
||||||
return signal_size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
template<typename T>
|
|
||||||
void dispatchEvent(const T& data)
|
|
||||||
{
|
|
||||||
dispatchEventImpl_(&data, sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
BaseType_t dispatchEventFromIsr(const T& data)
|
|
||||||
{
|
|
||||||
return dispatchEventFromIsrImpl_(&data, sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ActorThread* parent_thread_ = nullptr;
|
|
||||||
std::size_t actor_index_ = 0;
|
|
||||||
std::size_t signal_size_;
|
|
||||||
|
|
||||||
void dispatchEventImpl_(const void* data, const std::size_t data_size);
|
|
||||||
BaseType_t dispatchEventFromIsrImpl_(const void* data, const std::size_t data_size);
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace Threads
|
|
||||||
|
|
||||||
#endif /* SKULLC_THREADS_IACTOR_HPP_ */
|
|
||||||
@ -11,7 +11,6 @@
|
|||||||
#include <cmsis_os.h>
|
#include <cmsis_os.h>
|
||||||
#include <freertos_os2.h>
|
#include <freertos_os2.h>
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
|||||||
@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* threads_actor_thread.cpp
|
|
||||||
*
|
|
||||||
* Created on: Nov 13, 2021
|
|
||||||
* Author: erki
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "threads_actor_thread.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include <threads_iactor.hpp>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
struct SignalMessage
|
|
||||||
{
|
|
||||||
std::size_t actor_index;
|
|
||||||
const char* data;
|
|
||||||
|
|
||||||
static SignalMessage fromData(char* data)
|
|
||||||
{
|
|
||||||
SignalMessage message;
|
|
||||||
std::memcpy(&message.actor_index, data, sizeof(message.actor_index));
|
|
||||||
message.data = data + sizeof(message.actor_index);
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace
|
|
||||||
|
|
||||||
namespace Threads
|
|
||||||
{
|
|
||||||
|
|
||||||
ActorThread::ActorThread(const std::size_t queue_length, const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
|
||||||
: Thread<ActorThread>(name, priority, stack_size), queue_length_(queue_length), queue_(nullptr)
|
|
||||||
{
|
|
||||||
actors_.fill(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActorThread::operator()()
|
|
||||||
{
|
|
||||||
init_();
|
|
||||||
|
|
||||||
osThreadYield();
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
doMessageLoop_();
|
|
||||||
|
|
||||||
osThreadYield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActorThread::init_()
|
|
||||||
{
|
|
||||||
for (IActor* actor : actors_)
|
|
||||||
{
|
|
||||||
if (actor != nullptr)
|
|
||||||
{
|
|
||||||
queue_item_length_ = std::max(queue_item_length_, actor->signalSize());
|
|
||||||
|
|
||||||
actor->init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
queue_ = xQueueCreate(queue_length_, queue_item_length_ + sizeof(std::size_t));
|
|
||||||
assert(queue_ != nullptr);
|
|
||||||
|
|
||||||
rx_buffer_ = new char[queue_item_length_ + sizeof(std::size_t)];
|
|
||||||
tx_buffer_ = new char[queue_item_length_ + sizeof(std::size_t)];
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActorThread::doMessageLoop_()
|
|
||||||
{
|
|
||||||
const BaseType_t has_data = xQueueReceive(queue_, rx_buffer_, portMAX_DELAY);
|
|
||||||
|
|
||||||
if (has_data == pdTRUE)
|
|
||||||
{
|
|
||||||
const SignalMessage message = SignalMessage::fromData(rx_buffer_);
|
|
||||||
assert(message.actor_index <= actors_.size());
|
|
||||||
|
|
||||||
IActor* actor = actors_[message.actor_index];
|
|
||||||
assert(actor != nullptr);
|
|
||||||
|
|
||||||
actor->dispatchSignal(message.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t ActorThread::acceptActor_(IActor* actor)
|
|
||||||
{
|
|
||||||
for (auto it = actors_.begin(); it != actors_.end(); ++it)
|
|
||||||
{
|
|
||||||
if (*it == nullptr)
|
|
||||||
{
|
|
||||||
*it = actor;
|
|
||||||
return std::distance(actors_.begin(), it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(false);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace Threads
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* threads_iactor.cpp
|
|
||||||
*
|
|
||||||
* Created on: Nov 14, 2021
|
|
||||||
* Author: erki
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "threads_iactor.hpp"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include <queue.h>
|
|
||||||
|
|
||||||
#include "threads_actor_thread.hpp"
|
|
||||||
|
|
||||||
namespace Threads
|
|
||||||
{
|
|
||||||
|
|
||||||
void IActor::moveToThread(ActorThread* thread)
|
|
||||||
{
|
|
||||||
actor_index_ = thread->acceptActor_(this);
|
|
||||||
parent_thread_ = thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IActor::dispatchEventImpl_(const void* data, const std::size_t data_size)
|
|
||||||
{
|
|
||||||
taskENTER_CRITICAL();
|
|
||||||
|
|
||||||
std::memcpy(parent_thread_->tx_buffer_, &actor_index_, sizeof(std::size_t));
|
|
||||||
char* data_dest = parent_thread_->tx_buffer_ + sizeof(std::size_t);
|
|
||||||
std::memcpy(data_dest, data, data_size);
|
|
||||||
|
|
||||||
xQueueSend(parent_thread_->queue_, parent_thread_->tx_buffer_, 0);
|
|
||||||
|
|
||||||
taskEXIT_CRITICAL();
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseType_t IActor::dispatchEventFromIsrImpl_(const void* data, const std::size_t data_size)
|
|
||||||
{
|
|
||||||
const auto isr_data = taskENTER_CRITICAL_FROM_ISR();
|
|
||||||
|
|
||||||
std::memcpy(parent_thread_->tx_buffer_, &actor_index_, sizeof(std::size_t));
|
|
||||||
char* data_dest = parent_thread_->tx_buffer_ + sizeof(std::size_t);
|
|
||||||
std::memcpy(data_dest, data, data_size);
|
|
||||||
|
|
||||||
BaseType_t task_awoken = pdFALSE;
|
|
||||||
xQueueSendFromISR(parent_thread_->queue_, parent_thread_->tx_buffer_, &task_awoken);
|
|
||||||
|
|
||||||
taskEXIT_CRITICAL_FROM_ISR(isr_data);
|
|
||||||
|
|
||||||
return task_awoken;
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace Threads
|
|
||||||
@ -7,9 +7,7 @@
|
|||||||
|
|
||||||
#include "threads_primitivethread.hpp"
|
#include "threads_primitivethread.hpp"
|
||||||
|
|
||||||
#include "utility_bytes.hpp"
|
#include "peripherals_utility.hpp"
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
|
#ifdef INCLUDE_xTaskGetCurrentTaskHandle
|
||||||
#define ASSERT_IS_CURRENT() \
|
#define ASSERT_IS_CURRENT() \
|
||||||
@ -101,14 +99,14 @@ PrimitiveThread::PrimitiveThread(osThreadFunc_t function, void* runner_data, con
|
|||||||
|
|
||||||
|
|
||||||
PrimitiveThread::PrimitiveThread(TaskHandle_t threadHandle)
|
PrimitiveThread::PrimitiveThread(TaskHandle_t threadHandle)
|
||||||
: thread_id(static_cast<osThreadId_t>(threadHandle)), attributes(Utility::zeroInitialized<osThreadAttr_t>())
|
: thread_id(static_cast<osThreadId_t>(threadHandle)), attributes(Peripherals::zeroInitialized<osThreadAttr_t>())
|
||||||
{
|
{
|
||||||
assert(thread_id != nullptr);
|
assert(thread_id != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
osThreadAttr_t PrimitiveThread::constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
osThreadAttr_t PrimitiveThread::constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
||||||
{
|
{
|
||||||
auto attrs = Utility::zeroInitialized<osThreadAttr_t>();
|
auto attrs = Peripherals::zeroInitialized<osThreadAttr_t>();
|
||||||
|
|
||||||
attrs.name = name;
|
attrs.name = name;
|
||||||
attrs.priority = priority;
|
attrs.priority = priority;
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||||
|
|
||||||
|
#if(WITH_HAL)
|
||||||
|
# set(additional_sources
|
||||||
|
# )
|
||||||
|
#endif()
|
||||||
|
|
||||||
add_library(utility STATIC
|
add_library(utility STATIC
|
||||||
Src/utility_logging.cpp
|
Src/utility_logging.cpp
|
||||||
Src/utility_rand.cpp
|
Src/utility_rand.cpp
|
||||||
Src/utility_assert.cpp
|
|
||||||
${additional_sources}
|
${additional_sources}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,9 +18,5 @@ target_include_directories(utility
|
|||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
)
|
)
|
||||||
set_target_properties(utility
|
|
||||||
PROPERTIES
|
|
||||||
CXX_STANDARD 17
|
|
||||||
)
|
|
||||||
|
|
||||||
skullc_install_packages(skullc utility ${SKULLC_VERSION})
|
skullc_install_packages(skullc utility ${version})
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 29.06.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef SKULLC_UTILITY_ASSERT_HPP_
|
|
||||||
#define SKULLC_UTILITY_ASSERT_HPP_
|
|
||||||
|
|
||||||
namespace Utility::Assert::Detail
|
|
||||||
{
|
|
||||||
|
|
||||||
void assertImpl(const char* expression, const char* file, const int line);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
#define SKULLC_ASSERT_DEBUG(e) (!(e) ? Utility::Assert::Detail::assertImpl(#e, __FILE__, __LINE__) : ((void) 0))
|
|
||||||
#else
|
|
||||||
#define SKULLC_ASSERT_DEBUG(e)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SKULLC_ASSERT_SAFE(e) (!(e) ? Utility::Assert::Detail::assertImpl(#e, __FILE__, __LINE__) : ((void) 0))
|
|
||||||
|
|
||||||
namespace Utility::Assert
|
|
||||||
{
|
|
||||||
|
|
||||||
using assert_cb = void (*)(const char* expression, const char* file, const int line);
|
|
||||||
|
|
||||||
void setHandler(assert_cb callback);
|
|
||||||
assert_cb getHandler();
|
|
||||||
|
|
||||||
}// namespace Utility::Assert
|
|
||||||
|
|
||||||
#endif// SKULLC_UTILITY_ASSERT_HPP_
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 10/12/22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef SKULLC_UTILITY_BYTES_HPP_
|
|
||||||
#define SKULLC_UTILITY_BYTES_HPP_
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Utility
|
|
||||||
{
|
|
||||||
|
|
||||||
template<typename T, std::size_t N>
|
|
||||||
T arrayToType(const std::uint8_t raw[N])
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
|
||||||
static_assert(sizeof(T) == N, "The raw data array is not the same size as T.");
|
|
||||||
|
|
||||||
T t;
|
|
||||||
std::memcpy(&t, raw, sizeof(T));
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, std::size_t N>
|
|
||||||
T arrayToType(const std::array<std::uint8_t, N> raw)
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
|
||||||
static_assert(sizeof(T) == N, "The raw data array is not the same size as T.");
|
|
||||||
|
|
||||||
T t;
|
|
||||||
std::memcpy(&t, raw.data(), sizeof(T));
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
constexpr T zeroInitialized()
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
|
||||||
|
|
||||||
T t;
|
|
||||||
std::memset(&t, 0, sizeof(T));
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void zeroInitialized(T& t)
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
|
||||||
|
|
||||||
std::memset(&t, 0, sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void zeroInitialized(T* t)
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
|
||||||
|
|
||||||
std::memset(t, 0, sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace Utility
|
|
||||||
|
|
||||||
#endif//SKULLC_UTILITY_BYTES_HPP_
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 15.07.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef SKULLC_UTILITY_ENUM_HELPERS_HPP_
|
|
||||||
#define SKULLC_UTILITY_ENUM_HELPERS_HPP_
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#define SKULLC_ENUM_DECLARE_BITFLAG_OPERATORS(E) \
|
|
||||||
inline E operator|(const E& lhs, const E& rhs) \
|
|
||||||
{ \
|
|
||||||
using T = std::underlying_type_t<E>; \
|
|
||||||
return static_cast<E>(static_cast<T>(lhs) | static_cast<T>(rhs)); \
|
|
||||||
} \
|
|
||||||
inline E operator&(const E& lhs, const E& rhs) \
|
|
||||||
{ \
|
|
||||||
using T = std::underlying_type_t<E>; \
|
|
||||||
return static_cast<E>(static_cast<T>(lhs) & static_cast<T>(rhs)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif//SKULLC_UTILITY_ENUM_HELPERS_HPP_
|
|
||||||
@ -1,124 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 12/11/22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef SKULLC_UTILITY_FILTERS_HPP_
|
|
||||||
#define SKULLC_UTILITY_FILTERS_HPP_
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "utility_ifilter.hpp"
|
|
||||||
|
|
||||||
namespace Utility
|
|
||||||
{
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class RollingAverageFilter : public IFilter<T>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RollingAverageFilter() = delete;
|
|
||||||
explicit RollingAverageFilter(const std::size_t step)
|
|
||||||
: step_size_(step)
|
|
||||||
{}
|
|
||||||
|
|
||||||
T currentValue() const override
|
|
||||||
{
|
|
||||||
return value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void updateImpl_(const T& data) override
|
|
||||||
{
|
|
||||||
value_ -= value_ / step_size_;
|
|
||||||
value_ += data / step_size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::size_t step_size_;
|
|
||||||
T value_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, std::size_t N>
|
|
||||||
class MedianFilter : public IFilter<T>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MedianFilter()
|
|
||||||
{
|
|
||||||
data_sequence_.fill(T(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
T currentValue() const override
|
|
||||||
{
|
|
||||||
auto sorted = data_sequence_;
|
|
||||||
std::sort(sorted.begin(), sorted.end());
|
|
||||||
|
|
||||||
return sorted[N / 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void updateImpl_(const T& data) override
|
|
||||||
{
|
|
||||||
std::move(data_sequence_.begin() + 1, data_sequence_.end(), data_sequence_.begin());
|
|
||||||
data_sequence_[N - 1] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::array<T, N> data_sequence_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class LowPassFilter : public IFilter<T>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LowPassFilter() = delete;
|
|
||||||
explicit LowPassFilter(const T threshold)
|
|
||||||
: threshold_(threshold)
|
|
||||||
{}
|
|
||||||
|
|
||||||
T currentValue() const override
|
|
||||||
{
|
|
||||||
return value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void updateImpl_(const T& data) override
|
|
||||||
{
|
|
||||||
if (data < threshold_)
|
|
||||||
value_ = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
T threshold_;
|
|
||||||
T value_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class HighPassFilter : public IFilter<T>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HighPassFilter() = delete;
|
|
||||||
explicit HighPassFilter(const T threshold)
|
|
||||||
: threshold_(threshold)
|
|
||||||
{}
|
|
||||||
|
|
||||||
T currentValue() const override
|
|
||||||
{
|
|
||||||
return value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void updateImpl_(const T& data) override
|
|
||||||
{
|
|
||||||
if (data > threshold_)
|
|
||||||
value_ = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
T threshold_;
|
|
||||||
T value_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace Utility
|
|
||||||
|
|
||||||
#endif//SKULLC_UTILITY_FILTERS_HPP_
|
|
||||||
@ -9,7 +9,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace Utility
|
namespace Peripherals
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace Details
|
namespace Details
|
||||||
@ -301,6 +301,6 @@ using Q7 = FixedPoint<std::int8_t, 7>;
|
|||||||
using Q15 = FixedPoint<std::int16_t, 15>;
|
using Q15 = FixedPoint<std::int16_t, 15>;
|
||||||
using Q31 = FixedPoint<std::int32_t, 31>;
|
using Q31 = FixedPoint<std::int32_t, 31>;
|
||||||
|
|
||||||
}// namespace Utility
|
}// namespace Peripherals
|
||||||
|
|
||||||
#endif//SKULLC_UTILITY_FIXEDPOINT_HPP_
|
#endif//SKULLC_UTILITY_FIXEDPOINT_HPP_
|
||||||
|
|||||||
@ -8,8 +8,6 @@
|
|||||||
#ifndef SKULLC_UTILITY_FUNCTION_HPP_
|
#ifndef SKULLC_UTILITY_FUNCTION_HPP_
|
||||||
#define SKULLC_UTILITY_FUNCTION_HPP_
|
#define SKULLC_UTILITY_FUNCTION_HPP_
|
||||||
|
|
||||||
#include <utility_assert.hpp>
|
|
||||||
|
|
||||||
namespace Utility
|
namespace Utility
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -21,19 +19,11 @@ class IFunction<R(Args...)>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using result_type = R;
|
using result_type = R;
|
||||||
using static_signature = R (*)(Args...);
|
|
||||||
|
|
||||||
virtual ~IFunction()
|
virtual ~IFunction()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual result_type operator()(Args... args) = 0;
|
virtual result_type operator()(Args... args) = 0;
|
||||||
|
|
||||||
template<typename Tag>
|
|
||||||
static_signature toStaticFunction()
|
|
||||||
{
|
|
||||||
static decltype(this) h = this;
|
|
||||||
return +[](Args... args) { (*h)(args...); };
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class>
|
template<class>
|
||||||
@ -51,7 +41,7 @@ public:
|
|||||||
explicit Function(signature callable)
|
explicit Function(signature callable)
|
||||||
: callable_(callable)
|
: callable_(callable)
|
||||||
{
|
{
|
||||||
SKULLC_ASSERT_DEBUG(callable_);
|
assert(callable_);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Function() override
|
~Function() override
|
||||||
@ -83,7 +73,7 @@ public:
|
|||||||
explicit FunctionOwned(source& src, signature callable)
|
explicit FunctionOwned(source& src, signature callable)
|
||||||
: src_(&src), callable_(callable)
|
: src_(&src), callable_(callable)
|
||||||
{
|
{
|
||||||
SKULLC_ASSERT_DEBUG(callable_);
|
assert(callable_);
|
||||||
}
|
}
|
||||||
|
|
||||||
~FunctionOwned() override
|
~FunctionOwned() override
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 12/11/22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef SKULLC_UTILITY_IFILTER_HPP_
|
|
||||||
#define SKULLC_UTILITY_IFILTER_HPP_
|
|
||||||
|
|
||||||
namespace Utility
|
|
||||||
{
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class IFilter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~IFilter() = default;
|
|
||||||
|
|
||||||
void assignPrecedingFilter(IFilter<T>& filter)
|
|
||||||
{
|
|
||||||
preceding_filter_ = &filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearPrecedingFilter()
|
|
||||||
{
|
|
||||||
preceding_filter_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(T data)
|
|
||||||
{
|
|
||||||
if (preceding_filter_)
|
|
||||||
{
|
|
||||||
preceding_filter_->update(data);
|
|
||||||
data = preceding_filter_->currentValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateImpl_(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual T currentValue() const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void updateImpl_(const T& data) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
IFilter<T>* preceding_filter_ = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace Utility
|
|
||||||
|
|
||||||
#endif//SKULLC_UTILITY_IFILTER_HPP_
|
|
||||||
@ -38,13 +38,6 @@ public:
|
|||||||
virtual void log(const char* format, ...) = 0;
|
virtual void log(const char* format, ...) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NullLogSink : ILogger
|
|
||||||
{
|
|
||||||
void log(const char*, ...) override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace Utility
|
}// namespace Utility
|
||||||
|
|
||||||
#endif// SKULLC_UTILITY_ILOGGER_HPP_
|
#endif// SKULLC_UTILITY_ILOGGER_HPP_
|
||||||
|
|||||||
@ -158,18 +158,15 @@ public:
|
|||||||
if (dx == 0 && dy == 0)
|
if (dx == 0 && dy == 0)
|
||||||
{
|
{
|
||||||
at(start) = color;
|
at(start) = color;
|
||||||
}
|
} else if (dx == 0)
|
||||||
else if (dx == 0)
|
|
||||||
{
|
{
|
||||||
for (; y < y_(stop); y++)
|
for (; y < y_(stop); y++)
|
||||||
at({x, y}) = color;
|
at({x, y}) = color;
|
||||||
}
|
} else if (dy == 0)
|
||||||
else if (dy == 0)
|
|
||||||
{
|
{
|
||||||
for (; x < x_(stop); x++)
|
for (; x < x_(stop); x++)
|
||||||
at({x, y}) = color;
|
at({x, y}) = color;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Bresenham's algorithm.
|
// Bresenham's algorithm.
|
||||||
std::int32_t p = 2 * dy - dx;
|
std::int32_t p = 2 * dy - dx;
|
||||||
@ -182,8 +179,7 @@ public:
|
|||||||
{
|
{
|
||||||
y++;
|
y++;
|
||||||
p = p + 2 * dy - 2 * dx;
|
p = p + 2 * dy - 2 * dx;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
p = p + 2 * dy;
|
p = p + 2 * dy;
|
||||||
}
|
}
|
||||||
@ -221,8 +217,7 @@ public:
|
|||||||
{
|
{
|
||||||
y--;
|
y--;
|
||||||
d = d + 4 * (x - y) + 10;
|
d = d + 4 * (x - y) + 10;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
d = d + 4 * x + 6;
|
d = d + 4 * x + 6;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,8 +68,7 @@ public:
|
|||||||
if (naive < _arr_end)
|
if (naive < _arr_end)
|
||||||
{
|
{
|
||||||
return iterator(naive, _arr_begin, _arr_end);
|
return iterator(naive, _arr_begin, _arr_end);
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
const pointer remainder = pointer(naive - _arr_end);
|
const pointer remainder = pointer(naive - _arr_end);
|
||||||
return iterator(_arr_begin + difference_type(remainder), _arr_begin,
|
return iterator(_arr_begin + difference_type(remainder), _arr_begin,
|
||||||
@ -83,8 +82,7 @@ public:
|
|||||||
if (naive >= _arr_begin)
|
if (naive >= _arr_begin)
|
||||||
{
|
{
|
||||||
return iterator(naive, _arr_begin, _arr_end);
|
return iterator(naive, _arr_begin, _arr_end);
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
const pointer remainder = pointer(_arr_begin - naive);
|
const pointer remainder = pointer(_arr_begin - naive);
|
||||||
return iterator(_arr_end - difference_type(remainder), _arr_begin,
|
return iterator(_arr_end - difference_type(remainder), _arr_begin,
|
||||||
@ -204,8 +202,7 @@ public:
|
|||||||
if (distance > 0)
|
if (distance > 0)
|
||||||
{
|
{
|
||||||
return distance;
|
return distance;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return head_ - tail_ + 1;
|
return head_ - tail_ + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,8 @@
|
|||||||
#ifndef SKULLC_UTILITY_STATICPOINTER_HPP_
|
#ifndef SKULLC_UTILITY_STATICPOINTER_HPP_
|
||||||
#define SKULLC_UTILITY_STATICPOINTER_HPP_
|
#define SKULLC_UTILITY_STATICPOINTER_HPP_
|
||||||
|
|
||||||
#include <new>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <utility_assert.hpp>
|
|
||||||
|
|
||||||
namespace Utility
|
namespace Utility
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -60,18 +57,6 @@ struct StaticPointer
|
|||||||
return initialized_;
|
return initialized_;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_type* get()
|
|
||||||
{
|
|
||||||
SKULLC_ASSERT_DEBUG(initialized_);
|
|
||||||
return reinterpret_cast<value_type*>(storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
const value_type* get() const
|
|
||||||
{
|
|
||||||
SKULLC_ASSERT_DEBUG(initialized_);
|
|
||||||
return reinterpret_cast<const value_type*>(storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 26.06.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef SKULLC_UTILITY_TAG_HPP_
|
|
||||||
#define SKULLC_UTILITY_TAG_HPP_
|
|
||||||
|
|
||||||
#define SKULLC_CONCAT_IMPL(x, y) x##y
|
|
||||||
#define SKULLC_CONCAT(x, y) SKULLC_CONCAT_IMPL(x, y)
|
|
||||||
#define SKULLC_TAG struct SKULLC_CONCAT(SkullCTag_, __COUNTER__)
|
|
||||||
|
|
||||||
#endif// SKULLC_UTILITY_TAG_HPP_
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 29.06.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "utility_assert.hpp"
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
Utility::Assert::assert_cb INSTALLED_HANDLER = nullptr;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Utility::Assert
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace Detail
|
|
||||||
{
|
|
||||||
|
|
||||||
void assertImpl(const char* expression, const char* file, const int line)
|
|
||||||
{
|
|
||||||
if (INSTALLED_HANDLER)
|
|
||||||
INSTALLED_HANDLER(expression, file, line);
|
|
||||||
else
|
|
||||||
std::terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace Detail
|
|
||||||
|
|
||||||
void setHandler(assert_cb callback)
|
|
||||||
{
|
|
||||||
INSTALLED_HANDLER = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_cb getHandler()
|
|
||||||
{
|
|
||||||
return INSTALLED_HANDLER;
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace Utility::Assert
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
#Look for an executable called sphinx-build
|
|
||||||
find_program(SPHINX_EXECUTABLE
|
|
||||||
NAMES sphinx-build
|
|
||||||
DOC "Path to sphinx-build executable")
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
|
|
||||||
#Handle standard arguments to find_package like REQUIRED and QUIET
|
|
||||||
find_package_handle_standard_args(Sphinx
|
|
||||||
"Failed to find sphinx-build executable"
|
|
||||||
SPHINX_EXECUTABLE)
|
|
||||||
5
conanfile.txt
Normal file
5
conanfile.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[requires]
|
||||||
|
catch2/[>=2.0.0,<3.0.0]
|
||||||
|
|
||||||
|
[generators]
|
||||||
|
cmake_find_package
|
||||||
Loading…
x
Reference in New Issue
Block a user