[PIC] Converting SFR/GPR values to strings - Page 1
Forum section: electronics / general

author: icon
author_rank: Regular Contributor
timestamp: March 22, 2014, 08:53:54 pm
content: HiThere's probably something really obvious I can do, but I can't spot it in the XC8 compiler manual, though there are loads of 'nearly' library routines available. I'm just trying to print a byte (char?) value to an LCD module, so I need to convert it to (up to) 3 ASCII characters. So 0xFF becomes '2','5','5', for example.Hey; makes a change from an LED blinking...ThanksJohn

author: icon
author_rank: Regular Contributor
timestamp: March 22, 2014, 09:14:42 pm
content: Thinking about it a bit more, I think it comes down to binary to 3 byte BCD conversion, and then adding '0' (as opposed to 0) to each?Or might there be less/more to it?John

author: dannyf
author_rank: Super Contributor
timestamp: March 22, 2014, 09:15:16 pm
content: Fairly simple:Code:sRAM=(val % 10) + '0'; val /= 10; //convert the lowest digitsRAM=(val % 10) + '0'; val /= 10;sRAM=(val % 10) + '0'; //convert the highest digitIt can be applied for other approaches as well.Alternatively, you can use a look-up table - more space intensive but faster.

author: mariush
author_rank: Super Contributor
timestamp: March 22, 2014, 09:43:11 pm
content: One version would be using sprintf.If you don't want to add the complexity of stdio.h in your project, you can do as above. Alternatively ... a couple of while (number > 100) , while ( number > 10 ) ... but more cycles wasted.

author: miguelvp
author_rank: Super Contributor
timestamp: March 22, 2014, 09:53:46 pm
content: Quote from: icon on March 22, 2014, 09:14:42 pmI like that approach, danny's code will work but here is how I would do it if you don't want to link with the modulus and division functions.and mariush approach of using 2 while loops will work nice too.Code:// hard coding input but this will come from the value to be converted.unsigned char in_val = 255;unsigned short accumulator=0;unsigned short count=8;char digits;while(count) {// shift accumulator left.accumulator <<=1;// if most significant bit in in_val is one// increment the accumulator, otherwise the shift was enoughif(in_val&0x80) {accumulator++;}// Edit, no adjustment needed on the last loopif (count == 1) break;// check for adjustments (only 2 places since we are converting 0-255)if((accumulator&0x00f) > 0x004) {accumulator+=0x003;}if((accumulator&0x0f0) > 0x040) {accumulator+=0x030;}// shift in_val left chopping the already used most significant bit.in_val<<=1;count--;}// accumulator will now have 0000 0010 0101 0101for(count=0;count<3;count++) {digits = (accumulator&0x000f)+'0';// you can early out and don't do the last shift if count is 2// if (count == 2 ) break;accumulator >>= 4;}Edit added parenthesis on the adjustment comparisons and added an early out on the last loop so it wouldn't adjust the last result.But I like mariush approach better even if it's probably less efficient as in expandable to larger BCD numbers, but it's more readable and doesn't use that many more cycles:Code:unsigned char in_val = 255;char digits;digits = digits = digits  = '0';while (in_val > 99) {digits++;in_val -= 100;}while (in_val > 9) {digits++;in_val -= 10;}digits += in_val;edit, fixed code

author: dannyf
author_rank: Super Contributor
timestamp: March 22, 2014, 11:13:03 pm
content: QuoteQuoteDoes that really work?

author: miguelvp
author_rank: Super Contributor
timestamp: March 23, 2014, 12:01:41 am
content: Quote from: dannyf on March 22, 2014, 11:13:03 pmyup, here is my debug output of the result:Code:-digits0x0013ff70 "552Ì"53 '5'53 '5'50 '2'You are right, should be 99 and 9, fixed the code above

author: mariush
author_rank: Super Contributor
timestamp: March 23, 2014, 12:03:05 am
content: yeah, it should work.if not, he can do something like this :unsigned char number;unsigned char digits;for (number=original_number;number > 100;number=number-100) digits++;for (;number > 9;number=number-10) digits++;digits = number;or something like that.I didn't check which syntax is converted in less bytes of program or fewer instructions. PIC chips do have div and mod and that code will probably result in the smallest instruction count (fewest cycles lost) but I can't tell for sure.

author: dannyf
author_rank: Super Contributor
timestamp: March 23, 2014, 12:35:48 am
content: QuoteThink about what happens when the number is multiples of 100 or 10 or the combinations of the two.

