PDA

View Full Version : Binary to BCD conversion



cunninghamjohn
- 7th August 2017, 20:11
Hi all,

I was recently in need of a program to convert binary to BCD to display the result of a binary counter on a MAX7219 7 segment LED display module. Couldn't find anything suitable on the forum so had a go at it myself.


include "modedefs.bas"
ADCON0=7 'MESSES UP SERIAL COMMS IF OMITTED
ADCON1=7
CNTR VAR BYTE'COUNTS THROUGH 32 SHIFT OPERATIONS

DBYTE1 VAR BYTE'LOW 8 BITS OF SCRATCH SPACE BITS 7-0
DBYTE2 VAR BYTE'NEXT BYTE BITS 15-8
DBYTE3 VAR BYTE'NEXT BYTE BITS 23-16
DBYTE4 VAR BYTE'NEXT BYTE BITS 31-24
DBYTE5 var BYTE'HIGH 8 BITS OF SCRATCH SPACE BITS 39-32

BBIN4 VAR BYTE'HIGH BYTE OF INPUT VARIABLE
BBIN3 VAR BYTE
BBIN2 VAR BYTE
BBIN1 VAR BYTE'LOW BYTE OF INPUT VARIABLE
'MAX7219 DISPLAY SETUP
DataPin var PORTA.0
ClockPin var PORTA.1
Load var PORTA.2

pause 500
high load
pause 1000

LOW Load
SHIFTOUT datapin,clockpin,MSBFIRST,[$0C, $01] 'shutdown mode off
high load:low load
SHIFTOUT datapin,clockpin,MSBFIRST,[$0A, $0F] 'set intensity to maximum
high load:low load
SHIFTOUT datapin,clockpin,MSBFIRST,[$09, $FF] 'BCD decode mode on
high load:low load
SHIFTOUT datapin,clockpin,MSBFIRST,[$0B, $07] 'scan limit 8 digits
high load:low load
SHIFTOUT datapin,clockpin,MSBFIRST,[$0f, $00] 'display test off
high Load


MAIN:
PAUSE 2000
DBYTE1=0:DBYTE2=0:DBYTE3=0:DBYTE4=0:DBYTE5=0

CNTR=32'NUMBER OF BITS OF BINARY INPUT VARIABLE
BBIN4=$00'MSB
BBIN3=$FF
BBIN2=$FF
BBIN1=$FF'LSB

CYCLE:
GOSUB CHECK

DBYTE5=DBYTE5<<1
DBYTE5=DBYTE5+DBYTE4.7
DBYTE4=DBYTE4<<1
DBYTE4=DBYTE4+DBYTE3.7
DBYTE3=DBYTE3<<1
DBYTE3=DBYTE3+DBYTE2.7
DBYTE2=DBYTE2<<1
DBYTE2=DBYTE2+DBYTE1.7
DBYTE1=DBYTE1<<1
DBYTE1=DBYTE1+BBIN4.7
BBIN4=BBIN4<<1
BBIN4=BBIN4+BBIN3.7
BBIN3=BBIN3<<1
BBIN3=BBIN3+BBIN2.7
BBIN2=BBIN2<<1
BBIN2=BBIN2+BBIN1.7
BBIN1=BBIN1<<1

CNTR=CNTR-1
IF CNTR>0 THEN GOTO CYCLE

SEROUT2 PORTC.0,84,[BIN4 DBYTE5>>4,":",BIN4 DBYTE5&$0F,":",BIN4 DBYTE4>>4,":",BIN4 DBYTE4&$0F,":",BIN4 DBYTE3>>4,":",BIN4 DBYTE3&$0F,":",BIN4 DBYTE2>>4,":",BIN4 DBYTE2&$0F,":",BIN4 DBYTE1>>4,":",BIN4 DBYTE1&$0F,10,13]

'DISPLAY RESULT ON MAX7219 DISPLAY MODULE


low load
shiftout datapin,clockpin,MSBFIRST,[$08, DBYTE4>>4] ' MSB DIGIT 7
high load:low load
shiftout datapin,clockpin,MSBFIRST,[$07, DBYTE4&$0F] ' DIGIT 6
high load:low load
shiftout datapin,clockpin,MSBFIRST,[$06, DBYTE3>>4] ' DIGIT 5
high load:low load
shiftout datapin,clockpin,MSBFIRST,[$05, DBYTE3&$0F] ' DIGIT 4
high load:low load
shiftout datapin,clockpin,MSBFIRST,[$04, DBYTE2>>4] ' DIGIT 3
high load:low load
shiftout datapin,clockpin,MSBFIRST,[$03, DBYTE2&$0F] ' DIGIT 2
high load:low load
shiftout datapin,clockpin,MSBFIRST,[$02, DBYTE1>>4] ' DIGIT 1
high load:low load
shiftout datapin,clockpin,MSBFIRST,[$01, DBYTE1&$0F] ' DIGIT 0
high load
pause 1000
goto main
END

