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:
Robin Müller 2021-06-15 15:50:53 +02:00
commit f059aeb02d
45 changed files with 3320 additions and 39 deletions

View File

@ -20,6 +20,7 @@ if(NOT LIB_FSFW_NAME)
message(ERROR "LIB_FSFW_NAME needs to be set as a linkable target")
endif()
add_subdirectory(devicehandlers)
add_subdirectory(common)
if(FSFW_HAL_ADD_LINUX)
@ -34,8 +35,32 @@ target_link_libraries(${LIB_FSFW_HAL_NAME} PRIVATE
${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
${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")

View File

@ -1,5 +1,6 @@
#include "GpioCookie.h"
#include <fsfw/serviceinterface/ServiceInterface.h>
#include "fsfw/serviceinterface/ServiceInterface.h"
GpioCookie::GpioCookie() {
}

View File

@ -1,8 +1,9 @@
#ifndef LINUX_GPIO_GPIOCOOKIE_H_
#define LINUX_GPIO_GPIOCOOKIE_H_
#ifndef COMMON_GPIO_GPIOCOOKIE_H_
#define COMMON_GPIO_GPIOCOOKIE_H_
#include "GpioIF.h"
#include "gpioDefinitions.h"
#include <fsfw/devicehandlers/CookieIF.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
@ -37,4 +38,4 @@ private:
GpioMap gpioMap;
};
#endif /* LINUX_GPIO_GPIOCOOKIE_H_ */
#endif /* COMMON_GPIO_GPIOCOOKIE_H_ */

View File

@ -1,5 +1,5 @@
#ifndef LINUX_GPIO_GPIOIF_H_
#define LINUX_GPIO_GPIOIF_H_
#ifndef COMMON_GPIO_GPIOIF_H_
#define COMMON_GPIO_GPIOIF_H_
#include "gpioDefinitions.h"
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
@ -51,4 +51,4 @@ public:
virtual ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) = 0;
};
#endif /* LINUX_GPIO_GPIOIF_H_ */
#endif /* COMMON_GPIO_GPIOIF_H_ */

View File

@ -1,8 +1,9 @@
#ifndef LINUX_GPIO_GPIODEFINITIONS_H_
#define LINUX_GPIO_GPIODEFINITIONS_H_
#ifndef COMMON_GPIO_GPIODEFINITIONS_H_
#define COMMON_GPIO_GPIODEFINITIONS_H_
#include <string>
#include <unordered_map>
#include <map>
using gpioId_t = uint16_t;
@ -25,11 +26,14 @@ enum GpioOperation {
enum GpioTypes {
NONE,
GPIOD_REGULAR,
GPIO_REGULAR,
CALLBACK
};
static constexpr gpioId_t NO_GPIO = -1;
using gpio_cb_t = void (*) (gpioId_t gpioId, gpio::GpioOperation gpioOp, int value, void* args);
}
/**
@ -66,12 +70,12 @@ public:
class GpiodRegular: public GpioBase {
public:
GpiodRegular(): GpioBase(gpio::GpioTypes::GPIOD_REGULAR, std::string(),
GpiodRegular(): GpioBase(gpio::GpioTypes::GPIO_REGULAR, std::string(),
gpio::Direction::IN, 0) {};
GpiodRegular(std::string chipname_, int lineNum_, std::string consumer_,
gpio::Direction direction_, int initValue_):
GpioBase(gpio::GpioTypes::GPIOD_REGULAR, consumer_, direction_, initValue_),
GpioBase(gpio::GpioTypes::GPIO_REGULAR, consumer_, direction_, initValue_),
chipname(chipname_), lineNum(lineNum_) {}
std::string chipname;
int lineNum = 0;
@ -81,18 +85,18 @@ public:
class GpioCallback: public GpioBase {
public:
GpioCallback(std::string consumer, gpio::Direction direction_, int initValue_,
void (* callback) (gpioId_t gpioId, gpio::GpioOperation gpioOp, int value, void* args),
void* callbackArgs):
gpio::gpio_cb_t callback, void* callbackArgs):
GpioBase(gpio::GpioTypes::CALLBACK, consumer, direction_, initValue_),
callback(callback), callbackArgs(callbackArgs) {}
void (* callback) (gpioId_t gpioId, gpio::GpioOperation gpioOp,
int value, void* args) = nullptr;
gpio::gpio_cb_t callback = 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 GpioUnorderedMapIter = GpioUnorderedMap::iterator;
#endif /* LINUX_GPIO_GPIODEFINITIONS_H_ */

17
common/spi/spiCommon.h Normal file
View 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_ */

View File

@ -0,0 +1,3 @@
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
GyroL3GD20Handler.cpp
)

View 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;
}

View 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_ */

View 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_ */

View File

