#include #ifdef STA_PLATFORM_RASPI #include #include #include #include #include #include #include // Imports needed for SPI handling on Linux. #include #include #include #include #define STA_DEBUG_IOCTL_WRITE(result) STA_ASSERT_MSG(result >= 0, "ioctl writing failed!") #define STA_DEBUG_IOCTL_READ(result) STA_ASSERT_MSG(result >= 0, "ioctl reading failed!") #define STA_DEBUG_IOCTL_SEND(result) STA_ASSERT_MSG(result >= 0, "ioctl sending spi message failed!") #define STA_DEBUG_FD_OPEN() STA_ASSERT_MSG(open_, "File descriptor wasn't opened!") #define STA_DEBUG_NULLPTR(ptr) STA_ASSERT_MSG(ptr != nullptr, "Pointer is nullptr!") namespace sta { RaspiSPI::RaspiSPI(SPINode node, const SPISettings & settings, Mutex * mutex, bool persistent_open = true) : SPI(settings, mutex), open_{ false }, persistent_open_{ persistent_open } { // Check validity of parameters. STA_DEBUG_NULLPTR(mutex); open_ = false; // Safer version of malloc + strcpy spidev_ = strdup(node == SPINode::DEV_0_0 ? "/dev/spidev0.0" : "/dev/spidev0.1"); // Check if memory allocation was successful. STA_DEBUG_NULLPTR(spidev_); } RaspiSPI::~RaspiSPI() { if (spidev_ != NULL ) { free(spidev_); spidev_ = NULL; } if (open_) { close(spifd_); } } void RaspiSPI::transfer(uint8_t value) { STA_DEBUG_FD_OPEN(); struct spi_ioc_transfer spi_message[1]; memset(spi_message, 0, sizeof(spi_message)); spi_message[0].tx_buf = (unsigned long)&value; spi_message[0].len = 1; int result = ioctl(spifd_, SPI_IOC_MESSAGE(1), spi_message); STA_DEBUG_IOCTL_SEND(result); } void RaspiSPI::transfer16(uint16_t value) { STA_DEBUG_FD_OPEN(); struct spi_ioc_transfer spi_message[1]; memset(spi_message, 0, sizeof(spi_message)); spi_message[0].tx_buf = (unsigned long)&value; spi_message[0].len = 1; int result = ioctl(spifd_, SPI_IOC_MESSAGE(1), spi_message); STA_DEBUG_IOCTL_SEND(result); } void RaspiSPI::transfer(const uint8_t * buffer, size_t size) { STA_DEBUG_FD_OPEN(); STA_DEBUG_NULLPTR(buffer); STA_ASSERT_MSG(size != 0, "Buffer size cannot be 0!"); struct spi_ioc_transfer spi_message[1]; memset(spi_message, 0, sizeof(spi_message)); spi_message[0].tx_buf = (unsigned long)buffer; spi_message[0].len = size; int result = ioctl(spifd_, SPI_IOC_MESSAGE(1), spi_message); STA_DEBUG_IOCTL_SEND(result); } void RaspiSPI::transfer(const uint8_t * txBuffer, uint8_t * rxBuffer, size_t size) { STA_DEBUG_FD_OPEN(); STA_DEBUG_NULLPTR(txBuffer); STA_DEBUG_NULLPTR(rxBuffer); STA_ASSERT_MSG(size != 0, "Buffer size cannot be 0!"); struct spi_ioc_transfer spi_message[1]; memset(spi_message, 0, sizeof(spi_message)); spi_message[0].rx_buf = (unsigned long)rxBuffer; spi_message[0].tx_buf = (unsigned long)txBuffer; spi_message[0].len = size; int result = ioctl(spifd_, SPI_IOC_MESSAGE(1), spi_message); STA_DEBUG_IOCTL_SEND(result); } void RaspiSPI::receive(uint8_t * buffer, size_t size) { STA_DEBUG_FD_OPEN(); STA_DEBUG_NULLPTR(buffer); STA_ASSERT_MSG(size != 0, "Buffer size cannot be 0!"); struct spi_ioc_transfer spi_message[1]; memset(spi_message, 0, sizeof(spi_message)); spi_message[0].rx_buf = (unsigned long)buffer; spi_message[0].len = size; int result = ioctl(spifd_, SPI_IOC_MESSAGE(1), spi_message); STA_DEBUG_IOCTL_SEND(result); } void RaspiSPI::fill(uint8_t value, size_t count) { STA_DEBUG_FD_OPEN(); STA_ASSERT_MSG(count != 0, "Buffer size cannot be 0!"); uint8_t * buffer = new uint8_t[count]; memset(buffer, value, count); struct spi_ioc_transfer spi_message[1]; memset(spi_message, 0, sizeof(spi_message)); spi_message[0].tx_buf = (unsigned long)buffer; spi_message[0].len = count; int result = ioctl(spifd_, SPI_IOC_MESSAGE(1), spi_message); STA_DEBUG_IOCTL_SEND(result); delete[] buffer; } void RaspiSPI::update_setting(int setting, int value) { STA_DEBUG_FD_OPEN(); int result = ioctl(spifd_, setting, value); STA_DEBUG_IOCTL_WRITE(result); } int RaspiSPI::get_setting(int setting, void * rslt_ptr) { STA_DEBUG_FD_OPEN(); int result = ioctl(spifd_, setting, rslt_ptr); STA_DEBUG_IOCTL_READ(result); } void RaspiSPI::update_settings() { SPISettings settings = this->settings(); int mode; switch (settings.mode) { case SPIMode::MODE_0: mode = SPI_MODE_0; break; case SPIMode::MODE_1: mode = SPI_MODE_1; break; case SPIMode::MODE_2: mode = SPI_MODE_2; break; case SPIMode::MODE_3: mode = SPI_MODE_3; break; default: STA_ASSERT_MSG(false, "Case for SPIMode enum not handled"); STA_UNREACHABLE(); } // Set the spi mode. update_setting(SPI_IOC_WR_MODE, mode); // Set the word size. According to the documentation "the value zero signifies eight bits". update_setting(SPI_IOC_WR_BITS_PER_WORD, settings.dataSize == SPIDataSize::SIZE_8 ? 0 : 16); // Set the bit order. According to the documentation zero means MSB first, everything else means LSB first. update_setting(SPI_IOC_WR_LSB_FIRST, settings.bitOrder == SPIBitOrder::MSB ? 0 : 1); // Set the maximum clock speed. update_setting(SPI_IOC_WR_MAX_SPEED_HZ, settings.clkSpeed); #if defined(DEBUG) uint8_t r_mode = 0; get_setting(SPI_IOC_RD_MODE, &r_mode); STA_ASSERT_MSG(r_mode == mode, "The mode wasn't set correctly!"); uint8_t r_dataSize = 0; get_setting(SPI_IOC_WR_BITS_PER_WORD, &r_dataSize); STA_ASSERT_MSG(r_dataSize == (settings.dataSize == SPIDataSize::SIZE_8 ? 0 : 16), "The data size wasn't set correctly!"); uint8_t r_bitOrder = 0; get_setting(SPI_IOC_RD_LSB_FIRST, &r_bitOrder); STA_ASSERT_MSG(r_bitOrder == (settings.bitOrder == SPIBitOrder::MSB ? 0 : 1), "The data size wasn't set correctly!"); uint32_t r_clk_Speed; get_setting(SPI_IOC_RD_MAX_SPEED_HZ, &r_clk_Speed); STA_ASSERT_MSG(r_clk_Speed == settings.clkSpeed, "The clock speed wasn't set correctly!"); #endif // STA_DEBUG } void RaspiSPI::acquire() { SPI::acquire(); STA_ASSERT(spidev_ != nullptr); /* open spidev device */ if (open_ == true) return; spifd_ = open(spidev_, O_RDWR); STA_ASSERT(spifd_ >= 0); open_ = true; update_settings(); } void RaspiSPI::release() { if (!persistent_open_) { close(spifd_); } SPI::release(); } RaspiSPIDevice::RaspiSPIDevice(RaspiSPI * intf) : SPIDevice { intf, RaspiGpioPin::DUMMY_GPIO } { } } // namespace sta #endif // STA_PlATFORM_RASPI