| QUOTE ("SteveN") |
I have post below the applicable parts of my code that deals with the LCD display. I will attempt to explain why and how I do certain things certain ways. I cannot explain everything though because, to be honest, I have forgotten why certain things have to be done a certain way (or at least why I thought they had to be done that way). Such things as why do I have routines to determine whether the digit is odd or even, or why I swap nibbles in a byte. I used application notes AVR064 and AVR065 and the STK502 User Manual extensively when I was learning how to deal with the Butterfly LCD. You can find the application notes on Atmels web site but the STK502 User Manual is no longer there (I think). I do believe someone pointed out that the STK502 User Manual could be found on this site. I found it on an Atmel CD that came with one of my "toys" I bought from Atmel. Anyway, the code is cut and pasted from an application I did so it may seem chopped up. I hope I get everything. |
| CODE |
; ***************************************************************************** ; ***************************************************************************** ; ; REGISTER DEFINITIONS - START ; .def TC_cntL = r0 ; Total Cycles count, low byte .def TC_cntH = r1 ; Total Cycles count, high byte ; ; Note: The LCD display used on the Butterfly has 7 alphanumeric digits ; available (+ special symbols), BUT, the mega169 does not have enough ; I/O available to drive all those segments. Thus, the Butterfly only ; uses 6 of the digits. Reading from left to right on the display, the ; first digit is not used...that is why I start with digit2 below. ; .def digit2 = r2 ; = 100,000's digit on Butterfly display .def digit3 = r3 ; = 10,000's digit on Butterfly display .def digit4 = r4 ; = 1,000's digit on Butterfly display .def digit5 = r5 ; = 100's digit on Butterfly display .def digit6 = r6 ; = 10's digit on Butterfly display .def digit7 = r7 ; = 1's digit on Butterfly display .def FC_cntL = r8 ; Failed Cycles count, low byte .def FC_cntH = r9 ; Failed Cycles count, high byte .def Delay_amt1 = r10 .def Delay_amt2 = r11 .def Byte_cnt = r12 .def Bit_pntr = r13 .def Rotate_cnt = r14 .def PC_INT1_cnt = r15 .def temp1L = r16 .def temp1H = r17 .def temp2L = r18 .def temp2H = r19 ; .def T0OF_cnt = r20 ; Timer0 overflow count .def T2OF_cnt = r21 ; Timer2 overflow count ; .def RangeE = r22 ; Range Exceeded ; ; ----------------------------------------------------------------------------- .def Flags = r23 ; --- bit definitions for Flags register --- ; ; bit0 = One second passed yet (OS) 1=Yes 0=No ; bit1 = Pass/Fail status (PF) 1=Fail 0=Pass ; bit2 = Even/Odd digit (EOD) 1=Odd digit, 0=Even digit ; bit3 = First time PE4 ISR (FTPE4) 1=>than first 0=first time through ; bit4 = Out of range (OOR) 1=out of range 0=within range ; bit5 = TBA ; bit6 = TBA ; bit7 = TBA ; ----------------------------------------------------------------------------- ; .def wtempL = r24 ; register pair to be used with adiw, sbiw .def wtempH = r25 ; (for example to find when a 16 bit value ; equals zero) ; ; ; REGISTER DEFINITIONS - END ; ; ***************************************************************************** ; ***************************************************************************** Code: ; ***************************************************************************** ; ***************************************************************************** ; ; EQUATES - START ; .equ OS_bn = 0 ; One second flag bit number .equ OS_msk = (1<<0); One second flag bit mask (0b00000001) ; .equ PF_bn = 1 ; Pass/Fail flag bit number .equ PF_msk = (1<<1); Pass/Fail flag bit mask (0b00000010) ; .equ EOD_bn = 2 ; Even/Odd Digit flag bit number .equ EOD_msk = (1<<2); Even/Odd Digit flag bit mask (0b00000100) ; .equ FTPE4_bn = 3 ; First time PE4 ISR status bit number .equ FTPE4_msk = (1<<3); First time PE4 ISR status bit mask ; .equ OOR_bn = 4 ; Timer0 value Out Of Range flag bit number .equ OOR_msk = (1<<4); Timer0 value Out Of Range flag bit mask ; ;.equ TBA = 5 ; ;.equ TBA = (1<<5); ; ;.equ TBA = 6 ; ;.equ TBA = (1<<6); ; .equ PC_INT1_bn = 7 ; .equ PC_INT1_msk = (1<<7); ; ; ; EQUATES - END ; ; ***************************************************************************** ; ***************************************************************************** ; ; ***************************************************************************** ; ***************************************************************************** ; ; Internal SRAM assignments - Start ; .dseg ; .org 0x0100 ; start of internal SRAM and an even address LCDDRx_ptr: .byte 2 ; temp save of LCDDRx pointer (low byte=YL) LCD_ptr: .byte 2 ; temp save of LCD data pointer (low byte=ZL) X_save: .byte 2 ; save XH:XL pointer register value here Cycle: .byte 90 ; 90 x 8 = 720 possible Cycles - each bit of a ; byte represents a completed Cycle. A "0" in ; a bit represents a "Passed" Cycle, a "1" in ; a bit represents a "Failed" Cycle ; ; Note: A cycle consists of (approximately) a 1 minute On and a 1 minute Off ; period. Therefore it is possible to measure approximately 30 cycles ; per hour. Over a 12 hour period the total number of cycles is ; approximately 12 * 30 = 360. The above 720 possible cycles would ; provide room for a 24 hour burn-in period. ; ; Internal SRAM assignments - End ; ; ***************************************************************************** ; ***************************************************************************** Code: ; ----------------------------------------------------------------------------- ; ; LCD Initialization - Start ; ; Use external 8MHz clock oscillator ; Setup for 1/3 Bias (LCD2B = 0) ; Setup for 1/4 duty and enable all COM (LCDMUX1,LCDMUX0 = 0b11) pins ; Enable all segment (LCDPM2,LCDPM1,LCDPM0 = 0b111) pins ; ldi temp1L, (0<<LCDCS) | (0<<LCD2B) | (3<<LCDMUX0) | (7<<LCDPM0) sts LCDCRB, temp1L ; ; Dividing LCD clock (8.00MHz) by 4096 (LCDPS2,LCDPS1,LCDPS0 = 0b111) ; and dividing output from prescaler by 8 (LCDCD2,LCDCD1,LCDCD0 = 0b111) ; gives a frame rate of 30.5 Hz ; ldi temp1L, (7<<LCDPS0) | (7<<LCDCD0) sts LCDFRR, temp1L ; ; Set LCD Contrast Control output voltage to 3.0 V ; ldi temp1L, (1<<LCDCC3) sts LCDCCR, temp1L ; ; Enable LCD ; ldi temp1L, (1<<LCDEN) sts LCDCRA, temp1L ; setup to display "WAIT04" on the LCD display ; "04" is the version of this code ldi temp1L,0x21 ; W mov digit2,temp1L ldi temp1L,0x0B ; A mov digit3,temp1L ldi temp1L,0x13 ; I mov digit4,temp1L ldi temp1L,0x1E ; T mov digit5,temp1L ldi temp1L,0x00 ; 0 mov digit6,temp1L ldi temp1L,0x04 ; 4 mov digit7,temp1L call LCD_Display ; ; LCD Initialization - End ; Code: ; ----------------------------------------------------------------------------- ; ----------------------------------------------------------------------------- ; ; LCD_Display SUBROUTINE - START ; ; The purpose of this software is to take BCD coded values stored in r2-r7 and ; display them on the Butterfly LCD display. The BCD coded values are stored in ; registers R2 - R7. The first value stored is labeled ; digit2 and represents the 100,000's digit of a number (my end application ; for this routine will not actually use this digit...it is included here for ; possible future use). ; ; There are 6 registers reserved for these BCD values because there are 6 ; digits on the Butterfly display (actually there are 7 digits available on the ; Butterfly display but because of limitations of the mega169 only 6 are ; available for use). Please see the "REGISTER DEFINITIONS" section of this ; code for further naming conventions for these SRAM locations. ; ; The digits on the Butterfly LCD display are numbered 2 - 7 going from left to ; right (the unused digit is the far left #1 digit). The code is, obviously, ; written to place the 100,000's digit in the far left LCD digit, the 10'000's ; digit in the next LCD digit to the right and so forth until the 1's digit is ; written to the far right LCD digit. ; ; It is up to the routine calling this routine to place the BCD values into the ; (correct) registers before calling this routine. ; ; I have utilized pointers (X, Y, and Z) so that I can use looping code instead ; of straight line code. Please see the "Pointer Initialization" section of this ; routine to see how I have assigned the pointers. ; ; I have utilized a look-up table located in Flash program memory as a means of ; converting the BCD values (stored in SRAM) into values that the mega169 LCD ; controller can use to activate the appropriate segments. ; ; ***************************************************************************** ; ***************************************************************************** ; ; Statistics ; ; Execution time = 626 cycles (78.25uS @ 8MHz clock) ; Registers used = 5 ; SRAM used = 9 ; ; ; ***************************************************************************** ; ***************************************************************************** ; ; ; save registers used in this routine LCD_Display: push XL push XH push YL push YH push ZL push ZH push wtempL push wtempH push temp2L push temp2H push temp1L push temp1H ldi temp1L,0x00 ; clear LCDDRx registers of previous data ldi YL,LCDDR0 ; Y points to LCDDRx registers clr YH Clear_LCDDRx: st Y+,temp1L cpi YL,LCDDR18+1 brne Clear_LCDDRx ; suppress leading zeroes if present and/or required in number to be displayed ; in 100's, 10's and 1's digits (r5, r6 and r7) ldi XL,0x05 ; X points to 100's (r5) digit clr XH ; X will never be greater than 255 ; therefore XH will always be zero Leading_zero: ld temp1L,X ; temp1L = contents @ X (r5, r6 or r7) tst temp1L ; is it = 0 ? brne Pointer_init ; if not, go re-initialize pointer to r2 ; (r2 = leftmost digit of LCD) ldi temp1L,0x0A ; otherwise save 0x0A offset (0x0A points to ; value saved in the lookup table for a blank ; digit) st X,temp1L ; into location pointed to by X adiw XH:XL,0x01 ; point to next digit in SRAM cpi XL,0x08 ; compare X with address of r7 + 1 breq Pointer_init ; if 2 values compare r7 has been checked rjmp Leading_zero ; if not, go check r6 or r7 ; X will never be greater than 255 ; therefore XH will always be zero ; end of leading zero suppression checking ; setting up the pointer registers Pointer_init: ldi XL,0x02 ; X points to r2 (left most digit) clr XH ldi YL,LCDDR0 ; Y points to LCDDRx registers clr YH ldi ZL,low(2*LCD_data) ; Z points to LCD data lookup table ldi ZH,high(2*LCD_data) Save_seeds: sts LCDDRx_ptr,YL ; save Y pointer sts LCD_ptr,ZL ; save Z pointer sts LCD_ptr+1,ZH sbrc XL,0 ; if pointer = even digit - clear that flag bit rjmp Set_Odd ; otherwise go set that flag bit cbr Flags,EOD_msk ; clear Odd/Even Flags bit to indicate Even dig. rjmp Load_LCDDRx1 Set_Odd: sbr Flags,EOD_msk ; set Odd/Even Flags bit to indicate Odd digit ; Load_LCDDRx1: ldi wtempL,0x02 ; setup counter ld temp1L,X+ ; load digit value (X=BCD digit pointer) lsl temp1L ; multiply digit value by 2 add ZL,temp1L ; add offset to Z (Z=Lookup table pointer) clr temp1L adc ZH,temp1L Load_LCDDRx2: ; X points to low byte first time thru lpm temp1L,Z+ ; X points to high byte second time thru mov temp1H,temp1L ; copy temp1L into temp1H sbrc Flags,EOD_bn ; if Odd/Even bit = even (0) then go do Even rjmp Odd ; otherwise do Odd Even: swap temp1H ; swap nibbles for temp1H for even digits andi temp1H,0x0F ; clear high nibble andi temp1L,0x0F ; clear high nibble rjmp Continue_load Odd: swap temp1L ; swap nibbles for temp1L for odd digits andi temp1L,0xF0 ; clear low nibble andi temp1H,0xF0 ; clear low nibble Continue_load: ld temp2L,Y ; save whatever was in nibble that is not being or temp1L,temp2L ; written this time st Y,temp1L ; LCDDRx = low nibble of byte of LCD data code adiw YH:YL,0x05 ; go to next LCDDRx for this digit ld temp2L,Y ; save whatever was in nibble that is not being or temp1H,temp2L ; written this time st Y,temp1H ; LCDDRx = hi nibble of byte of LCD data code dec wtempL ; have we written all four registers ? breq Reload_seeds ; if so go reload seeds and get next digit adiw YH:YL,0x05 ; if not, go to next LCDDRx for this digit rjmp Load_LCDDRx2 Reload_seeds: cpi XL,0x08 ; have we displayed all locations? breq LCD_Display_ret ; if we have return to Main loop routine ; otherwise continue here lds YL,LCDDRx_ptr ; reload Y pointer seed (Y=LCDDRx pointer) sbrc Flags,EOD_bn ; if Odd/Even bit = Even (0) then don't inc YL inc YL ; Odd/Even bit = Odd, point to next higher ; LCDDRx lds ZL,LCD_ptr ; reload Z pointer seed lds ZH,LCD_ptr+1 rjmp Save_seeds ; display next digit LCD_Display_ret: cbr Flags,EOD_msk ; clear Even/Odd Flags bit pop temp1H pop temp1L pop temp2H pop temp2L pop wtempH pop wtempL pop ZH pop ZL pop YH pop YL pop XH pop XL ret ; ; ; ; LCD_Display SUBROUTINE - END ; ; ----------------------------------------------------------------------------- Code: ; ***************************************************************************** ; ***************************************************************************** ; ; ; LOOK-UP TABLES/FLASH CONSTANTS - START ; LCD_data: .dw 0x5559 ; (0x00) zero .dw 0x0118 ; (0x01) one .dw 0x1e11 ; (0x02) two .dw 0x1b11 ; (0x03) three .dw 0x0b50 ; (0x04) four .dw 0x1b41 ; (0x05) five .dw 0x1f41 ; (0x06) six .dw 0x0111 ; (0x07) seven .dw 0x1f51 ; (0x08) eight .dw 0x1b51 ; (0x09) nine .dw 0x0000 ; (0x0A) blank display, for leading zero suppression .dw 0x0f51 ; (0x0B)'A' .dw 0x3991 ; (0x0C)'B' .dw 0x1441 ; (0x0D)'C' .dw 0x3191 ; (0x0E)'D' .dw 0x1e41 ; (0x0F)'E' .dw 0x0e41 ; (0x10)'F' .dw 0x1d41 ; (0x11)'G' .dw 0x0f50 ; (0x12)'H' .dw 0x2080 ; (0x13)'I' .dw 0x1510 ; (0x14)'J' .dw 0x8648 ; (0x15)'K' .dw 0x1440 ; (0x16)'L' .dw 0x0578 ; (0x17)'M' .dw 0x8570 ; (0x18)'N' .dw 0x1551 ; (0x19)'O' .dw 0x0e51 ; (0x1A)'P' .dw 0x9551 ; (0x1B)'Q' .dw 0x8e51 ; (0x1C)'R' .dw 0x9021 ; (0x1D)'S' .dw 0x2081 ; (0x1E)'T' .dw 0x1550 ; (0x1F)'U' .dw 0x4448 ; (0x20)'V' .dw 0xc550 ; (0x21)'W' .dw 0xc028 ; (0x22)'X' .dw 0x2028 ; (0x23)'Y' .dw 0x5009 ; (0x24)'Z' ; |