From 3550fb6ca731c49b1d6c2e7a87e594d1a8bd52e1 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 23 Feb 2021 11:31:50 +0100 Subject: [PATCH] spi com if almost finished --- fsfw | 2 +- linux/gpio/LinuxLibgpioIF.h | 3 +- linux/gpio/gpioDefinitions.h | 2 + linux/i2c/I2cComIF.cpp | 26 ++- linux/i2c/I2cComIF.h | 4 +- linux/spi/CMakeLists.txt | 2 + linux/spi/SpiComIF.cpp | 303 ++++++++++++++++++++++++++++++++++ linux/spi/SpiComIF.h | 61 +++++++ linux/spi/SpiCookie.cpp | 99 +++++++++++ linux/spi/SpiCookie.h | 113 +++++++++++++ linux/spi/spiDefinitions.h | 15 ++ linux/utility/errorhandling.h | 30 ++++ 12 files changed, 649 insertions(+), 11 deletions(-) create mode 100644 linux/spi/SpiComIF.cpp create mode 100644 linux/spi/SpiComIF.h create mode 100644 linux/spi/SpiCookie.cpp create mode 100644 linux/spi/SpiCookie.h create mode 100644 linux/spi/spiDefinitions.h create mode 100644 linux/utility/errorhandling.h diff --git a/fsfw b/fsfw index 1ccfb747..a3d245f5 160000 --- a/fsfw +++ b/fsfw @@ -1 +1 @@ -Subproject commit 1ccfb74709ba0ce6d469eed3df1584dbcb444321 +Subproject commit a3d245f5a030c9450c516552283d26da2c799301 diff --git a/linux/gpio/LinuxLibgpioIF.h b/linux/gpio/LinuxLibgpioIF.h index a849bb09..03c78fdb 100644 --- a/linux/gpio/LinuxLibgpioIF.h +++ b/linux/gpio/LinuxLibgpioIF.h @@ -40,8 +40,7 @@ private: * @param gpioId The GPIO ID of the GPIO to drive. * @param logiclevel The logic level to set. O or 1. */ - ReturnValue_t driveGpio(gpioId_t gpioId, - unsigned int logiclevel); + ReturnValue_t driveGpio(gpioId_t gpioId, unsigned int logiclevel); /** * @brief This function checks if GPIOs are already registered and whether diff --git a/linux/gpio/gpioDefinitions.h b/linux/gpio/gpioDefinitions.h index ce26ec78..28ad1239 100644 --- a/linux/gpio/gpioDefinitions.h +++ b/linux/gpio/gpioDefinitions.h @@ -11,6 +11,8 @@ enum Direction { IN = 0, OUT = 1 }; + +static constexpr gpioId_t NO_GPIO = -1; } /** diff --git a/linux/i2c/I2cComIF.cpp b/linux/i2c/I2cComIF.cpp index ef77baa1..8e372b14 100644 --- a/linux/i2c/I2cComIF.cpp +++ b/linux/i2c/I2cComIF.cpp @@ -181,16 +181,30 @@ ReturnValue_t I2cComIF::openDevice(std::string deviceFile, address_t i2cAddress, int* fileDescriptor) { *fileDescriptor = open(deviceFile.c_str(), O_RDWR); if (*fileDescriptor < 0) { - sif::error << "I2cComIF: Opening i2c device failed with error code " - << errno << ". Error description: " << strerror(errno) - << std::endl; +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "I2cComIF: Opening I2C device failed with error code " << errno << "." << + std::endl; + sif::warning << "Error description: " << strerror(errno) << std::endl; +#else + sif::printWarning("I2cComIF: Opening I2C device failed with error code %d.\n"); + sif::printWarning("Error description: %s\n", strerror(errno)); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ return HasReturnvaluesIF::RETURN_FAILED; } if (ioctl(*fileDescriptor, I2C_SLAVE, i2cAddress) < 0) { - sif::error << "I2cComIF: Specifying target device failed with error " - << "code " << errno << ". Error description " - << strerror(errno) << std::endl; +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "I2cComIF: Specifying target device failed with error code " << errno << "." + << std::endl; + sif::warning << "Error description " << strerror(errno) << std::endl; +#else + sif::printWarning("I2cComIF: Specifying target device failed with error code %d.\n"); + sif::printWarning("Error description: %s\n", strerror(errno)); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ return HasReturnvaluesIF::RETURN_FAILED; } return HasReturnvaluesIF::RETURN_OK; diff --git a/linux/i2c/I2cComIF.h b/linux/i2c/I2cComIF.h index f5d07ac1..3529bde7 100644 --- a/linux/i2c/I2cComIF.h +++ b/linux/i2c/I2cComIF.h @@ -45,8 +45,8 @@ private: I2cDeviceMapIter i2cDeviceMapIter; /** - * @brief This function opens an i2c device and binds the opened file - * to a specific i2c address. + * @brief This function opens an I2C device and binds the opened file + * to a specific I2C address. * @param deviceFile The name of the device file. E.g. i2c-0 * @param i2cAddress The address of the i2c slave device. * @param fileDescriptor Pointer to device descriptor. diff --git a/linux/spi/CMakeLists.txt b/linux/spi/CMakeLists.txt index 45a7edcc..cb1c9277 100644 --- a/linux/spi/CMakeLists.txt +++ b/linux/spi/CMakeLists.txt @@ -1,4 +1,6 @@ target_sources(${TARGET_NAME} PUBLIC + SpiComIF.cpp + SpiCookie.cpp ) diff --git a/linux/spi/SpiComIF.cpp b/linux/spi/SpiComIF.cpp new file mode 100644 index 00000000..bdb0c705 --- /dev/null +++ b/linux/spi/SpiComIF.cpp @@ -0,0 +1,303 @@ +#include "SpiComIF.h" +#include "spiDefinitions.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +SpiComIF::SpiComIF(object_id_t objectId, GpioIF* gpioComIF): SystemObject(objectId), + gpioComIF(gpioComIF) { + if(gpioComIF == nullptr) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "SpiComIF::SpiComIF: GPIO communication interface invalid!" << std::endl; +#else + sif::printError("SpiComIF::SpiComIF: GPIO communication interface invalid!\n"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + } + + spiMutex = MutexFactory::instance()->createMutex(); +} + +ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) { + SpiCookie* spiCookie = dynamic_cast(cookie); + if(spiCookie == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + address_t spiAddress = spiCookie->getSpiAddress(); + + auto iter = spiDeviceMap.find(spiAddress); + if(iter == spiDeviceMap.end()) { + size_t bufferSize = spiCookie->getMaxBufferSize(); + SpiInstance spiInstance = {std::vector(bufferSize)}; + auto statusPair = spiDeviceMap.emplace(spiAddress, spiInstance); + if (not statusPair.second) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "SpiComIF::initializeInterface: Failed to insert device with address " << + spiAddress << "to SPI device map" << std::endl; +#else + sif::printError("SpiComIF::initializeInterface: Failed to insert device with address " + "%lu to SPI device map\n", static_cast(spiAddress)); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + return HasReturnvaluesIF::RETURN_FAILED; + } + /* Now we emplaced the read buffer in the map, we still need to assign that location + to the SPI driver transfer struct */ + spiCookie->assignReadBuffer(statusPair.first->second.replyBuffer.data()); + } + else { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "SpiComIF::initializeInterface: SPI address already exists!" << std::endl; +#else + sif::printError("SpiComIF::initializeInterface: SPI address already exists!\n"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + return HasReturnvaluesIF::RETURN_FAILED; + } + + gpioId_t gpioId = spiCookie->getChipSelectPin(); + if(gpioId != gpio::NO_GPIO) { + gpioComIF->pullHigh(gpioId); + } + + size_t spiSpeed = 0; + spi::SpiMode spiMode = spi::SpiMode::MODE_0; + + SpiCookie::UncommonParameters params; + spiCookie->getSpiParameters(spiMode, spiSpeed, ¶ms); + + int fileDescriptor = 0; + ReturnValue_t result = openDevice(spiCookie->getSpiDevice(), &fileDescriptor); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + int retval = ioctl(fileDescriptor, SPI_IOC_WR_MODE, spiSpeed); + if(retval != 0) { + utility::handleIoctlError("SpiComIF::initializeInterface: Setting SPI mode failed!"); + } + + retval = ioctl(fileDescriptor, SPI_IOC_WR_MAX_SPEED_HZ, spiSpeed); + if(retval != 0) { + utility::handleIoctlError("SpiComIF::initializeInterface: Setting SPI speed failed!"); + } + + /* These flags are rather uncommon */ + if(params.threeWireSpi or params.noCs or params.csHigh) { + uint32_t currentMode = 0; + retval = ioctl(fileDescriptor, SPI_IOC_RD_MODE32, ¤tMode); + if(retval != 0) { + utility::handleIoctlError("SpiComIF::initialiezInterface: Could not read full mode!"); + } + + if(params.threeWireSpi) { + currentMode |= SPI_3WIRE; + } + if(params.noCs) { + /* Some drivers like the Raspberry Pi ignore this flag in any case */ + currentMode |= SPI_NO_CS; + } + if(params.csHigh) { + currentMode |= SPI_CS_HIGH; + } + /* Write adapted mode */ + retval = ioctl(fileDescriptor, SPI_IOC_WR_MODE32, currentMode); + if(retval != 0) { + utility::handleIoctlError("SpiComIF::initialiezInterface: Could not write full mode!"); + } + } + if(params.lsbFirst) { + retval = ioctl(fileDescriptor, SPI_IOC_WR_LSB_FIRST, true); + if(retval != 0) { + utility::handleIoctlError("SpiComIF::initializeInterface: Setting LSB first failed"); + } + } + if(params.bitsPerWord != 8) { + retval = ioctl(fileDescriptor, SPI_IOC_WR_BITS_PER_WORD, params.bitsPerWord); + if(retval != 0) { + utility::handleIoctlError("SpiComIF::initializeInterface: " + "Could not write bits per word!"); + } + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t SpiComIF::sendMessage(CookieIF *cookie, const uint8_t *sendData, size_t sendLen) { + SpiCookie* spiCookie = dynamic_cast(cookie); + if(spiCookie == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + if(sendLen > spiCookie->getMaxBufferSize()) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "SpiComIF::sendMessage: Too much data sent, send length" << sendLen << + "larger than maximum buffer length" << spiCookie->getMaxBufferSize() << std::endl; +#else + sif::printWarning("SpiComIF::sendMessage: Too much data sent, send length %lu larger " + "than maximum buffer length %lu!\n", static_cast(sendLen), + static_cast(spiCookie->getMaxBufferSize())); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + return DeviceCommunicationIF::TOO_MUCH_DATA; + } + + spiCookie->assignWriteBuffer(sendData); + spiCookie->assignTransferSize(sendLen); + int fileDescriptor = 0; + std::string device = spiCookie->getSpiDevice(); + ReturnValue_t result = openDevice(device, &fileDescriptor); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + bool fullDuplex = spiCookie->isFullDuplex(); + int retval = 0; + + gpioId_t gpioId = spiCookie->getChipSelectPin(); + MutexHelper(spiMutex, timeoutType, timeoutMs); + if(gpioId != gpio::NO_GPIO) { + /* For now, no support for active high given */ + gpioComIF->pullLow(gpioId); + } + + if(fullDuplex) { + /* Initiate a full duplex SPI transfer. */ + retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), spiCookie->getTransferStructHandle()); + if(retval != 0) { + utility::handleIoctlError("SpiComIF::sendMessage: ioctl error."); + /* TODO: Better returnvalue */ + return HasReturnvaluesIF::RETURN_FAILED; + } + } + else { + /* We write with a blocking transfer here */ + if (write(fileDescriptor, sendData, sendLen) != static_cast(sendLen)) { + sif::warning << "SpiComIF::sendMessage: Half-Duplex write operation failed!" << + std::endl; + /* TODO: Better returnvalue */ + return HasReturnvaluesIF::RETURN_FAILED; + } + } + + if(gpioId != gpio::NO_GPIO) { + gpioComIF->pullHigh(gpioId); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t SpiComIF::getSendSuccess(CookieIF *cookie) { + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t SpiComIF::requestReceiveMessage(CookieIF *cookie, size_t requestLen) { + SpiCookie* spiCookie = dynamic_cast(cookie); + if(spiCookie == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + bool fullDuplex = spiCookie->isFullDuplex(); + if(fullDuplex) { + return HasReturnvaluesIF::RETURN_OK; + } + + std::string device = spiCookie->getSpiDevice(); + int fileDescriptor = 0; + ReturnValue_t result = openDevice(device, &fileDescriptor); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + uint8_t* rxBuf = nullptr; + size_t readSize = spiCookie->getCurrentTransferSize(); + result = getReadBuffer(spiCookie->getSpiAddress(), &rxBuf); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + gpioId_t gpioId = spiCookie->getChipSelectPin(); + MutexHelper(spiMutex, timeoutType, timeoutMs); + if(gpioId != gpio::NO_GPIO) { + gpioComIF->pullLow(gpioId); + } + + if(read(fileDescriptor, rxBuf, readSize) != static_cast(readSize)) { + sif::warning << "SpiComIF::sendMessage: Half-Duplex read operation failed!" << std::endl; + if(gpioId != gpio::NO_GPIO) { + gpioComIF->pullHigh(gpioId); + } + } + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t SpiComIF::readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) { + SpiCookie* spiCookie = dynamic_cast(cookie); + if(spiCookie == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + uint8_t* rxBuf = nullptr; + ReturnValue_t result = getReadBuffer(spiCookie->getSpiAddress(), &rxBuf); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + *buffer = rxBuf; + *size = spiCookie->getCurrentTransferSize(); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t SpiComIF::openDevice(std::string deviceFile, int *fileDescriptor, bool nonBlocking) { + if(fileDescriptor == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + int flags = O_RDWR; + if(nonBlocking) { + flags |= O_NONBLOCK; + } + *fileDescriptor = open(deviceFile.c_str(), flags); + if (*fileDescriptor < 0) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "SpiComIF: Opening SPI device failed with error code " << errno << "." << + std::endl; + sif::warning << "Error description: " << strerror(errno) << std::endl; +#else + sif::printError("SpiComIF: Opening SPI device failed with error code %d.\n"); + sif::printWarning("Error description: %s\n", strerror(errno)); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + return HasReturnvaluesIF::RETURN_FAILED; + } + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t SpiComIF::getReadBuffer(address_t spiAddress, uint8_t** buffer) { + if(buffer == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + auto iter = spiDeviceMap.find(spiAddress); + if(iter == spiDeviceMap.end()) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + *buffer = iter->second.replyBuffer.data(); + return HasReturnvaluesIF::RETURN_OK; +} diff --git a/linux/spi/SpiComIF.h b/linux/spi/SpiComIF.h new file mode 100644 index 00000000..4009eed9 --- /dev/null +++ b/linux/spi/SpiComIF.h @@ -0,0 +1,61 @@ +#ifndef LINUX_SPI_SPICOMIF_H_ +#define LINUX_SPI_SPICOMIF_H_ + +#include +#include +#include + +#include +#include + +/** + * @brief Encapsulates access to linux SPI driver for FSFW objects + * @details + * Right now, only full-duplex SPI is supported. + * @author R. Mueller + */ +class SpiComIF: public DeviceCommunicationIF, public SystemObject { +public: + SpiComIF(object_id_t objectId, GpioIF* gpioComIF); + + ReturnValue_t initializeInterface(CookieIF * cookie) override; + ReturnValue_t sendMessage(CookieIF *cookie,const uint8_t *sendData, + size_t sendLen) override; + ReturnValue_t getSendSuccess(CookieIF *cookie) override; + ReturnValue_t requestReceiveMessage(CookieIF *cookie, + size_t requestLen) override; + ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, + size_t *size) override; +private: + + struct SpiInstance { + std::vector replyBuffer; + }; + + GpioIF* gpioComIF = nullptr; + + MutexIF* spiMutex = nullptr; + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; + uint32_t timeoutMs = 20; + + using SpiDeviceMap = std::unordered_map; + using SpiDeviceMapIter = SpiDeviceMap::iterator; + + SpiDeviceMap spiDeviceMap; + + /** + * @brief This function opens an SPI device and binds the opened file + * to a specific SPI address. + * @param deviceFile The name of the device file. E.g. spi-0 + * @param i2cAddress The address of the SPI slave device. + * @param fileDescriptor Pointer to device descriptor. + * @return RETURN_OK if successful, otherwise RETURN_FAILED. + */ + ReturnValue_t openDevice(std::string deviceFile, int* fileDescriptor, bool nonBlocking = false); + + ReturnValue_t getReadBuffer(address_t spiAddress, uint8_t** buffer); +}; + + + +#endif /* LINUX_SPI_SPICOMIF_H_ */ diff --git a/linux/spi/SpiCookie.cpp b/linux/spi/SpiCookie.cpp new file mode 100644 index 00000000..f0b81f67 --- /dev/null +++ b/linux/spi/SpiCookie.cpp @@ -0,0 +1,99 @@ +#include "SpiCookie.h" + +SpiCookie::SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev, + const size_t maxSize, spi::SpiMode spiMode, uint32_t spiSpeed): spiAddress(spiAddress), + chipSelectPin(chipSelect), spiDevice(spiDev), maxSize(maxSize), spiMode(spiMode), + spiSpeed(spiSpeed) { +} + +SpiCookie::SpiCookie(address_t spiAddress, std::string spiDev, const size_t maxSize, + spi::SpiMode spiMode, uint32_t spiSpeed): + SpiCookie(spiAddress, gpio::NO_GPIO, spiDev, maxSize, spiMode, spiSpeed) { +} + +void SpiCookie::getSpiParameters(spi::SpiMode& spiMode, uint32_t& spiSpeed, + UncommonParameters* parameters) const { + spiMode = this->spiMode; + spiSpeed = this->spiSpeed; + + if(parameters != nullptr) { + parameters->threeWireSpi = uncommonParameters.threeWireSpi; + parameters->lsbFirst = uncommonParameters.lsbFirst; + parameters->noCs = uncommonParameters.noCs; + parameters->bitsPerWord = uncommonParameters.bitsPerWord; + parameters->csHigh = uncommonParameters.csHigh; + } +} + +gpioId_t SpiCookie::getChipSelectPin() const { + return chipSelectPin; +} + +size_t SpiCookie::getMaxBufferSize() const { + return maxSize; +} + +address_t SpiCookie::getSpiAddress() const { + return spiAddress; +} + +std::string SpiCookie::getSpiDevice() const { + return spiDevice; +} + +void SpiCookie::setThreeWireSpi(bool enable) { + uncommonParameters.threeWireSpi = enable; +} + +void SpiCookie::setLsbFirst(bool enable) { + uncommonParameters.lsbFirst = enable; +} + +void SpiCookie::setNoCs(bool enable) { + uncommonParameters.noCs = enable; +} + +void SpiCookie::setBitsPerWord(uint8_t bitsPerWord) { + uncommonParameters.bitsPerWord = bitsPerWord; +} + +void SpiCookie::setCsHigh(bool enable) { + uncommonParameters.csHigh = enable; +} + +void SpiCookie::activateCsDeselect(bool deselectCs, uint16_t delayUsecs) { + spiTransferStruct.cs_change = deselectCs; + spiTransferStruct.delay_usecs = delayUsecs; +} + +void SpiCookie::assignReadBuffer(uint8_t* rx) { + if(rx != nullptr) { + spiTransferStruct.rx_buf = reinterpret_cast<__u64>(rx); + } +} + +void SpiCookie::assignWriteBuffer(const uint8_t* tx) { + if(tx != nullptr) { + spiTransferStruct.tx_buf = reinterpret_cast<__u64>(tx); + } +} + +spi_ioc_transfer* SpiCookie::getTransferStructHandle() { + return &spiTransferStruct; +} + +void SpiCookie::setFullOrHalfDuplex(bool halfDuplex) { + this->halfDuplex = halfDuplex; +} + +bool SpiCookie::isFullDuplex() const { + return not this->halfDuplex; +} + +void SpiCookie::assignTransferSize(size_t transferSize) { + spiTransferStruct.len = transferSize; +} + +size_t SpiCookie::getCurrentTransferSize() const { + return spiTransferStruct.len; +} diff --git a/linux/spi/SpiCookie.h b/linux/spi/SpiCookie.h new file mode 100644 index 00000000..91d42a3c --- /dev/null +++ b/linux/spi/SpiCookie.h @@ -0,0 +1,113 @@ +#ifndef LINUX_SPI_SPICOOKIE_H_ +#define LINUX_SPI_SPICOOKIE_H_ + +#include "spiDefinitions.h" +#include +#include +#include + +class SpiCookie: public CookieIF { +public: + + /** + * Each SPI device will have a corresponding cookie. The cookie is used by the communication + * interface and contains device specific information like the largest expected size to be + * sent and received and the GPIO pin used to toggle the SPI slave select pin. + * @param spiAddress + * @param chipSelect Chip select. gpio::NO_GPIO can be used for hardware slave selects. + * @param spiDev + * @param maxSize + */ + SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev, + const size_t maxReplySize, spi::SpiMode spiMode, uint32_t spiSpeed); + + /** + * Like constructor above, but without a dedicated GPIO CS. Can be used for hardware + * slave select or if CS logic is performed with decoders. + */ + SpiCookie(address_t spiAddress, std::string spiDev, const size_t maxReplySize, + spi::SpiMode spiMode, uint32_t spiSpeed); + + address_t getSpiAddress() const; + std::string getSpiDevice() const; + gpioId_t getChipSelectPin() const; + size_t getMaxBufferSize() const; + /** + * True if SPI transfers should be performed in full duplex mode + * @return + */ + bool isFullDuplex() const; + + /** + * Set transfer type to full duplex or half duplex. Full duplex is the default setting, + * ressembling common SPI hardware implementation with shift registers, where read and writes + * happen simultaneosly. + * @param fullDuplex + */ + void setFullOrHalfDuplex(bool halfDuplex); + + /** + * This needs to be called to specify where the SPI driver writes to or reads from. + * @param readLocation + * @param writeLocation + */ + void assignReadBuffer(uint8_t* rx); + void assignWriteBuffer(const uint8_t* tx); + /** + * Assign size for the next transfer. + * @param transferSize + */ + void assignTransferSize(size_t transferSize); + size_t getCurrentTransferSize() const; + + struct UncommonParameters { + uint8_t bitsPerWord = 8; + bool noCs = false; + bool csHigh = false; + bool threeWireSpi = false; + /* MSB first is more common */ + bool lsbFirst = false; + }; + + /** + * Can be used to explicitely disable hardware chip select. + * Some drivers like the Raspberry Pi Linux driver will not use hardware chip select by default + * (see https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md) + * @param enable + */ + void setNoCs(bool enable); + void setThreeWireSpi(bool enable); + void setLsbFirst(bool enable); + void setCsHigh(bool enable); + void setBitsPerWord(uint8_t bitsPerWord); + + void getSpiParameters(spi::SpiMode& spiMode, uint32_t& spiSpeed, + UncommonParameters* parameters = nullptr) const; + + /** + * See spidev.h cs_change and delay_usecs + * @param deselectCs + * @param delayUsecs + */ + void activateCsDeselect(bool deselectCs, uint16_t delayUsecs); + + spi_ioc_transfer* getTransferStructHandle(); +private: + size_t currentTransferSize = 0; + + address_t spiAddress; + gpioId_t chipSelectPin; + std::string spiDevice; + + const size_t maxSize; + spi::SpiMode spiMode; + uint32_t spiSpeed; + bool halfDuplex = false; + + struct spi_ioc_transfer spiTransferStruct; + UncommonParameters uncommonParameters; +}; + + + +#endif /* LINUX_SPI_SPICOOKIE_H_ */ diff --git a/linux/spi/spiDefinitions.h b/linux/spi/spiDefinitions.h new file mode 100644 index 00000000..9c278a70 --- /dev/null +++ b/linux/spi/spiDefinitions.h @@ -0,0 +1,15 @@ +#ifndef LINUX_SPI_SPIDEFINITONS_H_ +#define LINUX_SPI_SPIDEFINITONS_H_ + +namespace spi { + +enum SpiMode { + MODE_0, + MODE_1, + MODE_2, + MODE_3 +}; + +} + +#endif /* LINUX_SPI_SPIDEFINITONS_H_ */ diff --git a/linux/utility/errorhandling.h b/linux/utility/errorhandling.h new file mode 100644 index 00000000..1b4bc38b --- /dev/null +++ b/linux/utility/errorhandling.h @@ -0,0 +1,30 @@ +#ifndef LINUX_UTILITY_ERRORHANDLING_H_ +#define LINUX_UTILITY_ERRORHANDLING_H_ + +#include +#include +#include + +namespace utility { + +void handleIoctlError(const char* const customPrintout) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + if(customPrintout != nullptr) { + sif::warning << customPrintout << std::endl; + } + sif::warning << "handleIoctlError: Error code " << errno << ", "<< strerror(errno) << + std::endl; +#else + if(customPrintout != nullptr) { + sif::printWarning("%s\n", customPrintout); + } + sif::printWarning("handleIoctlError: Error code %d, %s\n", errno, strerror(errno)); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + +} + +} + +#endif /* LINUX_UTILITY_ERRORHANDLING_H_ */