Improved lib, color system, pin system.

pull/1/head
Ondřej Hruška 8 years ago
parent 34103b7065
commit e78d91a900
  1. 28
      lib/arduino_pins.h
  2. 37
      lib/calc.h
  3. 57
      lib/colors.h
  4. 116
      lib/debounce.h
  5. 6
      lib/meta.h
  6. 18
      lib/nsdelay.h
  7. 109
      lib/pins.h
  8. 77
      lib/ws2812.h
  9. 153
      new-pins-test/Makefile
  10. 31
      new-pins-test/README.md
  11. 1
      new-pins-test/lib
  12. 118
      new-pins-test/main.c
  13. 3
      new-pins-test/utils.h
  14. 21
      rgb-led-strip/arduino-ws-anim-rainbow/Makefile
  15. 91
      rgb-led-strip/arduino-ws-anim-rainbow/arduino_pins.h
  16. 13
      rgb-led-strip/arduino-ws-anim-rainbow/config.h
  17. 1
      rgb-led-strip/arduino-ws-anim-rainbow/lib
  18. 125
      rgb-led-strip/arduino-ws-anim-rainbow/main.c
  19. 158
      rgb-led-strip/arduino-ws-gamepad/Makefile
  20. 13
      rgb-led-strip/arduino-ws-gamepad/README.md
  21. 16
      rgb-led-strip/arduino-ws-gamepad/config.h
  22. 22
      rgb-led-strip/arduino-ws-gamepad/gamepad_pins.h
  23. 1
      rgb-led-strip/arduino-ws-gamepad/lib
  24. 89
      rgb-led-strip/arduino-ws-gamepad/main.c
  25. 6
      rgb-led-strip/ws_driver_legacy/ws_driver.c
  26. 6
      rgb-led-strip/ws_driver_legacy/ws_driver.h
  27. 97
      rgb-led-strip/ws_driver_legacy/ws_driver2.c
  28. 56
      rgb-led-strip/ws_driver_legacy/ws_driver2.h
  29. 75
      rgb-led-strip/ws_driver_legacy/ws_driver3.c
  30. 54
      rgb-led-strip/ws_driver_legacy/ws_driver3.h
  31. 153
      test-passportbyref/Makefile
  32. 31
      test-passportbyref/README.md
  33. 1
      test-passportbyref/lib
  34. 16
      test-passportbyref/main.c

@ -0,0 +1,28 @@
#pragma once
/**
Pin definitions for Arduino (Pro Mini with ATmega328P)
*/
#include "pins.h"
#define D0 D,0
#define D1 D,1
#define D2 D,2
#define D3 D,3
#define D4 D,4
#define D5 D,5
#define D6 D,6
#define D7 D,7
#define D8 B,0
#define D9 B,1
#define D10 B,2
#define D11 B,3
#define D12 B,4
#define D13 B,5
#define A0 C,0
#define A1 C,1
#define A2 C,2
#define A3 C,3
#define A4 C,4
#define A5 C,5

@ -0,0 +1,37 @@
#pragma once
/**
General purpose calculation and bit manipulation utilities.
*/
// if max, go to zero. Else increment.
#define inc_wrap(var, max) do { if ((var) >= (max)) { (var)=0; } else { (var)++; } } while(0)
// If zero, go to max. Else decrement,
#define dec_wrap(var, max) do { if ((var) > 0) { (var)--; } else { (var)=(max); } } while(0)
// Check if value is in range A..B or B..A
#define in_range(x, low, high) (((low) < (high)) && ((x) > (low) && (x) < (high))) || (((low) > (high)) && ((x) < (low) || (x) > (high)))
// Check if value is in range A..B. If B < A, matches all outside B..A
#define in_range_wrap(x, low, high) (((low) < (high)) && ((x) > (low) && (x) < (high))) || (((low) > (high)) && ((x) > (low) || (x) < (high)))
// === general bit manipulation with register ===
#define set_bit(reg, bit) do { (reg) |= (1 << bit); } while(0)
#define clear_bit(reg, bit) do { (reg) &= ~(1 << bit); } while(0)
#define toggle_bit(reg, bit) do { (reg) ^= (1 << bit); } while(0)
#define sbi(reg, bit) set_bit(reg, bit)
#define cbi(reg, bit) clear_bit(reg, bit)
#define read_bit(reg, bit) ((((uint8_t)(reg)) >> ((uint8_t)(bit))) & 0x1)
#define write_bit(reg, bit, value) do { (reg) = (((reg) & ~(1 << bit)) | ((value & 0x1) << (bit))); } while(0)
// general pin manipulation - with pointer to register
#define set_bit_p(reg_p, bit) do { (*reg_p) |= (1 << bit); } while(0)
#define clear_bit_p(reg_p, bit) do { (*reg_p) &= ~(1 << bit); } while(0)
#define sbi_p(reg_p, bit) set_bit_p(reg_p, bit)
#define cbi_p(reg_p, bit) clear_bit_p(reg_p, bit)
#define read_bit_p(reg_p, bit) ((((uint8_t)(*reg_p)) >> ((uint8_t)(bit))) & 0x1)
#define write_bit_p(reg_p, bit, value) do { (*reg_p) = (((*reg_p) & ~(1 << bit)) | ((value & 0x1) << (bit))); } while(0)

