master
Ondřej Hruška 7 years ago
parent 2997cf5b52
commit d3bcf4fecc
  1. 45
      .gitignore
  2. 139
      Makefile
  3. 52
      gamepad.pro
  4. 154
      lib/calc.h
  5. 59
      lib/debounce.c
  6. 64
      lib/debounce.h
  7. 277
      lib/iopins.c
  8. 213
      lib/iopins.h
  9. 21
      lib/nsdelay.h
  10. 62
      lib/spi.c
  11. 69
      lib/spi.h
  12. 83
      lib/usart.c
  13. 86
      lib/usart.h
  14. 103
      main.c
  15. 14
      style.astylerc

45
.gitignore vendored

@ -0,0 +1,45 @@
# Object files
*.o
*.ko
*.obj
*.elf
*.axf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
# AVR specific stuff
*.lst
*.dis
*.disasm
*.list
*.eeprom
*.pre
# QtCreator user-specific
*.pro.user

@ -0,0 +1,139 @@
#############################################
# CPU type
MCU = atmega328p
# CPU frequency [Hz]
F_CPU = 16000000
# Fuses (refer to datasheet)
LFUSE = 0xFF
HFUSE = 0xDE
EFUSE = 0x05
# AVRDUDE settings
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)
#############################################
# Main file
BINARY = main
# Obj files to be built <- add .o for any .c files you add!
OBJS = $(BINARY).o
OBJS += lib/usart.o
OBJS += lib/iopins.o
OBJS += lib/spi.o
OBJS += lib/debounce.o
# Dirs with header files
INCL_DIRS = . lib/
# Pre-defined macros
DEFS = -DF_CPU=$(F_CPU)UL
#############################################
# C flags
CFLAGS = -std=gnu99 -mmcu=$(MCU) $(DEFS) $(INCL_DIRS:%=-I%)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -ffreestanding
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
LDFLAGS = -Wl,--gc-sections -Wl,--relax -lm
#LD_FLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf
#LD_FLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf
#############################################
## Defined programs / locations
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
AVRSIZE = avr-size
AVRDUDE = avrdude
JUNK = *.o *.d *.elf *.bin *.hex *.srec *.list *.lst *.map *.dis *.disasm *.eep *.eeprom *.lss
.SUFFIXES: .elf .bin .hex .lst .map .eeprom .pre
.SECONDEXPANSION:
.SECONDARY:
.PHONY: all elf bin hex lst pre ee eeprom dis size clean flash flashe shell fuses show_fuses set_default_fuses
all: hex size
elf: $(BINARY).elf
bin: $(BINARY).bin
hex: $(BINARY).hex
lst: $(BINARY).lst
pre: $(BINARY).pre
ee: $(BINARY).eeprom
eeprom: $(BINARY).eeprom
dis: lst
# Show how big the resulting program is
size: elf
$(AVRSIZE) -C --mcu=$(MCU) $(BINARY).elf
# --- Magic build targets ----------------
%.hex: %.elf
$(OBJCOPY) -R .eeprom -O ihex $< $@
%.elf %.map: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(*).elf
%.pre: %.c
$(CC) $(CFLAGS) -E $(*).c --output $@
%.eeprom: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
%.lst: %.elf
$(Q)$(OBJDUMP) -S $(*).elf > $(*).lst
%.o: %.c
$(CC) $(CFLAGS) -o $(*).o -c $(*).c
%.o: %.s
$(CC) $(CFLAGS) -o $(*).o -c $(*).s
# Clean all produced trash
clean:
rm -f $(JUNK)
cd lib && rm -f $(JUNK)
## === avrdude ===
flash: $(BINARY).hex
$(AVRDUDE) $(PROG_ARGS) -U flash:w:$<
flashe: $(BINARY).eeprom
$(AVRDUDE) $(PROG_ARGS) -U eeprom:w:$<
shell:
$(AVRDUDE) $(PROG_ARGS) -nt
# === fuses ===
# this may not work with the arduino programmer, I haven't tried.
FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
fuses:
$(AVRDUDE) $(PROG_ARGS) $(FUSE_STRING)
show_fuses:
$(AVRDUDE) $(PROG_ARGS) -nv

