Full Version : David Vanhorn's Fatuba Display Handler (AVR ASM)
avr >>ASSMBLER ROUTINES >>David Vanhorn's Fatuba Display Handler (AVR ASM)


Admin5- 04-19-2006
David Vanhorn's VFD.asm

Handler for Futaba Vacuum Fluorescent Displays

CODE

;
; File Name  :"VFD.asm"
; Title   :VFD routines
; Date   :
; Version  :
; Support telephone :765 287 1987  David B. VanHorn
; Support fax  :765 287 1989
; Support Email  :dvanhorn@cedar.net
; Target MCU  :AT90S8515
;
; DESCRIPTION
; Written for a Futaba US162SD03CB display
; Two lines of 16 chars each, Sync serial interface. Only 3 pins!
;
;***************************************************************************;
; M O D I F I C A T I O N   H I S T O R Y
;
;
;       rev.      date    who   why
; ---- --------  --- ------------------------------------------
; 0.01 98.11.14  dvh Creation
;
;***************************************************************************
;Pin
;number Symbol Function
   1 VCC
   2 Clock
   3 Ground
   4 Data
   5 Reset
;
;************************************************************
;
;Hardware pins assigned here to logical names.
;
.equ VFD_DATA=PORTA;VFD data lines interface
 ;Only D7-5 used. D4-0 are open
.equ VFD_INPUT=PINA;VFD Input Pins, as above
.equ VFD_CTRL=PORTD;VFD control lines interface
.equ VFD_R=6 ;VFD Reset
.equ VFD_D=7 ;VFD Data
.equ VFD_C=4 ;VFD Clock
.equ Half_Disp=((VFD_Size/2)-1)
;
;
;*************************************************************
;Things you need to set up:
;*************************************************************
;
;A register called "Bitflags". I use bit 6 (x1xxxxxx) to flag
;that the VFD_BUF has changed, and the display needs updating.
;
;An equate for the display size
;.equ VFD_Size=32;total number of chars
;
;Set a buffer somewhere in RAM, called VFD_BUF, whose size
;is VFD_Size
;
;In the idle routine, test Bitflags. If bit 6 is set, then call
;VFD_Spew to send the buffer to the display. It will clear bit
;6 so the test fails next time. This cuts down on EMI from
;excessive display updates. It also frees up a lot of CPU time.
;
;You can also call the following scroll routines:
;Scroll_All_Left;Entire display left
;Scroll_All_Right;Entire display right
;Scroll_Top_Left;Top half left
;Scroll_Top_Right;Top half right
;Scroll_Bot_Left;Bottom half left
;Scroll_Bot_Right;Bottom half right
;
;The scroll routines only affect the buffer, but they also
;set the flag in Bitflags so that the display will be updated.
;
;          00000000011111111112222222222333
;          12345678901234567890123456789012
VFD_TEST: "Hello, your dispis working fine."
;
;************************************************************
;Prompt routines. To start with, just call this one
;************************************************************
;
Say_Test:
ldi R30,low(VFD_TEST*2);Make the Z reg point at the table
ldi R31,high(VFD_TEST*2);preparing for the LPM instruction
rcall LCD_STR_OUT ;String to buf, buf to LCD
ret
;
;****************************************************************
;
;These routines roll the entire display memory from left to right, or right to left.
;
Scroll_All_Left:
push TEMP  ;
push TEMP2  ;
push LOOP  ;

ldi TEMP,VFD_Size-1 ;How wide is your buffer, -1
mov LOOP,TEMP ;
ldi ZH,high(VFD_OUT_BUF);Point at the beginning
ldi ZL,low(VFD_OUT_BUF);
ld TEMP2,Z+ ;Pick up the first one
ldi YH,high(VFD_OUT_BUF);
ldi YL,low(VFD_OUT_BUF);
;At this point, Z points to buffer+1, and Y points to buffer.
;Temp2 contains the first char, which will be over-written by
;the first shift.
rjmp Scroll_Left_Loop;

Scroll_Top_Left:
push TEMP  ;
push TEMP2  ;
push LOOP  ;

ldi TEMP,Half_Disp ;
mov LOOP,TEMP ;
ldi ZH,high(VFD_OUT_BUF);Point at the beginning
ldi ZL,low(VFD_OUT_BUF);
ld TEMP2,Z+ ;Pick up the first one
ldi YH,high(VFD_OUT_BUF);
ldi YL,low(VFD_OUT_BUF);
;At this point, Z points to buffer+1, and Y points to buffer.
;Temp2 contains the first char, which will be over-written by
;the first shift.
rjmp Scroll_Left_Loop;

