fsfw-hal/stm32h7/spi/SpiComIF.cpp

365 lines
13 KiB
C++

#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 "stm32h7xx_hal_gpio.h"
SpiComIF::SpiComIF(object_id_t objectId, SPI_TypeDef* spiInstance, SPI_HandleTypeDef* spiHandle,
spi::TransferModes transferMode): SystemObject(objectId), transferMode(transferMode),
spiHandle(spiHandle) {
if(spiHandle == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "SpiComIF::SpiComIF: Passed SPI handle invalid!" << std::endl;
#else
sif::printError("SpiComIF::SpiComIF: Passed SPI handle invalid!\n");
#endif
return;
}
spiHandle->Instance = spiInstance;
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::assignTransferRxTxCompleteCallback(&spiTransferCompleteCallback, this);
spi::assignTransferRxCompleteCallback(&spiTransferRxCompleteCallback, this);
spi::assignTransferTxCompleteCallback(&spiTransferTxCompleteCallback, this);
spi::assignTransferErrorCallback(&spiTransferErrorCallback, this);
}
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() {
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;
}
}
if(HAL_SPI_Init(spiHandle) != HAL_OK) {
sif::printWarning("SpiComIF::initialize: Error initializing SPI\n");
return HasReturnvaluesIF::RETURN_FAILED;
}
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;
}
if(transferMode == spi::TransferModes::DMA or transferMode == spi::TransferModes::INTERRUPT) {
spiSemaphore = dynamic_cast<BinarySemaphore*>(
SemaphoreFactory::instance()->createBinarySemaphore());
}
else {
spiMutex = MutexFactory::instance()->createMutex();
}
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();
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);
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::assignSpiMode(spiCookie->getSpiMode(), spiHandle);
spiHandle->Init.BaudRatePrescaler = spi::getPrescaler(HAL_RCC_GetHCLKFreq(),
spiCookie->getSpiSpeed());
auto iter = spiDeviceMap.find(spiCookie->getDeviceAddress());
if(iter == spiDeviceMap.end()) {
return HasReturnvaluesIF::RETURN_FAILED;
}
iter->second.currentTransferLen = sendLen;
switch(transferMode) {
case(spi::TransferModes::POLLING): {
return handlePollingSendOperation(iter->second.replyBuffer.data(), spiCookie, sendData,
sendLen);
}
case(spi::TransferModes::INTERRUPT): {
return handleInterruptSendOperation(iter->second.replyBuffer.data(), spiCookie, sendData,
sendLen);
}
case(spi::TransferModes::DMA): {
return handleDmaSendOperation(iter->second.replyBuffer.data(), 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;
}
auto iter = spiDeviceMap.find(spiCookie->getDeviceAddress());
if(iter == spiDeviceMap.end()) {
return HasReturnvaluesIF::RETURN_FAILED;
}
*buffer = iter->second.replyBuffer.data();
*size = iter->second.currentTransferLen;
return HasReturnvaluesIF::RETURN_OK;
}
void SpiComIF::setDefaultPollingTimeout(dur_millis_t timeout) {
this->defaultPollingTimeout = timeout;
}
ReturnValue_t SpiComIF::handlePollingSendOperation(uint8_t* recvPtr, SpiCookie *spiCookie,
const uint8_t *sendData, size_t sendLen) {
auto gpioPort = spiCookie->getChipSelectGpioPort();
auto gpioPin = spiCookie->getChipSelectGpioPin();
spiMutex->lockMutex(timeoutType, timeoutMs);
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);
spiMutex->unlockMutex();
switch(result) {
case(HAL_OK): {
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
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
return spi::HAL_ERROR_RETVAL;
}
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t SpiComIF::handleInterruptSendOperation(uint8_t* recvPtr, SpiCookie* spiCookie,
const uint8_t * sendData, size_t sendLen) {
return handleIrqSendOperation(recvPtr, spiCookie, sendData, sendLen);
}
ReturnValue_t SpiComIF::handleDmaSendOperation(uint8_t* recvPtr, SpiCookie* spiCookie,
const uint8_t * sendData, size_t sendLen) {
return handleIrqSendOperation(recvPtr, spiCookie, sendData, sendLen);
}
ReturnValue_t SpiComIF::handleIrqSendOperation(uint8_t *recvPtr, SpiCookie *spiCookie,
const uint8_t *sendData, size_t sendLen) {
ReturnValue_t result = genericIrqSendSetup(recvPtr, spiCookie, sendData, sendLen);
if(result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
// yet another HAL driver which is not const-correct..
HAL_StatusTypeDef status = HAL_OK;
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);
}
}
return result;
}
ReturnValue_t SpiComIF::halErrorHandler(HAL_StatusTypeDef status) {
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, SpiCookie *spiCookie,
const uint8_t *sendData, size_t sendLen) {
// These are required by the callback
currentGpioPort = spiCookie->getChipSelectGpioPort();
currentGpioPin = spiCookie->getChipSelectGpioPin();
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;
}
HAL_GPIO_WritePin(currentGpioPort, currentGpioPin, GPIO_PIN_RESET);
return HasReturnvaluesIF::RETURN_OK;
}
void SpiComIF::spiTransferTxCompleteCallback(SPI_HandleTypeDef *hspi, void *args) {
SpiComIF* spiComIF = reinterpret_cast<SpiComIF*>(args);
if(spiComIF == nullptr) {
return;
}
genericIrqHandler(spiComIF, TransferStates::FAILURE);
}
void SpiComIF::spiTransferRxCompleteCallback(SPI_HandleTypeDef *hspi, void *args) {
SpiComIF* spiComIF = reinterpret_cast<SpiComIF*>(args);
if(spiComIF == nullptr) {
return;
}
genericIrqHandler(spiComIF, TransferStates::FAILURE);
}
void SpiComIF::spiTransferCompleteCallback(SPI_HandleTypeDef *hspi, void *args) {
SpiComIF* spiComIF = reinterpret_cast<SpiComIF*>(args);
if(spiComIF == nullptr) {
return;
}
genericIrqHandler(spiComIF, TransferStates::FAILURE);
}
void SpiComIF::spiTransferErrorCallback(SPI_HandleTypeDef *hspi, void *args) {
SpiComIF* spiComIF = reinterpret_cast<SpiComIF*>(args);
if(spiComIF == nullptr) {
return;
}
genericIrqHandler(spiComIF, TransferStates::FAILURE);
}
void SpiComIF::genericIrqHandler(SpiComIF *spiComIF, TransferStates targetState) {
spiComIF->transferState = TransferStates::SUCCESS;
// Pull CS pin high again
HAL_GPIO_WritePin(spiComIF->currentGpioPort, spiComIF->currentGpioPin, GPIO_PIN_SET);
// Release the task semaphore
BaseType_t taskWoken = pdFALSE;
ReturnValue_t result = BinarySemaphore::releaseFromISR(spiComIF->spiSemaphore->getSemaphore(),
&taskWoken);
if(result != HasReturnvaluesIF::RETURN_FAILED) {
// Configuration error
printf("SpiComIF::genericIrqHandler: Failure releasing Semaphore!\n");
}
// Perform cache maintenance operation for DMA transfers
if(spiComIF->transferMode == spi::TransferModes::DMA) {
// Invalidate cache prior to access by CPU
SCB_InvalidateDCache_by_Addr ((uint32_t *) spiComIF->currentRecvPtr,
spiComIF->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);
}
}