author: miguelvp
author_rank: Super Contributor
timestamp: March 23, 2014, 12:48:59 am
content: yeah, I already changed my implementation above.Not proficient in PIC assembler, but this is his algorithm for an x86, the compiler doesn't even get close to this optimization.Code:unsigned char in_val = 255;char digits;digits = digits = digits  = '0';_asm {mov bl,in_valloop_100:cmp bl,100jb exit_loop_100inc digitssub bl,100jmp loop_100exit_loop_100:loop_10:cmp bl,10jb exit_loop_10inc digitssub bl,10jmp loop_10exit_loop_10:add digits,bl}// similar code in Cin_val = 255;digits = digits = digits  = '0';while (in_val > 99) {digits++;in_val -= 100;}while (in_val > 9) {digits++;in_val -= 10;}digits += in_val;Less instructions than my more general BCD algorithm that I posted earlier converted to assembler and without assigning the actual digits.Code:unsigned char in_val = 255;unsigned short accumulator=0;unsigned short count=8;char digits;_asm {mov cx,countmov ax,accumulatormov bl,in_valbegin_loop:shl ax,1mov bh,bland bh,80hje skip_incinc axskip_inc:cmp cx,1je end_loopmov bh,aland bh,0fhcmp bh,04hjbe skip_plus0x03add al,3skip_plus0x03:mov bh,aland bh,0f0hcmp bh,40hjbe skip_plus0x30add al,30hskip_plus0x30:shl bl,1dec cxjne begin_loopend_loop:mov accumulator,ax}

author: dannyf
author_rank: Super Contributor
timestamp: March 23, 2014, 12:55:53 am
content: QuoteOn chips with hardware division, it is not sure if it is still worthwhile to subtract multiple times in lieu of divisions.

author: miguelvp
author_rank: Super Contributor
timestamp: March 23, 2014, 01:17:24 am
content: Quote from: dannyf on March 23, 2014, 12:55:53 amThat's true for PIC24 but we don't know what he has, but since he mentioned the XC8 compiler it's safe to assume it's not a PIC24.

author: icon
author_rank: Regular Contributor
timestamp: March 23, 2014, 01:44:46 pm
content: HiYes, it's just a 16F690 (it happens to be the thing stuck in the Picdem Lab dev board I'm playing with).I don't know how I missed 'sprintf()' on a first pass - I was looking for something called 'ctoa' or something like that. It works as advertised - except that one of the registers I'm looking at apparently occasionally has values in excess of 255, which will need some investigation...Although it works, it takes a hefty chunk of code, so I think I experiment with some of the other solutions offered, too.ThanksJohn

author: dannyf
author_rank: Super Contributor
timestamp: March 23, 2014, 02:21:56 pm
content: -ctoa-itoa?on some compilers, it is done via sprintf,

author: icon
author_rank: Regular Contributor
timestamp: March 23, 2014, 08:15:14 pm
content: Quote from: icon on March 23, 2014, 01:44:46 pmAnd further investigation reveals that 'sprintf(buf,"%d",somechar)' left justifies, which is fair enough - should have thought of that. Slightly odder is the fact that the trailing 'spaces' are filled with something oddly oriental-looking, though I can't see it in the character set.'sprintf(buf",%3d",somechar)' right justifies, and leading spaces are blank, as one would want.J

author: miguelvp
author_rank: Super Contributor
timestamp: March 23, 2014, 08:27:36 pm
content: make sure your buf has space for the null termination. So for 3 digits you want the buffer to be 4 big. If it's not there the next variable after the buffer will control the null termination and it will print whatever if finds in memory until a 0 is reached.

author: icon
author_rank: Regular Contributor
timestamp: March 23, 2014, 09:01:03 pm
content: Quote from: miguelvp on March 23, 2014, 08:27:36 pmAh, the benefits of reading the manual. I'd have known that if I'd bothered... I've just done so now, and it does say "The resultant string will be null terminated" quite clearly.I'd just relied on knowing that buf was 3 digits, and doing a 'for' loop. I'll have to go and rewrite it properly.Thanks!John

author: miguelvp
author_rank: Super Contributor
timestamp: March 23, 2014, 09:20:12 pm
content: That's one of the problems using printf or their derivatives, other than it brings a lot of code to your program.You can still use the loop and use putchar (putc to stdout) to just print the characters one by one.end it with a putchar('\n'); or putc('\n',stdout); to jump to the next line. This way you can just use the buffer size 3 instead.