--- /dev/null
+/*****************************************************************************\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