Update framework #6
68
CHANGELOG
68
CHANGELOG
@ -1,17 +1,81 @@
|
||||
## Changes from ASTP 1.0.0 to 1.1.0
|
||||
# Changed from ASTP 1.1.0 to 1.2.0
|
||||
|
||||
## API Changes
|
||||
|
||||
### FSFW Architecture
|
||||
|
||||
- New src folder which contains all source files except the HAL, contributed code and test code
|
||||
- External and internal API mostly stayed the same
|
||||
- Folder names are now all smaller case: internalError was renamed to internalerror and
|
||||
FreeRTOS was renamed to freertos
|
||||
- Warning if optional headers are used but the modules was not added to the source files to compile
|
||||
|
||||
### HAL
|
||||
|
||||
- HAL added back into FSFW. It is tightly bound to the FSFW, and compiling it as a static library
|
||||
made using it more complicated than necessary
|
||||
|
||||
## Bugfixes
|
||||
|
||||
### FreeRTOS QueueMapManager
|
||||
|
||||
- Fixed a bug which causes the first generated Queue ID to be invalid
|
||||
|
||||
## Enhancements
|
||||
|
||||
### FSFW Architecture
|
||||
|
||||
- See API changes chapter. This change will keep the internal API consistent in the future
|
||||
|
||||
# Changes from ASTP 1.0.0 to 1.1.0
|
||||
|
||||
## API Changes
|
||||
|
||||
### PUS
|
||||
|
||||
- Added PUS C support
|
||||
- SUBSYSTEM_IDs added for PUS Services
|
||||
- Added new Parameter which must be defined in config: fsfwconfig::FSFW_MAX_TM_PACKET_SIZE
|
||||
|
||||
### ObjectManager
|
||||
|
||||
- ObjectManager is now a singelton
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
- Additional configuration option fsfwconfig::FSFW_MAX_TM_PACKET_SIZE which
|
||||
need to be specified in FSFWConfig.h
|
||||
|
||||
### CMake
|
||||
|
||||
- Changed Cmake FSFW_ADDITIONAL_INC_PATH to FSFW_ADDITIONAL_INC_PATHS
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- timemanager/TimeStamperIF.h: Timestamp config was not used correctly, leading to different timestamp sizes than configured in fsfwconfig::FSFW_MISSION_TIMESTAMP_SIZE
|
||||
- TCP server fixes
|
||||
|
||||
## Enhancements
|
||||
|
||||
### FreeRTOS Queue Handles
|
||||
|
||||
- Fixed an internal issue how FreeRTOS MessageQueues were handled
|
||||
|
||||
### Linux OSAL
|
||||
|
||||
- Better printf error messages
|
||||
|
||||
### CMake
|
||||
|
||||
- Check for C++11 as mininimum required Version
|
||||
|
||||
### Debug Output
|
||||
|
||||
- Changed Warning color to magenta, which is well readable on both dark and light mode IDEs
|
||||
|
||||
|
||||
## Changes from ASTP 0.0.1 to 1.0.0
|
||||
# Changes from ASTP 0.0.1 to 1.0.0
|
||||
|
||||
### Host OSAL
|
||||
|
||||
|
@ -3,20 +3,31 @@ cmake_minimum_required(VERSION 3.13)
|
||||
option(FSFW_GENERATE_SECTIONS
|
||||
"Generate function and data sections. Required to remove unused code" ON
|
||||
)
|
||||
|
||||
if(FSFW_GENERATE_SECTIONS)
|
||||
option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON)
|
||||
endif()
|
||||
|
||||
option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON)
|
||||
# Options to exclude parts of the FSFW from compilation.
|
||||
option(FSFW_USE_RMAP "Compile with RMAP" ON)
|
||||
option(FSFW_USE_DATALINKLAYER "Compile with Data Link Layer" ON)
|
||||
option(FSFW_ADD_INTERNAL_TESTS "Add internal unit tests" ON)
|
||||
|
||||
# Optional sources
|
||||
option(FSFW_ADD_PUS "Compile with PUS sources" ON)
|
||||
option(FSFW_ADD_MONITORING "Compile with monitoring components" ON)
|
||||
|
||||
option(FSFW_ADD_RMAP "Compile with RMAP" OFF)
|
||||
option(FSFW_ADD_DATALINKLAYER "Compile with Data Link Layer" OFF)
|
||||
option(FSFW_ADD_COORDINATES "Compile with coordinate components" OFF)
|
||||
option(FSFW_ADD_TMSTORAGE "Compile with tm storage components" OFF)
|
||||
|
||||
# Contrib sources
|
||||
option(FSFW_ADD_SPG4_PROPAGATOR "Add SPG4 propagator code" OFF)
|
||||
|
||||
set(LIB_FSFW_NAME fsfw)
|
||||
add_library(${LIB_FSFW_NAME})
|
||||
set(FSFW_CORE_INC_PATH "inc")
|
||||
|
||||
set_property(CACHE OS_FSFW PROPERTY STRINGS host linux rtems freertos)
|
||||
set_property(CACHE FSFW_OSAL PROPERTY STRINGS host linux rtems freertos)
|
||||
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
@ -25,38 +36,44 @@ elseif(${CMAKE_CXX_STANDARD} LESS 11)
|
||||
message(FATAL_ERROR "Compiling the FSFW requires a minimum of C++11 support")
|
||||
endif()
|
||||
|
||||
if(NOT OS_FSFW)
|
||||
message(STATUS "No OS for FSFW via OS_FSFW set. Assuming host OS")
|
||||
# Backwards comptability
|
||||
if(OS_FSFW)
|
||||
message(WARNING "Please pass the FSFW OSAL as FSFW_OSAL instead of OS_FSFW")
|
||||
set(FSFW_OSAL OS_FSFW)
|
||||
endif()
|
||||
|
||||
if(NOT FSFW_OSAL)
|
||||
message(STATUS "No OS for FSFW via FSFW_OSAL set. Assuming host OS")
|
||||
# Assume host OS and autodetermine from OS_FSFW
|
||||
if(UNIX)
|
||||
set(OS_FSFW "linux"
|
||||
set(FSFW_OSAL "linux"
|
||||
CACHE STRING
|
||||
"OS abstraction layer used in the FSFW"
|
||||
)
|
||||
elseif(WIN32)
|
||||
set(OS_FSFW "host"
|
||||
set(FSFW_OSAL "host"
|
||||
CACHE STRING "OS abstraction layer used in the FSFW"
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
set(FSFW_OSAL_DEFINITION FSFW_HOST)
|
||||
set(FSFW_OSAL_DEFINITION FSFW_OSAL_HOST)
|
||||
|
||||
if(${OS_FSFW} STREQUAL host)
|
||||
if(FSFW_OSAL MATCHES host)
|
||||
set(OS_FSFW_NAME "Host")
|
||||
elseif(${OS_FSFW} STREQUAL linux)
|
||||
elseif(FSFW_OSAL MATCHES linux)
|
||||
set(OS_FSFW_NAME "Linux")
|
||||
set(FSFW_OSAL_DEFINITION FSFW_LINUX)
|
||||
elseif(${OS_FSFW} STREQUAL freertos)
|
||||
set(FSFW_OSAL_DEFINITION FSFW_OSAL_LINUX)
|
||||
elseif(FSFW_OSAL MATCHES freertos)
|
||||
set(OS_FSFW_NAME "FreeRTOS")
|
||||
set(FSFW_OSAL_DEFINITION FSFW_FREERTOS)
|
||||
set(FSFW_OSAL_DEFINITION FSFW_OSAL_FREERTOS)
|
||||
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
|
||||
${LIB_OS_NAME}
|
||||
)
|
||||
elseif(${OS_FSFW} STREQUAL rtems)
|
||||
elseif(FSFW_OSAL STREQUAL rtems)
|
||||
set(OS_FSFW_NAME "RTEMS")
|
||||
set(FSFW_OSAL_DEFINITION FSFW_RTEMS)
|
||||
set(FSFW_OSAL_DEFINITION FSFW_OSAL_RTEMS)
|
||||
else()
|
||||
message(WARNING
|
||||
"Invalid operating system for FSFW specified! Setting to host.."
|
||||
@ -75,50 +92,10 @@ target_compile_definitions(${LIB_FSFW_NAME} INTERFACE
|
||||
|
||||
message(STATUS "Compiling FSFW for the ${OS_FSFW_NAME} operating system.")
|
||||
|
||||
add_subdirectory(action)
|
||||
add_subdirectory(container)
|
||||
add_subdirectory(controller)
|
||||
add_subdirectory(coordinates)
|
||||
|
||||
if(FSFW_USE_DATALINKLAYER)
|
||||
add_subdirectory(datalinklayer)
|
||||
endif()
|
||||
|
||||
add_subdirectory(datapool)
|
||||
add_subdirectory(datapoollocal)
|
||||
add_subdirectory(housekeeping)
|
||||
add_subdirectory(devicehandlers)
|
||||
add_subdirectory(events)
|
||||
add_subdirectory(fdir)
|
||||
add_subdirectory(globalfunctions)
|
||||
add_subdirectory(health)
|
||||
add_subdirectory(internalError)
|
||||
add_subdirectory(ipc)
|
||||
add_subdirectory(memory)
|
||||
add_subdirectory(modes)
|
||||
add_subdirectory(monitoring)
|
||||
add_subdirectory(objectmanager)
|
||||
add_subdirectory(osal)
|
||||
add_subdirectory(parameters)
|
||||
add_subdirectory(power)
|
||||
add_subdirectory(pus)
|
||||
|
||||
if(FSFW_USE_RMAP)
|
||||
add_subdirectory(rmap)
|
||||
endif()
|
||||
|
||||
add_subdirectory(serialize)
|
||||
add_subdirectory(serviceinterface)
|
||||
add_subdirectory(storagemanager)
|
||||
add_subdirectory(subsystem)
|
||||
add_subdirectory(tasks)
|
||||
add_subdirectory(tcdistribution)
|
||||
add_subdirectory(thermal)
|
||||
add_subdirectory(timemanager)
|
||||
add_subdirectory(tmstorage)
|
||||
add_subdirectory(tmtcpacket)
|
||||
add_subdirectory(tmtcservices)
|
||||
add_subdirectory(unittest)
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(hal)
|
||||
add_subdirectory(contrib)
|
||||
|
||||
# The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it.
|
||||
# If this is not given, we include the default configuration and emit a warning.
|
||||
@ -191,6 +168,7 @@ endif()
|
||||
target_include_directories(${LIB_FSFW_NAME} INTERFACE
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${FSFW_CONFIG_PATH_ABSOLUTE}
|
||||
${FSFW_CORE_INC_PATH}
|
||||
${FSFW_ADD_INC_PATHS_ABS}
|
||||
)
|
||||
|
||||
@ -200,6 +178,7 @@ target_include_directories(${LIB_FSFW_NAME} INTERFACE
|
||||
target_include_directories(${LIB_FSFW_NAME} PRIVATE
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${FSFW_CONFIG_PATH_ABSOLUTE}
|
||||
${FSFW_CORE_INC_PATH}
|
||||
${FSFW_ADD_INC_PATHS_ABS}
|
||||
)
|
||||
|
||||
|
7
FSFW.h
7
FSFW.h
@ -1,7 +0,0 @@
|
||||
#ifndef FSFW_FSFW_H_
|
||||
#define FSFW_FSFW_H_
|
||||
|
||||
#include "FSFWConfig.h"
|
||||
|
||||
|
||||
#endif /* FSFW_FSFW_H_ */
|
11
contrib/CMakeLists.txt
Normal file
11
contrib/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
if(FSFW_ADD_SPG4_PROPAGATOR)
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
sgp4/sgp4unit.cpp
|
||||
)
|
||||
target_include_directories(${LIB_FSFW_NAME} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sgp4
|
||||
)
|
||||
target_include_directories(${LIB_FSFW_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sgp4
|
||||
)
|
||||
endif()
|
@ -1,12 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
|
||||
/* Collected related headers */
|
||||
#include "LocalPoolVariable.h"
|
||||
#include "LocalPoolVector.h"
|
||||
#include "StaticLocalDataSet.h"
|
||||
#include "LocalDataSet.h"
|
||||
#include "SharedLocalDataSet.h"
|
||||
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */
|
@ -1,15 +0,0 @@
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/ipc/*.cpp)
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/objects/*.cpp)
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/pollingsequence/*.cpp)
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/events/*.cpp)
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/tmtc/*.cpp)
|
||||
CXXSRC += $(wildcard $(CURRENTPATH)/devices/*.cpp)
|
||||
|
||||
INCLUDES += $(CURRENTPATH)
|
||||
INCLUDES += $(CURRENTPATH)/objects
|
||||
INCLUDES += $(CURRENTPATH)/returnvalues
|
||||
INCLUDES += $(CURRENTPATH)/tmtc
|
||||
INCLUDES += $(CURRENTPATH)/events
|
||||
INCLUDES += $(CURRENTPATH)/devices
|
||||
INCLUDES += $(CURRENTPATH)/pollingsequence
|
||||
INCLUDES += $(CURRENTPATH)/ipc
|
@ -1,7 +0,0 @@
|
||||
target_sources(${LIB_FSFW_NAME}
|
||||
PRIVATE
|
||||
EventManager.cpp
|
||||
EventMessage.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(eventmatching)
|
75
fsfw.mk
75
fsfw.mk
@ -1,75 +0,0 @@
|
||||
# This submake file needs to be included by the primary Makefile.
|
||||
# This file needs FRAMEWORK_PATH and OS_FSFW set correctly by another Makefile.
|
||||
# Valid API settings: rtems, linux, freeRTOS, host
|
||||
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/action/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/container/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/contrib/sgp4/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/controller/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/coordinates/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datalinklayer/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datapool/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datapoollocal/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datapoollocal/internal/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/housekeeping/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/devicehandlers/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/events/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/events/eventmatching/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/fdir/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/globalfunctions/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/globalfunctions/matching/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/globalfunctions/math/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/health/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/internalError/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/ipc/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/memory/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/modes/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/monitoring/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/objectmanager/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/*.cpp)
|
||||
|
||||
# select the OS
|
||||
ifeq ($(OS_FSFW),rtems)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/rtems/*.cpp)
|
||||
|
||||
else ifeq ($(OS_FSFW),linux)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/linux/*.cpp)
|
||||
|
||||
else ifeq ($(OS_FSFW),freeRTOS)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/FreeRTOS/*.cpp)
|
||||
|
||||
else ifeq ($(OS_FSFW),host)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/host/*.cpp)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/windows/*.cpp)
|
||||
else
|
||||
# For now, the linux UDP bridge sources needs to be included manually by upper makefile
|
||||
# for host OS because we can't be sure the OS is linux.
|
||||
# Following lines can be used to do this:
|
||||
# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TcUnixUdpPollingTask.cpp
|
||||
# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TmTcUnixUdpBridge.cpp
|
||||
endif
|
||||
|
||||
else
|
||||
$(error invalid OS_FSFW specified, valid OS_FSFW are rtems, linux, freeRTOS, host)
|
||||
endif
|
||||
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/parameters/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/power/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/returnvalues/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/rmap/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/serialize/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/serviceinterface/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/storagemanager/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/subsystem/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/subsystem/modes/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tasks/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tcdistribution/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/thermal/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/timemanager/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmstorage/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcpacket/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcpacket/packetmatcher/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcpacket/pus/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/tmtcservices/*.cpp)
|
||||
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/pus/*.cpp)
|
41
hal/CMakeLists.txt
Normal file
41
hal/CMakeLists.txt
Normal file
@ -0,0 +1,41 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
# Can also be changed by upper CMakeLists.txt file
|
||||
find_library(LIB_FSFW_NAME fsfw REQUIRED)
|
||||
|
||||
option(FSFW_HAL_ADD_LINUX "Add the Linux HAL to the sources. Required gpiod library" OFF)
|
||||
option(FSFW_HAL_ADD_RASPBERRY_PI "Add Raspberry Pi specific code to the sources" OFF)
|
||||
option(FSFW_HAL_ADD_STM32H7 "Add the STM32H7 HAL to the sources" OFF)
|
||||
option(FSFW_HAL_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON)
|
||||
|
||||
set(LINUX_HAL_PATH_NAME linux)
|
||||
set(STM32H7_PATH_NAME stm32h7)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
foreach(INCLUDE_PATH ${FSFW_HAL_ADDITIONAL_INC_PATHS})
|
||||
if(IS_ABSOLUTE ${INCLUDE_PATH})
|
||||
set(CURR_ABS_INC_PATH "${INCLUDE_PATH}")
|
||||
else()
|
||||
get_filename_component(CURR_ABS_INC_PATH
|
||||
${INCLUDE_PATH} REALPATH BASE_DIR ${CMAKE_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
if(CMAKE_VERBOSE)
|
||||
message(STATUS "FSFW include path: ${CURR_ABS_INC_PATH}")
|
||||
endif()
|
||||
|
||||
list(APPEND FSFW_HAL_ADD_INC_PATHS_ABS ${CURR_ABS_INC_PATH})
|
||||
endforeach()
|
||||
|
||||
target_include_directories(${LIB_FSFW_NAME} PRIVATE
|
||||
${FSFW_HAL_ADD_INC_PATHS_ABS}
|
||||
)
|
||||
|
||||
target_compile_definitions(${LIB_FSFW_NAME} PRIVATE
|
||||
${FSFW_HAL_DEFINES}
|
||||
)
|
||||
|
||||
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
|
||||
${FSFW_HAL_LINK_LIBS}
|
||||
)
|
9
hal/src/CMakeLists.txt
Normal file
9
hal/src/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
target_include_directories(${LIB_FSFW_NAME} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
target_include_directories(${LIB_FSFW_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_subdirectory(fsfw)
|
1
hal/src/fsfw/CMakeLists.txt
Normal file
1
hal/src/fsfw/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(hal)
|
10
hal/src/fsfw/hal/CMakeLists.txt
Normal file
10
hal/src/fsfw/hal/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
add_subdirectory(devicehandlers)
|
||||
add_subdirectory(common)
|
||||
|
||||
if(FSFW_HAL_ADD_LINUX)
|
||||
add_subdirectory(linux)
|
||||
endif()
|
||||
|
||||
if(FSFW_HAL_ADD_STM32H7)
|
||||
add_subdirectory(stm32h7)
|
||||
endif()
|
1
hal/src/fsfw/hal/common/CMakeLists.txt
Normal file
1
hal/src/fsfw/hal/common/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(gpio)
|
3
hal/src/fsfw/hal/common/gpio/CMakeLists.txt
Normal file
3
hal/src/fsfw/hal/common/gpio/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
GpioCookie.cpp
|
||||
)
|
50
hal/src/fsfw/hal/common/gpio/GpioCookie.cpp
Normal file
50
hal/src/fsfw/hal/common/gpio/GpioCookie.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "fsfw/hal/common/gpio/GpioCookie.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
GpioCookie::GpioCookie() {
|
||||
}
|
||||
|
||||
ReturnValue_t GpioCookie::addGpio(gpioId_t gpioId, GpioBase* gpioConfig) {
|
||||
if (gpioConfig == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "GpioCookie::addGpio: gpioConfig is nullpointer" << std::endl;
|
||||
#else
|
||||
sif::printWarning("GpioCookie::addGpio: gpioConfig is nullpointer\n");
|
||||
#endif
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
auto gpioMapIter = gpioMap.find(gpioId);
|
||||
if(gpioMapIter == gpioMap.end()) {
|
||||
auto statusPair = gpioMap.emplace(gpioId, gpioConfig);
|
||||
if (statusPair.second == false) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "GpioCookie::addGpio: Failed to add GPIO " << gpioId <<
|
||||
" to GPIO map" << std::endl;
|
||||
#else
|
||||
sif::printWarning("GpioCookie::addGpio: Failed to add GPIO %d to GPIO map\n", gpioId);
|
||||
#endif
|
||||
#endif
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "GpioCookie::addGpio: GPIO already exists in GPIO map " << std::endl;
|
||||
#else
|
||||
sif::printWarning("GpioCookie::addGpio: GPIO already exists in GPIO map\n");
|
||||
#endif
|
||||
#endif
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
GpioMap GpioCookie::getGpioMap() const {
|
||||
return gpioMap;
|
||||
}
|
||||
|
||||
GpioCookie::~GpioCookie() {
|
||||
for(auto& config: gpioMap) {
|
||||
delete(config.second);
|
||||
}
|
||||
}
|
41
hal/src/fsfw/hal/common/gpio/GpioCookie.h
Normal file
41
hal/src/fsfw/hal/common/gpio/GpioCookie.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef COMMON_GPIO_GPIOCOOKIE_H_
|
||||
#define COMMON_GPIO_GPIOCOOKIE_H_
|
||||
|
||||
#include "GpioIF.h"
|
||||
#include "gpioDefinitions.h"
|
||||
|
||||
#include <fsfw/devicehandlers/CookieIF.h>
|
||||
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||
|
||||
/**
|
||||
* @brief Cookie for the GpioIF. Allows the GpioIF to determine which
|
||||
* GPIOs to initialize and whether they should be configured as in- or
|
||||
* output.
|
||||
* @details One GpioCookie can hold multiple GPIO configurations. To add a new
|
||||
* GPIO configuration to a GpioCookie use the GpioCookie::addGpio
|
||||
* function.
|
||||
*
|
||||
* @author J. Meier
|
||||
*/
|
||||
class GpioCookie: public CookieIF {
|
||||
public:
|
||||
|
||||
GpioCookie();
|
||||
|
||||
virtual ~GpioCookie();
|
||||
|
||||
ReturnValue_t addGpio(gpioId_t gpioId, GpioBase* gpioConfig);
|
||||
|
||||
/**
|
||||
* @brief Get map with registered GPIOs.
|
||||
*/
|
||||
GpioMap getGpioMap() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns a copy of the internal GPIO map.
|
||||
*/
|
||||
GpioMap gpioMap;
|
||||
};
|
||||
|
||||
#endif /* COMMON_GPIO_GPIOCOOKIE_H_ */
|
54
hal/src/fsfw/hal/common/gpio/GpioIF.h
Normal file
54
hal/src/fsfw/hal/common/gpio/GpioIF.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef COMMON_GPIO_GPIOIF_H_
|
||||
#define COMMON_GPIO_GPIOIF_H_
|
||||
|
||||
#include "gpioDefinitions.h"
|
||||
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||
#include <fsfw/devicehandlers/CookieIF.h>
|
||||
|
||||
class GpioCookie;
|
||||
|
||||
/**
|
||||
* @brief This class defines the interface for objects requiring the control
|
||||
* over GPIOs.
|
||||
* @author J. Meier
|
||||
*/
|
||||
class GpioIF : public HasReturnvaluesIF {
|
||||
public:
|
||||
|
||||
virtual ~GpioIF() {};
|
||||
|
||||
/**
|
||||
* @brief Called by the GPIO using object.
|
||||
* @param cookie Cookie specifying informations of the GPIOs required
|
||||
* by a object.
|
||||
*/
|
||||
virtual ReturnValue_t addGpios(GpioCookie* cookie) = 0;
|
||||
|
||||
/**
|
||||
* @brief By implementing this function a child must provide the
|
||||
* functionality to pull a certain GPIO to high logic level.
|
||||
*
|
||||
* @param gpioId A unique number which specifies the GPIO to drive.
|
||||
* @return Returns RETURN_OK for success. This should never return RETURN_FAILED.
|
||||
*/
|
||||
virtual ReturnValue_t pullHigh(gpioId_t gpioId) = 0;
|
||||
|
||||
/**
|
||||
* @brief By implementing this function a child must provide the
|
||||
* functionality to pull a certain GPIO to low logic level.
|
||||
*
|
||||
* @param gpioId A unique number which specifies the GPIO to drive.
|
||||
*/
|
||||
virtual ReturnValue_t pullLow(gpioId_t gpioId) = 0;
|
||||
|
||||
/**
|
||||
* @brief This function requires a child to implement the functionality to read the state of
|
||||
* an ouput or input gpio.
|
||||
*
|
||||
* @param gpioId A unique number which specifies the GPIO to read.
|
||||
* @param gpioState State of GPIO will be written to this pointer.
|
||||
*/
|
||||
virtual ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) = 0;
|
||||
};
|
||||
|
||||
#endif /* COMMON_GPIO_GPIOIF_H_ */
|
110
hal/src/fsfw/hal/common/gpio/gpioDefinitions.h
Normal file
110
hal/src/fsfw/hal/common/gpio/gpioDefinitions.h
Normal file
@ -0,0 +1,110 @@
|
||||
#ifndef COMMON_GPIO_GPIODEFINITIONS_H_
|
||||
#define COMMON_GPIO_GPIODEFINITIONS_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
|
||||
using gpioId_t = uint16_t;
|
||||
|
||||
namespace gpio {
|
||||
|
||||
enum Levels {
|
||||
LOW = 0,
|
||||
HIGH = 1
|
||||
};
|
||||
|
||||
enum Direction {
|
||||
IN = 0,
|
||||
OUT = 1
|
||||
};
|
||||
|
||||
enum GpioOperation {
|
||||
READ,
|
||||
WRITE
|
||||
};
|
||||
|
||||
enum GpioTypes {
|
||||
NONE,
|
||||
GPIO_REGULAR,
|
||||
CALLBACK
|
||||
};
|
||||
|
||||
static constexpr gpioId_t NO_GPIO = -1;
|
||||
|
||||
using gpio_cb_t = void (*) (gpioId_t gpioId, gpio::GpioOperation gpioOp, int value, void* args);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Struct containing information about the GPIO to use. This is
|
||||
* required by the libgpiod to access and drive a GPIO.
|
||||
* @param chipname String of the chipname specifying the group which contains the GPIO to
|
||||
* access. E.g. gpiochip0. To detect names of GPIO groups run gpiodetect on
|
||||
* the linux command line.
|
||||
* @param lineNum The offset of the GPIO within the GPIO group.
|
||||
* @param consumer Name of the consumer. Simply a description of the GPIO configuration.
|
||||
* @param direction Specifies whether the GPIO should be used as in- or output.
|
||||
* @param initValue Defines the initial state of the GPIO when configured as output.
|
||||
* Only required for output GPIOs.
|
||||
* @param lineHandle The handle returned by gpiod_chip_get_line will be later written to this
|
||||
* pointer.
|
||||
*/
|
||||
class GpioBase {
|
||||
public:
|
||||
|
||||
GpioBase() = default;
|
||||
|
||||
GpioBase(gpio::GpioTypes gpioType, std::string consumer, gpio::Direction direction,
|
||||
int initValue):
|
||||
gpioType(gpioType), consumer(consumer),direction(direction), initValue(initValue) {}
|
||||
|
||||
virtual~ GpioBase() {};
|
||||
|
||||
// Can be used to cast GpioBase to a concrete child implementation
|
||||
gpio::GpioTypes gpioType = gpio::GpioTypes::NONE;
|
||||
std::string consumer;
|
||||
gpio::Direction direction = gpio::Direction::IN;
|
||||
int initValue = 0;
|
||||
};
|
||||
|
||||
class GpiodRegular: public GpioBase {
|
||||
public:
|
||||
GpiodRegular() :
|
||||
GpioBase(gpio::GpioTypes::GPIO_REGULAR, std::string(), gpio::Direction::IN, 0) {
|
||||
}
|
||||
;
|
||||
|
||||
GpiodRegular(std::string chipname_, int lineNum_, std::string consumer_,
|
||||
gpio::Direction direction_, int initValue_) :
|
||||
GpioBase(gpio::GpioTypes::GPIO_REGULAR, consumer_, direction_, initValue_),
|
||||
chipname(chipname_), lineNum(lineNum_) {
|
||||
}
|
||||
|
||||
GpiodRegular(std::string chipname_, int lineNum_, std::string consumer_) :
|
||||
GpioBase(gpio::GpioTypes::GPIO_REGULAR, consumer_, gpio::Direction::IN, 0),
|
||||
chipname(chipname_), lineNum(lineNum_) {
|
||||
}
|
||||
std::string chipname;
|
||||
int lineNum = 0;
|
||||
struct gpiod_line* lineHandle = nullptr;
|
||||
};
|
||||
|
||||
class GpioCallback: public GpioBase {
|
||||
public:
|
||||
GpioCallback(std::string consumer, gpio::Direction direction_, int initValue_,
|
||||
gpio::gpio_cb_t callback, void* callbackArgs):
|
||||
GpioBase(gpio::GpioTypes::CALLBACK, consumer, direction_, initValue_),
|
||||
callback(callback), callbackArgs(callbackArgs) {}
|
||||
|
||||
gpio::gpio_cb_t callback = nullptr;
|
||||
void* callbackArgs = nullptr;
|
||||
};
|
||||
|
||||
|
||||
using GpioMap = std::map<gpioId_t, GpioBase*>;
|
||||
using GpioUnorderedMap = std::unordered_map<gpioId_t, GpioBase*>;
|
||||
using GpioMapIter = GpioMap::iterator;
|
||||
using GpioUnorderedMapIter = GpioUnorderedMap::iterator;
|
||||
|
||||
#endif /* LINUX_GPIO_GPIODEFINITIONS_H_ */
|
17
hal/src/fsfw/hal/common/spi/spiCommon.h
Normal file
17
hal/src/fsfw/hal/common/spi/spiCommon.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef FSFW_HAL_COMMON_SPI_SPICOMMON_H_
|
||||
#define FSFW_HAL_COMMON_SPI_SPICOMMON_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace spi {
|
||||
|
||||
enum SpiModes: uint8_t {
|
||||
MODE_0,
|
||||
MODE_1,
|
||||
MODE_2,
|
||||
MODE_3
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* FSFW_HAL_COMMON_SPI_SPICOMMON_H_ */
|
3
hal/src/fsfw/hal/devicehandlers/CMakeLists.txt
Normal file
3
hal/src/fsfw/hal/devicehandlers/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
GyroL3GD20Handler.cpp
|
||||
)
|
262
hal/src/fsfw/hal/devicehandlers/GyroL3GD20Handler.cpp
Normal file
262
hal/src/fsfw/hal/devicehandlers/GyroL3GD20Handler.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
#include "fsfw/hal/devicehandlers/GyroL3GD20Handler.h"
|
||||
|
||||
#include "fsfw/datapool/PoolReadGuard.h"
|
||||
|
||||
GyroHandlerL3GD20H::GyroHandlerL3GD20H(object_id_t objectId, object_id_t deviceCommunication,
|
||||
CookieIF *comCookie):
|
||||
DeviceHandlerBase(objectId, deviceCommunication, comCookie),
|
||||
dataset(this) {
|
||||
#if FSFW_HAL_L3GD20_GYRO_DEBUG == 1
|
||||
debugDivider = new PeriodicOperationDivider(5);
|
||||
#endif
|
||||
}
|
||||
|
||||
GyroHandlerL3GD20H::~GyroHandlerL3GD20H() {}
|
||||
|
||||
void GyroHandlerL3GD20H::doStartUp() {
|
||||
if(internalState == InternalState::NONE) {
|
||||
internalState = InternalState::CONFIGURE;
|
||||
}
|
||||
|
||||
if(internalState == InternalState::CONFIGURE) {
|
||||
if(commandExecuted) {
|
||||
internalState = InternalState::CHECK_REGS;
|
||||
commandExecuted = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(internalState == InternalState::CHECK_REGS) {
|
||||
if(commandExecuted) {
|
||||
internalState = InternalState::NORMAL;
|
||||
if(goNormalModeImmediately) {
|
||||
setMode(MODE_NORMAL);
|
||||
}
|
||||
else {
|
||||
setMode(_MODE_TO_ON);
|
||||
}
|
||||
commandExecuted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GyroHandlerL3GD20H::doShutDown() {
|
||||
setMode(_MODE_POWER_DOWN);
|
||||
}
|
||||
|
||||
ReturnValue_t GyroHandlerL3GD20H::buildTransitionDeviceCommand(DeviceCommandId_t *id) {
|
||||
switch(internalState) {
|
||||
case(InternalState::NONE):
|
||||
case(InternalState::NORMAL): {
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
case(InternalState::CONFIGURE): {
|
||||
*id = L3GD20H::CONFIGURE_CTRL_REGS;
|
||||
uint8_t command [5];
|
||||
command[0] = L3GD20H::CTRL_REG_1_VAL;
|
||||
command[1] = L3GD20H::CTRL_REG_2_VAL;
|
||||
command[2] = L3GD20H::CTRL_REG_3_VAL;
|
||||
command[3] = L3GD20H::CTRL_REG_4_VAL;
|
||||
command[4] = L3GD20H::CTRL_REG_5_VAL;
|
||||
return buildCommandFromCommand(*id, command, 5);
|
||||
}
|
||||
case(InternalState::CHECK_REGS): {
|
||||
*id = L3GD20H::READ_REGS;
|
||||
return buildCommandFromCommand(*id, nullptr, 0);
|
||||
}
|
||||
default:
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
/* Might be a configuration error. */
|
||||
sif::debug << "GyroHandler::buildTransitionDeviceCommand: Unknown internal state!" <<
|
||||
std::endl;
|
||||
#else
|
||||
sif::printDebug("GyroHandler::buildTransitionDeviceCommand: Unknown internal state!\n");
|
||||
#endif
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroHandlerL3GD20H::buildNormalDeviceCommand(DeviceCommandId_t *id) {
|
||||
*id = L3GD20H::READ_REGS;
|
||||
return buildCommandFromCommand(*id, nullptr, 0);
|
||||
}
|
||||
|
||||
ReturnValue_t GyroHandlerL3GD20H::buildCommandFromCommand(
|
||||
DeviceCommandId_t deviceCommand, const uint8_t *commandData,
|
||||
size_t commandDataLen) {
|
||||
switch(deviceCommand) {
|
||||
case(L3GD20H::READ_REGS): {
|
||||
commandBuffer[0] = L3GD20H::READ_START | L3GD20H::AUTO_INCREMENT_MASK | L3GD20H::READ_MASK;
|
||||
std::memset(commandBuffer + 1, 0, L3GD20H::READ_LEN);
|
||||
rawPacket = commandBuffer;
|
||||
rawPacketLen = L3GD20H::READ_LEN + 1;
|
||||
break;
|
||||
}
|
||||
case(L3GD20H::CONFIGURE_CTRL_REGS): {
|
||||
commandBuffer[0] = L3GD20H::CTRL_REG_1 | L3GD20H::AUTO_INCREMENT_MASK;
|
||||
if(commandData == nullptr or commandDataLen != 5) {
|
||||
return DeviceHandlerIF::INVALID_COMMAND_PARAMETER;
|
||||
}
|
||||
|
||||
ctrlReg1Value = commandData[0];
|
||||
ctrlReg2Value = commandData[1];
|
||||
ctrlReg3Value = commandData[2];
|
||||
ctrlReg4Value = commandData[3];
|
||||
ctrlReg5Value = commandData[4];
|
||||
|
||||
bool fsH = ctrlReg4Value & L3GD20H::SET_FS_1;
|
||||
bool fsL = ctrlReg4Value & L3GD20H::SET_FS_0;
|
||||
|
||||
if(not fsH and not fsL) {
|
||||
sensitivity = L3GD20H::SENSITIVITY_00;
|
||||
}
|
||||
else if(not fsH and fsL) {
|
||||
sensitivity = L3GD20H::SENSITIVITY_01;
|
||||
}
|
||||
else {
|
||||
sensitivity = L3GD20H::SENSITIVITY_11;
|
||||
}
|
||||
|
||||
commandBuffer[1] = ctrlReg1Value;
|
||||
commandBuffer[2] = ctrlReg2Value;
|
||||
commandBuffer[3] = ctrlReg3Value;
|
||||
commandBuffer[4] = ctrlReg4Value;
|
||||
commandBuffer[5] = ctrlReg5Value;
|
||||
|
||||
rawPacket = commandBuffer;
|
||||
rawPacketLen = 6;
|
||||
break;
|
||||
}
|
||||
case(L3GD20H::READ_CTRL_REGS): {
|
||||
commandBuffer[0] = L3GD20H::READ_START | L3GD20H::AUTO_INCREMENT_MASK |
|
||||
L3GD20H::READ_MASK;
|
||||
|
||||
std::memset(commandBuffer + 1, 0, 5);
|
||||
rawPacket = commandBuffer;
|
||||
rawPacketLen = 6;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroHandlerL3GD20H::scanForReply(const uint8_t *start, size_t len,
|
||||
DeviceCommandId_t *foundId, size_t *foundLen) {
|
||||
/* For SPI, the ID will always be the one of the last sent command. */
|
||||
*foundId = this->getPendingCommand();
|
||||
*foundLen = this->rawPacketLen;
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroHandlerL3GD20H::interpretDeviceReply(DeviceCommandId_t id,
|
||||
const uint8_t *packet) {
|
||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||
switch(id) {
|
||||
case(L3GD20H::CONFIGURE_CTRL_REGS): {
|
||||
commandExecuted = true;
|
||||
break;
|
||||
}
|
||||
case(L3GD20H::READ_CTRL_REGS): {
|
||||
if(packet[1] == ctrlReg1Value and packet[2] == ctrlReg2Value and
|
||||
packet[3] == ctrlReg3Value and packet[4] == ctrlReg4Value and
|
||||
packet[5] == ctrlReg5Value) {
|
||||
commandExecuted = true;
|
||||
}
|
||||
else {
|
||||
/* Attempt reconfiguration. */
|
||||
internalState = InternalState::CONFIGURE;
|
||||
return DeviceHandlerIF::DEVICE_REPLY_INVALID;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(L3GD20H::READ_REGS): {
|
||||
if(packet[1] != ctrlReg1Value and packet[2] != ctrlReg2Value and
|
||||
packet[3] != ctrlReg3Value and packet[4] != ctrlReg4Value and
|
||||
packet[5] != ctrlReg5Value) {
|
||||
return DeviceHandlerIF::DEVICE_REPLY_INVALID;
|
||||
}
|
||||
else {
|
||||
if(internalState == InternalState::CHECK_REGS) {
|
||||
commandExecuted = true;
|
||||
}
|
||||
}
|
||||
|
||||
statusReg = packet[L3GD20H::STATUS_IDX];
|
||||
|
||||
int16_t angVelocXRaw = packet[L3GD20H::OUT_X_H] << 8 | packet[L3GD20H::OUT_X_L];
|
||||
int16_t angVelocYRaw = packet[L3GD20H::OUT_Y_H] << 8 | packet[L3GD20H::OUT_Y_L];
|
||||
int16_t angVelocZRaw = packet[L3GD20H::OUT_Z_H] << 8 | packet[L3GD20H::OUT_Z_L];
|
||||
float angVelocX = angVelocXRaw * sensitivity;
|
||||
float angVelocY = angVelocYRaw * sensitivity;
|
||||
float angVelocZ = angVelocZRaw * sensitivity;
|
||||
|
||||
int8_t temperaturOffset = (-1) * packet[L3GD20H::TEMPERATURE_IDX];
|
||||
float temperature = 25.0 + temperaturOffset;
|
||||
#if FSFW_HAL_L3GD20_GYRO_DEBUG == 1
|
||||
if(debugDivider->checkAndIncrement()) {
|
||||
/* Set terminal to utf-8 if there is an issue with micro printout. */
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "GyroHandlerL3GD20H: Angular velocities in degrees per second:" <<
|
||||
std::endl;
|
||||
sif::info << "X: " << angVelocX << " \xC2\xB0" << std::endl;
|
||||
sif::info << "Y: " << angVelocY << " \xC2\xB0" << std::endl;
|
||||
sif::info << "Z: " << angVelocZ << " \xC2\xB0" << std::endl;
|
||||
#else
|
||||
sif::printInfo("GyroHandlerL3GD20H: Angular velocities in degrees per second:\n");
|
||||
sif::printInfo("X: %f\n", angVelocX);
|
||||
sif::printInfo("Y: %f\n", angVelocY);
|
||||
sif::printInfo("Z: %f\n", angVelocZ);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
PoolReadGuard readSet(&dataset);
|
||||
if(readSet.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
|
||||
dataset.angVelocX = angVelocX;
|
||||
dataset.angVelocY = angVelocY;
|
||||
dataset.angVelocZ = angVelocZ;
|
||||
dataset.temperature = temperature;
|
||||
dataset.setValidity(true, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint32_t GyroHandlerL3GD20H::getTransitionDelayMs(Mode_t from, Mode_t to) {
|
||||
return 10000;
|
||||
}
|
||||
|
||||
void GyroHandlerL3GD20H::setGoNormalModeAtStartup() {
|
||||
this->goNormalModeImmediately = true;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroHandlerL3GD20H::initializeLocalDataPool(
|
||||
localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
|
||||
localDataPoolMap.emplace(L3GD20H::ANG_VELOC_X,
|
||||
new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(L3GD20H::ANG_VELOC_Y,
|
||||
new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(L3GD20H::ANG_VELOC_Z,
|
||||
new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(L3GD20H::TEMPERATURE,
|
||||
new PoolEntry<float>({0.0}));
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
void GyroHandlerL3GD20H::fillCommandAndReplyMap() {
|
||||
insertInCommandAndReplyMap(L3GD20H::READ_REGS, 1, &dataset);
|
||||
insertInCommandAndReplyMap(L3GD20H::CONFIGURE_CTRL_REGS, 1);
|
||||
insertInCommandAndReplyMap(L3GD20H::READ_CTRL_REGS, 1);
|
||||
}
|
||||
|
||||
void GyroHandlerL3GD20H::modeChanged() {
|
||||
internalState = InternalState::NONE;
|
||||
}
|
86
hal/src/fsfw/hal/devicehandlers/GyroL3GD20Handler.h
Normal file
86
hal/src/fsfw/hal/devicehandlers/GyroL3GD20Handler.h
Normal file
@ -0,0 +1,86 @@
|
||||
#ifndef MISSION_DEVICES_GYROL3GD20HANDLER_H_
|
||||
#define MISSION_DEVICES_GYROL3GD20HANDLER_H_
|
||||
|
||||
#include "OBSWConfig.h"
|
||||
#include "devicedefinitions/GyroL3GD20Definitions.h"
|
||||
|
||||
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
|
||||
#include <fsfw/globalfunctions/PeriodicOperationDivider.h>
|
||||
|
||||
#ifndef FSFW_HAL_L3GD20_GYRO_DEBUG
|
||||
#define FSFW_HAL_L3GD20_GYRO_DEBUG 1
|
||||
#endif /* FSFW_HAL_L3GD20_GYRO_DEBUG */
|
||||
|
||||
/**
|
||||
* @brief Device Handler for the L3GD20H gyroscope sensor
|
||||
* (https://www.st.com/en/mems-and-sensors/l3gd20h.html)
|
||||
* @details
|
||||
* Advanced documentation:
|
||||
* https://egit.irs.uni-stuttgart.de/redmine/projects/eive-flight-manual/wiki/L3GD20H_Gyro
|
||||
*
|
||||
* Data is read big endian with the smallest possible range of 245 degrees per second.
|
||||
*/
|
||||
class GyroHandlerL3GD20H: public DeviceHandlerBase {
|
||||
public:
|
||||
GyroHandlerL3GD20H(object_id_t objectId, object_id_t deviceCommunication,
|
||||
CookieIF* comCookie);
|
||||
virtual ~GyroHandlerL3GD20H();
|
||||
|
||||
void setGoNormalModeAtStartup();
|
||||
protected:
|
||||
|
||||
/* DeviceHandlerBase overrides */
|
||||
ReturnValue_t buildTransitionDeviceCommand(
|
||||
DeviceCommandId_t *id) override;
|
||||
void doStartUp() override;
|
||||
void doShutDown() override;
|
||||
ReturnValue_t buildNormalDeviceCommand(
|
||||
DeviceCommandId_t *id) override;
|
||||
ReturnValue_t buildCommandFromCommand(
|
||||
DeviceCommandId_t deviceCommand, const uint8_t *commandData,
|
||||
size_t commandDataLen) override;
|
||||
ReturnValue_t scanForReply(const uint8_t *start, size_t len,
|
||||
DeviceCommandId_t *foundId, size_t *foundLen) override;
|
||||
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id,
|
||||
const uint8_t *packet) override;
|
||||
|
||||
void fillCommandAndReplyMap() override;
|
||||
void modeChanged() override;
|
||||
uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) override;
|
||||
|
||||
private:
|
||||
GyroPrimaryDataset dataset;
|
||||
|
||||
enum class InternalState {
|
||||
NONE,
|
||||
CONFIGURE,
|
||||
CHECK_REGS,
|
||||
NORMAL
|
||||
};
|
||||
InternalState internalState = InternalState::NONE;
|
||||
bool commandExecuted = false;
|
||||
|
||||
uint8_t statusReg = 0;
|
||||
bool goNormalModeImmediately = false;
|
||||
|
||||
uint8_t ctrlReg1Value = L3GD20H::CTRL_REG_1_VAL;
|
||||
uint8_t ctrlReg2Value = L3GD20H::CTRL_REG_2_VAL;
|
||||
uint8_t ctrlReg3Value = L3GD20H::CTRL_REG_3_VAL;
|
||||
uint8_t ctrlReg4Value = L3GD20H::CTRL_REG_4_VAL;
|
||||
uint8_t ctrlReg5Value = L3GD20H::CTRL_REG_5_VAL;
|
||||
|
||||
uint8_t commandBuffer[L3GD20H::READ_LEN + 1];
|
||||
|
||||
// Set default value
|
||||
float sensitivity = L3GD20H::SENSITIVITY_00;
|
||||
|
||||
#if FSFW_HAL_L3GD20_GYRO_DEBUG == 1
|
||||
PeriodicOperationDivider* debugDivider = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* MISSION_DEVICES_GYROL3GD20HANDLER_H_ */
|
@ -0,0 +1,143 @@
|
||||
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_GYROL3GD20DEFINITIONS_H_
|
||||
#define MISSION_DEVICES_DEVICEDEFINITIONS_GYROL3GD20DEFINITIONS_H_
|
||||
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace L3GD20H {
|
||||
|
||||
/* Actual size is 15 but we round up a bit */
|
||||
static constexpr size_t MAX_BUFFER_SIZE = 16;
|
||||
|
||||
static constexpr uint8_t READ_MASK = 0b10000000;
|
||||
|
||||
static constexpr uint8_t AUTO_INCREMENT_MASK = 0b01000000;
|
||||
|
||||
static constexpr uint8_t WHO_AM_I_REG = 0b00001111;
|
||||
static constexpr uint8_t WHO_AM_I_VAL = 0b11010111;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Control registers */
|
||||
/*------------------------------------------------------------------------*/
|
||||
static constexpr uint8_t CTRL_REG_1 = 0b00100000;
|
||||
static constexpr uint8_t CTRL_REG_2 = 0b00100001;
|
||||
static constexpr uint8_t CTRL_REG_3 = 0b00100010;
|
||||
static constexpr uint8_t CTRL_REG_4 = 0b00100011;
|
||||
static constexpr uint8_t CTRL_REG_5 = 0b00100100;
|
||||
|
||||
/* Register 1 */
|
||||
static constexpr uint8_t SET_DR_1 = 1 << 7;
|
||||
static constexpr uint8_t SET_DR_0 = 1 << 6;
|
||||
static constexpr uint8_t SET_BW_1 = 1 << 5;
|
||||
static constexpr uint8_t SET_BW_0 = 1 << 4;
|
||||
static constexpr uint8_t SET_POWER_NORMAL_MODE = 1 << 3;
|
||||
static constexpr uint8_t SET_Z_ENABLE = 1 << 2;
|
||||
static constexpr uint8_t SET_X_ENABLE = 1 << 1;
|
||||
static constexpr uint8_t SET_Y_ENABLE = 1;
|
||||
|
||||
static constexpr uint8_t CTRL_REG_1_VAL = SET_POWER_NORMAL_MODE | SET_Z_ENABLE |
|
||||
SET_Y_ENABLE | SET_X_ENABLE;
|
||||
|
||||
/* Register 2 */
|
||||
static constexpr uint8_t EXTERNAL_EDGE_ENB = 1 << 7;
|
||||
static constexpr uint8_t LEVEL_SENSITIVE_TRIGGER = 1 << 6;
|
||||
static constexpr uint8_t SET_HPM_1 = 1 << 5;
|
||||
static constexpr uint8_t SET_HPM_0 = 1 << 4;
|
||||
static constexpr uint8_t SET_HPCF_3 = 1 << 3;
|
||||
static constexpr uint8_t SET_HPCF_2 = 1 << 2;
|
||||
static constexpr uint8_t SET_HPCF_1 = 1 << 1;
|
||||
static constexpr uint8_t SET_HPCF_0 = 1;
|
||||
|
||||
static constexpr uint8_t CTRL_REG_2_VAL = 0b00000000;
|
||||
|
||||
/* Register 3 */
|
||||
static constexpr uint8_t CTRL_REG_3_VAL = 0b00000000;
|
||||
|
||||
/* Register 4 */
|
||||
static constexpr uint8_t SET_BNU = 1 << 7;
|
||||
static constexpr uint8_t SET_BLE = 1 << 6;
|
||||
static constexpr uint8_t SET_FS_1 = 1 << 5;
|
||||
static constexpr uint8_t SET_FS_0 = 1 << 4;
|
||||
static constexpr uint8_t SET_IMP_ENB = 1 << 3;
|
||||
static constexpr uint8_t SET_SELF_TEST_ENB_1 = 1 << 2;
|
||||
static constexpr uint8_t SET_SELF_TEST_ENB_0 = 1 << 1;
|
||||
static constexpr uint8_t SET_SPI_IF_SELECT = 1;
|
||||
|
||||
/* Enable big endian data format */
|
||||
static constexpr uint8_t CTRL_REG_4_VAL = SET_BLE;
|
||||
|
||||
/* Register 5 */
|
||||
static constexpr uint8_t SET_REBOOT_MEM = 1 << 7;
|
||||
static constexpr uint8_t SET_FIFO_ENB = 1 << 6;
|
||||
|
||||
static constexpr uint8_t CTRL_REG_5_VAL = 0b00000000;
|
||||
|
||||
/* Possible range values in degrees per second (DPS). */
|
||||
static constexpr uint16_t RANGE_DPS_00 = 245;
|
||||
static constexpr float SENSITIVITY_00 = 8.75 * 0.001;
|
||||
static constexpr uint16_t RANGE_DPS_01 = 500;
|
||||
static constexpr float SENSITIVITY_01 = 17.5 * 0.001;
|
||||
static constexpr uint16_t RANGE_DPS_11 = 2000;
|
||||
static constexpr float SENSITIVITY_11 = 70.0 * 0.001;
|
||||
|
||||
static constexpr uint8_t READ_START = CTRL_REG_1;
|
||||
static constexpr size_t READ_LEN = 14;
|
||||
|
||||
/* Indexing */
|
||||
static constexpr uint8_t REFERENCE_IDX = 6;
|
||||
static constexpr uint8_t TEMPERATURE_IDX = 7;
|
||||
static constexpr uint8_t STATUS_IDX = 8;
|
||||
static constexpr uint8_t OUT_X_H = 9;
|
||||
static constexpr uint8_t OUT_X_L = 10;
|
||||
static constexpr uint8_t OUT_Y_H = 11;
|
||||
static constexpr uint8_t OUT_Y_L = 12;
|
||||
static constexpr uint8_t OUT_Z_H = 13;
|
||||
static constexpr uint8_t OUT_Z_L = 14;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Device Handler specific */
|
||||
/*------------------------------------------------------------------------*/
|
||||
static constexpr DeviceCommandId_t READ_REGS = 0;
|
||||
static constexpr DeviceCommandId_t CONFIGURE_CTRL_REGS = 1;
|
||||
static constexpr DeviceCommandId_t READ_CTRL_REGS = 2;
|
||||
|
||||
static constexpr uint32_t GYRO_DATASET_ID = READ_REGS;
|
||||
|
||||
enum GyroPoolIds: lp_id_t {
|
||||
ANG_VELOC_X,
|
||||
ANG_VELOC_Y,
|
||||
ANG_VELOC_Z,
|
||||
TEMPERATURE
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class GyroPrimaryDataset: public StaticLocalDataSet<5> {
|
||||
public:
|
||||
|
||||
/** Constructor for data users like controllers */
|
||||
GyroPrimaryDataset(object_id_t mgmId):
|
||||
StaticLocalDataSet(sid_t(mgmId, L3GD20H::GYRO_DATASET_ID)) {
|
||||
setAllVariablesReadOnly();
|
||||
}
|
||||
|
||||
/* Angular velocities in degrees per second (DPS) */
|
||||
lp_var_t<float> angVelocX = lp_var_t<float>(sid.objectId,
|
||||
L3GD20H::ANG_VELOC_X, this);
|
||||
lp_var_t<float> angVelocY = lp_var_t<float>(sid.objectId,
|
||||
L3GD20H::ANG_VELOC_Y, this);
|
||||
lp_var_t<float> angVelocZ = lp_var_t<float>(sid.objectId,
|
||||
L3GD20H::ANG_VELOC_Z, this);
|
||||
lp_var_t<float> temperature = lp_var_t<float>(sid.objectId,
|
||||
L3GD20H::TEMPERATURE, this);
|
||||
private:
|
||||
|
||||
friend class GyroHandlerL3GD20H;
|
||||
/** Constructor for the data creator */
|
||||
GyroPrimaryDataset(HasLocalDataPoolIF* hkOwner):
|
||||
StaticLocalDataSet(hkOwner, L3GD20H::GYRO_DATASET_ID) {}
|
||||
};
|
||||
|
||||
|
||||
#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_GYROL3GD20DEFINITIONS_H_ */
|
1
hal/src/fsfw/hal/host/CMakeLists.txt
Normal file
1
hal/src/fsfw/hal/host/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
|
13
hal/src/fsfw/hal/linux/CMakeLists.txt
Normal file
13
hal/src/fsfw/hal/linux/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
if(FSFW_HAL_ADD_RASPBERRY_PI)
|
||||
add_subdirectory(rpi)
|
||||
endif()
|
||||
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
UnixFileGuard.cpp
|
||||
utility.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(gpio)
|
||||
add_subdirectory(spi)
|
||||
add_subdirectory(i2c)
|
||||
add_subdirectory(uart)
|
33
hal/src/fsfw/hal/linux/UnixFileGuard.cpp
Normal file
33
hal/src/fsfw/hal/linux/UnixFileGuard.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "fsfw/hal/linux/UnixFileGuard.h"
|
||||
|
||||
UnixFileGuard::UnixFileGuard(std::string device, int* fileDescriptor, int flags,
|
||||
std::string diagnosticPrefix):
|
||||
fileDescriptor(fileDescriptor) {
|
||||
if(fileDescriptor == nullptr) {
|
||||
return;
|
||||
}
|
||||
*fileDescriptor = open(device.c_str(), flags);
|
||||
if (*fileDescriptor < 0) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << diagnosticPrefix <<"Opening device failed with error code " << errno <<
|
||||
"." << std::endl;
|
||||
sif::warning << "Error description: " << strerror(errno) << std::endl;
|
||||
#else
|
||||
sif::printError("%sOpening device failed with error code %d.\n", diagnosticPrefix);
|
||||
sif::printWarning("Error description: %s\n", strerror(errno));
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
openStatus = OPEN_FILE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
UnixFileGuard::~UnixFileGuard() {
|
||||
if(fileDescriptor != nullptr) {
|
||||
close(*fileDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t UnixFileGuard::getOpenResult() const {
|
||||
return openStatus;
|
||||
}
|
33
hal/src/fsfw/hal/linux/UnixFileGuard.h
Normal file
33
hal/src/fsfw/hal/linux/UnixFileGuard.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef LINUX_UTILITY_UNIXFILEGUARD_H_
|
||||
#define LINUX_UTILITY_UNIXFILEGUARD_H_
|
||||
|
||||
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
class UnixFileGuard {
|
||||
public:
|
||||
static constexpr int READ_WRITE_FLAG = O_RDWR;
|
||||
static constexpr int READ_ONLY_FLAG = O_RDONLY;
|
||||
static constexpr int NON_BLOCKING_IO_FLAG = O_NONBLOCK;
|
||||
|
||||
static constexpr ReturnValue_t OPEN_FILE_FAILED = 1;
|
||||
|
||||
UnixFileGuard(std::string device, int* fileDescriptor, int flags,
|
||||
std::string diagnosticPrefix = "");
|
||||
|
||||
virtual~ UnixFileGuard();
|
||||
|
||||
ReturnValue_t getOpenResult() const;
|
||||
private:
|
||||
int* fileDescriptor = nullptr;
|
||||
ReturnValue_t openStatus = HasReturnvaluesIF::RETURN_OK;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* LINUX_UTILITY_UNIXFILEGUARD_H_ */
|
12
hal/src/fsfw/hal/linux/gpio/CMakeLists.txt
Normal file
12
hal/src/fsfw/hal/linux/gpio/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
LinuxLibgpioIF.cpp
|
||||
)
|
||||
|
||||
# This abstraction layer requires the gpiod library. You can install this library
|
||||
# with "sudo apt-get install -y libgpiod-dev". If you are cross-compiling, you need
|
||||
# to install the package before syncing the sysroot to your host computer.
|
||||
find_library(LIB_GPIO gpiod REQUIRED)
|
||||
|
||||
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
|
||||
${LIB_GPIO}
|
||||
)
|
305
hal/src/fsfw/hal/linux/gpio/LinuxLibgpioIF.cpp
Normal file
305
hal/src/fsfw/hal/linux/gpio/LinuxLibgpioIF.cpp
Normal file
@ -0,0 +1,305 @@
|
||||
#include "fsfw/hal/linux/gpio/LinuxLibgpioIF.h"
|
||||
#include "fsfw/hal/common/gpio/gpioDefinitions.h"
|
||||
#include "fsfw/hal/common/gpio/GpioCookie.h"
|
||||
|
||||
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
#include <gpiod.h>
|
||||
|
||||
LinuxLibgpioIF::LinuxLibgpioIF(object_id_t objectId) : SystemObject(objectId) {
|
||||
}
|
||||
|
||||
LinuxLibgpioIF::~LinuxLibgpioIF() {
|
||||
for(auto& config: gpioMap) {
|
||||
delete(config.second);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::addGpios(GpioCookie* gpioCookie) {
|
||||
ReturnValue_t result;
|
||||
if(gpioCookie == nullptr) {
|
||||
sif::error << "LinuxLibgpioIF::initialize: Invalid cookie" << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
GpioMap mapToAdd = gpioCookie->getGpioMap();
|
||||
|
||||
/* Check whether this ID already exists in the map and remove duplicates */
|
||||
result = checkForConflicts(mapToAdd);
|
||||
if (result != RETURN_OK){
|
||||
return result;
|
||||
}
|
||||
|
||||
result = configureGpios(mapToAdd);
|
||||
if (result != RETURN_OK) {
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
/* Register new GPIOs in gpioMap */
|
||||
gpioMap.insert(mapToAdd.begin(), mapToAdd.end());
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
|
||||
for(auto& gpioConfig: mapToAdd) {
|
||||
switch(gpioConfig.second->gpioType) {
|
||||
case(gpio::GpioTypes::NONE): {
|
||||
return GPIO_INVALID_INSTANCE;
|
||||
}
|
||||
case(gpio::GpioTypes::GPIO_REGULAR): {
|
||||
GpiodRegular* regularGpio = dynamic_cast<GpiodRegular*>(gpioConfig.second);
|
||||
if(regularGpio == nullptr) {
|
||||
return GPIO_INVALID_INSTANCE;
|
||||
}
|
||||
configureRegularGpio(gpioConfig.first, regularGpio);
|
||||
break;
|
||||
}
|
||||
case(gpio::GpioTypes::CALLBACK): {
|
||||
auto gpioCallback = dynamic_cast<GpioCallback*>(gpioConfig.second);
|
||||
if(gpioCallback->callback == nullptr) {
|
||||
return GPIO_INVALID_INSTANCE;
|
||||
}
|
||||
gpioCallback->callback(gpioConfig.first, gpio::GpioOperation::WRITE,
|
||||
gpioCallback->initValue, gpioCallback->callbackArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, GpiodRegular *regularGpio) {
|
||||
std::string chipname;
|
||||
unsigned int lineNum;
|
||||
struct gpiod_chip *chip;
|
||||
gpio::Direction direction;
|
||||
std::string consumer;
|
||||
struct gpiod_line *lineHandle;
|
||||
int result = 0;
|
||||
|
||||
chipname = regularGpio->chipname;
|
||||
chip = gpiod_chip_open_by_name(chipname.c_str());
|
||||
if (!chip) {
|
||||
sif::warning << "LinuxLibgpioIF::configureRegularGpio: Failed to open chip "
|
||||
<< chipname << ". Gpio ID: " << gpioId << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
lineNum = regularGpio->lineNum;
|
||||
lineHandle = gpiod_chip_get_line(chip, lineNum);
|
||||
if (!lineHandle) {
|
||||
sif::debug << "LinuxLibgpioIF::configureRegularGpio: Failed to open line " << std::endl;
|
||||
sif::debug << "GPIO ID: " << gpioId << ", line number: " << lineNum <<
|
||||
", chipname: " << chipname << std::endl;
|
||||
sif::debug << "Check if linux GPIO configuration has changed. " << std::endl;
|
||||
gpiod_chip_close(chip);
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
direction = regularGpio->direction;
|
||||
consumer = regularGpio->consumer;
|
||||
/* Configure direction and add a description to the GPIO */
|
||||
switch (direction) {
|
||||
case(gpio::OUT): {
|
||||
result = gpiod_line_request_output(lineHandle, consumer.c_str(),
|
||||
regularGpio->initValue);
|
||||
if (result < 0) {
|
||||
sif::error << "LinuxLibgpioIF::configureRegularGpio: Failed to request line " << lineNum <<
|
||||
" from GPIO instance with ID: " << gpioId << std::endl;
|
||||
gpiod_line_release(lineHandle);
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(gpio::IN): {
|
||||
result = gpiod_line_request_input(lineHandle, consumer.c_str());
|
||||
if (result < 0) {
|
||||
sif::error << "LinuxLibgpioIF::configureGpios: Failed to request line "
|
||||
<< lineNum << " from GPIO instance with ID: " << gpioId << std::endl;
|
||||
gpiod_line_release(lineHandle);
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sif::error << "LinuxLibgpioIF::configureGpios: Invalid direction specified"
|
||||
<< std::endl;
|
||||
return GPIO_INVALID_INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Write line handle to GPIO configuration instance so it can later be used to set or
|
||||
* read states of GPIOs.
|
||||
*/
|
||||
regularGpio->lineHandle = lineHandle;
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) {
|
||||
gpioMapIter = gpioMap.find(gpioId);
|
||||
if (gpioMapIter == gpioMap.end()) {
|
||||
sif::warning << "LinuxLibgpioIF::pullHigh: Unknown GPIO ID " << gpioId << std::endl;
|
||||
return UNKNOWN_GPIO_ID;
|
||||
}
|
||||
|
||||
if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIO_REGULAR) {
|
||||
return driveGpio(gpioId, dynamic_cast<GpiodRegular*>(gpioMapIter->second), 1);
|
||||
}
|
||||
else {
|
||||
auto gpioCallback = dynamic_cast<GpioCallback*>(gpioMapIter->second);
|
||||
if(gpioCallback->callback == nullptr) {
|
||||
return GPIO_INVALID_INSTANCE;
|
||||
}
|
||||
gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE,
|
||||
1, gpioCallback->callbackArgs);
|
||||
return RETURN_OK;
|
||||
}
|
||||
return GPIO_TYPE_FAILURE;
|
||||
}
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId) {
|
||||
gpioMapIter = gpioMap.find(gpioId);
|
||||
if (gpioMapIter == gpioMap.end()) {
|
||||
sif::warning << "LinuxLibgpioIF::pullLow: Unknown GPIO ID " << gpioId << std::endl;
|
||||
return UNKNOWN_GPIO_ID;
|
||||
}
|
||||
|
||||
if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIO_REGULAR) {
|
||||
return driveGpio(gpioId, dynamic_cast<GpiodRegular*>(gpioMapIter->second), 0);
|
||||
}
|
||||
else {
|
||||
auto gpioCallback = dynamic_cast<GpioCallback*>(gpioMapIter->second);
|
||||
if(gpioCallback->callback == nullptr) {
|
||||
return GPIO_INVALID_INSTANCE;
|
||||
}
|
||||
gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE,
|
||||
0, gpioCallback->callbackArgs);
|
||||
return RETURN_OK;
|
||||
}
|
||||
return GPIO_TYPE_FAILURE;
|
||||
}
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::driveGpio(gpioId_t gpioId,
|
||||
GpiodRegular* regularGpio, unsigned int logicLevel) {
|
||||
if(regularGpio == nullptr) {
|
||||
return GPIO_TYPE_FAILURE;
|
||||
}
|
||||
|
||||
int result = gpiod_line_set_value(regularGpio->lineHandle, logicLevel);
|
||||
if (result < 0) {
|
||||
sif::warning << "LinuxLibgpioIF::driveGpio: Failed to pull GPIO with ID " << gpioId <<
|
||||
" to logic level " << logicLevel << std::endl;
|
||||
return DRIVE_GPIO_FAILURE;
|
||||
}
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) {
|
||||
gpioMapIter = gpioMap.find(gpioId);
|
||||
if (gpioMapIter == gpioMap.end()){
|
||||
sif::warning << "LinuxLibgpioIF::readGpio: Unknown GPIOD ID " << gpioId << std::endl;
|
||||
return UNKNOWN_GPIO_ID;
|
||||
}
|
||||
|
||||
if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIO_REGULAR) {
|
||||
GpiodRegular* regularGpio = dynamic_cast<GpiodRegular*>(gpioMapIter->second);
|
||||
if(regularGpio == nullptr) {
|
||||
return GPIO_TYPE_FAILURE;
|
||||
}
|
||||
*gpioState = gpiod_line_get_value(regularGpio->lineHandle);
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::checkForConflicts(GpioMap& mapToAdd){
|
||||
ReturnValue_t status = HasReturnvaluesIF::RETURN_OK;
|
||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||
for(auto& gpioConfig: mapToAdd) {
|
||||
switch(gpioConfig.second->gpioType) {
|
||||
case(gpio::GpioTypes::GPIO_REGULAR): {
|
||||
auto regularGpio = dynamic_cast<GpiodRegular*>(gpioConfig.second);
|
||||
if(regularGpio == nullptr) {
|
||||
return GPIO_TYPE_FAILURE;
|
||||
}
|
||||
/* Check for conflicts and remove duplicates if necessary */
|
||||
result = checkForConflictsRegularGpio(gpioConfig.first, regularGpio, mapToAdd);
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
status = result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(gpio::GpioTypes::CALLBACK): {
|
||||
auto callbackGpio = dynamic_cast<GpioCallback*>(gpioConfig.second);
|
||||
if(callbackGpio == nullptr) {
|
||||
return GPIO_TYPE_FAILURE;
|
||||
}
|
||||
/* Check for conflicts and remove duplicates if necessary */
|
||||
result = checkForConflictsCallbackGpio(gpioConfig.first, callbackGpio, mapToAdd);
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
status = result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::checkForConflictsRegularGpio(gpioId_t gpioIdToCheck,
|
||||
GpiodRegular* gpioToCheck, GpioMap& mapToAdd) {
|
||||
/* Cross check with private map */
|
||||
gpioMapIter = gpioMap.find(gpioIdToCheck);
|
||||
if(gpioMapIter != gpioMap.end()) {
|
||||
if(gpioMapIter->second->gpioType != gpio::GpioTypes::GPIO_REGULAR) {
|
||||
sif::warning << "LinuxLibgpioIF::checkForConflicts: ID already exists for different "
|
||||
"GPIO type" << gpioIdToCheck << ". Removing duplicate." << std::endl;
|
||||
mapToAdd.erase(gpioIdToCheck);
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
auto ownRegularGpio = dynamic_cast<GpiodRegular*>(gpioMapIter->second);
|
||||
if(ownRegularGpio == nullptr) {
|
||||
return GPIO_TYPE_FAILURE;
|
||||
}
|
||||
|
||||
/* Remove element from map to add because a entry for this GPIO
|
||||
already exists */
|
||||
sif::warning << "LinuxLibgpioIF::checkForConflictsRegularGpio: Duplicate GPIO definition"
|
||||
<< " detected. Duplicate will be removed from map to add." << std::endl;
|
||||
mapToAdd.erase(gpioIdToCheck);
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LinuxLibgpioIF::checkForConflictsCallbackGpio(gpioId_t gpioIdToCheck,
|
||||
GpioCallback *callbackGpio, GpioMap& mapToAdd) {
|
||||
/* Cross check with private map */
|
||||
gpioMapIter = gpioMap.find(gpioIdToCheck);
|
||||
if(gpioMapIter != gpioMap.end()) {
|
||||
if(gpioMapIter->second->gpioType != gpio::GpioTypes::CALLBACK) {
|
||||
sif::warning << "LinuxLibgpioIF::checkForConflicts: ID already exists for different "
|
||||
"GPIO type" << gpioIdToCheck << ". Removing duplicate." << std::endl;
|
||||
mapToAdd.erase(gpioIdToCheck);
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
/* Remove element from map to add because a entry for this GPIO
|
||||
already exists */
|
||||
sif::warning << "LinuxLibgpioIF::checkForConflictsRegularGpio: Duplicate GPIO definition"
|
||||
<< " detected. Duplicate will be removed from map to add." << std::endl;
|
||||
mapToAdd.erase(gpioIdToCheck);
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
77
hal/src/fsfw/hal/linux/gpio/LinuxLibgpioIF.h
Normal file
77
hal/src/fsfw/hal/linux/gpio/LinuxLibgpioIF.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef LINUX_GPIO_LINUXLIBGPIOIF_H_
|
||||
#define LINUX_GPIO_LINUXLIBGPIOIF_H_
|
||||
|
||||
#include "../../common/gpio/GpioIF.h"
|
||||
#include <returnvalues/classIds.h>
|
||||
#include <fsfw/objectmanager/SystemObject.h>
|
||||
|
||||
class GpioCookie;
|
||||
|
||||
/**
|
||||
* @brief This class implements the GpioIF for a linux based system. The
|
||||
* implementation is based on the libgpiod lib which requires linux 4.8
|
||||
* or higher.
|
||||
* @note The Petalinux SDK from Xilinx supports libgpiod since Petalinux
|
||||
* 2019.1.
|
||||
*/
|
||||
class LinuxLibgpioIF : public GpioIF, public SystemObject {
|
||||
public:
|
||||
|
||||
static const uint8_t gpioRetvalId = CLASS_ID::HAL_GPIO;
|
||||
|
||||
static constexpr ReturnValue_t UNKNOWN_GPIO_ID =
|
||||
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 1);
|
||||
static constexpr ReturnValue_t DRIVE_GPIO_FAILURE =
|
||||
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 2);
|
||||
static constexpr ReturnValue_t GPIO_TYPE_FAILURE =
|
||||
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 3);
|
||||
static constexpr ReturnValue_t GPIO_INVALID_INSTANCE =
|
||||
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 4);
|
||||
|
||||
LinuxLibgpioIF(object_id_t objectId);
|
||||
virtual ~LinuxLibgpioIF();
|
||||
|
||||
ReturnValue_t addGpios(GpioCookie* gpioCookie) override;
|
||||
ReturnValue_t pullHigh(gpioId_t gpioId) override;
|
||||
ReturnValue_t pullLow(gpioId_t gpioId) override;
|
||||
ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) override;
|
||||
|
||||
private:
|
||||
/* Holds the information and configuration of all used GPIOs */
|
||||
GpioUnorderedMap gpioMap;
|
||||
GpioUnorderedMapIter gpioMapIter;
|
||||
|
||||
/**
|
||||
* @brief This functions drives line of a GPIO specified by the GPIO ID.
|
||||
*
|
||||
* @param gpioId The GPIO ID of the GPIO to drive.
|
||||
* @param logiclevel The logic level to set. O or 1.
|
||||
*/
|
||||
ReturnValue_t driveGpio(gpioId_t gpioId, GpiodRegular* regularGpio, unsigned int logiclevel);
|
||||
|
||||
ReturnValue_t configureRegularGpio(gpioId_t gpioId, GpiodRegular* regularGpio);
|
||||
|
||||
/**
|
||||
* @brief This function checks if GPIOs are already registered and whether
|
||||
* there exists a conflict in the GPIO configuration. E.g. the
|
||||
* direction.
|
||||
*
|
||||
* @param mapToAdd The GPIOs which shall be added to the gpioMap.
|
||||
*
|
||||
* @return RETURN_OK if successful, otherwise RETURN_FAILED
|
||||
*/
|
||||
ReturnValue_t checkForConflicts(GpioMap& mapToAdd);
|
||||
|
||||
ReturnValue_t checkForConflictsRegularGpio(gpioId_t gpiodId, GpiodRegular* regularGpio,
|
||||
GpioMap& mapToAdd);
|
||||
ReturnValue_t checkForConflictsCallbackGpio(gpioId_t gpiodId, GpioCallback* regularGpio,
|
||||
GpioMap& mapToAdd);
|
||||
|
||||
/**
|
||||
* @brief Performs the initial configuration of all GPIOs specified in the GpioMap mapToAdd.
|
||||
*/
|
||||
ReturnValue_t configureGpios(GpioMap& mapToAdd);
|
||||
|
||||
};
|
||||
|
||||
#endif /* LINUX_GPIO_LINUXLIBGPIOIF_H_ */
|
8
hal/src/fsfw/hal/linux/i2c/CMakeLists.txt
Normal file
8
hal/src/fsfw/hal/linux/i2c/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
target_sources(${LIB_FSFW_NAME} PUBLIC
|
||||
I2cComIF.cpp
|
||||
I2cCookie.cpp
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
205
hal/src/fsfw/hal/linux/i2c/I2cComIF.cpp
Normal file
205
hal/src/fsfw/hal/linux/i2c/I2cComIF.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
#include "fsfw/hal/linux/i2c/I2cComIF.h"
|
||||
#include "fsfw/hal/linux/utility.h"
|
||||
#include "fsfw/hal/linux/UnixFileGuard.h"
|
||||
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
I2cComIF::I2cComIF(object_id_t objectId): SystemObject(objectId){
|
||||
}
|
||||
|
||||
I2cComIF::~I2cComIF() {}
|
||||
|
||||
ReturnValue_t I2cComIF::initializeInterface(CookieIF* cookie) {
|
||||
|
||||
address_t i2cAddress;
|
||||
std::string deviceFile;
|
||||
|
||||
if(cookie == nullptr) {
|
||||
sif::error << "I2cComIF::initializeInterface: Invalid cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie);
|
||||
if(i2cCookie == nullptr) {
|
||||
sif::error << "I2cComIF::initializeInterface: Invalid I2C cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
i2cAddress = i2cCookie->getAddress();
|
||||
|
||||
i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress);
|
||||
if(i2cDeviceMapIter == i2cDeviceMap.end()) {
|
||||
size_t maxReplyLen = i2cCookie->getMaxReplyLen();
|
||||
I2cInstance i2cInstance = {std::vector<uint8_t>(maxReplyLen), 0};
|
||||
auto statusPair = i2cDeviceMap.emplace(i2cAddress, i2cInstance);
|
||||
if (not statusPair.second) {
|
||||
sif::error << "I2cComIF::initializeInterface: Failed to insert device with address " <<
|
||||
i2cAddress << "to I2C device " << "map" << std::endl;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
sif::error << "I2cComIF::initializeInterface: Device with address " << i2cAddress <<
|
||||
"already in use" << std::endl;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
ReturnValue_t I2cComIF::sendMessage(CookieIF *cookie,
|
||||
const uint8_t *sendData, size_t sendLen) {
|
||||
|
||||
ReturnValue_t result;
|
||||
int fd;
|
||||
std::string deviceFile;
|
||||
|
||||
if(sendData == nullptr) {
|
||||
sif::error << "I2cComIF::sendMessage: Send Data is nullptr"
|
||||
<< std::endl;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
if(sendLen == 0) {
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie);
|
||||
if(i2cCookie == nullptr) {
|
||||
sif::error << "I2cComIF::sendMessage: Invalid I2C Cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
address_t i2cAddress = i2cCookie->getAddress();
|
||||
i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress);
|
||||
if (i2cDeviceMapIter == i2cDeviceMap.end()) {
|
||||
sif::error << "I2cComIF::sendMessage: i2cAddress of Cookie not "
|
||||
<< "registered in i2cDeviceMap" << std::endl;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
deviceFile = i2cCookie->getDeviceFile();
|
||||
UnixFileGuard fileHelper(deviceFile, &fd, O_RDWR, "I2cComIF::sendMessage");
|
||||
if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
|
||||
return fileHelper.getOpenResult();
|
||||
}
|
||||
result = openDevice(deviceFile, i2cAddress, &fd);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK){
|
||||
return result;
|
||||
}
|
||||
|
||||
if (write(fd, sendData, sendLen) != (int)sendLen) {
|
||||
sif::error << "I2cComIF::sendMessage: Failed to send data to I2C "
|
||||
"device with error code " << errno << ". Error description: "
|
||||
<< strerror(errno) << std::endl;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t I2cComIF::getSendSuccess(CookieIF *cookie) {
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF *cookie,
|
||||
size_t requestLen) {
|
||||
ReturnValue_t result;
|
||||
int fd;
|
||||
std::string deviceFile;
|
||||
|
||||
if (requestLen == 0) {
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie);
|
||||
if(i2cCookie == nullptr) {
|
||||
sif::error << "I2cComIF::requestReceiveMessage: Invalid I2C Cookie!" << std::endl;
|
||||
i2cDeviceMapIter->second.replyLen = 0;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
address_t i2cAddress = i2cCookie->getAddress();
|
||||
i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress);
|
||||
if (i2cDeviceMapIter == i2cDeviceMap.end()) {
|
||||
sif::error << "I2cComIF::requestReceiveMessage: i2cAddress of Cookie not "
|
||||
<< "registered in i2cDeviceMap" << std::endl;
|
||||
i2cDeviceMapIter->second.replyLen = 0;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
deviceFile = i2cCookie->getDeviceFile();
|
||||
UnixFileGuard fileHelper(deviceFile, &fd, O_RDWR, "I2cComIF::requestReceiveMessage");
|
||||
if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
|
||||
return fileHelper.getOpenResult();
|
||||
}
|
||||
result = openDevice(deviceFile, i2cAddress, &fd);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK){
|
||||
i2cDeviceMapIter->second.replyLen = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t* replyBuffer = i2cDeviceMapIter->second.replyBuffer.data();
|
||||
|
||||
int readLen = read(fd, replyBuffer, requestLen);
|
||||
if (readLen != static_cast<int>(requestLen)) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1 and FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "I2cComIF::requestReceiveMessage: Reading from I2C "
|
||||
<< "device failed with error code " << errno <<". Description"
|
||||
<< " of error: " << strerror(errno) << std::endl;
|
||||
sif::error << "I2cComIF::requestReceiveMessage: Read only " << readLen << " from "
|
||||
<< requestLen << " bytes" << std::endl;
|
||||
#endif
|
||||
i2cDeviceMapIter->second.replyLen = 0;
|
||||
sif::debug << "I2cComIF::requestReceiveMessage: Read " << readLen << " of " << requestLen << " bytes" << std::endl;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
i2cDeviceMapIter->second.replyLen = requestLen;
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t I2cComIF::readReceivedMessage(CookieIF *cookie,
|
||||
uint8_t **buffer, size_t* size) {
|
||||
I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie);
|
||||
if(i2cCookie == nullptr) {
|
||||
sif::error << "I2cComIF::readReceivedMessage: Invalid I2C Cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
address_t i2cAddress = i2cCookie->getAddress();
|
||||
i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress);
|
||||
if (i2cDeviceMapIter == i2cDeviceMap.end()) {
|
||||
sif::error << "I2cComIF::readReceivedMessage: i2cAddress of Cookie not "
|
||||
<< "found in i2cDeviceMap" << std::endl;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
*buffer = i2cDeviceMapIter->second.replyBuffer.data();
|
||||
*size = i2cDeviceMapIter->second.replyLen;
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t I2cComIF::openDevice(std::string deviceFile,
|
||||
address_t i2cAddress, int* fileDescriptor) {
|
||||
|
||||
if (ioctl(*fileDescriptor, I2C_SLAVE, i2cAddress) < 0) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "I2cComIF: Specifying target device failed with error code " << errno << "."
|
||||
<< std::endl;
|
||||
sif::warning << "Error description " << strerror(errno) << std::endl;
|
||||
#else
|
||||
sif::printWarning("I2cComIF: Specifying target device failed with error code %d.\n");
|
||||
sif::printWarning("Error description: %s\n", strerror(errno));
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
61
hal/src/fsfw/hal/linux/i2c/I2cComIF.h
Normal file
61
hal/src/fsfw/hal/linux/i2c/I2cComIF.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef LINUX_I2C_I2COMIF_H_
|
||||
#define LINUX_I2C_I2COMIF_H_
|
||||
|
||||
#include "I2cCookie.h"
|
||||
#include <fsfw/objectmanager/SystemObject.h>
|
||||
#include <fsfw/devicehandlers/DeviceCommunicationIF.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief This is the communication interface for I2C devices connected
|
||||
* to a system running a Linux OS.
|
||||
*
|
||||
* @note The Xilinx Linux kernel might not support to read more than 255 bytes at once.
|
||||
*
|
||||
* @author J. Meier
|
||||
*/
|
||||
class I2cComIF: public DeviceCommunicationIF, public SystemObject {
|
||||
public:
|
||||
I2cComIF(object_id_t objectId);
|
||||
|
||||
virtual ~I2cComIF();
|
||||
|
||||
ReturnValue_t initializeInterface(CookieIF * cookie) override;
|
||||
ReturnValue_t sendMessage(CookieIF *cookie,const uint8_t *sendData,
|
||||
size_t sendLen) override;
|
||||
ReturnValue_t getSendSuccess(CookieIF *cookie) override;
|
||||
ReturnValue_t requestReceiveMessage(CookieIF *cookie,
|
||||
size_t requestLen) override;
|
||||
ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer,
|
||||
size_t *size) override;
|
||||
|
||||
private:
|
||||
|
||||
struct I2cInstance {
|
||||
std::vector<uint8_t> replyBuffer;
|
||||
size_t replyLen;
|
||||
};
|
||||
|
||||
using I2cDeviceMap = std::unordered_map<address_t, I2cInstance>;
|
||||
using I2cDeviceMapIter = I2cDeviceMap::iterator;
|
||||
|
||||
/* In this map all i2c devices will be registered with their address and
|
||||
* the appropriate file descriptor will be stored */
|
||||
I2cDeviceMap i2cDeviceMap;
|
||||
I2cDeviceMapIter i2cDeviceMapIter;
|
||||
|
||||
/**
|
||||
* @brief This function opens an I2C device and binds the opened file
|
||||
* to a specific I2C address.
|
||||
* @param deviceFile The name of the device file. E.g. i2c-0
|
||||
* @param i2cAddress The address of the i2c slave device.
|
||||
* @param fileDescriptor Pointer to device descriptor.
|
||||
* @return RETURN_OK if successful, otherwise RETURN_FAILED.
|
||||
*/
|
||||
ReturnValue_t openDevice(std::string deviceFile,
|
||||
address_t i2cAddress, int* fileDescriptor);
|
||||
};
|
||||
|
||||
#endif /* LINUX_I2C_I2COMIF_H_ */
|
20
hal/src/fsfw/hal/linux/i2c/I2cCookie.cpp
Normal file
20
hal/src/fsfw/hal/linux/i2c/I2cCookie.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "fsfw/hal/linux/i2c/I2cCookie.h"
|
||||
|
||||
I2cCookie::I2cCookie(address_t i2cAddress_, size_t maxReplyLen_,
|
||||
std::string deviceFile_) :
|
||||
i2cAddress(i2cAddress_), maxReplyLen(maxReplyLen_), deviceFile(deviceFile_) {
|
||||
}
|
||||
|
||||
address_t I2cCookie::getAddress() const {
|
||||
return i2cAddress;
|
||||
}
|
||||
|
||||
size_t I2cCookie::getMaxReplyLen() const {
|
||||
return maxReplyLen;
|
||||
}
|
||||
|
||||
std::string I2cCookie::getDeviceFile() const {
|
||||
return deviceFile;
|
||||
}
|
||||
|
||||
I2cCookie::~I2cCookie() {}
|
38
hal/src/fsfw/hal/linux/i2c/I2cCookie.h
Normal file
38
hal/src/fsfw/hal/linux/i2c/I2cCookie.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef LINUX_I2C_I2CCOOKIE_H_
|
||||
#define LINUX_I2C_I2CCOOKIE_H_
|
||||
|
||||
#include <fsfw/devicehandlers/CookieIF.h>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief Cookie for the i2cDeviceComIF.
|
||||
*
|
||||
* @author J. Meier
|
||||
*/
|
||||
class I2cCookie: public CookieIF {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Constructor for the I2C cookie.
|
||||
* @param i2cAddress_ The i2c address of the target device.
|
||||
* @param maxReplyLen_ The maximum expected length of a reply from the
|
||||
* target device.
|
||||
* @param devicFile_ The device file specifying the i2c interface to use. E.g. "/dev/i2c-0".
|
||||
*/
|
||||
I2cCookie(address_t i2cAddress_, size_t maxReplyLen_,
|
||||
std::string deviceFile_);
|
||||
|
||||
virtual ~I2cCookie();
|
||||
|
||||
address_t getAddress() const;
|
||||
size_t getMaxReplyLen() const;
|
||||
std::string getDeviceFile() const;
|
||||
|
||||
private:
|
||||
|
||||
address_t i2cAddress = 0;
|
||||
size_t maxReplyLen = 0;
|
||||
std::string deviceFile;
|
||||
};
|
||||
|
||||
#endif /* LINUX_I2C_I2CCOOKIE_H_ */
|
3
hal/src/fsfw/hal/linux/rpi/CMakeLists.txt
Normal file
3
hal/src/fsfw/hal/linux/rpi/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
GpioRPi.cpp
|
||||
)
|
38
hal/src/fsfw/hal/linux/rpi/GpioRPi.cpp
Normal file
38
hal/src/fsfw/hal/linux/rpi/GpioRPi.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "fsfw/FSFW.h"
|
||||
|
||||
#include "fsfw/hal/linux/rpi/GpioRPi.h"
|
||||
#include "fsfw/hal/common/gpio/GpioCookie.h"
|
||||
|
||||
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||
|
||||
|
||||
ReturnValue_t gpio::createRpiGpioConfig(GpioCookie* cookie, gpioId_t gpioId, int bcmPin,
|
||||
std::string consumer, gpio::Direction direction, int initValue) {
|
||||
if(cookie == nullptr) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
GpiodRegular* config = new GpiodRegular();
|
||||
/* Default chipname for Raspberry Pi. There is still gpiochip1 for expansion, but most users
|
||||
will not need this */
|
||||
config->chipname = "gpiochip0";
|
||||
|
||||
config->consumer = consumer;
|
||||
config->direction = direction;
|
||||
config->initValue = initValue;
|
||||
|
||||
/* Sanity check for the BCM pins before assigning it */
|
||||
if(bcmPin > 27) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "createRpiGpioConfig: BCM pin " << bcmPin << " invalid!" << std::endl;
|
||||
#else
|
||||
sif::printError("createRpiGpioConfig: BCM pin %d invalid!\n", bcmPin);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
config->lineNum = bcmPin;
|
||||
cookie->addGpio(gpioId, config);
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
26
hal/src/fsfw/hal/linux/rpi/GpioRPi.h
Normal file
26
hal/src/fsfw/hal/linux/rpi/GpioRPi.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef BSP_RPI_GPIO_GPIORPI_H_
|
||||
#define BSP_RPI_GPIO_GPIORPI_H_
|
||||
|
||||
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||
#include "../../common/gpio/gpioDefinitions.h"
|
||||
|
||||
class GpioCookie;
|
||||
|
||||
namespace gpio {
|
||||
|
||||
/**
|
||||
* Create a GpioConfig_t. This function does a sanity check on the BCM pin number and fails if the
|
||||
* BCM pin is invalid.
|
||||
* @param cookie Adds the configuration to this cookie directly
|
||||
* @param gpioId ID which identifies the GPIO configuration
|
||||
* @param bcmPin Raspberry Pi BCM pin
|
||||
* @param consumer Information string
|
||||
* @param direction GPIO direction
|
||||
* @param initValue Intial value for output pins, 0 for low, 1 for high
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t createRpiGpioConfig(GpioCookie* cookie, gpioId_t gpioId, int bcmPin,
|
||||
std::string consumer, gpio::Direction direction, int initValue);
|
||||
}
|
||||
|
||||
#endif /* BSP_RPI_GPIO_GPIORPI_H_ */
|
8
hal/src/fsfw/hal/linux/spi/CMakeLists.txt
Normal file
8
hal/src/fsfw/hal/linux/spi/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
target_sources(${LIB_FSFW_NAME} PUBLIC
|
||||
SpiComIF.cpp
|
||||
SpiCookie.cpp
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
398
hal/src/fsfw/hal/linux/spi/SpiComIF.cpp
Normal file
398
hal/src/fsfw/hal/linux/spi/SpiComIF.cpp
Normal file
@ -0,0 +1,398 @@
|
||||
#include "fsfw/FSFW.h"
|
||||
#include "fsfw/hal/linux/spi/SpiComIF.h"
|
||||
#include "fsfw/hal/linux/spi/SpiCookie.h"
|
||||
#include "fsfw/hal/linux/utility.h"
|
||||
#include "fsfw/hal/linux/UnixFileGuard.h"
|
||||
|
||||
#include <fsfw/ipc/MutexFactory.h>
|
||||
#include <fsfw/globalfunctions/arrayprinter.h>
|
||||
|
||||
#include <linux/spi/spidev.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
/* Can be used for low-level debugging of the SPI bus */
|
||||
#ifndef FSFW_HAL_LINUX_SPI_WIRETAPPING
|
||||
#define FSFW_HAL_LINUX_SPI_WIRETAPPING 0
|
||||
#endif
|
||||
|
||||
SpiComIF::SpiComIF(object_id_t objectId, GpioIF* gpioComIF):
|
||||
SystemObject(objectId), gpioComIF(gpioComIF) {
|
||||
if(gpioComIF == nullptr) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::SpiComIF: GPIO communication interface invalid!" << std::endl;
|
||||
#else
|
||||
sif::printError("SpiComIF::SpiComIF: GPIO communication interface invalid!\n");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
}
|
||||
|
||||
spiMutex = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) {
|
||||
int retval = 0;
|
||||
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||||
if(spiCookie == nullptr) {
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
address_t spiAddress = spiCookie->getSpiAddress();
|
||||
|
||||
auto iter = spiDeviceMap.find(spiAddress);
|
||||
if(iter == spiDeviceMap.end()) {
|
||||
size_t bufferSize = spiCookie->getMaxBufferSize();
|
||||
SpiInstance spiInstance(bufferSize);
|
||||
auto statusPair = spiDeviceMap.emplace(spiAddress, spiInstance);
|
||||
if (not statusPair.second) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::initializeInterface: Failed to insert device with address " <<
|
||||
spiAddress << "to SPI device map" << std::endl;
|
||||
#else
|
||||
sif::printError("SpiComIF::initializeInterface: Failed to insert device with address "
|
||||
"%lu to SPI device map\n", static_cast<unsigned long>(spiAddress));
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
/* Now we emplaced the read buffer in the map, we still need to assign that location
|
||||
to the SPI driver transfer struct */
|
||||
spiCookie->assignReadBuffer(statusPair.first->second.replyBuffer.data());
|
||||
}
|
||||
else {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::initializeInterface: SPI address already exists!" << std::endl;
|
||||
#else
|
||||
sif::printError("SpiComIF::initializeInterface: SPI address already exists!\n");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
/* Pull CS high in any case to be sure that device is inactive */
|
||||
gpioId_t gpioId = spiCookie->getChipSelectPin();
|
||||
if(gpioId != gpio::NO_GPIO) {
|
||||
gpioComIF->pullHigh(gpioId);
|
||||
}
|
||||
|
||||
size_t spiSpeed = 0;
|
||||
spi::SpiModes spiMode = spi::SpiModes::MODE_0;
|
||||
|
||||
SpiCookie::UncommonParameters params;
|
||||
spiCookie->getSpiParameters(spiMode, spiSpeed, ¶ms);
|
||||
|
||||
int fileDescriptor = 0;
|
||||
UnixFileGuard fileHelper(spiCookie->getSpiDevice(), &fileDescriptor, O_RDWR,
|
||||
"SpiComIF::initializeInterface: ");
|
||||
if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
|
||||
return fileHelper.getOpenResult();
|
||||
}
|
||||
|
||||
/* These flags are rather uncommon */
|
||||
if(params.threeWireSpi or params.noCs or params.csHigh) {
|
||||
uint32_t currentMode = 0;
|
||||
retval = ioctl(fileDescriptor, SPI_IOC_RD_MODE32, ¤tMode);
|
||||
if(retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::initialiezInterface: Could not read full mode!");
|
||||
}
|
||||
|
||||
if(params.threeWireSpi) {
|
||||
currentMode |= SPI_3WIRE;
|
||||
}
|
||||
if(params.noCs) {
|
||||
/* Some drivers like the Raspberry Pi ignore this flag in any case */
|
||||
currentMode |= SPI_NO_CS;
|
||||
}
|
||||
if(params.csHigh) {
|
||||
currentMode |= SPI_CS_HIGH;
|
||||
}
|
||||
/* Write adapted mode */
|
||||
retval = ioctl(fileDescriptor, SPI_IOC_WR_MODE32, ¤tMode);
|
||||
if(retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::initialiezInterface: Could not write full mode!");
|
||||
}
|
||||
}
|
||||
if(params.lsbFirst) {
|
||||
retval = ioctl(fileDescriptor, SPI_IOC_WR_LSB_FIRST, ¶ms.lsbFirst);
|
||||
if(retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::initializeInterface: Setting LSB first failed");
|
||||
}
|
||||
}
|
||||
if(params.bitsPerWord != 8) {
|
||||
retval = ioctl(fileDescriptor, SPI_IOC_WR_BITS_PER_WORD, ¶ms.bitsPerWord);
|
||||
if(retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::initializeInterface: "
|
||||
"Could not write bits per word!");
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::sendMessage(CookieIF *cookie, const uint8_t *sendData, size_t sendLen) {
|
||||
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||
|
||||
if(spiCookie == nullptr) {
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
if(sendLen > spiCookie->getMaxBufferSize()) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "SpiComIF::sendMessage: Too much data sent, send length" << sendLen <<
|
||||
"larger than maximum buffer length" << spiCookie->getMaxBufferSize() << std::endl;
|
||||
#else
|
||||
sif::printWarning("SpiComIF::sendMessage: Too much data sent, send length %lu larger "
|
||||
"than maximum buffer length %lu!\n", static_cast<unsigned long>(sendLen),
|
||||
static_cast<unsigned long>(spiCookie->getMaxBufferSize()));
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return DeviceCommunicationIF::TOO_MUCH_DATA;
|
||||
}
|
||||
|
||||
if(spiCookie->getComIfMode() == spi::SpiComIfModes::REGULAR) {
|
||||
result = performRegularSendOperation(spiCookie, sendData, sendLen);
|
||||
}
|
||||
else if(spiCookie->getComIfMode() == spi::SpiComIfModes::CALLBACK) {
|
||||
spi::send_callback_function_t sendFunc = nullptr;
|
||||
void* funcArgs = nullptr;
|
||||
spiCookie->getCallback(&sendFunc, &funcArgs);
|
||||
if(sendFunc != nullptr) {
|
||||
result = sendFunc(this, spiCookie, sendData, sendLen, funcArgs);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::performRegularSendOperation(SpiCookie *spiCookie, const uint8_t *sendData,
|
||||
size_t sendLen) {
|
||||
address_t spiAddress = spiCookie->getSpiAddress();
|
||||
auto iter = spiDeviceMap.find(spiAddress);
|
||||
if(iter != spiDeviceMap.end()) {
|
||||
spiCookie->assignReadBuffer(iter->second.replyBuffer.data());
|
||||
}
|
||||
|
||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||
int retval = 0;
|
||||
/* Prepare transfer */
|
||||
int fileDescriptor = 0;
|
||||
std::string device = spiCookie->getSpiDevice();
|
||||
UnixFileGuard fileHelper(device, &fileDescriptor, O_RDWR, "SpiComIF::sendMessage: ");
|
||||
if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
|
||||
return OPENING_FILE_FAILED;
|
||||
}
|
||||
spi::SpiModes spiMode = spi::SpiModes::MODE_0;
|
||||
uint32_t spiSpeed = 0;
|
||||
spiCookie->getSpiParameters(spiMode, spiSpeed, nullptr);
|
||||
setSpiSpeedAndMode(fileDescriptor, spiMode, spiSpeed);
|
||||
spiCookie->assignWriteBuffer(sendData);
|
||||
spiCookie->assignTransferSize(sendLen);
|
||||
|
||||
bool fullDuplex = spiCookie->isFullDuplex();
|
||||
gpioId_t gpioId = spiCookie->getChipSelectPin();
|
||||
|
||||
/* Pull SPI CS low. For now, no support for active high given */
|
||||
if(gpioId != gpio::NO_GPIO) {
|
||||
result = spiMutex->lockMutex(timeoutType, timeoutMs);
|
||||
if (result != RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::sendMessage: Failed to lock mutex" << std::endl;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
gpioComIF->pullLow(gpioId);
|
||||
}
|
||||
|
||||
/* Execute transfer */
|
||||
if(fullDuplex) {
|
||||
/* Initiate a full duplex SPI transfer. */
|
||||
retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), spiCookie->getTransferStructHandle());
|
||||
if(retval < 0) {
|
||||
utility::handleIoctlError("SpiComIF::sendMessage: ioctl error.");
|
||||
result = FULL_DUPLEX_TRANSFER_FAILED;
|
||||
}
|
||||
#if FSFW_HAL_LINUX_SPI_WIRETAPPING == 1
|
||||
performSpiWiretapping(spiCookie);
|
||||
#endif /* FSFW_LINUX_SPI_WIRETAPPING == 1 */
|
||||
}
|
||||
else {
|
||||
/* We write with a blocking half-duplex transfer here */
|
||||
if (write(fileDescriptor, sendData, sendLen) != static_cast<ssize_t>(sendLen)) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "SpiComIF::sendMessage: Half-Duplex write operation failed!" <<
|
||||
std::endl;
|
||||
#else
|
||||
sif::printWarning("SpiComIF::sendMessage: Half-Duplex write operation failed!\n");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
result = HALF_DUPLEX_TRANSFER_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if(gpioId != gpio::NO_GPIO) {
|
||||
gpioComIF->pullHigh(gpioId);
|
||||
result = spiMutex->unlockMutex();
|
||||
if (result != RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::sendMessage: Failed to unlock mutex" << std::endl;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::getSendSuccess(CookieIF *cookie) {
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::requestReceiveMessage(CookieIF *cookie, size_t requestLen) {
|
||||
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||||
if(spiCookie == nullptr) {
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
if(spiCookie->isFullDuplex()) {
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
return performHalfDuplexReception(spiCookie);
|
||||
}
|
||||
|
||||
|
||||
ReturnValue_t SpiComIF::performHalfDuplexReception(SpiCookie* spiCookie) {
|
||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||
std::string device = spiCookie->getSpiDevice();
|
||||
int fileDescriptor = 0;
|
||||
UnixFileGuard fileHelper(device, &fileDescriptor, O_RDWR,
|
||||
"SpiComIF::requestReceiveMessage: ");
|
||||
if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
|
||||
return OPENING_FILE_FAILED;
|
||||
}
|
||||
|
||||
uint8_t* rxBuf = nullptr;
|
||||
size_t readSize = spiCookie->getCurrentTransferSize();
|
||||
result = getReadBuffer(spiCookie->getSpiAddress(), &rxBuf);
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
gpioId_t gpioId = spiCookie->getChipSelectPin();
|
||||
if(gpioId != gpio::NO_GPIO) {
|
||||
result = spiMutex->lockMutex(timeoutType, timeoutMs);
|
||||
if (result != RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::getSendSuccess: Failed to lock mutex" << std::endl;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
gpioComIF->pullLow(gpioId);
|
||||
}
|
||||
|
||||
if(read(fileDescriptor, rxBuf, readSize) != static_cast<ssize_t>(readSize)) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "SpiComIF::sendMessage: Half-Duplex read operation failed!" << std::endl;
|
||||
#else
|
||||
sif::printWarning("SpiComIF::sendMessage: Half-Duplex read operation failed!\n");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
result = HALF_DUPLEX_TRANSFER_FAILED;
|
||||
}
|
||||
|
||||
if(gpioId != gpio::NO_GPIO) {
|
||||
gpioComIF->pullHigh(gpioId);
|
||||
result = spiMutex->unlockMutex();
|
||||
if (result != RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::getSendSuccess: Failed to unlock mutex" << std::endl;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) {
|
||||
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||||
if(spiCookie == nullptr) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
uint8_t* rxBuf = nullptr;
|
||||
ReturnValue_t result = getReadBuffer(spiCookie->getSpiAddress(), &rxBuf);
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
*buffer = rxBuf;
|
||||
*size = spiCookie->getCurrentTransferSize();
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
MutexIF* SpiComIF::getMutex(MutexIF::TimeoutType* timeoutType, uint32_t* timeoutMs) {
|
||||
if(timeoutType != nullptr) {
|
||||
*timeoutType = this->timeoutType;
|
||||
}
|
||||
if(timeoutMs != nullptr) {
|
||||
*timeoutMs = this->timeoutMs;
|
||||
}
|
||||
return spiMutex;
|
||||
}
|
||||
|
||||
void SpiComIF::performSpiWiretapping(SpiCookie* spiCookie) {
|
||||
if(spiCookie == nullptr) {
|
||||
return;
|
||||
}
|
||||
size_t dataLen = spiCookie->getTransferStructHandle()->len;
|
||||
uint8_t* dataPtr = reinterpret_cast<uint8_t*>(spiCookie->getTransferStructHandle()->tx_buf);
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "Sent SPI data: " << std::endl;
|
||||
arrayprinter::print(dataPtr, dataLen, OutputType::HEX, false);
|
||||
sif::info << "Received SPI data: " << std::endl;
|
||||
#else
|
||||
sif::printInfo("Sent SPI data: \n");
|
||||
arrayprinter::print(dataPtr, dataLen, OutputType::HEX, false);
|
||||
sif::printInfo("Received SPI data: \n");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
dataPtr = reinterpret_cast<uint8_t*>(spiCookie->getTransferStructHandle()->rx_buf);
|
||||
arrayprinter::print(dataPtr, dataLen, OutputType::HEX, false);
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::getReadBuffer(address_t spiAddress, uint8_t** buffer) {
|
||||
if(buffer == nullptr) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
auto iter = spiDeviceMap.find(spiAddress);
|
||||
if(iter == spiDeviceMap.end()) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
*buffer = iter->second.replyBuffer.data();
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
GpioIF* SpiComIF::getGpioInterface() {
|
||||
return gpioComIF;
|
||||
}
|
||||
|
||||
void SpiComIF::setSpiSpeedAndMode(int spiFd, spi::SpiModes mode, uint32_t speed) {
|
||||
int retval = ioctl(spiFd, SPI_IOC_WR_MODE, reinterpret_cast<uint8_t*>(&mode));
|
||||
if(retval != 0) {
|
||||
utility::handleIoctlError("SpiTestClass::performRm3100Test: Setting SPI mode failed!");
|
||||
}
|
||||
|
||||
retval = ioctl(spiFd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
|
||||
if(retval != 0) {
|
||||
utility::handleIoctlError("SpiTestClass::performRm3100Test: Setting SPI speed failed!");
|
||||
}
|
||||
}
|
90
hal/src/fsfw/hal/linux/spi/SpiComIF.h
Normal file
90
hal/src/fsfw/hal/linux/spi/SpiComIF.h
Normal file
@ -0,0 +1,90 @@
|
||||
#ifndef LINUX_SPI_SPICOMIF_H_
|
||||
#define LINUX_SPI_SPICOMIF_H_
|
||||
|
||||
#include "spiDefinitions.h"
|
||||
#include "returnvalues/classIds.h"
|
||||
#include "fsfw/hal/common/gpio/GpioIF.h"
|
||||
|
||||
#include "fsfw/devicehandlers/DeviceCommunicationIF.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
class SpiCookie;
|
||||
|
||||
/**
|
||||
* @brief Encapsulates access to linux SPI driver for FSFW objects
|
||||
* @details
|
||||
* Right now, only full-duplex SPI is supported. Most device specific transfer properties
|
||||
* are contained in the SPI cookie.
|
||||
* @author R. Mueller
|
||||
*/
|
||||
class SpiComIF: public DeviceCommunicationIF, public SystemObject {
|
||||
public:
|
||||
static constexpr uint8_t spiRetvalId = CLASS_ID::HAL_SPI;
|
||||
static constexpr ReturnValue_t OPENING_FILE_FAILED =
|
||||
HasReturnvaluesIF::makeReturnCode(spiRetvalId, 0);
|
||||
/* Full duplex (ioctl) transfer failure */
|
||||
static constexpr ReturnValue_t FULL_DUPLEX_TRANSFER_FAILED =
|
||||
HasReturnvaluesIF::makeReturnCode(spiRetvalId, 1);
|
||||
/* Half duplex (read/write) transfer failure */
|
||||
static constexpr ReturnValue_t HALF_DUPLEX_TRANSFER_FAILED =
|
||||
HasReturnvaluesIF::makeReturnCode(spiRetvalId, 2);
|
||||
|
||||
SpiComIF(object_id_t objectId, GpioIF* gpioComIF);
|
||||
|
||||
ReturnValue_t initializeInterface(CookieIF * cookie) override;
|
||||
ReturnValue_t sendMessage(CookieIF *cookie,const uint8_t *sendData,
|
||||
size_t sendLen) override;
|
||||
ReturnValue_t getSendSuccess(CookieIF *cookie) override;
|
||||
ReturnValue_t requestReceiveMessage(CookieIF *cookie,
|
||||
size_t requestLen) override;
|
||||
ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer,
|
||||
size_t *size) override;
|
||||
|
||||
/**
|
||||
* @brief This function returns the mutex which can be used to protect the spi bus when
|
||||
* the chip select must be driven from outside of the com if.
|
||||
*/
|
||||
MutexIF* getMutex(MutexIF::TimeoutType* timeoutType = nullptr, uint32_t* timeoutMs = nullptr);
|
||||
|
||||
/**
|
||||
* Perform a regular send operation using Linux iotcl. This is public so it can be used
|
||||
* in functions like a user callback if special handling is only necessary for certain commands.
|
||||
* @param spiCookie
|
||||
* @param sendData
|
||||
* @param sendLen
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t performRegularSendOperation(SpiCookie* spiCookie, const uint8_t *sendData,
|
||||
size_t sendLen);
|
||||
|
||||
GpioIF* getGpioInterface();
|
||||
void setSpiSpeedAndMode(int spiFd, spi::SpiModes mode, uint32_t speed);
|
||||
void performSpiWiretapping(SpiCookie* spiCookie);
|
||||
|
||||
ReturnValue_t getReadBuffer(address_t spiAddress, uint8_t** buffer);
|
||||
|
||||
private:
|
||||
|
||||
struct SpiInstance {
|
||||
SpiInstance(size_t maxRecvSize): replyBuffer(std::vector<uint8_t>(maxRecvSize)) {}
|
||||
std::vector<uint8_t> replyBuffer;
|
||||
};
|
||||
|
||||
GpioIF* gpioComIF = nullptr;
|
||||
|
||||
MutexIF* spiMutex = nullptr;
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||
uint32_t timeoutMs = 20;
|
||||
|
||||
using SpiDeviceMap = std::unordered_map<address_t, SpiInstance>;
|
||||
using SpiDeviceMapIter = SpiDeviceMap::iterator;
|
||||
|
||||
SpiDeviceMap spiDeviceMap;
|
||||
|
||||
ReturnValue_t performHalfDuplexReception(SpiCookie* spiCookie);
|
||||
};
|
||||
|
||||
#endif /* LINUX_SPI_SPICOMIF_H_ */
|
144
hal/src/fsfw/hal/linux/spi/SpiCookie.cpp
Normal file
144
hal/src/fsfw/hal/linux/spi/SpiCookie.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
#include "fsfw/hal/linux/spi/SpiCookie.h"
|
||||
|
||||
SpiCookie::SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev,
|
||||
const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed):
|
||||
SpiCookie(spi::SpiComIfModes::REGULAR, spiAddress, chipSelect, spiDev, maxSize, spiMode,
|
||||
spiSpeed, nullptr, nullptr) {
|
||||
|
||||
}
|
||||
|
||||
SpiCookie::SpiCookie(address_t spiAddress, std::string spiDev, const size_t maxSize,
|
||||
spi::SpiModes spiMode, uint32_t spiSpeed):
|
||||
SpiCookie(spiAddress, gpio::NO_GPIO, spiDev, maxSize, spiMode, spiSpeed) {
|
||||
}
|
||||
|
||||
SpiCookie::SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev,
|
||||
const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed,
|
||||
spi::send_callback_function_t callback, void *args):
|
||||
SpiCookie(spi::SpiComIfModes::CALLBACK, spiAddress, chipSelect, spiDev, maxSize,
|
||||
spiMode, spiSpeed, callback, args) {
|
||||
}
|
||||
|
||||
SpiCookie::SpiCookie(spi::SpiComIfModes comIfMode, address_t spiAddress, gpioId_t chipSelect,
|
||||
std::string spiDev, const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed,
|
||||
spi::send_callback_function_t callback, void* args):
|
||||
spiAddress(spiAddress), chipSelectPin(chipSelect), spiDevice(spiDev),
|
||||
comIfMode(comIfMode), maxSize(maxSize), spiMode(spiMode), spiSpeed(spiSpeed),
|
||||
sendCallback(callback), callbackArgs(args) {
|
||||
}
|
||||
|
||||
spi::SpiComIfModes SpiCookie::getComIfMode() const {
|
||||
return this->comIfMode;
|
||||
}
|
||||
|
||||
void SpiCookie::getSpiParameters(spi::SpiModes& spiMode, uint32_t& spiSpeed,
|
||||
UncommonParameters* parameters) const {
|
||||
spiMode = this->spiMode;
|
||||
spiSpeed = this->spiSpeed;
|
||||
|
||||
if(parameters != nullptr) {
|
||||
parameters->threeWireSpi = uncommonParameters.threeWireSpi;
|
||||
parameters->lsbFirst = uncommonParameters.lsbFirst;
|
||||
parameters->noCs = uncommonParameters.noCs;
|
||||
parameters->bitsPerWord = uncommonParameters.bitsPerWord;
|
||||
parameters->csHigh = uncommonParameters.csHigh;
|
||||
}
|
||||
}
|
||||
|
||||
gpioId_t SpiCookie::getChipSelectPin() const {
|
||||
return chipSelectPin;
|
||||
}
|
||||
|
||||
size_t SpiCookie::getMaxBufferSize() const {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
address_t SpiCookie::getSpiAddress() const {
|
||||
return spiAddress;
|
||||
}
|
||||
|
||||
std::string SpiCookie::getSpiDevice() const {
|
||||
return spiDevice;
|
||||
}
|
||||
|
||||
void SpiCookie::setThreeWireSpi(bool enable) {
|
||||
uncommonParameters.threeWireSpi = enable;
|
||||
}
|
||||
|
||||
void SpiCookie::setLsbFirst(bool enable) {
|
||||
uncommonParameters.lsbFirst = enable;
|
||||
}
|
||||
|
||||
void SpiCookie::setNoCs(bool enable) {
|
||||
uncommonParameters.noCs = enable;
|
||||
}
|
||||
|
||||
void SpiCookie::setBitsPerWord(uint8_t bitsPerWord) {
|
||||
uncommonParameters.bitsPerWord = bitsPerWord;
|
||||
}
|
||||
|
||||
void SpiCookie::setCsHigh(bool enable) {
|
||||
uncommonParameters.csHigh = enable;
|
||||
}
|
||||
|
||||
void SpiCookie::activateCsDeselect(bool deselectCs, uint16_t delayUsecs) {
|
||||
spiTransferStruct.cs_change = deselectCs;
|
||||
spiTransferStruct.delay_usecs = delayUsecs;
|
||||
}
|
||||
|
||||
void SpiCookie::assignReadBuffer(uint8_t* rx) {
|
||||
if(rx != nullptr) {
|
||||
spiTransferStruct.rx_buf = reinterpret_cast<__u64>(rx);
|
||||
}
|
||||
}
|
||||
|
||||
void SpiCookie::assignWriteBuffer(const uint8_t* tx) {
|
||||
if(tx != nullptr) {
|
||||
spiTransferStruct.tx_buf = reinterpret_cast<__u64>(tx);
|
||||
}
|
||||
}
|
||||
|
||||
void SpiCookie::setCallbackMode(spi::send_callback_function_t callback,
|
||||
void *args) {
|
||||
this->comIfMode = spi::SpiComIfModes::CALLBACK;
|
||||
this->sendCallback = callback;
|
||||
this->callbackArgs = args;
|
||||
}
|
||||
|
||||
void SpiCookie::setCallbackArgs(void *args) {
|
||||
this->callbackArgs = args;
|
||||
}
|
||||
|
||||
spi_ioc_transfer* SpiCookie::getTransferStructHandle() {
|
||||
return &spiTransferStruct;
|
||||
}
|
||||
|
||||
void SpiCookie::setFullOrHalfDuplex(bool halfDuplex) {
|
||||
this->halfDuplex = halfDuplex;
|
||||
}
|
||||
|
||||
bool SpiCookie::isFullDuplex() const {
|
||||
return not this->halfDuplex;
|
||||
}
|
||||
|
||||
void SpiCookie::assignTransferSize(size_t transferSize) {
|
||||
spiTransferStruct.len = transferSize;
|
||||
}
|
||||
|
||||
size_t SpiCookie::getCurrentTransferSize() const {
|
||||
return spiTransferStruct.len;
|
||||
}
|
||||
|
||||
void SpiCookie::setSpiSpeed(uint32_t newSpeed) {
|
||||
this->spiSpeed = newSpeed;
|
||||
}
|
||||
|
||||
void SpiCookie::setSpiMode(spi::SpiModes newMode) {
|
||||
this->spiMode = newMode;
|
||||
}
|
||||
|
||||
void SpiCookie::getCallback(spi::send_callback_function_t *callback,
|
||||
void **args) {
|
||||
*callback = this->sendCallback;
|
||||
*args = this->callbackArgs;
|
||||
}
|
185
hal/src/fsfw/hal/linux/spi/SpiCookie.h
Normal file
185
hal/src/fsfw/hal/linux/spi/SpiCookie.h
Normal file
@ -0,0 +1,185 @@
|
||||
#ifndef LINUX_SPI_SPICOOKIE_H_
|
||||
#define LINUX_SPI_SPICOOKIE_H_
|
||||
|
||||
#include "spiDefinitions.h"
|
||||
#include "../../common/gpio/gpioDefinitions.h"
|
||||
|
||||
#include <fsfw/devicehandlers/CookieIF.h>
|
||||
|
||||
#include <linux/spi/spidev.h>
|
||||
|
||||
/**
|
||||
* @brief This cookie class is passed to the SPI communication interface
|
||||
* @details
|
||||
* This cookie contains device specific properties like speed and SPI mode or the SPI transfer
|
||||
* struct required by the Linux SPI driver. It also contains a handle to a GPIO interface
|
||||
* to perform slave select switching when necessary.
|
||||
*
|
||||
* The user can specify gpio::NO_GPIO as the GPIO ID or use a custom send callback to meet
|
||||
* special requirements like expander slave select switching (e.g. GPIO or I2C expander)
|
||||
* or special timing related requirements.
|
||||
*/
|
||||
class SpiCookie: public CookieIF {
|
||||
public:
|
||||
/**
|
||||
* Each SPI device will have a corresponding cookie. The cookie is used by the communication
|
||||
* interface and contains device specific information like the largest expected size to be
|
||||
* sent and received and the GPIO pin used to toggle the SPI slave select pin.
|
||||
* @param spiAddress
|
||||
* @param chipSelect Chip select. gpio::NO_GPIO can be used for hardware slave selects.
|
||||
* @param spiDev
|
||||
* @param maxSize
|
||||
*/
|
||||
SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev,
|
||||
const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed);
|
||||
|
||||
/**
|
||||
* Like constructor above, but without a dedicated GPIO CS. Can be used for hardware
|
||||
* slave select or if CS logic is performed with decoders.
|
||||
*/
|
||||
SpiCookie(address_t spiAddress, std::string spiDev, const size_t maxReplySize,
|
||||
spi::SpiModes spiMode, uint32_t spiSpeed);
|
||||
|
||||
/**
|
||||
* Use the callback mode of the SPI communication interface. The user can pass the callback
|
||||
* function here or by using the setter function #setCallbackMode
|
||||
*/
|
||||
SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev, const size_t maxSize,
|
||||
spi::SpiModes spiMode, uint32_t spiSpeed, spi::send_callback_function_t callback,
|
||||
void *args);
|
||||
|
||||
/**
|
||||
* Get the callback function
|
||||
* @param callback
|
||||
* @param args
|
||||
*/
|
||||
void getCallback(spi::send_callback_function_t* callback, void** args);
|
||||
|
||||
address_t getSpiAddress() const;
|
||||
std::string getSpiDevice() const;
|
||||
gpioId_t getChipSelectPin() const;
|
||||
size_t getMaxBufferSize() const;
|
||||
|
||||
spi::SpiComIfModes getComIfMode() const;
|
||||
|
||||
/** Enables changing SPI speed at run-time */
|
||||
void setSpiSpeed(uint32_t newSpeed);
|
||||
/** Enables changing the SPI mode at run-time */
|
||||
void setSpiMode(spi::SpiModes newMode);
|
||||
|
||||
/**
|
||||
* Set the SPI to callback mode and assigns the user supplied callback and an argument
|
||||
* passed to the callback.
|
||||
* @param callback
|
||||
* @param args
|
||||
*/
|
||||
void setCallbackMode(spi::send_callback_function_t callback, void* args);
|
||||
|
||||
/**
|
||||
* Can be used to set the callback arguments and a later point than initialization.
|
||||
* @param args
|
||||
*/
|
||||
void setCallbackArgs(void* args);
|
||||
|
||||
/**
|
||||
* True if SPI transfers should be performed in full duplex mode
|
||||
* @return
|
||||
*/
|
||||
bool isFullDuplex() const;
|
||||
|
||||
/**
|
||||
* Set transfer type to full duplex or half duplex. Full duplex is the default setting,
|
||||
* ressembling common SPI hardware implementation with shift registers, where read and writes
|
||||
* happen simultaneosly.
|
||||
* @param fullDuplex
|
||||
*/
|
||||
void setFullOrHalfDuplex(bool halfDuplex);
|
||||
|
||||
/**
|
||||
* This needs to be called to specify where the SPI driver writes to or reads from.
|
||||
* @param readLocation
|
||||
* @param writeLocation
|
||||
*/
|
||||
void assignReadBuffer(uint8_t* rx);
|
||||
void assignWriteBuffer(const uint8_t* tx);
|
||||
/**
|
||||
* Assign size for the next transfer.
|
||||
* @param transferSize
|
||||
*/
|
||||
void assignTransferSize(size_t transferSize);
|
||||
size_t getCurrentTransferSize() const;
|
||||
|
||||
struct UncommonParameters {
|
||||
uint8_t bitsPerWord = 8;
|
||||
bool noCs = false;
|
||||
bool csHigh = false;
|
||||
bool threeWireSpi = false;
|
||||
/* MSB first is more common */
|
||||
bool lsbFirst = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Can be used to explicitely disable hardware chip select.
|
||||
* Some drivers like the Raspberry Pi Linux driver will not use hardware chip select by default
|
||||
* (see https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md)
|
||||
* @param enable
|
||||
*/
|
||||
void setNoCs(bool enable);
|
||||
void setThreeWireSpi(bool enable);
|
||||
void setLsbFirst(bool enable);
|
||||
void setCsHigh(bool enable);
|
||||
void setBitsPerWord(uint8_t bitsPerWord);
|
||||
|
||||
void getSpiParameters(spi::SpiModes& spiMode, uint32_t& spiSpeed,
|
||||
UncommonParameters* parameters = nullptr) const;
|
||||
|
||||
/**
|
||||
* See spidev.h cs_change and delay_usecs
|
||||
* @param deselectCs
|
||||
* @param delayUsecs
|
||||
*/
|
||||
void activateCsDeselect(bool deselectCs, uint16_t delayUsecs);
|
||||
|
||||
spi_ioc_transfer* getTransferStructHandle();
|
||||
private:
|
||||
|
||||
/**
|
||||
* Internal constructor which initializes every field
|
||||
* @param spiAddress
|
||||
* @param chipSelect
|
||||
* @param spiDev
|
||||
* @param maxSize
|
||||
* @param spiMode
|
||||
* @param spiSpeed
|
||||
* @param callback
|
||||
* @param args
|
||||
*/
|
||||
SpiCookie(spi::SpiComIfModes comIfMode, address_t spiAddress, gpioId_t chipSelect,
|
||||
std::string spiDev, const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed,
|
||||
spi::send_callback_function_t callback, void* args);
|
||||
|
||||
size_t currentTransferSize = 0;
|
||||
|
||||
address_t spiAddress;
|
||||
gpioId_t chipSelectPin;
|
||||
std::string spiDevice;
|
||||
|
||||
spi::SpiComIfModes comIfMode;
|
||||
|
||||
// Required for regular mode
|
||||
const size_t maxSize;
|
||||
spi::SpiModes spiMode;
|
||||
uint32_t spiSpeed;
|
||||
bool halfDuplex = false;
|
||||
|
||||
// Required for callback mode
|
||||
spi::send_callback_function_t sendCallback = nullptr;
|
||||
void* callbackArgs = nullptr;
|
||||
|
||||
struct spi_ioc_transfer spiTransferStruct = {};
|
||||
UncommonParameters uncommonParameters;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* LINUX_SPI_SPICOOKIE_H_ */
|
28
hal/src/fsfw/hal/linux/spi/spiDefinitions.h
Normal file
28
hal/src/fsfw/hal/linux/spi/spiDefinitions.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef LINUX_SPI_SPIDEFINITONS_H_
|
||||
#define LINUX_SPI_SPIDEFINITONS_H_
|
||||
|
||||
#include "../../common/gpio/gpioDefinitions.h"
|
||||
#include "../../common/spi/spiCommon.h"
|
||||
|
||||
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
|
||||
#include <linux/spi/spidev.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class SpiCookie;
|
||||
class SpiComIF;
|
||||
|
||||
namespace spi {
|
||||
|
||||
enum SpiComIfModes {
|
||||
REGULAR,
|
||||
CALLBACK
|
||||
};
|
||||
|
||||
|
||||
using send_callback_function_t = ReturnValue_t (*) (SpiComIF* comIf, SpiCookie *cookie,
|
||||
const uint8_t *sendData, size_t sendLen, void* args);
|
||||
|
||||
}
|
||||
|
||||
#endif /* LINUX_SPI_SPIDEFINITONS_H_ */
|
4
hal/src/fsfw/hal/linux/uart/CMakeLists.txt
Normal file
4
hal/src/fsfw/hal/linux/uart/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
target_sources(${LIB_FSFW_NAME} PUBLIC
|
||||
UartComIF.cpp
|
||||
UartCookie.cpp
|
||||
)
|
500
hal/src/fsfw/hal/linux/uart/UartComIF.cpp
Normal file
500
hal/src/fsfw/hal/linux/uart/UartComIF.cpp
Normal file
@ -0,0 +1,500 @@
|
||||
#include "fsfw/hal/linux/uart/UartComIF.h"
|
||||
#include "OBSWConfig.h"
|
||||
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
UartComIF::UartComIF(object_id_t objectId): SystemObject(objectId){
|
||||
}
|
||||
|
||||
UartComIF::~UartComIF() {}
|
||||
|
||||
ReturnValue_t UartComIF::initializeInterface(CookieIF* cookie) {
|
||||
|
||||
std::string deviceFile;
|
||||
UartDeviceMapIter uartDeviceMapIter;
|
||||
|
||||
if(cookie == nullptr) {
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
|
||||
if (uartCookie == nullptr) {
|
||||
sif::error << "UartComIF::initializeInterface: Invalid UART Cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
deviceFile = uartCookie->getDeviceFile();
|
||||
|
||||
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
|
||||
if(uartDeviceMapIter == uartDeviceMap.end()) {
|
||||
int fileDescriptor = configureUartPort(uartCookie);
|
||||
if (fileDescriptor < 0) {
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
size_t maxReplyLen = uartCookie->getMaxReplyLen();
|
||||
UartElements uartElements = {fileDescriptor, std::vector<uint8_t>(maxReplyLen), 0};
|
||||
auto status = uartDeviceMap.emplace(deviceFile, uartElements);
|
||||
if (status.second == false) {
|
||||
sif::warning << "UartComIF::initializeInterface: Failed to insert device " <<
|
||||
deviceFile << "to UART device map" << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sif::warning << "UartComIF::initializeInterface: UART device " << deviceFile <<
|
||||
" already in use" << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int UartComIF::configureUartPort(UartCookie* uartCookie) {
|
||||
|
||||
struct termios options = {};
|
||||
|
||||
std::string deviceFile = uartCookie->getDeviceFile();
|
||||
int fd = open(deviceFile.c_str(), O_RDWR);
|
||||
|
||||
if (fd < 0) {
|
||||
sif::warning << "UartComIF::configureUartPort: Failed to open uart " << deviceFile <<
|
||||
"with error code " << errno << strerror(errno) << std::endl;
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Read in existing settings */
|
||||
if(tcgetattr(fd, &options) != 0) {
|
||||
sif::warning << "UartComIF::configureUartPort: Error " << errno << "from tcgetattr: "
|
||||
<< strerror(errno) << std::endl;
|
||||
return fd;
|
||||
}
|
||||
|
||||
setParityOptions(&options, uartCookie);
|
||||
setStopBitOptions(&options, uartCookie);
|
||||
setDatasizeOptions(&options, uartCookie);
|
||||
setFixedOptions(&options);
|
||||
setUartMode(&options, *uartCookie);
|
||||
if(uartCookie->getInputShouldBeFlushed()) {
|
||||
tcflush(fd, TCIFLUSH);
|
||||
}
|
||||
|
||||
/* Sets uart to non-blocking mode. Read returns immediately when there are no data available */
|
||||
options.c_cc[VTIME] = 0;
|
||||
options.c_cc[VMIN] = 0;
|
||||
|
||||
configureBaudrate(&options, uartCookie);
|
||||
|
||||
/* Save option settings */
|
||||
if (tcsetattr(fd, TCSANOW, &options) != 0) {
|
||||
sif::warning << "UartComIF::configureUartPort: Failed to set options with error " <<
|
||||
errno << ": " << strerror(errno);
|
||||
return fd;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
void UartComIF::setParityOptions(struct termios* options, UartCookie* uartCookie) {
|
||||
/* Clear parity bit */
|
||||
options->c_cflag &= ~PARENB;
|
||||
switch (uartCookie->getParity()) {
|
||||
case Parity::EVEN:
|
||||
options->c_cflag |= PARENB;
|
||||
options->c_cflag &= ~PARODD;
|
||||
break;
|
||||
case Parity::ODD:
|
||||
options->c_cflag |= PARENB;
|
||||
options->c_cflag |= PARODD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UartComIF::setStopBitOptions(struct termios* options, UartCookie* uartCookie) {
|
||||
/* Clear stop field. Sets stop bit to one bit */
|
||||
options->c_cflag &= ~CSTOPB;
|
||||
switch (uartCookie->getStopBits()) {
|
||||
case StopBits::TWO_STOP_BITS:
|
||||
options->c_cflag |= CSTOPB;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UartComIF::setDatasizeOptions(struct termios* options, UartCookie* uartCookie) {
|
||||
/* Clear size bits */
|
||||
options->c_cflag &= ~CSIZE;
|
||||
switch (uartCookie->getBitsPerWord()) {
|
||||
case 5:
|
||||
options->c_cflag |= CS5;
|
||||
break;
|
||||
case 6:
|
||||
options->c_cflag |= CS6;
|
||||
break;
|
||||
case 7:
|
||||
options->c_cflag |= CS7;
|
||||
break;
|
||||
case 8:
|
||||
options->c_cflag |= CS8;
|
||||
break;
|
||||
default:
|
||||
sif::warning << "UartComIF::setDatasizeOptions: Invalid size specified" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UartComIF::setFixedOptions(struct termios* options) {
|
||||
/* Disable RTS/CTS hardware flow control */
|
||||
options->c_cflag &= ~CRTSCTS;
|
||||
/* Turn on READ & ignore ctrl lines (CLOCAL = 1) */
|
||||
options->c_cflag |= CREAD | CLOCAL;
|
||||
/* Disable echo */
|
||||
options->c_lflag &= ~ECHO;
|
||||
/* Disable erasure */
|
||||
options->c_lflag &= ~ECHOE;
|
||||
/* Disable new-line echo */
|
||||
options->c_lflag &= ~ECHONL;
|
||||
/* Disable interpretation of INTR, QUIT and SUSP */
|
||||
options->c_lflag &= ~ISIG;
|
||||
/* Turn off s/w flow ctrl */
|
||||
options->c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||
/* Disable any special handling of received bytes */
|
||||
options->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
|
||||
/* Prevent special interpretation of output bytes (e.g. newline chars) */
|
||||
options->c_oflag &= ~OPOST;
|
||||
/* Prevent conversion of newline to carriage return/line feed */
|
||||
options->c_oflag &= ~ONLCR;
|
||||
}
|
||||
|
||||
void UartComIF::configureBaudrate(struct termios* options, UartCookie* uartCookie) {
|
||||
switch (uartCookie->getBaudrate()) {
|
||||
case 50:
|
||||
cfsetispeed(options, B50);
|
||||
cfsetospeed(options, B50);
|
||||
break;
|
||||
case 75:
|
||||
cfsetispeed(options, B75);
|
||||
cfsetospeed(options, B75);
|
||||
break;
|
||||
case 110:
|
||||
cfsetispeed(options, B110);
|
||||
cfsetospeed(options, B110);
|
||||
break;
|
||||
case 134:
|
||||
cfsetispeed(options, B134);
|
||||
cfsetospeed(options, B134);
|
||||
break;
|
||||
case 150:
|
||||
cfsetispeed(options, B150);
|
||||
cfsetospeed(options, B150);
|
||||
break;
|
||||
case 200:
|
||||
cfsetispeed(options, B200);
|
||||
cfsetospeed(options, B200);
|
||||
break;
|
||||
case 300:
|
||||
cfsetispeed(options, B300);
|
||||
cfsetospeed(options, B300);
|
||||
break;
|
||||
case 600:
|
||||
cfsetispeed(options, B600);
|
||||
cfsetospeed(options, B600);
|
||||
break;
|
||||
case 1200:
|
||||
cfsetispeed(options, B1200);
|
||||
cfsetospeed(options, B1200);
|
||||
break;
|
||||
case 1800:
|
||||
cfsetispeed(options, B1800);
|
||||
cfsetospeed(options, B1800);
|
||||
break;
|
||||
case 2400:
|
||||
cfsetispeed(options, B2400);
|
||||
cfsetospeed(options, B2400);
|
||||
break;
|
||||
case 4800:
|
||||
cfsetispeed(options, B4800);
|
||||
cfsetospeed(options, B4800);
|
||||
break;
|
||||
case 9600:
|
||||
cfsetispeed(options, B9600);
|
||||
cfsetospeed(options, B9600);
|
||||
break;
|
||||
case 19200:
|
||||
cfsetispeed(options, B19200);
|
||||
cfsetospeed(options, B19200);
|
||||
break;
|
||||
case 38400:
|
||||
cfsetispeed(options, B38400);
|
||||
cfsetospeed(options, B38400);
|
||||
break;
|
||||
case 57600:
|
||||
cfsetispeed(options, B57600);
|
||||
cfsetospeed(options, B57600);
|
||||
break;
|
||||
case 115200:
|
||||
cfsetispeed(options, B115200);
|
||||
cfsetospeed(options, B115200);
|
||||
break;
|
||||
case 230400:
|
||||
cfsetispeed(options, B230400);
|
||||
cfsetospeed(options, B230400);
|
||||
break;
|
||||
case 460800:
|
||||
cfsetispeed(options, B460800);
|
||||
cfsetospeed(options, B460800);
|
||||
break;
|
||||
default:
|
||||
sif::warning << "UartComIF::configureBaudrate: Baudrate not supported" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t UartComIF::sendMessage(CookieIF *cookie,
|
||||
const uint8_t *sendData, size_t sendLen) {
|
||||
|
||||
int fd = 0;
|
||||
std::string deviceFile;
|
||||
UartDeviceMapIter uartDeviceMapIter;
|
||||
|
||||
if(sendData == nullptr) {
|
||||
sif::debug << "UartComIF::sendMessage: Send Data is nullptr" << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
if(sendLen == 0) {
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
|
||||
if(uartCookie == nullptr) {
|
||||
sif::debug << "UartComIF::sendMessasge: Invalid UART Cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
deviceFile = uartCookie->getDeviceFile();
|
||||
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
|
||||
if (uartDeviceMapIter == uartDeviceMap.end()) {
|
||||
sif::debug << "UartComIF::sendMessage: Device file " << deviceFile <<
|
||||
"not in UART map" << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
fd = uartDeviceMapIter->second.fileDescriptor;
|
||||
|
||||
if (write(fd, sendData, sendLen) != (int)sendLen) {
|
||||
sif::error << "UartComIF::sendMessage: Failed to send data with error code " <<
|
||||
errno << ": Error description: " << strerror(errno) << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t UartComIF::getSendSuccess(CookieIF *cookie) {
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t UartComIF::requestReceiveMessage(CookieIF *cookie, size_t requestLen) {
|
||||
std::string deviceFile;
|
||||
UartDeviceMapIter uartDeviceMapIter;
|
||||
|
||||
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
|
||||
if(uartCookie == nullptr) {
|
||||
sif::debug << "UartComIF::requestReceiveMessage: Invalid Uart Cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
UartModes uartMode = uartCookie->getUartMode();
|
||||
deviceFile = uartCookie->getDeviceFile();
|
||||
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
|
||||
|
||||
if(uartMode == UartModes::NON_CANONICAL and requestLen == 0) {
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
if (uartDeviceMapIter == uartDeviceMap.end()) {
|
||||
sif::debug << "UartComIF::requestReceiveMessage: Device file " << deviceFile
|
||||
<< " not in uart map" << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
if (uartMode == UartModes::CANONICAL) {
|
||||
return handleCanonicalRead(*uartCookie, uartDeviceMapIter, requestLen);
|
||||
}
|
||||
else if (uartMode == UartModes::NON_CANONICAL) {
|
||||
return handleNoncanonicalRead(*uartCookie, uartDeviceMapIter, requestLen);
|
||||
}
|
||||
else {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t UartComIF::handleCanonicalRead(UartCookie& uartCookie, UartDeviceMapIter& iter,
|
||||
size_t requestLen) {
|
||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||
uint8_t maxReadCycles = uartCookie.getReadCycles();
|
||||
uint8_t currentReadCycles = 0;
|
||||
int bytesRead = 0;
|
||||
size_t currentBytesRead = 0;
|
||||
size_t maxReplySize = uartCookie.getMaxReplyLen();
|
||||
int fd = iter->second.fileDescriptor;
|
||||
auto bufferPtr = iter->second.replyBuffer.data();
|
||||
do {
|
||||
size_t allowedReadSize = 0;
|
||||
if(currentBytesRead >= maxReplySize) {
|
||||
// Overflow risk. Emit warning, trigger event and break. If this happens,
|
||||
// the reception buffer is not large enough or data is not polled often enough.
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "UartComIF::requestReceiveMessage: Next read would cause overflow!"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning("UartComIF::requestReceiveMessage: "
|
||||
"Next read would cause overflow!");
|
||||
#endif
|
||||
#endif
|
||||
result = UART_RX_BUFFER_TOO_SMALL;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
allowedReadSize = maxReplySize - currentBytesRead;
|
||||
}
|
||||
|
||||
bytesRead = read(fd, bufferPtr, allowedReadSize);
|
||||
if (bytesRead < 0) {
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
else if(bytesRead > 0) {
|
||||
iter->second.replyLen += bytesRead;
|
||||
bufferPtr += bytesRead;
|
||||
currentBytesRead += bytesRead;
|
||||
}
|
||||
currentReadCycles++;
|
||||
} while(bytesRead > 0 and currentReadCycles < maxReadCycles);
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t UartComIF::handleNoncanonicalRead(UartCookie &uartCookie, UartDeviceMapIter &iter,
|
||||
size_t requestLen) {
|
||||
int fd = iter->second.fileDescriptor;
|
||||
auto bufferPtr = iter->second.replyBuffer.data();
|
||||
// Size check to prevent buffer overflow
|
||||
if(requestLen > uartCookie.getMaxReplyLen()) {
|
||||
#if OBSW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "UartComIF::requestReceiveMessage: Next read would cause overflow!"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning("UartComIF::requestReceiveMessage: "
|
||||
"Next read would cause overflow!");
|
||||
#endif
|
||||
#endif
|
||||
return UART_RX_BUFFER_TOO_SMALL;
|
||||
}
|
||||
int bytesRead = read(fd, bufferPtr, requestLen);
|
||||
if (bytesRead < 0) {
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
else if (bytesRead != static_cast<int>(requestLen)) {
|
||||
if(uartCookie.isReplySizeFixed()) {
|
||||
sif::warning << "UartComIF::requestReceiveMessage: Only read " << bytesRead <<
|
||||
" of " << requestLen << " bytes" << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
iter->second.replyLen = bytesRead;
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t UartComIF::readReceivedMessage(CookieIF *cookie,
|
||||
uint8_t **buffer, size_t* size) {
|
||||
|
||||
std::string deviceFile;
|
||||
UartDeviceMapIter uartDeviceMapIter;
|
||||
|
||||
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
|
||||
if(uartCookie == nullptr) {
|
||||
sif::debug << "UartComIF::readReceivedMessage: Invalid uart cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
deviceFile = uartCookie->getDeviceFile();
|
||||
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
|
||||
if (uartDeviceMapIter == uartDeviceMap.end()) {
|
||||
sif::debug << "UartComIF::readReceivedMessage: Device file " << deviceFile <<
|
||||
" not in uart map" << std::endl;
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
*buffer = uartDeviceMapIter->second.replyBuffer.data();
|
||||
*size = uartDeviceMapIter->second.replyLen;
|
||||
|
||||
/* Length is reset to 0 to prevent reading the same data twice */
|
||||
uartDeviceMapIter->second.replyLen = 0;
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t UartComIF::flushUartRxBuffer(CookieIF *cookie) {
|
||||
std::string deviceFile;
|
||||
UartDeviceMapIter uartDeviceMapIter;
|
||||
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
|
||||
if(uartCookie == nullptr) {
|
||||
sif::warning << "UartComIF::flushUartRxBuffer: Invalid uart cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
deviceFile = uartCookie->getDeviceFile();
|
||||
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
|
||||
int fd = uartDeviceMapIter->second.fileDescriptor;
|
||||
tcflush(fd, TCIFLUSH);
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t UartComIF::flushUartTxBuffer(CookieIF *cookie) {
|
||||
std::string deviceFile;
|
||||
UartDeviceMapIter uartDeviceMapIter;
|
||||
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
|
||||
if(uartCookie == nullptr) {
|
||||
sif::warning << "UartComIF::flushUartTxBuffer: Invalid uart cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
deviceFile = uartCookie->getDeviceFile();
|
||||
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
|
||||
int fd = uartDeviceMapIter->second.fileDescriptor;
|
||||
tcflush(fd, TCOFLUSH);
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t UartComIF::flushUartTxAndRxBuf(CookieIF *cookie) {
|
||||
std::string deviceFile;
|
||||
UartDeviceMapIter uartDeviceMapIter;
|
||||
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
|
||||
if(uartCookie == nullptr) {
|
||||
sif::warning << "UartComIF::flushUartTxAndRxBuf: Invalid uart cookie!" << std::endl;
|
||||
return NULLPOINTER;
|
||||
}
|
||||
deviceFile = uartCookie->getDeviceFile();
|
||||
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
|
||||
int fd = uartDeviceMapIter->second.fileDescriptor;
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
void UartComIF::setUartMode(struct termios *options, UartCookie &uartCookie) {
|
||||
UartModes uartMode = uartCookie.getUartMode();
|
||||
if(uartMode == UartModes::NON_CANONICAL) {
|
||||
/* Disable canonical mode */
|
||||
options->c_lflag &= ~ICANON;
|
||||
}
|
||||
else if(uartMode == UartModes::CANONICAL) {
|
||||
options->c_lflag |= ICANON;
|
||||
}
|
||||
}
|
125
hal/src/fsfw/hal/linux/uart/UartComIF.h
Normal file
125
hal/src/fsfw/hal/linux/uart/UartComIF.h
Normal file
@ -0,0 +1,125 @@
|
||||
#ifndef BSP_Q7S_COMIF_UARTCOMIF_H_
|
||||
#define BSP_Q7S_COMIF_UARTCOMIF_H_
|
||||
|
||||
#include "UartCookie.h"
|
||||
#include <fsfw/objectmanager/SystemObject.h>
|
||||
#include <fsfw/devicehandlers/DeviceCommunicationIF.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief This is the communication interface to access serial ports on linux based operating
|
||||
* systems.
|
||||
*
|
||||
* @details The implementation follows the instructions from https://blog.mbedded.ninja/programming/
|
||||
* operating-systems/linux/linux-serial-ports-using-c-cpp/#disabling-canonical-mode
|
||||
*
|
||||
* @author J. Meier
|
||||
*/
|
||||
class UartComIF: public DeviceCommunicationIF, public SystemObject {
|
||||
public:
|
||||
static constexpr uint8_t uartRetvalId = CLASS_ID::HAL_UART;
|
||||
|
||||
static constexpr ReturnValue_t UART_READ_FAILURE =
|
||||
HasReturnvaluesIF::makeReturnCode(uartRetvalId, 1);
|
||||
static constexpr ReturnValue_t UART_READ_SIZE_MISSMATCH =
|
||||
HasReturnvaluesIF::makeReturnCode(uartRetvalId, 2);
|
||||
static constexpr ReturnValue_t UART_RX_BUFFER_TOO_SMALL =
|
||||
HasReturnvaluesIF::makeReturnCode(uartRetvalId, 3);
|
||||
|
||||
UartComIF(object_id_t objectId);
|
||||
|
||||
virtual ~UartComIF();
|
||||
|
||||
ReturnValue_t initializeInterface(CookieIF * cookie) override;
|
||||
ReturnValue_t sendMessage(CookieIF *cookie,const uint8_t *sendData,
|
||||
size_t sendLen) override;
|
||||
ReturnValue_t getSendSuccess(CookieIF *cookie) override;
|
||||
ReturnValue_t requestReceiveMessage(CookieIF *cookie,
|
||||
size_t requestLen) override;
|
||||
ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer,
|
||||
size_t *size) override;
|
||||
|
||||
/**
|
||||
* @brief This function discards all data received but not read in the UART buffer.
|
||||
*/
|
||||
ReturnValue_t flushUartRxBuffer(CookieIF *cookie);
|
||||
|
||||
/**
|
||||
* @brief This function discards all data in the transmit buffer of the UART driver.
|
||||
*/
|
||||
ReturnValue_t flushUartTxBuffer(CookieIF *cookie);
|
||||
|
||||
/**
|
||||
* @brief This function discards both data in the transmit and receive buffer of the UART.
|
||||
*/
|
||||
ReturnValue_t flushUartTxAndRxBuf(CookieIF *cookie);
|
||||
|
||||
private:
|
||||
|
||||
using UartDeviceFile_t = std::string;
|
||||
|
||||
struct UartElements {
|
||||
int fileDescriptor;
|
||||
std::vector<uint8_t> replyBuffer;
|
||||
/** Number of bytes read will be written to this variable */
|
||||
size_t replyLen;
|
||||
};
|
||||
|
||||
using UartDeviceMap = std::unordered_map<UartDeviceFile_t, UartElements>;
|
||||
using UartDeviceMapIter = UartDeviceMap::iterator;
|
||||
|
||||
/**
|
||||
* The uart devie map stores informations of initialized uart ports.
|
||||
*/
|
||||
UartDeviceMap uartDeviceMap;
|
||||
|
||||
/**
|
||||
* @brief This function opens and configures a uart device by using the information stored
|
||||
* in the uart cookie.
|
||||
* @param uartCookie Pointer to uart cookie with information about the uart. Contains the
|
||||
* uart device file, baudrate, parity, stopbits etc.
|
||||
* @return The file descriptor of the configured uart.
|
||||
*/
|
||||
int configureUartPort(UartCookie* uartCookie);
|
||||
|
||||
/**
|
||||
* @brief This function adds the parity settings to the termios options struct.
|
||||
*
|
||||
* @param options Pointer to termios options struct which will be modified to enable or disable
|
||||
* parity checking.
|
||||
* @param uartCookie Pointer to uart cookie containing the information about the desired
|
||||
* parity settings.
|
||||
*
|
||||
*/
|
||||
void setParityOptions(struct termios* options, UartCookie* uartCookie);
|
||||
|
||||
void setStopBitOptions(struct termios* options, UartCookie* uartCookie);
|
||||
|
||||
/**
|
||||
* @brief This function sets options which are not configurable by the uartCookie.
|
||||
*/
|
||||
void setFixedOptions(struct termios* options);
|
||||
|
||||
/**
|
||||
* @brief With this function the datasize settings are added to the termios options struct.
|
||||
*/
|
||||
void setDatasizeOptions(struct termios* options, UartCookie* uartCookie);
|
||||
|
||||
/**
|
||||
* @brief This functions adds the baudrate specified in the uartCookie to the termios options
|
||||
* struct.
|
||||
*/
|
||||
void configureBaudrate(struct termios* options, UartCookie* uartCookie);
|
||||
|
||||
void setUartMode(struct termios* options, UartCookie& uartCookie);
|
||||
|
||||
ReturnValue_t handleCanonicalRead(UartCookie& uartCookie, UartDeviceMapIter& iter,
|
||||
size_t requestLen);
|
||||
ReturnValue_t handleNoncanonicalRead(UartCookie& uartCookie, UartDeviceMapIter& iter,
|
||||
size_t requestLen);
|
||||
|
||||
};
|
||||
|
||||
#endif /* BSP_Q7S_COMIF_UARTCOMIF_H_ */
|
97
hal/src/fsfw/hal/linux/uart/UartCookie.cpp
Normal file
97
hal/src/fsfw/hal/linux/uart/UartCookie.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
#include "fsfw/hal/linux/uart/UartCookie.h"
|
||||
|
||||
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||
|
||||
UartCookie::UartCookie(object_id_t handlerId, std::string deviceFile, UartModes uartMode,
|
||||
uint32_t baudrate, size_t maxReplyLen):
|
||||
handlerId(handlerId), deviceFile(deviceFile), uartMode(uartMode), baudrate(baudrate),
|
||||
maxReplyLen(maxReplyLen) {
|
||||
}
|
||||
|
||||
UartCookie::~UartCookie() {}
|
||||
|
||||
uint32_t UartCookie::getBaudrate() const {
|
||||
return baudrate;
|
||||
}
|
||||
|
||||
size_t UartCookie::getMaxReplyLen() const {
|
||||
return maxReplyLen;
|
||||
}
|
||||
|
||||
std::string UartCookie::getDeviceFile() const {
|
||||
return deviceFile;
|
||||
}
|
||||
|
||||
void UartCookie::setParityOdd() {
|
||||
parity = Parity::ODD;
|
||||
}
|
||||
|
||||
void UartCookie::setParityEven() {
|
||||
parity = Parity::EVEN;
|
||||
}
|
||||
|
||||
Parity UartCookie::getParity() const {
|
||||
return parity;
|
||||
}
|
||||
|
||||
void UartCookie::setBitsPerWord(uint8_t bitsPerWord_) {
|
||||
switch(bitsPerWord_) {
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
sif::debug << "UartCookie::setBitsPerWord: Invalid bits per word specified" << std::endl;
|
||||
return;
|
||||
}
|
||||
bitsPerWord = bitsPerWord_;
|
||||
}
|
||||
|
||||
uint8_t UartCookie::getBitsPerWord() const {
|
||||
return bitsPerWord;
|
||||
}
|
||||
|
||||
StopBits UartCookie::getStopBits() const {
|
||||
return stopBits;
|
||||
}
|
||||
|
||||
void UartCookie::setTwoStopBits() {
|
||||
stopBits = StopBits::TWO_STOP_BITS;
|
||||
}
|
||||
|
||||
void UartCookie::setOneStopBit() {
|
||||
stopBits = StopBits::ONE_STOP_BIT;
|
||||
}
|
||||
|
||||
UartModes UartCookie::getUartMode() const {
|
||||
return uartMode;
|
||||
}
|
||||
|
||||
void UartCookie::setReadCycles(uint8_t readCycles) {
|
||||
this->readCycles = readCycles;
|
||||
}
|
||||
|
||||
void UartCookie::setToFlushInput(bool enable) {
|
||||
this->flushInput = enable;
|
||||
}
|
||||
|
||||
uint8_t UartCookie::getReadCycles() const {
|
||||
return readCycles;
|
||||
}
|
||||
|
||||
bool UartCookie::getInputShouldBeFlushed() {
|
||||
return this->flushInput;
|
||||
}
|
||||
|
||||
object_id_t UartCookie::getHandlerId() const {
|
||||
return this->handlerId;
|
||||
}
|
||||
|
||||
void UartCookie::setNoFixedSizeReply() {
|
||||
replySizeFixed = false;
|
||||
}
|
||||
|
||||
bool UartCookie::isReplySizeFixed() {
|
||||
return replySizeFixed;
|
||||
}
|
121
hal/src/fsfw/hal/linux/uart/UartCookie.h
Normal file
121
hal/src/fsfw/hal/linux/uart/UartCookie.h
Normal file
@ -0,0 +1,121 @@
|
||||
#ifndef SAM9G20_COMIF_COOKIES_UART_COOKIE_H_
|
||||
#define SAM9G20_COMIF_COOKIES_UART_COOKIE_H_
|
||||
|
||||
#include <fsfw/devicehandlers/CookieIF.h>
|
||||
#include <fsfw/objectmanager/SystemObjectIF.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
enum class Parity {
|
||||
NONE,
|
||||
EVEN,
|
||||
ODD
|
||||
};
|
||||
|
||||
enum class StopBits {
|
||||
ONE_STOP_BIT,
|
||||
TWO_STOP_BITS
|
||||
};
|
||||
|
||||
enum class UartModes {
|
||||
CANONICAL,
|
||||
NON_CANONICAL
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Cookie for the UartComIF. There are many options available to configure the UART driver.
|
||||
* The constructor only requests for common options like the baudrate. Other options can
|
||||
* be set by member functions.
|
||||
*
|
||||
* @author J. Meier
|
||||
*/
|
||||
class UartCookie: public CookieIF {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Constructor for the uart cookie.
|
||||
* @param deviceFile The device file specifying the uart to use, e.g. "/dev/ttyPS1"
|
||||
* @param uartMode Specify the UART mode. The canonical mode should be used if the
|
||||
* messages are separated by a delimited character like '\n'. See the
|
||||
* termios documentation for more information
|
||||
* @param baudrate The baudrate to use for input and output. Possible Baudrates are: 50,
|
||||
* 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, B19200,
|
||||
* 38400, 57600, 115200, 230400, 460800
|
||||
* @param maxReplyLen The maximum size an object using this cookie expects
|
||||
* @details
|
||||
* Default configuration: No parity
|
||||
* 8 databits (number of bits transfered with one uart frame)
|
||||
* One stop bit
|
||||
*/
|
||||
UartCookie(object_id_t handlerId, std::string deviceFile, UartModes uartMode,
|
||||
uint32_t baudrate, size_t maxReplyLen);
|
||||
|
||||
virtual ~UartCookie();
|
||||
|
||||
uint32_t getBaudrate() const;
|
||||
size_t getMaxReplyLen() const;
|
||||
std::string getDeviceFile() const;
|
||||
Parity getParity() const;
|
||||
uint8_t getBitsPerWord() const;
|
||||
StopBits getStopBits() const;
|
||||
UartModes getUartMode() const;
|
||||
object_id_t getHandlerId() const;
|
||||
|
||||
/**
|
||||
* The UART ComIF will only perform a specified number of read cycles for the canonical mode.
|
||||
* The user can specify how many of those read cycles are performed for one device handler
|
||||
* communication cycle. An example use-case would be to read all available GPS NMEA strings
|
||||
* at once.
|
||||
* @param readCycles
|
||||
*/
|
||||
void setReadCycles(uint8_t readCycles);
|
||||
uint8_t getReadCycles() const;
|
||||
|
||||
/**
|
||||
* Allows to flush the data which was received but has not been read yet. This is useful
|
||||
* to discard obsolete data at software startup.
|
||||
*/
|
||||
void setToFlushInput(bool enable);
|
||||
bool getInputShouldBeFlushed();
|
||||
|
||||
/**
|
||||
* Functions two enable parity checking.
|
||||
*/
|
||||
void setParityOdd();
|
||||
void setParityEven();
|
||||
|
||||
/**
|
||||
* Function two set number of bits per UART frame.
|
||||
*/
|
||||
void setBitsPerWord(uint8_t bitsPerWord_);
|
||||
|
||||
/**
|
||||
* Function to specify the number of stopbits.
|
||||
*/
|
||||
void setTwoStopBits();
|
||||
void setOneStopBit();
|
||||
|
||||
/**
|
||||
* Calling this function prevents the UartComIF to return failed if not all requested bytes
|
||||
* could be read. This is required by a device handler when the size of a reply is not known.
|
||||
*/
|
||||
void setNoFixedSizeReply();
|
||||
|
||||
bool isReplySizeFixed();
|
||||
|
||||
private:
|
||||
|
||||
const object_id_t handlerId;
|
||||
std::string deviceFile;
|
||||
const UartModes uartMode;
|
||||
bool flushInput = false;
|
||||
uint32_t baudrate;
|
||||
size_t maxReplyLen = 0;
|
||||
Parity parity = Parity::NONE;
|
||||
uint8_t bitsPerWord = 8;
|
||||
uint8_t readCycles = 1;
|
||||
StopBits stopBits = StopBits::ONE_STOP_BIT;
|
||||
bool replySizeFixed = true;
|
||||
};
|
||||
|
||||
#endif
|
26
hal/src/fsfw/hal/linux/utility.cpp
Normal file
26
hal/src/fsfw/hal/linux/utility.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "fsfw/FSFW.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
#include "fsfw/hal/linux/utility.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
void utility::handleIoctlError(const char* const customPrintout) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
if(customPrintout != nullptr) {
|
||||
sif::warning << customPrintout << std::endl;
|
||||
}
|
||||
sif::warning << "handleIoctlError: Error code " << errno << ", "<< strerror(errno) <<
|
||||
std::endl;
|
||||
#else
|
||||
if(customPrintout != nullptr) {
|
||||
sif::printWarning("%s\n", customPrintout);
|
||||
}
|
||||
sif::printWarning("handleIoctlError: Error code %d, %s\n", errno, strerror(errno));
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
|
||||
}
|
||||
|
||||
|
10
hal/src/fsfw/hal/linux/utility.h
Normal file
10
hal/src/fsfw/hal/linux/utility.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef LINUX_UTILITY_UTILITY_H_
|
||||
#define LINUX_UTILITY_UTILITY_H_
|
||||
|
||||
namespace utility {
|
||||
|
||||
void handleIoctlError(const char* const customPrintout);
|
||||
|
||||
}
|
||||
|
||||
#endif /* LINUX_UTILITY_UTILITY_H_ */
|
7
hal/src/fsfw/hal/stm32h7/CMakeLists.txt
Normal file
7
hal/src/fsfw/hal/stm32h7/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
add_subdirectory(spi)
|
||||
add_subdirectory(gpio)
|
||||
add_subdirectory(devicetest)
|
||||
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
dma.cpp
|
||||
)
|
3
hal/src/fsfw/hal/stm32h7/devicetest/CMakeLists.txt
Normal file
3
hal/src/fsfw/hal/stm32h7/devicetest/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
GyroL3GD20H.cpp
|
||||
)
|
558
hal/src/fsfw/hal/stm32h7/devicetest/GyroL3GD20H.cpp
Normal file
558
hal/src/fsfw/hal/stm32h7/devicetest/GyroL3GD20H.cpp
Normal file
@ -0,0 +1,558 @@
|
||||
#include "fsfw/hal/stm32h7/devicetest/GyroL3GD20H.h"
|
||||
|
||||
#include "fsfw/hal/stm32h7/spi/mspInit.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiDefinitions.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiCore.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiInterrupts.h"
|
||||
#include "fsfw/hal/stm32h7/spi/stm32h743ziSpi.h"
|
||||
|
||||
#include "fsfw/tasks/TaskFactory.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
#include "stm32h7xx_hal_spi.h"
|
||||
#include "stm32h7xx_hal_rcc.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
alignas(32) std::array<uint8_t, GyroL3GD20H::recvBufferSize> GyroL3GD20H::rxBuffer;
|
||||
alignas(32) std::array<uint8_t, GyroL3GD20H::txBufferSize>
|
||||
GyroL3GD20H::txBuffer __attribute__((section(".dma_buffer")));
|
||||
|
||||
TransferStates transferState = TransferStates::IDLE;
|
||||
spi::TransferModes GyroL3GD20H::transferMode = spi::TransferModes::POLLING;
|
||||
|
||||
|
||||
GyroL3GD20H::GyroL3GD20H(SPI_HandleTypeDef *spiHandle, spi::TransferModes transferMode_):
|
||||
spiHandle(spiHandle) {
|
||||
txDmaHandle = new DMA_HandleTypeDef();
|
||||
rxDmaHandle = new DMA_HandleTypeDef();
|
||||
spi::setSpiHandle(spiHandle);
|
||||
spi::assignSpiUserArgs(spi::SpiBus::SPI_1, spiHandle);
|
||||
transferMode = transferMode_;
|
||||
if(transferMode == spi::TransferModes::DMA) {
|
||||
mspCfg = new spi::MspDmaConfigStruct();
|
||||
auto typedCfg = dynamic_cast<spi::MspDmaConfigStruct*>(mspCfg);
|
||||
spi::setDmaHandles(txDmaHandle, rxDmaHandle);
|
||||
spi::h743zi::standardDmaCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS,
|
||||
IrqPriorities::HIGHEST_FREERTOS, IrqPriorities::HIGHEST_FREERTOS);
|
||||
spi::setSpiDmaMspFunctions(typedCfg);
|
||||
}
|
||||
else if(transferMode == spi::TransferModes::INTERRUPT) {
|
||||
mspCfg = new spi::MspIrqConfigStruct();
|
||||
auto typedCfg = dynamic_cast<spi::MspIrqConfigStruct*>(mspCfg);
|
||||
spi::h743zi::standardInterruptCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS);
|
||||
spi::setSpiIrqMspFunctions(typedCfg);
|
||||
}
|
||||
else if(transferMode == spi::TransferModes::POLLING) {
|
||||
mspCfg = new spi::MspPollingConfigStruct();
|
||||
auto typedCfg = dynamic_cast<spi::MspPollingConfigStruct*>(mspCfg);
|
||||
spi::h743zi::standardPollingCfg(*typedCfg);
|
||||
spi::setSpiPollingMspFunctions(typedCfg);
|
||||
}
|
||||
|
||||
spi::assignTransferRxTxCompleteCallback(&spiTransferCompleteCallback, nullptr);
|
||||
spi::assignTransferErrorCallback(&spiTransferErrorCallback, nullptr);
|
||||
|
||||
GPIO_InitTypeDef chipSelect = {};
|
||||
__HAL_RCC_GPIOD_CLK_ENABLE();
|
||||
chipSelect.Pin = GPIO_PIN_14;
|
||||
chipSelect.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
HAL_GPIO_Init(GPIOD, &chipSelect);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
|
||||
}
|
||||
|
||||
GyroL3GD20H::~GyroL3GD20H() {
|
||||
delete txDmaHandle;
|
||||
delete rxDmaHandle;
|
||||
if(mspCfg != nullptr) {
|
||||
delete mspCfg;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t GyroL3GD20H::initialize() {
|
||||
// Configure the SPI peripheral
|
||||
spiHandle->Instance = SPI1;
|
||||
spiHandle->Init.BaudRatePrescaler = spi::getPrescaler(HAL_RCC_GetHCLKFreq(), 3900000);
|
||||
spiHandle->Init.Direction = SPI_DIRECTION_2LINES;
|
||||
spi::assignSpiMode(spi::SpiModes::MODE_3, *spiHandle);
|
||||
spiHandle->Init.DataSize = SPI_DATASIZE_8BIT;
|
||||
spiHandle->Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
spiHandle->Init.TIMode = SPI_TIMODE_DISABLE;
|
||||
spiHandle->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
||||
spiHandle->Init.CRCPolynomial = 7;
|
||||
spiHandle->Init.CRCLength = SPI_CRC_LENGTH_8BIT;
|
||||
spiHandle->Init.NSS = SPI_NSS_SOFT;
|
||||
spiHandle->Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
|
||||
// Recommended setting to avoid glitches
|
||||
spiHandle->Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
|
||||
spiHandle->Init.Mode = SPI_MODE_MASTER;
|
||||
if(HAL_SPI_Init(spiHandle) != HAL_OK) {
|
||||
sif::printWarning("Error initializing SPI\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
delete mspCfg;
|
||||
transferState = TransferStates::WAIT;
|
||||
|
||||
sif::printInfo("GyroL3GD20H::performOperation: Reading WHO AM I register\n");
|
||||
|
||||
txBuffer[0] = WHO_AM_I_REG | STM_READ_MASK;
|
||||
txBuffer[1] = 0;
|
||||
|
||||
switch(transferMode) {
|
||||
case(spi::TransferModes::DMA): {
|
||||
return handleDmaTransferInit();
|
||||
}
|
||||
case(spi::TransferModes::INTERRUPT): {
|
||||
return handleInterruptTransferInit();
|
||||
}
|
||||
case(spi::TransferModes::POLLING): {
|
||||
return handlePollingTransferInit();
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroL3GD20H::performOperation() {
|
||||
switch(transferMode) {
|
||||
case(spi::TransferModes::DMA): {
|
||||
return handleDmaSensorRead();
|
||||
}
|
||||
case(spi::TransferModes::POLLING): {
|
||||
return handlePollingSensorRead();
|
||||
}
|
||||
case(spi::TransferModes::INTERRUPT): {
|
||||
return handleInterruptSensorRead();
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroL3GD20H::handleDmaTransferInit() {
|
||||
/* Clean D-cache */
|
||||
/* Make sure the address is 32-byte aligned and add 32-bytes to length,
|
||||
in case it overlaps cacheline */
|
||||
// See https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices
|
||||
HAL_StatusTypeDef result = performDmaTransfer(2);
|
||||
if(result != HAL_OK) {
|
||||
// Transfer error in transmission process
|
||||
sif::printWarning("GyroL3GD20H::initialize: Error transmitting SPI with DMA\n");
|
||||
}
|
||||
|
||||
// Wait for the transfer to complete
|
||||
while (transferState == TransferStates::WAIT) {
|
||||
TaskFactory::delayTask(1);
|
||||
}
|
||||
|
||||
switch(transferState) {
|
||||
case(TransferStates::SUCCESS): {
|
||||
uint8_t whoAmIVal = rxBuffer[1];
|
||||
if(whoAmIVal != EXPECTED_WHO_AM_I_VAL) {
|
||||
sif::printDebug("GyroL3GD20H::initialize: "
|
||||
"Read WHO AM I value %d not equal to expected value!\n", whoAmIVal);
|
||||
}
|
||||
transferState = TransferStates::IDLE;
|
||||
break;
|
||||
}
|
||||
case(TransferStates::FAILURE): {
|
||||
sif::printWarning("Transfer failure\n");
|
||||
transferState = TransferStates::FAILURE;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
sif::printInfo("GyroL3GD20H::initialize: Configuring device\n");
|
||||
// Configure the 5 configuration registers
|
||||
uint8_t configRegs[5];
|
||||
prepareConfigRegs(configRegs);
|
||||
|
||||
result = performDmaTransfer(6);
|
||||
if(result != HAL_OK) {
|
||||
// Transfer error in transmission process
|
||||
sif::printWarning("Error transmitting SPI with DMA\n");
|
||||
}
|
||||
|
||||
// Wait for the transfer to complete
|
||||
while (transferState == TransferStates::WAIT) {
|
||||
TaskFactory::delayTask(1);
|
||||
}
|
||||
|
||||
switch(transferState) {
|
||||
case(TransferStates::SUCCESS): {
|
||||
sif::printInfo("GyroL3GD20H::initialize: Configuration transfer success\n");
|
||||
transferState = TransferStates::IDLE;
|
||||
break;
|
||||
}
|
||||
case(TransferStates::FAILURE): {
|
||||
sif::printWarning("GyroL3GD20H::initialize: Configuration transfer failure\n");
|
||||
transferState = TransferStates::FAILURE;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
txBuffer[0] = CTRL_REG_1 | STM_AUTO_INCREMENT_MASK | STM_READ_MASK;
|
||||
std::memset(txBuffer.data() + 1, 0 , 5);
|
||||
result = performDmaTransfer(6);
|
||||
if(result != HAL_OK) {
|
||||
// Transfer error in transmission process
|
||||
sif::printWarning("Error transmitting SPI with DMA\n");
|
||||
}
|
||||
// Wait for the transfer to complete
|
||||
while (transferState == TransferStates::WAIT) {
|
||||
TaskFactory::delayTask(1);
|
||||
}
|
||||
|
||||
switch(transferState) {
|
||||
case(TransferStates::SUCCESS): {
|
||||
if(rxBuffer[1] != configRegs[0] or rxBuffer[2] != configRegs[1] or
|
||||
rxBuffer[3] != configRegs[2] or rxBuffer[4] != configRegs[3] or
|
||||
rxBuffer[5] != configRegs[4]) {
|
||||
sif::printWarning("GyroL3GD20H::initialize: Configuration failure\n");
|
||||
}
|
||||
else {
|
||||
sif::printInfo("GyroL3GD20H::initialize: Configuration success\n");
|
||||
}
|
||||
transferState = TransferStates::IDLE;
|
||||
break;
|
||||
}
|
||||
case(TransferStates::FAILURE): {
|
||||
sif::printWarning("GyroL3GD20H::initialize: Configuration transfer failure\n");
|
||||
transferState = TransferStates::FAILURE;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroL3GD20H::handleDmaSensorRead() {
|
||||
txBuffer[0] = CTRL_REG_1 | STM_AUTO_INCREMENT_MASK | STM_READ_MASK;
|
||||
std::memset(txBuffer.data() + 1, 0 , 14);
|
||||
|
||||
HAL_StatusTypeDef result = performDmaTransfer(15);
|
||||
if(result != HAL_OK) {
|
||||
// Transfer error in transmission process
|
||||
sif::printDebug("GyroL3GD20H::handleDmaSensorRead: Error transmitting SPI with DMA\n");
|
||||
}
|
||||
// Wait for the transfer to complete
|
||||
while (transferState == TransferStates::WAIT) {
|
||||
TaskFactory::delayTask(1);
|
||||
}
|
||||
|
||||
switch(transferState) {
|
||||
case(TransferStates::SUCCESS): {
|
||||
handleSensorReadout();
|
||||
break;
|
||||
}
|
||||
case(TransferStates::FAILURE): {
|
||||
sif::printWarning("GyroL3GD20H::handleDmaSensorRead: Sensor read failure\n");
|
||||
transferState = TransferStates::FAILURE;
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef GyroL3GD20H::performDmaTransfer(size_t sendSize) {
|
||||
transferState = TransferStates::WAIT;
|
||||
#if STM_USE_PERIPHERAL_TX_BUFFER_MPU_PROTECTION == 0
|
||||
SCB_CleanDCache_by_Addr((uint32_t*)(((uint32_t)txBuffer.data()) & ~(uint32_t)0x1F),
|
||||
txBuffer.size()+32);
|
||||
#endif
|
||||
|
||||
// Start SPI transfer via DMA
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
return HAL_SPI_TransmitReceive_DMA(spiHandle, txBuffer.data(), rxBuffer.data(), sendSize);
|
||||
}
|
||||
|
||||
ReturnValue_t GyroL3GD20H::handlePollingTransferInit() {
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
auto result = HAL_SPI_TransmitReceive(spiHandle, txBuffer.data(), rxBuffer.data(), 2, 1000);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
|
||||
switch(result) {
|
||||
case(HAL_OK): {
|
||||
sif::printInfo("GyroL3GD20H::initialize: Polling transfer success\n");
|
||||
uint8_t whoAmIVal = rxBuffer[1];
|
||||
if(whoAmIVal != EXPECTED_WHO_AM_I_VAL) {
|
||||
sif::printDebug("GyroL3GD20H::performOperation: "
|
||||
"Read WHO AM I value %d not equal to expected value!\n", whoAmIVal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(HAL_TIMEOUT): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Polling transfer timeout\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
case(HAL_ERROR): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Polling transfer failure\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
sif::printInfo("GyroL3GD20H::initialize: Configuring device\n");
|
||||
// Configure the 5 configuration registers
|
||||
uint8_t configRegs[5];
|
||||
prepareConfigRegs(configRegs);
|
||||
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
result = HAL_SPI_TransmitReceive(spiHandle, txBuffer.data(), rxBuffer.data(), 6, 1000);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
|
||||
switch(result) {
|
||||
case(HAL_OK): {
|
||||
break;
|
||||
}
|
||||
case(HAL_TIMEOUT): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Polling transfer timeout\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
case(HAL_ERROR): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Polling transfer failure\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
txBuffer[0] = CTRL_REG_1 | STM_AUTO_INCREMENT_MASK | STM_READ_MASK;
|
||||
std::memset(txBuffer.data() + 1, 0 , 5);
|
||||
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
result = HAL_SPI_TransmitReceive(spiHandle, txBuffer.data(), rxBuffer.data(), 6, 1000);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
|
||||
switch(result) {
|
||||
case(HAL_OK): {
|
||||
if(rxBuffer[1] != configRegs[0] or rxBuffer[2] != configRegs[1] or
|
||||
rxBuffer[3] != configRegs[2] or rxBuffer[4] != configRegs[3] or
|
||||
rxBuffer[5] != configRegs[4]) {
|
||||
sif::printWarning("GyroL3GD20H::initialize: Configuration failure\n");
|
||||
}
|
||||
else {
|
||||
sif::printInfo("GyroL3GD20H::initialize: Configuration success\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(HAL_TIMEOUT): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Polling transfer timeout\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
case(HAL_ERROR): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Polling transfer failure\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroL3GD20H::handlePollingSensorRead() {
|
||||
txBuffer[0] = CTRL_REG_1 | STM_AUTO_INCREMENT_MASK | STM_READ_MASK;
|
||||
std::memset(txBuffer.data() + 1, 0 , 14);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
auto result = HAL_SPI_TransmitReceive(spiHandle, txBuffer.data(), rxBuffer.data(), 15, 1000);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
|
||||
|
||||
switch(result) {
|
||||
case(HAL_OK): {
|
||||
handleSensorReadout();
|
||||
break;
|
||||
}
|
||||
case(HAL_TIMEOUT): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Polling transfer timeout\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
case(HAL_ERROR): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Polling transfer failure\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroL3GD20H::handleInterruptTransferInit() {
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
switch(HAL_SPI_TransmitReceive_IT(spiHandle, txBuffer.data(), rxBuffer.data(), 2)) {
|
||||
case(HAL_OK): {
|
||||
sif::printInfo("GyroL3GD20H::initialize: Interrupt transfer success\n");
|
||||
// Wait for the transfer to complete
|
||||
while (transferState == TransferStates::WAIT) {
|
||||
TaskFactory::delayTask(1);
|
||||
}
|
||||
|
||||
uint8_t whoAmIVal = rxBuffer[1];
|
||||
if(whoAmIVal != EXPECTED_WHO_AM_I_VAL) {
|
||||
sif::printDebug("GyroL3GD20H::initialize: "
|
||||
"Read WHO AM I value %d not equal to expected value!\n", whoAmIVal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(HAL_BUSY):
|
||||
case(HAL_ERROR):
|
||||
case(HAL_TIMEOUT): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Initialization failure using interrupts\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
sif::printInfo("GyroL3GD20H::initialize: Configuring device\n");
|
||||
transferState = TransferStates::WAIT;
|
||||
// Configure the 5 configuration registers
|
||||
uint8_t configRegs[5];
|
||||
prepareConfigRegs(configRegs);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
switch(HAL_SPI_TransmitReceive_IT(spiHandle, txBuffer.data(), rxBuffer.data(), 6)) {
|
||||
case(HAL_OK): {
|
||||
// Wait for the transfer to complete
|
||||
while (transferState == TransferStates::WAIT) {
|
||||
TaskFactory::delayTask(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(HAL_BUSY):
|
||||
case(HAL_ERROR):
|
||||
case(HAL_TIMEOUT): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Initialization failure using interrupts\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
txBuffer[0] = CTRL_REG_1 | STM_AUTO_INCREMENT_MASK | STM_READ_MASK;
|
||||
std::memset(txBuffer.data() + 1, 0 , 5);
|
||||
transferState = TransferStates::WAIT;
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
switch(HAL_SPI_TransmitReceive_IT(spiHandle, txBuffer.data(), rxBuffer.data(), 6)) {
|
||||
case(HAL_OK): {
|
||||
// Wait for the transfer to complete
|
||||
while (transferState == TransferStates::WAIT) {
|
||||
TaskFactory::delayTask(1);
|
||||
}
|
||||
if(rxBuffer[1] != configRegs[0] or rxBuffer[2] != configRegs[1] or
|
||||
rxBuffer[3] != configRegs[2] or rxBuffer[4] != configRegs[3] or
|
||||
rxBuffer[5] != configRegs[4]) {
|
||||
sif::printWarning("GyroL3GD20H::initialize: Configuration failure\n");
|
||||
}
|
||||
else {
|
||||
sif::printInfo("GyroL3GD20H::initialize: Configuration success\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case(HAL_BUSY):
|
||||
case(HAL_ERROR):
|
||||
case(HAL_TIMEOUT): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Initialization failure using interrupts\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t GyroL3GD20H::handleInterruptSensorRead() {
|
||||
transferState = TransferStates::WAIT;
|
||||
txBuffer[0] = CTRL_REG_1 | STM_AUTO_INCREMENT_MASK | STM_READ_MASK;
|
||||
std::memset(txBuffer.data() + 1, 0 , 14);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
switch(HAL_SPI_TransmitReceive_IT(spiHandle, txBuffer.data(), rxBuffer.data(), 15)) {
|
||||
case(HAL_OK): {
|
||||
// Wait for the transfer to complete
|
||||
while (transferState == TransferStates::WAIT) {
|
||||
TaskFactory::delayTask(1);
|
||||
}
|
||||
handleSensorReadout();
|
||||
break;
|
||||
}
|
||||
case(HAL_BUSY):
|
||||
case(HAL_ERROR):
|
||||
case(HAL_TIMEOUT): {
|
||||
sif::printDebug("GyroL3GD20H::initialize: Sensor read failure using interrupts\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
void GyroL3GD20H::prepareConfigRegs(uint8_t* configRegs) {
|
||||
// Enable sensor
|
||||
configRegs[0] = 0b00001111;
|
||||
configRegs[1] = 0b00000000;
|
||||
configRegs[2] = 0b00000000;
|
||||
// Big endian select
|
||||
configRegs[3] = 0b01000000;
|
||||
configRegs[4] = 0b00000000;
|
||||
|
||||
txBuffer[0] = CTRL_REG_1 | STM_AUTO_INCREMENT_MASK;
|
||||
std::memcpy(txBuffer.data() + 1, configRegs, 5);
|
||||
}
|
||||
|
||||
uint8_t GyroL3GD20H::readRegPolling(uint8_t reg) {
|
||||
uint8_t rxBuf[2] = {};
|
||||
uint8_t txBuf[2] = {};
|
||||
txBuf[0] = reg | STM_READ_MASK;
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
|
||||
auto result = HAL_SPI_TransmitReceive(spiHandle, txBuf, rxBuf, 2, 1000);
|
||||
if(result) {};
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
|
||||
return rxBuf[1];
|
||||
}
|
||||
|
||||
void GyroL3GD20H::handleSensorReadout() {
|
||||
uint8_t statusReg = rxBuffer[8];
|
||||
int16_t gyroXRaw = rxBuffer[9] << 8 | rxBuffer[10];
|
||||
float gyroX = static_cast<float>(gyroXRaw) * 0.00875;
|
||||
int16_t gyroYRaw = rxBuffer[11] << 8 | rxBuffer[12];
|
||||
float gyroY = static_cast<float>(gyroYRaw) * 0.00875;
|
||||
int16_t gyroZRaw = rxBuffer[13] << 8 | rxBuffer[14];
|
||||
float gyroZ = static_cast<float>(gyroZRaw) * 0.00875;
|
||||
sif::printInfo("Status register: 0b" BYTE_TO_BINARY_PATTERN "\n", BYTE_TO_BINARY(statusReg));
|
||||
sif::printInfo("Gyro X: %f\n", gyroX);
|
||||
sif::printInfo("Gyro Y: %f\n", gyroY);
|
||||
sif::printInfo("Gyro Z: %f\n", gyroZ);
|
||||
}
|
||||
|
||||
|
||||
void GyroL3GD20H::spiTransferCompleteCallback(SPI_HandleTypeDef *hspi, void* args) {
|
||||
transferState = TransferStates::SUCCESS;
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
|
||||
if(GyroL3GD20H::transferMode == spi::TransferModes::DMA) {
|
||||
// Invalidate cache prior to access by CPU
|
||||
SCB_InvalidateDCache_by_Addr ((uint32_t *)GyroL3GD20H::rxBuffer.data(),
|
||||
GyroL3GD20H::recvBufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SPI error callbacks.
|
||||
* @param hspi: SPI handle
|
||||
* @note This example shows a simple way to report transfer error, and you can
|
||||
* add your own implementation.
|
||||
* @retval None
|
||||
*/
|
||||
void GyroL3GD20H::spiTransferErrorCallback(SPI_HandleTypeDef *hspi, void* args) {
|
||||
transferState = TransferStates::FAILURE;
|
||||
}
|
70
hal/src/fsfw/hal/stm32h7/devicetest/GyroL3GD20H.h
Normal file
70
hal/src/fsfw/hal/stm32h7/devicetest/GyroL3GD20H.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef FSFW_HAL_STM32H7_DEVICETEST_GYRO_L3GD20H_H_
|
||||
#define FSFW_HAL_STM32H7_DEVICETEST_GYRO_L3GD20H_H_
|
||||
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "stm32h7xx_hal_spi.h"
|
||||
#include "../spi/mspInit.h"
|
||||
#include "../spi/spiDefinitions.h"
|
||||
|
||||
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
enum class TransferStates {
|
||||
IDLE,
|
||||
WAIT,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
};
|
||||
|
||||
class GyroL3GD20H {
|
||||
public:
|
||||
GyroL3GD20H(SPI_HandleTypeDef* spiHandle, spi::TransferModes transferMode);
|
||||
~GyroL3GD20H();
|
||||
|
||||
ReturnValue_t initialize();
|
||||
ReturnValue_t performOperation();
|
||||
|
||||
private:
|
||||
|
||||
const uint8_t WHO_AM_I_REG = 0b00001111;
|
||||
const uint8_t STM_READ_MASK = 0b10000000;
|
||||
const uint8_t STM_AUTO_INCREMENT_MASK = 0b01000000;
|
||||
const uint8_t EXPECTED_WHO_AM_I_VAL = 0b11010111;
|
||||
const uint8_t CTRL_REG_1 = 0b00100000;
|
||||
const uint32_t L3G_RANGE = 245;
|
||||
|
||||
SPI_HandleTypeDef* spiHandle;
|
||||
|
||||
static spi::TransferModes transferMode;
|
||||
static constexpr size_t recvBufferSize = 32 * 10;
|
||||
static std::array<uint8_t, recvBufferSize> rxBuffer;
|
||||
static constexpr size_t txBufferSize = 32;
|
||||
static std::array<uint8_t, txBufferSize> txBuffer;
|
||||
|
||||
ReturnValue_t handleDmaTransferInit();
|
||||
ReturnValue_t handlePollingTransferInit();
|
||||
ReturnValue_t handleInterruptTransferInit();
|
||||
|
||||
ReturnValue_t handleDmaSensorRead();
|
||||
HAL_StatusTypeDef performDmaTransfer(size_t sendSize);
|
||||
ReturnValue_t handlePollingSensorRead();
|
||||
ReturnValue_t handleInterruptSensorRead();
|
||||
|
||||
uint8_t readRegPolling(uint8_t reg);
|
||||
|
||||
static void spiTransferCompleteCallback(SPI_HandleTypeDef *hspi, void* args);
|
||||
static void spiTransferErrorCallback(SPI_HandleTypeDef *hspi, void* args);
|
||||
|
||||
|
||||
void prepareConfigRegs(uint8_t* configRegs);
|
||||
void handleSensorReadout();
|
||||
|
||||
|
||||
DMA_HandleTypeDef* txDmaHandle = {};
|
||||
DMA_HandleTypeDef* rxDmaHandle = {};
|
||||
spi::MspCfgBase* mspCfg = {};
|
||||
};
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_DEVICETEST_GYRO_L3GD20H_H_ */
|
84
hal/src/fsfw/hal/stm32h7/dma.cpp
Normal file
84
hal/src/fsfw/hal/stm32h7/dma.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
#include <fsfw/hal/stm32h7/dma.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
user_handler_t DMA_1_USER_HANDLERS[8];
|
||||
user_args_t DMA_1_USER_ARGS[8];
|
||||
|
||||
user_handler_t DMA_2_USER_HANDLERS[8];
|
||||
user_args_t DMA_2_USER_ARGS[8];
|
||||
|
||||
void dma::assignDmaUserHandler(DMAIndexes dma_idx, DMAStreams stream_idx,
|
||||
user_handler_t user_handler, user_args_t user_args) {
|
||||
if(dma_idx == DMA_1) {
|
||||
DMA_1_USER_HANDLERS[stream_idx] = user_handler;
|
||||
DMA_1_USER_ARGS[stream_idx] = user_args;
|
||||
}
|
||||
else if(dma_idx == DMA_2) {
|
||||
DMA_2_USER_HANDLERS[stream_idx] = user_handler;
|
||||
DMA_2_USER_ARGS[stream_idx] = user_args;
|
||||
}
|
||||
}
|
||||
|
||||
// The interrupt handlers in the format required for the IRQ vector table
|
||||
|
||||
/* Do not change these function names! They need to be exactly equal to the name of the functions
|
||||
defined in the startup_stm32h743xx.s files! */
|
||||
|
||||
#define GENERIC_DMA_IRQ_HANDLER(DMA_IDX, STREAM_IDX) \
|
||||
if(DMA_##DMA_IDX##_USER_HANDLERS[STREAM_IDX] != NULL) { \
|
||||
DMA_##DMA_IDX##_USER_HANDLERS[STREAM_IDX](DMA_##DMA_IDX##_USER_ARGS[STREAM_IDX]); \
|
||||
return; \
|
||||
} \
|
||||
Default_Handler() \
|
||||
|
||||
extern"C" void DMA1_Stream0_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(1, 0);
|
||||
}
|
||||
extern"C" void DMA1_Stream1_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(1, 1);
|
||||
}
|
||||
extern"C" void DMA1_Stream2_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(1, 2);
|
||||
}
|
||||
extern"C" void DMA1_Stream3_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(1, 3);
|
||||
}
|
||||
extern"C" void DMA1_Stream4_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(1, 4);
|
||||
}
|
||||
extern"C" void DMA1_Stream5_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(1, 5);
|
||||
}
|
||||
extern"C" void DMA1_Stream6_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(1, 6);
|
||||
}
|
||||
extern"C" void DMA1_Stream7_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(1, 7);
|
||||
}
|
||||
|
||||
extern"C" void DMA2_Stream0_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(2, 0);
|
||||
}
|
||||
extern"C" void DMA2_Stream1_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(2, 1);
|
||||
}
|
||||
extern"C" void DMA2_Stream2_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(2, 2);
|
||||
}
|
||||
extern"C" void DMA2_Stream3_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(2, 3);
|
||||
}
|
||||
extern"C" void DMA2_Stream4_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(2, 4);
|
||||
}
|
||||
extern"C" void DMA2_Stream5_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(2, 5);
|
||||
}
|
||||
extern"C" void DMA2_Stream6_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(2, 6);
|
||||
}
|
||||
extern"C" void DMA2_Stream7_IRQHandler() {
|
||||
GENERIC_DMA_IRQ_HANDLER(2, 7);
|
||||
}
|
49
hal/src/fsfw/hal/stm32h7/dma.h
Normal file
49
hal/src/fsfw/hal/stm32h7/dma.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef FSFW_HAL_STM32H7_DMA_H_
|
||||
#define FSFW_HAL_STM32H7_DMA_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "interrupts.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace dma {
|
||||
|
||||
enum DMAType {
|
||||
TX = 0,
|
||||
RX = 1
|
||||
};
|
||||
|
||||
enum DMAIndexes: uint8_t {
|
||||
DMA_1 = 1,
|
||||
DMA_2 = 2
|
||||
};
|
||||
|
||||
enum DMAStreams {
|
||||
STREAM_0 = 0,
|
||||
STREAM_1 = 1,
|
||||
STREAM_2 = 2,
|
||||
STREAM_3 = 3,
|
||||
STREAM_4 = 4,
|
||||
STREAM_5 = 5,
|
||||
STREAM_6 = 6,
|
||||
STREAM_7 = 7,
|
||||
} ;
|
||||
|
||||
/**
|
||||
* Assign user interrupt handlers for DMA streams, allowing to pass an
|
||||
* arbitrary argument as well. Generally, this argument will be the related DMA handle.
|
||||
* @param user_handler
|
||||
* @param user_args
|
||||
*/
|
||||
void assignDmaUserHandler(DMAIndexes dma_idx, DMAStreams stream_idx,
|
||||
user_handler_t user_handler, user_args_t user_args);
|
||||
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_DMA_H_ */
|
3
hal/src/fsfw/hal/stm32h7/gpio/CMakeLists.txt
Normal file
3
hal/src/fsfw/hal/stm32h7/gpio/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
gpio.cpp
|
||||
)
|
71
hal/src/fsfw/hal/stm32h7/gpio/gpio.cpp
Normal file
71
hal/src/fsfw/hal/stm32h7/gpio/gpio.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "fsfw/hal/stm32h7/gpio/gpio.h"
|
||||
|
||||
#include "stm32h7xx_hal_rcc.h"
|
||||
|
||||
void gpio::initializeGpioClock(GPIO_TypeDef* gpioPort) {
|
||||
#ifdef GPIOA
|
||||
if(gpioPort == GPIOA) {
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOB
|
||||
if(gpioPort == GPIOB) {
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOC
|
||||
if(gpioPort == GPIOC) {
|
||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOD
|
||||
if(gpioPort == GPIOD) {
|
||||
__HAL_RCC_GPIOD_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOE
|
||||
if(gpioPort == GPIOE) {
|
||||
__HAL_RCC_GPIOE_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOF
|
||||
if(gpioPort == GPIOF) {
|
||||
__HAL_RCC_GPIOF_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOG
|
||||
if(gpioPort == GPIOG) {
|
||||
__HAL_RCC_GPIOG_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOH
|
||||
if(gpioPort == GPIOH) {
|
||||
__HAL_RCC_GPIOH_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOI
|
||||
if(gpioPort == GPIOI) {
|
||||
__HAL_RCC_GPIOI_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOJ
|
||||
if(gpioPort == GPIOJ) {
|
||||
__HAL_RCC_GPIOJ_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPIOK
|
||||
if(gpioPort == GPIOK) {
|
||||
__HAL_RCC_GPIOK_CLK_ENABLE();
|
||||
}
|
||||
#endif
|
||||
}
|
12
hal/src/fsfw/hal/stm32h7/gpio/gpio.h
Normal file
12
hal/src/fsfw/hal/stm32h7/gpio/gpio.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef FSFW_HAL_STM32H7_GPIO_GPIO_H_
|
||||
#define FSFW_HAL_STM32H7_GPIO_GPIO_H_
|
||||
|
||||
#include "stm32h7xx.h"
|
||||
|
||||
namespace gpio {
|
||||
|
||||
void initializeGpioClock(GPIO_TypeDef* gpioPort);
|
||||
|
||||
}
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_GPIO_GPIO_H_ */
|
2
hal/src/fsfw/hal/stm32h7/i2c/CMakeLists.txt
Normal file
2
hal/src/fsfw/hal/stm32h7/i2c/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
)
|
28
hal/src/fsfw/hal/stm32h7/interrupts.h
Normal file
28
hal/src/fsfw/hal/stm32h7/interrupts.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef FSFW_HAL_STM32H7_INTERRUPTS_H_
|
||||
#define FSFW_HAL_STM32H7_INTERRUPTS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Default handler which is defined in startup file as assembly code.
|
||||
*/
|
||||
extern void Default_Handler();
|
||||
|
||||
typedef void (*user_handler_t) (void*);
|
||||
typedef void* user_args_t;
|
||||
|
||||
enum IrqPriorities: uint8_t {
|
||||
HIGHEST = 0,
|
||||
HIGHEST_FREERTOS = 6,
|
||||
LOWEST = 15
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_INTERRUPTS_H_ */
|
9
hal/src/fsfw/hal/stm32h7/spi/CMakeLists.txt
Normal file
9
hal/src/fsfw/hal/stm32h7/spi/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
spiCore.cpp
|
||||
spiDefinitions.cpp
|
||||
spiInterrupts.cpp
|
||||
mspInit.cpp
|
||||
SpiCookie.cpp
|
||||
SpiComIF.cpp
|
||||
stm32h743ziSpi.cpp
|
||||
)
|
468
hal/src/fsfw/hal/stm32h7/spi/SpiComIF.cpp
Normal file
468
hal/src/fsfw/hal/stm32h7/spi/SpiComIF.cpp
Normal file
@ -0,0 +1,468 @@
|
||||
#include "fsfw/hal/stm32h7/spi/SpiComIF.h"
|
||||
#include "fsfw/hal/stm32h7/spi/SpiCookie.h"
|
||||
|
||||
#include "fsfw/tasks/SemaphoreFactory.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiCore.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiInterrupts.h"
|
||||
#include "fsfw/hal/stm32h7/spi/mspInit.h"
|
||||
#include "fsfw/hal/stm32h7/gpio/gpio.h"
|
||||
|
||||
// FreeRTOS required special Semaphore handling from an ISR. Therefore, we use the concrete
|
||||
// instance here, because RTEMS and FreeRTOS are the only relevant OSALs currently
|
||||
// and it is not trivial to add a releaseFromISR to the SemaphoreIF
|
||||
#if defined FSFW_OSAL_RTEMS
|
||||
#include "fsfw/osal/rtems/BinarySemaphore.h"
|
||||
#elif defined FSFW_OSAL_FREERTOS
|
||||
#include "fsfw/osal/freertos/TaskManagement.h"
|
||||
#include "fsfw/osal/freertos/BinarySemaphore.h"
|
||||
#endif
|
||||
|
||||
#include "stm32h7xx_hal_gpio.h"
|
||||
|
||||
SpiComIF::SpiComIF(object_id_t objectId): SystemObject(objectId) {
|
||||
void* irqArgsVoided = reinterpret_cast<void*>(&irqArgs);
|
||||
spi::assignTransferRxTxCompleteCallback(&spiTransferCompleteCallback, irqArgsVoided);
|
||||
spi::assignTransferRxCompleteCallback(&spiTransferRxCompleteCallback, irqArgsVoided);
|
||||
spi::assignTransferTxCompleteCallback(&spiTransferTxCompleteCallback, irqArgsVoided);
|
||||
spi::assignTransferErrorCallback(&spiTransferErrorCallback, irqArgsVoided);
|
||||
}
|
||||
|
||||
void SpiComIF::configureCacheMaintenanceOnTxBuffer(bool enable) {
|
||||
this->cacheMaintenanceOnTxBuffer = enable;
|
||||
}
|
||||
|
||||
void SpiComIF::addDmaHandles(DMA_HandleTypeDef *txHandle, DMA_HandleTypeDef *rxHandle) {
|
||||
spi::setDmaHandles(txHandle, rxHandle);
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::initialize() {
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) {
|
||||
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||||
if(spiCookie == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error < "SpiComIF::initializeInterface: Invalid cookie" << std::endl;
|
||||
#else
|
||||
sif::printError("SpiComIF::initializeInterface: Invalid cookie\n");
|
||||
#endif
|
||||
return NULLPOINTER;
|
||||
}
|
||||
auto transferMode = spiCookie->getTransferMode();
|
||||
|
||||
if(transferMode == spi::TransferModes::DMA) {
|
||||
DMA_HandleTypeDef *txHandle = nullptr;
|
||||
DMA_HandleTypeDef *rxHandle = nullptr;
|
||||
spi::getDmaHandles(&txHandle, &rxHandle);
|
||||
if(txHandle == nullptr or rxHandle == nullptr) {
|
||||
sif::printError("SpiComIF::initialize: DMA handles not set!\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
// This semaphore ensures thread-safety for a given bus
|
||||
spiSemaphore = dynamic_cast<BinarySemaphore*>(
|
||||
SemaphoreFactory::instance()->createBinarySemaphore());
|
||||
address_t spiAddress = spiCookie->getDeviceAddress();
|
||||
|
||||
auto iter = spiDeviceMap.find(spiAddress);
|
||||
if(iter == spiDeviceMap.end()) {
|
||||
size_t bufferSize = spiCookie->getMaxRecvSize();
|
||||
auto statusPair = spiDeviceMap.emplace(spiAddress, SpiInstance(bufferSize));
|
||||
if (not statusPair.second) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::initializeInterface: Failed to insert device with address " <<
|
||||
spiAddress << "to SPI device map" << std::endl;
|
||||
#else
|
||||
sif::printError("SpiComIF::initializeInterface: Failed to insert device with address "
|
||||
"%lu to SPI device map\n", static_cast<unsigned long>(spiAddress));
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
auto gpioPin = spiCookie->getChipSelectGpioPin();
|
||||
auto gpioPort = spiCookie->getChipSelectGpioPort();
|
||||
|
||||
SPI_HandleTypeDef& spiHandle = spiCookie->getSpiHandle();
|
||||
|
||||
auto spiIdx = spiCookie->getSpiIdx();
|
||||
if(spiIdx == spi::SpiBus::SPI_1) {
|
||||
#ifdef SPI1
|
||||
spiHandle.Instance = SPI1;
|
||||
#endif
|
||||
}
|
||||
else if(spiIdx == spi::SpiBus::SPI_2) {
|
||||
#ifdef SPI2
|
||||
spiHandle.Instance = SPI2;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
printCfgError("SPI Bus Index");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
auto mspCfg = spiCookie->getMspCfg();
|
||||
|
||||
if(transferMode == spi::TransferModes::POLLING) {
|
||||
auto typedCfg = dynamic_cast<spi::MspPollingConfigStruct*>(mspCfg);
|
||||
if(typedCfg == nullptr) {
|
||||
printCfgError("Polling MSP");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
spi::setSpiPollingMspFunctions(typedCfg);
|
||||
}
|
||||
else if(transferMode == spi::TransferModes::INTERRUPT) {
|
||||
auto typedCfg = dynamic_cast<spi::MspIrqConfigStruct*>(mspCfg);
|
||||
if(typedCfg == nullptr) {
|
||||
printCfgError("IRQ MSP");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
spi::setSpiIrqMspFunctions(typedCfg);
|
||||
}
|
||||
else if(transferMode == spi::TransferModes::DMA) {
|
||||
auto typedCfg = dynamic_cast<spi::MspDmaConfigStruct*>(mspCfg);
|
||||
if(typedCfg == nullptr) {
|
||||
printCfgError("DMA MSP");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
// Check DMA handles
|
||||
DMA_HandleTypeDef* txHandle = nullptr;
|
||||
DMA_HandleTypeDef* rxHandle = nullptr;
|
||||
spi::getDmaHandles(&txHandle, &rxHandle);
|
||||
if(txHandle == nullptr or rxHandle == nullptr) {
|
||||
printCfgError("DMA Handle");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
spi::setSpiDmaMspFunctions(typedCfg);
|
||||
}
|
||||
|
||||
gpio::initializeGpioClock(gpioPort);
|
||||
GPIO_InitTypeDef chipSelect = {};
|
||||
chipSelect.Pin = gpioPin;
|
||||
chipSelect.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
HAL_GPIO_Init(gpioPort, &chipSelect);
|
||||
HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET);
|
||||
|
||||
if(HAL_SPI_Init(&spiHandle) != HAL_OK) {
|
||||
sif::printWarning("SpiComIF::initialize: Error initializing SPI\n");
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
// The MSP configuration struct is not required anymore
|
||||
spiCookie->deleteMspCfg();
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::sendMessage(CookieIF *cookie, const uint8_t *sendData, size_t sendLen) {
|
||||
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||||
if(spiCookie == nullptr) {
|
||||
return NULLPOINTER;
|
||||
}
|
||||
|
||||
SPI_HandleTypeDef& spiHandle = spiCookie->getSpiHandle();
|
||||
|
||||
auto iter = spiDeviceMap.find(spiCookie->getDeviceAddress());
|
||||
if(iter == spiDeviceMap.end()) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
iter->second.currentTransferLen = sendLen;
|
||||
|
||||
auto transferMode = spiCookie->getTransferMode();
|
||||
switch(spiCookie->getTransferState()) {
|
||||
case(spi::TransferStates::IDLE): {
|
||||
break;
|
||||
}
|
||||
case(spi::TransferStates::WAIT):
|
||||
case(spi::TransferStates::FAILURE):
|
||||
case(spi::TransferStates::SUCCESS):
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
switch(transferMode) {
|
||||
case(spi::TransferModes::POLLING): {
|
||||
return handlePollingSendOperation(iter->second.replyBuffer.data(), spiHandle, *spiCookie,
|
||||
sendData, sendLen);
|
||||
}
|
||||
case(spi::TransferModes::INTERRUPT): {
|
||||
return handleInterruptSendOperation(iter->second.replyBuffer.data(), spiHandle, *spiCookie,
|
||||
sendData, sendLen);
|
||||
}
|
||||
case(spi::TransferModes::DMA): {
|
||||
return handleDmaSendOperation(iter->second.replyBuffer.data(), spiHandle, *spiCookie,
|
||||
sendData, sendLen);
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::getSendSuccess(CookieIF *cookie) {
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::requestReceiveMessage(CookieIF *cookie, size_t requestLen) {
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) {
|
||||
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||||
if(spiCookie == nullptr) {
|
||||
return NULLPOINTER;
|
||||
}
|
||||
switch(spiCookie->getTransferState()) {
|
||||
case(spi::TransferStates::SUCCESS): {
|
||||
auto iter = spiDeviceMap.find(spiCookie->getDeviceAddress());
|
||||
if(iter == spiDeviceMap.end()) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
*buffer = iter->second.replyBuffer.data();
|
||||
*size = iter->second.currentTransferLen;
|
||||
spiCookie->setTransferState(spi::TransferStates::IDLE);
|
||||
break;
|
||||
}
|
||||
case(spi::TransferStates::FAILURE): {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "SpiComIF::readReceivedMessage: Transfer failure" << std::endl;
|
||||
#else
|
||||
sif::printWarning("SpiComIF::readReceivedMessage: Transfer failure\n");
|
||||
#endif
|
||||
#endif
|
||||
spiCookie->setTransferState(spi::TransferStates::IDLE);
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
case(spi::TransferStates::WAIT):
|
||||
case(spi::TransferStates::IDLE): {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
void SpiComIF::setDefaultPollingTimeout(dur_millis_t timeout) {
|
||||
this->defaultPollingTimeout = timeout;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::handlePollingSendOperation(uint8_t* recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t *sendData, size_t sendLen) {
|
||||
auto gpioPort = spiCookie.getChipSelectGpioPort();
|
||||
auto gpioPin = spiCookie.getChipSelectGpioPin();
|
||||
auto returnval = spiSemaphore->acquire(timeoutType, timeoutMs);
|
||||
if(returnval != HasReturnvaluesIF::RETURN_OK) {
|
||||
return returnval;
|
||||
}
|
||||
spiCookie.setTransferState(spi::TransferStates::WAIT);
|
||||
HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_RESET);
|
||||
auto result = HAL_SPI_TransmitReceive(&spiHandle, const_cast<uint8_t*>(sendData),
|
||||
recvPtr, sendLen, defaultPollingTimeout);
|
||||
HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET);
|
||||
spiSemaphore->release();
|
||||
switch(result) {
|
||||
case(HAL_OK): {
|
||||
spiCookie.setTransferState(spi::TransferStates::SUCCESS);
|
||||
break;
|
||||
}
|
||||
case(HAL_TIMEOUT): {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "SpiComIF::sendMessage: Polling Mode | Timeout for SPI device" <<
|
||||
spiCookie->getDeviceAddress() << std::endl;
|
||||
#else
|
||||
sif::printWarning("SpiComIF::sendMessage: Polling Mode | Timeout for SPI device %d\n",
|
||||
spiCookie.getDeviceAddress());
|
||||
#endif
|
||||
#endif
|
||||
spiCookie.setTransferState(spi::TransferStates::FAILURE);
|
||||
return spi::HAL_TIMEOUT_RETVAL;
|
||||
}
|
||||
case(HAL_ERROR):
|
||||
default: {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "SpiComIF::sendMessage: Polling Mode | HAL error for SPI device" <<
|
||||
spiCookie->getDeviceAddress() << std::endl;
|
||||
#else
|
||||
sif::printWarning("SpiComIF::sendMessage: Polling Mode | HAL error for SPI device %d\n",
|
||||
spiCookie.getDeviceAddress());
|
||||
#endif
|
||||
#endif
|
||||
spiCookie.setTransferState(spi::TransferStates::FAILURE);
|
||||
return spi::HAL_ERROR_RETVAL;
|
||||
}
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::handleInterruptSendOperation(uint8_t* recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t * sendData, size_t sendLen) {
|
||||
return handleIrqSendOperation(recvPtr, spiHandle, spiCookie, sendData, sendLen);
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::handleDmaSendOperation(uint8_t* recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t * sendData, size_t sendLen) {
|
||||
return handleIrqSendOperation(recvPtr, spiHandle, spiCookie, sendData, sendLen);
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::handleIrqSendOperation(uint8_t *recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t *sendData, size_t sendLen) {
|
||||
ReturnValue_t result = genericIrqSendSetup(recvPtr, spiHandle, spiCookie, sendData, sendLen);
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
return result;
|
||||
}
|
||||
// yet another HAL driver which is not const-correct..
|
||||
HAL_StatusTypeDef status = HAL_OK;
|
||||
auto transferMode = spiCookie.getTransferMode();
|
||||
if(transferMode == spi::TransferModes::DMA) {
|
||||
if(cacheMaintenanceOnTxBuffer) {
|
||||
/* Clean D-cache. Make sure the address is 32-byte aligned and add 32-bytes to length,
|
||||
in case it overlaps cacheline */
|
||||
SCB_CleanDCache_by_Addr((uint32_t*)(((uint32_t) sendData ) & ~(uint32_t)0x1F),
|
||||
sendLen + 32);
|
||||
}
|
||||
status = HAL_SPI_TransmitReceive_DMA(&spiHandle, const_cast<uint8_t*>(sendData),
|
||||
currentRecvPtr, sendLen);
|
||||
}
|
||||
else {
|
||||
status = HAL_SPI_TransmitReceive_IT(&spiHandle, const_cast<uint8_t*>(sendData),
|
||||
currentRecvPtr, sendLen);
|
||||
}
|
||||
switch(status) {
|
||||
case(HAL_OK): {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return halErrorHandler(status, transferMode);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::halErrorHandler(HAL_StatusTypeDef status, spi::TransferModes transferMode) {
|
||||
char modeString[10];
|
||||
if(transferMode == spi::TransferModes::DMA) {
|
||||
std::snprintf(modeString, sizeof(modeString), "Dma");
|
||||
}
|
||||
else {
|
||||
std::snprintf(modeString, sizeof(modeString), "Interrupt");
|
||||
}
|
||||
sif::printWarning("SpiComIF::handle%sSendOperation: HAL error %d occured\n", modeString,
|
||||
status);
|
||||
switch(status) {
|
||||
case(HAL_BUSY): {
|
||||
return spi::HAL_BUSY_RETVAL;
|
||||
}
|
||||
case(HAL_ERROR): {
|
||||
return spi::HAL_ERROR_RETVAL;
|
||||
}
|
||||
case(HAL_TIMEOUT): {
|
||||
return spi::HAL_TIMEOUT_RETVAL;
|
||||
}
|
||||
default: {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ReturnValue_t SpiComIF::genericIrqSendSetup(uint8_t *recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t *sendData, size_t sendLen) {
|
||||
currentRecvPtr = recvPtr;
|
||||
currentRecvBuffSize = sendLen;
|
||||
|
||||
// Take the semaphore which will be released by a callback when the transfer is complete
|
||||
ReturnValue_t result = spiSemaphore->acquire(SemaphoreIF::TimeoutType::WAITING, timeoutMs);
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
// Configuration error
|
||||
sif::printWarning("SpiComIF::handleInterruptSendOperation: Semaphore "
|
||||
"could not be acquired after %d ms\n", timeoutMs);
|
||||
return result;
|
||||
}
|
||||
// Cache the current SPI handle in any case
|
||||
spi::setSpiHandle(&spiHandle);
|
||||
// Assign the IRQ arguments for the user callbacks
|
||||
irqArgs.comIF = this;
|
||||
irqArgs.spiCookie = &spiCookie;
|
||||
// The SPI handle is passed to the default SPI callback as a void argument. This callback
|
||||
// is different from the user callbacks specified above!
|
||||
spi::assignSpiUserArgs(spiCookie.getSpiIdx(), reinterpret_cast<void*>(&spiHandle));
|
||||
HAL_GPIO_WritePin(spiCookie.getChipSelectGpioPort(), spiCookie.getChipSelectGpioPin(),
|
||||
GPIO_PIN_RESET);
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
void SpiComIF::spiTransferTxCompleteCallback(SPI_HandleTypeDef *hspi, void *args) {
|
||||
genericIrqHandler(args, spi::TransferStates::SUCCESS);
|
||||
}
|
||||
|
||||
void SpiComIF::spiTransferRxCompleteCallback(SPI_HandleTypeDef *hspi, void *args) {
|
||||
genericIrqHandler(args, spi::TransferStates::SUCCESS);
|
||||
}
|
||||
|
||||
void SpiComIF::spiTransferCompleteCallback(SPI_HandleTypeDef *hspi, void *args) {
|
||||
genericIrqHandler(args, spi::TransferStates::SUCCESS);
|
||||
}
|
||||
|
||||
void SpiComIF::spiTransferErrorCallback(SPI_HandleTypeDef *hspi, void *args) {
|
||||
genericIrqHandler(args, spi::TransferStates::FAILURE);
|
||||
}
|
||||
|
||||
void SpiComIF::genericIrqHandler(void *irqArgsVoid, spi::TransferStates targetState) {
|
||||
IrqArgs* irqArgs = reinterpret_cast<IrqArgs*>(irqArgsVoid);
|
||||
if(irqArgs == nullptr) {
|
||||
return;
|
||||
}
|
||||
SpiCookie* spiCookie = irqArgs->spiCookie;
|
||||
SpiComIF* comIF = irqArgs->comIF;
|
||||
if(spiCookie == nullptr or comIF == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
spiCookie->setTransferState(targetState);
|
||||
|
||||
// Pull CS pin high again
|
||||
HAL_GPIO_WritePin(spiCookie->getChipSelectGpioPort(), spiCookie->getChipSelectGpioPin(),
|
||||
GPIO_PIN_SET);
|
||||
|
||||
#if defined FSFW_OSAL_FREERTOS
|
||||
// Release the task semaphore
|
||||
BaseType_t taskWoken = pdFALSE;
|
||||
ReturnValue_t result = BinarySemaphore::releaseFromISR(comIF->spiSemaphore->getSemaphore(),
|
||||
&taskWoken);
|
||||
#elif defined FSFW_OSAL_RTEMS
|
||||
ReturnValue_t result = comIF->spiSemaphore->release();
|
||||
#endif
|
||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||
// Configuration error
|
||||
printf("SpiComIF::genericIrqHandler: Failure releasing Semaphore!\n");
|
||||
}
|
||||
|
||||
// Perform cache maintenance operation for DMA transfers
|
||||
if(spiCookie->getTransferMode() == spi::TransferModes::DMA) {
|
||||
// Invalidate cache prior to access by CPU
|
||||
SCB_InvalidateDCache_by_Addr ((uint32_t *) comIF->currentRecvPtr,
|
||||
comIF->currentRecvBuffSize);
|
||||
}
|
||||
#if defined FSFW_OSAL_FREERTOS
|
||||
/* Request a context switch if the SPI ComIF task was woken up and has a higher priority
|
||||
than the currently running task */
|
||||
if(taskWoken == pdTRUE) {
|
||||
TaskManagement::requestContextSwitch(CallContext::ISR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SpiComIF::printCfgError(const char *const type) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "SpiComIF::initializeInterface: Invalid " << type << " configuration"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning("SpiComIF::initializeInterface: Invalid %s configuration\n", type);
|
||||
#endif
|
||||
}
|
130
hal/src/fsfw/hal/stm32h7/spi/SpiComIF.h
Normal file
130
hal/src/fsfw/hal/stm32h7/spi/SpiComIF.h
Normal file
@ -0,0 +1,130 @@
|
||||
#ifndef FSFW_HAL_STM32H7_SPI_SPICOMIF_H_
|
||||
#define FSFW_HAL_STM32H7_SPI_SPICOMIF_H_
|
||||
|
||||
#include "fsfw/tasks/SemaphoreIF.h"
|
||||
#include "fsfw/devicehandlers/DeviceCommunicationIF.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
|
||||
#include "fsfw/hal/stm32h7/spi/spiDefinitions.h"
|
||||
#include "stm32h7xx_hal_spi.h"
|
||||
#include "stm32h743xx.h"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class SpiCookie;
|
||||
class BinarySemaphore;
|
||||
|
||||
/**
|
||||
* @brief This communication interface allows using generic device handlers with using
|
||||
* the STM32H7 SPI peripherals
|
||||
* @details
|
||||
* This communication interface supports all three major communcation modes:
|
||||
* - Polling: Simple, but not recommended to real use-cases, blocks the CPU
|
||||
* - Interrupt: Good for small data only arriving occasionally
|
||||
* - DMA: Good for large data which also occur regularly. Please note that the number
|
||||
* of DMA channels in limited
|
||||
* The device specific information is usually kept in the SpiCookie class. The current
|
||||
* implementation limits the transfer mode for a given SPI bus.
|
||||
* @author R. Mueller
|
||||
*/
|
||||
class SpiComIF:
|
||||
public SystemObject,
|
||||
public DeviceCommunicationIF {
|
||||
public:
|
||||
/**
|
||||
* Create a SPI communication interface for the given SPI peripheral (spiInstance)
|
||||
* @param objectId
|
||||
* @param spiInstance
|
||||
* @param spiHandle
|
||||
* @param transferMode
|
||||
*/
|
||||
SpiComIF(object_id_t objectId);
|
||||
|
||||
/**
|
||||
* Allows the user to disable cache maintenance on the TX buffer. This can be done if the
|
||||
* TX buffers are places and MPU protected properly like specified in this link:
|
||||
* https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices
|
||||
* The cache maintenace is enabled by default.
|
||||
* @param enable
|
||||
*/
|
||||
void configureCacheMaintenanceOnTxBuffer(bool enable);
|
||||
|
||||
void setDefaultPollingTimeout(dur_millis_t timeout);
|
||||
|
||||
/**
|
||||
* Add the DMA handles. These need to be set in the DMA transfer mode is used.
|
||||
* @param txHandle
|
||||
* @param rxHandle
|
||||
*/
|
||||
void addDmaHandles(DMA_HandleTypeDef* txHandle, DMA_HandleTypeDef* rxHandle);
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
protected:
|
||||
|
||||
// DeviceCommunicationIF overrides
|
||||
virtual ReturnValue_t initializeInterface(CookieIF * cookie) override;
|
||||
virtual ReturnValue_t sendMessage(CookieIF *cookie,
|
||||
const uint8_t * sendData, size_t sendLen) override;
|
||||
virtual ReturnValue_t getSendSuccess(CookieIF *cookie) override;
|
||||
virtual ReturnValue_t requestReceiveMessage(CookieIF *cookie,
|
||||
size_t requestLen) override;
|
||||
virtual ReturnValue_t readReceivedMessage(CookieIF *cookie,
|
||||
uint8_t **buffer, size_t *size) override;
|
||||
|
||||
private:
|
||||
|
||||
struct SpiInstance {
|
||||
SpiInstance(size_t maxRecvSize): replyBuffer(std::vector<uint8_t>(maxRecvSize)) {}
|
||||
std::vector<uint8_t> replyBuffer;
|
||||
size_t currentTransferLen = 0;
|
||||
};
|
||||
|
||||
struct IrqArgs {
|
||||
SpiComIF* comIF = nullptr;
|
||||
SpiCookie* spiCookie = nullptr;
|
||||
};
|
||||
|
||||
IrqArgs irqArgs;
|
||||
|
||||
uint32_t defaultPollingTimeout = 50;
|
||||
|
||||
SemaphoreIF::TimeoutType timeoutType = SemaphoreIF::TimeoutType::WAITING;
|
||||
dur_millis_t timeoutMs = 20;
|
||||
|
||||
BinarySemaphore* spiSemaphore = nullptr;
|
||||
bool cacheMaintenanceOnTxBuffer = true;
|
||||
|
||||
using SpiDeviceMap = std::map<address_t, SpiInstance>;
|
||||
using SpiDeviceMapIter = SpiDeviceMap::iterator;
|
||||
|
||||
uint8_t* currentRecvPtr = nullptr;
|
||||
size_t currentRecvBuffSize = 0;
|
||||
|
||||
SpiDeviceMap spiDeviceMap;
|
||||
|
||||
ReturnValue_t handlePollingSendOperation(uint8_t* recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t * sendData, size_t sendLen);
|
||||
ReturnValue_t handleInterruptSendOperation(uint8_t* recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t * sendData, size_t sendLen);
|
||||
ReturnValue_t handleDmaSendOperation(uint8_t* recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t * sendData, size_t sendLen);
|
||||
ReturnValue_t handleIrqSendOperation(uint8_t* recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t * sendData, size_t sendLen);
|
||||
ReturnValue_t genericIrqSendSetup(uint8_t* recvPtr, SPI_HandleTypeDef& spiHandle,
|
||||
SpiCookie& spiCookie, const uint8_t * sendData, size_t sendLen);
|
||||
ReturnValue_t halErrorHandler(HAL_StatusTypeDef status, spi::TransferModes transferMode);
|
||||
|
||||
static void spiTransferTxCompleteCallback(SPI_HandleTypeDef *hspi, void* args);
|
||||
static void spiTransferRxCompleteCallback(SPI_HandleTypeDef *hspi, void* args);
|
||||
static void spiTransferCompleteCallback(SPI_HandleTypeDef *hspi, void* args);
|
||||
static void spiTransferErrorCallback(SPI_HandleTypeDef *hspi, void* args);
|
||||
|
||||
static void genericIrqHandler(void* irqArgs, spi::TransferStates targetState);
|
||||
|
||||
void printCfgError(const char* const type);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_SPI_SPICOMIF_H_ */
|
78
hal/src/fsfw/hal/stm32h7/spi/SpiCookie.cpp
Normal file
78
hal/src/fsfw/hal/stm32h7/spi/SpiCookie.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "fsfw/hal/stm32h7/spi/SpiCookie.h"
|
||||
|
||||
|
||||
SpiCookie::SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode,
|
||||
spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode,
|
||||
uint16_t chipSelectGpioPin, GPIO_TypeDef* chipSelectGpioPort, size_t maxRecvSize):
|
||||
deviceAddress(deviceAddress), spiIdx(spiIdx), spiSpeed(spiSpeed), spiMode(spiMode),
|
||||
transferMode(transferMode), chipSelectGpioPin(chipSelectGpioPin),
|
||||
chipSelectGpioPort(chipSelectGpioPort), mspCfg(mspCfg), maxRecvSize(maxRecvSize) {
|
||||
spiHandle.Init.DataSize = SPI_DATASIZE_8BIT;
|
||||
spiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
spiHandle.Init.TIMode = SPI_TIMODE_DISABLE;
|
||||
spiHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
||||
spiHandle.Init.CRCPolynomial = 7;
|
||||
spiHandle.Init.CRCLength = SPI_CRC_LENGTH_8BIT;
|
||||
spiHandle.Init.NSS = SPI_NSS_SOFT;
|
||||
spiHandle.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
|
||||
spiHandle.Init.Direction = SPI_DIRECTION_2LINES;
|
||||
// Recommended setting to avoid glitches
|
||||
spiHandle.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
|
||||
spiHandle.Init.Mode = SPI_MODE_MASTER;
|
||||
spi::assignSpiMode(spiMode, spiHandle);
|
||||
spiHandle.Init.BaudRatePrescaler = spi::getPrescaler(HAL_RCC_GetHCLKFreq(), spiSpeed);
|
||||
}
|
||||
|
||||
uint16_t SpiCookie::getChipSelectGpioPin() const {
|
||||
return chipSelectGpioPin;
|
||||
}
|
||||
|
||||
GPIO_TypeDef* SpiCookie::getChipSelectGpioPort() {
|
||||
return chipSelectGpioPort;
|
||||
}
|
||||
|
||||
address_t SpiCookie::getDeviceAddress() const {
|
||||
return deviceAddress;
|
||||
}
|
||||
|
||||
spi::SpiBus SpiCookie::getSpiIdx() const {
|
||||
return spiIdx;
|
||||
}
|
||||
|
||||
spi::SpiModes SpiCookie::getSpiMode() const {
|
||||
return spiMode;
|
||||
}
|
||||
|
||||
uint32_t SpiCookie::getSpiSpeed() const {
|
||||
return spiSpeed;
|
||||
}
|
||||
|
||||
size_t SpiCookie::getMaxRecvSize() const {
|
||||
return maxRecvSize;
|
||||
}
|
||||
|
||||
SPI_HandleTypeDef& SpiCookie::getSpiHandle() {
|
||||
return spiHandle;
|
||||
}
|
||||
|
||||
spi::MspCfgBase* SpiCookie::getMspCfg() {
|
||||
return mspCfg;
|
||||
}
|
||||
|
||||
void SpiCookie::deleteMspCfg() {
|
||||
if(mspCfg != nullptr) {
|
||||
delete mspCfg;
|
||||
}
|
||||
}
|
||||
|
||||
spi::TransferModes SpiCookie::getTransferMode() const {
|
||||
return transferMode;
|
||||
}
|
||||
|
||||
void SpiCookie::setTransferState(spi::TransferStates transferState) {
|
||||
this->transferState = transferState;
|
||||
}
|
||||
|
||||
spi::TransferStates SpiCookie::getTransferState() const {
|
||||
return this->transferState;
|
||||
}
|
75
hal/src/fsfw/hal/stm32h7/spi/SpiCookie.h
Normal file
75
hal/src/fsfw/hal/stm32h7/spi/SpiCookie.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef FSFW_HAL_STM32H7_SPI_SPICOOKIE_H_
|
||||
#define FSFW_HAL_STM32H7_SPI_SPICOOKIE_H_
|
||||
|
||||
#include "spiDefinitions.h"
|
||||
#include "mspInit.h"
|
||||
|
||||
#include "fsfw/devicehandlers/CookieIF.h"
|
||||
|
||||
#include "stm32h743xx.h"
|
||||
|
||||
/**
|
||||
* @brief SPI cookie implementation for the STM32H7 device family
|
||||
* @details
|
||||
* This cookie contains and caches device specific information to be used by the
|
||||
* SPI communication interface
|
||||
* @author R. Mueller
|
||||
*/
|
||||
class SpiCookie: public CookieIF {
|
||||
friend class SpiComIF;
|
||||
public:
|
||||
/**
|
||||
* Allows construction of a SPI cookie for a connected SPI device
|
||||
* @param deviceAddress
|
||||
* @param spiIdx SPI bus, e.g. SPI1 or SPI2
|
||||
* @param transferMode
|
||||
* @param mspCfg This is the MSP configuration. The user is expected to supply
|
||||
* a valid MSP configuration. See mspInit.h for functions
|
||||
* to create one.
|
||||
* @param spiSpeed
|
||||
* @param spiMode
|
||||
* @param chipSelectGpioPin GPIO port. Don't use a number here, use the 16 bit type
|
||||
* definitions supplied in the MCU header file! (e.g. GPIO_PIN_X)
|
||||
* @param chipSelectGpioPort GPIO port (e.g. GPIOA)
|
||||
* @param maxRecvSize Maximum expected receive size. Chose as small as possible.
|
||||
*/
|
||||
SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode,
|
||||
spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode,
|
||||
uint16_t chipSelectGpioPin, GPIO_TypeDef* chipSelectGpioPort, size_t maxRecvSize);
|
||||
|
||||
uint16_t getChipSelectGpioPin() const;
|
||||
GPIO_TypeDef* getChipSelectGpioPort();
|
||||
address_t getDeviceAddress() const;
|
||||
spi::SpiBus getSpiIdx() const;
|
||||
spi::SpiModes getSpiMode() const;
|
||||
spi::TransferModes getTransferMode() const;
|
||||
uint32_t getSpiSpeed() const;
|
||||
size_t getMaxRecvSize() const;
|
||||
SPI_HandleTypeDef& getSpiHandle();
|
||||
|
||||
private:
|
||||
address_t deviceAddress;
|
||||
SPI_HandleTypeDef spiHandle = {};
|
||||
spi::SpiBus spiIdx;
|
||||
uint32_t spiSpeed;
|
||||
spi::SpiModes spiMode;
|
||||
spi::TransferModes transferMode;
|
||||
volatile spi::TransferStates transferState = spi::TransferStates::IDLE;
|
||||
uint16_t chipSelectGpioPin;
|
||||
GPIO_TypeDef* chipSelectGpioPort;
|
||||
// The MSP configuration is cached here. Be careful when using this, it is automatically
|
||||
// deleted by the SPI communication interface if it is not required anymore!
|
||||
spi::MspCfgBase* mspCfg = nullptr;
|
||||
const size_t maxRecvSize;
|
||||
|
||||
// Only the SpiComIF is allowed to use this to prevent dangling pointers issues
|
||||
spi::MspCfgBase* getMspCfg();
|
||||
void deleteMspCfg();
|
||||
|
||||
void setTransferState(spi::TransferStates transferState);
|
||||
spi::TransferStates getTransferState() const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_SPI_SPICOOKIE_H_ */
|
253
hal/src/fsfw/hal/stm32h7/spi/mspInit.cpp
Normal file
253
hal/src/fsfw/hal/stm32h7/spi/mspInit.cpp
Normal file
@ -0,0 +1,253 @@
|
||||
#include "fsfw/hal/stm32h7/dma.h"
|
||||
#include "fsfw/hal/stm32h7/spi/mspInit.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiCore.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiInterrupts.h"
|
||||
|
||||
#include "stm32h743xx.h"
|
||||
#include "stm32h7xx_hal_spi.h"
|
||||
#include "stm32h7xx_hal_dma.h"
|
||||
#include "stm32h7xx_hal_def.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
spi::msp_func_t mspInitFunc = nullptr;
|
||||
spi::MspCfgBase* mspInitArgs = nullptr;
|
||||
|
||||
spi::msp_func_t mspDeinitFunc = nullptr;
|
||||
spi::MspCfgBase* mspDeinitArgs = nullptr;
|
||||
|
||||
/**
|
||||
* @brief SPI MSP Initialization
|
||||
* This function configures the hardware resources used in this example:
|
||||
* - Peripheral's clock enable
|
||||
* - Peripheral's GPIO Configuration
|
||||
* - DMA configuration for transmission request by peripheral
|
||||
* - NVIC configuration for DMA interrupt request enable
|
||||
* @param hspi: SPI handle pointer
|
||||
* @retval None
|
||||
*/
|
||||
void spi::halMspInitDma(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) {
|
||||
auto cfg = dynamic_cast<MspDmaConfigStruct*>(cfgBase);
|
||||
if(hspi == nullptr or cfg == nullptr) {
|
||||
return;
|
||||
}
|
||||
setSpiHandle(hspi);
|
||||
|
||||
DMA_HandleTypeDef* hdma_tx = nullptr;
|
||||
DMA_HandleTypeDef* hdma_rx = nullptr;
|
||||
spi::getDmaHandles(&hdma_tx, &hdma_rx);
|
||||
if(hdma_tx == nullptr or hdma_rx == nullptr) {
|
||||
printf("HAL_SPI_MspInit: Invalid DMA handles. Make sure to call setDmaHandles!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spi::halMspInitInterrupt(hspi, cfg);
|
||||
|
||||
// DMA setup
|
||||
if(cfg->dmaClkEnableWrapper == nullptr) {
|
||||
mspErrorHandler("spi::halMspInitDma", "DMA Clock init invalid");
|
||||
}
|
||||
cfg->dmaClkEnableWrapper();
|
||||
|
||||
// Configure the DMA
|
||||
/* Configure the DMA handler for Transmission process */
|
||||
if(hdma_tx->Instance == nullptr) {
|
||||
// Assume it was not configured properly
|
||||
mspErrorHandler("spi::halMspInitDma", "DMA TX handle invalid");
|
||||
}
|
||||
|
||||
HAL_DMA_Init(hdma_tx);
|
||||
/* Associate the initialized DMA handle to the the SPI handle */
|
||||
__HAL_LINKDMA(hspi, hdmatx, *hdma_tx);
|
||||
|
||||
HAL_DMA_Init(hdma_rx);
|
||||
/* Associate the initialized DMA handle to the the SPI handle */
|
||||
__HAL_LINKDMA(hspi, hdmarx, *hdma_rx);
|
||||
|
||||
/*##-4- Configure the NVIC for DMA #########################################*/
|
||||
/* NVIC configuration for DMA transfer complete interrupt (SPI1_RX) */
|
||||
// Assign the interrupt handler
|
||||
dma::assignDmaUserHandler(cfg->rxDmaIndex, cfg->rxDmaStream, &spi::dmaRxIrqHandler, hdma_rx);
|
||||
HAL_NVIC_SetPriority(cfg->rxDmaIrqNumber, cfg->rxPreEmptPriority, cfg->rxSubpriority);
|
||||
HAL_NVIC_EnableIRQ(cfg->rxDmaIrqNumber);
|
||||
|
||||
/* NVIC configuration for DMA transfer complete interrupt (SPI1_TX) */
|
||||
// Assign the interrupt handler
|
||||
dma::assignDmaUserHandler(cfg->txDmaIndex, cfg->txDmaStream,
|
||||
&spi::dmaTxIrqHandler, hdma_tx);
|
||||
HAL_NVIC_SetPriority(cfg->txDmaIrqNumber, cfg->txPreEmptPriority, cfg->txSubpriority);
|
||||
HAL_NVIC_EnableIRQ(cfg->txDmaIrqNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SPI MSP De-Initialization
|
||||
* This function frees the hardware resources used in this example:
|
||||
* - Disable the Peripheral's clock
|
||||
* - Revert GPIO, DMA and NVIC configuration to their default state
|
||||
* @param hspi: SPI handle pointer
|
||||
* @retval None
|
||||
*/
|
||||
void spi::halMspDeinitDma(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) {
|
||||
auto cfg = dynamic_cast<MspDmaConfigStruct*>(cfgBase);
|
||||
if(hspi == nullptr or cfg == nullptr) {
|
||||
return;
|
||||
}
|
||||
spi::halMspDeinitInterrupt(hspi, cfgBase);
|
||||
DMA_HandleTypeDef* hdma_tx = NULL;
|
||||
DMA_HandleTypeDef* hdma_rx = NULL;
|
||||
spi::getDmaHandles(&hdma_tx, &hdma_rx);
|
||||
if(hdma_tx == NULL || hdma_rx == NULL) {
|
||||
printf("HAL_SPI_MspInit: Invalid DMA handles. Make sure to call setDmaHandles!\n");
|
||||
}
|
||||
else {
|
||||
// Disable the DMA
|
||||
/* De-Initialize the DMA associated to transmission process */
|
||||
HAL_DMA_DeInit(hdma_tx);
|
||||
/* De-Initialize the DMA associated to reception process */
|
||||
HAL_DMA_DeInit(hdma_rx);
|
||||
}
|
||||
|
||||
// Disable the NVIC for DMA
|
||||
HAL_NVIC_DisableIRQ(cfg->txDmaIrqNumber);
|
||||
HAL_NVIC_DisableIRQ(cfg->rxDmaIrqNumber);
|
||||
|
||||
}
|
||||
|
||||
void spi::halMspInitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) {
|
||||
auto cfg = dynamic_cast<MspPollingConfigStruct*>(cfgBase);
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {};
|
||||
/*##-1- Enable peripherals and GPIO Clocks #################################*/
|
||||
/* Enable GPIO TX/RX clock */
|
||||
cfg->setupMacroWrapper();
|
||||
|
||||
/*##-2- Configure peripheral GPIO ##########################################*/
|
||||
/* SPI SCK GPIO pin configuration */
|
||||
GPIO_InitStruct.Pin = cfg->sckPin;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
||||
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
||||
GPIO_InitStruct.Alternate = cfg->sckAlternateFunction;
|
||||
HAL_GPIO_Init(cfg->sckPort, &GPIO_InitStruct);
|
||||
|
||||
/* SPI MISO GPIO pin configuration */
|
||||
GPIO_InitStruct.Pin = cfg->misoPin;
|
||||
GPIO_InitStruct.Alternate = cfg->misoAlternateFunction;
|
||||
HAL_GPIO_Init(cfg->misoPort, &GPIO_InitStruct);
|
||||
|
||||
/* SPI MOSI GPIO pin configuration */
|
||||
GPIO_InitStruct.Pin = cfg->mosiPin;
|
||||
GPIO_InitStruct.Alternate = cfg->mosiAlternateFunction;
|
||||
HAL_GPIO_Init(cfg->mosiPort, &GPIO_InitStruct);
|
||||
}
|
||||
|
||||
void spi::halMspDeinitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) {
|
||||
auto cfg = reinterpret_cast<MspPollingConfigStruct*>(cfgBase);
|
||||
// Reset peripherals
|
||||
cfg->cleanUpMacroWrapper();
|
||||
|
||||
// Disable peripherals and GPIO Clocks
|
||||
/* Configure SPI SCK as alternate function */
|
||||
HAL_GPIO_DeInit(cfg->sckPort, cfg->sckPin);
|
||||
/* Configure SPI MISO as alternate function */
|
||||
HAL_GPIO_DeInit(cfg->misoPort, cfg->misoPin);
|
||||
/* Configure SPI MOSI as alternate function */
|
||||
HAL_GPIO_DeInit(cfg->mosiPort, cfg->mosiPin);
|
||||
}
|
||||
|
||||
void spi::halMspInitInterrupt(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) {
|
||||
auto cfg = dynamic_cast<MspIrqConfigStruct*>(cfgBase);
|
||||
if(cfg == nullptr or hspi == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
spi::halMspInitPolling(hspi, cfg);
|
||||
// Configure the NVIC for SPI
|
||||
spi::assignSpiUserHandler(cfg->spiBus, cfg->spiIrqHandler, cfg->spiUserArgs);
|
||||
HAL_NVIC_SetPriority(cfg->spiIrqNumber, cfg->preEmptPriority, cfg->subpriority);
|
||||
HAL_NVIC_EnableIRQ(cfg->spiIrqNumber);
|
||||
}
|
||||
|
||||
void spi::halMspDeinitInterrupt(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) {
|
||||
auto cfg = dynamic_cast<MspIrqConfigStruct*>(cfgBase);
|
||||
spi::halMspDeinitPolling(hspi, cfg);
|
||||
// Disable the NVIC for SPI
|
||||
HAL_NVIC_DisableIRQ(cfg->spiIrqNumber);
|
||||
}
|
||||
|
||||
void spi::getMspInitFunction(msp_func_t* init_func, MspCfgBase** args) {
|
||||
if(init_func != NULL && args != NULL) {
|
||||
*init_func = mspInitFunc;
|
||||
*args = mspInitArgs;
|
||||
}
|
||||
}
|
||||
|
||||
void spi::getMspDeinitFunction(msp_func_t* deinit_func, MspCfgBase** args) {
|
||||
if(deinit_func != NULL && args != NULL) {
|
||||
*deinit_func = mspDeinitFunc;
|
||||
*args = mspDeinitArgs;
|
||||
}
|
||||
}
|
||||
|
||||
void spi::setSpiDmaMspFunctions(MspDmaConfigStruct* cfg,
|
||||
msp_func_t initFunc, msp_func_t deinitFunc) {
|
||||
mspInitFunc = initFunc;
|
||||
mspDeinitFunc = deinitFunc;
|
||||
mspInitArgs = cfg;
|
||||
mspDeinitArgs = cfg;
|
||||
}
|
||||
|
||||
void spi::setSpiIrqMspFunctions(MspIrqConfigStruct *cfg, msp_func_t initFunc,
|
||||
msp_func_t deinitFunc) {
|
||||
mspInitFunc = initFunc;
|
||||
mspDeinitFunc = deinitFunc;
|
||||
mspInitArgs = cfg;
|
||||
mspDeinitArgs = cfg;
|
||||
}
|
||||
|
||||
void spi::setSpiPollingMspFunctions(MspPollingConfigStruct *cfg, msp_func_t initFunc,
|
||||
msp_func_t deinitFunc) {
|
||||
mspInitFunc = initFunc;
|
||||
mspDeinitFunc = deinitFunc;
|
||||
mspInitArgs = cfg;
|
||||
mspDeinitArgs = cfg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SPI MSP Initialization
|
||||
* This function configures the hardware resources used in this example:
|
||||
* - Peripheral's clock enable
|
||||
* - Peripheral's GPIO Configuration
|
||||
* - DMA configuration for transmission request by peripheral
|
||||
* - NVIC configuration for DMA interrupt request enable
|
||||
* @param hspi: SPI handle pointer
|
||||
* @retval None
|
||||
*/
|
||||
extern "C" void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
|
||||
if(mspInitFunc != NULL) {
|
||||
mspInitFunc(hspi, mspInitArgs);
|
||||
}
|
||||
else {
|
||||
printf("HAL_SPI_MspInit: Please call set_msp_functions to assign SPI MSP functions\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SPI MSP De-Initialization
|
||||
* This function frees the hardware resources used in this example:
|
||||
* - Disable the Peripheral's clock
|
||||
* - Revert GPIO, DMA and NVIC configuration to their default state
|
||||
* @param hspi: SPI handle pointer
|
||||
* @retval None
|
||||
*/
|
||||
extern "C" void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi) {
|
||||
if(mspDeinitFunc != NULL) {
|
||||
mspDeinitFunc(hspi, mspDeinitArgs);
|
||||
}
|
||||
else {
|
||||
printf("HAL_SPI_MspDeInit: Please call set_msp_functions to assign SPI MSP functions\n");
|
||||
}
|
||||
}
|
||||
|
||||
void spi::mspErrorHandler(const char* const function, const char *const message) {
|
||||
printf("%s failure: %s\n", function, message);
|
||||
}
|
114
hal/src/fsfw/hal/stm32h7/spi/mspInit.h
Normal file
114
hal/src/fsfw/hal/stm32h7/spi/mspInit.h
Normal file
@ -0,0 +1,114 @@
|
||||
#ifndef FSFW_HAL_STM32H7_SPI_MSPINIT_H_
|
||||
#define FSFW_HAL_STM32H7_SPI_MSPINIT_H_
|
||||
|
||||
#include "spiDefinitions.h"
|
||||
#include "../dma.h"
|
||||
|
||||
#include "stm32h7xx_hal_spi.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief This file provides MSP implementation for DMA, IRQ and Polling mode for the
|
||||
* SPI peripheral. This configuration is required for the SPI communication to work.
|
||||
*/
|
||||
namespace spi {
|
||||
|
||||
struct MspCfgBase {
|
||||
virtual ~MspCfgBase() = default;
|
||||
|
||||
void (* cleanUpMacroWrapper) (void) = nullptr;
|
||||
void (* setupMacroWrapper) (void) = nullptr;
|
||||
|
||||
GPIO_TypeDef* sckPort = nullptr;
|
||||
uint32_t sckPin = 0;
|
||||
uint8_t sckAlternateFunction = 0;
|
||||
GPIO_TypeDef* mosiPort = nullptr;
|
||||
uint32_t mosiPin = 0;
|
||||
uint8_t mosiAlternateFunction = 0;
|
||||
GPIO_TypeDef* misoPort = nullptr;
|
||||
uint32_t misoPin = 0;
|
||||
uint8_t misoAlternateFunction = 0;
|
||||
};
|
||||
|
||||
struct MspPollingConfigStruct: public MspCfgBase {};
|
||||
|
||||
/* A valid instance of this struct must be passed to the MSP initialization function as a void*
|
||||
argument */
|
||||
struct MspIrqConfigStruct: public MspPollingConfigStruct {
|
||||
SpiBus spiBus = SpiBus::SPI_1;
|
||||
user_handler_t spiIrqHandler = nullptr;
|
||||
user_args_t spiUserArgs = nullptr;
|
||||
IRQn_Type spiIrqNumber = SPI1_IRQn;
|
||||
// Priorities for NVIC
|
||||
// Pre-Empt priority ranging from 0 to 15. If FreeRTOS calls are used, only 5-15 are allowed
|
||||
IrqPriorities preEmptPriority = IrqPriorities::LOWEST;
|
||||
IrqPriorities subpriority = IrqPriorities::LOWEST;
|
||||
};
|
||||
|
||||
/* A valid instance of this struct must be passed to the MSP initialization function as a void*
|
||||
argument */
|
||||
struct MspDmaConfigStruct: public MspIrqConfigStruct {
|
||||
void (* dmaClkEnableWrapper) (void) = nullptr;
|
||||
dma::DMAIndexes txDmaIndex;
|
||||
dma::DMAIndexes rxDmaIndex;
|
||||
dma::DMAStreams txDmaStream;
|
||||
dma::DMAStreams rxDmaStream;
|
||||
IRQn_Type txDmaIrqNumber = DMA1_Stream0_IRQn;
|
||||
IRQn_Type rxDmaIrqNumber = DMA1_Stream1_IRQn;
|
||||
// Priorities for NVIC
|
||||
IrqPriorities txPreEmptPriority = IrqPriorities::LOWEST;
|
||||
IrqPriorities rxPreEmptPriority = IrqPriorities::LOWEST;
|
||||
IrqPriorities txSubpriority = IrqPriorities::LOWEST;
|
||||
IrqPriorities rxSubpriority = IrqPriorities::LOWEST;
|
||||
};
|
||||
|
||||
using msp_func_t = void (*) (SPI_HandleTypeDef* hspi, MspCfgBase* cfg);
|
||||
|
||||
|
||||
void getMspInitFunction(msp_func_t* init_func, MspCfgBase **args);
|
||||
void getMspDeinitFunction(msp_func_t* deinit_func, MspCfgBase **args);
|
||||
|
||||
void halMspInitDma(SPI_HandleTypeDef* hspi, MspCfgBase* cfg);
|
||||
void halMspDeinitDma(SPI_HandleTypeDef* hspi, MspCfgBase* cfg);
|
||||
|
||||
void halMspInitInterrupt(SPI_HandleTypeDef* hspi, MspCfgBase* cfg);
|
||||
void halMspDeinitInterrupt(SPI_HandleTypeDef* hspi, MspCfgBase* cfg);
|
||||
|
||||
void halMspInitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfg);
|
||||
void halMspDeinitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfg);
|
||||
|
||||
/**
|
||||
* Assign MSP init functions. Important for SPI configuration
|
||||
* @param init_func
|
||||
* @param init_args
|
||||
* @param deinit_func
|
||||
* @param deinit_args
|
||||
*/
|
||||
void setSpiDmaMspFunctions(MspDmaConfigStruct* cfg,
|
||||
msp_func_t initFunc = &spi::halMspInitDma,
|
||||
msp_func_t deinitFunc= &spi::halMspDeinitDma
|
||||
);
|
||||
void setSpiIrqMspFunctions(MspIrqConfigStruct* cfg,
|
||||
msp_func_t initFunc = &spi::halMspInitInterrupt,
|
||||
msp_func_t deinitFunc= &spi::halMspDeinitInterrupt
|
||||
);
|
||||
void setSpiPollingMspFunctions(MspPollingConfigStruct* cfg,
|
||||
msp_func_t initFunc = &spi::halMspInitPolling,
|
||||
msp_func_t deinitFunc= &spi::halMspDeinitPolling
|
||||
);
|
||||
|
||||
void mspErrorHandler(const char* const function, const char *const message);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_SPI_MSPINIT_H_ */
|
341
hal/src/fsfw/hal/stm32h7/spi/spiCore.cpp
Normal file
341
hal/src/fsfw/hal/stm32h7/spi/spiCore.cpp
Normal file
@ -0,0 +1,341 @@
|
||||
#include "fsfw/hal/stm32h7/spi/spiCore.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiDefinitions.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
SPI_HandleTypeDef* spiHandle = nullptr;
|
||||
DMA_HandleTypeDef* hdmaTx = nullptr;
|
||||
DMA_HandleTypeDef* hdmaRx = nullptr;
|
||||
|
||||
spi_transfer_cb_t rxTxCb = nullptr;
|
||||
void* rxTxArgs = nullptr;
|
||||
spi_transfer_cb_t txCb = nullptr;
|
||||
void* txArgs = nullptr;
|
||||
spi_transfer_cb_t rxCb = nullptr;
|
||||
void* rxArgs = nullptr;
|
||||
spi_transfer_cb_t errorCb = nullptr;
|
||||
void* errorArgs = nullptr;
|
||||
|
||||
void mapIndexAndStream(DMA_HandleTypeDef* handle, dma::DMAType dmaType, dma::DMAIndexes dmaIdx,
|
||||
dma::DMAStreams dmaStream, IRQn_Type* dmaIrqNumber);
|
||||
void mapSpiBus(DMA_HandleTypeDef *handle, dma::DMAType dmaType, spi::SpiBus spiBus);
|
||||
|
||||
void spi::configureDmaHandle(DMA_HandleTypeDef *handle, spi::SpiBus spiBus, dma::DMAType dmaType,
|
||||
dma::DMAIndexes dmaIdx, dma::DMAStreams dmaStream, IRQn_Type* dmaIrqNumber,
|
||||
uint32_t dmaMode, uint32_t dmaPriority) {
|
||||
using namespace dma;
|
||||
mapIndexAndStream(handle, dmaType, dmaIdx, dmaStream, dmaIrqNumber);
|
||||
mapSpiBus(handle, dmaType, spiBus);
|
||||
|
||||
if(dmaType == DMAType::TX) {
|
||||
handle->Init.Direction = DMA_MEMORY_TO_PERIPH;
|
||||
}
|
||||
else {
|
||||
handle->Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
}
|
||||
|
||||
handle->Init.Priority = dmaPriority;
|
||||
handle->Init.Mode = dmaMode;
|
||||
|
||||
// Standard settings for the rest for now
|
||||
handle->Init.FIFOMode = DMA_FIFOMODE_DISABLE;
|
||||
handle->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||
handle->Init.MemBurst = DMA_MBURST_INC4;
|
||||
handle->Init.PeriphBurst = DMA_PBURST_INC4;
|
||||
handle->Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
handle->Init.MemInc = DMA_MINC_ENABLE;
|
||||
handle->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
|
||||
handle->Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
|
||||
}
|
||||
|
||||
void spi::setDmaHandles(DMA_HandleTypeDef* txHandle, DMA_HandleTypeDef* rxHandle) {
|
||||
hdmaTx = txHandle;
|
||||
hdmaRx = rxHandle;
|
||||
}
|
||||
|
||||
void spi::getDmaHandles(DMA_HandleTypeDef** txHandle, DMA_HandleTypeDef** rxHandle) {
|
||||
*txHandle = hdmaTx;
|
||||
*rxHandle = hdmaRx;
|
||||
}
|
||||
|
||||
void spi::setSpiHandle(SPI_HandleTypeDef *spiHandle_) {
|
||||
if(spiHandle_ == NULL) {
|
||||
return;
|
||||
}
|
||||
spiHandle = spiHandle_;
|
||||
}
|
||||
|
||||
void spi::assignTransferRxTxCompleteCallback(spi_transfer_cb_t callback, void *userArgs) {
|
||||
rxTxCb = callback;
|
||||
rxTxArgs = userArgs;
|
||||
}
|
||||
|
||||
void spi::assignTransferRxCompleteCallback(spi_transfer_cb_t callback, void *userArgs) {
|
||||
rxCb = callback;
|
||||
rxArgs = userArgs;
|
||||
}
|
||||
|
||||
void spi::assignTransferTxCompleteCallback(spi_transfer_cb_t callback, void *userArgs) {
|
||||
txCb = callback;
|
||||
txArgs = userArgs;
|
||||
}
|
||||
|
||||
void spi::assignTransferErrorCallback(spi_transfer_cb_t callback, void *userArgs) {
|
||||
errorCb = callback;
|
||||
errorArgs = userArgs;
|
||||
}
|
||||
|
||||
SPI_HandleTypeDef* spi::getSpiHandle() {
|
||||
return spiHandle;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief TxRx Transfer completed callback.
|
||||
* @param hspi: SPI handle
|
||||
*/
|
||||
extern "C" void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||
if(rxTxCb != NULL) {
|
||||
rxTxCb(hspi, rxTxArgs);
|
||||
}
|
||||
else {
|
||||
printf("HAL_SPI_TxRxCpltCallback: No user callback specified\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TxRx Transfer completed callback.
|
||||
* @param hspi: SPI handle
|
||||
*/
|
||||
extern "C" void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||
if(txCb != NULL) {
|
||||
txCb(hspi, txArgs);
|
||||
}
|
||||
else {
|
||||
printf("HAL_SPI_TxCpltCallback: No user callback specified\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TxRx Transfer completed callback.
|
||||
* @param hspi: SPI handle
|
||||
*/
|
||||
extern "C" void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||
if(rxCb != nullptr) {
|
||||
rxCb(hspi, rxArgs);
|
||||
}
|
||||
else {
|
||||
printf("HAL_SPI_RxCpltCallback: No user callback specified\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SPI error callbacks.
|
||||
* @param hspi: SPI handle
|
||||
* @note This example shows a simple way to report transfer error, and you can
|
||||
* add your own implementation.
|
||||
* @retval None
|
||||
*/
|
||||
extern "C" void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
|
||||
if(errorCb != nullptr) {
|
||||
errorCb(hspi, rxArgs);
|
||||
}
|
||||
else {
|
||||
printf("HAL_SPI_ErrorCallback: No user callback specified\n");
|
||||
}
|
||||
}
|
||||
|
||||
void mapIndexAndStream(DMA_HandleTypeDef* handle, dma::DMAType dmaType, dma::DMAIndexes dmaIdx,
|
||||
dma::DMAStreams dmaStream, IRQn_Type* dmaIrqNumber) {
|
||||
using namespace dma;
|
||||
if(dmaIdx == DMAIndexes::DMA_1) {
|
||||
#ifdef DMA1
|
||||
switch(dmaStream) {
|
||||
case(DMAStreams::STREAM_0): {
|
||||
#ifdef DMA1_Stream0
|
||||
handle->Instance = DMA1_Stream0;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA1_Stream0_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_1): {
|
||||
#ifdef DMA1_Stream1
|
||||
handle->Instance = DMA1_Stream1;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA1_Stream1_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_2): {
|
||||
#ifdef DMA1_Stream2
|
||||
handle->Instance = DMA1_Stream2;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA1_Stream2_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_3): {
|
||||
#ifdef DMA1_Stream3
|
||||
handle->Instance = DMA1_Stream3;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA1_Stream3_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_4): {
|
||||
#ifdef DMA1_Stream4
|
||||
handle->Instance = DMA1_Stream4;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA1_Stream4_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_5): {
|
||||
#ifdef DMA1_Stream5
|
||||
handle->Instance = DMA1_Stream5;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA1_Stream5_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_6): {
|
||||
#ifdef DMA1_Stream6
|
||||
handle->Instance = DMA1_Stream6;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA1_Stream6_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_7): {
|
||||
#ifdef DMA1_Stream7
|
||||
handle->Instance = DMA1_Stream7;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA1_Stream7_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(dmaType == DMAType::TX) {
|
||||
handle->Init.Request = DMA_REQUEST_SPI1_TX;
|
||||
}
|
||||
else {
|
||||
handle->Init.Request = DMA_REQUEST_SPI1_RX;
|
||||
}
|
||||
#endif /* DMA1 */
|
||||
}
|
||||
if(dmaIdx == DMAIndexes::DMA_2) {
|
||||
#ifdef DMA2
|
||||
switch(dmaStream) {
|
||||
case(DMAStreams::STREAM_0): {
|
||||
#ifdef DMA2_Stream0
|
||||
handle->Instance = DMA2_Stream0;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA2_Stream0_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_1): {
|
||||
#ifdef DMA2_Stream1
|
||||
handle->Instance = DMA2_Stream1;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA2_Stream1_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_2): {
|
||||
#ifdef DMA2_Stream2
|
||||
handle->Instance = DMA2_Stream2;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA2_Stream2_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_3): {
|
||||
#ifdef DMA2_Stream3
|
||||
handle->Instance = DMA2_Stream3;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA2_Stream3_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_4): {
|
||||
#ifdef DMA2_Stream4
|
||||
handle->Instance = DMA2_Stream4;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA2_Stream4_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_5): {
|
||||
#ifdef DMA2_Stream5
|
||||
handle->Instance = DMA2_Stream5;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA2_Stream5_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_6): {
|
||||
#ifdef DMA2_Stream6
|
||||
handle->Instance = DMA2_Stream6;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA2_Stream6_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case(DMAStreams::STREAM_7): {
|
||||
#ifdef DMA2_Stream7
|
||||
handle->Instance = DMA2_Stream7;
|
||||
if(dmaIrqNumber != nullptr) {
|
||||
*dmaIrqNumber = DMA2_Stream7_IRQn;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* DMA2 */
|
||||
}
|
||||
}
|
||||
|
||||
void mapSpiBus(DMA_HandleTypeDef *handle, dma::DMAType dmaType, spi::SpiBus spiBus) {
|
||||
if(dmaType == dma::DMAType::TX) {
|
||||
if(spiBus == spi::SpiBus::SPI_1) {
|
||||
#ifdef DMA_REQUEST_SPI1_TX
|
||||
handle->Init.Request = DMA_REQUEST_SPI1_TX;
|
||||
#endif
|
||||
}
|
||||
else if(spiBus == spi::SpiBus::SPI_2) {
|
||||
#ifdef DMA_REQUEST_SPI2_TX
|
||||
handle->Init.Request = DMA_REQUEST_SPI2_TX;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(spiBus == spi::SpiBus::SPI_1) {
|
||||
#ifdef DMA_REQUEST_SPI1_RX
|
||||
handle->Init.Request = DMA_REQUEST_SPI1_RX;
|
||||
#endif
|
||||
}
|
||||
else if(spiBus == spi::SpiBus::SPI_2) {
|
||||
#ifdef DMA_REQUEST_SPI2_RX
|
||||
handle->Init.Request = DMA_REQUEST_SPI2_RX;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
54
hal/src/fsfw/hal/stm32h7/spi/spiCore.h
Normal file
54
hal/src/fsfw/hal/stm32h7/spi/spiCore.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef FSFW_HAL_STM32H7_SPI_SPICORE_H_
|
||||
#define FSFW_HAL_STM32H7_SPI_SPICORE_H_
|
||||
|
||||
#include "fsfw/hal/stm32h7/dma.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiDefinitions.h"
|
||||
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "stm32h7xx_hal_dma.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
using spi_transfer_cb_t = void (*) (SPI_HandleTypeDef *hspi, void* userArgs);
|
||||
|
||||
namespace spi {
|
||||
|
||||
void configureDmaHandle(DMA_HandleTypeDef* handle, spi::SpiBus spiBus,
|
||||
dma::DMAType dmaType, dma::DMAIndexes dmaIdx,
|
||||
dma::DMAStreams dmaStream, IRQn_Type* dmaIrqNumber, uint32_t dmaMode = DMA_NORMAL,
|
||||
uint32_t dmaPriority = DMA_PRIORITY_LOW);
|
||||
|
||||
/**
|
||||
* Assign DMA handles. Required to use DMA for SPI transfers.
|
||||
* @param txHandle
|
||||
* @param rxHandle
|
||||
*/
|
||||
void setDmaHandles(DMA_HandleTypeDef* txHandle, DMA_HandleTypeDef* rxHandle);
|
||||
void getDmaHandles(DMA_HandleTypeDef** txHandle, DMA_HandleTypeDef** rxHandle);
|
||||
|
||||
/**
|
||||
* Assign SPI handle. Needs to be done before using the SPI
|
||||
* @param spiHandle
|
||||
*/
|
||||
void setSpiHandle(SPI_HandleTypeDef *spiHandle);
|
||||
|
||||
void assignTransferRxTxCompleteCallback(spi_transfer_cb_t callback, void* userArgs);
|
||||
void assignTransferRxCompleteCallback(spi_transfer_cb_t callback, void* userArgs);
|
||||
void assignTransferTxCompleteCallback(spi_transfer_cb_t callback, void* userArgs);
|
||||
void assignTransferErrorCallback(spi_transfer_cb_t callback, void* userArgs);
|
||||
|
||||
/**
|
||||
* Get the assigned SPI handle.
|
||||
* @return
|
||||
*/
|
||||
SPI_HandleTypeDef* getSpiHandle();
|
||||
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_SPI_SPICORE_H_ */
|
52
hal/src/fsfw/hal/stm32h7/spi/spiDefinitions.cpp
Normal file
52
hal/src/fsfw/hal/stm32h7/spi/spiDefinitions.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "fsfw/hal/stm32h7/spi/spiDefinitions.h"
|
||||
|
||||
void spi::assignSpiMode(SpiModes spiMode, SPI_HandleTypeDef& spiHandle) {
|
||||
switch(spiMode) {
|
||||
case(SpiModes::MODE_0): {
|
||||
spiHandle.Init.CLKPolarity = SPI_POLARITY_LOW;
|
||||
spiHandle.Init.CLKPhase = SPI_PHASE_1EDGE;
|
||||
break;
|
||||
}
|
||||
case(SpiModes::MODE_1): {
|
||||
spiHandle.Init.CLKPolarity = SPI_POLARITY_LOW;
|
||||
spiHandle.Init.CLKPhase = SPI_PHASE_2EDGE;
|
||||
break;
|
||||
}
|
||||
case(SpiModes::MODE_2): {
|
||||
spiHandle.Init.CLKPolarity = SPI_POLARITY_HIGH;
|
||||
spiHandle.Init.CLKPhase = SPI_PHASE_1EDGE;
|
||||
break;
|
||||
}
|
||||
case(SpiModes::MODE_3): {
|
||||
spiHandle.Init.CLKPolarity = SPI_POLARITY_HIGH;
|
||||
spiHandle.Init.CLKPhase = SPI_PHASE_2EDGE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t spi::getPrescaler(uint32_t clock_src_freq, uint32_t baudrate_mbps) {
|
||||
uint32_t divisor = 0;
|
||||
uint32_t spi_clk = clock_src_freq;
|
||||
uint32_t presc = 0;
|
||||
static const uint32_t baudrate[] = {
|
||||
SPI_BAUDRATEPRESCALER_2,
|
||||
SPI_BAUDRATEPRESCALER_4,
|
||||
SPI_BAUDRATEPRESCALER_8,
|
||||
SPI_BAUDRATEPRESCALER_16,
|
||||
SPI_BAUDRATEPRESCALER_32,
|
||||
SPI_BAUDRATEPRESCALER_64,
|
||||
SPI_BAUDRATEPRESCALER_128,
|
||||
SPI_BAUDRATEPRESCALER_256,
|
||||
};
|
||||
|
||||
while( spi_clk > baudrate_mbps) {
|
||||
presc = baudrate[divisor];
|
||||
if (++divisor > 7)
|
||||
break;
|
||||
|
||||
spi_clk = ( spi_clk >> 1);
|
||||
}
|
||||
|
||||
return presc;
|
||||
}
|
50
hal/src/fsfw/hal/stm32h7/spi/spiDefinitions.h
Normal file
50
hal/src/fsfw/hal/stm32h7/spi/spiDefinitions.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef FSFW_HAL_STM32H7_SPI_SPIDEFINITIONS_H_
|
||||
#define FSFW_HAL_STM32H7_SPI_SPIDEFINITIONS_H_
|
||||
|
||||
#include "../../common/spi/spiCommon.h"
|
||||
|
||||
#include "fsfw/returnvalues/FwClassIds.h"
|
||||
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
|
||||
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "stm32h7xx_hal_spi.h"
|
||||
|
||||
namespace spi {
|
||||
|
||||
static constexpr uint8_t HAL_SPI_ID = CLASS_ID::HAL_SPI;
|
||||
static constexpr ReturnValue_t HAL_TIMEOUT_RETVAL = HasReturnvaluesIF::makeReturnCode(HAL_SPI_ID, 0);
|
||||
static constexpr ReturnValue_t HAL_BUSY_RETVAL = HasReturnvaluesIF::makeReturnCode(HAL_SPI_ID, 1);
|
||||
static constexpr ReturnValue_t HAL_ERROR_RETVAL = HasReturnvaluesIF::makeReturnCode(HAL_SPI_ID, 2);
|
||||
|
||||
enum class TransferStates {
|
||||
IDLE,
|
||||
WAIT,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
};
|
||||
|
||||
enum SpiBus {
|
||||
SPI_1,
|
||||
SPI_2
|
||||
};
|
||||
|
||||
enum TransferModes {
|
||||
POLLING,
|
||||
INTERRUPT,
|
||||
DMA
|
||||
};
|
||||
|
||||
void assignSpiMode(SpiModes spiMode, SPI_HandleTypeDef& spiHandle);
|
||||
|
||||
/**
|
||||
* @brief Set SPI frequency to calculate correspondent baud-rate prescaler.
|
||||
* @param clock_src_freq Frequency of clock source
|
||||
* @param baudrate_mbps Baudrate to set to set
|
||||
* @retval Baudrate prescaler
|
||||
*/
|
||||
uint32_t getPrescaler(uint32_t clock_src_freq, uint32_t baudrate_mbps);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_SPI_SPIDEFINITIONS_H_ */
|
106
hal/src/fsfw/hal/stm32h7/spi/spiInterrupts.cpp
Normal file
106
hal/src/fsfw/hal/stm32h7/spi/spiInterrupts.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "fsfw/hal/stm32h7/spi/spiInterrupts.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiCore.h"
|
||||
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "stm32h7xx_hal_dma.h"
|
||||
#include "stm32h7xx_hal_spi.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
user_handler_t spi1UserHandler = &spi::spiIrqHandler;
|
||||
user_args_t spi1UserArgs = nullptr;
|
||||
|
||||
user_handler_t spi2UserHandler = &spi::spiIrqHandler;
|
||||
user_args_t spi2UserArgs = nullptr;
|
||||
|
||||
/**
|
||||
* @brief This function handles DMA Rx interrupt request.
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
void spi::dmaRxIrqHandler(void* dmaHandle) {
|
||||
if(dmaHandle == nullptr) {
|
||||
return;
|
||||
}
|
||||
HAL_DMA_IRQHandler((DMA_HandleTypeDef *) dmaHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles DMA Rx interrupt request.
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
void spi::dmaTxIrqHandler(void* dmaHandle) {
|
||||
if(dmaHandle == nullptr) {
|
||||
return;
|
||||
}
|
||||
HAL_DMA_IRQHandler((DMA_HandleTypeDef *) dmaHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles SPIx interrupt request.
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
void spi::spiIrqHandler(void* spiHandle) {
|
||||
if(spiHandle == nullptr) {
|
||||
return;
|
||||
}
|
||||
//auto currentSpiHandle = spi::getSpiHandle();
|
||||
HAL_SPI_IRQHandler((SPI_HandleTypeDef *) spiHandle);
|
||||
}
|
||||
|
||||
void spi::assignSpiUserHandler(spi::SpiBus spiIdx, user_handler_t userHandler,
|
||||
user_args_t userArgs) {
|
||||
if(spiIdx == spi::SpiBus::SPI_1) {
|
||||
spi1UserHandler = userHandler;
|
||||
spi1UserArgs = userArgs;
|
||||
}
|
||||
else {
|
||||
spi2UserHandler = userHandler;
|
||||
spi2UserArgs = userArgs;
|
||||
}
|
||||
}
|
||||
|
||||
void spi::getSpiUserHandler(spi::SpiBus spiBus, user_handler_t *userHandler,
|
||||
user_args_t *userArgs) {
|
||||
if(userHandler == nullptr or userArgs == nullptr) {
|
||||
return;
|
||||
}
|
||||
if(spiBus == spi::SpiBus::SPI_1) {
|
||||
*userArgs = spi1UserArgs;
|
||||
*userHandler = spi1UserHandler;
|
||||
}
|
||||
else {
|
||||
*userArgs = spi2UserArgs;
|
||||
*userHandler = spi2UserHandler;
|
||||
}
|
||||
}
|
||||
|
||||
void spi::assignSpiUserArgs(spi::SpiBus spiBus, user_args_t userArgs) {
|
||||
if(spiBus == spi::SpiBus::SPI_1) {
|
||||
spi1UserArgs = userArgs;
|
||||
}
|
||||
else {
|
||||
spi2UserArgs = userArgs;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do not change these function names! They need to be exactly equal to the name of the functions
|
||||
defined in the startup_stm32h743xx.s files! */
|
||||
|
||||
extern "C" void SPI1_IRQHandler() {
|
||||
if(spi1UserHandler != NULL) {
|
||||
spi1UserHandler(spi1UserArgs);
|
||||
return;
|
||||
}
|
||||
Default_Handler();
|
||||
}
|
||||
|
||||
extern "C" void SPI2_IRQHandler() {
|
||||
if(spi2UserHandler != nullptr) {
|
||||
spi2UserHandler(spi2UserArgs);
|
||||
return;
|
||||
}
|
||||
Default_Handler();
|
||||
}
|
41
hal/src/fsfw/hal/stm32h7/spi/spiInterrupts.h
Normal file
41
hal/src/fsfw/hal/stm32h7/spi/spiInterrupts.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef FSFW_HAL_STM32H7_SPI_INTERRUPTS_H_
|
||||
#define FSFW_HAL_STM32H7_SPI_INTERRUPTS_H_
|
||||
|
||||
#include "../interrupts.h"
|
||||
#include "spiDefinitions.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace spi {
|
||||
|
||||
void assignSpiUserArgs(spi::SpiBus spiBus, user_args_t userArgs);
|
||||
|
||||
/**
|
||||
* Assign a user interrupt handler for SPI bus 1, allowing to pass an arbitrary argument as well.
|
||||
* Generally, this argument will be the related SPI handle.
|
||||
* @param user_handler
|
||||
* @param user_args
|
||||
*/
|
||||
void assignSpiUserHandler(spi::SpiBus spiBus, user_handler_t user_handler,
|
||||
user_args_t user_args);
|
||||
void getSpiUserHandler(spi::SpiBus spiBus, user_handler_t* user_handler,
|
||||
user_args_t* user_args);
|
||||
|
||||
/**
|
||||
* Generic interrupt handlers supplied for convenience. Do not call these directly! Set them
|
||||
* instead with assign_dma_user_handler and assign_spi_user_handler functions.
|
||||
* @param dma_handle
|
||||
*/
|
||||
void dmaRxIrqHandler(void* dma_handle);
|
||||
void dmaTxIrqHandler(void* dma_handle);
|
||||
void spiIrqHandler(void* spi_handle);
|
||||
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_SPI_INTERRUPTS_H_ */
|
82
hal/src/fsfw/hal/stm32h7/spi/stm32h743ziSpi.cpp
Normal file
82
hal/src/fsfw/hal/stm32h7/spi/stm32h743ziSpi.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include "fsfw/hal/stm32h7/spi/stm32h743ziSpi.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiCore.h"
|
||||
#include "fsfw/hal/stm32h7/spi/spiInterrupts.h"
|
||||
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "stm32h7xx_hal_rcc.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
void spiSetupWrapper() {
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
__HAL_RCC_SPI1_CLK_ENABLE();
|
||||
}
|
||||
|
||||
void spiCleanUpWrapper() {
|
||||
__HAL_RCC_SPI1_FORCE_RESET();
|
||||
__HAL_RCC_SPI1_RELEASE_RESET();
|
||||
}
|
||||
|
||||
void spiDmaClockEnableWrapper() {
|
||||
__HAL_RCC_DMA2_CLK_ENABLE();
|
||||
}
|
||||
|
||||
void spi::h743zi::standardPollingCfg(MspPollingConfigStruct& cfg) {
|
||||
cfg.setupMacroWrapper = &spiSetupWrapper;
|
||||
cfg.cleanUpMacroWrapper = &spiCleanUpWrapper;
|
||||
cfg.sckPort = GPIOA;
|
||||
cfg.sckPin = GPIO_PIN_5;
|
||||
cfg.misoPort = GPIOA;
|
||||
cfg.misoPin = GPIO_PIN_6;
|
||||
cfg.mosiPort = GPIOA;
|
||||
cfg.mosiPin = GPIO_PIN_7;
|
||||
cfg.sckAlternateFunction = GPIO_AF5_SPI1;
|
||||
cfg.mosiAlternateFunction = GPIO_AF5_SPI1;
|
||||
cfg.misoAlternateFunction = GPIO_AF5_SPI1;
|
||||
}
|
||||
|
||||
void spi::h743zi::standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio,
|
||||
IrqPriorities spiSubprio) {
|
||||
// High, but works on FreeRTOS as well (priorities range from 0 to 15)
|
||||
cfg.preEmptPriority = spiIrqPrio;
|
||||
cfg.subpriority = spiSubprio;
|
||||
cfg.spiIrqNumber = SPI1_IRQn;
|
||||
cfg.spiBus = SpiBus::SPI_1;
|
||||
user_handler_t spiUserHandler = nullptr;
|
||||
user_args_t spiUserArgs = nullptr;
|
||||
getSpiUserHandler(spi::SpiBus::SPI_1, &spiUserHandler, &spiUserArgs);
|
||||
if(spiUserHandler == nullptr) {
|
||||
printf("spi::h743zi::standardInterruptCfg: Invalid SPI user handlers\n");
|
||||
return;
|
||||
}
|
||||
cfg.spiUserArgs = spiUserArgs;
|
||||
cfg.spiIrqHandler = spiUserHandler;
|
||||
standardPollingCfg(cfg);
|
||||
}
|
||||
|
||||
void spi::h743zi::standardDmaCfg(MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio,
|
||||
IrqPriorities txIrqPrio, IrqPriorities rxIrqPrio, IrqPriorities spiSubprio,
|
||||
IrqPriorities txSubprio, IrqPriorities rxSubprio) {
|
||||
cfg.dmaClkEnableWrapper = &spiDmaClockEnableWrapper;
|
||||
cfg.rxDmaIndex = dma::DMAIndexes::DMA_2;
|
||||
cfg.txDmaIndex = dma::DMAIndexes::DMA_2;
|
||||
cfg.txDmaStream = dma::DMAStreams::STREAM_3;
|
||||
cfg.rxDmaStream = dma::DMAStreams::STREAM_2;
|
||||
DMA_HandleTypeDef* txHandle;
|
||||
DMA_HandleTypeDef* rxHandle;
|
||||
spi::getDmaHandles(&txHandle, &rxHandle);
|
||||
if(txHandle == nullptr or rxHandle == nullptr) {
|
||||
printf("spi::h743zi::standardDmaCfg: Invalid DMA handles\n");
|
||||
return;
|
||||
}
|
||||
spi::configureDmaHandle(txHandle, spi::SpiBus::SPI_1, dma::DMAType::TX, cfg.txDmaIndex,
|
||||
cfg.txDmaStream, &cfg.txDmaIrqNumber);
|
||||
spi::configureDmaHandle(rxHandle, spi::SpiBus::SPI_1, dma::DMAType::RX, cfg.rxDmaIndex,
|
||||
cfg.rxDmaStream, &cfg.rxDmaIrqNumber, DMA_NORMAL, DMA_PRIORITY_HIGH);
|
||||
cfg.txPreEmptPriority = txIrqPrio;
|
||||
cfg.rxPreEmptPriority = txSubprio;
|
||||
cfg.txSubpriority = rxIrqPrio;
|
||||
cfg.rxSubpriority = rxSubprio;
|
||||
standardInterruptCfg(cfg, spiIrqPrio, spiSubprio);
|
||||
}
|
23
hal/src/fsfw/hal/stm32h7/spi/stm32h743ziSpi.h
Normal file
23
hal/src/fsfw/hal/stm32h7/spi/stm32h743ziSpi.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef FSFW_HAL_STM32H7_SPI_STM32H743ZISPI_H_
|
||||
#define FSFW_HAL_STM32H7_SPI_STM32H743ZISPI_H_
|
||||
|
||||
#include "mspInit.h"
|
||||
|
||||
namespace spi {
|
||||
|
||||
namespace h743zi {
|
||||
|
||||
void standardPollingCfg(MspPollingConfigStruct& cfg);
|
||||
void standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio,
|
||||
IrqPriorities spiSubprio = HIGHEST);
|
||||
void standardDmaCfg(MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio,
|
||||
IrqPriorities txIrqPrio, IrqPriorities rxIrqPrio,
|
||||
IrqPriorities spiSubprio = HIGHEST, IrqPriorities txSubPrio = HIGHEST,
|
||||
IrqPriorities rxSubprio = HIGHEST);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* FSFW_HAL_STM32H7_SPI_STM32H743ZISPI_H_ */
|
2
hal/src/fsfw/hal/stm32h7/uart/CMakeLists.txt
Normal file
2
hal/src/fsfw/hal/stm32h7/uart/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||
)
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@ -1,39 +0,0 @@
|
||||
#ifndef INTERNALERRORCODES_H_
|
||||
#define INTERNALERRORCODES_H_
|
||||
|
||||
#include "../returnvalues/HasReturnvaluesIF.h"
|
||||
|
||||
class InternalErrorCodes {
|
||||
public:
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::INTERNAL_ERROR_CODES;
|
||||
|
||||
static const ReturnValue_t NO_CONFIGURATION_TABLE = MAKE_RETURN_CODE(0x01 );
|
||||
static const ReturnValue_t NO_CPU_TABLE = MAKE_RETURN_CODE(0x02 );
|
||||
static const ReturnValue_t INVALID_WORKSPACE_ADDRESS = MAKE_RETURN_CODE(0x03 );
|
||||
static const ReturnValue_t TOO_LITTLE_WORKSPACE = MAKE_RETURN_CODE(0x04 );
|
||||
static const ReturnValue_t WORKSPACE_ALLOCATION = MAKE_RETURN_CODE(0x05 );
|
||||
static const ReturnValue_t INTERRUPT_STACK_TOO_SMALL = MAKE_RETURN_CODE(0x06 );
|
||||
static const ReturnValue_t THREAD_EXITTED = MAKE_RETURN_CODE(0x07 );
|
||||
static const ReturnValue_t INCONSISTENT_MP_INFORMATION = MAKE_RETURN_CODE(0x08 );
|
||||
static const ReturnValue_t INVALID_NODE = MAKE_RETURN_CODE(0x09 );
|
||||
static const ReturnValue_t NO_MPCI = MAKE_RETURN_CODE(0x0a );
|
||||
static const ReturnValue_t BAD_PACKET = MAKE_RETURN_CODE(0x0b );
|
||||
static const ReturnValue_t OUT_OF_PACKETS = MAKE_RETURN_CODE(0x0c );
|
||||
static const ReturnValue_t OUT_OF_GLOBAL_OBJECTS = MAKE_RETURN_CODE(0x0d );
|
||||
static const ReturnValue_t OUT_OF_PROXIES = MAKE_RETURN_CODE(0x0e );
|
||||
static const ReturnValue_t INVALID_GLOBAL_ID = MAKE_RETURN_CODE(0x0f );
|
||||
static const ReturnValue_t BAD_STACK_HOOK = MAKE_RETURN_CODE(0x10 );
|
||||
static const ReturnValue_t BAD_ATTRIBUTES = MAKE_RETURN_CODE(0x11 );
|
||||
static const ReturnValue_t IMPLEMENTATION_KEY_CREATE_INCONSISTENCY = MAKE_RETURN_CODE(0x12 );
|
||||
static const ReturnValue_t IMPLEMENTATION_BLOCKING_OPERATION_CANCEL = MAKE_RETURN_CODE(0x13 );
|
||||
static const ReturnValue_t MUTEX_OBTAIN_FROM_BAD_STATE = MAKE_RETURN_CODE(0x14 );
|
||||
static const ReturnValue_t UNLIMITED_AND_MAXIMUM_IS_0 = MAKE_RETURN_CODE(0x15 );
|
||||
|
||||
virtual ~InternalErrorCodes();
|
||||
|
||||
static ReturnValue_t translate(uint8_t code);
|
||||
private:
|
||||
InternalErrorCodes();
|
||||
};
|
||||
|
||||
#endif /* INTERNALERRORCODES_H_ */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user