summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Baumann <derflob@derflob.de>2014-09-09 00:51:13 +0200
committerFlorian Baumann <derflob@derflob.de>2014-09-09 00:51:13 +0200
commit592cbb591f3bb8364a569d016c0d18a1777a31fb (patch)
treec92b81c7f49f3cb5f2635bf597af4797615cd7bd
downloadStripClock-592cbb591f3bb8364a569d016c0d18a1777a31fb.tar.gz
StripClock-592cbb591f3bb8364a569d016c0d18a1777a31fb.tar.bz2
initial
-rw-r--r--.gitignore10
-rw-r--r--Makefile441
-rw-r--r--StripClock.c263
-rw-r--r--ds1307.c138
-rw-r--r--ds1307.h34
-rw-r--r--i2cmaster.S313
-rw-r--r--i2cmaster.h178
-rw-r--r--light_ws2812.c170
-rw-r--r--light_ws2812.h63
-rw-r--r--ws2812_config.h21
10 files changed, 1631 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c1eca99
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+.dep
+*.swp
+*.lst
+*.o
+*.eep
+*.elf
+*.hex
+*.lss
+*.map
+*.sym
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5a3e5aa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,441 @@
+# Hey Emacs, this is a -*- makefile -*-
+#
+# WinAVR makefile written by Eric B. Weddington, Jörg Wunsch, et al.
+# Released to the Public Domain
+# Please read the make user manual!
+#
+# Additional material for this makefile was submitted by:
+# Tim Henigan
+# Peter Fleury
+# Reiner Patommel
+# Sander Pool
+# Frederik Rouleau
+# Markus Pfaff
+#
+# On command line:
+#
+# make all = Make software.
+#
+# make clean = Clean out built project files.
+#
+# make coff = Convert ELF to AVR COFF (for use with AVR Studio 3.x or VMLAB).
+#
+# make extcoff = Convert ELF to AVR Extended COFF (for use with AVR Studio
+# 4.07 or greater).
+#
+# make program = Download the hex file to the device, using avrdude. Please
+# customize the avrdude settings below first!
+#
+# make filename.s = Just compile filename.c into the assembler code only
+#
+# To rebuild project do "make clean" then "make all".
+#
+
+# mth 2004/09
+# Differences from WinAVR 20040720 sample:
+# - DEPFLAGS according to Eric Weddingtion's fix (avrfreaks/gcc-forum)
+# - F_OSC Define in CFLAGS and AFLAGS
+
+
+# MCU name
+MCU = attiny2313a
+AVRDUDE_MCU = t2313
+
+# Main Oscillator Frequency
+# This is only used to define F_OSC in all assembler and c-sources.
+F_OSC = 20000000
+
+# Output format. (can be srec, ihex, binary)
+FORMAT = ihex
+
+# Target file name (without extension).
+TARGET = StripClock
+
+
+# List C source files here. (C dependencies are automatically generated.)
+SRC = $(TARGET).c ds1307.c light_ws2812.c
+
+
+# List Assembler source files here.
+# Make them always end in a capital .S. Files ending in a lowercase .s
+# will not be considered source files but generated files (assembler
+# output from the compiler), and will be deleted upon "make clean"!
+# Even though the DOS/Win* filesystem matches both .s and .S the same,
+# it will preserve the spelling of the filenames, and gcc itself does
+# care about how the name is spelled on its command-line.
+ASRC = i2cmaster.S
+
+
+AVRDUDE_FUSES = -U lfuse:w:0xff:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m
+
+
+# Optimization level, can be [0, 1, 2, 3, s].
+# 0 = turn off optimization. s = optimize for size.
+# (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
+OPT = s
+
+# Debugging format.
+# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
+# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
+#DEBUG = stabs
+DEBUG = dwarf-2
+
+# List any extra directories to look for include files here.
+# Each directory must be seperated by a space.
+EXTRAINCDIRS =
+
+
+# Compiler flag to set the C Standard level.
+# c89 - "ANSI" C
+# gnu89 - c89 plus GCC extensions
+# c99 - ISO C99 standard (not yet fully implemented)
+# gnu99 - c99 plus GCC extensions
+CSTANDARD = -std=gnu99
+
+# Place -D or -U options here
+CDEFS = -DF_CPU=20000000UL
+
+# Place -I options here
+CINCS =
+
+
+# Compiler flags.
+# -g*: generate debugging information
+# -O*: optimization level
+# -f...: tuning, see GCC manual and avr-libc documentation
+# -Wall...: warning level
+# -Wa,...: tell GCC to pass this to the assembler.
+# -adhlns...: create assembler listing
+CFLAGS = -g$(DEBUG)
+CFLAGS += $(CDEFS) $(CINCS)
+CFLAGS += -O$(OPT)
+CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
+CFLAGS += -Wall -Wstrict-prototypes
+CFLAGS += -Wa,-adhlns=$(<:.c=.lst)
+CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
+CFLAGS += $(CSTANDARD)
+CFLAGS += -DF_OSC=$(F_OSC)
+
+
+
+# Assembler flags.
+# -Wa,...: tell GCC to pass this to the assembler.
+# -ahlms: create listing
+# -gstabs: have the assembler create line number information; note that
+# for use in COFF files, additional information about filenames
+# and function names needs to be present in the assembler source
+# files -- see avr-libc docs [FIXME: not yet described there]
+ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs
+ASFLAGS += -DF_OSC=$(F_OSC)
+ASFLAGS += $(CDEFS)
+
+
+#Additional libraries.
+
+# Minimalistic printf version
+PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min
+
+# Floating point printf version (requires MATH_LIB = -lm below)
+PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt
+
+PRINTF_LIB =
+
+# Minimalistic scanf version
+SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min
+
+# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
+SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt
+
+SCANF_LIB =
+
+MATH_LIB = -lm
+
+# External memory options
+
+# 64 KB of external RAM, starting after internal RAM (ATmega128!),
+# used for variables (.data/.bss) and heap (malloc()).
+#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff
+
+# 64 KB of external RAM, starting after internal RAM (ATmega128!),
+# only used for heap (malloc()).
+#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff
+
+EXTMEMOPTS =
+
+# Linker flags.
+# -Wl,...: tell GCC to pass this to linker.
+# -Map: create map file
+# --cref: add cross reference to map file
+LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
+LDFLAGS += $(EXTMEMOPTS)
+LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)
+
+
+
+
+# Programming support using avrdude. Settings and variables.
+
+# Programming hardware: alf avr910 avrisp bascom bsd
+# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500
+#
+# Type: avrdude -c ?
+# to get a full listing.
+#
+AVRDUDE_PROGRAMMER = dragon_isp
+
+# com1 = serial port. Use lpt1 to connect to parallel port.
+AVRDUDE_PORT = /dev/ttyUSB0 # programmer connected to serial device
+
+AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
+#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep
+
+
+# Uncomment the following if you want avrdude's erase cycle counter.
+# Note that this counter needs to be initialized first using -Yn,
+# see avrdude manual.
+#AVRDUDE_ERASE_COUNTER = -y
+
+# Uncomment the following if you do /not/ wish a verification to be
+# performed after programming the device.
+#AVRDUDE_NO_VERIFY = -V
+
+# Increase verbosity level. Please use this when submitting bug
+# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude>
+# to submit bug reports.
+#AVRDUDE_VERBOSE = -v -v
+
+
+AVRDUDE_FLAGS = -p $(AVR_DUDE_MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
+AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY)
+AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE)
+AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER)
+AVRDUDE_FLAGS += $(AVR_FUSES)
+
+
+# ---------------------------------------------------------------------------
+
+# Define directories, if needed.
+DIRAVR = /usr
+DIRAVRBIN = $(DIRAVR)/bin
+DIRAVRUTILS = $(DIRAVR)/bin
+DIRINC = .
+DIRLIB = $(DIRAVR)/avr/lib
+
+
+# Define programs and commands.
+SHELL = sh
+CC = avr-gcc
+OBJCOPY = avr-objcopy
+OBJDUMP = avr-objdump
+SIZE = avr-size
+NM = avr-nm
+AVRDUDE = avrdude
+REMOVE = rm -f
+COPY = cp
+
+
+
+
+# Define Messages
+# English
+MSG_ERRORS_NONE = Errors: none
+MSG_BEGIN = -------- begin --------
+MSG_END = -------- end --------
+MSG_SIZE_BEFORE = Size before:
+MSG_SIZE_AFTER = Size after:
+MSG_COFF = Converting to AVR COFF:
+MSG_EXTENDED_COFF = Converting to AVR Extended COFF:
+MSG_FLASH = Creating load file for Flash:
+MSG_EEPROM = Creating load file for EEPROM:
+MSG_EXTENDED_LISTING = Creating Extended Listing:
+MSG_SYMBOL_TABLE = Creating Symbol Table:
+MSG_LINKING = Linking:
+MSG_COMPILING = Compiling:
+MSG_ASSEMBLING = Assembling:
+MSG_CLEANING = Cleaning project:
+
+
+
+
+# Define all object files.
+OBJ = $(SRC:.c=.o) $(ASRC:.S=.o)
+
+# Define all listing files.
+LST = $(ASRC:.S=.lst) $(SRC:.c=.lst)
+
+
+# Compiler flags to generate dependency files.
+### GENDEPFLAGS = -Wp,-M,-MP,-MT,$(*F).o,-MF,.dep/$(@F).d
+GENDEPFLAGS = -MD -MP -MF .dep/$(@F).d
+
+# Combine all necessary flags and optional flags.
+# Add target processor to flags.
+ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
+ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
+
+
+
+
+
+# Default target.
+all: begin gccversion sizebefore build sizeafter finished end
+
+build: elf hex eep lss sym
+
+elf: $(TARGET).elf
+hex: $(TARGET).hex
+eep: $(TARGET).eep
+lss: $(TARGET).lss
+sym: $(TARGET).sym
+
+
+
+# Eye candy.
+# AVR Studio 3.x does not check make's exit code but relies on
+# the following magic strings to be generated by the compile job.
+begin:
+ @echo
+ @echo $(MSG_BEGIN)
+
+finished:
+ @echo $(MSG_ERRORS_NONE)
+
+end:
+ @echo $(MSG_END)
+ @echo
+
+
+# Display size of file.
+HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
+ELFSIZE = $(SIZE) -A $(TARGET).elf
+sizebefore:
+ @if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); echo; fi
+
+sizeafter:
+ @if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); echo; fi
+
+
+
+# Display compiler version information.
+gccversion :
+ @$(CC) --version
+
+
+
+# Program the device.
+program: $(TARGET).hex $(TARGET).eep
+ $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM)
+
+
+
+
+# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
+COFFCONVERT=$(OBJCOPY) --debugging \
+--change-section-address .data-0x800000 \
+--change-section-address .bss-0x800000 \
+--change-section-address .noinit-0x800000 \
+--change-section-address .eeprom-0x810000
+
+
+coff: $(TARGET).elf
+ @echo
+ @echo $(MSG_COFF) $(TARGET).cof
+ $(COFFCONVERT) -O coff-avr $< $(TARGET).cof
+
+
+extcoff: $(TARGET).elf
+ @echo
+ @echo $(MSG_EXTENDED_COFF) $(TARGET).cof
+ $(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof
+
+
+
+# Create final output files (.hex, .eep) from ELF output file.
+%.hex: %.elf
+ @echo
+ @echo $(MSG_FLASH) $@
+ $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
+
+%.eep: %.elf
+ @echo
+ @echo $(MSG_EEPROM) $@
+ -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
+ --change-section-lma .eeprom=0 -O $(FORMAT) $< $@
+
+# Create extended listing file from ELF output file.
+%.lss: %.elf
+ @echo
+ @echo $(MSG_EXTENDED_LISTING) $@
+ $(OBJDUMP) -h -S $< > $@
+
+# Create a symbol table from ELF output file.
+%.sym: %.elf
+ @echo
+ @echo $(MSG_SYMBOL_TABLE) $@
+ $(NM) -n $< > $@
+
+
+
+# Link: create ELF output file from object files.
+.SECONDARY : $(TARGET).elf
+.PRECIOUS : $(OBJ)
+%.elf: $(OBJ)
+ @echo
+ @echo $(MSG_LINKING) $@
+ $(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS)
+
+
+# Compile: create object files from C source files.
+%.o : %.c
+ @echo
+ @echo $(MSG_COMPILING) $<
+ $(CC) -c $(ALL_CFLAGS) $< -o $@
+
+
+# Compile: create assembler files from C source files.
+%.s : %.c
+ $(CC) -S $(ALL_CFLAGS) $< -o $@
+
+
+# Assemble: create object files from assembler source files.
+%.o : %.S
+ @echo
+ @echo $(MSG_ASSEMBLING) $<
+ $(CC) -c $(ALL_ASFLAGS) $< -o $@
+
+
+
+# Target: clean project.
+clean: begin clean_list finished end
+
+clean_list :
+ @echo
+ @echo $(MSG_CLEANING)
+ $(REMOVE) $(TARGET).hex
+ $(REMOVE) $(TARGET).eep
+ $(REMOVE) $(TARGET).obj
+ $(REMOVE) $(TARGET).cof
+ $(REMOVE) $(TARGET).elf
+ $(REMOVE) $(TARGET).map
+ $(REMOVE) $(TARGET).obj
+ $(REMOVE) $(TARGET).a90
+ $(REMOVE) $(TARGET).sym
+ $(REMOVE) $(TARGET).lnk
+ $(REMOVE) $(TARGET).lss
+ $(REMOVE) $(OBJ)
+ $(REMOVE) $(LST)
+ $(REMOVE) $(SRC:.c=.s)
+ $(REMOVE) $(SRC:.c=.d)
+ $(REMOVE) .dep/*
+
+
+
+# Include the dependency files.
+-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)
+
+
+# Listing of phony targets.
+.PHONY : all begin finish end sizebefore sizeafter gccversion \
+build elf hex eep lss sym coff extcoff \
+clean clean_list program
+
diff --git a/StripClock.c b/StripClock.c
new file mode 100644
index 0000000..be723be
--- /dev/null
+++ b/StripClock.c
@@ -0,0 +1,263 @@
+#include <avr/interrupt.h>
+#include <avr/io.h>
+#include <string.h>
+
+#include "i2cmaster.h"
+#include "ds1307.h"
+#include "light_ws2812.h"
+
+
+#define BTN_MODE DDD3
+#define BTN_MODE_PORT PORTD
+#define BTN_MODE_PIN PIND
+#define BTN_MODE_DDR DDRD
+
+#define BTN_INC DDB0
+#define BTN_INC_PORT PORTB
+#define BTN_INC_PIN PINB
+#define BTN_INC_DDR DDRB
+
+enum display_mode {
+ DM_NONE,
+ DM_HOUR,
+ DM_MINUTE,
+ DM_SECOND,
+ DM_END
+};
+
+volatile uint8_t updated = 0;
+volatile uint16_t ticks = 4095;
+
+volatile uint8_t btn_debounce = 0;
+
+volatile enum display_mode mode = DM_NONE;
+
+volatile struct datetime dt;
+
+#define LEDS 60
+struct cRGB leds[LEDS];
+
+uint8_t updateStrip(void);
+
+int main(void)
+{
+ cli();
+ //enable INT0 on falling edges
+ MCUCR |= (1 << ISC01);
+ //enable INT0 on falling edges
+ MCUCR |= (1 << ISC11);
+ GIMSK |= ((1 << INT1) | (1 << INT0));
+
+ //set BTN_* as input and pull-ups high
+ BTN_INC_DDR &= ~(1 << BTN_INC);
+ BTN_INC_PORT |= (1 << BTN_INC);
+ BTN_MODE_DDR &= ~(1 << BTN_MODE);
+ BTN_MODE_PORT |= (1 << BTN_MODE);
+
+ //set INT0 from sqw as input
+ PORTD &= ~(1 << DDD2);
+
+ i2c_init();
+
+ ds1307_start();
+ ds1307_SQWRate(SQW_4kHz);
+ ds1307_SQW(1);
+
+ ds1307_getDateTime(&dt);
+
+ sei();
+
+ for(;;) {
+ if (mode == DM_NONE && updated) {
+ ds1307_getDateTime(&dt);
+ updateStrip();
+ updated = 0;
+ } else {
+ switch (mode) {
+ memset(leds, 0, sizeof(struct cRGB) * LEDS);
+ case DM_HOUR:
+ for (uint8_t i = 0; i < dt.hour; i++) {
+ leds[i].r = 255;
+ if (i % 5 == 0) {
+ leds[i].g = 50;
+ leds[i].b = 50;
+ }
+ }
+ ws2812_setleds(leds, LEDS);
+ break;
+ case DM_MINUTE:
+ for (uint8_t i = 0; i < dt.minute; i++) {
+ leds[i].g = 255;
+ if (i % 5 == 0) {
+ leds[i].r = 50;
+ leds[i].b = 50;
+ }
+ }
+ ws2812_setleds(leds, LEDS);
+ break;
+ case DM_SECOND:
+ for (uint8_t i = 0; i < dt.hour; i++) {
+ leds[i].b = 255;
+ if (i % 5 == 0) {
+ leds[i].r = 50;
+ leds[i].g = 50;
+ }
+ }
+ ws2812_setleds(leds, LEDS);
+ break;
+ case DM_END:
+ ds1307_setDateTime(&dt);
+ mode = DM_NONE;
+ updateStrip();
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+}
+
+ISR(INT0_vect)
+{
+ //after 4096 ticks 1 sec is elapsed
+ if (--ticks == 0) {
+ updated = 1;
+ ticks = 4095;
+ }
+}
+
+ISR(INT1_vect)
+{
+ //debounce button, by temp disabling this interrupt
+ GIMSK &= ~(1 << INT1);
+
+ if (mode == DM_NONE) {
+ //enable TIMER0 in normal mode, prescale 1024
+ TCCR0A = 0;
+ TCCR0B = ((1 << CS02) | (1 << CS00));
+ //enable overflow intr
+ TIMSK |= (1 << TOIE0);
+ }
+
+ mode++;
+
+
+ //ctc mode
+ TCCR1A = 0;
+ //ctc && precale 1024
+ TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);
+ //should be about 50ms
+ OCR1A = 1024;
+
+ //enable ctc intrrupt
+ TIMSK |= (1 << OCIE1A);
+}
+
+ISR(TIMER1_COMPA_vect)
+{
+ //debounce of BTN_MODE
+
+ //disable timer
+ TCCR1B = 0;
+ //disable this interrupt
+ TIMSK &= ~(1 << OCIE1A);
+
+ //enable INT1
+ GIMSK |= (1 << INT1);
+}
+
+ISR(TIMER0_OVF_vect)
+{
+ //after 30 overflows, ~ 360ms elapsed
+ if (++btn_debounce == 30) {
+ btn_debounce = 0;
+
+ if (BTN_INC_PIN & (1 << BTN_INC)) {
+ switch (mode) {
+ case DM_HOUR:
+ dt.hour = (++dt.hour) % 24;
+ break;
+ case DM_MINUTE:
+ dt.minute = (++dt.second) % 60;
+ break;
+ case DM_SECOND:
+ dt.second = (++dt.second) % 60;
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+}
+
+
+
+uint8_t updateStrip(void)
+{
+ uint8_t hour_led = ((dt.hour % 12) * (LEDS/12)) - 1;
+ if (hour_led == -1)
+ hour_led = 59;
+
+ for (uint8_t i = 0; i < LEDS; i++) {
+ int16_t r, g, b = 0;
+
+ const int16_t cur_min_r = 4;
+ const int16_t cur_min_g = 16;
+ const int16_t cur_min_b = 1;
+ const int16_t past_min_r = 2;
+ const int16_t past_min_g = 2;
+ const int16_t past_min_b = 1;
+
+ if (i < dt.minute - 1) {
+ r = g = 2;
+ b = 1;
+ } else if (i == dt.minute - 1 || (i == 59 && dt.minute == 0)) {
+ r = cur_min_r + (past_min_r - cur_min_r) * (int16_t)dt.second / 60;
+ g = cur_min_g + (past_min_g - cur_min_g) * (int16_t)dt.second / 60;
+ b = cur_min_b + (past_min_b - cur_min_b) * (int16_t)dt.second / 60;
+
+
+ //r = scalePing((uint8_t)r);
+ //g = scalePing((uint8_t)g);
+ //b = scalePing((uint8_t)b);
+ } else if (i == dt.minute) {
+ r = past_min_r + (cur_min_r - past_min_r) * (int16_t)dt.second / 60;
+ g = past_min_g + (cur_min_g - past_min_g) * (int16_t)dt.second / 60;
+ b = past_min_b + (cur_min_b - past_min_b) * (int16_t)dt.second / 60;
+
+ //r = scalePing((uint8_t)r);
+ //g = scalePing((uint8_t)g);
+ //b = scalePing((uint8_t)b);
+ } else if (i % 5 == 4) {
+ r = past_min_r;
+ g = past_min_g;
+ b = past_min_b;
+ } else {
+ r = g = b = 0;
+ }
+ if (i % 15 == 14) {
+ r *= 5;
+ g *= 10;
+ b *= 5;
+ } else if (i % 5 == 4) {
+ r *= 7;
+ g *= 7;
+ b *= 6;
+ }
+
+ if (i == hour_led) {
+ g += 50;
+ r += 40;
+ }
+
+ leds[i].r = r;
+ leds[i].g = g;
+ leds[i].b = b;
+ }
+
+ ws2812_setleds(leds, LEDS);
+
+ return 0;
+}
diff --git a/ds1307.c b/ds1307.c
new file mode 100644
index 0000000..6f1df2c
--- /dev/null
+++ b/ds1307.c
@@ -0,0 +1,138 @@
+#include "i2cmaster.h"
+#include "ds1307.h"
+
+static uint8_t ds1307_init_rw(unsigned char reg);
+static uint8_t ds1307_init_write(unsigned char reg);
+static uint8_t ds1307_init_read(unsigned char reg);
+
+
+#define BCD2DEC(x) ((0x0F & (x)) + 10 * ((x) >> 4))
+#define DEC2BCD(x) ((((x) / 10) << 4) | ((x) % 10))
+
+uint8_t ds1307_start(void)
+{
+ //set 24 hour mode
+ ds1307_init_read(0x02);
+ unsigned char hour = i2c_readNak();
+ hour &= ~(1 << DS1307_BIT_AMPM);
+ ds1307_init_write(0x02);
+ i2c_write(hour);
+ i2c_stop();
+
+ //unhalt clock
+ ds1307_halt(0);
+
+ return 0;
+}
+
+static uint8_t ds1307_init_rw(unsigned char reg)
+{
+ i2c_start_wait(DS1307_ADDR + I2C_WRITE);
+ i2c_write(reg);
+ return 0;
+
+}
+
+static uint8_t ds1307_init_write(unsigned char reg)
+{
+ return ds1307_init_rw(reg);
+}
+
+static uint8_t ds1307_init_read(unsigned char reg)
+{
+ ds1307_init_rw(reg);
+ i2c_rep_start(DS1307_ADDR + I2C_READ);
+ return 0;
+}
+
+uint8_t ds1307_halt(uint8_t halted)
+{
+ ds1307_init_read(0x00);
+ unsigned char sec = i2c_readAck();
+
+ if (halted)
+ sec |= (1 << DS1307_BIT_CH);
+ else
+ sec &= ~(1 << DS1307_BIT_CH);
+
+ ds1307_init_write(0x00);
+ i2c_write(sec);
+ i2c_stop();
+
+ return 0;
+}
+
+
+uint8_t ds1307_getDateTime(struct datetime* dt)
+{
+ unsigned char c;
+ ds1307_init_read(0x00);
+ //omit CH bit
+ dt->second = BCD2DEC(i2c_readAck() & ~(1 << DS1307_BIT_CH));
+ dt->minute = BCD2DEC(i2c_readAck());
+ //omit AMPM bit (which should be 0 because we only have 24 hour mode)
+ dt->hour = BCD2DEC(i2c_readAck() & ~(1 << DS1307_BIT_AMPM));
+ dt->dow = BCD2DEC(i2c_readAck());
+ dt->day = BCD2DEC(i2c_readAck());
+ dt->month = BCD2DEC(i2c_readAck());
+ dt->year = 2000 + BCD2DEC(i2c_readNak());
+
+ return 0;
+}
+
+uint8_t ds1307_setDateTime(struct datetime* dt)
+{
+ unsigned char ch = 0;
+ unsigned char ampm = 0;
+
+ ds1307_init_read(0x00);
+ ch = i2c_readAck() & (1 << DS1307_BIT_CH);
+ i2c_readAck();
+ ampm = i2c_readNak() & (1 << DS1307_BIT_AMPM);
+
+
+ ds1307_init_write(0x00);
+ i2c_write(DEC2BCD(dt->second) | (ch << DS1307_BIT_CH));
+ i2c_write(DEC2BCD(dt->minute));
+ i2c_write(DEC2BCD(dt->hour) | (ampm << DS1307_BIT_AMPM));
+ i2c_write(DEC2BCD(dt->dow));
+ i2c_write(DEC2BCD(dt->day));
+ i2c_write(DEC2BCD(dt->month));
+ i2c_write(DEC2BCD(dt->year - 2000));
+ i2c_stop();
+
+ return 0;
+}
+
+uint8_t ds1307_SQW(uint8_t enabled)
+{
+ unsigned char sqw;
+ ds1307_init_read(0x07);
+ sqw = i2c_readNak();
+
+ if (enabled)
+ sqw |= (1 << DS1307_BIT_SQWE);
+ else
+ sqw &= ~(1 << DS1307_BIT_SQWE);
+
+ ds1307_init_write(0x07);
+ i2c_write(sqw);
+ i2c_stop();
+
+ return 0;
+}
+
+uint8_t ds1307_SQWRate(enum SQWRate rate)
+{
+ unsigned char sqw;
+ ds1307_init_read(0x07);
+ sqw = i2c_readNak();
+
+ sqw = (sqw & ~(0x3)) | (rate & 0x3);
+
+ ds1307_init_write(0x07);
+ i2c_write(sqw);
+ i2c_stop();
+
+ return 0;
+}
diff --git a/ds1307.h b/ds1307.h
new file mode 100644
index 0000000..fb24b2b
--- /dev/null
+++ b/ds1307.h
@@ -0,0 +1,34 @@
+#ifndef DS1307_H_
+#define DS1307_H_
+
+#define DS1307_ADDR 0b11010000
+#define DS1307_BIT_CH 7
+#define DS1307_BIT_AMPM 6
+#define DS1307_BIT_SQWE 4
+
+enum SQWRate {
+ SQW_1Hz = 0,
+ SQW_4kHz = 1,
+ SQW_8kHz = 2,
+ SQW_32kHz = 3
+};
+
+struct datetime {
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ uint8_t dow;
+};
+
+uint8_t ds1307_start(void);
+uint8_t ds1307_halt(uint8_t halted);
+uint8_t ds1307_SQW(uint8_t enabled);
+uint8_t ds1307_SQWRate(enum SQWRate rate);
+uint8_t ds1307_getDateTime(struct datetime* dt);
+uint8_t ds1307_setDateTime(struct datetime* dt);
+
+
+#endif
diff --git a/i2cmaster.S b/i2cmaster.S
new file mode 100644
index 0000000..37ad5a3
--- /dev/null
+++ b/i2cmaster.S
@@ -0,0 +1,313 @@
+;*************************************************************************
+; Title : I2C (Single) Master Implementation
+; Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
+; based on Atmel Appl. Note AVR300
+; File: $Id: i2cmaster.S,v 1.12 2008/03/02 08:51:27 peter Exp $
+; Software: AVR-GCC 3.3 or higher
+; Target: any AVR device
+;
+; DESCRIPTION
+; Basic routines for communicating with I2C slave devices. This
+; "single" master implementation is limited to one bus master on the
+; I2C bus.
+;
+; Based on the Atmel Application Note AVR300, corrected and adapted
+; to GNU assembler and AVR-GCC C call interface
+; Replaced the incorrect quarter period delays found in AVR300 with
+; half period delays.
+;
+; USAGE
+; These routines can be called from C, refere to file i2cmaster.h.
+; See example test_i2cmaster.c
+; Adapt the SCL and SDA port and pin definitions and eventually
+; the delay routine to your target !
+; Use 4.7k pull-up resistor on the SDA and SCL pin.
+;
+; NOTES
+; The I2C routines can be called either from non-interrupt or
+; interrupt routines, not both.
+;
+;*************************************************************************
+
+#if (__GNUC__ * 100 + __GNUC_MINOR__) < 303
+#error "This library requires AVR-GCC 3.3 or later, update to newer AVR-GCC compiler !"
+#endif
+
+
+#include <avr/io.h>
+
+
+
+;***** Adapt these SCA and SCL port and pin definition to your target !!
+;
+#define SDA 1 // SDA Port D, Pin 4
+#define SCL 2 // SCL Port D, Pin 5
+#define SDA_PORT PORTB // SDA Port B
+#define SCL_PORT PORTB // SCL Port 2
+
+;******
+
+;-- map the IO register back into the IO address space
+#define SDA_DDR (_SFR_IO_ADDR(SDA_PORT) - 1)
+#define SCL_DDR (_SFR_IO_ADDR(SCL_PORT) - 1)
+#define SDA_OUT _SFR_IO_ADDR(SDA_PORT)
+#define SCL_OUT _SFR_IO_ADDR(SCL_PORT)
+#define SDA_IN (_SFR_IO_ADDR(SDA_PORT) - 2)
+#define SCL_IN (_SFR_IO_ADDR(SCL_PORT) - 2)
+
+
+#ifndef __tmp_reg__
+#define __tmp_reg__ 0
+#endif
+
+
+ .section .text
+
+;*************************************************************************
+; delay half period
+; For I2C in normal mode (100kHz), use T/2 > 5us
+; For I2C in fast mode (400kHz), use T/2 > 1.3us
+;*************************************************************************
+ .stabs "",100,0,0,i2c_delay_T2_
+ .stabs "i2cmaster.S",100,0,0,i2c_delay_T2_
+ .func i2c_delay_T2_ ; delay 5.0 microsec with 4 Mhz crystal
+i2c_delay_T2_: ; 4 cycles
+ rjmp 1f ; 2 "
+1: rjmp 2f ; 2 "
+2: rjmp 3f ; 2 "
+3: rjmp 4f ; 2 "
+4: rjmp 5f ; 2 "
+5: rjmp 6f ; 2 "
+6: nop ; 1 "
+ ret ; 3 "
+ .endfunc ; total 20 cyles = 5.0 microsec with 4 Mhz crystal
+
+ .func i2c_delay_T2 ; calling i2c_delay_T2_ five times gives about
+ ; the right timing for 20Mhz crystals +- a bit.
+i2c_delay_T2:
+ rcall i2c_delay_T2_
+ rcall i2c_delay_T2_
+ rcall i2c_delay_T2_
+ rcall i2c_delay_T2_
+ rcall i2c_delay_T2_
+ ret
+ .endfunc
+
+
+;*************************************************************************
+; Initialization of the I2C bus interface. Need to be called only once
+;
+; extern void i2c_init(void)
+;*************************************************************************
+ .global i2c_init
+ .func i2c_init
+i2c_init:
+ cbi SDA_DDR,SDA ;release SDA
+ cbi SCL_DDR,SCL ;release SCL
+ cbi SDA_OUT,SDA
+ cbi SCL_OUT,SCL
+ ret
+ .endfunc
+
+
+;*************************************************************************
+; Issues a start condition and sends address and transfer direction.
+; return 0 = device accessible, 1= failed to access device
+;
+; extern unsigned char i2c_start(unsigned char addr);
+; addr = r24, return = r25(=0):r24
+;*************************************************************************
+
+ .global i2c_start
+ .func i2c_start
+i2c_start:
+ sbi SDA_DDR,SDA ;force SDA low
+ rcall i2c_delay_T2 ;delay T/2
+
+ rcall i2c_write ;write address
+ ret
+ .endfunc
+
+
+;*************************************************************************
+; Issues a repeated start condition and sends address and transfer direction.
+; return 0 = device accessible, 1= failed to access device
+;
+; extern unsigned char i2c_rep_start(unsigned char addr);
+; addr = r24, return = r25(=0):r24
+;*************************************************************************
+
+ .global i2c_rep_start
+ .func i2c_rep_start
+i2c_rep_start:
+ sbi SCL_DDR,SCL ;force SCL low
+ rcall i2c_delay_T2 ;delay T/2
+ cbi SDA_DDR,SDA ;release SDA
+ rcall i2c_delay_T2 ;delay T/2
+ cbi SCL_DDR,SCL ;release SCL
+ rcall i2c_delay_T2 ;delay T/2
+ sbi SDA_DDR,SDA ;force SDA low
+ rcall i2c_delay_T2 ;delay T/2
+
+ rcall i2c_write ;write address
+ ret
+ .endfunc
+
+
+;*************************************************************************
+; Issues a start condition and sends address and transfer direction.
+; If device is busy, use ack polling to wait until device is ready
+;
+; extern void i2c_start_wait(unsigned char addr);
+; addr = r24
+;*************************************************************************
+
+ .global i2c_start_wait
+ .func i2c_start_wait
+i2c_start_wait:
+ mov __tmp_reg__,r24
+i2c_start_wait1:
+ sbi SDA_DDR,SDA ;force SDA low
+ rcall i2c_delay_T2 ;delay T/2
+ mov r24,__tmp_reg__
+ rcall i2c_write ;write address
+ tst r24 ;if device not busy -> done
+ breq i2c_start_wait_done
+ rcall i2c_stop ;terminate write operation
+ rjmp i2c_start_wait1 ;device busy, poll ack again
+i2c_start_wait_done:
+ ret
+ .endfunc
+
+
+;*************************************************************************
+; Terminates the data transfer and releases the I2C bus
+;
+; extern void i2c_stop(void)
+;*************************************************************************
+
+ .global i2c_stop
+ .func i2c_stop
+i2c_stop:
+ sbi SCL_DDR,SCL ;force SCL low
+ sbi SDA_DDR,SDA ;force SDA low
+ rcall i2c_delay_T2 ;delay T/2
+ cbi SCL_DDR,SCL ;release SCL
+ rcall i2c_delay_T2 ;delay T/2
+ cbi SDA_DDR,SDA ;release SDA
+ rcall i2c_delay_T2 ;delay T/2
+ ret
+ .endfunc
+
+
+;*************************************************************************
+; Send one byte to I2C device
+; return 0 = write successful, 1 = write failed
+;
+; extern unsigned char i2c_write( unsigned char data );
+; data = r24, return = r25(=0):r24
+;*************************************************************************
+ .global i2c_write
+ .func i2c_write
+i2c_write:
+ sec ;set carry flag
+ rol r24 ;shift in carry and out bit one
+ rjmp i2c_write_first
+i2c_write_bit:
+ lsl r24 ;if transmit register empty
+i2c_write_first:
+ breq i2c_get_ack
+ sbi SCL_DDR,SCL ;force SCL low
+ brcc i2c_write_low
+ nop
+ cbi SDA_DDR,SDA ;release SDA
+ rjmp i2c_write_high
+i2c_write_low:
+ sbi SDA_DDR,SDA ;force SDA low
+ rjmp i2c_write_high
+i2c_write_high:
+ rcall i2c_delay_T2 ;delay T/2
+ cbi SCL_DDR,SCL ;release SCL
+ rcall i2c_delay_T2 ;delay T/2
+ rjmp i2c_write_bit
+
+i2c_get_ack:
+ sbi SCL_DDR,SCL ;force SCL low
+ cbi SDA_DDR,SDA ;release SDA
+ rcall i2c_delay_T2 ;delay T/2
+ cbi SCL_DDR,SCL ;release SCL
+i2c_ack_wait:
+ sbis SCL_IN,SCL ;wait SCL high (in case wait states are inserted)
+ rjmp i2c_ack_wait
+
+ clr r24 ;return 0
+ sbic SDA_IN,SDA ;if SDA high -> return 1
+ ldi r24,1
+ rcall i2c_delay_T2 ;delay T/2
+ clr r25
+ ret
+ .endfunc
+
+
+
+;*************************************************************************
+; read one byte from the I2C device, send ack or nak to device
+; (ack=1, send ack, request more data from device
+; ack=0, send nak, read is followed by a stop condition)
+;
+; extern unsigned char i2c_read(unsigned char ack);
+; ack = r24, return = r25(=0):r24
+; extern unsigned char i2c_readAck(void);
+; extern unsigned char i2c_readNak(void);
+; return = r25(=0):r24
+;*************************************************************************
+ .global i2c_readAck
+ .global i2c_readNak
+ .global i2c_read
+ .func i2c_read
+i2c_readNak:
+ clr r24
+ rjmp i2c_read
+i2c_readAck:
+ ldi r24,0x01
+i2c_read:
+ ldi r23,0x01 ;data = 0x01
+i2c_read_bit:
+ sbi SCL_DDR,SCL ;force SCL low
+ cbi SDA_DDR,SDA ;release SDA (from previous ACK)
+ rcall i2c_delay_T2 ;delay T/2
+
+ cbi SCL_DDR,SCL ;release SCL
+ rcall i2c_delay_T2 ;delay T/2
+
+i2c_read_stretch:
+ sbis SCL_IN, SCL ;loop until SCL is high (allow slave to stretch SCL)
+ rjmp i2c_read_stretch
+
+ clc ;clear carry flag
+ sbic SDA_IN,SDA ;if SDA is high
+ sec ; set carry flag
+
+ rol r23 ;store bit
+ brcc i2c_read_bit ;while receive register not full
+
+i2c_put_ack:
+ sbi SCL_DDR,SCL ;force SCL low
+ cpi r24,1
+ breq i2c_put_ack_low ;if (ack=0)
+ cbi SDA_DDR,SDA ; release SDA
+ rjmp i2c_put_ack_high
+i2c_put_ack_low: ;else
+ sbi SDA_DDR,SDA ; force SDA low
+i2c_put_ack_high:
+ rcall i2c_delay_T2 ;delay T/2
+ cbi SCL_DDR,SCL ;release SCL
+i2c_put_ack_wait:
+ sbis SCL_IN,SCL ;wait SCL high
+ rjmp i2c_put_ack_wait
+ rcall i2c_delay_T2 ;delay T/2
+ mov r24,r23
+ clr r25
+ ret
+ .endfunc
+
diff --git a/i2cmaster.h b/i2cmaster.h
new file mode 100644
index 0000000..70f51fd
--- /dev/null
+++ b/i2cmaster.h
@@ -0,0 +1,178 @@
+#ifndef _I2CMASTER_H
+#define _I2CMASTER_H 1
+/*************************************************************************
+* Title: C include file for the I2C master interface
+* (i2cmaster.S or twimaster.c)
+* Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
+* File: $Id: i2cmaster.h,v 1.10 2005/03/06 22:39:57 Peter Exp $
+* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
+* Target: any AVR device
+* Usage: see Doxygen manual
+**************************************************************************/
+
+#ifdef DOXYGEN
+/**
+ @defgroup pfleury_ic2master I2C Master library
+ @code #include <i2cmaster.h> @endcode
+
+ @brief I2C (TWI) Master Software Library
+
+ Basic routines for communicating with I2C slave devices. This single master
+ implementation is limited to one bus master on the I2C bus.
+
+ This I2c library is implemented as a compact assembler software implementation of the I2C protocol
+ which runs on any AVR (i2cmaster.S) and as a TWI hardware interface for all AVR with built-in TWI hardware (twimaster.c).
+ Since the API for these two implementations is exactly the same, an application can be linked either against the
+ software I2C implementation or the hardware I2C implementation.
+
+ Use 4.7k pull-up resistor on the SDA and SCL pin.
+
+ Adapt the SCL and SDA port and pin definitions and eventually the delay routine in the module
+ i2cmaster.S to your target when using the software I2C implementation !
+
+ Adjust the CPU clock frequence F_CPU in twimaster.c or in the Makfile when using the TWI hardware implementaion.
+
+ @note
+ The module i2cmaster.S is based on the Atmel Application Note AVR300, corrected and adapted
+ to GNU assembler and AVR-GCC C call interface.
+ Replaced the incorrect quarter period delays found in AVR300 with
+ half period delays.
+
+ @author Peter Fleury pfleury@gmx.ch http://jump.to/fleury
+
+ @par API Usage Example
+ The following code shows typical usage of this library, see example test_i2cmaster.c
+
+ @code
+
+ #include <i2cmaster.h>
+
+
+ #define Dev24C02 0xA2 // device address of EEPROM 24C02, see datasheet
+
+ int main(void)
+ {
+ unsigned char ret;
+
+ i2c_init(); // initialize I2C library
+
+ // write 0x75 to EEPROM address 5 (Byte Write)
+ i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
+ i2c_write(0x05); // write address = 5
+ i2c_write(0x75); // write value 0x75 to EEPROM
+ i2c_stop(); // set stop conditon = release bus
+
+
+ // read previously written value back from EEPROM address 5
+ i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
+
+ i2c_write(0x05); // write address = 5
+ i2c_rep_start(Dev24C02+I2C_READ); // set device address and read mode
+
+ ret = i2c_readNak(); // read one byte from EEPROM
+ i2c_stop();
+
+ for(;;);
+ }
+ @endcode
+
+*/
+#endif /* DOXYGEN */
+
+/**@{*/
+
+#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
+#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !"
+#endif
+
+#include <avr/io.h>
+
+/** defines the data direction (reading from I2C device) in i2c_start(),i2c_rep_start() */
+#define I2C_READ 1
+
+/** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */
+#define I2C_WRITE 0
+
+
+/**
+ @brief initialize the I2C master interace. Need to be called only once
+ @param void
+ @return none
+ */
+extern void i2c_init(void);
+
+
+/**
+ @brief Terminates the data transfer and releases the I2C bus
+ @param void
+ @return none
+ */
+extern void i2c_stop(void);
+
+
+/**
+ @brief Issues a start condition and sends address and transfer direction
+
+ @param addr address and transfer direction of I2C device
+ @retval 0 device accessible
+ @retval 1 failed to access device
+ */
+extern unsigned char i2c_start(unsigned char addr);
+
+
+/**
+ @brief Issues a repeated start condition and sends address and transfer direction
+
+ @param addr address and transfer direction of I2C device
+ @retval 0 device accessible
+ @retval 1 failed to access device
+ */
+extern unsigned char i2c_rep_start(unsigned char addr);
+
+
+/**
+ @brief Issues a start condition and sends address and transfer direction
+
+ If device is busy, use ack polling to wait until device ready
+ @param addr address and transfer direction of I2C device
+ @return none
+ */
+extern void i2c_start_wait(unsigned char addr);
+
+
+/**
+ @brief Send one byte to I2C device
+ @param data byte to be transfered
+ @retval 0 write successful
+ @retval 1 write failed
+ */
+extern unsigned char i2c_write(unsigned char data);
+
+
+/**
+ @brief read one byte from the I2C device, request more data from device
+ @return byte read from I2C device
+ */
+extern unsigned char i2c_readAck(void);
+
+/**
+ @brief read one byte from the I2C device, read is followed by a stop condition
+ @return byte read from I2C device
+ */
+extern unsigned char i2c_readNak(void);
+
+/**
+ @brief read one byte from the I2C device
+
+ Implemented as a macro, which calls either i2c_readAck or i2c_readNak
+
+ @param ack 1 send ack, request more data from device<br>
+ 0 send nak, read is followed by a stop condition
+ @return byte read from I2C device
+ */
+extern unsigned char i2c_read(unsigned char ack);
+#define i2c_read(ack) (ack) ? i2c_readAck() : i2c_readNak();
+
+
+/**@}*/
+#endif
diff --git a/light_ws2812.c b/light_ws2812.c
new file mode 100644
index 0000000..0542ec9
--- /dev/null
+++ b/light_ws2812.c
@@ -0,0 +1,170 @@
+/*
+* light weight WS2812 lib V2.0b
+*
+* Controls WS2811/WS2812/WS2812B RGB-LEDs
+* Author: Tim (cpldcpu@gmail.com)
+*
+* Jan 18th, 2014 v2.0b Initial Version
+*
+* License: GNU GPL v2 (see License.txt)
+*/
+
+#include "light_ws2812.h"
+#include <avr/interrupt.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+void inline ws2812_setleds(struct cRGB *ledarray, uint16_t leds)
+{
+ ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin));
+}
+
+void inline ws2812_setleds_pin(struct cRGB *ledarray, uint16_t leds, uint8_t pinmask)
+{
+ ws2812_DDRREG |= _BV(ws2812_pin); // Enable DDR
+ ws2812_sendarray_mask((uint8_t*)ledarray,leds+leds+leds,pinmask);
+ _delay_us(50);
+}
+
+void ws2812_sendarray(uint8_t *data,uint16_t datlen)
+{
+ ws2812_sendarray_mask(data,datlen,_BV(ws2812_pin));
+}
+
+/*
+ This routine writes an array of bytes with RGB values to the Dataout pin
+ using the fast 800kHz clockless WS2811/2812 protocol.
+*/
+
+// Timing in ns
+#define w_zeropulse 350
+#define w_onepulse 900
+#define w_totalperiod 1250
+
+// Fixed cycles used by the inner loop
+#define w_fixedlow 2
+#define w_fixedhigh 4
+#define w_fixedtotal 8
+
+// Insert NOPs to match the timing, if possible
+#define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000)
+#define w_onecycles (((F_CPU/1000)*w_onepulse +500000)/1000000)
+#define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000)
+
+// w1 - nops between rising edge and falling edge - low
+#define w1 (w_zerocycles-w_fixedlow)
+// w2 nops between fe low and fe high
+#define w2 (w_onecycles-w_fixedhigh-w1)
+// w3 nops to complete loop
+#define w3 (w_totalcycles-w_fixedtotal-w1-w2)
+
+#if w1>0
+ #define w1_nops w1
+#else
+ #define w1_nops 0
+#endif
+
+// The only critical timing parameter is the minimum pulse length of the "0"
+// Warn or throw error if this timing can not be met with current F_CPU settings.
+#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
+#if w_lowtime>550
+ #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
+#elif w_lowtime>450
+ #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
+ #warning "Please consider a higher clockspeed, if possible"
+#endif
+
+#if w2>0
+#define w2_nops w2
+#else
+#define w2_nops 0
+#endif
+
+#if w3>0
+#define w3_nops w3
+#else
+#define w3_nops 0
+#endif
+
+#define w_nop1 "nop \n\t"
+#define w_nop2 "rjmp .+0 \n\t"
+#define w_nop4 w_nop2 w_nop2
+#define w_nop8 w_nop4 w_nop4
+#define w_nop16 w_nop8 w_nop8
+
+void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi)
+{
+ uint8_t curbyte,ctr,masklo;
+ uint8_t sreg_prev;
+
+ masklo =~maskhi&ws2812_PORTREG;
+ maskhi |= ws2812_PORTREG;
+ sreg_prev=SREG;
+ cli();
+
+ while (datlen--) {
+ curbyte=*data++;
+
+ asm volatile(
+ " ldi %0,8 \n\t"
+ "loop%=: \n\t"
+ " out %2,%3 \n\t" // '1' [01] '0' [01] - re
+#if (w1_nops&1)
+w_nop1
+#endif
+#if (w1_nops&2)
+w_nop2
+#endif
+#if (w1_nops&4)
+w_nop4
+#endif
+#if (w1_nops&8)
+w_nop8
+#endif
+#if (w1_nops&16)
+w_nop16
+#endif
+ " sbrs %1,7 \n\t" // '1' [03] '0' [02]
+ " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low
+ " lsl %1 \n\t" // '1' [04] '0' [04]
+#if (w2_nops&1)
+ w_nop1
+#endif
+#if (w2_nops&2)
+ w_nop2
+#endif
+#if (w2_nops&4)
+ w_nop4
+#endif
+#if (w2_nops&8)
+ w_nop8
+#endif
+#if (w2_nops&16)
+ w_nop16
+#endif
+ " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high
+#if (w3_nops&1)
+w_nop1
+#endif
+#if (w3_nops&2)
+w_nop2
+#endif
+#if (w3_nops&4)
+w_nop4
+#endif
+#if (w3_nops&8)
+w_nop8
+#endif
+#if (w3_nops&16)
+w_nop16
+#endif
+
+ " dec %0 \n\t" // '1' [+2] '0' [+2]
+ " brne loop%=\n\t" // '1' [+3] '0' [+4]
+ : "=&d" (ctr)
+ : "r" (curbyte), "I" (_SFR_IO_ADDR(ws2812_PORTREG)), "r" (maskhi), "r" (masklo)
+ );
+ }
+
+ SREG=sreg_prev;
+}
diff --git a/light_ws2812.h b/light_ws2812.h
new file mode 100644
index 0000000..bc2c4e8
--- /dev/null
+++ b/light_ws2812.h
@@ -0,0 +1,63 @@
+/*
+ * light weight WS2812 lib include
+ *
+ * Version 2.0a3 - Jan 18th 2014
+ * Author: Tim (cpldcpu@gmail.com)
+ *
+ * Please do not change this file! All configuration is handled in "ws2812_config.h"
+ *
+ * License: GNU GPL v2 (see License.txt)
+ +
+ */
+
+#ifndef LIGHT_WS2812_H_
+#define LIGHT_WS2812_H_
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include "ws2812_config.h"
+
+/*
+ * Structure of the LED array
+ */
+
+struct cRGB { uint8_t g; uint8_t r; uint8_t b; };
+
+/* User Interface
+ *
+ * Input:
+ * ledarray: An array of GRB data describing the LED colors
+ * number_of_leds: The number of LEDs to write
+ * pinmask (optional): Bitmask describing the output bin. e.g. _BV(PB0)
+ *
+ * The functions will perform the following actions:
+ * - Set the data-out pin as output
+ * - Send out the LED data
+ * - Wait 50µs to reset the LEDs
+ */
+
+void ws2812_setleds (struct cRGB *ledarray, uint16_t number_of_leds);
+void ws2812_setleds_pin(struct cRGB *ledarray, uint16_t number_of_leds,uint8_t pinmask);
+
+/*
+ * Old interface / Internal functions
+ *
+ * The functions take a byte-array and send to the data output as WS2812 bitstream.
+ * The length is the number of bytes to send - three per LED.
+ */
+
+void ws2812_sendarray (uint8_t *array,uint16_t length);
+void ws2812_sendarray_mask(uint8_t *array,uint16_t length, uint8_t pinmask);
+
+
+/*
+ * Internal defines
+ */
+
+#define CONCAT(a, b) a ## b
+#define CONCAT_EXP(a, b) CONCAT(a, b)
+
+#define ws2812_PORTREG CONCAT_EXP(PORT,ws2812_port)
+#define ws2812_DDRREG CONCAT_EXP(DDR,ws2812_port)
+
+#endif /* LIGHT_WS2812_H_ */
diff --git a/ws2812_config.h b/ws2812_config.h
new file mode 100644
index 0000000..a619cd9
--- /dev/null
+++ b/ws2812_config.h
@@ -0,0 +1,21 @@
+/*
+ * light_ws2812_config.h
+ *
+ * Created: 18.01.2014 09:58:15
+ *
+ * User Configuration file for the light_ws2812_lib
+ *
+ */
+
+
+#ifndef WS2812_CONFIG_H_
+#define WS2812_CONFIG_H_
+
+///////////////////////////////////////////////////////////////////////
+// Define I/O pin
+///////////////////////////////////////////////////////////////////////
+
+#define ws2812_port D // Data port
+#define ws2812_pin 6 // Data out pin
+
+#endif /* WS2812_CONFIG_H_ */