master
Ondřej Hruška 9 years ago
commit ac0bc06e29
  1. 45
      .gitignore
  2. 21
      LICENSE
  3. 139
      Makefile
  4. 11
      README.md
  5. 154
      lib/calc.h
  6. 59
      lib/debounce.c
  7. 64
      lib/debounce.h
  8. 277
      lib/iopins.c
  9. 213
      lib/iopins.h
  10. 21
      lib/nsdelay.h
  11. 62
      lib/spi.c
  12. 69
      lib/spi.h
  13. 83
      lib/usart.c
  14. 86
      lib/usart.h
  15. 241
      main.c
  16. 14
      style.astylerc
  17. 52
      us_rgb.pro

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,21 @@
The MIT License (MIT)
Copyright (c) 2016 Ondřej Hruška
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -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,11 @@
# arduino-gamepad
super simple gamepad project where Pro Mini is in a gamepad and sends keys over USART
LED connected to D9 (on-board one at D13).
Buttons connected from pins to ground with internal pull-up.
Keys are sent as A - pressed, a - released (different letters), over UART at 115200 baud.
Key press is indicated by LED at D9.

@ -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);

241
main.c

@ -0,0 +1,241 @@
#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>
#include <stdbool.h>
#include <stdlib.h>
// Include stuff from the library
#include "lib/iopins.h"
#include "lib/usart.h"
#include "lib/nsdelay.h"
static void sonar_start(void);
#define WS_PIN 5
#define TRIG_PIN 2
#define ECHO1_PIN 3
#define ECHO2_PIN 4
/** Wait long enough for the colors to show */
static void ws_show(void)
{
_delay_us(10);
}
static inline __attribute__((always_inline))
void delay_cyc(uint8_t __count)
{
__asm__ volatile (
"1: dec %0" "\n\t"
"brne 1b"
: "=r" (__count)
: "0" (__count)
);
}
/** Send one byte to the RGB strip */
static inline __attribute__((always_inline))
void ws_send_byte(uint8_t bb)
{
for (int8_t i = 8; i > 0; i--) {
pin_up(WS_PIN);
if (bb & 0x80) {
delay_cyc(4);
pin_down(WS_PIN);
delay_cyc(1);
} else {
delay_cyc(1);
pin_down(WS_PIN);
delay_cyc(4);
}
bb = (uint8_t)(bb << 1);
}
}
static void ws_send_rgb(uint8_t r, uint8_t g, uint8_t b)
{
ws_send_byte(g);
ws_send_byte(r);
ws_send_byte(b);
}
static void hw_init(void)
{
usart_init(BAUD_115200);
as_output(2);
as_input_pu(3);
as_input_pu(4);
as_output(5); // WS
}
typedef enum {
MEAS_WAIT_1,
MEAS_WAIT_0,
MEAS_DONE
} MeasPhase;
#define PULSE_LEN 9
const uint8_t pulse[PULSE_LEN] = {79, 150, 206, 243, 255, 243, 206, 150, 79};
#define MBUF_LEN 64
typedef struct {
float data[MBUF_LEN];
} MBuf;
float mbuf_add(MBuf *buf, float value)
{
float aggr = value;
for (int i = 1; i < MBUF_LEN; i++) {
aggr += buf->data[i];
buf->data[i] = buf->data[i-1];
}
buf->data[0] = value;
return aggr / (float)MBUF_LEN;
}
MBuf mb_offs1;
MBuf mb_offs2;
static void sonar_measure(void)
{
usart_puts("MEASURE!\n");
pin_up(TRIG_PIN);
_delay_ms(10);
pin_down(TRIG_PIN);
MeasPhase e1_ph = MEAS_WAIT_1;
MeasPhase e2_ph = MEAS_WAIT_1;
uint32_t echo1 = 0;
uint32_t echo2 = 0;
TCNT1 = 0;
TCCR1B = (0b010 << CS10);
while (true) {
switch (e1_ph) {
case MEAS_WAIT_1:
if (pin_is_high(ECHO1_PIN)) {
echo1 = TCNT1;
e1_ph = MEAS_WAIT_0;
}
break;
case MEAS_WAIT_0:
if (pin_is_low(ECHO1_PIN)) {
echo1 = TCNT1 - echo1;
e1_ph = MEAS_DONE;
}
break;
}
switch (e2_ph) {
case MEAS_WAIT_1:
if (pin_is_high(ECHO2_PIN)) {
echo2 = TCNT1;
e2_ph = MEAS_WAIT_0;
}
break;
case MEAS_WAIT_0:
if (pin_is_low(ECHO2_PIN)) {
echo2 = TCNT1 - echo2;
e2_ph = MEAS_DONE;
}
break;
}
if (e1_ph == MEAS_DONE && e2_ph == MEAS_DONE) {
break; // done
}
}
TCCR1B = 0; // stop
// Both pulses measured with 0.5us accuracy
// Convert to mm
echo1 *= 8000;
echo1 /= 10000;
echo2 *= 8000;
echo2 /= 10000;
char buf[10];
ltoa(echo1, buf, 10);
usart_puts("echo1: ");
usart_puts(buf);
usart_puts(" -> ");
float offset1 = echo1 / 100.0f;
float offset2 = echo2 / 100.0f;
offset1 = mbuf_add(&mb_offs1, offset1);
offset2 = mbuf_add(&mb_offs2, offset2);
int of1i = (int) roundf(offset1);
int of2i = (int) roundf(offset2);
of1i = 15 - of1i;
of2i = 15 - of2i;
int gi = -15 + PULSE_LEN/2 - of1i - 15;
int bi = -15 + PULSE_LEN/2 + of2i + 15;
int ri = -15 + PULSE_LEN/2 + 0;
uint8_t r, g, b;
for (int i = 0; i < 32; i++) {
if (ri >= 0 && ri < PULSE_LEN) {
r = pulse[ri];
} else {
r = 0;
}
if (gi >= 0 && gi < PULSE_LEN) {
g = pulse[gi];
} else {
g = 0;
}
if (bi >= 0 && bi < PULSE_LEN) {
b = pulse[bi];
} else {
b = 0;
}
ws_send_rgb(r,g,b);
ri++;
gi++;
bi++;
}
ws_show();
}
int main(void)
{
hw_init();
while (1) {
sonar_measure();
_delay_ms(20);
}
}

@ -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

@ -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
Loading…
Cancel
Save