Compare commits

...

1 Commits

Author SHA1 Message Date
Michael Ring
3184965aad spi: Add support for soft SPI
Signed-off-by: Michael Ring <mail@michael-ring.org>
Signed-off-by: Brendan Le Foll <brendan.le.foll@intel.com>
2015-04-04 17:35:45 +01:00
3 changed files with 241 additions and 42 deletions

View File

@@ -61,6 +61,13 @@ typedef enum {
output data (change) on falling edge */
} mraa_spi_mode_t;
typedef enum {
MRAA_SPI_SOFT_8BIS = 8, /**< SoftSPI handles CS pin in chunks of 8 Bits */
MRAA_SPI_SOFT_9BIS = 9, /**< SoftSPI handles CS pin in chunks of 9 Bits (if spidev associated
allows 9 bit transfer) */
MRAA_SPI_SOFT_16BIS = 16, /**< SoftSPI handles CS pin in chunks of 16 bits */
} mraa_spi_soft_bits_t;
/**
* Opaque pointer definition to the internal struct _spi
*/
@@ -101,6 +108,31 @@ mraa_result_t mraa_spi_mode(mraa_spi_context dev, mraa_spi_mode_t mode);
*/
mraa_result_t mraa_spi_frequency(mraa_spi_context dev, int hz);
/**
* Disables the hardware chip select on the platform that is toggled by spidev
* effectively giving you a handle on the raw spi bus
*
* @param dev The Spi context
* @param hwcs Disable/Enable hardware chip select
* @return Result of operation
*/
mraa_result_t mraa_spi_hw_cs(mraa_spi_context dev, mraa_boolean_t hwcs);
/**
* Disables the hardware chip select on the platform that is toggled by spidev
* and replaces it with a software based chip select managed by mraa to
* overcome limitations of the implementation on some platforms supporting only
* 8 or 9 bit transfers do not use this on platforms that have native support
* for your required bitlength as spi performance will suffer using soft chip
* select
*
* @param dev The Spi context
* @param number of bits per chip select. Only multiples of existing number of bits are supported
* @param replacement cs pin (can be any valid mraa Gpio)
* @return Result of operation
*/
mraa_result_t mraa_spi_sw_cs(mraa_spi_context dev, mraa_spi_soft_bits_t softbits, int pin);
/**
* Write Single Byte to the SPI device.
*
@@ -117,7 +149,7 @@ int mraa_spi_write(mraa_spi_context dev, uint8_t data);
* @param data Data to send
* @return Data received on the miso line
*/
uint16_t mraa_spi_write_word(mraa_spi_context dev, uint16_t data);
long mraa_spi_write_word(mraa_spi_context dev, uint16_t data);
/**
* Write Buffer of bytes to the SPI device. The pointer return has to be

View File

@@ -44,14 +44,20 @@ typedef enum {
output data (change) on falling edge */
} Spi_Mode;
typedef enum {
MRAA_SPI_SOFT_8BIS = 8, //** SoftSPI handles CS pin in chunks of 8 Bits */
MRAA_SPI_SOFT_9BIS = 9, //** SoftSPI handles CS pin in chunks of 9 Bits (if spidev associated
//allows 9 bit transfer) */
MRAA_SPI_SOFT_16BIS = 16, //** SoftSPI handles CS pin in chunks of 16 bits */
} Spi_Soft_Bits;
/**
* @brief API to Serial Peripheral Interface
*
* This file defines the SPI interface for libmraa
*
* @snippet Spi-pot.cpp Interesting
*/
* @brief API to Serial Peripheral Interface
*
* This file defines the SPI interface for libmraa
*
* @snippet Spi-pot.cpp Interesting
*/
class Spi
{
public:
@@ -199,6 +205,38 @@ class Spi
return mraa_spi_lsbmode(m_spi, (mraa_boolean_t) lsb);
}
/**
* Disables the hardware chip select on the platform that is toggled by spidev
* effectively giving you a handle on the raw spi bus
*
* @param hwcs Disable/Enable hardware chip select
* @return Result of operation
*/
mraa_result_t
hwcs(bool hwcs)
{
return mraa_spi_hw_cs(m_spi, (mraa_boolean_t) hwcs);
}
/**
* Disables the hardware chip select on the platform that is toggled by spidev
* and replaces it with a software based chip select managed by mraa to
* overcome limitations of the implementation on some platforms supporting only
* 8 or 9 bit transfers do not use this on platforms that have native support
* for your required bitlength as spi performance will suffer using soft chip
* select
*
* @param number of bits per chip select. Only multiples of existing number of bits are
*supported
* @param replacement cs pin (can be any valid mraa Gpio)
* @return Result of operation
*/
mraa_result_t
swcs(Spi_Soft_Bits softbits, int pin)
{
return mraa_spi_sw_cs(m_spi, (mraa_spi_soft_bits_t) softbits, pin);
}
/**
* Set bits per mode on transaction, default is 8
*

View File

@@ -41,11 +41,14 @@
*/
struct _spi {
/*@{*/
int devfd; /**< File descriptor to SPI Device */
uint32_t mode; /**< Spi mode see spidev.h */
int clock; /**< clock to run transactions at */
mraa_boolean_t lsb; /**< least significant bit mode */
unsigned int bpw; /**< Bits per word */
int devfd; /**< File descriptor to SPI Device */
uint32_t mode; /**< Spi mode see spidev.h */
int clock; /**< clock to run transactions at */
mraa_boolean_t lsb; /**< least significant bit mode */
mraa_boolean_t hwcs; /**< Hardware CS */
mraa_gpio_context softcspin_context; /**< Soft CS pin */
unsigned int bpw; /**< Bits per word */
unsigned int softbpw; /**< Bits per word in softspi */
/*@}*/
};
@@ -113,6 +116,7 @@ mraa_spi_init(int bus)
}
}
dev->softbpw = 0;
return dev;
}
@@ -143,6 +147,8 @@ mraa_spi_init_raw(unsigned int bus, unsigned int cs)
dev->clock = 4000000;
}
// default is to enable HW cs
dev->hwcs = 1;
if (mraa_spi_mode(dev, MRAA_SPI_MODE0) != MRAA_SUCCESS) {
free(dev);
return NULL;
@@ -183,6 +189,10 @@ mraa_spi_mode(mraa_spi_context dev, mraa_spi_mode_t mode)
break;
}
if (!dev->hwcs) {
spi_mode |= SPI_NO_CS;
}
if (ioctl(dev->devfd, SPI_IOC_WR_MODE, &spi_mode) < 0) {
syslog(LOG_ERR, "spi: Failed to set spi mode");
return MRAA_ERROR_INVALID_RESOURCE;
@@ -192,6 +202,66 @@ mraa_spi_mode(mraa_spi_context dev, mraa_spi_mode_t mode)
return MRAA_SUCCESS;
}
mraa_result_t
mraa_spi_hw_cs(mraa_spi_context dev, mraa_boolean_t hwcs)
{
dev->hwcs = hwcs;
if (!dev->hwcs) {
syslog(LOG_DEBUG, "spi: Disabled HW CS");
} else {
dev->softbpw = 0;
if (dev->softcspin_context != NULL) {
mraa_gpio_close(dev->softcspin_context);
}
}
return mraa_spi_mode(dev, dev->mode);
}
mraa_result_t
mraa_spi_sw_cs(mraa_spi_context dev, mraa_spi_soft_bits_t softbits, int pin)
{
mraa_result_t result;
switch (softbits) {
case MRAA_SPI_SOFT_8BIS:
result = mraa_spi_bit_per_word(dev, 8);
break;
case MRAA_SPI_SOFT_9BIS:
result = mraa_spi_bit_per_word(dev, 9);
break;
case MRAA_SPI_SOFT_16BIS:
result = mraa_spi_bit_per_word(dev, 8);
break;
default:
result = mraa_spi_bit_per_word(dev, 8);
}
if (result != MRAA_SUCCESS) {
syslog(LOG_ERR, "spi: Could not initialize 8 transfer for soft cs");
return result;
}
dev->softbpw = softbits;
// disable HW CS
result = mraa_spi_hw_cs(dev, 0);
if (result != MRAA_SUCCESS) {
return result;
}
dev->softcspin_context = mraa_gpio_init(pin);
if (dev->softcspin_context == NULL) {
syslog(LOG_ERR, "spi: Could not initialize soft cs pin %d", pin);
return MRAA_ERROR_INVALID_RESOURCE;
}
// try to enable mmap gpio access to speed up CS
if (mraa_pin_mode_test(pin, MRAA_PIN_FAST_GPIO)) {
if (mraa_gpio_use_mmaped(dev->softcspin_context, 1) != MRAA_SUCCESS) {
syslog(LOG_INFO, "spi: could do initialise mmap for soft cs pin");
}
}
return mraa_gpio_dir(dev->softcspin_context, MRAA_GPIO_OUT_HIGH);
}
mraa_result_t
mraa_spi_frequency(mraa_spi_context dev, int hz)
{
@@ -200,7 +270,13 @@ mraa_spi_frequency(mraa_spi_context dev, int hz)
if (ioctl(dev->devfd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) != -1) {
if (speed < hz) {
dev->clock = speed;
syslog(LOG_WARNING, "spi: Selected speed reduced to max allowed speed");
syslog(LOG_WARNING, "spi: Selected speed reduced to max allowed read speed of %d", speed);
}
}
if (ioctl(dev->devfd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) != -1) {
if (speed < dev->clock) {
dev->clock = speed;
syslog(LOG_WARNING, "spi: Selected speed reduced to max allowed write speed of %d", speed);
}
}
return MRAA_SUCCESS;
@@ -229,12 +305,23 @@ mraa_spi_bit_per_word(mraa_spi_context dev, unsigned int bits)
syslog(LOG_ERR, "spi: Failed to set bit per word");
return MRAA_ERROR_INVALID_RESOURCE;
}
if (ioctl(dev->devfd, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0) {
syslog(LOG_ERR, "spi: Failed to set bit per word");
return MRAA_ERROR_INVALID_RESOURCE;
}
dev->bpw = bits;
if (dev->softbpw > 0) {
syslog(LOG_NOTICE, "spi: turned off soft-cs");
if (dev->softcspin_context != NULL) {
mraa_gpio_close(dev->softcspin_context);
dev->softbpw = 0;
}
}
return MRAA_SUCCESS;
}
int
mraa_spi_write(mraa_spi_context dev, uint8_t data)
mraa_spi_write_internal(mraa_spi_context dev, uint8_t data)
{
struct spi_ioc_transfer msg;
memset(&msg, 0, sizeof(msg));
@@ -255,31 +342,58 @@ mraa_spi_write(mraa_spi_context dev, uint8_t data)
return (int) recv;
}
uint16_t
mraa_spi_write_word(mraa_spi_context dev, uint16_t data)
int
mraa_spi_write(mraa_spi_context dev, uint8_t data)
{
struct spi_ioc_transfer msg;
memset(&msg, 0, sizeof(msg));
uint16_t length = 2;
uint16_t recv = 0;
msg.tx_buf = (unsigned long) &data;
msg.rx_buf = (unsigned long) &recv;
msg.speed_hz = dev->clock;
msg.bits_per_word = dev->bpw;
msg.delay_usecs = 0;
msg.len = length;
if (ioctl(dev->devfd, SPI_IOC_MESSAGE(1), &msg) < 0) {
syslog(LOG_ERR, "spi: Failed to perform dev transfer");
if ((dev->bpw) > 8 || (dev->softbpw > 8)) {
syslog(LOG_ERR,
"spi: refusing to transfer 8 bits in non 8-bit mode, use mraa_spi_write_word");
return -1;
}
return recv;
return mraa_spi_write_internal(dev, data);
}
long
mraa_spi_write_word(mraa_spi_context dev, uint16_t data)
{
if (((dev->bpw < 9) && (dev->softbpw == 0)) || ((dev->softbpw > 0) && (dev->softbpw < 9))) {
syslog(LOG_ERR, "spi: refusing to transfer <9 bits in 16-bit mode, use mraa_spi_write");
return -1;
}
struct spi_ioc_transfer msg;
memset(&msg, 0, sizeof(msg));
unsigned long recv = 0;
if ((dev->softcspin_context != NULL) && (dev->hwcs == 0) && dev->lsb == 0) {
mraa_gpio_write(dev->softcspin_context, 0);
recv = mraa_spi_write_internal(dev, data >> 8) << 8;
recv += mraa_spi_write_internal(dev, data & 0xff);
mraa_gpio_write(dev->softcspin_context, 1);
} else {
uint16_t length = 2;
msg.tx_buf = (unsigned long) &data;
msg.rx_buf = (unsigned long) &recv;
msg.speed_hz = dev->clock;
msg.bits_per_word = dev->bpw;
msg.delay_usecs = 0;
msg.len = length;
if (ioctl(dev->devfd, SPI_IOC_MESSAGE(1), &msg) < 0) {
syslog(LOG_ERR, "spi: Failed to perform dev transfer");
return -1;
}
}
return (long) recv;
}
mraa_result_t
mraa_spi_transfer_buf(mraa_spi_context dev, uint8_t* data, uint8_t* rxbuf, int length)
{
if ((dev->bpw) > 8 || (dev->softbpw > 8)) {
syslog(LOG_ERR, "spi: refusing to transfer 8 bits in non 8-bit mode, use "
"mraa_spi_transfer_buf_word");
return MRAA_ERROR_INVALID_RESOURCE;
}
struct spi_ioc_transfer msg;
memset(&msg, 0, sizeof(msg));
@@ -299,19 +413,31 @@ mraa_spi_transfer_buf(mraa_spi_context dev, uint8_t* data, uint8_t* rxbuf, int l
mraa_result_t
mraa_spi_transfer_buf_word(mraa_spi_context dev, uint16_t* data, uint16_t* rxbuf, int length)
{
struct spi_ioc_transfer msg;
memset(&msg, 0, sizeof(msg));
msg.tx_buf = (unsigned long) data;
msg.rx_buf = (unsigned long) rxbuf;
msg.speed_hz = dev->clock;
msg.bits_per_word = dev->bpw;
msg.delay_usecs = 0;
msg.len = length;
if (ioctl(dev->devfd, SPI_IOC_MESSAGE(1), &msg) < 0) {
syslog(LOG_ERR, "spi: Failed to perform dev transfer");
if (((dev->bpw < 9) && (dev->softbpw == 0)) || ((dev->softbpw > 0) && (dev->softbpw < 9))) {
syslog(LOG_ERR,
"spi: refusing to transfer <9 bits in 16-bit mode, use mraa_spi_transfer_buf");
return MRAA_ERROR_INVALID_RESOURCE;
}
if ((dev->softcspin_context != NULL) && (dev->hwcs == 0) && dev->lsb == 0) {
int i;
for (i = 0; i < length / 2; i++) {
rxbuf[i] = mraa_spi_write_word(dev, data[i]);
}
} else {
struct spi_ioc_transfer msg;
memset(&msg, 0, sizeof(msg));
msg.tx_buf = (unsigned long) data;
msg.rx_buf = (unsigned long) rxbuf;
msg.speed_hz = dev->clock;
msg.bits_per_word = dev->bpw;
msg.delay_usecs = 0;
msg.len = length;
if (ioctl(dev->devfd, SPI_IOC_MESSAGE(1), &msg) < 0) {
syslog(LOG_ERR, "spi: Failed to perform dev transfer");
return MRAA_ERROR_INVALID_RESOURCE;
}
}
return MRAA_SUCCESS;
}
@@ -343,6 +469,9 @@ mraa_result_t
mraa_spi_stop(mraa_spi_context dev)
{
close(dev->devfd);
if (dev->softcspin_context != NULL) {
mraa_gpio_close(dev->softcspin_context);
}
free(dev);
return MRAA_SUCCESS;
}