Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

hex number to ASCII number

Status
Not open for further replies.

qtommer

Member
hi

I have a timer implemented which increments and overflows updating minute and hour registers. Each time 1 minute has passed,min_reg gets incremented. if 60 mins have passed, hour_reg gets incremented by 1.

I am now planning to implement a function whereby the user can read these registers (min_reg, hour_reg) to know the amount of time that has elapsed, the user will be reading these values via the UART.
Hence, the min_reg and the hour_reg has to be converted to ASCII before being sent out through the UART (say from a PIC-PC)

From the ASCII table , numbers 0-9 are respectively 0x30-0x39. Hence the value of the ASCII number to be converted is the [hex number + 0x30] but this is only applicable if the number in the register is below or equal to 9. Once this is a ten, then we have a problem.

So my question is, how should I implement number hex to ASCII number conversion that takes into account a hex number which has a decimal value more than 9?

THanks:)
 
hi qt,
Which programming language are you using.?
 
hi
i'm so sorry . That was the most vital point I should have presented! Im using PIC assembly. Thanks=)
 
Check my PIC tutorials, many of them include a routine to convert 16 bit HEX numbers to single digits and then to ASCII.

For single byte (8 bit) numbers you simply load the top byte with zero.
 
You code just divide by 10, and add 48 to the result as well as to the remainder to get ascii, but I would also look at Nigel's page.
 
Plenty of bin-to-dec or bin-to-ascii routines around. A few have been published here on the forum and many others can be found on piclist. I wouldn't want anyone to accuse you of being lazy so I'll let you do the research.

Another approach not too many people consider is using packed BCD numbers in the range of 0x00 through 0x59 which don't really need much to convert to decimal digits. You simply separate the packed BCD byte into the 'tens' and 'ones' nybbles and OR them with '0' (0x30) before sending to LCD or UART. Here's a couple examples for a clock;

Code:
;******************************************************************
time    equ     0x40            ; char time[3], HH,MM,SS
hour    equ     time+0          ; hour, packed bcd 0x00..0x23
mins    equ     time+1          ; mins, packed bcd 0x00..0x59
secs    equ     time+2          ; secs, packed bcd 0x00..0x59
;
;  packed BCD 24 Hour RTC 'bump' routine (18 words)
;
bcdinc
        movlw   time+3          ; setup indirect array access
        movwf   FSR             ; starting with "secs" element
bcdnxt
        decf    FSR,F           ;
        incf    INDF,F          ; bump array element
        movlw   6               ;
        addwf   INDF,F          ; transition from 9 to A?
        skpdc                   ; no, skip, else
        subwf   INDF,F          ; fix it (increase tens digit)
        movlw   0x60            ;
        xorwf   INDF,F          ; transition from 59 to 60?
        skpnz                   ; no, skip, else
        goto    bcdnxt          ; bump next array element
        xorwf   INDF,F          ; restore current element
        movlw   0x24            ; 
        xorwf   time+0,F        ; transition 23:59:59 -> 24:00:00
        skpz                    ; no, skip, else
        xorwf   time+0,F        ; Z=1 on rollover to 00:00:00
        return                  ;
Code:
;
;  send time to uart
;
PutClk
        swapf   hour,W          ; the 'tens' nybble
        andlw   0x0F            ; trim off upper nybble
        iorlw   '0'             ; ascii '0'..'2'
        call    Put232          ;
        movf    hour,W          ; the 'ones' nybble
        andlw   0x0F            ; trim off upper nybble
        iorlw   '0'             ; ascii '0'..'9'
        call    Put232          ;
...
 
Last edited:
Another approach not too many people consider is using packed BCD numbers in the range of 0x00 through 0x59 which don't really don't need much to convert to decimal digits. You simply separate the packed BCD byte into the 'tens' and 'ones' nybbles and OR them with '0' (0x30) before sending to LCD or UART. Here's a couple examples for a clock;

The reason not many people consider it is that it has few advantages - you're just swapping complexity converting from BIN to ASCII for complexity when storing the orginal information. Generally speed creating the information is more important than speed displaying it.

It's a different story on various old microprocessors though (like the 6502) where they actually provided packed BCD modes in the instruction set.
 
