16-bit WORD to BCD for Serial Transmit

techElder

Well-known member
Having used the process from this thread to convert 24-bit numbers into BCD numbers suitable for serial transmission, something occurred to me in my current project.

I am accumulating up to a 32-bit number. %11111111 11111111 11111111 11111111

However, in reality, I am never going to use the highest 8-bits, and the lowest 8-bits are going to be discarded as part of the rounding. %00000000 11111111 11111111 00000000

So, what I have left is 16-bits shifted up 2^8. %11111111 11111111 00000000

Is there a short way to get these remaining bits correctly converted to BCD?

I can use my version of the conversion that I posted here, but it just seems like there should be a simpler way.

Thanks for thinking about this!
 

AllyCat

Senior Member
Hi,

I haven't looked at your links in detail and assume from the title that you want one digit per byte, so I think it's fairly easy to code in PICaxe Basic (but not tested), However, there might be more compact methods:

If your double-word is w1:w2 (low:high) then you can calculate the middle word by w2 = w2 * 256 : w1 = w1 ** 256 + w2 (or you can simply shuffle the bytes around within the words). Then convert to BCD by dividing w1 consecutively by 10000, 1000 // 10 , 100 // 10 , 10 // 10 and finally // 10 .

Cheers, Alan.
 

techElder

Well-known member
If the decimal value of the 24-bit binary number is 15,432,101, then I would want to serially transmit a 1,5,4,3,2,1,0,1.
 

AllyCat

Senior Member
Hi,

From an email notification this morning, it looks as if technical misunderstood the issue, and then also deleted my last (brief) post as well?

Cheers, Alan.
 

techElder

Well-known member
For me the connection between the brain and the keyboard sometimes garbles the communication! :)

My subroutine (from the link above) is based on work that you (AllyCat) did and works fine with 24-bit conversions to BCD. Problem solved.

However, stated in another way, my question in this topic was "Can a 24-bit number be somehow shifted right 8-bits (yes, losing those 8-bits), convert the resulting 16-bits to BCD and then (somehow) turn that BCD result back into the value that is equivalent to the original 24-bit number?"

It sounds stupid to me now, but perhaps the conversion process would somehow be simpler than the above referenced procedure?

I don't know. That's the question.
 

inglewoodpete

Senior Member
To shift a 24-bit value 8-bits to the right, just copy the middle byte to the lower byte, then the upper byte to the middle spot. Then, if necessary, zero the high-byte.

The word can then be converted to 7 BCD nibbles. I haven't got my head around doing the remultiply of BCD 'digits' by 256, though! I'm sure there is a maths guru who can show how it can be done.
 

BillBWann

Member
If the decimal value of the 24-bit binary number is 15,432,101, then I would want to serially transmit a 1,5,4,3,2,1,0,1.
I would think that you can’t get much more compact code than the program I submitted in #10 of the post you referred to in your first post in this thread. The working part of the code is only 12 lines long and uses just 45 bytes of code space.

The 24 bit binary number equal to 15432101 is expressed in hex as EB79A5. I’ve repeated my previous code here with your binary value replacing my original number. Try it in the simulator and it should print out 015432101. If you don’t want the leading 0, then decrement bptr before you print out the number.

Code:
#picaxe 08m2
#no_data

symbol LSW=w12
symbol MSW=w13

bptr=27		'Load hex double word $12345678 into MSW & LSW
@bptrdec=$00
@bptrdec=$EB
@bptrdec=$79
@bptr=$A5

bptr=28	'push remainders onto stack starting from 28

do
	b3=MSW//10
	b2=b25
	MSW=MSW/10
	
	b25=w1/10
	b3=w1//10
	b2=b24
	b24=w1/10
	
	@bptrinc=w1//10
loop until LSW=0 and MSW=0