@ -49,7 +49,7 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
case(gpio::GpioTypes::NONE): {
return GPIO_INVALID_INSTANCE;
}
case(gpio::GpioTypes::GPIOD_REGULAR): {
case(gpio::GpioTypes::GPIO_REGULAR): {
GpiodRegular* regularGpio = dynamic_cast<GpiodRegular*>(gpioConfig.second);
if(regularGpio == nullptr) {
return GPIO_INVALID_INSTANCE;
@ -145,7 +145,7 @@ ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) {
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);
}
else {
@ -166,7 +166,7 @@ ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId) {
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);
}
else {
@ -203,7 +203,7 @@ ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) {
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);
if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE;
@ -223,7 +223,7 @@ ReturnValue_t LinuxLibgpioIF::checkForConflicts(GpioMap& mapToAdd){
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
for(auto& gpioConfig: mapToAdd) {
switch(gpioConfig.second->gpioType) {
case(gpio::GpioTypes::GPIOD_REGULAR): {
case(gpio::GpioTypes::GPIO_REGULAR): {
auto regularGpio = dynamic_cast<GpiodRegular*>(gpioConfig.second);
if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE;
@ -261,7 +261,7 @@ ReturnValue_t LinuxLibgpioIF::checkForConflictsRegularGpio(gpioId_t gpioIdToChec
/* Cross check with private map */
gpioMapIter = gpioMap.find(gpioIdToCheck);
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 "
"GPIO type" << gpioIdToCheck << ". Removing duplicate." << std::endl;
mapToAdd.erase(gpioIdToCheck);

View File

@ -38,8 +38,8 @@ public:
private:
/* Holds the information and configuration of all used GPIOs */
GpioMap gpioMap;
GpioMapIter gpioMapIter;
GpioUnorderedMap gpioMap;
GpioUnorderedMapIter gpioMapIter;
/**
* @brief This functions drives line of a GPIO specified by the GPIO ID.

View File

@ -1,4 +1,4 @@
target_sources(${TARGET_NAME} PUBLIC
target_sources(${LIB_FSFW_HAL_NAME} PUBLIC
I2cComIF.cpp
I2cCookie.cpp
)

View File

@ -1,4 +1,4 @@
target_sources(${TARGET_NAME} PUBLIC
target_sources(${LIB_FSFW_HAL_NAME} PUBLIC
SpiComIF.cpp
SpiCookie.cpp
)

View File

@ -47,7 +47,7 @@ ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) {
auto iter = spiDeviceMap.find(spiAddress);
if(iter == spiDeviceMap.end()) {
size_t bufferSize = spiCookie->getMaxBufferSize();
SpiInstance spiInstance = {std::vector<uint8_t>(bufferSize)};
SpiInstance spiInstance(bufferSize);
auto statusPair = spiDeviceMap.emplace(spiAddress, spiInstance);
if (not statusPair.second) {
#if FSFW_VERBOSE_LEVEL >= 1

View File

@ -67,6 +67,7 @@ public:
private:
struct SpiInstance {
SpiInstance(size_t maxRecvSize): replyBuffer(std::vector<uint8_t>(maxRecvSize)) {}
std::vector<uint8_t> replyBuffer;
};

View File

@ -2,6 +2,7 @@
#define LINUX_SPI_SPIDEFINITONS_H_
#include "../../common/gpio/gpioDefinitions.h"
#include "../../common/spi/spiCommon.h"
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include <linux/spi/spidev.h>
@ -13,13 +14,6 @@ class SpiComIF;
namespace spi {
enum SpiModes: uint8_t {
MODE_0,
MODE_1,
MODE_2,
MODE_3
};
enum SpiComIfModes {
REGULAR,
CALLBACK

View File

@ -38,15 +38,15 @@ ReturnValue_t UartComIF::initializeInterface(CookieIF * cookie) {
}
size_t maxReplyLen = uartCookie->getMaxReplyLen();
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) {
sif::debug << "UartComIF::initializeInterface: Failed to insert device " << deviceFile
<< "to Uart device map" << std::endl;
sif::debug << "UartComIF::initializeInterface: Failed to insert device " << deviceFile
<< "to UART device map" << std::endl;
return RETURN_FAILED;
}
}
else {
sif::debug << "UartComIF::initializeInterface: Uart device " << deviceFile << "already in "
sif::debug << "UartComIF::initializeInterface: UART device " << deviceFile << " already in "
<< "use" << std::endl;
return RETURN_FAILED;
}

View File

@ -0,0 +1,7 @@
add_subdirectory(spi)
add_subdirectory(gpio)
add_subdirectory(devicetest)
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
dma.cpp
)

View File

@ -0,0 +1,3 @@
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
GyroL3GD20H.cpp
)

View 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;
}

View 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
View 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
View 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_ */

View File

@ -0,0 +1,3 @@
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
gpio.cpp
)

69
stm32h7/gpio/gpio.cpp Normal file
View 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
View 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_ */

View File

@ -0,0 +1,2 @@
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
)

28
stm32h7/interrupts.h Normal file
View 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_ */

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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_ */

View 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;
}

View 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_ */

View 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();
}

View 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_ */

View 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);
}

View 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_ */

View File

@ -0,0 +1,2 @@
target_sources(${LIB_FSFW_HAL_NAME} PRIVATE
)