Add lss file for cpp_obj program
[avr_serial_lcd.git] / serial_lcd.S
1 /*****************************************************************************\r
2 * FILE IDENTIFICATION\r
3 *\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
8 **\r
9 **   Copyright (c) 2008 by Kevin Rosenberg\r
10 *******************************************************************************/\r
11 \r
12         #include <avr/io.h>\r
13 #ifndef F_CPU\r
14 #define F_CPU 14745600UL\r
15 #endif\r
16 \r
17 #define CONSERVATIVE_ENABLE_DURATION 0\r
18 #define USE_CTS 0\r
19 \r
20 /*************************************************************/\r
21 /*************************************************************/\r
22 \r
23 /*      //// From the LCD perspective \\\\\r
24         RxD --> PORTD:0\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
28         LCD:R/W <-- PORTD:4\r
29         LCD:RS <-- PORTD:5\r
30         LCD:E <-- PORTD:6\r
31         N/A <-> PORTD:7\r
32 \r
33         LCD:R/W <-- PORTD:4\r
34         LCD:RS <-- PORTD:5\r
35         LCD:E <-- PORTD:6\r
36         LCD:Vee <-- CONTRAST\r
37         LCD:DB0 <-- PORTB:0\r
38         LCD:DB1 <-- PORTB:1\r
39         LCD:DB2 <-- PORTB:2\r
40         LCD:DB3 <-- PORTB:3\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
45 \r
46         //// From the I/O PORTx perspective \\\\\r
47         PORTD:0 <--RxD\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
51         PORTD:4 --> LCD:R/W\r
52         PORTD:5 --> LCD:RS\r
53         PORTD:6 --> LCD:E\r
54         PORTD:7 <-> N/A\r
55 \r
56         PORTB:0 --> LCD:DB0\r
57         PORTB:1 --> LCD:DB1\r
58         PORTB:2 --> LCD:DB2\r
59         PORTB:3 --> LCD:DB3\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
64 */\r
65 \r
66 /*************************************************************/\r
67 /*************************************************************/\r
68 // If you want to use a different I/O port for LCD control & data,\r
69 // do it here!!!\r
70 #define LCD_DATA_PORT PORTB\r
71 #define LCD_DATA_PIN_REG PINB\r
72 #define LCD_DATA_DIR DDRB\r
73 \r
74 #define LCD_CONTROL_PORT PORTD\r
75 #define LCD_CONTROL_PIN_REG PIND\r
76 #define LCD_CONTROL_DIR DDRD\r
77 \r
78 // LED backlight control pin\r
79 #define LED_DIR DDRD\r
80 #define LED_PORT PORTD\r
81 #define LED_PIN PD1\r
82 \r
83 // LCD Read/Write Pin\r
84 #define LCD_RW PD4\r
85 // LCD Register Select Pin\r
86 #define LCD_RS PD5\r
87 // LCD Enable Pin\r
88 #define LCD_E PD6\r
89 /*************************************************************/\r
90 /*************************************************************/\r
91 \r
92 // LCD busy status pin\r
93 #define LCD_BUSY PB7\r
94 \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
99 #define BAUD_J1 PD2\r
100 #define BAUD_J2 PD3\r
101 \r
102 #if USE_CTS\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
110 #endif\r
111 \r
112 // Number of PWM brightness levels supported\r
113 #define LED_BRIGHTNESS_LEVELS 8\r
114 \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
120 // Set 4 data bits\r
121 #define LCD_4_Bit 0x20\r
122 // Set 8 data bits\r
123 #define LCD_8_Bit 0x30\r
124 // Set number of lines\r
125 #define LCD_4_Line 0x08\r
126 // Set 8 data bits\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
134 \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
145 \r
146 // Circular buffer for UART RX\r
147 #define UART_RX_BUFFER_SIZE 48\r
148 .section .data\r
149 sUartRxBuf:\r
150         .org 0\r
151 sUartTxBufEnd:\r
152         .org sUartRxBuf + UART_RX_BUFFER_SIZE\r
153 \r
154 .section .text\r
155 \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
169         \r
170 BaudLookupTable:\r
171         .byte BAUD_115200\r
172         .byte BAUD_38400\r
173         .byte BAUD_19200\r
174         .byte BAUD_9600\r
175 \r
176 // PWM Patterns (8-brightness levels)\r
177 ledPwmPatterns: \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
186 \r
187 \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
192 .macro ENABLE_WAIT\r
193        rjmp    1f\r
194 1:     rjmp    2f\r
195 2:     rjmp    3f\r
196 3:     nop\r
197 .endm\r
198 #else\r
199 .macro ENABLE_WAIT\r
200        rjmp    1f\r
201 1:     rjmp    2f\r
202 2:      \r
203 .endm\r
204 #endif\r
205 \r
206 // register variables\r
207 #define zeroReg       r1\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
224         \r
225 .macro LedTimerStop\r
226         out     _SFR_IO_ADDR(TCCR0B), zeroReg\r
227 .endm        \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
232 .endm\r
233 \r
234 .macro BACKLIGHT_OFF\r
235         cbr     ledStatus, 0\r
236 .endm\r
237 \r
238 .macro BACKLIGHT_ON\r
239         sbr     ledStatus, 0\r
240 .endm\r
241 \r
242 .macro LedPwmSwitchOff\r
243         cbi     _SFR_IO_ADDR(LED_PORT), LED_PIN\r
244         BACKLIGHT_OFF\r
245         LedTimerStop\r
246 .endm\r
247 \r
248 .macro LedPwmSwitchOn\r
249         // if (ledPwmPattern == 0xFF) { \r
250         //  LedTimerStop();\r
251         //  LED_PORT |= (1<<LED_PIN); // keep LED on at all times\r
252         // } else {\r
253         //  LedTimerStart();\r
254         // }\r
255         BACKLIGHT_ON\r
256         cpi     ledPwmPattern, 0xFF\r
257         breq    1f\r
258         LedTimerStart\r
259         rjmp    2f\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
262 2:     \r
263 .endm\r
264 \r
265 .macro StoreSREG\r
266 // Start with 256 prescaler\r
267         in      saveSregReg, _SFR_IO_ADDR(SREG)\r
268 .endm        \r
269 .macro RestoreSREG\r
270 // Start with 256 prescaler\r
271         out      _SFR_IO_ADDR(SREG), saveSregReg\r
272 .endm        \r
273 \r
274 .macro LedPwmInit\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
286         BACKLIGHT_OFF\r
287         ldi     ledPwmPattern, 0xFF     // maximum brightness\r
288 .endm\r
289 \r
290 \r
291 \r
292  // Initialize USART\r
293 .macro UsartInit\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
298 \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
305         asr     loadReg\r
306         asr     loadReg\r
307         ldi     ZH, lo8(BaudLookupTable)\r
308         ldi     ZL, hi8(BaudLookupTable)\r
309         add     ZL, loadReg\r
310         adc     ZH, zeroReg\r
311         lpm     r0, Z\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
318 \r
319 #if USE_CTS\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
322 #endif\r
323 .endm\r
324 \r
325 .macro LcdInit\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
330 \r
331         // delay 4100 ms\r
332         ldi     tmpReg1, 255\r
333         ldi     tmpReg2, 127\r
334         ldi     tmpReg3, 50\r
335         ldi     tmpReg4, 2\r
336 dl2:    subi    tmpReg1, 1\r
337         sbc     tmpReg2, zeroReg\r
338         sbc     tmpReg3, zeroReg\r
339         sbc     tmpReg4, zeroReg\r
340         brne    dl2\r
341         rjmp    dj2\r
342 dj2:    nop\r
343 \r
344         sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS   // Set LCD_RS high\r
345 \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
350 \r
351         // Initialize the LCD controller\r
352         cbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS\r
353         ldi     returnReg1, DATA_8\r
354         rcall   LcdWriteData\r
355 \r
356         // delay 4100 ms\r
357         ldi     tmpReg1, 255\r
358         ldi     tmpReg2, 127\r
359         ldi     tmpReg3, 184\r
360 dl0:    subi    tmpReg1, 1\r
361         sbc     tmpReg2, zeroReg\r
362         sbc     tmpReg3, zeroReg\r
363         brne    dl0\r
364         rjmp    dj0\r
365 dj0:    nop\r
366         \r
367         rcall   LcdWriteData\r
368 \r
369         // Delay 100uS\r
370         ldi     tmpReg1, 112\r
371         ldi     tmpReg2, 1\r
372 dl1:    subi    tmpReg1, 1\r
373         sbc     tmpReg2, zeroReg\r
374         brne    dl1\r
375         rjmp    dj1\r
376 dj1:\r
377         \r
378         rcall   LcdWriteData\r
379         sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS\r
380 \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
383         rcall   LcdWriteCmd\r
384         ldi     loadReg, LCD_ON // Power up the display\r
385         rcall   LcdWriteCmd\r
386         ldi     loadReg, LCD_CLR // Clear display\r
387         rcall   LcdWriteCmd\r
388 .endm\r
389 \r
390 ;;; Pass brightness (0-255) in returnReg1\r
391 LedPwmSetBrightness:\r
392         cpi     returnReg1, 0\r
393         brne    brightnessNotZero\r
394         // turn backlight off for 0 brightness\r
395 \r
396         sbrc    ledStatus, 0    ; Check if backlight on\r
397         rjmp    ledNotOn\r
398         LedTimerStop\r
399         mov     ledPwmPattern, zeroReg\r
400         mov     ledPwmCycling, zeroReg\r
401         cbi     _SFR_IO_ADDR(LED_PORT), LED_PIN\r
402 ledNotOn:       \r
403         ret\r
404 \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
410 brightMultLoop:\r
411         add     tmpReg1, returnReg1\r
412         adc     tmpReg2, zeroReg\r
413         dec     tmpReg3\r
414         brne    brightMultLoop\r
415         ldi     loadReg, 127\r
416         add     tmpReg1, loadReg\r
417         adc     tmpReg2, zeroReg\r
418         // ledPwmPos now in tmpReg2\r
419         \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
424         brlo    brightMax\r
425         ldi     tmpReg2, LED_BRIGHTNESS_LEVELS - 1\r
426 brightMax:      \r
427         //      ledPwmPattern = PGM_READ_BYTE (&ledPwmPatterns[ledPwmPos]);\r
428         ldi     ZL,lo8(ledPwmPatterns)\r
429         ldi     ZH,hi8(ledPwmPatterns)\r
430         add     ZL, tmpReg3\r
431         adc     ZH, zeroReg\r
432         lpm     r0,Z    // ledPwmPos is in r0\r
433         mov     tmpReg2, r0\r
434         mov     ledPwmCount, zeroReg\r
435         mov     ledPwmCycling, ledPwmPattern\r
436 \r
437         cpi     tmpReg2, (LED_BRIGHTNESS_LEVELS-1) // compare to maxmimum brightness\r
438         brsh    brightSetMax\r
439         // if (IS_BACKLIGHT_ON()) LedTimerStart();\r
440         ret\r
441 brightSetMax:\r
442         sbrc    ledStatus, 0\r
443         // don't need PWM for continuously on\r
444         ret\r
445         LedTimerStop\r
446         sbi     _SFR_IO_ADDR(LED_PORT), LED_PIN\r
447         ret\r
448         \r
449 .global main\r
450 main:\r
451         clr     zeroReg\r
452         wdr\r
453 \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
458 \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
462 \r
463         LcdInit \r
464 \r
465         // Initialize the AVR USART\r
466         UsartInit               \r
467 \r
468         sei\r
469 mainLoop:\r
470         wdr\r
471         cp      sUartRxTail, sUartRxHead\r
472         breq    mainNoChars\r
473         rcall   WaitRxChar\r
474 \r
475         cpi     returnReg1, LED_SW_OFF\r
476         brne    main1\r
477         LedPwmSwitchOff\r
478         rjmp    mainLoop\r
479 main1:  cpi     returnReg1, LED_SW_ON\r
480         brne    main2\r
481         LedPwmSwitchOn\r
482         rjmp    mainLoop\r
483 main2:  cpi     returnReg1, LED_SET_BRIGHTNESS\r
484         brne    main3\r
485         rcall   WaitRxChar  // read next byte which will be brightness\r
486         rcall   LedPwmSetBrightness\r
487         rjmp    mainLoop\r
488 main3:  cpi     returnReg1, REG_MODE\r
489         brne    main4\r
490         rcall   WaitRxChar\r
491         rcall   LcdWriteCmd // Send LCD command character\r
492         rjmp    mainLoop\r
493 main4:\r
494         rcall   LcdWriteData // Send LCD data character\r
495         rjmp    mainLoop\r
496 \r
497 mainNoChars:    \r
498       // No characters waiting in RX buffer\r
499         cli\r
500         wdr\r
501         \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
505 \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
511 \r
512         sei\r
513         in      loadReg, _SFR_IO_ADDR(MCUCR)\r
514         sbr     loadReg, SE\r
515         out     _SFR_IO_ADDR(MCUCR), loadReg\r
516         sleep\r
517         in      loadReg, _SFR_IO_ADDR(MCUCR)\r
518         cbr     loadReg, SE\r
519         out     _SFR_IO_ADDR(MCUCR), loadReg\r
520 \r
521         cli\r
522         wdr\r
523 \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
529 \r
530         sei\r
531         ret\r
532 \r
533 ;; input cmd in returnReg1\r
534 LcdWriteCmd:    \r
535         rcall   LcdBusyWait\r
536         out     _SFR_IO_ADDR(LCD_DATA_PORT), returnReg1\r
537         nop\r
538         sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
539         ENABLE_WAIT\r
540         cbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
541         nop\r
542         sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_RS // Set display to "Character mode"\r
543         ret\r
544 \r
545 // write character in returnReg1\r
546 LcdWriteData:\r
547         rcall   LcdBusyWait\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
550         nop\r
551         sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
552         ENABLE_WAIT\r
553         cbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
554         ret\r
555 \r
556         \r
557 ;;;  Waits for LCD to stop being busy, returns LCD status in returnReg1\r
558 LcdBusyWait:    \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
564         nop\r
565 LcdBusyLoop:\r
566         wdr\r
567         // LCD_CONTROL_PORT |= (1<<LCD_E);\r
568         sbi     _SFR_IO_ADDR(LCD_CONTROL_PORT), LCD_E\r
569         ENABLE_WAIT\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
575         rjmp    LcdBusyLoop\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
579         ldi     loadReg, 0xFF\r
580         out     _SFR_IO_ADDR(LCD_DATA_DIR), loadReg\r
581         ret                     \r
582         \r
583 ;;; Waits for a UART RX character and returns it in returnReg1 \r
584 WaitRxChar:     \r
585         // waits for next RX character, then return it\r
586         cp      sUartRxTail, sUartRxHead\r
587         wdr\r
588         breq    WaitRxChar\r
589 \r
590 #if USE_CTS\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
594   cli();\r
595 #endif\r
596 \r
597         // increment tail position\r
598         // if (tail == UART_RX_BUFFER_SIZE-1) sUartRxTail = 0  else  sUartRxTail++;\r
599         inc     sUartRxTail\r
600         cpi     sUartRxTail, UART_RX_BUFFER_SIZE\r
601         brne    waitRxBufIncremented\r
602         clr     sUartRxTail\r
603 waitRxBufIncremented:\r
604         \r
605 #if USE_CTS\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
608 #endif\r
609 \r
610         ldi     ZL, lo8(sUartRxBuf)\r
611         ldi     ZH, hi8(sUartRxBuf)\r
612         add     ZL, sUartRxTail\r
613         adc     ZH, zeroReg\r
614         ld      returnReg1, Z\r
615         ret\r
616 \r
617 \r
618 .global TIMER0_COMPA_vect\r
619 TIMER0_COMPA_vect:\r
620         StoreSREG\r
621         sei             // Okay to allow USART interrupts while controlling LED PWM\r
622 \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
630 \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
635 //  } else {\r
636 //    ledPwmCount++;\r
637 //    ledPwmCycling >>= 1;\r
638 //  }\r
639 //}\r
640         cpi     ledPwmCount, (LED_BRIGHTNESS_LEVELS - 1)\r
641         brsh    tcIsrResetCount\r
642         inc     ledPwmCount\r
643         asr     ledPwmCycling\r
644         rjmp    tcIsrDone\r
645 tcIsrResetCount:\r
646         clr     ledPwmCount\r
647         mov     ledPwmCycling, ledPwmPattern\r
648 \r
649 tcIsrDone:      \r
650         RestoreSREG\r
651         reti\r
652 \r
653 \r
654         .global USART_RX_vect\r
655 USART_RX_vect:  \r
656         StoreSREG\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
662         rjmp    urxIsrDone\r
663         \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
668         //        tmphead = 0;\r
669         // else\r
670         //        tmphead++;\r
671         clr     isrTemp2\r
672         cpi     sUartRxHead, (UART_RX_BUFFER_SIZE-1)\r
673         breq    urxIsrAtEnd\r
674         mov     isrTemp2, sUartRxHead\r
675         inc     isrTemp2\r
676 urxIsrAtEnd:\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
681         //  }\r
682         cp      isrTemp2, sUartRxTail\r
683         breq    urxIsrNoRoom\r
684         mov     sUartRxHead, isrTemp2\r
685         push    ZL\r
686         push    ZH\r
687         ldi     ZL, lo8(sUartRxBuf)\r
688         ldi     ZH, hi8(sUartRxBuf)\r
689         add     ZL, isrTemp2\r
690         adc     ZH, 0\r
691         st      Z, isrTemp1\r
692         pop     ZH\r
693         pop     ZL\r
694 urxIsrNoRoom:\r
695         \r
696 #if USE_CTS\r
697   // check if buffer is now full, if so switch CTS to inactive\r
698   if (tmphead == UART_RX_BUFFER_SIZE-1)\r
699     tmphead = 0;\r
700   else\r
701     tmphead++;\r
702   // CTS active if still room in buffer\r
703   if (tmphead != sUartRxTail) {\r
704     CTS_PORT |= (1<<CTS_PIN);\r
705   } else {\r
706     // no room left in buffer, so make CTS inactive\r
707     CTS_PORT &= ~(1<<CTS_PIN);\r
708   }\r
709 #endif\r
710 urxIsrDone:\r
711         RestoreSREG\r
712         reti\r
713 \r