If you have used LCDs, whether serial or parallel, then you will be used to the issue of getting numbers correctly formatted. You use bintoascii and then you have to strip of the leading zeroes before output. This all looks OK and then you get a number a decade smaller and the display leaves the last character of the larger number there so you then have to put in some code to overwrite it with a space. Then you decide you want a decimal point (e.g. for a DS18B20) so that needs coding. Finally, you need negative numbers so that needs special code as well.
I've finally got bored with doing this repeatedly so have written a general purpose formatting routine.
The single routine takes three inputs: the data to be output, the width and the format. The width parameter defines how big a field is to be created, this will be completely filled with the number or spaces (or * characters if it is not big enough for the number). The format parameter allows you to define by setting the appropriate bits:
byte or word data
signed or unsigned
left or right justified
scale by 10 (i.e. insert decimal point before last digit)
scale by 100(i.e. insert decimal point before second last digit)
It also allows for a termination string to be optionally specified e.g cr,lf
The number output uses a separately callable routine to output a text string left or right justified with the field width specified by width
The program uses a call to a single character output routine "outchar" which can be modified to drive the specific device required. In the example I use a call to sertxd. I've used it on serial LCDs and SPI Nokia 5110 displays
The example code shows most of the features and tests the special case handling.
I've included at the bottom compatible routines for multiplying and dividing signed words. These make it very easy to deal with, for example, temperature measurements that can go negative. To use a DS18B20 for the full range of temperatures +ve or -ve to 1 decimal place is done with just four calls as follows:
readtemp12
signed multiply by 10
signed divide by 16
signed word output
Hope this is useful
I've finally got bored with doing this repeatedly so have written a general purpose formatting routine.
The single routine takes three inputs: the data to be output, the width and the format. The width parameter defines how big a field is to be created, this will be completely filled with the number or spaces (or * characters if it is not big enough for the number). The format parameter allows you to define by setting the appropriate bits:
byte or word data
signed or unsigned
left or right justified
scale by 10 (i.e. insert decimal point before last digit)
scale by 100(i.e. insert decimal point before second last digit)
It also allows for a termination string to be optionally specified e.g cr,lf
The number output uses a separately callable routine to output a text string left or right justified with the field width specified by width
The program uses a call to a single character output routine "outchar" which can be modified to drive the specific device required. In the example I use a call to sertxd. I've used it on serial LCDs and SPI Nokia 5110 displays
The example code shows most of the features and tests the special case handling.
I've included at the bottom compatible routines for multiplying and dividing signed words. These make it very easy to deal with, for example, temperature measurements that can go negative. To use a DS18B20 for the full range of temperatures +ve or -ve to 1 decimal place is done with just four calls as follows:
readtemp12
signed multiply by 10
signed divide by 16
signed word output
Hope this is useful
Code:
#Picaxe 20X2
#Terminal 9600
' Variable Definitions
symbol sign =bit0
symbol signed = bit1
symbol rightjustify = bit2
symbol scale100= bit3
symbol scale10 = bit4
symbol byteout = bit5
symbol overflow=bit6
symbol termination=bit7
symbol char = b1
symbol width = b2
symbol format=b3
symbol i=b4
symbol OutDat = w3 '-32768 to 32767
symbol ODl = b6 '-128 to 127
symbol ODh = b7
symbol word2 = w4
symbol W2l = b8
symbol W2h = b9
symbol wordsave=w5
'
symbol n_array=0x30
symbol s_array=0x40
symbol t_array=0x50
symbol f_scale100=0x02
symbol f_scale10=0x01
symbol f_err= f_scale100 | f_scale10
symbol f_signed=0x04
symbol f_rightjustify=0x08
symbol f_byteout=0x10
symbol f_default=0x00
symbol f_termination=0x80
symbol positive=0
symbol negative=1
symbol yes=1
symbol no=0
'
Start:
SetFreq M8
OutDat=-110
poke s_array,"F","o","r","m","a","t"," ","t","e","s","t",0
poke t_array,cr,lf,0
pause 2000
'
Main:
bptr=s_array
' zero terminated string right justified in a field of length 14
width=14:format=f_rightjustify | f_termination: gosub outstring
' unsigned integer left justified in 11 char field
width=11: format=f_default: gosub FormNum
' osigned integer, right justified field of only 3 characters, note data will be replaced with * characters if too big
width=3: format=f_signed | f_rightjustify | f_termination: gosub FormNum
' unsigned integer left justified in 7 character field with 1 decimal place
width=7: format=f_scale10 : gosub FormNum
' signed integer right justified in a 7 character field with one decimal place
width=7: format=f_signed | f_scale10 | f_rightjustify | f_termination: gosub FormNum
' unsigned integer left justified in a 7 character field with 2 decimal places
width=7: format=f_scale100 gosub FormNum
' signed integer right justified in a 7 character field with two decimal places
width=7: format=f_scale100 | f_rightjustify | f_signed | f_termination: gosub FormNum
'output with conflicting format
width=7: format=f_scale10 | f_scale100 gosub FormNum
'signed byte right justified in a 7 character field
width=7: format=f_byteout |F_rightjustify | f_termination | F_signed: gosub FormNum
OutDat=OutDat+3
pause 4000
goto main
end
outchar:
sertxd(char) 'this can be replaced with a write to a LCD or other output device
return
'
' creates zero terminated string starting at n_array from OutDat based on the format and width inputs
'
FormNum:
bptr = n_array
b0=0 ' clear the status bits
wordsave=OutDat
sign=positive
i= format & f_signed: if i<>0 then:signed=yes:endif
i= format & f_byteout: if i<>0 then:byteout=yes:endif
if byteout=yes then 'convert a byte input to a word copying sign bit if required
OutDat=OutDat & 0xFF
if signed = yes and OutDat>=$80 then
OutDat =OutDat | 0xFF00
endif
endif
If OutDat >= $8000 and signed=yes Then
sign= negative
OutDat = -OutDat
endif
i=format & f_err
if i=f_err then 'check for conflicting formats
@bptrinc="F":@bptrinc="_":@bptrinc="E":@bptrinc="r":@bptrinc="r":@bptr=0
else 'process if formatting commands make sense
if i=f_scale100 then :scale100=yes:endif
if i=f_scale10 then :scale10=yes:endif
bintoascii OutDat,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptr 'do the basic conversion
W2l=@bptr
if scale10 = yes then : @bptrinc="." :endif 'insert decimal point if scale10
if scale100 = yes then 'insert decimal point if scale100
W2l=@bptrdec
W2h=@bptr
@bptrinc="."
@bptrinc=W2h
@bptr=W2l
endif
@bptrinc=W2l
@bptr=0 'denotes end of string with a null
W2l=0
bptr = n_array
if @bptrinc="0" then ' at least one leading zero
W2l=1
if @bptrinc="0" then ' at least two leading zeros
W2l=2
if @bptrinc="0" then ' at least three leading zeros
W2l=3
endif
endif
endif
ODl=n_array+W2l 'move array left by the number of leading zeroes
ODh=n_array+6
W2h=n_array
for i= ODl to ODh
bptr=i
W2l=@bptr
bptr=W2h
@bptr=W2l
W2h=W2h+1
next i
if signed = yes then 'move array right 1 space to allow for sign character if required
ODl=n_array
ODh=n_array+6
for i= ODh to ODl step -1
bptr=i
W2l=@bptrinc
@bptr=W2l
next i
bptr=n_array 'insert the sign bit at the beginning of the string
if sign=negative then
@bptr="-"
else
@bptr=" "
endif
endif
OutDat=wordsave
bptr = n_array
if scale10=no and @bptr="0" then 'special case of unsigned positive numbers less than 10
inc bptr
W2l=@bptr
@bptrdec=0
@bptr=W2l
endif
if signed=yes and scale10=no then 'special case of signed positive numbers between -10 and 10
bptr=n_array+1
if @bptr="0" then
W2l=bptr+1
peek W2l,W2h
@bptrinc=W2h
@bptr=0
endif
endif
if scale100=yes and OutDat<100 and signed=no then 'special case of unsigned 2-decimal positive numbers less than 100
bptr=n_array+1
W2l=@bptrinc
W2h=@bptr
bptr=n_array
@bptrinc="0":@bptrinc=".":@bptrinc=W2l:@bptrinc=W2h:@bptr=0
endif
if scale100=yes and signed=yes then 'special case of signed 2-decimal positive numbers between -100 and 100
if OutDat>65436 or OutDat<100 then
bptr=n_array+2
W2l=@bptrinc
W2h=@bptr
bptr=n_array
if sign=negative then
@bptrinc="-"
else
@bptrinc=" "
endif
@bptrinc="0":@bptrinc=".":@bptrinc=W2l:@bptrinc=W2h:@bptr=0
endif
endif
endif
bptr = n_array 'reset bptr ready for output
'outputs a zero terminated string pointed to by bptr
outstring:
i= format & f_rightjustify: if i<>0 then:rightjustify=yes:endif
i= format & f_termination: if i<>0 then:termination=yes:endif
W2l=0
do while @bptrinc<>0 'count the valid characters
inc W2l
loop
if W2l>width then: overflow=yes: W2l=width: endif 'too big so replace with * characters
bptr=bptr-W2l-1
if rightjustify=yes and W2l<>width then 'if right justified pad out beginning of the string with spaces
char=" "
W2l=W2l+1
for W2h=width to W2l step -1
gosub outchar
next W2h
W2l=W2l-1
endif
for W2h=1 to W2l
if overflow=no then: char=@bptrinc :else :char="*":endif
gosub outchar
next W2h
if rightjustify=no and W2l<>width then 'if left justified pad out end of the string with spaces
char=" "
W2l=W2l+1
for W2h=width to W2l step -1
gosub outchar
next W2h
endif
if termination=yes then
bptr=t_array
do
char=@bptrinc
gosub outchar
loop while @bptr<>0
endif
return
'Function multsignedword(OutDat As Word, word2 As Word) As Word
'multiply OutDat by word2 and return the result in OutDat
'
multsignedword:
sign = positive
If OutDat >= $8000 Then
sign = Not sign
OutDat = -OutDat
Endif
If word2 >= $8000 Then
sign = Not sign
word2 = -word2
Endif
OutDat = OutDat * word2
If sign = negative Then
OutDat = -OutDat
Endif
return
'
'Function divsignedword(OutDat As Word, word2 As Word) As Word
'divide OutDat by word2 and return the result in OutDat
'
divsignedword:
sign = positive
If OutDat >= $8000 Then
sign = Not sign
OutDat = -OutDat
Endif
If word2 >= $8000 Then
sign = Not sign
word2 = -word2
Endif
OutDat = OutDat / word2
If sign = negative Then
OutDat = -OutDat
Endif
return