It's not so much a matter of complexity as it is performance. The "cost" for converting a 0..99 binary number to "tens" and "ones" digits can be anywhere from 10-20 to 40-50 cycles, depending on the value of the number and the method used. The "cost" for incrementing or decrementing a packed BCD number or set of numbers instead of binary number(s) is just a few cycles.

Generally speed creating the information is more important than speed displaying it.
How would you support that conclusion, please? Or should that statement have been prefaced with "in my opinion" instead of "generally"?

Cheerful regards, Mike
 
Last edited:
Another approach: rewrite the original counting algorithm to count in ASCII. Have 4 registers/ram bytes as minutes ones, minutes tens, hours ones, hours tens. Count from $30->$39 in minute ones, at $3a reset to $30 & increment minute tens from $30 through $35 etc. A little extra housekeeping stuffs $20 in for leading zeros. This all presumes you don't need the ordinal counting format for something else. Happy Coding... <<<)))
 
I normally use a byte variable for each digit ie;
hours10
hours
mins10
mins
secs10
secs

Which are incremented and handled as binary.

Then display to ascii for each digit I do something like this
output_to_UART(hours10 + '0');

it's quite economical of code and ram. I think that's the same system OlPhart suggests above, although the actual data is binary (not ASCII).
 
Gentlemen, I'm sure the OP would appreciate seeing assembler examples of your methods...

Cheerful regards, Mike
 
It's all binary, I'm just suggesting counting in the ASCII character set of "0"->"9" as $30->$39 (00110000->00111001). Ordinal counting is 0->9 as binary 0000->1001. By counting in "ASCII", all queries of value get already formatted data.

If practical, I'll adopt the output format for internal process use. Then I don't have to do any conversion. Season to taste... <<<)))

As to assembler considerations: coding is essentially the same as ordinal; zero is $30, nine is $39. Increment the next position when the current position overflows, just like any other counting process. It's just offset by the ASCII numeric base of $30. Since I don't "do" PIC, my assembler examples aren't relevant.
 
Last edited:
OlPhart, thanks that's what I thought you meant. That can be a good system.

I prefer to work in binary digits, because often you need to actually do something with the clock data like compare to a set time or add some time period etc and if the numbers are 0-9 as binary numbers it is much easier to work with.

As for the display side, adding +='0' is very trivial and can be done right when the data is displayed (or transmitted). But that's just my preference.

Here is the clock code, I just typed it in the compiler and dumped the ASM output;

Code:
  // C code to use 6 binary digits as a clock 12:00:00

  unsigned char hours10;
  unsigned char hours;
  unsigned char mins10;
  unsigned char mins;
  unsigned char secs10;
  unsigned char secs;

  // update clock each second
  secs++;
  if(secs >= 10)
  {
    secs=0;
    secs10++;
  }
  if(secs10 >= 6)
  {
    secs10=0;
    mins++;
  }
  if(mins >= 10)
  {
    mins=0;
    mins10++;
  }
  if(mins10 >= 6)
  {
    mins10=0;
    hours++;
  }
  if(hours >= 10)
  {
    hours=0;
    hours10++;
  }
  if(hours10 == 1 && hours == 3)  // roll from 12 to 1 o'clock
  {
    hours10=0;
    hours = 1;
  }



------------------------------------------
; Below is the assembler version