CHECK:
IF (DBYTE1&$0F)>=5 THEN DBYTE1=DBYTE1+$03'+3 TO LOWER NIBBLE IF >= TO 5
if (DBYTE1>>4)>=5 then DBYTE1=DBYTE1+$30'+3 TO UPPER NIBBLE IF >= TO 5
IF (DBYTE2&$0F)>=5 THEN DBYTE2=DBYTE2+$03'+3 TO LOWER NIBBLE IF >= TO 5
if (DBYTE2>>4)>=5 then DBYTE2=DBYTE2+$30'+3 TO UPPER NIBBLE IF >= TO 5
IF (DBYTE3&$0F)>=5 THEN DBYTE3=DBYTE3+$03'+3 TO LOWER NIBBLE IF >= TO 5
if (DBYTE3>>4)>=5 then DBYTE3=DBYTE3+$30'+3 TO UPPER NIBBLE IF >= TO 5
IF (DBYTE4&$0F)>=5 THEN DBYTE4=DBYTE4+$03'+3 TO LOWER NIBBLE IF >= TO 5
if (DBYTE4>>4)>=5 then DBYTE4=DBYTE4+$30'+3 TO UPPER NIBBLE IF >= TO 5
IF (DBYTE5&$0F)>=5 THEN DBYTE5=DBYTE5+$03'+3 TO LOWER NIBBLE IF >= TO 5
if (DBYTE5>>4)>=5 then DBYTE5=DBYTE5+$30'+3 TO UPPER NIBBLE IF >= TO 5
RETURN

Dave
- 7th August 2017, 22:16
I do believe there is an easier way. One question I have is, How is the original data retrieved or stored ie. LONG or WORD array elements?

cunninghamjohn
- 8th August 2017, 15:14
Dave,

I would be interested to see how you would implement the binary/BCD conversion. The code I posted is my take on the shift and add 3/double dabble routine.

The original data will come from 4 74HC165 shift registers, I will shift in 4 bytes of data and store these as BBIN1...4. The program is part of a frequency counter I am working on. I am 4 using 74HC393 binary counters followed by the shift registers to give a 32 bit count. The PIC will only do the arithmetic and control the MAX7219 display driver. I will be using an external timebase.

I'm sure this isn't the simplest or most elegant way to implement a frequency counter, but I will learn more doing it this way. My first thought is that I may be able to reduce the number of 393/165 chips and count the number of overflows during the timing period to reduce chip count.

I could use 74HC390 to avoid having to do the binary/BCD conversion but this would need more chips due to the redundancy of bits in BCD

Regards,
John

towlerg
- 8th August 2017, 15:37
How about


shift right 4 times (thus losing bottom 4 bits and shifting 0 into top 4 bits)
if 0-9 then add x30
else add x37

mask off the 4 msb's
if 0-9 then add x30
else add x37

so for example 3A
shifted becomes 3
3 + x30 = x33 ASCII for "3"

masked becomes x0A
x0A + x37= x47 ASCII for "F"

Dave
- 8th August 2017, 22:17
Well John, Here is the way I would do it. Assuming all 4, 8 bit counters are connected in series to get 32 bits:

BIGNUM VAR LONG
VALUE VAR BYTE
JUNK VAR BYTE
CHARPOS VAR BYTE
DIGVALUE VAR BYTE

MAIN:
'THIS ASSUMES ALL 4, 8 BIT COUNTERS ARE IN SERIES ON SAME DATA AND CLOCK LINE
'ADJUST DATA AND CLOCK LINE TO YOUR HARDWARE CONFIGURATION

SHIFTIN REGDATAPIN,REGCLKPIN,MSBFIRST,[BIGNUM.BYTE3,BIGNUM.BYTE2,BIGNUM.BYTE1,BIGNUM.BYTE 0]

'32 BIT VALUE IS CLOCKED IN TO LONG VARIABLE

JUNK = 7 'SET DIGIT POINTER FOR 8 DIGITS TOTAL
WHILE JUNK < 255
VALUE = BIGNUM DIG(JUNK) 'VALUE OF DIGIT
CHARPOS = JUNK + 1 'CALCULATED CHARACTER POSITION
low load
shiftout datapin,clockpin,MSBFIRST,[CHARPOS, VALUE] 'DATA SHIFTED TO DISPLAY
high load
JUNK = JUNK - 1 'NEXT DIGIT FROM MSBYTE TO LSBYTE
WEND
pause 1000
goto main

I hope this bit of code will help you.

cunninghamjohn
- 10th August 2017, 22:36
Dave,

Thank you for sharing this, your method does seem a lot simpler. I don't have LONGS available but imagine this could be worked around. I would be interested in having a go with something based on your method and see if there is much difference in the size of the compiled program.

George,

Thanks for your reply. I am a little confused though, does this convert to ASCII rather than BCD?

John

towlerg
- 10th August 2017, 23:59
Thanks for your reply. I am a little confused though, does this convert to ASCII rather than BCD?

I think I misunderstood the question, I thought BCD was a way of expressing a binary value as ASCII characters. So for example a byte would become two ASCII chars, a word 4 ASCII chars etc.

Dave
- 11th August 2017, 11:37
John, If you limit the accumulated value to say 5 digits as in a word (65535 max) then you can use it without LONG's. I use this method to display MPH with 3 decimal places on a terminal thru a serial port by adding:

TX_BYTES(TX_OUTPUT) = $30 + VALUE DIG JUNK 'Turn decimal value into ASCII

cunninghamjohn
- 14th August 2017, 21:53
Dave,

Interesting approach, seems like I got too bogged down in shifting and adding multiple times rather than using the more obvious arithmetic capabilities of PICBASIC.

George,

Thanks for the hint on conversion to ASCII, I'm sure it will come in useful at some stage.
For info, BCD represents the decimal digits 0-9 using a nibble of data. With BCD only the states 0-9 are valid. 0xA to 0xF are forbidden. Its used for seven segment displays, real time clock modules, etc.