From c7b83a9ba43e6ebb5e1dfedc79155a04256e4b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 14 Aug 2018 08:21:32 +0200 Subject: [PATCH] initial code, working LCD --- .gitignore | 47 +++++++ CMakeLists.txt | 29 ++++ LICENSE | 21 +++ Makefile | 140 +++++++++++++++++++ lcd.c | 354 +++++++++++++++++++++++++++++++++++++++++++++++++ lcd.h | 135 +++++++++++++++++++ lib/adc.c | 77 +++++++++++ lib/adc.h | 44 ++++++ lib/calc.h | 154 +++++++++++++++++++++ lib/iopins.c | 277 ++++++++++++++++++++++++++++++++++++++ lib/iopins.h | 213 +++++++++++++++++++++++++++++ lib/nsdelay.h | 21 +++ lib/spi.c | 62 +++++++++ lib/spi.h | 69 ++++++++++ lib/usart.c | 83 ++++++++++++ lib/usart.h | 86 ++++++++++++ main.c | 56 ++++++++ style.astylerc | 14 ++ 18 files changed, 1882 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 lcd.c create mode 100644 lcd.h create mode 100644 lib/adc.c create mode 100644 lib/adc.h create mode 100644 lib/calc.h create mode 100644 lib/iopins.c create mode 100644 lib/iopins.h create mode 100644 lib/nsdelay.h create mode 100644 lib/spi.c create mode 100644 lib/spi.h create mode 100644 lib/usart.c create mode 100644 lib/usart.h create mode 100644 main.c create mode 100644 style.astylerc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9de2f7b --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a52e83b --- /dev/null +++ b/CMakeLists.txt @@ -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}) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..522de4e --- /dev/null +++ b/LICENSE @@ -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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d3f8255 --- /dev/null +++ b/Makefile @@ -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 diff --git a/lcd.c b/lcd.c new file mode 100644 index 0000000..7f54874 --- /dev/null +++ b/lcd.c @@ -0,0 +1,354 @@ +#include +#include +#include +#include +#include + +#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)); +} diff --git a/lcd.h b/lcd.h new file mode 100644 index 0000000..cec0e0b --- /dev/null +++ b/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 +#include + +#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); diff --git a/lib/adc.c b/lib/adc.c new file mode 100644 index 0000000..8202ff6 --- /dev/null +++ b/lib/adc.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#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(); +} diff --git a/lib/adc.h b/lib/adc.h new file mode 100644 index 0000000..af93226 --- /dev/null +++ b/lib/adc.h @@ -0,0 +1,44 @@ +#pragma once + +// +// Utilities for build-in A/D converter +// + +#include + +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); diff --git a/lib/calc.h b/lib/calc.h new file mode 100644 index 0000000..56438f8 --- /dev/null +++ b/lib/calc.h @@ -0,0 +1,154 @@ +#pragma once + +// +// Bit and byte manipulation utilities +// + +#include + + +// --- 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))) diff --git a/lib/iopins.c b/lib/iopins.c new file mode 100644 index 0000000..493210b --- /dev/null +++ b/lib/iopins.c @@ -0,0 +1,277 @@ +#include +#include +#include + +#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); +} diff --git a/lib/iopins.h b/lib/iopins.h new file mode 100644 index 0000000..f918922 --- /dev/null +++ b/lib/iopins.h @@ -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 +#include +#include + +#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 diff --git a/lib/nsdelay.h b/lib/nsdelay.h new file mode 100644 index 0000000..dd93a83 --- /dev/null +++ b/lib/nsdelay.h @@ -0,0 +1,21 @@ +#pragma once + +// +// Functions for precise delays (nanoseconds / cycles) +// + +#include +#include +#include + +/* 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)) diff --git a/lib/spi.c b/lib/spi.c new file mode 100644 index 0000000..4315fb1 --- /dev/null +++ b/lib/spi.c @@ -0,0 +1,62 @@ +#include +#include +#include + +#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; +} diff --git a/lib/spi.h b/lib/spi.h new file mode 100644 index 0000000..0d9546d --- /dev/null +++ b/lib/spi.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#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 */ +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); diff --git a/lib/usart.c b/lib/usart.c new file mode 100644 index 0000000..8927dff --- /dev/null +++ b/lib/usart.c @@ -0,0 +1,83 @@ +#include +#include +#include + +#include +#include +#include + +#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; + } +} diff --git a/lib/usart.h b/lib/usart.h new file mode 100644 index 0000000..f03fda5 --- /dev/null +++ b/lib/usart.h @@ -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 +#include +#include + +#include +#include + +#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); diff --git a/main.c b/main.c new file mode 100644 index 0000000..0792990 --- /dev/null +++ b/main.c @@ -0,0 +1,56 @@ +#include // register definitions +#include // storing data in program memory +#include // interrupt vectors +#include // delay functions + +#include // C header for int types like uint8_t +#include // C header for the bool type +#include + +// 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); + } +} diff --git a/style.astylerc b/style.astylerc new file mode 100644 index 0000000..d7cde65 --- /dev/null +++ b/style.astylerc @@ -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