/** @file ConnectionSPI.cpp @author Lime Microsystems (www.limemicro.com) @brief Implementation of communications through SPI port */ #include "ConnectionSPI.h" #include "string.h" #ifdef __unix__ #include <fstream> #include <errno.h> #include <unistd.h> #include <termios.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <sys/ioctl.h> #include <linux/types.h> #include <linux/spi/spidev.h> #include <linux/i2c-dev.h> #endif // LINUX #include <iostream> #include <sstream> const int ConnectionSPI::cSPI_BUF_SIZE = 128; const int ConnectionSPI::cSPI_SPEED_HZ = 2000000; /** @brief Tries to read EEPROM for Novena board signature @return true if Novena board */ bool IsNovenaBoard() { #ifdef __unix__ char data[8]; int count = 6; memset(data, 0, 8); int addr = 0; struct i2c_rdwr_ioctl_data session; struct i2c_msg messages[2]; char set_addr_buf[2]; memset(set_addr_buf, 0, sizeof(set_addr_buf)); memset(data, 0, count); set_addr_buf[0] = addr>>8; set_addr_buf[1] = addr; messages[0].addr = 0xac>>1; messages[0].flags = 0; messages[0].len = sizeof(set_addr_buf); messages[0].buf = set_addr_buf; messages[1].addr = 0xac>>1; messages[1].flags = I2C_M_RD; messages[1].len = count; messages[1].buf = data; session.msgs = messages; session.nmsgs = 2; bool isNovena = false; int fd = open("/dev/i2c-2", O_RDWR); if(fd > 0) { if(ioctl(fd, I2C_RDWR, &session) < 0) { perror("Unable to communicate with i2c device"); isNovena = false; } if(strcmp("Novena", data) == 0) isNovena = true; } close(fd); return isNovena; #else return false; #endif } /** @brief Initializes SPI port and exports needed GPIO */ ConnectionSPI::ConnectionSPI() { fd = -1; m_connectionType = SPI_PORT; std::fstream gpio; //export SEN pin gpio.open("/sys/class/gpio/export", ios::out); if(gpio.good()) { gpio << 122; gpio.flush(); gpio.close(); gpio.open("/sys/class/gpio/gpio122/direction", ios::out); gpio << "out"; gpio.flush(); gpio.close(); m_SEN.open("/sys/class/gpio/gpio122/value", ios::out); m_SEN<< 1; m_SEN.flush(); cout << "GPIO122: set to 1" << endl; } } ConnectionSPI::~ConnectionSPI() { Close(); m_SEN.close(); } /** @brief Opens connection to first found chip @return 0-success */ IConnection::DeviceStatus ConnectionSPI::Open() { Close(); #ifdef __unix__ fd = open("/dev/spidev2.0", O_RDWR | O_SYNC); if (fd < 0) { //MessageLog::getInstance()->write("SPI PORT: device not found\n", LOG_ERROR); return IConnection::FAILURE; } int mode = SPI_MODE_0; int ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); if (ret == -1) printf("can't set spi mode"); ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); if (ret == -1) printf("can't get spi mode"); /* * bits per word */ int bits = 8; ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); if (ret == -1) printf("can't set bits per word"); ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); if (ret == -1) printf("can't get bits per word"); /* * max speed hz */ int speed = cSPI_SPEED_HZ; ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); if (ret == -1) printf("can't set max speed hz"); ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); if (ret == -1) printf("can't get max speed hz"); printf("spi mode: 0x%x\n", mode); printf("bits per word: %d\n", bits); printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); return IConnection::SUCCESS; #else return IConnection::FAILURE; #endif } /** @brief Opens connection to selected chip @param index chip index in device list @return 0-success */ IConnection::DeviceStatus ConnectionSPI::Open(unsigned index) { return Open(); } /** @brief Closes connection to chip */ void ConnectionSPI::Close() { #ifdef __unix__ close(fd); fd = -1; #endif } /** @brief Returns whether chip is connected @return chip is connected */ bool ConnectionSPI::IsOpen() { return (fd >= 0); } /** @brief Sends data through SPI port @param buffer data buffer to send @param length size of data buffer @param timeout_ms timeout limit for operation in milliseconds @return Number of bytes sent */ int ConnectionSPI::Write(const unsigned char *buffer, int length, int timeout_ms) { #ifdef __unix__ if(fd < 0) return 0; m_SEN << 0; m_SEN.flush(); unsigned long bytesWritten = 0; rxbuf.clear(); int bytesReceived = 0; char rxbytes[2]; while(bytesWritten < length) { int toWrite = length-bytesWritten > cSPI_BUF_SIZE ? cSPI_BUF_SIZE : length-bytesWritten; for(int i=0; i<toWrite; i+=2) { if(buffer[i] < 0x80) //reading { write(fd, &buffer[i+bytesWritten], 2); bytesReceived += read(fd, rxbytes, 2); rxbuf.push_back(rxbytes[0]); rxbuf.push_back(rxbytes[1]); } else //writing { write(fd, &buffer[i+bytesWritten], 4); i+=2; //data bytes have been written } } bytesWritten += toWrite; } m_SEN << 1; m_SEN.flush(); return bytesWritten; #else return 0; #endif } /** @brief Reads data from SPI port @param buffer pointer to data buffer for receiving @param length number of bytes to read @param timeout_ms timeout limit for operation in milliseconds @return Number of bytes received */ int ConnectionSPI::Read(unsigned char *buffer, int length, int timeout_ms) { #ifdef __unix__ if(fd < 0) return 0; //because transfer is done in full duplex, function returns data from last transfer int tocpy = length > rxbuf.size() ? rxbuf.size() : length; memcpy(buffer, &rxbuf[0], tocpy); rxbuf.clear(); return tocpy; #else return 0; #endif } /** @brief Finds SPI port @return number of devices found */ int ConnectionSPI::RefreshDeviceList() { m_deviceNames.clear(); #ifdef __unix__ int spidev = open("/dev/spidev2.0", O_RDWR); if (spidev >= 0) { if(IsNovenaBoard() == true) m_deviceNames.push_back("SPI (Novena)"); else m_deviceNames.push_back("SPI"); } close(spidev); #endif return m_deviceNames.size(); } /** @brief Returns found devices names @return vector of device names */ vector<string> ConnectionSPI::GetDeviceNames() { return m_deviceNames; } int ConnectionSPI::GetOpenedIndex() { return 0; }