diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 15af3bc..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "components/f4ll"] - path = components/f4ll - url = ssh://git@codeberg.org/abody/f4ll.git diff --git a/Core/Src/freertos.c b/Core/Src/freertos.c index ae32c6c..4427b4d 100644 --- a/Core/Src/freertos.c +++ b/Core/Src/freertos.c @@ -127,7 +127,7 @@ void MX_FREERTOS_Init(void) __weak void StartDefaultTask(void const *argument) { /* USER CODE BEGIN StartDefaultTask */ - app_main(); + app_main(NULL); /* Infinite loop */ for (;;) { osDelay(1); diff --git a/components/app/inc/app.h b/components/app/inc/app.h index c6f2767..37b146f 100644 --- a/components/app/inc/app.h +++ b/components/app/inc/app.h @@ -13,17 +13,15 @@ #ifdef __cplusplus #include -#include - #include #include -class app : public f4ll::singleton, public f4ll::console_handler::iconsole_input +class app : public f4ll::initialized_singleton, public f4ll::console_handler::iconsole_input { - friend class f4ll::singleton; + friend class f4ll::initialized_singleton; private: - app(); + app(void const *param); public: __attribute__((noreturn)) void main(); @@ -51,7 +49,7 @@ private: extern "C" { #endif // __cplusplus -void app_main() __attribute__((noreturn)); +void app_main(void const *param) __attribute__((noreturn)); #ifdef __cplusplus } // extern "C" { diff --git a/components/app/src/app.cpp b/components/app/src/app.cpp index 4c41163..747c88d 100644 --- a/components/app/src/app.cpp +++ b/components/app/src/app.cpp @@ -15,21 +15,18 @@ #include #include -#include -#include - #include #include #include -void app_main() +void app_main(void const *param) { - app::instance().main(); + app::init(param).main(); } -app::app() +app::app(void const *param) : m_con( f4ll::console_handler::init( USART2, DMA1, LL_DMA_STREAM_5, LL_DMA_STREAM_6, m_rx_buf_mem, sizeof(m_rx_buf_mem), m_tx_buf_mem, sizeof(m_tx_buf_mem), @@ -37,6 +34,7 @@ app::app() m_safe_conout(m_con) { + (void)param; } void app::main() diff --git a/components/f4ll b/components/f4ll deleted file mode 160000 index 80fc520..0000000 --- a/components/f4ll +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 80fc520b6fad8a012e239efca77f33f759337344 diff --git a/components/f4ll/.clang-format b/components/f4ll/.clang-format new file mode 100644 index 0000000..624e7ab --- /dev/null +++ b/components/f4ll/.clang-format @@ -0,0 +1,37 @@ +BasedOnStyle: LLVM +UseTab: Never +IndentWidth: 4 +TabWidth: 4 +BreakBeforeBraces: Custom +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: true +AllowShortLambdasOnASingleLine: true +AllowAllArgumentsOnNextLine: true +IndentCaseLabels: true +AccessModifierOffset: -4 +NamespaceIndentation: None +FixNamespaceComments: false +PackConstructorInitializers: Never +AlignAfterOpenBracket: AlwaysBreak +InsertBraces: true +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterFunctionDefinitionName: false +BraceWrapping: + AfterClass: true # false + AfterControlStatement: false + AfterEnum: false # false + AfterFunction: true # false + AfterNamespace: false + AfterObjCDeclaration: true # false + AfterStruct: true # false + AfterUnion: true # false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +ColumnLimit: 140 diff --git a/components/f4ll/.gitignore b/components/f4ll/.gitignore new file mode 100644 index 0000000..c570f57 --- /dev/null +++ b/components/f4ll/.gitignore @@ -0,0 +1,4 @@ +CMakeFiles/ +CMakeCache.txt +build.ninja +cmake_install.cmake \ No newline at end of file diff --git a/components/f4ll/.gitrepo b/components/f4ll/.gitrepo new file mode 100644 index 0000000..7046842 --- /dev/null +++ b/components/f4ll/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme +; +[subrepo] + remote = ssh://git@codeberg.org/abody/f4ll.git + branch = master + commit = f86c164de5c279c378c5cde408af5fe75497c474 + parent = d72d3af2a7e99af89780fcfa66071f99db11c766 + method = merge + cmdver = 0.4.9 diff --git a/components/f4ll/CMakeLists.txt b/components/f4ll/CMakeLists.txt new file mode 100644 index 0000000..1236174 --- /dev/null +++ b/components/f4ll/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.22) + +add_library(f4ll STATIC + src/console_handler.cpp + src/crc_handler.cpp + src/dma_helper.cpp + src/fault.cpp + src/memcpy_dma.cpp + src/packet_usart.cpp + src/ringbuffer.cpp + src/str_util.cpp + src/usart_core.cpp +) + +target_include_directories(f4ll PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/inc +) + +target_link_libraries(f4ll PUBLIC platform stm32cubemx) + +# ST code quality workaround - Suppres register storage class warning (C++17...) +target_compile_options(f4ll PRIVATE -Wno-register) diff --git a/components/f4ll/inc/f4ll/console_handler.h b/components/f4ll/inc/f4ll/console_handler.h new file mode 100644 index 0000000..210e410 --- /dev/null +++ b/components/f4ll/inc/f4ll/console_handler.h @@ -0,0 +1,65 @@ +/* + * ll_consolehandler.h + * + * Created on: Nov 7, 2019 + * Author: abody + */ + +#pragma once + +#include +#include +#include + +#include + +namespace f4ll { + +class console_handler : public usart_core, public initialized_singleton +{ + friend class initialized_singleton; + +public: + using size_type = iringbuffer::size_type; + + class iconsole_input + { + public: + virtual void input_available(size_type len) = 0; + }; + + void print(char const *s); + void flush(); + size_type append(char const *s); + + iringbuffer &get_rx_buffer() { return m_rx_buffer; } + +private: + console_handler( + USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx, uint8_t *rx_buffer, size_type x_buffer_size, + uint8_t *tx_buffer, size_type tx_buffer_size, iconsole_input *rx_callback); + + void setup_receive(void); + + // LL_UsartCore pure virtual function implementations + virtual void receiver_idle(void) override; + virtual void transmission_complete(void) override; + virtual void framing_error(void) override; + virtual void overrun(void) override; + virtual void rx_dma_transfer_complete(void) override; + virtual void rx_dma_half_transfer(void) override; + virtual void rx_dma_error(dma_helper::dma_error_type reason) override; + virtual void tx_dma_transfer_complete(void) override; + virtual void tx_dma_half_transfer(void) override; + virtual void tx_dma_error(dma_helper::dma_error_type reason) override; + + ringbuffer_ext m_tx_buffer; + size_type m_bytes_sent = 0; + + ringbuffer_ext m_rx_buffer; + size_type m_bytes_requested = 0; + size_type m_reqd_bytes_registered = 0; + iconsole_input *m_rx_callback; +}; + +} /* namespace f4ll */ diff --git a/components/f4ll/inc/f4ll/crc_handler.h b/components/f4ll/inc/f4ll/crc_handler.h new file mode 100644 index 0000000..26ae056 --- /dev/null +++ b/components/f4ll/inc/f4ll/crc_handler.h @@ -0,0 +1,92 @@ +/* + * ll_crc_handler.h + * + * Created on: Oct 26, 2019 + * Author: compi + */ +#pragma once + +#include + +#include + +#include +#include + +namespace f4ll { + +class crc_handler : public initialized_singleton +{ + friend class initialized_singleton; + +public: + struct icallback + { + virtual void crc_succeeded(uintptr_t callback_param, uint32_t crc, uint8_t task) = 0; + virtual void crc_failed(uintptr_t callback_param, uint32_t crc, uint8_t task) = 0; + }; + + class slot_base + { + friend class crc_handler; + + public: + struct crc_task + { + void const *m_address; // changed to nullptr when execution starts + uint16_t m_word_count; + icallback *m_callback; + uintptr_t m_callback_param; + }; + + private: + slot_base volatile *m_next = nullptr; + uint8_t m_task_count; + + virtual crc_task volatile &operator[](int index) volatile = 0; + + protected: + slot_base(unsigned int task_count) + : m_task_count(task_count) + { + } + slot_base() = delete; + slot_base(slot_base const &other) = delete; + }; + + template class slot : public slot_base + { + public: + slot() + : slot_base(n) + { + } + virtual crc_task volatile &operator[](int index) volatile { return m_tasks[index]; } + + private: + slot::crc_task m_tasks[n]; + }; + + void attach_slot(slot_base &slot); + bool enqueue(slot_base &slot, uint8_t task, void const *address, uint16_t len, icallback *cb, uintptr_t cb_param); + uint32_t compute(slot_base &slot, uint8_t task, void const *address, uint16_t len); + + bool is_active(slot_base &slot, uint8_t task) const; + bool is_queued(slot_base &slot, uint8_t task) const; + bool is_running(slot_base &slot, uint8_t task) const; + + void dma_transfer_completed(void); + +private: + crc_handler(DMA_TypeDef *dma, uint32_t stream); + + void start_next_task(void); + void wait_results(slot_base &slot, uint8_t task) const; + + dma_helper m_dma; + slot_base volatile *m_first_slot = nullptr; + slot_base volatile *m_active_slot = nullptr; + int volatile m_active_task; +}; + +} // namespace f4ll diff --git a/components/f4ll/inc/f4ll/dma_helper.h b/components/f4ll/inc/f4ll/dma_helper.h new file mode 100644 index 0000000..b2d1045 --- /dev/null +++ b/components/f4ll/inc/f4ll/dma_helper.h @@ -0,0 +1,58 @@ +/* + * ll_dmahelper.h + * + * Created on: Oct 25, 2019 + * Author: abody + */ + +#pragma once + +#include + +#include + +namespace f4ll { + +class dma_helper +{ +public: + dma_helper(DMA_TypeDef *dma, uint32_t stream); + dma_helper(dma_helper const &base) = default; + + inline DMA_TypeDef *get_dma() const { return m_dma; } + inline uint32_t get_stream() const { return m_stream; } + inline volatile uint32_t *get_is_reg() const { return m_is_reg; } + inline volatile uint32_t *get_ifc_reg() const { return m_ifc_reg; } + inline uint32_t get_fe_mask() const { return m_fe_masks[m_stream]; } + inline uint32_t get_dme_mask() const { return m_dme_masks[m_stream]; } + inline uint32_t get_te_mask() const { return m_te_masks[m_stream]; } + inline uint32_t get_ht_mask() const { return m_ht_masks[m_stream]; } + inline uint32_t get_tc_mask() const { return m_tc_masks[m_stream]; } + + inline bool is_enabled_it_ht() const { return LL_DMA_IsEnabledIT_HT(m_dma, m_stream) != 0; } + inline bool is_enabled_it_te() const { return LL_DMA_IsEnabledIT_TE(m_dma, m_stream) != 0; } + inline bool is_enabled_it_tc() const { return LL_DMA_IsEnabledIT_TC(m_dma, m_stream) != 0; } + inline bool is_enabled_it_dme() const { return LL_DMA_IsEnabledIT_DME(m_dma, m_stream) != 0; } + inline bool is_enabled_it_fe() const { return LL_DMA_IsEnabledIT_FE(m_dma, m_stream) != 0; } + + enum class dma_error_type { transfer, direct_mode, fifo }; + +private: + DMA_TypeDef *m_dma; + uint32_t const m_stream; + volatile uint32_t * const m_is_reg; + volatile uint32_t * const m_ifc_reg; + + static constexpr uint32_t const m_fe_masks[8] = {DMA_LISR_FEIF0, DMA_LISR_FEIF1, DMA_LISR_FEIF2, DMA_LISR_FEIF3, + DMA_HISR_FEIF4, DMA_HISR_FEIF5, DMA_HISR_FEIF6, DMA_HISR_FEIF7}; + static constexpr uint32_t const m_dme_masks[8] = {DMA_LISR_DMEIF0, DMA_LISR_DMEIF1, DMA_LISR_DMEIF2, DMA_LISR_DMEIF3, + DMA_HISR_DMEIF4, DMA_HISR_DMEIF5, DMA_HISR_DMEIF6, DMA_HISR_DMEIF7}; + static constexpr uint32_t const m_te_masks[8] = {DMA_LISR_TEIF0, DMA_LISR_TEIF1, DMA_LISR_TEIF2, DMA_LISR_TEIF3, + DMA_HISR_TEIF4, DMA_HISR_TEIF5, DMA_HISR_TEIF6, DMA_HISR_TEIF7}; + static constexpr uint32_t const m_ht_masks[8] = {DMA_LISR_HTIF0, DMA_LISR_HTIF1, DMA_LISR_HTIF2, DMA_LISR_HTIF3, + DMA_HISR_HTIF4, DMA_HISR_HTIF5, DMA_HISR_HTIF6, DMA_HISR_HTIF7}; + static constexpr uint32_t const m_tc_masks[8] = {DMA_LISR_TCIF0, DMA_LISR_TCIF1, DMA_LISR_TCIF2, DMA_LISR_TCIF3, + DMA_HISR_TCIF4, DMA_HISR_TCIF5, DMA_HISR_TCIF6, DMA_HISR_TCIF7}; +}; + +} /* namespace f4ll */ diff --git a/components/f4ll/inc/f4ll/fault.h b/components/f4ll/inc/f4ll/fault.h new file mode 100644 index 0000000..8bb1f2e --- /dev/null +++ b/components/f4ll/inc/f4ll/fault.h @@ -0,0 +1,44 @@ +#pragma once + +#define FAULT_REASON_HARD_FAULT 1 +#define FAULT_REASON_MEMMANAGE_FAULT 2 +#define FAULT_REASON_BUS_FAULT 3 +#define FAULT_REASON_USAGE_FAULT 4 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + uint32_t R0; + uint32_t R1; + uint32_t R2; + uint32_t R3; + uint32_t R4; + uint32_t R5; + uint32_t R6; + uint32_t R7; + uint32_t R8; + uint32_t R9; + uint32_t R10; + uint32_t R11; + uint32_t R12; + uint32_t SP; + uint32_t LR; + uint32_t PC; + uint32_t xPSR; + uint32_t PSP; + uint32_t MSP; + uint32_t EXC_RETURN; + uint32_t CONTROL; +} fault_context_t; + +extern fault_context_t g_fault_context; + +void app_fault_callback(uint32_t reason); +__attribute__((noreturn)) void fault_handler(uint32_t type, fault_context_t *context); + +#ifdef __cplusplus +} +#endif diff --git a/components/f4ll/inc/f4ll/initialized_singleton.h b/components/f4ll/inc/f4ll/initialized_singleton.h new file mode 100644 index 0000000..ff9936d --- /dev/null +++ b/components/f4ll/inc/f4ll/initialized_singleton.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace f4ll { + +template class initialized_singleton +{ +public: + static T &instance() { return *m_instance; } + template static T &init(args_t &&...args) + { + static T instance{std::forward(args)...}; + m_instance = &instance; + return instance; + } + +protected: + initialized_singleton() = default; + initialized_singleton(const initialized_singleton &) = delete; + initialized_singleton &operator=(const initialized_singleton &) = delete; + static T *m_instance; +}; + +template T *initialized_singleton::m_instance = nullptr; + +} // namespace f1ll { diff --git a/components/f4ll/inc/f4ll/irq_lock.h b/components/f4ll/inc/f4ll/irq_lock.h new file mode 100644 index 0000000..b36ef75 --- /dev/null +++ b/components/f4ll/inc/f4ll/irq_lock.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace f4ll { + +class irq_lock +{ +public: + inline irq_lock() + : m_primask(__get_PRIMASK()) + { + __disable_irq(); + } + inline void release() { __set_PRIMASK(m_primask); } + + inline ~irq_lock() { __set_PRIMASK(m_primask); } + +private: + uint32_t m_primask; +}; +} diff --git a/components/f4ll/inc/f4ll/memcpy_dma.h b/components/f4ll/inc/f4ll/memcpy_dma.h new file mode 100644 index 0000000..ab728d1 --- /dev/null +++ b/components/f4ll/inc/f4ll/memcpy_dma.h @@ -0,0 +1,29 @@ +/* + * llmemcpydma.h + * + * Created on: Nov 4, 2019 + * Author: abody + */ +#pragma once + +#include + +#include +#include + +namespace f4ll { + +class memcpy_dma : public initialized_singleton, private dma_helper +{ + friend class initialized_singleton; + +public: + void *copy(void *dst, void const *src, uint16_t length); + void dma_transfer_completed(); + +private: + memcpy_dma(DMA_TypeDef *dma, uint32_t stream); + bool volatile m_busy = false; +}; + +} // namespace f4ll diff --git a/components/f4ll/inc/f4ll/packet_usart.h b/components/f4ll/inc/f4ll/packet_usart.h new file mode 100644 index 0000000..1bb9a96 --- /dev/null +++ b/components/f4ll/inc/f4ll/packet_usart.h @@ -0,0 +1,122 @@ +/* + * ll_HsUsart.h + * + * Created on: Oct 29, 2019 + * Author: abody + */ + +#pragma once + +#include + +#include +#include + +namespace f4ll { + +struct DMAINFO; + +class packet_usart : public crc_handler::icallback, public usart_core +{ + // friend class UsartCore; +public: + packet_usart(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx); + + struct packet_header + { // !!! size should be multiple of 4 !!! + uint8_t start_byte; + uint8_t serial; + uint8_t payload_length; + uint8_t hash; + }; + + struct packet + { + packet_header header; + uint8_t payload[256 + sizeof(uint32_t)]; // extra room for crc32 + } __attribute__((aligned)); + + struct stats + { + uint32_t overrun = 0; + uint32_t hdr_error = 0; + uint32_t payload_errror = 0; + uint32_t pep1 = 0; + uint32_t pep2 = 0; + uint32_t rx_dma_error = 0; + uint32_t tx_dma_error = 0; + uint32_t rcvd = 0; + uint32_t premature_hdr = 0; + uint32_t premature_payload = 0; + uint32_t sent = 0; + uint32_t skiped = 0; + }; + + struct ihs_usart_callback + { + virtual bool packet_received(packet_usart *caller, uintptr_t user_param, packet const &packet) = 0; + }; + + // crc_handler::ICallback interface functions + virtual void crc_succeeded(uintptr_t callback_param, uint32_t crc, uint8_t task) override; + virtual void crc_failed(uintptr_t callback_param, uint32_t crc, uint8_t task) override; + + void post_packet(uint8_t const *payload, uint8_t length, bool wait_for_crc_queue = true); + void setup_receive(void); + + void rx_processed(bool second); + + // Getters + uint8_t *get_tx_packet_buffer(void) { return m_tx_buffer.pkt.payload; } + uint8_t const *get_rx_packet_buffer(bool second) { return m_rx_buffers[second].pkt.payload; } + USART_TypeDef *get_usart(void) const { return m_usart; } + stats const &get_stats(void) const { return m_stats; } + inline bool is_tx_busy(void) const { return m_tx_buffer.busy; } + inline bool is_tx_failed(void) const { return m_tx_buffer.error; } + inline bool is_rx_busy(bool second) const { return m_rx_buffers[second].busy; } + inline bool is_rx_failed(bool second) const { return m_rx_buffers[second].error; } + + void set_callback(ihs_usart_callback *callback, uintptr_t callback_param); + +private: + void build_header(packet &packet, uint8_t serial_nr, uint8_t length); + bool check_header(packet_header &header); + void switch_rx_buffers(void); + + // UsartCore pure virtual function implementations + virtual void receiver_idle(void) override; + virtual void transmission_complete(void) override; + virtual void framing_error(void) override; + virtual void overrun(void) override; + virtual void rx_dma_transfer_complete(void) override; + virtual void rx_dma_half_transfer(void) override; + virtual void rx_dma_error(dma_helper::dma_error_type reason) override; + virtual void tx_dma_transfer_complete(void) override; + virtual void tx_dma_half_transfer(void) override; + virtual void tx_dma_error(dma_helper::dma_error_type reason) override; + + struct Buffer + { + packet pkt; + // transfer area ends here + bool volatile busy = 0; + bool volatile error = 0; + uint16_t requested_length = 0; + uint32_t error_info = 0; + }; + + static const uint8_t STARTMARKER = 0x95; + + uint8_t m_rx_serial_nr = -1; + uint8_t m_tx_serial_nr = -1; + stats m_stats; + bool m_rx_buffer_selector = false; + + crc_handler::slot<2> m_crc_slot; + ihs_usart_callback *m_user_callback = nullptr; + uintptr_t m_user_callback_param = 0; + Buffer m_tx_buffer; + Buffer m_rx_buffers[2]; +}; + +} diff --git a/components/f4ll/inc/f4ll/ringbuffer.h b/components/f4ll/inc/f4ll/ringbuffer.h new file mode 100644 index 0000000..c2dc08d --- /dev/null +++ b/components/f4ll/inc/f4ll/ringbuffer.h @@ -0,0 +1,133 @@ +/* + * ringbuffer.h + * + * Created on: May 31, 2025 + * Author: Attila Body + */ + +#pragma once + +#include +#include + +namespace f4ll { + +class iringbuffer +{ +public: + using size_type = size_t; + + /// @brief Copies data to the ring buffer (without committing it) + /// @param data Pointer to the data to copy + /// @param len Length of the data to copy + /// @retval Length of the data copied (differs when not all data can fit) + virtual size_type put(uint8_t const *data, size_type len) = 0; + + /// @brief Commits the data already placed into the buffer and notifies the + /// consumer about it's availability + virtual void commit() = 0; + + /// @brief Waits until all the data from the ring buffer gets consumed. + // void flush(); + + /// @brief Gets a pointer to the next chunk of committed data in the buffer + /// without registering the consumption. + /// The caller should also call report_consumption using the returned + /// chunk length after it finished processing the data. + /// @param[out] data Receives a pointer to the first byte of the available + /// data in the buffer + /// @param[out] len Receives the length of the chunk available in the buffer. + /// Will not exceed len_requested. + /// @retval true if the buffer has more available data, false otherwise. + virtual bool get_chunk(uint8_t const *&data, size_type &len) const = 0; + + /// @brief Marks the chunk returned by ringbuffer_GetChunk as available. + /// @param consumed The length of the chunk as returned by + /// ringbuffer_GetChunk(..., len) + virtual void consumed(size_type len) = 0; + + /// @brief Gets a pointer to the next free chunk in the buffer + /// @retval Pointer to the beginning of the next free buffer chmemory area + /// @param[out] len Receives the length of the returned buffer area. + virtual uint8_t *get_free_chunk(size_type &len) = 0; + + /// @brief Registers the data written in the free buffer chunk. + /// IMPORTANT: Do not call put() after start modifying the buffer + /// returned by get_free_chunk() before registering the written data by + /// calling supplied() + /// @param len The length of the data written in the buffer + virtual bool produced(size_type len) = 0; + + /// @brief Returns the number of uncommited bytes in the ring buffer. + virtual size_type uncommited() const = 0; + + /// @brief Returns the number of commited bytes in the ring buffer. + virtual size_type commited() const = 0; + + /// @brief Discards the uncommited data in the ring buffer. + virtual void discard() = 0; + + /// @brief Returns the size of the internal buffer. + /// One byte in the buffer is always reserved as separator + virtual size_type size() const = 0; + + /// @brief Returns the size of the used place in the buffer + // (including non-commited data) + virtual size_type used() const = 0; + + /// @brief Returns the free storage capacity of the buffer + virtual size_type unused() const = 0; +}; + +// +class ringbuffer_ext : public iringbuffer +{ +public: + ringbuffer_ext(uint8_t *bptr, size_type bsize); + + size_type put(uint8_t const *data, size_type len) override; + void commit() override; + + bool get_chunk(uint8_t const *&data, size_type &len) const override; + void consumed(size_type len) override; + + uint8_t *get_free_chunk(size_type &len) override; + bool produced(size_type len) override; + + size_type uncommited() const override; + size_type commited() const override; + void discard() override; + size_type size() const override; + size_type used() const override; + size_type unused() const override; + +private: + uint8_t *m_buffer; //!< Data bufer + size_type const m_bsize; //!< Data buffer size + size_type m_head = 0; //!< Write position + size_type m_head_shadow = 0; //!< Shadowed write position for collecting data + //!< before committing it + size_type m_tail = 0; //!< Read position + + size_type marker_diff(size_type m1, size_type m2) const; + size_type max_chunk_len() const; +}; + +// +template class ringbuffer : public ringbuffer_ext +{ +public: + ringbuffer(); + +private: + uint8_t m_buffer[SZ]; +}; + +// +template +ringbuffer::ringbuffer() + : ringbuffer_ext(m_buffer, SZ) +{ +} + +} // namespace f1ll diff --git a/components/f4ll/inc/f4ll/str_util.h b/components/f4ll/inc/f4ll/str_util.h new file mode 100644 index 0000000..01bb412 --- /dev/null +++ b/components/f4ll/inc/f4ll/str_util.h @@ -0,0 +1,28 @@ +/* + * strutil.h + * + * Created on: Feb 11, 2017 + * Author: compi + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +size_t strcpy_ex(char *dst, char const *src); +size_t uitodec(char* buffer, uint32_t data); +size_t uitohex(char* buffer, uint32_t data, uint8_t chars); +size_t itodec(char* buffer, int data); +size_t itohex(char* buffer, int data); +void strrev(char *first, char *last); +char tochr(const uint8_t in, const uint8_t upper); + +#ifdef __cplusplus +} +#endif diff --git a/components/f4ll/inc/f4ll/usart_core.h b/components/f4ll/inc/f4ll/usart_core.h new file mode 100644 index 0000000..fa17f2d --- /dev/null +++ b/components/f4ll/inc/f4ll/usart_core.h @@ -0,0 +1,54 @@ +/* + * ll_dmadrivenusartcore.h + * + * Created on: Nov 4, 2019 + * Author: abody + */ + +#pragma once + +#include + +#include + +namespace f4ll { + +class usart_core +{ +public: + static inline void usart_isr(usart_core *_this) { _this->usart_isr(); } + static inline void rx_dma_isr(usart_core *_this) { _this->rx_dma_isr(); } + static inline void tx_dma_isr(usart_core *_this) { _this->tx_dma_isr(); } + + void setup_transmit(void const *buffer, uint16_t length); + void setup_receive(void *buffer, uint16_t length); + +protected: + usart_core(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx); + + USART_TypeDef *m_usart; + dma_helper m_rx_dma; + dma_helper m_tx_dma; + +private: + // these functions are called from interrup context! + virtual void receiver_idle(void) = 0; + virtual void transmission_complete(void) = 0; + virtual void framing_error(void) = 0; + virtual void overrun(void) = 0; + + virtual void rx_dma_transfer_complete(void) = 0; + virtual void rx_dma_half_transfer(void) = 0; + virtual void rx_dma_error(dma_helper::dma_error_type reason) = 0; + + virtual void tx_dma_transfer_complete(void) = 0; + virtual void tx_dma_half_transfer(void) = 0; + virtual void tx_dma_error(dma_helper::dma_error_type reason) = 0; + +public: + void usart_isr(); + void rx_dma_isr(); + void tx_dma_isr(); +}; + +} /* namespace f4ll */ diff --git a/components/f4ll/src/console_handler.cpp b/components/f4ll/src/console_handler.cpp new file mode 100644 index 0000000..e1955f1 --- /dev/null +++ b/components/f4ll/src/console_handler.cpp @@ -0,0 +1,133 @@ +/* + * ll_consolehandler.cpp + * + * Created on: Nov 7, 2019 + * Author: abody + */ + +#include +#include + +#include + +namespace f4ll { + +console_handler::console_handler( + USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx, uint8_t *rx_buffer, size_type rx_buffer_size, + uint8_t *tx_buffer, size_type tx_buffer_size, iconsole_input *rx_callback) + : usart_core(usart, dma, stream_rx, stream_tx), + m_tx_buffer(tx_buffer, tx_buffer_size), + m_rx_buffer(rx_buffer, rx_buffer_size), + m_rx_callback(rx_callback) +{ + LL_USART_EnableIT_IDLE(usart); + LL_USART_EnableIT_ERROR(usart); + setup_receive(); +} + +void console_handler::setup_receive(void) +{ + uint8_t *bptr = m_rx_buffer.get_free_chunk(m_bytes_requested); + m_reqd_bytes_registered = 0; + usart_core::setup_receive(bptr, m_bytes_requested); +} + +void console_handler::receiver_idle(void) +{ + uint16_t rcvd_bytes = m_bytes_requested - m_reqd_bytes_registered - LL_DMA_GetDataLength(m_rx_dma.get_dma(), m_rx_dma.get_stream()); + if (rcvd_bytes) { + m_reqd_bytes_registered += rcvd_bytes; + m_rx_buffer.produced(rcvd_bytes); + m_rx_buffer.commit(); + if (m_rx_callback) { + m_rx_callback->input_available(rcvd_bytes); + } + } +} + +void console_handler::framing_error(void) {} + +void console_handler::overrun(void) {} + +void console_handler::rx_dma_transfer_complete(void) +{ + LL_DMA_DisableStream(m_rx_dma.get_dma(), m_rx_dma.get_stream()); + uint16_t rcvd_bytes = m_bytes_requested - m_reqd_bytes_registered - LL_DMA_GetDataLength(m_rx_dma.get_dma(), m_rx_dma.get_stream()); + if (rcvd_bytes) { + m_rx_buffer.produced(rcvd_bytes); + setup_receive(); + m_rx_buffer.commit(); + if (m_rx_callback) { + m_rx_callback->input_available(rcvd_bytes); + } + } +} + +void console_handler::rx_dma_half_transfer(void) {} + +void console_handler::rx_dma_error(dma_helper::dma_error_type reason) +{ + (void)reason; +} + +void console_handler::tx_dma_transfer_complete(void) +{ + LL_USART_EnableIT_TC(m_usart); + LL_DMA_DisableStream(m_tx_dma.get_dma(), m_tx_dma.get_stream()); +} + +void console_handler::transmission_complete(void) +{ + m_tx_buffer.consumed(m_bytes_sent); + if (m_tx_buffer.commited()) { + uint8_t const *chunk; + m_tx_buffer.get_chunk(chunk, m_bytes_sent); + if (m_bytes_sent) { + setup_transmit(chunk, m_bytes_sent); + } + } else { + m_bytes_sent = 0; + } +} + +void console_handler::tx_dma_half_transfer(void) {} + +void console_handler::tx_dma_error(dma_helper::dma_error_type reason) +{ + (void)reason; +} + +console_handler::size_type console_handler::append(char const *s) +{ + size_t len = strlen(s); + if (!len) { + return 0; + } + return len - m_tx_buffer.put(reinterpret_cast(s), len); +} + +void console_handler::flush() +{ + if (!m_tx_buffer.uncommited()) { + return; + } + m_tx_buffer.commit(); + + if (m_bytes_sent) { + return; + } + + uint8_t const *chunk; + m_tx_buffer.get_chunk(chunk, m_bytes_sent); + if (m_bytes_sent) { + setup_transmit(chunk, m_bytes_sent); + } +} + +void console_handler::print(char const *s) +{ + append(s); + flush(); +} + +} /* namespace f4ll */ diff --git a/components/f4ll/src/crc_handler.cpp b/components/f4ll/src/crc_handler.cpp new file mode 100644 index 0000000..7144fb3 --- /dev/null +++ b/components/f4ll/src/crc_handler.cpp @@ -0,0 +1,161 @@ +/* + * ll_crc_handler.cpp + * + * Created on: Oct 26, 2019 + * Author: compi + */ + +#include + +namespace f4ll { + +crc_handler::crc_handler(DMA_TypeDef *dma, uint32_t stream) + : m_dma(dma, stream) +{ + LL_DMA_EnableIT_TC(dma, stream); + LL_DMA_EnableIT_TE(dma, stream); + LL_DMA_SetM2MDstAddress(dma, stream, (uint32_t)&CRC->DR); +} + +void crc_handler::attach_slot(slot_base &slot) +{ + for (unsigned int i = 0; i < slot.m_task_count; ++i) { + auto &task(slot[i]); + task.m_address = nullptr; + task.m_word_count = 0; + task.m_callback = nullptr; + task.m_callback_param = 0; + } + uint32_t prim = __get_PRIMASK(); + __disable_irq(); + slot.m_next = m_first_slot; + m_first_slot = &slot; + __set_PRIMASK(prim); +} + +bool crc_handler::enqueue(slot_base &slot, uint8_t task, void const *address, uint16_t len, icallback *cb, uintptr_t cb_param) +{ + uint32_t prim = __get_PRIMASK(); + bool immediate; + + // TODO: do we need sanity check here? (is slot attached, is task in range, + // etc...?) + + while (is_active(slot, task)) + ; + __disable_irq(); + immediate = m_active_slot == nullptr; + slot[task].m_address = (!immediate) ? address : nullptr; + slot[task].m_word_count = (len + 3) / 4; + slot[task].m_callback = cb; + slot[task].m_callback_param = cb_param; + if (immediate) { + m_active_slot = &slot; + m_active_task = task; + } + __set_PRIMASK(prim); + + if (immediate) { + CRC->CR = 1; + LL_DMA_SetM2MSrcAddress(m_dma.get_dma(), m_dma.get_stream(), (uint32_t)address); + LL_DMA_SetDataLength(m_dma.get_dma(), m_dma.get_stream(), (len + 3) / 4); + LL_DMA_EnableStream(m_dma.get_dma(), m_dma.get_stream()); + } + return immediate; +} + +bool crc_handler::is_active(slot_base &slot, uint8_t task) const +{ + return task < slot.m_task_count && slot[task].m_word_count != 0; +} + +bool crc_handler::is_queued(slot_base &slot, uint8_t task) const +{ + return task < slot.m_task_count && slot[task].m_address != nullptr; +} + +bool crc_handler::is_running(slot_base &slot, uint8_t task) const +{ + return task < slot.m_task_count && slot[task].m_word_count && !slot[task].m_address; +} + +void crc_handler::dma_transfer_completed(void) +{ + if (*m_dma.get_is_reg() & m_dma.get_tc_mask()) { // DMA transfer complete + *m_dma.get_ifc_reg() = m_dma.get_tc_mask(); + LL_DMA_DisableStream(m_dma.get_dma(), m_dma.get_stream()); + if (m_active_slot) { + if ((*m_active_slot)[m_active_task].m_callback) { + (*m_active_slot)[m_active_task].m_callback->crc_succeeded( + (*m_active_slot)[m_active_task].m_callback_param, CRC->DR, m_active_task); + } else if ((*m_active_slot)[m_active_task].m_callback_param) { + *reinterpret_cast((*m_active_slot)[m_active_task].m_callback_param) = CRC->DR; + } + } + } else if (*m_dma.get_is_reg() & m_dma.get_te_mask()) { // DMA transfer error + *m_dma.get_ifc_reg() = m_dma.get_te_mask(); + LL_DMA_DisableStream(m_dma.get_dma(), m_dma.get_stream()); + if (m_active_slot) { + if ((*m_active_slot)[m_active_task].m_callback) { + (*m_active_slot)[m_active_task].m_callback->crc_failed( + (*m_active_slot)[m_active_task].m_callback_param, CRC->DR, m_active_task); + } else if ((*m_active_slot)[m_active_task].m_callback_param) { + *reinterpret_cast((*m_active_slot)[m_active_task].m_callback_param) = -1; + } + } + } + (*m_active_slot)[m_active_task].m_callback = nullptr; + (*m_active_slot)[m_active_task].m_callback_param = 0; + (*m_active_slot)[m_active_task].m_word_count = 0; + start_next_task(); +} + +void crc_handler::start_next_task(void) +{ + bool more_tasks; + uint8_t index = 0; + + do { + slot_base volatile *slot = m_first_slot; + more_tasks = false; + while (slot) { + if (index < slot->m_task_count) { + if ((*slot)[index].m_address) { + m_active_slot = slot; + m_active_task = index; + CRC->CR = 1; + LL_DMA_SetM2MSrcAddress(m_dma.get_dma(), m_dma.get_stream(), reinterpret_cast((*slot)[index].m_address)); + LL_DMA_SetDataLength(m_dma.get_dma(), m_dma.get_stream(), (*slot)[index].m_word_count); + LL_DMA_EnableStream(m_dma.get_dma(), m_dma.get_stream()); + (*slot)[index].m_address = nullptr; // marking as started + return; + } + if (index + 1 < slot->m_task_count) { + more_tasks = true; + } + } + slot = slot->m_next; + } + ++index; + } while (more_tasks); + m_active_slot = nullptr; +} + +void crc_handler::wait_results(slot_base &slot, uint8_t task) const +{ + while (is_queued(slot, task)) + ; + while (is_active(slot, task)) + ; +} + +uint32_t crc_handler::compute(slot_base &slot, uint8_t task, void const *address, uint16_t len) +{ + uint32_t result; + enqueue(slot, task, address, len, nullptr, reinterpret_cast(&result)); + while (is_active(slot, task)) + ; + return result; +} + +} // namespace f4ll diff --git a/components/f4ll/src/dma_helper.cpp b/components/f4ll/src/dma_helper.cpp new file mode 100644 index 0000000..42e2e11 --- /dev/null +++ b/components/f4ll/src/dma_helper.cpp @@ -0,0 +1,24 @@ +/* +q * ll_dmahelper.cpp + * + * Created on: Oct 25, 2019 + * Author: abody + */ + +#include + +namespace f4ll { + +dma_helper::dma_helper(DMA_TypeDef *dma, uint32_t stream) + : m_dma(dma), + m_stream(stream), + m_is_reg( + (dma == DMA1) ? ((m_stream < LL_DMA_STREAM_4) ? &DMA1->LISR : &DMA1->HISR) + : ((m_stream < LL_DMA_STREAM_4) ? &DMA2->LISR : &DMA2->HISR)), + m_ifc_reg( + (dma == DMA1) ? ((m_stream < LL_DMA_STREAM_4) ? &DMA1->LIFCR : &DMA1->HIFCR) + : ((m_stream < LL_DMA_STREAM_4) ? &DMA2->LIFCR : &DMA2->HIFCR)) +{ +} + +} /* namespace f4ll */ diff --git a/components/f4ll/src/fault.cpp b/components/f4ll/src/fault.cpp new file mode 100644 index 0000000..d89b280 --- /dev/null +++ b/components/f4ll/src/fault.cpp @@ -0,0 +1,142 @@ +/* + * fault.c + * + * Created on: Oct 1, 2019 + * Author: abody + * -c "tpiu config internal uart off " + */ +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +fault_context_t g_fault_context; + +void __attribute__((weak)) app_fault_callback(uint32_t reason) +{ + (void)reason; +} + +void swo_send_str(char const *str, uint8_t len, uint8_t port) +{ + while (len) { + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && // ITM enabled + ((ITM->TER & (1UL << port)) != 0UL)) // ITM Port enabled + { + // Wait until shift register is free + while (ITM->PORT[port].u32 == 0UL) { + __ASM volatile("nop"); + } + if (len >= 4) { + ITM->PORT[port].u32 = *(uint32_t *)(str); + str += 4; + len -= 4; + } else if (len >= 2) { + ITM->PORT[port].u16 = *(uint16_t *)(str); + str += 2; + len -= 2; + } else { + ITM->PORT[port].u8 = *(uint8_t *)(str); + ++str; + --len; + } + } else { + break; + } + } +} + +void fault_print_str(char const *fmtstr, uint32_t *values) +{ + char hex_str[9] = {0}; + char const *next_chunk = fmtstr; + + while (*fmtstr) { + if (*fmtstr == '%') { + swo_send_str(next_chunk, fmtstr - next_chunk, 0); + uitohex(hex_str, *values++, 8); + swo_send_str(hex_str, 8, 0); + next_chunk = fmtstr + 1; + } + ++fmtstr; + } + if (next_chunk != fmtstr) { + swo_send_str(next_chunk, fmtstr - next_chunk, 0); + } +} + +void fault_handler(uint32_t type, fault_context_t *context) +{ + uint32_t FSR[9] = { + SCB->HFSR, 0xff & SCB->CFSR, (0xff00 & SCB->CFSR) >> 8, (0xffff0000 & SCB->CFSR) >> 16, SCB->DFSR, SCB->AFSR, SCB->SHCSR, + SCB->MMFAR, SCB->BFAR}; + + while (1) { + fault_print_str("\n++ Fault Handler ++\n\nFaultType: ", NULL); + switch (type) { + case FAULT_REASON_HARD_FAULT: + fault_print_str("HardFault", NULL); + break; + case FAULT_REASON_MEMMANAGE_FAULT: + fault_print_str("MemManageFault", NULL); + break; + case FAULT_REASON_BUS_FAULT: + fault_print_str("BusFault", NULL); + break; + case FAULT_REASON_USAGE_FAULT: + fault_print_str("UsageFault", NULL); + break; + default: + fault_print_str("Unknown Fault", NULL); + break; + } + + fault_print_str("\n\nContext:", NULL); + + fault_print_str( + "\nR0 : %" + "\nR1 : %" + "\nR2 : %" + "\nR3 : %" + "\nR4 : %" + "\nR5 : %" + "\nR6 : %" + "\nR7 : %" + "\nR8 : %" + "\nR9 : %" + "\nR10 : %" + "\nR11 : %" + "\nR12 : %" + "\nSP : %" + "\nLR : %" + "\nPC : %" + "\nxPSR : %" + "\nPSP : %" + "\nMSP : %", + (uint32_t *)context); + + // Capture CPUID to get core/cpu info + fault_print_str("\nCPUID: %", (uint32_t *)&SCB->CPUID); + + fault_print_str( + "\nHFSR : %" + "\nMMFSR: %" + "\nBFSR : %" + "\nUFSR : %" + "\nDFSR : %" + "\nAFSR : %" + "\nSHCSR: %", + FSR); + + app_fault_callback(type); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/components/f4ll/src/memcpy_dma.cpp b/components/f4ll/src/memcpy_dma.cpp new file mode 100644 index 0000000..dedca9b --- /dev/null +++ b/components/f4ll/src/memcpy_dma.cpp @@ -0,0 +1,38 @@ +/* + * llmemcpydma.cpp + * + * Created on: Nov 4, 2019 + * Author: abody + */ + +#include + +namespace f4ll { + +memcpy_dma::memcpy_dma(DMA_TypeDef *dma, uint32_t stream) + : dma_helper(dma, stream) +{ + LL_DMA_EnableIT_TC(dma, stream); +} + +void *memcpy_dma::copy(void *dst, void const *src, uint16_t length) +{ + LL_DMA_SetM2MSrcAddress(get_dma(), get_stream(), (uint32_t)src); + LL_DMA_SetM2MDstAddress(get_dma(), get_stream(), (uint32_t)dst); + LL_DMA_SetDataLength(get_dma(), get_stream(), (length + 3) / 4); + m_busy = 1; + LL_DMA_EnableStream(get_dma(), get_stream()); + while (m_busy) + ; + return dst; +} + +void memcpy_dma::dma_transfer_completed() +{ + if (*get_is_reg() & get_tc_mask()) { // DMA transfer complete + *get_ifc_reg() = get_tc_mask(); + LL_DMA_DisableStream(get_dma(), get_stream()); + m_busy = 0; + } +} +} /* namespace f4ll */ diff --git a/components/f4ll/src/packet_usart.cpp b/components/f4ll/src/packet_usart.cpp new file mode 100644 index 0000000..046d5ea --- /dev/null +++ b/components/f4ll/src/packet_usart.cpp @@ -0,0 +1,213 @@ +/* + * ll_hsusart_impl.h + * + * Created on: Oct 29, 2019 + * Author: abody + */ +#include + +#include + +namespace f4ll { + +template static inline T round_up_to_4(T input) +{ + return (input + 3) & (((T)-1) - 3); +} + +packet_usart::packet_usart(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx) + : usart_core(usart, dma, stream_rx, stream_tx) +{ + crc_handler::instance().attach_slot(m_crc_slot); + LL_USART_EnableIT_IDLE(usart); + LL_USART_EnableIT_ERROR(usart); +} + +void packet_usart::rx_processed(bool second) +{ + m_rx_buffers[second].busy = false; + m_rx_buffers[second].error = false; +} + +void packet_usart::set_callback(ihs_usart_callback *callback, uintptr_t callback_param) +{ + m_user_callback = callback; + m_user_callback_param = callback_param; +} + +void packet_usart::post_packet(uint8_t const *payload, uint8_t length, bool wait_for_crc_queue) +{ + uint16_t payload_length = round_up_to_4((uint16_t)length); + + build_header(m_tx_buffer.pkt, m_tx_serial_nr++, length); + if (payload) { + memcpy(m_tx_buffer.pkt.payload, payload, length); + } + m_tx_buffer.requested_length = sizeof(m_tx_buffer.pkt.header) + payload_length + sizeof(uint32_t); + m_tx_buffer.busy = true; + m_tx_buffer.error = false; + + crc_handler::instance().enqueue( + m_crc_slot, 0, &m_tx_buffer.pkt, sizeof(packet_header) + payload_length, nullptr, + reinterpret_cast(m_tx_buffer.pkt.payload + payload_length)); + + while (wait_for_crc_queue && crc_handler::instance().is_queued(m_crc_slot, 0)) + ; + + setup_transmit(&m_tx_buffer.pkt, m_tx_buffer.requested_length); + + ++m_stats.sent; +} + +void packet_usart::setup_receive() +{ + m_rx_buffers[m_rx_buffer_selector].requested_length = sizeof(m_rx_buffers[m_rx_buffer_selector].pkt); + usart_core::setup_receive(&m_rx_buffers[m_rx_buffer_selector], sizeof(m_rx_buffers[m_rx_buffer_selector].pkt)); +} + +////////////////////////////////////// +// UsartCore pure virtual functions // +////////////////////////////////////// + +void packet_usart::receiver_idle(void) +{ + uint16_t rcvdLen = + m_rx_buffers[m_rx_buffer_selector].requested_length - LL_DMA_GetDataLength(m_rx_dma.get_dma(), m_rx_dma.get_stream()); + + if (rcvdLen >= sizeof(packet_header)) { + if (check_header(m_rx_buffers[m_rx_buffer_selector].pkt.header)) { + if (rcvdLen >= sizeof(packet_header) + round_up_to_4((uint16_t)m_rx_buffers[m_rx_buffer_selector].pkt.header.payload_length) + + sizeof(uint32_t)) { + LL_DMA_DisableStream(m_rx_dma.get_dma(), m_rx_dma.get_stream()); + } else { + ++m_stats.premature_payload; + } + } else { + m_rx_buffers[m_rx_buffer_selector].error = 1; + LL_DMA_DisableStream(m_rx_dma.get_dma(), m_rx_dma.get_stream()); + } + } else { + ++m_stats.premature_hdr; + } +} + +void packet_usart::transmission_complete(void) +{ + LL_USART_DisableDirectionTx(m_usart); // enforcing an idle frame + LL_USART_EnableDirectionTx(m_usart); + m_tx_buffer.busy = 0; +} + +void packet_usart::framing_error(void) {} + +void packet_usart::overrun(void) {} + +void packet_usart::rx_dma_transfer_complete(void) +{ + if (check_header(m_rx_buffers[m_rx_buffer_selector].pkt.header)) { + crc_handler::instance().enqueue( + m_crc_slot, 1, &m_rx_buffers[m_rx_buffer_selector].pkt, + sizeof(packet_header) + round_up_to_4((uint16_t)m_rx_buffers[m_rx_buffer_selector].pkt.header.payload_length), this, + m_rx_buffer_selector); + } else { + ++m_stats.hdr_error; + m_rx_buffers[m_rx_buffer_selector].error = true; + } + switch_rx_buffers(); +} + +void packet_usart::rx_dma_half_transfer(void) {} + +void packet_usart::rx_dma_error(dma_helper::dma_error_type reason) +{ + (void)reason; + + m_rx_buffers[m_rx_buffer_selector].error = 1; + ++m_stats.rx_dma_error; + switch_rx_buffers(); +} + +void packet_usart::tx_dma_transfer_complete(void) +{ + LL_USART_EnableIT_TC(m_usart); + LL_DMA_DisableStream(m_tx_dma.get_dma(), m_tx_dma.get_stream()); +} + +void packet_usart::tx_dma_half_transfer(void) {} + +void packet_usart::tx_dma_error(dma_helper::dma_error_type reason) +{ + (void)reason; + + m_tx_buffer.error = 1; + ++m_stats.tx_dma_error; +} + +/////////////////////// +// Private functions // +/////////////////////// + +void packet_usart::build_header(packet &packet, uint8_t serial_nr, uint8_t length) +{ + uint8_t hash = STARTMARKER; + + packet.header.start_byte = STARTMARKER; + packet.header.serial = serial_nr; + hash ^= serial_nr; + packet.header.payload_length = length; + hash ^= length; + packet.header.hash = hash; +} + +bool packet_usart::check_header(packet_header &header) +{ + return header.start_byte == STARTMARKER && (header.start_byte ^ header.serial ^ header.payload_length) == header.hash; +} + +void packet_usart::switch_rx_buffers(void) +{ + ++m_stats.rcvd; + m_rx_buffer_selector = !m_rx_buffer_selector; + + if (m_rx_buffers[m_rx_buffer_selector].busy) { + ++m_stats.overrun; + } + setup_receive(); +} + +/////////////////////////// +// crc_handler::ICallback // +/////////////////////////// + +void packet_usart::crc_succeeded(uintptr_t callback_param, uint32_t crc, uint8_t task) +{ + (void)task; + + Buffer &buf(m_rx_buffers[static_cast(callback_param)]); + + buf.busy = 1; + if (*(uint32_t *)(buf.pkt.payload + round_up_to_4((uint16_t)buf.pkt.header.payload_length)) != crc) { + buf.error = 1; + buf.error_info = crc; + ++m_stats.payload_errror; + } + if (m_user_callback) { + buf.busy = !m_user_callback->packet_received(this, m_user_callback_param, buf.pkt); + } +} + +void packet_usart::crc_failed(uintptr_t callback_param, uint32_t crc, uint8_t task) +{ + (void)crc; + (void)task; + + Buffer &buf(m_rx_buffers[static_cast(callback_param)]); + buf.busy = buf.error = true; + buf.error_info = 0; + ++m_stats.payload_errror; + if (m_user_callback) { + buf.busy = !m_user_callback->packet_received(this, m_user_callback_param, buf.pkt); + } +} + +} // namespace f4ll diff --git a/components/f4ll/src/ringbuffer.cpp b/components/f4ll/src/ringbuffer.cpp new file mode 100644 index 0000000..ae81ac2 --- /dev/null +++ b/components/f4ll/src/ringbuffer.cpp @@ -0,0 +1,152 @@ +#include + +#include + +namespace f4ll { + +ringbuffer_ext::ringbuffer_ext(uint8_t *bptr, size_type bsize) + : m_buffer(bptr), + m_bsize(bsize) +{ +} + +iringbuffer::size_type ringbuffer_ext::put(uint8_t const *data, size_type len) +{ + size_type chunk1 = 0; + size_type chunk2 = 0; + + if (!data || !len) { + return 0; + } + + size_type max_len = unused(); + len = len < max_len ? len : max_len; + + chunk1 = m_bsize - m_head_shadow; + if (chunk1 >= len) { + chunk1 = len; + } else { + chunk2 = len - chunk1; + } + + std::memcpy(m_buffer + m_head_shadow, data, chunk1); + m_head_shadow += chunk1; + if (m_head_shadow == m_bsize) { + m_head_shadow = 0; + } + + if (chunk2) { + std::memcpy(m_buffer, data + chunk1, chunk2); + m_head_shadow += chunk2; + if (m_head_shadow == m_bsize) { + m_head_shadow = 0; + } + } + + return len; +} + +void ringbuffer_ext::commit() +{ + m_head = m_head_shadow; +} + +bool ringbuffer_ext::get_chunk(uint8_t const *&data, size_type &len) const +{ + size_type head = m_head; + size_type tail = m_tail; + size_type chunk_size = head >= tail ? head - tail : m_bsize - tail; + + if (!chunk_size) { + len = 0; + return tail != head; + } + + data = m_buffer + tail; + len = chunk_size; + + tail += chunk_size; + if (tail == m_bsize) { + tail = 0; + } + + return tail != head; +} + +void ringbuffer_ext::consumed(size_type len) +{ + if (!len) { + return; + } + m_tail += len; + if (m_tail == m_bsize) { + m_tail = 0; + } +} + +iringbuffer::size_type ringbuffer_ext::max_chunk_len() const +{ + if (m_tail <= m_head_shadow) { + return m_bsize - m_head_shadow - (m_tail ? 0 : 1); + } else { + return m_tail - m_head_shadow - 1; + } +} + +uint8_t *ringbuffer_ext::get_free_chunk(size_type &len) +{ + len = max_chunk_len(); + + return m_buffer + m_head_shadow; +} + +bool ringbuffer_ext::produced(size_type len) +{ + size_type max_len = max_chunk_len(); + if (len > max_len) { + return false; + } + m_head_shadow += len; + if (m_head_shadow == m_bsize) { + m_head_shadow = 0; + } + return true; +} + +iringbuffer::size_type ringbuffer_ext::uncommited() const +{ + return marker_diff(m_head_shadow, m_head); +} + +iringbuffer::size_type ringbuffer_ext::commited() const +{ + return marker_diff(m_head, m_tail); +} + +void ringbuffer_ext::discard() +{ + m_head_shadow = m_head; +} + +iringbuffer::size_type ringbuffer_ext::size() const +{ + return m_bsize; +} + +iringbuffer::size_type ringbuffer_ext::used() const +{ + return marker_diff(m_head_shadow, m_tail); +} + +iringbuffer::size_type ringbuffer_ext::unused() const +{ + return m_bsize - used() - 1; +} + +iringbuffer::size_type ringbuffer_ext::marker_diff(size_type m1, size_type m2) const +{ + return (m1 >= m2) ? (m1 - m2) : (m_bsize - m2 + m1); +} + +// +} diff --git a/components/f4ll/src/str_util.cpp b/components/f4ll/src/str_util.cpp new file mode 100644 index 0000000..9b6ef95 --- /dev/null +++ b/components/f4ll/src/str_util.cpp @@ -0,0 +1,104 @@ +#include + +////////////////////////////////////////////////////////////////////////////// +size_t strcpy_ex(char *dst, char const *src) +{ + size_t ret = 0; + do { + *dst++ = *src; + ++ret; + } while (*src++); + return ret - 1; +} + +////////////////////////////////////////////////////////////////////////////// +void strrev(char *first, char *last) +{ + char tmp; + while (last > first) { + tmp = *first; + *first++ = *last; + *last-- = tmp; + } +} + +////////////////////////////////////////////////////////////////////////////// +char tochr(const uint8_t in, const uint8_t upper) +{ + return in + ((in < 10) ? '0' : (upper ? 'A' : 'a') - 10); +} + +////////////////////////////////////////////////////////////////////////////// +size_t uitodec(char *buffer, uint32_t data) +{ + char *b2 = buffer; + if (!data) { + *b2++ = '0'; + *b2 = '\0'; + return 1; + } + + while (data) { + *b2++ = (data % 10) + '0'; + data /= 10; + } + size_t ret = b2 - buffer; + + *b2-- = 0; + + strrev(buffer, b2); + return ret; +} + +////////////////////////////////////////////////////////////////////////////// +size_t uitohex(char *buffer, uint32_t data, uint8_t chars) +{ + char *b2 = buffer; + size_t ret = 0; + + if (chars == 0xff || !chars) { + if (!data) { + *b2++ = '0'; + *b2 = '\0'; + return 1; + } + + while (data) { + uint8_t curval = data & 0x0f; + *b2++ = tochr(curval, 1); + data >>= 4; + } + ret = b2 - buffer; + + } else { + ret = chars; + for (uint8_t pos = 0; pos < (uint8_t)ret; ++pos) { + *b2++ = tochr(data & 0x0f, 1); + data >>= 4; + } + } + *b2-- = 0; + strrev(buffer, b2); + return ret; +} + +////////////////////////////////////////////////////////////////////////////// +size_t itodec(char *buffer, int data) +{ + if (data < 0) { + *buffer++ = '-'; + return uitodec(buffer, -data) + 1; + } + + return uitodec(buffer, data); +} + +////////////////////////////////////////////////////////////////////////////// +size_t itohex(char *buffer, int data) +{ + if (data < 0) { + *buffer++ = '-'; + return uitohex(buffer, -data, 0) + 1; + } + return uitohex(buffer, data, 0); +} diff --git a/components/f4ll/src/usart_core.cpp b/components/f4ll/src/usart_core.cpp new file mode 100644 index 0000000..c0051d6 --- /dev/null +++ b/components/f4ll/src/usart_core.cpp @@ -0,0 +1,145 @@ +/* + * ll_dmadrivenusartcore.cpp + * + * Created on: Nov 4, 2019 + * Author: abody + */ + +#include + +namespace f4ll { + +usart_core::usart_core(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t streamRx, uint32_t streamTx) + : m_usart(usart), + m_rx_dma(dma, streamRx), + m_tx_dma(dma, streamTx) +{ + uint32_t status = usart->SR; + volatile uint32_t tmpreg = usart->DR; // clearing some of the error/status bits in the USART + (void)tmpreg; + (void)status; + + *m_tx_dma.get_ifc_reg() = + m_tx_dma.get_tc_mask() | m_rx_dma.get_ht_mask() | m_tx_dma.get_te_mask() | m_rx_dma.get_fe_mask() | m_rx_dma.get_dme_mask(); + *m_rx_dma.get_ifc_reg() = + m_rx_dma.get_tc_mask() | m_rx_dma.get_ht_mask() | m_rx_dma.get_te_mask() | m_rx_dma.get_fe_mask() | m_rx_dma.get_dme_mask(); + + LL_DMA_EnableIT_TC(dma, streamRx); + LL_DMA_EnableIT_TE(dma, streamRx); + LL_DMA_EnableIT_TC(dma, streamTx); + LL_DMA_EnableIT_TE(dma, streamTx); +} + +void usart_core::usart_isr() +{ + uint32_t status = m_usart->SR; + volatile uint32_t tmpreg = m_usart->DR; // clearing some of the error/status bits in the HW + (void)tmpreg; + + if (LL_USART_IsEnabledIT_TC(m_usart) && LL_USART_IsActiveFlag_TC(m_usart)) { // transmission complete + LL_USART_DisableIT_TC(m_usart); + transmission_complete(); + } + if (LL_USART_IsEnabledIT_IDLE(m_usart) && (status & USART_SR_IDLE)) { + receiver_idle(); + } + if (LL_USART_IsEnabledIT_ERROR(m_usart)) { + if (status & USART_SR_FE) { + framing_error(); + } + if (status & USART_SR_ORE) { + overrun(); + } + } +} + +void usart_core::rx_dma_isr() +{ + if (*m_rx_dma.get_is_reg() & m_rx_dma.get_tc_mask()) { + *m_rx_dma.get_ifc_reg() = m_rx_dma.get_tc_mask(); + if (m_rx_dma.is_enabled_it_tc()) { + rx_dma_transfer_complete(); + } + } + if (*m_rx_dma.get_is_reg() & m_rx_dma.get_ht_mask()) { + *m_rx_dma.get_ifc_reg() = m_rx_dma.get_ht_mask(); + if (m_rx_dma.is_enabled_it_ht()) { + rx_dma_half_transfer(); + } + } + if (*m_rx_dma.get_is_reg() & m_rx_dma.get_te_mask()) { + *m_rx_dma.get_ifc_reg() = m_rx_dma.get_te_mask(); + if (m_rx_dma.is_enabled_it_te()) { + rx_dma_error(dma_helper::dma_error_type::transfer); + } + } + if (*m_rx_dma.get_is_reg() & m_rx_dma.get_fe_mask()) { + *m_rx_dma.get_ifc_reg() = m_rx_dma.get_fe_mask(); + if (m_rx_dma.is_enabled_it_fe()) { + rx_dma_error(dma_helper::dma_error_type::fifo); + } + } + if (*m_rx_dma.get_is_reg() & m_rx_dma.get_dme_mask()) { + *m_rx_dma.get_ifc_reg() = m_rx_dma.get_dme_mask(); + if (m_rx_dma.is_enabled_it_dme()) { + rx_dma_error(dma_helper::dma_error_type::direct_mode); + } + } +} + +void usart_core::tx_dma_isr() +{ + if (*m_tx_dma.get_is_reg() & m_tx_dma.get_tc_mask()) { // DMA transfer complete + *m_tx_dma.get_ifc_reg() = m_tx_dma.get_tc_mask(); + if (m_tx_dma.is_enabled_it_tc()) { + tx_dma_transfer_complete(); + } + } + if (*m_tx_dma.get_is_reg() & m_tx_dma.get_ht_mask()) { + *m_tx_dma.get_ifc_reg() = m_tx_dma.get_ht_mask(); + if (m_tx_dma.is_enabled_it_ht()) { + tx_dma_half_transfer(); + } + } + if (*m_tx_dma.get_is_reg() & m_tx_dma.get_te_mask()) { + *m_tx_dma.get_ifc_reg() = m_tx_dma.get_te_mask(); + if (m_tx_dma.is_enabled_it_te()) { + tx_dma_error(dma_helper::dma_error_type::transfer); + } + } + if (*m_tx_dma.get_is_reg() & m_tx_dma.get_fe_mask()) { + *m_tx_dma.get_ifc_reg() = m_tx_dma.get_fe_mask(); + if (m_tx_dma.is_enabled_it_fe()) { + tx_dma_error(dma_helper::dma_error_type::fifo); + } + } + if (*m_tx_dma.get_is_reg() & m_tx_dma.get_dme_mask()) { + *m_tx_dma.get_ifc_reg() = m_tx_dma.get_dme_mask(); + if (m_tx_dma.is_enabled_it_dme()) { + tx_dma_error(dma_helper::dma_error_type::direct_mode); + } + } +} + +void usart_core::setup_transmit(void const *buffer, uint16_t length) +{ + LL_DMA_ConfigAddresses( + m_tx_dma.get_dma(), m_tx_dma.get_stream(), reinterpret_cast(buffer), LL_USART_DMA_GetRegAddr(m_usart), + LL_DMA_DIRECTION_MEMORY_TO_PERIPH); + LL_DMA_SetDataLength(m_tx_dma.get_dma(), m_tx_dma.get_stream(), length); + LL_USART_EnableDMAReq_TX(m_usart); + LL_DMA_EnableStream(m_tx_dma.get_dma(), m_tx_dma.get_stream()); +} + +void usart_core::setup_receive(void *buffer, uint16_t length) +{ + LL_DMA_ConfigAddresses( + m_rx_dma.get_dma(), m_rx_dma.get_stream(), LL_USART_DMA_GetRegAddr(m_usart), reinterpret_cast(buffer), + LL_DMA_DIRECTION_PERIPH_TO_MEMORY); + LL_DMA_SetDataLength(m_rx_dma.get_dma(), m_rx_dma.get_stream(), length); + LL_USART_EnableDMAReq_RX(m_usart); + LL_USART_ClearFlag_ORE(m_usart); + LL_DMA_EnableStream(m_rx_dma.get_dma(), m_rx_dma.get_stream()); +} + +} /* namespace f4ll */