First shot at SPI interface

This commit is contained in:
Thomas Kolb 2018-07-31 23:09:41 +02:00
parent 8bc5d05ec1
commit 7ca06eecd6
6 changed files with 184 additions and 119 deletions

View File

@ -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(

129
src/hat_spi.c Normal file
View File

@ -0,0 +1,129 @@
#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);
}

14
src/hat_spi.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -1,124 +1,42 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#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);
}

View File

@ -3,6 +3,8 @@
#include <stdint.h>
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);