added minmal libaries
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "lib/nanopb"]
|
||||||
|
path = lib/nanopb
|
||||||
|
url = https://github.com/nanopb/nanopb.git
|
||||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"stm32h7xx_hal_conf.h": "c"
|
||||||
|
}
|
||||||
|
}
|
||||||
113
CMakeLists.txt
113
CMakeLists.txt
@@ -60,62 +60,28 @@ set(linker_OPTS)
|
|||||||
# information to the project
|
# information to the project
|
||||||
include("cmake_generated/cmake_generated.cmake")
|
include("cmake_generated/cmake_generated.cmake")
|
||||||
|
|
||||||
# Link directories setup
|
|
||||||
# Must be before executable is added
|
|
||||||
link_directories(${CMAKE_PROJECT_NAME} ${link_DIRS})
|
|
||||||
|
|
||||||
# Create an executable object type
|
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib/nanopb/extra)
|
||||||
add_executable(${CMAKE_PROJECT_NAME})
|
find_package(Nanopb REQUIRED)
|
||||||
|
include_directories(${NANOPB_INCLUDE_DIRS})
|
||||||
|
nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS RELPATH proto proto/firmware.proto)
|
||||||
|
add_library(PROTOS ${PROTO_SRCS} ${PROTO_HDRS})
|
||||||
|
target_include_directories(PROTOS PUBLIC ${NANOPB_INCLUDE_DIRS})
|
||||||
|
|
||||||
# Add sources to executable
|
|
||||||
target_sources(${CMAKE_PROJECT_NAME} PUBLIC ${sources_SRCS})
|
|
||||||
|
|
||||||
# Add include paths
|
# these options need to be applied for the hole project
|
||||||
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
|
# so that all subprojects and libaries are compiled and compatable
|
||||||
${include_DIRS}
|
add_compile_options(
|
||||||
$<$<COMPILE_LANGUAGE:C>: ${include_c_DIRS}>
|
|
||||||
$<$<COMPILE_LANGUAGE:CXX>: ${include_cxx_DIRS}>
|
|
||||||
$<$<COMPILE_LANGUAGE:ASM>: ${include_asm_DIRS}>
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add project symbols (macros)
|
|
||||||
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
|
|
||||||
${symbols_SYMB}
|
|
||||||
$<$<COMPILE_LANGUAGE:C>: ${symbols_c_SYMB}>
|
|
||||||
$<$<COMPILE_LANGUAGE:CXX>: ${symbols_cxx_SYMB}>
|
|
||||||
$<$<COMPILE_LANGUAGE:ASM>: ${symbols_asm_SYMB}>
|
|
||||||
|
|
||||||
# Configuration specific
|
|
||||||
$<$<CONFIG:Debug>:DEBUG>
|
|
||||||
$<$<CONFIG:Release>: >
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add linked libraries
|
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME} ${link_LIBS})
|
|
||||||
|
|
||||||
# Compiler options
|
|
||||||
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE
|
|
||||||
${cpu_PARAMS}
|
${cpu_PARAMS}
|
||||||
${compiler_OPTS}
|
${compiler_OPTS}
|
||||||
-Wall
|
-Wall
|
||||||
-Wextra
|
-Wextra
|
||||||
-Wpedantic
|
-Wpedantic
|
||||||
-Wno-unused-parameter
|
-Wno-unused-parameter
|
||||||
$<$<COMPILE_LANGUAGE:C>: >
|
-flto
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:
|
|
||||||
|
|
||||||
# -Wno-volatile
|
|
||||||
# -Wold-style-cast
|
|
||||||
# -Wuseless-cast
|
|
||||||
# -Wsuggest-override
|
|
||||||
>
|
|
||||||
$<$<COMPILE_LANGUAGE:ASM>:-x assembler-with-cpp -MMD -MP>
|
|
||||||
$<$<CONFIG:Debug>:-Og -g3 -ggdb>
|
|
||||||
$<$<CONFIG:Release>:-Og -g0>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Linker options
|
add_link_options(
|
||||||
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
|
|
||||||
-T${linker_script_SRC}
|
-T${linker_script_SRC}
|
||||||
${cpu_PARAMS}
|
${cpu_PARAMS}
|
||||||
${linker_OPTS}
|
${linker_OPTS}
|
||||||
@@ -128,10 +94,67 @@ target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
|
|||||||
-lstdc++
|
-lstdc++
|
||||||
-lsupc++
|
-lsupc++
|
||||||
-Wl,--end-group
|
-Wl,--end-group
|
||||||
|
-flto
|
||||||
-Wl,-z,max-page-size=8 # Allow good software remapping across address space (with proper GCC section making)
|
-Wl,-z,max-page-size=8 # Allow good software remapping across address space (with proper GCC section making)
|
||||||
-Wl,--print-memory-usage
|
-Wl,--print-memory-usage
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Link directories setup
|
||||||
|
# Must be before executable is added
|
||||||
|
link_directories(${CMAKE_PROJECT_NAME} ${link_DIRS})
|
||||||
|
|
||||||
|
# Create an executable object type
|
||||||
|
add_executable(${CMAKE_PROJECT_NAME})
|
||||||
|
|
||||||
|
# Add sources to executable
|
||||||
|
target_sources(${CMAKE_PROJECT_NAME} PUBLIC ${sources_SRCS})
|
||||||
|
|
||||||
|
# Add include paths
|
||||||
|
# these options need to be applied for the hole project
|
||||||
|
include_directories(
|
||||||
|
${include_DIRS}
|
||||||
|
$<$<COMPILE_LANGUAGE:C>: ${include_c_DIRS}>
|
||||||
|
$<$<COMPILE_LANGUAGE:CXX>: ${include_cxx_DIRS}>
|
||||||
|
$<$<COMPILE_LANGUAGE:ASM>: ${include_asm_DIRS}>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add project symbols (macros)
|
||||||
|
add_compile_definitions(
|
||||||
|
${symbols_SYMB}
|
||||||
|
${symbols_c_SYMB}
|
||||||
|
${symbols_cxx_SYMB}
|
||||||
|
${symbols_asm_SYMB}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory("lib")
|
||||||
|
add_subdirectory("Application")
|
||||||
|
|
||||||
|
# Add linked libraries
|
||||||
|
# target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC protobuf-nanopb-static)
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC uart_driver)
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC lwrb)
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC PROTOS)
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC Tasks)
|
||||||
|
|
||||||
|
# Compiler options
|
||||||
|
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE
|
||||||
|
$<$<COMPILE_LANGUAGE:C>: >
|
||||||
|
$<$<COMPILE_LANGUAGE:CXX>:
|
||||||
|
|
||||||
|
# -Wno-volatile
|
||||||
|
# -Wold-style-cast
|
||||||
|
# -Wuseless-cast
|
||||||
|
# -Wsuggest-override
|
||||||
|
>
|
||||||
|
$<$<COMPILE_LANGUAGE:ASM>:-x assembler-with-cpp -MMD -MP>
|
||||||
|
$<$<CONFIG:Debug>:-Og -g3 -ggdb>
|
||||||
|
$<$<CONFIG:Release>:-O3 -g0>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Linker options
|
||||||
|
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
# Execute post-build to print size, generate hex and bin
|
# Execute post-build to print size, generate hex and bin
|
||||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
|
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
|
||||||
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:${CMAKE_PROJECT_NAME}>
|
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:${CMAKE_PROJECT_NAME}>
|
||||||
|
|||||||
@@ -641,7 +641,7 @@ void BSP_SD_WriteCpltCallback(void)
|
|||||||
osMessagePut(SDQueueID, WRITE_CPLT_MSG, 0);
|
osMessagePut(SDQueueID, WRITE_CPLT_MSG, 0);
|
||||||
#else
|
#else
|
||||||
const uint16_t msg = WRITE_CPLT_MSG;
|
const uint16_t msg = WRITE_CPLT_MSG;
|
||||||
osMessageQueuePut(SDQueueID, (const void *)&msg, NULL, 0);
|
osMessageQueuePut(SDQueueID, (const void *)&msg, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,7 +660,7 @@ void BSP_SD_ReadCpltCallback(void)
|
|||||||
osMessagePut(SDQueueID, READ_CPLT_MSG, 0);
|
osMessagePut(SDQueueID, READ_CPLT_MSG, 0);
|
||||||
#else
|
#else
|
||||||
const uint16_t msg = READ_CPLT_MSG;
|
const uint16_t msg = READ_CPLT_MSG;
|
||||||
osMessageQueuePut(SDQueueID, (const void *)&msg, NULL, 0);
|
osMessageQueuePut(SDQueueID, (const void *)&msg, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
#include "usbd_cdc_if.h"
|
#include "usbd_cdc_if.h"
|
||||||
|
|
||||||
/* USER CODE BEGIN INCLUDE */
|
/* USER CODE BEGIN INCLUDE */
|
||||||
|
#include "UsbDataHandler.h"
|
||||||
/* USER CODE END INCLUDE */
|
/* USER CODE END INCLUDE */
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
/* Private typedef -----------------------------------------------------------*/
|
||||||
@@ -264,6 +264,7 @@ static int8_t CDC_Control_HS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
|
|||||||
static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
|
static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
|
||||||
{
|
{
|
||||||
/* USER CODE BEGIN 11 */
|
/* USER CODE BEGIN 11 */
|
||||||
|
UsbDataHandler_RxCallback(Buf,*Len);
|
||||||
USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
|
USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
|
||||||
USBD_CDC_ReceivePacket(&hUsbDeviceHS);
|
USBD_CDC_ReceivePacket(&hUsbDeviceHS);
|
||||||
return (USBD_OK);
|
return (USBD_OK);
|
||||||
|
|||||||
@@ -23,10 +23,8 @@ set(sources_SRCS ${sources_SRCS}
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/crc.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/crc.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/dma.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/dma.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/fdcan.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/fdcan.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/freertos.c
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/gpio.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/gpio.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/i2c.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/i2c.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/main.c
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/rng.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/rng.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/rtc.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/rtc.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/sdmmc.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/sdmmc.c
|
||||||
@@ -36,6 +34,8 @@ set(sources_SRCS ${sources_SRCS}
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/syscalls.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/syscalls.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/sysmem.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/sysmem.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/system_stm32h7xx.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/system_stm32h7xx.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/freertos.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/main.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/usart.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Src/usart.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Core/Startup/startup_stm32h723vgtx.s
|
${CMAKE_CURRENT_SOURCE_DIR}/Core/Startup/startup_stm32h723vgtx.s
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal.c
|
${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal.c
|
||||||
@@ -140,15 +140,3 @@ set(symbols_cxx_SYMB ${symbols_cxx_SYMB})
|
|||||||
set(symbols_asm_SYMB ${symbols_asm_SYMB}
|
set(symbols_asm_SYMB ${symbols_asm_SYMB}
|
||||||
"DEBUG"
|
"DEBUG"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Link directories
|
|
||||||
set(link_DIRS ${link_DIRS})
|
|
||||||
|
|
||||||
# Link libraries
|
|
||||||
set(link_LIBS ${link_LIBS})
|
|
||||||
|
|
||||||
# Compiler options
|
|
||||||
set(compiler_OPTS ${compiler_OPTS})
|
|
||||||
|
|
||||||
# Linker options
|
|
||||||
set(linker_OPTS ${linker_OPTS})
|
|
||||||
|
|||||||
3
lib/CMakeLists.txt
Normal file
3
lib/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
add_subdirectory(lwrb)
|
||||||
|
add_subdirectory(uart_driver)
|
||||||
|
add_subdirectory(nanopb)
|
||||||
16
lib/lwrb/CMakeLists.txt
Normal file
16
lib/lwrb/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
|
project(lwrb C)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} "")
|
||||||
|
target_sources(${PROJECT_NAME}
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/lwrb.c
|
||||||
|
INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/lwrb.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME}
|
||||||
|
INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
)
|
||||||
462
lib/lwrb/lwrb.c
Normal file
462
lib/lwrb/lwrb.c
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
/**
|
||||||
|
* \file lwrb.c
|
||||||
|
* \brief Lightweight ring buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Tilen MAJERLE
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation
|
||||||
|
* files (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge,
|
||||||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of LwRB - Lightweight ring buffer library.
|
||||||
|
*
|
||||||
|
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||||
|
* Version: v2.0.0
|
||||||
|
*/
|
||||||
|
#include "lwrb.h"
|
||||||
|
|
||||||
|
/* Memory set and copy functions */
|
||||||
|
#define BUF_MEMSET memset
|
||||||
|
#define BUF_MEMCPY memcpy
|
||||||
|
|
||||||
|
#if LWRB_USE_MAGIC
|
||||||
|
#define BUF_IS_VALID(b) ((b) != NULL && (b)->magic1 == 0xDEADBEEF && (b)->magic2 == ~0xDEADBEEF && (b)->buff != NULL && (b)->size > 0)
|
||||||
|
#else
|
||||||
|
#define BUF_IS_VALID(b) ((b) != NULL && (b)->buff != NULL && (b)->size > 0)
|
||||||
|
#endif /* LWRB_USE_MAGIC */
|
||||||
|
#define BUF_MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
#define BUF_MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||||
|
#define BUF_SEND_EVT(b, type, bp) do { if ((b)->evt_fn != NULL) { (b)->evt_fn((b), (type), (bp)); } } while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Initialize buffer handle to default values with size and buffer data array
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \param[in] buffdata: Pointer to memory to use as buffer data
|
||||||
|
* \param[in] size: Size of `buffdata` in units of bytes
|
||||||
|
* Maximum number of bytes buffer can hold is `size - 1`
|
||||||
|
* \return `1` on success, `0` otherwise
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
lwrb_init(LWRB_VOLATILE lwrb_t* buff, void* buffdata, size_t size) {
|
||||||
|
if (buff == NULL || buffdata == NULL || size == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BUF_MEMSET((void*)buff, 0x00, sizeof(*buff));
|
||||||
|
|
||||||
|
buff->size = size;
|
||||||
|
buff->buff = buffdata;
|
||||||
|
|
||||||
|
#if LWRB_USE_MAGIC
|
||||||
|
buff->magic1 = 0xDEADBEEF;
|
||||||
|
buff->magic2 = ~0xDEADBEEF;
|
||||||
|
#endif /* LWRB_USE_MAGIC */
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check if buff is initialized and ready to use
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \return `1` if ready, `0` otherwise
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
lwrb_is_ready(LWRB_VOLATILE lwrb_t* buff) {
|
||||||
|
return BUF_IS_VALID(buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Free buffer memory
|
||||||
|
* \note Since implementation does not use dynamic allocation,
|
||||||
|
* it just sets buffer handle to `NULL`
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lwrb_free(LWRB_VOLATILE lwrb_t* buff) {
|
||||||
|
if (BUF_IS_VALID(buff)) {
|
||||||
|
buff->buff = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Set event function callback for different buffer operations
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \param[in] evt_fn: Callback function
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lwrb_set_evt_fn(LWRB_VOLATILE lwrb_t* buff, lwrb_evt_fn evt_fn) {
|
||||||
|
if (BUF_IS_VALID(buff)) {
|
||||||
|
buff->evt_fn = evt_fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write data to buffer.
|
||||||
|
* Copies data from `data` array to buffer and marks buffer as full for maximum `btw` number of bytes
|
||||||
|
*
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \param[in] data: Pointer to data to write into buffer
|
||||||
|
* \param[in] btw: Number of bytes to write
|
||||||
|
* \return Number of bytes written to buffer.
|
||||||
|
* When returned value is less than `btw`, there was no enough memory available
|
||||||
|
* to copy full data array
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
lwrb_write(LWRB_VOLATILE lwrb_t* buff, const void* data, size_t btw) {
|
||||||
|
size_t tocopy, free;
|
||||||
|
const uint8_t* d = data;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || data == NULL || btw == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate maximum number of bytes available to write */
|
||||||
|
free = lwrb_get_free(buff);
|
||||||
|
btw = BUF_MIN(free, btw);
|
||||||
|
if (btw == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 1: Write data to linear part of buffer */
|
||||||
|
tocopy = BUF_MIN(buff->size - buff->w, btw);
|
||||||
|
BUF_MEMCPY(&buff->buff[buff->w], d, tocopy);
|
||||||
|
buff->w += tocopy;
|
||||||
|
btw -= tocopy;
|
||||||
|
|
||||||
|
/* Step 2: Write data to beginning of buffer (overflow part) */
|
||||||
|
if (btw > 0) {
|
||||||
|
BUF_MEMCPY(buff->buff, &d[tocopy], btw);
|
||||||
|
buff->w = btw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 3: Check end of buffer */
|
||||||
|
if (buff->w >= buff->size) {
|
||||||
|
buff->w = 0;
|
||||||
|
}
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_WRITE, tocopy + btw);
|
||||||
|
return tocopy + btw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read data from buffer.
|
||||||
|
* Copies data from buffer to `data` array and marks buffer as free for maximum `btr` number of bytes
|
||||||
|
*
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \param[out] data: Pointer to output memory to copy buffer data to
|
||||||
|
* \param[in] btr: Number of bytes to read
|
||||||
|
* \return Number of bytes read and copied to data array
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
lwrb_read(LWRB_VOLATILE lwrb_t* buff, void* data, size_t btr) {
|
||||||
|
size_t tocopy, full;
|
||||||
|
uint8_t* d = data;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || data == NULL || btr == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate maximum number of bytes available to read */
|
||||||
|
full = lwrb_get_full(buff);
|
||||||
|
btr = BUF_MIN(full, btr);
|
||||||
|
if (btr == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 1: Read data from linear part of buffer */
|
||||||
|
tocopy = BUF_MIN(buff->size - buff->r, btr);
|
||||||
|
BUF_MEMCPY(d, &buff->buff[buff->r], tocopy);
|
||||||
|
buff->r += tocopy;
|
||||||
|
btr -= tocopy;
|
||||||
|
|
||||||
|
/* Step 2: Read data from beginning of buffer (overflow part) */
|
||||||
|
if (btr > 0) {
|
||||||
|
BUF_MEMCPY(&d[tocopy], buff->buff, btr);
|
||||||
|
buff->r = btr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 3: Check end of buffer */
|
||||||
|
if (buff->r >= buff->size) {
|
||||||
|
buff->r = 0;
|
||||||
|
}
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_READ, tocopy + btr);
|
||||||
|
return tocopy + btr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read from buffer without changing read pointer (peek only)
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \param[in] skip_count: Number of bytes to skip before reading data
|
||||||
|
* \param[out] data: Pointer to output memory to copy buffer data to
|
||||||
|
* \param[in] btp: Number of bytes to peek
|
||||||
|
* \return Number of bytes peeked and written to output array
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
lwrb_peek(LWRB_VOLATILE lwrb_t* buff, size_t skip_count, void* data, size_t btp) {
|
||||||
|
size_t full, tocopy, r;
|
||||||
|
uint8_t* d = data;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || data == NULL || btp == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = buff->r;
|
||||||
|
|
||||||
|
/* Calculate maximum number of bytes available to read */
|
||||||
|
full = lwrb_get_full(buff);
|
||||||
|
|
||||||
|
/* Skip beginning of buffer */
|
||||||
|
if (skip_count >= full) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
r += skip_count;
|
||||||
|
full -= skip_count;
|
||||||
|
if (r >= buff->size) {
|
||||||
|
r -= buff->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check maximum number of bytes available to read after skip */
|
||||||
|
btp = BUF_MIN(full, btp);
|
||||||
|
if (btp == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 1: Read data from linear part of buffer */
|
||||||
|
tocopy = BUF_MIN(buff->size - r, btp);
|
||||||
|
BUF_MEMCPY(d, &buff->buff[r], tocopy);
|
||||||
|
btp -= tocopy;
|
||||||
|
|
||||||
|
/* Step 2: Read data from beginning of buffer (overflow part) */
|
||||||
|
if (btp > 0) {
|
||||||
|
BUF_MEMCPY(&d[tocopy], buff->buff, btp);
|
||||||
|
}
|
||||||
|
return tocopy + btp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get available size in buffer for write operation
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \return Number of free bytes in memory
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
lwrb_get_free(LWRB_VOLATILE lwrb_t* buff) {
|
||||||
|
size_t size, w, r;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use temporary values in case they are changed during operations */
|
||||||
|
w = buff->w;
|
||||||
|
r = buff->r;
|
||||||
|
if (w == r) {
|
||||||
|
size = buff->size;
|
||||||
|
} else if (r > w) {
|
||||||
|
size = r - w;
|
||||||
|
} else {
|
||||||
|
size = buff->size - (w - r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buffer free size is always 1 less than actual size */
|
||||||
|
return size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get number of bytes currently available in buffer
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \return Number of bytes ready to be read
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
lwrb_get_full(LWRB_VOLATILE lwrb_t* buff) {
|
||||||
|
size_t w, r, size;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use temporary values in case they are changed during operations */
|
||||||
|
w = buff->w;
|
||||||
|
r = buff->r;
|
||||||
|
if (w == r) {
|
||||||
|
size = 0;
|
||||||
|
} else if (w > r) {
|
||||||
|
size = w - r;
|
||||||
|
} else {
|
||||||
|
size = buff->size - (r - w);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resets buffer to default values. Buffer size is not modified
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lwrb_reset(LWRB_VOLATILE lwrb_t* buff) {
|
||||||
|
if (BUF_IS_VALID(buff)) {
|
||||||
|
buff->w = 0;
|
||||||
|
buff->r = 0;
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_RESET, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get linear address for buffer for fast read
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \return Linear buffer start address
|
||||||
|
*/
|
||||||
|
void*
|
||||||
|
lwrb_get_linear_block_read_address(LWRB_VOLATILE lwrb_t* buff) {
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &buff->buff[buff->r];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get length of linear block address before it overflows for read operation
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \return Linear buffer size in units of bytes for read operation
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
lwrb_get_linear_block_read_length(LWRB_VOLATILE lwrb_t* buff) {
|
||||||
|
size_t w, r, len;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use temporary values in case they are changed during operations */
|
||||||
|
w = buff->w;
|
||||||
|
r = buff->r;
|
||||||
|
if (w > r) {
|
||||||
|
len = w - r;
|
||||||
|
} else if (r > w) {
|
||||||
|
len = buff->size - r;
|
||||||
|
} else {
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Skip (ignore; advance read pointer) buffer data
|
||||||
|
* Marks data as read in the buffer and increases free memory for up to `len` bytes
|
||||||
|
*
|
||||||
|
* \note Useful at the end of streaming transfer such as DMA
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \param[in] len: Number of bytes to skip and mark as read
|
||||||
|
* \return Number of bytes skipped
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
lwrb_skip(LWRB_VOLATILE lwrb_t* buff, size_t len) {
|
||||||
|
size_t full;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
full = lwrb_get_full(buff);
|
||||||
|
len = BUF_MIN(len, full);
|
||||||
|
buff->r += len;
|
||||||
|
if (buff->r >= buff->size) {
|
||||||
|
buff->r -= buff->size;
|
||||||
|
}
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_READ, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get linear address for buffer for fast read
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \return Linear buffer start address
|
||||||
|
*/
|
||||||
|
void*
|
||||||
|
lwrb_get_linear_block_write_address(LWRB_VOLATILE lwrb_t* buff) {
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &buff->buff[buff->w];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get length of linear block address before it overflows for write operation
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \return Linear buffer size in units of bytes for write operation
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
lwrb_get_linear_block_write_length(LWRB_VOLATILE lwrb_t* buff) {
|
||||||
|
size_t w, r, len;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use temporary values in case they are changed during operations */
|
||||||
|
w = buff->w;
|
||||||
|
r = buff->r;
|
||||||
|
if (w >= r) {
|
||||||
|
len = buff->size - w;
|
||||||
|
/*
|
||||||
|
* When read pointer is 0,
|
||||||
|
* maximal length is one less as if too many bytes
|
||||||
|
* are written, buffer would be considered empty again (r == w)
|
||||||
|
*/
|
||||||
|
if (r == 0) {
|
||||||
|
/*
|
||||||
|
* Cannot overflow:
|
||||||
|
* - If r is not 0, statement does not get called
|
||||||
|
* - buff->size cannot be 0 and if r is 0, len is greater 0
|
||||||
|
*/
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
len = r - w - 1;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Advance write pointer in the buffer.
|
||||||
|
* Similar to skip function but modifies write pointer instead of read
|
||||||
|
*
|
||||||
|
* \note Useful when hardware is writing to buffer and application needs to increase number
|
||||||
|
* of bytes written to buffer by hardware
|
||||||
|
* \param[in] buff: Buffer handle
|
||||||
|
* \param[in] len: Number of bytes to advance
|
||||||
|
* \return Number of bytes advanced for write operation
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
lwrb_advance(LWRB_VOLATILE lwrb_t* buff, size_t len) {
|
||||||
|
size_t free;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
free = lwrb_get_free(buff);
|
||||||
|
len = BUF_MIN(len, free);
|
||||||
|
buff->w += len;
|
||||||
|
if (buff->w >= buff->size) {
|
||||||
|
buff->w -= buff->size;
|
||||||
|
}
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_WRITE, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
135
lib/lwrb/lwrb.h
Normal file
135
lib/lwrb/lwrb.h
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* \file lwrb.h
|
||||||
|
* \brief LwRB - Lightweight ring buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Tilen MAJERLE
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation
|
||||||
|
* files (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge,
|
||||||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of LwRB - Lightweight ring buffer library.
|
||||||
|
*
|
||||||
|
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||||
|
* Version: v2.0.0
|
||||||
|
*/
|
||||||
|
#ifndef LWRB_HDR_H
|
||||||
|
#define LWRB_HDR_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup LWRB Lightweight ring buffer manager
|
||||||
|
* \brief Lightweight ring buffer manager
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Enable buffer structure pointer parameter as volatile
|
||||||
|
* To use this feature, uncomment keyword below
|
||||||
|
*/
|
||||||
|
#define LWRB_VOLATILE /* volatile */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds 2 magic words to make sure if memory is corrupted
|
||||||
|
* application can detect wrong data pointer and maximum size
|
||||||
|
*/
|
||||||
|
#define LWRB_USE_MAGIC 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Event type for buffer operations
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LWRB_EVT_READ, /*!< Read event */
|
||||||
|
LWRB_EVT_WRITE, /*!< Write event */
|
||||||
|
LWRB_EVT_RESET, /*!< Reset event */
|
||||||
|
} lwrb_evt_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Buffer structure forward declaration
|
||||||
|
*/
|
||||||
|
struct lwrb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Event callback function type
|
||||||
|
* \param[in] buff: Buffer handle for event
|
||||||
|
* \param[in] evt: Event type
|
||||||
|
* \param[in] bp: Number of bytes written or read (when used), depends on event type
|
||||||
|
*/
|
||||||
|
typedef void (*lwrb_evt_fn)(LWRB_VOLATILE struct lwrb* buff, lwrb_evt_type_t evt, size_t bp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Buffer structure
|
||||||
|
*/
|
||||||
|
typedef struct lwrb {
|
||||||
|
#if LWRB_USE_MAGIC
|
||||||
|
uint32_t magic1; /*!< Magic 1 word */
|
||||||
|
#endif /* LWRB_USE_MAGIC */
|
||||||
|
uint8_t* buff; /*!< Pointer to buffer data.
|
||||||
|
Buffer is considered initialized when `buff != NULL` and `size > 0` */
|
||||||
|
size_t size; /*!< Size of buffer data. Size of actual buffer is `1` byte less than value holds */
|
||||||
|
size_t r; /*!< Next read pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
|
||||||
|
size_t w; /*!< Next write pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
|
||||||
|
lwrb_evt_fn evt_fn; /*!< Pointer to event callback function */
|
||||||
|
#if LWRB_USE_MAGIC
|
||||||
|
uint32_t magic2; /*!< Magic 2 word */
|
||||||
|
#endif /* LWRB_USE_MAGIC */
|
||||||
|
} lwrb_t;
|
||||||
|
|
||||||
|
uint8_t lwrb_init(LWRB_VOLATILE lwrb_t* buff, void* buffdata, size_t size);
|
||||||
|
uint8_t lwrb_is_ready(LWRB_VOLATILE lwrb_t* buff);
|
||||||
|
void lwrb_free(LWRB_VOLATILE lwrb_t* buff);
|
||||||
|
void lwrb_reset(LWRB_VOLATILE lwrb_t* buff);
|
||||||
|
void lwrb_set_evt_fn(LWRB_VOLATILE lwrb_t* buff, lwrb_evt_fn fn);
|
||||||
|
|
||||||
|
/* Read/Write functions */
|
||||||
|
size_t lwrb_write(LWRB_VOLATILE lwrb_t* buff, const void* data, size_t btw);
|
||||||
|
size_t lwrb_read(LWRB_VOLATILE lwrb_t* buff, void* data, size_t btr);
|
||||||
|
size_t lwrb_peek(LWRB_VOLATILE lwrb_t* buff, size_t skip_count, void* data, size_t btp);
|
||||||
|
|
||||||
|
/* Buffer size information */
|
||||||
|
size_t lwrb_get_free(LWRB_VOLATILE lwrb_t* buff);
|
||||||
|
size_t lwrb_get_full(LWRB_VOLATILE lwrb_t* buff);
|
||||||
|
|
||||||
|
/* Read data block management */
|
||||||
|
void* lwrb_get_linear_block_read_address(LWRB_VOLATILE lwrb_t* buff);
|
||||||
|
size_t lwrb_get_linear_block_read_length(LWRB_VOLATILE lwrb_t* buff);
|
||||||
|
size_t lwrb_skip(LWRB_VOLATILE lwrb_t* buff, size_t len);
|
||||||
|
|
||||||
|
/* Write data block management */
|
||||||
|
void* lwrb_get_linear_block_write_address(LWRB_VOLATILE lwrb_t* buff);
|
||||||
|
size_t lwrb_get_linear_block_write_length(LWRB_VOLATILE lwrb_t* buff);
|
||||||
|
size_t lwrb_advance(LWRB_VOLATILE lwrb_t* buff, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* LWRB_HDR_H */
|
||||||
1
lib/nanopb
Submodule
1
lib/nanopb
Submodule
Submodule lib/nanopb added at 1f0c2e19c6
391
lib/uart_driver/AsyncComm.c
Normal file
391
lib/uart_driver/AsyncComm.c
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
/**
|
||||||
|
* @file CBSP_AsyncComm.c
|
||||||
|
* @author owalter
|
||||||
|
* @brief Continuus receive UART-driver based on circular DMA
|
||||||
|
* @version 0.1
|
||||||
|
* @date 28.11.2022
|
||||||
|
*
|
||||||
|
* driver based on example imlementation of https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx
|
||||||
|
*
|
||||||
|
* @copyright Copyright © 2022 habemus! electronic + transfer GmbH. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AsyncComm.h"
|
||||||
|
|
||||||
|
#include "stm32h7xx_ll_dma.h"
|
||||||
|
#include "stm32h7xx_ll_usart.h"
|
||||||
|
|
||||||
|
#ifndef USE_HAL_DRIVER
|
||||||
|
#include "stm32l4xx_ll_gpio.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void usart_rx_check(const uart_desc_t* uart);
|
||||||
|
void usart_process_data(const uart_desc_t* uart, const void* data, size_t len);
|
||||||
|
void usart_send_string(const uart_desc_t* uart, const char* str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief USART DMA check thread
|
||||||
|
* \param[in] arg: Thread argument
|
||||||
|
*/
|
||||||
|
_Noreturn void usart_rx_dma_thread(void* arg) {
|
||||||
|
uart_desc_t* uart = arg;
|
||||||
|
void* d;
|
||||||
|
|
||||||
|
/* Notify user to start sending data */
|
||||||
|
// usart_send_string(uart, "USART DMA example: DMA HT & TC + USART IDLE LINE IRQ + RTOS processing\r\n");
|
||||||
|
// usart_send_string(uart, "Start sending data to STM32\r\n");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Block thread and wait for event to process USART data */
|
||||||
|
osMessageQueueGet(uart->data->queue, &d, NULL, osWaitForever);
|
||||||
|
|
||||||
|
/* Simply call processing function */
|
||||||
|
usart_rx_check(uart);
|
||||||
|
|
||||||
|
(void)d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check for new data received with DMA
|
||||||
|
*
|
||||||
|
* User must select context to call this function from:
|
||||||
|
* - Only interrupts (DMA HT, DMA TC, UART IDLE) with same preemption priority level
|
||||||
|
* - Only thread context (outside interrupts)
|
||||||
|
*
|
||||||
|
* If called from both context-es, exclusive access protection must be implemented
|
||||||
|
* This mode is not advised as it usually means architecture design problems
|
||||||
|
*
|
||||||
|
* When IDLE interrupt is not present, application must rely only on thread context,
|
||||||
|
* by manually calling function as quickly as possible, to make sure
|
||||||
|
* data are read from raw buffer and processed.
|
||||||
|
*
|
||||||
|
* Not doing reads fast enough may cause DMA to overflow unread received bytes,
|
||||||
|
* hence application will lost useful data.
|
||||||
|
*
|
||||||
|
* Solutions to this are:
|
||||||
|
* - Improve architecture design to achieve faster reads
|
||||||
|
* - Increase raw buffer size and allow DMA to write more data before this function is called
|
||||||
|
*/
|
||||||
|
void usart_rx_check(const uart_desc_t* uart) {
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
/* Calculate current position in buffer and check for new data available */
|
||||||
|
pos = ARRAY_LEN(uart->data->dma_rx_buffer) - LL_DMA_GetDataLength(uart->dma_rx, uart->dma_rx_ch);
|
||||||
|
if (pos != uart->data->old_pos) { /* Check change in received data */
|
||||||
|
if (pos > uart->data->old_pos) { /* Current position is over previous one */
|
||||||
|
/*
|
||||||
|
* Processing is done in "linear" mode.
|
||||||
|
*
|
||||||
|
* Application processing is fast with single data block,
|
||||||
|
* length is simply calculated by subtracting pointers
|
||||||
|
*
|
||||||
|
* [ 0 ]
|
||||||
|
* [ 1 ] <- old_pos |------------------------------------|
|
||||||
|
* [ 2 ] | |
|
||||||
|
* [ 3 ] | Single block (len = pos - old_pos) |
|
||||||
|
* [ 4 ] | |
|
||||||
|
* [ 5 ] |------------------------------------|
|
||||||
|
* [ 6 ] <- pos
|
||||||
|
* [ 7 ]
|
||||||
|
* [ N - 1 ]
|
||||||
|
*/
|
||||||
|
usart_process_data(uart, &uart->data->dma_rx_buffer[uart->data->old_pos], pos - uart->data->old_pos);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Processing is done in "overflow" mode..
|
||||||
|
*
|
||||||
|
* Application must process data twice,
|
||||||
|
* since there are 2 linear memory blocks to handle
|
||||||
|
*
|
||||||
|
* [ 0 ] |---------------------------------|
|
||||||
|
* [ 1 ] | Second block (len = pos) |
|
||||||
|
* [ 2 ] |---------------------------------|
|
||||||
|
* [ 3 ] <- pos
|
||||||
|
* [ 4 ] <- old_pos |---------------------------------|
|
||||||
|
* [ 5 ] | |
|
||||||
|
* [ 6 ] | First block (len = N - old_pos) |
|
||||||
|
* [ 7 ] | |
|
||||||
|
* [ N - 1 ] |---------------------------------|
|
||||||
|
*/
|
||||||
|
usart_process_data(uart, &uart->data->dma_rx_buffer[uart->data->old_pos],
|
||||||
|
ARRAY_LEN(uart->data->dma_rx_buffer) - uart->data->old_pos);
|
||||||
|
if (pos > 0) {
|
||||||
|
usart_process_data(uart, &uart->data->dma_rx_buffer[0], pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uart->data->old_pos = pos; /* Save current position as old for next transfers */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check if DMA is active and if not try to send data
|
||||||
|
* \return `1` if transfer just started, `0` if on-going or no data to transmit
|
||||||
|
*/
|
||||||
|
uint8_t usart_start_tx_dma_transfer(const uart_desc_t* uart) {
|
||||||
|
uint32_t primask;
|
||||||
|
uint8_t started = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First check if transfer is currently in-active,
|
||||||
|
* by examining the value of usart_tx_dma_current_len variable.
|
||||||
|
*
|
||||||
|
* This variable is set before DMA transfer is started and cleared in DMA TX complete interrupt.
|
||||||
|
*
|
||||||
|
* It is not necessary to disable the interrupts before checking the variable:
|
||||||
|
*
|
||||||
|
* When usart_tx_dma_current_len == 0
|
||||||
|
* - This function is called by either application or TX DMA interrupt
|
||||||
|
* - When called from interrupt, it was just reset before the call,
|
||||||
|
* indicating transfer just completed and ready for more
|
||||||
|
* - When called from an application, transfer was previously already in-active
|
||||||
|
* and immediate call from interrupt cannot happen at this moment
|
||||||
|
*
|
||||||
|
* When usart_tx_dma_current_len != 0
|
||||||
|
* - This function is called only by an application.
|
||||||
|
* - It will never be called from interrupt with usart_tx_dma_current_len != 0 condition
|
||||||
|
*
|
||||||
|
* Disabling interrupts before checking for next transfer is advised
|
||||||
|
* only if multiple operating system threads can access to this function w/o
|
||||||
|
* exclusive access protection (mutex) configured,
|
||||||
|
* or if application calls this function from multiple interrupts.
|
||||||
|
*
|
||||||
|
* This example assumes worst use case scenario,
|
||||||
|
* hence interrupts are disabled prior every check
|
||||||
|
*/
|
||||||
|
primask = __get_PRIMASK();
|
||||||
|
__disable_irq();
|
||||||
|
if (uart->data->tx_dma_current_len == 0 &&
|
||||||
|
(uart->data->tx_dma_current_len = lwrb_get_linear_block_read_length(&uart->data->tx_rb)) > 0) {
|
||||||
|
/* Disable channel if enabled */
|
||||||
|
LL_DMA_DisableStream(uart->dma_tx, uart->dma_tx_ch);
|
||||||
|
|
||||||
|
/* Clear all flags */
|
||||||
|
uart->dma_tx_clear_tc_fn(uart->dma_tx);
|
||||||
|
uart->dma_tx_clear_ht_fn(uart->dma_tx);
|
||||||
|
uart->dma_tx_clear_gi_fn(uart->dma_tx);
|
||||||
|
uart->dma_tx_clear_te_fn(uart->dma_tx);
|
||||||
|
uart->dma_tx_clear_fe_fn(uart->dma_tx);
|
||||||
|
|
||||||
|
/* Prepare DMA data and length */
|
||||||
|
LL_DMA_SetDataLength(uart->dma_tx, uart->dma_tx_ch, uart->data->tx_dma_current_len);
|
||||||
|
LL_DMA_SetMemoryAddress(uart->dma_tx, uart->dma_tx_ch,
|
||||||
|
(uint32_t)lwrb_get_linear_block_read_address(&uart->data->tx_rb));
|
||||||
|
|
||||||
|
/* Start transfer */
|
||||||
|
LL_DMA_EnableStream(uart->dma_tx, uart->dma_tx_ch);
|
||||||
|
uart->data->tx_running = 1;
|
||||||
|
started = 1;
|
||||||
|
}
|
||||||
|
__set_PRIMASK(primask);
|
||||||
|
return started;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Process received data over UART
|
||||||
|
* \note Either process them directly or copy to other bigger buffer
|
||||||
|
* \param[in] data: Data to process
|
||||||
|
* \param[in] len: Length in units of bytes
|
||||||
|
*/
|
||||||
|
void usart_process_data(const uart_desc_t* uart, const void* data, size_t len) {
|
||||||
|
/*
|
||||||
|
* This function is called on DMA TC or HT events, and on UART IDLE (if enabled) event.
|
||||||
|
*/
|
||||||
|
uart->uart_external_receive_callback(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Send data over UART
|
||||||
|
* \note copy data into ringbuffer and trigger UART transmission
|
||||||
|
* \param[in] data: Data to process
|
||||||
|
* \param[in] len: Length in units of bytes
|
||||||
|
*/
|
||||||
|
void usart_send_data(const uart_desc_t* uart, const void* data, size_t len) {
|
||||||
|
lwrb_write(&uart->data->tx_rb, data, len); /* Write data to TX buffer */
|
||||||
|
usart_start_tx_dma_transfer(uart); /* Then try to start transfer */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Send string to USART
|
||||||
|
* \param[in] str: String to send
|
||||||
|
*/
|
||||||
|
void usart_send_string(const uart_desc_t* uart, const char* str) {
|
||||||
|
lwrb_write(&uart->data->tx_rb, str, strnlen(str, 255)); /* Write data to TX buffer for loopback */
|
||||||
|
usart_start_tx_dma_transfer(uart); /* Then try to start transfer */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief USART DMA Initialization Function
|
||||||
|
* other init is handled by MX_HAL
|
||||||
|
*/
|
||||||
|
osThreadId_t usart_init(const uart_desc_t* uart, const osThreadAttr_t* attr) {
|
||||||
|
|
||||||
|
#ifndef USE_HAL_DRIVER
|
||||||
|
LL_USART_InitTypeDef USART_InitStruct = { 0 };
|
||||||
|
LL_GPIO_InitTypeDef GPIO_InitStruct = { 0 };
|
||||||
|
|
||||||
|
/* USART GPIO Configuration */
|
||||||
|
/* RX pin */
|
||||||
|
GPIO_InitStruct.Pin = uart->uart_rx_pin;
|
||||||
|
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||||
|
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
|
||||||
|
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||||
|
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||||||
|
GPIO_InitStruct.Alternate = uart->uart_rx_pin_af;
|
||||||
|
LL_GPIO_Init(uart->uart_rx_port, &GPIO_InitStruct);
|
||||||
|
|
||||||
|
/* Optional TX pin */
|
||||||
|
if (uart->uart_tx_port != NULL) {
|
||||||
|
GPIO_InitStruct.Pin = uart->uart_tx_pin;
|
||||||
|
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||||
|
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
|
||||||
|
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||||
|
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||||||
|
GPIO_InitStruct.Alternate = uart->uart_tx_pin_af;
|
||||||
|
LL_GPIO_Init(uart->uart_tx_port, &GPIO_InitStruct);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* USART RX DMA init */
|
||||||
|
#ifndef USE_HAL_DRIVER
|
||||||
|
LL_DMA_SetPeriphRequest(uart->dma_rx, uart->dma_rx_ch, uart->dma_rx, uart->dma_rx_req);
|
||||||
|
LL_DMA_SetDataTransferDirection(uart->dma_rx, uart->dma_rx_ch, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
|
||||||
|
LL_DMA_SetChannelPriorityLevel(uart->dma_rx, uart->dma_rx_ch, LL_DMA_PRIORITY_LOW);
|
||||||
|
LL_DMA_SetMode(uart->dma_rx, uart->dma_rx_ch, LL_DMA_MODE_NORMAL);
|
||||||
|
LL_DMA_SetPeriphIncMode(uart->dma_rx, uart->dma_rx_ch, LL_DMA_PERIPH_NOINCREMENT);
|
||||||
|
LL_DMA_SetMemoryIncMode(uart->dma_rx, uart->dma_rx_ch, LL_DMA_MEMORY_INCREMENT);
|
||||||
|
LL_DMA_SetPeriphSize(uart->dma_rx, uart->dma_rx_ch, LL_DMA_PDATAALIGN_BYTE);
|
||||||
|
LL_DMA_SetMemorySize(uart->dma_rx, uart->dma_rx_ch, LL_DMA_MDATAALIGN_BYTE);
|
||||||
|
#endif
|
||||||
|
LL_DMA_SetPeriphAddress(uart->dma_rx, uart->dma_rx_ch,
|
||||||
|
LL_USART_DMA_GetRegAddr(uart->uart, LL_USART_DMA_REG_DATA_RECEIVE));
|
||||||
|
LL_DMA_SetMemoryAddress(uart->dma_rx, uart->dma_rx_ch, (uint32_t)uart->data->dma_rx_buffer);
|
||||||
|
LL_DMA_SetDataLength(uart->dma_rx, uart->dma_rx_ch, ARRAY_LEN(uart->data->dma_rx_buffer));
|
||||||
|
|
||||||
|
/* USART TX DMA init */
|
||||||
|
#ifndef USE_HAL_DRIVER
|
||||||
|
LL_DMA_SetPeriphRequest(uart->dma_tx, uart->dma_tx_ch, uart->dma_tx, uart->dma_tx_req);
|
||||||
|
LL_DMA_SetDataTransferDirection(uart->dma_tx, uart->dma_tx_ch, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
|
||||||
|
LL_DMA_SetChannelPriorityLevel(uart->dma_tx, uart->dma_tx_ch, LL_DMA_PRIORITY_LOW);
|
||||||
|
LL_DMA_SetMode(uart->dma_tx, uart->dma_tx_ch, LL_DMA_MODE_NORMAL);
|
||||||
|
LL_DMA_SetPeriphIncMode(uart->dma_tx, uart->dma_tx_ch, LL_DMA_PERIPH_NOINCREMENT);
|
||||||
|
LL_DMA_SetMemoryIncMode(uart->dma_tx, uart->dma_tx_ch, LL_DMA_MEMORY_INCREMENT);
|
||||||
|
LL_DMA_SetPeriphSize(uart->dma_tx, uart->dma_tx_ch, LL_DMA_PDATAALIGN_BYTE);
|
||||||
|
LL_DMA_SetMemorySize(uart->dma_tx, uart->dma_tx_ch, LL_DMA_MDATAALIGN_BYTE);
|
||||||
|
#endif
|
||||||
|
LL_DMA_SetPeriphAddress(uart->dma_tx, uart->dma_tx_ch,
|
||||||
|
LL_USART_DMA_GetRegAddr(uart->uart, LL_USART_DMA_REG_DATA_TRANSMIT));
|
||||||
|
|
||||||
|
/* Enable HT & TC interrupts */
|
||||||
|
LL_DMA_EnableIT_HT(uart->dma_rx, uart->dma_rx_ch);
|
||||||
|
LL_DMA_EnableIT_TC(uart->dma_rx, uart->dma_rx_ch);
|
||||||
|
|
||||||
|
/* Enable TC interrupts for TX */
|
||||||
|
LL_DMA_EnableIT_TC(uart->dma_tx, uart->dma_tx_ch);
|
||||||
|
|
||||||
|
|
||||||
|
/* DMA interrupt init */
|
||||||
|
#ifndef USE_HAL_DRIVER
|
||||||
|
NVIC_SetPriority(uart->dma_irq_rx, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), uart->prio, 0));
|
||||||
|
NVIC_EnableIRQ(uart->dma_irq_rx);
|
||||||
|
NVIC_SetPriority(uart->dma_irq_tx, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), uart->prio, 0));
|
||||||
|
NVIC_EnableIRQ(uart->dma_irq_tx);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* USART configuration */
|
||||||
|
#ifndef USE_HAL_DRIVER
|
||||||
|
USART_InitStruct.BaudRate = uart->uart_baudrate;
|
||||||
|
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
|
||||||
|
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
|
||||||
|
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
|
||||||
|
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
|
||||||
|
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
|
||||||
|
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
|
||||||
|
LL_USART_Init(uart->uart, &USART_InitStruct);
|
||||||
|
LL_USART_ConfigAsyncMode(uart->uart);
|
||||||
|
#endif
|
||||||
|
LL_USART_Disable(uart->uart);
|
||||||
|
LL_USART_EnableDMAReq_RX(uart->uart);
|
||||||
|
LL_USART_EnableDMAReq_TX(uart->uart);
|
||||||
|
LL_USART_EnableIT_IDLE(uart->uart);
|
||||||
|
|
||||||
|
/* USART interrupt */
|
||||||
|
#ifndef USE_HAL_DRIVER
|
||||||
|
NVIC_SetPriority(uart->uart_irq, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), uart->prio, 1));
|
||||||
|
NVIC_EnableIRQ(uart->uart_irq);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Create message queue before enabling UART or DMA */
|
||||||
|
/* This is to make sure message queue is ready before UART/DMA interrupts are enabled */
|
||||||
|
uart->data->queue = osMessageQueueNew(10, sizeof(void*), NULL);
|
||||||
|
|
||||||
|
/*init tx ringbuffer*/
|
||||||
|
lwrb_init(&uart->data->tx_rb, uart->data->tx_rb_data, sizeof(uart->data->tx_rb_data));
|
||||||
|
|
||||||
|
/* Enable USART and DMA */
|
||||||
|
LL_DMA_EnableStream(uart->dma_rx, uart->dma_rx_ch);
|
||||||
|
LL_USART_Enable(uart->uart);
|
||||||
|
|
||||||
|
/* Create new thread for USART RX DMA processing */
|
||||||
|
return osThreadNew(usart_rx_dma_thread, (void*)uart, attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief General purpose DMA interrupt handler
|
||||||
|
* \note Function must be called from DMA interrupt
|
||||||
|
*
|
||||||
|
* It handles half-transfer and transfer-cmakecomplete interrupts and does the job accordingly
|
||||||
|
*
|
||||||
|
* must be called from the IT function with the correct context pointer
|
||||||
|
*
|
||||||
|
* \param[in] uart: Uart description to handle
|
||||||
|
*/
|
||||||
|
void usart_dma_irq_handler(const uart_desc_t* uart) {
|
||||||
|
void* d = (void*)1;
|
||||||
|
|
||||||
|
/* Check half-transfer complete interrupt */
|
||||||
|
if (LL_DMA_IsEnabledIT_HT(uart->dma_rx, uart->dma_rx_ch) && uart->dma_rx_is_ht_fn(uart->dma_rx)) {
|
||||||
|
uart->dma_rx_clear_ht_fn(uart->dma_rx); /* Clear half-transfer complete flag */
|
||||||
|
osMessageQueuePut(uart->data->queue, &d, 0, 0); /* Write data to queue. Do not use wait function! */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check transfer-complete interrupt */
|
||||||
|
if (LL_DMA_IsEnabledIT_TC(uart->dma_rx, uart->dma_rx_ch) && uart->dma_rx_is_tc_fn(uart->dma_rx)) {
|
||||||
|
uart->dma_rx_clear_tc_fn(uart->dma_rx); /* Clear transfer complete flag */
|
||||||
|
osMessageQueuePut(uart->data->queue, &d, 0, 0); /* Write data to queue. Do not use wait function! */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// must be called from the IT function with the correct context pointer
|
||||||
|
void usart_dma_irq_handler_tx(const uart_desc_t* uart) {
|
||||||
|
/* Check transfer-complete interrupt */
|
||||||
|
if (LL_DMA_IsEnabledIT_TC(uart->dma_tx, uart->dma_tx_ch) && uart->dma_tx_is_tc_fn(uart->dma_tx)) {
|
||||||
|
uart->dma_tx_clear_tc_fn(uart->dma_tx);
|
||||||
|
/* Clear transfer complete flag */
|
||||||
|
lwrb_skip(&uart->data->tx_rb,
|
||||||
|
uart->data->tx_dma_current_len); /* Skip buffer, it has been successfully sent out */
|
||||||
|
uart->data->tx_dma_current_len = 0; /* Reset data length */
|
||||||
|
uart->data->tx_running = 0;
|
||||||
|
usart_start_tx_dma_transfer(uart); /* Start new transfer */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief General purpose UART interrupt handler
|
||||||
|
* \note Function must be called from UART interrupt
|
||||||
|
*
|
||||||
|
* It handles IDLE line detection interrupt and does the job accordingly
|
||||||
|
*
|
||||||
|
* must be called from the IT function with the correct context pointer
|
||||||
|
*
|
||||||
|
* \param[in] uart: Uart description to handle
|
||||||
|
*/
|
||||||
|
void usart_irq_handler(const uart_desc_t* uart) {
|
||||||
|
void* d = (void*)1;
|
||||||
|
|
||||||
|
/* Check for IDLE line interrupt */
|
||||||
|
if (LL_USART_IsEnabledIT_IDLE(uart->uart) && LL_USART_IsActiveFlag_IDLE(uart->uart)) {
|
||||||
|
LL_USART_ClearFlag_IDLE(uart->uart); /* Clear IDLE line flag */
|
||||||
|
osMessageQueuePut(uart->data->queue, &d, 0, 0); /* Write data to queue. Do not use wait function! */
|
||||||
|
}
|
||||||
|
}
|
||||||
108
lib/uart_driver/AsyncComm.h
Normal file
108
lib/uart_driver/AsyncComm.h
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/**
|
||||||
|
* @file CBSP_AsyncComm.h
|
||||||
|
* @author owalter
|
||||||
|
* @brief
|
||||||
|
* @version 0.1
|
||||||
|
|
||||||
|
* @date 28.11.2022
|
||||||
|
*
|
||||||
|
* @copyright Copyright © 2022 habemus! electronic + transfer GmbH. All rights reserved.
|
||||||
|
*/
|
||||||
|
#ifndef NUN10K22_DISPLAY_CBSP_ASYNCCOMM_H
|
||||||
|
#define NUN10K22_DISPLAY_CBSP_ASYNCCOMM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "cmsis_os.h"
|
||||||
|
#include "lwrb.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
typedef void (*data_receive_callback)(const void* data, size_t len);
|
||||||
|
typedef void (*char_receive_callback)(const char* str, size_t len);
|
||||||
|
typedef void (*bytes_receive_callback)(const uint8_t* str, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Volatile data structure
|
||||||
|
*
|
||||||
|
* Variables declared using this structure can not be put to non-volatile memory (such as flash, EEPROM, ..)
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/* OS queue */
|
||||||
|
osMessageQueueId_t queue; /*!< Message queue */
|
||||||
|
|
||||||
|
/* Raw data buffer */
|
||||||
|
uint8_t dma_rx_buffer[1024]; /*!< DMA buffer for receive data */
|
||||||
|
size_t old_pos; /*!< Position for data */
|
||||||
|
|
||||||
|
/* Tx data buffer */
|
||||||
|
volatile size_t tx_dma_current_len;
|
||||||
|
volatile uint8_t tx_running;
|
||||||
|
lwrb_t tx_rb;
|
||||||
|
uint8_t tx_rb_data[512];
|
||||||
|
} uart_desc_volatile_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Non-Volatile data structure
|
||||||
|
*
|
||||||
|
* Variables declared using this structure may be put to non-volative memory.
|
||||||
|
* This is to avoid using RAM for constant data
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/* UART config */
|
||||||
|
USART_TypeDef* uart; /*!< UART/USART/LPUART instance */
|
||||||
|
#ifndef USE_HAL_DRIVER
|
||||||
|
GPIO_TypeDef* uart_tx_port;
|
||||||
|
GPIO_TypeDef* uart_rx_port;
|
||||||
|
uint16_t uart_tx_pin;
|
||||||
|
uint16_t uart_rx_pin;
|
||||||
|
uint16_t uart_tx_pin_af;
|
||||||
|
uint16_t uart_rx_pin_af;
|
||||||
|
|
||||||
|
/* Interrupts config */
|
||||||
|
uint8_t prio; /*!< Preemption priority number */
|
||||||
|
IRQn_Type uart_irq; /*!< UART IRQ instance */
|
||||||
|
IRQn_Type dma_irq_rx; /*!< DMA IRQ RX instance */
|
||||||
|
IRQn_Type dma_irq_tx; /*!< DMA IRQ TX instance */
|
||||||
|
|
||||||
|
/* DMA config & flags management */
|
||||||
|
uint32_t dma_rx_req; /*!< RX DMA request */
|
||||||
|
uint32_t dma_tx_req; /*!< TX DMA request */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DMA_TypeDef* dma_rx; /*!< RX DMA instance */
|
||||||
|
uint32_t dma_rx_ch; /*!< RX DMA channel */
|
||||||
|
void (*dma_rx_clear_tc_fn)(DMA_TypeDef*);
|
||||||
|
void (*dma_rx_clear_ht_fn)(DMA_TypeDef*);
|
||||||
|
uint32_t (*dma_rx_is_tc_fn)(DMA_TypeDef*);
|
||||||
|
uint32_t (*dma_rx_is_ht_fn)(DMA_TypeDef*);
|
||||||
|
|
||||||
|
DMA_TypeDef* dma_tx; /*!< TX DMA instance */
|
||||||
|
uint32_t dma_tx_ch; /*!< TX DMA channel */
|
||||||
|
void (*dma_tx_clear_tc_fn)(DMA_TypeDef*);
|
||||||
|
uint32_t (*dma_tx_is_tc_fn)(DMA_TypeDef*);
|
||||||
|
void (*dma_tx_clear_ht_fn)(DMA_TypeDef*);
|
||||||
|
void (*dma_tx_clear_gi_fn)(DMA_TypeDef*);
|
||||||
|
void (*dma_tx_clear_te_fn)(DMA_TypeDef*);
|
||||||
|
void (*dma_tx_clear_fe_fn)(DMA_TypeDef*);
|
||||||
|
|
||||||
|
uart_desc_volatile_t* data; /*!< Pointer to volatile data */
|
||||||
|
|
||||||
|
/* receive callback */
|
||||||
|
data_receive_callback uart_external_receive_callback;
|
||||||
|
|
||||||
|
} uart_desc_t;
|
||||||
|
|
||||||
|
/* USART related functions */
|
||||||
|
osThreadId_t usart_init(const uart_desc_t* uart, const osThreadAttr_t* attr);
|
||||||
|
void usart_dma_irq_handler(const uart_desc_t* uart);
|
||||||
|
void usart_dma_irq_handler_tx(const uart_desc_t* uart);
|
||||||
|
void usart_irq_handler(const uart_desc_t* uart);
|
||||||
|
void usart_send_data(const uart_desc_t* uart, const void* data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Calculate length of statically allocated array
|
||||||
|
*/
|
||||||
|
#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
|
||||||
|
#endif // NUN10K22_DISPLAY_CBSP_ASYNCCOMM_H
|
||||||
5
lib/uart_driver/AsyncFrames.c
Normal file
5
lib/uart_driver/AsyncFrames.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by oliver on 12/8/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AsyncFrames.h"
|
||||||
17
lib/uart_driver/AsyncFrames.h
Normal file
17
lib/uart_driver/AsyncFrames.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// Created by oliver on 12/8/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef MASTERCOMMISION_ASYNCFAMES_H
|
||||||
|
#define MASTERCOMMISION_ASYNCFAMES_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "stdint.h"
|
||||||
|
|
||||||
|
typedef struct AsyncFrameHeader_s {
|
||||||
|
uint16_t sof;
|
||||||
|
uint16_t leng;
|
||||||
|
} AsyncFrameHeader_t;
|
||||||
|
|
||||||
|
|
||||||
|
#endif //MASTERCOMMISION_ASYNCFAMES_H
|
||||||
20
lib/uart_driver/CMakeLists.txt
Normal file
20
lib/uart_driver/CMakeLists.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
|
project(uart_driver C)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} "")
|
||||||
|
target_sources(${PROJECT_NAME}
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/AsyncComm.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/AsyncFrames.c
|
||||||
|
INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/AsyncComm.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/AsyncFrames.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME}
|
||||||
|
INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC lwrb)
|
||||||
546
lib/uart_driver/main.c
Normal file
546
lib/uart_driver/main.c
Normal file
@@ -0,0 +1,546 @@
|
|||||||
|
/*
|
||||||
|
* This example shows how application can implement RX and TX DMA for UART.
|
||||||
|
* It uses simple packet example approach and 3 separate buffers:
|
||||||
|
*
|
||||||
|
* - Raw DMA RX buffer where DMA transfers data from UART to memory
|
||||||
|
* - Ringbuff for RX data which are transfered from raw buffer and ready for application processin
|
||||||
|
* - Ringbuff for TX data to be sent out by DMA TX
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
#include "main.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "lwrb.h"
|
||||||
|
|
||||||
|
/* Private function prototypes */
|
||||||
|
void SystemClock_Config(void);
|
||||||
|
|
||||||
|
/* USART related functions */
|
||||||
|
void usart_init(void);
|
||||||
|
void usart_rx_check(void);
|
||||||
|
void usart_process_data(const void* data, size_t len);
|
||||||
|
void usart_send_string(const char* str);
|
||||||
|
uint8_t usart_start_tx_dma_transfer(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Calculate length of statically allocated array
|
||||||
|
*/
|
||||||
|
#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief USART RX buffer for DMA to transfer every received byte RX
|
||||||
|
* \note Contains raw data that are about to be processed by different events
|
||||||
|
*
|
||||||
|
* Special use case for STM32H7 series.
|
||||||
|
* Default memory configuration in STM32H7 may put variables to DTCM RAM,
|
||||||
|
* part of memory that is super fast, however DMA has no access to it.
|
||||||
|
*
|
||||||
|
* For this specific example, all variables are by default
|
||||||
|
* configured in D1 RAM. This is configured in linker script
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
usart_rx_dma_buffer[64];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Ring buffer instance for TX data
|
||||||
|
*/
|
||||||
|
lwrb_t
|
||||||
|
usart_rx_rb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Ring buffer data array for RX DMA
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
usart_rx_rb_data[128];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Ring buffer instance for TX data
|
||||||
|
*/
|
||||||
|
lwrb_t
|
||||||
|
usart_tx_rb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Ring buffer data array for TX DMA
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
usart_tx_rb_data[128];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Length of currently active TX DMA transfer
|
||||||
|
*/
|
||||||
|
volatile size_t
|
||||||
|
usart_tx_dma_current_len;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Application entry point
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
main(void) {
|
||||||
|
uint8_t state, cmd, len;
|
||||||
|
|
||||||
|
/* MCU Configuration */
|
||||||
|
// SCB_DisableDCache();
|
||||||
|
// SCB_DisableICache();
|
||||||
|
|
||||||
|
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
|
||||||
|
LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_SYSCFG);
|
||||||
|
|
||||||
|
/* Configure the system clock */
|
||||||
|
SystemClock_Config();
|
||||||
|
|
||||||
|
/* Initialize ringbuff for TX & RX */
|
||||||
|
lwrb_init(&usart_tx_rb, usart_tx_rb_data, sizeof(usart_tx_rb_data));
|
||||||
|
lwrb_init(&usart_rx_rb, usart_rx_rb_data, sizeof(usart_rx_rb_data));
|
||||||
|
|
||||||
|
/* Initialize all configured peripherals */
|
||||||
|
usart_init();
|
||||||
|
usart_send_string("USART DMA example: DMA HT & TC + USART IDLE LINE interrupts\r\n");
|
||||||
|
usart_send_string("Start sending data to STM32\r\n");
|
||||||
|
|
||||||
|
/* After this point, do not use usart_send_string function anymore */
|
||||||
|
/* Send packet data over UART from PC (or other STM32 device) */
|
||||||
|
|
||||||
|
/* Infinite loop */
|
||||||
|
state = 0;
|
||||||
|
while (1) {
|
||||||
|
uint8_t b;
|
||||||
|
|
||||||
|
/* Process RX ringbuffer */
|
||||||
|
|
||||||
|
/* Packet format: START_BYTE, CMD, LEN[, DATA[0], DATA[len - 1]], STOP BYTE */
|
||||||
|
/* DATA bytes are included only if LEN > 0 */
|
||||||
|
/* An example, send sequence of these bytes: 0x55, 0x01, 0x01, 0xFF, 0xAA */
|
||||||
|
|
||||||
|
/* Read byte by byte */
|
||||||
|
|
||||||
|
if (lwrb_read(&usart_rx_rb, &b, 1) == 1) {
|
||||||
|
lwrb_write(&usart_tx_rb, &b, 1); /* Write data to transmit buffer */
|
||||||
|
usart_start_tx_dma_transfer();
|
||||||
|
switch (state) {
|
||||||
|
case 0: { /* Wait for start byte */
|
||||||
|
if (b == 0x55) {
|
||||||
|
++state;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: { /* Check packet command */
|
||||||
|
cmd = b;
|
||||||
|
++state;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: { /* Packet data length */
|
||||||
|
len = b;
|
||||||
|
++state;
|
||||||
|
if (len == 0) {
|
||||||
|
++state; /* Ignore data part if len = 0 */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: { /* Data for command */
|
||||||
|
--len; /* Decrease for received character */
|
||||||
|
if (len == 0) {
|
||||||
|
++state;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: { /* End of packet */
|
||||||
|
if (b == 0xAA) {
|
||||||
|
/* Packet is valid */
|
||||||
|
|
||||||
|
/* Send out response with CMD = 0xFF */
|
||||||
|
b = 0x55; /* Start byte */
|
||||||
|
lwrb_write(&usart_tx_rb, &b, 1);
|
||||||
|
cmd = 0xFF; /* Command = 0xFF = OK response */
|
||||||
|
lwrb_write(&usart_tx_rb, &cmd, 1);
|
||||||
|
b = 0x00; /* Len = 0 */
|
||||||
|
lwrb_write(&usart_tx_rb, &b, 1);
|
||||||
|
b = 0xAA; /* Stop byte */
|
||||||
|
lwrb_write(&usart_tx_rb, &b, 1);
|
||||||
|
|
||||||
|
/* Flush everything */
|
||||||
|
usart_start_tx_dma_transfer();
|
||||||
|
}
|
||||||
|
state = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do other tasks ... */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check for new data received with DMA
|
||||||
|
*
|
||||||
|
* User must select context to call this function from:
|
||||||
|
* - Only interrupts (DMA HT, DMA TC, UART IDLE) with same preemption priority level
|
||||||
|
* - Only thread context (outside interrupts)
|
||||||
|
*
|
||||||
|
* If called from both context-es, exclusive access protection must be implemented
|
||||||
|
* This mode is not advised as it usually means architecture design problems
|
||||||
|
*
|
||||||
|
* When IDLE interrupt is not present, application must rely only on thread context,
|
||||||
|
* by manually calling function as quickly as possible, to make sure
|
||||||
|
* data are read from raw buffer and processed.
|
||||||
|
*
|
||||||
|
* Not doing reads fast enough may cause DMA to overflow unread received bytes,
|
||||||
|
* hence application will lost useful data.
|
||||||
|
*
|
||||||
|
* Solutions to this are:
|
||||||
|
* - Improve architecture design to achieve faster reads
|
||||||
|
* - Increase raw buffer size and allow DMA to write more data before this function is called
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
usart_rx_check(void) {
|
||||||
|
static size_t old_pos;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
/* Calculate current position in buffer and check for new data available */
|
||||||
|
pos = ARRAY_LEN(usart_rx_dma_buffer) - LL_DMA_GetDataLength(DMA1, LL_DMA_STREAM_0);
|
||||||
|
if (pos != old_pos) { /* Check change in received data */
|
||||||
|
if (pos > old_pos) { /* Current position is over previous one */
|
||||||
|
/*
|
||||||
|
* Processing is done in "linear" mode.
|
||||||
|
*
|
||||||
|
* Application processing is fast with single data block,
|
||||||
|
* length is simply calculated by subtracting pointers
|
||||||
|
*
|
||||||
|
* [ 0 ]
|
||||||
|
* [ 1 ] <- old_pos |------------------------------------|
|
||||||
|
* [ 2 ] | |
|
||||||
|
* [ 3 ] | Single block (len = pos - old_pos) |
|
||||||
|
* [ 4 ] | |
|
||||||
|
* [ 5 ] |------------------------------------|
|
||||||
|
* [ 6 ] <- pos
|
||||||
|
* [ 7 ]
|
||||||
|
* [ N - 1 ]
|
||||||
|
*/
|
||||||
|
usart_process_data(&usart_rx_dma_buffer[old_pos], pos - old_pos);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Processing is done in "overflow" mode..
|
||||||
|
*
|
||||||
|
* Application must process data twice,
|
||||||
|
* since there are 2 linear memory blocks to handle
|
||||||
|
*
|
||||||
|
* [ 0 ] |---------------------------------|
|
||||||
|
* [ 1 ] | Second block (len = pos) |
|
||||||
|
* [ 2 ] |---------------------------------|
|
||||||
|
* [ 3 ] <- pos
|
||||||
|
* [ 4 ] <- old_pos |---------------------------------|
|
||||||
|
* [ 5 ] | |
|
||||||
|
* [ 6 ] | First block (len = N - old_pos) |
|
||||||
|
* [ 7 ] | |
|
||||||
|
* [ N - 1 ] |---------------------------------|
|
||||||
|
*/
|
||||||
|
usart_process_data(&usart_rx_dma_buffer[old_pos], ARRAY_LEN(usart_rx_dma_buffer) - old_pos);
|
||||||
|
if (pos > 0) {
|
||||||
|
usart_process_data(&usart_rx_dma_buffer[0], pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
old_pos = pos; /* Save current position as old for next transfers */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check if DMA is active and if not try to send data
|
||||||
|
*
|
||||||
|
* This function can be called either by application to start data transfer
|
||||||
|
* or from DMA TX interrupt after previous transfer just finished
|
||||||
|
*
|
||||||
|
* \return `1` if transfer just started, `0` if on-going or no data to transmit
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
usart_start_tx_dma_transfer(void) {
|
||||||
|
uint32_t primask;
|
||||||
|
uint8_t started = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First check if transfer is currently in-active,
|
||||||
|
* by examining the value of usart_tx_dma_current_len variable.
|
||||||
|
*
|
||||||
|
* This variable is set before DMA transfer is started and cleared in DMA TX complete interrupt.
|
||||||
|
*
|
||||||
|
* It is not necessary to disable the interrupts before checking the variable:
|
||||||
|
*
|
||||||
|
* When usart_tx_dma_current_len == 0
|
||||||
|
* - This function is called by either application or TX DMA interrupt
|
||||||
|
* - When called from interrupt, it was just reset before the call,
|
||||||
|
* indicating transfer just completed and ready for more
|
||||||
|
* - When called from an application, transfer was previously already in-active
|
||||||
|
* and immediate call from interrupt cannot happen at this moment
|
||||||
|
*
|
||||||
|
* When usart_tx_dma_current_len != 0
|
||||||
|
* - This function is called only by an application.
|
||||||
|
* - It will never be called from interrupt with usart_tx_dma_current_len != 0 condition
|
||||||
|
*
|
||||||
|
* Disabling interrupts before checking for next transfer is advised
|
||||||
|
* only if multiple operating system threads can access to this function w/o
|
||||||
|
* exclusive access protection (mutex) configured,
|
||||||
|
* or if application calls this function from multiple interrupts.
|
||||||
|
*
|
||||||
|
* This example assumes worst use case scenario,
|
||||||
|
* hence interrupts are disabled prior every check
|
||||||
|
*/
|
||||||
|
primask = __get_PRIMASK();
|
||||||
|
__disable_irq();
|
||||||
|
if (usart_tx_dma_current_len == 0
|
||||||
|
&& (usart_tx_dma_current_len = lwrb_get_linear_block_read_length(&usart_tx_rb)) > 0) {
|
||||||
|
/* Disable channel if enabled */
|
||||||
|
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
|
||||||
|
|
||||||
|
/* Clear all flags */
|
||||||
|
LL_DMA_ClearFlag_TC1(DMA1);
|
||||||
|
LL_DMA_ClearFlag_HT1(DMA1);
|
||||||
|
LL_DMA_ClearFlag_TE1(DMA1);
|
||||||
|
LL_DMA_ClearFlag_DME1(DMA1);
|
||||||
|
LL_DMA_ClearFlag_FE1(DMA1);
|
||||||
|
|
||||||
|
/* Prepare DMA data and length */
|
||||||
|
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, usart_tx_dma_current_len);
|
||||||
|
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_1, (uint32_t)lwrb_get_linear_block_read_address(&usart_tx_rb));
|
||||||
|
|
||||||
|
/* Start transfer */
|
||||||
|
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
|
||||||
|
started = 1;
|
||||||
|
}
|
||||||
|
__set_PRIMASK(primask);
|
||||||
|
return started;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Process received data over UART
|
||||||
|
* Data are written to RX ringbuffer for application processing at latter stage
|
||||||
|
* \param[in] data: Data to process
|
||||||
|
* \param[in] len: Length in units of bytes
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
usart_process_data(const void* data, size_t len) {
|
||||||
|
lwrb_write(&usart_rx_rb, data, len); /* Write data to receive buffer */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Send string over USART
|
||||||
|
* \param[in] str: String to send
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
usart_send_string(const char* str) {
|
||||||
|
lwrb_write(&usart_tx_rb, str, strlen(str)); /* Write data to transmit buffer */
|
||||||
|
usart_start_tx_dma_transfer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief USART3 Initialization Function
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
usart_init(void) {
|
||||||
|
LL_USART_InitTypeDef USART_InitStruct = {0};
|
||||||
|
|
||||||
|
// CUBE_MX does this already
|
||||||
|
//LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||||
|
|
||||||
|
/* Peripheral clock enable */
|
||||||
|
//LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);
|
||||||
|
//LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOD);
|
||||||
|
//LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* USART3 GPIO Configuration
|
||||||
|
*
|
||||||
|
* PD8 ------> USART3_TX
|
||||||
|
* PD9 ------> USART3_RX
|
||||||
|
*/
|
||||||
|
//GPIO_InitStruct.Pin = LL_GPIO_PIN_8 | LL_GPIO_PIN_9;
|
||||||
|
//GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||||
|
//GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
|
||||||
|
//GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||||
|
//GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||||||
|
//GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
|
||||||
|
//LL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
||||||
|
|
||||||
|
/* USART3_RX Init */
|
||||||
|
// CUBE_MX does this already
|
||||||
|
//LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_0, LL_DMAMUX1_REQ_USART3_RX);
|
||||||
|
//LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_0, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
|
||||||
|
//LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_0, LL_DMA_PRIORITY_LOW);
|
||||||
|
//LL_DMA_SetMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MODE_CIRCULAR);
|
||||||
|
//LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_PERIPH_NOINCREMENT);
|
||||||
|
//LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MEMORY_INCREMENT);
|
||||||
|
//LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_0, LL_DMA_PDATAALIGN_BYTE);
|
||||||
|
//LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_0, LL_DMA_MDATAALIGN_BYTE);
|
||||||
|
//LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_0);
|
||||||
|
|
||||||
|
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_0, LL_USART_DMA_GetRegAddr(USART3, LL_USART_DMA_REG_DATA_RECEIVE));
|
||||||
|
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_0, (uint32_t)usart_rx_dma_buffer);
|
||||||
|
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, ARRAY_LEN(usart_rx_dma_buffer));
|
||||||
|
|
||||||
|
/* USART3_TX Init */
|
||||||
|
// CUBE_MX does this already
|
||||||
|
//LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_USART3_TX);
|
||||||
|
//LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_1, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
|
||||||
|
//LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_1, LL_DMA_PRIORITY_LOW);
|
||||||
|
//LL_DMA_SetMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MODE_NORMAL);
|
||||||
|
//LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);
|
||||||
|
//LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);
|
||||||
|
//LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_BYTE);
|
||||||
|
//LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_BYTE);
|
||||||
|
//LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
|
||||||
|
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_1, LL_USART_DMA_GetRegAddr(USART3, LL_USART_DMA_REG_DATA_TRANSMIT));
|
||||||
|
|
||||||
|
/* Enable DMA RX HT & TC interrupts */
|
||||||
|
LL_DMA_EnableIT_HT(DMA1, LL_DMA_STREAM_0);
|
||||||
|
LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_0);
|
||||||
|
/* Enable DMA TX TC interrupts */
|
||||||
|
LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_1);
|
||||||
|
|
||||||
|
/* DMA interrupt init */
|
||||||
|
// CUBE_MX does this already dma.c
|
||||||
|
//NVIC_SetPriority(DMA1_Stream0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
|
||||||
|
//NVIC_EnableIRQ(DMA1_Stream0_IRQn);
|
||||||
|
//NVIC_SetPriority(DMA1_Stream1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
|
||||||
|
//NVIC_EnableIRQ(DMA1_Stream1_IRQn);
|
||||||
|
|
||||||
|
/* Configure USART3 */
|
||||||
|
// CUBE_MX does this already usart.c
|
||||||
|
//USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
|
||||||
|
//USART_InitStruct.BaudRate = 115200;
|
||||||
|
//USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
|
||||||
|
//USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
|
||||||
|
//USART_InitStruct.Parity = LL_USART_PARITY_NONE;
|
||||||
|
//USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
|
||||||
|
//USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
|
||||||
|
//USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
|
||||||
|
//LL_USART_Init(USART3, &USART_InitStruct);
|
||||||
|
//LL_USART_SetTXFIFOThreshold(USART3, LL_USART_FIFOTHRESHOLD_7_8);
|
||||||
|
//LL_USART_SetRXFIFOThreshold(USART3, LL_USART_FIFOTHRESHOLD_7_8);
|
||||||
|
//LL_USART_EnableFIFO(USART3);
|
||||||
|
//LL_USART_ConfigAsyncMode(USART3);
|
||||||
|
|
||||||
|
LL_USART_EnableDMAReq_RX(USART3);
|
||||||
|
LL_USART_EnableDMAReq_TX(USART3);
|
||||||
|
LL_USART_EnableIT_IDLE(USART3);
|
||||||
|
|
||||||
|
/* USART interrupt, same priority as DMA channel */
|
||||||
|
// CUBE_MX does this already usart.c
|
||||||
|
//NVIC_SetPriority(USART3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
|
||||||
|
//NVIC_EnableIRQ(USART3_IRQn);
|
||||||
|
|
||||||
|
/* Enable USART and DMA RX */
|
||||||
|
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0);
|
||||||
|
|
||||||
|
//LL_USART_Enable(USART3);
|
||||||
|
|
||||||
|
/* Polling USART3 initialization */
|
||||||
|
while (!LL_USART_IsActiveFlag_TEACK(USART3) || !LL_USART_IsActiveFlag_REACK(USART3)) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interrupt handlers here */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DMA1 stream1 interrupt handler for USART3 RX
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
DMA1_Stream0_IRQHandler(void) {
|
||||||
|
/* Check half-transfer complete interrupt */
|
||||||
|
if (LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_STREAM_0) && LL_DMA_IsActiveFlag_HT0(DMA1)) {
|
||||||
|
LL_DMA_ClearFlag_HT0(DMA1); /* Clear half-transfer complete flag */
|
||||||
|
usart_rx_check(); /* Check for data to process */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check transfer-complete interrupt */
|
||||||
|
if (LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_STREAM_0) && LL_DMA_IsActiveFlag_TC0(DMA1)) {
|
||||||
|
LL_DMA_ClearFlag_TC0(DMA1); /* Clear transfer complete flag */
|
||||||
|
usart_rx_check(); /* Check for data to process */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement other events when needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DMA1 stream1 interrupt handler for USART3 TX
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
DMA1_Stream1_IRQHandler(void) {
|
||||||
|
/* Check transfer complete */
|
||||||
|
if (LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_STREAM_1) && LL_DMA_IsActiveFlag_TC1(DMA1)) {
|
||||||
|
LL_DMA_ClearFlag_TC1(DMA1); /* Clear transfer complete flag */
|
||||||
|
lwrb_skip(&usart_tx_rb, usart_tx_dma_current_len);/* Skip sent data, mark as read */
|
||||||
|
usart_tx_dma_current_len = 0; /* Clear length variable */
|
||||||
|
usart_start_tx_dma_transfer(); /* Start sending more data */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement other events when needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief USART3 global interrupt handler
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
USART3_IRQHandler(void) {
|
||||||
|
/* Check for IDLE line interrupt */
|
||||||
|
if (LL_USART_IsEnabledIT_IDLE(USART3) && LL_USART_IsActiveFlag_IDLE(USART3)) {
|
||||||
|
LL_USART_ClearFlag_IDLE(USART3); /* Clear IDLE line flag */
|
||||||
|
usart_rx_check(); /* Check for data to process */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement other events when needed */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief System Clock Configuration
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
SystemClock_Config(void) {
|
||||||
|
/* Configure flash latency */
|
||||||
|
LL_FLASH_SetLatency(LL_FLASH_LATENCY_4);
|
||||||
|
if (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_4) {
|
||||||
|
while (1) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure power supply and voltage scale */
|
||||||
|
LL_PWR_ConfigSupply(LL_PWR_LDO_SUPPLY);
|
||||||
|
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE0);
|
||||||
|
|
||||||
|
/* Uncomment if used on STM32H745/H755 Nucleo */
|
||||||
|
/* Dual-Core Nucleo board used external SMPS instead of LDO */
|
||||||
|
/* Manually enable it */
|
||||||
|
//PWR->CR3 |= 1 << 2;
|
||||||
|
|
||||||
|
/* Configure HSE */
|
||||||
|
LL_RCC_HSE_EnableBypass();
|
||||||
|
LL_RCC_HSE_Enable();
|
||||||
|
while (!LL_RCC_HSE_IsReady()) {}
|
||||||
|
|
||||||
|
/* Configure PLL */
|
||||||
|
LL_RCC_PLL_SetSource(LL_RCC_PLLSOURCE_HSE);
|
||||||
|
LL_RCC_PLL1P_Enable();
|
||||||
|
LL_RCC_PLL1Q_Enable();
|
||||||
|
LL_RCC_PLL1_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_8_16);
|
||||||
|
LL_RCC_PLL1_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE);
|
||||||
|
LL_RCC_PLL1_SetM(1);
|
||||||
|
LL_RCC_PLL1_SetN(120);
|
||||||
|
LL_RCC_PLL1_SetP(2);
|
||||||
|
LL_RCC_PLL1_SetQ(20);
|
||||||
|
LL_RCC_PLL1_SetR(2);
|
||||||
|
LL_RCC_PLL1_Enable();
|
||||||
|
while (!LL_RCC_PLL1_IsReady()) {}
|
||||||
|
|
||||||
|
/* Intermediate AHB prescaler 2 when target frequency clock is higher than 80 MHz */
|
||||||
|
LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_2);
|
||||||
|
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL1);
|
||||||
|
LL_RCC_SetSysPrescaler(LL_RCC_SYSCLK_DIV_1);
|
||||||
|
LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_2);
|
||||||
|
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
|
||||||
|
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_2);
|
||||||
|
LL_RCC_SetAPB3Prescaler(LL_RCC_APB3_DIV_2);
|
||||||
|
LL_RCC_SetAPB4Prescaler(LL_RCC_APB4_DIV_2);
|
||||||
|
|
||||||
|
/* Configure systick */
|
||||||
|
LL_Init1msTick(480000000);
|
||||||
|
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
|
||||||
|
LL_SetSystemCoreClock(480000000);
|
||||||
|
LL_RCC_SetUSARTClockSource(LL_RCC_USART234578_CLKSOURCE_PCLK1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user