Move included examples to their own repositories

Move examples (digital_thermometer and uart_echo) to their own git
repositories. Later they can be re-added via git submodules, once this
project has been published.

Signed-off-by: Collin J. Doering <collin.doering@rekahsoft.ca>
This commit is contained in:
Collin J. Doering 2016-01-11 22:09:11 -05:00
parent 87070fb281
commit 28a71ff8e8
16 changed files with 0 additions and 3687 deletions

View File

@ -1,197 +0,0 @@
##########------------------------------------------------------##########
########## Project-specific Details ##########
########## Check these every time you start a new project ##########
##########------------------------------------------------------##########
MCU = atmega328p
F_CPU = 8000000UL
BAUD = 9600
## Also try BAUD = 19200 or 38400 if you're feeling lucky.
## A directory for common include files and the simple USART library.
## If you move either the current folder or the Library folder, you'll
## need to change this path to match.
#LIBDIR = ../../AVR-Programming-Library
##########------------------------------------------------------##########
########## Programmer Defaults ##########
########## Set up once, then forget about it ##########
########## (Can override. See bottom of file.) ##########
##########------------------------------------------------------##########
PROGRAMMER_TYPE = avrisp
# extra arguments to avrdude: baud rate, chip type, -F flag, etc.
PROGRAMMER_ARGS = -b 19200 -P /dev/ttyUSB0
##########------------------------------------------------------##########
########## Program Locations ##########
########## Won't need to change if they're in your PATH ##########
##########------------------------------------------------------##########
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
AVRSIZE = avr-size
AVRDUDE = avrdude
##########------------------------------------------------------##########
########## Makefile Magic! ##########
########## Summary: ##########
########## We want a .hex file ##########
########## Compile source files into .elf ##########
########## Convert .elf file into .hex ##########
########## You shouldn't need to edit below. ##########
##########------------------------------------------------------##########
## The name of your project (without the .c)
# TARGET = blinkLED
## Or name it automatically after the enclosing directory
TARGET = $(lastword $(subst /, ,$(CURDIR)))
# Object files: will find all .c/.h files in current directory
# and in LIBDIR. If you have any other (sub-)directories with code,
# you can add them in to SOURCES below in the wildcard statement.
SOURCES=$(wildcard *.c $(LIBDIR)/*.c)
OBJECTS=$(SOURCES:.c=.o)
HEADERS=$(SOURCES:.c=.h)
## Compilation options, type man avr-gcc if you're curious.
# Removed "-I$(LIBDIR)" from end of CPPFLAGS
CPPFLAGS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -I.
CFLAGS = -Os -g -std=gnu99 -Wall
## Use short (8-bit) data types
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
## Splits up object files per function
CFLAGS += -ffunction-sections -fdata-sections
LDFLAGS = -Wl,-Map,$(TARGET).map
## Optional, but often ends up with smaller code
LDFLAGS += -Wl,--gc-sections
## Relax shrinks code even more, but makes disassembly messy
## LDFLAGS += -Wl,--relax
## LDFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf
## LDFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf
TARGET_ARCH = -mmcu=$(MCU)
## Explicit pattern rules:
## To make .o files from .c files
%.o: %.c $(HEADERS) Makefile
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<;
$(TARGET).elf: $(OBJECTS)
$(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@
%.hex: %.elf
$(OBJCOPY) -j .text -j .data -O ihex $< $@
%.eeprom: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
%.lst: %.elf
$(OBJDUMP) -S $< > $@
## These targets don't have files named after them
.PHONY: all disassemble disasm eeprom size clean squeaky_clean flash fuses
all: $(TARGET).hex
debug:
@echo
@echo "Source files:" $(SOURCES)
@echo "MCU, F_CPU, BAUD:" $(MCU), $(F_CPU), $(BAUD)
@echo
# Optionally create listing file from .elf
# This creates approximate assembly-language equivalent of your code.
# Useful for debugging time-sensitive bits,
# or making sure the compiler does what you want.
disassemble: $(TARGET).lst
disasm: disassemble
# Optionally show how big the resulting program is
size: $(TARGET).elf
$(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf
clean:
rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \
$(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \
$(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \
$(TARGET).eeprom
squeaky_clean:
rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~ *.eeprom
##########------------------------------------------------------##########
########## Programmer-specific details ##########
########## Flashing code to AVR using avrdude ##########
##########------------------------------------------------------##########
flash: $(TARGET).hex
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$<
## An alias
program: flash
flash_eeprom: $(TARGET).eeprom
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$<
avrdude_terminal:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt
## If you've got multiple programmers that you use,
## you can define them here so that it's easy to switch.
## To invoke, use something like `make flash_arduinoISP`
flash_usbtiny: PROGRAMMER_TYPE = usbtiny
flash_usbtiny: PROGRAMMER_ARGS = # USBTiny works with no further arguments
flash_usbtiny: flash
flash_usbasp: PROGRAMMER_TYPE = usbasp
flash_usbasp: PROGRAMMER_ARGS = # USBasp works with no further arguments
flash_usbasp: flash
flash_arduinoISP: PROGRAMMER_TYPE = avrisp
flash_arduinoISP: PROGRAMMER_ARGS = -b 19200 -P /dev/ttyACM0
## (for windows) flash_arduinoISP: PROGRAMMER_ARGS = -b 19200 -P com5
flash_arduinoISP: flash
flash_109: PROGRAMMER_TYPE = avr109
flash_109: PROGRAMMER_ARGS = -b 9600 -P /dev/ttyUSB0
flash_109: flash
##########------------------------------------------------------##########
########## Fuse settings and suitable defaults ##########
##########------------------------------------------------------##########
## Mega 48, 88, 168, 328 default values
LFUSE = 0x62
HFUSE = 0xdf
EFUSE = 0x00
## Generic
FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \
$(PROGRAMMER_ARGS) $(FUSE_STRING)
show_fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv
## Called with no extra definitions, sets to defaults
set_default_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
set_default_fuses: fuses
## Set the fuse byte for full-speed mode
## Note: can also be set in firmware for modern chips
set_fast_fuse: LFUSE = 0xE2
set_fast_fuse: FUSE_STRING = -U lfuse:w:$(LFUSE):m
set_fast_fuse: fuses
## Set the EESAVE fuse byte to preserve EEPROM across flashes
set_eeprom_save_fuse: HFUSE = 0xD7
set_eeprom_save_fuse: FUSE_STRING = -U hfuse:w:$(HFUSE):m
set_eeprom_save_fuse: fuses
## Clear the EESAVE fuse byte
clear_eeprom_save_fuse: FUSE_STRING = -U hfuse:w:$(HFUSE):m
clear_eeprom_save_fuse: fuses

View File

@ -1,56 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file ansi_escapes.h
* @author Collin J. Doering <collin.doering@rekahsoft.ca>
* @date Oct 9, 2015
* @brief Macros to ease writing ANSI escapes.
*/
#define CSI "\e[" ///< Control Sequence Introducer
#define CUU(n) CSI #n "A" ///< Cursor up
#define CUD(n) CSI #n "B" ///< Cursor down
#define CUF(n) CSI #n "C" ///< Cursor forward
#define CUB(n) CSI #n "D" ///< Cursor backward
#define CNL(n) CSI #n "E" ///< Cursor next line
#define CPL(n) CSI #n "F" ///< Cursor previous line
#define CHA(n) CSI #n "G" ///< Cursor horizontal absolute
#define CUP(n,m) CSI #n ";" #m "H" ///< Cursor position
#define ED(n) CSI #n "J" ///< Erase display
#define EL(n) CSI #n "K" ///< Erase in line
#define SU(n) CSI #n "S" ///< Scroll up
#define SD(n) CSI #n "T" ///< Scroll down
#define HVP(n,m) CSI #n ";" #m "f" ///< Horizontal and vertical position
// #define SGR(n,m) CSI #n #m ///< Select graphic rendition
#define AUX_ON CSI "5i" ///< AUX port on
#define AUX_OFF CSI "4i" ///< AUX port off
// #define DSR CSI "6 n" ///< Device status report
#define SCP CSI "s" ///< Save cursor position
#define RCP CSI "u" ///< Restore cursor position
#define HIDE_CURSOR CSI "?25l" ///< DECTCEM: hide cursor
#define SHOW_CURSOR CSI "?25h" ///< DECTCEM: show cursor

View File

@ -1,96 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* File: digital_thermometer.c
* Author: Collin J. Doering <collin.doering@rekahsoft.ca>
* Date: Oct 28, 2015
*/
/*---------.
| Includes |
`---------*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <util/delay.h>
#include <stdlib.h>
#include "lcdLib.h"
#include "ansi_escapes.h"
void initADC(void) {
ADMUX |= (1 << REFS0); // reference voltage on AVCC
ADCSRA |= (1 << ADPS2) | (1 << ADPS1); // ADC prescaler 64
ADCSRA |= (1 << ADEN); // Enable ADC
ADCSRA |= (1 << ADATE); // Auto trigger enable
ADCSRA |= (1 << ADSC); // Start first conversion
}
int main(void) {
clock_prescale_set(clock_div_1);
initLCD();
initADC();
hideCursor();
writeStringToLCD("Temperature: ");
saveCursorPosition();
while (1) {
float v = ADC * (5000 / 1024);
int8_t t = (int8_t) ((v - 500) / 10);
char str[5];
uint8_t i = 1, sigDig = 0;
if (t < 0) {
str[0] = '-';
t *= -1;
} else {
str[0] = '+';
}
for (uint8_t j = 100; j >= 1; j /= 10) {
uint8_t digit = (uint8_t) (t / j);
if (!sigDig) {
if (digit != 0) {
sigDig = 1;
str[i++] = digit + 0x30;
} else {
str[i++] = ' ';
}
} else {
str[i++] = digit + 0x30;
}
t = t - digit*j;
}
str[i] = '\0';
writeStringToLCD(str);
writeCharToLCD(0xdf);
writeStringToLCD("C");
restoreCursorPosition();
_delay_ms(16);
}
return 0;
}

View File

@ -1,969 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* File: lcdLib.c
* Author: Collin J. Doering <collin.doering@rekahsoft.ca>
* Date: Sep 29, 2015
*/
// Includes -----------------------------------------------------------------------------------
#include <math.h>
#include <avr/io.h>
#include <util/delay.h>
#include "lcdLib.h"
//---------------------------------------------------------------------------------------------
// Static global variables
static volatile uint8_t currentLineNum;
static volatile uint8_t currentLineChars;
static volatile uint8_t saveCursorLineNum;
static volatile uint8_t saveCursorLineChars;
static volatile uint8_t lcdState;
static const uint8_t lineBeginnings[LCD_NUMBER_OF_LINES] = { LCD_LINE_BEGINNINGS };
//---------------------------------------------------------------------------------------------
// Static functions
/*
Bring LCD_ENABLE line high, wait for LCD_ENABLE_HIGH_DELAY; then bring LCD_ENABLE line low
and wait for LCD_ENABLE_LOW_DELAY.
Note: LCD_ENABLE, LCD_ENABLE_HIGH_DELAY, and LCD_ENABLE_LOW_DELAY must be defined in lcdLibConfig.h
*/
static void clkLCD(void) {
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(LCD_ENABLE_HIGH_DELAY);
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(LCD_ENABLE_LOW_DELAY);
}
/*
Wait until LCD_BF (busy flag) is cleared (low).
*/
static void loop_until_LCD_BF_clear(void) {
uint8_t bf;
LCD_RS_PORT &= ~(1 << LCD_RS); // RS=0
LCD_RW_PORT |= (1 << LCD_RW); // RW=1
// Set LCD_BF as input
LCD_DBUS7_DDR &= ~(1 << LCD_BF);
do {
bf = 0;
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(1); // 'delay data time' and 'enable pulse width'
bf |= (LCD_DBUS7_PIN & (1 << LCD_BF));
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(1); // 'address hold time', 'data hold time' and 'enable cycle width'
#ifdef FOUR_BIT_MODE
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(1); // 'delay data time' and 'enable pulse width'
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(1); // 'address hold time', 'data hold time' and 'enable cycle width'
#endif
} while (bf);
#if defined (FOUR_BIT_MODE) || defined (EIGHT_BIT_ARBITRARY_PIN_MODE)
LCD_DBUS7_DDR |= (1 << LCD_DBUS7);
LCD_DBUS6_DDR |= (1 << LCD_DBUS6);
LCD_DBUS5_DDR |= (1 << LCD_DBUS5);
LCD_DBUS4_DDR |= (1 << LCD_DBUS4);
#ifdef EIGHT_BIT_ARBITRARY_PIN_MODE
LCD_DBUS3_DDR |= (1 << LCD_DBUS3);
LCD_DBUS2_DDR |= (1 << LCD_DBUS2);
LCD_DBUS1_DDR |= (1 << LCD_DBUS1);
LCD_DBUS0_DDR |= (1 << LCD_DBUS0);
#endif
#else
LCD_DBUS_DDR = 0xff; // Reset all LCD_DBUS_PORT pins as outputs
#endif
}
/*
Given a 8 bit integer, writes the four MSB's (one nibble) to the LCD data bus.
Note: this is only defined in FOUR_BIT_MODE
*/
#ifdef FOUR_BIT_MODE
static void writeLCDDBusNibble_(uint8_t b) {
// Reset data lines to zeros
LCD_DBUS7_PORT &= ~(1 << LCD_DBUS7);
LCD_DBUS6_PORT &= ~(1 << LCD_DBUS6);
LCD_DBUS5_PORT &= ~(1 << LCD_DBUS5);
LCD_DBUS4_PORT &= ~(1 << LCD_DBUS4);
// Write 1's where appropriate on data lines
if (b & (1 << 7)) LCD_DBUS7_PORT |= (1 << LCD_DBUS7);
if (b & (1 << 6)) LCD_DBUS6_PORT |= (1 << LCD_DBUS6);
if (b & (1 << 5)) LCD_DBUS5_PORT |= (1 << LCD_DBUS5);
if (b & (1 << 4)) LCD_DBUS4_PORT |= (1 << LCD_DBUS4);
// Pulse the enable line
clkLCD();
}
#endif
/*
Given an 8 bit integer, writes it to the LCD data bus, regardless of its
configuration (default 8-bit mode, 8-bit arbitrary pin mode and 4-bit mode). In the default
8-bit mode and EIGHT_BIT_ARBITRARY_PIN_MODE, the given data is written in one cycle using the
writeLCDDBusByte_ function. In FOUR_BIT_MODE however, the given data is written in two cycles
using two successive calls to the writeLCDDBusNibble_ function.
This function does not ensure the LCD is ready to accept new data and thus needs to
be handled by the caller.
*/
static void writeLCDDBusByte_(uint8_t b) {
#ifdef FOUR_BIT_MODE
writeLCDDBusNibble_(b);
writeLCDDBusNibble_(b << 4);
#elif defined (EIGHT_BIT_ARBITRARY_PIN_MODE)
// Reset data lines to zeros
LCD_DBUS7_PORT &= ~(1 << LCD_DBUS7);
LCD_DBUS6_PORT &= ~(1 << LCD_DBUS6);
LCD_DBUS5_PORT &= ~(1 << LCD_DBUS5);
LCD_DBUS4_PORT &= ~(1 << LCD_DBUS4);
LCD_DBUS3_PORT &= ~(1 << LCD_DBUS3);
LCD_DBUS2_PORT &= ~(1 << LCD_DBUS2);
LCD_DBUS1_PORT &= ~(1 << LCD_DBUS1);
LCD_DBUS0_PORT &= ~(1 << LCD_DBUS0);
// Write 1's where appropriate on data lines
if (b & (1 << 7)) LCD_DBUS7_PORT |= (1 << LCD_DBUS7);
if (b & (1 << 6)) LCD_DBUS6_PORT |= (1 << LCD_DBUS6);
if (b & (1 << 5)) LCD_DBUS5_PORT |= (1 << LCD_DBUS5);
if (b & (1 << 4)) LCD_DBUS4_PORT |= (1 << LCD_DBUS4);
if (b & (1 << 3)) LCD_DBUS3_PORT |= (1 << LCD_DBUS3);
if (b & (1 << 2)) LCD_DBUS2_PORT |= (1 << LCD_DBUS2);
if (b & (1 << 1)) LCD_DBUS1_PORT |= (1 << LCD_DBUS1);
if (b & (1 << 0)) LCD_DBUS0_PORT |= (1 << LCD_DBUS0);
clkLCD();
#else
LCD_DBUS_PORT = b;
clkLCD();
#endif
}
/*
Given a 8 bit integer representing a LCD instruction, sends it to the LCD display.
Sets RS=RW=0 and writes the given 8 bit integer to the LCD databus.
Note that this function does not ensure the LCD is ready to accept a new instruction and thus
needs to be handled by the caller.
*/
static void writeLCDInstr_(uint8_t instr) {
LCD_RS_PORT &= ~(1 << LCD_RS); // RS=0
LCD_RW_PORT &= ~(1 << LCD_RW); // RW=0
writeLCDDBusByte_(instr);
}
/*
Given a 8 bit integer representing a LCD instruction, waits until the LCD is ready and sends
the instruction.
*/
static inline void writeLCDInstr(uint8_t instr) {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
writeLCDInstr_(instr);
}
/*
Sets RS=1, RW=0 and accepts a char (8 bit) and outputs it to the current cursor position of
the LCD. In the default 8-bit mode and EIGHT_BIT_ARBITRARY_PIN_MODE, the given data is
written in one cycle using the writeLCDDBusByte_ function. In FOUR_BIT_MODE however, the given
data is written in two cycles using two successive calls to the writeLCDDBusNibble_ function.
*/
static void writeCharToLCD_(char c) {
LCD_RS_PORT |= (1 << LCD_RS); // RS=1
LCD_RW_PORT &= ~(1 << LCD_RW); // RW=0
writeLCDDBusByte_(c);
}
static uint8_t readLCDDBusByte_(void) {
LCD_RS_PORT |= (1 << LCD_RS); // RS=1
LCD_RW_PORT |= (1 << LCD_RW); // RW=1
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(1); // 'delay data time' and 'enable pulse width'
// Read data
char c = 0;
#if defined(FOUR_BIT_MODE)
if (LCD_DBUS7_PIN & (1 << LCD_DBUS7)) c |= (1 << 7);
if (LCD_DBUS6_PIN & (1 << LCD_DBUS6)) c |= (1 << 6);
if (LCD_DBUS5_PIN & (1 << LCD_DBUS5)) c |= (1 << 5);
if (LCD_DBUS4_PIN & (1 << LCD_DBUS4)) c |= (1 << 4);
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(1); // 'address hold time', 'data hold time' and 'enable cycle width'
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(1); // 'delay data time' and 'enable pulse width'
if (LCD_DBUS7_PIN & (1 << LCD_DBUS7)) c |= (1 << 3);
if (LCD_DBUS6_PIN & (1 << LCD_DBUS6)) c |= (1 << 2);
if (LCD_DBUS5_PIN & (1 << LCD_DBUS5)) c |= (1 << 1);
if (LCD_DBUS4_PIN & (1 << LCD_DBUS4)) c |= (1 << 0);
#elif defined(EIGHT_BIT_ARBITRARY_PIN_MODE)
if (LCD_DBUS7_PIN & (1 << LCD_DBUS7)) c |= (1 << 7);
if (LCD_DBUS6_PIN & (1 << LCD_DBUS6)) c |= (1 << 6);
if (LCD_DBUS5_PIN & (1 << LCD_DBUS5)) c |= (1 << 5);
if (LCD_DBUS4_PIN & (1 << LCD_DBUS4)) c |= (1 << 4);
if (LCD_DBUS3_PIN & (1 << LCD_DBUS3)) c |= (1 << 3);
if (LCD_DBUS2_PIN & (1 << LCD_DBUS2)) c |= (1 << 2);
if (LCD_DBUS1_PIN & (1 << LCD_DBUS1)) c |= (1 << 1);
if (LCD_DBUS0_PIN & (1 << LCD_DBUS0)) c |= (1 << 0);
#else
c = LCD_DBUS_PIN;
#endif
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(1); // 'address hold time', 'data hold time' and 'enable cycle width'
return c;
}
/*
Given a character string, and a uint8_t pointer, reads the character string until a
non-numerical ASCII character, returning the integer representation of the number read. At
the end of the functions execution, the found_num uint8_t* will be updated to indicate how
many digits were read. The new_loc char** will be updated with the new parsing position in
the string.
*/
static uint8_t readASCIINumber(char* str, uint8_t* found_num, char** new_loc) {
uint8_t nums[3];
*found_num = 0;
while (*str != '\0' && *found_num < 3) {
if (*str >= 0x30 && *str <= 0x39) {
// Use *str as a number (specified in ASCII)
nums[(*found_num)++] = *str - 0x30;
} else {
break;
}
str++;
}
*new_loc = str;
uint8_t ret = 0;
uint8_t i = *found_num - 1;
for (uint8_t fnd = 0; fnd < *found_num; fnd++)
ret += nums[fnd] * pow(10, i--);
return ret;
}
/*
Set all pins of LCD_DBUS as outputs
*/
static inline void setLCDDBusAsOutputs(void) {
#if defined (FOUR_BIT_MODE) || defined (EIGHT_BIT_ARBITRARY_PIN_MODE)
LCD_DBUS7_DDR |= (1 << LCD_DBUS7);
LCD_DBUS6_DDR |= (1 << LCD_DBUS6);
LCD_DBUS5_DDR |= (1 << LCD_DBUS5);
LCD_DBUS4_DDR |= (1 << LCD_DBUS4);
#ifdef EIGHT_BIT_ARBITRARY_PIN_MODE
LCD_DBUS3_DDR |= (1 << LCD_DBUS3);
LCD_DBUS2_DDR |= (1 << LCD_DBUS2);
LCD_DBUS1_DDR |= (1 << LCD_DBUS1);
LCD_DBUS0_DDR |= (1 << LCD_DBUS0);
#endif
#else
LCD_DBUS_DDR = 0xff;
#endif
}
/*
Set all pins of LCD_DBUS as inputs (disabling their output)
*/
static inline void setLCDDBusAsInputs(void) {
#if defined (FOUR_BIT_MODE) || defined (EIGHT_BIT_ARBITRARY_PIN_MODE)
LCD_DBUS7_DDR &= ~(1 << LCD_DBUS7);
LCD_DBUS6_DDR &= ~(1 << LCD_DBUS6);
LCD_DBUS5_DDR &= ~(1 << LCD_DBUS5);
LCD_DBUS4_DDR &= ~(1 << LCD_DBUS4);
#ifdef EIGHT_BIT_ARBITRARY_PIN_MODE
LCD_DBUS3_DDR &= ~(1 << LCD_DBUS3);
LCD_DBUS2_DDR &= ~(1 << LCD_DBUS2);
LCD_DBUS1_DDR &= ~(1 << LCD_DBUS1);
LCD_DBUS0_DDR &= ~(1 << LCD_DBUS0);
#endif
#else
LCD_DBUS_DDR = 0;
#endif
}
/*
Set RS=RW=0 and write the CMD_INIT command to the LCD data bus. Note that an appropriate
pause must follow before sending new commands to the LCD using writeLCD*_ functions.
*/
static inline void softwareLCDInitPulse(void) {
LCD_RS_PORT &= ~(1 << LCD_RS); // RS=0
LCD_RW_PORT &= ~(1 << LCD_RW); // RW=0
#ifdef FOUR_BIT_MODE
writeLCDDBusNibble_(CMD_INIT);
#else
writeLCDDBusByte_(CMD_INIT);
#endif
}
//---------------------------------------------------------------------------------------------
// Library function definitions
/*
Do software initialization as specified by the datasheet
*/
void initLCD(void) {
// Set LCD_RS, LCD_RW and LCD_ENABLE as outputs
LCD_RS_DDR |= (1 << LCD_RS);
LCD_RW_DDR |= (1 << LCD_RW);
LCD_ENABLE_DDR |= (1 << LCD_ENABLE);
setLCDDBusAsOutputs();
_delay_us(LCD_INIT_DELAY0); // Wait minimum 15ms as per datasheet
softwareLCDInitPulse();
_delay_us(LCD_INIT_DELAY1); // Wait minimum 4.1ms as per datasheet
softwareLCDInitPulse();
_delay_us(LCD_INIT_DELAY2); // Wait minimum 100us as per datasheet
softwareLCDInitPulse();
#if defined (FOUR_BIT_MODE)
// Function Set (4-bit interface)
writeLCDDBusNibble_(CMD_INIT_FOUR_BIT);
writeLCDInstr_(CMD_INIT_FOUR_BIT | LCD_LINES | LCD_FONT);
#else
// Function set (8-bit interface)
writeLCDInstr_(INSTR_FUNC_SET | (1 << INSTR_FUNC_SET_DL) | LCD_LINES | LCD_FONT);
#endif
/* BF now can be checked */
// Set functions of LCD
writeLCDInstr(INSTR_DISPLAY); // Display off
// Clear display
writeLCDInstr(CMD_CLEAR_DISPLAY);
// Increment mode, no shift
writeLCDInstr(INSTR_ENTRY_SET | (1 << INSTR_ENTRY_SET_ID));
// Display on, cursor on, blink off
lcdState = (1 << INSTR_DISPLAY_D) | (1 << INSTR_DISPLAY_C);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
/*
Given a single character, checks whether its a ASCII escape and does the following:
- Newline '\n': moves the cursor to the next physical line of the LCD display; if the cursor is on
the last line of the display, clears the display and positions the cursor at the top left
of the LCD
- Carriage return '\r': moves the cursor to the beginning of the current line
- Backspace '\b': moves the cursor one position backwards, wrapping to the end of the
previous line when at the beginning of a line (other then the first one). A space is then
inserted to replace the character at point, without moving the cursor. When the cursor is
at the beginning of the first line, does nothing.
- Form feed '\f': clears the LCD display and places the cursor at the beginning of the first line.
- Alarm '\a': ignored
Any other character is sent to the LCD display using writeCharToLCD_.
*/
void writeCharToLCD(char c) {
switch (c) {
case '\n': // Line feed
if (currentLineNum == LCD_NUMBER_OF_LINES - 1) {
scrollUp(1);
currentLineChars = 0;
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[currentLineNum]);
} else {
currentLineChars = 0;
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[++currentLineNum]);
}
break;
case '\a': // Alarm
break;
case '\b': // Backspace (non-destructive)
if (currentLineChars == 0 && currentLineNum == 0) {
// At first line, first column; there is no where to move; do nothing
break;
} else if (currentLineChars == 0) {
// At beginning of line, need to move the end of previous line
currentLineChars = LCD_CHARACTERS_PER_LINE - 1;
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[--currentLineNum] + currentLineChars));
} else {
// OK, simply go back one character
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + --currentLineChars));
}
break;
case '\r': // Carriage return
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[currentLineNum]);
currentLineChars = 0;
break;
case '\f': // Form feed
clearDisplay();
break;
default: // Printable character
if (currentLineChars == LCD_CHARACTERS_PER_LINE - 1 && currentLineNum == LCD_NUMBER_OF_LINES - 1) {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
writeCharToLCD_(c);
scrollUp(1);
currentLineChars = 0;
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[currentLineNum]);
} else if (currentLineChars == LCD_CHARACTERS_PER_LINE - 1) {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
writeCharToLCD_(c);
currentLineChars = 0;
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[++currentLineNum]);
} else {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
writeCharToLCD_(c);
currentLineChars++;
}
}
}
void writeStringToLCD(char* str) {
while (*str != '\0') {
#ifdef LCD_ANSI_ESCAPE_ENABLE
// Check for ANSI CSI (Control Sequence Introducer)
if (*str == '\e') {
if (*(++str) != '\0' && *str == '[') {
char* str_ref = ++str;
switch (*str) {
case 's': // SCP - Save cursor position
saveCursorPosition();
return;
case 'u': // RCP - Restore cursor position
restoreCursorPosition();
return;
case '?': // DECTCEM
if (*(++str_ref) != '\0' && *str_ref == '2') {
if (*(++str_ref) != '\0' && *str_ref == '5') {
if (*(++str_ref) != '\0') {
if (*str_ref == 'l') {
hideCursor();
} else if (*str_ref == 'h') {
showCursor();
} else {
// Invalid escape
}
} // Invalid escape (early termination)
} // Invalid escape
} // Invalid escape
return;
default:
break;
}
// Read optional variable length number in ASCII (0x30 - 0x3f) where 0x3a - 0x3f are
// ignored (they are used as flags by some terminals)
uint8_t fnd0;
uint8_t num0 = readASCIINumber(str, &fnd0, &str);
// Read optional (semicolon followed by optional variable length number)
uint8_t fnd1;
uint8_t num1;
if (*str != '\0' && *str == ';') {
num1 = readASCIINumber(++str, &fnd1, &str);
// Read control character (between 0x40 - 0x7e) for two argument sequences
switch (*str) {
case 'f': // HVP - Horizontal and vertical position
case 'H': // CUP - Cursor position
num0 = fnd0 ? num0 : 1;
num1 = fnd1 ? num1 : 1;
setCursorPosition(num0, num1);
break;
default: // Invalid control character
break;
}
} else if (*str != '\0') {
// Read control character (between 0x40 - 0x7e) for single argument sequences
switch (*str) {
case 'A': // CUU - Cursor up
num0 = fnd0 ? num0 : 1;
moveCursorUp(num0);
break;
case 'B': // CUD - Cursor down
num0 = fnd0 ? num0 : 1;
moveCursorDown(num0);
break;
case 'C': // CUF - Cursor forward
num0 = fnd0 ? num0 : 1;
moveCursorForward(num0);
break;
case 'D': // CUB - Cursor back
num0 = fnd0 ? num0 : 1;
moveCursorBackward(num0);
break;
case 'E': // CNL - Cursor next line
num0 = fnd0 ? num0 : 1;
moveCursorNextLine(num0);
break;
case 'F': // CPL - Cursor previous line
num0 = fnd0 ? num0 : 1;
moveCursorPreviousLine(num0);
break;
case 'G': // CHA - Cursor horizontal absolute
num0 = fnd0 ? num0 : 1;
moveCursorToColumn(num0);
break;
case 'J': // ED - Erase display
num0 = fnd0 ? num0 : 1;
eraseDisplay(num0);
break;
case 'K': // EL - Erase in line
num0 = fnd0 ? num0 : 1;
eraseInline(num0);
break;
case 'S': // SU - Scroll up
num0 = fnd0 ? num0 : 1;
scrollUp(num0);
break;
case 'T': // SD Scroll down
num0 = fnd0 ? num0 : 1;
scrollDown(num0);
break;
case 'm': // SGR - Select graphic rendition (single optional argument)
break;
case ';': // SGR - Select graphic rendition (multiple arguments)
if (fnd0) {
while (fnd0) {
readASCIINumber(++str, &fnd0, &str);
if (fnd0) {
if (*str == 'm') {
break; // Valid SGR
} else if (*str == ';') {
continue; // More SGR parameters yet
} else {
break; // Invalid escape
}
} else {
// Invalid escape; expected SGR parameter
}
}
} else {
// Invalid escape; expected first SGR parameter but none given
}
break;
case 'n': // DSR - Device status report
if (fnd0 && num0 == 6) {
// Valid DSR
} else {
// Invalid DSR
}
break;
default: // Invalid control character
writeCharToLCD(*str);
break;
}
} else {
return; // Invalid escape sequence (terminated early)
}
}
} else {
writeCharToLCD(*str);
}
str++;
#else
writeCharToLCD(*(str++));
#endif
}
}
//---------------------------------------------------------------------------------------------
// LCD command functions (all have associated ANSI escape)
/*
Writes the CMD_CLEAR_DISPLAY command to the LCD using writeLCDINSTR, and clears the local
char and line counters.
*/
void clearDisplay(void) {
writeLCDInstr(CMD_CLEAR_DISPLAY);
// Reset line and char number tracking
currentLineNum = 0;
currentLineChars = 0;
}
/*
Writes the CMD_RETURN_HOME command to the LCD using writeLCDInstr, and clears the local char
and line counters.
*/
void returnHome(void) {
writeLCDInstr(CMD_RETURN_HOME);
// Reset line and char number tracking
currentLineNum = 0;
currentLineChars = 0;
}
void getCursorPosition(uint8_t* row, uint8_t* column) {
*row = currentLineNum + 1;
*column = currentLineChars + 1;
}
void setCursorPosition(uint8_t row, uint8_t column) {
// Set currentLineNum and currentLineChars
currentLineNum = row ? row - 1 : 0;
currentLineChars = column ? column - 1 : 0;
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorUp(uint8_t n) {
if (n < currentLineNum + 1) {
currentLineNum -= n;
} else {
currentLineNum = 0;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorDown(uint8_t n) {
if (n + currentLineNum < LCD_NUMBER_OF_LINES) {
currentLineNum += n;
} else {
currentLineNum = LCD_NUMBER_OF_LINES - 1;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorForward(uint8_t n) {
if (n + currentLineChars < LCD_CHARACTERS_PER_LINE) {
currentLineChars += n;
} else {
currentLineChars = LCD_CHARACTERS_PER_LINE - 1;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorBackward(uint8_t n) {
if (n < currentLineChars + 1) {
currentLineChars -= n;
} else {
currentLineChars = 0;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorNextLine(uint8_t n) {
currentLineChars = 0;
if (n + currentLineNum < LCD_NUMBER_OF_LINES) {
currentLineNum += n;
} else {
currentLineNum = LCD_NUMBER_OF_LINES - 1;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorPreviousLine(uint8_t n) {
currentLineChars = 0;
if (n < currentLineNum + 1) {
currentLineNum -= n;
} else {
currentLineNum = 0;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorToColumn(uint8_t n) {
if (n <= LCD_CHARACTERS_PER_LINE) {
currentLineChars = n ? n - 1 : 0;
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
} // else index out of range (off screen column)
}
void eraseDisplay(uint8_t n) {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
switch (n) {
case 0: // Clear from cursor to end of screen
{
uint8_t len = (LCD_NUMBER_OF_LINES - old_row)*LCD_CHARACTERS_PER_LINE + (LCD_CHARACTERS_PER_LINE - old_column);
for (uint8_t i = 0; i < len; i++)
writeCharToLCD(' ');
// Write last char without scrolling
loop_until_LCD_BF_clear();
writeCharToLCD_(' ');
break;
}
case 1: // Clear from cursor to beginning of screen
{
uint8_t len = (old_row - 1)*LCD_CHARACTERS_PER_LINE + old_column;
returnHome();
for (uint8_t i = 0; i < len; i++)
writeCharToLCD(' ');
// Write last char without scrolling
loop_until_LCD_BF_clear();
writeCharToLCD_(' ');
break;
}
case 2: // Clear entire screen
clearDisplay();
break;
default: // Invalid argument; do nothing
break;
}
setCursorPosition(old_row, old_column);
}
void eraseInline(uint8_t n) {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
switch (n) {
case 0: // Clear from cursor to end of line
for (uint8_t i = old_column; i <= LCD_CHARACTERS_PER_LINE; i++)
writeCharToLCD(' ');
break;
case 1: // Clear from cursor to beginning of line
setCursorPosition(old_row, 1);
for (uint8_t i = 1; i <= old_column; i++)
writeCharToLCD(' ');
break;
case 2: // Clear entire line
setCursorPosition(old_row, 1);
for (uint8_t i = 1; i <= LCD_CHARACTERS_PER_LINE; i++) {
loop_until_LCD_BF_clear();
writeCharToLCD_(' ');
}
break;
default: // Invalid argument; do nothing
return;
}
setCursorPosition(old_row, old_column);
}
void scrollUp(uint8_t n) {
#if LCD_NUMBER_OF_LINES == 1
clearDisplay();
#else
if (n >= LCD_NUMBER_OF_LINES) {
clearDisplay();
} else {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
uint8_t len = (LCD_NUMBER_OF_LINES - n)*LCD_CHARACTERS_PER_LINE + 1;
char str[len];
readCharsFromLCD(n + 1, 1, LCD_NUMBER_OF_LINES, LCD_CHARACTERS_PER_LINE, str, len);
setCursorPosition(1, 1); // returnHome();
writeStringToLCD(str);
// Add n newlines to bottom of screen
for (uint8_t i = 0; i < n; i++) {
setCursorPosition(LCD_NUMBER_OF_LINES - i, 1);
eraseInline(2);
}
setCursorPosition(old_row, old_column);
}
#endif
}
void scrollDown(uint8_t n) {
#if LCD_NUMBER_OF_LINES == 1
clearDisplay();
#else
if (n >= LCD_NUMBER_OF_LINES) {
clearDisplay();
} else {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
uint8_t len = (LCD_NUMBER_OF_LINES - n)*LCD_CHARACTERS_PER_LINE + 1;
char str[len];
readCharsFromLCD(1, 1, LCD_NUMBER_OF_LINES - n, LCD_CHARACTERS_PER_LINE, str, len);
for (uint8_t column = n + 1, i = 0; column <= LCD_NUMBER_OF_LINES; column++) {
setCursorPosition(column , 1);
for (uint8_t row = 1; row <= LCD_CHARACTERS_PER_LINE; row++) {
loop_until_LCD_BF_clear();
writeCharToLCD_(str[i++]);
}
}
// Add n newlines to top of screen
for (uint8_t i = 1; i <= n; i++) {
setCursorPosition(i, 1);
eraseInline(2);
}
setCursorPosition(old_row, old_column);
}
#endif
}
void saveCursorPosition() {
saveCursorLineNum = currentLineNum;
saveCursorLineChars = currentLineChars;
}
void restoreCursorPosition() {
currentLineNum = saveCursorLineNum;
currentLineChars = saveCursorLineChars;
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void hideCursor(void) {
lcdState &= ~(1 << INSTR_DISPLAY_C);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
void showCursor(void) {
lcdState |= (1 << INSTR_DISPLAY_C);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
//-----------------------------------------------------------------------------------------------
// Utility functions (with no associated ASCII or ANSI escape)
void blinkCursorOff(void) {
lcdState &= ~(1 << INSTR_DISPLAY_B);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
void blinkCursorOn(void) {
lcdState |= (1 << INSTR_DISPLAY_B);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
void displayOff(void) {
lcdState &= ~(1 << INSTR_DISPLAY_D);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
void displayOn(void) {
lcdState |= (1 << INSTR_DISPLAY_D);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
//-----------------------------------------------------------------------------------------------
char readCharFromLCD(uint8_t row, uint8_t column) {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
setCursorPosition(row, column);
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
setLCDDBusAsInputs();
char c = readLCDDBusByte_();
setLCDDBusAsOutputs();
setCursorPosition(old_row, old_column);
return c;
}
void readLCDLine(uint8_t i, char* str) {
readCharsFromLCD(i, 1, i, LCD_CHARACTERS_PER_LINE, str, LCD_CHARACTERS_PER_LINE + 1);
}
//---------------------------------------------------------------------------------------------
// Advanced functions for special cases
void readCharsFromLCD(uint8_t from_row, uint8_t from_column, uint8_t to_row, uint8_t to_column, char* str, uint8_t len) {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
setCursorPosition(from_row, from_column);
for (uint8_t i = 0; i < len - 1 && from_row <= to_row; i++) {
if (from_row == LCD_NUMBER_OF_LINES && from_column == LCD_CHARACTERS_PER_LINE) {
// Last character on screen
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
setLCDDBusAsInputs();
*(str++) = readLCDDBusByte_();
setLCDDBusAsOutputs();
} else if (from_column == LCD_CHARACTERS_PER_LINE) { // End of line (but not last one)
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
setLCDDBusAsInputs();
*(str++) = readLCDDBusByte_();
setLCDDBusAsOutputs();
from_row += 1;
from_column = 1;
setCursorPosition(from_row, from_column);
} else {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
setLCDDBusAsInputs();
*(str++) = readLCDDBusByte_();
setLCDDBusAsOutputs();
from_column++;
}
}
// Ensure array is terminated with null character
*str = '\0';
setCursorPosition(old_row, old_column);
setLCDDBusAsOutputs();
}
/*
Initialize LCD using the internal reset circuitry.
Note: This currently only works with 8 bit modes, but is not recommended. Instead use the
initLCD function which uses the software initialization method and works for 8-bit
modes as well the 4-bit mode.
*/
void initLCDByInternalReset(void) {
setLCDDBusAsOutputs();
// Function set (8-bit interface; 2 lines with 5x7 dot character font)
writeLCDInstr_(INSTR_FUNC_SET | (1 << INSTR_FUNC_SET_DL) | (1 << INSTR_FUNC_SET_N));
writeLCDInstr_(0x0F);
writeLCDInstr_(0x06);
writeLCDInstr_(CMD_CLEAR_DISPLAY);
_delay_ms(LCD_CLEAR_DISPLAY_DELAY);
}

View File

@ -1,328 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lcdLib.h
* @author Collin J. Doering <collin.doering@rekahsoft.ca>
* @date Sep 29, 2015
* @brief Functions to initialize, and operate a character LCD.
*/
#ifndef LCD_LIB_H
#define LCD_LIB_H
// Includes -----------------------------------------------------------------------------------
#include "lcd_instr.h"
#include "lcdLibConfig.h"
//---------------------------------------------------------------------------------------------
// Library function declarations
/**
Initialize the LCD display via software initialization as specified by the datasheet.
*/
void initLCD(void);
/**
Writes a character to the LCD display at the current cursor position after the LCD display is
ready for new data. Allows the following ASCII escapes: '\n', '\r', '\f' and '\b'; ignores
ASCII escape '\a'.
*/
void writeCharToLCD(char);
/**
Writes a string to the LCD starting from the current cursor position.
*/
void writeStringToLCD(char*);
//---------------------------------------------------------------------------------------------
// LCD command functions (all have associated ANSI escape)
/**
Clears the display and positions the cursor in the top left of the LCD screen.
*/
void clearDisplay(void);
/**
Brings the cursor the the top left of the LCD screen.
*/
void returnHome(void);
/**
Gets the current row and column of the LCD cursor and sets given pointers row and column to
their respective values. Note indexes start at 1.
*/
void getCursorPosition(uint8_t* row, uint8_t* column);
/**
Using the given parameters row and column, sets the current row and column occupied by the LCD
cursor. Note indexes start at 1.
*/
void setCursorPosition(uint8_t row, uint8_t column);
/**
Moves the cursor n positions up. If the cursor is at the edge of the screen this has no effect.
*/
void moveCursorUp(uint8_t n);
/**
Moves the cursor n positions down. If the cursor is at the edge of the screen this has no effect.
*/
void moveCursorDown(uint8_t n);
/**
Moves the cursor n positions forward. If the cursor is at the edge of the screen this has no effect.
*/
void moveCursorForward(uint8_t n);
/**
Moves the cursor n positions backwards. If the cursor is at the edge of the screen this has no effect.
*/
void moveCursorBackward(uint8_t n);
/**
Moves the cursor to the beginning of the line n lines down.
*/
void moveCursorNextLine(uint8_t n);
/**
Moves the cursor to the beginning of the line n lines up.
*/
void moveCursorPreviousLine(uint8_t n);
/**
Moves the cursor to column n. If n is off screen go to the last line.
*/
void moveCursorToColumn(uint8_t n);
/**
Scroll whole page up by n lines. New lines are added at the bottom.
*/
void scrollUp(uint8_t n);
/**
Scroll whole page down by n lines. New lines are added at the top.
*/
void scrollDown(uint8_t n);
/**
Saves the cursors current position.
*/
void saveCursorPosition(void);
/**
Restores the last saved cursor position.
*/
void restoreCursorPosition(void);
/**
Clears part or all of screen dependent on the value of n:
0 or missing: clear from cursor to end of screen
1: clear from cursor to end of screen
2: clear entire screen
*/
void eraseDisplay(uint8_t n);
/**
Erases part of a line dependent on the value of n:
0 or missing: clear from cursor to end of the line
1: clear from cursor to beginning of the line
2: clear entire line
*/
void eraseInline(uint8_t n);
/**
Hides the cursor
*/
void hideCursor(void);
/**
Shows the cursor
*/
void showCursor(void);
//---------------------------------------------------------------------------------------------
// Utility functions (with no associated ASCII or ANSI escape)
/**
Turns the cursor blink off.
*/
void blinkCursorOff(void);
/**
Turns the cursor blink on.
*/
void blinkCursorOn(void);
/**
Turns the display off.
*/
void displayOff(void);
/**
Turns the display on.
*/
void displayOn(void);
//---------------------------------------------------------------------------------------------
/**
Read a single character from the row and column given (1 based) returning the cursor to
its previous original position.
*/
char readCharFromLCD(uint8_t row, uint8_t column);
/**
Read a line i (ones based) into str.
*/
void readLCDLine(uint8_t i, char* str);
//---------------------------------------------------------------------------------------------
// Advanced functions for special cases
/**
Read len characters from (from_row, from_column) to (to_row, to_column) returning the cursor
to its original position after the read. Upon success (not overflowing the screen), 0 is
returned; otherwise non zero will be returned. The str pointer will be updated with the
characters read from the screen. Even in the case of failure, str may be partially populated.
*/
void readCharsFromLCD(uint8_t from_row, uint8_t from_column, uint8_t to_row, uint8_t to_column, char* str, uint8_t len);
/**
Initialize the LCD display via its internal reset circuit.
Note: this is not the recommended way to initialize the LCD as it is unreliable and depends
on the power supply. Preferably the software initialization method should be used (via
the initLCD function).
*/
void initLCDByInternalReset(void);
//---------------------------------------------------------------------------------------------
// Mode and settings sanity check (preprocessor tests of lcdLibConfig.h)
//---------------------------------------------------------------------------------------------
#if !defined(LCD_CHARACTERS_PER_LINE)
#error "All modes require LCD_CHARACTERS_PER_LINE to be defined."
#elif !defined(LCD_NUMBER_OF_LINES)
#error "All modes require LCD_NUMBER_OF_LINES to be defined."
#elif !defined(LCD_LINE_BEGINNINGS)
#error "All modes require LCD_LINE_BEGINNINGS to be defined."
#else
#if LCD_NUMBER_OF_LINES == 1
#define LCD_LINES 0
#else
#define LCD_LINES (1 << INSTR_FUNC_SET_N)
#endif
#define LCD_CHARACTERS_PER_SCREEN (LCD_CHARACTERS_PER_LINE * LCD_NUMBER_OF_LINES)
#endif
#if !defined(LCD_FONT_5x8) &&\
!defined(LCD_FONT_5x10)
#error "All modes require LCD_FONT_5x8 or LCD_FONT_5x10 to be defined."
#elif defined(LCD_FONT_5x8) && \
defined(LCD_FONT_5x10)
#error "LCD_FONT_5x8 and LCD_FONT_5x10 are mutually exclusive. Choose one."
#elif defined(LCD_FONT_5x8)
#define LCD_FONT 0
#elif defined(LCD_FONT_5x10)
#define LCD_FONT (1 << INSTR_FUNC_SET_F)
#endif
#if !defined (LCD_RS) || \
!defined (LCD_RS_PORT) || \
!defined (LCD_RS_DDR) || \
!defined (LCD_RW) || \
!defined (LCD_RW_PORT) || \
!defined (LCD_RW_DDR) || \
!defined (LCD_ENABLE) || \
!defined (LCD_ENABLE_PORT) || \
!defined (LCD_ENABLE_DDR)
#error "All modes require LCD_RS[,_PORT,_DDR], LCD_RW[,_PORT,_DDR], and LCD_ENABLE[,_PORT,_DDR] be defined."
#endif
#if defined (EIGHT_BIT_ARBITRARY_PIN_MODE) && \
defined (FOUR_BIT_MODE)
#error "EIGHT_BIT_ARBITRARY_PIN_MODE and FOUR_BIT_MODE are mutually exclusive. Choose one."
#elif defined (EIGHT_BIT_ARBITRARY_PIN_MODE) || \
defined (FOUR_BIT_MODE)
// EIGHT_BIT_ARBITRARY_PIN_MODE specific requirements
#ifdef EIGHT_BIT_ARBITRARY_PIN_MODE
#if !defined (LCD_DBUS0) || \
!defined (LCD_DBUS0_PORT) || \
!defined (LCD_DBUS0_DDR) || \
!defined (LCD_DBUS0_PIN) || \
!defined (LCD_DBUS1) || \
!defined (LCD_DBUS1_PORT) || \
!defined (LCD_DBUS1_DDR) || \
!defined (LCD_DBUS1_PIN) || \
!defined (LCD_DBUS2) || \
!defined (LCD_DBUS2_PORT) || \
!defined (LCD_DBUS2_DDR) || \
!defined (LCD_DBUS2_PIN) || \
!defined (LCD_DBUS3) || \
!defined (LCD_DBUS3_PORT) || \
!defined (LCD_DBUS3_DDR) || \
!defined (LCD_DBUS3_PIN)
#error "EIGHT_BIT_ARBITRARY_PIN_MODE require that LCD_DBUS*[,_PORT,_DDR,_PIN] be defined."
#endif
#endif
// Requirements for EIGHT_BIT_ARBITRARY_PIN_MODE and FOUR_BIT_MODE
#if !defined (LCD_DBUS4) || \
!defined (LCD_DBUS4_PORT) || \
!defined (LCD_DBUS4_DDR) || \
!defined (LCD_DBUS4_PIN) || \
!defined (LCD_DBUS5) || \
!defined (LCD_DBUS5_PORT) || \
!defined (LCD_DBUS5_DDR) || \
!defined (LCD_DBUS5_PIN) || \
!defined (LCD_DBUS6) || \
!defined (LCD_DBUS6_PORT) || \
!defined (LCD_DBUS6_DDR) || \
!defined (LCD_DBUS6_PIN) || \
!defined (LCD_DBUS7) || \
!defined (LCD_DBUS7_PORT) || \
!defined (LCD_DBUS7_DDR) || \
!defined (LCD_DBUS7_PIN)
#error "Both EIGHT_BIT_ARBITRARY_PIN_MODE and FOUR_BIT_MODE require that LCD_DBUS*[,_PORT,_DDR,_PIN] be defined."
#endif
// Set LCD_BF automatically for both EIGHT_BIT_ARBITRARY_PIN_MODE and FOUR_BIT_MODE
#undef LCD_BF
#define LCD_BF LCD_DBUS7
#else
#if !defined (LCD_DBUS_PORT) || \
!defined (LCD_DBUS_DDR) || \
!defined (LCD_DBUS_PIN) || \
!defined (LCD_BF)
#error "Default mode requires that LCD_DBUS_[PORT,DDR,PIN] and LCD_BF be defined."
#endif
#undef LCD_DBUS7_PORT
#define LCD_DBUS7_PORT LCD_DBUS_PORT
#undef LCD_DBUS7_DDR
#define LCD_DBUS7_DDR LCD_DBUS_DDR
#undef LCD_DBUS7_PIN
#define LCD_DBUS7_PIN LCD_DBUS_PIN
#endif
#endif /* LCD_LIB_H */

View File

@ -1,156 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lcdLibConfig.h
* @author Collin J. Doering <collin.doering@rekahsoft.ca>
* @date Oct 4, 2015
* @brief Configuration file for lcdLib.h
*/
// Includes -------------------------------------------------------------------------------
#include <avr/io.h>
//------------------------------------------------------------------------------------------
/**
Usage
=====
Operates in 3 mutually exclusive modes:
1. Default Mode
8-bit mode that requires all its data bus lines be on the same PORT.
2. EIGHT_BIT_ARBITRARY_PIN_MODE
8-bit mode that allows the data bus lines to use any IO pin.
3. FOUR_BIT_MODE
4-bit mode that allows the data bus lines to use any IO pin.
*/
/*
Screen characteristics
*/
#define LCD_CHARACTERS_PER_LINE 20 ///< Number of characters per line of the LCD
#define LCD_NUMBER_OF_LINES 4 ///< Number of lines of the LCD
#define LCD_LINE_BEGINNINGS 0x00, \
0x40, \
0x14, \
0x54 ///< Memory locations for each physical line ordered 1 to LCD_NUMBER_OF_LINES
/* Which font to use (can only leave one uncommented) */
#define LCD_FONT_5x8
//#define LCD_FONT_5x10
/* Support ANSI escapes; comment to disable */
//#define LCD_ANSI_ESCAPE_ENABLE
/* Modes */
// Default mode: 8-bit data bus
// 8-bit mode with data bus on arbitrary pins
//#define EIGHT_BIT_ARBITRARY_PIN_MODE
// LCD in 4-bit mode (on arbitrary pins)
#define FOUR_BIT_MODE
/* All mode options */
#define LCD_RS PD2
#define LCD_RS_PORT PORTD
#define LCD_RS_DDR DDRD
#define LCD_RW PD3
#define LCD_RW_PORT PORTD
#define LCD_RW_DDR DDRD
#define LCD_ENABLE PD4
#define LCD_ENABLE_PORT PORTD
#define LCD_ENABLE_DDR DDRD
/*
Mode specific settings
*/
/* Default Mode */
// LCD data bus PORT, PIN and DDR.
#define LCD_DBUS_PORT PORTB
#define LCD_DBUS_DDR DDRB
#define LCD_DBUS_PIN PINB
// This must be set in default mode to the MSB of the data lines
#define LCD_BF PB7
/* EIGHT_BIT_ARBITRARY_PIN_MODE specific settings */
#define LCD_DBUS0 PB0
#define LCD_DBUS0_PORT PORTB
#define LCD_DBUS0_DDR DDRB
#define LCD_DBUS0_PIN PINB
#define LCD_DBUS1 PB1
#define LCD_DBUS1_PORT PORTB
#define LCD_DBUS1_DDR DDRB
#define LCD_DBUS1_PIN PINB
#define LCD_DBUS2 PB2
#define LCD_DBUS2_PORT PORTB
#define LCD_DBUS2_DDR DDRB
#define LCD_DBUS2_PIN PINB
#define LCD_DBUS3 PB3
#define LCD_DBUS3_PORT PORTB
#define LCD_DBUS3_DDR DDRB
#define LCD_DBUS3_PIN PINB
/* FOUR_BIT_MODE and EIGHT_BIT_ARBITRARY_PIN_MODE shared settings */
#define LCD_DBUS4 PB4
#define LCD_DBUS4_PORT PORTB
#define LCD_DBUS4_DDR DDRB
#define LCD_DBUS4_PIN PINB
#define LCD_DBUS5 PB5
#define LCD_DBUS5_PORT PORTB
#define LCD_DBUS5_DDR DDRB
#define LCD_DBUS5_PIN PINB
#define LCD_DBUS6 PB6
#define LCD_DBUS6_PORT PORTB
#define LCD_DBUS6_DDR DDRB
#define LCD_DBUS6_PIN PINB
#define LCD_DBUS7 PB7
#define LCD_DBUS7_PORT PORTB
#define LCD_DBUS7_DDR DDRB
#define LCD_DBUS7_PIN PINB
/* LCD delays (in microseconds when unspecified) */
#define LCD_ENABLE_HIGH_DELAY 25
#define LCD_ENABLE_LOW_DELAY 25
#define LCD_INIT_DELAY0 15000
#define LCD_INIT_DELAY1 8200
#define LCD_INIT_DELAY2 200
#define LCD_CLEAR_DISPLAY_DELAY 16000
#define LCD_RETURN_HOME_DELAY 16000
#define LCD_GENERIC_INSTR_DELAY 50

View File

@ -1,61 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lcd_instr.h
* @author Collin J. Doering <collin.doering@rekahsoft.ca>
* @date Oct 26, 2015
* @brief Constant macro definitions of HD44780 compatible character LCD instruction set
*/
/*
LCD character display instructions
*/
// Simple instructions with no options
#define CMD_INIT 0x30
#define CMD_INIT_FOUR_BIT 0x20
#define CMD_CLEAR_DISPLAY 0x01
#define CMD_RETURN_HOME 0x02
// Entry Set instruction and associated options
#define INSTR_ENTRY_SET 0x04
#define INSTR_ENTRY_SET_ID 1
#define INSTR_ENTRY_SET_S 0
// Display control instruction and associated options
#define INSTR_DISPLAY 0x08
#define INSTR_DISPLAY_D 2
#define INSTR_DISPLAY_C 1
#define INSTR_DISPLAY_B 0
// Cursor or display shift instruction and associated options
#define INSTR_MOV_SHIFT 0x10
#define INSTR_MOV_SHIFT_SC 3
#define INSTR_MOV_SHIFT_RL 2
// Function set instruction and associated options
#define INSTR_FUNC_SET 0x20
#define INSTR_FUNC_SET_DL 4
#define INSTR_FUNC_SET_N 3
#define INSTR_FUNC_SET_F 2
// Set CG RAM address instruction
#define INSTR_CGRAM_ADDR 0x60
// Set DD RAM address instruction
#define INSTR_DDRAM_ADDR 0x80

View File

@ -1,197 +0,0 @@
##########------------------------------------------------------##########
########## Project-specific Details ##########
########## Check these every time you start a new project ##########
##########------------------------------------------------------##########
MCU = atmega328p
F_CPU = 8000000UL
BAUD = 9600
## Also try BAUD = 19200 or 38400 if you're feeling lucky.
## A directory for common include files and the simple USART library.
## If you move either the current folder or the Library folder, you'll
## need to change this path to match.
#LIBDIR = ../../AVR-Programming-Library
##########------------------------------------------------------##########
########## Programmer Defaults ##########
########## Set up once, then forget about it ##########
########## (Can override. See bottom of file.) ##########
##########------------------------------------------------------##########
PROGRAMMER_TYPE = avrisp
# extra arguments to avrdude: baud rate, chip type, -F flag, etc.
PROGRAMMER_ARGS = -b 19200 -P /dev/ttyUSB0
##########------------------------------------------------------##########
########## Program Locations ##########
########## Won't need to change if they're in your PATH ##########
##########------------------------------------------------------##########
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
AVRSIZE = avr-size
AVRDUDE = avrdude
##########------------------------------------------------------##########
########## Makefile Magic! ##########
########## Summary: ##########
########## We want a .hex file ##########
########## Compile source files into .elf ##########
########## Convert .elf file into .hex ##########
########## You shouldn't need to edit below. ##########
##########------------------------------------------------------##########
## The name of your project (without the .c)
# TARGET = blinkLED
## Or name it automatically after the enclosing directory
TARGET = $(lastword $(subst /, ,$(CURDIR)))
# Object files: will find all .c/.h files in current directory
# and in LIBDIR. If you have any other (sub-)directories with code,
# you can add them in to SOURCES below in the wildcard statement.
SOURCES=$(wildcard *.c $(LIBDIR)/*.c)
OBJECTS=$(SOURCES:.c=.o)
HEADERS=$(SOURCES:.c=.h)
## Compilation options, type man avr-gcc if you're curious.
# Removed "-I$(LIBDIR)" from end of CPPFLAGS
CPPFLAGS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -I.
CFLAGS = -Os -g -std=gnu99 -Wall
## Use short (8-bit) data types
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
## Splits up object files per function
CFLAGS += -ffunction-sections -fdata-sections
LDFLAGS = -Wl,-Map,$(TARGET).map
## Optional, but often ends up with smaller code
LDFLAGS += -Wl,--gc-sections
## Relax shrinks code even more, but makes disassembly messy
## LDFLAGS += -Wl,--relax
## LDFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf
## LDFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf
TARGET_ARCH = -mmcu=$(MCU)
## Explicit pattern rules:
## To make .o files from .c files
%.o: %.c $(HEADERS) Makefile
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<;
$(TARGET).elf: $(OBJECTS)
$(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@
%.hex: %.elf
$(OBJCOPY) -j .text -j .data -O ihex $< $@
%.eeprom: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
%.lst: %.elf
$(OBJDUMP) -S $< > $@
## These targets don't have files named after them
.PHONY: all disassemble disasm eeprom size clean squeaky_clean flash fuses
all: $(TARGET).hex
debug:
@echo
@echo "Source files:" $(SOURCES)
@echo "MCU, F_CPU, BAUD:" $(MCU), $(F_CPU), $(BAUD)
@echo
# Optionally create listing file from .elf
# This creates approximate assembly-language equivalent of your code.
# Useful for debugging time-sensitive bits,
# or making sure the compiler does what you want.
disassemble: $(TARGET).lst
disasm: disassemble
# Optionally show how big the resulting program is
size: $(TARGET).elf
$(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf
clean:
rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \
$(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \
$(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \
$(TARGET).eeprom
squeaky_clean:
rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~ *.eeprom
##########------------------------------------------------------##########
########## Programmer-specific details ##########
########## Flashing code to AVR using avrdude ##########
##########------------------------------------------------------##########
flash: $(TARGET).hex
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$<
## An alias
program: flash
flash_eeprom: $(TARGET).eeprom
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$<
avrdude_terminal:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt
## If you've got multiple programmers that you use,
## you can define them here so that it's easy to switch.
## To invoke, use something like `make flash_arduinoISP`
flash_usbtiny: PROGRAMMER_TYPE = usbtiny
flash_usbtiny: PROGRAMMER_ARGS = # USBTiny works with no further arguments
flash_usbtiny: flash
flash_usbasp: PROGRAMMER_TYPE = usbasp
flash_usbasp: PROGRAMMER_ARGS = # USBasp works with no further arguments
flash_usbasp: flash
flash_arduinoISP: PROGRAMMER_TYPE = avrisp
flash_arduinoISP: PROGRAMMER_ARGS = -b 19200 -P /dev/ttyACM0
## (for windows) flash_arduinoISP: PROGRAMMER_ARGS = -b 19200 -P com5
flash_arduinoISP: flash
flash_109: PROGRAMMER_TYPE = avr109
flash_109: PROGRAMMER_ARGS = -b 9600 -P /dev/ttyUSB0
flash_109: flash
##########------------------------------------------------------##########
########## Fuse settings and suitable defaults ##########
##########------------------------------------------------------##########
## Mega 48, 88, 168, 328 default values
LFUSE = 0x62
HFUSE = 0xdf
EFUSE = 0x00
## Generic
FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \
$(PROGRAMMER_ARGS) $(FUSE_STRING)
show_fuses:
$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv
## Called with no extra definitions, sets to defaults
set_default_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
set_default_fuses: fuses
## Set the fuse byte for full-speed mode
## Note: can also be set in firmware for modern chips
set_fast_fuse: LFUSE = 0xE2
set_fast_fuse: FUSE_STRING = -U lfuse:w:$(LFUSE):m
set_fast_fuse: fuses
## Set the EESAVE fuse byte to preserve EEPROM across flashes
set_eeprom_save_fuse: HFUSE = 0xD7
set_eeprom_save_fuse: FUSE_STRING = -U hfuse:w:$(HFUSE):m
set_eeprom_save_fuse: fuses
## Clear the EESAVE fuse byte
clear_eeprom_save_fuse: FUSE_STRING = -U hfuse:w:$(HFUSE):m
clear_eeprom_save_fuse: fuses

View File

@ -1,62 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* File: USART.c
* Author: Collin J. Doering <collin.doering@rekahsoft.ca>
* Date: Oct 6, 2015
*/
#include <avr/io.h>
#include "USART.h"
#include <util/setbaud.h>
#ifndef BAUD /* if not defined in Makefile... */
#define BAUD 9600 /* set a safe default baud rate */
#endif
void initUSART(void) { /* requires BAUD */
UBRR0H = UBRRH_VALUE; /* defined in setbaud.h */
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
/* Enable USART transmitter/receiver */
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); /* 8 data bits, 1 stop bit */
}
void transmitByte(uint8_t data) {
/* Wait for empty transmit buffer */
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = data; /* send data */
}
void transmitString(const char* data) {
while (*data != '\0') {
transmitByte(*data);
data++;
}
}
uint8_t receiveByte(void) {
loop_until_bit_is_set(UCSR0A, RXC0); /* Wait for incoming data */
return UDR0; /* return register value */
}

View File

@ -1,43 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file USART.h
* @author Collin J. Doering <collin.doering@rekahsoft.ca>
* @date Oct 6, 2015
* @brief Functions to initialize, read and write using USART.
*/
/**
Initialize USART hardware.
*/
void initUSART(void);
/**
Transmit a single byte using USART.
*/
void transmitByte(uint8_t data);
/**
Transmit a string using USART.
*/
void transmitString(const char* data);
/**
Receive a single byte using USART
*/
uint8_t receiveByte(void);

View File

@ -1,56 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file ansi_escapes.h
* @author Collin J. Doering <collin.doering@rekahsoft.ca>
* @date Oct 9, 2015
* @brief Macros to ease writing ANSI escapes.
*/
#define CSI "\e[" ///< Control Sequence Introducer
#define CUU(n) CSI #n "A" ///< Cursor up
#define CUD(n) CSI #n "B" ///< Cursor down
#define CUF(n) CSI #n "C" ///< Cursor forward
#define CUB(n) CSI #n "D" ///< Cursor backward
#define CNL(n) CSI #n "E" ///< Cursor next line
#define CPL(n) CSI #n "F" ///< Cursor previous line
#define CHA(n) CSI #n "G" ///< Cursor horizontal absolute
#define CUP(n,m) CSI #n ";" #m "H" ///< Cursor position
#define ED(n) CSI #n "J" ///< Erase display
#define EL(n) CSI #n "K" ///< Erase in line
#define SU(n) CSI #n "S" ///< Scroll up
#define SD(n) CSI #n "T" ///< Scroll down
#define HVP(n,m) CSI #n ";" #m "f" ///< Horizontal and vertical position
// #define SGR(n,m) CSI #n #m ///< Select graphic rendition
#define AUX_ON CSI "5i" ///< AUX port on
#define AUX_OFF CSI "4i" ///< AUX port off
// #define DSR CSI "6 n" ///< Device status report
#define SCP CSI "s" ///< Save cursor position
#define RCP CSI "u" ///< Restore cursor position
#define HIDE_CURSOR CSI "?25l" ///< DECTCEM: hide cursor
#define SHOW_CURSOR CSI "?25h" ///< DECTCEM: show cursor

View File

@ -1,969 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* File: lcdLib.c
* Author: Collin J. Doering <collin.doering@rekahsoft.ca>
* Date: Sep 29, 2015
*/
// Includes -----------------------------------------------------------------------------------
#include <math.h>
#include <avr/io.h>
#include <util/delay.h>
#include "lcdLib.h"
//---------------------------------------------------------------------------------------------
// Static global variables
static volatile uint8_t currentLineNum;
static volatile uint8_t currentLineChars;
static volatile uint8_t saveCursorLineNum;
static volatile uint8_t saveCursorLineChars;
static volatile uint8_t lcdState;
static const uint8_t lineBeginnings[LCD_NUMBER_OF_LINES] = { LCD_LINE_BEGINNINGS };
//---------------------------------------------------------------------------------------------
// Static functions
/*
Bring LCD_ENABLE line high, wait for LCD_ENABLE_HIGH_DELAY; then bring LCD_ENABLE line low
and wait for LCD_ENABLE_LOW_DELAY.
Note: LCD_ENABLE, LCD_ENABLE_HIGH_DELAY, and LCD_ENABLE_LOW_DELAY must be defined in lcdLibConfig.h
*/
static void clkLCD(void) {
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(LCD_ENABLE_HIGH_DELAY);
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(LCD_ENABLE_LOW_DELAY);
}
/*
Wait until LCD_BF (busy flag) is cleared (low).
*/
static void loop_until_LCD_BF_clear(void) {
uint8_t bf;
LCD_RS_PORT &= ~(1 << LCD_RS); // RS=0
LCD_RW_PORT |= (1 << LCD_RW); // RW=1
// Set LCD_BF as input
LCD_DBUS7_DDR &= ~(1 << LCD_BF);
do {
bf = 0;
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(1); // 'delay data time' and 'enable pulse width'
bf |= (LCD_DBUS7_PIN & (1 << LCD_BF));
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(1); // 'address hold time', 'data hold time' and 'enable cycle width'
#ifdef FOUR_BIT_MODE
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(1); // 'delay data time' and 'enable pulse width'
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(1); // 'address hold time', 'data hold time' and 'enable cycle width'
#endif
} while (bf);
#if defined (FOUR_BIT_MODE) || defined (EIGHT_BIT_ARBITRARY_PIN_MODE)
LCD_DBUS7_DDR |= (1 << LCD_DBUS7);
LCD_DBUS6_DDR |= (1 << LCD_DBUS6);
LCD_DBUS5_DDR |= (1 << LCD_DBUS5);
LCD_DBUS4_DDR |= (1 << LCD_DBUS4);
#ifdef EIGHT_BIT_ARBITRARY_PIN_MODE
LCD_DBUS3_DDR |= (1 << LCD_DBUS3);
LCD_DBUS2_DDR |= (1 << LCD_DBUS2);
LCD_DBUS1_DDR |= (1 << LCD_DBUS1);
LCD_DBUS0_DDR |= (1 << LCD_DBUS0);
#endif
#else
LCD_DBUS_DDR = 0xff; // Reset all LCD_DBUS_PORT pins as outputs
#endif
}
/*
Given a 8 bit integer, writes the four MSB's (one nibble) to the LCD data bus.
Note: this is only defined in FOUR_BIT_MODE
*/
#ifdef FOUR_BIT_MODE
static void writeLCDDBusNibble_(uint8_t b) {
// Reset data lines to zeros
LCD_DBUS7_PORT &= ~(1 << LCD_DBUS7);
LCD_DBUS6_PORT &= ~(1 << LCD_DBUS6);
LCD_DBUS5_PORT &= ~(1 << LCD_DBUS5);
LCD_DBUS4_PORT &= ~(1 << LCD_DBUS4);
// Write 1's where appropriate on data lines
if (b & (1 << 7)) LCD_DBUS7_PORT |= (1 << LCD_DBUS7);
if (b & (1 << 6)) LCD_DBUS6_PORT |= (1 << LCD_DBUS6);
if (b & (1 << 5)) LCD_DBUS5_PORT |= (1 << LCD_DBUS5);
if (b & (1 << 4)) LCD_DBUS4_PORT |= (1 << LCD_DBUS4);
// Pulse the enable line
clkLCD();
}
#endif
/*
Given an 8 bit integer, writes it to the LCD data bus, regardless of its
configuration (default 8-bit mode, 8-bit arbitrary pin mode and 4-bit mode). In the default
8-bit mode and EIGHT_BIT_ARBITRARY_PIN_MODE, the given data is written in one cycle using the
writeLCDDBusByte_ function. In FOUR_BIT_MODE however, the given data is written in two cycles
using two successive calls to the writeLCDDBusNibble_ function.
This function does not ensure the LCD is ready to accept new data and thus needs to
be handled by the caller.
*/
static void writeLCDDBusByte_(uint8_t b) {
#ifdef FOUR_BIT_MODE
writeLCDDBusNibble_(b);
writeLCDDBusNibble_(b << 4);
#elif defined (EIGHT_BIT_ARBITRARY_PIN_MODE)
// Reset data lines to zeros
LCD_DBUS7_PORT &= ~(1 << LCD_DBUS7);
LCD_DBUS6_PORT &= ~(1 << LCD_DBUS6);
LCD_DBUS5_PORT &= ~(1 << LCD_DBUS5);
LCD_DBUS4_PORT &= ~(1 << LCD_DBUS4);
LCD_DBUS3_PORT &= ~(1 << LCD_DBUS3);
LCD_DBUS2_PORT &= ~(1 << LCD_DBUS2);
LCD_DBUS1_PORT &= ~(1 << LCD_DBUS1);
LCD_DBUS0_PORT &= ~(1 << LCD_DBUS0);
// Write 1's where appropriate on data lines
if (b & (1 << 7)) LCD_DBUS7_PORT |= (1 << LCD_DBUS7);
if (b & (1 << 6)) LCD_DBUS6_PORT |= (1 << LCD_DBUS6);
if (b & (1 << 5)) LCD_DBUS5_PORT |= (1 << LCD_DBUS5);
if (b & (1 << 4)) LCD_DBUS4_PORT |= (1 << LCD_DBUS4);
if (b & (1 << 3)) LCD_DBUS3_PORT |= (1 << LCD_DBUS3);
if (b & (1 << 2)) LCD_DBUS2_PORT |= (1 << LCD_DBUS2);
if (b & (1 << 1)) LCD_DBUS1_PORT |= (1 << LCD_DBUS1);
if (b & (1 << 0)) LCD_DBUS0_PORT |= (1 << LCD_DBUS0);
clkLCD();
#else
LCD_DBUS_PORT = b;
clkLCD();
#endif
}
/*
Given a 8 bit integer representing a LCD instruction, sends it to the LCD display.
Sets RS=RW=0 and writes the given 8 bit integer to the LCD databus.
Note that this function does not ensure the LCD is ready to accept a new instruction and thus
needs to be handled by the caller.
*/
static void writeLCDInstr_(uint8_t instr) {
LCD_RS_PORT &= ~(1 << LCD_RS); // RS=0
LCD_RW_PORT &= ~(1 << LCD_RW); // RW=0
writeLCDDBusByte_(instr);
}
/*
Given a 8 bit integer representing a LCD instruction, waits until the LCD is ready and sends
the instruction.
*/
static inline void writeLCDInstr(uint8_t instr) {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
writeLCDInstr_(instr);
}
/*
Sets RS=1, RW=0 and accepts a char (8 bit) and outputs it to the current cursor position of
the LCD. In the default 8-bit mode and EIGHT_BIT_ARBITRARY_PIN_MODE, the given data is
written in one cycle using the writeLCDDBusByte_ function. In FOUR_BIT_MODE however, the given
data is written in two cycles using two successive calls to the writeLCDDBusNibble_ function.
*/
static void writeCharToLCD_(char c) {
LCD_RS_PORT |= (1 << LCD_RS); // RS=1
LCD_RW_PORT &= ~(1 << LCD_RW); // RW=0
writeLCDDBusByte_(c);
}
static uint8_t readLCDDBusByte_(void) {
LCD_RS_PORT |= (1 << LCD_RS); // RS=1
LCD_RW_PORT |= (1 << LCD_RW); // RW=1
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(1); // 'delay data time' and 'enable pulse width'
// Read data
char c = 0;
#if defined(FOUR_BIT_MODE)
if (LCD_DBUS7_PIN & (1 << LCD_DBUS7)) c |= (1 << 7);
if (LCD_DBUS6_PIN & (1 << LCD_DBUS6)) c |= (1 << 6);
if (LCD_DBUS5_PIN & (1 << LCD_DBUS5)) c |= (1 << 5);
if (LCD_DBUS4_PIN & (1 << LCD_DBUS4)) c |= (1 << 4);
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(1); // 'address hold time', 'data hold time' and 'enable cycle width'
LCD_ENABLE_PORT |= (1 << LCD_ENABLE);
_delay_us(1); // 'delay data time' and 'enable pulse width'
if (LCD_DBUS7_PIN & (1 << LCD_DBUS7)) c |= (1 << 3);
if (LCD_DBUS6_PIN & (1 << LCD_DBUS6)) c |= (1 << 2);
if (LCD_DBUS5_PIN & (1 << LCD_DBUS5)) c |= (1 << 1);
if (LCD_DBUS4_PIN & (1 << LCD_DBUS4)) c |= (1 << 0);
#elif defined(EIGHT_BIT_ARBITRARY_PIN_MODE)
if (LCD_DBUS7_PIN & (1 << LCD_DBUS7)) c |= (1 << 7);
if (LCD_DBUS6_PIN & (1 << LCD_DBUS6)) c |= (1 << 6);
if (LCD_DBUS5_PIN & (1 << LCD_DBUS5)) c |= (1 << 5);
if (LCD_DBUS4_PIN & (1 << LCD_DBUS4)) c |= (1 << 4);
if (LCD_DBUS3_PIN & (1 << LCD_DBUS3)) c |= (1 << 3);
if (LCD_DBUS2_PIN & (1 << LCD_DBUS2)) c |= (1 << 2);
if (LCD_DBUS1_PIN & (1 << LCD_DBUS1)) c |= (1 << 1);
if (LCD_DBUS0_PIN & (1 << LCD_DBUS0)) c |= (1 << 0);
#else
c = LCD_DBUS_PIN;
#endif
LCD_ENABLE_PORT &= ~(1 << LCD_ENABLE);
_delay_us(1); // 'address hold time', 'data hold time' and 'enable cycle width'
return c;
}
/*
Given a character string, and a uint8_t pointer, reads the character string until a
non-numerical ASCII character, returning the integer representation of the number read. At
the end of the functions execution, the found_num uint8_t* will be updated to indicate how
many digits were read. The new_loc char** will be updated with the new parsing position in
the string.
*/
static uint8_t readASCIINumber(char* str, uint8_t* found_num, char** new_loc) {
uint8_t nums[3];
*found_num = 0;
while (*str != '\0' && *found_num < 3) {
if (*str >= 0x30 && *str <= 0x39) {
// Use *str as a number (specified in ASCII)
nums[(*found_num)++] = *str - 0x30;
} else {
break;
}
str++;
}
*new_loc = str;
uint8_t ret = 0;
uint8_t i = *found_num - 1;
for (uint8_t fnd = 0; fnd < *found_num; fnd++)
ret += nums[fnd] * pow(10, i--);
return ret;
}
/*
Set all pins of LCD_DBUS as outputs
*/
static inline void setLCDDBusAsOutputs(void) {
#if defined (FOUR_BIT_MODE) || defined (EIGHT_BIT_ARBITRARY_PIN_MODE)
LCD_DBUS7_DDR |= (1 << LCD_DBUS7);
LCD_DBUS6_DDR |= (1 << LCD_DBUS6);
LCD_DBUS5_DDR |= (1 << LCD_DBUS5);
LCD_DBUS4_DDR |= (1 << LCD_DBUS4);
#ifdef EIGHT_BIT_ARBITRARY_PIN_MODE
LCD_DBUS3_DDR |= (1 << LCD_DBUS3);
LCD_DBUS2_DDR |= (1 << LCD_DBUS2);
LCD_DBUS1_DDR |= (1 << LCD_DBUS1);
LCD_DBUS0_DDR |= (1 << LCD_DBUS0);
#endif
#else
LCD_DBUS_DDR = 0xff;
#endif
}
/*
Set all pins of LCD_DBUS as inputs (disabling their output)
*/
static inline void setLCDDBusAsInputs(void) {
#if defined (FOUR_BIT_MODE) || defined (EIGHT_BIT_ARBITRARY_PIN_MODE)
LCD_DBUS7_DDR &= ~(1 << LCD_DBUS7);
LCD_DBUS6_DDR &= ~(1 << LCD_DBUS6);
LCD_DBUS5_DDR &= ~(1 << LCD_DBUS5);
LCD_DBUS4_DDR &= ~(1 << LCD_DBUS4);
#ifdef EIGHT_BIT_ARBITRARY_PIN_MODE
LCD_DBUS3_DDR &= ~(1 << LCD_DBUS3);
LCD_DBUS2_DDR &= ~(1 << LCD_DBUS2);
LCD_DBUS1_DDR &= ~(1 << LCD_DBUS1);
LCD_DBUS0_DDR &= ~(1 << LCD_DBUS0);
#endif
#else
LCD_DBUS_DDR = 0;
#endif
}
/*
Set RS=RW=0 and write the CMD_INIT command to the LCD data bus. Note that an appropriate
pause must follow before sending new commands to the LCD using writeLCD*_ functions.
*/
static inline void softwareLCDInitPulse(void) {
LCD_RS_PORT &= ~(1 << LCD_RS); // RS=0
LCD_RW_PORT &= ~(1 << LCD_RW); // RW=0
#ifdef FOUR_BIT_MODE
writeLCDDBusNibble_(CMD_INIT);
#else
writeLCDDBusByte_(CMD_INIT);
#endif
}
//---------------------------------------------------------------------------------------------
// Library function definitions
/*
Do software initialization as specified by the datasheet
*/
void initLCD(void) {
// Set LCD_RS, LCD_RW and LCD_ENABLE as outputs
LCD_RS_DDR |= (1 << LCD_RS);
LCD_RW_DDR |= (1 << LCD_RW);
LCD_ENABLE_DDR |= (1 << LCD_ENABLE);
setLCDDBusAsOutputs();
_delay_us(LCD_INIT_DELAY0); // Wait minimum 15ms as per datasheet
softwareLCDInitPulse();
_delay_us(LCD_INIT_DELAY1); // Wait minimum 4.1ms as per datasheet
softwareLCDInitPulse();
_delay_us(LCD_INIT_DELAY2); // Wait minimum 100us as per datasheet
softwareLCDInitPulse();
#if defined (FOUR_BIT_MODE)
// Function Set (4-bit interface)
writeLCDDBusNibble_(CMD_INIT_FOUR_BIT);
writeLCDInstr_(CMD_INIT_FOUR_BIT | LCD_LINES | LCD_FONT);
#else
// Function set (8-bit interface)
writeLCDInstr_(INSTR_FUNC_SET | (1 << INSTR_FUNC_SET_DL) | LCD_LINES | LCD_FONT);
#endif
/* BF now can be checked */
// Set functions of LCD
writeLCDInstr(INSTR_DISPLAY); // Display off
// Clear display
writeLCDInstr(CMD_CLEAR_DISPLAY);
// Increment mode, no shift
writeLCDInstr(INSTR_ENTRY_SET | (1 << INSTR_ENTRY_SET_ID));
// Display on, cursor on, blink off
lcdState = (1 << INSTR_DISPLAY_D) | (1 << INSTR_DISPLAY_C);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
/*
Given a single character, checks whether its a ASCII escape and does the following:
- Newline '\n': moves the cursor to the next physical line of the LCD display; if the cursor is on
the last line of the display, clears the display and positions the cursor at the top left
of the LCD
- Carriage return '\r': moves the cursor to the beginning of the current line
- Backspace '\b': moves the cursor one position backwards, wrapping to the end of the
previous line when at the beginning of a line (other then the first one). A space is then
inserted to replace the character at point, without moving the cursor. When the cursor is
at the beginning of the first line, does nothing.
- Form feed '\f': clears the LCD display and places the cursor at the beginning of the first line.
- Alarm '\a': ignored
Any other character is sent to the LCD display using writeCharToLCD_.
*/
void writeCharToLCD(char c) {
switch (c) {
case '\n': // Line feed
if (currentLineNum == LCD_NUMBER_OF_LINES - 1) {
scrollUp(1);
currentLineChars = 0;
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[currentLineNum]);
} else {
currentLineChars = 0;
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[++currentLineNum]);
}
break;
case '\a': // Alarm
break;
case '\b': // Backspace (non-destructive)
if (currentLineChars == 0 && currentLineNum == 0) {
// At first line, first column; there is no where to move; do nothing
break;
} else if (currentLineChars == 0) {
// At beginning of line, need to move the end of previous line
currentLineChars = LCD_CHARACTERS_PER_LINE - 1;
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[--currentLineNum] + currentLineChars));
} else {
// OK, simply go back one character
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + --currentLineChars));
}
break;
case '\r': // Carriage return
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[currentLineNum]);
currentLineChars = 0;
break;
case '\f': // Form feed
clearDisplay();
break;
default: // Printable character
if (currentLineChars == LCD_CHARACTERS_PER_LINE - 1 && currentLineNum == LCD_NUMBER_OF_LINES - 1) {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
writeCharToLCD_(c);
scrollUp(1);
currentLineChars = 0;
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[currentLineNum]);
} else if (currentLineChars == LCD_CHARACTERS_PER_LINE - 1) {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
writeCharToLCD_(c);
currentLineChars = 0;
writeLCDInstr(INSTR_DDRAM_ADDR | lineBeginnings[++currentLineNum]);
} else {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
writeCharToLCD_(c);
currentLineChars++;
}
}
}
void writeStringToLCD(char* str) {
while (*str != '\0') {
#ifdef LCD_ANSI_ESCAPE_ENABLE
// Check for ANSI CSI (Control Sequence Introducer)
if (*str == '\e') {
if (*(++str) != '\0' && *str == '[') {
char* str_ref = ++str;
switch (*str) {
case 's': // SCP - Save cursor position
saveCursorPosition();
return;
case 'u': // RCP - Restore cursor position
restoreCursorPosition();
return;
case '?': // DECTCEM
if (*(++str_ref) != '\0' && *str_ref == '2') {
if (*(++str_ref) != '\0' && *str_ref == '5') {
if (*(++str_ref) != '\0') {
if (*str_ref == 'l') {
hideCursor();
} else if (*str_ref == 'h') {
showCursor();
} else {
// Invalid escape
}
} // Invalid escape (early termination)
} // Invalid escape
} // Invalid escape
return;
default:
break;
}
// Read optional variable length number in ASCII (0x30 - 0x3f) where 0x3a - 0x3f are
// ignored (they are used as flags by some terminals)
uint8_t fnd0;
uint8_t num0 = readASCIINumber(str, &fnd0, &str);
// Read optional (semicolon followed by optional variable length number)
uint8_t fnd1;
uint8_t num1;
if (*str != '\0' && *str == ';') {
num1 = readASCIINumber(++str, &fnd1, &str);
// Read control character (between 0x40 - 0x7e) for two argument sequences
switch (*str) {
case 'f': // HVP - Horizontal and vertical position
case 'H': // CUP - Cursor position
num0 = fnd0 ? num0 : 1;
num1 = fnd1 ? num1 : 1;
setCursorPosition(num0, num1);
break;
default: // Invalid control character
break;
}
} else if (*str != '\0') {
// Read control character (between 0x40 - 0x7e) for single argument sequences
switch (*str) {
case 'A': // CUU - Cursor up
num0 = fnd0 ? num0 : 1;
moveCursorUp(num0);
break;
case 'B': // CUD - Cursor down
num0 = fnd0 ? num0 : 1;
moveCursorDown(num0);
break;
case 'C': // CUF - Cursor forward
num0 = fnd0 ? num0 : 1;
moveCursorForward(num0);
break;
case 'D': // CUB - Cursor back
num0 = fnd0 ? num0 : 1;
moveCursorBackward(num0);
break;
case 'E': // CNL - Cursor next line
num0 = fnd0 ? num0 : 1;
moveCursorNextLine(num0);
break;
case 'F': // CPL - Cursor previous line
num0 = fnd0 ? num0 : 1;
moveCursorPreviousLine(num0);
break;
case 'G': // CHA - Cursor horizontal absolute
num0 = fnd0 ? num0 : 1;
moveCursorToColumn(num0);
break;
case 'J': // ED - Erase display
num0 = fnd0 ? num0 : 1;
eraseDisplay(num0);
break;
case 'K': // EL - Erase in line
num0 = fnd0 ? num0 : 1;
eraseInline(num0);
break;
case 'S': // SU - Scroll up
num0 = fnd0 ? num0 : 1;
scrollUp(num0);
break;
case 'T': // SD Scroll down
num0 = fnd0 ? num0 : 1;
scrollDown(num0);
break;
case 'm': // SGR - Select graphic rendition (single optional argument)
break;
case ';': // SGR - Select graphic rendition (multiple arguments)
if (fnd0) {
while (fnd0) {
readASCIINumber(++str, &fnd0, &str);
if (fnd0) {
if (*str == 'm') {
break; // Valid SGR
} else if (*str == ';') {
continue; // More SGR parameters yet
} else {
break; // Invalid escape
}
} else {
// Invalid escape; expected SGR parameter
}
}
} else {
// Invalid escape; expected first SGR parameter but none given
}
break;
case 'n': // DSR - Device status report
if (fnd0 && num0 == 6) {
// Valid DSR
} else {
// Invalid DSR
}
break;
default: // Invalid control character
writeCharToLCD(*str);
break;
}
} else {
return; // Invalid escape sequence (terminated early)
}
}
} else {
writeCharToLCD(*str);
}
str++;
#else
writeCharToLCD(*(str++));
#endif
}
}
//---------------------------------------------------------------------------------------------
// LCD command functions (all have associated ANSI escape)
/*
Writes the CMD_CLEAR_DISPLAY command to the LCD using writeLCDINSTR, and clears the local
char and line counters.
*/
void clearDisplay(void) {
writeLCDInstr(CMD_CLEAR_DISPLAY);
// Reset line and char number tracking
currentLineNum = 0;
currentLineChars = 0;
}
/*
Writes the CMD_RETURN_HOME command to the LCD using writeLCDInstr, and clears the local char
and line counters.
*/
void returnHome(void) {
writeLCDInstr(CMD_RETURN_HOME);
// Reset line and char number tracking
currentLineNum = 0;
currentLineChars = 0;
}
void getCursorPosition(uint8_t* row, uint8_t* column) {
*row = currentLineNum + 1;
*column = currentLineChars + 1;
}
void setCursorPosition(uint8_t row, uint8_t column) {
// Set currentLineNum and currentLineChars
currentLineNum = row ? row - 1 : 0;
currentLineChars = column ? column - 1 : 0;
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorUp(uint8_t n) {
if (n < currentLineNum + 1) {
currentLineNum -= n;
} else {
currentLineNum = 0;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorDown(uint8_t n) {
if (n + currentLineNum < LCD_NUMBER_OF_LINES) {
currentLineNum += n;
} else {
currentLineNum = LCD_NUMBER_OF_LINES - 1;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorForward(uint8_t n) {
if (n + currentLineChars < LCD_CHARACTERS_PER_LINE) {
currentLineChars += n;
} else {
currentLineChars = LCD_CHARACTERS_PER_LINE - 1;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorBackward(uint8_t n) {
if (n < currentLineChars + 1) {
currentLineChars -= n;
} else {
currentLineChars = 0;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorNextLine(uint8_t n) {
currentLineChars = 0;
if (n + currentLineNum < LCD_NUMBER_OF_LINES) {
currentLineNum += n;
} else {
currentLineNum = LCD_NUMBER_OF_LINES - 1;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorPreviousLine(uint8_t n) {
currentLineChars = 0;
if (n < currentLineNum + 1) {
currentLineNum -= n;
} else {
currentLineNum = 0;
}
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void moveCursorToColumn(uint8_t n) {
if (n <= LCD_CHARACTERS_PER_LINE) {
currentLineChars = n ? n - 1 : 0;
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
} // else index out of range (off screen column)
}
void eraseDisplay(uint8_t n) {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
switch (n) {
case 0: // Clear from cursor to end of screen
{
uint8_t len = (LCD_NUMBER_OF_LINES - old_row)*LCD_CHARACTERS_PER_LINE + (LCD_CHARACTERS_PER_LINE - old_column);
for (uint8_t i = 0; i < len; i++)
writeCharToLCD(' ');
// Write last char without scrolling
loop_until_LCD_BF_clear();
writeCharToLCD_(' ');
break;
}
case 1: // Clear from cursor to beginning of screen
{
uint8_t len = (old_row - 1)*LCD_CHARACTERS_PER_LINE + old_column;
returnHome();
for (uint8_t i = 0; i < len; i++)
writeCharToLCD(' ');
// Write last char without scrolling
loop_until_LCD_BF_clear();
writeCharToLCD_(' ');
break;
}
case 2: // Clear entire screen
clearDisplay();
break;
default: // Invalid argument; do nothing
break;
}
setCursorPosition(old_row, old_column);
}
void eraseInline(uint8_t n) {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
switch (n) {
case 0: // Clear from cursor to end of line
for (uint8_t i = old_column; i <= LCD_CHARACTERS_PER_LINE; i++)
writeCharToLCD(' ');
break;
case 1: // Clear from cursor to beginning of line
setCursorPosition(old_row, 1);
for (uint8_t i = 1; i <= old_column; i++)
writeCharToLCD(' ');
break;
case 2: // Clear entire line
setCursorPosition(old_row, 1);
for (uint8_t i = 1; i <= LCD_CHARACTERS_PER_LINE; i++) {
loop_until_LCD_BF_clear();
writeCharToLCD_(' ');
}
break;
default: // Invalid argument; do nothing
return;
}
setCursorPosition(old_row, old_column);
}
void scrollUp(uint8_t n) {
#if LCD_NUMBER_OF_LINES == 1
clearDisplay();
#else
if (n >= LCD_NUMBER_OF_LINES) {
clearDisplay();
} else {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
uint8_t len = (LCD_NUMBER_OF_LINES - n)*LCD_CHARACTERS_PER_LINE + 1;
char str[len];
readCharsFromLCD(n + 1, 1, LCD_NUMBER_OF_LINES, LCD_CHARACTERS_PER_LINE, str, len);
setCursorPosition(1, 1); // returnHome();
writeStringToLCD(str);
// Add n newlines to bottom of screen
for (uint8_t i = 0; i < n; i++) {
setCursorPosition(LCD_NUMBER_OF_LINES - i, 1);
eraseInline(2);
}
setCursorPosition(old_row, old_column);
}
#endif
}
void scrollDown(uint8_t n) {
#if LCD_NUMBER_OF_LINES == 1
clearDisplay();
#else
if (n >= LCD_NUMBER_OF_LINES) {
clearDisplay();
} else {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
uint8_t len = (LCD_NUMBER_OF_LINES - n)*LCD_CHARACTERS_PER_LINE + 1;
char str[len];
readCharsFromLCD(1, 1, LCD_NUMBER_OF_LINES - n, LCD_CHARACTERS_PER_LINE, str, len);
for (uint8_t column = n + 1, i = 0; column <= LCD_NUMBER_OF_LINES; column++) {
setCursorPosition(column , 1);
for (uint8_t row = 1; row <= LCD_CHARACTERS_PER_LINE; row++) {
loop_until_LCD_BF_clear();
writeCharToLCD_(str[i++]);
}
}
// Add n newlines to top of screen
for (uint8_t i = 1; i <= n; i++) {
setCursorPosition(i, 1);
eraseInline(2);
}
setCursorPosition(old_row, old_column);
}
#endif
}
void saveCursorPosition() {
saveCursorLineNum = currentLineNum;
saveCursorLineChars = currentLineChars;
}
void restoreCursorPosition() {
currentLineNum = saveCursorLineNum;
currentLineChars = saveCursorLineChars;
writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars));
}
void hideCursor(void) {
lcdState &= ~(1 << INSTR_DISPLAY_C);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
void showCursor(void) {
lcdState |= (1 << INSTR_DISPLAY_C);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
//-----------------------------------------------------------------------------------------------
// Utility functions (with no associated ASCII or ANSI escape)
void blinkCursorOff(void) {
lcdState &= ~(1 << INSTR_DISPLAY_B);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
void blinkCursorOn(void) {
lcdState |= (1 << INSTR_DISPLAY_B);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
void displayOff(void) {
lcdState &= ~(1 << INSTR_DISPLAY_D);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
void displayOn(void) {
lcdState |= (1 << INSTR_DISPLAY_D);
writeLCDInstr(INSTR_DISPLAY | lcdState);
}
//-----------------------------------------------------------------------------------------------
char readCharFromLCD(uint8_t row, uint8_t column) {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
setCursorPosition(row, column);
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
setLCDDBusAsInputs();
char c = readLCDDBusByte_();
setLCDDBusAsOutputs();
setCursorPosition(old_row, old_column);
return c;
}
void readLCDLine(uint8_t i, char* str) {
readCharsFromLCD(i, 1, i, LCD_CHARACTERS_PER_LINE, str, LCD_CHARACTERS_PER_LINE + 1);
}
//---------------------------------------------------------------------------------------------
// Advanced functions for special cases
void readCharsFromLCD(uint8_t from_row, uint8_t from_column, uint8_t to_row, uint8_t to_column, char* str, uint8_t len) {
uint8_t old_row, old_column;
getCursorPosition(&old_row, &old_column);
setCursorPosition(from_row, from_column);
for (uint8_t i = 0; i < len - 1 && from_row <= to_row; i++) {
if (from_row == LCD_NUMBER_OF_LINES && from_column == LCD_CHARACTERS_PER_LINE) {
// Last character on screen
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
setLCDDBusAsInputs();
*(str++) = readLCDDBusByte_();
setLCDDBusAsOutputs();
} else if (from_column == LCD_CHARACTERS_PER_LINE) { // End of line (but not last one)
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
setLCDDBusAsInputs();
*(str++) = readLCDDBusByte_();
setLCDDBusAsOutputs();
from_row += 1;
from_column = 1;
setCursorPosition(from_row, from_column);
} else {
loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions
setLCDDBusAsInputs();
*(str++) = readLCDDBusByte_();
setLCDDBusAsOutputs();
from_column++;
}
}
// Ensure array is terminated with null character
*str = '\0';
setCursorPosition(old_row, old_column);
setLCDDBusAsOutputs();
}
/*
Initialize LCD using the internal reset circuitry.
Note: This currently only works with 8 bit modes, but is not recommended. Instead use the
initLCD function which uses the software initialization method and works for 8-bit
modes as well the 4-bit mode.
*/
void initLCDByInternalReset(void) {
setLCDDBusAsOutputs();
// Function set (8-bit interface; 2 lines with 5x7 dot character font)
writeLCDInstr_(INSTR_FUNC_SET | (1 << INSTR_FUNC_SET_DL) | (1 << INSTR_FUNC_SET_N));
writeLCDInstr_(0x0F);
writeLCDInstr_(0x06);
writeLCDInstr_(CMD_CLEAR_DISPLAY);
_delay_ms(LCD_CLEAR_DISPLAY_DELAY);
}

View File

@ -1,328 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lcdLib.h
* @author Collin J. Doering <collin.doering@rekahsoft.ca>
* @date Sep 29, 2015
* @brief Functions to initialize, and operate a character LCD.
*/
#ifndef LCD_LIB_H
#define LCD_LIB_H
// Includes -----------------------------------------------------------------------------------
#include "lcd_instr.h"
#include "lcdLibConfig.h"
//---------------------------------------------------------------------------------------------
// Library function declarations
/**
Initialize the LCD display via software initialization as specified by the datasheet.
*/
void initLCD(void);
/**
Writes a character to the LCD display at the current cursor position after the LCD display is
ready for new data. Allows the following ASCII escapes: '\n', '\r', '\f' and '\b'; ignores
ASCII escape '\a'.
*/
void writeCharToLCD(char);
/**
Writes a string to the LCD starting from the current cursor position.
*/
void writeStringToLCD(char*);
//---------------------------------------------------------------------------------------------
// LCD command functions (all have associated ANSI escape)
/**
Clears the display and positions the cursor in the top left of the LCD screen.
*/
void clearDisplay(void);
/**
Brings the cursor the the top left of the LCD screen.
*/
void returnHome(void);
/**
Gets the current row and column of the LCD cursor and sets given pointers row and column to
their respective values. Note indexes start at 1.
*/
void getCursorPosition(uint8_t* row, uint8_t* column);
/**
Using the given parameters row and column, sets the current row and column occupied by the LCD
cursor. Note indexes start at 1.
*/
void setCursorPosition(uint8_t row, uint8_t column);
/**
Moves the cursor n positions up. If the cursor is at the edge of the screen this has no effect.
*/
void moveCursorUp(uint8_t n);
/**
Moves the cursor n positions down. If the cursor is at the edge of the screen this has no effect.
*/
void moveCursorDown(uint8_t n);
/**
Moves the cursor n positions forward. If the cursor is at the edge of the screen this has no effect.
*/
void moveCursorForward(uint8_t n);
/**
Moves the cursor n positions backwards. If the cursor is at the edge of the screen this has no effect.
*/
void moveCursorBackward(uint8_t n);
/**
Moves the cursor to the beginning of the line n lines down.
*/
void moveCursorNextLine(uint8_t n);
/**
Moves the cursor to the beginning of the line n lines up.
*/
void moveCursorPreviousLine(uint8_t n);
/**
Moves the cursor to column n. If n is off screen go to the last line.
*/
void moveCursorToColumn(uint8_t n);
/**
Scroll whole page up by n lines. New lines are added at the bottom.
*/
void scrollUp(uint8_t n);
/**
Scroll whole page down by n lines. New lines are added at the top.
*/
void scrollDown(uint8_t n);
/**
Saves the cursors current position.
*/
void saveCursorPosition(void);
/**
Restores the last saved cursor position.
*/
void restoreCursorPosition(void);
/**
Clears part or all of screen dependent on the value of n:
0 or missing: clear from cursor to end of screen
1: clear from cursor to end of screen
2: clear entire screen
*/
void eraseDisplay(uint8_t n);
/**
Erases part of a line dependent on the value of n:
0 or missing: clear from cursor to end of the line
1: clear from cursor to beginning of the line
2: clear entire line
*/
void eraseInline(uint8_t n);
/**
Hides the cursor
*/
void hideCursor(void);
/**
Shows the cursor
*/
void showCursor(void);
//---------------------------------------------------------------------------------------------
// Utility functions (with no associated ASCII or ANSI escape)
/**
Turns the cursor blink off.
*/
void blinkCursorOff(void);
/**
Turns the cursor blink on.
*/
void blinkCursorOn(void);
/**
Turns the display off.
*/
void displayOff(void);
/**
Turns the display on.
*/
void displayOn(void);
//---------------------------------------------------------------------------------------------
/**
Read a single character from the row and column given (1 based) returning the cursor to
its previous original position.
*/
char readCharFromLCD(uint8_t row, uint8_t column);
/**
Read a line i (ones based) into str.
*/
void readLCDLine(uint8_t i, char* str);
//---------------------------------------------------------------------------------------------
// Advanced functions for special cases
/**
Read len characters from (from_row, from_column) to (to_row, to_column) returning the cursor
to its original position after the read. Upon success (not overflowing the screen), 0 is
returned; otherwise non zero will be returned. The str pointer will be updated with the
characters read from the screen. Even in the case of failure, str may be partially populated.
*/
void readCharsFromLCD(uint8_t from_row, uint8_t from_column, uint8_t to_row, uint8_t to_column, char* str, uint8_t len);
/**
Initialize the LCD display via its internal reset circuit.
Note: this is not the recommended way to initialize the LCD as it is unreliable and depends
on the power supply. Preferably the software initialization method should be used (via
the initLCD function).
*/
void initLCDByInternalReset(void);
//---------------------------------------------------------------------------------------------
// Mode and settings sanity check (preprocessor tests of lcdLibConfig.h)
//---------------------------------------------------------------------------------------------
#if !defined(LCD_CHARACTERS_PER_LINE)
#error "All modes require LCD_CHARACTERS_PER_LINE to be defined."
#elif !defined(LCD_NUMBER_OF_LINES)
#error "All modes require LCD_NUMBER_OF_LINES to be defined."
#elif !defined(LCD_LINE_BEGINNINGS)
#error "All modes require LCD_LINE_BEGINNINGS to be defined."
#else
#if LCD_NUMBER_OF_LINES == 1
#define LCD_LINES 0
#else
#define LCD_LINES (1 << INSTR_FUNC_SET_N)
#endif
#define LCD_CHARACTERS_PER_SCREEN (LCD_CHARACTERS_PER_LINE * LCD_NUMBER_OF_LINES)
#endif
#if !defined(LCD_FONT_5x8) &&\
!defined(LCD_FONT_5x10)
#error "All modes require LCD_FONT_5x8 or LCD_FONT_5x10 to be defined."
#elif defined(LCD_FONT_5x8) && \
defined(LCD_FONT_5x10)
#error "LCD_FONT_5x8 and LCD_FONT_5x10 are mutually exclusive. Choose one."
#elif defined(LCD_FONT_5x8)
#define LCD_FONT 0
#elif defined(LCD_FONT_5x10)
#define LCD_FONT (1 << INSTR_FUNC_SET_F)
#endif
#if !defined (LCD_RS) || \
!defined (LCD_RS_PORT) || \
!defined (LCD_RS_DDR) || \
!defined (LCD_RW) || \
!defined (LCD_RW_PORT) || \
!defined (LCD_RW_DDR) || \
!defined (LCD_ENABLE) || \
!defined (LCD_ENABLE_PORT) || \
!defined (LCD_ENABLE_DDR)
#error "All modes require LCD_RS[,_PORT,_DDR], LCD_RW[,_PORT,_DDR], and LCD_ENABLE[,_PORT,_DDR] be defined."
#endif
#if defined (EIGHT_BIT_ARBITRARY_PIN_MODE) && \
defined (FOUR_BIT_MODE)
#error "EIGHT_BIT_ARBITRARY_PIN_MODE and FOUR_BIT_MODE are mutually exclusive. Choose one."
#elif defined (EIGHT_BIT_ARBITRARY_PIN_MODE) || \
defined (FOUR_BIT_MODE)
// EIGHT_BIT_ARBITRARY_PIN_MODE specific requirements
#ifdef EIGHT_BIT_ARBITRARY_PIN_MODE
#if !defined (LCD_DBUS0) || \
!defined (LCD_DBUS0_PORT) || \
!defined (LCD_DBUS0_DDR) || \
!defined (LCD_DBUS0_PIN) || \
!defined (LCD_DBUS1) || \
!defined (LCD_DBUS1_PORT) || \
!defined (LCD_DBUS1_DDR) || \
!defined (LCD_DBUS1_PIN) || \
!defined (LCD_DBUS2) || \
!defined (LCD_DBUS2_PORT) || \
!defined (LCD_DBUS2_DDR) || \
!defined (LCD_DBUS2_PIN) || \
!defined (LCD_DBUS3) || \
!defined (LCD_DBUS3_PORT) || \
!defined (LCD_DBUS3_DDR) || \
!defined (LCD_DBUS3_PIN)
#error "EIGHT_BIT_ARBITRARY_PIN_MODE require that LCD_DBUS*[,_PORT,_DDR,_PIN] be defined."
#endif
#endif
// Requirements for EIGHT_BIT_ARBITRARY_PIN_MODE and FOUR_BIT_MODE
#if !defined (LCD_DBUS4) || \
!defined (LCD_DBUS4_PORT) || \
!defined (LCD_DBUS4_DDR) || \
!defined (LCD_DBUS4_PIN) || \
!defined (LCD_DBUS5) || \
!defined (LCD_DBUS5_PORT) || \
!defined (LCD_DBUS5_DDR) || \
!defined (LCD_DBUS5_PIN) || \
!defined (LCD_DBUS6) || \
!defined (LCD_DBUS6_PORT) || \
!defined (LCD_DBUS6_DDR) || \
!defined (LCD_DBUS6_PIN) || \
!defined (LCD_DBUS7) || \
!defined (LCD_DBUS7_PORT) || \
!defined (LCD_DBUS7_DDR) || \
!defined (LCD_DBUS7_PIN)
#error "Both EIGHT_BIT_ARBITRARY_PIN_MODE and FOUR_BIT_MODE require that LCD_DBUS*[,_PORT,_DDR,_PIN] be defined."
#endif
// Set LCD_BF automatically for both EIGHT_BIT_ARBITRARY_PIN_MODE and FOUR_BIT_MODE
#undef LCD_BF
#define LCD_BF LCD_DBUS7
#else
#if !defined (LCD_DBUS_PORT) || \
!defined (LCD_DBUS_DDR) || \
!defined (LCD_DBUS_PIN) || \
!defined (LCD_BF)
#error "Default mode requires that LCD_DBUS_[PORT,DDR,PIN] and LCD_BF be defined."
#endif
#undef LCD_DBUS7_PORT
#define LCD_DBUS7_PORT LCD_DBUS_PORT
#undef LCD_DBUS7_DDR
#define LCD_DBUS7_DDR LCD_DBUS_DDR
#undef LCD_DBUS7_PIN
#define LCD_DBUS7_PIN LCD_DBUS_PIN
#endif
#endif /* LCD_LIB_H */

View File

@ -1 +0,0 @@
../../lcdLibConfig.h

View File

@ -1,61 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lcd_instr.h
* @author Collin J. Doering <collin.doering@rekahsoft.ca>
* @date Oct 26, 2015
* @brief Constant macro definitions of HD44780 compatible character LCD instruction set
*/
/*
LCD character display instructions
*/
// Simple instructions with no options
#define CMD_INIT 0x30
#define CMD_INIT_FOUR_BIT 0x20
#define CMD_CLEAR_DISPLAY 0x01
#define CMD_RETURN_HOME 0x02
// Entry Set instruction and associated options
#define INSTR_ENTRY_SET 0x04
#define INSTR_ENTRY_SET_ID 1
#define INSTR_ENTRY_SET_S 0
// Display control instruction and associated options
#define INSTR_DISPLAY 0x08
#define INSTR_DISPLAY_D 2
#define INSTR_DISPLAY_C 1
#define INSTR_DISPLAY_B 0
// Cursor or display shift instruction and associated options
#define INSTR_MOV_SHIFT 0x10
#define INSTR_MOV_SHIFT_SC 3
#define INSTR_MOV_SHIFT_RL 2
// Function set instruction and associated options
#define INSTR_FUNC_SET 0x20
#define INSTR_FUNC_SET_DL 4
#define INSTR_FUNC_SET_N 3
#define INSTR_FUNC_SET_F 2
// Set CG RAM address instruction
#define INSTR_CGRAM_ADDR 0x60
// Set DD RAM address instruction
#define INSTR_DDRAM_ADDR 0x80

View File

@ -1,107 +0,0 @@
/**
* (C) Copyright Collin J. Doering 2015
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* File: uart_echo.c
* Author: Collin J. Doering <collin.doering@rekahsoft.ca>
* Date: Sep 17, 2015
*/
/*---------.
| Includes |
`---------*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <util/delay.h>
#include <stdlib.h>
#include "lcdLib.h"
#include "ansi_escapes.h"
#include "USART.h"
#define STATUS_LED_PORT PORTC
#define STATUS_LED_DDR DDRC
#define STATUS_LED PC5
//--------------------------------------------------
void flashLED(uint8_t times) {
while (times > 0) {
STATUS_LED_PORT |= 1 << STATUS_LED; // turn on status LED
_delay_ms(100);
STATUS_LED_PORT &= ~(1 << STATUS_LED); // turn status LED off
_delay_ms(100);
times--;
}
}
//--------------------------------------------------
int main(void) {
clock_prescale_set(clock_div_1);
STATUS_LED_DDR |= 1 << STATUS_LED; // DEBUG
initUSART();
char serialChar;
initLCD();
//initLCDByInternalReset();
flashLED(5); // DEBUG
while (1) {
serialChar = receiveByte();
switch (serialChar) {
case '\r':
writeStringToLCD("\r\n");
transmitString("\n" CNL(1) "\r");
break;
case '\f':
writeCharToLCD(serialChar);
transmitString(ED(2) CUP(1,1));
break;
case 0x7f: // Backspace (sent as delete)
writeStringToLCD("\b \b");
transmitString(CUB(1) " " CUB(1));
break;
case '\e': // Beginning of ANSI escape
{
char j = receiveByte();
if (j == '[') {
char buf[11] = "\e[";
for (uint8_t i = 2, j = receiveByte(); i < 10 && j > 0x20 && j < 0x7e; i++, j = receiveByte()) {
buf[i] = j;
if (j > 0x40 && j < 0x7e) {
break;
}
}
writeStringToLCD(buf);
}
break;
}
default:
writeCharToLCD(serialChar);
transmitByte(serialChar); // Echo character back to serial console
}
}
return 0;
}