;Clock.c,12 :: 		secs++;
$0004	$1303			BCF	STATUS, RP1
$0005	$1283			BCF	STATUS, RP0
$0006	$0AA0			INCF	_secs, 1
;Clock.c,13 :: 		if(secs >= 10)
$0007	$300A			MOVLW	10
$0008	$0220			SUBWF	_secs, 0
$0009	$1C03			BTFSS	STATUS, C
$000A	$280D			GOTO	L_main_0
;Clock.c,15 :: 		secs=0;
$000B	$01A0			CLRF	_secs, 1
;Clock.c,16 :: 		secs10++;
$000C	$0AA1			INCF	_secs10, 1
;Clock.c,17 :: 		}
$000D	$	L_main_0:
;Clock.c,18 :: 		if(secs10 >= 6)
$000D	$3006			MOVLW	6
$000E	$0221			SUBWF	_secs10, 0
$000F	$1C03			BTFSS	STATUS, C
$0010	$2813			GOTO	L_main_1
;Clock.c,20 :: 		secs10=0;
$0011	$01A1			CLRF	_secs10, 1
;Clock.c,21 :: 		mins++;
$0012	$0AA2			INCF	_mins, 1
;Clock.c,22 :: 		}
$0013	$	L_main_1:
;Clock.c,23 :: 		if(mins >= 10)
$0013	$300A			MOVLW	10
$0014	$0222			SUBWF	_mins, 0
$0015	$1C03			BTFSS	STATUS, C
$0016	$2819			GOTO	L_main_2
;Clock.c,25 :: 		mins=0;
$0017	$01A2			CLRF	_mins, 1
;Clock.c,26 :: 		mins10++;
$0018	$0AA3			INCF	_mins10, 1
;Clock.c,27 :: 		}
$0019	$	L_main_2:
;Clock.c,28 :: 		if(mins10 >= 6)
$0019	$3006			MOVLW	6
$001A	$0223			SUBWF	_mins10, 0
$001B	$1C03			BTFSS	STATUS, C
$001C	$281F			GOTO	L_main_3
;Clock.c,30 :: 		mins10=0;
$001D	$01A3			CLRF	_mins10, 1
;Clock.c,31 :: 		hours++;
$001E	$0AA4			INCF	_hours, 1
;Clock.c,32 :: 		}
$001F	$	L_main_3:
;Clock.c,33 :: 		if(hours >= 10)
$001F	$300A			MOVLW	10
$0020	$0224			SUBWF	_hours, 0
$0021	$1C03			BTFSS	STATUS, C
$0022	$2825			GOTO	L_main_4
;Clock.c,35 :: 		hours=0;
$0023	$01A4			CLRF	_hours, 1
;Clock.c,36 :: 		hours10++;
$0024	$0AA5			INCF	_hours10, 1
;Clock.c,37 :: 		}
$0025	$	L_main_4:
;Clock.c,38 :: 		if(hours10 == 1 && hours == 3)  // roll from 12 to 1 o'clock
$0025	$0825			MOVF	_hours10, 0
$0026	$3A01			XORLW	1
$0027	$1D03			BTFSS	STATUS, Z
$0028	$2830			GOTO	L_main_7
$0029	$0824			MOVF	_hours, 0
$002A	$3A03			XORLW	3
$002B	$1D03			BTFSS	STATUS, Z
$002C	$2830			GOTO	L_main_7
$002D	$	L42_ex_L_main_7:
;Clock.c,40 :: 		hours10=0;
$002D	$01A5			CLRF	_hours10, 1
;Clock.c,41 :: 		hours = 1;
$002E	$3001			MOVLW	1
$002F	$00A4			MOVWF	_hours
 
hi everyone.

Many thanks for your replies , I have gone through each and every method that each of you have laid out and chosen the one best suited to my application. =) Everything works sweet.:)
I now know more than one way of binary to ASCII conversion.=) and have my "mental programming library" further equipped. Thanks to all of you..

Cheers!:D
 
If you are using an 18F or 24F, that have multiply, you can use these routines that are very quick. The convert numbers in the range 0 - 99 (0x00 to 0x63) to BCD.

18F:-
Code:
 ;if we multiply by d'51' and add d'52', and divide by 2, the msb is the number divided by 10
	movwf	hex0
	mullw	d'51'
	movlw	d'52'
	addwf	prodl, f		
	movlw	0x00	
	addwfc	prodh, f	;this should clear the carry bit
	rrcf	prodh, w	;now w is the input/10
	movwf	bcd0
	mullw	d'10'
	movf	prodl, w
	subwf	hex0, f
	
	swapf	bcd0, w
	iorwf	hex0, w
	movwf	bcd0

24F :-

Code:
;if we multiply by d'51' and add d'52', and divide by 2, the msb is the number divided by 10
	mov		#51, w1
	mul.uu		w0, w1, w2
	mov		#52, w1
	add		w1, w2, w2	;this makes w2 512 times too big
	lsr		w2, #9, w1	;now w1 is hex /10
	mul.uu  	w1, #10, w2	;multiply by 10 so that the remainder can be calculated

	sub		w0, w2, w0	;w0 is the remainder, which is the last digit
	swap.b		w1
	ior		w0, w1, w0
	ior		w4, w0, w4		;combine the results
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top