commit
484bc2480b
@ -0,0 +1,2 @@ |
||||
.idea |
||||
build |
@ -0,0 +1,94 @@ |
||||
# https://github.com/jkent/caterina-promicro
|
||||
|
||||
MCU = atmega328p
|
||||
F_CPU = 16000000
|
||||
|
||||
# AVRDUDE settings # 57600
|
||||
PROG_BAUD = 57600
|
||||
PROG_DEV = /dev/ttyUSB0
|
||||
PROG_TYPE = arduino
|
||||
# Build the final AVRDUDE arguments
|
||||
PROG_ARGS = -c $(PROG_TYPE) -p $(MCU) -b $(PROG_BAUD) -P $(PROG_DEV)
|
||||
|
||||
INCFLAGS += -Isrc/ -Ilib/porklib/
|
||||
|
||||
LIB_SOURCES =
|
||||
LIB_SOURCES += lib/porklib/iopins.c
|
||||
LIB_SOURCES += lib/porklib/wsrgb.c
|
||||
LIB_SOURCES += lib/porklib/color.c
|
||||
|
||||
CFLAGS = -std=gnu99 -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL
|
||||
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
|
||||
CFLAGS += -Wall -Wno-main -Wno-strict-prototypes -Wno-comment
|
||||
CFLAGS += -g2 -Wextra -Wfatal-errors -Wno-unused-but-set-variable
|
||||
CFLAGS += -ffunction-sections -fdata-sections -Os -Wno-array-bounds
|
||||
|
||||
LFLAGS = -Wl,--gc-sections -Wl,--relax
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
APP = App
|
||||
SRC_DIR = src
|
||||
BUILD_DIR = build
|
||||
|
||||
## Defined programs / locations
|
||||
CC = avr-gcc
|
||||
LD = avr-gcc
|
||||
OBJCOPY = avr-objcopy
|
||||
OBJDUMP = avr-objdump
|
||||
AVRSIZE = avr-size
|
||||
AVRDUDE = avrdude
|
||||
|
||||
SOURCES=$(wildcard $(SRC_DIR)/*.c) $(LIB_SOURCES)
|
||||
OBJECTS=$(SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
|
||||
DEPENDS=$(BUILD_DIR)/.depends
|
||||
|
||||
TARGET=$(BUILD_DIR)/$(APP)
|
||||
|
||||
|
||||
.PHONY: all clean eeprom size |
||||
|
||||
all: $(TARGET).hex size |
||||
|
||||
debug: |
||||
@echo "SOURCES $(SOURCES)"
|
||||
@echo "OBJECTS $(OBJECTS)"
|
||||
@echo "TARGET $(TARGET)"
|
||||
|
||||
eeprom: $(TARGET).eeprom |
||||
|
||||
size: $(TARGET).elf |
||||
$(AVRSIZE) -C --mcu=$(MCU) $<
|
||||
|
||||
$(TARGET).elf: $(OBJECTS) | $(BUILD_DIR) |
||||
$(LD) $(CFLAGS) $(INCFLAGS) $(LFLAGS) -o $@ $^
|
||||
|
||||
%.hex: %.elf |
||||
$(OBJCOPY) -R .eeprom -O ihex $< $@
|
||||
|
||||
%.eeprom: %.elf |
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) |
||||
$(CC) -c $(CFLAGS) $(INCFLAGS) -o $@ $<
|
||||
|
||||
$(DEPENDS): $(SOURCES) | $(BUILD_DIR) |
||||
$(CC) $(INCFLAGS) -MM $(SOURCES) | sed -e 's!^!$(BUILD_DIR)/!' >$@
|
||||
|
||||
$(BUILD_DIR): |
||||
mkdir -p $@
|
||||
|
||||
clean: |
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
## === avrdude ===
|
||||
|
||||
flash: $(TARGET).hex |
||||
$(AVRDUDE) $(PROG_ARGS) -U flash:w:$<
|
||||
|
||||
flashe: $(TARGET).eeprom |
||||
$(AVRDUDE) $(PROG_ARGS) -U eeprom:w:$<
|
||||
|
||||
shell: |
||||
$(AVRDUDE) $(PROG_ARGS) -nt
|
@ -0,0 +1,67 @@ |
||||
#include <avr/io.h> |
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "calc.h" |
||||
#include "adc.h" |
||||
|
||||
/** Initialize the ADC */ |
||||
void adc_init() |
||||
{ |
||||
ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128 prescaler -> 125 kHz
|
||||
// ADCSRA |= _BV(ADPS2) | _BV(ADPS0); // 32 prescaler -> 500 kHz, good for 8-bit measurement
|
||||
|
||||
ADMUX |= _BV(REFS0) | _BV(REFS1); // Voltage reference = internal 1.1V
|
||||
|
||||
sbi(ADCSRA, ADEN); // Enable ADC
|
||||
} |
||||
|
||||
|
||||
/** Disable AD */ |
||||
void adc_disable() |
||||
{ |
||||
cbi(ADCSRA, ADEN); |
||||
} |
||||
|
||||
|
||||
/** Sample analog pin with 8-bit precision */ |
||||
uint8_t adc_read_byte(uint8_t channel) |
||||
{ |
||||
set_low_nibble(ADMUX, channel); // Select channel to sample
|
||||
sbi(ADMUX, ADLAR); // Align result to left
|
||||
sbi(ADCSRA, ADSC); // Start conversion
|
||||
|
||||
while (bit_is_high(ADCSRA, ADSC)); // Wait for it...
|
||||
|
||||
return ADCH; // The upper 8 bits of ADC result
|
||||
} |
||||
|
||||
|
||||
/** Sample analog pin with 10-bit precision */ |
||||
uint16_t adc_read_word(uint8_t channel) |
||||
{ |
||||
set_low_nibble(ADMUX, channel); // Select channel to sample
|
||||
cbi(ADMUX, ADLAR); // Align result to right
|
||||
sbi(ADCSRA, ADSC); // Start conversion
|
||||
|
||||
while (get_bit(ADCSRA, ADSC)); // Wait for it...
|
||||
|
||||
return ADCW; // The whole ADC word (10 bits)
|
||||
} |
||||
|
||||
/** Sample analog pin with 10-bit precision */ |
||||
void adc_async_start_measure_word(uint8_t channel) |
||||
{ |
||||
set_low_nibble(ADMUX, channel); // Select channel to sample
|
||||
cbi(ADMUX, ADLAR); // Align result to right
|
||||
sbi(ADCSRA, ADSC); // Start conversion
|
||||
} |
||||
|
||||
bool adc_async_ready() |
||||
{ |
||||
return 0 == get_bit(ADCSRA, ADSC); |
||||
} |
||||
|
||||
uint16_t adc_async_get_result_word() { |
||||
return ADCW; // The whole ADC word (10 bits)
|
||||
} |
@ -0,0 +1,26 @@ |
||||
#pragma once |
||||
|
||||
//
|
||||
// Utilities for build-in A/D converter
|
||||
//
|
||||
|
||||
#include <avr/io.h> |
||||
#include <stdint.h> |
||||
|
||||
/** Initialize the ADC */ |
||||
void adc_init(); |
||||
|
||||
/** Disable AD (for power saving?) */ |
||||
void adc_disable(); |
||||
|
||||
/** Sample analog pin with 8-bit precision */ |
||||
uint8_t adc_read_byte(uint8_t channel); |
||||
|
||||
/** Sample analog pin with 10-bit precision */ |
||||
uint16_t adc_read_word(uint8_t channel); |
||||
|
||||
void adc_async_start_measure_word(uint8_t channel); |
||||
|
||||
bool adc_async_ready(); |
||||
|
||||
uint16_t adc_async_get_result_word(); |
@ -0,0 +1,66 @@ |
||||
#pragma once |
||||
|
||||
//
|
||||
// Block device interface, somewhat akin to stream.h
|
||||
// Used for filesystem implementations.
|
||||
//
|
||||
|
||||
#include <stdint.h> |
||||
|
||||
/** Abstract block device interface
|
||||
* |
||||
* Populate an instance of this with pointers to your I/O functions. |
||||
*/ |
||||
typedef struct |
||||
{ |
||||
/** Sequential read at cursor
|
||||
* @param dest destination memory structure |
||||
* @param len number of bytes to load and store in {dest} |
||||
*/ |
||||
void (*load)(void* dest, const uint16_t len); |
||||
|
||||
|
||||
/** Sequential write at cursor
|
||||
* @param src source memory structure |
||||
* @param len number of bytes to write |
||||
*/ |
||||
void (*store)(const void* src, const uint16_t len); |
||||
|
||||
|
||||
/** Write one byte at cursor
|
||||
* @param b byte to write |
||||
*/ |
||||
void (*write)(const uint8_t b); |
||||
|
||||
|
||||
/** Read one byte at cursor
|
||||
* @return the read byte |
||||
*/ |
||||
uint8_t (*read)(void); |
||||
|
||||
|
||||
/** Absolute seek - set cursor
|
||||
* @param addr new cursor address |
||||
*/ |
||||
void (*seek)(const uint32_t addr); |
||||
|
||||
|
||||
/** Relative seek - move cursor
|
||||
* @param offset cursor address change |
||||
*/ |
||||
void (*rseek)(const int16_t offset); |
||||
|
||||
|
||||
|
||||
/** Flush the data buffer if it's dirty.
|
||||
* |
||||
* Should be called after each sequence of writes, |
||||
* to avoid data loss. |
||||
* |
||||
* Tmplementations that do not need this should provide |
||||
* a no-op function. |
||||
*/ |
||||
void (*flush)(void); |
||||
|
||||
} BLOCKDEV; |
||||
|
@ -0,0 +1,89 @@ |
||||
#pragma once |
||||
|
||||
//
|
||||
// Bit and byte manipulation utilities
|
||||
//
|
||||
|
||||
#include <stdint.h> |
||||
|
||||
|
||||
// --- Increment in range ---
|
||||
// when overflown, wraps within range. Lower bound < upper bound.
|
||||
// ..., upper bound excluded
|
||||
#define inc_wrap(var, min, max) { if ((var) >= (max - 1)) { (var) = (min); } else { (var)++; } } |
||||
// ..., upper bound included
|
||||
#define inc_wrapi(var, min, max) inc_wrap((var), (min), (max) + 1) |
||||
|
||||
|
||||
// --- Decrement in range ---
|
||||
// when underflown, wraps within range. Lower bound < upper bound.
|
||||
// ..., upper bound excluded
|
||||
#define dec_wrap(var, min, max) { if ((var) <= (min)) { (var) = (max) - 1; } else { (var)--; } } |
||||
// ..., upper bound included
|
||||
#define dec_wrapi(var, min, max) dec_wrap((var), (min), (max) + 1) |
||||
|
||||
|
||||
// --- Bit manipulation --
|
||||
|
||||
// Set bit
|
||||
#define sbi(reg, bit) { (reg) |= (1 << (uint8_t)(bit)); } |
||||
|
||||
// Clear bit
|
||||
#define cbi(reg, bit) { (reg) &= ~(1 << (uint8_t)(bit)); } |
||||
|
||||
// Get n-th bit
|
||||
#define get_bit(reg, bit) (((reg) >> (uint8_t)(bit)) & 0x1) |
||||
|
||||
// Test n-th bit (Can't use bit_is_set, as it's redefined in sfr_def.h)
|
||||
#define bit_is_high(reg, bit) get_bit(reg, bit) |
||||
#define bit_is_low(reg, bit) (!get_bit(reg, bit)) |
||||
|
||||
// Write value to n-th bit
|
||||
#define set_bit(reg, bit, value) { (reg) = ((reg) & ~(1 << (uint8_t)(bit))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } |
||||
|
||||
// Invert n-th bit
|
||||
#define toggle_bit(reg, bit) { (reg) ^= (1 << (uint8_t)(bit)); } |
||||
|
||||
|
||||
// --- Bit manipulation with pointer to variable ---
|
||||
|
||||
// Set n-th bit in pointee
|
||||
#define sbi_p(reg_p, bit) { (*(reg_p)) |= (1 << (uint8_t)(bit)); } |
||||
// Clear n-th bit in pointee
|
||||
#define cbi_p(reg_p, bit) { (*(reg_p)) &= ~(1 << (uint8_t)(bit)); } |
||||
|
||||
// Get n-th bit in pointee
|
||||
#define get_bit_p(reg_p, bit) ((*(reg_p) >> (uint8_t)(bit)) & 0x1) |
||||
|
||||
// Test n-th bit in pointee (Can't use bit_is_set, as it's redefined in sfr_def.h)
|
||||
#define bit_is_high_p(reg_p, bit) get_bit_p(reg_p, bit) |
||||
#define bit_is_low_p(reg_p, bit) (!get_bit_p(reg_p, bit)) |
||||
|
||||
// Write value to a bit in pointee
|
||||
#define set_bit_p(reg_p, bit, value) { *(reg_p) = (*(reg_p) & ~(1 << ((uint8_t)(bit) & 0x1))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } |
||||
#define toggle_bit_p(reg_p, bit) { *(reg_p) ^= (1 << (uint8_t)(bit)); } |
||||
|
||||
|
||||
// --- Nibble manipulation ---
|
||||
|
||||
// Replace nibble in a byte
|
||||
#define set_low_nibble(reg, value) { (reg) = ((reg) & 0xF0) | ((uint8_t)(value) & 0xF); } |
||||
#define set_high_nibble(reg, value) { (reg) = ((reg) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } |
||||
|
||||
#define set_low_nibble_p(reg_p, value) { *(reg_p) = (*(reg_p) & 0xF0) | ((uint8_t)(value) & 0xF); } |
||||
#define set_high_nibble_p(reg_p, value) { *(reg_p) = (*(reg_p) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } |
||||
|
||||
#define low_nibble(x) ((uint8_t)(x) & 0xF) |
||||
#define high_nibble(x) (((uint8_t)(x) & 0xF0) >> 4) |
||||
|
||||
// --- Range tests ---
|
||||
|
||||
// Test if X is within low..high, regardless of bounds order
|
||||
#define in_range(x, low, high) ((((low) < (high)) && ((x) >= (low) && (x) < (high))) || (((low) > (high)) && ((x) >= (high) && (x) < (low)))) |
||||
// ..., include greater bound
|
||||
#define in_rangei(x, low, high) ((((low) <= (high)) && ((x) >= (low) && (x) <= (high))) || (((low) > (high)) && ((x) >= (high) && (x) <= (low)))) |
||||
|
||||
// Test if X in low..high, wrap around ends if needed.
|
||||
#define in_range_wrap(x, low, high) ((((low) < (high)) && ((x) >= (low) && (x) < (high))) || (((low) > (high)) && ((x) >= (low) || (x) < (high)))) |
||||
// ..., include upper bound
|
||||
#define in_range_wrapi(x, low, high) ((((low) <= (high)) && ((x) >= (low) && (x) <= (high))) || (((low) > (high)) && ((x) >= (low) || (x) <= (high)))) |
@ -0,0 +1,103 @@ |
||||
#include <avr/io.h> |
||||
#include <util/delay.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "iopins.h" |
||||
#include "nsdelay.h" |
||||
#include "color.h" |
||||
|
||||
|
||||
// --- HSL ---
|
||||
|
||||
#ifdef HSL_LINEAR |
||||
const uint8_t FADE_128[] = |
||||
{ |
||||
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, 68, 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, |
||||
153, 156, 160, 163, 165, 170, 175, 180, 185, 190, 195, 200, 207, 214, 218, |
||||
221, 225, 228, 232, 234, 241, 248, 254, 255 |
||||
}; |
||||
#endif |
||||
|
||||
// based on: https://github.com/lewisd32/avr-hsl2rgb
|
||||
xrgb_t hsl_xrgb(const hsl_t cc) |
||||
{ |
||||
// 0 .. 256*3
|
||||
const uint16_t hh = (uint16_t) cc.h * 3; |
||||
const uint8_t hue_mod = hh % 256; |
||||
|
||||
uint8_t r_temp, g_temp, b_temp; |
||||
if (hh < 256) |
||||
{ |
||||
r_temp = hue_mod ^ 255; |
||||
g_temp = hue_mod; |
||||
b_temp = 0; |
||||
} |
||||
else if (hh < 512) |
||||
{ |
||||
r_temp = 0; |
||||
g_temp = hue_mod ^ 255; |
||||
b_temp = hue_mod; |
||||
} |
||||
else if (hh < 768) |
||||
{ |
||||
r_temp = hue_mod; |
||||
g_temp = 0; |
||||
b_temp = hue_mod ^ 255; |
||||
} |
||||
else |
||||
{ |
||||
r_temp = 0; |
||||
g_temp = 0; |
||||
b_temp = 0; |
||||
} |
||||
|
||||
const uint8_t inverse_sat = (cc.s ^ 255); |
||||
|
||||
xrgb_t rgb; |
||||
|
||||
uint8_t t8; |
||||
uint16_t t16; |
||||
|
||||
#ifdef HSL_LINEAR |
||||
const uint8_t bri = FADE_128[cc.l >> 1]; |
||||
#else |
||||
const uint8_t bri = cc.l; |
||||
#endif |
||||
|
||||
t8 = r_temp; |
||||
t16 = t8 * cc.s + t8; |
||||
t16 = t16 + t8; |
||||
t8 = t16 >> 8; |
||||
t8 = t8 + inverse_sat; |
||||
t16 = t8 * bri; |
||||
t16 = t16 + t8; |
||||
t8 = t16 >> 8; |
||||
rgb.r = t8; |
||||
|
||||
t8 = g_temp; |
||||
t16 = t8 * cc.s; |
||||
t16 = t16 + t8; |
||||
t8 = t16 >> 8; |
||||
t8 = t8 + inverse_sat; |
||||
t16 = t8 * bri; |
||||
t16 = t16 + t8; |
||||
t8 = t16 >> 8; |
||||
rgb.g = t8; |
||||
|
||||
t8 = b_temp; |
||||
t16 = t8 * cc.s; |
||||
t16 = t16 + t8; |
||||
t8 = t16 >> 8; |
||||
t8 = t8 + inverse_sat; |
||||
t16 = t8 * bri; |
||||
t16 = t16 + t8; |
||||
t8 = t16 >> 8; |
||||
rgb.b = t8; |
||||
|
||||
return rgb; |
||||
} |
@ -0,0 +1,57 @@ |
||||
#pragma once |
||||
|
||||
// --- color types ---
|
||||
//
|
||||
// The XXXc macros don't use cast, so they can be used in array initializers.
|
||||
//
|
||||
// xrgb ... 3-byte true-color RGB (8 bits per component)
|
||||
// rgb24 ... 24-bit color value, with equal nr of bits per component
|
||||
//
|
||||
// XX_r (_g, _b) ... extract component from the color, and convert it to 0..255
|
||||
|
||||
// Define HSL_LINEAR to get more linear brightness in hsl->rgb conversion
|
||||
|
||||
|
||||
typedef struct |
||||
{ |
||||
uint8_t r; |
||||
uint8_t g; |
||||
uint8_t b; |
||||
} xrgb_t; |
||||
|
||||
|
||||
typedef uint32_t rgb24_t; |
||||
|
||||
#define xrgb(rr, gg, bb) ((xrgb_t)xrgbc(rr, gg, bb)) |
||||
// xrgb for constant array declarations
|
||||
#define xrgbc(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 rgb24c(r,g,b) (((((rgb24_t)r) & 0xFF) << 16) | ((((rgb24_t)g) & 0xFF) << 8) | (((rgb24_t)b) & 0xFF)) |
||||
#define rgb24(r,g,b) ((rgb24_t) rgb24(r,g,b)) |
||||
|
||||
#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 rgb24_xrgbc(c) xrgbc(rgb24_r(c), rgb24_g(c), rgb24_b(c)) |
||||
|
||||
#define add_xrgb(x, y) ((xrgb_t) { (((y).r > (255 - (x).r)) ? 255 : ((x).r + (y).r)), (((y).g > (255 - (x).g)) ? 255 : ((x).g + (y).g)), (((y).b > 255 - (x).b) ? 255 : ((x).b + (y).b)) }) |
||||
|
||||
|
||||
// HSL data structure
|
||||
typedef struct |
||||
{ |
||||
uint8_t h; |
||||
uint8_t s; |
||||
uint8_t l; |
||||
} hsl_t; |
||||
|
||||
/* Convert HSL to XRGB */ |
||||
xrgb_t hsl_xrgb(const hsl_t color); |
@ -0,0 +1,52 @@ |
||||
#include <avr/io.h> |
||||
#include <stdbool.h> |
||||
|
||||
#include "debounce.h" |
||||
#include "calc.h" |
||||
#include "iopins.h" |
||||
#include "debo_config.h" |
||||
|
||||
/** Debounce data array */ |
||||
uint8_t debo_next_slot = 0; |
||||
|
||||
uint8_t debo_register(PORT_P reg, uint8_t bit, bool invert) |
||||
{ |
||||
debo_slots[debo_next_slot] = (debo_slot_t)({ |
||||
.reg = reg, |
||||
.bit = bit | ((invert & 1) << 7) | (get_bit_p(reg, bit) << 6), // bit 7 = invert, bit 6 = state
|
||||
.count = 0, |
||||
}); |
||||
|
||||
return debo_next_slot++; |
||||
} |
||||
|
||||
|
||||
/** Check debounced pins, should be called periodically. */ |
||||
void debo_tick() |
||||
{ |
||||
for (uint8_t i = 0; i < debo_next_slot; i++) |
||||
{ |
||||
// current pin value (right 3 bits, xored with inverse bit)
|
||||
bool value = get_bit_p(debo_slots[i].reg, debo_slots[i].bit & 0x7); |
||||
|
||||
if (value != get_bit(debo_slots[i].bit, 6)) |
||||
{ |
||||
|
||||
// different pin state than last recorded state
|
||||
if (debo_slots[i].count < DEBO_TICKS) |
||||
{ |
||||
debo_slots[i].count++; |
||||
} |
||||
else |
||||
{ |
||||
// overflown -> latch value
|
||||
set_bit(debo_slots[i].bit, 6, value); // set state bit
|
||||
debo_slots[i].count = 0; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
debo_slots[i].count = 0; // reset the counter
|
||||
} |
||||
} |
||||
} |
@ -0,0 +1,66 @@ |
||||
#pragma once |
||||
|
||||
//
|
||||
// An implementation of button debouncer.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// You must provide a config file debo_config.h (next to your main.c)
|
||||
//
|
||||
// A pin is registered like this:
|
||||
//
|
||||
// #define BTN1 12 // pin D12
|
||||
// #define BTN2 13
|
||||
//
|
||||
// debo_add(BTN0); // The function returns number assigned to the pin (0, 1, ...)
|
||||
// debo_add_rev(BTN1); // active low
|
||||
// debo_register(&PINB, PB2, 0); // direct access - register, pin & invert
|
||||
//
|
||||
// Then periodically call the tick function (perhaps in a timer interrupt):
|
||||
//
|
||||
// debo_tick();
|
||||
//
|
||||
// To check if input is active, use
|
||||
//
|
||||
// debo_get_pin(0); // state of input #0 (registered first)
|
||||
// debo_get_pin(1); // state of input #1 (registered second)
|
||||
//
|
||||
|
||||
|
||||
#include <avr/io.h> |
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "calc.h" |
||||
#include "iopins.h" |
||||
|
||||
// Your config file
|
||||
#include "debo_config.h" |
||||
/*
|
||||
#define DEBO_CHANNELS 2 |
||||
#define DDEBO_TICKS 5 |
||||
*/ |
||||
|
||||
|
||||
/* Internal deboucer entry */ |
||||
typedef struct |
||||
{ |
||||
PORT_P reg; // pointer to IO register
|
||||
uint8_t bit; // bits 6 and 7 of this hold "state" & "invert" flag
|
||||
uint8_t count; // number of ticks this was in the new state
|
||||
} debo_slot_t; |
||||
|
||||
debo_slot_t debo_slots[DEBO_CHANNELS]; |
||||
|
||||
/** Add a pin for debouncing (must be used with constant args) */ |
||||
#define debo_add_rev(pin) debo_register(&_pin(pin), _pn(pin), 1) |
||||
#define debo_add(pin) debo_register(&_pin(pin), _pn(pin), 0) |
||||
|
||||
/** Add a pin for debouncing (low level function) */ |
||||
uint8_t debo_register(PORT_P pin_reg_pointer, uint8_t bit, bool invert); |
||||
|
||||
/** Check debounced pins, should be called periodically. */ |
||||
void debo_tick(); |
||||
|
||||
/** Get a value of debounced pin */ |
||||
#define debo_get_pin(i) (get_bit(debo_slots[i].bit, 6) ^ get_bit(debo_slots[i].bit, 7)) |
@ -0,0 +1,88 @@ |
||||
#include <avr/io.h> |
||||
#include <util/delay.h> |
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
|
||||
#include "iopins.h" |
||||
#include "dht11.h" |
||||
|
||||
/** Read one bit */ |
||||
bool _dht11_rxbit(const uint8_t pin) |
||||
{ |
||||
// Wait until start of pulse
|
||||
while (is_low_n(pin)); |
||||
|
||||
uint8_t cnt = 0; |
||||
while (is_high_n(pin)) |
||||
{ |
||||
cnt++; |
||||
_delay_us(5); |
||||
} |
||||
|
||||
return (cnt > 8); |
||||
} |
||||
|
||||
|
||||
/** Read one byte */ |
||||
uint8_t _dht11_rxbyte(const uint8_t pin) |
||||
{ |
||||
uint8_t byte = 0; |
||||
|
||||
for (uint8_t i = 0; i < 8; i++) |
||||
{ |
||||
if (_dht11_rxbit(pin)) |
||||
byte |= (1 << (7 - i)); |
||||
} |
||||
|
||||
return byte; |
||||
} |
||||
|
||||
|
||||
/** Read tehmperature and humidity from the DHT11, returns false on failure */ |
||||
bool dht11_read(const uint8_t pin, dht11_result_t* result) |
||||
{ |
||||
// bus down for > 18 ms
|
||||
as_output_n(pin); |
||||
pin_low_n(pin); |
||||
_delay_ms(20); |
||||
|
||||
// bus up for 20-40us
|
||||
pin_high_n(pin); |
||||
_delay_us(20); |
||||
|
||||
// release
|
||||
as_input_pu_n(pin); |
||||
|
||||
// DHT should send 80us LOW & 80us HIGH
|
||||
|
||||
_delay_us(40); |
||||
if (!is_low_n(pin)) |
||||
return false; // init error
|
||||
|
||||
_delay_us(80); |
||||
if (!is_high_n(pin)) |
||||
return false; // init error
|
||||
|
||||
// skip to start of first bit
|
||||
_delay_us(50); |
||||
|
||||
// Receive 5 data bytes (Rh int, Rh dec, Temp int, Temp dec, Checksum)
|
||||
// Decimal bytes are zero for DHT11 -> we can ignore them.
|
||||
uint8_t bytes[5]; |
||||
uint8_t sum = 0; |
||||
|
||||
for (uint8_t i = 0; i < 5; i++) |
||||
{ |
||||
uint8_t b = _dht11_rxbyte(pin); |
||||
bytes[i] = b; |
||||
if (i < 4) sum += b; |
||||
} |
||||
|
||||
// Verify checksum
|
||||
if (sum != bytes[4]) return false; |
||||
|
||||
result->rh = bytes[0]; |
||||
result->temp = bytes[2]; |
||||
|
||||
return true; |
||||
} |
@ -0,0 +1,17 @@ |
||||
#pragma once |
||||
|
||||
//
|
||||
// Reading temperature and relative humidity from DHT11
|
||||
//
|
||||
|
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
|
||||
typedef struct |
||||
{ |
||||
int8_t temp; |
||||
int8_t rh; |
||||
} dht11_result_t; |
||||
|
||||
/** Read tehmperature and humidity from the DHT11, returns false on failure */ |
||||
bool dht11_read(const uint8_t pin, dht11_result_t* result); |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,276 @@ |
||||
#pragma once |
||||
|
||||
//
|
||||
// Simple FAT16 library.
|
||||
//
|
||||
// To use it, implement BLOCKDEV functions
|
||||
// and attach them to it's instance.
|
||||
//
|
||||
|
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
|
||||
#include "blockdev.h" |
||||
|
||||
|
||||
// -------------------------------
|
||||
|
||||
/**
|
||||
* File types (values can be used for debug printing). |
||||
* Accessible using file->type |
||||
*/ |
||||
typedef enum |
||||
{ |
||||
FT_NONE = '-', |
||||
FT_DELETED = 'x', |
||||
FT_SUBDIR = 'D', |
||||
FT_PARENT = 'P', |
||||
FT_LABEL = 'L', |
||||
FT_LFN = '~', |
||||
FT_INVALID = '?', // not recognized weird file
|
||||
FT_SELF = '.', |
||||
FT_FILE = 'F' |
||||
} FAT16_FT; |
||||
|
||||
|
||||
/** "File address" for saving and restoring file */ |
||||
typedef struct |
||||
{ |
||||
uint16_t clu; |
||||
uint16_t num; |
||||
uint32_t cur_rel; |
||||
} FSAVEPOS; |
||||
|
||||
|
||||
// Include definitions of fully internal structs
|
||||
#include "fat16_internal.h" |
||||
|
||||
|
||||
/**
|
||||
* File handle struct. |
||||
* |
||||
* File handle contains cursor, file name, type, size etc. |
||||
* Everything (files, dirs) is accessed using this. |
||||
*/ |
||||
typedef struct __attribute__((packed)) |
||||
{ |
||||
/**
|
||||
* Raw file name. Starting 0x05 was converted to 0xE5. |
||||
* To get PRINTABLE file name, use fat16_dispname() |
||||
*/ |
||||
uint8_t name[11]; |
||||
|
||||
/**
|
||||
* File attributes - bit field composed of FA_* flags |
||||
* (internal) |
||||
*/ |
||||
uint8_t attribs; |
||||
|
||||
// 14 bytes skipped (10 reserved, date, time)
|
||||
|
||||
/** First cluster of the file. (internal) */ |
||||
uint16_t clu_start; |
||||
|
||||
/**
|
||||
* File size in bytes. |
||||
* This is the current allocated and readable file size. |
||||
*/ |
||||
uint32_t size; |
||||
|
||||
|
||||
// --- the following fields are added when reading ---
|
||||
|
||||
/** File type. */ |
||||
FAT16_FT type; |
||||
|
||||
|
||||
// --- INTERNAL FIELDS ---
|
||||
|
||||
// Cursor variables. (internal)
|
||||
uint32_t cur_abs; // absolute position in device
|
||||
uint32_t cur_rel; // relative position in file
|
||||
uint16_t cur_clu; // cluster where the cursor is
|
||||
uint16_t cur_ofs; // offset within the active cluster
|
||||
|
||||
// File position in the directory. (internal)
|
||||
uint16_t clu; // first cluster of directory
|
||||
uint16_t num; // file entry number
|
||||
|
||||
// Pointer to the FAT16 handle. (internal)
|
||||
const FAT16* fat; |
||||
} |
||||
FFILE; |
||||
|
||||
|
||||
/**
|
||||
* Store modified file metadata and flush it to disk. |
||||
*/ |
||||
void ff_flush_file(FFILE* file); |
||||
|
||||
|
||||
/**
|
||||
* Save a file "position" into a struct, for later restoration. |
||||
* Cursor is also saved. |
||||
*/ |
||||
FSAVEPOS ff_savepos(const FFILE* file); |
||||
|
||||
/**
|
||||
* Restore a file from a saved position. |
||||
*/ |
||||
void ff_reopen(FFILE* file, const FSAVEPOS* pos); |
||||
|
||||
|
||||
/**
|
||||
* Initialize the file system - store into "fat" |
||||
*/ |
||||
bool ff_init(const BLOCKDEV* dev, FAT16* fat); |
||||
|
||||
|
||||
/**
|
||||
* Open the first file of the root directory. |
||||
* The file may be invalid (eg. a volume label, deleted etc), |
||||
* or blank (type FT_NONE) if the filesystem is empty. |
||||
*/ |
||||
void ff_root(const FAT16* fat, FFILE* file); |
||||
|
||||
|
||||
/**
|
||||
* Resolve the disk label. |
||||
* That can be in the Boot Sector, or in the first root directory entry. |
||||
* |
||||
* @param fat the FAT handle |
||||
* @param label_out string to store the label in. Should have at least 12 bytes. |
||||
*/ |
||||
char* ff_disk_label(const FAT16* fat, char* label_out); |
||||
|
||||
|
||||
// ----------- FILE I/O -------------
|
||||
|
||||
|
||||
/**
|
||||
* Move file cursor to a position relative to file start |
||||
* Returns false on I/O error (bad file, out of range...) |
||||
*/ |
||||
bool ff_seek(FFILE* file, uint32_t addr); |
||||
|
||||
|
||||
/**
|
||||
* Read bytes from file into memory |
||||
* Returns number of bytes read, 0 on error. |
||||
*/ |
||||
uint16_t ff_read(FFILE* file, void* target, uint16_t len); |
||||
|
||||
|
||||
/**
|
||||
* Write into file at a "seek" position. |
||||
*/ |
||||
bool ff_write(FFILE* file, const void* source, uint32_t len); |
||||
|
||||
|
||||
/**
|
||||
* Store a 0-terminated string at cursor. |
||||
*/ |
||||
bool ff_write_str(FFILE* file, const char* source); |
||||
|
||||
|
||||
/**
|
||||
* Create a new file in given folder |
||||
* |
||||
* file ... open directory; new file is opened into this handle. |
||||
* name ... name of the new file, including extension |
||||
*/ |
||||
bool ff_newfile(FFILE* file, const char* name); |
||||
|
||||
|
||||
/**
|
||||
* Create a sub-directory of given name. |
||||
* Directory is allocated and populated with entries "." and ".." |
||||
*/ |
||||
bool ff_mkdir(FFILE* file, const char* name); |
||||
|
||||
|
||||
/**
|
||||
* Set new file size. |
||||
* Allocates / frees needed clusters, does NOT erase them. |
||||
* |
||||
* Useful mainly for shrinking. |
||||
*/ |
||||
void set_file_size(FFILE* file, uint32_t size); |
||||
|
||||
|
||||
/**
|
||||
* Delete a *FILE* and free it's clusters. |
||||
*/ |
||||
bool ff_rmfile(FFILE* file); |
||||
|
||||
|
||||
/**
|
||||
* Delete an empty *DIRECTORY* and free it's clusters. |
||||
*/ |
||||
bool ff_rmdir(FFILE* file); |
||||
|
||||
|
||||
/**
|
||||
* Delete a file or directory, even FT_LFN and FT_INVALID. |
||||
* Directories are deleted recursively (!) |
||||
*/ |
||||
bool ff_delete(FFILE* file); |
||||
|
||||
|
||||
|
||||
// --------- NAVIGATION ------------
|
||||
|
||||
|
||||
/** Go to previous file in the directory (false = no prev file) */ |
||||
bool ff_prev(FFILE* file); |
||||
|
||||
|
||||
/** Go to next file in directory (false = no next file) */ |
||||
bool ff_next(FFILE* file); |
||||
|
||||
|
||||
/**
|
||||
* Open a subdirectory denoted by the file. |
||||
* Provided handle changes to the first entry of the directory. |
||||
*/ |
||||
bool ff_opendir(FFILE* dir); |
||||
|
||||
|
||||
/**
|
||||
* Open a parent directory. Fails in root. |
||||
* Provided handle changes to the first entry of the parent directory. |
||||
*/ |
||||
bool ff_parent(FFILE* file); |
||||
|
||||
|
||||
/** Jump to first file in this directory */ |
||||
void ff_first(FFILE* file); |
||||
|
||||
|
||||
/**
|
||||
* Find a file with given "display name" in this directory, and open it. |
||||
* If file is found, "file" will contain it's handle. |
||||
* Otherwise, the handle is unchanged. |
||||
*/ |
||||
bool ff_find(FFILE* file, const char* name); |
||||
|
||||
|
||||
// -------- FILE INSPECTION -----------
|
||||
|
||||
/** Check if file is a valid entry, or long-name/label/deleted */ |
||||
bool ff_is_regular(const FFILE* file); |
||||
|
||||
|
||||
/**
|
||||
* Resolve a file name, trim spaces and add null terminator. |
||||
* Returns the passed char*, or NULL on error. |
||||
*/ |
||||
char* ff_dispname(const FFILE* file, char* disp_out); |
||||
|
||||
|
||||
/**
|
||||
* Convert filename to zero-padded fixed length one |
||||
* Returns the passed char*. |
||||
*/ |
||||
char* ff_rawname(const char* disp_in, char* raw_out); |
||||
|
@ -0,0 +1,64 @@ |
||||
#pragma once |
||||
|
||||
#include <stdint.h> |
||||
#include <stdbool.h> |
||||
|
||||
// Internal types and stuff that is needed in the header for declarations,
|
||||
// but is not a part of the public API.
|
||||
|
||||
/** Boot Sector structure */ |
||||
typedef struct __attribute__((packed)) |
||||
{ |
||||
// Fields loaded directly from disk:
|
||||
|
||||
// 13 bytes skipped
|
||||
uint8_t sectors_per_cluster; |
||||
uint16_t reserved_sectors; |
||||
uint8_t num_fats; |
||||
uint16_t root_entries; |
||||
// 3 bytes skipped
|
||||
uint16_t fat_size_sectors; |
||||
// 8 bytes skipped
|
||||
uint32_t total_sectors; // if "short size sectors" is used, it's copied here too
|
||||
// 7 bytes skipped
|
||||
char volume_label[11]; // space padded, no terminator
|
||||
|
||||
// Added fields:
|
||||
|
||||
uint32_t bytes_per_cluster; |
||||
|
||||
} |
||||
Fat16BootSector; |
||||
|
||||
|
||||
/** FAT filesystem handle */ |
||||
typedef struct __attribute__((packed)) |
||||
{ |
||||
// Backing block device
|
||||
const BLOCKDEV* dev; |
||||
|
||||
// Root directory sector start
|
||||
uint32_t rd_addr; |
||||
|
||||
// Start of first cluster (number "2")
|
||||
uint32_t data_addr; |
||||
|
||||
// Start of fat table
|
||||
uint32_t fat_addr; |
||||
|
||||
// Boot sector data struct
|
||||
Fat16BootSector bs; |
||||
} |
||||
FAT16; |
||||
|
||||
|
||||
/**
|
||||
* File Attributes (bit flags) |
||||
* Accessible using file->attribs |
||||
*/ |
||||
#define FA_READONLY 0x01 // read only file
|
||||
#define FA_HIDDEN 0x02 // hidden file
|
||||
#define FA_SYSTEM 0x04 // system file
|
||||
#define FA_LABEL 0x08 // volume label entry, found only in root directory.
|
||||
#define FA_DIR 0x10 // subdirectory
|
||||
#define FA_ARCHIVE 0x20 // archive flag
|
@ -0,0 +1,276 @@ |
||||
#include <avr/io.h> |
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "calc.h" |
||||
#include "iopins.h" |
||||
|
||||
|
||||
void set_dir_n(const uint8_t pin, const uint8_t d) |
||||
{ |
||||
switch(pin) { |
||||
case 0: set_dir(0, d); return; |
||||
case 1: set_dir(1, d); return; |
||||
case 2: set_dir(2, d); return; |
||||
case 3: set_dir(3, d); return; |
||||
case 4: set_dir(4, d); return; |
||||
case 5: set_dir(5, d); return; |
||||
case 6: set_dir(6, d); return; |
||||
case 7: set_dir(7, d); return; |
||||
case 8: set_dir(8, d); return; |
||||
case 9: set_dir(9, d); return; |
||||
case 10: set_dir(10, d); return; |
||||
case 11: set_dir(11, d); return; |
||||
case 12: set_dir(12, d); return; |
||||
case 13: set_dir(13, d); return; |
||||
case 14: set_dir(14, d); return; |
||||
case 15: set_dir(15, d); return; |
||||
case 16: set_dir(16, d); return; |
||||
case 17: set_dir(17, d); return; |
||||
case 18: set_dir(18, d); return; |
||||
case 19: set_dir(19, d); return; |
||||
case 20: set_dir(20, d); return; |
||||
case 21: set_dir(21, d); return; |
||||
} |
||||
} |
||||
|
||||
void as_input_n(const uint8_t pin) |
||||
{ |
||||
switch(pin) { |
||||
case 0: as_input(0); return; |
||||
case 1: as_input(1); return; |
||||
case 2: as_input(2); return; |
||||
case 3: as_input(3); return; |
||||
case 4: as_input(4); return; |
||||
case 5: as_input(5); return; |
||||
case 6: as_input(6); return; |
||||
case 7: as_input(7); return; |
||||
case 8: as_input(8); return; |
||||
case 9: as_input(9); return; |
||||
case 10: as_input(10); return; |
||||
case 11: as_input(11); return; |
||||
case 12: as_input(12); return; |
||||
case 13: as_input(13); return; |
||||
case 14: as_input(14); return; |
||||
case 15: as_input(15); return; |
||||
case 16: as_input(16); return; |
||||
case 17: as_input(17); return; |
||||
case 18: as_input(18); return; |
||||
case 19: as_input(19); return; |
||||
case 20: as_input(20); return; |
||||
case 21: as_input(21); return; |
||||
} |
||||
} |
||||
|
||||
|
||||
void as_input_pu_n(const uint8_t pin) |
||||
{ |
||||
switch(pin) { |
||||
case 0: as_input_pu(0); return; |
||||
case 1: as_input_pu(1); return; |
||||
case 2: as_input_pu(2); return; |
||||
case 3: as_input_pu(3); return; |
||||
case 4: as_input_pu(4); return; |
||||
case 5: as_input_pu(5); return; |
||||
case 6: as_input_pu(6); return; |
||||
case 7: as_input_pu(7); return; |
||||
case 8: as_input_pu(8); return; |
||||
case 9: as_input_pu(9); return; |
||||
case 10: as_input_pu(10); return; |
||||
case 11: as_input_pu(11); return; |
||||
case 12: as_input_pu(12); return; |
||||
case 13: as_input_pu(13); return; |
||||
case 14: as_input_pu(14); return; |
||||
case 15: as_input_pu(15); return; |
||||
case 16: as_input_pu(16); return; |
||||
case 17: as_input_pu(17); return; |
||||
case 18: as_input_pu(18); return; |
||||
case 19: as_input_pu(19); return; |
||||
case 20: as_input_pu(20); return; |
||||
case 21: as_input_pu(21); return; |
||||
} |
||||
} |
||||
|
||||
|
||||
void as_output_n(const uint8_t pin) |
||||
{ |
||||
switch(pin) { |
||||
case 0: as_output(0); return; |
||||
case 1: as_output(1); return; |
||||
case 2: as_output(2); return; |
||||
case 3: as_output(3); return; |
||||
case 4: as_output(4); return; |
||||
case 5: as_output(5); return; |
||||
case 6: as_output(6); return; |
||||
case 7: as_output(7); return; |
||||
case 8: as_output(8); return; |
||||
case 9: as_output(9); return; |
||||
case 10: as_output(10); return; |
||||
case 11: as_output(11); return; |
||||
case 12: as_output(12); return; |
||||
case 13: as_output(13); return; |
||||
case 14: as_output(14); return; |
||||
case 15: as_output(15); return; |
||||
case 16: as_output(16); return; |
||||
case 17: as_output(17); return; |
||||
case 18: as_output(18); return; |
||||
case 19: as_output(19); return; |
||||
case 20: as_output(20); return; |
||||
case 21: as_output(21); return; |
||||
} |
||||
} |
||||
|
||||
void set_pin_n(const uint8_t pin, const uint8_t v) |
||||
{ |
||||
switch(pin) { |
||||
case 0: set_pin(0, v); return; |
||||
case 1: set_pin(1, v); return; |
||||
case 2: set_pin(2, v); return; |
||||
case 3: set_pin(3, v); return; |
||||
case 4: set_pin(4, v); return; |
||||
case 5: set_pin(5, v); return; |
||||
case 6: set_pin(6, v); return; |
||||
case 7: set_pin(7, v); return; |
||||
case 8: set_pin(8, v); return; |
||||
case 9: set_pin(9, v); return; |
||||
case 10: set_pin(10, v); return; |
||||
case 11: set_pin(11, v); return; |
||||
case 12: set_pin(12, v); return; |
||||
case 13: set_pin(13, v); return; |
||||
case 14: set_pin(14, v); return; |
||||
case 15: set_pin(15, v); return; |
||||
case 16: set_pin(16, v); return; |
||||
case 17: set_pin(17, v); return; |
||||
case 18: set_pin(18, v); return; |
||||
case 19: set_pin(19, v); return; |
||||
case 20: set_pin(20, v); return; |
||||
case 21: set_pin(21, v); return; |
||||
} |
||||
} |
||||
|
||||
void pin_low_n(const uint8_t pin) |
||||
{ |
||||
switch(pin) { |
||||
case 0: pin_low(0); return; |
||||
case 1: pin_low(1); return; |
||||
case 2: pin_low(2); return; |
||||
case 3: pin_low(3); return; |
||||
case 4: pin_low(4); return; |
||||
case 5: pin_low(5); return; |
||||
case 6: pin_low(6); return; |
||||
case 7: pin_low(7); return; |
||||
case 8: pin_low(8); return; |
||||
case 9: pin_low(9); return; |
||||
case 10: pin_low(10); return; |
||||
case 11: pin_low(11); return; |
||||
case 12: pin_low(12); return; |
||||
case 13: pin_low(13); return; |
||||
case 14: pin_low(14); return; |
||||
case 15: pin_low(15); return; |
||||
case 16: pin_low(16); return; |
||||
case 17: pin_low(17); return; |
||||
case 18: pin_low(18); return; |
||||
case 19: pin_low(19); return; |
||||
case 20: pin_low(20); return; |
||||
case 21: pin_low(21); return; |
||||
} |
||||
} |
||||
|
||||
void pin_high_n(const uint8_t pin) |
||||
{ |
||||
switch(pin) { |
||||
case 0: pin_high(0); return; |
||||
case 1: pin_high(1); return; |
||||
case 2: pin_high(2); return; |
||||
case 3: pin_high(3); return; |
||||
case 4: pin_high(4); return; |
||||
case 5: pin_high(5); return; |
||||
case 6: pin_high(6); return; |
||||
case 7: pin_high(7); return; |
||||
case 8: pin_high(8); return; |
||||
case 9: pin_high(9); return; |
||||
case 10: pin_high(10); return; |
||||
case 11: pin_high(11); return; |
||||
case 12: pin_high(12); return; |
||||
case 13: pin_high(13); return; |
||||
case 14: pin_high(14); return; |
||||
case 15: pin_high(15); return; |
||||
case 16: pin_high(16); return; |
||||
case 17: pin_high(17); return; |
||||
case 18: pin_high(18); return; |
||||
case 19: pin_high(19); return; |
||||
case 20: pin_high(20); return; |
||||
case 21: pin_high(21); return; |
||||
} |
||||
} |
||||
|
||||
|
||||
void toggle_pin_n(const uint8_t pin) |
||||
{ |
||||
switch(pin) { |
||||
case 0: toggle_pin(0); return; |
||||
case 1: toggle_pin(1); return; |
||||
case 2: toggle_pin(2); return; |
||||
case 3: toggle_pin(3); return; |
||||
case 4: toggle_pin(4); return; |
||||
case 5: toggle_pin(5); return; |
||||
case 6: toggle_pin(6); return; |
||||
case 7: toggle_pin(7); return; |
||||
case 8: toggle_pin(8); return; |
||||
case 9: toggle_pin(9); return; |
||||
case 10: toggle_pin(10); return; |
||||
case 11: toggle_pin(11); return; |
||||
case 12: toggle_pin(12); return; |
||||
case 13: toggle_pin(13); return; |
||||
case 14: toggle_pin(14); return; |
||||
case 15: toggle_pin(15); return; |
||||
case 16: toggle_pin(16); return; |
||||
case 17: toggle_pin(17); return; |
||||
case 18: toggle_pin(18); return; |
||||
case 19: toggle_pin(19); return; |
||||
case 20: toggle_pin(20); return; |
||||
case 21: toggle_pin(21); return; |
||||
} |
||||
} |
||||
|
||||
|
||||
bool get_pin_n(const uint8_t pin) |
||||
{ |
||||
switch(pin) { |
||||
case 0: return get_pin(0); |
||||
case 1: return get_pin(1); |
||||
case 2: return get_pin(2); |
||||
case 3: return get_pin(3); |
||||
case 4: return get_pin(4); |
||||
case 5: return get_pin(5); |
||||
case 6: return get_pin(6); |
||||
case 7: return get_pin(7); |
||||
case 8: return get_pin(8); |
||||
case 9: return get_pin(9); |
||||
case 10: return get_pin(10); |
||||
case 11: return get_pin(11); |
||||
case 12: return get_pin(12); |
||||
case 13: return get_pin(13); |
||||
case 14: return get_pin(14); |
||||
case 15: return get_pin(15); |
||||
case 16: return get_pin(16); |
||||
case 17: return get_pin(17); |
||||
case 18: return get_pin(18); |
||||
case 19: return get_pin(19); |
||||
case 20: return get_pin(20); |
||||
case 21: return get_pin(21); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
bool is_low_n(const uint8_t pin) |
||||
{ |
||||
return !get_pin_n(pin); |
||||
} |
||||
|
||||
|
||||
bool is_high_n(const uint8_t pin) |
||||
{ |
||||
return get_pin_n(pin); |
||||
} |
@ -0,0 +1,213 @@ |
||||
#pragma once |
||||
|
||||
//
|
||||
// * Utilities for pin aliasing / numbering. *
|
||||
//
|
||||
// Designed for Arduino.
|
||||
//
|
||||
// If you know the pin number beforehand, you can use the macros.
|
||||
//
|
||||
// If you need to use a variable for pin number, use the `_n` functions.
|
||||
// They are much slower, so always check if you really need them
|
||||
// - and they aren't fit for things where precise timing is required.
|
||||
//
|
||||
|
||||
#include <avr/io.h> |
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
|
||||
#include "calc.h" |
||||
|
||||
|
||||
// type: pointer to port
|
||||
typedef volatile uint8_t* PORT_P; |
||||
|
||||
|
||||
/** Pin numbering reference */ |
||||
#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 8 |
||||
#define D9 9 |
||||
#define D10 10 |
||||
#define D11 11 |
||||
#define D12 12 |
||||
#define D13 13 |
||||
#define D14 14 |
||||
#define D15 15 |
||||
#define D16 16 |
||||
#define D17 17 |
||||
#define D18 18 |
||||
#define D19 19 |
||||
#define D20 20 |
||||
#define D21 21 |
||||
#define A0 14 |
||||
#define A1 15 |
||||
#define A2 16 |
||||
#define A3 17 |
||||
#define A4 18 |
||||
#define A5 19 |
||||
#define A6 20 |
||||
#define A7 21 |
||||
|
||||
|
||||
#define _ddr(pin) _DDR_##pin |
||||
#define _pin(pin) _PIN_##pin |
||||
#define _pn(pin) _PN_##pin |
||||
#define _port(pin) _PORT_##pin |
||||
|
||||
|
||||
/** Set pin direction */ |
||||
#define set_dir(pin, d) set_bit( _ddr(pin), _pn(pin), d ) |
||||
void set_dir_n(const uint8_t pin, const uint8_t d); |
||||
|
||||
|
||||
/** Configure pin as input */ |
||||
#define as_input(pin) cbi( _ddr(pin), _pn(pin) ) |
||||
void as_input_n(const uint8_t pin); |
||||
|
||||
|
||||
/** Configure pin as input, with pull-up enabled */ |
||||
#define as_input_pu(pin) { as_input(pin); pin_high(pin); } |
||||
void as_input_pu_n(const uint8_t pin); |
||||
|
||||
|
||||
/** Configure pin as output */ |
||||
#define as_output(pin) sbi( _ddr(pin), _pn(pin) ) |
||||
void as_output_n(const uint8_t pin); |
||||
|
||||
|
||||
/** Write value to a pin */ |
||||
#define set_pin(pin, v) set_bit( _port(pin), _pn(pin), v ) |
||||