; ; 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 ; ; ;************************************************************