@ -0,0 +1,52 @@
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
INCLUDEPATH += \
lib \
/usr/avr/include
DEFINES += __AVR_ATmega328P__ F_CPU=16000000UL
DISTFILES += \
style.astylerc \
Makefile \
README.md \
LICENSE
HEADERS += \
lib/calc.h \
lib/iopins.h \
lib/usart.h \
lib/nsdelay.h \
lib/spi.h \
lib/debounce.h
SOURCES += \
lib/iopins.c \
main.c \
lib/usart.c \
lib/spi.c \
lib/debounce.c
# === Flags for the Clang code model===
#
#-Weverything
#-Wno-c++98-compat
#-Wno-c++98-compat-pedantic
#-Wno-unused-macros
#-Wno-newline-eof
#-Wno-exit-time-destructors
#-Wno-global-constructors
#-Wno-gnu-zero-variadic-macro-arguments
#-Wno-documentation
#-Wno-missing-prototypes
#-std=gnu99
#-Wno-gnu
#-Wno-format-nonliteral
#-Wno-conversion
#-Wno-pointer-sign
#-Wno-unknown-attributes
#-Wno-main-return-type
#-Wno-missing-noreturn

@ -0,0 +1,154 @@
#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) \
do { \
if ((var) >= ((max) - 1)) { \
(var) = (min); \
} else { \
(var)++; \
} \
} while(0)
// ..., 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) \
do { \
if ((var) <= (min)) { \
(var) = (max) - 1; \
} else { \
(var)--; \
} \
} while(0)
// ..., upper bound included
#define dec_wrapi(var, min, max) dec_wrap((var), (min), (max) + 1)
// --- Bit manipulation --
// Set bit
#define sbi(reg, n) do { (reg) |= (1 << (uint8_t)(n)); } while(0)
// Clear bit
#define cbi(reg, n) do { (reg) &= ~(1 << (uint8_t)(n)); } while(0)
// Get n-th bit
#define get_bit(reg, n) (((reg) >> (uint8_t)(n)) & 1)
// Test n-th bit (Can't use bit_is_set, as it's defined in sfr_def.h)
#define bit_is_high(reg, n) get_bit((reg), (n))
#define bit_is_low(reg, n) (!get_bit((reg), (n)))
// Write value to n-th bit
#define set_bit(reg, n, val) \
do { \
(reg) = ((reg) & ~(1 << (uint8_t)(n))) \
| (((val) & 1) << (uint8_t)(n)); \
} while(0)
// Invert n-th bit
#define toggle_bit(reg, n) do { (reg) ^= (1 << (uint8_t)(n)); } while(0)
// --- Bit manipulation with pointer to variable ---
// Set n-th bit in pointee
#define sbi_p(reg_p, n) do { (*(reg_p)) |= (1 << (uint8_t)(n)); } while(0)
// Clear n-th bit in pointee
#define cbi_p(reg_p, n) do { (*(reg_p)) &= ~(1 << (uint8_t)(n)); } while(0)
// Get n-th bit in pointee
#define get_bit_p(reg_p, n) ((*(reg_p) >> (uint8_t)(n)) & 1)
// 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, n, value) \
do { \
*(reg_p) = (*(reg_p) & ~(1 << (uint8_t)(n))) \
| (((value) & 1) << (uint8_t)(n)); \
} while(0)
#define toggle_bit_p(reg_p, n) do { *(reg_p) ^= (1 << (uint8_t)(n)); } while(0)
// --- Nibble manipulation ---
// Replace nibble in a byte
#define set_low_nibble(reg, value) \
do { \
(reg) = ((reg) & 0xF0) \
| ((uint8_t)(value) & 0xF); \
} while(0)
#define set_high_nibble(reg, value) \
do { \
(reg) = ((reg) & 0x0F) \
| (((uint8_t)(value) & 0xF) << 4); \
} while(0)
#define set_low_nibble_p(reg_p, value) \
do { \
*(reg_p) = (*(reg_p) & 0xF0) \
| ((uint8_t)(value) & 0xF); \
} while(0)
#define set_high_nibble_p(reg_p, value) \
do { \
*(reg_p) = (*(reg_p) & 0x0F) \
| (((uint8_t)(value) & 0xF) << 4); \
} while(0)
#define low_nibble(x) ((uint8_t)(x) & 0x0F)
#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)) \
: ((x) >= (high) && (x) < (low))) \
// ..., include greater bound
#define in_rangei(x, low, high) \
((low) < (high) ? ((x) >= (low) && (x) <= (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)) \
: ((x) >= (low) || (x) < (high)))
// ..., include upper bound
#define in_range_wrapi(x, low, high) \
(((low) <= (high)) ? ((x) >= (low) && (x) <= (high)) \
: ((x) >= (low) || (x) <= (high)))

