From: Jan "Yenya" Kasprzak Date: Sun, 10 Apr 2016 17:37:08 +0000 (+0200) Subject: New project: RGB LED lights X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=commitdiff_plain;h=a1f7cb553db8b485094faafcc431afff2b785736;p=tinyboard.git New project: RGB LED lights --- diff --git a/projects/rgb-led-light/Makefile b/projects/rgb-led-light/Makefile new file mode 100644 index 0000000..e63225b --- /dev/null +++ b/projects/rgb-led-light/Makefile @@ -0,0 +1,68 @@ + +PROGRAM=rgbstring +SRC=version.c main.c logging.c serial.c +OBJ=$(SRC:.c=.o) + + +MCU=attiny45 +AVRDUDE_MCU=$(MCU) +AVRDUDE_PROGRAMMER=usbasp + +CFLAGS=-Wall -Os -mmcu=$(MCU) -DUSE_LOGGING=1 -DF_CPU=8000000UL -std=gnu99 +LDFLAGS= +AVRDUDE_FLAGS= -p$(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -B10 + +FORMAT=ihex + +CC=avr-gcc +OBJCOPY=avr-objcopy +OBJDUMP=avr-objdump +AVRDUDE=avrdude + +all: $(PROGRAM).hex $(PROGRAM).eep + +program: $(PROGRAM).hex $(PROGRAM).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i -U eeprom:w:$(PROGRAM).eep:i + +program_flash: $(PROGRAM).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i + +program_eeprom: $(PROGRAM).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) eeprom:w:$(PROGRAM).eep:i + +dump_eeprom: + $(AVRDUDE) $(AVRDUDE_FLAGS) -U eeprom:r:eeprom.raw:r + od -tx1 eeprom.raw + +objdump: $(PROGRAM).elf + $(OBJDUMP) --disassemble $< + +.PRECIOUS : $(OBJ) $(PROGRAM).elf + +%.hex: %.elf + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +%.eep: %.elf + $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O $(FORMAT) $< $@ + +%.elf: $(OBJ) + $(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS) + +%.o: %.c rgbstring.h Makefile + $(CC) -c $(CFLAGS) $< -o $@ + +%.s: %.c rgbstring.h Makefile + $(CC) -S -c $(CFLAGS) $< -o $@ + +%.o: %.S + $(CC) -c $(CFLAGS) $< -o $@ + +clean: + rm -f $(PROGRAM).hex $(PROGRAM).eep $(PROGRAM).elf *.o *.s eeprom.raw + +version.c: + ./version.pl > version.c + +.PHONY: all clean dump_eeprom program program_flash program_eeprom objdump version.c + diff --git a/projects/rgb-led-light/README b/projects/rgb-led-light/README new file mode 100644 index 0000000..78d3010 --- /dev/null +++ b/projects/rgb-led-light/README @@ -0,0 +1,25 @@ + +Controller for the string of RGB LEDs + +CPU: ATtiny45 + +Bill of materials: + +C3 10uF ceramic +U1 ATtiny45-20SU + +Pin-out: + +PB0 header: + 2: Button 1 +PB1 header: + 2: DATA +PB2 header: + 2: CLK +PB3 header: + 2: Button 2 +PB4 header: + 2: Button 3 + + + diff --git a/projects/rgb-led-light/logging.c b/projects/rgb-led-light/logging.c new file mode 100644 index 0000000..d11275b --- /dev/null +++ b/projects/rgb-led-light/logging.c @@ -0,0 +1,81 @@ +#ifdef USE_LOGGING + +#include +#include + +#include "rgbstring.h" + +#define LOG_BUFFER 64 +static unsigned char log_buffer_ee[LOG_BUFFER] EEMEM; +static unsigned char log_buffer_count; +static unsigned char log_buffer[LOG_BUFFER]; +static unsigned char log_state EEMEM; +/* Upper 4 bits are reset count, lower 4 bits are reset reason from MCUSR */ +static unsigned char reboot_count EEMEM = 0; +static unsigned char can_write_eeprom = 0; +static uint16_t flushed_end; + +void log_set_state(unsigned char val) +{ + if (can_write_eeprom) + eeprom_write_byte(&log_state, val); +} + +void init_log() +{ + unsigned char r_count; + + r_count = eeprom_read_byte(&reboot_count); + r_count >>= 4; + + if (r_count < 5) { + r_count++; + eeprom_write_byte(&reboot_count, + (r_count << 4) | (MCUSR & 0xF)); + MCUSR = 0; + can_write_eeprom = 1; + } else { + //eeprom_write_byte(&log_state, 0xFF); + can_write_eeprom = 0; + } + + log_set_state(1); + log_buffer_count = 0; + flushed_end = 0; +} + +void log_byte(unsigned char byte) { + if (log_buffer_count >= LOG_BUFFER) + return; + + // eeprom_write_word(&log_buffer[log_buffer_count], word); + log_buffer[log_buffer_count++] = byte; + + if (log_buffer_count == LOG_BUFFER) + log_flush(); +} + +void log_word(uint16_t word) { + log_byte(word & 0xFF); + log_byte(word >> 8); +} + +void log_flush() { + unsigned char i; + + if (!can_write_eeprom) + return; + + for (i=flushed_end; i < log_buffer_count; i++) { + eeprom_write_byte(&log_buffer_ee[i], + log_buffer[i]); + } + + flushed_end = i; + + if (flushed_end == LOG_BUFFER) + log_set_state(0x42); +} + +#endif + diff --git a/projects/rgb-led-light/main.c b/projects/rgb-led-light/main.c new file mode 100644 index 0000000..a89481c --- /dev/null +++ b/projects/rgb-led-light/main.c @@ -0,0 +1,192 @@ +#include +#include +#include + +#include "rgbstring.h" + +static volatile uint16_t jiffies; + +// #define CHRISTMAS_TREE 1 + +#define rgb_return(r, g, b) do { send_rgb((r), (g), (b)); return 1; } while(0) + +#define VERT_SIZE 47 + +/* RNG from ADC noise */ +static unsigned char rand_pool[8], rand_pool_off, prev_bit, rand_pool_out; + +static void init_rng() +{ + ADCSRA |= _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1); // enable, clk/64 + ADMUX = _BV(REFS1) | _BV(MUX0) | _BV(MUX3); // 1.1V, PB5:PB5, gain 20 + DIDR0 = _BV(ADC0D); + ADCSRA |= _BV(ADIE) | _BV(ADSC); + rand_pool_off = 0; + prev_bit = 0; +} + +static unsigned char rand() { + unsigned char rv = 0; + + rv = rand_pool[rand_pool_out]; + rand_pool_out++; + if (rand_pool_out >= sizeof(rand_pool)) + rand_pool_out = 0; + + return rv; +} + +ISR(ADC_vect) { + ADCSRA |= _BV(ADSC); + jiffies++; + if ((rand_pool_off & 1) == 0) { // first bit of the pair + prev_bit = ADCW & 1; + rand_pool_off++; + } else { + unsigned char bit = ADCW & 1; + if (bit == prev_bit) { // whitening fail: try again + rand_pool_off--; + return; + } + + if (bit) { + rand_pool[rand_pool_off >> 4] + ^= 1 << ((rand_pool_off >> 1) & 7); + } + + rand_pool_off++; + if (rand_pool_off >= 16*sizeof(rand_pool)) + rand_pool_off = 0; + } +} + +static unsigned int slow_dim[] = { + 255, 27, 7, 2, +}; + +static void fill_color(unsigned char r, unsigned char g, unsigned char b) +{ + unsigned char i; + + for (i = 0; i < STRIP_SIZE; i++) + send_rgb(r, g, b); + + end_frame(); +} + +unsigned int state; + +static void do_buttons() +{ + static uint8_t prev_buttons = _BV(PB0) | _BV(PB3) | _BV(PB4); + static uint16_t prev_len = 0; + + uint8_t buttons = PINB & (_BV(PB0) | _BV(PB3) | _BV(PB4)); + + if (prev_buttons == buttons) { + prev_len++; + return; + } + + // was change + if (prev_len < 3 || (buttons != (_BV(PB0) | _BV(PB3) | _BV(PB4)))) { + prev_buttons = buttons; + prev_len = 0; + return; + } + + if ((prev_buttons & _BV(PB0)) == 0) { + if (state) + state--; + } else if ((prev_buttons & _BV(PB3)) == 0) { + if (state < 5) + state++; + } else if ((prev_buttons & _BV(PB4)) == 0) { + state = 1; + } + + prev_buttons = buttons; + prev_len = 0; +} + +int main(void) +{ + + init_log(); + init_rng(); + init_serial(); + + _delay_ms(3000/8); // wait for a bit and then increase the CPU clock + CLKPR = _BV(CLKPCE); + CLKPR = 0; + + PORTB |= _BV(PB0) | _BV(PB3) | _BV(PB4); // pull-ups for buttons + + state = 0; + + sei(); + + while (1) { + unsigned char i; + static unsigned char c = 28; + + do_buttons(); + + switch (state) { + case 0: + zero_frame(); + break; + case 1: + i = 0; + while (i < STRIP_SIZE) { + send_rgb(4, 0, 0); + send_rgb(4, 1, 0); + send_rgb(0, 2, 0); + send_rgb(0, 1, 1); + send_rgb(0, 0, 2); + send_rgb(4, 0, 2); + i += 6; + } + end_frame(); + break; + case 2: + if ((jiffies & 0x1ff) == 0) { + c++; + if (c >= 30) + c = 0; + } + + for (i = 0; i < STRIP_SIZE; i++) { + unsigned char x = c; // + i / 2; + if (x >= 30) + x %= 30; + if (x < 10) { + send_rgb(8*(10-x), x, 0); + } else if (x < 20) { + send_rgb(0, 20-x, x-10); + } else { + send_rgb(8*(x-20), 0, 30-x); + } + } + end_frame(); + break; + case 3: + fill_color(32, 4, 8); + break; + case 4: + fill_color(255, 92, 92); + break; + case 5: + fill_color(255, 255, 255); + break; + default: + { unsigned char light; + + light = slow_dim[sizeof(slow_dim)/sizeof(slow_dim[0]) - state]; + fill_color(4*light, light, 2*light); + } + break; + } + } +} + diff --git a/projects/rgb-led-light/rgbstring.h b/projects/rgb-led-light/rgbstring.h new file mode 100644 index 0000000..1f14867 --- /dev/null +++ b/projects/rgb-led-light/rgbstring.h @@ -0,0 +1,29 @@ +#ifndef LIGHTS_H__ +#define LIGHTS_H__ 1 + +#define N_PWMLED_MODES 3 + +/* logging.c */ +#ifdef USE_LOGGING +void init_log(); +void log_set_state(unsigned char val); +void log_flush(); +void log_byte(unsigned char byte); +void log_word(uint16_t word); +#else +void inline init_log() { } +void inline log_set_state(unsigned char val) { } +void inline log_flush() { } +void inline log_byte(unsigned char byte) { } +void inline log_word(uint16_t word) { } +#endif + +/* serial.c */ +#define STRIP_SIZE 10 +void init_serial(); +void zero_frame(); +void end_frame(); +void send_rgb(unsigned char r, unsigned char g, unsigned char b); + +#endif /* !LIGHTS_H__ */ + diff --git a/projects/rgb-led-light/serial.c b/projects/rgb-led-light/serial.c new file mode 100644 index 0000000..1ab2e42 --- /dev/null +++ b/projects/rgb-led-light/serial.c @@ -0,0 +1,88 @@ +#include +#include +#include + +#include "rgbstring.h" + +void init_serial() +{ + PORTB &= ~(_BV(PB1) | _BV(PB2)); + DDRB |= _BV(PB1) | _BV(PB2); + +#if 0 + TCCR0A = _BV(WGM01) | _BV(WGM00); + TCCR0B = _BV(WGM02) | _BV(CS00); + OCR0A = 2; +#endif + + zero_frame(); +} + +static void send_byte(unsigned char b) +{ + unsigned char i, mask; + +#if 0 + USIDR = b; + USISR = _BV(USIOIF); + USICR = _BV(USIWM0) | _BV(USICS0); + + while (!(USISR & _BV(USIOIF))) + ; +#endif + +#if 0 + USIDR = b; + USISR = _BV(USIOIF); + + while ( (USISR & _BV(USIOIF)) == 0 ) { + USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK); + USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); + } +#endif + +#if 0 + for (i = 0; i < 8; i++) { + USICR = _BV(USIWM0) | _BV(USITC); + USICR = _BV(USIWM0) | _BV(USITC) | _BV(USICLK); + } +#endif + +#if 1 + for (i = 0; i < 8; i++) { + PORTB &= ~_BV(PB2); // clock low + if (b & 0x80) // data bit on or off + PORTB |= _BV(PB1); + else + PORTB &= ~_BV(PB1); + b <<= 1; + PORTB |= _BV(PB2); // clock high + } +#endif +} + +void end_frame() +{ + PORTB &= ~_BV(PB2); // clock low + _delay_us(1000); +} + +void send_rgb(unsigned char r, unsigned char g, unsigned char b) +{ + send_byte(r); + send_byte(g); + send_byte(b); +} + + +void zero_frame() +{ + unsigned char i; + + for (i = 0; i < STRIP_SIZE; i++) { + send_rgb(0, 0, 0); + } + + end_frame(); +} + diff --git a/projects/rgb-led-light/version.pl b/projects/rgb-led-light/version.pl new file mode 100755 index 0000000..cb4ecc7 --- /dev/null +++ b/projects/rgb-led-light/version.pl @@ -0,0 +1,32 @@ +#!/usr/bin/perl -w + +use strict; +use POSIX qw(strftime); + +my $git = `git rev-parse --short HEAD`; +chomp $git; + +my $now = strftime('%Y%m%d', localtime(time)); + +print < + +unsigned char version[] EEMEM = { +EOF + +print hex2c($git, "git revision"); +print hex2c($now, "date"); + +print "};\n\n/* EOF - this file has not been truncated */\n\n"; + +sub hex2c { + my ($data, $comment) = @_; + + my $data1 = $data; + $data1 .= '0' if (length($data1) & 1 == 1); + $data1 =~ s/(..)/0x$1, /g; + return "\t$data1 /* $comment $data */\n"; +} +