Compare commits

...

2 Commits

Author SHA1 Message Date
faa1685e18 Add async UART logger (running on DMA).
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-21 17:03:06 +02:00
b7789064fa Update ringbuffer 2021-03-21 17:02:55 +02:00
7 changed files with 305 additions and 3 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
add_library(utility STATIC
Src/utility_itmlogger.cpp
Src/utility_logging.cpp
)
add_library(skullc::utility ALIAS utility)

View 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_ */

View 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_ */

View File

@ -155,6 +155,9 @@ public:
*_tail = value;
++_tail;
if (_tail == _head)
_is_full = true;
}
template<typename... Args>
@ -167,12 +170,21 @@ public:
++_tail;
}
void increment_tail()
{
if (_tail == _head)
++_head;
++_tail;
}
void pop_front()
{
if (empty())
return;
++_head;
_is_full = false;
}
reference front()
@ -198,7 +210,12 @@ public:
size_type size() const
{
if (_head == _tail)
{
if (_is_full)
return N;
else
return 0;
}
const typename Ringbuffer<T, N>::iterator::difference_type distance = _tail - _head;
@ -208,10 +225,15 @@ public:
}
else
{
return _head - _tail;
return _head - _tail + 1;
}
}
constexpr size_type max_size() const
{
return N;
}
bool empty() const
{
return size() == 0;
@ -219,6 +241,7 @@ public:
private:
std::array<T, N> _data;
bool _is_full = false;
iterator _head;
iterator _tail;

View 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();
}
}

View 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();
}
}

View File

@ -96,6 +96,50 @@ TEST_CASE("Constructed buffer is empty.", "[ringbuffer]")
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]")
{
Ringbuffer<10> buffer;
@ -248,3 +292,53 @@ TEST_CASE("Clearing a ringbuffer works.", "[ringbuffer]")
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);
}
}