initial code, working LCD
This commit is contained in:
+47
@@ -0,0 +1,47 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
cmake-build-debug/
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.7)
|
||||||
|
project(firmware)
|
||||||
|
|
||||||
|
# Fake CMake config for CLion
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD GNU99)
|
||||||
|
|
||||||
|
set(SOURCE_FILES
|
||||||
|
main.c
|
||||||
|
lib/calc.h
|
||||||
|
lib/iopins.c
|
||||||
|
lib/iopins.h
|
||||||
|
lib/nsdelay.h
|
||||||
|
lib/spi.c
|
||||||
|
lib/spi.h
|
||||||
|
lib/adc.c
|
||||||
|
lib/adc.h
|
||||||
|
lib/usart.c
|
||||||
|
lib/usart.h
|
||||||
|
lcd.c
|
||||||
|
lcd.h
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(lib
|
||||||
|
/usr/avr/include/)
|
||||||
|
|
||||||
|
add_definitions(-D__AVR_ATmega328P__)
|
||||||
|
|
||||||
|
add_executable(firmware ${SOURCE_FILES})
|
||||||
@@ -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,140 @@
|
|||||||
|
#############################################
|
||||||
|
|
||||||
|
# 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/adc.o
|
||||||
|
OBJS += lcd.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,354 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
|
||||||
|
#include "calc.h"
|
||||||
|
#include "iopins.h"
|
||||||
|
#include "nsdelay.h"
|
||||||
|
#include "lcd.h"
|
||||||
|
|
||||||
|
// Start address of rows
|
||||||
|
const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54};
|
||||||
|
|
||||||
|
// Internal prototypes
|
||||||
|
void _lcd_mode_r();
|
||||||
|
void _lcd_mode_w();
|
||||||
|
void _lcd_clk();
|
||||||
|
void _lcd_wait_bf();
|
||||||
|
void _lcd_write_byte(uint8_t bb);
|
||||||
|
uint8_t _lcd_read_byte();
|
||||||
|
|
||||||
|
|
||||||
|
// Write utilities
|
||||||
|
#define _lcd_write_low(bb) _lcd_write_nibble((bb) & 0x0F)
|
||||||
|
#define _lcd_write_high(bb) _lcd_write_nibble(((bb) & 0xF0) >> 4)
|
||||||
|
#define _lcd_write_nibble(nib) do { \
|
||||||
|
pin_set(LCD_D7, get_bit((nib), 3)); \
|
||||||
|
pin_set(LCD_D6, get_bit((nib), 2)); \
|
||||||
|
pin_set(LCD_D5, get_bit((nib), 1)); \
|
||||||
|
pin_set(LCD_D4, get_bit((nib), 0)); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
// 0 W, 1 R
|
||||||
|
bool _lcd_mode;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t x;
|
||||||
|
uint8_t y;
|
||||||
|
} _pos;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TEXT = 0,
|
||||||
|
CG = 1
|
||||||
|
} _addrtype;
|
||||||
|
|
||||||
|
|
||||||
|
/** Initialize the display */
|
||||||
|
void lcd_init()
|
||||||
|
{
|
||||||
|
// configure pins as output
|
||||||
|
as_output(LCD_E);
|
||||||
|
as_output(LCD_RW);
|
||||||
|
as_output(LCD_RS);
|
||||||
|
_lcd_mode = 1; // force data pins to output
|
||||||
|
_lcd_mode_w();
|
||||||
|
|
||||||
|
// Magic sequence to invoke Cthulhu (or enter 4-bit mode)
|
||||||
|
_delay_ms(16);
|
||||||
|
_lcd_write_nibble(0b0011);
|
||||||
|
_lcd_clk();
|
||||||
|
_delay_ms(5);
|
||||||
|
_lcd_clk();
|
||||||
|
_delay_ms(5);
|
||||||
|
_lcd_clk();
|
||||||
|
_delay_ms(5);
|
||||||
|
_lcd_write_nibble(0b0010);
|
||||||
|
_lcd_clk();
|
||||||
|
_delay_us(100);
|
||||||
|
|
||||||
|
// Configure the display
|
||||||
|
lcd_command(LCD_IFACE_4BIT_2LINE);
|
||||||
|
lcd_command(LCD_DISABLE);
|
||||||
|
lcd_command(LCD_CLEAR);
|
||||||
|
lcd_command(LCD_MODE_INC);
|
||||||
|
|
||||||
|
// mark as enabled
|
||||||
|
lcd_enable();
|
||||||
|
|
||||||
|
_pos.x = 0;
|
||||||
|
_pos.y = 0;
|
||||||
|
_addrtype = TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Send a pulse on the ENABLE line */
|
||||||
|
void _lcd_clk()
|
||||||
|
{
|
||||||
|
pin_up(LCD_E);
|
||||||
|
delay_ns(450);
|
||||||
|
pin_down(LCD_E);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Enter READ mode */
|
||||||
|
void _lcd_mode_r()
|
||||||
|
{
|
||||||
|
if (_lcd_mode == 1) return; // already in R mode
|
||||||
|
|
||||||
|
pin_up(LCD_RW);
|
||||||
|
|
||||||
|
as_input_pu(LCD_D7);
|
||||||
|
as_input_pu(LCD_D6);
|
||||||
|
as_input_pu(LCD_D5);
|
||||||
|
as_input_pu(LCD_D4);
|
||||||
|
|
||||||
|
_lcd_mode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Enter WRITE mode */
|
||||||
|
void _lcd_mode_w()
|
||||||
|
{
|
||||||
|
if (_lcd_mode == 0) return; // already in W mode
|
||||||
|
|
||||||
|
pin_down(LCD_RW);
|
||||||
|
|
||||||
|
as_output(LCD_D7);
|
||||||
|
as_output(LCD_D6);
|
||||||
|
as_output(LCD_D5);
|
||||||
|
as_output(LCD_D4);
|
||||||
|
|
||||||
|
_lcd_mode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Read a byte */
|
||||||
|
uint8_t _lcd_read_byte()
|
||||||
|
{
|
||||||
|
_lcd_mode_r();
|
||||||
|
|
||||||
|
uint8_t res = 0;
|
||||||
|
|
||||||
|
_lcd_clk();
|
||||||
|
res = (pin_read(LCD_D7) << 7) | (pin_read(LCD_D6) << 6) | (pin_read(LCD_D5) << 5) | (pin_read(LCD_D4) << 4);
|
||||||
|
|
||||||
|
_lcd_clk();
|
||||||
|
res |= (pin_read(LCD_D7) << 3) | (pin_read(LCD_D6) << 2) | (pin_read(LCD_D5) << 1) | (pin_read(LCD_D4) << 0);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Write an instruction byte */
|
||||||
|
void lcd_command(uint8_t bb)
|
||||||
|
{
|
||||||
|
_lcd_wait_bf();
|
||||||
|
pin_down(LCD_RS); // select instruction register
|
||||||
|
_lcd_write_byte(bb); // send instruction byte
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Write a data byte */
|
||||||
|
void lcd_write(uint8_t bb)
|
||||||
|
{
|
||||||
|
if (_addrtype == TEXT)
|
||||||
|
{
|
||||||
|
if (bb == '\r')
|
||||||
|
{
|
||||||
|
// CR
|
||||||
|
_pos.x = 0;
|
||||||
|
lcd_xy(_pos.x, _pos.y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bb == '\n')
|
||||||
|
{
|
||||||
|
// LF
|
||||||
|
_pos.y++;
|
||||||
|
lcd_xy(_pos.x, _pos.y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pos.x++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lcd_wait_bf();
|
||||||
|
pin_up(LCD_RS); // select data register
|
||||||
|
_lcd_write_byte(bb); // send data byte
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Read BF & Address */
|
||||||
|
uint8_t lcd_read_bf_addr()
|
||||||
|
{
|
||||||
|
pin_down(LCD_RS);
|
||||||
|
return _lcd_read_byte();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Read CGRAM or DDRAM */
|
||||||
|
uint8_t lcd_read()
|
||||||
|
{
|
||||||
|
if (_addrtype == TEXT) _pos.x++;
|
||||||
|
|
||||||
|
pin_up(LCD_RS);
|
||||||
|
return _lcd_read_byte();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Write a byte using the 4-bit interface */
|
||||||
|
void _lcd_write_byte(uint8_t bb)
|
||||||
|
{
|
||||||
|
_lcd_mode_w(); // enter W mode
|
||||||
|
|
||||||
|
_lcd_write_high(bb);
|
||||||
|
_lcd_clk();
|
||||||
|
|
||||||
|
_lcd_write_low(bb);
|
||||||
|
_lcd_clk();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Wait until the device is ready */
|
||||||
|
void _lcd_wait_bf()
|
||||||
|
{
|
||||||
|
uint8_t d = 0;
|
||||||
|
while (d++ < 120 && (lcd_read_bf_addr() & _BV(7)))
|
||||||
|
_delay_us(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Send a string to LCD */
|
||||||
|
void lcd_puts(char* str_p)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
while ((c = *str_p++))
|
||||||
|
lcd_putc(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Print from progmem */
|
||||||
|
void lcd_puts_P(const char* str_p)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
while ((c = pgm_read_byte(str_p++)))
|
||||||
|
lcd_putc(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Sedn a char to LCD */
|
||||||
|
void lcd_putc(const char c)
|
||||||
|
{
|
||||||
|
lcd_write(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Set cursor position */
|
||||||
|
void lcd_xy(const uint8_t x, const uint8_t y)
|
||||||
|
{
|
||||||
|
_pos.x = x;
|
||||||
|
_pos.y = y;
|
||||||
|
lcd_addr(LCD_ROW_ADDR[y] + (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t _lcd_old_cursor = CURSOR_NONE;
|
||||||
|
bool _lcd_enabled = false;
|
||||||
|
|
||||||
|
/** Set LCD cursor. If not enabled, only remember it. */
|
||||||
|
void lcd_cursor(uint8_t type)
|
||||||
|
{
|
||||||
|
_lcd_old_cursor = (type & CURSOR_BOTH);
|
||||||
|
|
||||||
|
if (_lcd_enabled) lcd_command(LCD_CURSOR_NONE | _lcd_old_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Display display (preserving cursor) */
|
||||||
|
void lcd_disable()
|
||||||
|
{
|
||||||
|
lcd_command(LCD_DISABLE);
|
||||||
|
_lcd_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Enable display (restoring cursor) */
|
||||||
|
void lcd_enable()
|
||||||
|
{
|
||||||
|
_lcd_enabled = true;
|
||||||
|
lcd_cursor(_lcd_old_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Go home */
|
||||||
|
void lcd_home()
|
||||||
|
{
|
||||||
|
lcd_command(LCD_HOME);
|
||||||
|
_pos.x = 0;
|
||||||
|
_pos.y = 0;
|
||||||
|
_addrtype = TEXT;
|
||||||
|
_delay_ms(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Clear the screen */
|
||||||
|
void lcd_clear()
|
||||||
|
{
|
||||||
|
lcd_command(LCD_CLEAR);
|
||||||
|
_pos.x = 0;
|
||||||
|
_pos.y = 0;
|
||||||
|
_addrtype = TEXT;
|
||||||
|
_delay_ms(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Define a glyph */
|
||||||
|
void lcd_glyph(const uint8_t index, const uint8_t* array)
|
||||||
|
{
|
||||||
|
lcd_addr_cg(index * 8);
|
||||||
|
for (uint8_t i = 0; i < 8; ++i)
|
||||||
|
{
|
||||||
|
lcd_write(array[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore previous position
|
||||||
|
lcd_xy(_pos.x, _pos.y);
|
||||||
|
_addrtype = TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Define a glyph */
|
||||||
|
void lcd_glyph_P(const uint8_t index, const uint8_t* array)
|
||||||
|
{
|
||||||
|
lcd_addr_cg(index * 8);
|
||||||
|
for (uint8_t i = 0; i < 8; ++i)
|
||||||
|
{
|
||||||
|
lcd_write(pgm_read_byte(&array[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore previous position
|
||||||
|
lcd_xy(_pos.x, _pos.y);
|
||||||
|
_addrtype = TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Set address in CGRAM */
|
||||||
|
void lcd_addr_cg(const uint8_t acg)
|
||||||
|
{
|
||||||
|
_addrtype = CG;
|
||||||
|
lcd_command(0b01000000 | ((acg) & 0b00111111));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Set address in DDRAM */
|
||||||
|
void lcd_addr(const uint8_t add)
|
||||||
|
{
|
||||||
|
_addrtype = TEXT;
|
||||||
|
lcd_command(0b10000000 | ((add) & 0b01111111));
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// HD44780 LCD display driver - 4-bit mode
|
||||||
|
//
|
||||||
|
// LCD pins are configured using a file lcd_config.h, which you
|
||||||
|
// have to add next to your main.c file.
|
||||||
|
//
|
||||||
|
// Content can be something like this:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define LCD_RS D2
|
||||||
|
#define LCD_RW D3
|
||||||
|
#define LCD_E D4
|
||||||
|
#define LCD_D4 D5
|
||||||
|
#define LCD_D5 D6
|
||||||
|
#define LCD_D6 D7
|
||||||
|
#define LCD_D7 D8
|
||||||
|
|
||||||
|
|
||||||
|
// --- Commands ---
|
||||||
|
|
||||||
|
// Clear screen (reset)
|
||||||
|
#define LCD_CLEAR 0b00000001
|
||||||
|
// Move cursor to (0,0), unshift...
|
||||||
|
#define LCD_HOME 0b00000010
|
||||||
|
|
||||||
|
// Set mode: Increment + NoShift
|
||||||
|
#define LCD_MODE_INC 0b00000110
|
||||||
|
// Set mode: Increment + Shift
|
||||||
|
#define LCD_MODE_INC_SHIFT 0b00000111
|
||||||
|
|
||||||
|
// Set mode: Decrement + NoShift
|
||||||
|
#define LCD_MODE_DEC 0b00000100
|
||||||
|
// Set mode: Decrement + Shift
|
||||||
|
#define LCD_MODE_DEC_SHIFT 0b00000101
|
||||||
|
|
||||||
|
// Disable display (data remains untouched)
|
||||||
|
#define LCD_DISABLE 0b00001000
|
||||||
|
|
||||||
|
// Disable cursor
|
||||||
|
#define LCD_CURSOR_NONE 0b00001100
|
||||||
|
// Set cursor to still underscore
|
||||||
|
#define LCD_CURSOR_BAR 0b00001110
|
||||||
|
// Set cursor to blinking block
|
||||||
|
#define LCD_CURSOR_BLINK 0b00001101
|
||||||
|
// Set cursor to both of the above at once
|
||||||
|
#define LCD_CURSOR_BOTH (LCD_CURSOR_BAR | LCD_CURSOR_BLINK)
|
||||||
|
|
||||||
|
// Move cursor
|
||||||
|
#define LCD_MOVE_LEFT 0b00010000
|
||||||
|
#define LCD_MOVE_RIGHT 0b00010100
|
||||||
|
|
||||||
|
// Shift display
|
||||||
|
#define LCD_SHIFT_LEFT 0b00011000
|
||||||
|
#define LCD_SHIFT_RIGHT 0b00011100
|
||||||
|
|
||||||
|
// Set iface to 5x7 font, 1-line
|
||||||
|
#define LCD_IFACE_4BIT_1LINE 0b00100000
|
||||||
|
#define LCD_IFACE_8BIT_1LINE 0b00110000
|
||||||
|
// Set iface to 5x7 font, 2-line
|
||||||
|
#define LCD_IFACE_4BIT_2LINE 0b00101000
|
||||||
|
#define LCD_IFACE_8BIT_2LINE 0b00111000
|
||||||
|
|
||||||
|
extern const uint8_t LCD_ROW_ADDR[4];
|
||||||
|
|
||||||
|
/** Initialize the display */
|
||||||
|
void lcd_init();
|
||||||
|
|
||||||
|
/** Write an instruction byte */
|
||||||
|
void lcd_command(uint8_t bb);
|
||||||
|
|
||||||
|
/** Write a data byte */
|
||||||
|
void lcd_write(uint8_t bb);
|
||||||
|
|
||||||
|
/** Read BF & Address */
|
||||||
|
uint8_t lcd_read_bf_addr();
|
||||||
|
|
||||||
|
/** Read CGRAM or DDRAM */
|
||||||
|
uint8_t lcd_read();
|
||||||
|
|
||||||
|
/** Send a string to LCD */
|
||||||
|
void lcd_puts(char* str_p);
|
||||||
|
|
||||||
|
/** Send a string to LCD from program memory */
|
||||||
|
void lcd_puts_P(const char* str_p);
|
||||||
|
|
||||||
|
/** Sedn a char to LCD */
|
||||||
|
void lcd_putc(const char c);
|
||||||
|
|
||||||
|
/** Show string at X, Y */
|
||||||
|
#define lcd_puts_xy(x, y, str_p) do { lcd_xy((x), (y)); lcd_puts((str_p)); } while(0)
|
||||||
|
|
||||||
|
/** Show string at X, Y */
|
||||||
|
#define lcd_puts_xy_P(x, y, str_p) do { lcd_xy((x), (y)); lcd_puts_P((str_p)); } while(0)
|
||||||
|
|
||||||
|
/** Show char at X, Y */
|
||||||
|
#define lcd_putc_xy(x, y, c) do { lcd_xy((x), (y)); lcd_putc((c)); } while(0)
|
||||||
|
|
||||||
|
/** Set cursor position */
|
||||||
|
void lcd_xy(const uint8_t x, const uint8_t y);
|
||||||
|
|
||||||
|
/** Set LCD cursor. If not enabled, only remember it. */
|
||||||
|
#define CURSOR_NONE 0b00
|
||||||
|
#define CURSOR_BAR 0b10
|
||||||
|
#define CURSOR_BLINK 0b01
|
||||||
|
#define CURSOR_BOTH 0b11
|
||||||
|
void lcd_cursor(uint8_t type);
|
||||||
|
|
||||||
|
/** Display display (preserving cursor) */
|
||||||
|
void lcd_disable();
|
||||||
|
|
||||||
|
/** Enable display (restoring cursor) */
|
||||||
|
void lcd_enable();
|
||||||
|
|
||||||
|
/** Go home */
|
||||||
|
void lcd_home();
|
||||||
|
|
||||||
|
/** Clear the screen */
|
||||||
|
void lcd_clear();
|
||||||
|
|
||||||
|
/** Define a glyph - 8 bytes, right 5 bits are used */
|
||||||
|
void lcd_glyph(const uint8_t index, const uint8_t* array);
|
||||||
|
|
||||||
|
/** Define a glyph that's in PROGMEM */
|
||||||
|
void lcd_glyph_P(const uint8_t index, const uint8_t* array);
|
||||||
|
|
||||||
|
/** Set address in CGRAM */
|
||||||
|
void lcd_addr_cg(const uint8_t acg);
|
||||||
|
|
||||||
|
/** Set address in DDRAM */
|
||||||
|
void lcd_addr(const uint8_t add);
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#include <avr/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "calc.h"
|
||||||
|
#include "adc.h"
|
||||||
|
|
||||||
|
/** Initialize the ADC */
|
||||||
|
void adc_init(enum ADC_Prescaller presc)
|
||||||
|
{
|
||||||
|
ADCSRA |= presc; // 128 prescaler -> 125 kHz
|
||||||
|
ADMUX |= _BV(REFS0); // Voltage reference
|
||||||
|
sbi(ADCSRA, ADEN); // Enable ADC
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Disable ADC */
|
||||||
|
void adc_disable(void)
|
||||||
|
{
|
||||||
|
cbi(ADCSRA, ADEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Enable ADC */
|
||||||
|
void adc_enable(void)
|
||||||
|
{
|
||||||
|
sbi(ADCSRA, ADEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Start a new conversion */
|
||||||
|
void adc_start_conversion(uint8_t channel)
|
||||||
|
{
|
||||||
|
set_low_nibble(ADMUX, channel); // Select channel to sample
|
||||||
|
cbi(ADMUX, ADLAR); // Align result to right
|
||||||
|
sbi(ADCSRA, ADSC); // Start conversion
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if ADC is done converting */
|
||||||
|
bool adc_ready(void)
|
||||||
|
{
|
||||||
|
return bit_is_low(ADCSRA, ADSC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read the result of last conversion with 8bit precision */
|
||||||
|
uint8_t adc_read_8bit()
|
||||||
|
{
|
||||||
|
uint8_t low = ADCL;
|
||||||
|
uint8_t high = ADCH;
|
||||||
|
return low >> 2 | high << 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read the result of last conversion with 10bit precision */
|
||||||
|
uint16_t adc_read_10bit()
|
||||||
|
{
|
||||||
|
uint8_t low = ADCL;
|
||||||
|
uint8_t high = ADCH;
|
||||||
|
return ((uint16_t) high << 8) | low;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Start ADC conversion and wait for the result */
|
||||||
|
static void adc_convert(uint8_t channel)
|
||||||
|
{
|
||||||
|
adc_start_conversion(channel);
|
||||||
|
while (!adc_ready()); // Wait for it...
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sample analog pin with 8-bit precision */
|
||||||
|
uint8_t adc_convert_8bit(uint8_t channel)
|
||||||
|
{
|
||||||
|
adc_convert(channel);
|
||||||
|
return adc_read_8bit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sample analog pin with 10-bit precision */
|
||||||
|
uint16_t adc_convert_10bit(uint8_t channel)
|
||||||
|
{
|
||||||
|
adc_convert(channel);
|
||||||
|
return adc_read_10bit();
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
//
|
||||||
|
// Utilities for build-in A/D converter
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum ADC_Prescaller {
|
||||||
|
ADC_PRESC_2 = 1,
|
||||||
|
ADC_PRESC_4 = 2,
|
||||||
|
ADC_PRESC_8 = 3,
|
||||||
|
ADC_PRESC_16 = 4,
|
||||||
|
ADC_PRESC_32 = 5,
|
||||||
|
ADC_PRESC_64 = 6,
|
||||||
|
ADC_PRESC_128 = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Initialize the ADC and enable it */
|
||||||
|
void adc_init(enum ADC_Prescaller presc);
|
||||||
|
|
||||||
|
/** Disable ADC (for power saving) */
|
||||||
|
void adc_disable(void);
|
||||||
|
|
||||||
|
/** Enable (already initialized) ADC */
|
||||||
|
void adc_enable(void);
|
||||||
|
|
||||||
|
/** Start a new conversion */
|
||||||
|
void adc_start_conversion(uint8_t channel);
|
||||||
|
|
||||||
|
/** Check if ADC is done converting */
|
||||||
|
bool adc_ready(void);
|
||||||
|
|
||||||
|
/** Read the result of last conversion with 8bit precision */
|
||||||
|
uint8_t adc_read_8bit(void);
|
||||||
|
|
||||||
|
/** Read the result of last conversion with 10bit precision */
|
||||||
|
uint16_t adc_read_10bit(void);
|
||||||
|
|
||||||
|
/** Sample analog pin with 8-bit precision. Blocking! */
|
||||||
|
uint8_t adc_convert_8bit(uint8_t channel);
|
||||||
|
|
||||||
|
/** Sample analog pin with 10-bit precision. Blocking! */
|
||||||
|
uint16_t adc_convert_10bit(uint8_t channel);
|
||||||
+154
@@ -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)))
|
||||||
+277
@@ -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);
|
||||||
|
}
|
||||||
+213
@@ -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);
|
||||||
+83
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+86
@@ -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);
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#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 <stdlib.h>
|
||||||
|
|
||||||
|
// Include stuff from the library
|
||||||
|
#include "lib/iopins.h"
|
||||||
|
#include "lib/usart.h"
|
||||||
|
#include "lcd.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Pins
|
||||||
|
#define LED 13
|
||||||
|
|
||||||
|
void _lcd_wait_bf();
|
||||||
|
void _lcd_write_byte(uint8_t bb);
|
||||||
|
|
||||||
|
// UART receive handler
|
||||||
|
ISR(USART_RX_vect)
|
||||||
|
{
|
||||||
|
// "ECHO" function:
|
||||||
|
uint8_t b = usart_rx();
|
||||||
|
usart_tx(b); // send back
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
usart_init(BAUD_115200);
|
||||||
|
usart_isr_rx_enable(true); // enable RX interrupt handler
|
||||||
|
|
||||||
|
// configure pins
|
||||||
|
as_output(LED);
|
||||||
|
|
||||||
|
lcd_init();
|
||||||
|
|
||||||
|
// globally enable interrupts (for the USART_RX handler)
|
||||||
|
sei();
|
||||||
|
uint8_t cnt = 0;
|
||||||
|
while (1) {
|
||||||
|
lcd_clear();
|
||||||
|
|
||||||
|
lcd_xy(cnt, 0);
|
||||||
|
lcd_putc('A'+cnt);
|
||||||
|
|
||||||
|
cnt = (cnt+1)%20;
|
||||||
|
|
||||||
|
pin_toggle(13); // blink the LED
|
||||||
|
|
||||||
|
_delay_ms(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user