1 /*****************************************************************************
\r
2 * FILE IDENTIFICATION
\r
4 ** Name: serial_lcd.S
\r
5 ** Purpose: Serial LCD Backpack code for GCC Assembler
\r
6 ** Programmer: Kevin Rosenberg <kevin@rosenberg.net> (AVR Freaks member kmr)
\r
7 ** Date Started: April 2008
\r
9 ** Copyright (c) 2008 by Kevin Rosenberg
\r
10 *******************************************************************************/
\r
14 #define F_CPU 14745600UL
\r
17 #define CONSERVATIVE_ENABLE_DURATION 0
\r
20 /*************************************************************/
\r
21 /*************************************************************/
\r
23 /* //// From the LCD perspective \\\\
\r
25 LED <-- PORTD:1 // High = ON, Low = OFF
\r
26 J_1 --> PORTD:2 // BAUD rate select
\r
27 J_2 --> PORTD:3 // BAUD rate select
\r
36 LCD:Vee <-- CONTRAST
\r
41 LCD:DB4 <-- 4.7K Ohm <-- PORTx:4
\r
42 LCD:DB5 <-- 4.7K Ohm <-- PORTx:5
\r
43 LCD:DB6 <-- 4.7K Ohm <-- PORTx:6
\r
44 LCD:DB7 <-- 4.7K Ohm <-- PORTx:7
\r
46 //// From the I/O PORTx perspective \\\\
\r
48 PORTD:1 --> LED // High = ON, Low = OFF
\r
49 PORTD:2 <-- J_1 // BAUD rate select
\r
50 PORTD:3 <-- J_2 // BAUD rate select
\r
60 PORTB:4 --> 4.7K Ohm --> LCD:DB4
\r
61 PORTB:5 --> 4.7K Ohm --> LCD:DB5
\r
62 PORTB:6 --> 4.7K Ohm --> LCD:DB6
\r
63 PORTB:7 --> 4.7K Ohm --> LCD:DB7
\r
66 /*************************************************************/
\r
67 /*************************************************************/
\r
68 // If you want to use a different I/O port for LCD control & data,
\r
70 #define LCD_DATA_PORT PORTB
\r
71 #define LCD_DATA_PIN_REG PINB
\r
72 #define LCD_DATA_DIR DDRB
\r
74 #define LCD_CONTROL_PORT PORTD
\r
75 #define LCD_CONTROL_PIN_REG PIND
\r
76 #define LCD_CONTROL_DIR DDRD
\r
78 // LED backlight control pin
\r
79 #define LED_DIR DDRD
\r
80 #define LED_PORT PORTD
\r
83 // LCD Read/Write Pin
\r
85 // LCD Register Select Pin
\r
89 /*************************************************************/
\r
90 /*************************************************************/
\r
92 // LCD busy status pin
\r
93 #define LCD_BUSY PB7
\r
95 // BAID rate setting pins
\r
96 #define BAUD_PORT PORTD
\r
97 #define BAUD_DIR DDRB
\r
98 #define BAUD_PIN_REG PIND
\r
100 #define BAUD_J2 PD3
\r
103 // Direction register for clear to send signal
\r
104 #define CTS_DIR DDRA
\r
105 // Output port for clear to send signal
\r
106 #define CTS_PORT PORTA
\r
107 // Pin for clear to send signal (RESET pin on Tiny2313)
\r
108 // Ensure fuse is set to disable RESET function on RESET pin
\r
109 #define CTS_PIN PA2
\r
112 // Number of PWM brightness levels supported
\r
113 #define LED_BRIGHTNESS_LEVELS 8
\r
115 ////// LCD Commands ///////
\r
116 // Turn on power to the display, no cursor
\r
117 #define LCD_ON 0x0C
\r
118 // Clear display command
\r
119 #define LCD_CLR 0x01
\r
121 #define LCD_4_Bit 0x20
\r
123 #define LCD_8_Bit 0x30
\r
124 // Set number of lines
\r
125 #define LCD_4_Line 0x08
\r
127 #define DATA_8 0x30
\r
128 // Set character font
\r
129 #define LCD_Font 0x04
\r
130 // Turn the cursor on
\r
131 #define LCD_CURSOR_ON 0x02
\r
132 // Turn on cursor blink
\r
133 #define LCD_CURSOR_BLINK 0x01
\r
135 ////// Serial command codes ///////
\r
136 // ASCII control code to set brightness level
\r
137 // Next byte is brightness ranging from 0 (no backlight) to 255 (max backlight)
\r
138 #define LED_SET_BRIGHTNESS 0xFB
\r
139 // ASCII control code turns on LED
\r
140 #define LED_SW_ON 0xFC
\r
141 // ASCII control code turns off LED
\r
142 #define LED_SW_OFF 0xFD
\r
143 // Character generator RAM command
\r
144 #define REG_MODE 0xFE
\r
146 // Circular buffer for UART RX
\r
147 #define UART_RX_BUFFER_SIZE 48
\r
152 .org sUartRxBuf + UART_RX_BUFFER_SIZE
\r
156 // Actual baud rate = 9600 BAUD, (0.0% error) @ 14.7456MHz
\r
157 // Actual baud rate = 19.2K BAUD, (0.0% error) @ 14.7456MHz
\r
158 // Actual baud rate = 38.4K BAUD, (0.0% error) @ 14.7456MHz
\r
159 // Actual baud rate = 115.2K BAUD, (0.0% error) @ 14.7456MHz
\r
160 #define BAUD_4800 191
\r
161 #define BAUD_9600 95
\r
162 #define BAUD_14400 63
\r
163 #define BAUD_19200 47
\r
164 #define BAUD_28800 31
\r
165 #define BAUD_38400 23
\r
166 #define BAUD_57600 15
\r
167 #define BAUD_76800 11
\r
168 #define BAUD_115200 7
\r
176 // PWM Patterns (8-brightness levels)
\r
178 .byte 0x10 // 0b00010000 0
\r
179 .byte 0x11 // 0b00010001 1
\r
180 .byte 0x4A // 0b01001010 2
\r
181 .byte 0x55 // 0b01010101 3
\r
182 .byte 0x5D // 0b01011101 4
\r
183 .byte 0xEE // 0b11101110 5
\r
184 .byte 0x7F // 0b11110111 6
\r
185 .byte 0xFF // 0b11111111 7
\r
188 // PWeh must be 230nS minimum, nop = 67nS @ 14.7456MHz
\r
189 // At 4 cycles, E = 271nS
\r
190 // Some slow systems require 450ns, or 7 cycles at 14.7456MHz
\r
191 #if CONSERVATIVE_ENABLE_DURATION
\r
206 // register variables
\r
208 #define saveSregReg r2
\r
209 #define ledPwmCycling r3
\r
210 #define isrTemp1 r16
\r
211 #define isrTemp2 r17
\r
212 #define isrTemp3 r18
\r
213 #define ledPwmCount r19
\r
214 #define ledStatus r20
\r
215 #define sUartRxHead r21
\r
216 #define sUartRxTail r22
\r
217 #define returnReg1 r23
\r
218 #define loadReg r24
\r
219 #define ledPwmPattern r25
\r
220 #define tmpReg1 r26
\r
221 #define tmpReg2 r27
\r
222 #define tmpReg3 r28
\r
223 #define tmpReg4 r29
\r
225 .macro LedTimerStop
\r
226 out _SFR_IO_ADDR(TCCR0B), zeroReg
\r
228 .macro LedTimerStart
\r
229 // Start with 256 prescaler
\r
230 ldi loadReg, (1<<CS02)
\r
231 out _SFR_IO_ADDR(TCCR0B), loadReg
\r
234 .macro BACKLIGHT_OFF
\r
238 .macro BACKLIGHT_ON
\r
242 .macro LedPwmSwitchOff
\r
243 cbi _SFR_IO_ADDR(LED_PORT), LED_PIN
\r
248 .macro LedPwmSwitchOn
\r
249 // if (ledPwmPattern == 0xFF) {
\r
251 // LED_PORT |= (1<<LED_PIN); // keep LED on at all times
\r
253 // LedTimerStart();
\r
256 cpi ledPwmPattern, 0xFF
\r
260 1: LedTimerStop ; maximum brightness, no need for PWM
\r
261 sbi _SFR_IO_ADDR(LED_PORT), LED_PIN ; keep LED on at al times
\r
266 // Start with 256 prescaler
\r
267 in saveSregReg, _SFR_IO_ADDR(SREG)
\r
270 // Start with 256 prescaler
\r
271 out _SFR_IO_ADDR(SREG), saveSregReg
\r
275 // setup PWM to run at 1.25ms per interrupt.
\r
276 // This allows 8 levels of brightness at minimum of 100Hz flicker rate.
\r
277 ldi loadReg, (1<<WGM01) // CTC mode
\r
278 out _SFR_IO_ADDR(TCCR0A), loadReg
\r
279 out _SFR_IO_ADDR(TCCR0B), zeroReg // timer off
\r
280 ldi loadReq, 72 // 1.25ms with CLK/256 prescaler @ 14.7456MHz
\r
281 out _SFR_IO_ADDR(OCR0A), loadReg
\r
282 ldi loadReg, (1<<OCIE0A) // Turn on timer0 COMPA intr (all other timer intr off)
\r
283 out _SFR_IO_ADDR(TIMSK), loadReg
\r
284 cbi _SFR_IO_ADDR(LED_PORT), LED_PIN // Ensure LED is off during initialization
\r
285 sbi _SFR_IO_ADDR(LED_DIR), LED_PIN // Ensure LED is output direction
\r
287 ldi ledPwmPattern, 0xFF // maximum brightness
\r
292 // Initialize USART
\r
294 out _SFR_IO_ADDR(UCSRB), zeroReg // Disable while setting baud rate
\r
295 out _SFR_IO_ADDR(UCSRA), zeroReg
\r
296 ldi loadReg, ((1<<UCSZ1) | (1<<UCSZ0)) // 8 bit data
\r
297 out _SFR_IO_ADDR(UCSRC), loadReg
\r
299 in loadReg, _SFR_IO_ADDR(BAUD_PIN_REG) // Get BAUD rate jumper settings
\r
300 andi loadReg, ((1<<BAUD_J2) | (1<<BAUD_J1)) // Mask off unwanted bits
\r
301 // BAUD_J2 = PD.3, BAUD_J1 = PD.2
\r
302 // This is two bits too far to the left and the array index will
\r
303 // increment by 4, instead of 1.
\r
304 // Shift BAUD_J2 & BAUD_J1 right by two bit positions for proper array indexing
\r
307 ldi ZH, lo8(BaudLookupTable)
\r
308 ldi ZL, hi8(BaudLookupTable)
\r
312 out _SFR_IO_ADDR(UBRRL), r0 // Set baud rate
\r
313 out _SFR_IO_ADDR(UBRRH), zeroReg // Set baud rate hi
\r
314 ldi loadReg, ((1<<RXEN)|(1<<RXCIE)) // RXEN = Enable
\r
315 out _SFR_IO_ADDR(UCSRB), loadReg
\r
316 clr sUartRxHead // register variable, need to explicitly zero
\r
317 clr sUartRxTail // register variable, need to explicitly zero
\r
320 sbi _SFR_IO_ADDR(CTS_PORT), CTS_PIN // bring signal high
\r
321 sbi _SFR_IO_ADDR(CTS_DIR), CTS_PIN // CTS is active low
\r
326 ldi loadReg, ((1<<BAUD_J2) | (1<<BAUD_J1)) // Set BAUD_J2:BAUD_J1 PULL-UPS active
\r
327 out _SFR_IO_ADDR(LCD_CONTROL_PORT), loadReg
\r
328 ldi loadReg, 0xF2 // Set LCD_control BAUD_J2:BAUD_J1 to inputs
\r
329 out _SFR_IO_ADDR(LCD_CONTROL_DIR), loadReg
\r
336 dl2: subi tmpReg1, 1
\r
337 sbc tmpReg2, zeroReg
\r
338 sbc tmpReg3, zeroReg
\r
339 sbc tmpReg4, zeroReg
\r
344 sbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS // Set LCD_RS high
\r
346 // Initialize the LCD Data AVR I/O
\r
347 ldi loadReg, 0xFF // Set LCD_DATA_PORT as all outputs
\r
348 out _SFR_IO_ADDR(LCD_DATA_DIR), loadReg
\r
349 out _SFR_IO_ADDR(LCD_DATA_PORT), zeroReg // Set LCD_DATA_PORT to logic low
\r
351 // Initialize the LCD controller
\r
352 cbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS
\r
353 ldi returnReg1, DATA_8
\r
360 dl0: subi tmpReg1, 1
\r
361 sbc tmpReg2, zeroReg
\r
362 sbc tmpReg3, zeroReg
\r
372 dl1: subi tmpReg1, 1
\r
373 sbc tmpReg2, zeroReg
\r
379 sbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS
\r
381 // The display will be out into 8 bit data mode here
\r
382 ldi loadReg, (LCD_8_Bit | LCD_4_Line); // Set 8 bit data, 4 display lines
\r
384 ldi loadReg, LCD_ON // Power up the display
\r
386 ldi loadReg, LCD_CLR // Clear display
\r
390 ;;; Pass brightness (0-255) in returnReg1
\r
391 LedPwmSetBrightness:
\r
393 brne brightnessNotZero
\r
394 // turn backlight off for 0 brightness
\r
396 sbrc ledStatus, 0 ; Check if backlight on
\r
399 mov ledPwmPattern, zeroReg
\r
400 mov ledPwmCycling, zeroReg
\r
401 cbi _SFR_IO_ADDR(LED_PORT), LED_PIN
\r
405 brightnessNotZero:
\r
406 // ledPwmPos = (brightness * (LED_BRIGHTNESS_LEVELS-1) + (unsigned int) 127) >> 8;
\r
407 mov tmpReg1, returnReg1
\r
408 mov tmpReg2, zeroReg
\r
409 mov tmpReg3, (LED_BRIGHTNESS_LEVELS-2)
\r
411 add tmpReg1, returnReg1
\r
412 adc tmpReg2, zeroReg
\r
414 brne brightMultLoop
\r
416 add tmpReg1, loadReg
\r
417 adc tmpReg2, zeroReg
\r
418 // ledPwmPos now in tmpReg2
\r
420 // Below is probably not required, but ensures we don't exceed array
\r
421 // if (ledPwmPos > LED_BRIGHTNESS_LEVELS - 1)
\r
422 // ledPwmPos = LED_BRIGHTNESS_LEVELS - 1;
\r
423 cpi tmpReg2, LED_BRIGHTNESS_LEVELS - 1
\r
425 ldi tmpReg2, LED_BRIGHTNESS_LEVELS - 1
\r
427 // ledPwmPattern = PGM_READ_BYTE (&ledPwmPatterns[ledPwmPos]);
\r
428 ldi ZL,lo8(ledPwmPatterns)
\r
429 ldi ZH,hi8(ledPwmPatterns)
\r
432 lpm r0,Z // ledPwmPos is in r0
\r
434 mov ledPwmCount, zeroReg
\r
435 mov ledPwmCycling, ledPwmPattern
\r
437 cpi tmpReg2, (LED_BRIGHTNESS_LEVELS-1) // compare to maxmimum brightness
\r
439 // if (IS_BACKLIGHT_ON()) LedTimerStart();
\r
443 // don't need PWM for continuously on
\r
446 sbi _SFR_IO_ADDR(LED_PORT), LED_PIN
\r
454 ldi loadReg, (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)
\r
455 out _SFR_IO_ADDR(WDTCSR), loadReg
\r
456 ldi loadReg, (1<<WDE)|(1<<WDP3)|(1<<WDP0); // 1024K cycles, 8.0sec
\r
457 out _SFR_IO_ADDR(WDTCSR), loadReg
\r
459 in loadReg, _SFR_IO_ADDR(MCUCR)
\r
460 andi loadReg, ~((1<<SM1)|(1<<SM0)); // use idle sleep mode
\r
461 out _SFR_IO_ADDR(MCUCR), loadReg
\r
465 // Initialize the AVR USART
\r
471 cp sUartRxTail, sUartRxHead
\r
475 cpi returnReg1, LED_SW_OFF
\r
479 main1: cpi returnReg1, LED_SW_ON
\r
483 main2: cpi returnReg1, LED_SET_BRIGHTNESS
\r
485 rcall WaitRxChar // read next byte which will be brightness
\r
486 rcall LedPwmSetBrightness
\r
488 main3: cpi returnReg1, REG_MODE
\r
491 rcall LcdWriteCmd // Send LCD command character
\r
494 rcall LcdWriteData // Send LCD data character
\r
498 // No characters waiting in RX buffer
\r
502 in loadReg, _SFR_IO_ADDR(MCUSR)
\r
503 cbr loadReg, WDRF // clear any watchdog interrupt flags
\r
504 out _SFR_IO_ADDR(MCUSR), loadReg
\r
506 in loadReg, _SFR_IO_ADDR(WDTCSR)
\r
507 ori loadReg, (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)
\r
508 out _SFR_IO_ADDR(WDTCSR), loadReg
\r
509 cbr loadReg, WDE // watchdog timer off
\r
510 out _SFR_IO_ADDR(WDTCSR), loadReg
\r
513 in loadReg, _SFR_IO_ADDR(MCUCR)
\r
515 out _SFR_IO_ADDR(MCUCR), loadReg
\r
517 in loadReg, _SFR_IO_ADDR(MCUCR)
\r
519 out _SFR_IO_ADDR(MCUCR), loadReg
\r
524 in loadReg, _SFR_IO_ADDR(WDTCSR)
\r
525 ori loadReg, (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)
\r
526 out _SFR_IO_ADDR(WDTCSR), loadReg
\r
527 cbr loadReg, WDE // watchdog timer off
\r
528 out _SFR_IO_ADDR(WDTCSR), loadReg
\r
533 ;; input cmd in returnReg1
\r
536 out _SFR_IO_ADDR(LCD_DATA_PORT), returnReg1
\r
538 sbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E
\r
540 cbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E
\r
542 sbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS // Set display to "Character mode"
\r
545 // write character in returnReg1
\r
548 sbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS // Set display to "Character Mode"
\r
549 out _SFR_IO_ADDR(LCD_DATA_PORT), returnReg1
\r
551 sbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E
\r
553 cbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E
\r
557 ;;; Waits for LCD to stop being busy, returns LCD status in returnReg1
\r
559 out LCD_DATA_DIR, zeroReg // Set LCD data port to inputs
\r
560 // LCD_CONTROL_PORT &= ~(1<<LCD_RS); // Set display to "Register mode"
\r
561 cbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS
\r
562 // LCD_CONTROL_PORT |= (1<<LCD_RW); // Put the display in the read mode
\r
563 sbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RW
\r
567 // LCD_CONTROL_PORT |= (1<<LCD_E);
\r
568 sbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E
\r
570 in returnReg1, _SFR_IO_ADDR(LCD_DATA_PIN_REG)
\r
571 // LCD_CONTROL_PORT &= ~(1<<LCD_E);
\r
572 sbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E
\r
573 // } while (LCDStatus & (1<<LCD_BUSY));
\r
574 sbrc returnReg1, LCD_BUSY
\r
576 // LCD_CONTROL_PORT &= ~(1<<LCD_RW); // Put display in write mode
\r
577 cbi _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RW
\r
578 // LCD_DATA_DIR = 0xFF; // Set LCD data port to outputs
\r
580 out _SFR_IO_ADDR(LCD_DATA_DIR), loadReg
\r
583 ;;; Waits for a UART RX character and returns it in returnReg1
\r
585 // waits for next RX character, then return it
\r
586 cp sUartRxTail, sUartRxHead
\r
591 // turn off interrupts so that if UART char ready to be stored, then storage
\r
592 // will occur after CTS pin is set active. This allows UART ISR to set
\r
593 // CTS inactive if byte fills buffer after we remove current byte
\r
597 // increment tail position
\r
598 // if (tail == UART_RX_BUFFER_SIZE-1) sUartRxTail = 0 else sUartRxTail++;
\r
600 cpi sUartRxTail, UART_RX_BUFFER_SIZE
\r
601 brne waitRxBufIncremented
\r
603 waitRxBufIncremented:
\r
606 CTS_PORT |= (1<<CTS_PIN); // Ensure CTS is active since just removed a byte
\r
607 sei(); // Allow UART ISR to read character and change potentially change CTS
\r
610 ldi ZL, lo8(sUartRxBuf)
\r
611 ldi ZH, hi8(sUartRxBuf)
\r
612 add ZL, sUartRxTail
\r
618 .global TIMER0_COMPA_vect
\r
621 sei // Okay to allow USART interrupts while controlling LED PWM
\r
623 // Set current LED state based on cycling variable
\r
624 // if (ledPwmCycling & 0x01) LED_PORT |= (1<<LED_PIN); else LED_PORT &= ~(1<<LED_PIN);
\r
625 in isrTemp3,_SFR_IO_ADDR(LED_PORT)
\r
626 sbr isrTemp3, LED_PIN
\r
627 sbrc ledPwmCycling, 0
\r
628 cbr isrTemp3, LED_PIN
\r
629 out _SFR_IO_ADDR(LED_PORT), isrTemp3
\r
631 // Update cycling variable for next interrupt
\r
632 // if (ledPwmCount >= LED_BRIGHTNESS_LEVELS-1) {
\r
633 // ledPwmCount = 0;
\r
634 // ledPwmCycling = ledPwmPattern;
\r
637 // ledPwmCycling >>= 1;
\r
640 cpi ledPwmCount, (LED_BRIGHTNESS_LEVELS - 1)
\r
641 brsh tcIsrResetCount
\r
647 mov ledPwmCycling, ledPwmPattern
\r
654 .global USART_RX_vect
\r
657 // check for frame error
\r
658 sbic _SFR_IO_ADDR(UCSRA), FE
\r
659 // framing error. Currrently, this is silently ignored
\r
660 // real applications may wish to output information to LCD to indicate
\r
661 // erroroneous byte received
\r
664 in isrTemp1, _SFR_IO_ADDR(UDR)
\r
665 // Calculate next buffer position.
\r
666 // tmphead = sUartRxHead;
\r
667 // if (tmphead == UART_RX_BUFFER_SIZE-1)
\r
672 cpi sUartRxHead, (UART_RX_BUFFER_SIZE-1)
\r
674 mov isrTemp2, sUartRxHead
\r
677 // store in buffer if there is room
\r
678 // if (tmphead != sUartRxTail) {
\r
679 // sUartRxHead = tmphead; // Store new index.
\r
680 // sUartRxBuf[tmphead] = rx;
\r
682 cp isrTemp2, sUartRxTail
\r
684 mov sUartRxHead, isrTemp2
\r
687 ldi ZL, lo8(sUartRxBuf)
\r
688 ldi ZH, hi8(sUartRxBuf)
\r
697 // check if buffer is now full, if so switch CTS to inactive
\r
698 if (tmphead == UART_RX_BUFFER_SIZE-1)
\r
702 // CTS active if still room in buffer
\r
703 if (tmphead != sUartRxTail) {
\r
704 CTS_PORT |= (1<<CTS_PIN);
\r
706 // no room left in buffer, so make CTS inactive
\r
707 CTS_PORT &= ~(1<<CTS_PIN);
\r