220 lines
5.5 KiB
C++
220 lines
5.5 KiB
C++
/*
|
|
PIP - Platform Independent Primitives
|
|
SPI
|
|
Andrey Bychkov work.a.b@yandex.ru
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "pispi.h"
|
|
|
|
#include "piincludes_p.h"
|
|
#include "pipropertystorage.h"
|
|
#ifdef LINUX
|
|
# define PIP_SPI
|
|
#endif
|
|
#ifdef PIP_SPI
|
|
# include <fcntl.h>
|
|
# include <linux/spi/spidev.h>
|
|
# include <sys/ioctl.h>
|
|
#endif
|
|
|
|
|
|
PRIVATE_DEFINITION_START(PISPI)
|
|
#ifdef PIP_SPI
|
|
int fd;
|
|
spi_ioc_transfer spi_ioc_tr;
|
|
#endif
|
|
PRIVATE_DEFINITION_END(PISPI)
|
|
|
|
|
|
REGISTER_DEVICE(PISPI)
|
|
|
|
|
|
PISPI::PISPI(const PIString & path, uint speed, PIIODevice::DeviceMode mode): PIIODevice(path, mode) {
|
|
#ifdef MICRO_PIP
|
|
setThreadedReadBufferSize(512);
|
|
#else
|
|
setThreadedReadBufferSize(1024);
|
|
#endif
|
|
setPath(path);
|
|
setSpeed(speed);
|
|
setBits(8);
|
|
spi_mode = 0;
|
|
if (mode == ReadOnly) piCoutObj << "error, SPI can't work in ReadOnly mode";
|
|
#ifdef PIP_SPI
|
|
PRIVATE->fd = 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
PISPI::~PISPI() {
|
|
stop();
|
|
close();
|
|
}
|
|
|
|
|
|
void PISPI::setSpeed(uint speed_hz) {
|
|
spi_speed = speed_hz;
|
|
}
|
|
|
|
|
|
void PISPI::setBits(uchar bits) {
|
|
spi_bits = bits;
|
|
}
|
|
|
|
|
|
void PISPI::setParameter(PISPI::Parameters parameter, bool on) {
|
|
PIFlags<Parameters> cp = (PIFlags<Parameters>)spi_mode;
|
|
cp.setFlag(parameter, on);
|
|
spi_mode = (int)cp;
|
|
}
|
|
|
|
|
|
bool PISPI::isParameterSet(PISPI::Parameters parameter) const {
|
|
PIFlags<Parameters> cp = (PIFlags<Parameters>)spi_mode;
|
|
return cp[parameter];
|
|
}
|
|
|
|
|
|
ssize_t PISPI::bytesAvailable() const {
|
|
return recv_buf.size();
|
|
}
|
|
|
|
|
|
bool PISPI::openDevice() {
|
|
#ifdef PIP_SPI
|
|
int ret = 0;
|
|
// piCoutObj << "open device" << path();
|
|
PRIVATE->fd = ::open(path().dataAscii(), O_RDWR);
|
|
if (PRIVATE->fd < 0) {
|
|
piCoutObj << "can't open device";
|
|
return false;
|
|
}
|
|
// piCoutObj << "set mode" << spi_mode;
|
|
ret = ioctl(PRIVATE->fd, SPI_IOC_WR_MODE, &spi_mode);
|
|
if (ret == -1) {
|
|
piCoutObj << "can't set spi write mode";
|
|
return false;
|
|
}
|
|
// piCoutObj << "set bits" << spi_bits;
|
|
ret = ioctl(PRIVATE->fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits);
|
|
if (ret == -1) {
|
|
piCoutObj << "can't set bits per word";
|
|
return false;
|
|
}
|
|
// piCoutObj << "set speed" << spi_speed;
|
|
ret = ioctl(PRIVATE->fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed);
|
|
if (ret == -1) {
|
|
piCoutObj << "can't set max write speed hz";
|
|
return false;
|
|
}
|
|
piCoutObj << "SPI open" << path() << "speed:" << spi_speed / 1000 << "KHz"
|
|
<< "mode" << spi_mode << "bits" << spi_bits;
|
|
PRIVATE->spi_ioc_tr.delay_usecs = 0;
|
|
PRIVATE->spi_ioc_tr.speed_hz = 0;
|
|
PRIVATE->spi_ioc_tr.bits_per_word = spi_bits;
|
|
return true;
|
|
#else
|
|
piCoutObj << "PISPI not implemented on windows";
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
|
|
bool PISPI::closeDevice() {
|
|
#ifdef PIP_SPI
|
|
if (PRIVATE->fd) ::close(PRIVATE->fd);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
|
|
ssize_t PISPI::readDevice(void * read_to, ssize_t max_size) {
|
|
ssize_t sz = piMini(recv_buf.size_s(), max_size);
|
|
memcpy(read_to, recv_buf.data(), sz);
|
|
recv_buf.remove(0, sz);
|
|
return sz;
|
|
}
|
|
|
|
|
|
ssize_t PISPI::writeDevice(const void * data, ssize_t max_size) {
|
|
#ifdef PIP_SPI
|
|
if (max_size > 0) {
|
|
if (tx_buf.size_s() != max_size) {
|
|
tx_buf.resize(max_size);
|
|
rx_buf.resize(max_size);
|
|
PRIVATE->spi_ioc_tr.tx_buf = (ulong)(tx_buf.data());
|
|
PRIVATE->spi_ioc_tr.rx_buf = (ulong)(rx_buf.data());
|
|
PRIVATE->spi_ioc_tr.len = max_size;
|
|
}
|
|
memcpy(tx_buf.data(), data, max_size);
|
|
int ret;
|
|
// piCoutObj << "write" << max_size << tx_buf.size();
|
|
ret = ioctl(PRIVATE->fd, SPI_IOC_MESSAGE(1), &PRIVATE->spi_ioc_tr);
|
|
if (ret < 1) {
|
|
piCoutObj << "can't send spi message" << ret;
|
|
return -1;
|
|
}
|
|
if (canRead()) recv_buf.append(rx_buf);
|
|
if (recv_buf.size_s() > threadedReadBufferSize()) recv_buf.resize(threadedReadBufferSize());
|
|
return max_size;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
PIString PISPI::constructFullPathDevice() const {
|
|
PIString ret;
|
|
ret += path() + ":" + PIString::fromNumber((int)speed()) + ":" + PIString::fromNumber((int)bits()) + ":" +
|
|
PIString::fromNumber((int)parameters());
|
|
return ret;
|
|
}
|
|
|
|
|
|
void PISPI::configureFromFullPathDevice(const PIString & full_path) {
|
|
PIStringList pl = full_path.split(":");
|
|
for (int i = 0; i < pl.size_s(); ++i) {
|
|
PIString p(pl[i]);
|
|
switch (i) {
|
|
case 0: setPath(p); break;
|
|
case 1: setSpeed(p.toInt()); break;
|
|
case 2: setBits(p.toInt()); break;
|
|
case 3: setParameters(p.toInt()); break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PIPropertyStorage PISPI::constructVariantDevice() const {
|
|
PIPropertyStorage ret;
|
|
ret.addProperty("path", path());
|
|
ret.addProperty("speed", int(speed()));
|
|
ret.addProperty("bits", int(bits()));
|
|
ret.addProperty("clock inverse", isParameterSet(ClockInverse));
|
|
ret.addProperty("clock phase shift", isParameterSet(ClockPhaseShift));
|
|
return ret;
|
|
}
|
|
|
|
|
|
void PISPI::configureFromVariantDevice(const PIPropertyStorage & d) {
|
|
setPath(d.propertyValueByName("path").toString());
|
|
setSpeed(d.propertyValueByName("speed").toInt());
|
|
setBits(d.propertyValueByName("bits").toInt());
|
|
setParameter(ClockInverse, d.propertyValueByName("clock inverse").toBool());
|
|
setParameter(ClockPhaseShift, d.propertyValueByName("clock phase shift").toBool());
|
|
}
|