From 2437c7b18d6f934a69a55e3ff912d3531d082f11 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Mon, 8 Apr 2013 23:55:01 +0200 Subject: [PATCH] Initial commit --- Makefile | 79 +++++++++++++++++++++++ include/logger.h | 37 +++++++++++ include/ws2801.h | 11 ++++ src/logger.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 54 ++++++++++++++++ src/ws2801.c | 107 +++++++++++++++++++++++++++++++ 6 files changed, 449 insertions(+) create mode 100644 Makefile create mode 100644 include/logger.h create mode 100644 include/ws2801.h create mode 100644 src/logger.c create mode 100644 src/main.c create mode 100644 src/ws2801.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..83810c0 --- /dev/null +++ b/Makefile @@ -0,0 +1,79 @@ +# --- START OF CONFIG --------------------------------------------------- +# Edit the following variables for your own needs + +# toolchain configuration +CXX=gcc +LD=gcc + +# default build configuration +# "make BUILD=release" does a release build +BUILD:=release + +# basic build flags configuration +CFLAGS+=-std=c99 -Wall -pedantic -Wno-long-long -D_POSIX_C_SOURCE=20120607L -D_FILE_OFFSET_BITS=64 +LIBS+=-lm + +# library specific flags +# wiringPi +CFLAGS+= +LIBS+=-lwiringPi + +# build type specific flags +CFLAGS_debug=-O0 -ggdb -DDEBUG +LIBS_debug= + +CFLAGS_release=-Os -pipe -march=armv6zk -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard +LIBS_release= + +# target configuration +TARGET := ws2801d +VERSION := 0.0.0 +VCSVERSION := $(shell git rev-parse --short HEAD) + +# source files for the project +SOURCE := $(shell find src/ -name '*.c') +INCLUDES := $(shell find include/ -name '*.h') + +# --- END OF CONFIG ----------------------------------------------------- + +OBJ1=$(patsubst %.c, %.o, $(SOURCE)) +OBJ=$(patsubst src/%, obj/$(BUILD)/%, $(OBJ1)) + +VERSIONSTR="\"$(VERSION)-$(VCSVERSION)\"" + +CFLAGS+=-Iinclude -DVERSION=$(VERSIONSTR) + +TARGETFILE := bin/$(BUILD)/$(TARGET) + +all: $(TARGET) + +CFLAGS+=$(CFLAGS_$(BUILD)) +LIBS+=$(LIBS_$(BUILD)) + +.PHONY show_cflags: + @echo --- Build parameters: ------------------------------------------ + @echo CFLAGS\=$(CFLAGS) + @echo LIBS\=$(LIBS) + @echo SOURCE\=$(SOURCE) + @echo ----------------------------------------------------------------- + +$(TARGET): show_cflags $(TARGETFILE) + @echo ">>> $(BUILD) build complete." + +$(TARGETFILE): $(OBJ) $(INCLUDES) Makefile + @echo Linking $@ ... + @mkdir -p $(shell dirname $@) + @$(LD) -o $(TARGETFILE) $(OBJ) $(LIBS) + +obj/$(BUILD)/%.o: src/%.c $(INCLUDES) Makefile + @echo Compiling $< ... + @mkdir -p $(shell dirname $@) + @$(CXX) -c $(CFLAGS) -o $@ $< + +doc: + doxygen doxygen.conf + +clean: + rm -f $(TARGETFILE) + rm -f $(OBJ) + diff --git a/include/logger.h b/include/logger.h new file mode 100644 index 0000000..844175f --- /dev/null +++ b/include/logger.h @@ -0,0 +1,37 @@ +/* + * vim: sw=2 ts=2 expandtab + * + * "THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"): + * Thomas Kolb wrote this file. As long as you retain this + * notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a pizza in return. + * - Thomas Kolb + */ + +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include + +static const int LVL_FATAL = 0; /*!< Fatal message level */ +static const int LVL_ERR = 5; /*!< Error message level */ +static const int LVL_WARN = 50; /*!< Warning message level */ +static const int LVL_INFO = 100; /*!< Information message level */ +static const int LVL_DEBUG = 200; /*!< Debug message level */ +static const int LVL_DUMP = 500; /*!< Dump message level */ + +extern sem_t logger_semaphore; +extern int logger_verbosity; +extern int logger_use_colors; + +void logger_init(void); +void logger_shutdown(void); +void logger_enable_colors(int enable); +void logger_set_verbosity(int verbosity); + +void logger_log(int level, const char *format, ...); + +#define LOG(level, ...) logger_log(level, __VA_ARGS__) + +#endif // LOGGER_H diff --git a/include/ws2801.h b/include/ws2801.h new file mode 100644 index 0000000..7db9b57 --- /dev/null +++ b/include/ws2801.h @@ -0,0 +1,11 @@ +#ifndef WS2801_H +#define WS2801_H + +#include + +int ws2801_init(uint8_t num_modules); +void ws2801_shutdown(void); +void ws2801_set_colour(uint8_t module, uint8_t red, uint8_t green, uint8_t blue); +void ws2801_send_update(void); + +#endif // WS2801_H diff --git a/src/logger.c b/src/logger.c new file mode 100644 index 0000000..2618482 --- /dev/null +++ b/src/logger.c @@ -0,0 +1,161 @@ +/* + * vim: sw=2 ts=2 expandtab + * + * "THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"): + * Thomas Kolb wrote this file. As long as you retain this + * notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a pizza in return. + * - Thomas Kolb + */ + +#include +#include + +#include +#include + +#include "logger.h" + +// define constants +const char *LOGGER_STR_FATAL = "FATAL"; +const char *LOGGER_STR_ERR = "ERROR"; +const char *LOGGER_STR_WARN = "WARN "; +const char *LOGGER_STR_INFO = "INFO "; +const char *LOGGER_STR_DEBUG = "DEBUG"; +const char *LOGGER_STR_DUMP = "DUMP "; + +const char *LOGGER_COLOR_FATAL = "\033[1;31m"; +const char *LOGGER_COLOR_ERR = "\033[1;31m"; +const char *LOGGER_COLOR_WARN = "\033[1;33m"; +const char *LOGGER_COLOR_INFO = "\033[1;32m"; +const char *LOGGER_COLOR_DEBUG = "\033[1m"; +const char *LOGGER_COLOR_DUMP = "\033[1;30m"; +const char *LOGGER_COLOR_NONE = "\033[0m"; + +// global variables +sem_t logger_semaphore; +int logger_verbosity; +int logger_use_colors; + +void logger_init(void) { + // Initialize the semaphore + sem_init(&logger_semaphore, 0, 1); + + logger_verbosity = 2147483647; + logger_use_colors = 1; +} + +void logger_shutdown(void) { + sem_destroy(&logger_semaphore); +} + +void logger_enable_colors(int enable) { + logger_use_colors = enable; +} + +void logger_set_verbosity(int verbosity) { + logger_verbosity = verbosity; +} + +void logger_debug_message(const char *prefix, const char *fmt, va_list ap) { + /* Guess we need no more than 100 bytes. */ + int n, size = 100; + char *p, *np; + va_list internal_ap; + + if ((p = (char*)malloc(size)) == NULL) { + fprintf(stderr, "[%s] FATAL: Cannot allocate string buffer while processing arguments.\n", LOGGER_STR_ERR); + return; + } + + while (1) { + /* Try to print in the allocated space. */ + va_copy(internal_ap, ap); + n = vsnprintf(p, size, fmt, internal_ap); + va_end(internal_ap); + + /* If that worked, return the string. */ + if (n > -1 && n < size) + break; + + /* Else try again with more space. */ + if (n > -1) /* glibc 2.1 */ + size = n+1; /* precisely what is needed */ + else /* glibc 2.0 */ + size *= 2; /* twice the old size */ + + if ((np = (char*)realloc (p, size)) == NULL) { + free(p); + fprintf(stderr, "[%s] FATAL: Cannot reallocate string buffer while processing arguments.\n", LOGGER_STR_ERR); + return; + } else { + p = np; + } + } + + sem_wait(&logger_semaphore); + fprintf(stderr, "%s %s\n", prefix, p); + sem_post(&logger_semaphore); + + free(p); +} + +void logger_log(int level, const char *format, ...) { + va_list argptr; + + char timebuf[32]; + char timebuf2[32]; + + char prefixbuf[64]; + const char *prefixcolor = "", *prefixtext = ""; + + if(level > logger_verbosity) + return; + + struct timeval tv; + gettimeofday(&tv, NULL); + strftime(timebuf, 32, "%Y-%M-%d %H:%M:%S.%%03d", localtime(&(tv.tv_sec))); + snprintf(timebuf2, 32, timebuf, tv.tv_usec/1000); + + if(level >= LVL_DUMP) { + if(logger_use_colors) + prefixcolor = LOGGER_COLOR_DUMP; + + prefixtext = LOGGER_STR_DUMP; + } else if(level >= LVL_DEBUG) { + if(logger_use_colors) + prefixcolor = LOGGER_COLOR_DEBUG; + + prefixtext = LOGGER_STR_DEBUG; + } else if(level >= LVL_INFO) { + if(logger_use_colors) + prefixcolor = LOGGER_COLOR_INFO; + + prefixtext = LOGGER_STR_INFO; + } else if(level >= LVL_WARN) { + if(logger_use_colors) + prefixcolor = LOGGER_COLOR_WARN; + + prefixtext = LOGGER_STR_WARN; + } else if(level >= LVL_ERR) { + if(logger_use_colors) + prefixcolor = LOGGER_COLOR_ERR; + + prefixtext = LOGGER_STR_ERR; + } else { + if(logger_use_colors) + prefixcolor = LOGGER_COLOR_FATAL; + + prefixtext = LOGGER_STR_FATAL; + } + + if(logger_use_colors) { + sprintf(prefixbuf, "%s [%s%s%s]", timebuf2, prefixcolor, prefixtext, LOGGER_COLOR_NONE); + } else { + sprintf(prefixbuf, "%s [%s]", timebuf2, prefixtext); + } + + va_start(argptr, format); + logger_debug_message(prefixbuf, format, argptr); + va_end(argptr); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..afe9de7 --- /dev/null +++ b/src/main.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../include/ws2801.h" +#include "../include/logger.h" + +#define PI 3.141592654f + +void set_all(uint8_t r, uint8_t g, uint8_t b) +{ + for(uint8_t i = 0; i < 20; i++) { + ws2801_set_colour(i, r, g, b); + } +} + +float sin_off(float phase) +{ + return 255 * (0.5f + 0.5f * sin(phase)); +} + +int main(void) +{ + float phase = 0; + + logger_init(); + + if(ws2801_init(20) == -1) { + LOG(LVL_FATAL, "Could not initialize WS2801 library."); + return 1; + } + + + while(1) { + //phase += PI/1000; + + for(uint8_t i = 0; i < 20; i++) { + ws2801_set_colour(i, sin_off(i * PI / 10 + phase), sin_off(i * PI / 10 + 1.241*phase), sin_off(i * PI / 10 + 1.537*phase)); + } + + ws2801_send_update(); + + static const struct timespec sleepval = {0, 40000000}; + nanosleep(&sleepval, NULL); + } + + ws2801_shutdown(); + + return 0; +} diff --git a/src/ws2801.c b/src/ws2801.c new file mode 100644 index 0000000..fddf4ee --- /dev/null +++ b/src/ws2801.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +#include + +#include "../include/logger.h" +#include "../include/ws2801.h" + +#define WS2801_CLK_GPIO 17 +#define WS2801_DAT_GPIO 18 + +// 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; + +void busy_wait(void) +{ + static const struct timespec bitsleep = {0, 1000}; + nanosleep(&bitsleep, NULL); +} + +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; + } +} + +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)); + return -1; + } + + numModules = nMod; + modRed = malloc(nMod * sizeof(uint8_t)); + modGreen = malloc(nMod * sizeof(uint8_t)); + modBlue = malloc(nMod * sizeof(uint8_t)); + + if(!modRed || !modGreen || !modBlue) { + LOG(LVL_ERR, "ws2801: could not allocate all colour arrays!"); + return -1; + } + + return 0; +} + +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; } +} + +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; +} + +void ws2801_send_update(void) +{ + for(uint8_t i = 0; i < numModules; i++) { + send_colour(modRed[i], modGreen[i], modBlue[i]); + } + + send_commit_condition(); + + static const struct timespec sleepval = {0, 550000}; + nanosleep(&sleepval, NULL); +} +