From 242521678d11483b5ae03d0e9126cd202add156c Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Mon, 3 Mar 2025 17:02:22 +0100 Subject: [PATCH] uart fixes --- boards/bartoszek/rims/rims_h503cbt.dts | 13 +- drivers/serial/uart_stm32.c | 2 - rims_app/CMakeLists.txt | 2 - rims_app/common.proto | 54 ++-- rims_app/configuration.proto | 78 +++--- rims_app/ctrl.proto | 62 +++-- rims_app/log.proto | 2 +- rims_app/message.proto | 16 +- rims_app/prj.conf | 24 +- rims_app/src/circular_buffer.hpp | 52 +++- rims_app/src/log.cpp | 16 +- rims_app/src/main.cpp | 68 +---- rims_app/src/message_decode.cpp | 15 +- rims_app/src/message_decode.hpp | 19 +- rims_app/src/temperature_measurements.cpp | 1 - rims_app/src/uart.cpp | 306 ++++++++++++---------- rims_app/src/uart.hpp | 52 ++-- rims_app/temperature.proto | 146 ++++++----- 18 files changed, 488 insertions(+), 440 deletions(-) diff --git a/boards/bartoszek/rims/rims_h503cbt.dts b/boards/bartoszek/rims/rims_h503cbt.dts index e63c79f0e34..08238b99154 100644 --- a/boards/bartoszek/rims/rims_h503cbt.dts +++ b/boards/bartoszek/rims/rims_h503cbt.dts @@ -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"; diff --git a/drivers/serial/uart_stm32.c b/drivers/serial/uart_stm32.c index 13c85a5066d..c9c25be5175 100644 --- a/drivers/serial/uart_stm32.c +++ b/drivers/serial/uart_stm32.c @@ -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; diff --git a/rims_app/CMakeLists.txt b/rims_app/CMakeLists.txt index a48283b08c4..8d7099cf169 100644 --- a/rims_app/CMakeLists.txt +++ b/rims_app/CMakeLists.txt @@ -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) diff --git a/rims_app/common.proto b/rims_app/common.proto index b2179c23489..d1b1b893dac 100644 --- a/rims_app/common.proto +++ b/rims_app/common.proto @@ -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: - SetRequest: Specifies the new value for the parameter. - SetResponse: Acknowledges the result of the Set operation. - GetRequest: Requests the current value of the parameter. - GetResponse: 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: SetRequest: Specifies the new value for the parameter. SetResponse: +Acknowledges the result of the Set operation. GetRequest: Requests the current value of the +parameter. GetResponse: 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; }; diff --git a/rims_app/configuration.proto b/rims_app/configuration.proto index 8a3810e9eef..78127e7a552 100644 --- a/rims_app/configuration.proto +++ b/rims_app/configuration.proto @@ -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; + } }; diff --git a/rims_app/ctrl.proto b/rims_app/ctrl.proto index b13ffd704ef..950b9e12932 100644 --- a/rims_app/ctrl.proto +++ b/rims_app/ctrl.proto @@ -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 + } }; diff --git a/rims_app/log.proto b/rims_app/log.proto index 6432e523900..1b051454700 100644 --- a/rims_app/log.proto +++ b/rims_app/log.proto @@ -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 } diff --git a/rims_app/message.proto b/rims_app/message.proto index aa2f04e36fa..e9bde3f089f 100644 --- a/rims_app/message.proto +++ b/rims_app/message.proto @@ -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; } diff --git a/rims_app/prj.conf b/rims_app/prj.conf index 16928694305..ee777d885ea 100644 --- a/rims_app/prj.conf +++ b/rims_app/prj.conf @@ -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 + diff --git a/rims_app/src/circular_buffer.hpp b/rims_app/src/circular_buffer.hpp index 859686c688e..ddaf8f6b06f 100644 --- a/rims_app/src/circular_buffer.hpp +++ b/rims_app/src/circular_buffer.hpp @@ -1,6 +1,7 @@ #pragma once #include "zephyr.hpp" +#include "zephyr/sys/__assert.h" #include #include #include @@ -21,11 +22,49 @@ namespace rims { // virtual void pop_back() = 0; // }; +template class static_vector { + private: + std::array 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 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 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(std::addressof(buf_[tail_]))); std::destroy_at(std::addressof(buf_[tail_])); @@ -60,7 +99,7 @@ template 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 class zephyr_fifo_buffer { if (_elements.full()) return false; auto &el = _elements.emplace_front(ZephyrFifoElement{}); // 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; } diff --git a/rims_app/src/log.cpp b/rims_app/src/log.cpp index 8796be36bba..03fc8e72a0f 100644 --- a/rims_app/src/log.cpp +++ b/rims_app/src/log.cpp @@ -8,7 +8,6 @@ namespace rims { K_FIFO_DEFINE(klogFifoQueue); - zephyr_fifo_buffer 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(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); } } diff --git a/rims_app/src/main.cpp b/rims_app/src/main.cpp index 59f0590b417..143f6510648 100644 --- a/rims_app/src/main.cpp +++ b/rims_app/src/main.cpp @@ -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 #include -#include #include +#include #include #include -#include -#include +#include #include #include -// 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 class LazyInit { unique_placed_ptr obj; public: - template void init(Args &&...args) { + template constexpr void init(Args &&...args) { obj = placement_unique(_buf)(std::forward(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 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) { diff --git a/rims_app/src/message_decode.cpp b/rims_app/src/message_decode.cpp index 08dc6f48a17..d705953d558 100644 --- a/rims_app/src/message_decode.cpp +++ b/rims_app/src/message_decode.cpp @@ -17,8 +17,6 @@ #include "uart.hpp" -#include "thread_analyzer.hpp" - #include #include #include @@ -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 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(outBuf), encoded_len}); diff --git a/rims_app/src/message_decode.hpp b/rims_app/src/message_decode.hpp index c64e0e13326..548f92578ae 100644 --- a/rims_app/src/message_decode.hpp +++ b/rims_app/src/message_decode.hpp @@ -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); diff --git a/rims_app/src/temperature_measurements.cpp b/rims_app/src/temperature_measurements.cpp index 6065162353b..c9e1af4ea99 100644 --- a/rims_app/src/temperature_measurements.cpp +++ b/rims_app/src/temperature_measurements.cpp @@ -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 diff --git a/rims_app/src/uart.cpp b/rims_app/src/uart.cpp index 2a00aa23f0e..c1b614fa4ea 100644 --- a/rims_app/src/uart.cpp +++ b/rims_app/src/uart.cpp @@ -1,196 +1,217 @@ #include "uart.hpp" -#include "common.hpp" +#include "log.hpp" #include "message_decode.hpp" +#include "syscalls/uart.h" -#include #include +#include #include #include #include -#include +#include #include #include #include #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(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 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(_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(_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(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(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(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; diff --git a/rims_app/src/uart.hpp b/rims_app/src/uart.hpp index 11c55548cd1..8da04004cf1 100644 --- a/rims_app/src/uart.hpp +++ b/rims_app/src/uart.hpp @@ -1,9 +1,10 @@ #pragma once +#include "circular_buffer.hpp" #include "common.hpp" -#include #include +#include #include #include #include @@ -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; + using tx_buffer_t = circular_buffer; + public: AsyncUART(); void loop(); static void transmit(AsyncUART *dev, std::span 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_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 _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 { diff --git a/rims_app/temperature.proto b/rims_app/temperature.proto index 68b7b9938e9..f67d9abbc8d 100644 --- a/rims_app/temperature.proto +++ b/rims_app/temperature.proto @@ -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; + } };