From 9b9f6ee3d657fbf1fe58bfbc0b989d886c82a191 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Fri, 12 Apr 2013 19:25:23 +0200 Subject: [PATCH] Use SPI to send commands to the WS2801 strip --- Makefile | 2 +- src/ws2801.c | 123 +++++++++++++++++++++++++-------------------------- 2 files changed, 61 insertions(+), 64 deletions(-) diff --git a/Makefile b/Makefile index 83810c0..673683b 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ LD=gcc # default build configuration # "make BUILD=release" does a release build -BUILD:=release +BUILD:=debug # basic build flags configuration CFLAGS+=-std=c99 -Wall -pedantic -Wno-long-long -D_POSIX_C_SOURCE=20120607L -D_FILE_OFFSET_BITS=64 diff --git a/src/ws2801.c b/src/ws2801.c index fddf4ee..a3a5def 100644 --- a/src/ws2801.c +++ b/src/ws2801.c @@ -3,76 +3,77 @@ #include #include -#include +#include +#include +#include +#include +#include + +//#include #include "../include/logger.h" #include "../include/ws2801.h" -#define WS2801_CLK_GPIO 17 -#define WS2801_DAT_GPIO 18 +static const char *spi_dev = "/dev/spidev0.0"; +static const uint32_t spi_speed = 250000; // clock freq in Hz +static const uint16_t spi_delay = 0; // us +static const uint8_t spi_bits = 8; // bits per word -// the interface rpi->ws2801 is active low -#define BUS_HIGH LOW -#define BUS_LOW HIGH - -uint8_t *modRed = NULL; -uint8_t *modGreen = NULL; -uint8_t *modBlue = NULL; uint8_t numModules; +uint8_t *message; -void busy_wait(void) +int spi_fd = 0; + +void send_message(void) { - static const struct timespec bitsleep = {0, 1000}; - nanosleep(&bitsleep, NULL); -} + struct spi_ioc_transfer tr = { + .tx_buf = (unsigned long)message, + .rx_buf = (unsigned long)NULL, + .len = 3 * numModules, + .delay_usecs = spi_delay, + .speed_hz = spi_speed, + .bits_per_word = 8 + }; -void send_bit(uint8_t value) -{ - digitalWrite(WS2801_DAT_GPIO, (value ? BUS_HIGH : BUS_LOW)); - - digitalWrite(WS2801_CLK_GPIO, BUS_LOW); - busy_wait(); - - // rising edge at clock - digitalWrite(WS2801_CLK_GPIO, BUS_HIGH); - busy_wait(); -} - -void send_commit_condition(void) -{ - digitalWrite(WS2801_DAT_GPIO, BUS_LOW); - digitalWrite(WS2801_CLK_GPIO, BUS_LOW); -} - -void send_byte(uint8_t byte) -{ - for(int i = 0; i <= 7; i++) { - send_bit(byte & 0x1); - byte >>= 1; + if(ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr) < 0) { + LOG(LVL_ERR, "ws2801: could not send SPI message: %s", strerror(errno)); } } -void send_colour(uint8_t red, uint8_t green, uint8_t blue) -{ - send_byte(red); - send_byte(green); - send_byte(blue); -} - int ws2801_init(uint8_t nMod) { - if(wiringPiSetupGpio() == -1) { - LOG(LVL_FATAL, "ws2801: wiringPi setup failed: %s", strerror(errno)); + // Initialize SPI + // We need to have stable data at falling edge of the spi interface (this + // means rising edge at the led strip, as the signal is inverted. + uint8_t spi_mode = SPI_NO_CS | SPI_CPOL; + + spi_fd = open(spi_dev, O_RDWR); + if(spi_fd < 0) { + LOG(LVL_ERR, "ws2801: cannot open %s: %s", spi_dev, strerror(errno)); return -1; } - numModules = nMod; - modRed = malloc(nMod * sizeof(uint8_t)); - modGreen = malloc(nMod * sizeof(uint8_t)); - modBlue = malloc(nMod * sizeof(uint8_t)); + if(ioctl(spi_fd, SPI_IOC_WR_MODE, &spi_mode) == -1) { + LOG(LVL_ERR, "ws2801: cannot change SPI mode: %s", strerror(errno)); + return -1; + } - if(!modRed || !modGreen || !modBlue) { - LOG(LVL_ERR, "ws2801: could not allocate all colour arrays!"); + if(ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits) == -1) { + LOG(LVL_ERR, "ws2801: cannot change SPI bits per word: %s", strerror(errno)); + return -1; + } + + if(ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed) == -1) { + LOG(LVL_ERR, "ws2801: cannot change SPI speed: %s", strerror(errno)); + return -1; + } + + // create the message array + numModules = nMod; + message = malloc(3 * nMod * sizeof(uint8_t)); + + if(!message) { + LOG(LVL_ERR, "ws2801: could not allocate the message array!"); return -1; } @@ -81,25 +82,21 @@ int ws2801_init(uint8_t nMod) void ws2801_shutdown(void) { - if(modRed != NULL) { free(modRed); modRed = NULL; } - if(modGreen != NULL) { free(modGreen); modGreen = NULL; } - if(modBlue != NULL) { free(modBlue); modBlue = NULL; } + close(spi_fd); + + if(message != NULL) { free(message); message = NULL; } } void ws2801_set_colour(uint8_t module, uint8_t red, uint8_t green, uint8_t blue) { - modRed[module] = red; - modGreen[module] = green; - modBlue[module] = blue; + message[3*module + 0] = ~red; + message[3*module + 1] = ~green; + message[3*module + 2] = ~blue; } void ws2801_send_update(void) { - for(uint8_t i = 0; i < numModules; i++) { - send_colour(modRed[i], modGreen[i], modBlue[i]); - } - - send_commit_condition(); + send_message(); static const struct timespec sleepval = {0, 550000}; nanosleep(&sleepval, NULL);