|
|
View previous topic :: View next topic |
Author |
Message |
io_Joe
Joined: 20 Nov 2013 Posts: 5
|
hex to decimal converter - efficiency |
Posted: Wed Dec 11, 2013 10:02 am |
|
|
Hi everyone,
I am doing a [sort of] hex to decimal converter, and it works, but I feel that I am doing it "the hard way"
I searched the forums and found a few posts but they deal with ASCII hex, this isn't quite that.
I have my code commented to what is going on.
Code: |
// dataBytes = 4 hex digits that are inputs from the user for data values.
// These values need to be converted from hex to decimal.
// The greatest any hex number will be is 0x9.
uint8 byte_0;
uint8 byte_1; // if dataByte = 0x1234 [ 1 2 3 4]
uint8 tmp_byte = dataByte; // gets the LSB of dataByte [x x 3 4], tmp_byte = 34 (hex)
byte_1 = ((tmp_byte >>4) *10); // shifts over the 3, then * 10, byte_1 = 30 (decimal)
tmp_byte = tmp_byte << 4; // shifts the 4 into MSB
tmp_byte = tmp_byte >> 4; // shifts the 4 back to LSB to remove the 3, and give tmp_byte a value of 4 (decimal)
byte_1 += (tmp_byte); // adds 4 (decimal) to 30 decimal
byte_0 = (dataByte >> 8); // gets the MSB of dataByte [1 2 x x] byte_0 = 12 (hex)
tmp_byte = byte_0;
tmp_byte = tmp_byte <<4;
tmp_byte = tmp_byte >>4;
byte_0 = ((byte_0 >> 4) *10 ); // now byte_0 is 10 (1 * 10)
byte_0 += tmp_byte;
uint16 total_bytes = byte_1;
total_bytes *= 100;
total_bytes += byte_0; |
This works, but I am using a 16f pic, with limited memory, currently my ROM is at 90%, but commonly I am getting issues to push it over 100%. If there are any tricks to clean this up I would greatly appreciate any direction.
Thank you,
io_joe |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Wed Dec 11, 2013 2:20 pm |
|
|
check out Printf()
I'm almost certain you can print to a variable... or at least I've always thought you could.
Also, you can consider using BCD which uses 4 bits per DEC digit... so you could store up to DEC 99 in a singe HEX byte.
G. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Wed Dec 11, 2013 2:36 pm |
|
|
i just re read your post...
are you just trying to get ASCII _NUMBER_ inputs like from a serial com to register as decimal?
... just substract 0x30.
G. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Dec 11, 2013 4:20 pm |
|
|
Quote: |
// dataBytes = 4 hex digits that are inputs from the user for data values.
// These values need to be converted from hex to decimal.
// The greatest any hex number will be is 0x9.
uint8 byte_0;
uint8 byte_1; // if dataByte = 0x1234 [ 1 2 3 4]
uint8 tmp_byte = dataByte; // gets the LSB of dataByte [x x 3 4], tmp_byte = 34 (hex)
|
You're actually describing BCD (binary coded decimal) format.
You have an int16 with four BCD digits, that can have a value from
0000 to 9999 (in BCD). You want to convert this to an unsigned int16
integer. This is the actual statement of your problem or request.
Do a Google search for this:
Quote: | fast bcd to binary conversion |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Dec 12, 2013 2:21 am |
|
|
There are also several little optimisations that are 'PIC specific'. For instance the swap instruction (a PIC instruction that CCS gives you a C command to access), which in one instruction, swaps the high and low nibbles of a byte.
Then multiplying by ten, is more efficiently done by adding val*2+val*8. There was a thread about doing this as efficiently as possible here only a couple of weeks ago.
Key is that if you store intermediate results, it is much more efficient than recalculating them.
Something like:
Code: |
//Your processor header here
#inline
int8 BCDtobin(int8 val)
{
//take a single byte coded as two BCD digits and return the binary value
int8 temp;
temp=val;
swap(temp);
temp&=0xF; //high nibble
temp*=2;
temp=temp+(temp*4); //efficient *10 *8+*2
temp+=(val&0xF); //add the low nibble
return temp;
}
int16 BCDtoLongbin(int16 val)
{
//Now convert a 16bit BCD value to 16bit result;
int16 ltemp, ltemp2;
ltemp=BCDtobin(make8(val,0)); //convert the MSB
//now need *100.
ltemp*=4;
ltemp2=ltemp*8;
ltemp=ltemp+ltemp2+(ltemp2*2); //efficient *100 -> *64+*32+*4
ltemp+=BCDtobin(make8(val,1)); //now the LSB
return ltemp;
}
void main()
{
int16 testval;
testval=0x1234;
testval=BCDtoLongbin(testval);
//here 'testval' is 0xD54 = 3412 as the original
while(TRUE) ;
}
|
Now, this takes just 96 machine instructions to do the conversion, while the original version takes 182. Nearly twice as fast.
Removing the inline, costs about ten instruction times, but saves about 13 instructions on size.
I note that the original code is reversing the byte order. Treating the number as if it is stored MSB first in memory. I have coded this to be the same. If this is wrong, then just swap the 0's and 1's in the make8 operations.
May well be possible to improve further, haven't looked deeply.
As a comment have just compared the size using a PIC16, and it is 48 bytes smaller (using the non #inline form)
Best Wishes |
|
|
io_Joe
Joined: 20 Nov 2013 Posts: 5
|
|
Posted: Thu Dec 12, 2013 10:10 am |
|
|
Thanks a ton for the info guys.
Ttelmah - wow! nice catch on the data coming in backwards on total_bytes, thank you! |
|
|
dorinm
Joined: 07 Jan 2006 Posts: 38
|
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Thu Dec 12, 2013 11:52 am |
|
|
hmm... another way... haven't done the code but...
if you breakdown the 2 bytes into nibbles...
ie: 0x1234 -> 1,2,3,4
then
sum=(4096*1)+(256*2)+(16*3)+(1x4)= 4660
don't know if it's faster, trying to thaw my -11*C toes from being outside....
be interesting to see if it is though.
jay |
|
|
dorinm
Joined: 07 Jan 2006 Posts: 38
|
|
Posted: Thu Dec 12, 2013 3:18 pm |
|
|
more interesting, too!
Code: |
.................... //actually, we start here
.................... h=l;
002FE: MOVFF 2C,2A
00302: MOVFF 2B,29
....................
.................... int16 k;
.................... k=(4096*(h&0xf000))+(256*(h&0x0f00))+(16*(h&0x00f0))+(h&0x000f);
00306: ANDLW 00
00308: MOVWF 00
0030A: MOVF 2A,W
0030C: ANDLW F0
0030E: MOVWF 03
00310: MOVFF 00,02
00314: SWAPF 00,W
00316: MOVWF 30
00318: CLRF 2F
0031A: MOVLW F0
0031C: ANDWF 30,F
0031E: ANDLW 00
00320: MOVWF 00
00322: MOVF 2A,W
00324: ANDLW 0F
00326: MOVWF 03
00328: MOVFF 00,03
0032C: MOVLW 00
0032E: ADDWF 2F,F
00330: MOVF 00,W
00332: ADDWFC 30,F
00334: MOVF 29,W
00336: ANDLW F0
00338: MOVWF 00
0033A: CLRF 03
0033C: MOVWF 02
0033E: RLCF 02,F
00340: RLCF 03,F
00342: RLCF 02,F
00344: RLCF 03,F
00346: RLCF 02,F
00348: RLCF 03,F
0034A: RLCF 02,F
0034C: RLCF 03,F
0034E: MOVLW F0
00350: ANDWF 02,F
00352: MOVF 02,W
00354: ADDWF 2F,F
00356: MOVF 03,W
00358: ADDWFC 30,F
0035A: MOVF 29,W
0035C: ANDLW 0F
0035E: MOVWF 00
00360: CLRF 03
00362: MOVF 00,W
00364: ADDWF 2F,W
00366: MOVWF 2D
00368: MOVF 03,W
0036A: ADDWFC 30,W
0036C: MOVWF 2E
.................... //and k is the final result
|
...51 instructions ) ...oh! wait! it's not 51, it might be 0x51, it's 81! ... lost between numbers |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Dec 16, 2013 5:40 am |
|
|
Unfortunately though, won't work....
(h&0xF000), does not return '1', but '0x1000'. 4096. But the value this digit represents wants to only be 1000 decimal.....
Try it.
No wonder it involves a less work!. Actually gives the same number 'out' as 'in'....
It is the multiplications by 10, 100 etc., that makes the BCD conversion much harder than hex conversion.
Best Wishes |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|