PDA

View Full Version : LCD serial terminal



Art
- 21st February 2020, 18:18
Hi Guys :)

This was pretty easy to share since it’s literally the schematic out of the manual, except 20 MHz clock, and has a serial input.

I was looking for a simple serial terminal display project, even though a character LCD would be hopeless for a lot of applications,
I wanted something simple, just to show some data is coming in.
This is specifically intended for monitoring APRS packet data (Amateur Radio), but could easily be adapted to something else.

It receives up to 60 bytes for a line, and is currently terminated with line feed 0x0A. This could be changed to return 0x0D.
The first 20 characters are printed to LCD, and subsequent lines push the top line down (with no line wrapping).

LCD graphics demo code is used for the intro, which is pixel based (not just rolling odometer effect), was done in 2013,
and adjusted to work with this. A lot of functionality from the original demo code is removed to suit this program.

Fancy Intro:
https://www.youtube.com/watch?v=WPqp4V7sJV4

Serial terminal - video made prior to intro being finished:
https://www.youtube.com/watch?v=91PADqEPwI8

8801

Art
- 21st February 2020, 18:20
and some code.
16F628A with MCLR disabled, 20 MHz clock, and watchdog time left on.




'
' Dumb LCD Terminal - Brek Martin 2020
'
DEFINE OSC 20 '20 MHz oscillator
DEFINE NO_CLRWDT 1 'tell compiler not to insert clrwdt instructions
DEFINE HSER_RCSTA 90h 'enable hardware serial receiver
DEFINE HSER_TXSTA 24h 'at least one bit in this register must be set
DEFINE HSER_SPBRG 129 'set SPBRG directly 9600 baud 20 MHz clock
DEFINE HSER_CLROERR 1 'auto clear serial buffer overflow errors
DEFINE LCD_DATAUS 49 'lcd module timing
DEFINE LCD_COMMANDUS 1850 '
'
DATA 142,145,145,143,129,130,140,128 '9 graphic digits roll stored horizontal
DATA 142,145,147,149,153,145,142,128 '0
DATA 132,140,132,132,132,132,142,128 '1
DATA 142,145,129,130,132,136,159,128 '2
DATA 159,130,132,130,129,145,142,128 '3
DATA 130,134,138,146,159,130,130,128 '4
DATA 159,144,158,129,129,145,142,128 '5
DATA 134,136,144,158,145,145,142,128 '6
DATA 159,129,130,132,136,136,136,128 '7
DATA 142,145,145,142,145,145,142,128 '8
DATA 142,145,145,143,129,130,140,128 '9
DATA 142,145,147,149,153,145,142,128 '0
DATA " DISPLAY TERMINAL VK4FAST 2020" 'program credit in spare eeprom space
'
'''''''''''''''''''''''''''''''''''''''''''''''''' '''
' DO NOT DECLARE ANY PROGRAM VARIABLES HERE! '
' PRIVATE PROPERTY - KEEP OUT OF MY CODESPACE! '
'''''''''''''''''''''''''''''''''''''''''''''''''' '''
serrec var byte[61] ' serial buffer also used as pixel buffer 24x17
rolls var byte [8] ' LCD RAM buffers
rolls0 var rolls[0] : rolls1 var rolls[1] '
rolls2 var rolls[2] : rolls3 var rolls[3] '
rolls4 var rolls[4] : rolls5 var rolls[5] '
rolls6 var rolls[6] : rolls7 var rolls[7] '
L0 var byte ' LCD RAM index and used as counter
CW var byte : CL var byte ' multi purpose counters
lind var byte : pind var byte ' framebuffer indexing variables
slug var byte : invert var byte ' set invert 0xFF to invert display
wtemp var word '
temp var wtemp.byte0 '
tempb var wtemp.byte1 '
px var byte : py var byte ' coordinates for library routines
'''''''''''''''''''''''''''''''''''''''''''''''''' ''' end graphics memory private property
' terminal program variables
serincnt var byte 'serial input rotate counter
inputbyte var byte 'serial input byte
larray var byte[40] 'display character array
lcdcnt var byte 'lcd character counter
sercnt var byte 'serial input character counter
' graphic demo variables
cx var byte ' shared with square drawing routine
cy var byte '
glc_x var byte '
glc_y var byte '
glc_s var word ' slope
radius var byte ' radius
cdx var byte ' circle demo horizontal coordinate
' rolling odometer effect variables
digit var byte 'buffer for printing sprites
doff var byte 'digit odometer effect variables
doffl var byte '
doffm var byte '
xdoffl var byte '
xdoffm var byte '
dcompl var byte '
dcompm var byte '
xdcompl var byte'
xdcompm var byte'
demd var byte 'actual digits on the screen
xdemd var byte 'another two digits on the screen
demdl var byte '
dval var byte 'value for printed variable
dgx var byte 'coordinates to print sprite
dgy var byte '
index var byte 'eeprom index for sprite data
dfc var byte 'digit frame counter
yfix var byte 'dynamic circle radius
qualify var bit '
demo var byte 'circles or digits flag
speed var byte 'odometer effect speed
acount var byte 'another counter
'
pause 16 'startup delay
CMCON = 7 'set ports to digital
trisb = %00000010 'set direction
trisa = %00000000 'set direction
porta = $00 'default states
portb = $00 'default states
gosub delayb '
gosub cleararrays '
lcdout $FE,$01 'clear display
inputbyte = $FF 'used as counter for the intro demo
invert = $FF 'set inverted graphics display mode
dfc = 0 'reset digit frame counter
cdx = 11 'set initial circle position
yfix = 7 'set initial circle radius
demd = 75 'rolling odometer display value
xdemd = 19 'first two year digits
dcompm = $FF '
dcompl = $FF '
xdcompm = $FF '
xdcompl = $FF '
doff = 0 'may break things if not initialised
speed = 1 'set speed
dgy = $F7 'initial digits position off screen
'
'******************* demo section *********************
demo = 0 'circles demo flag value
while inputbyte > 204
@ clrwdt
cx = cdx : cy = 8 'trick to use the entire display
radius = yfix
gosub drawcircle
cx = cdx + 24
gosub drawcircle
cdx = cdx - 1
if cdx = 242 then 'check bounds for moving circle demos
cdx = 10 '
endif '
gosub writecg 'write the frame buffer to LCD
inputbyte = inputbyte - 1
pause 1
if inputbyte = 204 then
invert = 0
endif
wend
'
'
'
' " VK4FAST 2020 " - template for rolling odometer intro
gosub cleararrays '
lcdout $FE,$01 'clear display
lcdout $FE,$80
gosub printfourspaces
acount = 116
while acount < 124
read acount,digit
lcdout digit
acount = acount + 1
wend
gosub printfourspaces
gosub printfourspaces
inputbyte = $FF '
yfix = 1 'used as a delay value here
demo = 2 'invisible until start digits appear
while inputbyte > 1 'draw rolling odometer effect digits
dval = demd /10 'example of drawing sprites
demdl = demd 'data is stored horizontal
demdl = demdl - (dval * 10)
if dval != dcompm then
dcompm = dval
doffm = 8
endif
doff = doffm
if doffm > 0 then
doffm = doffm - 1
else
demo = 1 'activate rolling odometer effect
endif
dgx = 12
gosub printdigit
if demdl != dcompl then
dcompl = demdl
doffl = 8
endif
doff = doffl
if doffl > 0 then
doffl = doffl - speed
else
inputbyte = inputbyte - 1
endif
dval = demdl
dgx = 18
gosub printdigit
if demd = 0 then ' change first two year digits
xdemd = 20
endif
dval = xdemd /10 'two more digits for year
demdl = xdemd 'data is stored horizontal
demdl = demdl - (dval * 10)
if dval != xdcompm then
xdcompm = dval
xdoffm = 8
endif
doff = xdoffm
if xdoffm > 0 then
xdoffm = xdoffm - 1
else
demo = 1 'activate rolling odometer effect
endif
dgx = 0
gosub printdigit
if demdl != xdcompl then
xdcompl = demdl
xdoffl = 8
endif
doff = xdoffl
if xdoffl > 0 then
xdoffl = xdoffl - 1
endif
dval = demdl
dgx = 6
gosub printdigit
gosub writecg 'write the frame buffer to LCD
@ clrwdt
if demd = 21 then 'quit this section at the year 2020
inputbyte = 0 '
endif '
dfc = dfc + 1 'increment digit roll counter
if dfc > 7 then '
dfc = 0 '
demd = demd + 1 'increment counter for display variable
if dgy != 0 then 'slide year down for start of intro
dgy = dgy + 1
endif
endif '
if demd > 99 then 'keep display variable in two digit range
demd = 0 '
endif '
wend
inputbyte = $FF 'graphics mode has ended
'************************************************* ******
'
'
'
'program intro section
lcdout $FE,$C0
acount = 96 ' "DISPLAY TERMINAL"
while acount < 116
read acount,digit
lcdout digit
acount = acount + 1
wend
gosub delays
lcdout $FE,$C2,"WAITING FOR DATA"
gosub cleararrays
'
'
'
HSERIN 1,nodatw,[inputbyte]'clear buffer
nodatw:
HSERIN 0,nodatx,[inputbyte]'clear buffer
nodatx:
HSERIN 0,nodaty,[inputbyte]'clear buffer
nodaty:
if inputbyte != $FF then ' ignore the first packet if incomplete
@ clrwdt
inputbyte = $FF
goto nodatw
endif
'
'
'
cycle: 'main program loop
@ clrwdt
HSERIN 0,cycle,[inputbyte]' hardware serial receive
if sercnt < 60 then 'rotate character into input array if received
for serincnt = 60 to 1 step - 1 '
serrec[serincnt] = serrec[serincnt-1]
next serincnt '
serrec[0] = inputbyte '
sercnt = sercnt + 1 'increment input character counter
endif '
'qualify line termination for data received from serial port
if inputbyte = $0a then ' check for enter character received - or $0d for line feed
gosub scrollprint
inputbyte = 0
endif
nodat: 'timeout label for no character received
goto cycle
'
'
'
scrollprint: 'scroll old line down and print to display top line
acount = 0
while acount < 20
larray[acount+20] = larray[acount]
acount = acount + 1
wend
lcdout $FE,$C0 'set second line position
lcdcnt = 0
while lcdcnt < 20 'print old data to second line
lcdout larray[lcdcnt+20]
lcdcnt = lcdcnt + 1
wend
lcdout $FE,$80 'set first line position
lcdcnt = 0
if sercnt > 0 then
sercnt = sercnt - 1
endif
while lcdcnt < 20 'print new data to first line
lcdout serrec[sercnt-lcdcnt]
larray[lcdcnt] = serrec[sercnt-lcdcnt]
lcdcnt = lcdcnt + 1
wend
@ clrwdt
gosub checkfortime 'extract data
sercnt = 0
return
'
'
'
'lcd character display graphics originally by Brek Martin 2013
drawcircle: ' draw circle to screen
cx = cx - (7-yfix) 'moved here for optimisation
glc_x = 0
glc_y = radius
glc_s = 2-2*radius
while (glc_x <= glc_y)
px = glc_x+cx : py=glc_y+cy
gosub setpixel
px = cx-glc_x : py=glc_y+cy
gosub setpixel
px = cx+glc_x : py=cy-glc_y
gosub setpixel
px = cx-glc_x : py=cy-glc_y
gosub setpixel
px = glc_y+cx : py=glc_x+cy
gosub setpixel
px = cx-glc_y : py=cy+glc_x
gosub setpixel
px = cx+glc_y : py=cy-glc_x
gosub setpixel
px = cx-glc_y : py=cy-glc_x
gosub setpixel
if (glc_s.15 =1) then
glc_s = glc_s + (4*glc_x + 6)
else
glc_s = glc_s + (4*(glc_x-glc_y) + 10)
glc_y = glc_y - 1
endif
glc_x = glc_x + 1
wend
return
'
setpixel: ' size and performance win for V2
lind = py * 3 ' byte index for line
pind = 0
slug = px
IF px > 7 THEN
pind = 1
slug = px - 8
ENDIF
IF px > 15 THEN
pind = pind + 1
slug = px - 16
ENDIF
IF lind+pind < 51 THEN ' range check
temp = serrec[lind+pind]
FOR CW = 0 TO 7
IF slug = CW THEN
temp.bit7 = 1
ENDIF
wtemp = wtemp << 1
NEXT CW
serrec[lind+pind] = tempb
ENDIF
return
'
writecg: ' size, ram and performance win for V2
L0 = $40 ' reset lines
FOR CL = 0 TO 7 ' write first half of display
FOR CW = 0 TO 3
rolls[CW] = (serrec[0] ^ invert) >> 3
gosub Rotate
NEXT CW
gosub lcdgfxchunk
NEXT CL
FOR CW = 0 TO 23 ' skip invisible line
gosub RotateDisplay
NEXT CW
'
if demo = 0 then 'not for rolling odometer
L0 = $60 ' reset lines
FOR CL = 0 TO 7 ' write second half of display
FOR CW = 0 TO 3
rolls[CW] = (serrec[0] ^ invert) >> 3
gosub Rotate
NEXT CW
gosub lcdgfxchunk
NEXT CL
' draw to LCD
LCDOUT $FE,$80 'draw first line of display
FOR CL = 0 TO 4
LCDOUT 0,1,2,3 '
NEXT CL
LCDOUT $FE,$C0 'draw second line of display
FOR CL = 0 TO 4
LCDOUT 4,5,6,7 '
NEXT CL
endif
if demo = 1 then ' digits on one display area only
LCDOUT $FE,($80+12) 'draw first line of display
LCDOUT 0,1,2,3
endif
return
'
Rotate: ' the only assembler routine
gosub rota ' needed for speed
rota: ' by neglecting to carry the
gosub RotateDisplay ' status bit, it also clears
gosub RotateDisplay ' the buffer for the next frame
RotateDisplay: ' bitwise rotate array right
@ rlf _serrec+50 ,F ; ditching the first bit
@ rlf _serrec+49 ,F ;
@ rlf _serrec+48 ,F ;
@ rlf _serrec+47 ,F ;
@ rlf _serrec+46 ,F ;
@ rlf _serrec+45 ,F ;
@ rlf _serrec+44 ,F ;
@ rlf _serrec+43 ,F ;
@ rlf _serrec+42 ,F ;
@ rlf _serrec+41 ,F ;
@ rlf _serrec+40 ,F ;
@ rlf _serrec+39 ,F ;
@ rlf _serrec+38 ,F ;
@ rlf _serrec+37 ,F ;
@ rlf _serrec+36 ,F ;
@ rlf _serrec+35 ,F ;
@ rlf _serrec+34 ,F ;
@ rlf _serrec+33 ,F ;
@ rlf _serrec+32 ,F ;
@ rlf _serrec+31 ,F ;
@ rlf _serrec+30 ,F ;
@ rlf _serrec+29 ,F ;
@ rlf _serrec+28 ,F ;
@ rlf _serrec+27 ,F ;
@ rlf _serrec+26 ,F ;
@ rlf _serrec+25 ,F ;
@ rlf _serrec+24 ,F ;
@ rlf _serrec+23 ,F ;
@ rlf _serrec+22 ,F ;
@ rlf _serrec+21 ,F ;
@ rlf _serrec+20 ,F ;
@ rlf _serrec+19 ,F ;
@ rlf _serrec+18 ,F ;
@ rlf _serrec+17 ,F ;
@ rlf _serrec+16 ,F ;
@ rlf _serrec+15 ,F ;
@ rlf _serrec+14 ,F ;
@ rlf _serrec+13 ,F ;
@ rlf _serrec+12 ,F ;
@ rlf _serrec+11 ,F ;
@ rlf _serrec+10 ,F ;
@ rlf _serrec+9 ,F ;
@ rlf _serrec+8 ,F ;
@ rlf _serrec+7 ,F ;
@ rlf _serrec+6 ,F ;
@ rlf _serrec+5 ,F ;
@ rlf _serrec+4 ,F ;
@ rlf _serrec+3 ,F ;
@ rlf _serrec+2 ,F ;
@ rlf _serrec+1 ,F ;
@ rlf _serrec+0 ,F ;
@ bcf _serrec+50 ,0 ;clear MSB
return
'
'
'
printdigit: 'print a single graphic digit to screen
index = dval * 8' set the index position for digit
FOR L0 = 0 TO 6
read (index-doff)+8,digit 'rolling odometer data
FOR CL = 0 TO 4
IF digit.bit4 = 1 THEN
px = dgx+CL
py = dgy+L0
gosub setpixel
ENDIF
digit = digit << 1
NEXT CL
index = index + 1
NEXT L0
return
'
'
'
cleararrays:
sercnt = 0 ' reset to use as a counter
while sercnt < 61
serrec[sercnt] = 0 'clear serial receive buffer
if sercnt < 40 then 'clear both display buffer lines
larray[sercnt] = $20
endif
sercnt = sercnt + 1
wend
sercnt = 0
return
'
'
'
printfourspaces:
lcdout " "
return
'
'
'
lcdgfxchunk: ' program memory optimisation
LCDOUT $FE,L0+$00,rolls[0]
LCDOUT $FE,L0+$08,rolls[1]
LCDOUT $FE,L0+$10,rolls[2]
LCDOUT $FE,L0+$18,rolls[3]
L0 = L0 + 1
return
'
'
'
delays:
@ clrwdt
pause 660
delayb:
@ clrwdt
pause 660
@ clrwdt
pause 660
@ clrwdt
return
'
'
'
checkfortime:
qualify = 0 : digit = 0
acount = sercnt 'used as a counter here
while acount > 8
acount = acount - 1
if serrec[acount] = ":" && serrec[acount-1] = "@" then
digit = acount-4 : acount = 0
endif
wend
'
if digit > 0 then
if serrec[digit-3] < $3A then 'extra sanity check
if serrec[digit-3] > $2F then '
if serrec[digit] < $33 then
lcdout $FE,$C0 'set second line position
gosub printfourspaces
lcdout "TIME: " 'start of time display line
'convert to local time here
cx = serrec[digit] - $30 'get real values
cy = serrec[digit-1] - $30 '
cx = cx * 10 'move to msd
cx = cx + cy 'cx should be true value now
cx = cx + 10 'convert to local eastern time
if cx > 23 then 'fix possible overflow
cx = cx - 24 '
endif '
cy = cx/10 'split digits and convert back to ascii
serrec[digit] = cy + $30 'most significant hour digit restored
cx = cx - (cy*10) '
serrec[digit-1] = cx + $30 'least significant hour digit restored
'
lcdout serrec[digit],serrec[digit-1],":",serrec[digit-2],serrec[digit-3]," " ' use z for zulu if not local time
gosub printfourspaces
endif
endif
endif
endif
return
'
'

Ioannis
- 22nd February 2020, 20:21
Nice work Brek! Especially with the effects!

Ioannis

boroko
- 5th May 2020, 21:37
Art (Brek?),
This thread caught my attention enough to look at your YT page. Lots of impressive stuff there! I definitely have to spend more time looking at your ideas. Thanks for putting it out there.

The videos in this thread don't seem to come up. Can I get a pointer to where they are so I can see them? I suspect I have a use for a simple serial display, and I'd like to see how you did it.

Thanks
bo

boroko
- 7th May 2020, 15:00
After thinking about it, that was just silly. I'll just throw it together and see it for myself. I already know I have uses for it.