Compare commits
4 commits
d72d3af2a7
...
d8f35c562c
Author | SHA1 | Date | |
---|---|---|---|
d8f35c562c | |||
d7dbc95a7b | |||
90b332a1cc | |||
27af775a37 |
27 changed files with 17 additions and 1735 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "components/f4ll"]
|
||||||
|
path = components/f4ll
|
||||||
|
url = ssh://git@codeberg.org/abody/f4ll.git
|
|
@ -127,7 +127,7 @@ void MX_FREERTOS_Init(void)
|
||||||
__weak void StartDefaultTask(void const *argument)
|
__weak void StartDefaultTask(void const *argument)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN StartDefaultTask */
|
/* USER CODE BEGIN StartDefaultTask */
|
||||||
app_main(NULL);
|
app_main();
|
||||||
/* Infinite loop */
|
/* Infinite loop */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
osDelay(1);
|
osDelay(1);
|
||||||
|
|
|
@ -13,15 +13,17 @@
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <f4ll/console_handler.h>
|
#include <f4ll/console_handler.h>
|
||||||
|
#include <f4ll/singleton.h>
|
||||||
|
|
||||||
#include <task.h>
|
#include <task.h>
|
||||||
#include <thread_safe_console_output.h>
|
#include <thread_safe_console_output.h>
|
||||||
|
|
||||||
class app : public f4ll::initialized_singleton<app>, public f4ll::console_handler::iconsole_input
|
class app : public f4ll::singleton<app>, public f4ll::console_handler::iconsole_input
|
||||||
{
|
{
|
||||||
friend class f4ll::initialized_singleton<app>;
|
friend class f4ll::singleton<app>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
app(void const *param);
|
app();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
__attribute__((noreturn)) void main();
|
__attribute__((noreturn)) void main();
|
||||||
|
@ -49,7 +51,7 @@ private:
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
void app_main(void const *param) __attribute__((noreturn));
|
void app_main() __attribute__((noreturn));
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C" {
|
} // extern "C" {
|
||||||
|
|
|
@ -15,18 +15,21 @@
|
||||||
#include <f4ll/console_handler.h>
|
#include <f4ll/console_handler.h>
|
||||||
#include <f4ll/str_util.h>
|
#include <f4ll/str_util.h>
|
||||||
|
|
||||||
|
#include <f4ll/fault.h>
|
||||||
|
#include <f4ll/irq_lock.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <app.h>
|
#include <app.h>
|
||||||
|
|
||||||
void app_main(void const *param)
|
void app_main()
|
||||||
{
|
{
|
||||||
|
|
||||||
app::init(param).main();
|
app::instance().main();
|
||||||
}
|
}
|
||||||
|
|
||||||
app::app(void const *param)
|
app::app()
|
||||||
: m_con(
|
: m_con(
|
||||||
f4ll::console_handler::init(
|
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),
|
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),
|
||||||
|
@ -34,7 +37,6 @@ app::app(void const *param)
|
||||||
m_safe_conout(m_con)
|
m_safe_conout(m_con)
|
||||||
|
|
||||||
{
|
{
|
||||||
(void)param;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void app::main()
|
void app::main()
|
||||||
|
|
1
components/f4ll
Submodule
1
components/f4ll
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 80fc520b6fad8a012e239efca77f33f759337344
|
|
@ -1,37 +0,0 @@
|
||||||
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
|
|
|
@ -1,12 +0,0 @@
|
||||||
; 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 = a4c1baa4109ed591c9db556da430fadfac0642a6
|
|
||||||
parent = 4e1f01c4d45a06b03c631b229402f8c625900922
|
|
||||||
method = merge
|
|
||||||
cmdver = 0.4.9
|
|
|
@ -1,19 +0,0 @@
|
||||||
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)
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* ll_consolehandler.h
|
|
||||||
*
|
|
||||||
* Created on: Nov 7, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <f4ll/initialized_singleton.h>
|
|
||||||
#include <f4ll/packet_usart.h>
|
|
||||||
#include <f4ll/ringbuffer.h>
|
|
||||||
|
|
||||||
namespace f4ll {
|
|
||||||
|
|
||||||
class console_handler : public usart_core, public initialized_singleton<console_handler>
|
|
||||||
{
|
|
||||||
friend class initialized_singleton<console_handler>;
|
|
||||||
|
|
||||||
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 */
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* ll_crc_handler.h
|
|
||||||
*
|
|
||||||
* Created on: Oct 26, 2019
|
|
||||||
* Author: compi
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <f4ll/dma_helper.h>
|
|
||||||
#include <f4ll/initialized_singleton.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <platform/dma_ll.h>
|
|
||||||
|
|
||||||
namespace f4ll {
|
|
||||||
|
|
||||||
class crc_handler : public initialized_singleton<crc_handler>
|
|
||||||
{
|
|
||||||
friend class initialized_singleton<crc_handler>;
|
|
||||||
|
|
||||||
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 <uint8_t n> 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
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* ll_dmahelper.h
|
|
||||||
*
|
|
||||||
* Created on: Oct 25, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <platform/dma_ll.h>
|
|
||||||
|
|
||||||
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 */
|
|
|
@ -1,44 +0,0 @@
|
||||||
#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
|
|
|
@ -1,27 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace f4ll {
|
|
||||||
|
|
||||||
template <typename T> class initialized_singleton
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static T &instance() { return *m_instance; }
|
|
||||||
template <typename... args_t> static T &init(args_t &&...args)
|
|
||||||
{
|
|
||||||
static T instance{std::forward<args_t>(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 <typename T> T *initialized_singleton<T>::m_instance = nullptr;
|
|
||||||
|
|
||||||
} // namespace f1ll {
|
|
|
@ -1,23 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stm32f4xx.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* llmemcpydma.h
|
|
||||||
*
|
|
||||||
* Created on: Nov 4, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <f4ll/dma_helper.h>
|
|
||||||
#include <f4ll/initialized_singleton.h>
|
|
||||||
|
|
||||||
namespace f4ll {
|
|
||||||
|
|
||||||
class memcpy_dma : public initialized_singleton<memcpy_dma>, private dma_helper
|
|
||||||
{
|
|
||||||
friend class initialized_singleton<memcpy_dma>;
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,122 +0,0 @@
|
||||||
/*
|
|
||||||
* ll_HsUsart.h
|
|
||||||
*
|
|
||||||
* Created on: Oct 29, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <platform/usart_ll.h>
|
|
||||||
|
|
||||||
#include <f4ll/crc_handler.h>
|
|
||||||
#include <f4ll/usart_core.h>
|
|
||||||
|
|
||||||
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];
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,218 +0,0 @@
|
||||||
/*
|
|
||||||
* ringbuffer.h
|
|
||||||
*
|
|
||||||
* Created on: May 31, 2025
|
|
||||||
* Author: Attila Body
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
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 <iringbuffer::size_type SZ> 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 SZ> iringbuffer::size_type ringbuffer<SZ>::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 <iringbuffer::size_type SZ> void ringbuffer<SZ>::commit()
|
|
||||||
{
|
|
||||||
m_head = m_head_shadow;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <iringbuffer::size_type SZ> bool ringbuffer<SZ>::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 <iringbuffer::size_type SZ> void ringbuffer<SZ>::consumed(size_type len)
|
|
||||||
{
|
|
||||||
if (!len) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_tail += len;
|
|
||||||
if (m_tail == sizeof(m_buffer)) {
|
|
||||||
m_tail = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::uncommited() const
|
|
||||||
{
|
|
||||||
return marker_diff(m_head_shadow, m_head);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::commited() const
|
|
||||||
{
|
|
||||||
return marker_diff(m_head, m_tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <iringbuffer::size_type SZ> void ringbuffer<SZ>::discard()
|
|
||||||
{
|
|
||||||
m_head_shadow = m_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::size() const
|
|
||||||
{
|
|
||||||
return SZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::used() const
|
|
||||||
{
|
|
||||||
return marker_diff(m_head_shadow, m_tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::unused() const
|
|
||||||
{
|
|
||||||
return SZ - used() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <iringbuffer::size_type SZ> iringbuffer::size_type ringbuffer<SZ>::marker_diff(size_type m1, size_type m2) const
|
|
||||||
{
|
|
||||||
return (m1 >= m2) ? (m1 - m2) : (SZ - m2 + m1);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace f1ll
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* strutil.h
|
|
||||||
*
|
|
||||||
* Created on: Feb 11, 2017
|
|
||||||
* Author: compi
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#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
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* ll_dmadrivenusartcore.h
|
|
||||||
*
|
|
||||||
* Created on: Nov 4, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <platform/usart_ll.h>
|
|
||||||
|
|
||||||
#include <f4ll/dma_helper.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void usart_isr();
|
|
||||||
void rx_dma_isr();
|
|
||||||
void tx_dma_isr();
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace f4ll */
|
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* ll_consolehandler.cpp
|
|
||||||
*
|
|
||||||
* Created on: Nov 7, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <f4ll/console_handler.h>
|
|
||||||
#include <f4ll/irq_lock.h>
|
|
||||||
#include <f4ll/str_util.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
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<uint8_t const *>(s), len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void console_handler::flush()
|
|
||||||
{
|
|
||||||
if (!m_tx_buffer.uncommited()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_tx_buffer.commit();
|
|
||||||
|
|
||||||
if (m_in_flight_size) {
|
|
||||||
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 */
|
|
|
@ -1,161 +0,0 @@
|
||||||
/*
|
|
||||||
* ll_crc_handler.cpp
|
|
||||||
*
|
|
||||||
* Created on: Oct 26, 2019
|
|
||||||
* Author: compi
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <f4ll/crc_handler.h>
|
|
||||||
|
|
||||||
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<uint32_t *>((*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<uint32_t *>((*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<uint32_t>((*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<uintptr_t>(&result));
|
|
||||||
while (is_active(slot, task))
|
|
||||||
;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace f4ll
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
q * ll_dmahelper.cpp
|
|
||||||
*
|
|
||||||
* Created on: Oct 25, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <f4ll/dma_helper.h>
|
|
||||||
|
|
||||||
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 */
|
|
|
@ -1,142 +0,0 @@
|
||||||
/*
|
|
||||||
* fault.c
|
|
||||||
*
|
|
||||||
* Created on: Oct 1, 2019
|
|
||||||
* Author: abody
|
|
||||||
* -c "tpiu config internal <logfile_full_path> uart off <cpufreq>"
|
|
||||||
*/
|
|
||||||
#include <inttypes.h>
|
|
||||||
// #include <core_cm4.h>
|
|
||||||
#include <f4ll/fault.h>
|
|
||||||
#include <f4ll/str_util.h>
|
|
||||||
#include <stm32f4xx.h>
|
|
||||||
|
|
||||||
#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
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* llmemcpydma.cpp
|
|
||||||
*
|
|
||||||
* Created on: Nov 4, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <f4ll/memcpy_dma.h>
|
|
||||||
|
|
||||||
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 */
|
|
|
@ -1,213 +0,0 @@
|
||||||
/*
|
|
||||||
* ll_hsusart_impl.h
|
|
||||||
*
|
|
||||||
* Created on: Oct 29, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
#include <f4ll/packet_usart.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace f4ll {
|
|
||||||
|
|
||||||
template <typename T> 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<uintptr_t>(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<int>(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<int>(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
|
|
|
@ -1,106 +0,0 @@
|
||||||
#include <f4ll/str_util.h>
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
/*
|
|
||||||
* ll_dmadrivenusartcore.cpp
|
|
||||||
*
|
|
||||||
* Created on: Nov 4, 2019
|
|
||||||
* Author: abody
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <f4ll/usart_core.h>
|
|
||||||
|
|
||||||
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<uint32_t>(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<uint32_t>(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 */
|
|
Loading…
Add table
Add a link
Reference in a new issue