Merge pull request 'STM32H7 SpiComIF and first device handler' (#9) from mueller/stm32h7-spi-com-if-l3gd20h-handler into master
Reviewed-on: #9
This commit is contained in:
commit
f059aeb02d
@ -20,6 +20,7 @@ if(NOT LIB_FSFW_NAME)
|
|||||||
message(ERROR "LIB_FSFW_NAME needs to be set as a linkable target")
|
message(ERROR "LIB_FSFW_NAME needs to be set as a linkable target")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(devicehandlers)
|
||||||
add_subdirectory(common)
|
add_subdirectory(common)
|
||||||
|
|
||||||
if(FSFW_HAL_ADD_LINUX)
|
if(FSFW_HAL_ADD_LINUX)
|
||||||
@ -34,8 +35,32 @@ target_link_libraries(${LIB_FSFW_HAL_NAME} PRIVATE
|
|||||||
${LIB_FSFW_NAME}
|
${LIB_FSFW_NAME}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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_HAL_NAME} PRIVATE
|
target_include_directories(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${FSFW_HAL_ADD_INC_PATHS_ABS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
|
${FSFW_HAL_DEFINES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
|
${FSFW_HAL_LINK_LIBS}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "GpioCookie.h"
|
#include "GpioCookie.h"
|
||||||
#include <fsfw/serviceinterface/ServiceInterface.h>
|
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||||
|
|
||||||
GpioCookie::GpioCookie() {
|
GpioCookie::GpioCookie() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#ifndef LINUX_GPIO_GPIOCOOKIE_H_
|
#ifndef COMMON_GPIO_GPIOCOOKIE_H_
|
||||||
#define LINUX_GPIO_GPIOCOOKIE_H_
|
#define COMMON_GPIO_GPIOCOOKIE_H_
|
||||||
|
|
||||||
#include "GpioIF.h"
|
#include "GpioIF.h"
|
||||||
#include "gpioDefinitions.h"
|
#include "gpioDefinitions.h"
|
||||||
|
|
||||||
#include <fsfw/devicehandlers/CookieIF.h>
|
#include <fsfw/devicehandlers/CookieIF.h>
|
||||||
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||||
|
|
||||||
@ -37,4 +38,4 @@ private:
|
|||||||
GpioMap gpioMap;
|
GpioMap gpioMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LINUX_GPIO_GPIOCOOKIE_H_ */
|
#endif /* COMMON_GPIO_GPIOCOOKIE_H_ */
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#ifndef LINUX_GPIO_GPIOIF_H_
|
#ifndef COMMON_GPIO_GPIOIF_H_
|
||||||
#define LINUX_GPIO_GPIOIF_H_
|
#define COMMON_GPIO_GPIOIF_H_
|
||||||
|
|
||||||
#include "gpioDefinitions.h"
|
#include "gpioDefinitions.h"
|
||||||
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||||
@ -51,4 +51,4 @@ public:
|
|||||||
virtual ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) = 0;
|
virtual ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LINUX_GPIO_GPIOIF_H_ */
|
#endif /* COMMON_GPIO_GPIOIF_H_ */
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#ifndef LINUX_GPIO_GPIODEFINITIONS_H_
|
#ifndef COMMON_GPIO_GPIODEFINITIONS_H_
|
||||||
#define LINUX_GPIO_GPIODEFINITIONS_H_
|
#define COMMON_GPIO_GPIODEFINITIONS_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
using gpioId_t = uint16_t;
|
using gpioId_t = uint16_t;
|
||||||
|
|
||||||
@ -25,11 +26,14 @@ enum GpioOperation {
|
|||||||
|
|
||||||
enum GpioTypes {
|
enum GpioTypes {
|
||||||
NONE,
|
NONE,
|
||||||
GPIOD_REGULAR,
|
GPIO_REGULAR,
|
||||||
CALLBACK
|
CALLBACK
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr gpioId_t NO_GPIO = -1;
|
static constexpr gpioId_t NO_GPIO = -1;
|
||||||
|
|
||||||
|
using gpio_cb_t = void (*) (gpioId_t gpioId, gpio::GpioOperation gpioOp, int value, void* args);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,12 +70,12 @@ public:
|
|||||||
|
|
||||||
class GpiodRegular: public GpioBase {
|
class GpiodRegular: public GpioBase {
|
||||||
public:
|
public:
|
||||||
GpiodRegular(): GpioBase(gpio::GpioTypes::GPIOD_REGULAR, std::string(),
|
GpiodRegular(): GpioBase(gpio::GpioTypes::GPIO_REGULAR, std::string(),
|
||||||
gpio::Direction::IN, 0) {};
|
gpio::Direction::IN, 0) {};
|
||||||
|
|
||||||
GpiodRegular(std::string chipname_, int lineNum_, std::string consumer_,
|
GpiodRegular(std::string chipname_, int lineNum_, std::string consumer_,
|
||||||
gpio::Direction direction_, int initValue_):
|
gpio::Direction direction_, int initValue_):
|
||||||
GpioBase(gpio::GpioTypes::GPIOD_REGULAR, consumer_, direction_, initValue_),
|
GpioBase(gpio::GpioTypes::GPIO_REGULAR, consumer_, direction_, initValue_),
|
||||||
chipname(chipname_), lineNum(lineNum_) {}
|
chipname(chipname_), lineNum(lineNum_) {}
|
||||||
std::string chipname;
|
std::string chipname;
|
||||||
int lineNum = 0;
|
int lineNum = 0;
|
||||||
@ -81,18 +85,18 @@ public:
|
|||||||
class GpioCallback: public GpioBase {
|
class GpioCallback: public GpioBase {
|
||||||
public:
|
public:
|
||||||
GpioCallback(std::string consumer, gpio::Direction direction_, int initValue_,
|
GpioCallback(std::string consumer, gpio::Direction direction_, int initValue_,
|
||||||
void (* callback) (gpioId_t gpioId, gpio::GpioOperation gpioOp, int value, void* args),
|
gpio::gpio_cb_t callback, void* callbackArgs):
|
||||||
void* callbackArgs):
|
|
||||||
GpioBase(gpio::GpioTypes::CALLBACK, consumer, direction_, initValue_),
|
GpioBase(gpio::GpioTypes::CALLBACK, consumer, direction_, initValue_),
|
||||||
callback(callback), callbackArgs(callbackArgs) {}
|
callback(callback), callbackArgs(callbackArgs) {}
|
||||||
|
|
||||||
void (* callback) (gpioId_t gpioId, gpio::GpioOperation gpioOp,
|
gpio::gpio_cb_t callback = nullptr;
|
||||||
int value, void* args) = nullptr;
|
|
||||||
void* callbackArgs = nullptr;
|
void* callbackArgs = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
using GpioMap = std::unordered_map<gpioId_t, GpioBase*>;
|
using GpioMap = std::map<gpioId_t, GpioBase*>;
|
||||||
|
using GpioUnorderedMap = std::unordered_map<gpioId_t, GpioBase*>;
|
||||||
using GpioMapIter = GpioMap::iterator;
|
using GpioMapIter = GpioMap::iterator;
|
||||||
|
using GpioUnorderedMapIter = GpioUnorderedMap::iterator;
|
||||||
|
|
||||||
#endif /* LINUX_GPIO_GPIODEFINITIONS_H_ */
|
#endif /* LINUX_GPIO_GPIODEFINITIONS_H_ */
|
||||||
|
17
common/spi/spiCommon.h
Normal file
17
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
devicehandlers/CMakeLists.txt
Normal file
3
devicehandlers/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
|
GyroL3GD20Handler.cpp
|
||||||
|
)
|
262
devicehandlers/GyroL3GD20Handler.cpp
Normal file
262
devicehandlers/GyroL3GD20Handler.cpp
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
#include "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
devicehandlers/GyroL3GD20Handler.h
Normal file
86
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_ */
|
143
devicehandlers/devicedefinitions/GyroL3GD20Definitions.h
Normal file
143
devicehandlers/devicedefinitions/GyroL3GD20Definitions.h
Normal file
@ -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_ */
|
@ -49,7 +49,7 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
|
|||||||
case(gpio::GpioTypes::NONE): {
|
case(gpio::GpioTypes::NONE): {
|
||||||
return GPIO_INVALID_INSTANCE;
|
return GPIO_INVALID_INSTANCE;
|
||||||
}
|
}
|
||||||
case(gpio::GpioTypes::GPIOD_REGULAR): {
|
case(gpio::GpioTypes::GPIO_REGULAR): {
|
||||||
GpiodRegular* regularGpio = dynamic_cast<GpiodRegular*>(gpioConfig.second);
|
GpiodRegular* regularGpio = dynamic_cast<GpiodRegular*>(gpioConfig.second);
|
||||||
if(regularGpio == nullptr) {
|
if(regularGpio == nullptr) {
|
||||||
return GPIO_INVALID_INSTANCE;
|
return GPIO_INVALID_INSTANCE;
|
||||||
@ -145,7 +145,7 @@ ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) {
|
|||||||
return UNKNOWN_GPIO_ID;
|
return UNKNOWN_GPIO_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIOD_REGULAR) {
|
if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIO_REGULAR) {
|
||||||
return driveGpio(gpioId, dynamic_cast<GpiodRegular*>(gpioMapIter->second), 1);
|
return driveGpio(gpioId, dynamic_cast<GpiodRegular*>(gpioMapIter->second), 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -166,7 +166,7 @@ ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId) {
|
|||||||
return UNKNOWN_GPIO_ID;
|
return UNKNOWN_GPIO_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIOD_REGULAR) {
|
if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIO_REGULAR) {
|
||||||
return driveGpio(gpioId, dynamic_cast<GpiodRegular*>(gpioMapIter->second), 0);
|
return driveGpio(gpioId, dynamic_cast<GpiodRegular*>(gpioMapIter->second), 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -203,7 +203,7 @@ ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) {
|
|||||||
return UNKNOWN_GPIO_ID;
|
return UNKNOWN_GPIO_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIOD_REGULAR) {
|
if(gpioMapIter->second->gpioType == gpio::GpioTypes::GPIO_REGULAR) {
|
||||||
GpiodRegular* regularGpio = dynamic_cast<GpiodRegular*>(gpioMapIter->second);
|
GpiodRegular* regularGpio = dynamic_cast<GpiodRegular*>(gpioMapIter->second);
|
||||||
if(regularGpio == nullptr) {
|
if(regularGpio == nullptr) {
|
||||||
return GPIO_TYPE_FAILURE;
|
return GPIO_TYPE_FAILURE;
|
||||||
@ -223,7 +223,7 @@ ReturnValue_t LinuxLibgpioIF::checkForConflicts(GpioMap& mapToAdd){
|
|||||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||||
for(auto& gpioConfig: mapToAdd) {
|
for(auto& gpioConfig: mapToAdd) {
|
||||||
switch(gpioConfig.second->gpioType) {
|
switch(gpioConfig.second->gpioType) {
|
||||||
case(gpio::GpioTypes::GPIOD_REGULAR): {
|
case(gpio::GpioTypes::GPIO_REGULAR): {
|
||||||
auto regularGpio = dynamic_cast<GpiodRegular*>(gpioConfig.second);
|
auto regularGpio = dynamic_cast<GpiodRegular*>(gpioConfig.second);
|
||||||
if(regularGpio == nullptr) {
|
if(regularGpio == nullptr) {
|
||||||
return GPIO_TYPE_FAILURE;
|
return GPIO_TYPE_FAILURE;
|
||||||
@ -261,7 +261,7 @@ ReturnValue_t LinuxLibgpioIF::checkForConflictsRegularGpio(gpioId_t gpioIdToChec
|
|||||||
/* Cross check with private map */
|
/* Cross check with private map */
|
||||||
gpioMapIter = gpioMap.find(gpioIdToCheck);
|
gpioMapIter = gpioMap.find(gpioIdToCheck);
|
||||||
if(gpioMapIter != gpioMap.end()) {
|
if(gpioMapIter != gpioMap.end()) {
|
||||||
if(gpioMapIter->second->gpioType != gpio::GpioTypes::GPIOD_REGULAR) {
|
if(gpioMapIter->second->gpioType != gpio::GpioTypes::GPIO_REGULAR) {
|
||||||
sif::warning << "LinuxLibgpioIF::checkForConflicts: ID already exists for different "
|
sif::warning << "LinuxLibgpioIF::checkForConflicts: ID already exists for different "
|
||||||
"GPIO type" << gpioIdToCheck << ". Removing duplicate." << std::endl;
|
"GPIO type" << gpioIdToCheck << ". Removing duplicate." << std::endl;
|
||||||
mapToAdd.erase(gpioIdToCheck);
|
mapToAdd.erase(gpioIdToCheck);
|
||||||
|
@ -38,8 +38,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/* Holds the information and configuration of all used GPIOs */
|
/* Holds the information and configuration of all used GPIOs */
|
||||||
GpioMap gpioMap;
|
GpioUnorderedMap gpioMap;
|
||||||
GpioMapIter gpioMapIter;
|
GpioUnorderedMapIter gpioMapIter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This functions drives line of a GPIO specified by the GPIO ID.
|
* @brief This functions drives line of a GPIO specified by the GPIO ID.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
target_sources(${TARGET_NAME} PUBLIC
|
target_sources(${LIB_FSFW_HAL_NAME} PUBLIC
|
||||||
I2cComIF.cpp
|
I2cComIF.cpp
|
||||||
I2cCookie.cpp
|
I2cCookie.cpp
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
target_sources(${TARGET_NAME} PUBLIC
|
target_sources(${LIB_FSFW_HAL_NAME} PUBLIC
|
||||||
SpiComIF.cpp
|
SpiComIF.cpp
|
||||||
SpiCookie.cpp
|
SpiCookie.cpp
|
||||||
)
|
)
|
||||||
|
@ -47,7 +47,7 @@ ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) {
|
|||||||
auto iter = spiDeviceMap.find(spiAddress);
|
auto iter = spiDeviceMap.find(spiAddress);
|
||||||
if(iter == spiDeviceMap.end()) {
|
if(iter == spiDeviceMap.end()) {
|
||||||
size_t bufferSize = spiCookie->getMaxBufferSize();
|
size_t bufferSize = spiCookie->getMaxBufferSize();
|
||||||
SpiInstance spiInstance = {std::vector<uint8_t>(bufferSize)};
|
SpiInstance spiInstance(bufferSize);
|
||||||
auto statusPair = spiDeviceMap.emplace(spiAddress, spiInstance);
|
auto statusPair = spiDeviceMap.emplace(spiAddress, spiInstance);
|
||||||
if (not statusPair.second) {
|
if (not statusPair.second) {
|
||||||
#if FSFW_VERBOSE_LEVEL >= 1
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
@ -67,6 +67,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
struct SpiInstance {
|
struct SpiInstance {
|
||||||
|
SpiInstance(size_t maxRecvSize): replyBuffer(std::vector<uint8_t>(maxRecvSize)) {}
|
||||||
std::vector<uint8_t> replyBuffer;
|
std::vector<uint8_t> replyBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define LINUX_SPI_SPIDEFINITONS_H_
|
#define LINUX_SPI_SPIDEFINITONS_H_
|
||||||
|
|
||||||
#include "../../common/gpio/gpioDefinitions.h"
|
#include "../../common/gpio/gpioDefinitions.h"
|
||||||
|
#include "../../common/spi/spiCommon.h"
|
||||||
|
|
||||||
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
|
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
|
||||||
#include <linux/spi/spidev.h>
|
#include <linux/spi/spidev.h>
|
||||||
@ -13,13 +14,6 @@ class SpiComIF;
|
|||||||
|
|
||||||
namespace spi {
|
namespace spi {
|
||||||
|
|
||||||
enum SpiModes: uint8_t {
|
|
||||||
MODE_0,
|
|
||||||
MODE_1,
|
|
||||||
MODE_2,
|
|
||||||
MODE_3
|
|
||||||
};
|
|
||||||
|
|
||||||
enum SpiComIfModes {
|
enum SpiComIfModes {
|
||||||
REGULAR,
|
REGULAR,
|
||||||
CALLBACK
|
CALLBACK
|
||||||
|
@ -38,15 +38,15 @@ ReturnValue_t UartComIF::initializeInterface(CookieIF * cookie) {
|
|||||||
}
|
}
|
||||||
size_t maxReplyLen = uartCookie->getMaxReplyLen();
|
size_t maxReplyLen = uartCookie->getMaxReplyLen();
|
||||||
UartElements_t uartElements = {fileDescriptor, std::vector<uint8_t>(maxReplyLen), 0};
|
UartElements_t uartElements = {fileDescriptor, std::vector<uint8_t>(maxReplyLen), 0};
|
||||||
std::pair status = uartDeviceMap.emplace(deviceFile, uartElements);
|
auto status = uartDeviceMap.emplace(deviceFile, uartElements);
|
||||||
if (status.second == false) {
|
if (status.second == false) {
|
||||||
sif::debug << "UartComIF::initializeInterface: Failed to insert device " << deviceFile
|
sif::debug << "UartComIF::initializeInterface: Failed to insert device " << deviceFile
|
||||||
<< "to Uart device map" << std::endl;
|
<< "to UART device map" << std::endl;
|
||||||
return RETURN_FAILED;
|
return RETURN_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sif::debug << "UartComIF::initializeInterface: Uart device " << deviceFile << "already in "
|
sif::debug << "UartComIF::initializeInterface: UART device " << deviceFile << " already in "
|
||||||
<< "use" << std::endl;
|
<< "use" << std::endl;
|
||||||
return RETURN_FAILED;
|
return RETURN_FAILED;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
add_subdirectory(spi)
|
||||||
|
add_subdirectory(gpio)
|
||||||
|
add_subdirectory(devicetest)
|
||||||
|
|
||||||
|
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
|
dma.cpp
|
||||||
|
)
|
3
stm32h7/devicetest/CMakeLists.txt
Normal file
3
stm32h7/devicetest/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
|
GyroL3GD20H.cpp
|
||||||
|
)
|
557
stm32h7/devicetest/GyroL3GD20H.cpp
Normal file
557
stm32h7/devicetest/GyroL3GD20H.cpp
Normal file
@ -0,0 +1,557 @@
|
|||||||
|
#include "GyroL3GD20H.h"
|
||||||
|
|
||||||
|
#include "../spi/mspInit.h"
|
||||||
|
#include "../spi/spiDefinitions.h"
|
||||||
|
#include "../spi/spiCore.h"
|
||||||
|
#include "../spi/spiInterrupts.h"
|
||||||
|
#include "../spi/stm32h743ziSpi.h"
|
||||||
|
|
||||||
|
#include "fsfw/tasks/TaskFactory.h"
|
||||||
|
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||||
|
|
||||||
|
#include "stm32h7xx_nucleo.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);
|
||||||
|
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);
|
||||||
|
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
stm32h7/devicetest/GyroL3GD20H.h
Normal file
70
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_ */
|
83
stm32h7/dma.cpp
Normal file
83
stm32h7/dma.cpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include <fsfw_hal/stm32h7/dma.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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
stm32h7/dma.h
Normal file
49
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
stm32h7/gpio/CMakeLists.txt
Normal file
3
stm32h7/gpio/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
|
gpio.cpp
|
||||||
|
)
|
69
stm32h7/gpio/gpio.cpp
Normal file
69
stm32h7/gpio/gpio.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include "gpio.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
|
||||||
|
}
|
14
stm32h7/gpio/gpio.h
Normal file
14
stm32h7/gpio/gpio.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#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
stm32h7/i2c/CMakeLists.txt
Normal file
2
stm32h7/i2c/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
|
)
|
28
stm32h7/interrupts.h
Normal file
28
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
stm32h7/spi/CMakeLists.txt
Normal file
9
stm32h7/spi/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
|
spiCore.cpp
|
||||||
|
spiDefinitions.cpp
|
||||||
|
spiInterrupts.cpp
|
||||||
|
mspInit.cpp
|
||||||
|
SpiCookie.cpp
|
||||||
|
SpiComIF.cpp
|
||||||
|
stm32h743ziSpi.cpp
|
||||||
|
)
|
453
stm32h7/spi/SpiComIF.cpp
Normal file
453
stm32h7/spi/SpiComIF.cpp
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
#include "SpiComIF.h"
|
||||||
|
#include "SpiCookie.h"
|
||||||
|
|
||||||
|
#include "fsfw/tasks/SemaphoreFactory.h"
|
||||||
|
#include "fsfw/osal/FreeRTOS/TaskManagement.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"
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
// Release the task semaphore
|
||||||
|
BaseType_t taskWoken = pdFALSE;
|
||||||
|
ReturnValue_t result = BinarySemaphore::releaseFromISR(comIF->spiSemaphore->getSemaphore(),
|
||||||
|
&taskWoken);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
stm32h7/spi/SpiComIF.h
Normal file
130
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/osal/FreeRTOS/BinarySemaphore.h"
|
||||||
|
#include "fsfw_hal/stm32h7/spi/spiDefinitions.h"
|
||||||
|
#include "stm32h7xx_hal_spi.h"
|
||||||
|
#include "stm32h743xx.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
class SpiCookie;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
stm32h7/spi/SpiCookie.cpp
Normal file
78
stm32h7/spi/SpiCookie.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#include "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), transferMode(transferMode),
|
||||||
|
spiSpeed(spiSpeed), spiMode(spiMode), 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
stm32h7/spi/SpiCookie.h
Normal file
75
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_ */
|
252
stm32h7/spi/mspInit.cpp
Normal file
252
stm32h7/spi/mspInit.cpp
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
#include <fsfw_hal/stm32h7/dma.h>
|
||||||
|
#include "mspInit.h"
|
||||||
|
#include "spiCore.h"
|
||||||
|
#include "spiInterrupts.h"
|
||||||
|
#include "stm32h743xx.h"
|
||||||
|
#include "stm32h7xx_hal_spi.h"
|
||||||
|
#include "stm32h7xx_hal_dma.h"
|
||||||
|
#include "stm32h7xx_hal_def.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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
stm32h7/spi/mspInit.h
Normal file
114
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_ */
|
340
stm32h7/spi/spiCore.cpp
Normal file
340
stm32h7/spi/spiCore.cpp
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
#include "spiDefinitions.h"
|
||||||
|
#include "spiCore.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
stm32h7/spi/spiCore.h
Normal file
53
stm32h7/spi/spiCore.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef FSFW_HAL_STM32H7_SPI_SPICORE_H_
|
||||||
|
#define FSFW_HAL_STM32H7_SPI_SPICORE_H_
|
||||||
|
|
||||||
|
#include <fsfw_hal/stm32h7/dma.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
stm32h7/spi/spiDefinitions.cpp
Normal file
52
stm32h7/spi/spiDefinitions.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include "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
stm32h7/spi/spiDefinitions.h
Normal file
50
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
stm32h7/spi/spiInterrupts.cpp
Normal file
106
stm32h7/spi/spiInterrupts.cpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#include "spiInterrupts.h"
|
||||||
|
#include "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
stm32h7/spi/spiInterrupts.h
Normal file
41
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_ */
|
81
stm32h7/spi/stm32h743ziSpi.cpp
Normal file
81
stm32h7/spi/stm32h743ziSpi.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include "stm32h743ziSpi.h"
|
||||||
|
#include "spiCore.h"
|
||||||
|
#include "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
stm32h7/spi/stm32h743ziSpi.h
Normal file
23
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
stm32h7/uart/CMakeLists.txt
Normal file
2
stm32h7/uart/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user