From 7ca06eecd6c2722b67e1366fd26f0d1a21d511ba Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Tue, 31 Jul 2018 23:09:41 +0200 Subject: [PATCH] First shot at SPI interface --- CMakeLists.txt | 2 + src/hat_spi.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++ src/hat_spi.h | 14 ++++++ src/main.c | 30 ++++++------ src/sk6812.c | 114 ++++++------------------------------------- src/sk6812.h | 14 +++--- 6 files changed, 184 insertions(+), 119 deletions(-) create mode 100644 src/hat_spi.c create mode 100644 src/hat_spi.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f1b8ff..0278a31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,11 +14,13 @@ set(sources src/udpproto.c src/utils.c src/sk6812.c + src/hat_spi.c src/fader.h src/logger.h src/udpproto.h src/utils.h src/sk6812.h + src/hat_spi.h ) include_directories( diff --git a/src/hat_spi.c b/src/hat_spi.c new file mode 100644 index 0000000..024df9d --- /dev/null +++ b/src/hat_spi.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#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); +} + diff --git a/src/hat_spi.h b/src/hat_spi.h new file mode 100644 index 0000000..fea625c --- /dev/null +++ b/src/hat_spi.h @@ -0,0 +1,14 @@ +#ifndef HAT_SPI_H +#define HAT_SPI_H + +struct hat_spi_ctx { + int spidev_fd; +}; + +int hat_spi_init(struct hat_spi_ctx *ctx); +int hat_spi_set_data(struct hat_spi_ctx *ctx, uint8_t strip, size_t len, uint8_t *data); +int hat_spi_flush(struct hat_spi_ctx *ctx); +int hat_spi_config(struct hat_spi_ctx *ctx, uint16_t data_len); +void hat_spi_shutdown(struct hat_spi_ctx *ctx); + +#endif // HAT_SPI_H diff --git a/src/main.c b/src/main.c index f50070b..108cc09 100644 --- a/src/main.c +++ b/src/main.c @@ -11,6 +11,7 @@ #include "logger.h" #include "fader.h" #include "udpproto.h" +#include "hat_spi.h" #include "utils.h" #define PORT 2703 @@ -23,8 +24,6 @@ struct CmdLineArgs { uint16_t port; - void *base_addr; - uint32_t gpio_idx; uint32_t num_modules; }; @@ -52,15 +51,6 @@ int parse_args(int argc, char **argv, struct CmdLineArgs *args) args->port = (uint16_t)tmp_ul; break; - case 'a': // base address - tmp_ull = strtoull(optarg, NULL, 0); - args->base_addr = (void*)tmp_ull; - break; - - case 'g': // gpio - args->gpio_idx = strtoul(optarg, NULL, 0); - break; - case 'n': // number of modules args->num_modules = strtoul(optarg, NULL, 0); break; @@ -77,6 +67,7 @@ int main(int argc, char **argv) { struct sk6812_ctx ctx; struct fader_ctx fader_ctx; + struct hat_spi_ctx hat_ctx; struct CmdLineArgs args; @@ -87,8 +78,6 @@ int main(int argc, char **argv) // default arguments args.port = PORT; - args.base_addr = BASE_ADDR; - args.gpio_idx = GPIO_IDX; args.num_modules = NUM_MODULES; // parse command line arguments @@ -98,8 +87,19 @@ int main(int argc, char **argv) return 1; } - // initialize ws2801 library - if(sk6812_init(&ctx, GPIO_IDX, BASE_ADDR, NUM_MODULES) == -1) { + // initialize SPI interface for hat + if(hat_spi_init(&hat_ctx) == -1) { + LOG(LVL_FATAL, "Could not initialize SPI interface."); + return 1; + } + + if(hat_spi_config(&hat_ctx, NUM_MODULES*4) == -1) { + LOG(LVL_FATAL, "Could not configure LED driver hat."); + return 1; + } + + // initialize sk6812 library + if(sk6812_init(&ctx, &hat_ctx, 0, NUM_MODULES) == -1) { LOG(LVL_FATAL, "Could not initialize SK6812 library."); return 1; } diff --git a/src/sk6812.c b/src/sk6812.c index 298714a..84f30a0 100644 --- a/src/sk6812.c +++ b/src/sk6812.c @@ -1,124 +1,42 @@ -#include -#include -#include -#include -#include -#include - -#include #include -#include +#include #include "logger.h" +#include "hat_spi.h" + #include "sk6812.h" -int sk6812_init(struct sk6812_ctx *ctx, uint32_t gpio_idx, void *phys_baseptr, uint32_t num_modules) +int sk6812_init(struct sk6812_ctx *ctx, struct hat_spi_ctx *hat_ctx, uint8_t strip_id, uint32_t num_modules) { - ctx->gpio_idx = gpio_idx; + ctx->hat_ctx = hat_ctx; + ctx->num_modules = num_modules; + ctx->strip_id = strip_id; - /* - * Setup memory map for LED memory. - */ - size_t mapsize = num_modules*sizeof(uint32_t); - size_t pagesize = (size_t)getpagesize(); - - if((mapsize % pagesize) != 0) { - mapsize = pagesize * (mapsize / pagesize + 1); - } - - ctx->devmem_fd = open("/dev/mem", O_RDWR|O_SYNC); - if(ctx->devmem_fd == -1) { - LOG(LVL_ERR, "sk6812: open(/dev/mem) failed: %s.", strerror(errno)); + ctx->pixeldata = malloc(4*num_modules * sizeof(ctx->pixeldata[0])); + if(!ctx->pixeldata) { + LOG(LVL_ERR, "sk6812: malloc for pixeldata failed."); return -1; } - ctx->memptr = (struct sk6812_memory*)mmap(0, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, ctx->devmem_fd, (off_t)phys_baseptr); - if(ctx->memptr == MAP_FAILED) { - LOG(LVL_ERR, "sk6812: mmap(0x%08X) failed: %s.", phys_baseptr, strerror(errno)); - close(ctx->devmem_fd); - return -1; - } - - ctx->mapped_size = mapsize; - - // write number of modules to mapped memory - ctx->memptr->num_leds = num_modules; - - /* - * set up GPIO - */ - char filename[256]; - char data[32]; - int dlen; - - int fd = open("/sys/class/gpio/export", O_WRONLY); - if(ctx->devmem_fd == -1) { - LOG(LVL_ERR, "sk6812: open(/sys/class/gpio/export, WRONLY) failed: %s.", strerror(errno)); - return -1; - } - - dlen = snprintf(data, 32, "%d\n", ctx->gpio_idx); - write(fd, data, dlen); - close(fd); - - snprintf(filename, 256, "/sys/class/gpio/gpio%u/direction", ctx->gpio_idx); - - fd = open(filename, O_WRONLY); - if(ctx->devmem_fd == -1) { - LOG(LVL_ERR, "sk6812: open(%s, WRONLY) failed: %s.", filename, strerror(errno)); - return -1; - } - - write(fd, "out\n", 4); - close(fd); - return 0; } void sk6812_shutdown(struct sk6812_ctx *ctx) { - munmap(ctx->memptr, ctx->mapped_size); - close(ctx->devmem_fd); + free(ctx->pixeldata); } void sk6812_set_colour(struct sk6812_ctx *ctx, uint32_t module, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) { // despite the info in the datasheet, red and green are swapped :/ - uint32_t value = - ((uint32_t)green << 24U) | - ((uint32_t)red << 16U) | - ((uint32_t)blue << 8U) | - (uint32_t)white; - - ctx->memptr->led_data[module] = value; + ctx->pixeldata[4*module + 0] = green; + ctx->pixeldata[4*module + 1] = red; + ctx->pixeldata[4*module + 2] = blue; + ctx->pixeldata[4*module + 3] = white; } int sk6812_send_update(struct sk6812_ctx *ctx) { - LOG(LVL_DEBUG, "Updating with LED count: %u", ctx->memptr->num_leds); - - char filename[256]; - - snprintf(filename, 256, "/sys/class/gpio/gpio%u/value", ctx->gpio_idx); - - int fd = open(filename, O_WRONLY); - if(ctx->devmem_fd == -1) { - LOG(LVL_ERR, "sk6812: open(%s, WRONLY) failed: %s.", filename, strerror(errno)); - return -1; - } - - write(fd, "0\n", 2); - close(fd); - - fd = open(filename, O_WRONLY); - if(ctx->devmem_fd == -1) { - LOG(LVL_ERR, "sk6812: open(%s, WRONLY) failed: %s.", filename, strerror(errno)); - return -1; - } - - write(fd, "1\n", 2); - close(fd); - - return 0; + return hat_spi_set_data(ctx->hat_ctx, ctx->strip_id, ctx->num_modules*4, ctx->pixeldata); } diff --git a/src/sk6812.h b/src/sk6812.h index 00ffee5..daa7cf5 100644 --- a/src/sk6812.h +++ b/src/sk6812.h @@ -3,6 +3,8 @@ #include +struct hat_spi_ctx; + struct sk6812_memory { uint32_t num_leds; @@ -11,14 +13,14 @@ struct sk6812_memory struct sk6812_ctx { - struct sk6812_memory *memptr; - char *gpio_value_path; - int devmem_fd; - size_t mapped_size; - uint32_t gpio_idx; + uint8_t *pixeldata; + uint32_t num_modules; + uint8_t strip_id; + + struct hat_spi_ctx *hat_ctx; }; -int sk6812_init(struct sk6812_ctx *ctx, uint32_t gpio_idx, void *phys_baseptr, uint32_t num_modules); +int sk6812_init(struct sk6812_ctx *ctx, struct hat_spi_ctx *hat_ctx, uint8_t strip_id, uint32_t num_modules); void sk6812_shutdown(struct sk6812_ctx *ctx); void sk6812_set_colour(struct sk6812_ctx *ctx, uint32_t module, uint8_t red, uint8_t green, uint8_t blue, uint8_t white); int sk6812_send_update(struct sk6812_ctx *ctx);