uart fixes
This commit is contained in:
parent
25999cde55
commit
242521678d
@ -180,18 +180,17 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpdma1 {
|
||||
status = "okay";
|
||||
};
|
||||
//&gpdma1 {
|
||||
// status = "okay";
|
||||
//};
|
||||
|
||||
&usart1 {
|
||||
pinctrl-0 = <&usart1_tx_pb14 &usart1_rx_pb15>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
dmas = <&gpdma1 7 22 (STM32_DMA_PERIPH_TX | STM32_DMA_PRIORITY_LOW) // GPDMA1_REQUEST_USART1_TX
|
||||
&gpdma1 2 21 (STM32_DMA_PERIPH_RX | STM32_DMA_PRIORITY_LOW)>; // GPDMA1_REQUEST_USART1_RX
|
||||
|
||||
dma-names = "tx", "rx";
|
||||
// dmas = <&gpdma1 7 22 (STM32_DMA_PERIPH_TX | STM32_DMA_PRIORITY_LOW) // GPDMA1_REQUEST_USART1_TX
|
||||
// &gpdma1 2 21 (STM32_DMA_PERIPH_RX | STM32_DMA_PRIORITY_LOW)>; // GPDMA1_REQUEST_USART1_RX
|
||||
// dma-names = "tx", "rx";
|
||||
|
||||
current-speed = <921600>;
|
||||
status = "okay";
|
||||
|
||||
@ -836,7 +836,6 @@ static int uart_stm32_fifo_fill_visitor(const struct device *dev, const void *tx
|
||||
|
||||
/* Lock interrupts to prevent nested interrupts or thread switch */
|
||||
key = irq_lock();
|
||||
|
||||
while ((size - num_tx > 0) && LL_USART_IsActiveFlag_TXE(usart)) {
|
||||
/* TXE flag will be cleared with byte write to DR|RDR register */
|
||||
|
||||
@ -844,7 +843,6 @@ static int uart_stm32_fifo_fill_visitor(const struct device *dev, const void *tx
|
||||
fill_fn(usart, tx_data, num_tx);
|
||||
num_tx++;
|
||||
}
|
||||
|
||||
irq_unlock(key);
|
||||
|
||||
return num_tx;
|
||||
|
||||
@ -6,8 +6,6 @@ project(rims_controller C CXX)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
add_compile_definitions(CONFIG_UART_ASYNC_API)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${ZEPHYR_BASE}/modules/nanopb)
|
||||
|
||||
include(nanopb)
|
||||
|
||||
@ -2,41 +2,41 @@
|
||||
Request/Response Architecture
|
||||
|
||||
Purpose:
|
||||
The architecture supports interactions where a client sends a request (e.g., Set or Get) to configure or query the system.
|
||||
The system processes the request and responds with a result (e.g., requested data on success, or a failure).
|
||||
|
||||
The architecture supports interactions where a client sends a request (e.g., Set or Get) to
|
||||
configure or query the system. The system processes the request and responds with a result (e.g.,
|
||||
requested data on success, or a failure).
|
||||
|
||||
Message Flow:
|
||||
A request message encapsulates the client's intent (e.g., to set or retrieve a parameter).
|
||||
A response message provides the outcome of that intent, including any data or error codes.
|
||||
|
||||
For each configurable aspect (e.g., a parameter or operation), you define four types of messages:
|
||||
Set<Param>Request: Specifies the new value for the parameter.
|
||||
Set<Param>Response: Acknowledges the result of the Set operation.
|
||||
Get<Param>Request: Requests the current value of the parameter.
|
||||
Get<Param>Response: Returns the value of the parameter or an error.
|
||||
|
||||
A request message encapsulates the client's intent (e.g., to set or retrieve a parameter).
|
||||
A response message provides the outcome of that intent, including any data or error codes.
|
||||
|
||||
For each configurable aspect (e.g., a parameter or operation), you define four types of
|
||||
messages: Set<Param>Request: Specifies the new value for the parameter. Set<Param>Response:
|
||||
Acknowledges the result of the Set operation. Get<Param>Request: Requests the current value of the
|
||||
parameter. Get<Param>Response: Returns the value of the parameter or an error.
|
||||
|
||||
Request Fields:
|
||||
Common fields include a requestid (for tracking) and parameter-specific data, such as ModeOfOperationType or Error.
|
||||
Response Fields:
|
||||
Responses use a oneof structure to return either the result of the operation (e.g., the current mode) or an error.
|
||||
Common fields include a requestid (for tracking) and parameter-specific data, such as
|
||||
ModeOfOperationType or Error. Response Fields: Responses use a oneof structure to return either the
|
||||
result of the operation (e.g., the current mode) or an error.
|
||||
|
||||
|
||||
Ingress and Egress Messages
|
||||
IngressMessages:
|
||||
Encapsulates all incoming requests (Set or Get).
|
||||
The oneof field allows only one request type to be sent at a time.
|
||||
|
||||
Encapsulates all incoming requests (Set or Get).
|
||||
The oneof field allows only one request type to be sent at a time.
|
||||
|
||||
EgressMessages:
|
||||
Encapsulates all outgoing responses (Set or Get responses).
|
||||
Similarly, the oneof field ensures that only one response type is included.
|
||||
|
||||
EngressMessages can also send a message that is not a response to given request but also Failure or temperature updates
|
||||
Encapsulates all outgoing responses (Set or Get responses).
|
||||
Similarly, the oneof field ensures that only one response type is included.
|
||||
|
||||
EngressMessages can also send a message that is not a response to given request but also
|
||||
Failure or temperature updates
|
||||
|
||||
*/
|
||||
|
||||
enum MessageIDs{
|
||||
Temperature = 0;
|
||||
Ctrl = 1;
|
||||
Log = 2;
|
||||
|
||||
enum MessageIDs {
|
||||
Temperature = 0;
|
||||
Ctrl = 1;
|
||||
Log = 2;
|
||||
};
|
||||
|
||||
@ -3,56 +3,62 @@ syntax = "proto3";
|
||||
package config;
|
||||
// channel configuration
|
||||
|
||||
enum Error{
|
||||
NoError = 0;
|
||||
enum Error {
|
||||
NoError = 0;
|
||||
}
|
||||
|
||||
enum ModeOfOperationType{
|
||||
/**
|
||||
Device is currently OFF
|
||||
*/
|
||||
Off = 0;
|
||||
|
||||
/**
|
||||
In manual operation, no PID loop will be enabled on the device.
|
||||
In this mode there is no way to set output temperature, you can only set the output power
|
||||
|
||||
Mode will be applied to all channels
|
||||
*/
|
||||
Manual = 1;
|
||||
|
||||
/**
|
||||
TODO not implemented ;(
|
||||
*/
|
||||
Automatic = 2;
|
||||
enum ModeOfOperationType {
|
||||
/**
|
||||
Device is currently OFF
|
||||
*/
|
||||
Off = 0;
|
||||
|
||||
/**
|
||||
In manual operation, no PID loop will be enabled on the device.
|
||||
In this mode there is no way to set output temperature, you can only set the output
|
||||
power
|
||||
|
||||
Mode will be applied to all channels
|
||||
*/
|
||||
Manual = 1;
|
||||
|
||||
/**
|
||||
TODO not implemented ;(
|
||||
*/
|
||||
Automatic = 2;
|
||||
}
|
||||
|
||||
/// Request / Response API
|
||||
message ModeOfOperationRequest{
|
||||
// not specyfying this, resulte in a "read only" request
|
||||
ModeOfOperationType type = 1;
|
||||
message ModeOfOperationRequest
|
||||
{
|
||||
// not specyfying this, resulte in a "read only" request
|
||||
ModeOfOperationType type = 1;
|
||||
};
|
||||
|
||||
message ModeOfOperationResponse{
|
||||
oneof resp {
|
||||
ModeOfOperationType type = 1;
|
||||
Error error = 254;
|
||||
}
|
||||
message ModeOfOperationResponse
|
||||
{
|
||||
oneof resp
|
||||
{
|
||||
ModeOfOperationType type = 1;
|
||||
Error error = 254;
|
||||
}
|
||||
}
|
||||
|
||||
// only those messages are send
|
||||
message IngressMessages
|
||||
{
|
||||
uint32 requestID = 255; // Unique request ID
|
||||
oneof data {
|
||||
ModeOfOperationRequest modeOfOperationRequest = 1;
|
||||
}
|
||||
uint32 requestID = 255; // Unique request ID
|
||||
oneof data
|
||||
{
|
||||
ModeOfOperationRequest modeOfOperationRequest = 1;
|
||||
}
|
||||
};
|
||||
|
||||
message EgressMessages
|
||||
{
|
||||
optional uint32 requestID = 255; // Unique request ID
|
||||
oneof data{
|
||||
ModeOfOperationResponse modeOfOperationResponse = 1;
|
||||
}
|
||||
optional uint32 requestID = 255; // Unique request ID
|
||||
oneof data
|
||||
{
|
||||
ModeOfOperationResponse modeOfOperationResponse = 1;
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,58 +4,64 @@ package ctrl;
|
||||
// channel configuration
|
||||
|
||||
enum Error {
|
||||
NoError = 0;
|
||||
NoError = 0;
|
||||
|
||||
WrongMode = 1;
|
||||
WrongChannel = 2;
|
||||
WrongMode = 1;
|
||||
WrongChannel = 2;
|
||||
}
|
||||
|
||||
message MaxPower{
|
||||
|
||||
message MaxPower
|
||||
{
|
||||
}
|
||||
|
||||
// Parameters for Phase Modulation
|
||||
message PhaseModulationParams
|
||||
{
|
||||
/**/
|
||||
/**/
|
||||
}
|
||||
|
||||
// Parameters for Group Modulation
|
||||
message GroupModulationParams
|
||||
{
|
||||
uint32 cycles = 1; // Duration of each group in milliseconds (optional)
|
||||
uint32 cycles = 1; // Duration of each group in milliseconds (optional)
|
||||
}
|
||||
|
||||
message ManualPowerControlRequest{
|
||||
uint32 channelID = 1; // required
|
||||
oneof modulation{
|
||||
PhaseModulationParams phase = 2;
|
||||
GroupModulationParams group = 3;
|
||||
}
|
||||
message ManualPowerControlRequest
|
||||
{
|
||||
uint32 channelID = 1; // required
|
||||
oneof modulation
|
||||
{
|
||||
PhaseModulationParams phase = 2;
|
||||
GroupModulationParams group = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message ManualPowerControlResponse{
|
||||
oneof modulation{
|
||||
PhaseModulationParams phase = 2;
|
||||
GroupModulationParams group = 3;
|
||||
Error error = 254;
|
||||
}
|
||||
message ManualPowerControlResponse
|
||||
{
|
||||
oneof modulation
|
||||
{
|
||||
PhaseModulationParams phase = 2;
|
||||
GroupModulationParams group = 3;
|
||||
Error error = 254;
|
||||
}
|
||||
}
|
||||
|
||||
// only those messages are send through interface
|
||||
message IngressMessages
|
||||
{
|
||||
uint32 requestID = 255; // Unique request ID
|
||||
oneof data {
|
||||
ManualPowerControlRequest manualPowerControlRequest = 1;
|
||||
}
|
||||
uint32 requestID = 255; // Unique request ID
|
||||
oneof data
|
||||
{
|
||||
ManualPowerControlRequest manualPowerControlRequest = 1;
|
||||
}
|
||||
};
|
||||
|
||||
message EgressMessages
|
||||
{
|
||||
optional uint32 requestID = 255; // Unique request ID
|
||||
oneof data{
|
||||
ManualPowerControlResponse manualPowerControlResponse = 1;
|
||||
// also broadcast
|
||||
}
|
||||
optional uint32 requestID = 255; // Unique request ID
|
||||
oneof data
|
||||
{
|
||||
ManualPowerControlResponse manualPowerControlResponse = 1;
|
||||
// also broadcast
|
||||
}
|
||||
};
|
||||
|
||||
@ -29,7 +29,7 @@ message LogEntry
|
||||
LogLevel level = 1; // Log severity level
|
||||
string sourceFile = 2 [ (nanopb).max_size = 32 ]; // Source file where log was generated
|
||||
uint32 lineNumber = 3; // Line number in the source file
|
||||
string logLine = 4 [ (nanopb).max_size = 200 ]; // The formatted log message
|
||||
string logLine = 4 [ (nanopb).max_size = 120 ]; // The formatted log message
|
||||
fixed64 systick = 5; // optional systick
|
||||
}
|
||||
|
||||
|
||||
@ -3,16 +3,16 @@ syntax = "proto3";
|
||||
import "nanopb.proto";
|
||||
|
||||
enum Error {
|
||||
NoError = 0;
|
||||
BadCRC = 1;
|
||||
BadId = 2;
|
||||
UnknownId = 3;
|
||||
NoError = 0;
|
||||
BadCRC = 1;
|
||||
BadId = 2;
|
||||
UnknownId = 3;
|
||||
}
|
||||
|
||||
message Message
|
||||
{
|
||||
fixed32 id = 1;
|
||||
bytes data = 2 [ (nanopb).max_size = 250 ];
|
||||
fixed32 crc = 3;
|
||||
optional Error error = 4;
|
||||
fixed32 id = 1;
|
||||
bytes data = 2 [ (nanopb).max_size = 250 ];
|
||||
fixed32 crc = 3;
|
||||
optional Error error = 4;
|
||||
}
|
||||
|
||||
@ -32,16 +32,24 @@ CONFIG_EARLY_CONSOLE=n
|
||||
CONFIG_BOOT_BANNER=n
|
||||
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_UART_ASYNC_API=y
|
||||
CONFIG_DMA=y
|
||||
|
||||
# not working as expected for TX
|
||||
CONFIG_UART_ASYNC_API=n
|
||||
CONFIG_DMA=n
|
||||
|
||||
CONFIG_UART_INTERRUPT_DRIVEN=y
|
||||
|
||||
CONFIG_ADC=y
|
||||
|
||||
CONFIG_LOG=n
|
||||
CONFIG_LOG_MODE_IMMEDIATE=n # Log messages are output immediately
|
||||
CONFIG_LOG_BACKEND_UART=n # Use UART for log output
|
||||
#CONFIG_LOG=n
|
||||
#CONFIG_LOG_MODE_IMMEDIATE=n # Log messages are output immediately
|
||||
#CONFIG_LOG_BACKEND_UART=n # Use UART for log output
|
||||
|
||||
#CONFIG_MAIN_STACK_SIZE=512
|
||||
|
||||
#CONFIG_MAIN_STACK_SIZE=4096
|
||||
#
|
||||
CONFIG_CRC=y
|
||||
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_ASSERT=n
|
||||
|
||||
#CONFIG_NUM_PREEMPT_PRIORITIES=0
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "zephyr.hpp"
|
||||
#include "zephyr/sys/__assert.h"
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
@ -21,11 +22,49 @@ namespace rims {
|
||||
// virtual void pop_back() = 0;
|
||||
// };
|
||||
|
||||
template <typename T, std::size_t Capacity> class static_vector {
|
||||
private:
|
||||
std::array<T, Capacity> buffer;
|
||||
std::size_t count = 0;
|
||||
|
||||
public:
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return count;
|
||||
}
|
||||
|
||||
constexpr void clean() noexcept {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
constexpr const T *data() const noexcept {
|
||||
return buffer.data();
|
||||
}
|
||||
constexpr T *data() noexcept {
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
constexpr bool full() const noexcept {
|
||||
return count >= Capacity;
|
||||
}
|
||||
|
||||
constexpr bool empty() const noexcept {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
constexpr T &push_back(const T &value) {
|
||||
if (count >= Capacity) {
|
||||
throw std::out_of_range("static_vector is full");
|
||||
}
|
||||
buffer[count++] = value;
|
||||
return buffer[count - 1];
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, std::size_t N> class circular_buffer {
|
||||
public:
|
||||
explicit circular_buffer() = default;
|
||||
|
||||
T &emplace_front(T &&item) {
|
||||
T &emplace_front(const T &item) {
|
||||
if (size() == capacity()) {
|
||||
std::destroy_at(std::addressof(buf_[head_]));
|
||||
std::memset(std::addressof(buf_[head_]), 0, sizeof(T));
|
||||
@ -43,7 +82,7 @@ template <class T, std::size_t N> class circular_buffer {
|
||||
}
|
||||
|
||||
T get() {
|
||||
assert(not empty());
|
||||
__ASSERT_NO_MSG(not empty());
|
||||
// Read data and advance the tail (we now have a free space)
|
||||
T val = std::move(*reinterpret_cast<T *>(std::addressof(buf_[tail_])));
|
||||
std::destroy_at(std::addressof(buf_[tail_]));
|
||||
@ -60,7 +99,7 @@ template <class T, std::size_t N> class circular_buffer {
|
||||
tail_ = (tail_ + 1) % N;
|
||||
full_ = false;
|
||||
}
|
||||
|
||||
|
||||
void pop_front() {
|
||||
std::destroy_at(std::addressof(buf_[head_]));
|
||||
std::memset(std::addressof(buf_[head_]), 0, sizeof(T));
|
||||
@ -350,10 +389,9 @@ template <typename T, size_t N> class zephyr_fifo_buffer {
|
||||
if (_elements.full()) return false;
|
||||
|
||||
auto &el = _elements.emplace_front(ZephyrFifoElement<T>{}); // create new element
|
||||
if (fn(el.item)) // fill new data
|
||||
k_fifo_put(&_fifo, &el); // put data into a queue
|
||||
else
|
||||
_elements.pop_front(); //cleanup
|
||||
if (fn(el.item)) // fill new data
|
||||
k_fifo_put(&_fifo, &el); // put data into a queue
|
||||
else _elements.pop_front(); // cleanup
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
|
||||
namespace rims {
|
||||
K_FIFO_DEFINE(klogFifoQueue);
|
||||
|
||||
zephyr_fifo_buffer<log_EgressMessages, 10> logEgressFifoQueueBuffer{klogFifoQueue};
|
||||
|
||||
static std::size_t g_droppedLogs{0};
|
||||
@ -48,7 +47,7 @@ void Log::critical(const char *fmt, ...) const {
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
constexpr log_LogLevel toPbLogLevel(Log::Level level) {
|
||||
static constexpr log_LogLevel toPbLogLevel(Log::Level level) {
|
||||
return static_cast<log_LogLevel>(level);
|
||||
}
|
||||
|
||||
@ -65,11 +64,10 @@ static void setBasename(char *destination, const char *path) {
|
||||
}
|
||||
|
||||
void Log::formatLog(Level level, const char *fmt, va_list args) const {
|
||||
|
||||
if (should_log(level)) {
|
||||
// handle case when queue if full and we are waitnig for free space
|
||||
bool ok{false};
|
||||
// do {
|
||||
do {
|
||||
ok = logEgressFifoQueueBuffer.try_produce([&](log_EgressMessages &logmsg) {
|
||||
logmsg = log_EgressMessages_init_zero;
|
||||
|
||||
@ -80,12 +78,16 @@ void Log::formatLog(Level level, const char *fmt, va_list args) const {
|
||||
logmsg.data.logEntry.lineNumber = this->_sl.line();
|
||||
|
||||
setBasename(logmsg.data.logEntry.sourceFile, this->_sl.file_name());
|
||||
vsnprintf(logmsg.data.logEntry.logLine, 200, fmt, args);
|
||||
vsnprintf(logmsg.data.logEntry.logLine, sizeof(logmsg.data.logEntry.logLine), fmt, args);
|
||||
/// TODO too long logs?
|
||||
|
||||
return true;
|
||||
});
|
||||
if (not ok) g_droppedLogs++;// std::this_thread::sleep_for(std::chrono::microseconds{100});
|
||||
// } while (not ok);
|
||||
if (not ok) {
|
||||
g_droppedLogs++;
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{100});
|
||||
}
|
||||
} while (not ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,45 +1,30 @@
|
||||
#include "common.hpp"
|
||||
|
||||
#include "log.hpp"
|
||||
#include "placement_unique_ptr.hpp"
|
||||
|
||||
#include "message_decode.hpp"
|
||||
#include "phase_modulation.hpp"
|
||||
#include "syscalls/kernel.h"
|
||||
#include "temperature_measurements.hpp"
|
||||
#include "uart.hpp"
|
||||
#include "zephyr.hpp"
|
||||
#include "zero_cross_detection.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
|
||||
#include <utility>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/kernel/thread.h>
|
||||
#include <zephyr/kernel/thread_stack.h>
|
||||
|
||||
// priority lower value -> higher priority, can be negative
|
||||
|
||||
// constexpr int power_measurements_priority = 10;
|
||||
// constexpr int temperature_measurements_priority = 5;
|
||||
// constexpr int zero_cross_detection_priority = 4;
|
||||
// constexpr int phase_control_priority = 4;
|
||||
// constexpr int pid_priority = 0;
|
||||
|
||||
using namespace rims;
|
||||
|
||||
static K_THREAD_STACK_DEFINE(k_messengerStack, 1024);
|
||||
TStack messengerStack{k_messengerStack, K_THREAD_STACK_SIZEOF(k_messengerStack)};
|
||||
|
||||
static K_THREAD_STACK_DEFINE(k_uartStack, 1024);
|
||||
static K_THREAD_STACK_DEFINE(k_uartStack, 2048);
|
||||
TStack uartStack{k_uartStack, K_THREAD_STACK_SIZEOF(k_uartStack)};
|
||||
|
||||
static K_THREAD_STACK_DEFINE(k_temperatureSamplerStack, 3000);
|
||||
static K_THREAD_STACK_DEFINE(k_temperatureSamplerStack, 2048);
|
||||
TStack temperatureSamplerStack{k_temperatureSamplerStack, K_THREAD_STACK_SIZEOF(k_temperatureSamplerStack)};
|
||||
|
||||
static K_THREAD_STACK_DEFINE(k_zeroCrossDetectionStack, 1500);
|
||||
@ -64,13 +49,13 @@ template <typename T> class LazyInit {
|
||||
unique_placed_ptr<T> obj;
|
||||
|
||||
public:
|
||||
template <typename... Args> void init(Args &&...args) {
|
||||
template <typename... Args> constexpr void init(Args &&...args) {
|
||||
obj = placement_unique<T>(_buf)(std::forward<Args>(args)...);
|
||||
}
|
||||
T &operator*() {
|
||||
constexpr T &operator*() {
|
||||
return *obj;
|
||||
}
|
||||
T *operator->() {
|
||||
constexpr T *operator->() {
|
||||
return obj.get();
|
||||
}
|
||||
};
|
||||
@ -102,44 +87,11 @@ int main() {
|
||||
zcd->init_hw();
|
||||
zcd->start();
|
||||
|
||||
// phaseModulation->init_hw();
|
||||
// phaseModulation->start();
|
||||
phaseModulation->init_hw();
|
||||
phaseModulation->start();
|
||||
|
||||
zephyr::semaphore::sem semaphore{0, 1};
|
||||
|
||||
RecurringSemaphoreTimer tm{semaphore, std::chrono::seconds{2}};
|
||||
{
|
||||
auto interval = tm.interval().count();
|
||||
auto period = tm.period().count();
|
||||
auto kinterval = tm._interval.ticks;
|
||||
auto kperiod = tm._period.ticks;
|
||||
ULOG_INFO("interval: %lld period: %lld us", interval, period);
|
||||
ULOG_INFO("kinterval: %lld kperiod: %lld t", kinterval, kperiod);
|
||||
}
|
||||
tm.start();
|
||||
{
|
||||
auto interval = tm.interval().count();
|
||||
auto period = tm.period().count();
|
||||
auto kinterval = tm._interval.ticks;
|
||||
auto kperiod = tm._period.ticks;
|
||||
ULOG_INFO("st interval: %lld period: %lld us", interval, period);
|
||||
ULOG_INFO("st kinterval: %lld kperiod: %lld t", kinterval, kperiod);
|
||||
}
|
||||
|
||||
std::array<k_poll_event, 1> events;
|
||||
zephyr::event_pool::k_init(events[0], semaphore);
|
||||
|
||||
int i{};
|
||||
while (1) {
|
||||
auto ret = zephyr::event_pool::k_poll_forever(events);
|
||||
|
||||
if (ret == 0) {
|
||||
zephyr::event_pool::k_poll_handle(events[0], [&]() {
|
||||
k_sem_take(&semaphore, K_NO_WAIT);
|
||||
ULOG_INFO("hello from main thread nr %d!", i++);
|
||||
});
|
||||
}
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds{4000});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{50});
|
||||
}
|
||||
|
||||
// auto getStats = [](auto &diffs) {
|
||||
|
||||
@ -17,8 +17,6 @@
|
||||
|
||||
#include "uart.hpp"
|
||||
|
||||
#include "thread_analyzer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
@ -105,9 +103,9 @@ void rims::MessengerThread::event_logEgress()
|
||||
logEgressFifoQueueBuffer.try_consume([&](log_EgressMessages &in) { ipcPush(in, log_EgressMessages_msg, 4); });
|
||||
}
|
||||
|
||||
void MessengerThread::handle_brokenIngress()
|
||||
void MessengerThread::handle_badCRC()
|
||||
{
|
||||
|
||||
/// TODO
|
||||
}
|
||||
|
||||
void MessengerThread::threadMain() {
|
||||
@ -171,7 +169,7 @@ void MessengerThread::event_dataArrived() {
|
||||
/// TODO check CRC for data
|
||||
/// TODO check checksum od ID
|
||||
if(zephyr::crc::crc32_ieee({databegin, datasize}) != crc){
|
||||
/// TODO
|
||||
handle_badCRC();
|
||||
}
|
||||
|
||||
stream = pb_istream_from_buffer(databegin, datasize);
|
||||
@ -250,10 +248,10 @@ void MessengerThread::handle_configIngressMsg(pb_istream_t &stream, AsyncUART *c
|
||||
|
||||
template <typename Msg> void MessengerThread::ipcPush(Msg &m, const pb_msgdesc_t &fields, int id) {
|
||||
// encode embedded message directly to the buffer of output message to save stack
|
||||
auto ostream = pb_ostream_from_buffer(message.data.bytes, 250); // TODO max data size
|
||||
auto ostream = pb_ostream_from_buffer(message.data.bytes, sizeof(message.data.bytes)); // TODO max data size
|
||||
pb_encode(&ostream, &fields, &m);
|
||||
|
||||
// set ID od sender
|
||||
// set IF od sender
|
||||
message.id = id; /// TODO make response ID
|
||||
message.data.size = ostream.bytes_written;
|
||||
message.crc = zephyr::crc::crc32_ieee({message.data.bytes, message.data.size});
|
||||
@ -276,13 +274,14 @@ bool MessengerThread::decode(pb_istream_t &stream, const pb_msgdesc_t *fields, v
|
||||
|
||||
bool MessengerThread::encode(Message &msg, AsyncUART *cb) {
|
||||
auto outputStream = pb_ostream_from_buffer(outBuf+8, sizeof(outBuf)-8);
|
||||
// msg.crc = 0xffffffff; /// TODO calculate CRC
|
||||
|
||||
pb_encode(&outputStream, &Message_msg, &message);
|
||||
|
||||
unsigned encoded_len;
|
||||
cobs_ret_t const result = cobs_encode(outBuf+8, outputStream.bytes_written, outBuf, sizeof(outBuf), &encoded_len);
|
||||
|
||||
/// TODO create exceptions for this part insted of return values
|
||||
|
||||
if (result == COBS_RET_SUCCESS) {
|
||||
// encoding succeeded, 'encoded' and 'encoded_len' hold details.
|
||||
cb->transmit(cb, std::span{reinterpret_cast<std::byte *>(outBuf), encoded_len});
|
||||
|
||||
@ -20,35 +20,32 @@ extern k_msgq messenger_buffer_arrived_queue;
|
||||
class MessengerThread : public ZephyrThread {
|
||||
public:
|
||||
MessengerThread(TStackBase &stack);
|
||||
|
||||
static void send(){
|
||||
|
||||
|
||||
static void send() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
void threadMain() override;
|
||||
|
||||
|
||||
private:
|
||||
struct requestData {
|
||||
int type;
|
||||
int requestId;
|
||||
AsyncUART *cb;
|
||||
};
|
||||
|
||||
|
||||
void event_dataArrived();
|
||||
void event_temperatureEgress();
|
||||
void event_ctrlEgress();
|
||||
void event_configEgress();
|
||||
void event_logEgress();
|
||||
|
||||
void handle_brokenIngress();
|
||||
|
||||
|
||||
void handle_badCRC();
|
||||
|
||||
void handle_temperatureIngressMsg(pb_istream_t &stream, AsyncUART *cb);
|
||||
void handle_ctrlIngressMsg(pb_istream_t &stream, AsyncUART *cb);
|
||||
void handle_configIngressMsg(pb_istream_t &stream, AsyncUART *cb);
|
||||
|
||||
|
||||
bool decode(pb_istream_t &stream, const pb_msgdesc_t *fields, void *dest) const;
|
||||
bool encode(Message &msg, AsyncUART *cb);
|
||||
|
||||
|
||||
@ -255,7 +255,6 @@ void TemperatureSamplerOrchestrator::handle_samplerConfigRequest( //
|
||||
|
||||
void TemperatureSamplerOrchestrator::action_takeSample() {
|
||||
zephyr::semaphore::k_sem_take_now(_samplerSem);
|
||||
ULOG_INFO("s");
|
||||
_current_channel = (_current_channel + 1) % channelNumber;
|
||||
_channel[_current_channel].take_sample();
|
||||
/// TODO change channel
|
||||
|
||||
@ -1,196 +1,217 @@
|
||||
#include "uart.hpp"
|
||||
|
||||
#include "common.hpp"
|
||||
#include "log.hpp"
|
||||
#include "message_decode.hpp"
|
||||
#include "syscalls/uart.h"
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <exception>
|
||||
#include <thread>
|
||||
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#define UART_DEVICE_NODE
|
||||
// static const struct device *uart_dev = ;
|
||||
|
||||
namespace rims {
|
||||
K_MSGQ_DEFINE(usartEgressQueue, 100, 4, 1);
|
||||
K_SEM_DEFINE(uart_tx_done, 1, 1);
|
||||
|
||||
// static const device *uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE);
|
||||
|
||||
constexpr uint16_t rx_buf_size = 300;
|
||||
constexpr uint8_t rx_buf_num = 2;
|
||||
static uint8_t rx_buf_channel{0};
|
||||
static uint8_t rx_buf[rx_buf_num][rx_buf_size]; // DMA RX buffer
|
||||
|
||||
static int uartQueueWaitCounter{};
|
||||
AsyncUART *defaultUartInterface;
|
||||
|
||||
class uart_error : public std::exception {
|
||||
// static int uartQueueWaitCounter{};
|
||||
static AsyncUART *defaultUartInterface = nullptr;
|
||||
|
||||
class uart_not_ready_error : public std::exception {
|
||||
// exception interface
|
||||
public:
|
||||
const char *what() const noexcept override {
|
||||
return "uard device not ready";
|
||||
constexpr const char *what() const noexcept override {
|
||||
return "uart device not ready";
|
||||
}
|
||||
};
|
||||
|
||||
class uart_rx_not_ready_error : public std::exception {
|
||||
public:
|
||||
constexpr const char *what() const noexcept override {
|
||||
return "uart RX not ready to read data";
|
||||
}
|
||||
};
|
||||
|
||||
class uart_rx_buffer_overflow : public std::exception {
|
||||
public:
|
||||
constexpr const char *what() const noexcept override {
|
||||
return "uart RX buffer overflow";
|
||||
}
|
||||
};
|
||||
|
||||
// k_work structure to hold data for log information
|
||||
struct k_work_log_data {
|
||||
struct k_work work;
|
||||
const char *msg;
|
||||
};
|
||||
|
||||
// Work handler function
|
||||
void log_worker(struct k_work *work) {
|
||||
struct k_work_log_data *log_data = CONTAINER_OF(work, struct k_work_log_data, work);
|
||||
ULOG_ERROR("%s", log_data->msg);
|
||||
}
|
||||
|
||||
// Global log work item
|
||||
static struct k_work_log_data uart_log_work;
|
||||
|
||||
AsyncUART::AsyncUART() {
|
||||
_dev = DEVICE_DT_GET(DT_NODELABEL(usart1));
|
||||
k_work_init(&_work, AsyncUART::workHandler);
|
||||
|
||||
uart_callback_set(_dev, AsyncUART::uartCallback, this);
|
||||
|
||||
// Start DMA-based reception on the first buffer
|
||||
// printk("Setup DMA\n");
|
||||
int ret = uart_rx_enable(_dev, rx_buf[rx_buf_channel], rx_buf_size, 0); // Timeout 1ms
|
||||
auto ret = uart_irq_callback_user_data_set(_dev, AsyncUART::uartCallback, this);
|
||||
if (ret < 0) {
|
||||
throw uart_error{}; // catch this somewhere??
|
||||
throw uart_not_ready_error{}; // catch this somewhere??
|
||||
}
|
||||
|
||||
uart_irq_rx_enable(_dev);
|
||||
}
|
||||
|
||||
void AsyncUART::loop() {
|
||||
|
||||
if (!device_is_ready(_dev)) {
|
||||
// printk("%s device not ready, exit\n", __PRETTY_FUNCTION__);
|
||||
///TODO throw?
|
||||
/// TODO throw?
|
||||
return; // Exit if the UART device is not ready
|
||||
}
|
||||
|
||||
// static char buffer[300]{};
|
||||
while (1) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds{2});
|
||||
|
||||
// int offset = 0;
|
||||
// offset += std::sprintf(buffer + offset, "\n\r");
|
||||
// offset += std::sprintf(buffer + offset, " UART_TX_DONE : %4d\n\r", uartInterruptsCounters[UART_TX_DONE]);
|
||||
// offset += std::sprintf(buffer + offset, " UART_TX_ABORTED : %4d\n\r", uartInterruptsCounters[UART_TX_ABORTED]);
|
||||
// offset += std::sprintf(buffer + offset, " UART_RX_RDY : %4d\n\r", uartInterruptsCounters[UART_RX_RDY]);
|
||||
// offset += std::sprintf(buffer + offset, " UART_RX_BUF_REQUEST : %4d\n\r", uartInterruptsCounters[UART_RX_BUF_REQUEST]);
|
||||
// offset += std::sprintf(buffer + offset, "UART_RX_BUF_RELEASED : %4d\n\r", uartInterruptsCounters[UART_RX_BUF_RELEASED]);
|
||||
// offset += std::sprintf(buffer + offset, " UART_RX_DISABLED : %4d\n\r", uartInterruptsCounters[UART_RX_DISABLED]);
|
||||
// offset += std::sprintf(buffer + offset, " UART_RX_STOPPED : %4d\n\r", uartInterruptsCounters[UART_RX_STOPPED]);
|
||||
|
||||
// offset += std::sprintf(buffer + offset, " TOTAL tx bytes : %4d\n\r", sent_bytes);
|
||||
// offset += std::sprintf(buffer + offset, " TOTAL rx bytes : %4d\n\r", received_bytes);
|
||||
// offset += std::sprintf(buffer + offset, " waits : %4d\n\r", uartQueueWaitCounter);
|
||||
// offset += std::sprintf(buffer + offset, " work total [us] : %4lld\n\r", _workTotal.count() / 1000);
|
||||
// offset += std::sprintf(buffer + offset, " isr total [us]: %4lld\n\r", _isrTotal.count() / 1000);
|
||||
|
||||
// buffer[offset + 1] = 0;
|
||||
|
||||
// AsyncUART::transmit(defaultUart(), std::span{reinterpret_cast<std::byte *>(buffer), strlen(buffer)});
|
||||
}
|
||||
}
|
||||
|
||||
// free function, only need to copy data to uart's TX_BUFFER and that's it
|
||||
void AsyncUART::transmit(AsyncUART *dev, std::span<std::byte> bytes) {
|
||||
while (dev->txActive()) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10}); // wait for finelize transfer
|
||||
uartQueueWaitCounter++;
|
||||
}
|
||||
// assert length ok
|
||||
memcpy(dev->_txBuffer, bytes.data(), bytes.size());
|
||||
dev->_txBufferLength = bytes.size();
|
||||
if (bytes.empty()) return;
|
||||
|
||||
k_work_submit(&dev->_work);
|
||||
}
|
||||
|
||||
void AsyncUART::workHandler(k_work *work) {
|
||||
CONTAINER_OF(work, AsyncUART, _work)->workHandler();
|
||||
}
|
||||
int g_ret=0;
|
||||
|
||||
void AsyncUART::workHandler() {
|
||||
auto start = clock::now();
|
||||
size_t sent = 0;
|
||||
size_t CHUNK_SIZE = 8;
|
||||
|
||||
int maxwaits = 10;
|
||||
while ((sent < _txBufferLength) && (maxwaits> 0)) {
|
||||
volatile auto r = k_sem_take(&uart_tx_done, K_MSEC(10));
|
||||
|
||||
if (r == 0) {
|
||||
auto bytes = std::min(CHUNK_SIZE, _txBufferLength - sent);
|
||||
// sending 1 byte through UART hangs the device, to ommit this situation on larget streams,
|
||||
// we can split 9 to 5+4 bytes, instead of 8+1
|
||||
if (bytes == 9) bytes = 5;
|
||||
if (bytes == 1) {
|
||||
/// if you really ahve only one byte to send, use non async api
|
||||
uart_poll_out(_dev, static_cast<unsigned char>(_txBuffer[sent]));
|
||||
// we need to manualy set the semaphore to protect from lock
|
||||
k_sem_give(&uart_tx_done);
|
||||
} else {
|
||||
g_ret = uart_tx(_dev, reinterpret_cast<uint8_t *>(_txBuffer) + sent, bytes, 50);
|
||||
if (g_ret != 0) {
|
||||
while (true) {
|
||||
k_busy_wait(1);
|
||||
}
|
||||
}
|
||||
bool first = true;
|
||||
for (auto byte : bytes) {
|
||||
if (first) {
|
||||
if (dev->tx_buffer.empty()) {
|
||||
dev->tx_buffer.emplace_front((uint8_t)byte);
|
||||
uart_irq_tx_enable(dev->_dev); // enable interrupt
|
||||
continue;
|
||||
}
|
||||
sent += bytes;
|
||||
}else{
|
||||
maxwaits--;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
_txBufferLength = 0;
|
||||
auto stop = clock::now();
|
||||
_workTotal += {stop - start};
|
||||
while (dev->tx_buffer.full()) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{12});
|
||||
};
|
||||
dev->tx_buffer.emplace_front((uint8_t)byte);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncUART::uartCallback(const device *dev, uart_event *evt, void *user_data) {
|
||||
void AsyncUART::uartCallback(const device *dev, void *user_data) {
|
||||
auto _this = static_cast<AsyncUART *>(user_data);
|
||||
_this->callback(evt);
|
||||
_this->uartISR();
|
||||
}
|
||||
|
||||
void AsyncUART::callback(uart_event *evt) {
|
||||
auto start = clock::now();
|
||||
_nterruptsCounters[evt->type]++;
|
||||
buffer buf;
|
||||
switch (evt->type) {
|
||||
case UART_TX_DONE:
|
||||
sent_bytes += evt->data.tx.len;
|
||||
k_sem_give(&uart_tx_done);
|
||||
break;
|
||||
|
||||
case UART_TX_ABORTED:
|
||||
while (true) {
|
||||
k_busy_wait(1);
|
||||
void AsyncUART::uartISR() {
|
||||
try {
|
||||
while (uart_irq_update(_dev) && uart_irq_is_pending(_dev)) {
|
||||
if (rxHasByte()) {
|
||||
readByteUart();
|
||||
}
|
||||
if (uart_irq_tx_ready(_dev)) {
|
||||
putByteUart();
|
||||
}
|
||||
}
|
||||
k_sem_give(&uart_tx_done);
|
||||
break;
|
||||
|
||||
case UART_RX_RDY:
|
||||
// Process received bytes
|
||||
received_bytes += evt->data.rx.len;
|
||||
buf = buffer{
|
||||
.data = reinterpret_cast<std::byte *>(evt->data.rx.buf + evt->data.rx.offset), .size = evt->data.rx.len, .device = this
|
||||
}; // add callback
|
||||
k_msgq_put(&messenger_buffer_arrived_queue, &buf, K_MSEC(10));
|
||||
[[fallthrough]];
|
||||
case UART_RX_BUF_REQUEST:
|
||||
rx_buf_channel = rx_buf_channel == 1 ? 0 : 1;
|
||||
uart_rx_buf_rsp(_dev, rx_buf[rx_buf_channel], rx_buf_size);
|
||||
break;
|
||||
case UART_RX_BUF_RELEASED:
|
||||
// rx_buf_channel = rx_buf_channel == 1 ? 0 : 1;
|
||||
break;
|
||||
|
||||
case UART_RX_DISABLED:
|
||||
// printk("UART_RX_DISABLED\n");
|
||||
uart_rx_enable(_dev, rx_buf[rx_buf_channel], rx_buf_size, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} catch (const uart_rx_buffer_overflow &e) {
|
||||
handleRxBufferOverflowError(e);
|
||||
} catch (const uart_rx_not_ready_error &e) {
|
||||
handleRxNotReadyError(e); // Offload logging to a worker thread
|
||||
} catch (const std::exception &e) { // nothing can go here really
|
||||
[[maybe_unused]] volatile auto what = e.what();
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
auto stop = clock::now();
|
||||
_isrTotal += stop - start;
|
||||
}
|
||||
|
||||
bool AsyncUART::rxHasByte() const {
|
||||
return uart_irq_rx_ready(_dev);
|
||||
}
|
||||
|
||||
void AsyncUART::readByteUart() {
|
||||
if (faultFlag) {
|
||||
if (rxByte() == 0) faultFlag = false;
|
||||
}
|
||||
// throw on buffer overflow
|
||||
else if (tx_buffer.full()) {
|
||||
throw uart_rx_buffer_overflow{};
|
||||
}
|
||||
// push_back returns last placed byte, if the byte is 0x00 we got end of frame
|
||||
else if (rxBuffer().push_back(rxByte()) == 0) {
|
||||
processMessage();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t AsyncUART::rxByte() {
|
||||
int recv_len;
|
||||
uint8_t buffer[1];
|
||||
|
||||
recv_len = uart_fifo_read(_dev, buffer, 1);
|
||||
if (recv_len < 0) {
|
||||
// LOG_ERR("Failed to read UART FIFO");
|
||||
throw uart_rx_not_ready_error{};
|
||||
};
|
||||
|
||||
return buffer[0];
|
||||
}
|
||||
|
||||
void AsyncUART::processMessage() {
|
||||
buffer buf{.data = reinterpret_cast<std::byte *>(rxBuffer().data()), .size = rxBuffer().size(), .device = this};
|
||||
switchRxBuffer();
|
||||
k_msgq_put(&messenger_buffer_arrived_queue, &buf, K_MSEC(10));
|
||||
}
|
||||
|
||||
AsyncUART::rx_buffer_t &AsyncUART::rxBuffer() {
|
||||
return rx_buffers[_currentBufferIndex];
|
||||
}
|
||||
|
||||
void AsyncUART::switchRxBuffer() {
|
||||
_currentBufferIndex = _currentBufferIndex == 1 ? 0 : 1;
|
||||
rxBuffer().clean();
|
||||
}
|
||||
|
||||
bool AsyncUART::txHasByte() const {
|
||||
return tx_buffer.size();
|
||||
}
|
||||
|
||||
bool AsyncUART::txByte(uint8_t byte) {
|
||||
auto send_len = uart_fifo_fill(_dev, &byte, 1);
|
||||
if (send_len != 1) {
|
||||
// LOG_ERR("Drop %d bytes", rb_len - send_len);
|
||||
while (1) {
|
||||
/// just loop here
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AsyncUART::putByteUart() {
|
||||
bool hasData = txHasByte();
|
||||
if (hasData) txByte(tx_buffer.get());
|
||||
else uart_irq_tx_disable(_dev);
|
||||
}
|
||||
|
||||
void AsyncUART::handleRxBufferOverflowError(const uart_rx_buffer_overflow &e) {
|
||||
// log what went wrong, this is safe
|
||||
// Offload logging to a worker thread
|
||||
uart_log_work.msg = e.what();
|
||||
k_work_submit(&uart_log_work.work);
|
||||
|
||||
// clean current buffer as is is usless for us now
|
||||
rxBuffer().clean();
|
||||
|
||||
// indicate the driver to skip bytes until end of frame
|
||||
faultFlag = true;
|
||||
}
|
||||
|
||||
void AsyncUART::handleRxNotReadyError(const uart_rx_not_ready_error &e) {
|
||||
// log what went wrong, this is safe
|
||||
uart_log_work.msg = e.what();
|
||||
k_work_submit(&uart_log_work.work);
|
||||
}
|
||||
|
||||
int UARTThread::do_hardwarenInit() {
|
||||
@ -198,6 +219,7 @@ int UARTThread::do_hardwarenInit() {
|
||||
}
|
||||
|
||||
void UARTThread::threadMain() {
|
||||
k_work_init(&uart_log_work.work, log_worker);
|
||||
/// runs in context of new thread, on new thread stack etc.
|
||||
AsyncUART thread{};
|
||||
defaultUartInterface = &thread;
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "circular_buffer.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/kernel.h>
|
||||
@ -13,37 +14,46 @@ namespace rims {
|
||||
class AsyncUART;
|
||||
AsyncUART *defaultUart();
|
||||
|
||||
class uart_rx_buffer_overflow;
|
||||
class uart_rx_not_ready_error;
|
||||
|
||||
class AsyncUART {
|
||||
using rx_buffer_t = static_vector<uint8_t, 256>;
|
||||
using tx_buffer_t = circular_buffer<uint8_t, 256>;
|
||||
|
||||
public:
|
||||
AsyncUART();
|
||||
void loop();
|
||||
|
||||
static void transmit(AsyncUART *dev, std::span<std::byte> bytes);
|
||||
static void workHandler(k_work *work);
|
||||
static void uartCallback(const struct device *dev, struct uart_event *evt, void *user_data);
|
||||
|
||||
constexpr bool txFree() const {
|
||||
return _txBufferLength == 0;
|
||||
}
|
||||
constexpr bool txActive() const {
|
||||
return _txBufferLength != 0;
|
||||
}
|
||||
|
||||
void workHandler();
|
||||
void callback(struct uart_event *evt);
|
||||
|
||||
static void uartCallback(const struct device *dev, void *user_data);
|
||||
|
||||
void uartISR();
|
||||
const device *_dev;
|
||||
|
||||
static constexpr int txBufferSize = 300;
|
||||
private:
|
||||
std::array<rx_buffer_t, 2> rx_buffers;
|
||||
uint8_t _currentBufferIndex{};
|
||||
bool faultFlag = false;
|
||||
tx_buffer_t tx_buffer;
|
||||
|
||||
std::byte _txBuffer[txBufferSize];
|
||||
std::size_t _txBufferLength = 0;
|
||||
k_work _work;
|
||||
// ISR RX part
|
||||
inline bool rxHasByte() const;
|
||||
inline void readByteUart(); // process byte
|
||||
inline uint8_t rxByte(); // low level read byte from device
|
||||
inline void processMessage();
|
||||
inline rx_buffer_t &rxBuffer();
|
||||
inline void switchRxBuffer();
|
||||
|
||||
clock::duration _isrTotal{}, _workTotal{};
|
||||
std::array<int, 7> _nterruptsCounters{};
|
||||
size_t received_bytes = 0;
|
||||
size_t sent_bytes = 0;
|
||||
// ISR TX part
|
||||
inline void putByteUart();
|
||||
inline bool txHasByte() const;
|
||||
inline bool txByte(uint8_t byte); // low level write byte to device
|
||||
|
||||
// exception handlers
|
||||
inline void handleRxNotReadyError(const uart_rx_not_ready_error &e);
|
||||
inline void handleRxBufferOverflowError(const uart_rx_buffer_overflow &e);
|
||||
};
|
||||
|
||||
class UARTThread : public ZephyrThread {
|
||||
|
||||
@ -2,109 +2,123 @@ syntax = "proto3";
|
||||
|
||||
package temperature;
|
||||
|
||||
enum Error{
|
||||
NoError = 0;
|
||||
UnknownChannel = 1;
|
||||
TemperatureSensorDisconnected = 2;
|
||||
TemperatureSensorBroken = 3;
|
||||
enum Error {
|
||||
NoError = 0;
|
||||
UnknownChannel = 1;
|
||||
TemperatureSensorDisconnected = 2;
|
||||
TemperatureSensorBroken = 3;
|
||||
}
|
||||
|
||||
/* CONFIGURATION */
|
||||
message SamplerConfig{
|
||||
optional uint32 samples = 3; // optional, if ommited return current value
|
||||
optional uint32 period_ms = 4; // optional
|
||||
message SamplerConfig
|
||||
{
|
||||
optional uint32 samples = 3; // optional, if ommited return current value
|
||||
optional uint32 period_ms = 4; // optional
|
||||
}
|
||||
|
||||
/* INTERNAL STRUCTURES */
|
||||
|
||||
// Last temp sample
|
||||
message TemperatureCurrent {
|
||||
uint32 channel_id = 1;
|
||||
|
||||
// last update
|
||||
float temp_c = 2;
|
||||
message TemperatureCurrent
|
||||
{
|
||||
uint32 channel_id = 1;
|
||||
|
||||
// last update
|
||||
float temp_c = 2;
|
||||
}
|
||||
|
||||
// Statictics of temperature on given channel
|
||||
message TemperatureStatistics {
|
||||
uint32 channel_id = 1;
|
||||
message TemperatureStatistics
|
||||
{
|
||||
uint32 channel_id = 1;
|
||||
|
||||
// last update
|
||||
float temp_c = 2;
|
||||
// last update
|
||||
float temp_c = 2;
|
||||
|
||||
// avarage from last nsamples
|
||||
float temp_avg_c = 3;
|
||||
// avarage from last nsamples
|
||||
float temp_avg_c = 3;
|
||||
|
||||
// standard deviation of temperature on given channel from last nsamples
|
||||
float temp_stddev_c = 4;
|
||||
// standard deviation of temperature on given channel from last nsamples
|
||||
float temp_stddev_c = 4;
|
||||
|
||||
// samples taken
|
||||
uint32 n_samples = 5;
|
||||
// samples taken
|
||||
uint32 n_samples = 5;
|
||||
}
|
||||
|
||||
/* CONFIG API */
|
||||
|
||||
message SamplerConfigRequest{
|
||||
uint32 channel_id = 1;
|
||||
SamplerConfig config = 2;
|
||||
message SamplerConfigRequest
|
||||
{
|
||||
uint32 channel_id = 1;
|
||||
SamplerConfig config = 2;
|
||||
}
|
||||
|
||||
message SamplerConfigResponse{
|
||||
oneof data {
|
||||
SamplerConfig config = 2;
|
||||
Error error = 254;
|
||||
}
|
||||
message SamplerConfigResponse
|
||||
{
|
||||
oneof data
|
||||
{
|
||||
SamplerConfig config = 2;
|
||||
Error error = 254;
|
||||
}
|
||||
}
|
||||
|
||||
// API
|
||||
message GetCurrentTemperatureRequest{
|
||||
uint32 channel_id = 1;
|
||||
message GetCurrentTemperatureRequest
|
||||
{
|
||||
uint32 channel_id = 1;
|
||||
};
|
||||
|
||||
message GetCurrentTemperatureResponse{
|
||||
oneof data {
|
||||
TemperatureCurrent temperatureCurrent = 1;
|
||||
Error error = 254;
|
||||
}
|
||||
message GetCurrentTemperatureResponse
|
||||
{
|
||||
oneof data
|
||||
{
|
||||
TemperatureCurrent temperatureCurrent = 1;
|
||||
Error error = 254;
|
||||
}
|
||||
}
|
||||
|
||||
message GetTemperatureRequest{
|
||||
uint32 channel_id = 1;
|
||||
message GetTemperatureRequest
|
||||
{
|
||||
uint32 channel_id = 1;
|
||||
};
|
||||
|
||||
message GetTemperatureResponse{
|
||||
oneof data {
|
||||
TemperatureStatistics temperatureStats = 1;
|
||||
Error error = 254;
|
||||
}
|
||||
message GetTemperatureResponse
|
||||
{
|
||||
oneof data
|
||||
{
|
||||
TemperatureStatistics temperatureStats = 1;
|
||||
Error error = 254;
|
||||
}
|
||||
}
|
||||
|
||||
// only those messages are send through wire at temperature endpoint
|
||||
message IngressMessages
|
||||
{
|
||||
uint32 requestID = 255;
|
||||
oneof data {
|
||||
// data access
|
||||
GetCurrentTemperatureRequest currentTemperatureRequest = 1;
|
||||
GetTemperatureRequest temperatureRequest = 2;
|
||||
|
||||
// configuration
|
||||
SamplerConfigRequest samplerConfigRequest = 3;
|
||||
}
|
||||
uint32 requestID = 255;
|
||||
oneof data
|
||||
{
|
||||
// data access
|
||||
GetCurrentTemperatureRequest currentTemperatureRequest = 1;
|
||||
GetTemperatureRequest temperatureRequest = 2;
|
||||
|
||||
// configuration
|
||||
SamplerConfigRequest samplerConfigRequest = 3;
|
||||
}
|
||||
};
|
||||
|
||||
message EgressMessages
|
||||
{
|
||||
optional uint32 requestID = 255;
|
||||
oneof data{
|
||||
// data access
|
||||
GetCurrentTemperatureResponse currentTemperatureResponse = 1;
|
||||
GetTemperatureResponse temperatureResponse = 2;
|
||||
|
||||
// configuration
|
||||
SamplerConfigResponse samplerConfigResponse = 3;
|
||||
|
||||
// broadcast
|
||||
TemperatureStatistics broadcastTemperatureStatistics = 16;
|
||||
}
|
||||
optional uint32 requestID = 255;
|
||||
oneof data
|
||||
{
|
||||
// data access
|
||||
GetCurrentTemperatureResponse currentTemperatureResponse = 1;
|
||||
GetTemperatureResponse temperatureResponse = 2;
|
||||
|
||||
// configuration
|
||||
SamplerConfigResponse samplerConfigResponse = 3;
|
||||
|
||||
// broadcast
|
||||
TemperatureStatistics broadcastTemperatureStatistics = 16;
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user