From 2d464719e7aee56c0018cf912a4bccb6c4198bfa Mon Sep 17 00:00:00 2001 From: "Holly F. Hudson" Date: Sun, 16 Sep 2012 16:06:06 -0400 Subject: [PATCH] import --- firmware/Makefile | 373 ++++++ firmware/anim.c | 242 ++++ firmware/avrlibdefs.h | 83 ++ firmware/avrlibtypes.h | 84 ++ firmware/bootloader/ATmegaBOOT_168.c | 1056 ++++++++++++++++ .../ATmegaBOOT_168_atmega328_pro_8MHz.hex | 125 ++ firmware/bootloader/Makefile | 231 ++++ firmware/bootloader/instructions.txt | 2 + firmware/buttons.c | 137 ++ firmware/config.c | 933 ++++++++++++++ firmware/font5x7.h | 119 ++ firmware/fontgr.h | 31 + firmware/fonttable.h | 51 + firmware/glcd.c | 353 ++++++ firmware/glcd.h | 95 ++ firmware/global.h | 40 + firmware/i2c.c | 568 +++++++++ firmware/i2c.h | 176 +++ firmware/i2cconf.h | 28 + firmware/ks0108.c | 452 +++++++ firmware/ks0108.h | 104 ++ firmware/ks0108conf.h | 109 ++ firmware/ratt.c | 1102 +++++++++++++++++ firmware/ratt.h | 225 ++++ firmware/util.c | 158 +++ firmware/util.h | 41 + 26 files changed, 6918 insertions(+) create mode 100644 firmware/Makefile create mode 100644 firmware/anim.c create mode 100644 firmware/avrlibdefs.h create mode 100644 firmware/avrlibtypes.h create mode 100644 firmware/bootloader/ATmegaBOOT_168.c create mode 100644 firmware/bootloader/ATmegaBOOT_168_atmega328_pro_8MHz.hex create mode 100644 firmware/bootloader/Makefile create mode 100644 firmware/bootloader/instructions.txt create mode 100644 firmware/buttons.c create mode 100644 firmware/config.c create mode 100644 firmware/font5x7.h create mode 100644 firmware/fontgr.h create mode 100644 firmware/fonttable.h create mode 100644 firmware/glcd.c create mode 100644 firmware/glcd.h create mode 100644 firmware/global.h create mode 100644 firmware/i2c.c create mode 100644 firmware/i2c.h create mode 100644 firmware/i2cconf.h create mode 100644 firmware/ks0108.c create mode 100644 firmware/ks0108.h create mode 100644 firmware/ks0108conf.h create mode 100644 firmware/ratt.c create mode 100644 firmware/ratt.h create mode 100644 firmware/util.c create mode 100644 firmware/util.h diff --git a/firmware/Makefile b/firmware/Makefile new file mode 100644 index 0000000..f02f208 --- /dev/null +++ b/firmware/Makefile @@ -0,0 +1,373 @@ +# MCU name +MCU = atmega328p +F_CPU = 8000000 + +# Output format. (can be srec, ihex, binary) +FORMAT = ihex + +# Target file name (without extension). +TARGET = monochron + +# Optimization level, can be [0, 1, 2, 3, s]. 0 turns off optimization. +# (Note: 3 is not always the best optimization level. See avr-libc FAQ.) +OPT = s + + +# List C source files here. (C dependencies are automatically generated.) + +SRC = ratt.c config.c buttons.c anim.c util.c glcd.c ks0108.c i2c.c + +# List Assembler source files here. +# Make them always end in a capital .S. Files ending in a lowercase .s +# will not be considered source files but generated files (assembler +# output from the compiler), and will be deleted upon "make clean"! +# Even though the DOS/Win* filesystem matches both .s and .S the same, +# it will preserve the spelling of the filenames, and gcc itself does +# care about how the name is spelled on its command-line. +ASRC = + + +# List any extra directories to look for include files here. +# Each directory must be seperated by a space. +EXTRAINCDIRS = + + +# Optional compiler flags. +# -g: generate debugging information (for GDB, or for COFF conversion) +# -O*: optimization level +# -f...: tuning, see gcc manual and avr-libc documentation +# -Wall...: warning level +# -Wa,...: tell GCC to pass this to the assembler. +# -ahlms: create assembler listing +CFLAGS = -g -O$(OPT) \ +-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \ +-Wall -Wstrict-prototypes \ +-DF_CPU=$(F_CPU) \ +-Wa,-adhlns=$(<:.c=.lst) \ +$(patsubst %,-I%,$(EXTRAINCDIRS)) + + +# Set a "language standard" compiler flag. +# Unremark just one line below to set the language standard to use. +# gnu99 = C99 + GNU extensions. See GCC manual for more information. +#CFLAGS += -std=c89 +#CFLAGS += -std=gnu89 +#CFLAGS += -std=c99 +CFLAGS += -std=gnu99 + + + +# Optional assembler flags. +# -Wa,...: tell GCC to pass this to the assembler. +# -ahlms: create listing +# -gstabs: have the assembler create line number information; note that +# for use in COFF files, additional information about filenames +# and function names needs to be present in the assembler source +# files -- see avr-libc docs [FIXME: not yet described there] +ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs + + + +# Optional linker flags. +# -Wl,...: tell GCC to pass this to linker. +# -Map: create map file +# --cref: add cross reference to map file +LDFLAGS = -Wl,-Map=$(TARGET).map,--cref + + + +# Additional libraries + +# Minimalistic printf version +#LDFLAGS += -Wl,-u,vfprintf -lprintf_min + +# Floating point printf version (requires -lm below) +#LDFLAGS += -Wl,-u,vfprintf -lprintf_flt + +# -lm = math library +#LDFLAGS += -lm + + + +# Programming support using avrdude. Settings and variables. + +# Programming hardware: alf avr910 avrisp bascom bsd +# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500 +# +# Type: avrdude -c ? +# to get a full listing. +# +#AVRDUDE_PROGRAMMER = dt006 +#AVRDUDE_PROGRAMMER = stk500v2 +AVRDUDE_PROGRAMMER = usbtiny + + +AVRDUDE_PORT = com5 # programmer connected to serial device +#AVRDUDE_PORT = /dev/cu.KeySerial1 # programmer connected to serial device +#AVRDUDE_PORT = /dev/cu.PL2303-0B2 +#AVRDUDE_PORT = lpt1 # programmer connected to parallel port + +AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex +#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep + +AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) + +# Uncomment the following if you want avrdude's erase cycle counter. +# Note that this counter needs to be initialized first using -Yn, +# see avrdude manual. +#AVRDUDE_ERASE += -y + +# Uncomment the following if you do /not/ wish a verification to be +# performed after programming the device. +#AVRDUDE_FLAGS += -V + +# Increase verbosity level. Please use this when submitting bug +# reports about avrdude. See +# to submit bug reports. +#AVRDUDE_FLAGS += -v -v + + + + +# --------------------------------------------------------------------------- + +# Define directories, if needed. +DIRAVR = c:/winavr +DIRAVRBIN = $(DIRAVR)/bin +DIRAVRUTILS = $(DIRAVR)/utils/bin +DIRINC = . +DIRLIB = $(DIRAVR)/avr/lib + + +# Define programs and commands. +SHELL = sh + +CC = avr-gcc + +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size + + +# Programming support using avrdude. +AVRDUDE = avrdude + + +REMOVE = rm -f +COPY = cp + +HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex +ELFSIZE = $(SIZE) -A $(TARGET).elf + + + +# Define Messages +# English +MSG_ERRORS_NONE = Errors: none +MSG_BEGIN = -------- begin -------- +MSG_END = -------- end -------- +MSG_SIZE_BEFORE = Size before: +MSG_SIZE_AFTER = Size after: +MSG_COFF = Converting to AVR COFF: +MSG_EXTENDED_COFF = Converting to AVR Extended COFF: +MSG_FLASH = Creating load file for Flash: +MSG_EEPROM = Creating load file for EEPROM: +MSG_EXTENDED_LISTING = Creating Extended Listing: +MSG_SYMBOL_TABLE = Creating Symbol Table: +MSG_LINKING = Linking: +MSG_COMPILING = Compiling: +MSG_ASSEMBLING = Assembling: +MSG_CLEANING = Cleaning project: + + + + +# Define all object files. +OBJ = $(SRC:.c=.o) $(ASRC:.S=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) +ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) + + + +# Default target. +all: begin gccversion sizebefore $(TARGET).elf $(TARGET).hex $(TARGET).eep \ + $(TARGET).lss $(TARGET).sym sizeafter finished end + +burn-fuse: + $(AVRDUDE) $(AVRDUDE_FLAGS) -u -U lfuse:w:0xE2:m -u -U hfuse:w:0xD7:m + +# Eye candy. +# AVR Studio 3.x does not check make's exit code but relies on +# the following magic strings to be generated by the compile job. +begin: + @echo + @echo $(MSG_BEGIN) + +finished: + @echo $(MSG_ERRORS_NONE) + +end: + @echo $(MSG_END) + @echo + + +# Display size of file. +sizebefore: + @if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); echo; fi + +sizeafter: + @if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); echo; fi + + + +# Display compiler version information. +gccversion : + @$(CC) --version + + + + +# Convert ELF to COFF for use in debugging / simulating in +# AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ + --change-section-address .data-0x800000 \ + --change-section-address .bss-0x800000 \ + --change-section-address .noinit-0x800000 \ + --change-section-address .eeprom-0x810000 + + +coff: $(TARGET).elf + @echo + @echo $(MSG_COFF) $(TARGET).cof + $(COFFCONVERT) -O coff-avr $< $(TARGET).cof + + +extcoff: $(TARGET).elf + @echo + @echo $(MSG_EXTENDED_COFF) $(TARGET).cof + $(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof + + + + +# Program the device. +program: $(TARGET).hex $(TARGET).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) -B 1 -U flash:w:$< + +reset: + $(AVRDUDE) $(AVRDUDE_FLAGS) + + +# Create final output files (.hex, .eep) from ELF output file. +%.hex: %.elf + @echo + @echo $(MSG_FLASH) $@ + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +%.eep: %.elf + @echo + @echo $(MSG_EEPROM) $@ + -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O $(FORMAT) $< $@ + +# Create extended listing file from ELF output file. +%.lss: %.elf + @echo + @echo $(MSG_EXTENDED_LISTING) $@ + $(OBJDUMP) -h -S $< > $@ + +# Create a symbol table from ELF output file. +%.sym: %.elf + @echo + @echo $(MSG_SYMBOL_TABLE) $@ + avr-nm -n $< > $@ + + + +# Link: create ELF output file from object files. +.SECONDARY : $(TARGET).elf +.PRECIOUS : $(OBJ) +%.elf: $(OBJ) + @echo + @echo $(MSG_LINKING) $@ + $(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS) + + +# Compile: create object files from C source files. +%.o : %.c + @echo + @echo $(MSG_COMPILING) $< + $(CC) -c $(ALL_CFLAGS) -DTXMODE=$(TXMODE) $< -o $@ + + +# Compile: create assembler files from C source files. +%.s : %.c + $(CC) -S $(ALL_CFLAGS) $< -o $@ + + +# Assemble: create object files from assembler source files. +%.o : %.S + @echo + @echo $(MSG_ASSEMBLING) $< + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + + + + + + +# Target: clean project. +clean: begin clean_list finished end + +clean_list : + @echo + @echo $(MSG_CLEANING) + $(REMOVE) $(TARGET).hex + $(REMOVE) $(TARGET).eep + $(REMOVE) $(TARGET).obj + $(REMOVE) $(TARGET).cof + $(REMOVE) $(TARGET).elf + $(REMOVE) $(TARGET).map + $(REMOVE) $(TARGET).obj + $(REMOVE) $(TARGET).a90 + $(REMOVE) $(TARGET).sym + $(REMOVE) $(TARGET).lnk + $(REMOVE) $(TARGET).lss + $(REMOVE) $(OBJ) + $(REMOVE) $(LST) + $(REMOVE) $(SRC:.c=.s) + $(REMOVE) $(SRC:.c=.d) + + +# Automatically generate C source code dependencies. +# (Code originally taken from the GNU make user manual and modified +# (See README.txt Credits).) +# +# Note that this will work with sh (bash) and sed that is shipped with WinAVR +# (see the SHELL variable defined above). +# This may not work with other shells or other seds. +# +%.d: %.c + set -e; $(CC) -MM $(ALL_CFLAGS) $< \ + | sed 's,\(.*\)\.o[ :]*,\1.o \1.d : ,g' > $@; \ + [ -s $@ ] || rm -f $@ + + +# Remove the '-' if you want to see the dependency files generated. +-include $(SRC:.c=.d) + + + +# Listing of phony targets. +.PHONY : all begin finish end sizebefore sizeafter gccversion coff extcoff \ + clean clean_list program install + +SERIAL := /dev/cu.usbserial-FTE531WL +install: monochron.hex + avrdude -c arduino -p m328p -P $(SERIAL) -b 57600 -U flash:w:$< diff --git a/firmware/anim.c b/firmware/anim.c new file mode 100644 index 0000000..75748ee --- /dev/null +++ b/firmware/anim.c @@ -0,0 +1,242 @@ +#include // this contains all the IO port definitions +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "ratt.h" +#include "ks0108.h" +#include "glcd.h" +#include "font5x7.h" +#include "fonttable.h" + + +extern volatile uint8_t time_s, time_m, time_h; +extern volatile uint8_t old_m, old_h; +extern volatile uint8_t date_m, date_d, date_y; +extern volatile uint8_t alarming, alarm_h, alarm_m; +extern volatile uint8_t time_format; +extern volatile uint8_t region; +extern volatile uint8_t score_mode; + +extern volatile uint8_t second_changed, minute_changed, hour_changed; + +#ifdef OPTION_DOW_DATELONG +const uint8_t DOWText[] PROGMEM = "sunmontuewedthufrisat"; +const uint8_t MonthText[] PROGMEM = " janfebmaraprmayjunjulaugsepoctnovdec"; +#endif + +uint8_t redraw_time = 0; +uint8_t last_score_mode = 0; + + + +void initanim(void) { + DEBUG(putstring("screen width: ")); + DEBUG(uart_putw_dec(GLCD_XPIXELS)); + DEBUG(putstring("\n\rscreen height: ")); + DEBUG(uart_putw_dec(GLCD_YPIXELS)); + DEBUG(putstring_nl("")); + +} + +void initdisplay(uint8_t inverted) { + glcdFillRectangle(0, 0, GLCD_XPIXELS, GLCD_YPIXELS, inverted); +} + + +void drawdisplay(void) { + uint8_t inverted = 0; + + if ((score_mode != SCORE_MODE_TIME) && (score_mode != SCORE_MODE_ALARM)) + { + drawdot(GLCD_XPIXELS/2, GLCD_YPIXELS*1/3, !inverted); + drawdot(GLCD_XPIXELS/2, GLCD_YPIXELS*2/3, !inverted); + drawdot(GLCD_XPIXELS/2, GLCD_YPIXELS*1/10, !inverted); + } + + if (score_mode == SCORE_MODE_YEAR) { + drawdigit(DISPLAY_H10_X, DISPLAY_TIME_Y, 2 , inverted); + drawdigit(DISPLAY_H1_X, DISPLAY_TIME_Y, 0, inverted); + drawdigit(DISPLAY_M10_X, DISPLAY_TIME_Y, (date_y % 100)/10, inverted); + drawdigit(DISPLAY_M1_X, DISPLAY_TIME_Y, date_y % 10, inverted); + } else if (score_mode == SCORE_MODE_DATE) { + uint8_t left, right; + if (region == REGION_US) { + left = date_m; + right = date_d; + } else { + left = date_d; + right = date_m; + } + drawdigit(DISPLAY_H10_X, DISPLAY_TIME_Y, left/10 , inverted); + drawdigit(DISPLAY_H1_X, DISPLAY_TIME_Y, left%10, inverted); + drawdigit(DISPLAY_M10_X, DISPLAY_TIME_Y, right/10, inverted); + drawdigit(DISPLAY_M1_X, DISPLAY_TIME_Y, right % 10, inverted); + } +#ifdef OPTION_DOW_DATELONG + else if (score_mode == SCORE_MODE_DOW) { + uint8_t dow = dotw(date_m, date_d, date_y); + draw7seg(DISPLAY_H10_X, DISPLAY_TIME_Y, 0x00 , inverted); + drawdigit(DISPLAY_H1_X, DISPLAY_TIME_Y, pgm_read_byte(DOWText + (dow*3) + 0), inverted); + drawdigit(DISPLAY_M10_X, DISPLAY_TIME_Y, pgm_read_byte(DOWText + (dow*3) + 1), inverted); + drawdigit(DISPLAY_M1_X, DISPLAY_TIME_Y, pgm_read_byte(DOWText + (dow*3) + 2), inverted); + } else if (score_mode == SCORE_MODE_DATELONG_MON) { + draw7seg(DISPLAY_H10_X, DISPLAY_TIME_Y, 0x00 , inverted); + drawdigit(DISPLAY_H1_X, DISPLAY_TIME_Y, pgm_read_byte(MonthText + (date_m*3) + 0), inverted); + drawdigit(DISPLAY_M10_X, DISPLAY_TIME_Y, pgm_read_byte(MonthText + (date_m*3) + 1), inverted); + drawdigit(DISPLAY_M1_X, DISPLAY_TIME_Y, pgm_read_byte(MonthText + (date_m*3) + 2), inverted); + } else if (score_mode == SCORE_MODE_DATELONG_DAY) { + draw7seg(DISPLAY_H10_X, DISPLAY_TIME_Y, 0x00 , inverted); + draw7seg(DISPLAY_H1_X, DISPLAY_TIME_Y, 0x00 , inverted); + drawdigit(DISPLAY_M10_X, DISPLAY_TIME_Y, date_d/10, inverted); + drawdigit(DISPLAY_M1_X, DISPLAY_TIME_Y, date_d % 10, inverted); + } +#endif + else if ((score_mode == SCORE_MODE_TIME) || (score_mode == SCORE_MODE_ALARM)) { + // draw time or alarm + uint8_t left, right; + if (score_mode == SCORE_MODE_ALARM) { + left = alarm_h; + right = alarm_m; + } else { + left = time_h; + right = time_m; + } + uint8_t am = (left < 12); + if (time_format == TIME_12H) { + left = (left + 23)%12 + 1; + if(am) { + drawdot(GLCD_XPIXELS/2, GLCD_YPIXELS*1/10, inverted); + } else { + drawdot(GLCD_XPIXELS/2, GLCD_YPIXELS*1/10, !inverted); + } + } + else + drawdot(GLCD_XPIXELS/2, GLCD_YPIXELS*1/10, !inverted); + + + // draw hours + if (left >= 10) { + drawdigit(DISPLAY_H10_X, DISPLAY_TIME_Y, left/10, inverted); + } else { + drawdigit(DISPLAY_H10_X, DISPLAY_TIME_Y, 8, !inverted); + } + drawdigit(DISPLAY_H1_X, DISPLAY_TIME_Y, left%10, inverted); + + drawdigit(DISPLAY_M10_X, DISPLAY_TIME_Y, right/10, inverted); + drawdigit(DISPLAY_M1_X, DISPLAY_TIME_Y, right%10, inverted); + + if (second_changed && time_s%2) { + drawdot(GLCD_XPIXELS/2, DISPLAY_TIME_Y+VSEGMENT_H/2+4, 0); + drawdot(GLCD_XPIXELS/2, DISPLAY_TIME_Y+VSEGMENT_H+VSEGMENT_H/2+10, 0); + } else { + drawdot(GLCD_XPIXELS/2, DISPLAY_TIME_Y+VSEGMENT_H/2+4, 1); + drawdot(GLCD_XPIXELS/2, DISPLAY_TIME_Y+VSEGMENT_H+VSEGMENT_H/2+10, 1); + } + } +} + + +void step(void) { +} + +void drawdot(uint8_t x, uint8_t y, uint8_t inverted) { + glcdFillCircle(x, y, DOTRADIUS, !inverted); +} + +void draw7seg(uint8_t x, uint8_t y, uint8_t segs, uint8_t inverted) +{ + for(uint8_t i=0;i<7;i++) + { + if(segs & (1 << (7 - i))) + drawsegment('a'+i, x, y, inverted); + else + drawsegment('a'+i, x, y, !inverted); + } +} + +void drawdigit(uint8_t x, uint8_t y, uint8_t d, uint8_t inverted) { + if(d < 10) + draw7seg(x,y,pgm_read_byte(numbertable_p + d),inverted); + else if ((d >= 'a') || (d <= 'z')) + draw7seg(x,y,pgm_read_byte(alphatable_p + (d - 'a')),inverted); + else + draw7seg(x,y,0x00,inverted); +} +void drawsegment(uint8_t s, uint8_t x, uint8_t y, uint8_t inverted) { + switch (s) { + case 'a': + drawhseg(x+VSEGMENT_W/2+1, y, inverted); + break; + case 'b': + // b is the right top vertical segment + drawvseg(x+HSEGMENT_W+2, y+HSEGMENT_H/2+2, inverted); + break; + case 'c': + // c is the right bottom vertical segment + drawvseg(x+HSEGMENT_W+2, y+VSEGMENT_H+4+HSEGMENT_H/2+2, inverted); + break; + case 'd': + // d must be the bottom segment + drawhseg(x+VSEGMENT_W/2+1, y+2*VSEGMENT_H+8, inverted); + break; + case 'e': + // e is the left bottom vertical segment + drawvseg(x, y+VSEGMENT_H+4+HSEGMENT_H/2+2, inverted); + break; + case 'f': + // f is the left top vertical segment + drawvseg(x,y+HSEGMENT_H/2+2, inverted); + break; + case 'g': + // g is the center segment + //drawhseg(x+VSEGMENT_W/2+1, (GLCD_YPIXELS - HSEGMENT_H)/2, inverted); + drawhseg(x+VSEGMENT_W/2+1, y+VSEGMENT_H+4, inverted); + break; + } +} +void drawvseg(uint8_t x, uint8_t y, uint8_t inverted) { + // rectangles for all vertical segments + glcdFillRectangle(x, y+2, VSEGMENT_W, VSEGMENT_H-4, ! inverted); + + // wider part of top triangle for vertical segments + glcdFillRectangle(x+1, y+1, VSEGMENT_W-2, 1, ! inverted); + // narrower part of top triangle for vertical segments + glcdFillRectangle(x+2, y, VSEGMENT_W-4, 1, ! inverted); + + // wider part of bottom triangle for vertical segments + glcdFillRectangle(x+1, y+VSEGMENT_H-2, VSEGMENT_W-2, 1, ! inverted); + // narrower part of bottom triangle for vertical segments + glcdFillRectangle(x+2, y+VSEGMENT_H-1, VSEGMENT_W-4, 1, ! inverted); +} + +void drawhseg(uint8_t x, uint8_t y, uint8_t inverted) { + glcdFillRectangle(x+2, y, HSEGMENT_W-4, HSEGMENT_H, ! inverted); + + glcdFillRectangle(x+1, y+1, 1, HSEGMENT_H - 2, ! inverted); + glcdFillRectangle(x, y+2, 1, HSEGMENT_H - 4, ! inverted); + + glcdFillRectangle(x+HSEGMENT_W-2, y+1, 1, HSEGMENT_H - 2, ! inverted); + glcdFillRectangle(x+HSEGMENT_W-1, y+2, 1, HSEGMENT_H - 4, ! inverted); +} + +uint8_t dotw(uint8_t mon, uint8_t day, uint8_t yr) +{ + uint16_t month, year; + + // Calculate day of the week + + month = mon; + year = 2000 + yr; + if (mon < 3) { + month += 12; + year -= 1; + } + return (day + (2 * month) + (6 * (month+1)/10) + year + (year/4) - (year/100) + (year/400) + 1) % 7; +} diff --git a/firmware/avrlibdefs.h b/firmware/avrlibdefs.h new file mode 100644 index 0000000..0762225 --- /dev/null +++ b/firmware/avrlibdefs.h @@ -0,0 +1,83 @@ +/*! \file avrlibdefs.h \brief AVRlib global defines and macros. */ +//***************************************************************************** +// +// File Name : 'avrlibdefs.h' +// Title : AVRlib global defines and macros include file +// Author : Pascal Stang +// Created : 7/12/2001 +// Revised : 9/30/2002 +// Version : 1.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : This include file is designed to contain items useful to all +// code files and projects, regardless of specific implementation. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + + +#ifndef AVRLIBDEFS_H +#define AVRLIBDEFS_H + +// Code compatibility to new AVR-libc +// outb(), inb(), inw(), outw(), BV(), sbi(), cbi(), sei(), cli() +#ifndef outb + #define outb(addr, data) addr = (data) +#endif +#ifndef inb + #define inb(addr) (addr) +#endif +#ifndef outw + #define outw(addr, data) addr = (data) +#endif +#ifndef inw + #define inw(addr) (addr) +#endif +#ifndef BV + #define BV(bit) (1<<(bit)) +#endif +#ifndef cbi + #define cbi(reg,bit) reg &= ~(BV(bit)) +#endif +#ifndef sbi + #define sbi(reg,bit) reg |= (BV(bit)) +#endif +#ifndef cli + #define cli() __asm__ __volatile__ ("cli" ::) +#endif +#ifndef sei + #define sei() __asm__ __volatile__ ("sei" ::) +#endif + +// support for individual port pin naming in the mega128 +// see port128.h for details +#ifdef __AVR_ATmega128__ +// not currently necessary due to inclusion +// of these defines in newest AVR-GCC +// do a quick test to see if include is needed +#ifndef PD0 + #include "port128.h" +#endif +#endif + +// use this for packed structures +// (this is seldom necessary on an 8-bit architecture like AVR, +// but can assist in code portability to AVR) +#define GNUC_PACKED __attribute__((packed)) + +// port address helpers +#define DDR(x) ((x)-1) // address of data direction register of port x +#define PIN(x) ((x)-2) // address of input register of port x + +// MIN/MAX/ABS macros +#define MIN(a,b) ((ab)?(a):(b)) +#define ABS(x) ((x>0)?(x):(-x)) + +// constants +#define PI 3.14159265359 + +#endif diff --git a/firmware/avrlibtypes.h b/firmware/avrlibtypes.h new file mode 100644 index 0000000..6b04bdd --- /dev/null +++ b/firmware/avrlibtypes.h @@ -0,0 +1,84 @@ +/*! \file avrlibtypes.h \brief AVRlib global types and typedefines. */ +//***************************************************************************** +// +// File Name : 'avrlibtypes.h' +// Title : AVRlib global types and typedefines include file +// Author : Pascal Stang +// Created : 7/12/2001 +// Revised : 9/30/2002 +// Version : 1.0 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : Type-defines required and used by AVRlib. Most types are also +// generally useful. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + + +#ifndef AVRLIBTYPES_H +#define AVRLIBTYPES_H + +#ifndef WIN32 + // true/false defines + #define FALSE 0 + #define TRUE -1 +#endif + +// datatype definitions macros +typedef unsigned char u08; +typedef signed char s08; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned long u32; +typedef signed long s32; +typedef unsigned long long u64; +typedef signed long long s64; + +/* use inttypes.h instead +// C99 standard integer type definitions +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +typedef unsigned long uint32_t; +typedef signed long int32_t; +typedef unsigned long uint64_t; +typedef signed long int64_t; +*/ +// maximum value that can be held +// by unsigned data types (8,16,32bits) +#define MAX_U08 255 +#define MAX_U16 65535 +#define MAX_U32 4294967295 + +// maximum values that can be held +// by signed data types (8,16,32bits) +#define MIN_S08 -128 +#define MAX_S08 127 +#define MIN_S16 -32768 +#define MAX_S16 32767 +#define MIN_S32 -2147483648 +#define MAX_S32 2147483647 + +#ifndef WIN32 + // more type redefinitions + typedef unsigned char BOOL; + typedef unsigned char BYTE; + typedef unsigned int WORD; + typedef unsigned long DWORD; + + typedef unsigned char UCHAR; + typedef unsigned int UINT; + typedef unsigned short USHORT; + typedef unsigned long ULONG; + + typedef char CHAR; + typedef int INT; + typedef long LONG; +#endif + +#endif diff --git a/firmware/bootloader/ATmegaBOOT_168.c b/firmware/bootloader/ATmegaBOOT_168.c new file mode 100644 index 0000000..497852c --- /dev/null +++ b/firmware/bootloader/ATmegaBOOT_168.c @@ -0,0 +1,1056 @@ +/**********************************************************/ +/* Serial Bootloader for Atmel megaAVR Controllers */ +/* */ +/* tested with ATmega8, ATmega128 and ATmega168 */ +/* should work with other mega's, see code for details */ +/* */ +/* ATmegaBOOT.c */ +/* */ +/* */ +/* 20090308: integrated Mega changes into main bootloader */ +/* source by D. Mellis */ +/* 20080930: hacked for Arduino Mega (with the 1280 */ +/* processor, backwards compatible) */ +/* by D. Cuartielles */ +/* 20070626: hacked for Arduino Diecimila (which auto- */ +/* resets when a USB connection is made to it) */ +/* by D. Mellis */ +/* 20060802: hacked for Arduino by D. Cuartielles */ +/* based on a previous hack by D. Mellis */ +/* and D. Cuartielles */ +/* */ +/* Monitor and debug functions were added to the original */ +/* code by Dr. Erik Lins, chip45.com. (See below) */ +/* */ +/* Thanks to Karl Pitrich for fixing a bootloader pin */ +/* problem and more informative LED blinking! */ +/* */ +/* For the latest version see: */ +/* http://www.chip45.com/ */ +/* */ +/* ------------------------------------------------------ */ +/* */ +/* based on stk500boot.c */ +/* Copyright (c) 2003, Jason P. Kyle */ +/* All rights reserved. */ +/* see avr1.org for original file and information */ +/* */ +/* 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 2 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, write */ +/* to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* Licence can be viewed at */ +/* http://www.fsf.org/licenses/gpl.txt */ +/* */ +/* Target = Atmel AVR m128,m64,m32,m16,m8,m162,m163,m169, */ +/* m8515,m8535. ATmega161 has a very small boot block so */ +/* isn't supported. */ +/* */ +/* Tested with m168 */ +/**********************************************************/ + +/* $Id$ */ + + +/* some includes */ +#include +#include +#include +#include +#include +#include + +/* the current avr-libc eeprom functions do not support the ATmega168 */ +/* own eeprom write/read functions are used instead */ +#if !defined(__AVR_ATmega168__) || !defined(__AVR_ATmega328P__) +#include +#endif + +/* Use the F_CPU defined in Makefile */ + +/* 20060803: hacked by DojoCorp */ +/* 20070626: hacked by David A. Mellis to decrease waiting time for auto-reset */ +/* set the waiting time for the bootloader */ +/* get this from the Makefile instead */ +/* #define MAX_TIME_COUNT (F_CPU>>4) */ + +/* 20070707: hacked by David A. Mellis - after this many errors give up and launch application */ +#define MAX_ERROR_COUNT 5 + +/* set the UART baud rate */ +/* 20060803: hacked by DojoCorp */ +//#define BAUD_RATE 115200 +#ifndef BAUD_RATE +#define BAUD_RATE 19200 +#endif + + +/* SW_MAJOR and MINOR needs to be updated from time to time to avoid warning message from AVR Studio */ +/* never allow AVR Studio to do an update !!!! */ +#define HW_VER 0x02 +#define SW_MAJOR 0x01 +#define SW_MINOR 0x10 + + +/* Adjust to suit whatever pin your hardware uses to enter the bootloader */ +/* ATmega128 has two UARTS so two pins are used to enter bootloader and select UART */ +/* ATmega1280 has four UARTS, but for Arduino Mega, we will only use RXD0 to get code */ +/* BL0... means UART0, BL1... means UART1 */ +#ifdef __AVR_ATmega128__ +#define BL_DDR DDRF +#define BL_PORT PORTF +#define BL_PIN PINF +#define BL0 PINF7 +#define BL1 PINF6 +#elif defined __AVR_ATmega1280__ +/* we just don't do anything for the MEGA and enter bootloader on reset anyway*/ +#else +/* other ATmegas have only one UART, so only one pin is defined to enter bootloader */ +#define BL_DDR DDRD +#define BL_PORT PORTD +#define BL_PIN PIND +#define BL PIND6 +#endif + + +/* onboard LED is used to indicate, that the bootloader was entered (3x flashing) */ +/* if monitor functions are included, LED goes on after monitor was entered */ +#if defined __AVR_ATmega128__ || defined __AVR_ATmega1280__ +/* Onboard LED is connected to pin PB7 (e.g. Crumb128, PROBOmega128, Savvy128, Arduino Mega) */ +#define LED_DDR DDRB +#define LED_PORT PORTB +#define LED_PIN PINB +#define LED PINB7 +#else +/* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duomilanuove */ +/* other boards like e.g. Crumb8, Crumb168 are using PB2 */ + + // MONOCHRON MOD +#define LED_DDR DDRD +#define LED_PORT PORTD +#define LED_PIN PIND +#define LED PIND3 +#endif + + +/* monitor functions will only be compiled when using ATmega128, due to bootblock size constraints */ +#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) +#define MONITOR 1 +#endif + + +/* define various device id's */ +/* manufacturer byte is always the same */ +#define SIG1 0x1E // Yep, Atmel is the only manufacturer of AVR micros. Single source :( + +#if defined __AVR_ATmega1280__ +#define SIG2 0x97 +#define SIG3 0x03 +#define PAGE_SIZE 0x80U //128 words + +#elif defined __AVR_ATmega1281__ +#define SIG2 0x97 +#define SIG3 0x04 +#define PAGE_SIZE 0x80U //128 words + +#elif defined __AVR_ATmega128__ +#define SIG2 0x97 +#define SIG3 0x02 +#define PAGE_SIZE 0x80U //128 words + +#elif defined __AVR_ATmega64__ +#define SIG2 0x96 +#define SIG3 0x02 +#define PAGE_SIZE 0x80U //128 words + +#elif defined __AVR_ATmega32__ +#define SIG2 0x95 +#define SIG3 0x02 +#define PAGE_SIZE 0x40U //64 words + +#elif defined __AVR_ATmega16__ +#define SIG2 0x94 +#define SIG3 0x03 +#define PAGE_SIZE 0x40U //64 words + +#elif defined __AVR_ATmega8__ +#define SIG2 0x93 +#define SIG3 0x07 +#define PAGE_SIZE 0x20U //32 words + +#elif defined __AVR_ATmega88__ +#define SIG2 0x93 +#define SIG3 0x0a +#define PAGE_SIZE 0x20U //32 words + +#elif defined __AVR_ATmega168__ +#define SIG2 0x94 +#define SIG3 0x06 +#define PAGE_SIZE 0x40U //64 words + +#elif defined __AVR_ATmega328P__ +#define SIG2 0x95 +#define SIG3 0x0F +#define PAGE_SIZE 0x40U //64 words + +#elif defined __AVR_ATmega162__ +#define SIG2 0x94 +#define SIG3 0x04 +#define PAGE_SIZE 0x40U //64 words + +#elif defined __AVR_ATmega163__ +#define SIG2 0x94 +#define SIG3 0x02 +#define PAGE_SIZE 0x40U //64 words + +#elif defined __AVR_ATmega169__ +#define SIG2 0x94 +#define SIG3 0x05 +#define PAGE_SIZE 0x40U //64 words + +#elif defined __AVR_ATmega8515__ +#define SIG2 0x93 +#define SIG3 0x06 +#define PAGE_SIZE 0x20U //32 words + +#elif defined __AVR_ATmega8535__ +#define SIG2 0x93 +#define SIG3 0x08 +#define PAGE_SIZE 0x20U //32 words +#endif + + +/* function prototypes */ +void putch(char); +char getch(void); +void getNch(uint8_t); +void byte_response(uint8_t); +void nothing_response(void); +char gethex(void); +void puthex(char); +void flash_led(uint8_t); + +/* some variables */ +union address_union { + uint16_t word; + uint8_t byte[2]; +} address; + +union length_union { + uint16_t word; + uint8_t byte[2]; +} length; + +struct flags_struct { + unsigned eeprom : 1; + unsigned rampz : 1; +} flags; + +uint8_t buff[256]; +uint8_t address_high; + +uint8_t pagesz=0x80; + +uint8_t i; +uint8_t bootuart = 0; + +uint8_t error_count = 0; + +void (*app_start)(void) = 0x0000; + + +/* main program starts here */ +int main(void) +{ + uint8_t ch,ch2; + uint16_t w; + + WDTCSR |= _BV(WDCE) | _BV(WDE); + WDTCSR = 0; + +#ifdef WATCHDOG_MODS + ch = MCUSR; + MCUSR = 0; + + // Check if the WDT was used to reset, in which case we dont bootload and skip straight to the code. woot. + if (! (ch & _BV(EXTRF))) // if its a not an external reset... + app_start(); // skip bootloader +#else + asm volatile("nop\n\t"); +#endif + + /* set pin direction for bootloader pin and enable pullup */ + /* for ATmega128, two pins need to be initialized */ +#ifdef __AVR_ATmega128__ + BL_DDR &= ~_BV(BL0); + BL_DDR &= ~_BV(BL1); + BL_PORT |= _BV(BL0); + BL_PORT |= _BV(BL1); +#else + /* We run the bootloader regardless of the state of this pin. Thus, don't + put it in a different state than the other pins. --DAM, 070709 + This also applies to Arduino Mega -- DC, 080930 + BL_DDR &= ~_BV(BL); + BL_PORT |= _BV(BL); + */ +#endif + + +#ifdef __AVR_ATmega128__ + /* check which UART should be used for booting */ + if(bit_is_clear(BL_PIN, BL0)) { + bootuart = 1; + } + else if(bit_is_clear(BL_PIN, BL1)) { + bootuart = 2; + } +#endif + +#if defined __AVR_ATmega1280__ + /* the mega1280 chip has four serial ports ... we could eventually use any of them, or not? */ + /* however, we don't wanna confuse people, to avoid making a mess, we will stick to RXD0, TXD0 */ + bootuart = 1; +#endif + + /* check if flash is programmed already, if not start bootloader anyway */ + if(pgm_read_byte_near(0x0000) != 0xFF) { + +#ifdef __AVR_ATmega128__ + /* no UART was selected, start application */ + if(!bootuart) { + app_start(); + } +#else + /* check if bootloader pin is set low */ + /* we don't start this part neither for the m8, nor m168 */ + //if(bit_is_set(BL_PIN, BL)) { + // app_start(); + // } +#endif + } + +#ifdef __AVR_ATmega128__ + /* no bootuart was selected, default to uart 0 */ + if(!bootuart) { + bootuart = 1; + } +#endif + + + /* initialize UART(s) depending on CPU defined */ +#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) + if(bootuart == 1) { + UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1); + UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8; + UCSR0A = 0x00; + UCSR0C = 0x06; + UCSR0B = _BV(TXEN0)|_BV(RXEN0); + } + if(bootuart == 2) { + UBRR1L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1); + UBRR1H = (F_CPU/(BAUD_RATE*16L)-1) >> 8; + UCSR1A = 0x00; + UCSR1C = 0x06; + UCSR1B = _BV(TXEN1)|_BV(RXEN1); + } +#elif defined __AVR_ATmega163__ + UBRR = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1); + UBRRHI = (F_CPU/(BAUD_RATE*16L)-1) >> 8; + UCSRA = 0x00; + UCSRB = _BV(TXEN)|_BV(RXEN); +#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) + +#ifdef DOUBLE_SPEED + UCSR0A = (1<> 8; +#else + UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1); + UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8; +#endif + + UCSR0B = (1<>8; // set baud rate + UBRRL = (((F_CPU/BAUD_RATE)/16)-1); + UCSRB = (1<> 8; + UCSRA = 0x00; + UCSRC = 0x06; + UCSRB = _BV(TXEN)|_BV(RXEN); +#endif + +#if defined __AVR_ATmega1280__ + /* Enable internal pull-up resistor on pin D0 (RX), in order + to supress line noise that prevents the bootloader from + timing out (DAM: 20070509) */ + /* feature added to the Arduino Mega --DC: 080930 */ + DDRE &= ~_BV(PINE0); + PORTE |= _BV(PINE0); +#endif + + + /* set LED pin as output */ + LED_DDR |= _BV(LED); + + + /* flash onboard LED to signal entering of bootloader */ +#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) + // 4x for UART0, 5x for UART1 + flash_led(NUM_LED_FLASHES + bootuart); +#else + flash_led(NUM_LED_FLASHES); +#endif + + /* 20050803: by DojoCorp, this is one of the parts provoking the + system to stop listening, cancelled from the original */ + //putch('\0'); + + /* forever loop */ + for (;;) { + + /* get character from UART */ + ch = getch(); + + /* A bunch of if...else if... gives smaller code than switch...case ! */ + + /* Hello is anyone home ? */ + if(ch=='0') { + nothing_response(); + } + + + /* Request programmer ID */ + /* Not using PROGMEM string due to boot block in m128 being beyond 64kB boundry */ + /* Would need to selectively manipulate RAMPZ, and it's only 9 characters anyway so who cares. */ + else if(ch=='1') { + if (getch() == ' ') { + putch(0x14); + putch('A'); + putch('V'); + putch('R'); + putch(' '); + putch('I'); + putch('S'); + putch('P'); + putch(0x10); + } else { + if (++error_count == MAX_ERROR_COUNT) + app_start(); + } + } + + + /* AVR ISP/STK500 board commands DON'T CARE so default nothing_response */ + else if(ch=='@') { + ch2 = getch(); + if (ch2>0x85) getch(); + nothing_response(); + } + + + /* AVR ISP/STK500 board requests */ + else if(ch=='A') { + ch2 = getch(); + if(ch2==0x80) byte_response(HW_VER); // Hardware version + else if(ch2==0x81) byte_response(SW_MAJOR); // Software major version + else if(ch2==0x82) byte_response(SW_MINOR); // Software minor version + else if(ch2==0x98) byte_response(0x03); // Unknown but seems to be required by avr studio 3.56 + else byte_response(0x00); // Covers various unnecessary responses we don't care about + } + + + /* Device Parameters DON'T CARE, DEVICE IS FIXED */ + else if(ch=='B') { + getNch(20); + nothing_response(); + } + + + /* Parallel programming stuff DON'T CARE */ + else if(ch=='E') { + getNch(5); + nothing_response(); + } + + + /* P: Enter programming mode */ + /* R: Erase device, don't care as we will erase one page at a time anyway. */ + else if(ch=='P' || ch=='R') { + nothing_response(); + } + + + /* Leave programming mode */ + else if(ch=='Q') { + nothing_response(); +#ifdef WATCHDOG_MODS + // autoreset via watchdog (sneaky!) + WDTCSR = _BV(WDE); + while (1); // 16 ms +#endif + } + + + /* Set address, little endian. EEPROM in bytes, FLASH in words */ + /* Perhaps extra address bytes may be added in future to support > 128kB FLASH. */ + /* This might explain why little endian was used here, big endian used everywhere else. */ + else if(ch=='U') { + address.byte[0] = getch(); + address.byte[1] = getch(); + nothing_response(); + } + + + /* Universal SPI programming command, disabled. Would be used for fuses and lock bits. */ + else if(ch=='V') { + if (getch() == 0x30) { + getch(); + ch = getch(); + getch(); + if (ch == 0) { + byte_response(SIG1); + } else if (ch == 1) { + byte_response(SIG2); + } else { + byte_response(SIG3); + } + } else { + getNch(3); + byte_response(0x00); + } + } + + + /* Write memory, length is big endian and is in bytes */ + else if(ch=='d') { + length.byte[1] = getch(); + length.byte[0] = getch(); + flags.eeprom = 0; + if (getch() == 'E') flags.eeprom = 1; + for (w=0;w127) address_high = 0x01; //Only possible with m128, m256 will need 3rd address byte. FIXME + else address_high = 0x00; +#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) + RAMPZ = address_high; +#endif + address.word = address.word << 1; //address * 2 -> byte location + /* if ((length.byte[0] & 0x01) == 0x01) length.word++; //Even up an odd number of bytes */ + if ((length.byte[0] & 0x01)) length.word++; //Even up an odd number of bytes + cli(); //Disable interrupts, just to be sure +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) + while(bit_is_set(EECR,EEPE)); //Wait for previous EEPROM writes to complete +#else + while(bit_is_set(EECR,EEWE)); //Wait for previous EEPROM writes to complete +#endif + asm volatile( + "clr r17 \n\t" //page_word_count + "lds r30,address \n\t" //Address of FLASH location (in bytes) + "lds r31,address+1 \n\t" + "ldi r28,lo8(buff) \n\t" //Start of buffer array in RAM + "ldi r29,hi8(buff) \n\t" + "lds r24,length \n\t" //Length of data to be written (in bytes) + "lds r25,length+1 \n\t" + "length_loop: \n\t" //Main loop, repeat for number of words in block + "cpi r17,0x00 \n\t" //If page_word_count=0 then erase page + "brne no_page_erase \n\t" + "wait_spm1: \n\t" + "lds r16,%0 \n\t" //Wait for previous spm to complete + "andi r16,1 \n\t" + "cpi r16,1 \n\t" + "breq wait_spm1 \n\t" + "ldi r16,0x03 \n\t" //Erase page pointed to by Z + "sts %0,r16 \n\t" + "spm \n\t" +#ifdef __AVR_ATmega163__ + ".word 0xFFFF \n\t" + "nop \n\t" +#endif + "wait_spm2: \n\t" + "lds r16,%0 \n\t" //Wait for previous spm to complete + "andi r16,1 \n\t" + "cpi r16,1 \n\t" + "breq wait_spm2 \n\t" + + "ldi r16,0x11 \n\t" //Re-enable RWW section + "sts %0,r16 \n\t" + "spm \n\t" +#ifdef __AVR_ATmega163__ + ".word 0xFFFF \n\t" + "nop \n\t" +#endif + "no_page_erase: \n\t" + "ld r0,Y+ \n\t" //Write 2 bytes into page buffer + "ld r1,Y+ \n\t" + + "wait_spm3: \n\t" + "lds r16,%0 \n\t" //Wait for previous spm to complete + "andi r16,1 \n\t" + "cpi r16,1 \n\t" + "breq wait_spm3 \n\t" + "ldi r16,0x01 \n\t" //Load r0,r1 into FLASH page buffer + "sts %0,r16 \n\t" + "spm \n\t" + + "inc r17 \n\t" //page_word_count++ + "cpi r17,%1 \n\t" + "brlo same_page \n\t" //Still same page in FLASH + "write_page: \n\t" + "clr r17 \n\t" //New page, write current one first + "wait_spm4: \n\t" + "lds r16,%0 \n\t" //Wait for previous spm to complete + "andi r16,1 \n\t" + "cpi r16,1 \n\t" + "breq wait_spm4 \n\t" +#ifdef __AVR_ATmega163__ + "andi r30,0x80 \n\t" // m163 requires Z6:Z1 to be zero during page write +#endif + "ldi r16,0x05 \n\t" //Write page pointed to by Z + "sts %0,r16 \n\t" + "spm \n\t" +#ifdef __AVR_ATmega163__ + ".word 0xFFFF \n\t" + "nop \n\t" + "ori r30,0x7E \n\t" // recover Z6:Z1 state after page write (had to be zero during write) +#endif + "wait_spm5: \n\t" + "lds r16,%0 \n\t" //Wait for previous spm to complete + "andi r16,1 \n\t" + "cpi r16,1 \n\t" + "breq wait_spm5 \n\t" + "ldi r16,0x11 \n\t" //Re-enable RWW section + "sts %0,r16 \n\t" + "spm \n\t" +#ifdef __AVR_ATmega163__ + ".word 0xFFFF \n\t" + "nop \n\t" +#endif + "same_page: \n\t" + "adiw r30,2 \n\t" //Next word in FLASH + "sbiw r24,2 \n\t" //length-2 + "breq final_write \n\t" //Finished + "rjmp length_loop \n\t" + "final_write: \n\t" + "cpi r17,0 \n\t" + "breq block_done \n\t" + "adiw r24,2 \n\t" //length+2, fool above check on length after short page write + "rjmp write_page \n\t" + "block_done: \n\t" + "clr __zero_reg__ \n\t" //restore zero register +#if defined __AVR_ATmega168__ || __AVR_ATmega328P__ || __AVR_ATmega128__ || __AVR_ATmega1280__ || __AVR_ATmega1281__ + : "=m" (SPMCSR) : "M" (PAGE_SIZE) : "r0","r16","r17","r24","r25","r28","r29","r30","r31" +#else + : "=m" (SPMCR) : "M" (PAGE_SIZE) : "r0","r16","r17","r24","r25","r28","r29","r30","r31" +#endif + ); + /* Should really add a wait for RWW section to be enabled, don't actually need it since we never */ + /* exit the bootloader without a power cycle anyhow */ + } + putch(0x14); + putch(0x10); + } else { + if (++error_count == MAX_ERROR_COUNT) + app_start(); + } + } + + + /* Read memory block mode, length is big endian. */ + else if(ch=='t') { + length.byte[1] = getch(); + length.byte[0] = getch(); +#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) + if (address.word>0x7FFF) flags.rampz = 1; // No go with m256, FIXME + else flags.rampz = 0; +#endif + address.word = address.word << 1; // address * 2 -> byte location + if (getch() == 'E') flags.eeprom = 1; + else flags.eeprom = 0; + if (getch() == ' ') { // Command terminator + putch(0x14); + for (w=0;w < length.word;w++) { // Can handle odd and even lengths okay + if (flags.eeprom) { // Byte access EEPROM read +#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) + while(EECR & (1<= 'a') { + return (a - 'a' + 0x0a); + } else if(a >= '0') { + return(a - '0'); + } + return a; +} + + +char gethex(void) { + return (gethexnib() << 4) + gethexnib(); +} + + +void puthex(char ch) { + char ah; + + ah = ch >> 4; + if(ah >= 0x0a) { + ah = ah - 0x0a + 'a'; + } else { + ah += '0'; + } + + ch &= 0x0f; + if(ch >= 0x0a) { + ch = ch - 0x0a + 'a'; + } else { + ch += '0'; + } + + putch(ah); + putch(ch); +} + + +void putch(char ch) +{ +#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) + if(bootuart == 1) { + while (!(UCSR0A & _BV(UDRE0))); + UDR0 = ch; + } + else if (bootuart == 2) { + while (!(UCSR1A & _BV(UDRE1))); + UDR1 = ch; + } +#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) + while (!(UCSR0A & _BV(UDRE0))); + UDR0 = ch; +#else + /* m8,16,32,169,8515,8535,163 */ + while (!(UCSRA & _BV(UDRE))); + UDR = ch; +#endif +} + + +char getch(void) +{ +#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) + uint32_t count = 0; + if(bootuart == 1) { + while(!(UCSR0A & _BV(RXC0))) { + /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/ + /* HACKME:: here is a good place to count times*/ + count++; + if (count > MAX_TIME_COUNT) + app_start(); + } + + return UDR0; + } + else if(bootuart == 2) { + while(!(UCSR1A & _BV(RXC1))) { + /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/ + /* HACKME:: here is a good place to count times*/ + count++; + if (count > MAX_TIME_COUNT) + app_start(); + } + + return UDR1; + } + return 0; +#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) + uint32_t count = 0; + while(!(UCSR0A & _BV(RXC0))){ + /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/ + /* HACKME:: here is a good place to count times*/ + count++; + if (count > MAX_TIME_COUNT) + app_start(); + } + return UDR0; +#else + /* m8,16,32,169,8515,8535,163 */ + uint32_t count = 0; + while(!(UCSRA & _BV(RXC))){ + /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/ + /* HACKME:: here is a good place to count times*/ + count++; + if (count > MAX_TIME_COUNT) + app_start(); + } + return UDR; +#endif +} + + +void getNch(uint8_t count) +{ + while(count--) { +#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega1280__) + if(bootuart == 1) { + while(!(UCSR0A & _BV(RXC0))); + UDR0; + } + else if(bootuart == 2) { + while(!(UCSR1A & _BV(RXC1))); + UDR1; + } +#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) + getch(); +#else + /* m8,16,32,169,8515,8535,163 */ + /* 20060803 DojoCorp:: Addon coming from the previous Bootloader*/ + //while(!(UCSRA & _BV(RXC))); + //UDR; + getch(); // need to handle time out +#endif + } +} + + +void byte_response(uint8_t val) +{ + if (getch() == ' ') { + putch(0x14); + putch(val); + putch(0x10); + } else { + if (++error_count == MAX_ERROR_COUNT) + app_start(); + } +} + + +void nothing_response(void) +{ + if (getch() == ' ') { + putch(0x14); + putch(0x10); + } else { + if (++error_count == MAX_ERROR_COUNT) + app_start(); + } +} + +void flash_led(uint8_t count) +{ + while (count--) { + LED_PORT |= _BV(LED); + _delay_ms(100); + LED_PORT &= ~_BV(LED); + _delay_ms(100); + } +} + + +/* end of file ATmegaBOOT.c */ diff --git a/firmware/bootloader/ATmegaBOOT_168_atmega328_pro_8MHz.hex b/firmware/bootloader/ATmegaBOOT_168_atmega328_pro_8MHz.hex new file mode 100644 index 0000000..cea233f --- /dev/null +++ b/firmware/bootloader/ATmegaBOOT_168_atmega328_pro_8MHz.hex @@ -0,0 +1,125 @@ +:107800000C94343C0C94513C0C94513C0C94513CE1 +:107810000C94513C0C94513C0C94513C0C94513CB4 +:107820000C94513C0C94513C0C94513C0C94513CA4 +:107830000C94513C0C94513C0C94513C0C94513C94 +:107840000C94513C0C94513C0C94513C0C94513C84 +:107850000C94513C0C94513C0C94513C0C94513C74 +:107860000C94513C0C94513C11241FBECFEFD8E036 +:10787000DEBFCDBF11E0A0E0B1E0E8E9FFE702C064 +:1078800005900D92A230B107D9F712E0A2E0B1E065 +:1078900001C01D92AD30B107E1F70E942D3D0C945F +:1078A000CA3F0C94003C982F959595959595959584 +:1078B000905D8F708A307CF0282F295A8091C0000B +:1078C00085FFFCCF9093C6008091C00085FFFCCF60 +:1078D0002093C6000895282F205DF0CF982F809127 +:1078E000C00085FFFCCF9093C6000895EF92FF92F1 +:1078F0000F931F93EE24FF2487018091C00087FD22 +:1079000017C00894E11CF11C011D111D81E4E8164B +:1079100082E4F8068FE0080780E0180770F3E09132 +:107920000401F091050109958091C00087FFE9CF1E +:107930008091C6001F910F91FF90EF9008950E94D3 +:10794000763C982F8091C00085FFFCCF9093C600B5 +:1079500091362CF490330CF09053892F089597555D +:10796000892F08951F930E949F3C182F0E949F3CCF +:107970001295107F810F1F9108951F93182F882350 +:1079800021F00E94763C1150E1F71F9108951F935A +:10799000182F0E94763C803249F0809103018F5F5E +:1079A000809303018530C1F01F9108958091C0003C +:1079B00085FFFCCF84E18093C6008091C00085FFE5 +:1079C000FCCF1093C6008091C00085FFFCCF80E102 +:1079D0008093C6001F910895E0910401F091050184 +:1079E00009951F9108950E94763C803241F0809164 +:1079F00003018F5F80930301853081F008958091AA +:107A0000C00085FFFCCF84E18093C6008091C00058 +:107A100085FFFCCF80E18093C6000895E0910401CA +:107A2000F09105010995089548EC50E08823A1F0F4 +:107A30005B9A28EE33E0FA013197F1F7215030409C +:107A4000D1F75B9828EE33E0FA013197F1F7215036 +:107A50003040D1F7815061F708953F924F925F9285 +:107A60006F927F928F929F92AF92BF92CF92DF924E +:107A7000EF92FF920F931F93CF93DF93809160005B +:107A800088618093600010926000000082E0809323 +:107A9000C00080E18093C4001092C50088E180930B +:107AA000C10086E08093C2005098589A539A81E0B2 +:107AB0000E94143D24E1F22E9EE1E92E85E9D82EA4 +:107AC0000FE0C02E10E1B12EAA24A394B1E49B2EA6 +:107AD000A6E58A2EF2E57F2EE0E26E2E79E4572E9F +:107AE00063E5462E50E5352E0E94763C8033B1F199 +:107AF0008133B9F1803409F46FC0813409F476C060 +:107B0000823409F485C0853409F488C0803531F1A8 +:107B1000823521F1813511F1853509F485C086352D +:107B200009F48DC0843609F496C0843709F403C182 +:107B3000853709F472C1863709F466C08091030164 +:107B40008F5F80930301853079F6E0910401F09115 +:107B5000050109950E94763C803351F60E94F33C62 +:107B6000C3CF0E94763C803249F78091C00085FFE8 +:107B7000FCCFF092C6008091C00085FFFCCF9092B0 +:107B8000C6008091C00085FFFCCF8092C600809126 +:107B9000C00085FFFCCF7092C6008091C00085FFB9 +:107BA000FCCF6092C6008091C00085FFFCCF509250 +:107BB000C6008091C00085FFFCCF4092C600809136 +:107BC000C00085FFFCCF3092C6008091C00085FFC9 +:107BD000FCCFB092C60088CF0E94763C863808F46D +:107BE000BDCF0E94763C0E94F33C7ECF0E94763C43 +:107BF000803809F49CC0813809F40BC1823809F43B +:107C000030C1883909F48FC080E00E94C73C6CCF36 +:107C100084E10E94BD3C0E94F33C66CF85E00E9457 +:107C2000BD3C0E94F33C60CF0E94763C80930601ED +:107C30000E94763C809307010E94F33C55CF0E943E +:107C4000763C803309F411C183E00E94BD3C80E0A2 +:107C50000E94C73C49CF0E94763C809309020E9453 +:107C6000763C8093080280910C028E7F80930C02F8 +:107C70000E94763C853409F409C1809108029091F4 +:107C80000902892B89F000E010E00E94763CF8019F +:107C9000E85FFE4F80830F5F1F4F80910802909135 +:107CA00009020817190788F30E94763C803209F010 +:107CB00045CF80910C0280FFF5C060910601709164 +:107CC0000701660F771F7093070160930601A0916B +:107CD0000802B09109021097C9F0E8E0F1E09B01B9 +:107CE000AD014E0F5F1FF999FECF32BD21BD8191CD +:107CF00080BDFA9AF99A2F5F3F4FE417F50799F77D +:107D00006A0F7B1F70930701609306018091C0008A +:107D100085FFFCCFF092C6008091C00085FFFCCFAC +:107D2000B092C600E1CE83E00E94C73CDDCE82E087 +:107D30000E94C73CD9CE0E94763C809309020E94E3 +:107D4000763C809308028091060190910701880F8C +:107D5000991F90930701809306010E94763C853419 +:107D600009F49AC080910C028E7F80930C020E94CD +:107D7000763C803209F0B8CE8091C00085FFFCCF00 +:107D8000F092C600A0910802B09109021097C1F1CB +:107D900080910C02082F0170182F16951170E09138 +:107DA0000601F0910701AF014F5F5F4FBA0120E07C +:107DB00030E00023B1F4112339F494918091C00094 +:107DC00085FFFCCF9093C6002F5F3F4FCB010196FC +:107DD000FA012A173B0780F4BC014F5F5F4F002375 +:107DE00051F3F999FECFF2BDE1BDF89A90B58091BB +:107DF000C00085FFFCCFE6CF7093070160930601BA +:107E00008091C00085FDE5CE8091C00085FFF8CF50 +:107E1000E0CE81E00E94C73C67CE0E94763C803273 +:107E200009F08CCE8091C00085FFFCCFF092C60097 +:107E30008091C00085FFFCCFE092C6008091C00019 +:107E400085FFFCCFD092C6008091C00085FFFCCF9B +:107E5000C092C6008091C00085FFFCCFB092C600E2 +:107E600043CE80E10E94C73C3FCE0E94763C0E94F8 +:107E7000763C182F0E94763C112309F483C0113000 +:107E800009F484C08FE00E94C73C2ECE80910C0282 +:107E9000816080930C02F1CE80910C02816080930E +:107EA0000C0265CF809107018823880F880B8A21F7 +:107EB00080930B028091060190910701880F991F12 +:107EC00090930701809306018091080280FF09C00A +:107ED0008091080290910902019690930902809383 +:107EE0000802F894F999FECF1127E0910601F0916C +:107EF0000701C8E0D1E0809108029091090210309A +:107F000091F40091570001700130D9F303E0009320 +:107F10005700E8950091570001700130D9F301E155 +:107F200000935700E895099019900091570001704F +:107F30000130D9F301E000935700E8951395103410 +:107F400098F011270091570001700130D9F305E036 +:107F500000935700E8950091570001700130D9F364 +:107F600001E100935700E8953296029709F0C7CFD8 +:107F7000103011F00296E5CF11248091C00085FFEA +:107F8000C5CEC8CE8EE10E94C73CAECD85E90E9429 +:087F9000C73CAACDF894FFCF15 +:027F9800800067 +:040000030000780081 +:00000001FF diff --git a/firmware/bootloader/Makefile b/firmware/bootloader/Makefile new file mode 100644 index 0000000..aa6eb86 --- /dev/null +++ b/firmware/bootloader/Makefile @@ -0,0 +1,231 @@ +# Makefile for ATmegaBOOT +# E.Lins, 18.7.2005 +# $Id$ +# +# Instructions +# +# To make bootloader .hex file: +# make diecimila +# make lilypad +# make ng +# etc... +# +# To burn bootloader .hex file: +# make diecimila_isp +# make lilypad_isp +# make ng_isp +# etc... + +# program name should not be changed... +PROGRAM = ATmegaBOOT_168 + +# enter the parameters for the avrdude isp tool +ISPTOOL = usbtiny +ISPPORT = usb +ISPSPEED = -B 1 + +MCU_TARGET = atmega168 +LDSECTION = --section-start=.text=0x3800 + +# the efuse should really be 0xf8; since, however, only the lower +# three bits of that byte are used on the atmega168, avrdude gets +# confused if you specify 1's for the higher bits, see: +# http://tinker.it/now/2007/02/24/the-tale-of-avrdude-atmega168-and-extended-bits-fuses/ +# +# similarly, the lock bits should be 0xff instead of 0x3f (to +# unlock the bootloader section) and 0xcf instead of 0x0f (to +# lock it), but since the high two bits of the lock byte are +# unused, avrdude would get confused. + +ISPFUSES = avrdude -c $(ISPTOOL) -p $(MCU_TARGET) -P $(ISPPORT) $(ISPSPEED) \ +-e -u -U lock:w:0x3f:m -U efuse:w:0x$(EFUSE):m -U hfuse:w:0x$(HFUSE):m -U lfuse:w:0x$(LFUSE):m +ISPFLASH = avrdude -c $(ISPTOOL) -p $(MCU_TARGET) -P $(ISPPORT) $(ISPSPEED) \ +-U flash:w:$(PROGRAM)_$(TARGET).hex -U lock:w:0x0f:m + +STK500 = "C:\Program Files\Atmel\AVR Tools\STK500\Stk500.exe" +STK500-1 = $(STK500) -e -d$(MCU_TARGET) -pf -vf -if$(PROGRAM)_$(TARGET).hex \ +-lFF -LFF -f$(HFUSE)$(LFUSE) -EF8 -ms -q -cUSB -I200kHz -s -wt +STK500-2 = $(STK500) -d$(MCU_TARGET) -ms -q -lCF -LCF -cUSB -I200kHz -s -wt + + +OBJ = $(PROGRAM).o +OPTIMIZE = -O2 + +DEFS = +LIBS = + +CC = avr-gcc + +# Override is only needed by avr-lib build system. + +override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) -DF_CPU=$(AVR_FREQ) $(DEFS) +override LDFLAGS = -Wl,$(LDSECTION) +#override LDFLAGS = -Wl,-Map,$(PROGRAM).map,$(LDSECTION) + +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump + +all: + +lilypad: TARGET = lilypad +lilypad: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>1' '-DNUM_LED_FLASHES=3' +lilypad: AVR_FREQ = 8000000L +lilypad: $(PROGRAM)_lilypad.hex + +lilypad_isp: lilypad +lilypad_isp: TARGET = lilypad +lilypad_isp: HFUSE = DD +lilypad_isp: LFUSE = E2 +lilypad_isp: EFUSE = 00 +lilypad_isp: isp + +lilypad_resonator: TARGET = lilypad_resonator +lilypad_resonator: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=3' +lilypad_resonator: AVR_FREQ = 8000000L +lilypad_resonator: $(PROGRAM)_lilypad_resonator.hex + +lilypad_resonator_isp: lilypad_resonator +lilypad_resonator_isp: TARGET = lilypad_resonator +lilypad_resonator_isp: HFUSE = DD +lilypad_resonator_isp: LFUSE = C6 +lilypad_resonator_isp: EFUSE = 00 +lilypad_resonator_isp: isp + +pro8: TARGET = pro_8MHz +pro8: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=1' '-DWATCHDOG_MODS' +pro8: AVR_FREQ = 8000000L +pro8: $(PROGRAM)_pro_8MHz.hex + +pro8_isp: pro8 +pro8_isp: TARGET = pro_8MHz +pro8_isp: HFUSE = DD +pro8_isp: LFUSE = C6 +pro8_isp: EFUSE = 00 +pro8_isp: isp + +pro16: TARGET = pro_16MHz +pro16: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=1' '-DWATCHDOG_MODS' +pro16: AVR_FREQ = 16000000L +pro16: $(PROGRAM)_pro_16MHz.hex + +pro16_isp: pro16 +pro16_isp: TARGET = pro_16MHz +pro16_isp: HFUSE = DD +pro16_isp: LFUSE = C6 +pro16_isp: EFUSE = 00 +pro16_isp: isp + +pro20: TARGET = pro_20mhz +pro20: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=1' '-DWATCHDOG_MODS' +pro20: AVR_FREQ = 20000000L +pro20: $(PROGRAM)_pro_20mhz.hex + +pro20_isp: pro20 +pro20_isp: TARGET = pro_20mhz +pro20_isp: HFUSE = DD +pro20_isp: LFUSE = C6 +pro20_isp: EFUSE = 00 +pro20_isp: isp + +diecimila: TARGET = diecimila +diecimila: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=1' +diecimila: AVR_FREQ = 16000000L +diecimila: $(PROGRAM)_diecimila.hex + +diecimila_isp: diecimila +diecimila_isp: TARGET = diecimila +diecimila_isp: HFUSE = DD +diecimila_isp: LFUSE = FF +diecimila_isp: EFUSE = 00 +diecimila_isp: isp + +ng: TARGET = ng +ng: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>1' '-DNUM_LED_FLASHES=3' +ng: AVR_FREQ = 16000000L +ng: $(PROGRAM)_ng.hex + +ng_isp: ng +ng_isp: TARGET = ng +ng_isp: HFUSE = DD +ng_isp: LFUSE = FF +ng_isp: EFUSE = 00 +ng_isp: isp + +atmega328: TARGET = atmega328 +atmega328: MCU_TARGET = atmega328p +atmega328: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=1' -DBAUD_RATE=57600 +atmega328: AVR_FREQ = 16000000L +atmega328: LDSECTION = --section-start=.text=0x7800 +atmega328: $(PROGRAM)_atmega328.hex + +atmega328_isp: atmega328 +atmega328_isp: TARGET = atmega328 +atmega328_isp: MCU_TARGET = atmega328p +atmega328_isp: HFUSE = DA +atmega328_isp: LFUSE = FF +atmega328_isp: EFUSE = 05 +atmega328_isp: isp + +atmega328_pro8: TARGET = atmega328_pro_8MHz +atmega328_pro8: MCU_TARGET = atmega328p +atmega328_pro8: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>3' '-DNUM_LED_FLASHES=1' -DBAUD_RATE=57600 -DDOUBLE_SPEED +atmega328_pro8: AVR_FREQ = 8000000L +atmega328_pro8: LDSECTION = --section-start=.text=0x7800 +atmega328_pro8: $(PROGRAM)_atmega328_pro_8MHz.hex + +atmega328_pro8_isp: atmega328_pro8 +atmega328_pro8_isp: TARGET = atmega328_pro_8MHz +atmega328_pro8_isp: MCU_TARGET = atmega328p +atmega328_pro8_isp: HFUSE = DA +atmega328_pro8_isp: LFUSE = FF +atmega328_pro8_isp: EFUSE = 05 +atmega328_pro8_isp: isp + +atmega328_prores_isp: atmega328_pro8 +atmega328_prores_isp: TARGET = atmega328_pro_8MHz +atmega328_prores_isp: MCU_TARGET = atmega328p +atmega328_prores_isp: HFUSE = D2 +atmega328_prores_isp: LFUSE = E2 +atmega328_prores_isp: EFUSE = 05 +atmega328_prores_isp: isp + +mega: TARGET = atmega1280 +mega: MCU_TARGET = atmega1280 +mega: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=0' -DBAUD_RATE=57600 +mega: AVR_FREQ = 16000000L +mega: LDSECTION = --section-start=.text=0x1F000 +mega: $(PROGRAM)_atmega1280.hex + +mega_isp: mega +mega_isp: TARGET = atmega1280 +mega_isp: MCU_TARGET = atmega1280 +mega_isp: HFUSE = DA +mega_isp: LFUSE = FF +mega_isp: EFUSE = F5 +mega_isp: isp + +isp: $(TARGET) + $(ISPFUSES) + $(ISPFLASH) + +isp-stk500: $(PROGRAM)_$(TARGET).hex + $(STK500-1) + $(STK500-2) + +%.elf: $(OBJ) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + rm -rf *.o *.elf *.lst *.map *.sym *.lss *.eep *.srec *.bin *.hex + +%.lst: %.elf + $(OBJDUMP) -h -S $< > $@ + +%.hex: %.elf + $(OBJCOPY) -j .text -j .data -O ihex $< $@ + +%.srec: %.elf + $(OBJCOPY) -j .text -j .data -O srec $< $@ + +%.bin: %.elf + $(OBJCOPY) -j .text -j .data -O binary $< $@ \ No newline at end of file diff --git a/firmware/bootloader/instructions.txt b/firmware/bootloader/instructions.txt new file mode 100644 index 0000000..05b4e97 --- /dev/null +++ b/firmware/bootloader/instructions.txt @@ -0,0 +1,2 @@ +We're using the classic Arduino bootloader but for the internal 8MHz oscillator. +Edit the makefile for your type of programmer and type in "make atmega328_prores_isp" \ No newline at end of file diff --git a/firmware/buttons.c b/firmware/buttons.c new file mode 100644 index 0000000..d959b85 --- /dev/null +++ b/firmware/buttons.c @@ -0,0 +1,137 @@ +#include // this contains all the IO port definitions +#include +#include +#include +#include +#include +#include "util.h" +#include "ratt.h" +#include "ks0108.h" +#include "glcd.h" + +// These store the current button states for all 3 buttons. We can +// then query whether the buttons are pressed and released or pressed +// This allows for 'high speed incrementing' when setting the time +volatile uint8_t last_buttonstate = 0, just_pressed = 0, pressed = 0; +volatile uint8_t buttonholdcounter = 0; + +// whether hte alarm is going off +extern volatile uint8_t alarming; + +void initbuttons(void) { + // alarm pin requires a pullup + ALARM_DDR &= ~_BV(ALARM); + ALARM_PORT |= _BV(ALARM); + + // alarm switching is detected by using the pin change interrupt + PCICR = _BV(PCIE0); + PCMSK0 |= _BV(ALARM); + + // The buttons are totem pole'd together so we can read the buttons with one pin + // set up ADC + ADMUX = 2; // listen to ADC2 for button presses + ADCSRB = 0; // free running mode + // enable ADC and interrupts, prescale down to <200KHz + ADCSRA = _BV(ADEN) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1); + ADCSRA |= _BV(ADSC); // start a conversion +} + +uint16_t readADC(void) { + // basically just the busy-wait code to read the ADC and return the value + ADCSRA &= ~_BV(ADIE); // no interrupt + ADCSRA |= _BV(ADSC); // start a conversion + while (! (ADCSRA & _BV(ADIF))); + return ADC; +} + +// Every time the ADC finishes a conversion, we'll see whether +// the buttons have changed +SIGNAL(ADC_vect) { + uint16_t reading, reading2; + sei(); + + // We get called when ADC is ready so no need to request a conversion + reading = ADC; + + if (reading > 735) { + // no presses + pressed = 0; + last_buttonstate = 0; + + ADCSRA |= _BV(ADIE) | _BV(ADSC); // start next conversion + return; + } else if (reading > 610) { + // button 3 "+" pressed + if (! (last_buttonstate & 0x4)) { // was not pressed before + // debounce by taking a second reading + _delay_ms(10); + reading2 = readADC(); + if ( (reading2 > 735) || (reading2 < 610)) { + // was a bounce, ignore it + ADCSRA |= _BV(ADIE) | _BV(ADSC); // start next conversion + return; + } + + buttonholdcounter = 2; // see if we're press-and-holding + // the buttonholdcounter is decremented by a timer! + while (buttonholdcounter) { + reading2 = readADC(); + if ( (reading2 > 735) || (reading2 < 610)) { + // button was released + last_buttonstate &= ~0x4; + + DEBUG(putstring_nl("b3")); + just_pressed |= 0x4; + ADCSRA |= _BV(ADIE) | _BV(ADSC); // start next conversion + return; + } + } + // 2 seconds later... + last_buttonstate |= 0x4; + pressed |= 0x4; // The button was held down (fast advance) + } + + } else if (reading > 270) { + // button 2 "SET" pressed + if (! (last_buttonstate & 0x2)) { // was not pressed before + // debounce by taking a second reading + _delay_ms(10); + reading2 = readADC(); + if ( (reading2 > 610) || (reading2 < 270)) { + // was a bounce, ignore it + ADCSRA |= _BV(ADIE) | _BV(ADSC); // start next conversion + return; + } + DEBUG(putstring_nl("b2")); + just_pressed |= 0x2; + } + last_buttonstate |= 0x2; + pressed |= 0x2; // held down + + } else { + // button 1 "MENU" pressed + if (! (last_buttonstate & 0x1)) { // was not pressed before + // debounce by taking a second reading + _delay_ms(10); + reading2 = readADC(); + if (reading2 > 270) { + // was a bounce, ignore it + ADCSRA |= _BV(ADIE) | _BV(ADSC); // start next conversion + return; + } + DEBUG(putstring_nl("b1")); + just_pressed |= 0x1; + } + last_buttonstate |= 0x1; + pressed |= 0x1; // held down + + } + ADCSRA |= _BV(ADIE) | _BV(ADSC); // start next conversion +} + +// We use the pin change interrupts to detect when alarm changes +SIGNAL(PCINT0_vect) { + // allow interrupts while we're doing this + sei(); + setalarmstate(); +} diff --git a/firmware/config.c b/firmware/config.c new file mode 100644 index 0000000..06b9507 --- /dev/null +++ b/firmware/config.c @@ -0,0 +1,933 @@ +#include // this contains all the IO port definitions +#include +#include +#include +#include +#include +#include "util.h" +#include "ratt.h" +#include "ks0108.h" +#include "glcd.h" + +extern volatile uint8_t time_s, time_m, time_h; +extern volatile uint8_t date_m, date_d, date_y; +extern volatile uint8_t alarm_h, alarm_m; +extern volatile uint8_t last_buttonstate, just_pressed, pressed; +extern volatile uint8_t buttonholdcounter; +extern volatile uint8_t time_format; + +extern volatile uint8_t displaymode; +// This variable keeps track of whether we have not pressed any +// buttons in a few seconds, and turns off the menu display +volatile uint8_t timeoutcounter = 0; + +volatile uint8_t screenmutex = 0; + +int brights_state = 0; +int reds_state = 0; +int lamp_state = 0; +int demo_state = 0; + +// True when the bright lights are on, false when they're turned off +void print_brights_state(int brights_state) +{ + glcdSetAddress(MENU_INDENT, 3); + glcdPutStr("Brights:", NORMAL); + if (brights_state == 1) { + glcdPutStr(" ",NORMAL); + glcdPutStr("ON",NORMAL); + } else { + glcdPutStr(" ",NORMAL); + glcdPutStr("OFF",NORMAL); + } +} + +// True when the red night lights are on, false when they're off +void print_reds_state(int reds_state) +{ + glcdSetAddress(MENU_INDENT, 4); + glcdPutStr("Reds:", NORMAL); + if (reds_state == 1) { + glcdPutStr(" ",NORMAL); + glcdPutStr("ON",NORMAL); + } else { + glcdPutStr(" ",NORMAL); + glcdPutStr("OFF",NORMAL); + } +} + +// True when the lamp is on, false when it's off +void print_lamp_state(int lamp_state) +{ + glcdSetAddress(MENU_INDENT, 5); + glcdPutStr("Lamp:", NORMAL); + if (lamp_state == 1) { + glcdPutStr(" ",NORMAL); + glcdPutStr("ON",NORMAL); + } else { + glcdPutStr(" ",NORMAL); + glcdPutStr("OFF",NORMAL); + } +} + +// True when demo mode is on, false when it's off +void print_demo_state(int demo_state) +{ + glcdSetAddress(MENU_INDENT, 0); + glcdPutStr("Demo Mode:", NORMAL); + if (brights_state == 1) { + glcdPutStr(" ",NORMAL); + glcdPutStr("ON",NORMAL); + } else { + glcdPutStr(" ",NORMAL); + glcdPutStr("OFF",NORMAL); + } +} + +void display_menu(void) +{ + DEBUGP("display menu"); + + screenmutex++; + + glcdClearScreen(); + + glcdSetAddress(MENU_INDENT, 0); + print_demo_state(demo_state); + + glcdSetAddress(MENU_INDENT, 1); + glcdPutStr("Set Alarm: ", NORMAL); + print_alarmhour(alarm_h, NORMAL); + glcdWriteChar(':', NORMAL); + printnumber(alarm_m, NORMAL); + + glcdSetAddress(MENU_INDENT, 2); + glcdPutStr("Set Time: ", NORMAL); + print_timehour(time_h, NORMAL); + glcdWriteChar(':', NORMAL); + printnumber(time_m, NORMAL); + glcdWriteChar(':', NORMAL); + printnumber(time_s, NORMAL); + if (time_format == TIME_12H) { + glcdWriteChar(' ', NORMAL); + if (time_h >= 12) { + glcdWriteChar('P', NORMAL); + } else { + glcdWriteChar('A', NORMAL); + } + } + + glcdSetAddress(MENU_INDENT, 3); + print_brights_state(brights_state); + + glcdSetAddress(MENU_INDENT, 4); + print_reds_state(reds_state); + +/*********** +#ifdef BACKLIGHT_ADJUST + glcdSetAddress(MENU_INDENT, 5); + glcdPutStr("Set Backlight: ", NORMAL); + printnumber(OCR2B>>OCR2B_BITSHIFT,NORMAL); +#endif +***********/ + + glcdSetAddress(MENU_INDENT, 5); + print_lamp_state(lamp_state); + + glcdSetAddress(0, 6); + glcdPutStr("Press MENU to advance", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to set", NORMAL); + + screenmutex--; +} + +void toggle_demo_mode(void) +{ + uint8_t mode = TOGGLE_DEMO_MODE; + + display_menu(); + + screenmutex++; + // put a small arrow next to 'set date' + drawArrow(0, 3, MENU_INDENT -1); + screenmutex--; + + timeoutcounter = INACTIVITYTIMEOUT; + + while (1) { + if (just_pressed & 0x1) { // mode change + return; + } + if (just_pressed || pressed) { + timeoutcounter = INACTIVITYTIMEOUT; + // timeout w/no buttons pressed after 3 seconds? + } else if (!timeoutcounter) { + //timed out! + displaymode = SHOW_TIME; + return; + } + if (just_pressed & 0x2) { + just_pressed = 0; + screenmutex++; + + if (mode == TOGGLE_DEMO_MODE) { + // now it has been selected + mode = SET_TOG_DEMO; + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to toggle ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + }else{ + mode = TOGGLE_DEMO_MODE; + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press MENU to advance.", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to toggle.", NORMAL); + } + } + screenmutex--; + if ((just_pressed & 0x4) || (pressed & 0x4)) { + just_pressed = 0; + + if (mode == SET_TOG_DEMO) { + // toggle demo_state + if(demo_state){ + demo_state = 0; + glcdSetAddress(MENU_INDENT + 15*6, 0); + glcdPutStr("OFF", INVERTED); + }else{ + demo_state = 1; + glcdSetAddress(MENU_INDENT + 15*6, 0); + glcdPutStr("ON", INVERTED); + } + + // use new value + if(demo_state){ + ramp_fast(); + }else{ + all_off(); + } + screenmutex++; + display_menu(); + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to toggle ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + + // put a small arrow next to 'set 12h/24h' + // (I don't think this does anything) + drawArrow(0, 3, MENU_INDENT -1); + glcdSetAddress(MENU_INDENT + 15*6, 0); + if(demo_state){ + glcdPutStr("ON", INVERTED); + }else{ + glcdPutStr("OFF", INVERTED); + } + } + } + screenmutex--; + if (pressed & 0x4) _delay_ms(200); + } +} + +void toggle_brights(void) +{ + uint8_t mode = TOGGLE_BRIGHTS; + + display_menu(); + + screenmutex++; + // put a small arrow next to 'set date' + drawArrow(0, 27, MENU_INDENT -1); + screenmutex--; + + timeoutcounter = INACTIVITYTIMEOUT; + + while (1) { + if (just_pressed & 0x1) { // mode change + return; + } + if (just_pressed || pressed) { + timeoutcounter = INACTIVITYTIMEOUT; + // timeout w/no buttons pressed after 3 seconds? + } else if (!timeoutcounter) { + //timed out! + displaymode = SHOW_TIME; + return; + } + if (just_pressed & 0x2) { + just_pressed = 0; + screenmutex++; + + if (mode == TOGGLE_BRIGHTS) { + // now it has been selected + mode = SET_TOG_BTS; + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to toggle ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + }else{ + mode = TOGGLE_BRIGHTS; + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press MENU to advance.", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to toggle.", NORMAL); + } + } + screenmutex--; + if ((just_pressed & 0x4) || (pressed & 0x4)) { + just_pressed = 0; + + if (mode == SET_TOG_BTS) { + // toggle brights_state + if(brights_state){ + brights_state = 0; + glcdSetAddress(MENU_INDENT + 15*6, 3); + glcdPutStr("OFF", INVERTED); + }else{ + brights_state = 1; + glcdSetAddress(MENU_INDENT + 15*6, 3); + glcdPutStr("ON", INVERTED); + } + + // use new value + if(brights_state){ + brightest_lights(); + }else{ + all_off(); + } + screenmutex++; + display_menu(); + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to toggle ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + + // put a small arrow next to 'set 12h/24h' + // (I don't think this does anything) + drawArrow(0, 27, MENU_INDENT -1); + glcdSetAddress(MENU_INDENT + 15*6, 3); + if(brights_state){ + glcdPutStr("ON", INVERTED); + }else{ + glcdPutStr("OFF", INVERTED); + } + } + } + screenmutex--; + if (pressed & 0x4) _delay_ms(200); + } +} + +void toggle_reds(void) +{ +#if 1 + uint8_t mode = TOGGLE_REDS; + + display_menu(); + + screenmutex++; + // put a small arrow next to 'set date' + drawArrow(0, 35, MENU_INDENT -1); + screenmutex--; + + timeoutcounter = INACTIVITYTIMEOUT; + + while (1) { + if (just_pressed & 0x1) { // mode change + return; + } + if (just_pressed || pressed) { + timeoutcounter = INACTIVITYTIMEOUT; + // timeout w/no buttons pressed after 3 seconds? + } else if (!timeoutcounter) { + //timed out! + displaymode = SHOW_TIME; + return; + } + if (just_pressed & 0x2) { + just_pressed = 0; + screenmutex++; + + if (mode == TOGGLE_REDS) { + // now it has been selected + mode = SET_TOG_RDS; + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to toggle ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + }else{ + mode = TOGGLE_REDS; + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press MENU to advance", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to toggle", NORMAL); + + // put a small arrow next to 'set 12h/24h' + // (I don't think this does anything) + drawArrow(0, 35, MENU_INDENT -1); + if(reds_state){ + reds_state = 0; + glcdSetAddress(MENU_INDENT + 15*6, 4); + glcdPutStr("OFF", INVERTED); + }else{ + reds_state = 1; + glcdSetAddress(MENU_INDENT + 15*6, 4); + glcdPutStr("ON", INVERTED); + } + + } + } + screenmutex--; + if ((just_pressed & 0x4) || (pressed & 0x4)) { + just_pressed = 0; + + if (mode == SET_TOG_RDS) { + // toggle brights_state + if(reds_state){ + reds_state = 0; + }else{ + reds_state = 1; + } + + // use new value + if(reds_state){ + nightlight(); + }else{ + all_off(); + } + screenmutex++; + display_menu(); + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to toggle ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + + // put a small arrow next to 'set 12h/24h' + // (I don't think this does anything) + drawArrow(0, 35, MENU_INDENT -1); + glcdSetAddress(MENU_INDENT + 15*6, 4); + if(reds_state){ + glcdPutStr("ON", INVERTED); + }else{ + glcdPutStr("OFF", INVERTED); + } + } + } + screenmutex--; + if (pressed & 0x4) _delay_ms(200); + } +#endif +} + +void toggle_lamp(void) +{ +#if 1 + uint8_t mode = TOGGLE_LAMP; + + display_menu(); + + screenmutex++; + // put a small arrow next to 'set date' + drawArrow(0, 43, MENU_INDENT -1); + screenmutex--; + + timeoutcounter = INACTIVITYTIMEOUT; + + while (1) { + if (just_pressed & 0x1) { // mode change + return; + } + if (just_pressed || pressed) { + timeoutcounter = INACTIVITYTIMEOUT; + // timeout w/no buttons pressed after 3 seconds? + } else if (!timeoutcounter) { + //timed out! + displaymode = SHOW_TIME; + return; + } + if (just_pressed & 0x2) { + just_pressed = 0; + screenmutex++; + + if (mode == TOGGLE_LAMP) { + // now it has been selected + mode = SET_TOG_LAMP; + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to toggle ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + }else{ + mode = TOGGLE_LAMP; + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press MENU to advance", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to toggle", NORMAL); + + // put a small arrow next to 'set 12h/24h' + // (I don't think this does anything) + drawArrow(0, 43, MENU_INDENT -1); + if(reds_state){ + lamp_state = 0; + glcdSetAddress(MENU_INDENT + 15*6, 5); + glcdPutStr("OFF", INVERTED); + }else{ + lamp_state = 1; + glcdSetAddress(MENU_INDENT + 15*6, 5); + glcdPutStr("ON", INVERTED); + } + + } + } + screenmutex--; + if ((just_pressed & 0x4) || (pressed & 0x4)) { + just_pressed = 0; + + if (mode == SET_TOG_LAMP) { + // toggle brights_state + if(lamp_state){ + lamp_state = 0; + }else{ + lamp_state = 1; + } + + // use new value + if(lamp_state){ + turn_lamp_on(); + }else{ + turn_lamp_off(); + } + screenmutex++; + display_menu(); + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to toggle ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + + // put a small arrow next to 'set 12h/24h' + // (I don't think this does anything) + drawArrow(0, 43, MENU_INDENT -1); + glcdSetAddress(MENU_INDENT + 15*6, 5); + if(lamp_state){ + glcdPutStr("ON", INVERTED); + }else{ + glcdPutStr("OFF", INVERTED); + } + } + } + screenmutex--; + if (pressed & 0x4) _delay_ms(200); + } +#endif +} + +#ifdef BACKLIGHT_ADJUST +void set_backlight(void) +{ + uint8_t mode = SET_BRIGHTNESS; + + display_menu(); + + screenmutex++; + glcdSetAddress(0, 6); + glcdPutStr("Press MENU to exit ", NORMAL); + + // put a small arrow next to 'set 12h/24h' + drawArrow(0, 43, MENU_INDENT -1); + screenmutex--; + + timeoutcounter = INACTIVITYTIMEOUT; + + while (1) { + if (just_pressed & 0x1) { // mode change + return; + } + if (just_pressed || pressed) { + timeoutcounter = INACTIVITYTIMEOUT; + // timeout w/no buttons pressed after 3 seconds? + } else if (!timeoutcounter) { + //timed out! + displaymode = SHOW_TIME; + return; + } + + if (just_pressed & 0x2) { + just_pressed = 0; + screenmutex++; + + if (mode == SET_BRIGHTNESS) { + DEBUG(putstring("Setting backlight")); + // ok now its selected + mode = SET_BRT; + // print the region + glcdSetAddress(MENU_INDENT + 15*6, 5); + printnumber(OCR2B>>OCR2B_BITSHIFT,INVERTED); + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to change ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + } else { + mode = SET_BRIGHTNESS; + // print the region normal + glcdSetAddress(MENU_INDENT + 15*6, 5); + printnumber(OCR2B>>OCR2B_BITSHIFT,NORMAL); + + glcdSetAddress(0, 6); + glcdPutStr("Press MENU to exit", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to set ", NORMAL); + } + screenmutex--; + } + if ((just_pressed & 0x4) || (pressed & 0x4)) { + just_pressed = 0; + + if (mode == SET_BRT) { + OCR2B += OCR2B_PLUS; + if(OCR2B > OCR2A_VALUE) + OCR2B = 0; + screenmutex++; + display_menu(); + glcdSetAddress(0, 6); + glcdPutStr("Press + to change ", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to save ", NORMAL); + + // put a small arrow next to 'set 12h/24h' + drawArrow(0, 43, MENU_INDENT -1); + glcdSetAddress(MENU_INDENT + 15*6, 5); + printnumber(OCR2B>>OCR2B_BITSHIFT,INVERTED); + + screenmutex--; + + eeprom_write_byte((uint8_t *)EE_BRIGHT, OCR2B); + } + } + } +} +#endif + +void set_alarm(void) +{ + uint8_t mode = SET_ALARM; + + display_menu(); + screenmutex++; + // put a small arrow next to 'set alarm' + drawArrow(0, 11, MENU_INDENT -1); + screenmutex--; + timeoutcounter = INACTIVITYTIMEOUT; + + while (1) { + if (just_pressed & 0x1) { // mode change + return; + } + if (just_pressed || pressed) { + timeoutcounter = INACTIVITYTIMEOUT; + // timeout w/no buttons pressed after 3 seconds? + } else if (!timeoutcounter) { + //timed out! + displaymode = SHOW_TIME; + return; + } + if (just_pressed & 0x2) { + just_pressed = 0; + screenmutex++; + + if (mode == SET_ALARM) { + DEBUG(putstring("Set alarm hour")); + // ok now its selected + mode = SET_HOUR; + + // print the hour inverted + print_alarmhour(alarm_h, INVERTED); + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to change hr.", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to set hour", NORMAL); + } else if (mode == SET_HOUR) { + DEBUG(putstring("Set alarm min")); + mode = SET_MIN; + // print the hour normal + glcdSetAddress(MENU_INDENT + 12*6, 1); + print_alarmhour(alarm_h, NORMAL); + // and the minutes inverted + glcdSetAddress(MENU_INDENT + 15*6, 1); + printnumber(alarm_m, INVERTED); + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to change min", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to set mins", NORMAL); + + } else { + mode = SET_ALARM; + // print the hour normal + glcdSetAddress(MENU_INDENT + 12*6, 1); + print_alarmhour(alarm_h, NORMAL); + // and the minutes inverted + glcdSetAddress(MENU_INDENT + 15*6, 1); + printnumber(alarm_m, NORMAL); + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press MENU to advance", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to set", NORMAL); + } + screenmutex--; + } + if ((just_pressed & 0x4) || (pressed & 0x4)) { + just_pressed = 0; + screenmutex++; + + if (mode == SET_HOUR) { + alarm_h = (alarm_h+1) % 24; + // print the hour inverted + print_alarmhour(alarm_h, INVERTED); + eeprom_write_byte((uint8_t *)EE_ALARM_HOUR, alarm_h); + + } + if (mode == SET_MIN) { + alarm_m = (alarm_m+1) % 60; + glcdSetAddress(MENU_INDENT + 15*6, 1); + printnumber(alarm_m, INVERTED); + eeprom_write_byte((uint8_t *)EE_ALARM_MIN, alarm_m); + } + screenmutex--; + if (pressed & 0x4) + _delay_ms(200); + set_begin_seq(alarm_m, alarm_h); + } + } +} + +void set_time(void) +{ + uint8_t mode = SET_TIME; + + uint8_t hour, min, sec; + + hour = time_h; + min = time_m; + sec = time_s; + + display_menu(); + + screenmutex++; + // put a small arrow next to 'set time' + drawArrow(0, 19, MENU_INDENT -1); + screenmutex--; + + timeoutcounter = INACTIVITYTIMEOUT; + + while (1) { + if (just_pressed & 0x1) { // mode change + return; + } + if (just_pressed || pressed) { + timeoutcounter = INACTIVITYTIMEOUT; + // timeout w/no buttons pressed after 3 seconds? + } else if (!timeoutcounter) { + //timed out! + displaymode = SHOW_TIME; + return; + } + if (just_pressed & 0x2) { + just_pressed = 0; + screenmutex++; + + if (mode == SET_TIME) { + DEBUG(putstring("Set time hour")); + // ok now its selected + mode = SET_HOUR; + + // print the hour inverted + glcdSetAddress(MENU_INDENT + 10*6, 2); + print_timehour(hour, INVERTED); + glcdSetAddress(MENU_INDENT + 18*6, 2); + if (time_format == TIME_12H) { + glcdWriteChar(' ', NORMAL); + if (hour >= 12) { + glcdWriteChar('P', INVERTED); + } else { + glcdWriteChar('A', INVERTED); + } + } + + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to change hr.", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to set hour", NORMAL); + } else if (mode == SET_HOUR) { + DEBUG(putstring("Set time min")); + mode = SET_MIN; + // print the hour normal + glcdSetAddress(MENU_INDENT + 10*6, 2); + print_timehour(hour, NORMAL); + // and the minutes inverted + glcdWriteChar(':', NORMAL); + printnumber(min, INVERTED); + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to change min", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to set mins", NORMAL); + + glcdSetAddress(MENU_INDENT + 18*6, 2); + if (time_format == TIME_12H) { + glcdWriteChar(' ', NORMAL); + if (hour >= 12) { + glcdWriteChar('P', NORMAL); + } else { + glcdWriteChar('A', NORMAL); + } + } + } else if (mode == SET_MIN) { + DEBUG(putstring("Set time sec")); + mode = SET_SEC; + // and the minutes normal + if(time_format == TIME_12H) { + glcdSetAddress(MENU_INDENT + 13*6, 2); + } else { + glcdSetAddress(MENU_INDENT + 15*6, 2); + } + printnumber(min, NORMAL); + glcdWriteChar(':', NORMAL); + // and the seconds inverted + printnumber(sec, INVERTED); + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press + to change sec", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to set secs", NORMAL); + } else { + // done! + DEBUG(putstring("done setting time")); + mode = SET_TIME; + // print the seconds normal + if(time_format == TIME_12H) { + glcdSetAddress(MENU_INDENT + 16*6, 2); + } else { + glcdSetAddress(MENU_INDENT + 18*6, 2); + } + printnumber(sec, NORMAL); + // display instructions below + glcdSetAddress(0, 6); + glcdPutStr("Press MENU to advance", NORMAL); + glcdSetAddress(0, 7); + glcdPutStr("Press SET to set", NORMAL); + + time_h = hour; + time_m = min; + time_s = sec; + writei2ctime(time_s, time_m, time_h, 0, date_d, date_m, date_y); + } + screenmutex--; + } + if ((just_pressed & 0x4) || (pressed & 0x4)) { + just_pressed = 0; + screenmutex++; + if (mode == SET_HOUR) { + hour = (hour+1) % 24; + time_h = hour; + + glcdSetAddress(MENU_INDENT + 10*6, 2); + print_timehour(hour, INVERTED); + glcdSetAddress(MENU_INDENT + 18*6, 2); + if (time_format == TIME_12H) { + glcdWriteChar(' ', NORMAL); + if (time_h >= 12) { + glcdWriteChar('P', INVERTED); + } else { + glcdWriteChar('A', INVERTED); + } + } + } + if (mode == SET_MIN) { + min = (min+1) % 60; + if(time_format == TIME_12H) { + glcdSetAddress(MENU_INDENT + 13*6, 2); + } else { + glcdSetAddress(MENU_INDENT + 15*6, 2); + } + printnumber(min, INVERTED); + } + if (mode == SET_SEC) { + sec = (sec+1) % 60; + if(time_format == TIME_12H) { + glcdSetAddress(MENU_INDENT + 16*6, 2); + } else { + glcdSetAddress(MENU_INDENT + 18*6, 2); + } + printnumber(sec, INVERTED); + } + screenmutex--; + if (pressed & 0x4) + _delay_ms(200); + } + } +} + +void print_timehour(uint8_t h, uint8_t inverted) +{ + if (time_format == TIME_12H) { + if (((h + 23)%12 + 1) >= 10 ) { + printnumber((h + 23)%12 + 1, inverted); + } else { + glcdWriteChar(' ', NORMAL); + glcdWriteChar('0' + (h + 23)%12 + 1, inverted); + } + } else { + glcdWriteChar(' ', NORMAL); + glcdWriteChar(' ', NORMAL); + printnumber(h, inverted); + } +} + +void print_alarmhour(uint8_t h, uint8_t inverted) +{ + if (time_format == TIME_12H) { + glcdSetAddress(MENU_INDENT + 18*6, 1); + if (h >= 12) + glcdWriteChar('P', NORMAL); + else + glcdWriteChar('A', NORMAL); + glcdWriteChar('M', NORMAL); + glcdSetAddress(MENU_INDENT + 12*6, 1); + + if (((h + 23)%12 + 1) >= 10 ) { + printnumber((h + 23)%12 + 1, inverted); + } else { + glcdWriteChar(' ', NORMAL); + glcdWriteChar('0' + (h + 23)%12 + 1, inverted); + } + } else { + glcdSetAddress(MENU_INDENT + 12*6, 1); + printnumber(h, inverted); + } +} diff --git a/firmware/font5x7.h b/firmware/font5x7.h new file mode 100644 index 0000000..4e002ef --- /dev/null +++ b/firmware/font5x7.h @@ -0,0 +1,119 @@ +/*! \file font5x7.h \brief Graphic LCD Font (Ascii Characters). */ +//***************************************************************************** +// +// File Name : 'font5x7.h' +// Title : Graphic LCD Font (Ascii Charaters) +// Author : Pascal Stang +// Date : 10/19/2001 +// Revised : 10/19/2001 +// Version : 0.1 +// Target MCU : Atmel AVR +// Editor Tabs : 4 +// +//***************************************************************************** + +#ifndef FONT5X7_H +#define FONT5X7_H + +// standard ascii 5x7 font +// defines ascii characters 0x20-0x7F (32-127) +static unsigned char __attribute__ ((progmem)) Font5x7[] = { + 0x00, 0x00, 0x00, 0x00, 0x00,// (space) + 0x00, 0x00, 0x5F, 0x00, 0x00,// ! + 0x00, 0x07, 0x00, 0x07, 0x00,// " + 0x14, 0x7F, 0x14, 0x7F, 0x14,// # + 0x24, 0x2A, 0x7F, 0x2A, 0x12,// $ + 0x23, 0x13, 0x08, 0x64, 0x62,// % + 0x36, 0x49, 0x55, 0x22, 0x50,// & + 0x00, 0x05, 0x03, 0x00, 0x00,// ' + 0x00, 0x1C, 0x22, 0x41, 0x00,// ( + 0x00, 0x41, 0x22, 0x1C, 0x00,// ) + 0x08, 0x2A, 0x1C, 0x2A, 0x08,// * + 0x08, 0x08, 0x3E, 0x08, 0x08,// + + 0x00, 0x50, 0x30, 0x00, 0x00,// , + 0x08, 0x08, 0x08, 0x08, 0x08,// - + 0x00, 0x60, 0x60, 0x00, 0x00,// . + 0x20, 0x10, 0x08, 0x04, 0x02,// / + 0x3E, 0x51, 0x49, 0x45, 0x3E,// 0 + 0x00, 0x42, 0x7F, 0x40, 0x00,// 1 + 0x42, 0x61, 0x51, 0x49, 0x46,// 2 + 0x21, 0x41, 0x45, 0x4B, 0x31,// 3 + 0x18, 0x14, 0x12, 0x7F, 0x10,// 4 + 0x27, 0x45, 0x45, 0x45, 0x39,// 5 + 0x3C, 0x4A, 0x49, 0x49, 0x30,// 6 + 0x01, 0x71, 0x09, 0x05, 0x03,// 7 + 0x36, 0x49, 0x49, 0x49, 0x36,// 8 + 0x06, 0x49, 0x49, 0x29, 0x1E,// 9 + 0x00, 0x36, 0x36, 0x00, 0x00,// : + 0x00, 0x56, 0x36, 0x00, 0x00,// ; + 0x00, 0x08, 0x14, 0x22, 0x41,// < + 0x14, 0x14, 0x14, 0x14, 0x14,// = + 0x41, 0x22, 0x14, 0x08, 0x00,// > + 0x02, 0x01, 0x51, 0x09, 0x06,// ? + 0x32, 0x49, 0x79, 0x41, 0x3E,// @ + 0x7E, 0x11, 0x11, 0x11, 0x7E,// A + 0x7F, 0x49, 0x49, 0x49, 0x36,// B + 0x3E, 0x41, 0x41, 0x41, 0x22,// C + 0x7F, 0x41, 0x41, 0x22, 0x1C,// D + 0x7F, 0x49, 0x49, 0x49, 0x41,// E + 0x7F, 0x09, 0x09, 0x01, 0x01,// F + 0x3E, 0x41, 0x41, 0x51, 0x32,// G + 0x7F, 0x08, 0x08, 0x08, 0x7F,// H + 0x00, 0x41, 0x7F, 0x41, 0x00,// I + 0x20, 0x40, 0x41, 0x3F, 0x01,// J + 0x7F, 0x08, 0x14, 0x22, 0x41,// K + 0x7F, 0x40, 0x40, 0x40, 0x40,// L + 0x7F, 0x02, 0x04, 0x02, 0x7F,// M + 0x7F, 0x04, 0x08, 0x10, 0x7F,// N + 0x3E, 0x41, 0x41, 0x41, 0x3E,// O + 0x7F, 0x09, 0x09, 0x09, 0x06,// P + 0x3E, 0x41, 0x51, 0x21, 0x5E,// Q + 0x7F, 0x09, 0x19, 0x29, 0x46,// R + 0x46, 0x49, 0x49, 0x49, 0x31,// S + 0x01, 0x01, 0x7F, 0x01, 0x01,// T + 0x3F, 0x40, 0x40, 0x40, 0x3F,// U + 0x1F, 0x20, 0x40, 0x20, 0x1F,// V + 0x7F, 0x20, 0x18, 0x20, 0x7F,// W + 0x63, 0x14, 0x08, 0x14, 0x63,// X + 0x03, 0x04, 0x78, 0x04, 0x03,// Y + 0x61, 0x51, 0x49, 0x45, 0x43,// Z + 0x00, 0x00, 0x7F, 0x41, 0x41,// [ + 0x02, 0x04, 0x08, 0x10, 0x20,// "\" + 0x41, 0x41, 0x7F, 0x00, 0x00,// ] + 0x04, 0x02, 0x01, 0x02, 0x04,// ^ + 0x40, 0x40, 0x40, 0x40, 0x40,// _ + 0x00, 0x01, 0x02, 0x04, 0x00,// ` + 0x20, 0x54, 0x54, 0x54, 0x78,// a + 0x7F, 0x48, 0x44, 0x44, 0x38,// b + 0x38, 0x44, 0x44, 0x44, 0x20,// c + 0x38, 0x44, 0x44, 0x48, 0x7F,// d + 0x38, 0x54, 0x54, 0x54, 0x18,// e + 0x08, 0x7E, 0x09, 0x01, 0x02,// f + 0x08, 0x14, 0x54, 0x54, 0x3C,// g + 0x7F, 0x08, 0x04, 0x04, 0x78,// h + 0x00, 0x44, 0x7D, 0x40, 0x00,// i + 0x20, 0x40, 0x44, 0x3D, 0x00,// j + 0x00, 0x7F, 0x10, 0x28, 0x44,// k + 0x00, 0x41, 0x7F, 0x40, 0x00,// l + 0x7C, 0x04, 0x18, 0x04, 0x78,// m + 0x7C, 0x08, 0x04, 0x04, 0x78,// n + 0x38, 0x44, 0x44, 0x44, 0x38,// o + 0x7C, 0x14, 0x14, 0x14, 0x08,// p + 0x08, 0x14, 0x14, 0x18, 0x7C,// q + 0x7C, 0x08, 0x04, 0x04, 0x08,// r + 0x48, 0x54, 0x54, 0x54, 0x20,// s + 0x04, 0x3F, 0x44, 0x40, 0x20,// t + 0x3C, 0x40, 0x40, 0x20, 0x7C,// u + 0x1C, 0x20, 0x40, 0x20, 0x1C,// v + 0x3C, 0x40, 0x30, 0x40, 0x3C,// w + 0x44, 0x28, 0x10, 0x28, 0x44,// x + 0x0C, 0x50, 0x50, 0x50, 0x3C,// y + 0x44, 0x64, 0x54, 0x4C, 0x44,// z + 0x00, 0x08, 0x36, 0x41, 0x00,// { + 0x00, 0x00, 0x7F, 0x00, 0x00,// | + 0x00, 0x41, 0x36, 0x08, 0x00,// } + 0x08, 0x08, 0x2A, 0x1C, 0x08,// -> + 0x08, 0x1C, 0x2A, 0x08, 0x08 // <- +}; + +#endif diff --git a/firmware/fontgr.h b/firmware/fontgr.h new file mode 100644 index 0000000..54202ef --- /dev/null +++ b/firmware/fontgr.h @@ -0,0 +1,31 @@ +/*! \file fontgr.h \brief Graphic LCD Font (Graphic Characters). */ +//***************************************************************************** +// +// File Name : 'fontgr.h' +// Title : Graphic LCD Font (Graphic Charaters) +// Author : Pascal Stang +// Date : 10/19/2001 +// Revised : 10/19/2001 +// Version : 0.1 +// Target MCU : Atmel AVR +// Editor Tabs : 4 +// +//***************************************************************************** + +#ifndef FONTGR_H +#define FONTGR_H + +#ifndef WIN32 +// AVR specific includes + #include +#endif + +static unsigned char __attribute__ ((progmem)) FontGr[] = +{ +// format is one character per line: +// length, byte array[length] + 0x0B,0x3E,0x41,0x41,0x41,0x41,0x42,0x42,0x42,0x42,0x3C,0x00,// 0. Folder Icon + 0x06,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF // 1. Solid 6x8 block +}; + +#endif diff --git a/firmware/fonttable.h b/firmware/fonttable.h new file mode 100644 index 0000000..3290dff --- /dev/null +++ b/firmware/fonttable.h @@ -0,0 +1,51 @@ +const uint8_t alphatable[] PROGMEM = { + 0xFA, /* a */ + 0x3E, /* b */ + 0x1A, /* c */ + 0x7A, /* d */ + 0xDE, /* e */ + 0x8E, /* f */ + 0xF6, /* g */ + 0x2E, /* h */ + 0x60, /* i */ + 0x78, /* j */ + 0xAE, //k + 0x1C, // l + 0xAA, // m + 0x2A, // n + 0x3A, // o + 0xCE, //p + 0xF3, // q + 0x0A, //r + 0xB6, //s + 0x1E, //t + 0x38, //u + 0x38, //v // fix? + 0xB8, //w + 0x6E, //x + 0x76, // y + 0xDA, //z + /* more */ +}; +PGM_P alphatable_p PROGMEM = alphatable; + +const uint8_t numbertable[] PROGMEM = { + 0xFC /* 0 */, + 0x60 /* 1 */, + 0xDA /* 2 */, + 0xF2 /* 3 */, + 0x66 /* 4 */, + 0xB6 /* 5 */, + 0xBE, /* 6 */ + 0xE0, /* 7 */ + 0xFE, /* 8 */ +#ifdef FEATURE_9 + // Normal 7-segment "9" digit looks the same as letter "g". + // "This notation is ambiguous but the meaning will be clear in context." - Hungerford, "Algebra" + 0xF6, /* 9 */ +#else + // ladyada's version of "9" is non-standard but distinct from letter "g". + 0xE6, /* 9 */ +#endif +}; +PGM_P numbertable_p PROGMEM = numbertable; diff --git a/firmware/glcd.c b/firmware/glcd.c new file mode 100644 index 0000000..2fed670 --- /dev/null +++ b/firmware/glcd.c @@ -0,0 +1,353 @@ +/*! \file glcd.c \brief Graphic LCD API functions. */ +//***************************************************************************** +// +// File Name : 'glcd.c' +// Title : Graphic LCD API functions +// Author : Pascal Stang - Copyright (C) 2002 +// Date : 5/30/2002 +// Revised : 5/30/2002 +// Version : 0.5 +// Target MCU : Atmel AVR +// Editor Tabs : 4 +// +// NOTE: This code is currently below version 1.0, and therefore is considered +// to be lacking in some functionality or documentation, or may not be fully +// tested. Nonetheless, you can expect most functions to work. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#ifndef WIN32 +// AVR specific includes + #include + #include +#endif + +#include "glcd.h" + +// include hardware support +#include "ks0108.h" +// include fonts +#include "font5x7.h" +#include "fontgr.h" + +#include "util.h" + + + + +// graphic routines + +// set dot +void glcdSetDot(u08 x, u08 y) +{ + unsigned char temp; + + //putstring("->addr "); uart_putw_dec(x); + //putstring(", "); uart_putw_dec(y/8); + //putstring_nl(")"); + + glcdSetAddress(x, y/8); + temp = glcdDataRead(); // dummy read + temp = glcdDataRead(); // read back current value + glcdSetAddress(x, y/8); + glcdDataWrite(temp | (1 << (y % 8))); + glcdStartLine(0); +} + +// clear dot +void glcdClearDot(u08 x, u08 y) +{ + unsigned char temp; + + glcdSetAddress(x, y/8); + temp = glcdDataRead(); // dummy read + temp = glcdDataRead(); // read back current value + glcdSetAddress(x, y/8); + glcdDataWrite(temp & ~(1 << (y % 8))); + + glcdStartLine(0); +} + +// draw line +void glcdLine(u08 x1, u08 y1, u08 x2, u08 y2) +{ +}; + +// draw rectangle +void glcdRectangle(u08 x, u08 y, u08 w, u08 h) +{ + /* + unsigned char j; + + for (j = 0; j < a; j++) { + glcdSetDot(x, y + j); + glcdSetDot(x + b - 1, y + j); + } + + for (j = 0; j < b; j++) { + glcdSetDot(x + j, y); + glcdSetDot(x + j, y + a - 1); + } +*/ + // optimized! + + glcdFillRectangle(x, y, 1, h, ON); + glcdFillRectangle(x+w-1, y, 1, h, ON); + glcdFillRectangle(x, y, w, 1, ON); + glcdFillRectangle(x, y+h-1, w, 1, ON); +} + + +// draw filled rectangle +void glcdFillRectangle(u08 x, u08 y, u08 a, u08 b, u08 color) +{ + unsigned char i, j, temp, bitsleft; + signed char k; + + /* +// slow :( + for (i = 0; i < a; i++) { + if ( (x+i) > GLCD_XPIXELS ) + break; + for (j = 0; j < b; j++) { + if ( (y+j) > GLCD_YPIXELS ) + break; + if (color == ON) { + glcdSetDot(x + i, y + j); + } else { + glcdClearDot(x + i, y + j); + } + } + } + */ + + // fast! :) + /* + for (i=0; i < a; i++) { + glcdSetAddress(x+i, y/8); + bitsleft = b; + j = 0; + // first byte is strange + if ((bitsleft % 8) || (y%8)) { + temp = glcdDataRead(); // dummy read + temp = glcdDataRead(); // read back current value + // not on a perfect boundary + for (k=(y%8); k < 8; k++) { + if (bitsleft) { + if (color == ON) + temp |= _BV(k); + else + temp &= ~_BV(k); + bitsleft--; + } + } + glcdSetAddress(x+i, (y+j)/8); + glcdDataWrite(temp); + j = 8; + } + + for(; bitsleft >= 8; bitsleft-=8) { + glcdSetAddress(x+i, (y+j)/8); + if (color == ON) { + glcdDataWrite(0xFF); + } else { + glcdDataWrite(0x00); + } + j+=8; + } + // do the remainder + if (bitsleft) { + glcdSetAddress(x+i, (y+j)/8); + temp = glcdDataRead(); // dummy read + temp = glcdDataRead(); // read back current value + if (color == ON) + temp |= (1 << ((y+b)%8)) - 1; + else + temp &= ~ ((1 << (y+b)%8) - 1); + + glcdSetAddress(x+i, (y+j)/8); + glcdDataWrite(temp); + } + } + */ + + // fastest! + if (y%8) { + for (i=0; i 8-(y%8)) + b -= 8-(y%8); + else + b = 0; + y -= (y%8); + y+=8; + } + // skip to next section + for (j=(y/8); j < (y+b)/8; j++) { + glcdSetAddress(x, j); + for (i=0; i +#endif + +#include "global.h" + +#define LINE1 0 +#define LINE2 1 +#define LINE3 2 +#define LINE4 3 +#define LINE5 4 +#define LINE6 5 +#define LINE7 6 +#define LINE8 7 + +#define ON 1 +#define OFF 0 + +#define INVERTED 1 +#define NORMAL 0 + +// API-level interface commands +// ***** Public Functions ***** + +//! set a dot on the display (x is horiz 0:127, y is vert 0:63) +void glcdSetDot(u08 x, u08 y); + +//! clear a dot on the display (x is horiz 0:127, y is vert 0:63) +void glcdClearDot(u08 x, u08 y); + +//! draw line +void glcdLine(u08 x1, u08 y1, u08 x2, u08 y2); + +//! draw rectangle (coords????) +void glcdRectangle(u08 x, u08 y, u08 a, u08 b); + +void glcdFillRectangle(u08 x, u08 y, u08 a, u08 b, u08 color); + +//! draw circle of at +void glcdCircle(u08 xcenter, u08 ycenter, u08 radius, u08 color); +void glcdFillCircle(u08 xcenter, u08 ycenter, u08 radius, u08 color); + +//! write a standard ascii charater (values 20-127) +// to the display at current position +void glcdWriteChar(unsigned char c, uint8_t inverted); + +//! write a special graphic character/icon +// to the display at current position +void glcdWriteCharGr(u08 grCharIndex); + +// ***** Private Functions ***** (or depricated) +void glcdPutStr(char *data, uint8_t inverted); + + +#endif diff --git a/firmware/global.h b/firmware/global.h new file mode 100644 index 0000000..dd607d2 --- /dev/null +++ b/firmware/global.h @@ -0,0 +1,40 @@ +/*! \file global.h \brief AVRlib project global include. */ +//***************************************************************************** +// +// File Name : 'global.h' +// Title : AVRlib project global include +// Author : Pascal Stang - Copyright (C) 2001-2002 +// Created : 7/12/2001 +// Revised : 9/30/2002 +// Version : 1.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : This include file is designed to contain items useful to all +// code files and projects. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#ifndef GLOBAL_H +#define GLOBAL_H + +// global AVRLIB defines +#include "avrlibdefs.h" +// global AVRLIB types definitions +#include "avrlibtypes.h" + +// project/system dependent defines + +// CPU clock speed +//#define F_CPU 16000000 // 16MHz processor +//#define F_CPU 14745000 // 14.745MHz processor +//#define F_CPU 8000000 // 8MHz processor + //#define F_CPU 7372800 // 7.37MHz processor +//#define F_CPU 4000000 // 4MHz processor +//#define F_CPU 3686400 // 3.69MHz processor +#define CYCLES_PER_US ((F_CPU+500000)/1000000) // cpu cycles per microsecond + +#endif diff --git a/firmware/i2c.c b/firmware/i2c.c new file mode 100644 index 0000000..002ca99 --- /dev/null +++ b/firmware/i2c.c @@ -0,0 +1,568 @@ +/*! \file i2c.c \brief I2C interface using AVR Two-Wire Interface (TWI) hardware. */ +//***************************************************************************** +// +// File Name : 'i2c.c' +// Title : I2C interface using AVR Two-Wire Interface (TWI) hardware +// Author : Pascal Stang - Copyright (C) 2002-2003 +// Created : 2002.06.25 +// Revised : 2003.03.02 +// Version : 0.9 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#include +#include + +#include "i2c.h" +#include "util.h" + +// Standard I2C bit rates are: +// 100KHz for slow speed +// 400KHz for high speed + +#define I2C_DEBUG 1 + +// I2C state and address variables +static volatile eI2cStateType I2cState; +static u08 I2cDeviceAddrRW; +// send/transmit buffer (outgoing data) +static u08 I2cSendData[I2C_SEND_DATA_BUFFER_SIZE]; +static u08 I2cSendDataIndex; +static u08 I2cSendDataLength; +// receive buffer (incoming data) +static u08 I2cReceiveData[I2C_RECEIVE_DATA_BUFFER_SIZE]; +static u08 I2cReceiveDataIndex; +static u08 I2cReceiveDataLength; + +// function pointer to i2c receive routine +//! I2cSlaveReceive is called when this processor +// is addressed as a slave for writing +static void (*i2cSlaveReceive)(u08 receiveDataLength, u08* recieveData); +//! I2cSlaveTransmit is called when this processor +// is addressed as a slave for reading +static u08 (*i2cSlaveTransmit)(u08 transmitDataLengthMax, u08* transmitData); + +// functions +void i2cInit(void) +{ + // set pull-up resistors on I2C bus pins + // TODO: should #ifdef these + sbi(PORTC, 5); // i2c SCL on ATmegaxx8 + sbi(PORTC, 4); // i2c SDA on ATmegaxx8 + + // clear SlaveReceive and SlaveTransmit handler to null + i2cSlaveReceive = 0; + i2cSlaveTransmit = 0; + // set i2c bit rate to 100KHz + i2cSetBitrate(100); + // enable TWI (two-wire interface) + sbi(TWCR, TWEN); + // set state + I2cState = I2C_IDLE; + // enable TWI interrupt and slave address ACK + sbi(TWCR, TWIE); + sbi(TWCR, TWEA); + //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)); + // enable interrupts + sei(); +} + +void i2cSetBitrate(u16 bitrate) +{ + u08 bitrate_div; + // set i2c bitrate + // SCL freq = F_CPU/(16+2*TWBR)) + /* + #ifdef TWPS0 + // for processors with additional bitrate division (mega128) + // SCL freq = F_CPU/(16+2*TWBR*4^TWPS) + // set TWPS to zero + cbi(TWSR, TWPS0); + cbi(TWSR, TWPS1); + #endif + */ + + // calculate bitrate division + bitrate_div = (F_CPU/32)/bitrate; + //outb(TWBR, bitrate_div); + TWBR = 32; +} + +void i2cSetLocalDeviceAddr(u08 deviceAddr, u08 genCallEn) +{ + // set local device address (used in slave mode only) + outb(TWAR, ((deviceAddr&0xFE) | (genCallEn?1:0)) ); +} + +void i2cSetSlaveReceiveHandler(void (*i2cSlaveRx_func)(u08 receiveDataLength, u08* recieveData)) +{ + i2cSlaveReceive = i2cSlaveRx_func; +} + +void i2cSetSlaveTransmitHandler(u08 (*i2cSlaveTx_func)(u08 transmitDataLengthMax, u08* transmitData)) +{ + i2cSlaveTransmit = i2cSlaveTx_func; +} + +inline void i2cSendStart(void) +{ + // send start condition + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA)); +} + +inline void i2cSendStop(void) +{ + // transmit stop condition + // leave with TWEA on for slave receiving + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)|BV(TWSTO)); +} + +inline void i2cWaitForComplete(void) +{ + // wait for i2c interface to complete operation + while( !(inb(TWCR) & BV(TWINT)) ); +} + +inline void i2cSendByte(u08 data) +{ + // save data to the TWDR + outb(TWDR, data); + // begin send + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)); +} + +inline void i2cReceiveByte(u08 ackFlag) +{ + // begin receive over i2c + if( ackFlag ) + { + // ackFlag = TRUE: ACK the recevied data + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)); + } + else + { + // ackFlag = FALSE: NACK the recevied data + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)); + } +} + +inline u08 i2cGetReceivedByte(void) +{ + // retieve received data byte from i2c TWDR + return( inb(TWDR) ); +} + +inline u08 i2cGetStatus(void) +{ + // retieve current i2c status from i2c TWSR + return( inb(TWSR) ); +} + +void i2cMasterSend(u08 deviceAddr, u08 length, u08* data) +{ + u08 i; + // wait for interface to be ready + while(I2cState); + // set state + I2cState = I2C_MASTER_TX; + // save data + I2cDeviceAddrRW = (deviceAddr & 0xFE); // RW cleared: write operation + for(i=0; i 1) + { + i2cReceiveByte(TRUE); + i2cWaitForComplete(); + *data++ = i2cGetReceivedByte(); + // decrement length + length--; + } + + // accept receive data and nack it (last-byte signal) + i2cReceiveByte(FALSE); + i2cWaitForComplete(); + *data++ = i2cGetReceivedByte(); + } + else + { + // device did not ACK it's address, + // data will not be transferred + // return error + retval = I2C_ERROR_NODEV; + } + + // transmit stop condition + // leave with TWEA on for slave receiving + i2cSendStop(); + + // enable TWI interrupt + sbi(TWCR, TWIE); + + return retval; +} +/* +void i2cMasterTransferNI(u08 deviceAddr, u08 sendlength, u08* senddata, u08 receivelength, u08* receivedata) +{ + // disable TWI interrupt + cbi(TWCR, TWIE); + + // send start condition + i2cSendStart(); + i2cWaitForComplete(); + + // if there's data to be sent, do it + if(sendlength) + { + // send device address with write + i2cSendByte( deviceAddr & 0xFE ); + i2cWaitForComplete(); + + // send data + while(sendlength) + { + i2cSendByte( *senddata++ ); + i2cWaitForComplete(); + sendlength--; + } + } + + // if there's data to be received, do it + if(receivelength) + { + // send repeated start condition + i2cSendStart(); + i2cWaitForComplete(); + + // send device address with read + i2cSendByte( deviceAddr | 0x01 ); + i2cWaitForComplete(); + + // accept receive data and ack it + while(receivelength > 1) + { + i2cReceiveByte(TRUE); + i2cWaitForComplete(); + *receivedata++ = i2cGetReceivedByte(); + // decrement length + receivelength--; + } + + // accept receive data and nack it (last-byte signal) + i2cReceiveByte(TRUE); + i2cWaitForComplete(); + *receivedata++ = i2cGetReceivedByte(); + } + + // transmit stop condition + // leave with TWEA on for slave receiving + i2cSendStop(); + while( !(inb(TWCR) & BV(TWSTO)) ); + + // enable TWI interrupt + sbi(TWCR, TWIE); +} +*/ + +//! I2C (TWI) interrupt service routine +SIGNAL(TWI_vect) +{ + // read status bits + u08 status = inb(TWSR) & TWSR_STATUS_MASK; + + switch(status) + { + // Master General + case TW_START: // 0x08: Sent start condition + case TW_REP_START: // 0x10: Sent repeated start condition + #ifdef I2C_DEBUG + putstring("I2C: M->START\r\n"); + #endif + // send device address + i2cSendByte(I2cDeviceAddrRW); + break; + + // Master Transmitter & Receiver status codes + case TW_MT_SLA_ACK: // 0x18: Slave address acknowledged + case TW_MT_DATA_ACK: // 0x28: Data acknowledged + #ifdef I2C_DEBUG + putstring("I2C: MT->SLA_ACK or DATA_ACK\r\n"); + #endif + if(I2cSendDataIndex < I2cSendDataLength) + { + // send data + i2cSendByte( I2cSendData[I2cSendDataIndex++] ); + } + else + { + // transmit stop condition, enable SLA ACK + i2cSendStop(); + // set state + I2cState = I2C_IDLE; + } + break; + case TW_MR_DATA_NACK: // 0x58: Data received, NACK reply issued + #ifdef I2C_DEBUG + putstring("I2C: MR->DATA_NACK\r\n"); + #endif + // store final received data byte + I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR); + // continue to transmit STOP condition + case TW_MR_SLA_NACK: // 0x48: Slave address not acknowledged + case TW_MT_SLA_NACK: // 0x20: Slave address not acknowledged + case TW_MT_DATA_NACK: // 0x30: Data not acknowledged + #ifdef I2C_DEBUG + putstring("I2C: MTR->SLA_NACK or MT->DATA_NACK\r\n"); + #endif + // transmit stop condition, enable SLA ACK + i2cSendStop(); + // set state + I2cState = I2C_IDLE; + break; + case TW_MT_ARB_LOST: // 0x38: Bus arbitration lost + //case TW_MR_ARB_LOST: // 0x38: Bus arbitration lost + #ifdef I2C_DEBUG + putstring("I2C: MT->ARB_LOST\r\n"); + #endif + // release bus + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)); + // set state + I2cState = I2C_IDLE; + // release bus and transmit start when bus is free + //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA)); + break; + case TW_MR_DATA_ACK: // 0x50: Data acknowledged + #ifdef I2C_DEBUG + putstring("I2C: MR->DATA_ACK\r\n"); + #endif + // store received data byte + uint8_t x = inb(TWDR); + I2cReceiveData[I2cReceiveDataIndex++] = x; + uart_putw_hex(x); + // fall-through to see if more bytes will be received + case TW_MR_SLA_ACK: // 0x40: Slave address acknowledged + #ifdef I2C_DEBUG + putstring("I2C: MR->SLA_ACK\r\n"); + #endif + if(I2cReceiveDataIndex < (I2cReceiveDataLength-1)) + // data byte will be received, reply with ACK (more bytes in transfer) + i2cReceiveByte(TRUE); + else + // data byte will be received, reply with NACK (final byte in transfer) + i2cReceiveByte(FALSE); + break; + + // Slave Receiver status codes + case TW_SR_SLA_ACK: // 0x60: own SLA+W has been received, ACK has been returned + case TW_SR_ARB_LOST_SLA_ACK: // 0x68: own SLA+W has been received, ACK has been returned + case TW_SR_GCALL_ACK: // 0x70: GCA+W has been received, ACK has been returned + case TW_SR_ARB_LOST_GCALL_ACK: // 0x78: GCA+W has been received, ACK has been returned + #ifdef I2C_DEBUG + putstring("I2C: SR->SLA_ACK\r\n"); + #endif + // we are being addressed as slave for writing (data will be received from master) + // set state + I2cState = I2C_SLAVE_RX; + // prepare buffer + I2cReceiveDataIndex = 0; + // receive data byte and return ACK + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)); + break; + case TW_SR_DATA_ACK: // 0x80: data byte has been received, ACK has been returned + case TW_SR_GCALL_DATA_ACK: // 0x90: data byte has been received, ACK has been returned + #ifdef I2C_DEBUG + putstring("I2C: SR->DATA_ACK\r\n"); + #endif + // get previously received data byte + I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR); + // check receive buffer status + if(I2cReceiveDataIndex < I2C_RECEIVE_DATA_BUFFER_SIZE) + { + // receive data byte and return ACK + i2cReceiveByte(TRUE); + //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)); + } + else + { + // receive data byte and return NACK + i2cReceiveByte(FALSE); + //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)); + } + break; + case TW_SR_DATA_NACK: // 0x88: data byte has been received, NACK has been returned + case TW_SR_GCALL_DATA_NACK: // 0x98: data byte has been received, NACK has been returned + #ifdef I2C_DEBUG + putstring("I2C: SR->DATA_NACK\r\n"); + #endif + // receive data byte and return NACK + i2cReceiveByte(FALSE); + //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)); + break; + case TW_SR_STOP: // 0xA0: STOP or REPEATED START has been received while addressed as slave + #ifdef I2C_DEBUG + putstring("I2C: SR->SR_STOP\r\n"); + #endif + // switch to SR mode with SLA ACK + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)); + // i2c receive is complete, call i2cSlaveReceive + if(i2cSlaveReceive) i2cSlaveReceive(I2cReceiveDataIndex, I2cReceiveData); + // set state + I2cState = I2C_IDLE; + break; + + // Slave Transmitter + case TW_ST_SLA_ACK: // 0xA8: own SLA+R has been received, ACK has been returned + case TW_ST_ARB_LOST_SLA_ACK: // 0xB0: GCA+R has been received, ACK has been returned + #ifdef I2C_DEBUG + putstring("I2C: ST->SLA_ACK\r\n"); + #endif + // we are being addressed as slave for reading (data must be transmitted back to master) + // set state + I2cState = I2C_SLAVE_TX; + // request data from application + if(i2cSlaveTransmit) I2cSendDataLength = i2cSlaveTransmit(I2C_SEND_DATA_BUFFER_SIZE, I2cSendData); + // reset data index + I2cSendDataIndex = 0; + // fall-through to transmit first data byte + case TW_ST_DATA_ACK: // 0xB8: data byte has been transmitted, ACK has been received + #ifdef I2C_DEBUG + putstring("I2C: ST->DATA_ACK\r\n"); + #endif + // transmit data byte + outb(TWDR, I2cSendData[I2cSendDataIndex++]); + if(I2cSendDataIndex < I2cSendDataLength) + // expect ACK to data byte + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)); + else + // expect NACK to data byte + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)); + break; + case TW_ST_DATA_NACK: // 0xC0: data byte has been transmitted, NACK has been received + case TW_ST_LAST_DATA: // 0xC8: + #ifdef I2C_DEBUG + putstring("I2C: ST->DATA_NACK or LAST_DATA\r\n"); + #endif + // all done + // switch to open slave + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)); + // set state + I2cState = I2C_IDLE; + break; + + // Misc + case TW_NO_INFO: // 0xF8: No relevant state information + // do nothing + #ifdef I2C_DEBUG + putstring("I2C: NO_INFO\r\n"); + #endif + break; + case TW_BUS_ERROR: // 0x00: Bus error due to illegal start or stop condition + #ifdef I2C_DEBUG + putstring("I2C: BUS_ERROR\r\n"); + #endif + // reset internal hardware and release bus + outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTO)|BV(TWEA)); + // set state + I2cState = I2C_IDLE; + break; + } +} + +eI2cStateType i2cGetState(void) +{ + return I2cState; +} diff --git a/firmware/i2c.h b/firmware/i2c.h new file mode 100644 index 0000000..088bbe6 --- /dev/null +++ b/firmware/i2c.h @@ -0,0 +1,176 @@ +/*! \file i2c.h \brief I2C interface using AVR Two-Wire Interface (TWI) hardware. */ +//***************************************************************************** +// +// File Name : 'i2c.h' +// Title : I2C interface using AVR Two-Wire Interface (TWI) hardware +// Author : Pascal Stang - Copyright (C) 2002-2003 +// Created : 2002.06.25 +// Revised : 2003.03.03 +// Version : 0.9 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +/// \ingroup driver_avr +/// \defgroup i2c I2C Serial Interface Function Library (i2c.c) +/// \code #include "i2c.h" \endcode +/// \par Overview +/// This library provides the high-level functions needed to use the I2C +/// serial interface supported by the hardware of several AVR processors. +/// The library is functional but has not been exhaustively tested yet and is +/// still expanding.  Thanks to the standardization of the I2C protocol and +/// register access, the send and receive commands are everything you need to +/// talk to thousands of different I2C devices including: EEPROMS, Flash memory, +/// MP3 players, A/D and D/A converters, electronic potentiometers, etc. +/// +/// \par About I2C +/// I2C (pronounced "eye-squared-see") is a two-wire bidirectional +/// network designed for easy transfer of information between a wide variety +/// of intelligent devices. Many of the Atmel AVR series processors have +/// hardware support for transmitting and receiving using an I2C-type bus. +/// In addition to the AVRs, there are thousands of other parts made by +/// manufacturers like Philips, Maxim, National, TI, etc that use I2C as +/// their primary means of communication and control. Common device types +/// are A/D & D/A converters, temp sensors, intelligent battery monitors, +/// MP3 decoder chips, EEPROM chips, multiplexing switches, etc. +/// +/// I2C uses only two wires (SDA and SCL) to communicate bidirectionally +/// between devices. I2C is a multidrop network, meaning that you can have +/// several devices on a single bus. Because I2C uses a 7-bit number to +/// identify which device it wants to talk to, you cannot have more than +/// 127 devices on a single bus. +/// +/// I2C ordinarily requires two 4.7K pull-up resistors to power (one each on +/// SDA and SCL), but for small numbers of devices (maybe 1-4), it is enough +/// to activate the internal pull-up resistors in the AVR processor. To do +/// this, set the port pins, which correspond to the I2C pins SDA/SCL, high. +/// For example, on the mega163, sbi(PORTC, 0); sbi(PORTC, 1);. +/// +/// For complete information about I2C, see the Philips Semiconductor +/// website. They created I2C and have the largest family of devices that +/// work with I2C. +/// +/// \Note: Many manufacturers market I2C bus devices under a different or generic +/// bus name like "Two-Wire Interface". This is because Philips still holds +/// "I2C" as a trademark. For example, SMBus and SMBus devices are hardware +/// compatible and closely related to I2C. They can be directly connected +/// to an I2C bus along with other I2C devices are are generally accessed in +/// the same way as I2C devices. SMBus is often found on modern motherboards +/// for temp sensing and other low-level control tasks. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#ifndef I2C_H +#define I2C_H + +#include "global.h" + +// include project-specific configuration +#include "i2cconf.h" + +// TWSR values (not bits) +// (taken from avr-libc twi.h - thank you Marek Michalkiewicz) +// Master +#define TW_START 0x08 +#define TW_REP_START 0x10 +// Master Transmitter +#define TW_MT_SLA_ACK 0x18 +#define TW_MT_SLA_NACK 0x20 +#define TW_MT_DATA_ACK 0x28 +#define TW_MT_DATA_NACK 0x30 +#define TW_MT_ARB_LOST 0x38 +// Master Receiver +#define TW_MR_ARB_LOST 0x38 +#define TW_MR_SLA_ACK 0x40 +#define TW_MR_SLA_NACK 0x48 +#define TW_MR_DATA_ACK 0x50 +#define TW_MR_DATA_NACK 0x58 +// Slave Transmitter +#define TW_ST_SLA_ACK 0xA8 +#define TW_ST_ARB_LOST_SLA_ACK 0xB0 +#define TW_ST_DATA_ACK 0xB8 +#define TW_ST_DATA_NACK 0xC0 +#define TW_ST_LAST_DATA 0xC8 +// Slave Receiver +#define TW_SR_SLA_ACK 0x60 +#define TW_SR_ARB_LOST_SLA_ACK 0x68 +#define TW_SR_GCALL_ACK 0x70 +#define TW_SR_ARB_LOST_GCALL_ACK 0x78 +#define TW_SR_DATA_ACK 0x80 +#define TW_SR_DATA_NACK 0x88 +#define TW_SR_GCALL_DATA_ACK 0x90 +#define TW_SR_GCALL_DATA_NACK 0x98 +#define TW_SR_STOP 0xA0 +// Misc +#define TW_NO_INFO 0xF8 +#define TW_BUS_ERROR 0x00 + +// defines and constants +#define TWCR_CMD_MASK 0x0F +#define TWSR_STATUS_MASK 0xF8 + +// return values +#define I2C_OK 0x00 +#define I2C_ERROR_NODEV 0x01 + +// types +typedef enum +{ + I2C_IDLE = 0, I2C_BUSY = 1, + I2C_MASTER_TX = 2, I2C_MASTER_RX = 3, + I2C_SLAVE_TX = 4, I2C_SLAVE_RX = 5 +} eI2cStateType; + +// functions + +//! Initialize I2C (TWI) interface +void i2cInit(void); + +//! Set the I2C transaction bitrate (in KHz) +void i2cSetBitrate(u16 bitrateKHz); + +// I2C setup and configurations commands +//! Set the local (AVR processor's) I2C device address +void i2cSetLocalDeviceAddr(u08 deviceAddr, u08 genCallEn); + +//! Set the user function which handles receiving (incoming) data as a slave +void i2cSetSlaveReceiveHandler(void (*i2cSlaveRx_func)(u08 receiveDataLength, u08* recieveData)); +//! Set the user function which handles transmitting (outgoing) data as a slave +void i2cSetSlaveTransmitHandler(u08 (*i2cSlaveTx_func)(u08 transmitDataLengthMax, u08* transmitData)); + +// Low-level I2C transaction commands +//! Send an I2C start condition in Master mode +void i2cSendStart(void); +//! Send an I2C stop condition in Master mode +void i2cSendStop(void); +//! Wait for current I2C operation to complete +void i2cWaitForComplete(void); +//! Send an (address|R/W) combination or a data byte over I2C +void i2cSendByte(u08 data); +//! Receive a data byte over I2C +// ackFlag = TRUE if recevied data should be ACK'ed +// ackFlag = FALSE if recevied data should be NACK'ed +void i2cReceiveByte(u08 ackFlag); +//! Pick up the data that was received with i2cReceiveByte() +u08 i2cGetReceivedByte(void); +//! Get current I2c bus status from TWSR +u08 i2cGetStatus(void); + +// high-level I2C transaction commands + +//! send I2C data to a device on the bus +void i2cMasterSend(u08 deviceAddr, u08 length, u08 *data); +//! receive I2C data from a device on the bus +void i2cMasterReceive(u08 deviceAddr, u08 length, u08* data); + +//! send I2C data to a device on the bus (non-interrupt based) +u08 i2cMasterSendNI(u08 deviceAddr, u08 length, u08* data); +//! receive I2C data from a device on the bus (non-interrupt based) +u08 i2cMasterReceiveNI(u08 deviceAddr, u08 length, u08 *data); + +//! Get the current high-level state of the I2C interface +eI2cStateType i2cGetState(void); + +#endif diff --git a/firmware/i2cconf.h b/firmware/i2cconf.h new file mode 100644 index 0000000..cbd61df --- /dev/null +++ b/firmware/i2cconf.h @@ -0,0 +1,28 @@ +/*! \file i2cconf.h \brief I2C (TWI) interface configuration. */ +//***************************************************************************** +// +// File Name : 'i2cconf.h' +// Title : I2C (TWI) interface configuration +// Author : Pascal Stang - Copyright (C) 2002-2003 +// Created : 2002.06.25 +// Revised : 2003.03.02 +// Version : 0.7 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#ifndef I2CCONF_H +#define I2CCONF_H + +// define I2C data buffer sizes +// These buffers are used in interrupt-driven Master sending and receiving, +// and in slave sending and receiving. They must be large enough to store +// the largest I2C packet you expect to send and receive, respectively. +#define I2C_SEND_DATA_BUFFER_SIZE 0x20 +#define I2C_RECEIVE_DATA_BUFFER_SIZE 0x20 + +#endif diff --git a/firmware/ks0108.c b/firmware/ks0108.c new file mode 100644 index 0000000..536f519 --- /dev/null +++ b/firmware/ks0108.c @@ -0,0 +1,452 @@ +/*! \file ks0108.c \brief Graphic LCD driver for HD61202/KS0108 displays. */ +//***************************************************************************** +// +// File Name : 'ks0108.c' +// Title : Graphic LCD driver for HD61202/KS0108 displays +// Author : Pascal Stang - Copyright (C) 2001-2003 +// Date : 10/19/2002 +// Revised : 5/5/2003 +// Version : 0.5 +// Target MCU : Atmel AVR +// Editor Tabs : 4 +// +// NOTE: This code is currently below version 1.0, and therefore is considered +// to be lacking in some functionality or documentation, or may not be fully +// tested. Nonetheless, you can expect most functions to work. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#ifndef WIN32 +// AVR specific includes + #include + #include +#endif + +#include "global.h" +#include "ks0108.h" + +#include "util.h" +// global variables +GrLcdStateType GrLcdState; + +/*************************************************************/ +/********************** LOCAL FUNCTIONS **********************/ +/*************************************************************/ + +void glcdInitHW(void) +{ + // initialize I/O ports + // if I/O interface is in use +#ifdef GLCD_PORT_INTERFACE + + //TODO: make setup of chip select lines contingent on how + // many controllers are actually in the display + + // initialize LCD control lines levels + cbi(GLCD_CTRL_RS_PORT, GLCD_CTRL_RS); + cbi(GLCD_CTRL_RW_PORT, GLCD_CTRL_RW); + cbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + cbi(GLCD_CTRL_CS0_PORT, GLCD_CTRL_CS0); +#ifdef GLCD_CTRL_CS1 + cbi(GLCD_CTRL_CS1_PORT, GLCD_CTRL_CS1); +#endif +#ifdef GLCD_CTRL_CS2 + cbi(GLCD_CTRL_CS2_PORT, GLCD_CTRL_CS2); +#endif +#ifdef GLCD_CTRL_CS3 + cbi(GLCD_CTRL_CS3_PORT, GLCD_CTRL_CS3); +#endif +#ifdef GLCD_CTRL_RESET + cbi(GLCD_CTRL_RESET_PORT, GLCD_CTRL_RESET); +#endif + // initialize LCD control port to output + sbi(GLCD_CTRL_RS_DDR, GLCD_CTRL_RS); + sbi(GLCD_CTRL_RW_DDR, GLCD_CTRL_RW); + sbi(GLCD_CTRL_E_DDR, GLCD_CTRL_E); + sbi(GLCD_CTRL_CS0_DDR, GLCD_CTRL_CS0); +#ifdef GLCD_CTRL_CS1 + sbi(GLCD_CTRL_CS1_DDR, GLCD_CTRL_CS1); +#endif +#ifdef GLCD_CTRL_CS2 + sbi(GLCD_CTRL_CS2_DDR, GLCD_CTRL_CS2); +#endif +#ifdef GLCD_CTRL_CS3 + sbi(GLCD_CTRL_CS3_DDR, GLCD_CTRL_CS3); +#endif +#ifdef GLCD_CTRL_RESET + sbi(GLCD_CTRL_RESET_DDR, GLCD_CTRL_RESET); +#endif + // initialize LCD data + GLCD_DATAH_PORT &= ~(0xF0); + GLCD_DATAL_PORT &= ~(0x0F); + //outb(GLCD_DATA_PORT, 0x00); + // initialize LCD data port to output + GLCD_DATAH_DDR |= 0xF0; + GLCD_DATAL_DDR |= 0x0F; + //outb(GLCD_DATA_DDR, 0xFF); +#endif +} + +void glcdControllerSelect(u08 controller) +{ +#ifdef GLCD_PORT_INTERFACE + //TODO: make control of chip select lines contingent on how + // many controllers are actually in the display + + // unselect all controllers + cbi(GLCD_CTRL_CS0_PORT, GLCD_CTRL_CS0); +#ifdef GLCD_CTRL_CS1 + cbi(GLCD_CTRL_CS1_PORT, GLCD_CTRL_CS1); +#endif +#ifdef GLCD_CTRL_CS2 + cbi(GLCD_CTRL_CS2_PORT, GLCD_CTRL_CS2); +#endif +#ifdef GLCD_CTRL_CS3 + cbi(GLCD_CTRL_CS3_PORT, GLCD_CTRL_CS3); +#endif + + // select requested controller + switch(controller) + { + case 0: sbi(GLCD_CTRL_CS0_PORT, GLCD_CTRL_CS0); break; +#ifdef GLCD_CTRL_CS1 + case 1: sbi(GLCD_CTRL_CS1_PORT, GLCD_CTRL_CS1); break; +#endif +#ifdef GLCD_CTRL_CS2 + case 2: sbi(GLCD_CTRL_CS2_PORT, GLCD_CTRL_CS2); break; +#endif +#ifdef GLCD_CTRL_CS3 + case 3: sbi(GLCD_CTRL_CS3_PORT, GLCD_CTRL_CS3); break; +#endif + default: break; + } +#endif + +} + +void glcdBusyWait(u08 controller) +{ +#ifdef GLCD_PORT_INTERFACE + cli(); + // wait until LCD busy bit goes to zero + // select the controller chip + glcdControllerSelect(controller); + // do a read from control register + //outb(GLCD_DATA_PORT, 0xFF); + GLCD_DATAH_PORT |= 0xF0; + GLCD_DATAL_PORT |= 0x0F; + + cbi(GLCD_CTRL_RS_PORT, GLCD_CTRL_RS); + //outb(GLCD_DATA_DDR, 0x00); + GLCD_DATAH_DDR &= ~(0xF0); + GLCD_DATAL_DDR &= ~(0x0F); + sbi(GLCD_CTRL_RW_PORT, GLCD_CTRL_RW); + sbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + asm volatile ("nop"); asm volatile ("nop"); + //while(inb(GLCD_DATA_PIN) & GLCD_STATUS_BUSY) + while(((GLCD_DATAH_PIN & 0xF0) | (GLCD_DATAL_PIN & 0x0F)) & GLCD_STATUS_BUSY) + { + cbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + sbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + } + cbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + cbi(GLCD_CTRL_RW_PORT, GLCD_CTRL_RW); + // outb(GLCD_DATA_DDR, 0xFF); + GLCD_DATAH_DDR |= 0xF0; + GLCD_DATAL_DDR |= 0x0F; + sei(); +#else + // sbi(MCUCR, SRW); // enable RAM waitstate + // wait until LCD busy bit goes to zero + while(*(volatile unsigned char *) + (GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller) & GLCD_STATUS_BUSY); + // cbi(MCUCR, SRW); // disable RAM waitstate +#endif +} + +void glcdControlWrite(u08 controller, u08 data) +{ +#ifdef GLCD_PORT_INTERFACE + cli(); + glcdBusyWait(controller); // wait until LCD not busy + cbi(GLCD_CTRL_RS_PORT, GLCD_CTRL_RS); + cbi(GLCD_CTRL_RW_PORT, GLCD_CTRL_RW); + sbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + //outb(GLCD_DATA_DDR, 0xFF); + GLCD_DATAH_DDR |= 0xF0; + GLCD_DATAL_DDR |= 0x0F; + //outb(GLCD_DATA_PORT, data); + GLCD_DATAH_PORT &= ~0xF0; // clear top nibble + GLCD_DATAH_PORT |= data & 0xF0; // set top nibble + GLCD_DATAL_PORT &= ~0x0F; // clear bottom nibble + GLCD_DATAL_PORT |= data & 0x0F; // set bottom nibble + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + cbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + sei(); +#else + //sbi(MCUCR, SRW); // enable RAM waitstate + glcdBusyWait(controller); // wait until LCD not busy + *(volatile unsigned char *) (GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller) = data; + //cbi(MCUCR, SRW); // disable RAM waitstate +#endif +} + +u08 glcdControlRead(u08 controller) +{ + register u08 data; +#ifdef GLCD_PORT_INTERFACE + cli(); + glcdBusyWait(controller); // wait until LCD not busy + cbi(GLCD_CTRL_RS_PORT, GLCD_CTRL_RS); + //outb(GLCD_DATA_DDR, 0x00); + GLCD_DATAH_DDR &= ~(0xF0); + GLCD_DATAL_DDR &= ~(0x0F); + sbi(GLCD_CTRL_RW_PORT, GLCD_CTRL_RW); + sbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + //data = inb(GLCD_DATA_PIN); + data = (GLCD_DATAH_PIN & 0xF0) | (GLCD_DATAL_PIN & 0x0F); + cbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + cbi(GLCD_CTRL_RW_PORT, GLCD_CTRL_RW); + //outb(GLCD_DATA_DDR, 0xFF); + GLCD_DATAH_DDR |= 0xF0; + GLCD_DATAL_DDR |= 0x0F; + sei(); +#else + //sbi(MCUCR, SRW); // enable RAM waitstate + glcdBusyWait(controller); // wait until LCD not busy + data = *(volatile unsigned char *) (GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller); + //cbi(MCUCR, SRW); // disable RAM waitstate +#endif + return data; +} + +void glcdDataWrite(u08 data) +{ + register u08 controller = (GrLcdState.lcdXAddr/GLCD_CONTROLLER_XPIXELS); +#ifdef GLCD_PORT_INTERFACE + cli(); + glcdBusyWait(controller); // wait until LCD not busy + sbi(GLCD_CTRL_RS_PORT, GLCD_CTRL_RS); + cbi(GLCD_CTRL_RW_PORT, GLCD_CTRL_RW); + sbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + //outb(GLCD_DATA_DDR, 0xFF); + GLCD_DATAH_DDR |= 0xF0; + GLCD_DATAL_DDR |= 0x0F; + + //outb(GLCD_DATA_PORT, data); + GLCD_DATAH_PORT &= ~0xF0; // clear top nibble + GLCD_DATAH_PORT |= data & 0xF0; // set top nibble + GLCD_DATAL_PORT &= ~0x0F; // clear bottom nibble + GLCD_DATAL_PORT |= data & 0x0F; // set bottom nibble + + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + cbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + sei(); +#else + //sbi(MCUCR, SRW); // enable RAM waitstate + glcdBusyWait(controller); // wait until LCD not busy + *(volatile unsigned char *) (GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller) = data; + //cbi(MCUCR, SRW); // disable RAM waitstate +#endif + + // increment our local address counter + GrLcdState.ctrlr[controller].xAddr++; + GrLcdState.lcdXAddr++; + if(GrLcdState.lcdXAddr >= GLCD_XPIXELS) + { + GrLcdState.lcdYAddr++; + glcdSetYAddress(GrLcdState.lcdYAddr); + glcdSetXAddress(0); + } +} + +u08 glcdDataRead(void) +{ + register u08 data; + register u08 controller = (GrLcdState.lcdXAddr/GLCD_CONTROLLER_XPIXELS); +#ifdef GLCD_PORT_INTERFACE + cli(); + glcdBusyWait(controller); // wait until LCD not busy + sbi(GLCD_CTRL_RS_PORT, GLCD_CTRL_RS); + //outb(GLCD_DATA_DDR, 0x00); + GLCD_DATAH_DDR &= ~(0xF0); + GLCD_DATAL_DDR &= ~(0x0F); + + sbi(GLCD_CTRL_RW_PORT, GLCD_CTRL_RW); + sbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); + //data = inb(GLCD_DATA_PIN); + data = (GLCD_DATAH_PIN & 0xF0) | (GLCD_DATAL_PIN & 0x0F); + + cbi(GLCD_CTRL_E_PORT, GLCD_CTRL_E); + cbi(GLCD_CTRL_RW_PORT, GLCD_CTRL_RW); + sei(); +#else + //sbi(MCUCR, SRW); // enable RAM waitstate + glcdBusyWait(controller); // wait until LCD not busy + data = *(volatile unsigned char *) (GLCD_CONTROLLER0_CTRL_ADDR + GLCD_CONTROLLER_ADDR_OFFSET*controller); + //cbi(MCUCR, SRW); // disable RAM waitstate +#endif + // increment our local address counter + + /* + GrLcdState.ctrlr[controller].xAddr++; + GrLcdState.lcdXAddr++; + if(GrLcdState.lcdXAddr >= GLCD_XPIXELS) + { + GrLcdState.lcdYAddr++; + glcdSetYAddress(GrLcdState.lcdYAddr); + glcdSetXAddress(0); + }*/ + return data; +} + +void glcdReset(u08 resetState) +{ + // reset lcd if argument is true + // run lcd if argument is false +#ifdef GLCD_PORT_INTERFACE +#ifdef GLCD_CTRL_RESET + if(resetState) + cbi(GLCD_CTRL_RESET_PORT, GLCD_CTRL_RESET); + else + sbi(GLCD_CTRL_RESET_PORT, GLCD_CTRL_RESET); +#endif +#endif +} + +void glcdSetXAddress(u08 xAddr) +{ + u08 i; + // record address change locally + GrLcdState.lcdXAddr = xAddr; + + // clear y (col) address on all controllers + for(i=0; i>3); pageAddr++) + { + // set page address + glcdSetAddress(0, pageAddr); + // clear all lines of this page of display memory + for(xAddr=0; xAddrLCD IS BUSY +#define GLCD_STATUS_ONOFF 0x20 // (0)->LCD IS ON +#define GLCD_STATUS_RESET 0x10 // (1)->LCD IS RESET + +// determine the number of controllers +// (make sure we round up for partial use of more than one controller) +#define GLCD_NUM_CONTROLLERS ((GLCD_XPIXELS+GLCD_CONTROLLER_XPIXELS-1)/GLCD_CONTROLLER_XPIXELS) + +// typedefs/structures +typedef struct struct_GrLcdCtrlrStateType +{ + unsigned char xAddr; + unsigned char yAddr; +} GrLcdCtrlrStateType; + +typedef struct struct_GrLcdStateType +{ + unsigned char lcdXAddr; + unsigned char lcdYAddr; + GrLcdCtrlrStateType ctrlr[GLCD_NUM_CONTROLLERS]; +} GrLcdStateType; + +// function prototypes +void glcdInitHW(void); +void glcdBusyWait(u08 controller); +void glcdControlWrite(u08 controller, u08 data); +u08 glcdControlRead(u08 controller); +void glcdDataWrite(u08 data); +u08 glcdDataRead(void); +void glcdSetXAddress(u08 xAddr); +void glcdSetYAddress(u08 yAddr); + + +//! Initialize the display, clear it, and prepare it for access +void glcdInit(void); +//! Clear the display +void glcdClearScreen(void); +//! Set display memory access point back to upper,left corner +void glcdHome(void); +//! Set display memory access point to row [line] and column [col] assuming 5x7 font +void glcdGotoChar(u08 line, u08 col); +//! Set display memory access point to [x] horizontal pixel and [y] vertical line +void glcdSetAddress(u08 x, u08 yLine); +//! Set display memory access point to row [line] and column [col] assuming 5x7 font +void glcdStartLine(u08 start); +//! Generic delay routine for timed glcd access +void glcdDelay(u16 p); +#endif diff --git a/firmware/ks0108conf.h b/firmware/ks0108conf.h new file mode 100644 index 0000000..67ff3d2 --- /dev/null +++ b/firmware/ks0108conf.h @@ -0,0 +1,109 @@ +/*! \file ks0108conf.h \brief Graphic LCD driver configuration. */ +//***************************************************************************** +// +// File Name : 'ks0108conf.h' +// Title : Graphic LCD driver for HD61202/KS0108 displays +// Author : Pascal Stang - Copyright (C) 2001-2003 +// Date : 10/19/2001 +// Revised : 5/1/2003 +// Version : 0.5 +// Target MCU : Atmel AVR +// Editor Tabs : 4 +// +// NOTE: This code is currently below version 1.0, and therefore is considered +// to be lacking in some functionality or documentation, or may not be fully +// tested. Nonetheless, you can expect most functions to work. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + + +#ifndef KS0108CONF_H +#define KS0108CONF_H + +// define LCD hardware interface +// -LCD_MEMORY_INTERFACE assumes that the registers of the LCD have been mapped +// into the external memory space of the AVR processor memory bus +// -LCD_PORT_INTERFACE is a direct-connection interface from port pins to LCD +// SELECT (UNCOMMENT) ONLY ONE! + +// *** NOTE: memory interface is not yet fully supported, but it might work + +//#define GLCD_MEMORY_INTERFACE +#define GLCD_PORT_INTERFACE + +// GLCD_PORT_INTERFACE specifics +#ifdef GLCD_PORT_INTERFACE + // make sure these parameters are not already defined elsewhere + #ifndef GLCD_CTRL_PORT + +#define GLCD_CTRL_RS 7 +#define GLCD_CTRL_RS_PORT PORTB +#define GLCD_CTRL_RS_DDR DDRB + +#define GLCD_CTRL_RW 5 +#define GLCD_CTRL_RW_PORT PORTB +#define GLCD_CTRL_RW_DDR DDRB + +#define GLCD_CTRL_E 4 +#define GLCD_CTRL_E_PORT PORTB +#define GLCD_CTRL_E_DDR DDRB + +#define GLCD_CTRL_CS0 0 +#define GLCD_CTRL_CS0_PORT PORTC +#define GLCD_CTRL_CS0_DDR DDRC + +#define GLCD_CTRL_CS1 2 +#define GLCD_CTRL_CS1_PORT PORTD +#define GLCD_CTRL_CS1_DDR DDRD + + // this too + //#define GLCD_CTRL_RESET PC4 + //#define GLCD_CTRL_RESET_PORT PORTC + //#define GLCD_CTRL_RESET_DDR DDRC + // (*) NOTE: additonal controller chip selects are optional and + // will be automatically used per each step in 64 pixels of display size + // Example: Display with 128 hozizontal pixels uses 2 controllers + #endif + #ifndef GLCD_DATA_PORT + // we split the data port into 2 4byte chunks so we dont bash the SPI or RXTX pins + //#define GLCD_DATA_PORT PORTD // PORT for LCD data signals + // #define GLCD_DATA_DDR DDRD // DDR register of LCD_DATA_PORT + // #define GLCD_DATA_PIN PIND // PIN register of LCD_DATA_PORT + + + #define GLCD_DATAH_PORT PORTD // PORT for LCD data signals + #define GLCD_DATAH_DDR DDRD // DDR register of LCD_DATA_PORT + #define GLCD_DATAH_PIN PIND // PIN register of LCD_DATA_PORT + #define GLCD_DATAL_PORT PORTB // PORT for LCD data signals + #define GLCD_DATAL_DDR DDRB // DDR register of LCD_DATA_PORT + #define GLCD_DATAL_PIN PINB // PIN register of LCD_DATA_PORT + #endif +#endif + +// GLCD_MEMORY_INTERFACE specifics +#ifdef GLCD_MEMORY_INTERFACE + // make sure these parameters are not already defined elsewhere + #ifndef GLCD_CONTROLLER0_CTRL_ADDR + // absolute address of LCD Controller #0 CTRL and DATA registers + #define GLCD_CONTROLLER0_CTRL_ADDR 0x1000 + #define GLCD_CONTROLLER0_DATA_ADDR 0x1001 + // offset of other controllers with respect to controller0 + #define GLCD_CONTROLLER_ADDR_OFFSET 0x0002 + #endif +#endif + + +// LCD geometry defines (change these definitions to adapt code/settings) +#define GLCD_XPIXELS 128 // pixel width of entire display +#define GLCD_YPIXELS 64 // pixel height of entire display +#define GLCD_CONTROLLER_XPIXELS 64 // pixel width of one display controller + +// Set text size of display +// These definitions are not currently used and will probably move to glcd.h +#define GLCD_TEXT_LINES 8 // visible lines +#define GLCD_TEXT_LINE_LENGTH 22 // internal line length + +#endif diff --git a/firmware/ratt.c b/firmware/ratt.c new file mode 100644 index 0000000..7bf9922 --- /dev/null +++ b/firmware/ratt.c @@ -0,0 +1,1102 @@ +#include // this contains all the IO port definitions +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "ratt.h" +#include "ks0108.h" +#include "glcd.h" + + +// NUM_LEDS are num in strip, the 3 is for RGB. Each LED is 1 pixel of RGB. +static uint8_t pixels[NUM_LEDS][3]; + +int LED_seq_running = 0; +int intensity = 0; +int times_through = 0; +int begin_seq_m = 0; +int begin_seq_h = 0; +volatile uint8_t time_s, time_m, time_h; +volatile uint8_t old_h, old_m, old_s; +volatile uint8_t timeunknown = 1; +volatile uint8_t date_m, date_d, date_y; +volatile uint8_t alarming, alarm_on, alarm_tripped, alarm_h, alarm_m; +volatile uint8_t displaymode; +volatile uint8_t volume; +volatile uint8_t sleepmode = 0; +volatile uint8_t region; +volatile uint8_t time_format; +extern volatile uint8_t screenmutex; +volatile uint8_t minute_changed = 0, hour_changed = 0, second_changed = 0; +volatile uint8_t score_mode_timeout = 0; +volatile uint8_t score_mode = SCORE_MODE_TIME; +volatile uint8_t last_score_mode; + +// These store the current button states for all 3 buttons. We can +// then query whether the buttons are pressed and released or pressed +// This allows for 'high speed incrementing' when setting the time +extern volatile uint8_t last_buttonstate, just_pressed, pressed; +extern volatile uint8_t buttonholdcounter; + +extern volatile uint8_t timeoutcounter; + +// How long we have been snoozing +uint16_t snoozetimer = 0; + +SIGNAL(TIMER1_OVF_vect) { + PIEZO_PORT ^= _BV(PIEZO); +} + +volatile uint16_t millis = 0; +volatile uint16_t animticker, alarmticker; +SIGNAL(TIMER0_COMPA_vect) { + if (millis) + millis--; + if (animticker) + animticker--; + + if (alarming && !snoozetimer) { + if (alarmticker == 0) { + // length of beeping + alarmticker = 300; + // makes the beep + if (TCCR1B == 0) { + TCCR1A = 0; + TCCR1B = _BV(WGM12) | _BV(CS10); // CTC with fastest timer + TIMSK1 = _BV(TOIE1) | _BV(OCIE1A); + OCR1A = (F_CPU / ALARM_FREQ) / 2; + } else { + TCCR1B = 0; + // turn off piezo + PIEZO_PORT &= ~_BV(PIEZO); + // end of code that makes the beep + } + } + alarmticker--; + } +} + + +SIGNAL(TIMER1_COMPA_vect) { + PIEZO_PORT ^= _BV(PIEZO); +} + +static inline void +beep(const uint16_t freq, const uint8_t duration) +{ + // use timer 1 for the piezo/buzzer + TCCR1A = 0; + TCCR1B = _BV(WGM12) | _BV(CS10); // CTC with fastest timer + TIMSK1 = _BV(TOIE1) | _BV(OCIE1A); + OCR1A = (F_CPU / freq) / 2; + _delay_ms(duration); + TCCR1B = 0; + // turn off piezo + PIEZO_PORT &= ~_BV(PIEZO); +} + + +void init_eeprom(void) +{ //Set eeprom to a default state. +#ifndef OPTION_DOW_DATELONG + if(eeprom_read_byte((uint8_t *)EE_INIT) != EE_INITIALIZED) { +#else + if(eeprom_read_byte((uint8_t *)EE_INIT) != (EE_INITIALIZED-1)) { +#endif + eeprom_write_byte((uint8_t *)EE_ALARM_HOUR, 8); + eeprom_write_byte((uint8_t *)EE_ALARM_MIN, 0); + eeprom_write_byte((uint8_t *)EE_BRIGHT, OCR2A_VALUE); + eeprom_write_byte((uint8_t *)EE_VOLUME, 1); + eeprom_write_byte((uint8_t *)EE_REGION, REGION_US); + eeprom_write_byte((uint8_t *)EE_TIME_FORMAT, TIME_12H); + eeprom_write_byte((uint8_t *)EE_SNOOZE, 10); +#ifndef OPTION_DOW_DATELONG + eeprom_write_byte((uint8_t *)EE_INIT, EE_INITIALIZED); +#else + eeprom_write_byte((uint8_t *)EE_INIT, EE_INITIALIZED-1); +#endif + } +} + +int main(void) +{ + uint8_t inverted = 0; + uint8_t mcustate; + uint8_t display_date = 0; + + // check if we were reset + mcustate = MCUSR; + MCUSR = 0; + + //Just in case we were reset inside of the glcd init function + //which would happen if the lcd is not plugged in. The end result + //of that, is it will beep, pause, for as long as there is no lcd + //plugged in. + wdt_disable(); + + // setup uart + uart_init(BRRL_192); + DEBUGP("RATT Clock"); + + // set up piezo + PIEZO_DDR |= _BV(PIEZO); + + DEBUGP("clock!"); + clock_init(); + //beep(4000, 100); + + init_eeprom(); + region = eeprom_read_byte((uint8_t *)EE_REGION); + time_format = eeprom_read_byte((uint8_t *)EE_TIME_FORMAT); + DEBUGP("buttons!"); + initbuttons(); + + setalarmstate(); + + // setup 1ms timer on timer0 + TCCR0A = _BV(WGM01); + TCCR0B = _BV(CS01) | _BV(CS00); + OCR0A = 125; + TIMSK0 |= _BV(OCIE0A); + + // turn backlight on + DDRD |= _BV(3); +#ifndef BACKLIGHT_ADJUST + PORTD |= _BV(3); +#else + TCCR2A = _BV(COM2B1); // PWM output on pin D3 + TCCR2A |= _BV(WGM21) | _BV(WGM20); // fast PWM + TCCR2B |= _BV(WGM22); + OCR2A = OCR2A_VALUE; + OCR2B = eeprom_read_byte((uint8_t *)EE_BRIGHT); +#endif + + DDRB |= _BV(5); + beep(4000, 100); + + //glcdInit locks and disables interrupts in one of its + //functions. If the LCD is not plugged in, glcd will run + //forever. For good reason, it would be desirable to know + //that the LCD is plugged in and working correctly as a result. + //This is why we are using a watch dog timer. The lcd should + //initialized in way less than 500 ms. + + wdt_enable(WDTO_2S); + glcdInit(); + glcdClearScreen(); + + initanim(); + initdisplay(0); + + _delay_ms(1000); + LEDsetup(); + + //added by Holly for debugging purposes + beep(4000, 100); + + /********** LOOP LOOP LOOP *******************/ + while (1) { + + /********** Holly's LED strip code (begin) *************/ + // LED_sep_running = 1 during ramp_up process, we must keep + //returning to the function each loop to test whether it is + //time to increase intensity. + + if(LED_seq_running) ramp_up(); + + // if it's time for the alarm to go off, and the sequence + // isn't running, go into the ramp_up function + if((time_h == begin_seq_h) && (time_m == begin_seq_m) && (time_h != 0)) + { + if(!LED_seq_running){ + LED_seq_running = 1; + times_through = 0; + ramp_up(); + } + } + /********** Holly's LED strip code (end) *************/ + +/// begin Trammell's debug code for dead pixels +#if 0 + static int led_counter; + static int value = 0; + pixels[led_counter][0] = value == 0 ? 0xFF : 0; + pixels[led_counter][1] = value == 1 ? 0xFF : 0; + pixels[led_counter][2] = value == 2 ? 0xFF : 0; + if (led_counter < NUM_LEDS) + led_counter++; + else { + led_counter = 0; + if (value == 2) + value = 0; + else + value++; + } + send_pixels(); +#endif +/// end Trammell's code + + animticker = ANIMTICK_MS; + + glcdSetAddress(0, 7); + //glcdPutStr("Holly clock", NORMAL); + printnumber(alarm_h, NORMAL); + glcdWriteChar(':', NORMAL); + printnumber(alarm_m, NORMAL); + //glcdPutStr(alarm_h,alarm_m, NORMAL); + + // check buttons to see if we have interaction stuff to deal with + if(just_pressed && alarming) { + just_pressed = 0; + setsnooze(); + } + + if(display_date==3 && !score_mode_timeout) { + display_date=0; + score_mode = SCORE_MODE_YEAR; + score_mode_timeout = 3; + //drawdisplay(); + } +#ifdef OPTION_DOW_DATELONG + else if(display_date==2 && !score_mode_timeout) { + display_date=3; + score_mode = SCORE_MODE_DATE; + score_mode_timeout = 3; + //drawdisplay(); + } + else if(display_date==1 && !score_mode_timeout) { + display_date=4; + score_mode = SCORE_MODE_DATELONG_MON; + score_mode_timeout = 3; + //drawdisplay(); + } + else if(display_date==4 && !score_mode_timeout) { + display_date=3; + score_mode = SCORE_MODE_DATELONG_DAY; + score_mode_timeout = 3; + } +#endif + + /*if(display_date && !score_mode_timeout) { + if(last_score_mode == SCORE_MODE_DATELONG) { + score_mode = SCORE_MODE_DOW; + score_mode_timeout = 3; + setscore(); + } else if(last_score_mode == SCORE_MODE_DOW) { + + score_mode = SCORE_MODE_DATE; + score_mode_timeout = 3; + setscore(); + } + else if(last_score_mode == SCORE_MODE_DATE) { + score_mode = SCORE_MODE_YEAR; + score_mode_timeout = 3; + setscore(); + display_date = 0; + } + + }*/ + /*if(display_date && !score_mode_timeout) { + score_mode = SCORE_MODE_YEAR; + score_mode_timeout = 3; + setscore(); + display_date = 0; + }*/ + + //Was formally set for just the + button. However, + //because the Set button was never accounted for, If + //the alarm was turned on, and ONLY the set button + //was pushed since then, the alarm would not sound + //at alarm time, but go into a snooze immediately + //after going off. This could potentially make you + //late for work, and had to be fixed. + + if (just_pressed & 0x6) { + just_pressed = 0; +#ifdef OPTION_DOW_DATELONG + if((region == REGION_US) || (region == REGION_EU)) { +#endif + display_date = 3; + score_mode = SCORE_MODE_DATE; +#ifdef OPTION_DOW_DATELONG + } else if ((region == DOW_REGION_US) || (region == DOW_REGION_EU)) { + display_date = 2; + score_mode = SCORE_MODE_DOW; + } else if (region == DATELONG) { + display_date = 4; + score_mode = SCORE_MODE_DATELONG_MON; + } else { + display_date = 1; + score_mode = SCORE_MODE_DOW; + } +#endif + score_mode_timeout = 3; + //drawdisplay(); + } + + if (just_pressed & 0x1) { + just_pressed = 0; + display_date = 0; + score_mode = SCORE_MODE_TIME; + score_mode_timeout = 0; + //drawdisplay(); + switch(displaymode) { + case (SHOW_TIME): + displaymode = TOGGLE_DEMO_MODE; + toggle_demo_mode(); + break; + case TOGGLE_DEMO_MODE: + displaymode = SET_ALARM; + set_alarm(); + break; + case SET_ALARM: + displaymode = SET_TIME; + set_time(); + timeunknown = 0; + break; + case SET_TIME: + displaymode = TOGGLE_BRIGHTS; + //set_date(); + toggle_brights(); + break; + case TOGGLE_BRIGHTS: + displaymode = TOGGLE_REDS; + //set_region(); + toggle_reds(); + break; +//#ifdef BACKLIGHT_ADJUST + case TOGGLE_REDS: + displaymode = TOGGLE_LAMP; + toggle_lamp(); + break; +//#endif + default: + displaymode = SHOW_TIME; + glcdClearScreen(); + initdisplay(0); + } + + if (displaymode == SHOW_TIME) { + glcdClearScreen(); + initdisplay(0); + } + } + + step(); + if (displaymode == SHOW_TIME) { + if (! inverted && alarming && (time_s & 0x1)) { + inverted = 1; + initdisplay(inverted); + } else if ((inverted && ! alarming) || (alarming && inverted && !(time_s & 0x1))) { + inverted = 0; + initdisplay(0); + } else { + PORTB |= _BV(5); + drawdisplay(inverted); + PORTB &= ~_BV(5); + } + } + + while (animticker); + //uart_getchar(); // you would uncomment this so you can manually 'step' + } + + halt(); +} + + +// This turns on/off the alarm when the switch has been +// set. It also displays the alarm time +void setalarmstate(void) +{ + DEBUGP("a"); + if (ALARM_PIN & _BV(ALARM)) { + if (alarm_on) { + // turn off the alarm + alarm_on = 0; + alarm_tripped = 0; + snoozetimer = 0; + if (alarming) { + // if the alarm is going off, we + // should turn it off and quiet the + // speaker + DEBUGP("alarm off"); + alarming = 0; + TCCR1B = 0; + // turn off piezo + PIEZO_PORT &= ~_BV(PIEZO); + } + } + } else { + // Don't display the alarm/beep if we already have + if (!alarm_on) { + // alarm on! + alarm_on = 1; + // reset snoozing + snoozetimer = 0; + score_mode = SCORE_MODE_ALARM; + score_mode_timeout = 3; + //drawdisplay(); + DEBUGP("alarm on"); + } + } +} + + +void drawArrow(uint8_t x, uint8_t y, uint8_t l) +{ + glcdFillRectangle(x, y, l, 1, ON); + glcdSetDot(x+l-2,y-1); + glcdSetDot(x+l-2,y+1); + glcdSetDot(x+l-3,y-2); + glcdSetDot(x+l-3,y+2); +} + + +void printnumber(uint8_t n, uint8_t inverted) +{ + glcdWriteChar(n/10+'0', inverted); + glcdWriteChar(n%10+'0', inverted); +} + +uint8_t readi2ctime(void) +{ + uint8_t regaddr = 0, r; + uint8_t clockdata[8]; + + // check the time from the RTC + cli(); + r = i2cMasterSendNI(0xD0, 1, ®addr); + + if (r != 0) { + DEBUG(putstring("Reading i2c data: ")); DEBUG(uart_putw_dec(r)); DEBUG(putstring_nl("")); + while(1) { + sei(); + beep(4000, 100); + _delay_ms(100); + beep(4000, 100); + _delay_ms(1000); + } + } + + r = i2cMasterReceiveNI(0xD0, 7, &clockdata[0]); + sei(); + + if (r != 0) { + DEBUG(putstring("Reading i2c data: ")); DEBUG(uart_putw_dec(r)); DEBUG(putstring_nl("")); + while(1) { + beep(4000, 100); + _delay_ms(100); + beep(4000, 100); + _delay_ms(1000); + } + } + + time_s = ((clockdata[0] >> 4) & 0x7)*10 + (clockdata[0] & 0xF); + time_m = ((clockdata[1] >> 4) & 0x7)*10 + (clockdata[1] & 0xF); + if (clockdata[2] & _BV(6)) { + // "12 hr" mode + time_h = ((clockdata[2] >> 5) & 0x1)*12 + + ((clockdata[2] >> 4) & 0x1)*10 + (clockdata[2] & 0xF); + } else { + time_h = ((clockdata[2] >> 4) & 0x3)*10 + (clockdata[2] & 0xF); + } + + date_d = ((clockdata[4] >> 4) & 0x3)*10 + (clockdata[4] & 0xF); + date_m = ((clockdata[5] >> 4) & 0x1)*10 + (clockdata[5] & 0xF); + date_y = ((clockdata[6] >> 4) & 0xF)*10 + (clockdata[6] & 0xF); + + return clockdata[0] & 0x80; +} + +void writei2ctime(uint8_t sec, uint8_t min, uint8_t hr, uint8_t day, + uint8_t date, uint8_t mon, uint8_t yr) +{ + uint8_t clockdata[8] = {0,0,0,0,0,0,0,0}; + + clockdata[0] = 0; // address + clockdata[1] = i2bcd(sec); // s + clockdata[2] = i2bcd(min); // m + clockdata[3] = i2bcd(hr); // h + clockdata[4] = i2bcd(day); // day + clockdata[5] = i2bcd(date); // date + clockdata[6] = i2bcd(mon); // month + clockdata[7] = i2bcd(yr); // year + + cli(); + uint8_t r = i2cMasterSendNI(0xD0, 8, &clockdata[0]); + sei(); + + //DEBUG(putstring("Writing i2c data: ")); DEBUG(uart_putw_dec()); DEBUG(putstring_nl("")); + + if (r != 0) { + while(1) { + beep(4000, 100); + _delay_ms(100); + beep(4000, 100); + _delay_ms(1000); + } + } +} + +// runs at about 30 hz +uint8_t t2divider1 = 0, t2divider2 = 0; +SIGNAL (TIMER2_OVF_vect) +{ + wdt_reset(); +#ifdef BACKLIGHT_ADJUST + if (t2divider1 == TIMER2_RETURN) { +#else + if (t2divider1 == 5) { +#endif + t2divider1 = 0; + } else { + t2divider1++; + return; + } + + //This occurs at 6 Hz + + uint8_t last_s = time_s; + uint8_t last_m = time_m; + uint8_t last_h = time_h; + + readi2ctime(); + + if (time_h != last_h) { + hour_changed = 1; + minute_changed = 1; + second_changed = 1; + old_h = last_h; + old_m = last_m; + } else if (time_m != last_m) { + hour_changed = 0; + minute_changed = 1; + second_changed = 1; + old_m = last_m; + } else if (time_s != last_s) { + hour_changed = 0; + minute_changed = 0; + second_changed = 1; + old_s = last_s; + } + + + if (time_s != last_s) { + if(alarming && snoozetimer) + snoozetimer--; + + if(score_mode_timeout) { + score_mode_timeout--; + if(!score_mode_timeout) { + last_score_mode = score_mode; + score_mode = SCORE_MODE_TIME; + if(hour_changed) { + time_h = old_h; + time_m = old_m; + } else if (minute_changed) { + time_m = old_m; + } + if(hour_changed || minute_changed) { + time_h = last_h; + time_m = last_m; + } + } + } + + + DEBUG(putstring("**** ")); + DEBUG(uart_putw_dec(time_h)); + DEBUG(uart_putchar(':')); + DEBUG(uart_putw_dec(time_m)); + DEBUG(uart_putchar(':')); + DEBUG(uart_putw_dec(time_s)); + DEBUG(putstring_nl("****")); + } + + if (((displaymode == SET_ALARM) || + (displaymode == TOGGLE_BRIGHTS) || + (displaymode == TOGGLE_REDS) || + (displaymode == TOGGLE_LAMP)) && + (!screenmutex) ) { + glcdSetAddress(MENU_INDENT + 10*6, 2); + print_timehour(time_h, NORMAL); + glcdWriteChar(':', NORMAL); + printnumber(time_m, NORMAL); + glcdWriteChar(':', NORMAL); + printnumber(time_s, NORMAL); + + if (time_format == TIME_12H) { + glcdWriteChar(' ', NORMAL); + if (time_h >= 12) { + glcdWriteChar('P', NORMAL); + } else { + glcdWriteChar('A', NORMAL); + } + } + } + + // check if we have an alarm set + if (alarm_on && (time_s == 0) && (time_m == alarm_m) && (time_h == alarm_h)) { + DEBUG(putstring_nl("ALARM TRIPPED!!!")); + alarm_tripped = 1; + } + + //if (minute_changed) + // beep(1000, 100); + + //And wait till the score changes to actually set the alarm off. + if(!minute_changed && !hour_changed && alarm_tripped) { + DEBUG(putstring_nl("ALARM GOING!!!!")); + alarming = 1; + // DEBUG DEBUG DEBUG -- can't print to screen in this function + //glcdSetAddress(0, 7); + //glcdPutStr("Is alarm going off?", NORMAL); + // DEBUG DEBUG DEBUG + alarm_tripped = 0; + } + + if (t2divider2 == 6) { + t2divider2 = 0; + } else { + t2divider2++; + return; + } + + if (buttonholdcounter) { + buttonholdcounter--; + } + + if (timeoutcounter) { + timeoutcounter--; + } +} + +uint8_t leapyear(uint16_t y) +{ + return ( (!(y % 4) && (y % 100)) || !(y % 400)); +} + +void tick(void) +{ + +} + +inline uint8_t i2bcd(uint8_t x) +{ + return ((x/10)<<4) | (x%10); +} + +void clock_init(void) +{ + // talk to clock + i2cInit(); + + + if (readi2ctime()) { + DEBUGP("uh oh, RTC was off, lets reset it!"); + writei2ctime(0, 0, 12, 0, 1, 1, 9); // noon 1/1/2009 + } + + readi2ctime(); + + DEBUG(putstring("\n\rread ")); + DEBUG(uart_putw_dec(time_h)); + DEBUG(uart_putchar(':')); + DEBUG(uart_putw_dec(time_m)); + DEBUG(uart_putchar(':')); + DEBUG(uart_putw_dec(time_s)); + + DEBUG(uart_putchar('\t')); + DEBUG(uart_putw_dec(date_d)); + DEBUG(uart_putchar('/')); + DEBUG(uart_putw_dec(date_m)); + DEBUG(uart_putchar('/')); + DEBUG(uart_putw_dec(date_y)); + DEBUG(putstring_nl("")); + + alarm_m = eeprom_read_byte((uint8_t *)EE_ALARM_MIN) % 60; + alarm_h = eeprom_read_byte((uint8_t *)EE_ALARM_HOUR) % 24; + // count 2 min before alarm should go off to begin sequence. + // this will not work near midnight/1 oclock!!! + + set_begin_seq(alarm_m, alarm_h); + + //ASSR |= _BV(AS2); // use crystal + + TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); // div by 1024 + // overflow ~30Hz = 8MHz/(255 * 1024) + + // enable interrupt + TIMSK2 = _BV(TOIE2); + + sei(); +} + +void setsnooze(void) +{ + //snoozetimer = eeprom_read_byte((uint8_t *)EE_SNOOZE); + //snoozetimer *= 60; // convert minutes to seconds + snoozetimer = MAXSNOOZE; + TCCR1B = 0; + // turn off piezo + PIEZO_PORT &= ~_BV(PIEZO); + DEBUGP("snooze"); + //displaymode = SHOW_SNOOZE; + //_delay_ms(1000); + displaymode = SHOW_TIME; +} + +/**************************************************/ +// Support for Holly's light alarm project. +/**************************************************/ + +// These values will ramp up the LEDs at the correct rate, +// assuming intensity levels range from 0 to 255. +static const int lookup[] = { 1, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, + 14, 16, 18 , 20, 22, 25, 28, 30, 33, 36, 39, 42, 46, 49, + 52, 56, 60, 64, 68, 72, 76, 81, 86,90, 95, 100, 105, 110, + 116, 121, 126, 132, 138, 144, 150, 156, 162,169, 176, 182, + 189, 196, 203, 210, 218, 225, 232, 240, 248, 255}; +// check to see if the LEDs are busy running a sequence + +// Turn the lamp on +void turn_lamp_on(void) +{ + DDRC |= 2; + PORTC |= 2; + lamp_state = 1; +} + +// Turn the lamp off +void turn_lamp_off(void) +{ + PORTC &= ~(1 << 1); + lamp_state = 0; +} + +// All lights at brightest possible setting. +void brightest_lights(void) +{ + for(int j = 0 ; j < NUM_LEDS ; j++) + { + pixels[j][0] = 255; + pixels[j][1] = 255; + pixels[j][2] = 255; + } + send_pixels(); + _delay_ms(1000); +} + +// Slowly raise the lights from dark to bright, with bluish cast. +static void ramp_up(void) +{ + // mod 4 is 2 min, mod 64 should be 32 min (it's actually 20) + // 80 is too short, 128 is too long + // if(times_through++ % 32 != 0) + if(times_through++ % 100 != 0) + { + /***** DEBUGGING *******/ + //glcdSetAddress(0, 1); + //printnumber(times_through, NORMAL); + /***** DEBUGGING *******/ + return; + } + + //if(intensity == 60){ + if(intensity == 90){ + // end the sequences with all lights bright + brightest_lights(); + turn_lamp_on(); + // stop entering fx from main loop when + // we're done ramping up + LED_seq_running = 0; + // reset intensity to be ready for tomorrow's ramp_up + intensity = 0; + /************ DEBUG ***********/ + pixels[0][0] = 0; + pixels[0][1] = 255; + pixels[0][2] = 0; + send_pixels(); + /*********** DEBUG ************/ + // we're already at brightest level, so get out of here + return; + } + + // increase brightness to previously set intensity + // set all the pixels on the strip to something +#if 1 + for(int j = 0 ; j < NUM_LEDS ; j++) + { + if(intensity < 30) + { + pixels[j][0] = 0; + pixels[j][1] = 0; + pixels[j][2] = lookup[intensity]; + /***** DEBUGGING *******/ + //glcdSetAddress(0, 2); + //printnumber(intensity, NORMAL); + /***** DEBUGGING *******/ + }else if(intensity < 61){ + pixels[j][0] = lookup[intensity - 30]; + pixels[j][1] = lookup[intensity - 30]; + pixels[j][2] = lookup[intensity]; + //pixels[j][2] = 255; + //glcdSetAddress(0, 1); + //printnumber(intensity, NORMAL); + }else if(intensity < 90 ){ + pixels[j][0] = lookup[intensity - 30]; + pixels[j][1] = lookup[intensity - 30]; + pixels[j][2] = 255; + } + } + //_delay_ms(200); + send_pixels(); + intensity++; +#endif +#if 0 + for(int j = 0 ; j < NUM_LEDS ; j++) + { + if(intensity < 60) + { + if(lookup[intensity] == intensity){ + pixels[j][0] = lookup[intensity]; + pixels[j][1] = lookup[intensity]; + } + pixels[j][2] = intensity; + glcdSetAddress(0, 1); + printnumber(intensity, NORMAL); + } + } +#endif +} + +// Slowly turn the lights off, with bluish cast. +static void ramp_down(void) +{ + for( int intensity = 30 ; intensity > -1 ; intensity = intensity - 1 ) + { + for(int j = 0 ; j < NUM_LEDS ; j++) + { + if(lookup[intensity] < 150) + { + pixels[j][0] = 0; + pixels[j][1] = 0; + pixels[j][2] = lookup[intensity]; + }else{ + pixels[j][0] = lookup[intensity] - 150; + pixels[j][1] = lookup[intensity] - 150; + pixels[j][2] = lookup[intensity]; + } + } + send_pixels(); + _delay_ms(50); + } +} + +// Lights dimly red for night time. +void nightlight(void) +{ + for(int j = 0 ; j < NUM_LEDS ; j++) + { + pixels[j][0] = 70; + pixels[j][1] = 5; + pixels[j][2] = 0; + } + send_pixels(); + _delay_ms(1000); +} + +// Shuts all the aEDs off. +void all_off(void) +{ + for(int j = 0 ; j < NUM_LEDS ; j++) + { + pixels[j][0] = 0; + pixels[j][1] = 0; + pixels[j][2] = 0; + } + send_pixels(); +} + +// Quickly raise the lights from dark to bright, with bluish cast, for demo. +void ramp_fast(void) +{ + //if(intensity == 60){ + // increase brightness to previously set intensity + // set all the pixels on the strip to something +#if 1 + while(intensity <= 89){ + + for(int j = 0 ; j < NUM_LEDS ; j++) + { + if(intensity < 30) + { + pixels[j][0] = 0; + pixels[j][1] = 0; + pixels[j][2] = lookup[intensity]; + /***** DEBUGGING *******/ + //glcdSetAddress(0, 2); + //printnumber(intensity, NORMAL); + /***** DEBUGGING *******/ + }else if(intensity < 61){ + pixels[j][0] = lookup[intensity - 30]; + pixels[j][1] = lookup[intensity - 30]; + pixels[j][2] = lookup[intensity]; + //pixels[j][2] = 255; + //glcdSetAddress(0, 1); + //printnumber(intensity, NORMAL); + }else if(intensity < 90 ){ + pixels[j][0] = lookup[intensity - 30]; + pixels[j][1] = lookup[intensity - 30]; + pixels[j][2] = 255; + } + } + _delay_ms(100); + send_pixels(); + intensity++; + } + + if(intensity == 90){ + // end the sequences with all lights bright + brightest_lights(); + turn_lamp_on(); + // reset intensity to be ready for tomorrow's ramp_up + intensity = 0; + /************ DEBUG ***********/ + pixels[0][0] = 0; + pixels[0][1] = 255; + pixels[0][2] = 0; + send_pixels(); + /*********** DEBUG ************/ + // we're already at brightest level, so get out of here + return; + } + +#endif +#if 0 + for(int j = 0 ; j < NUM_LEDS ; j++) + { + if(intensity < 60) + { + if(lookup[intensity] == intensity){ + pixels[j][0] = lookup[intensity]; + pixels[j][1] = lookup[intensity]; + } + pixels[j][2] = intensity; + glcdSetAddress(0, 1); + printnumber(intensity, NORMAL); + } + } +#endif +} + +// copy array to physical strip to light up LEDs +static void send_pixels(void) +{ + uint16_t i; + + for (i = 0 ; i < NUM_LEDS ; i++) + { + uint8_t * const p = pixels[i]; + uint8_t r = p[0]; + uint8_t g = p[1]; + uint8_t b = p[2]; + + send_byte(0x80 | (g >> 1)); + send_byte(0x80 | (r >> 1)); + send_byte(0x80 | (b >> 1)); + } + + latch(); +} + +static void send_byte(uint8_t v) +{ + unsigned bit; + for (bit = 0x80 ; bit ; bit >>= 1) + { + clock(0); + if (v & bit) + PORTD |= (1 << 1); + else + PORTD &= ~(1 << 1); + clock(1); + } +} + +static inline void clock(const uint8_t value) +{ + if (value) + PORTD |= (1 << 0); + else + PORTD &= ~(1 << 0); +} + +static void send_zero(void) +{ + unsigned i; + PORTD &= ~(1 << 1); + + for (i = 0 ; i < 8 ; i++) + { + clock(1); + clock(0); + } +} + +static void latch(void) +{ + unsigned i; + const unsigned zeros = 3 * ((NUM_LEDS + 63) / 64); + for (i = 0 ; i < zeros ; i++) + send_zero(); +} + +static void LEDsetup(void) +{ + // initialize the digital pin as an output. + cbi(UCSR0B, TXEN0); + cbi(UCSR0B, RXEN0); + DDRD |= 1 << 1; + DDRD |= 1 << 0; + + latch(); + // initialization sequence (they all blink really bright) + //uint16_t i; + //for (i = 0 ; i < NUM_LEDS ; i++) + //{ + //pixels[i][0] = 0xFF; + //pixels[i][1] = 0x00; + //pixels[i][2] = 0x00; + //} + //send_pixels(); + all_off(); +} + +void set_begin_seq(int min, int hour) +{ + // here is where we set the sequence length + // from where the lights start to come up, until full brightness + // This code works for a 35 minute long sequence + + int diff = 0; // holds offset for minutes if needed + + if(min < 35) + { + // we need to wrap hours back + begin_seq_h = hour - 1; + // we need to determine new minutes + diff = 35 - min; + begin_seq_m = 60 - diff; + }else{ + // no need to go back an hour + begin_seq_h = hour; + begin_seq_m = min - 35; + } +} diff --git a/firmware/ratt.h b/firmware/ratt.h new file mode 100644 index 0000000..9b4f353 --- /dev/null +++ b/firmware/ratt.h @@ -0,0 +1,225 @@ +#define halt(x) while (1) + +#define DEBUGGING 0 +#define DEBUG(x) if (DEBUGGING) { x; } +#define DEBUGP(x) DEBUG(putstring_nl(x)) + +// Software options. Uncomment to enable. +//BACKLIGHT_ADJUST - Allows software control of backlight, assuming you mounted your 100ohm resistor in R2'. +#define BACKLIGHT_ADJUST 1 + +//OPTION_DOW_DATELONG - Allows showing Day of Week, and Longer format Dates, +//Like " sat","0807","2010", or " aug"," 07","2010" or " sat"," aug"," 07","2010". +//#define OPTION_DOW_DATELONG 1 + +// This is a tradeoff between sluggish and too fast to see +#define MAX_BALL_SPEED 5 // note this is in vector arith. +#define ball_radius 2 // in pixels + +// If the angle is too shallow or too narrow, the game is boring +#define MIN_BALL_ANGLE 20 + +// how fast to proceed the animation, note that the redrawing +// takes some time too so you dont want this too small or itll +// 'hiccup' and appear jittery +#define ANIMTICK_MS 75 + +// Beeep! +#define ALARM_FREQ 4000 +#define ALARM_MSONOFF 300 + +#define MAXSNOOZE 600 // 10 minutes + +// how many seconds we will wait before turning off menus +#define INACTIVITYTIMEOUT 10 + +/*************************** DISPLAY PARAMETERS */ + +// how many pixels to indent the menu items +#define MENU_INDENT 8 + +#define DIGIT_W 32 +#define HSEGMENT_H 6 +#define HSEGMENT_W 18 +#define VSEGMENT_H 20 +//#define VSEGMENT_H 25 +#define VSEGMENT_W 6 +#define DIGITSPACING 4 +#define DOTRADIUS 4 + +#define DISPLAY_H10_X 0 +#define DISPLAY_H1_X VSEGMENT_W + HSEGMENT_W + 2 + DIGITSPACING +#define DISPLAY_M10_X GLCD_XPIXELS - 2*VSEGMENT_W - 2*HSEGMENT_W - 4 - DIGITSPACING +#define DISPLAY_M1_X GLCD_XPIXELS - VSEGMENT_W - HSEGMENT_W - 2 +#define DISPLAY_TIME_Y 0 + + +/* not used +#define ALARMBOX_X 20 +#define ALARMBOX_Y 24 +#define ALARMBOX_W 80 +#define ALARMBOX_H 20 +*/ + +/*************************** PIN DEFINITIONS */ +// there's more in KS0108.h for the display + +#define ALARM_DDR DDRB +#define ALARM_PIN PINB +#define ALARM_PORT PORTB +#define ALARM 6 + +#define PIEZO_PORT PORTC +#define PIEZO_PIN PINC +#define PIEZO_DDR DDRC +#define PIEZO 3 + + +/*************************** ENUMS */ + +// Whether we are displaying time (99% of the time) +// alarm (for a few sec when alarm switch is flipped) +// date/year (for a few sec when + is pressed) +#define SCORE_MODE_TIME 0 +#define SCORE_MODE_DATE 1 +#define SCORE_MODE_YEAR 2 +#define SCORE_MODE_ALARM 3 +#define SCORE_MODE_DOW 4 +#define SCORE_MODE_DATELONG_MON 5 +#define SCORE_MODE_DATELONG_DAY 6 + +// Constants for how to display time & date +#define REGION_US 0 +#define REGION_EU 1 +#define DOW_REGION_US 2 +#define DOW_REGION_EU 3 +#define DATELONG 4 +#define DATELONG_DOW 5 +#define TIME_12H 0 +#define TIME_24H 1 + +//Contstants for calcualting the Timer2 interrupt return rate. +//Desired rate, is to have the i2ctime read out about 6 times +//a second, and a few other values about once a second. +#define OCR2B_BITSHIFT 0 +#define OCR2B_PLUS 1 +#define OCR2A_VALUE 16 +#define TIMER2_RETURN 80 +//#define TIMER2_RETURN (8000000 / (OCR2A_VALUE * 1024 * 6)) + +// displaymode +#define NONE 99 +#define SHOW_TIME 0 +#define SHOW_DATE 1 +#define SHOW_ALARM 2 +#define SET_TIME 3 +#define SET_ALARM 4 +#define TOGGLE_BRIGHTS 5 +#define SET_BRIGHTNESS 6 +#define SET_VOLUME 7 +#define TOGGLE_REDS 8 +#define SHOW_SNOOZE 9 +#define SET_SNOOZE 10 + +#define SET_MONTH 11 +#define SET_DAY 12 +#define SET_YEAR 13 +#define TOGGLE_LAMP 14 +#define TOGGLE_DEMO_MODE 15 + +#define SET_HOUR 101 +#define SET_MIN 102 +#define SET_SEC 103 + +#define SET_REG 104 + +#define SET_BRT 105 +#define SET_TOG_BTS 106 +#define SET_TOG_RDS 107 +#define SET_TOG_LAMP 108 +#define SET_TOG_DEMO 109 + +//DO NOT set EE_INITIALIZED to 0xFF / 255, as that is +//the state the eeprom will be in, when totally erased. +#define EE_INITIALIZED 0xC3 +#define EE_INIT 0 +#define EE_ALARM_HOUR 1 +#define EE_ALARM_MIN 2 +#define EE_BRIGHT 3 +#define EE_VOLUME 4 +#define EE_REGION 5 +#define EE_TIME_FORMAT 6 +#define EE_SNOOZE 7 +/***** For Holly's LED strip project *****/ +#define NUM_LEDS ((int16_t) (32 * 5)) +#define LED_SCALE 8 + +/*************************** FUNCTION PROTOTYPES */ + +/***** For Holly's LED strip project *****/ +// removed "static" declaration for fxs called in config.c +void turn_lamp_on(void); +void turn_lamp_off(void); +void brightest_lights(void); +static void ramp_up(void); +static void ramp_down(void); +void nightlight(void); +void all_off(void); +void ramp_fast(void); +static void send_pixels(void); +static void send_byte(uint8_t v); +static inline void clock(const uint8_t value); +static void latch(void); +static void LEDsetup(void); +void set_begin_seq(int, int); + +extern int lamp_state; //used by config.c and ratt.c + +/****** Monochron and sevenchron stuff ******/ +uint8_t leapyear(uint16_t y); +void clock_init(void); +void initbuttons(void); +void tick(void); +void setsnooze(void); +void initanim(void); +void initdisplay(uint8_t inverted); +void step(void); +void setscore(void); +void draw(uint8_t inverted); + +void set_alarm(void); +void set_time(void); +//void set_region(void); +//void set_date(void); +void set_backlight(void); +void print_timehour(uint8_t h, uint8_t inverted); +void print_alarmhour(uint8_t h, uint8_t inverted); +void display_menu(void); +void drawArrow(uint8_t x, uint8_t y, uint8_t l); +void setalarmstate(void); +//void beep(uint16_t freq, uint8_t duration); +void printnumber(uint8_t n, uint8_t inverted); + + +float random_angle_rads(void); + +void init_crand(void); +uint8_t dotw(uint8_t mon, uint8_t day, uint8_t yr); + +uint8_t i2bcd(uint8_t x); + +uint8_t readi2ctime(void); + +void writei2ctime(uint8_t sec, uint8_t min, uint8_t hr, uint8_t day, + uint8_t date, uint8_t mon, uint8_t yr); + +//void print_date(uint8_t month, uint8_t day, uint8_t year, uint8_t mode); +void print_brights_status(int brights_state); +void print_reds_status(int reds_state); + +void draw7seg(uint8_t x, uint8_t y, uint8_t segs, uint8_t inverted); +void drawsegment(uint8_t s, uint8_t x, uint8_t y, uint8_t inverted); +void drawdigit(uint8_t d, uint8_t x, uint8_t y, uint8_t inverted); +void drawvseg(uint8_t x, uint8_t y, uint8_t inverted); +void drawhseg(uint8_t x, uint8_t y, uint8_t inverted); +void drawdot(uint8_t x, uint8_t y, uint8_t inverted); diff --git a/firmware/util.c b/firmware/util.c new file mode 100644 index 0000000..75c4bb8 --- /dev/null +++ b/firmware/util.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include "util.h" + +// Creates a 8N1 UART connect +// remember that the BBR is #defined for each F_CPU in util.h +void uart_init(uint16_t BRR) { + UBRR0 = BRR; // set baudrate counter + + UCSR0B = _BV(RXEN0) | _BV(TXEN0); + UCSR0C = _BV(USBS0) | (3<> 4) < 0x0a) + uart_putc((b >> 4) + '0'); + else + uart_putc((b >> 4) - 0x0a + 'a'); + + /* lower nibble */ + if((b & 0x0f) < 0x0a) + uart_putc((b & 0x0f) + '0'); + else + uart_putc((b & 0x0f) - 0x0a + 'a'); +} + +void uart_putw_hex(uint16_t w) +{ + uart_putc_hex((uint8_t) (w >> 8)); + uart_putc_hex((uint8_t) (w & 0xff)); +} + +void uart_putdw_hex(uint32_t dw) +{ + uart_putw_hex((uint16_t) (dw >> 16)); + uart_putw_hex((uint16_t) (dw & 0xffff)); +} + +void uart_putw_dec(uint16_t w) +{ + uint16_t num = 10000; + uint8_t started = 0; + + while(num > 0) + { + uint8_t b = w / num; + if(b > 0 || started || num == 1) + { + uart_putc('0' + b); + started = 1; + } + w -= b * num; + + num /= 10; + } +} + +void uart_put_dec(int8_t w) +{ + uint16_t num = 100; + uint8_t started = 0; + + if (w <0 ) { + uart_putc('-'); + w *= -1; + } + while(num > 0) + { + int8_t b = w / num; + if(b > 0 || started || num == 1) + { + uart_putc('0' + b); + started = 1; + } + w -= b * num; + + num /= 10; + } +} + +void uart_putdw_dec(uint32_t dw) +{ + uint32_t num = 1000000000; + uint8_t started = 0; + + while(num > 0) + { + uint8_t b = dw / num; + if(b > 0 || started || num == 1) + { + uart_putc('0' + b); + started = 1; + } + dw -= b * num; + + num /= 10; + } +} diff --git a/firmware/util.h b/firmware/util.h new file mode 100644 index 0000000..61b7f7b --- /dev/null +++ b/firmware/util.h @@ -0,0 +1,41 @@ +#include + +// Raw constants for the UART to make the bit timing nice + +#if (F_CPU == 16000000) +#define BRRL_9600 103 // for 16MHZ +#define BRRL_192 52 // for 16MHZ +#elif (F_CPU == 8000000) +#define BRRL_9600 52 +#define BRRL_192 26 +#endif + +// Debug printing functions - handy! +#define uart_putc(c) uart_putchar(c) + +int uart_putchar(char c); +void uart_init(uint16_t BRR); +char uart_getchar(void); + +void uart_putc_hex(uint8_t b); +void uart_putw_hex(uint16_t w); +void uart_putdw_hex(uint32_t dw); + +void uart_putw_dec(uint16_t w); +void uart_putdw_dec(uint32_t dw); +void uart_puts(const char* str); + +void RAM_putstring(char *str); +void ROM_putstring(const char *str, uint8_t nl); + +// by default we stick strings in ROM to save RAM +#define putstring(x) ROM_putstring(PSTR(x), 0) +#define putstring_nl(x) ROM_putstring(PSTR(x), 1) +#define nop asm volatile ("nop\n\t") + +// some timing functions + +void delay_ms(unsigned char ms); +void delay_10us(uint8_t us); +void delay_s(uint8_t s); +