Scroll_Bot_Left:
push TEMP  ;
push TEMP2  ;
push LOOP  ;

ldi TEMP,Half_Disp  ;How wide is your buffer, -1
mov LOOP,TEMP  ;
ldi ZH,high(VFD_OUT_BUF+Half_Disp+1);Point at the beginning
ldi ZL,low(VFD_OUT_BUF+Half_Disp+1);
ld TEMP2,Z+  ;Pick up the first one
ldi YH,high(VFD_OUT_BUF+Half_Disp+1);
ldi YL,low(VFD_OUT_BUF+Half_Disp+1);
rjmp Scroll_Left_Loop ;
;
;The Y and Z pointers are set, and TEMP2 contains the end char, TEMP has the
;number of chars to scroll. All we do here is pick them up, and set them down.
;
Scroll_Left_Loop:

ld TEMP,Z+  ;Pick up the second char and then point at
   ;the third
st Y+,TEMP  ;Store second at first, and then point at
   ;the second.
dec Loop  ;One less to shift.
brne Scroll_Left_Loop;

st Y,TEMP2  ;Put the first char in the last cell
mov TEMP,Bitflags ;
ori TEMP,$40 ;Flag the display dirty

pop LOOP  ;
pop TEMP2  ;
pop TEMP  ;
ret



Scroll_All_Right:
push TEMP  ;
push TEMP2  ;
push LOOP  ;

ldi TEMP,LCD_Size-1  ;
mov LOOP,TEMP  ;
ldi ZH,high(LCD_OUT_BUF+LCD_Size);Point at the beginning
ldi ZL,low(LCD_OUT_BUF+LCD_Size);
ld TEMP2,Z   ;Pick up the first one
ld R0,-Z   ;Post dec
ldi YH,high(LCD_OUT_BUF+LCD_Size);
ldi YL,low(LCD_OUT_BUF+LCD_Size);
rjmp Scroll_Right_Loop ;

Scroll_Top_Right:
push TEMP  ;
push TEMP2  ;
push LOOP  ;

ldi TEMP,Half_Disp  ;
mov LOOP,TEMP  ;
ldi ZH,high(LCD_OUT_BUF+Half_Disp);Point at the beginning
ldi ZL,low(LCD_OUT_BUF+Half_Disp);
ld TEMP2,Z   ;Pick up the first one
ld R0,-Z   ;Toss
ldi YH,high(LCD_OUT_BUF+Half_Disp);
ldi YL,low(LCD_OUT_BUF+Half_Disp);
rjmp Scroll_Right_Loop ;

Scroll_Bot_Right:
push TEMP   ;
push TEMP2   ;
push LOOP   ;

ldi TEMP,Half_Disp  ;
mov LOOP,TEMP  ;
ldi ZH,high(LCD_OUT_BUF+LCD_Size-1);Point at the beginning
ldi ZL,low(LCD_OUT_BUF+LCD_Size-1);
ld TEMP2,Z   ;Pick up the first one
ld R0,-Z   ;Toss
ldi YH,high(LCD_OUT_BUF+LCD_Size-1);
ldi YL,low(LCD_OUT_BUF+LCD_Size-1);
rjmp Scroll_Right_Loop ;
;
;Same as Scroll_Left_Loop, but note the pre-dec rather than post inc.
;
Scroll_Right_Loop:

ld TEMP,Z  ;Pick it up here
ld R0,-Z  ;This just does a post dec on Z
st Y,TEMP  ;Put it down there
ld R0,-Y  ;And a post dec on Y
dec Loop  ;One less to mess with
brne Scroll_Right_Loop;If not done, do more

st Y,TEMP2  ;Put the first char in the last cell
mov TEMP,Bitflags ;
ori TEMP,$40 ;Flag the display dirty

pop LOOP  ;
pop TEMP2  ;
pop TEMP  ;

ret
;****************************************************************
;Called with Z pointing to a string in rom.
;Seeks and displays forward, until we find a null.
;Shoves that text into the LCD_OUT_BUF
;****************************************************************
;
LCD_STR_NOPAD:

push TEMP ;
push TEMP2 ;
push LOOP ;
rcall LCD_STR2BUF;Move a string in rom to LCD_OUT_BUF
mov TEMP,Bitflags;
ori TEMP,$40;Flag the display dirty
pop LOOP ;
pop TEMP2 ;
pop TEMP ;
ret

LCD_STR_OUT:

push TEMP ;
push TEMP2 ;
push LOOP ;
rcall LCD_STR2BUF;Move a string in rom to LCD_OUT_BUF
rcall LCD_Fill;
mov TEMP,Bitflags;
ori TEMP,$40;Flag the display dirty
pop LOOP ;
pop TEMP2 ;
pop TEMP ;
ret
;
;****************************************************************
;This routine moves a string of arbitrary size into LCD_OUT_BUF,
;and padds it with spaces at the end as needed.
;****************************************************************
;
LCD_STR2BUF:
;Point at beginning of LCD_OUT_BUF
ldi YH,high(LCD_OUT_BUF);
ldi YL,low(LCD_OUT_BUF);
ldi TEMP2,0 ;
mov LOOP,TEMP2;

LCD_STR_Loop:
lpm  ;look up character
ld TEMP,Z+ ;Move pointer to in string
mov TEMP,R0 ;move to temp
and TEMP,TEMP;Is it null?
breq LCD_STR_Done;Yes, we're done, see if we need to fill

inc LOOP ;Keep track of how many we've stored
mov TEMP2,LOOP;
cpi TEMP2,VFD_Size+1;If full
breq VFD_STR_DONE;No need to fill, can't do anymore anyway

st Y+,TEMP ;Output to ram buffer, inc after
rjmp VFD_STR_Loop;
VFD_STR_Done:
ret


VFD_Fill:;Now the VFD_OUT_BUF has the output data, but may contain junk
inc LOOP ;
mov TEMP2,LOOP;
cpi TEMP2,VFD_SIZE+1;
breq VFD_Fill_Done;If full, then we're ready to spew
ldi TEMP,$20;(Space)

st Y+,TEMP ;
rjmp VFD_Fill;

VFD_Fill_Done:
ret
;
;**************************************************************
;This routine moves the data in LCD_OUT_BUF to the display.
;Called only from IDLE, this updates the display to be current
;with the contents of the lcd buffer.
;**************************************************************
;
VFD_SPEW:
push TEMP  ;Save them
push LOOP  ;
;
rcall VFD_Clear ;Clear and home.
ldi TEMP,VFD_Size ;
mov LOOP,TEMP ;
;
;Point at the beginning of the LCD buffer
ldi ZH,high(LCD_OUT_BUF);
ldi ZL,low(LCD_OUT_BUF);

VFD_SPEW_Loop:
ld TEMP,Z+  ;Get a char, and inc Z
rcall VFD_Talk ;Send it to the display
dec LOOP  ;One less to send
brne VFD_Spew_Loop ;If not done, do it again

mov TEMP,Bitflags ;
andi TEMP,$BF ;Flag the display clean
pop LOOP  ;Put them back.
pop TEMP  ;
ret   ;
;
;
;*************************************************************
;The main powerup init routine. Only called after powerup
;*************************************************************
;
VFD_INIT:
sbi VFD_Data,VFD_C;
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
rcall VFD_Reset;
rcall VFD_Clear;

rcall VFD_Flash_Off;

ldi TEMP,$30;Set flash rate
rcall VFD_Flash_Rate;

ldi TEMP,$FF;Max bright
rcall VFD_Bright;