@ -0,0 +1,59 @@
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdbool.h>
#include <stdlib.h>
#include "debounce.h"
#include "calc.h"
#include "iopins.h"
#include "lib/usart.h"
DeboSlot debo_slots[DEBO_CHANNELS];
/** Debounce data array */
static uint8_t debo_next_slot = 0;
uint8_t debo_add_do(PORT_P pin_ptr, uint8_t bit, bool invert, void (*handler)(uint8_t, bool))
{
DeboSlot *slot = &debo_slots[debo_next_slot];
slot->reg = pin_ptr;
slot->mask = (uint8_t)(1 << bit);
slot->invert = invert;
slot->count = 0;
slot->state = (*slot->reg & slot->mask);
slot->handler = handler;
return debo_next_slot++;
}
/** Check debounced pins, should be called periodically. */
void debo_tick(void)
{
for (uint8_t i = 0; i < debo_next_slot; i++) {
DeboSlot *slot = &debo_slots[i];
// current pin value (right 3 bits, xored with inverse bit)
bool state = (*slot->reg & slot->mask);
if (state != slot->state) {
// different pin state than last recorded state
if (slot->count < DEBO_TICKS) {
slot->count++;
} else {
// overflown -> latch value
slot->state = state; // set state bit
slot->count = 0;
if (slot->handler != NULL) {
slot->handler(i, slot->invert ^ state);
}
}
} else {
slot->count = 0;; // reset the counter
}
}
}

@ -0,0 +1,64 @@
#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"
#define DEBO_CHANNELS 11
#define DEBO_TICKS 20
/* Internal deboucer entry */
typedef struct
{
PORT_P reg; // pin ptr
uint8_t mask;
uint8_t count; // number of ticks this was in the new state
bool invert;
bool state;
void (*handler)(uint8_t pin_n, bool state);
} DeboSlot;
extern DeboSlot debo_slots[DEBO_CHANNELS];
/** Add a pin for debouncing (must be used with constant args) */
#define debo_add(pin, reverse, hdlr) debo_add_do(&_pin(pin), _pn(pin), reverse, hdlr)
/** Add a pin for debouncing (low level function) */
uint8_t debo_add_do(PORT_P pin_reg_pointer, uint8_t bit, bool invert, void (*handler)(uint8_t, bool));
/** Check debounced pins, should be called periodically. */
void debo_tick(void);
/** Get a value of debounced pin */
#define debo_get_pin(i) (debo_slots[i].state ^ debo_slots[i].invert)

