From 61fce5992e95e448b3fddc1e29945812b8eaa43b Mon Sep 17 00:00:00 2001 From: Attila Body Date: Mon, 9 Jun 2025 18:13:21 +0200 Subject: [PATCH] git subrepo clone ssh://git@codeberg.org/abody/f4ll.git components/f4ll subrepo: subdir: "components/f4ll" merged: "a4df325" upstream: origin: "ssh://git@codeberg.org/abody/f4ll.git" branch: "master" commit: "a4df325" git-subrepo: version: "0.4.9" origin: "???" commit: "???" --- components/f4ll/.clang-format | 33 ++++ components/f4ll/.gitrepo | 12 ++ components/f4ll/CMakeLists.txt | 19 ++ components/f4ll/inc/f4ll/console_handler.h | 44 +++++ components/f4ll/inc/f4ll/crc_handler.h | 90 +++++++++ components/f4ll/inc/f4ll/dma_helper.h | 60 ++++++ components/f4ll/inc/f4ll/fault.h | 47 +++++ components/f4ll/inc/f4ll/irq_lock.h | 26 +++ components/f4ll/inc/f4ll/memcpy_dma.h | 27 +++ components/f4ll/inc/f4ll/packet_usart.h | 122 ++++++++++++ components/f4ll/inc/f4ll/ringbuffer.h | 218 +++++++++++++++++++++ components/f4ll/inc/f4ll/singleton.h | 33 ++++ components/f4ll/inc/f4ll/str_util.h | 31 +++ components/f4ll/inc/f4ll/usart_core.h | 54 +++++ components/f4ll/src/console_handler.cpp | 100 ++++++++++ components/f4ll/src/crc_handler.cpp | 160 +++++++++++++++ components/f4ll/src/dma_helper.cpp | 24 +++ components/f4ll/src/fault.cpp | 150 ++++++++++++++ components/f4ll/src/memcpy_dma.cpp | 38 ++++ components/f4ll/src/packet_usart.cpp | 212 ++++++++++++++++++++ components/f4ll/src/str_util.cpp | 105 ++++++++++ components/f4ll/src/usart_core.cpp | 145 ++++++++++++++ 22 files changed, 1750 insertions(+) create mode 100644 components/f4ll/.clang-format create mode 100644 components/f4ll/.gitrepo create mode 100644 components/f4ll/CMakeLists.txt create mode 100644 components/f4ll/inc/f4ll/console_handler.h create mode 100644 components/f4ll/inc/f4ll/crc_handler.h create mode 100644 components/f4ll/inc/f4ll/dma_helper.h create mode 100644 components/f4ll/inc/f4ll/fault.h create mode 100644 components/f4ll/inc/f4ll/irq_lock.h create mode 100644 components/f4ll/inc/f4ll/memcpy_dma.h create mode 100644 components/f4ll/inc/f4ll/packet_usart.h create mode 100644 components/f4ll/inc/f4ll/ringbuffer.h create mode 100644 components/f4ll/inc/f4ll/singleton.h create mode 100644 components/f4ll/inc/f4ll/str_util.h create mode 100644 components/f4ll/inc/f4ll/usart_core.h create mode 100644 components/f4ll/src/console_handler.cpp create mode 100644 components/f4ll/src/crc_handler.cpp create mode 100644 components/f4ll/src/dma_helper.cpp create mode 100644 components/f4ll/src/fault.cpp create mode 100644 components/f4ll/src/memcpy_dma.cpp create mode 100644 components/f4ll/src/packet_usart.cpp create mode 100644 components/f4ll/src/str_util.cpp create mode 100644 components/f4ll/src/usart_core.cpp diff --git a/components/f4ll/.clang-format b/components/f4ll/.clang-format new file mode 100644 index 0000000..6d24e95 --- /dev/null +++ b/components/f4ll/.clang-format @@ -0,0 +1,33 @@ +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 +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/.gitrepo b/components/f4ll/.gitrepo new file mode 100644 index 0000000..bc56f99 --- /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 = a4df3252f2c04eeb83c01b12a754a2d1f5f800d2 + parent = ce3dd83b9f437590ecf153437139a609662498db + method = merge + cmdver = 0.4.9 diff --git a/components/f4ll/CMakeLists.txt b/components/f4ll/CMakeLists.txt new file mode 100644 index 0000000..0664851 --- /dev/null +++ b/components/f4ll/CMakeLists.txt @@ -0,0 +1,19 @@ +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/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..9972539 --- /dev/null +++ b/components/f4ll/inc/f4ll/console_handler.h @@ -0,0 +1,44 @@ +/* + * ll_consolehandler.h + * + * Created on: Nov 7, 2019 + * Author: abody + */ + +#pragma once + +#include +#include +#include + +namespace f4ll { + +class console_handler : public usart_core, public singleton +{ + friend class singleton; + +public: + void print(char const *s); + void flush(); + size_t append(char const *s); + +private: + console_handler(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx); + + // 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<128> m_tx_buffer; + iringbuffer::size_type m_in_flight_size = 0; +}; + +} /* 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..650eebb --- /dev/null +++ b/components/f4ll/inc/f4ll/crc_handler.h @@ -0,0 +1,90 @@ +/* + * ll_crc_handler.h + * + * Created on: Oct 26, 2019 + * Author: compi + */ +#pragma once + +#include +#include +#include +#include + +namespace f4ll { + +class crc_handler : public singleton +{ + friend class 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..534b59f --- /dev/null +++ b/components/f4ll/inc/f4ll/dma_helper.h @@ -0,0 +1,60 @@ +/* + * ll_dmahelper.h + * + * Created on: Oct 25, 2019 + * Author: abody + */ + +#ifndef LL_DMAHELPER_H_ +#define LL_DMAHELPER_H_ + +#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 m_stream; + volatile uint32_t *m_is_reg; + volatile uint32_t *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 */ + +#endif /* LL_DMAHELPER_H_ */ diff --git a/components/f4ll/inc/f4ll/fault.h b/components/f4ll/inc/f4ll/fault.h new file mode 100644 index 0000000..b6787ea --- /dev/null +++ b/components/f4ll/inc/f4ll/fault.h @@ -0,0 +1,47 @@ +#ifndef __FAULT_H +#define __FAULT_H + +#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 + +#endif /* __FAULT_H */ diff --git a/components/f4ll/inc/f4ll/irq_lock.h b/components/f4ll/inc/f4ll/irq_lock.h new file mode 100644 index 0000000..f5c03a5 --- /dev/null +++ b/components/f4ll/inc/f4ll/irq_lock.h @@ -0,0 +1,26 @@ +#ifndef _IRQLOCK_H_INCLUDED +#define _IRQLOCK_H_INCLUDED + +#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; +}; +} + +#endif // _IRQLOCK_H_INCLUDED diff --git a/components/f4ll/inc/f4ll/memcpy_dma.h b/components/f4ll/inc/f4ll/memcpy_dma.h new file mode 100644 index 0000000..2b078f6 --- /dev/null +++ b/components/f4ll/inc/f4ll/memcpy_dma.h @@ -0,0 +1,27 @@ +/* + * llmemcpydma.h + * + * Created on: Nov 4, 2019 + * Author: abody + */ +#pragma once + +#include +#include + +namespace f4ll { + +class memcpy_dma : public singleton, private dma_helper +{ + friend class 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..649a813 --- /dev/null +++ b/components/f4ll/inc/f4ll/packet_usart.h @@ -0,0 +1,122 @@ +/* + * ll_HsUsart.h + * + * Created on: Oct 29, 2019 + * Author: abody + */ + +#ifndef LL_HSUSART_H_ +#define LL_HSUSART_H_ +#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]; +}; + +} +#endif /* LL_HSUSART_H_ */ diff --git a/components/f4ll/inc/f4ll/ringbuffer.h b/components/f4ll/inc/f4ll/ringbuffer.h new file mode 100644 index 0000000..acf39d1 --- /dev/null +++ b/components/f4ll/inc/f4ll/ringbuffer.h @@ -0,0 +1,218 @@ +/* + * ringbuffer.h + * + * Created on: May 31, 2025 + * Author: Attila Body + */ + +#pragma once + +#include +#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[in] len_requested Length of the data requested from the buffer. + /// The length of the actual data provided + /// might be actually smaller (because either reaching the end of + /// the buffer or not enough data in the buffer). + /// @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(size_type len_requested, 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 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; +}; + +template class ringbuffer : public iringbuffer +{ +public: + ringbuffer() = default; + + size_type put(uint8_t const *data, size_type len) override; + void commit() override; + bool get_chunk(size_type len_requested, uint8_t const *&data, size_type &len) const override; + void consumed(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[SZ]; //!< Data bufer + 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; +}; + +// +template iringbuffer::size_type ringbuffer::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 = sizeof(m_buffer) - 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 == sizeof(m_buffer)) { + m_head_shadow = 0; + } + + if (chunk2) { + std::memcpy(m_buffer, data + chunk1, chunk2); + m_head_shadow += chunk2; + if (m_head_shadow == sizeof(m_buffer)) { + m_head_shadow = 0; + } + } + + return len; +} + +template void ringbuffer::commit() +{ + m_head = m_head_shadow; +}; + +template bool ringbuffer::get_chunk(size_type len_requested, uint8_t const *&data, size_type &len) const +{ + if (!len_requested) { + return false; + } + + size_type head = m_head; + size_type tail = m_tail; + size_type chunk_size = head >= tail ? head - tail : sizeof(m_buffer) - tail; + + if (!chunk_size) { + len = 0; + return false; + } + + if (chunk_size > len_requested) { + chunk_size = len_requested; + } + data = m_buffer + tail; + len = chunk_size; + + tail += chunk_size; + if (tail == sizeof(m_buffer)) { + tail = 0; + } + + return tail != head; +} + +template void ringbuffer::consumed(size_type len) +{ + if (!len) { + return; + } + m_tail += len; + if (m_tail == sizeof(m_buffer)) { + m_tail = 0; + } +} + +template iringbuffer::size_type ringbuffer::uncommited() const +{ + return marker_diff(m_head_shadow, m_head); +} + +template iringbuffer::size_type ringbuffer::commited() const +{ + return marker_diff(m_head, m_tail); +} + +template void ringbuffer::discard() +{ + m_head_shadow = m_head; +} + +template iringbuffer::size_type ringbuffer::size() const +{ + return SZ; +} + +template iringbuffer::size_type ringbuffer::used() const +{ + return marker_diff(m_head_shadow, m_tail); +} + +template iringbuffer::size_type ringbuffer::unused() const +{ + return SZ - used() - 1; +} + +template iringbuffer::size_type ringbuffer::marker_diff(size_type m1, size_type m2) const +{ + return (m1 >= m2) ? (m1 - m2) : (SZ - m2 + m1); +} + +} // namespace f1ll \ No newline at end of file diff --git a/components/f4ll/inc/f4ll/singleton.h b/components/f4ll/inc/f4ll/singleton.h new file mode 100644 index 0000000..e9a8fe8 --- /dev/null +++ b/components/f4ll/inc/f4ll/singleton.h @@ -0,0 +1,33 @@ +#ifndef SINGLETON_H_ +#define SINGLETON_H_ + +#include + +namespace f4ll { + +template class 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: + singleton() = default; + singleton(const singleton &) = delete; + singleton &operator=(const singleton &) = delete; + static T *m_instance; +}; + +template T *singleton::m_instance = nullptr; + +} // namespace f1ll { + +#endif /* SINGLETON_H_ */ diff --git a/components/f4ll/inc/f4ll/str_util.h b/components/f4ll/inc/f4ll/str_util.h new file mode 100644 index 0000000..49ddc39 --- /dev/null +++ b/components/f4ll/inc/f4ll/str_util.h @@ -0,0 +1,31 @@ +/* + * strutil.h + * + * Created on: Feb 11, 2017 + * Author: compi + */ + +#ifndef _STM32PLUS_STRUTIL_H_ +#define _STM32PLUS_STRUTIL_H_ + +#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 + +#endif /* _STM32PLUS_STRUTIL_H_ */ diff --git a/components/f4ll/inc/f4ll/usart_core.h b/components/f4ll/inc/f4ll/usart_core.h new file mode 100644 index 0000000..f4f44be --- /dev/null +++ b/components/f4ll/inc/f4ll/usart_core.h @@ -0,0 +1,54 @@ +/* + * ll_dmadrivenusartcore.h + * + * Created on: Nov 4, 2019 + * Author: abody + */ + +#ifndef LL_USARTCORE_H_ +#define LL_USARTCORE_H_ +#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: + 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; + + void usart_isr(); + void rx_dma_isr(); + void tx_dma_isr(); +}; + +} /* namespace f4ll */ + +#endif /* LL_USARTCORE_H_ */ diff --git a/components/f4ll/src/console_handler.cpp b/components/f4ll/src/console_handler.cpp new file mode 100644 index 0000000..694e107 --- /dev/null +++ b/components/f4ll/src/console_handler.cpp @@ -0,0 +1,100 @@ +/* + * ll_consolehandler.cpp + * + * Created on: Nov 7, 2019 + * Author: abody + */ + +#include +#include +#include + +#include + +namespace f4ll { + +console_handler::console_handler(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx) + : usart_core(usart, dma, stream_rx, stream_tx) +{ +} + +void console_handler::receiver_idle(void) {} + +void console_handler::transmission_complete(void) +{ + m_tx_buffer.consumed(m_in_flight_size); + if (m_tx_buffer.commited()) { + uint8_t const *chunk; + m_tx_buffer.get_chunk(m_tx_buffer.size(), chunk, m_in_flight_size); + if (m_in_flight_size) { + setup_transmit(chunk, m_in_flight_size); + } + } else { + m_in_flight_size = 0; + } +} + +void console_handler::framing_error(void) {} + +void console_handler::overrun(void) {} + +void console_handler::rx_dma_transfer_complete(void) {} + +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::tx_dma_half_transfer(void) {} + +void console_handler::tx_dma_error(dma_helper::dma_error_type reason) +{ + (void)reason; +} + +size_t 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() +{ + bool busy; + + if (!m_tx_buffer.uncommited()) { + return; + } + m_tx_buffer.commit(); + { + irq_lock l; + busy = m_in_flight_size != 0; + } + if (busy) { + return; + } + uint8_t const *chunk; + m_tx_buffer.get_chunk(m_tx_buffer.size(), chunk, m_in_flight_size); + if (m_in_flight_size) { + setup_transmit(chunk, m_in_flight_size); + } +} + +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..8693a6f --- /dev/null +++ b/components/f4ll/src/crc_handler.cpp @@ -0,0 +1,160 @@ +/* + * 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..2187ade --- /dev/null +++ b/components/f4ll/src/fault.cpp @@ -0,0 +1,150 @@ +/* + * fault.c + * + * Created on: Oct 1, 2019 + * Author: abody + * -c "tpiu config internal uart off " + */ +#include +//#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..f56a0b6 --- /dev/null +++ b/components/f4ll/src/packet_usart.cpp @@ -0,0 +1,212 @@ +/* + * 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/str_util.cpp b/components/f4ll/src/str_util.cpp new file mode 100644 index 0000000..7bc9bcb --- /dev/null +++ b/components/f4ll/src/str_util.cpp @@ -0,0 +1,105 @@ +#include +#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 */