ldi TEMP,$01;Select Western font
rcall VFD_Font;
ret
;
;*************************************************************
;Unlike most of these routines, this one takes two parms.
;Start of range in TEMP (1-32) end of range in TEMP2 (1-32)
;*************************************************************
;
VFD_Flash_Range:
sbi VFD_Data,VFD_C;
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
push TEMP ;Save the start byte
rcall VFD_Talk;Send the command
pop TEMP ;Get the start byte back
rcall VFD_Talk;Send it
mov TEMP,TEMP2;Get the end byte
rcall VFD_Talk;Send it
ret
;
;*************************************************************
;Turn on the flashing
;*************************************************************
;
VFD_Flash_On:
sbi VFD_Data,VFD_C;
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
ldi TEMP,$07;
rcall VFD_Talk;
ldi TEMP,$02;
rcall VFD_Talk;
ret
;
;*************************************************************
;Reset the display
;*************************************************************
;
VFD_Reset:
sbi VFD_Data,VFD_C;Assure the initial conditions
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
ldi TEMP,255;Wait a bit
rcall MS_Delay;
sbi VFD_Data,VFD_R;Assert reset
ldi TEMP,1 ;Wait a bit
rcall MS_Delay;
cbi VFD_Data,VFD_R;De-Assert reset
ldi TEMP,3 ;Wait a bit
rcall MS_Delay;
sbi VFD_Data,VFD_C;Assure exit conditions
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
ret  ;
;
;*************************************************************
;Turn off the flashing
;*************************************************************
;
VFD_Flash_Off:
sbi VFD_Data,VFD_C;
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
ldi TEMP,$07;
rcall VFD_Talk;
ldi TEMP,$01;
rcall VFD_Talk;
ret
;
;*************************************************************
;Set the flash rate, remember to set a zone for flashing
;*************************************************************
;
VFD_Flash_Rate:
sbi VFD_Data,VFD_C;
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
Push TEMP ;
ldi TEMP,$08;
rcall VFD_Talk;
pop TEMP ;
rcall VFD_Talk;
ret
;
;*************************************************************
;Select english or Japaneese font.
;*************************************************************
;
VFD_Font:
sbi VFD_Data,VFD_C;
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
push TEMP ;Save the brightness
ldi TEMP,$09;The command
rcall VFD_Talk;Send it
pop TEMP ;
rcall VFD_Talk;
ret  ;
;
;*************************************************************
;Sets the display brightness
;*************************************************************
;
VFD_Bright:
sbi VFD_Data,VFD_C;Assure the initial conditions
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
push TEMP ;Save the brightness
ldi TEMP,$04;The command
rcall VFD_Talk;Send it
pop TEMP ;Get the brightness back
rcall VFD_Talk;Send it
ret  ;
;
;*************************************************************
;Clear and home the display
;*************************************************************
;
VFD_CLEAR:

sbi VFD_Data,VFD_C;Assure the initial conditions
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
       ldi TEMP,$01;The command itself
       rcall VFD_Talk;
ret
;
;**************************************************************
;Sends character to LCD
;Required character must be in TEMP
;**************************************************************
;
VFD_Talk:
sbi VFD_Data,VFD_C;Assure the startup conditions
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;

push LOOP ;I need to borrow these
push TEMP ;

ldi TEMP,8 ;How many bits to send
mov LOOP,TEMP;Put it in LOOP
pop TEMP ;Put temp back

VFDT_Loop:
rol TEMP ;Don't know which direction yet.
brcc VFDT_Zero;If it's a zero, then send zero
sbi VFD_Data,VFD_D;Otherwise it's a one
rjmp VFDT_Clk;Clock it out

VFDT_Zero:
cbi VFD_Data,VFD_D;Simple, isn't it!

VFDT_Clk:
rcall VFD_Clock;Whang the clock line
dec LOOP ;One less to send
brne VFDT_Loop;If not done, then

pop LOOP
sbi VFD_Data,VFD_C;
cbi VFD_Data,VFD_R;
sbi VFD_Data,VFD_D;
ret  
;
;*************************************************************
;This measures out at slightly longer than the spec timing,
;but when I tried it at 2uS over the spec, it had intermittent
;problems with spurious chars on the display.
;*************************************************************
;
VFD_Clock:
rcall TwouS ;Required setup time
rcall TwouS ;
rcall TwouS ;
cbi VFD_Data,VFD_C;
rcall TwouS ;Required pulse width
rcall TwouS ;
rcall TwouS ;
rcall TwouS ;
rcall TwouS ;
rcall TwouS ;
sbi VFD_Data,VFD_C;    
rcall TwouS ;Required hold time and
rcall TwouS ;cycle time
rcall TwouS ;
rcall TwouS ;
rcall TwouS ;
rcall TwouS ;
rcall TwouS ;
rcall TwouS ;
rcall TwouS ;
ret  ;
;
;*************************************************************
;Brute force delay, 2uS at 8 MHz
;*************************************************************
;
TwouS:
nop;Two uS delay, plus a bit
nop;
nop;
nop;
nop;
nop;
nop;
nop;

nop;
nop;
nop;
nop;
nop;
nop;
nop;
nop;
ret;
;
;************************************************************




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