@ -0,0 +1,277 @@
#include <avr/io.h>
#include <stdbool.h>
#include <stdint.h>
#include "calc.h"
#include "iopins.h"
void set_dir_n(uint8_t pin, 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(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(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(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 pin_set_n(uint8_t pin, uint8_t v)
{
switch(pin) {
case 0: pin_set(0, v); return;
case 1: pin_set(1, v); return;
case 2: pin_set(2, v); return;
case 3: pin_set(3, v); return;
case 4: pin_set(4, v); return;
case 5: pin_set(5, v); return;
case 6: pin_set(6, v); return;
case 7: pin_set(7, v); return;
case 8: pin_set(8, v); return;
case 9: pin_set(9, v); return;
case 10: pin_set(10, v); return;
case 11: pin_set(11, v); return;
case 12: pin_set(12, v); return;
case 13: pin_set(13, v); return;
case 14: pin_set(14, v); return;
case 15: pin_set(15, v); return;
case 16: pin_set(16, v); return;
case 17: pin_set(17, v); return;
case 18: pin_set(18, v); return;
case 19: pin_set(19, v); return;
case 20: pin_set(20, v); return;
case 21: pin_set(21, v); return;
}
}
void pin_down_n(uint8_t pin)
{
switch(pin) {
case 0: pin_down(0); return;
case 1: pin_down(1); return;
case 2: pin_down(2); return;
case 3: pin_down(3); return;
case 4: pin_down(4); return;
case 5: pin_down(5); return;
case 6: pin_down(6); return;
case 7: pin_down(7); return;
case 8: pin_down(8); return;
case 9: pin_down(9); return;
case 10: pin_down(10); return;
case 11: pin_down(11); return;
case 12: pin_down(12); return;
case 13: pin_down(13); return;
case 14: pin_down(14); return;
case 15: pin_down(15); return;
case 16: pin_down(16); return;
case 17: pin_down(17); return;
case 18: pin_down(18); return;
case 19: pin_down(19); return;
case 20: pin_down(20); return;
case 21: pin_down(21); return;
}
}
void pin_up_n(uint8_t pin)
{
switch(pin) {
case 0: pin_up(0); return;
case 1: pin_up(1); return;
case 2: pin_up(2); return;
case 3: pin_up(3); return;
case 4: pin_up(4); return;
case 5: pin_up(5); return;
case 6: pin_up(6); return;
case 7: pin_up(7); return;
case 8: pin_up(8); return;
case 9: pin_up(9); return;
case 10: pin_up(10); return;
case 11: pin_up(11); return;
case 12: pin_up(12); return;
case 13: pin_up(13); return;
case 14: pin_up(14); return;
case 15: pin_up(15); return;
case 16: pin_up(16); return;
case 17: pin_up(17); return;
case 18: pin_up(18); return;
case 19: pin_up(19); return;
case 20: pin_up(20); return;
case 21: pin_up(21); return;
}
}
void pin_toggle_n(uint8_t pin)
{
switch(pin) {
case 0: pin_toggle(0); return;
case 1: pin_toggle(1); return;
case 2: pin_toggle(2); return;
case 3: pin_toggle(3); return;
case 4: pin_toggle(4); return;
case 5: pin_toggle(5); return;
case 6: pin_toggle(6); return;
case 7: pin_toggle(7); return;
case 8: pin_toggle(8); return;
case 9: pin_toggle(9); return;
case 10: pin_toggle(10); return;
case 11: pin_toggle(11); return;
case 12: pin_toggle(12); return;
case 13: pin_toggle(13); return;
case 14: pin_toggle(14); return;
case 15: pin_toggle(15); return;
case 16: pin_toggle(16); return;
case 17: pin_toggle(17); return;
case 18: pin_toggle(18); return;
case 19: pin_toggle(19); return;
case 20: pin_toggle(20); return;
case 21: pin_toggle(21); return;
}
}
bool pin_read_n(uint8_t pin)
{
switch(pin) {
case 0: return pin_read(0);
case 1: return pin_read(1);
case 2: return pin_read(2);
case 3: return pin_read(3);
case 4: return pin_read(4);
case 5: return pin_read(5);
case 6: return pin_read(6);
case 7: return pin_read(7);
case 8: return pin_read(8);
case 9: return pin_read(9);
case 10: return pin_read(10);
case 11: return pin_read(11);
case 12: return pin_read(12);
case 13: return pin_read(13);
case 14: return pin_read(14);
case 15: return pin_read(15);
case 16: return pin_read(16);
case 17: return pin_read(17);
case 18: return pin_read(18);
case 19: return pin_read(19);
case 20: return pin_read(20);
case 21: return pin_read(21);
}
return false;
}
bool pin_is_low_n(uint8_t pin)
{
return !pin_read_n(pin);
}
bool pin_is_high_n(uint8_t pin)
{
return pin_read_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_up(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 pin_set(pin, v) set_bit( _port(pin), _pn(pin), v )
void pin_set_n(const uint8_t pin, const uint8_t v);
/** Write 0 to a pin */
#define pin_down(pin) cbi( _port(pin), _pn(pin) )
void pin_down_n(const uint8_t pin);
/** Write 1 to a pin */
#define pin_up(pin) sbi( _port(pin), _pn(pin) )
void pin_up_n(uint8_t pin);
/** Toggle a pin state */
#define pin_toggle(pin) sbi( _pin(pin), _pn(pin) )
void pin_toggle_n(uint8_t pin);
/** Read a pin value */
#define pin_read(pin) get_bit( _pin(pin), _pn(pin) )
bool pin_read_n(uint8_t pin);
/** CHeck if pin is low */
#define pin_is_low(pin) (pin_read(pin) == 0)
bool pin_is_low_n(uint8_t pin);
/** CHeck if pin is high */
#define pin_is_high(pin) (pin_read(pin) != 0)
bool pin_is_high_n(uint8_t pin);
// Helper macros
#define _PORT_0 PORTD
#define _PORT_1 PORTD
#define _PORT_2 PORTD
#define _PORT_3 PORTD
#define _PORT_4 PORTD
#define _PORT_5 PORTD
#define _PORT_6 PORTD
#define _PORT_7 PORTD
#define _PORT_8 PORTB
#define _PORT_9 PORTB
#define _PORT_10 PORTB
#define _PORT_11 PORTB
#define _PORT_12 PORTB
#define _PORT_13 PORTB
#define _PORT_14 PORTC
#define _PORT_15 PORTC
#define _PORT_16 PORTC
#define _PORT_17 PORTC
#define _PORT_18 PORTC
#define _PORT_19 PORTC
#define _PORT_20 PORTC
#define _PORT_21 PORTC
#define _PIN_0 PIND
#define _PIN_1 PIND
#define _PIN_2 PIND
#define _PIN_3 PIND
#define _PIN_4 PIND
#define _PIN_5 PIND
#define _PIN_6 PIND
#define _PIN_7 PIND
#define _PIN_8 PINB
#define _PIN_9 PINB
#define _PIN_10 PINB
#define _PIN_11 PINB
#define _PIN_12 PINB
#define _PIN_13 PINB
#define _PIN_14 PINC
#define _PIN_15 PINC
#define _PIN_16 PINC
#define _PIN_17 PINC
#define _PIN_18 PINC
#define _PIN_19 PINC
#define _PIN_20 PINC
#define _PIN_21 PINC
#define _DDR_0 DDRD
#define _DDR_1 DDRD
#define _DDR_2 DDRD
#define _DDR_3 DDRD
#define _DDR_4 DDRD
#define _DDR_5 DDRD
#define _DDR_6 DDRD
#define _DDR_7 DDRD
#define _DDR_8 DDRB
#define _DDR_9 DDRB
#define _DDR_10 DDRB
#define _DDR_11 DDRB
#define _DDR_12 DDRB
#define _DDR_13 DDRB
#define _DDR_14 DDRC
#define _DDR_15 DDRC
#define _DDR_16 DDRC
#define _DDR_17 DDRC
#define _DDR_18 DDRC
#define _DDR_19 DDRC
#define _DDR_20 DDRC
#define _DDR_21 DDRC
#define _PN_0 0
#define _PN_1 1
#define _PN_2 2
#define _PN_3 3
#define _PN_4 4
#define _PN_5 5
#define _PN_6 6
#define _PN_7 7
#define _PN_8 0
#define _PN_9 1
#define _PN_10 2
#define _PN_11 3
#define _PN_12 4
#define _PN_13 5
#define _PN_14 0
#define _PN_15 1
#define _PN_16 2
#define _PN_17 3
#define _PN_18 4
#define _PN_19 5
#define _PN_20 6
#define _PN_21 7

@ -0,0 +1,21 @@
#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))
/** Wait n nanoseconds */
#define delay_ns(ns) delay_c(ns2cycles(ns))

@ -0,0 +1,62 @@
#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>
#include "iopins.h"
#include "spi.h"
/** Init SPI (for SD card communication) */
void spi_init_master(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha, enum SPI_clk_div clkdiv)
{
as_output(PIN_SS); // SS output - we control slave
as_output(PIN_MOSI); // MOSI output - we talk to slave
as_output(PIN_SCK); // SCK output - we're generating clock
// MISO is configured automatically as input
SPCR = 0;
SPCR |= (1 << MSTR);
SPCR |= (order << DORD);
SPCR |= (cpol << CPOL);
SPCR |= (cpha << CPHA);
// speed
SPCR |= (clkdiv & 0b11);
SPSR = (bool)(clkdiv & 0b100); // write SPI2X flag
// enable SPI
SPCR |= (1 << SPE);
}
/** Init SPI (for SD card communication) */
void spi_init_slave(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha)
{
as_output(PIN_MISO); // we're listening to master
// MOSI, SS, SCK are configured automatically
SPCR = 0;
SPCR |= (order << DORD);
SPCR |= (cpol << CPOL);
SPCR |= (cpha << CPHA);
// enable SPI
SPCR |= (1 << SPE);
}
/** Write a byte to SPI. Returns received byte. */
uint8_t spi_send(uint8_t byte)
{
SPDR = byte;
while (bit_is_low(SPSR, SPIF));
return SPDR;
}
/** Receive (as slave). Blocking. */
uint8_t spi_receive(uint8_t reply)
{
SPDR = reply;
while (bit_is_low(SPSR, SPIF));
return SPDR;
}

@ -0,0 +1,69 @@
#pragma once
#include <avr/io.h>
#include <stdint.h>
#include "calc.h"
#include "iopins.h"
#define PIN_MISO 12
#define PIN_MOSI 11
#define PIN_SCK 13
#define PIN_SS 10
/** Bit order */
enum SPI_order {
SPI_LSB_FIRST = 0,
SPI_MSB_FIRST = 1
};
/** Clock polarity */
enum SPI_cpol {
CPOL_0 = 0,
CPOL_1 = 1
};
/** Clock phase */
enum SPI_cpha {
CPHA_0 = 0,
CPHA_1 = 1
};
/** Clock prescaller <SPI2X><SPR1><SPR0> */
enum SPI_clk_div {
SPI_DIV_2 = 0b100, // 2x (master only, can't receive at this speed)
SPI_DIV_4 = 0b000,
SPI_DIV_8 = 0b101, // 2x
SPI_DIV_16 = 0b001,
SPI_DIV_32 = 0b110, // 2x
SPI_DIV_64 = 0b010,
SPI_DIV_128 = 0b011
};
/** Set SS to active state (LOW) */
#define spi_ss_enable() pin_down(PIN_SS)
/** Set SS to disabled state (HIGH) */
#define spi_ss_disable() pin_up(PIN_SS)
/** Enable SPI ISR */
#define spi_isr_enable(yes) set_bit(SPCR, SPIE, yes)
/** Init SPI (for SD card communication) */
void spi_init_master(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha, enum SPI_clk_div clkdiv);
/** Init SPI (for SD card communication) */
void spi_init_slave(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha);
/** Write a byte to SPI. Returns received byte. */
uint8_t spi_send(uint8_t byte);
/** Receive (as slave). Blocking. */
uint8_t spi_receive(uint8_t reply);

@ -0,0 +1,83 @@
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "calc.h"
#include "usart.h"
void usart_init(uint16_t ubrr)
{
/*Set baud rate */
UBRR0H = (uint8_t)(ubrr >> 8);
UBRR0L = (uint8_t) ubrr;
// Enable Rx and Tx
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
// Clear U2X0
cbi(UCSR0A, U2X0);
// 8-bit data, 1 stop bit
UCSR0C = (0b11 << UCSZ00);
}
/** Set Double Speed Asynchronous mode */
void usart_set_2x(bool set)
{
set_bit(UCSR0A, U2X0, set);
}
/** Send byte over USART */
void usart_tx(uint8_t data)
{
// Wait for transmit buffer
while (!usart_tx_ready());
// send it
UDR0 = data;
}
/** Receive one byte over USART */
uint8_t usart_rx(void)
{
// Wait for data to be received
while (!usart_rx_ready());
// Get and return received data from buffer
return UDR0;
}
/** Send string over USART */
void usart_puts(const char* str)
{
while (*str) {
usart_tx(*str++);
}
}
/** Send progmem string over USART */
void usart_puts_P(const char* str)
{
char c;
while ((c = pgm_read_byte(str++))) {
usart_tx(c);
}
}
/** Clear receive buffer */
void usart_flush_rx(void)
{
uint8_t dummy;
while (bit_is_high(UCSR0A, RXC0)) {
dummy = UDR0;
}
}

@ -0,0 +1,86 @@
#pragma once
//
// Utilities for UART communication.
//
// First, init uart with usart_init().
// Then enable interrupts you want with usart_XXX_isr_enable().
//
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdint.h>
#include "calc.h"
/* USART BAUD RATE REGISTER values at 16 MHz */
enum {
BAUD_9600 = 103,
BAUD_14400 = 68,
BAUD_19200 = 51,
BAUD_28800 = 34,
BAUD_38400 = 25,
BAUD_57600 = 16,
BAUD_76800 = 12,
BAUD_115200 = 8,
BAUD_250k = 3,
BAUD_500k = 1,
BAUD_1M = 0,
};
/** Init UART with a UBRR value - can use the BAUD_* constants for 16 MHz */
void usart_init(uint16_t ubrr);
/** Set Double Speed Asynchronous mode on or off */
void usart_set_2x(bool set);
/** Check if there's a byte in the RX register */
#define usart_rx_ready() bit_is_high(UCSR0A, RXC0)
/** Check if USART is ready to accept new byte to send */
#define usart_tx_ready() bit_is_high(UCSR0A, UDRE0)
// ---- Enable UART interrupts ------------
/** Enable or disable RX ISR */
#define usart_isr_rx_enable(yes) set_bit(UCSR0B, RXCIE0, (yes))
/** Enable or disable TX ISR (all data sent) */
#define usart_isr_tx_enable(yes) set_bit(UCSR0B, TXCIE0, (yes))
/** Enable or disable DRE ISR (data register empty) */
#define usart_isr_dre_enable(yes) set_bit(UCSR0B, UDRIE0, (yes))
// ---- Basic IO --------------------------
/** Send byte over USART */
void usart_tx(uint8_t data);
/** Receive one byte over USART */
uint8_t usart_rx(void);
/** Clear receive buffer */
void usart_flush_rx(void);
// ---- Strings ---------------------------
/** Send string over UART */
void usart_puts(const char* str);
/** Send progmem string `PSTR("foobar")` over UART */
void usart_puts_P(const char* str);

103
main.c

@ -0,0 +1,103 @@
#include <avr/io.h> // register definitions
#include <avr/pgmspace.h> // storing data in program memory
#include <avr/interrupt.h> // interrupt vectors
#include <util/delay.h> // delay functions
#include <stdint.h> // C header for int types like uint8_t
#include <stdbool.h> // C header for the bool type
// Include stuff from the library
#include "lib/iopins.h"
#include "lib/usart.h"
#include "lib/debounce.h"
static const char names[] = {
'L', // 2
'U', // 3
'R', // 4
'D', // 5
'I', // 6
'J', // 7
'K', // 8
'X', // 9
'B', // 10
'A', // 11
'Y', // 12
};
uint8_t down = 0;
static void pin_change_handler(uint8_t n, bool state)
{
char c = names[n];
if (!state) {
c += 32; // lowecase
}
if (state) {
down++;
} else {
down--;
}
pin_set(9, (bool)(down > 0));
usart_tx((uint8_t) c);
}
static void hw_init(void)
{
usart_init(BAUD_115200);
as_input_pu(2);
as_input_pu(3);
as_input_pu(4);
as_input_pu(5);
as_input_pu(6);
as_input_pu(7);
as_input_pu(8);
// 9 - LED
as_output(9); // front LED
as_output(13); // LED on pro mini
as_input_pu(10);
as_input_pu(11);
as_input_pu(12);
as_input_pu(14);
// cant use loop because macros
debo_add(2, true, pin_change_handler);
debo_add(3, true, pin_change_handler);
debo_add(4, true, pin_change_handler);
debo_add(5, true, pin_change_handler);
debo_add(6, true, pin_change_handler);
debo_add(7, true, pin_change_handler);
debo_add(8, true, pin_change_handler);
debo_add(14, true, pin_change_handler);
debo_add(10, true, pin_change_handler);
debo_add(11, true, pin_change_handler);
debo_add(12, true, pin_change_handler);
}
void main(void)
{
hw_init();
// globally enable interrupts (for the USART_RX handler)
//sei();
//pin_up(9);
int cnt = 0;
while (1) {
_delay_ms(1);
debo_tick();
if (cnt++ > 500) {
cnt = 0;
}
}
}

@ -0,0 +1,14 @@
style=kr
indent=tab
max-instatement-indent=60
convert-tabs
indent-switches
keep-one-line-statements
pad-oper
unpad-paren
pad-header
verbose
Loading…
Cancel
Save