@ -0,0 +1,57 @@
#pragma once
/*
Some useful utilities for RGB color manipulation
*/
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} xrgb_t;
typedef uint32_t rgb24_t;
typedef uint16_t rgb16_t;
typedef uint16_t rgb12_t;
typedef uint8_t rgb6_t;
#define xrgb(rr, gg, bb) { .r = ((uint8_t)(rr)), .g = ((uint8_t)(gg)), .b = ((uint8_t)(bb)) }
#define xrgb_r(c) ((uint8_t)(c.r))
#define xrgb_g(c) ((uint8_t)(c.g))
#define xrgb_b(c) ((uint8_t)(c.b))
#define xrgb_rgb24(c) ((((rgb24_t)c.r) << 16) | (((rgb24_t)c.g) << 8) | (((rgb24_t)c.b)))
#define xrgb_rgb15(c) (((((rgb15_t)c.r) & 0xF8) << 7) | ((((rgb15_t)c.g) & 0xF8) << 2) | ((((rgb15_t)c.b) & 0xF8) >> 3))
#define xrgb_rgb12(c) (((((rgb12_t)c.r) & 0xF0) << 4) | ((((rgb12_t)c.g) & 0xF0)) | ((((rgb12_t)c.b) & 0xF0) >> 4))
#define xrgb_rgb6(c) (((((rgb6_t)c.r) & 0xC0) >> 2) | ((((rgb6_t)c.g) & 0xC0) >> 4) | ((((rgb6_t)c.b) & 0xC0) >> 6))
#define rgb24(r,g,b) ((rgb24_t) (((((rgb24_t)r) & 0xFF) << 16) | ((((rgb24_t)g) & 0xFF) << 8) | (((rgb24_t)b) & 0xFF)))
#define rgb24_r(c) ((((rgb24_t) (c)) >> 16) & 0xFF)
#define rgb24_g(c) ((((rgb24_t) (c)) >> 8) & 0xFF)
#define rgb24_b(c) ((((rgb24_t) (c)) >> 0) & 0xFF)
#define rgb24_xrgb(c) xrgb(rgb24_r(c), rgb24_g(c), rgb24_b(c))
#define rgb15(r,g,b) ((rgb16_t) (((r & 0x1F) << 10) | ((g & 0x1F) << 5) | (b & 0x1F)))
#define rgb15_r(c) ((((rgb15_t) (c)) & 0x7C00) >> 7)
#define rgb15_g(c) ((((rgb15_t) (c)) & 0x3E0) >> 2)
#define rgb15_b(c) ((((rgb15_t) (c)) & 0x1F) << 3)
#define rgb15_xrgb(c) xrgb(rgb15_r(c), rgb15_g(c), rgb15_b(c))
#define rgb15_rgb24(c) rgb24(rgb15_r(c), rgb15_g(c), rgb15_b(c))
#define rgb12(r,g,b) ((rgb12_t) (((r & 0xF) << 8) | ((g & 0xF) << 4) | (b & 0xF)))
#define rgb12_r(c) ((((rgb12_t) (c)) & 0xF00) >> 4)
#define rgb12_g(c) (((rgb12_t) (c)) & 0xF0)
#define rgb12_b(c) (((r(rgb12_t) (c)gb) & 0x0F) << 4)
#define rgb12_xrgb(c) xrgb(rgb12_r(c), rgb12_g(c), rgb12_b(c))
#define rgb12_rgb24(c) rgb24(rgb12_r(c), rgb12_g(c), rgb12_b(c))
#define rgb6(r,g,b) ((rgb6_t) (((r & 3) << 4) | ((g & 3) << 2) | (b & 3)))
#define rgb6_r(c) ((((rgb6_t) (c)) & 0x30) << 2)
#define rgb6_g(c) ((((rgb6_t) (c)) & 0xC) << 4)
#define rgb6_b(c) ((((rgb6_t) (c)) & 0x3) << 6)
#define rgb6_xrgb(c) xrgb(rgb6_r(c), rgb6_g(c), rgb6_b(c))
#define rgb6_rgb24(c) rgb24(rgb6_r(c), rgb6_g(c), rgb6_b(c))

@ -0,0 +1,116 @@
#pragma once
/**
An implementation of button debouncer.
First, the system must be initialized:
debounce_init();
A pin is registered for debouncing by calling
#define BTN0 B,0
#define BTN1 B,1
debounce_register(0, BTN0);
debounce_register(1, BTN1);
Then periodically the tick function must be called:
debounce_tick();
To check if pin is high, use
debounce_get_pin(0); // registered as #0
debounce_get_pin(1); // registered as #1
*/
#include "pins.h"
#include "calc.h"
#include "avr/io.h"
/** Number of ticks the pin must be in given state */
#ifndef DEBOUNCE_TICKS
# define DEBOUNCE_TICKS 5
#endif
/** Max number of pins observed */
#ifndef DEBOUNCE_SLOT_COUNT
#define DEBOUNCE_SLOT_COUNT 16
#endif
/* Internal deboucer entry */
typedef struct {
PORT_P port;
PIN_N bit;
uint8_t count;
bool state;
} debounce_slot_t;
/** Debounce data array */
debounce_slot_t debounce_slots[DEBOUNCE_SLOT_COUNT];
/** Init the debounce slots table */
void debounce_init()
{
for (uint8_t i = 0; i < DEBOUNCE_SLOT_COUNT; i++) {
debounce_slots[i] = {
.port = (PORT_P)0;
.bit = 0;
.state = 0;
.count = 0;
};
}
}
/** Define a debounced pin (must be IO!) */
inline void debounce_register_real(uint8_t number, PORT_P port, PIN_N bit)
{
debounce_slots[number] = {
.port = port;
.bit = bit;
.count = 0;
.state = 0;
};
}
#define debounce_register(number, io) debounce_register_real((number), &io2port(io), io2n(io))
/** Check debounced pins, should be called periodically. */
void debounce_tick()
{
for (uint8_t i = 0; i < DEBOUNCE_SLOT_COUNT; i++) {
if (debounce_slots[i].port == 0) continue; // slot is unused
// current pin value
bool value = read_bit_p(debounce_slots[i].port, debounce_slots[i].bit);
if (value != debounce_slots[i].state) {
// different pin state than last recorded state
if (debounce_slots[i].count < DEBOUNCE_TICKS) {
// increment
if (++debounce_slots[i].count == DEBOUNCE_TICKS) {
// overflown -> latch value
debounce_slots[i].state = value;
}
}
} else {
debounce_slots[i].count = 0; // reset the counter
}
}
}
/** Get a value of debounced pin */
inline bool debounce_get_pin(uint8_t number)
{
return debounce_slots[number].state;
}

@ -0,0 +1,6 @@
#pragma once
/** Weird constructs for the compiler */
// general macros
#define SECTION(pos) __attribute__((naked, used, section(pos)))

@ -0,0 +1,18 @@
#pragma once
/**
Functions for precise delays (nanoseconds / cycles)
*/
#include <avr/io.h>
#include <util/delay_basic.h>
#include <stdint.h>
/* Convert nanoseconds to cycle count */
#define ns2cycles(ns) ( (ns) / (1000000000L / (signed long) F_CPU) )
/** Wait c cycles */
#define delay_c(c) (((c) > 0) ? __builtin_avr_delay_cycles(c) : __builtin_avr_delay_cycles(0))
/** Wait n nanoseconds, plus c cycles */
#define delay_ns_c(ns, c) delay_c(ns2cycles(ns) + (c))

@ -0,0 +1,109 @@
#pragma once
/**
This file provides macros for pin manipulation.
You can define your application pins like so:
// Led at PORTB, pin 1
#define LED B,1
// Switch at PORTD, pin 7
#define SW1 D,7
Now you can use macros from this file to wirh with the pins, eg:
as_output(LED);
as_input(SW1);
pullup_on(SW1);
toggle_pin(LED);
while (pin_is_low(SW1));
- The macros io2XXX() can be used to get literal name of register associated with the pin.
io2n() provides pin number.
- The XXX_aux() macros are internal and should not be used elsewhere.
- The io_pack() macro is used to pass pin (io) to other macro without expanding it.
Additionaly, there's general-purpose bit-manipulation macros (set_bit, clear_bit etc.).
Those work with register name and pin number, not the "io" format (#define LED2 D,3).
*/
#include <avr/io.h>
#include "calc.h"
// Get particular register associated with the name X (eg. D -> PORTD)
#define reg_ddr(X) DDR ## X
#define reg_port(X) PORT ## X
#define reg_pin(X) PIN ## X
#define io2ddr_aux(reg, bit) reg_ddr(reg)
#define io2ddr(io) io2ddr_aux(io)
#define io2port_aux(reg, bit) reg_port(reg)
#define io2port(io) io2port_aux(io)
#define io2pin_aux(reg, bit) reg_pin(reg)
#define io2pin(io) io2pin_aux(io)
#define io2n_aux(reg, bit) bit
#define io2n(io) io2n_aux(io)
#define io_pack(port, bit) port, bit
// pointer to port
typedef volatile uint8_t* PORT_P;
// number of bit in port
typedef uint8_t BIT_N;
// === pin manipulation ===
#define set_pin_aux(port, bit) set_bit(reg_port(port), bit)
#define clear_pin_aux(port, bit) clear_bit(reg_port(port), bit)
#define read_pin_aux(port, bit) read_bit(reg_pin(port), bit)
#define write_pin_aux(port, bit, value) write_bit(reg_port(port), bit, value)
#define toggle_pin_aux(port, bit) set_bit(reg_pin(port), bit)
#define set_pin(io) set_pin_aux(io)
#define pin_high(io) set_pin_aux(io)
#define clear_pin(io) clear_pin_aux(io)
#define pin_low(io) clear_pin_aux(io)
#define read_pin(io) read_pin_aux(io)
#define pin_is_low(io) !read_pin_aux(io)
#define pin_is_high(io) read_pin_aux(io)
#define write_pin(io, value) write_pin_aux(io, value)
#define toggle_pin(io) toggle_pin_aux(io)
// setting pin direction
#define as_input_aux(port, bit) clear_bit(reg_ddr(port), bit)
#define as_output_aux(port, bit) set_bit(reg_ddr(port), bit)
#define set_dir_aux(port, bit, dir) write_bit(reg_ddr(port), bit, dir)
#define as_input(io) as_input_aux(io)
#define as_output(io) as_output_aux(io)
#define set_dir(io, dir) set_dir_aux(io, dir)
// setting pullup
#define pullup_enable_aux(port, bit) set_bit(reg_port(port), bit)
#define pullup_disable_aux(port, bit) clear_bit(reg_port(port), bit)
#define set_pullup_aux(port, bit, on) write_bit(reg_port(port), bit, on)
#define pullup_enable(io) pullup_enable_aux(io)
#define pullup_on(io) pullup_enable_aux(io)
#define pullup_disable(io) pullup_disable_aux(io)
#define pullup_off(io) pullup_disable_aux(io)
#define set_pullup(io, on) set_pullup_aux(io, on)

@ -0,0 +1,77 @@
#pragma once
/**
Utils for driving a WS2812 (WS2812B) RGB LED strips.
It's implemented as macros to avoid overhead when passing values, and to
enable driving multiple strips at once. There is over 1us of free time between
the colors, which can be used for some processing or color computation.
To avoid bloating your code, try to reduce the nuýmber of invocations -
compute color and then send it.
*/
#include <avr/io.h>
#include "pins.h"
#include "nsdelay.h"
#include "colors.h"
/* Driver code for WS2812B */
// --- timing constraints (NS) ---
#ifndef WS_T_1H
#define WS_T_1H 700
#endif
#ifndef WS_T_1L
#define WS_T_1L 150
#endif
#ifndef WS_T_0H
#define WS_T_0H 150
#endif
#ifndef WS_T_0L
#define WS_T_0L 700
#endif
#ifndef WS_T_LATCH
#define WS_T_LATCH 6000
#endif
/** Wait long enough for the colors to show */
#define ws_show() do { delay_ns_c(WS_T_LATCH, 0); } while(0)
/** Send one byte to the RGB strip */
#define ws_send_byte(io, bb) do { \
for (volatile int8_t __wsba_i = 7; __wsba_i >= 0; --__wsba_i) { \
if ((bb) & (1 << __wsba_i)) { \
pin_high(io_pack(io)); delay_ns_c(WS_T_1H, -2); \
pin_low(io_pack(io)); delay_ns_c(WS_T_1L, -10); \
} else { \
pin_high(io_pack(io)); delay_ns_c(WS_T_0H, -2); \
pin_low(io_pack(io)); delay_ns_c(WS_T_0L, -10); \
} \
} \
} while(0)
/** Send R,G,B color to the strip */
#define ws_send_rgb(io, r, g, b) do { \
ws_send_byte(io_pack(io), g); \
ws_send_byte(io_pack(io), r); \
ws_send_byte(io_pack(io), b); \
} while(0)
/** Send a RGB struct */
#define ws_send_xrgb(io, xrgb) ws_send_rgb(io_pack(io), (xrgb).r, (xrgb).g, (xrgb).b)
/** Send color hex */
#define ws_send_rgb24(io, rgb) ws_send_rgb(io_pack(io), rgb24_r(rgb), rgb24_g(rgb), rgb24_b(rgb))
#define ws_send_rgb15(io, rgb) ws_send_rgb(io_pack(io), rgb15_r(rgb), rgb15_g(rgb), rgb15_b(rgb))
#define ws_send_rgb12(io, rgb) ws_send_rgb(io_pack(io), rgb12_r(rgb), rgb12_g(rgb), rgb12_b(rgb))
#define ws_send_rgb6(io, rgb) ws_send_rgb(io_pack(io), rgb6_r(rgb), rgb6_g(rgb), rgb6_b(rgb))

@ -0,0 +1,153 @@
MCU = attiny13
F_CPU = 9600000
LFUSE = 0x72
HFUSE = 0xFF
MAIN = main.c
## If you've split your program into multiple files,
## include the additional .c source (in same directory) here
## (and include the .h files in your foo.c)
LOCAL_SOURCE =
## Here you can link to one more directory (and multiple .c files)
# EXTRA_SOURCE_DIR = ../AVR-Programming-Library/
EXTRA_SOURCE_DIR =
EXTRA_SOURCE_FILES =
##########------------------------------------------------------##########
########## Programmer Defaults ##########
########## Set up once, then forget about it ##########
########## (Can override. See bottom of file.) ##########
##########------------------------------------------------------##########
PROGRAMMER_TYPE = dragon_isp
PROGRAMMER_ARGS =
##########------------------------------------------------------##########
########## Makefile Magic! ##########
########## Summary: ##########
########## We want a .hex file ##########
########## Compile source files into .elf ##########
########## Convert .elf file into .hex ##########
########## You shouldn't need to edit below. ##########
##########------------------------------------------------------##########
## Defined programs / locations
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
AVRSIZE = avr-size
AVRDUDE = sudo avrdude
## Compilation options, type man avr-gcc if you're curious.
CFLAGS = -std=gnu99 -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL -Os -I. -I$(EXTRA_SOURCE_DIR)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CFLAGS += -Wall -Wno-main
CFLAGS += -g2 -ggdb
CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--relax
CFLAGS += -std=gnu99
# CFLAGS += -lm
## CFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf
## CFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf
## Lump target and extra source files together
TARGET = $(strip $(basename $(MAIN)))
SRC = $(TARGET).c
EXTRA_SOURCE = $(addprefix $(EXTRA_SOURCE_DIR), $(EXTRA_SOURCE_FILES))
SRC += $(EXTRA_SOURCE)
SRC += $(LOCAL_SOURCE)
## List of all header files
HEADERS = $(SRC:.c=.h)
## For every .c file, compile an .o object file
OBJ = $(SRC:.c=.o)
## Generic Makefile targets. (Only .hex file is necessary)
all: $(TARGET).hex size
%.hex: %.elf
$(OBJCOPY) -R .eeprom -O ihex $< $@
%.elf: $(SRC)
$(CC) $(CFLAGS) $(SRC) --output $@
%.eeprom: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
debug:
@echo
@echo "Source files:" $(SRC)
@echo "MCU, F_CPU, BAUD:" $(MCU), $(F_CPU), $(BAUD)
@echo
# Optionally create listing file from .elf
# This creates approximate assembly-language equivalent of your code.
# Useful for debugging time-sensitive bits,
# or making sure the compiler does what you want.
disassemble: $(TARGET).lst
dis: disassemble
eeprom: $(TARGET).eeprom
%.lst: %.elf
$(OBJDUMP) -S $< > $@
# Optionally show how big the resulting program is
size: $(TARGET).elf
$(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf
clean:
rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \
$(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \
$(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \
$(TARGET).eeprom
squeaky_clean:
rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~
##########------------------------------------------------------##########
########## Programmer-specific details ##########
########## Flashing code to AVR using avrdude ##########
##########------------------------------------------------------##########
flash: $(TARGET).hex
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$<
flash_eeprom: $(TARGET).eeprom
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$<
terminal:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt
flash_dragon_isp: PROGRAMMER_TYPE = dragon_isp
flash_dragon_isp: PROGRAMMER_ARGS =
flash_dragon_isp: flash
##########------------------------------------------------------##########
########## Fuse settings and suitable defaults ##########
##########------------------------------------------------------##########
## Generic
FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m
fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \
$(PROGRAMMER_ARGS) $(FUSE_STRING)
show_fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv
## Called with no extra definitions, sets to defaults
set_default_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m
set_default_fuses: fuses

@ -0,0 +1,31 @@
Keyboard Lamp
=============
This is a keyboard lamp with adjustable brightness, that remembers last brightnbess level even when powered off.
I've made it as a replacement for my broken ThinkLight.
It uses ATtiny13, and the schematic is something like this:
Vcc
|
,---------------+
| |
| +----u----+ |
'--| |--'
| ATMEL | Buttons
,--[180R]-|<|---| |--BRT----o/ o---,
| LED2 | ATtiny | |
| -| 13 |--DIM----o/ o---+--- GND
GND | |
,--| |----,
| +---------+ | LED1
| | ,-|<|--[47R]-- Vcc
GND | |
'-[10k]-|< Transistor NPN
|
GND
LED1 is the bright white LED, LED2 is an indicator that the brightness has been saved. You can leave LED2 out if you want.
**Fuses** and other settings can be found in the Makefile.

@ -0,0 +1 @@
/home/ondra/devel/avr/avr-projects/lib

@ -0,0 +1,118 @@
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdbool.h>
#include "lib/meta.h"
#include "lib/pins.h"
/**
* +--u--+
* RST --| |-- Vcc
* SAVE LED : PB3 --| t13 |-- PB2 : BTN: HIGH
* N.C. : PB4 --| |-- PB1 : BTN: LOW
* GND --| |-- PB0 : LED
* +-----+
*/
// pins config
#define LED B,0
#define BTN_DOWN B,1
#define BTN_UP B,2
#define INDICATOR B,3
#define btn_on() pin_is_low(BTN_UP)
/** Initialize IO ports. */
void SECTION(".init8") init_io(void)
{
as_output(LED);
as_output(INDICATOR);
as_input(BTN_DOWN);
as_input(BTN_UP);
pullup_on(BTN_DOWN);
pullup_on(BTN_UP);
// initialize the timer
// Fast PWM mode, Output to OC0A
OCR0A = 32;
TCCR0A = _BV(WGM00) | _BV(WGM01) | _BV(COM0A1);
TCCR0B = _BV(CS00);
}
#define BRIGHTNESS_LEN 121
const uint8_t BRIGHTNESS[] PROGMEM = {
0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5,
6, 6, 6, 7, 7, 8, 8, 8, 9, 10, 10, 10, 11, 12, 13, 14, 14,
15, 16, 17, 18, 20, 21, 22, 24, 26, 27, 28, 30, 31, 32, 34,
35, 36, 38, 39, 40, 41, 42, 44, 45, 46, 48, 49, 50, 52, 54,
56, 58, 59, 61, 63, 65, 67, 69, 71, 72, 74, 76, 78, 80, 82,
85, 88, 90, 92, 95, 98, 100, 103, 106, 109, 112, 116, 119,
122, 125, 129, 134, 138, 142, 147, 151, 156, 160, 165, 170,
175, 180, 185, 190, 195, 200, 207, 214, 221, 228, 234, 241,
248, 255
};
#define apply_brightness(level) OCR0A = pgm_read_byte( & BRIGHTNESS[level] )
/**
* Main function
*/
void main()
{
bool changed = 0;
bool changed_since_last_save = 0;
uint8_t savetimer = 0;
uint8_t level = eeprom_read_byte((uint8_t *) 0);
if (level >= BRIGHTNESS_LEN)
level = BRIGHTNESS_LEN - 1;
else if (level == 0)
level = BRIGHTNESS_LEN / 2;
apply_brightness(level);
while (1) {
if (btn_on() && level < BRIGHTNESS_LEN - 1) {
level++;
changed = 1;
}
if (!btn_on() && level > 1) {
level--;
changed = 1;
}
if (changed) {
changed = 0;
savetimer = 0;
changed_since_last_save = 1;
apply_brightness(level);
}
_delay_ms(20);
savetimer++;
if (savetimer >= 50 && changed_since_last_save) {
changed_since_last_save = 0;
savetimer = 0;
pin_high(INDICATOR);
eeprom_update_byte((uint8_t*) 0, level);
_delay_ms(10);
pin_low(INDICATOR);
}
}
}

@ -1,10 +1,9 @@
#pragma once
#include <avr/io.h>
// general macros
#define SECTION(pos) __attribute__((naked, used, section(pos)))
// pin manipulation
#define sbi(port, bit) (port) |= _BV(bit)
#define cbi(port, bit) (port) &= ~ _BV(bit)

@ -12,7 +12,7 @@ MAIN = main.c
## If you've split your program into multiple files,
## include the additional .c source (in same directory) here
## (and include the .h files in your foo.c)
LOCAL_SOURCE = ws_driver.c
LOCAL_SOURCE =
## Here you can link to one more directory (and multiple .c files)
# EXTRA_SOURCE_DIR = ../AVR-Programming-Library/
@ -48,19 +48,22 @@ AVRSIZE = avr-size
AVRDUDE = sudo avrdude
## Compilation options, type man avr-gcc if you're curious.
CFLAGS = -std=gnu99 -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL -Os -I. -I$(EXTRA_SOURCE_DIR)
CFLAGS = -std=gnu99 -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL -I. -I$(EXTRA_SOURCE_DIR)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CFLAGS += -Wall -Wstrict-prototypes -Wno-main -Wno-strict-prototypes -Wno-comment
CFLAGS += -g2 -ggdb
CFLAGS += -Wall -Wno-main -Wno-strict-prototypes -Wno-comment
CFLAGS += -g2 -Wextra -pedantic
CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--relax
CFLAGS += -std=gnu99
CFLAGS_BUILD = $(CFLAGS) -Os
# CFLAGS += -lm
## CFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf
## CFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf
## Lump target and extra source files together
TARGET = $(strip $(basename $(MAIN)))
SRC = $(TARGET).c
SRC1 = $(TARGET).c
SRC = $(SRC1)
EXTRA_SOURCE = $(addprefix $(EXTRA_SOURCE_DIR), $(EXTRA_SOURCE_FILES))
SRC += $(EXTRA_SOURCE)
SRC += $(LOCAL_SOURCE)
@ -73,12 +76,16 @@ OBJ = $(SRC:.c=.o)
## Generic Makefile targets. (Only .hex file is necessary)
all: $(TARGET).hex size
pre: $(TARGET).pre
%.hex: %.elf
$(OBJCOPY) -R .eeprom -O ihex $< $@
%.elf: $(SRC)
$(CC) $(CFLAGS) $(SRC) --output $@
$(CC) $(CFLAGS_BUILD) $(SRC) --output $@
%.pre: $(SRC1)
$(CC) $(CFLAGS) -E $(SRC1) --output $@
%.eeprom: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@

@ -1,91 +0,0 @@
#pragma once
#include <avr/io.h>
#define D0 0
#define D1 1
#define D2 2
#define D3 3
#define D4 4
#define D5 5
#define D6 6
#define D7 7
#define D8 0
#define D9 1
#define D10 2
#define D11 3
#define D12 4
#define D13 5
#define A0 0
#define A1 1
#define A2 2
#define A3 3
#define A4 4
#define A5 5
// port definitions
#define PORT_D0 PORTD
#define PORT_D1 PORTD
#define PORT_D2 PORTD
#define PORT_D3 PORTD
#define PORT_D4 PORTD
#define PORT_D5 PORTD
#define PORT_D6 PORTD
#define PORT_D7 PORTD
#define PORT_D8 PORTB
#define PORT_D9 PORTB
#define PORT_D10 PORTB
#define PORT_D11 PORTB
#define PORT_D12 PORTB
#define PORT_D13 PORTB
#define PORT_A0 PORTC
#define PORT_A1 PORTC
#define PORT_A2 PORTC
#define PORT_A3 PORTC
#define PORT_A4 PORTC
#define PORT_A5 PORTC
#define PIN_D0 PIND
#define PIN_D1 PIND
#define PIN_D2 PIND
#define PIN_D3 PIND
#define PIN_D4 PIND
#define PIN_D5 PIND
#define PIN_D6 PIND
#define PIN_D7 PIND
#define PIN_D8 PINB
#define PIN_D9 PINB
#define PIN_D10 PINB
#define PIN_D11 PINB
#define PIN_D12 PINB
#define PIN_D13 PINB
#define PIN_A0 PINC
#define PIN_A1 PINC
#define PIN_A2 PINC
#define PIN_A3 PINC
#define PIN_A4 PINC
#define PIN_A5 PINC
#define DDR_D0 DDRD
#define DDR_D1 DDRD
#define DDR_D2 DDRD
#define DDR_D3 DDRD
#define DDR_D4 DDRD
#define DDR_D5 DDRD
#define DDR_D6 DDRD
#define DDR_D7 DDRD
#define DDR_D8 DDRB
#define DDR_D9 DDRB
#define DDR_D10 DDRB
#define DDR_D11 DDRB
#define DDR_D12 DDRB
#define DDR_D13 DDRB
#define DDR_A0 DDRC
#define DDR_A1 DDRC
#define DDR_A2 DDRC
#define DDR_A3 DDRC
#define DDR_A4 DDRC
#define DDR_A5 DDRC
#define OUT 1
#define IN 0

@ -1,13 +0,0 @@
#pragma once
#include "arduino_pins.h"
#define WS_PORT PORT_D8
#define WS_BIT D8
#define WS_DDR DDR_D8
#define WS_T_1H 700
#define WS_T_1L 150
#define WS_T_0H 150
#define WS_T_0L 700
#define WS_T_LATCH 6000

@ -0,0 +1 @@
/home/ondra/devel/avr/avr-projects/lib

@ -1,135 +1,78 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/cpufunc.h>
#include <util/delay.h>
#include <util/delay_basic.h>
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include "arduino_pins.h"
#include "utils.h"
#include "config.h"
#include "ws_driver.h"
#include "lib/meta.h"
#include "lib/arduino_pins.h"
#include "lib/calc.h"
#include "lib/colors.h"
#include "lib/ws2812.h"
#define WS1 D8
#define WS2 D9
void init_io(void) SECTION(".init8");
void init_io()
void SECTION(".init8") init_io()
{
set_bit(WS_DDR, WS_BIT, OUT);
as_output(WS1);
as_output(WS2);
}
void main()
{
const uint8_t anim_step = 5;
const uint8_t anim_max = 255;
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
const uint8_t anim_step = 10;
const uint8_t anim_max = 250;
const uint8_t pixel_count = 30;
xrgb_t color = xrgb(anim_max, 0, 0);
uint8_t step = 0;
uint8_t r2 = anim_max;
uint8_t g2 = 0;
uint8_t b2 = 0;
xrgb_t color2 = xrgb(anim_max, 0, 0);
uint8_t step2 = 0;
while (1) {
r = r2;
g = g2;
b = b2;
color = color2;
step = step2;
for (uint8_t i = 0; i < 72; i++) {
ws_send_rgb(r, g, b);
for (uint8_t i = 0; i < pixel_count; i++) {
ws_send_xrgb(WS1, color);
if (i == 1) {
r2 = r;
g2 = g;
b2 = b;
color2 = color;
step2 = step;
}
switch (step) {
case 0:
g += anim_step;
if (g == anim_max) step++;
color.g += anim_step;
if (color.g >= anim_max) step++;
break;
case 1:
r -= anim_step;
if (r == 0) step++;
color.r -= anim_step;
if (color.r == 0) step++;
break;
case 2:
b += anim_step;
if (b == anim_max) step++;
color.b += anim_step;
if (color.b >= anim_max) step++;
break;
case 3:
g -= anim_step;
if (g == 0) step++;
color.g -= anim_step;
if (color.g == 0) step++;
break;
case 4:
r += anim_step;
if (r == anim_max) step++;
color.r += anim_step;
if (color.r >= anim_max) step++;
break;
default:
b -= anim_step;
if (b == 0) step = 0;
color.b -= anim_step;
if (color.b == 0) step = 0;
break;
}
}
ws_show();
_delay_ms(30);
_delay_ms(20);
}
}
// void main()
// {
// while (1) {
// uint8_t r = 250;
// uint8_t g = 0;
// uint8_t b = 0;
// uint8_t step = 0;
// for (uint8_t i = 0; i < 30; i++) {
// ws_send_rgb(r, g, b);
// switch (step) {
// case 0:
// r -= 50;
// g += 50;
// if (g == 250) step++;
// break;
// case 1:
// g -= 50;
// b += 50;
// if (b == 250) step++;
// break;
// case 2:
// b -= 50;
// r += 50;
// if (r == 250) step=0;
// break;
// }
// }
// ws_show();
// _delay_ms(100);
// }
// }

@ -0,0 +1,158 @@
MCU = atmega328p
F_CPU = 16000000
LFUSE = 0xFF
HFUSE = 0xDE
EFUSE = 0x05
MAIN = main.c
## If you've split your program into multiple files,
## include the additional .c source (in same directory) here
## (and include the .h files in your foo.c)
LOCAL_SOURCE = lib/ws_driver.c
## Here you can link to one more directory (and multiple .c files)
# EXTRA_SOURCE_DIR = ../AVR-Programming-Library/
EXTRA_SOURCE_DIR =
EXTRA_SOURCE_FILES =
##########------------------------------------------------------##########
########## Programmer Defaults ##########
########## Set up once, then forget about it ##########
########## (Can override. See bottom of file.) ##########
##########------------------------------------------------------##########
#19200
PROGRAMMER_TYPE = arduino
PROGRAMMER_ARGS = -b 57600 -P /dev/ttyUSB0
##########------------------------------------------------------##########
########## Makefile Magic! ##########
########## Summary: ##########
########## We want a .hex file ##########
########## Compile source files into .elf ##########
########## Convert .elf file into .hex ##########
########## You shouldn't need to edit below. ##########
##########------------------------------------------------------##########
## Defined programs / locations
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
AVRSIZE = avr-size
AVRDUDE = sudo avrdude
## Compilation options, type man avr-gcc if you're curious.
CFLAGS = -std=gnu99 -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL -Os -I. -I$(EXTRA_SOURCE_DIR)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CFLAGS += -Wall -Wstrict-prototypes -Wno-main -Wno-strict-prototypes -Wno-comment
CFLAGS += -g2 -ggdb
CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--relax
CFLAGS += -std=gnu99
# CFLAGS += -lm
## CFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf
## CFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf
## Lump target and extra source files together
TARGET = $(strip $(basename $(MAIN)))
SRC = $(TARGET).c
EXTRA_SOURCE = $(addprefix $(EXTRA_SOURCE_DIR), $(EXTRA_SOURCE_FILES))
SRC += $(EXTRA_SOURCE)
SRC += $(LOCAL_SOURCE)
## List of all header files
HEADERS = $(SRC:.c=.h)
## For every .c file, compile an .o object file
OBJ = $(SRC:.c=.o)
## Generic Makefile targets. (Only .hex file is necessary)
all: $(TARGET).hex size
%.hex: %.elf
$(OBJCOPY) -R .eeprom -O ihex $< $@
%.elf: $(SRC)
$(CC) $(CFLAGS) $(SRC) --output $@
%.eeprom: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
debug:
@echo
@echo "Source files:" $(SRC)
@echo "MCU, F_CPU, BAUD:" $(MCU), $(F_CPU), $(BAUD)
@echo
# Optionally create listing file from .elf
# This creates approximate assembly-language equivalent of your code.
# Useful for debugging time-sensitive bits,
# or making sure the compiler does what you want.
disassemble: $(TARGET).lst
dis: disassemble
eeprom: $(TARGET).eeprom
%.lst: %.elf
$(OBJDUMP) -S $< > $@
# Optionally show how big the resulting program is
size: $(TARGET).elf
$(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf
clean:
rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \
$(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \
$(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \
$(TARGET).eeprom
squeaky_clean:
rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~
##########------------------------------------------------------##########
########## Programmer-specific details ##########
########## Flashing code to AVR using avrdude ##########
##########------------------------------------------------------##########
flash: $(TARGET).hex
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$<
flash_eeprom: $(TARGET).eeprom
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$<
terminal:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt
flash_arduino: PROGRAMMER_TYPE = arduino
flash_arduino: PROGRAMMER_ARGS =
flash_arduino: flash
flash_dragon_isp: PROGRAMMER_TYPE = dragon_isp
flash_dragon_isp: PROGRAMMER_ARGS =
flash_dragon_isp: flash
##########------------------------------------------------------##########
########## Fuse settings and suitable defaults ##########
##########------------------------------------------------------##########
## Generic
FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \
$(PROGRAMMER_ARGS) $(FUSE_STRING)
show_fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv
## Called with no extra definitions, sets to defaults
set_default_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
set_default_fuses: fuses

@ -0,0 +1,13 @@
WS2812B RGB LED strip driving code
==================================
This code is meant for 16MHz Arduino, but should work on any 16MHz AVR. It is in plain C, not the Arduino language.
All you need is `ws_driver.c` and `ws_driver.h`, and `config.c` where you define the port / pin information.
Connection
----------
Digital pin 8 on Arduino (PB0) is connected via a 200-500 R to WS2812B data input. In parallel to the LED strip is a 500-1000 uF capacitor that takes care of smoothening the power supply. Without the cap, the LEDs will flicker.
It is recommended to use stronger power supply for the LED strip, a 30-led one takes around to 1A when fully powered.

@ -0,0 +1,16 @@
#pragma once
#include "lib/arduino_pins2.h"
#define WSDATA_in D8_in
#define WSDATA_out D8_out
#define WSDATA_dir D8_dir
#define WS_T_1H 700
#define WS_T_1L 150
#define WS_T_0H 150
#define WS_T_0L 700
#define WS_T_LATCH 6000
# define DEBOUNCE_COUNT 63
#define DEBOUNCE_SLOT_COUNT 4

@ -0,0 +1,22 @@
#pragma once
#include "lib/arduino_pins.h"
#define PIN_LEFT PIN_D2
#define DDR_LEFT DDR_D2
#define PORT_LEFT PORT_D2
#define LEFT D2
#define PIN_RIGHT PIN_D3
#define DDR_RIGHT DDR_D3
#define PORT_RIGHT PORT_D3
#define RIGHT D3
#define PIN_UP PIN_D4
#define DDR_UP DDR_D4
#define PORT_UP PORT_D4
#define UP D4
#define PIN_DOWN PIN_D5
#define DDR_DOWN DDR_D5
#define PORT_DOWN PORT_D5
#define DOWN D5

@ -0,0 +1 @@
/home/ondra/devel/avr/avr-projects/lib

@ -0,0 +1,89 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/cpufunc.h>
#include <util/delay.h>
#include <util/delay_basic.h>
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include "lib/arduino_pins2.h"
#include "lib/utils.h"
#include "lib/ws_driver.h"
#include "lib/debounce.h"
#include "config.h"
#include "gamepad_pins.h"
void init_io() SECTION(".init8")
{
// WS2182B data pin as output
set_bit(WS_dir, OUT);
// enable pullups on input pins
set_bit(LEFT_out);
sbi(PORT_RIGHT, RIGHT);
sbi(PORT_UP, UP);
sbi(PORT_DOWN, DOWN);
// init timer
TCCR0A = _BV(WGM01); // CTC mode
TCCR0B = _BV(CS02) | _BV(CS00); // prescaler 1024
OCR0A = 250; // interrupt every 16 ms
sbi(TIMSK0, OCIE0A); // enable interrupt from timer
// set up debouncer
debounce_register(0, &PIN)
}
/** timer 0 interrupt vector */
ISR(TIM0_COMPA_vect)
{
debounce_tick();
}
typedef struct
{
uint8_t r,
uint8_t g,
uint8_t b,
} color_t;
#define RED {.r=0xFF, .g=0x00, .b=0x00}
#define GREEN {.r=0xFF, .g=0x00, .b=0x00}
#define BLUE {.r=0xFF, .g=0x00, .b=0x00}
#define YELLOW {.r=0xFF, .g=0xFF, .b=0x00}
#define MAGENTA {.r=0xFF, .g=0x00, .b=0xFF}
#define CYAN {.r=0x00, .g=0xFF, .b=0xFF}
#define BLACK {.r=0x00, .g=0x00, .b=0x00}
#define WHITE {.r=0xFF, .g=0xFF, .b=0xFF}
#define BULB_COUNT 4;
color_t bulbs[BULB_COUNT];
void show_lights()
{
for (uint8_t i = 0; i < BULB_COUNT; i++) {
ws_send_rgb(bulbs[i].r, bulbs[i].g, bulbs[i].b);
}
ws_show();
}
void main()
{
while (1) {
show_lights();
}
}

@ -19,6 +19,7 @@
#define delay_ns_c(ns, c) delay_c(ns2cycles(ns) + (c))
/** Latch and display the RGB values */
void ws_show()
{
ws_low();
@ -26,6 +27,7 @@ void ws_show()
}
/** Send one byte to the RGB strip */
void ws_send_byte(uint8_t bb)
{
for (int8_t i = 7; i >= 0; --i) {
@ -46,9 +48,13 @@ void ws_send_byte(uint8_t bb)
}
/** Send RGB color to the strip */
void ws_send_rgb(uint8_t r, uint8_t g, uint8_t b)
{
int8_t i;
// manualy inlined, because `inline` doesn't work with delays.
// GREEN
for (i = 7; i >= 0; --i) {
if (g & (1 << i)) {

@ -2,7 +2,7 @@
// -- AVR GCC utility for driving WS2812B and similar RGB LED stripes --
// You must define the foillowing in config file or here:
// You must define the following in config file or here:
// * WS_PORT
// * WS_BIT
@ -34,7 +34,7 @@
#endif
// More accurate timing
// More precise timing
// #define WS_T_1H 800
// #define WS_T_1L 450
// #define WS_T_0H 200