Compare commits
4 commits
Author | SHA1 | Date | |
---|---|---|---|
4734b8eaeb | |||
fc7173afff | |||
Thomas Kolb | 7ca06eecd6 | ||
Thomas Kolb | 8bc5d05ec1 |
|
@ -14,11 +14,13 @@ set(sources
|
||||||
src/udpproto.c
|
src/udpproto.c
|
||||||
src/utils.c
|
src/utils.c
|
||||||
src/sk6812.c
|
src/sk6812.c
|
||||||
|
src/hat_spi.c
|
||||||
src/fader.h
|
src/fader.h
|
||||||
src/logger.h
|
src/logger.h
|
||||||
src/udpproto.h
|
src/udpproto.h
|
||||||
src/utils.h
|
src/utils.h
|
||||||
src/sk6812.h
|
src/sk6812.h
|
||||||
|
src/hat_spi.h
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
|
2
make.sh
2
make.sh
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||||
make $@
|
make $@
|
||||||
|
|
146
src/fader.c
146
src/fader.c
|
@ -6,102 +6,80 @@
|
||||||
|
|
||||||
#include "fader.h"
|
#include "fader.h"
|
||||||
|
|
||||||
uint32_t numModules;
|
int fader_init(struct fader_ctx *ctx, uint32_t nMod, struct sk6812_ctx *ledCtx)
|
||||||
|
|
||||||
float fadestep = 1;
|
|
||||||
double nextFrame;
|
|
||||||
|
|
||||||
int somethingChanged = 0; // indicates when a sk6812 update is required
|
|
||||||
|
|
||||||
static const double interval = 1.0f / 75.0f;
|
|
||||||
|
|
||||||
struct Colour {
|
|
||||||
float red, green, blue, white; // value range is 0.0 to 255.0
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Colour *curColour;
|
|
||||||
struct Colour *targetColour;
|
|
||||||
|
|
||||||
struct sk6812_ctx *ledCtx; // global context for the one strip we control
|
|
||||||
|
|
||||||
int fader_init(uint32_t nMod, struct sk6812_ctx *ctx)
|
|
||||||
{
|
{
|
||||||
numModules = nMod;
|
ctx->num_modules = nMod;
|
||||||
|
ctx->fadestep = 1;
|
||||||
|
|
||||||
curColour = malloc(nMod * sizeof(struct Colour));
|
ctx->cur_colour = malloc(nMod * sizeof(struct fader_colour));
|
||||||
if(!curColour) {
|
if(!ctx->cur_colour) {
|
||||||
LOG(LVL_ERR, "fader: could not allocate the array of current colours!");
|
LOG(LVL_ERR, "fader: could not allocate the array of current colours!");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
targetColour = malloc(nMod * sizeof(struct Colour));
|
ctx->target_colour = malloc(nMod * sizeof(struct fader_colour));
|
||||||
if(!targetColour) {
|
if(!ctx->target_colour) {
|
||||||
LOG(LVL_ERR, "fader: could not allocate the array of target colours!");
|
LOG(LVL_ERR, "fader: could not allocate the array of target colours!");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(uint32_t i = 0; i < numModules; i++) {
|
for(uint32_t i = 0; i < ctx->num_modules; i++) {
|
||||||
curColour[i].red = targetColour[i].red = 0;
|
ctx->cur_colour[i].red = ctx->target_colour[i].red = 0;
|
||||||
curColour[i].green = targetColour[i].green = 0;
|
ctx->cur_colour[i].green = ctx->target_colour[i].green = 0;
|
||||||
curColour[i].blue = targetColour[i].blue = 0;
|
ctx->cur_colour[i].blue = ctx->target_colour[i].blue = 0;
|
||||||
curColour[i].white = targetColour[i].white = 0;
|
ctx->cur_colour[i].white = ctx->target_colour[i].white = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// store LED context (initalized externally)
|
// store LED context (initalized externally)
|
||||||
ledCtx = ctx;
|
ctx->led_ctx = ledCtx;
|
||||||
|
|
||||||
// timestamp for the first frame
|
|
||||||
nextFrame = get_hires_time() + interval;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fader_shutdown(void)
|
void fader_shutdown(struct fader_ctx *ctx)
|
||||||
{
|
{
|
||||||
free(curColour);
|
free(ctx->cur_colour);
|
||||||
free(targetColour);
|
free(ctx->target_colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fader_set_colour(uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w)
|
void fader_set_colour(struct fader_ctx *ctx, uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w)
|
||||||
{
|
{
|
||||||
curColour[module].red = targetColour[module].red = r;
|
ctx->cur_colour[module].red = ctx->target_colour[module].red = r;
|
||||||
curColour[module].green = targetColour[module].green = g;
|
ctx->cur_colour[module].green = ctx->target_colour[module].green = g;
|
||||||
curColour[module].blue = targetColour[module].blue = b;
|
ctx->cur_colour[module].blue = ctx->target_colour[module].blue = b;
|
||||||
curColour[module].white = targetColour[module].white = w;
|
ctx->cur_colour[module].white = ctx->target_colour[module].white = w;
|
||||||
|
|
||||||
somethingChanged = 1;
|
ctx->something_changed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fader_fade_colour(uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w)
|
void fader_fade_colour(struct fader_ctx *ctx, uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w)
|
||||||
{
|
{
|
||||||
targetColour[module].red = r;
|
ctx->target_colour[module].red = r;
|
||||||
targetColour[module].green = g;
|
ctx->target_colour[module].green = g;
|
||||||
targetColour[module].blue = b;
|
ctx->target_colour[module].blue = b;
|
||||||
targetColour[module].white = w;
|
ctx->target_colour[module].white = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fader_add_colour(uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w)
|
void fader_add_colour(struct fader_ctx *ctx, uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w)
|
||||||
{
|
{
|
||||||
curColour[module].red += r;
|
ctx->cur_colour[module].red += r;
|
||||||
curColour[module].green += g;
|
ctx->cur_colour[module].green += g;
|
||||||
curColour[module].blue += b;
|
ctx->cur_colour[module].blue += b;
|
||||||
curColour[module].white += w;
|
ctx->cur_colour[module].white += w;
|
||||||
|
|
||||||
if(curColour[module].red > 255) { curColour[module].red = 255; }
|
if(ctx->cur_colour[module].red > 255) { ctx->cur_colour[module].red = 255; }
|
||||||
if(curColour[module].green > 255) { curColour[module].green = 255; }
|
if(ctx->cur_colour[module].green > 255) { ctx->cur_colour[module].green = 255; }
|
||||||
if(curColour[module].blue > 255) { curColour[module].blue = 255; }
|
if(ctx->cur_colour[module].blue > 255) { ctx->cur_colour[module].blue = 255; }
|
||||||
if(curColour[module].white > 255) { curColour[module].white = 255; }
|
if(ctx->cur_colour[module].white > 255) { ctx->cur_colour[module].white = 255; }
|
||||||
|
|
||||||
targetColour[module] = curColour[module];
|
ctx->target_colour[module] = ctx->cur_colour[module];
|
||||||
|
|
||||||
somethingChanged = 1;
|
ctx->something_changed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fader_set_fadestep(uint8_t newFadestep)
|
void fader_set_fadestep(struct fader_ctx *ctx, uint8_t newFadestep)
|
||||||
{
|
{
|
||||||
// The original avr implementition had a frame rate of 25fps (interval 0,04 sec.).
|
ctx->fadestep = newFadestep;
|
||||||
// This scales the fadestep to the current frame rate.
|
|
||||||
fadestep = (float)newFadestep * interval / 0.04f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -111,53 +89,47 @@ void fader_set_fadestep(uint8_t newFadestep)
|
||||||
* \param target The target value that should be reached.
|
* \param target The target value that should be reached.
|
||||||
* \param changed Output value which is set to 1 if cur was changed.
|
* \param changed Output value which is set to 1 if cur was changed.
|
||||||
*/
|
*/
|
||||||
void fade_colour(float *cur, const float *target, int *changed)
|
void fade_colour(struct fader_ctx *ctx, float *cur, const float *target, uint8_t *changed)
|
||||||
{
|
{
|
||||||
float diff;
|
float diff;
|
||||||
if(*cur > *target) {
|
if(*cur > *target) {
|
||||||
diff = *cur - *target;
|
diff = *cur - *target;
|
||||||
if(diff < fadestep) {
|
if(diff < ctx->fadestep) {
|
||||||
*cur = *target;
|
*cur = *target;
|
||||||
} else {
|
} else {
|
||||||
*cur -= fadestep;
|
*cur -= ctx->fadestep;
|
||||||
}
|
}
|
||||||
|
|
||||||
*changed = 1;
|
*changed = 1;
|
||||||
} else if(*cur < *target) {
|
} else if(*cur < *target) {
|
||||||
diff = *target - *cur;
|
diff = *target - *cur;
|
||||||
if(diff < fadestep) {
|
if(diff < ctx->fadestep) {
|
||||||
*cur = *target;
|
*cur = *target;
|
||||||
} else {
|
} else {
|
||||||
*cur += fadestep;
|
*cur += ctx->fadestep;
|
||||||
}
|
}
|
||||||
|
|
||||||
*changed = 1;
|
*changed = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fader_update(void)
|
void fader_update(struct fader_ctx *ctx)
|
||||||
{
|
{
|
||||||
for(uint32_t i = 0; i < numModules; i++) {
|
for(uint32_t i = 0; i < ctx->num_modules; i++) {
|
||||||
fade_colour(&(curColour[i].red), &(targetColour[i].red), &somethingChanged);
|
fade_colour(ctx, &(ctx->cur_colour[i].red), &(ctx->target_colour[i].red), &ctx->something_changed);
|
||||||
fade_colour(&(curColour[i].green), &(targetColour[i].green), &somethingChanged);
|
fade_colour(ctx, &(ctx->cur_colour[i].green), &(ctx->target_colour[i].green), &ctx->something_changed);
|
||||||
fade_colour(&(curColour[i].blue), &(targetColour[i].blue), &somethingChanged);
|
fade_colour(ctx, &(ctx->cur_colour[i].blue), &(ctx->target_colour[i].blue), &ctx->something_changed);
|
||||||
fade_colour(&(curColour[i].white), &(targetColour[i].white), &somethingChanged);
|
fade_colour(ctx, &(ctx->cur_colour[i].white), &(ctx->target_colour[i].white), &ctx->something_changed);
|
||||||
|
|
||||||
sk6812_set_colour(ledCtx, i,
|
sk6812_set_colour(ctx->led_ctx, i,
|
||||||
(uint8_t)curColour[i].red,
|
(uint8_t)ctx->cur_colour[i].red,
|
||||||
(uint8_t)curColour[i].green,
|
(uint8_t)ctx->cur_colour[i].green,
|
||||||
(uint8_t)curColour[i].blue,
|
(uint8_t)ctx->cur_colour[i].blue,
|
||||||
(uint8_t)curColour[i].white);
|
(uint8_t)ctx->cur_colour[i].white);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(somethingChanged) {
|
if(ctx->something_changed) {
|
||||||
sk6812_send_update(ledCtx);
|
sk6812_send_update(ctx->led_ctx);
|
||||||
somethingChanged = 0;
|
ctx->something_changed = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fader_wait_frame(void)
|
|
||||||
{
|
|
||||||
sleep_until(nextFrame);
|
|
||||||
nextFrame += interval;
|
|
||||||
}
|
|
||||||
|
|
32
src/fader.h
32
src/fader.h
|
@ -5,13 +5,29 @@
|
||||||
|
|
||||||
struct sk6812_ctx;
|
struct sk6812_ctx;
|
||||||
|
|
||||||
int fader_init(uint32_t nMod, struct sk6812_ctx *ctx);
|
struct fader_colour {
|
||||||
void fader_shutdown(void);
|
float red, green, blue, white; // value range is 0.0 to 255.0
|
||||||
void fader_set_colour(uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
|
};
|
||||||
void fader_fade_colour(uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
|
|
||||||
void fader_add_colour(uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
|
struct fader_ctx {
|
||||||
void fader_set_fadestep(uint8_t newFadestep);
|
uint32_t num_modules;
|
||||||
void fader_update(void);
|
|
||||||
void fader_wait_frame(void);
|
struct fader_colour *cur_colour;
|
||||||
|
struct fader_colour *target_colour;
|
||||||
|
|
||||||
|
struct sk6812_ctx *led_ctx;
|
||||||
|
|
||||||
|
uint8_t something_changed;
|
||||||
|
|
||||||
|
float fadestep;
|
||||||
|
};
|
||||||
|
|
||||||
|
int fader_init(struct fader_ctx *ctx, uint32_t nMod, struct sk6812_ctx *ledCtx);
|
||||||
|
void fader_shutdown(struct fader_ctx *ctx);
|
||||||
|
void fader_set_colour(struct fader_ctx *ctx, uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
|
||||||
|
void fader_fade_colour(struct fader_ctx *ctx, uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
|
||||||
|
void fader_add_colour(struct fader_ctx *ctx, uint32_t module, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
|
||||||
|
void fader_set_fadestep(struct fader_ctx *ctx, uint8_t newFadestep);
|
||||||
|
void fader_update(struct fader_ctx *ctx);
|
||||||
|
|
||||||
#endif // FADER_H
|
#endif // FADER_H
|
||||||
|
|
129
src/hat_spi.c
Normal file
129
src/hat_spi.c
Normal 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
14
src/hat_spi.h
Normal 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
|
86
src/main.c
86
src/main.c
|
@ -11,27 +11,35 @@
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "fader.h"
|
#include "fader.h"
|
||||||
#include "udpproto.h"
|
#include "udpproto.h"
|
||||||
|
#include "hat_spi.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
#define PORT 2703
|
#define PORT 2703
|
||||||
|
|
||||||
#define NUM_MODULES 60
|
#define NUM_MODULES 60
|
||||||
#define GPIO_IDX 960
|
#define NUM_STRIPS 4
|
||||||
#define BASE_ADDR ((void*)0x40000000U)
|
|
||||||
|
#define INTERVAL 0.01
|
||||||
|
|
||||||
struct CmdLineArgs {
|
struct CmdLineArgs {
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
void *base_addr;
|
|
||||||
uint32_t gpio_idx;
|
|
||||||
uint32_t num_modules;
|
uint32_t num_modules;
|
||||||
|
uint32_t num_strips;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void wait_frame(double *nextFrame, double interval)
|
||||||
|
{
|
||||||
|
sleep_until(*nextFrame);
|
||||||
|
*nextFrame += interval;
|
||||||
|
}
|
||||||
|
|
||||||
int parse_args(int argc, char **argv, struct CmdLineArgs *args)
|
int parse_args(int argc, char **argv, struct CmdLineArgs *args)
|
||||||
{
|
{
|
||||||
int opt;
|
int opt;
|
||||||
unsigned long tmp_ul;
|
unsigned long tmp_ul;
|
||||||
unsigned long long tmp_ull;
|
unsigned long long tmp_ull;
|
||||||
|
|
||||||
while((opt = getopt(argc, argv, "p:a:g:n:")) != -1) {
|
while((opt = getopt(argc, argv, "p:s:n:")) != -1) {
|
||||||
switch(opt) {
|
switch(opt) {
|
||||||
case 'p': // port
|
case 'p': // port
|
||||||
tmp_ul = strtoul(optarg, NULL, 0);
|
tmp_ul = strtoul(optarg, NULL, 0);
|
||||||
|
@ -43,18 +51,12 @@ int parse_args(int argc, char **argv, struct CmdLineArgs *args)
|
||||||
args->port = (uint16_t)tmp_ul;
|
args->port = (uint16_t)tmp_ul;
|
||||||
break;
|
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
|
case 'n': // number of modules
|
||||||
args->num_modules = strtoul(optarg, NULL, 0);
|
args->num_modules = strtoul(optarg, NULL, 0);
|
||||||
break;
|
break;
|
||||||
|
case 's': // number of modules
|
||||||
|
args->num_strips = strtoul(optarg, NULL, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -66,41 +68,60 @@ int parse_args(int argc, char **argv, struct CmdLineArgs *args)
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct sk6812_ctx ctx;
|
struct hat_spi_ctx hat_ctx;
|
||||||
|
|
||||||
struct CmdLineArgs args;
|
struct CmdLineArgs args;
|
||||||
|
|
||||||
|
double nextFrame = get_hires_time() + INTERVAL;
|
||||||
|
|
||||||
// initialize logger
|
// initialize logger
|
||||||
logger_init();
|
logger_init();
|
||||||
|
|
||||||
// default arguments
|
// default arguments
|
||||||
args.port = PORT;
|
args.port = PORT;
|
||||||
args.base_addr = BASE_ADDR;
|
|
||||||
args.gpio_idx = GPIO_IDX;
|
|
||||||
args.num_modules = NUM_MODULES;
|
args.num_modules = NUM_MODULES;
|
||||||
|
args.num_strips = NUM_STRIPS;
|
||||||
|
|
||||||
// parse command line arguments
|
// parse command line arguments
|
||||||
if(parse_args(argc, argv, &args) == -1) {
|
if(parse_args(argc, argv, &args) == -1) {
|
||||||
LOG(LVL_FATAL, "Error while parsing command line arguments!\n\n"
|
LOG(LVL_FATAL, "Error while parsing command line arguments!\n\n"
|
||||||
"Usage: %s [-p port] [-a base_address] [-g gpio_index] [-n number_of_modules]\n\n", argv[0]);
|
"Usage: %s [-p port] [-s number_of_strips] [-n number_of_modules]\n\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialise the UDP server
|
struct fader_ctx fader_ctx[args.num_strips];
|
||||||
if(udpproto_init(PORT) == -1) {
|
struct sk6812_ctx ctx[args.num_strips];
|
||||||
LOG(LVL_FATAL, "Could not initialize the UDP server.");
|
|
||||||
|
// initialize SPI interface for hat
|
||||||
|
if(hat_spi_init(&hat_ctx) == -1) {
|
||||||
|
LOG(LVL_FATAL, "Could not initialize SPI interface.");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize ws2801 library
|
if(hat_spi_config(&hat_ctx, args.num_modules*4) == -1) {
|
||||||
if(sk6812_init(&ctx, GPIO_IDX, BASE_ADDR, NUM_MODULES) == -1) {
|
LOG(LVL_FATAL, "Could not configure LED driver hat.");
|
||||||
LOG(LVL_FATAL, "Could not initialize SK6812 library.");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize sk6812 library
|
||||||
|
for(int i = 0; i < args.num_strips; i++) {
|
||||||
|
if(sk6812_init(&(ctx[i]), &hat_ctx, i, args.num_modules) == -1) {
|
||||||
|
LOG(LVL_FATAL, "Could not initialize SK6812 library.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// initialise the LED fader
|
// initialise the LED fader
|
||||||
if(fader_init(NUM_MODULES, &ctx) == -1) {
|
for(int i = 0; i < args.num_strips; i++) {
|
||||||
LOG(LVL_FATAL, "Could not initialize the LED fader.");
|
if(fader_init(&(fader_ctx[i]), args.num_modules, &(ctx[i])) == -1) {
|
||||||
|
LOG(LVL_FATAL, "Could not initialize the LED fader.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialise the UDP server
|
||||||
|
if(udpproto_init(PORT, fader_ctx, args.num_strips) == -1) {
|
||||||
|
LOG(LVL_FATAL, "Could not initialize the UDP server.");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,14 +129,19 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
udpproto_process();
|
udpproto_process();
|
||||||
fader_update();
|
for(int i = 0; i < args.num_strips; i++) {
|
||||||
fader_wait_frame();
|
fader_update(&(fader_ctx[i]));
|
||||||
|
}
|
||||||
|
hat_spi_flush(&hat_ctx);
|
||||||
|
wait_frame(&nextFrame, INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// shut down all modules
|
// shut down all modules
|
||||||
fader_shutdown();
|
|
||||||
sk6812_shutdown(&ctx);
|
|
||||||
udpproto_shutdown();
|
udpproto_shutdown();
|
||||||
|
for(int i = 0; i < args.num_strips; i++) {
|
||||||
|
fader_shutdown(&(fader_ctx[i]));
|
||||||
|
sk6812_shutdown(&(ctx[i]));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
114
src/sk6812.c
114
src/sk6812.c
|
@ -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 <stdlib.h>
|
||||||
#include <string.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "hat_spi.h"
|
||||||
|
|
||||||
#include "sk6812.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;
|
||||||
|
|
||||||
/*
|
ctx->pixeldata = malloc(4*num_modules * sizeof(ctx->pixeldata[0]));
|
||||||
* Setup memory map for LED memory.
|
if(!ctx->pixeldata) {
|
||||||
*/
|
LOG(LVL_ERR, "sk6812: malloc for pixeldata failed.");
|
||||||
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));
|
|
||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sk6812_shutdown(struct sk6812_ctx *ctx)
|
void sk6812_shutdown(struct sk6812_ctx *ctx)
|
||||||
{
|
{
|
||||||
munmap(ctx->memptr, ctx->mapped_size);
|
free(ctx->pixeldata);
|
||||||
close(ctx->devmem_fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sk6812_set_colour(struct sk6812_ctx *ctx, uint32_t module, uint8_t red, uint8_t green, uint8_t blue, uint8_t white)
|
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 :/
|
// despite the info in the datasheet, red and green are swapped :/
|
||||||
uint32_t value =
|
ctx->pixeldata[4*module + 0] = green;
|
||||||
((uint32_t)green << 24U) |
|
ctx->pixeldata[4*module + 1] = red;
|
||||||
((uint32_t)red << 16U) |
|
ctx->pixeldata[4*module + 2] = blue;
|
||||||
((uint32_t)blue << 8U) |
|
ctx->pixeldata[4*module + 3] = white;
|
||||||
(uint32_t)white;
|
|
||||||
|
|
||||||
ctx->memptr->led_data[module] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sk6812_send_update(struct sk6812_ctx *ctx)
|
int sk6812_send_update(struct sk6812_ctx *ctx)
|
||||||
{
|
{
|
||||||
LOG(LVL_DEBUG, "Updating with LED count: %u", ctx->memptr->num_leds);
|
return hat_spi_set_data(ctx->hat_ctx, ctx->strip_id, ctx->num_modules*4, ctx->pixeldata);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/sk6812.h
14
src/sk6812.h
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct hat_spi_ctx;
|
||||||
|
|
||||||
struct sk6812_memory
|
struct sk6812_memory
|
||||||
{
|
{
|
||||||
uint32_t num_leds;
|
uint32_t num_leds;
|
||||||
|
@ -11,14 +13,14 @@ struct sk6812_memory
|
||||||
|
|
||||||
struct sk6812_ctx
|
struct sk6812_ctx
|
||||||
{
|
{
|
||||||
struct sk6812_memory *memptr;
|
uint8_t *pixeldata;
|
||||||
char *gpio_value_path;
|
uint32_t num_modules;
|
||||||
int devmem_fd;
|
uint8_t strip_id;
|
||||||
size_t mapped_size;
|
|
||||||
uint32_t gpio_idx;
|
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_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);
|
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);
|
int sk6812_send_update(struct sk6812_ctx *ctx);
|
||||||
|
|
|
@ -17,14 +17,19 @@
|
||||||
#define ADD_COLOUR 2
|
#define ADD_COLOUR 2
|
||||||
#define SET_FADESTEP 3
|
#define SET_FADESTEP 3
|
||||||
|
|
||||||
#define BYTES_PER_PACKET 7
|
#define BYTES_PER_PACKET 8
|
||||||
|
|
||||||
int sock;
|
int sock;
|
||||||
|
struct fader_ctx *udpproto_faders;
|
||||||
|
uint32_t udpproto_nstrips;
|
||||||
|
|
||||||
int udpproto_init(uint16_t port)
|
int udpproto_init(uint16_t port, struct fader_ctx *faders, uint32_t nstrips)
|
||||||
{
|
{
|
||||||
struct sockaddr_in listen_addr;
|
struct sockaddr_in listen_addr;
|
||||||
|
|
||||||
|
udpproto_nstrips = nstrips;
|
||||||
|
udpproto_faders = faders;
|
||||||
|
|
||||||
// initialize UDP server socket
|
// initialize UDP server socket
|
||||||
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
if(sock == -1) {
|
if(sock == -1) {
|
||||||
|
@ -52,10 +57,12 @@ int udpproto_process(void)
|
||||||
uint8_t pkgbuf[65536];
|
uint8_t pkgbuf[65536];
|
||||||
ssize_t rcvbytes, offset = 0;
|
ssize_t rcvbytes, offset = 0;
|
||||||
|
|
||||||
uint8_t r, g, b, w, action;
|
uint8_t r, g, b, w, action, strip;
|
||||||
uint16_t module;
|
uint16_t module;
|
||||||
int fds_ready;
|
int fds_ready;
|
||||||
|
|
||||||
|
struct fader_ctx *fader;
|
||||||
|
|
||||||
// check if there is data to be read (to prevent blocking)
|
// check if there is data to be read (to prevent blocking)
|
||||||
struct pollfd pfd = {
|
struct pollfd pfd = {
|
||||||
.fd = sock,
|
.fd = sock,
|
||||||
|
@ -86,29 +93,36 @@ int udpproto_process(void)
|
||||||
offset = 0;
|
offset = 0;
|
||||||
while(offset <= rcvbytes - BYTES_PER_PACKET) {
|
while(offset <= rcvbytes - BYTES_PER_PACKET) {
|
||||||
action = pkgbuf[offset + 0];
|
action = pkgbuf[offset + 0];
|
||||||
module = (uint16_t)pkgbuf[offset + 1] << 8;
|
strip = pkgbuf[offset + 1];
|
||||||
module |= pkgbuf[offset + 2];
|
module = (uint16_t)pkgbuf[offset + 2] << 8;
|
||||||
r = pkgbuf[offset + 3];
|
module |= pkgbuf[offset + 3];
|
||||||
g = pkgbuf[offset + 4];
|
r = pkgbuf[offset + 4];
|
||||||
b = pkgbuf[offset + 5];
|
g = pkgbuf[offset + 5];
|
||||||
w = pkgbuf[offset + 6];
|
b = pkgbuf[offset + 6];
|
||||||
|
w = pkgbuf[offset + 7];
|
||||||
offset += BYTES_PER_PACKET;
|
offset += BYTES_PER_PACKET;
|
||||||
|
|
||||||
|
if(strip > udpproto_nstrips) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fader = udpproto_faders + strip;
|
||||||
|
|
||||||
switch(action) {
|
switch(action) {
|
||||||
case SET_COLOUR:
|
case SET_COLOUR:
|
||||||
fader_set_colour(module, r, g, b, w);
|
fader_set_colour(fader, module, r, g, b, w);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FADE_COLOUR:
|
case FADE_COLOUR:
|
||||||
fader_fade_colour(module, r, g, b, w);
|
fader_fade_colour(fader, module, r, g, b, w);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ADD_COLOUR:
|
case ADD_COLOUR:
|
||||||
fader_add_colour(module, r, g, b, w);
|
fader_add_colour(fader, module, r, g, b, w);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SET_FADESTEP:
|
case SET_FADESTEP:
|
||||||
fader_set_fadestep(r); // red channel contains the fadestep in this case
|
fader_set_fadestep(fader, r); // red channel contains the fadestep in this case
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
int udpproto_init(uint16_t port);
|
struct fader_ctx;
|
||||||
|
|
||||||
|
int udpproto_init(uint16_t port, struct fader_ctx *faders, uint32_t nstrips);
|
||||||
int udpproto_process(void);
|
int udpproto_process(void);
|
||||||
void udpproto_shutdown(void);
|
void udpproto_shutdown(void);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue