/***************************************************************************** * FILE IDENTIFICATION * ** Name: serial_lcd.S ** Purpose: Serial LCD Backpack code for GCC Assembler ** Programmer: Kevin Rosenberg (AVR Freaks member kmr) ** Date Started: April 2008 ** ** Copyright (c) 2008 by Kevin Rosenberg *******************************************************************************/ #include #ifndef F_CPU #define F_CPU 14745600UL #endif #define CONSERVATIVE_ENABLE_DURATION 0 #define USE_CTS 0 /*************************************************************/ /*************************************************************/ /* //// From the LCD perspective \\\\ RxD --> PORTD:0 LED <-- PORTD:1 // High = ON, Low = OFF J_1 --> PORTD:2 // BAUD rate select J_2 --> PORTD:3 // BAUD rate select LCD:R/W <-- PORTD:4 LCD:RS <-- PORTD:5 LCD:E <-- PORTD:6 N/A <-> PORTD:7 LCD:R/W <-- PORTD:4 LCD:RS <-- PORTD:5 LCD:E <-- PORTD:6 LCD:Vee <-- CONTRAST LCD:DB0 <-- PORTB:0 LCD:DB1 <-- PORTB:1 LCD:DB2 <-- PORTB:2 LCD:DB3 <-- PORTB:3 LCD:DB4 <-- 4.7K Ohm <-- PORTx:4 LCD:DB5 <-- 4.7K Ohm <-- PORTx:5 LCD:DB6 <-- 4.7K Ohm <-- PORTx:6 LCD:DB7 <-- 4.7K Ohm <-- PORTx:7 //// From the I/O PORTx perspective \\\\ PORTD:0 <--RxD PORTD:1 --> LED // High = ON, Low = OFF PORTD:2 <-- J_1 // BAUD rate select PORTD:3 <-- J_2 // BAUD rate select PORTD:4 --> LCD:R/W PORTD:5 --> LCD:RS PORTD:6 --> LCD:E PORTD:7 <-> N/A PORTB:0 --> LCD:DB0 PORTB:1 --> LCD:DB1 PORTB:2 --> LCD:DB2 PORTB:3 --> LCD:DB3 PORTB:4 --> 4.7K Ohm --> LCD:DB4 PORTB:5 --> 4.7K Ohm --> LCD:DB5 PORTB:6 --> 4.7K Ohm --> LCD:DB6 PORTB:7 --> 4.7K Ohm --> LCD:DB7 */ /*************************************************************/ /*************************************************************/ // If you want to use a different I/O port for LCD control & data, // do it here!!! #define LCD_DATA_PORT PORTB #define LCD_DATA_PIN_REG PINB #define LCD_DATA_DIR DDRB #define LCD_CONTROL_PORT PORTD #define LCD_CONTROL_PIN_REG PIND #define LCD_CONTROL_DIR DDRD // LED backlight control pin #define LED_DIR DDRD #define LED_PORT PORTD #define LED_PIN PD1 // LCD Read/Write Pin #define LCD_RW PD4 // LCD Register Select Pin #define LCD_RS PD5 // LCD Enable Pin #define LCD_E PD6 /*************************************************************/ /*************************************************************/ // LCD busy status pin #define LCD_BUSY PB7 // BAID rate setting pins #define BAUD_PORT PORTD #define BAUD_DIR DDRB #define BAUD_PIN_REG PIND #define BAUD_J1 PD2 #define BAUD_J2 PD3 #if USE_CTS // Direction register for clear to send signal #define CTS_DIR DDRA // Output port for clear to send signal #define CTS_PORT PORTA // Pin for clear to send signal (RESET pin on Tiny2313) // Ensure fuse is set to disable RESET function on RESET pin #define CTS_PIN PA2 #endif // Number of PWM brightness levels supported #define LED_BRIGHTNESS_LEVELS 8 ////// LCD Commands /////// // Turn on power to the display, no cursor #define LCD_ON 0x0C // Clear display command #define LCD_CLR 0x01 // Set 4 data bits #define LCD_4_Bit 0x20 // Set 8 data bits #define LCD_8_Bit 0x30 // Set number of lines #define LCD_4_Line 0x08 // Set 8 data bits #define DATA_8 0x30 // Set character font #define LCD_Font 0x04 // Turn the cursor on #define LCD_CURSOR_ON 0x02 // Turn on cursor blink #define LCD_CURSOR_BLINK 0x01 ////// Serial command codes /////// // ASCII control code to set brightness level // Next byte is brightness ranging from 0 (no backlight) to 255 (max backlight) #define LED_SET_BRIGHTNESS 0xFB // ASCII control code turns on LED #define LED_SW_ON 0xFC // ASCII control code turns off LED #define LED_SW_OFF 0xFD // Character generator RAM command #define REG_MODE 0xFE // Circular buffer for UART RX #define UART_RX_BUFFER_SIZE 48 .section .data sUartRxBuf: .org 0 sUartTxBufEnd: .org sUartRxBuf + UART_RX_BUFFER_SIZE .section .text // Actual baud rate = 9600 BAUD, (0.0% error) @ 14.7456MHz // Actual baud rate = 19.2K BAUD, (0.0% error) @ 14.7456MHz // Actual baud rate = 38.4K BAUD, (0.0% error) @ 14.7456MHz // Actual baud rate = 115.2K BAUD, (0.0% error) @ 14.7456MHz #define BAUD_4800 191 #define BAUD_9600 95 #define BAUD_14400 63 #define BAUD_19200 47 #define BAUD_28800 31 #define BAUD_38400 23 #define BAUD_57600 15 #define BAUD_76800 11 #define BAUD_115200 7 BaudLookupTable: .byte BAUD_115200 .byte BAUD_38400 .byte BAUD_19200 .byte BAUD_9600 // PWM Patterns (8-brightness levels) ledPwmPatterns: .byte 0x10 // 0b00010000 0 .byte 0x11 // 0b00010001 1 .byte 0x4A // 0b01001010 2 .byte 0x55 // 0b01010101 3 .byte 0x5D // 0b01011101 4 .byte 0xEE // 0b11101110 5 .byte 0x7F // 0b11110111 6 .byte 0xFF // 0b11111111 7 // PWeh must be 230nS minimum, nop = 67nS @ 14.7456MHz // At 4 cycles, E = 271nS // Some slow systems require 450ns, or 7 cycles at 14.7456MHz #if CONSERVATIVE_ENABLE_DURATION .macro ENABLE_WAIT rjmp 1f 1: rjmp 2f 2: rjmp 3f 3: nop .endm #else .macro ENABLE_WAIT rjmp 1f 1: rjmp 2f 2: .endm #endif // register variables #define zeroReg r1 #define saveSregReg r2 #define ledPwmCycling r3 #define isrTemp1 r16 #define isrTemp2 r17 #define isrTemp3 r18 #define ledPwmCount r19 #define ledStatus r20 #define sUartRxHead r21 #define sUartRxTail r22 #define returnReg1 r23 #define loadReg r24 #define ledPwmPattern r25 #define tmpReg1 r26 #define tmpReg2 r27 #define tmpReg3 r28 #define tmpReg4 r29 .macro LedTimerStop out _SFR_IO_ADDR(TCCR0B), zeroReg .endm .macro LedTimerStart // Start with 256 prescaler ldi loadReg, (1<> 8; mov tmpReg1, returnReg1 mov tmpReg2, zeroReg mov tmpReg3, (LED_BRIGHTNESS_LEVELS-2) brightMultLoop: add tmpReg1, returnReg1 adc tmpReg2, zeroReg dec tmpReg3 brne brightMultLoop ldi loadReg, 127 add tmpReg1, loadReg adc tmpReg2, zeroReg // ledPwmPos now in tmpReg2 // Below is probably not required, but ensures we don't exceed array // if (ledPwmPos > LED_BRIGHTNESS_LEVELS - 1) // ledPwmPos = LED_BRIGHTNESS_LEVELS - 1; cpi tmpReg2, LED_BRIGHTNESS_LEVELS - 1 brlo brightMax ldi tmpReg2, LED_BRIGHTNESS_LEVELS - 1 brightMax: // ledPwmPattern = PGM_READ_BYTE (&ledPwmPatterns[ledPwmPos]); ldi ZL,lo8(ledPwmPatterns) ldi ZH,hi8(ledPwmPatterns) add ZL, tmpReg3 adc ZH, zeroReg lpm r0,Z // ledPwmPos is in r0 mov tmpReg2, r0 mov ledPwmCount, zeroReg mov ledPwmCycling, ledPwmPattern cpi tmpReg2, (LED_BRIGHTNESS_LEVELS-1) // compare to maxmimum brightness brsh brightSetMax // if (IS_BACKLIGHT_ON()) LedTimerStart(); ret brightSetMax: sbrc ledStatus, 0 // don't need PWM for continuously on ret LedTimerStop sbi _SFR_IO_ADDR(LED_PORT), LED_PIN ret .global main main: clr zeroReg wdr ldi loadReg, (1<= LED_BRIGHTNESS_LEVELS-1) { // ledPwmCount = 0; // ledPwmCycling = ledPwmPattern; // } else { // ledPwmCount++; // ledPwmCycling >>= 1; // } //} cpi ledPwmCount, (LED_BRIGHTNESS_LEVELS - 1) brsh tcIsrResetCount inc ledPwmCount asr ledPwmCycling rjmp tcIsrDone tcIsrResetCount: clr ledPwmCount mov ledPwmCycling, ledPwmPattern tcIsrDone: RestoreSREG reti .global USART_RX_vect USART_RX_vect: StoreSREG // check for frame error sbic _SFR_IO_ADDR(UCSRA), FE // framing error. Currrently, this is silently ignored // real applications may wish to output information to LCD to indicate // erroroneous byte received rjmp urxIsrDone in isrTemp1, _SFR_IO_ADDR(UDR) // Calculate next buffer position. // tmphead = sUartRxHead; // if (tmphead == UART_RX_BUFFER_SIZE-1) // tmphead = 0; // else // tmphead++; clr isrTemp2 cpi sUartRxHead, (UART_RX_BUFFER_SIZE-1) breq urxIsrAtEnd mov isrTemp2, sUartRxHead inc isrTemp2 urxIsrAtEnd: // store in buffer if there is room // if (tmphead != sUartRxTail) { // sUartRxHead = tmphead; // Store new index. // sUartRxBuf[tmphead] = rx; // } cp isrTemp2, sUartRxTail breq urxIsrNoRoom mov sUartRxHead, isrTemp2 push ZL push ZH ldi ZL, lo8(sUartRxBuf) ldi ZH, hi8(sUartRxBuf) add ZL, isrTemp2 adc ZH, 0 st Z, isrTemp1 pop ZH pop ZL urxIsrNoRoom: #if USE_CTS // check if buffer is now full, if so switch CTS to inactive if (tmphead == UART_RX_BUFFER_SIZE-1) tmphead = 0; else tmphead++; // CTS active if still room in buffer if (tmphead != sUartRxTail) { CTS_PORT |= (1<