do				'print out the decimal value
	sertxd(#@bptrdec)
loop until bptr<28
stop
 

hippy

Technical Support
Staff member
However, stated in another way, my question in this topic was "Can a 24-bit number be somehow shifted right 8-bits (yes, losing those 8-bits), convert the resulting 16-bits to BCD and then (somehow) turn that BCD result back into the value that is equivalent to the original 24-bit number?"
I would doubt it. The shifting right by 8 is easy enough but once you have a set of BCD or decimal digits you have to multiply those by 256. That would seem to be simply exchanging one complexity for another.
 

techElder

Well-known member
After a little conversion work on BillBWann's binary too BCD converter routine, I've come up with something that I can use in my programs. Perhaps someone else can find it useful.

I reworked it so it operates within the confines of the variables that get saved with PUSHRAM; b0 - b15.

I works very well in simulation on everything from 32-bits significant on down.

I added some specific formatting to the digits stream for my program; leading zero blanking and a decimal point.

I'm purty happy with it for now.

Code:
[color=Green]' Program to demonstrate 32-bit binary to BCD convertion
'     PUSHRAM / POPRAM not supported on all chips[/color]

[color=Navy]#picaxe [/color][color=Black]40X2[/color]
[color=Blue]pushram [/color][color=DarkCyan]clear[/color]

[color=Purple]w0 [/color][color=DarkCyan]= [/color][color=Navy]$ffff[/color]
[color=Purple]w1 [/color][color=DarkCyan]= [/color][color=Navy]$00ff[/color]

[color=Blue]gosub [/color][color=Black]BIN2BCD[/color]
[color=Blue]gosub [/color][color=Black]ShowBcdDigits[/color]

[color=Green]' Save b6 - b15 into other variables if needed.[/color]
[color=Blue]popram [/color][color=Green]; Note that converted number will go away with this command.[/color]

[color=Blue]end[/color]

[color=Black]BIN2BCD: [/color][color=Green]' ACKNOWLEDGEMENT:   PICAXE forum's BillBWann's routine for binary to BCD.
      '                       Texasclodhopper's fitting it into using only b0 - b15.

      ' Binary to BCD Conversion
      '     All calculations contained within first 16 bytes; b0 - b15
      '     Pass double-word value in w0 and w1
      '     Calling program should do "PUSHRAM clear" before calling

      ' RETURNS: BCD number returned starting at b6 up to b15.

      ' w0 ; LSW b1, b0
      ' b0 ; LSWL
      ' b1 ; LSWH
      ' w1 ; MSW b3, b2
      ' b2 ; MSWL
      ' b3 ; MSWH

      [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]6    [/color][color=Green]'push remainders onto stack starting b6

      [/color][color=Blue]do
            [/color][color=Purple]b5 [/color][color=DarkCyan]= [/color][color=Purple]w1 [/color][color=DarkCyan]// [/color][color=Navy]10           [/color][color=Green]; MSW b3, b2
            [/color][color=Purple]b4 [/color][color=DarkCyan]= [/color][color=Purple]b1                 [/color][color=Green]; LSWH b1
            [/color][color=Purple]w1 [/color][color=DarkCyan]= [/color][color=Purple]w1 [/color][color=DarkCyan]/ [/color][color=Navy]10            [/color][color=Green]; MSW b3, b2
            [/color][color=Purple]b1 [/color][color=DarkCyan]= [/color][color=Purple]w2 [/color][color=DarkCyan]/ [/color][color=Navy]10            [/color][color=Green]; temp w2 b5, b4
            [/color][color=Purple]b5 [/color][color=DarkCyan]= [/color][color=Purple]w2 [/color][color=DarkCyan]// [/color][color=Navy]10           [/color][color=Green]; temp w2 b5, b4
            [/color][color=Purple]b4 [/color][color=DarkCyan]= [/color][color=Purple]b0                 [/color][color=Green]; LSWL b0
            [/color][color=Purple]b0 [/color][color=DarkCyan]= [/color][color=Purple]w2 [/color][color=DarkCyan]/ [/color][color=Navy]10            [/color][color=Green]; temp w2 b5, b4
            [/color][color=Purple]@bptrinc [/color][color=DarkCyan]= [/color][color=Purple]w2 [/color][color=DarkCyan]// [/color][color=Navy]10     [/color][color=Green]; temp w2 b5, b4

      [/color][color=Blue]loop until [/color][color=Purple]w0 [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=DarkCyan]AND [/color][color=Purple]w1 [/color][color=DarkCyan]= [/color][color=Navy]0

      [/color][color=Blue]return[/color]

[color=Black]ShowBcdDigits:[/color]
[color=Green]' Sends BCD digits to serial one digit at a time
      [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]15 [/color][color=Green]' for b15 starting point
      [/color][color=Blue]do
            if [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]15 [/color][color=DarkCyan]AND [/color][color=Purple]@bptr [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]then
                  sertxd([/color][color=Red]" "[/color][color=Blue])
                  dec [/color][color=Purple]bptr
            [/color][color=Blue]elseif [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]14 [/color][color=DarkCyan]AND [/color][color=Purple]@bptr [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]then
                  sertxd([/color][color=Red]" "[/color][color=Blue])
                  dec [/color][color=Purple]bptr
            [/color][color=Blue]elseif [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]13 [/color][color=DarkCyan]AND [/color][color=Purple]@bptr [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]then
                  sertxd([/color][color=Red]" "[/color][color=Blue])
                  dec [/color][color=Purple]bptr
            [/color][color=Blue]else
                  sertxd([/color][color=Black]#[/color][color=Purple]@bptrdec[/color][color=Blue])
            endif
            if [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]11 [/color][color=Blue]then [/color][color=Black]: [/color][color=Blue]sertxd([/color][color=Red]"."[/color][color=Blue]) [/color][color=Black]: [/color][color=Blue]endif
      loop until [/color][color=Purple]bptr [/color][color=DarkCyan]< [/color][color=Navy]6
      [/color][color=Blue]sertxd(cr[/color][color=Black],[/color][color=Blue]lf)
      return[/color]
 

inglewoodpete

Senior Member
I find the code for the top 3 digits a little confusing. What if your number (in decimal) is 203xxx.....? The routine would transmit 2 3xxx.....

Perhaps you should have 2 loops: the first one searching for the first significant digit, sending a space character for each zero. Once a digit > 0 is found, drop through to the second, number-sending loop.
 

BillBWann

Member
What if your number (in decimal) is 203xxx.....?
I'd suggest using the following code.

Code:
' Program to demonstrate 32-bit binary to BCD convertion
'     PUSHRAM / POPRAM not supported on all chips

#picaxe 40X2
pushram clear

w0 = $f119	'Decimal 20312345
w1 = $0135

gosub BIN2BCD
gosub ShowBcdDigits

w0 = $00ff	'Decimal 255
w1 = $0000

gosub BIN2BCD
gosub ShowBcdDigits

' Save b6 - b15 into other variables if needed.
'popram ; Note that converted number will go away with this command.

end

BIN2BCD: ' ACKNOWLEDGEMENT:   PICAXE forum's BillBWann's routine for binary to BCD.
      '                       Texasclodhopper's fitting it into using only b0 - b15.

      ' Binary to BCD Conversion
      '     All calculations contained within first 16 bytes; b0 - b15
      '     Pass double-word value in w0 and w1
  
      ' RETURNS: BCD number returned starting at b6 up to b15.

      ' w0 ; LSW b1, b0
      ' b0 ; LSWL
      ' b1 ; LSWH
      ' w1 ; MSW b3, b2
      ' b2 ; MSWL
      ' b3 ; MSWH

      bptr = 6    'push remainders onto stack starting b6

      do
            b5 = w1 // 10           ; MSW b3, b2
            b4 = b1                 ; LSWH b1
            w1 = w1 / 10            ; MSW b3, b2
            b1 = w2 / 10            ; temp w2 b5, b4
            b5 = w2 // 10           ; temp w2 b5, b4
            b4 = b0                 ; LSWL b0
            b0 = w2 / 10            ; temp w2 b5, b4
            @bptrinc = w2 // 10     ; temp w2 b5, b4

      loop until w0 = 0 AND w1 = 0
		dec bptr
      return

ShowBcdDigits:
' Sends BCD digits to serial one digit at a time
      
      For b0=15 to 6 step-1
        if b0 = 11 then : sertxd(".") : endif
      	if b0>bptr then
	      	if b0>12 then
	      		sertxd (" ")
	      	else
	      		sertxd ("0")
	      	endif
      	else
            sertxd(#@bptrdec)
			endif
		next b0	      		
      sertxd(cr,lf)
      return
Note that in this version there is also no need to PUSHRAM CLEAR between successive calls to BIN2BCD as only the relevant part of the stack is assigned to a digit.
 

techElder

Well-known member
Dang, I knew I forgot to do something ... test the ... blanking ... routine! :)

I remember testing at the extremes, but should have run a loop through all numbers. That would have shown up.

I'll run through what y'all suggest and get back.

Thanks!
 

techElder

Well-known member
Ok, have been running some tests and can't find any reason not to stand by my original code. I tried to run tests through transitions and couldn't see any problems. Don't have time to run through 32-bits. :) I did test out to 32-bits but not every bit.

IWP, leading zero blanking works, but I'm not going to use that version anyway.

BillBWann, your display routine, ShowBcdDigits2, works better, faster and is more memory efficient. As you stated, there is no reason to worry about low RAM between calls to this routine. You've convinced me!

Code:
[color=Green]' Program to demonstrate 32-bit binary to BCD convertion
'     PUSHRAM / POPRAM not supported on all chips[/color]

[color=Navy]#picaxe [/color][color=Black]40X2[/color]
[color=Navy]#no_table
#freq [/color][color=Blue]m32[/color]
[color=Navy]#terminal 38400[/color]

[color=Blue]setfreq em32[/color]

[color=Green]'pushram clear

;w1 = $0135
;w0 = $f119
;
;gosub BIN2BCD
;gosub ShowBcdDigits
;
;' Save b6 - b15 into other variables if needed.
;'popram ; Note that converted number will go away with this command.
;
;pushram clear
;
;w1 = $0000
;w0 = $0015
;
;gosub BIN2BCD
;gosub ShowBcdDigits[/color]
[color=Blue]pause [/color][color=Navy]1[/color]
[color=Blue]sertxd(cr[/color][color=Black],[/color][color=Blue]lf[/color][color=Black],[/color][color=Blue]cr[/color][color=Black],[/color][color=Blue]lf)[/color]
[color=Green]'sertxd("ShowBcdDigits",cr,lf)[/color]
[color=Blue]sertxd([/color][color=Red]"ShowBcdDigits Rev.2"[/color][color=Black],[/color][color=Blue]cr[/color][color=Black],[/color][color=Blue]lf)

for [/color][color=Purple]w11 [/color][color=DarkCyan]= [/color][color=Navy]$0b50 [/color][color=Blue]to [/color][color=Navy]$0c1f
      [/color][color=Blue]for [/color][color=Purple]w10 [/color][color=DarkCyan]= [/color][color=Navy]$0000 [/color][color=Blue]to [/color][color=Navy]$0001
            [/color][color=Green]'pushram clear
            [/color][color=Purple]w0 [/color][color=DarkCyan]= [/color][color=Purple]w10 [/color][color=Black]: [/color][color=Purple]w1 [/color][color=DarkCyan]= [/color][color=Purple]w11
            [/color][color=Blue]gosub [/color][color=Black]BIN2BCD
            [/color][color=Blue]gosub [/color][color=Black]ShowBcdDigits2
            [/color][color=Green]'popram ; Note that converted number will go away with this command.
      [/color][color=Blue]next
next[/color]

[color=Green]' Save b6 - b15 into other variables if needed.
'popram ; Note that converted number will go away with this command.[/color]

[color=Blue]end[/color]

[color=Black]BIN2BCD: [/color][color=Green]' ACKNOWLEDGEMENT:   PICAXE forum's BillBWann's routine for binary to BCD.
      '                       Texasclodhopper's fitting it into using only b0 - b15.

      ' Binary to BCD Conversion
      '     All calculations contained within first 16 bytes; b0 - b15
      '     Pass double-word value in w0 and w1
      '     Calling program should do "PUSHRAM clear" before calling

      ' RETURNS: BCD number returned starting at b6 up to b15.

      ' w0 ; LSW b1, b0
      ' b0 ; LSWL
      ' b1 ; LSWH
      ' w1 ; MSW b3, b2
      ' b2 ; MSWL
      ' b3 ; MSWH

      [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]6    [/color][color=Green]'push remainders onto stack starting b6

      [/color][color=Blue]do
            [/color][color=Purple]b5 [/color][color=DarkCyan]= [/color][color=Purple]w1 [/color][color=DarkCyan]// [/color][color=Navy]10           [/color][color=Green]; MSW b3, b2
            [/color][color=Purple]b4 [/color][color=DarkCyan]= [/color][color=Purple]b1                 [/color][color=Green]; LSWH b1
            [/color][color=Purple]w1 [/color][color=DarkCyan]= [/color][color=Purple]w1 [/color][color=DarkCyan]/ [/color][color=Navy]10            [/color][color=Green]; MSW b3, b2
            [/color][color=Purple]b1 [/color][color=DarkCyan]= [/color][color=Purple]w2 [/color][color=DarkCyan]/ [/color][color=Navy]10            [/color][color=Green]; temp w2 b5, b4
            [/color][color=Purple]b5 [/color][color=DarkCyan]= [/color][color=Purple]w2 [/color][color=DarkCyan]// [/color][color=Navy]10           [/color][color=Green]; temp w2 b5, b4
            [/color][color=Purple]b4 [/color][color=DarkCyan]= [/color][color=Purple]b0                 [/color][color=Green]; LSWL b0
            [/color][color=Purple]b0 [/color][color=DarkCyan]= [/color][color=Purple]w2 [/color][color=DarkCyan]/ [/color][color=Navy]10            [/color][color=Green]; temp w2 b5, b4
            [/color][color=Purple]@bptrinc [/color][color=DarkCyan]= [/color][color=Purple]w2 [/color][color=DarkCyan]// [/color][color=Navy]10     [/color][color=Green]; temp w2 b5, b4

      [/color][color=Blue]loop until [/color][color=Purple]w0 [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=DarkCyan]AND [/color][color=Purple]w1 [/color][color=DarkCyan]= [/color][color=Navy]0
      [/color][color=Blue]dec [/color][color=Purple]bptr                      [/color][color=Green]; added per BillBWann July 5, 2015
      [/color][color=Blue]return[/color]

[color=Black]ShowBcdDigits:[/color]
[color=Green]' Sends BCD digits to serial one digit at a time
      [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]15 [/color][color=Green]' for b15 starting point
      [/color][color=Blue]do
            if [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]15 [/color][color=DarkCyan]AND [/color][color=Purple]@bptr [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]then
                  sertxd([/color][color=Red]" "[/color][color=Blue])
                  dec [/color][color=Purple]bptr
            [/color][color=Blue]elseif [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]14 [/color][color=DarkCyan]AND [/color][color=Purple]@bptr [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]then
                  sertxd([/color][color=Red]" "[/color][color=Blue])
                  dec [/color][color=Purple]bptr
            [/color][color=Blue]elseif [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]13 [/color][color=DarkCyan]AND [/color][color=Purple]@bptr [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=Blue]then
                  sertxd([/color][color=Red]" "[/color][color=Blue])
                  dec [/color][color=Purple]bptr
            [/color][color=Blue]else
                  sertxd([/color][color=Black]#[/color][color=Purple]@bptrdec[/color][color=Blue])
            endif
            if [/color][color=Purple]bptr [/color][color=DarkCyan]= [/color][color=Navy]11 [/color][color=Blue]then [/color][color=Black]: [/color][color=Blue]sertxd([/color][color=Red]"."[/color][color=Blue]) [/color][color=Black]: [/color][color=Blue]endif
      loop until [/color][color=Purple]bptr [/color][color=DarkCyan]< [/color][color=Navy]6
      [/color][color=Blue]sertxd(cr[/color][color=Black],[/color][color=Blue]lf)
      return[/color]

[color=Black]ShowBcdDigits2:                     [/color][color=Green]; added per BillBWann July 5, 2015
' Sends BCD digits to serial one digit at a time

      [/color][color=Blue]for [/color][color=Purple]b0 [/color][color=DarkCyan]= [/color][color=Navy]15 [/color][color=Blue]to [/color][color=Navy]6 [/color][color=Blue]step [/color][color=DarkCyan]-[/color][color=Navy]1
            [/color][color=Blue]if [/color][color=Purple]b0 [/color][color=DarkCyan]= [/color][color=Navy]11 [/color][color=Blue]then [/color][color=Black]: [/color][color=Blue]sertxd([/color][color=Red]"."[/color][color=Blue]) [/color][color=Black]: [/color][color=Blue]endif
            if [/color][color=Purple]b0 [/color][color=DarkCyan]> [/color][color=Purple]bptr [/color][color=Blue]then
                  if [/color][color=Purple]b0 [/color][color=DarkCyan]> [/color][color=Navy]12 [/color][color=Blue]then
                        sertxd([/color][color=Red]" "[/color][color=Blue])
                  else
                        sertxd([/color][color=Red]"0"[/color][color=Blue])
                  endif
            else
                  sertxd([/color][color=Black]#[/color][color=Purple]@bptrdec[/color][color=Blue])
            endif
      next [/color][color=Purple]b0
      [/color][color=Blue]sertxd(cr[/color][color=Black],[/color][color=Blue]lf)
      return[/color]
EDIT: Noticed that in the blanking routine ShowBcdDigits2 the line IF b0 > bptr then ... depends on this value remaining from the conversion routine BIN2BCD. It works as written as long as the transmission of the converted digits happens right after the conversion.
 
Last edited:

techElder

Well-known member
IWP, I have to apologize to you about the blanking. I tested your example, but it occurred to me to add another zero in the example and it failed. Decimal "203xxx....." passes, but "2003xxx....." exhibits the behavior that you describe.

Still a work in progress ...
 

BillBWann

Member
EDIT: Noticed that in the blanking routine ShowBcdDigits2 the line IF b0 > bptr then ... depends on this value remaining from the conversion routine BIN2BCD. It works as written as long as the transmission of the converted digits happens right after the conversion.
If this is a limitation, then save bptr in say b1 (so it's included in the PUSHRAM) before you leave BIN2BCD and then restore it again on entry to ShowBcdDigits2.
 

techElder

Well-known member
Oh yeah, its only a problem when the two routines are separate.

I combined then (as your original code suggested) into a very neat routine that executes as fast as many other routines that I use.

Recommended.
 

techElder

Well-known member
The reason that I separated the routines in the first place was that I was both transmitting the BCD coded digits over serial and storing them in upper ram. That way I could use them in other parts of my program.
 
Top