Compare commits
2 Commits
d945e7a799
...
faa1685e18
| Author | SHA1 | Date | |
|---|---|---|---|
| faa1685e18 | |||
| b7789064fa |
@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
|
||||||
|
|
||||||
add_library(utility STATIC
|
add_library(utility STATIC
|
||||||
Src/utility_itmlogger.cpp
|
Src/utility_logging.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(skullc::utility ALIAS utility)
|
add_library(skullc::utility ALIAS utility)
|
||||||
|
|||||||
49
Utility/Inc/utility_asyncaurtlogger.hpp
Normal file
49
Utility/Inc/utility_asyncaurtlogger.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* utility_asyncaurtlogger.hpp
|
||||||
|
*
|
||||||
|
* Created on: Mar 20, 2021
|
||||||
|
* Author: erki
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SKULLC_UTILITY_ASYNCAURTLOGGER_HPP_
|
||||||
|
#define SKULLC_UTILITY_ASYNCAURTLOGGER_HPP_
|
||||||
|
|
||||||
|
#include "utility_ilogger.hpp"
|
||||||
|
#include "utility_ringbuffer.hpp"
|
||||||
|
|
||||||
|
#include "usart.h"
|
||||||
|
|
||||||
|
namespace Utility
|
||||||
|
{
|
||||||
|
|
||||||
|
class AsyncUARTLogger : public ILogger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AsyncUARTLogger(UART_HandleTypeDef* huart);
|
||||||
|
AsyncUARTLogger() = delete;
|
||||||
|
AsyncUARTLogger(const AsyncUARTLogger&) = delete;
|
||||||
|
AsyncUARTLogger(AsyncUARTLogger&&) = delete;
|
||||||
|
|
||||||
|
void log(const char* format, ...);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct _Data
|
||||||
|
{
|
||||||
|
std::array<char, 255> buffer;
|
||||||
|
std::int32_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ringbuffer<_Data, 10> _buffer_queue;
|
||||||
|
UART_HandleTypeDef* _huart;
|
||||||
|
bool _in_flight = false;
|
||||||
|
|
||||||
|
static AsyncUARTLogger* _this;
|
||||||
|
static void _txCompleteCallback(UART_HandleTypeDef* huart);
|
||||||
|
|
||||||
|
void _sendNextLog();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* SKULLC_UTILITY_ASYNCAURTLOGGER_HPP_ */
|
||||||
31
Utility/Inc/utility_atomicscopeguard.hpp
Normal file
31
Utility/Inc/utility_atomicscopeguard.hpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* utility_atomicscopeguard.hpp
|
||||||
|
*
|
||||||
|
* Created on: Mar 21, 2021
|
||||||
|
* Author: erki
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTILITY_INC_UTILITY_ATOMICSCOPEGUARD_HPP_
|
||||||
|
#define UTILITY_INC_UTILITY_ATOMICSCOPEGUARD_HPP_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Utility
|
||||||
|
{
|
||||||
|
|
||||||
|
class AtomicScopeGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AtomicScopeGuard();
|
||||||
|
AtomicScopeGuard(const AtomicScopeGuard&) = delete;
|
||||||
|
AtomicScopeGuard(AtomicScopeGuard&&) = delete;
|
||||||
|
|
||||||
|
~AtomicScopeGuard();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::int32_t _reentrancy_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* UTILITY_INC_UTILITY_ATOMICSCOPEGUARD_HPP_ */
|
||||||
@ -155,6 +155,9 @@ public:
|
|||||||
|
|
||||||
*_tail = value;
|
*_tail = value;
|
||||||
++_tail;
|
++_tail;
|
||||||
|
|
||||||
|
if (_tail == _head)
|
||||||
|
_is_full = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
@ -167,12 +170,21 @@ public:
|
|||||||
++_tail;
|
++_tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void increment_tail()
|
||||||
|
{
|
||||||
|
if (_tail == _head)
|
||||||
|
++_head;
|
||||||
|
|
||||||
|
++_tail;
|
||||||
|
}
|
||||||
|
|
||||||
void pop_front()
|
void pop_front()
|
||||||
{
|
{
|
||||||
if (empty())
|
if (empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
++_head;
|
++_head;
|
||||||
|
_is_full = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
reference front()
|
reference front()
|
||||||
@ -198,7 +210,12 @@ public:
|
|||||||
size_type size() const
|
size_type size() const
|
||||||
{
|
{
|
||||||
if (_head == _tail)
|
if (_head == _tail)
|
||||||
return 0;
|
{
|
||||||
|
if (_is_full)
|
||||||
|
return N;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const typename Ringbuffer<T, N>::iterator::difference_type distance = _tail - _head;
|
const typename Ringbuffer<T, N>::iterator::difference_type distance = _tail - _head;
|
||||||
|
|
||||||
@ -208,10 +225,15 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return _head - _tail;
|
return _head - _tail + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr size_type max_size() const
|
||||||
|
{
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const
|
||||||
{
|
{
|
||||||
return size() == 0;
|
return size() == 0;
|
||||||
@ -219,6 +241,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<T, N> _data;
|
std::array<T, N> _data;
|
||||||
|
bool _is_full = false;
|
||||||
|
|
||||||
iterator _head;
|
iterator _head;
|
||||||
iterator _tail;
|
iterator _tail;
|
||||||
|
|||||||
73
Utility/Src/utility_asynchuartlogger.cpp
Normal file
73
Utility/Src/utility_asynchuartlogger.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* utility_asynchuartlogger.cpp
|
||||||
|
*
|
||||||
|
* Created on: Mar 20, 2021
|
||||||
|
* Author: erki
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utility_asyncaurtlogger.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "utility_atomicscopeguard.hpp"
|
||||||
|
|
||||||
|
namespace Utility
|
||||||
|
{
|
||||||
|
|
||||||
|
AsyncUARTLogger* AsyncUARTLogger::_this = nullptr;
|
||||||
|
|
||||||
|
AsyncUARTLogger::AsyncUARTLogger(UART_HandleTypeDef* huart)
|
||||||
|
: _huart(huart)
|
||||||
|
{
|
||||||
|
assert(!_this);
|
||||||
|
|
||||||
|
_this = this;
|
||||||
|
_huart->TxCpltCallback = &AsyncUARTLogger::_txCompleteCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncUARTLogger::log(const char* format, ...)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
AtomicScopeGuard s;
|
||||||
|
|
||||||
|
if (_buffer_queue.size() == _buffer_queue.max_size())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
|
||||||
|
auto tail = _buffer_queue.end();
|
||||||
|
std::array<char, 255>& buffer = tail->buffer;
|
||||||
|
|
||||||
|
tail->length = vsnprintf(buffer.data(), buffer.size(), format, args);
|
||||||
|
|
||||||
|
{
|
||||||
|
AtomicScopeGuard s;
|
||||||
|
_buffer_queue.increment_tail();
|
||||||
|
|
||||||
|
if (!_in_flight)
|
||||||
|
_sendNextLog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncUARTLogger::_txCompleteCallback(UART_HandleTypeDef*)
|
||||||
|
{
|
||||||
|
if (!AsyncUARTLogger::_this->_buffer_queue.empty())
|
||||||
|
AsyncUARTLogger::_this->_sendNextLog();
|
||||||
|
else
|
||||||
|
AsyncUARTLogger::_this->_in_flight = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncUARTLogger::_sendNextLog()
|
||||||
|
{
|
||||||
|
_in_flight = true;
|
||||||
|
|
||||||
|
_Data& head = _buffer_queue.front();
|
||||||
|
HAL_UART_Transmit_DMA(_huart, reinterpret_cast<uint8_t*>(head.buffer.data()), head.length);
|
||||||
|
_buffer_queue.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
32
Utility/Src/utility_atomicscopeguard.cpp
Normal file
32
Utility/Src/utility_atomicscopeguard.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* utility_atomicscopeguard.cpp
|
||||||
|
*
|
||||||
|
* Created on: Mar 21, 2021
|
||||||
|
* Author: erki
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utility_atomicscopeguard.hpp"
|
||||||
|
|
||||||
|
#include "cmsis_gcc.h"
|
||||||
|
|
||||||
|
namespace Utility
|
||||||
|
{
|
||||||
|
|
||||||
|
std::int32_t AtomicScopeGuard::_reentrancy_counter;
|
||||||
|
|
||||||
|
AtomicScopeGuard::AtomicScopeGuard()
|
||||||
|
{
|
||||||
|
__disable_irq();
|
||||||
|
_reentrancy_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomicScopeGuard::~AtomicScopeGuard()
|
||||||
|
{
|
||||||
|
_reentrancy_counter--;
|
||||||
|
|
||||||
|
if (!_reentrancy_counter)
|
||||||
|
__enable_irq();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -96,6 +96,50 @@ TEST_CASE("Constructed buffer is empty.", "[ringbuffer]")
|
|||||||
REQUIRE(buffer.empty());
|
REQUIRE(buffer.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Buffer reports size properly.", "[ringbuffer]")
|
||||||
|
{
|
||||||
|
Ringbuffer<3> buffer;
|
||||||
|
|
||||||
|
buffer.push_back(1);
|
||||||
|
buffer.push_back(2);
|
||||||
|
|
||||||
|
SECTION("max_size() reports maximum size properly.")
|
||||||
|
{
|
||||||
|
REQUIRE(buffer.max_size() == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("After pushing 2 elements, size is reported properly.")
|
||||||
|
{
|
||||||
|
REQUIRE(buffer.size() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("A full buffer reports size as max_size.")
|
||||||
|
{
|
||||||
|
buffer.push_back(3);
|
||||||
|
REQUIRE(buffer.size() == 3);
|
||||||
|
REQUIRE(buffer.size() == buffer.max_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Removing an element updates size properly.")
|
||||||
|
{
|
||||||
|
buffer.pop_front();
|
||||||
|
|
||||||
|
REQUIRE(buffer.size() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Removing and adding an element updates size properly.")
|
||||||
|
{
|
||||||
|
buffer.pop_front();
|
||||||
|
buffer.push_back(4);
|
||||||
|
|
||||||
|
REQUIRE(buffer.size() == 2);
|
||||||
|
|
||||||
|
buffer.push_back(5);
|
||||||
|
|
||||||
|
REQUIRE(buffer.size() == 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Adding single element.", "[ringbuffer]")
|
TEST_CASE("Adding single element.", "[ringbuffer]")
|
||||||
{
|
{
|
||||||
Ringbuffer<10> buffer;
|
Ringbuffer<10> buffer;
|
||||||
@ -248,3 +292,53 @@ TEST_CASE("Clearing a ringbuffer works.", "[ringbuffer]")
|
|||||||
REQUIRE(buffer.begin() == buffer.end());
|
REQUIRE(buffer.begin() == buffer.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Manually incrementing tail works.", "[ringbuffer]")
|
||||||
|
{
|
||||||
|
Ringbuffer<10> buffer;
|
||||||
|
|
||||||
|
buffer.push_back(1);
|
||||||
|
|
||||||
|
auto old_end = buffer.end();
|
||||||
|
*old_end = 2;
|
||||||
|
|
||||||
|
buffer.increment_tail();
|
||||||
|
|
||||||
|
SECTION("New end is set forward by 1.")
|
||||||
|
{
|
||||||
|
const auto new_end = buffer.end();
|
||||||
|
REQUIRE(old_end != new_end);
|
||||||
|
REQUIRE(new_end - old_end == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Manually incrementing tail when full deletes data.", "[ringbuffer]")
|
||||||
|
{
|
||||||
|
Ringbuffer<2> buffer;
|
||||||
|
|
||||||
|
buffer.push_back(1);
|
||||||
|
buffer.push_back(2);
|
||||||
|
|
||||||
|
const auto old_start = buffer.begin();
|
||||||
|
const auto old_end = buffer.end();
|
||||||
|
|
||||||
|
REQUIRE(buffer.size() == buffer.max_size());
|
||||||
|
|
||||||
|
buffer.increment_tail();
|
||||||
|
|
||||||
|
REQUIRE(buffer.size() == buffer.max_size());
|
||||||
|
|
||||||
|
SECTION("End is incremented properly.")
|
||||||
|
{
|
||||||
|
const auto new_end = buffer.end();
|
||||||
|
REQUIRE(old_end != new_end);
|
||||||
|
REQUIRE(new_end - old_end == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Begin is incremented properly.")
|
||||||
|
{
|
||||||
|
const auto new_start = buffer.begin();
|
||||||
|
REQUIRE(old_start != new_start);
|
||||||
|
REQUIRE(new_start - old_start == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user