initial version of assembly port of serial_lcd.
authorKevin Rosenberg <kevin@rosenberg.net>
Wed, 9 Apr 2008 03:14:07 +0000 (21:14 -0600)
committerKevin Rosenberg <kevin@rosenberg.net>
Wed, 9 Apr 2008 03:14:07 +0000 (21:14 -0600)
Not yet tested

gcc_asm/Makefile [new file with mode: 0644]
serial_lcd.S [new file with mode: 0644]

diff --git a/gcc_asm/Makefile b/gcc_asm/Makefile
new file mode 100644 (file)
index 0000000..68a8d88
--- /dev/null
@@ -0,0 +1,74 @@
+###############################################################################\r
+# Makefile for the serial_lcd C version for GCC\r
+\r
+## General Flags\r
+PROJECT = serial_lcd\r
+MCU = attiny2313\r
+TARGET = $(PROJECT).elf\r
+CC = avr-gcc\r
+\r
+## Options common to compile, link and assembly rules\r
+COMMON = -mmcu=$(MCU) \r
+\r
+## Compile options common for all C compilation units.\r
+CFLAGS = $(COMMON)\r
+CFLAGS += -DF_CPU=14745600UL -Os\r
+\r
+## Assembly specific flags\r
+ASMFLAGS = $(COMMON) -D__ASSEMBLER__\r
+ASMFLAGS += -DF_CPU=14745600UL -Os\r
+ASMFLAGS += -Wa,-gstabs\r
+\r
+## Linker flags\r
+LDFLAGS = $(COMMON)\r
+LDFLAGS +=  -Wl,-Map=$(PROJECT).map,--cref\r
+\r
+\r
+## Intel Hex file production flags\r
+HEX_FLASH_FLAGS = -R .eeprom\r
+HEX_EEPROM_FLAGS = -j .eeprom\r
+HEX_EEPROM_FLAGS += --set-section-flags=.eeprom="alloc,load"\r
+HEX_EEPROM_FLAGS += --change-section-lma .eeprom=0 --no-change-warnings\r
+\r
+\r
+## Objects that must be built in order to link\r
+OBJECTS = $(PROJECT).o \r
+\r
+## Objects explicitly added by the user\r
+LINKONLYOBJECTS = \r
+\r
+## Build\r
+all: $(PROJECT).hex $(PROJECT).eep $(PROJECT).lss size \r
+\r
+## Compile\r
+$(PROJECT).o: ../$(PROJECT).S\r
+       $(CC) $(ASMFLAGS) -c $< -o $(PROJECT).o\r
+\r
+##Link\r
+$(TARGET): $(OBJECTS)\r
+        $(CC) $(LDFLAGS) $(OBJECTS) $(LINKONLYOBJECTS) $(LIBDIRS) $(LIBS) -o $(TARGET)\r
+\r
+$(PROJECT).hex: $(TARGET)\r
+       avr-objcopy -O ihex $(HEX_FLASH_FLAGS)  $< $@\r
+\r
+$(PROJECT).eep: $(TARGET)\r
+       -avr-objcopy $(HEX_EEPROM_FLAGS) -O ihex $< $@ || exit 0\r
+\r
+$(PROJECT).lss: $(TARGET)\r
+       avr-objdump -h -S $< > $@\r
+\r
+.PHONY: size\r
+size: ${TARGET}\r
+       @echo $(TARGET) size\r
+       @avr-size -C --mcu=${MCU} ${TARGET}\r
+\r
+## Clean target\r
+.PHONY: distclean\r
+distclean: clean\r
+       @rm -rf $(PROJECT).hex $(PROJECT).lss $(PROJECT).map $(PROJECT).lst \r
+\r
+.PHONY: clean\r
+clean:\r
+       @rm -rf $(OBJECTS) $(TARGET) $(PROJECT).eep\r
+\r
+\r
diff --git a/serial_lcd.S b/serial_lcd.S
new file mode 100644 (file)
index 0000000..1de9453
--- /dev/null
@@ -0,0 +1,713 @@
+/*****************************************************************************\r
+* FILE IDENTIFICATION\r
+*\r
+**   Name:          serial_lcd.S\r
+**   Purpose:       Serial LCD Backpack code for GCC Assembler\r
+**   Programmer:    Kevin Rosenberg <kevin@rosenberg.net> (AVR Freaks member kmr)\r
+**   Date Started:  April 2008\r
+**\r
+**   Copyright (c) 2008 by Kevin Rosenberg\r
+*******************************************************************************/\r
+\r
+        #include <avr/io.h>\r
+#ifndef F_CPU\r
+#define F_CPU 14745600UL\r
+#endif\r
+\r
+#define CONSERVATIVE_ENABLE_DURATION 0\r
+#define USE_CTS 0\r
+\r
+/*************************************************************/\r
+/*************************************************************/\r
+\r
+/*     //// From the LCD perspective \\\\\r
+        RxD --> PORTD:0\r
+        LED <-- PORTD:1 // High = ON, Low = OFF\r
+        J_1 --> PORTD:2 // BAUD rate select\r
+        J_2 --> PORTD:3 // BAUD rate select\r
+        LCD:R/W <-- PORTD:4\r
+        LCD:RS <-- PORTD:5\r
+        LCD:E <-- PORTD:6\r
+        N/A <-> PORTD:7\r
+\r
+        LCD:R/W <-- PORTD:4\r
+        LCD:RS <-- PORTD:5\r
+        LCD:E <-- PORTD:6\r
+        LCD:Vee <-- CONTRAST\r
+        LCD:DB0 <-- PORTB:0\r
+        LCD:DB1 <-- PORTB:1\r
+        LCD:DB2 <-- PORTB:2\r
+        LCD:DB3 <-- PORTB:3\r
+        LCD:DB4 <-- 4.7K Ohm <-- PORTx:4\r
+        LCD:DB5 <-- 4.7K Ohm <-- PORTx:5\r
+        LCD:DB6 <-- 4.7K Ohm <-- PORTx:6\r
+        LCD:DB7 <-- 4.7K Ohm <-- PORTx:7\r
+\r
+       //// From the I/O PORTx perspective \\\\\r
+        PORTD:0 <--RxD\r
+        PORTD:1 --> LED // High = ON, Low = OFF\r
+        PORTD:2 <-- J_1 // BAUD rate select\r
+        PORTD:3 <-- J_2 // BAUD rate select\r
+        PORTD:4 --> LCD:R/W\r
+        PORTD:5 --> LCD:RS\r
+        PORTD:6 --> LCD:E\r
+        PORTD:7 <-> N/A\r
+\r
+        PORTB:0 --> LCD:DB0\r
+        PORTB:1 --> LCD:DB1\r
+        PORTB:2 --> LCD:DB2\r
+        PORTB:3 --> LCD:DB3\r
+        PORTB:4 --> 4.7K Ohm --> LCD:DB4\r
+        PORTB:5 --> 4.7K Ohm --> LCD:DB5\r
+        PORTB:6 --> 4.7K Ohm --> LCD:DB6\r
+        PORTB:7 --> 4.7K Ohm --> LCD:DB7\r
+*/\r
+\r
+/*************************************************************/\r
+/*************************************************************/\r
+// If you want to use a different I/O port for LCD control & data,\r
+// do it here!!!\r
+#define LCD_DATA_PORT PORTB\r
+#define LCD_DATA_PIN_REG PINB\r
+#define LCD_DATA_DIR DDRB\r
+\r
+#define LCD_CONTROL_PORT PORTD\r
+#define LCD_CONTROL_PIN_REG PIND\r
+#define LCD_CONTROL_DIR DDRD\r
+\r
+// LED backlight control pin\r
+#define LED_DIR DDRD\r
+#define LED_PORT PORTD\r
+#define LED_PIN PD1\r
+\r
+// LCD Read/Write Pin\r
+#define LCD_RW PD4\r
+// LCD Register Select Pin\r
+#define LCD_RS PD5\r
+// LCD Enable Pin\r
+#define LCD_E PD6\r
+/*************************************************************/\r
+/*************************************************************/\r
+\r
+// LCD busy status pin\r
+#define LCD_BUSY PB7\r
+\r
+// BAID rate setting pins\r
+#define BAUD_PORT PORTD\r
+#define BAUD_DIR  DDRB\r
+#define BAUD_PIN_REG PIND\r
+#define BAUD_J1 PD2\r
+#define BAUD_J2 PD3\r
+\r
+#if USE_CTS\r
+// Direction register for clear to send signal\r
+#define CTS_DIR DDRA\r
+// Output port for clear to send signal\r
+#define CTS_PORT PORTA\r
+// Pin for clear to send signal (RESET pin on Tiny2313)\r
+// Ensure fuse is set to disable RESET function on RESET pin\r
+#define CTS_PIN PA2\r
+#endif\r
+\r
+// Number of PWM brightness levels supported\r
+#define LED_BRIGHTNESS_LEVELS 8\r
+\r
+////// LCD Commands ///////\r
+// Turn on power to the display, no cursor\r
+#define        LCD_ON  0x0C\r
+// Clear display command\r
+#define LCD_CLR 0x01\r
+// Set 4 data bits\r
+#define LCD_4_Bit 0x20\r
+// Set 8 data bits\r
+#define LCD_8_Bit 0x30\r
+// Set number of lines\r
+#define LCD_4_Line 0x08\r
+// Set 8 data bits\r
+#define        DATA_8  0x30\r
+// Set character font\r
+#define LCD_Font 0x04\r
+// Turn the cursor on\r
+#define LCD_CURSOR_ON 0x02\r
+// Turn on cursor blink\r
+#define LCD_CURSOR_BLINK 0x01\r
+\r
+////// Serial command codes ///////\r
+// ASCII control code to set brightness level\r
+// Next byte is brightness ranging from 0 (no backlight) to 255 (max backlight)\r
+#define LED_SET_BRIGHTNESS 0xFB\r
+// ASCII control code turns on LED\r
+#define LED_SW_ON 0xFC\r
+// ASCII control code turns off LED\r
+#define LED_SW_OFF 0xFD\r
+// Character generator RAM command\r
+#define REG_MODE 0xFE\r
+\r
+// Circular buffer for UART RX\r
+#define UART_RX_BUFFER_SIZE 48\r
+.section .data\r
+sUartRxBuf:\r
+        .org 0\r
+sUartTxBufEnd:\r
+        .org sUartRxBuf + UART_RX_BUFFER_SIZE\r
+\r
+.section .text\r
+\r
+// Actual baud rate = 9600 BAUD, (0.0% error) @ 14.7456MHz\r
+// Actual baud rate = 19.2K BAUD, (0.0% error) @ 14.7456MHz\r
+// Actual baud rate = 38.4K BAUD, (0.0% error) @ 14.7456MHz\r
+// Actual baud rate = 115.2K BAUD, (0.0% error) @ 14.7456MHz\r
+#define BAUD_4800 191\r
+#define BAUD_9600 95\r
+#define BAUD_14400 63\r
+#define BAUD_19200 47\r
+#define BAUD_28800 31\r
+#define BAUD_38400 23\r
+#define BAUD_57600 15\r
+#define BAUD_76800 11\r
+#define BAUD_115200 7\r
+        \r
+BaudLookupTable:\r
+        .byte BAUD_115200\r
+        .byte BAUD_38400\r
+        .byte BAUD_19200\r
+        .byte BAUD_9600\r
+\r
+// PWM Patterns (8-brightness levels)\r
+ledPwmPatterns: \r
+        .byte 0x10 // 0b00010000  0\r
+        .byte 0x11 // 0b00010001  1\r
+        .byte 0x4A // 0b01001010  2\r
+        .byte 0x55 // 0b01010101  3\r
+        .byte 0x5D // 0b01011101  4\r
+        .byte 0xEE // 0b11101110  5\r
+        .byte 0x7F // 0b11110111  6\r
+        .byte 0xFF // 0b11111111  7\r
+\r
+\r
+// PWeh must be 230nS minimum, nop = 67nS @ 14.7456MHz\r
+// At 4 cycles, E = 271nS\r
+// Some slow systems require 450ns, or 7 cycles at 14.7456MHz\r
+#if CONSERVATIVE_ENABLE_DURATION\r
+.macro ENABLE_WAIT\r
+       rjmp    1f\r
+1:     rjmp    2f\r
+2:     rjmp    3f\r
+3:     nop\r
+.endm\r
+#else\r
+.macro ENABLE_WAIT\r
+       rjmp    1f\r
+1:     rjmp    2f\r
+2:      \r
+.endm\r
+#endif\r
+\r
+// register variables\r
+#define zeroReg       r1\r
+#define saveSregReg   r2\r
+#define ledPwmCycling r3\r
+#define isrTemp1      r16\r
+#define isrTemp2      r17\r
+#define isrTemp3      r18\r
+#define ledPwmCount   r19\r
+#define ledStatus     r20        \r
+#define sUartRxHead   r21\r
+#define sUartRxTail   r22\r
+#define returnReg1    r23\r
+#define loadReg       r24\r
+#define ledPwmPattern r25\r
+#define tmpReg1       r26\r
+#define tmpReg2       r27\r
+#define tmpReg3       r28\r
+#define tmpReg4       r29\r
+        \r
+.macro LedTimerStop\r
+        out     _SFR_IO_ADDR(TCCR0B), zeroReg\r
+.endm        \r
+.macro LedTimerStart\r
+// Start with 256 prescaler\r
+        ldi     loadReg, (1<<CS02)\r
+        out     _SFR_IO_ADDR(TCCR0B), loadReg\r
+.endm\r
+\r
+.macro BACKLIGHT_OFF\r
+        cbr     ledStatus, 0\r
+.endm\r
+\r
+.macro BACKLIGHT_ON\r
+        sbr     ledStatus, 0\r
+.endm\r
+\r
+.macro LedPwmSwitchOff\r
+        cbi     _SFR_IO_ADDR(LED_PORT), LED_PIN\r
+        BACKLIGHT_OFF\r
+        LedTimerStop\r
+.endm\r
+\r
+.macro LedPwmSwitchOn\r
+        // if (ledPwmPattern == 0xFF) { \r
+        //  LedTimerStop();\r
+        //  LED_PORT |= (1<<LED_PIN); // keep LED on at all times\r
+        // } else {\r
+        //  LedTimerStart();\r
+        // }\r
+        BACKLIGHT_ON\r
+        cpi     ledPwmPattern, 0xFF\r
+        breq    1f\r
+        LedTimerStart\r
+        rjmp    2f\r
+1:     LedTimerStop                            ; maximum brightness, no need for PWM\r
+        sbi     _SFR_IO_ADDR(LED_PORT), LED_PIN ; keep LED on at al times\r
+2:     \r
+.endm\r
+\r
+.macro StoreSREG\r
+// Start with 256 prescaler\r
+        in      saveSregReg, _SFR_IO_ADDR(SREG)\r
+.endm        \r
+.macro RestoreSREG\r
+// Start with 256 prescaler\r
+        out      _SFR_IO_ADDR(SREG), saveSregReg\r
+.endm        \r
+\r
+.macro LedPwmInit\r
+  // setup PWM to run at 1.25ms per interrupt.\r
+  // This allows 8 levels of brightness at minimum of 100Hz flicker rate.\r
+        ldi     loadReg, (1<<WGM01)      // CTC mode\r
+        out     _SFR_IO_ADDR(TCCR0A), loadReg \r
+        out     _SFR_IO_ADDR(TCCR0B), zeroReg   // timer off\r
+        ldi     loadReq, 72            // 1.25ms with CLK/256 prescaler @ 14.7456MHz\r
+        out     _SFR_IO_ADDR(OCR0A), loadReg\r
+        ldi     loadReg, (1<<OCIE0A)      // Turn on timer0 COMPA intr (all other timer intr off)\r
+        out     _SFR_IO_ADDR(TIMSK), loadReg\r
+        cbi     _SFR_IO_ADDR(LED_PORT), LED_PIN // Ensure LED is off during initialization\r
+        sbi     _SFR_IO_ADDR(LED_DIR), LED_PIN  // Ensure LED is output direction\r
+        BACKLIGHT_OFF\r
+        ldi     ledPwmPattern, 0xFF     // maximum brightness\r
+.endm\r
+\r
+\r
+\r
+ // Initialize USART\r
+.macro UsartInit\r
+        out     _SFR_IO_ADDR(UCSRB), zeroReg   // Disable while setting baud rate\r
+        out     _SFR_IO_ADDR(UCSRA), zeroReg \r
+        ldi     loadReg, ((1<<UCSZ1) | (1<<UCSZ0)) // 8 bit data\r
+        out     _SFR_IO_ADDR(UCSRC), loadReg\r
+\r
+        in      loadReg, _SFR_IO_ADDR(BAUD_PIN_REG)   // Get BAUD rate jumper settings\r
+        andi    loadReg, ((1<<BAUD_J2) | (1<<BAUD_J1))    // Mask off unwanted bits\r
+        // BAUD_J2 = PD.3, BAUD_J1 = PD.2\r
+        // This is two bits too far to the left and the array index will\r
+        // increment by 4, instead of 1.\r
+        // Shift BAUD_J2 & BAUD_J1 right by two bit positions for proper array indexing\r
+        asr     loadReg\r
+        asr     loadReg\r
+        ldi     ZH, lo8(BaudLookupTable)\r
+        ldi     ZL, hi8(BaudLookupTable)\r
+        add     ZL, loadReg\r
+        adc     ZH, zeroReg\r
+        lpm     r0, Z\r
+        out     _SFR_IO_ADDR(UBRRL), r0    // Set baud rate\r
+        out     _SFR_IO_ADDR(UBRRH), zeroReg  // Set baud rate hi\r
+        ldi     loadReg, ((1<<RXEN)|(1<<RXCIE)) // RXEN = Enable\r
+        out     _SFR_IO_ADDR(UCSRB), loadReg\r
+        clr     sUartRxHead  // register variable, need to explicitly zero\r
+        clr     sUartRxTail  // register variable, need to explicitly zero\r
+\r
+#if USE_CTS\r
+        sbi     _SFR_IO_ADDR(CTS_PORT), CTS_PIN // bring signal high\r
+        sbi     _SFR_IO_ADDR(CTS_DIR), CTS_PIN // CTS is active low\r
+#endif\r
+.endm\r
+\r
+.macro LcdInit\r
+        ldi     loadReg, ((1<<BAUD_J2) | (1<<BAUD_J1))    // Set BAUD_J2:BAUD_J1 PULL-UPS active\r
+        out     _SFR_IO_ADDR(LCD_CONTROL_PORT), loadReg\r
+        ldi     loadReg, 0xF2 // Set LCD_control BAUD_J2:BAUD_J1 to inputs\r
+        out     _SFR_IO_ADDR(LCD_CONTROL_DIR), loadReg      \r
+\r
+        // delay 4100 ms\r
+        ldi     tmpReg1, 255\r
+        ldi     tmpReg2, 127\r
+        ldi     tmpReg3, 50\r
+        ldi     tmpReg4, 2\r
+dl2:    subi    tmpReg1, 1\r
+        sbc     tmpReg2, zeroReg\r
+        sbc     tmpReg3, zeroReg\r
+        sbc     tmpReg4, zeroReg\r
+        brne    dl2\r
+        rjmp    dj2\r
+dj2:    nop\r
+\r
+        sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS   // Set LCD_RS high\r
+\r
+        // Initialize the LCD Data AVR I/O\r
+        ldi     loadReg, 0xFF // Set LCD_DATA_PORT as all outputs\r
+        out     _SFR_IO_ADDR(LCD_DATA_DIR), loadReg\r
+        out     _SFR_IO_ADDR(LCD_DATA_PORT), zeroReg // Set LCD_DATA_PORT to logic low\r
+\r
+        // Initialize the LCD controller\r
+        cbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS\r
+        ldi     returnReg1, DATA_8\r
+        rcall   LcdWriteData\r
+\r
+        // delay 4100 ms\r
+        ldi     tmpReg1, 255\r
+        ldi     tmpReg2, 127\r
+        ldi     tmpReg3, 184\r
+dl0:    subi    tmpReg1, 1\r
+        sbc     tmpReg2, zeroReg\r
+        sbc     tmpReg3, zeroReg\r
+        brne    dl0\r
+        rjmp    dj0\r
+dj0:    nop\r
+        \r
+        rcall   LcdWriteData\r
+\r
+        // Delay 100uS\r
+        ldi     tmpReg1, 112\r
+        ldi     tmpReg2, 1\r
+dl1:    subi    tmpReg1, 1\r
+        sbc     tmpReg2, zeroReg\r
+        brne    dl1\r
+        rjmp    dj1\r
+dj1:\r
+        \r
+        rcall   LcdWriteData\r
+        sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS\r
+\r
+        // The display will be out into 8 bit data mode here\r
+        ldi     loadReg, (LCD_8_Bit | LCD_4_Line); // Set 8 bit data, 4 display lines\r
+        rcall   LcdWriteCmd\r
+        ldi     loadReg, LCD_ON // Power up the display\r
+        rcall   LcdWriteCmd\r
+        ldi     loadReg, LCD_CLR // Clear display\r
+        rcall   LcdWriteCmd\r
+.endm\r
+\r
+;;; Pass brightness (0-255) in returnReg1\r
+LedPwmSetBrightness:\r
+        cpi     returnReg1, 0\r
+        brne    brightnessNotZero\r
+        // turn backlight off for 0 brightness\r
+\r
+        sbrc    ledStatus, 0    ; Check if backlight on\r
+        rjmp    ledNotOn\r
+        LedTimerStop\r
+        mov     ledPwmPattern, zeroReg\r
+        mov     ledPwmCycling, zeroReg\r
+        cbi     _SFR_IO_ADDR(LED_PORT), LED_PIN\r
+ledNotOn:       \r
+        ret\r
+\r
+brightnessNotZero:      \r
+        // ledPwmPos = (brightness * (LED_BRIGHTNESS_LEVELS-1) + (unsigned int) 127) >> 8;\r
+        mov     tmpReg1, returnReg1\r
+        mov     tmpReg2, zeroReg\r
+        mov     tmpReg3, (LED_BRIGHTNESS_LEVELS-2)\r
+brightMultLoop:\r
+        add     tmpReg1, returnReg1\r
+        adc     tmpReg2, zeroReg\r
+        dec     tmpReg3\r
+        brne    brightMultLoop\r
+        ldi     loadReg, 127\r
+        add     tmpReg1, loadReg\r
+        adc     tmpReg2, zeroReg\r
+        // ledPwmPos now in tmpReg2\r
+        \r
+        // Below is probably not required, but ensures we don't exceed array\r
+        // if (ledPwmPos > LED_BRIGHTNESS_LEVELS - 1)\r
+        //      ledPwmPos = LED_BRIGHTNESS_LEVELS - 1;\r
+        cpi     tmpReg2, LED_BRIGHTNESS_LEVELS - 1\r
+        brlo    brightMax\r
+        ldi     tmpReg2, LED_BRIGHTNESS_LEVELS - 1\r
+brightMax:      \r
+        //      ledPwmPattern = PGM_READ_BYTE (&ledPwmPatterns[ledPwmPos]);\r
+        ldi     ZL,lo8(ledPwmPatterns)\r
+        ldi     ZH,hi8(ledPwmPatterns)\r
+        add     ZL, tmpReg3\r
+        adc     ZH, zeroReg\r
+        lpm     r0,Z    // ledPwmPos is in r0\r
+        mov     tmpReg2, r0\r
+        mov     ledPwmCount, zeroReg\r
+        mov     ledPwmCycling, ledPwmPattern\r
+\r
+        cpi     tmpReg2, (LED_BRIGHTNESS_LEVELS-1) // compare to maxmimum brightness\r
+        brsh    brightSetMax\r
+        // if (IS_BACKLIGHT_ON()) LedTimerStart();\r
+        ret\r
+brightSetMax:\r
+        sbrc    ledStatus, 0\r
+        // don't need PWM for continuously on\r
+        ret\r
+        LedTimerStop\r
+        sbi     _SFR_IO_ADDR(LED_PORT), LED_PIN\r
+        ret\r
+        \r
+.global main\r
+main:\r
+        clr     zeroReg\r
+        wdr\r
+\r
+        ldi     loadReg, (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)\r
+        out     _SFR_IO_ADDR(WDTCSR), loadReg\r
+        ldi     loadReg, (1<<WDE)|(1<<WDP3)|(1<<WDP0); // 1024K cycles, 8.0sec\r
+        out     _SFR_IO_ADDR(WDTCSR), loadReg\r
+\r
+        in      loadReg, _SFR_IO_ADDR(MCUCR)\r
+        andi    loadReg, ~((1<<SM1)|(1<<SM0)); // use idle sleep mode\r
+        out     _SFR_IO_ADDR(MCUCR), loadReg\r
+\r
+        LcdInit \r
+\r
+        // Initialize the AVR USART\r
+        UsartInit               \r
+\r
+        sei\r
+mainLoop:\r
+        wdr\r
+        cp      sUartRxTail, sUartRxHead\r
+        breq    mainNoChars\r
+        rcall   WaitRxChar\r
+\r
+        cpi     returnReg1, LED_SW_OFF\r
+        brne    main1\r
+        LedPwmSwitchOff\r
+        rjmp    mainLoop\r
+main1:  cpi     returnReg1, LED_SW_ON\r
+        brne    main2\r
+        LedPwmSwitchOn\r
+        rjmp    mainLoop\r
+main2:  cpi     returnReg1, LED_SET_BRIGHTNESS\r
+        brne    main3\r
+        rcall   WaitRxChar  // read next byte which will be brightness\r
+        rcall   LedPwmSetBrightness\r
+        rjmp    mainLoop\r
+main3:  cpi     returnReg1, REG_MODE\r
+        brne    main4\r
+        rcall   WaitRxChar\r
+        rcall   LcdWriteCmd // Send LCD command character\r
+        rjmp    mainLoop\r
+main4:\r
+        rcall   LcdWriteData // Send LCD data character\r
+        rjmp    mainLoop\r
+\r
+mainNoChars:    \r
+      // No characters waiting in RX buffer\r
+        cli\r
+        wdr\r
+        \r
+        in      loadReg, _SFR_IO_ADDR(MCUSR)\r
+        cbr     loadReg, WDRF // clear any watchdog interrupt flags\r
+        out     _SFR_IO_ADDR(MCUSR), loadReg\r
+\r
+        in      loadReg, _SFR_IO_ADDR(WDTCSR)\r
+        ori     loadReg, (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)\r
+        out     _SFR_IO_ADDR(WDTCSR), loadReg\r
+        cbr     loadReg, WDE  // watchdog timer off\r
+        out     _SFR_IO_ADDR(WDTCSR), loadReg\r
+\r
+        sei\r
+        in      loadReg, _SFR_IO_ADDR(MCUCR)\r
+        sbr     loadReg, SE\r
+        out     _SFR_IO_ADDR(MCUCR), loadReg\r
+        sleep\r
+        in      loadReg, _SFR_IO_ADDR(MCUCR)\r
+        cbr     loadReg, SE\r
+        out     _SFR_IO_ADDR(MCUCR), loadReg\r
+\r
+        cli\r
+        wdr\r
+\r
+        in      loadReg, _SFR_IO_ADDR(WDTCSR)\r
+        ori     loadReg, (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)\r
+        out     _SFR_IO_ADDR(WDTCSR), loadReg\r
+        cbr     loadReg, WDE // watchdog timer off\r
+        out     _SFR_IO_ADDR(WDTCSR), loadReg\r
+\r
+        sei\r
+        ret\r
+\r
+;; input cmd in returnReg1\r
+LcdWriteCmd:    \r
+        rcall   LcdBusyWait\r
+        out     _SFR_IO_ADDR(LCD_DATA_PORT), returnReg1\r
+        nop\r
+        sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
+        ENABLE_WAIT\r
+        cbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
+        nop\r
+        sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS // Set display to "Character mode"\r
+        ret\r
+\r
+// write character in returnReg1\r
+LcdWriteData:\r
+        rcall   LcdBusyWait\r
+        sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS // Set display to "Character Mode"\r
+        out     _SFR_IO_ADDR(LCD_DATA_PORT), returnReg1\r
+        nop\r
+        sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
+        ENABLE_WAIT\r
+        cbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
+        ret\r
+\r
+        \r
+;;;  Waits for LCD to stop being busy, returns LCD status in returnReg1\r
+LcdBusyWait:    \r
+        out     LCD_DATA_DIR, zeroReg // Set LCD data port to inputs\r
+        // LCD_CONTROL_PORT &= ~(1<<LCD_RS); // Set display to "Register mode"\r
+        cbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS \r
+        // LCD_CONTROL_PORT |= (1<<LCD_RW); // Put the display in the read mode\r
+        sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RW\r
+        nop\r
+LcdBusyLoop:\r
+        wdr\r
+        // LCD_CONTROL_PORT |= (1<<LCD_E);\r
+        sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
+        ENABLE_WAIT\r
+        in      returnReg1, _SFR_IO_ADDR(LCD_DATA_PIN_REG)\r
+        // LCD_CONTROL_PORT &= ~(1<<LCD_E);\r
+        sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
+        // } while (LCDStatus & (1<<LCD_BUSY));\r
+        sbrc    returnReg1, LCD_BUSY\r
+        rjmp    LcdBusyLoop\r
+        // LCD_CONTROL_PORT &= ~(1<<LCD_RW); // Put display in write mode\r
+        cbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RW\r
+        // LCD_DATA_DIR = 0xFF; // Set LCD data port to outputs\r
+        ldi     loadReg, 0xFF\r
+        out     _SFR_IO_ADDR(LCD_DATA_DIR), loadReg\r
+        ret                     \r
+        \r
+;;; Waits for a UART RX character and returns it in returnReg1 \r
+WaitRxChar:     \r
+        // waits for next RX character, then return it\r
+        cp      sUartRxTail, sUartRxHead\r
+        wdr\r
+        breq    WaitRxChar\r
+\r
+#if USE_CTS\r
+  // turn off interrupts so that if UART char ready to be stored, then storage\r
+  // will occur after CTS pin is set active. This allows UART ISR to set\r
+  // CTS inactive if byte fills buffer after we remove current byte\r
+  cli();\r
+#endif\r
+\r
+        // increment tail position\r
+        // if (tail == UART_RX_BUFFER_SIZE-1) sUartRxTail = 0  else  sUartRxTail++;\r
+        inc     sUartRxTail\r
+        cpi     sUartRxTail, UART_RX_BUFFER_SIZE\r
+        brne    waitRxBufIncremented\r
+        clr     sUartRxTail\r
+waitRxBufIncremented:\r
+        \r
+#if USE_CTS\r
+  CTS_PORT |= (1<<CTS_PIN); // Ensure CTS is active since just removed a byte\r
+  sei();   // Allow UART ISR to read character and change potentially change CTS\r
+#endif\r
+\r
+        ldi     ZL, lo8(sUartRxBuf)\r
+        ldi     ZH, hi8(sUartRxBuf)\r
+        add     ZL, sUartRxTail\r
+        adc     ZH, zeroReg\r
+        ld      returnReg1, Z\r
+        ret\r
+\r
+\r
+.global TIMER0_COMPA_vect\r
+TIMER0_COMPA_vect:\r
+        StoreSREG\r
+        sei             // Okay to allow USART interrupts while controlling LED PWM\r
+\r
+        // Set current LED state based on cycling variable\r
+        //  if (ledPwmCycling & 0x01)  LED_PORT |= (1<<LED_PIN); else LED_PORT &= ~(1<<LED_PIN);\r
+        in      isrTemp3,_SFR_IO_ADDR(LED_PORT)\r
+        sbr     isrTemp3, LED_PIN\r
+        sbrc    ledPwmCycling, 0\r
+        cbr     isrTemp3, LED_PIN\r
+        out     _SFR_IO_ADDR(LED_PORT), isrTemp3\r
+\r
+ // Update cycling variable for next interrupt\r
+//  if (ledPwmCount >= LED_BRIGHTNESS_LEVELS-1) {\r
+//    ledPwmCount = 0;\r
+//    ledPwmCycling = ledPwmPattern;\r
+//  } else {\r
+//    ledPwmCount++;\r
+//    ledPwmCycling >>= 1;\r
+//  }\r
+//}\r
+        cpi     ledPwmCount, (LED_BRIGHTNESS_LEVELS - 1)\r
+        brsh    tcIsrResetCount\r
+        inc     ledPwmCount\r
+        asr     ledPwmCycling\r
+        rjmp    tcIsrDone\r
+tcIsrResetCount:\r
+        clr     ledPwmCount\r
+        mov     ledPwmCycling, ledPwmPattern\r
+\r
+tcIsrDone:      \r
+        RestoreSREG\r
+        reti\r
+\r
+\r
+        .global USART_RX_vect\r
+USART_RX_vect:  \r
+        StoreSREG\r
+        // check for frame error\r
+        sbic    _SFR_IO_ADDR(UCSRA), FE\r
+        // framing error. Currrently, this is silently ignored\r
+        // real applications may wish to output information to LCD to indicate\r
+        // erroroneous byte received\r
+        rjmp    urxIsrDone\r
+        \r
+        in      isrTemp1, _SFR_IO_ADDR(UDR)\r
+        // Calculate next buffer position.\r
+        // tmphead = sUartRxHead;\r
+        // if (tmphead == UART_RX_BUFFER_SIZE-1)\r
+        //        tmphead = 0;\r
+        // else\r
+        //        tmphead++;\r
+        clr     isrTemp2\r
+        cpi     sUartRxHead, (UART_RX_BUFFER_SIZE-1)\r
+        breq    urxIsrAtEnd\r
+        mov     isrTemp2, sUartRxHead\r
+        inc     isrTemp2\r
+urxIsrAtEnd:\r
+        // store in buffer if there is room\r
+        //  if (tmphead != sUartRxTail) {\r
+        //    sUartRxHead = tmphead;         // Store new index.\r
+        //    sUartRxBuf[tmphead] = rx;\r
+        //  }\r
+        cp      isrTemp2, sUartRxTail\r
+        breq    urxIsrNoRoom\r
+        mov     sUartRxHead, isrTemp2\r
+        push    ZL\r
+        push    ZH\r
+        ldi     ZL, lo8(sUartRxBuf)\r
+        ldi     ZH, hi8(sUartRxBuf)\r
+        add     ZL, isrTemp2\r
+        adc     ZH, 0\r
+        st      Z, isrTemp1\r
+        pop     ZH\r
+        pop     ZL\r
+urxIsrNoRoom:\r
+        \r
+#if USE_CTS\r
+  // check if buffer is now full, if so switch CTS to inactive\r
+  if (tmphead == UART_RX_BUFFER_SIZE-1)\r
+    tmphead = 0;\r
+  else\r
+    tmphead++;\r
+  // CTS active if still room in buffer\r
+  if (tmphead != sUartRxTail) {\r
+    CTS_PORT |= (1<<CTS_PIN);\r
+  } else {\r
+    // no room left in buffer, so make CTS inactive\r
+    CTS_PORT &= ~(1<<CTS_PIN);\r
+  }\r
+#endif\r
+urxIsrDone:\r
+        RestoreSREG\r
+        reti\r
+\r