Line data Source code
1 : /*! 2 : * @file SPIController.cpp 3 : * @brief Implementation of the SPIController class. 4 : * @version 0.1 5 : * @date 2025-01-31 6 : * @author Félix LE BIHAN (@Fle-bihh) 7 : * @author Tiago Pereira (@t-pereira06) 8 : * @author Ricardo Melo (@reomelo) 9 : * @author Michel Batista (@MicchelFAB) 10 : * 11 : * @details This file contains the implementation of the SPIController class, 12 : * which controls the SPI communication. 13 : * 14 : * @note This class is used to control the SPI communication for the MCP2515 CAN 15 : * controller. 16 : * 17 : * @warning Ensure that the SPI device is properly connected and configured on 18 : * your system. 19 : * 20 : * @see SPIController.hpp for the class definition. 21 : * 22 : * @copyright Copyright (c) 2025 23 : * 24 : */ 25 : 26 : #include "SPIController.hpp" 27 : #include <cstring> 28 : #include <fcntl.h> 29 : #include <linux/spi/spidev.h> 30 : #include <stdexcept> 31 : #include <sys/ioctl.h> 32 : #include <unistd.h> 33 : 34 : /*! 35 : * @brief Construct a new SPIController::SPIController object 36 : * 37 : * @param ioctlFunc 38 : * @param openFunc 39 : * @param closeFunc 40 : * @details This constructor initializes the SPIController object with the 41 : * specified functions. 42 : */ 43 7 : SPIController::SPIController(IoctlFunc ioctlFunc, OpenFunc openFunc, 44 7 : CloseFunc closeFunc) 45 : : spi_fd(-1), mode(DefaultMode), bits(DefaultBitsPerWord), 46 : speed(DefaultSpeedHz), m_ioctlFunc(ioctlFunc), m_openFunc(openFunc), 47 7 : m_closeFunc(closeFunc) {} 48 : 49 : /*! 50 : * @brief Destroy the SPIController::SPIController object 51 : * 52 : * @details This destructor closes the SPI device. 53 : */ 54 14 : SPIController::~SPIController() { closeDevice(); } 55 : 56 : /*! 57 : * @brief Open the SPI device. 58 : * 59 : * @param device The device to open. 60 : * @returns True if the device was opened successfully. 61 : * @throws std::runtime_error if the device cannot be opened. 62 : * @details This function opens the SPI device with the specified device name. 63 : */ 64 7 : bool SPIController::openDevice(const std::string &device) { 65 7 : spi_fd = m_openFunc(device.c_str(), O_RDWR); 66 7 : if (spi_fd < 0) { 67 1 : throw std::runtime_error("Failed to open SPI device"); 68 : } 69 6 : return true; 70 : } 71 : 72 : /*! 73 : * @brief Configure the SPI device. 74 : * 75 : * @param mode The SPI mode. 76 : * @param bits The number of bits per word. 77 : * @param speed The speed in Hz. 78 : * @throws std::runtime_error if the device is not open. 79 : * @throws std::runtime_error if the SPI mode cannot be set. 80 : * @throws std::runtime_error if the bits per word cannot be set. 81 : * @throws std::runtime_error if the speed cannot be set. 82 : * @details This function configures the SPI device with the specified mode, 83 : * bits per word, and speed. 84 : */ 85 1 : void SPIController::configure(uint8_t mode, uint8_t bits, uint32_t speed) { 86 1 : if (spi_fd < 0) { 87 0 : throw std::runtime_error("SPI device not open"); 88 : } 89 : 90 1 : this->mode = mode; 91 1 : this->bits = bits; 92 1 : this->speed = speed; 93 : 94 1 : if (m_ioctlFunc(spi_fd, SPI_IOC_WR_MODE, &mode) < 0) { 95 0 : throw std::runtime_error("Failed to set SPI mode"); 96 : } 97 : 98 1 : if (m_ioctlFunc(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) { 99 0 : throw std::runtime_error("Failed to set SPI bits per word"); 100 : } 101 : 102 1 : if (m_ioctlFunc(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) { 103 0 : throw std::runtime_error("Failed to set SPI speed"); 104 : } 105 1 : } 106 : 107 : /*! 108 : * @brief Write a byte to the SPI device. 109 : * 110 : * @param address The address to write to. 111 : * @param data The data to write. 112 : * @details This function writes a byte to the SPI device at the specified 113 : * address. 114 : */ 115 1 : void SPIController::writeByte(uint8_t address, uint8_t data) { 116 1 : uint8_t tx[] = {static_cast<uint8_t>(Opcode::Write), address, data}; 117 1 : spiTransfer(tx, nullptr, sizeof(tx)); 118 1 : } 119 : 120 : /*! 121 : * @brief Read a byte from the SPI device. 122 : * 123 : * @param address The address to read from. 124 : * @returns The byte read from the SPI device. 125 : * @details This function reads a byte from the SPI device at the specified 126 : * address. 127 : */ 128 1 : uint8_t SPIController::readByte(uint8_t address) { 129 1 : uint8_t tx[] = {static_cast<uint8_t>(Opcode::Read), address, 0x00}; 130 1 : uint8_t rx[sizeof(tx)] = {0}; 131 1 : spiTransfer(tx, rx, sizeof(tx)); 132 1 : return rx[2]; 133 : } 134 : 135 : /*! 136 : * @brief Transfer data over SPI. 137 : * 138 : * @param tx The data to transmit. 139 : * @param rx The data to receive. 140 : * @param length The length of the data. 141 : * @throws std::runtime_error if the SPI device is not open. 142 : * @throws std::runtime_error if the SPI transfer fails. 143 : * @details This function transfers data over SPI. 144 : */ 145 3 : void SPIController::spiTransfer(const uint8_t *tx, uint8_t *rx, size_t length) { 146 3 : if (spi_fd < 0) { 147 0 : throw std::runtime_error("SPI device not open"); 148 : } 149 : 150 3 : struct spi_ioc_transfer transfer = {}; 151 3 : transfer.tx_buf = reinterpret_cast<unsigned long>(tx); 152 3 : transfer.rx_buf = reinterpret_cast<unsigned long>(rx); 153 3 : transfer.len = length; 154 3 : transfer.speed_hz = speed; 155 3 : transfer.bits_per_word = bits; 156 : 157 3 : if (m_ioctlFunc(spi_fd, SPI_IOC_MESSAGE(1), &transfer) < 0) { 158 0 : throw std::runtime_error("SPI transfer error"); 159 : } 160 3 : } 161 : 162 : /*! 163 : * @brief Close the SPI device. 164 : * @details This function closes the SPI device. 165 : */ 166 8 : void SPIController::closeDevice() { 167 8 : if (spi_fd >= 0) { 168 6 : m_closeFunc(spi_fd); 169 6 : spi_fd = -1; 170 : } 171 8 : }