130 lines
3.0 KiB
C
130 lines
3.0 KiB
C
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <linux/spi/spidev.h>
|
|
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include "logger.h"
|
|
|
|
#include "hat_spi.h"
|
|
|
|
static const char *spi_dev = "/dev/spidev0.0";
|
|
static const uint32_t spi_speed = 12000000; // clock freq in Hz
|
|
static const uint16_t spi_delay = 0; // us
|
|
static const uint8_t spi_bits = 8; // bits per word
|
|
|
|
int hat_spi_init(struct hat_spi_ctx *ctx)
|
|
{
|
|
// Initialize SPI
|
|
uint8_t spi_mode = 0;
|
|
|
|
ctx->spidev_fd = open(spi_dev, O_RDWR);
|
|
if(ctx->spidev_fd < 0) {
|
|
LOG(LVL_ERR, "hat_spi: cannot open %s: %s", spi_dev, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if(ioctl(ctx->spidev_fd, SPI_IOC_WR_MODE, &spi_mode) == -1) {
|
|
LOG(LVL_ERR, "hat_spi: cannot change SPI mode: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if(ioctl(ctx->spidev_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits) == -1) {
|
|
LOG(LVL_ERR, "hat_spi: cannot change SPI bits per word: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if(ioctl(ctx->spidev_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed) == -1) {
|
|
LOG(LVL_ERR, "hat_spi: cannot change SPI speed: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hat_spi_set_data(struct hat_spi_ctx *ctx, uint8_t strip, size_t len, uint8_t *data)
|
|
{
|
|
uint8_t message[4 + len];
|
|
|
|
message[0] = 0x02; // cmd
|
|
message[1] = strip; // buffer index
|
|
message[2] = 0x00; // start offset (LSB)
|
|
message[3] = 0x00; // start offset (MSB)
|
|
|
|
memcpy(message + 4, data, len);
|
|
|
|
struct spi_ioc_transfer tr = {
|
|
.tx_buf = (unsigned long)message,
|
|
.rx_buf = (unsigned long)NULL,
|
|
.len = sizeof(message),
|
|
.delay_usecs = spi_delay,
|
|
.speed_hz = spi_speed,
|
|
.bits_per_word = 8
|
|
};
|
|
|
|
int ret;
|
|
if((ret = ioctl(ctx->spidev_fd, SPI_IOC_MESSAGE(1), &tr)) < 0) {
|
|
LOG(LVL_ERR, "ws2801: set_data: could not send SPI message: %s", strerror(errno));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int hat_spi_flush(struct hat_spi_ctx *ctx)
|
|
{
|
|
uint8_t message[1];
|
|
|
|
message[0] = 0x80; // cmd
|
|
|
|
struct spi_ioc_transfer tr = {
|
|
.tx_buf = (unsigned long)message,
|
|
.rx_buf = (unsigned long)NULL,
|
|
.len = sizeof(message),
|
|
.delay_usecs = spi_delay,
|
|
.speed_hz = spi_speed,
|
|
.bits_per_word = 8
|
|
};
|
|
|
|
int ret;
|
|
if((ret = ioctl(ctx->spidev_fd, SPI_IOC_MESSAGE(1), &tr)) < 0) {
|
|
LOG(LVL_ERR, "ws2801: flush: could not send SPI message: %s", strerror(errno));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int hat_spi_config(struct hat_spi_ctx *ctx, uint16_t data_len)
|
|
{
|
|
uint8_t message[3];
|
|
|
|
message[0] = 0x01; // cmd
|
|
message[1] = data_len & 0xFF; // bytes per strip (LSB)
|
|
message[2] = (data_len >> 8) & 0xFF; // bytes per strip (MSB)
|
|
|
|
struct spi_ioc_transfer tr = {
|
|
.tx_buf = (unsigned long)message,
|
|
.rx_buf = (unsigned long)NULL,
|
|
.len = sizeof(message),
|
|
.delay_usecs = spi_delay,
|
|
.speed_hz = spi_speed,
|
|
.bits_per_word = 8
|
|
};
|
|
|
|
int ret;
|
|
if((ret = ioctl(ctx->spidev_fd, SPI_IOC_MESSAGE(1), &tr)) < 0) {
|
|
LOG(LVL_ERR, "ws2801: flush: could not send SPI message: %s", strerror(errno));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void hat_spi_shutdown(struct hat_spi_ctx *ctx)
|
|
{
|
|
close(ctx->spidev_fd);
|
|
}
|
|
|