Linux: Add support for SPIDEV (#734)

RPi and Linux refactor.

Add GPIO Sysfs for GPIO operations.

Update configure script.

Fix some cppcheck warnings.

Add serial emulation that prints to stdout.

Fix some file headers.
This commit is contained in:
Marcelo Aquino
2017-01-14 23:50:21 +01:00
committed by Henrik Ekblad
parent aa5cecfca1
commit 2e0f6b6229
43 changed files with 1574 additions and 593 deletions
+289
View File
@@ -0,0 +1,289 @@
/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2017 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* Based on TMRh20 RF24 library, Copyright (c) 2015 Charles-Henri Hallard <tmrh20@gmail.com>
*/
#include "SPI.h"
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "log.h"
static pthread_mutex_t spiMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutexattr_t attr;
// Declare a single default instance
SPIClass SPI = SPIClass();
uint8_t SPIClass::initialized = 0;
int SPIClass::fd = -1;
std::string SPIClass::device = SPI_SPIDEV_DEVICE;
uint32_t SPIClass::speed = SPI_CLOCK_BASE;
uint32_t SPIClass::speed_temp = SPI_CLOCK_BASE;
struct spi_ioc_transfer SPIClass::tr = {0,0,0,0,0,8,1,0,0,0}; // 8 bits_per_word, 1 cs_change
SPIClass::SPIClass()
{
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&spiMutex, &attr);
}
void SPIClass::begin(int busNo)
{
if (!initialized) {
/* set spidev accordingly to busNo like:
* busNo = 23 -> /dev/spidev2.3
*
* a bit messy but simple
* */
device[11] += (busNo / 10) % 10;
device[13] += busNo % 10;
init();
}
initialized++; // reference count
}
void SPIClass::end()
{
if (initialized) {
initialized--;
}
if (!initialized) {
if (!(fd < 0)) {
close(fd);
fd = -1;
}
}
}
void SPIClass::setBitOrder(uint8_t bit_order)
{
pthread_mutex_lock(&spiMutex);
/*
* bit order
*/
int lsb_setting = bit_order;
int ret = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &lsb_setting);
if (ret == -1) {
logError("Can't set SPI bit order.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}
void SPIClass::setDataMode(uint8_t data_mode)
{
pthread_mutex_lock(&spiMutex);
/*
* spi mode
*/
int ret = ioctl(fd, SPI_IOC_WR_MODE, &data_mode);
if (ret == -1) {
logError("Can't set SPI mode.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}
void SPIClass::setClockDivider(uint16_t divider)
{
pthread_mutex_lock(&spiMutex);
/*
* max speed hz
*/
speed = SPI_CLOCK_BASE / divider;
int ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1) {
logError("Can't set SPI max speed hz.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}
void SPIClass::chipSelect(int csn_chip)
{
if (csn_chip >= 0 && csn_chip <= 9) {
device[13] = '0' + (csn_chip % 10);
init();
}
}
uint8_t SPIClass::transfer(uint8_t data)
{
int ret;
uint8_t tx[1] = {data};
uint8_t rx[1] = {0};
pthread_mutex_lock(&spiMutex);
tr.tx_buf = (unsigned long)&tx[0];
tr.rx_buf = (unsigned long)&rx[0];
tr.len = 1;
tr.speed_hz = speed;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
logError("Can't send spi message.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
return rx[0];
}
void SPIClass::transfernb(char* tbuf, char* rbuf, uint32_t len)
{
int ret;
pthread_mutex_lock(&spiMutex);
tr.tx_buf = (unsigned long)tbuf;
tr.rx_buf = (unsigned long)rbuf;
tr.len = len;
tr.speed_hz = speed;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
logError("Can't send spi message.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}
void SPIClass::transfern(char* buf, uint32_t len)
{
transfernb(buf, buf, len);
}
void SPIClass::beginTransaction(SPISettings settings)
{
int ret;
pthread_mutex_lock(&spiMutex);
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &settings.dmode);
if (ret == -1) {
logError("Can't set spi mode.\n");
abort();
}
// Save the current speed
speed_temp = speed;
speed = settings.clock;
/*
* bit order
*/
ret = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &settings.border);
if (ret == -1) {
logError("Can't set bits per word.\n");
abort();
}
}
void SPIClass::endTransaction()
{
speed = speed_temp;
pthread_mutex_unlock(&spiMutex);
}
void SPIClass::usingInterrupt(uint8_t interruptNumber)
{
(void)interruptNumber;
}
void SPIClass::notUsingInterrupt(uint8_t interruptNumber)
{
(void)interruptNumber;
}
void SPIClass::init()
{
pthread_mutex_lock(&spiMutex);
if (fd >= 0) {
close(fd);
}
fd = open(device.c_str(), O_RDWR);
if (fd < 0) {
logError("Can't open SPI device: %s\n", device.c_str());
abort();
}
/*
* spi mode
*/
uint8_t mode = SPI_MODE0;
int ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1) {
logError("Can't set SPI mode.\n");
abort();
}
/*
* bits per word
*/
uint8_t bits = 0; // 0 corresponds to 8 bits per word
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1) {
logError("Can't set SPI bits per word.\n");
abort();
}
/*
* max speed hz
*/
speed = SPI_CLOCK_BASE;
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1) {
logError("Can't set SPI max speed hz.\n");
abort();
}
/*
* bit order
*/
int lsb_setting = MSBFIRST;
ret = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &lsb_setting);
if (ret == -1) {
logError("Can't set SPI bit order.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}
+206
View File
@@ -0,0 +1,206 @@
/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2017 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* Based on TMRh20 RF24 library, Copyright (c) 2015 Charles-Henri Hallard <tmrh20@gmail.com>
*/
#ifndef SPI_H
#define SPI_H
#include <stdint.h>
#include <string>
#include <linux/spi/spidev.h>
#define SPI_HAS_TRANSACTION
#define MSBFIRST 0
#define LSBFIRST SPI_LSB_FIRST
#define SPI_CLOCK_BASE 16000000 // 16Mhz
#define SPI_CLOCK_DIV2 2
#define SPI_CLOCK_DIV4 4
#define SPI_CLOCK_DIV8 8
#define SPI_CLOCK_DIV16 16
#define SPI_CLOCK_DIV32 32
#define SPI_CLOCK_DIV64 64
#define SPI_CLOCK_DIV128 128
// SPI Data mode
#define SPI_MODE0 SPI_MODE_0
#define SPI_MODE1 SPI_MODE_1
#define SPI_MODE2 SPI_MODE_2
#define SPI_MODE3 SPI_MODE_3
#ifndef SPI_SPIDEV_DEVICE
#define SPI_SPIDEV_DEVICE "/dev/spidev0.0"
#endif
// Default to Raspberry Pi
const uint8_t SS = 24;
const uint8_t MOSI = 19;
const uint8_t MISO = 21;
const uint8_t SCK = 23;
/**
* SPISettings class
*/
class SPISettings
{
public:
/**
* @brief SPISettings constructor.
*/
SPISettings()
{
init(SPI_CLOCK_BASE, MSBFIRST, SPI_MODE0);
}
/**
* @brief SPISettings constructor.
*
* @param clock SPI clock speed in Hz.
* @param bitOrder SPI bit order.
* @param dataMode SPI data mode.
*/
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
{
init(clock, bitOrder, dataMode);
}
uint32_t clock; //!< @brief SPI clock.
uint8_t border; //!< @brief SPI bit order.
uint8_t dmode; //!< @brief SPI data mode.
private:
/**
* @brief Initialized class members.
*
* @param clk SPI clock.
* @param bitOrder SPI bit order.
* @param dataMode SPI data mode.
*/
void init(uint32_t clk, uint8_t bitOrder, uint8_t dataMode)
{
clock = clk;
border = bitOrder;
dmode = dataMode;
}
friend class SPIClass;
};
class SPIClass
{
public:
/**
* @brief SPIClass constructor.
*/
SPIClass();
/**
* @brief Start SPI operations.
*/
static void begin(int busNo=0);
/**
* @brief End SPI operations.
*/
static void end();
/**
* @brief Sets the SPI bit order.
*
* @param bit_order The desired bit order.
*/
static void setBitOrder(uint8_t bit_order);
/**
* @brief Sets the SPI data mode.
*
* @param data_mode The desired data mode.
*/
static void setDataMode(uint8_t data_mode);
/**
* @brief Sets the SPI clock divider and therefore the SPI clock speed.
*
* @param divider The desired SPI clock divider.
*/
static void setClockDivider(uint16_t divider);
/**
* @brief Sets the chip select pin.
*
* @param csn_chip Specifies the CS chip.
*/
static void chipSelect(int csn_chip);
/**
* @brief Transfer a single byte
*
* @param data Byte to send
* @return Data returned via spi
*/
static uint8_t transfer(uint8_t data);
/**
* @brief Transfer a buffer of data
*
* @param tbuf Transmit buffer
* @param rbuf Receive buffer
* @param len Length of the data
*/
static void transfernb(char* tbuf, char* rbuf, uint32_t len);
/**
* @briefTransfer a buffer of data without an rx buffer
*
* @param buf Pointer to a buffer of data
* @param len Length of the data
*/
static void transfern(char* buf, uint32_t len);
/**
* @brief Start SPI transaction.
*
* @param settings for SPI.
*/
static void beginTransaction(SPISettings settings);
/**
* @brief End SPI transaction.
*/
static void endTransaction();
/**
* @brief Not implemented.
*
* @param interruptNumber ignored parameter.
*/
static void usingInterrupt(uint8_t interruptNumber);
/**
* @brief Not implemented.
*
* @param interruptNumber ignored parameter.
*/
static void notUsingInterrupt(uint8_t interruptNumber);
private:
static uint8_t initialized; //!< @brief SPI initialized flag.
static int fd; //!< @brief SPI device file descriptor.
static std::string device; //!< @brief Default SPI device.
static uint32_t speed; //!< @brief SPI speed.
static uint32_t speed_temp; //!< @brief Used when doing transaction.
static struct spi_ioc_transfer tr; //!< @brief Auxiliar struct for data transfer.
static void init();
};
extern SPIClass SPI;
#endif