Full Version : LCD Driver for Butterfly (ASM)
avr >>BEGINNER UTILITIES >>LCD Driver for Butterfly (ASM)


AVR_Admin- 04-27-2006
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'
;



So, I am sorry that the above is sooooo long. I am too lazy to copy/paste, zip and attach as a separate file here. I am very sure there are things I could do better. One of the first things I would do if I had time would be to arrange the lookup table such that I could just use ASCII characters and numbers as the index into the table. I am sure I could shorten up the routine if I had time to really study it. My life and job aren't cooperating as far as time goes .

Regards,
Steve


Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.