initial code, working LCD

master
Ondřej Hruška 6 years ago
commit c7b83a9ba4
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 47
      .gitignore
  2. 29
      CMakeLists.txt
  3. 21
      LICENSE
  4. 140
      Makefile
  5. 354
      lcd.c
  6. 135
      lcd.h
  7. 77
      lib/adc.c
  8. 44
      lib/adc.h
  9. 154
      lib/calc.h
  10. 277
      lib/iopins.c
  11. 213
      lib/iopins.h
  12. 21
      lib/nsdelay.h
  13. 62
      lib/spi.c
  14. 69
      lib/spi.h
  15. 83
      lib/usart.c
  16. 86
      lib/usart.h
  17. 56
      main.c
  18. 14
      style.astylerc

47
.gitignore vendored

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

354
lcd.c

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

135
lcd.h

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

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

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