Commit a1f7cb55 authored by Jan Kasprzak's avatar Jan Kasprzak
Browse files

New project: RGB LED lights

parent 85a86985
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
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
#ifdef USE_LOGGING
#include <avr/io.h>
#include <avr/eeprom.h>
#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
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#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;
}
}
}
#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__ */
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#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();
}
#!/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 <<EOF;
/* DO NOT EDIT - GENERATED BY $0 */
#include <avr/eeprom.h>
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";
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment