parent
34103b7065
commit
e78d91a900
@ -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) |
@ -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 |
@ -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(); |
||||
} |
||||
} |
@ -0,0 +1,97 @@ |
||||
#include <avr/io.h> |
||||
#include <util/delay_basic.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "extra_delays.h" |
||||
#include "pins.h" |
||||
#include "ws_driver2.h" |
||||
|
||||
|
||||
|
||||
/** Latch and display the RGB values */ |
||||
void ws_show_real(PORT_P portp, BIT_N pin) |
||||
{ |
||||
cbi_p(portp, pin); |
||||
delay_ns_c(WS_T_LATCH, 0); |
||||
} |
||||
|
||||
|
||||
/** Send one byte to the RGB strip */ |
||||
void ws_send_byte_real(PORT_P portp, BIT_N pin, uint8_t bb) |
||||
{ |
||||
for (int8_t i = 7; i >= 0; --i) { |
||||
if (bb & (1 << i)) { |
||||
// send ONE
|
||||
sbi_p(portp, pin); |
||||
delay_ns_c(WS_T_1H, -2); |
||||
cbi_p(portp, pin); |
||||
delay_ns_c(WS_T_1L, -10); // compensation for overhead
|
||||
} else { |
||||
// send ZERO
|
||||
sbi_p(portp, pin); |
||||
delay_ns_c(WS_T_0H, -2); |
||||
cbi_p(portp, pin); |
||||
delay_ns_c(WS_T_0L, -10); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** Send RGB color to the strip */ |
||||
void ws_send_rgb_real(PORT_P portp, BIT_N pin, 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)) { |
||||
// send ONE
|
||||
sbi_p(portp, pin); |
||||
delay_ns_c(WS_T_1H, -2); |
||||
cbi_p(portp, pin); |
||||
delay_ns_c(WS_T_1L, -10); |
||||
} else { |
||||
// send ZERO
|
||||
sbi_p(portp, pin); |
||||
delay_ns_c(WS_T_0H, -2); |
||||
cbi_p(portp, pin); |
||||
delay_ns_c(WS_T_0L, -10); |
||||
} |
||||
} |
||||
|
||||
// RED
|
||||
for (i = 7; i >= 0; --i) { |
||||
if (r & (1 << i)) { |
||||
// send ONE
|
||||
sbi_p(portp, pin); |
||||
delay_ns_c(WS_T_1H, -2); |
||||
cbi_p(portp, pin); |
||||
delay_ns_c(WS_T_1L, -10); |
||||
} else { |
||||
// send ZERO
|
||||
sbi_p(portp, pin); |
||||
delay_ns_c(WS_T_0H, -2); |
||||
cbi_p(portp, pin); |
||||
delay_ns_c(WS_T_0L, -10); |
||||
} |
||||
} |
||||
|
||||
// BLUE
|
||||
for (i = 7; i >= 0; --i) { |
||||
if (b & (1 << i)) { |
||||
// send ONE
|
||||
sbi_p(portp, pin); |
||||
delay_ns_c(WS_T_1H, -2); |
||||
cbi_p(portp, pin); |
||||
delay_ns_c(WS_T_1L, -10); |
||||
} else { |
||||
// send ZERO
|
||||
sbi_p(portp, pin); |
||||
delay_ns_c(WS_T_0H, -2); |
||||
cbi_p(portp, pin); |
||||
delay_ns_c(WS_T_0L, -10); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,56 @@ |
||||
#pragma once |
||||
|
||||
#include "pins.h" |
||||
|
||||
// -- Utility for driving WS2812B and similar RGB LED stripes --
|
||||
|
||||
// --- 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 |
||||
|
||||
|
||||
// More precise timing
|
||||
// #define WS_T_1H 800
|
||||
// #define WS_T_1L 450
|
||||
// #define WS_T_0H 200
|
||||
// #define WS_T_0L 650
|
||||
// #define WS_T_LATCH 50000
|
||||
|
||||
|
||||
// Only ws_show / ws_send_byte / ws_send_rgb should be used, with port alias macros
|
||||
// See pins.h for reference
|
||||
|
||||
/** Latch and display the RGB values */ |
||||
void ws_show_real(PORT_P port, BIT_N pin); |
||||
#define ws_show_aux(port, pin) ws_show_real(®_port(port), pin) |
||||
#define ws_show(io) ws_show_aux(io) |
||||
|
||||
|
||||
/** Send one byte to the RGB strip */ |
||||
void ws_send_byte_real(PORT_P port, BIT_N pin, uint8_t b); |
||||
#define ws_send_byte_aux(port, pin, b) ws_send_byte_real(®_port(port), pin, b) |
||||
#define ws_send_byte(io, b) ws_send_byte_aux(io, b) |
||||
|
||||
|
||||
/** Send RGB color to the strip */ |
||||
void ws_send_rgb_real(PORT_P port, BIT_N pin, uint8_t r, uint8_t g, uint8_t b); |
||||
#define ws_send_rgb_aux(port, pin, r, g, b) ws_send_rgb_real(®_port(port), pin, r, g, b) |
||||
#define ws_send_rgb(io, r, g, b) ws_send_rgb_aux(io, r, g, b) |
@ -0,0 +1,75 @@ |
||||
#include <avr/io.h> |
||||
#include <avr/sfr_defs.h> |
||||
#include <util/delay_basic.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "ws_driver3.h" |
||||
|
||||
#define WS_PORT PORTB |
||||
#define WS_BIT 0 |
||||
|
||||
uint16_t ws_port = _SFR_ADDR(PORTB); |
||||
uint8_t ws_bit; |
||||
|
||||
/** Raise the bus line *///_SFR_MEM8(ws_port)
|
||||
#define ws_high() do { _SFR_MEM8(ws_port) |= (1 << 0); } while(0) |
||||
//#define ws_high() (_SFR_MEM8(_SFR_ADDR(PORTB))) |= (1 << WS_BIT)
|
||||
|
||||
/** Drop the bus line */ |
||||
#define ws_low() do { _SFR_MEM8(ws_port) &= ~(1 << 0); } while(0) |
||||
//#define ws_low() (_SFR_MEM8(_SFR_ADDR(PORTB))) &= ~(1 << WS_BIT)
|
||||
|
||||
/* 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)) |
||||
|
||||
|
||||
void ws_bind(uint8_t port_addr, uint8_t bit) |
||||
{ |
||||
// ws_port = port_addr;
|
||||
// ws_bit = bit;
|
||||
} |
||||
|
||||
|
||||
|
||||
/** Latch and display the RGB values */ |
||||
void ws_show() |
||||
{ |
||||
ws_low(); |
||||
delay_ns_c(WS_T_LATCH, 0); |
||||
} |
||||
|
||||
#define _do_ws_send_byte(x) do { \ |
||||
for (int8_t i = 7; i >= 0; --i) { \
|
||||
if (x & (1 << i)) { \
|
||||
ws_high(); \
|
||||
delay_ns_c(WS_T_1H, -2); \
|
||||
ws_low(); \
|
||||
delay_ns_c(WS_T_1L, -10); \
|
||||
} else { \
|
||||
ws_high(); \
|
||||
delay_ns_c(WS_T_0H, -2); \
|
||||
ws_low(); \
|
||||
delay_ns_c(WS_T_0L, -10); \
|
||||
} \
|
||||
} \
|
||||
} while(0) |
||||
|
||||
void ws_send_byte(uint8_t b) |
||||
{ |
||||
_do_ws_send_byte(b); |
||||
} |
||||
|
||||
|
||||
/** Send RGB color to the strip */ |
||||
void ws_send_rgb(uint8_t r, uint8_t g, uint8_t b) |
||||
{ |
||||
_do_ws_send_byte(g); |
||||
_do_ws_send_byte(r); |
||||
_do_ws_send_byte(b); |
||||
} |
@ -0,0 +1,54 @@ |
||||
#pragma once |
||||
|
||||
// -- AVR GCC utility for driving WS2812B and similar RGB LED stripes --
|
||||
|
||||
// You must define the following in config file or here:
|
||||
|
||||
// The pin must be set to OUTPUT before using the output functions
|
||||
|
||||
|
||||
// --- 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 7000 |
||||
#endif |
||||
|
||||
|
||||
// More precise timing
|
||||
// #define WS_T_1H 800
|
||||
// #define WS_T_1L 450
|
||||
// #define WS_T_0H 200
|
||||
// #define WS_T_0L 650
|
||||
// #define WS_T_LATCH 50000
|
||||
|
||||
|
||||
|
||||
void ws_bind(uint8_t port_addr, uint8_t bit); |
||||
|
||||
|
||||
/** Latch and display the RGB values */ |
||||
void ws_show(void); |
||||
|
||||
|
||||
/** Send one byte to the RGB strip */ |
||||
void ws_send_byte(uint8_t b); |
||||
|
||||
|
||||
/** Send RGB color to the strip */ |
||||
void ws_send_rgb(uint8_t r, uint8_t g, uint8_t b); |
@ -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,16 @@ |
||||
#include <avr/io.h> |
||||
|
||||
void led_on(volatile uint8_t* portp, uint8_t pinn) { |
||||
*portp |= _BV(pinn); |
||||
} |
||||
|
||||
void led_off(volatile uint8_t* portp, uint8_t pinn) { |
||||
*portp &= ~ _BV(pinn); |
||||
} |
||||
|
||||
void main() |
||||
{ |
||||
led_on(&PORTB, 3); |
||||
led_off(&PORTB, 5); |
||||
led_on(&DDRB, 3); |
||||
} |
Loading…
Reference in new issue