ATMEGA 8 HEXADECIMAL TO DECIMAL Conversion Problem!!

Status
Not open for further replies.

Narendra1190

Member level 2
Joined
Jan 23, 2014
Messages
44
Helped
6
Reputation
12
Reaction score
6
Trophy points
1,288
Activity points
1,666
Hi Geeks!!
I am working on project with Atmega8 and want to communicate with another MCU using USART.
I am able to communicate with USART at 19200 Baud rate at 8MHz frequency.
But i am getting values from another MCU in HEXADECIMAL.
I have written Code to convert hex to decimal.I am closer,But getting little Error.
Each next incremental digit i am getting -1 error in result.In Method-2 code i found there is an error in Pow function of math.
Can anyone help me out.

Posting code below.



Method 1

Code:
#include <avr/io.h>
#include <inttypes.h>
#include<util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <avr/io.h>
#include <avr/eeprom.h>
#include <math.h>

	char Digits[] ="0123456789abcdef";
	int a,b,j,len,i,val,power=0,test=10;  
	long int decimal;
	 char stringOfData[50];
	 char stringOfReadData[4];
	 char data;
	 char hexbuff[4];
	 char testbuff[2];
	char jj[2];
void USARTInit(uint16_t ubrr_value)

{

   

	   UBRRL = ubrr_value;
	   UBRRH = (ubrr_value>>8);
	   UCSRC=(1<<URSEL)|(3<<UCSZ0);
	   UCSRB=(1<<RXEN)|(1<<TXEN);


}

char USARTReadChar()
{
  

			   while(!(UCSRA & (1<<RXC)))
			   {
      
			   }
			   return UDR;
}



void USARTWriteChar(char data)
{
			   while(!(UCSRA & (1<<UDRE)))
			   {
      
			   }

			   UDR=data;
}


void USARTWriteStr(unsigned char * str)
	{
				while(*str)
					{
					UDR = *str++;
					while(!(UCSRA&(1<<UDRE)));
					}
	}


void htod()
{
							for(i=3; i<=0; i--)
							{
  						
							/*search currect character in Digits array */
								for(j=0; j<16; j++)
								{
 									if(hexbuff[i] == Digits[j])
									{
			  							
										
										USARTWriteStr("\n\r");
										USARTWriteStr("no=");
										itoa(j,jj,10);
										USARTWriteStr(jj);
									 }
								}
								power++;
							}  
}

void main()
{
	USARTInit(25);
	

//	USARTWriteStr("start");
	while(1)
	
		{
				decimal = 0;
				power = 0;
					USARTWriteStr("Enter value=");
					
							for(a=0; a<=3; a++)
							{
				
											hexbuff[a] = USARTReadChar();	
											USARTWriteChar(hexbuff[a]);
							}
					

				//	USARTWriteStr("\n\r");
					USARTWriteStr("Value=");
					USARTWriteStr(hexbuff);
				//	htod();


				for(i=3; i>=0; i--)
							{
  										
									
							/*search currect character in Digits array */
								for(j=0; j<=15; j++)
								{
 									if(hexbuff[i] == Digits[j])
									{
										
										decimal +=  j*pow(16,power);
										ltoa(decimal,stringOfData,10);
										USARTWriteStr("D=");
										USARTWriteStr(stringOfData);
										USARTWriteStr("\n\r");

									 }
								}
								power++;
							}  

					
					/*b= 3;
					val = pow(16,b);
					itoa(val,testbuff,10);

					USARTWriteStr("\n\r test=");
					USARTWriteStr(testbuff);
					USARTWriteStr("\n\r");
					
					itoa(decimal,stringOfData,10);
								
					USARTWriteStr("D=");
					USARTWriteStr(stringOfData);
					USARTWriteStr("\n\r");*/
					
					
		}
		
	return 0;
}


Method 2

Code:
#include <avr/io.h>

#include<util/delay.h>


#include <avr/io.h>

#include <math.h>

	char Digits[] ="0123456789abcdef";
	int a,b,j,len,i,val,power=0,test=10;  
	 int decimal,varg,calcn;
unsigned	 char vargData[50];
unsigned	 char stringOfnData[50];
unsigned	 char stringOfReadData[50];
	 char data;
unsigned	 char hexbuff[4];
unsigned	 char testbuff[2];
	char jj[2];
void USARTInit(uint16_t ubrr_value)

{

   

	   UBRRL = ubrr_value;
	   UBRRH = (ubrr_value>>8);
	   UCSRC=(1<<URSEL)|(3<<UCSZ0);
	   UCSRB=(1<<RXEN)|(1<<TXEN);


}

char USARTReadChar()
{
  

			   while(!(UCSRA & (1<<RXC)))
			   {
      
			   }
			   return UDR;
}



void USARTWriteChar(char data)
{
			   while(!(UCSRA & (1<<UDRE)))
			   {
      
			   }

			   UDR=data;
}


void USARTWriteStr(unsigned char * str)
	{
				while(*str)
					{
					UDR = *str++;
					while(!(UCSRA&(1<<UDRE)));
					}
	}




void main()
{
	USARTInit(25);
	

//	USARTWriteStr("start");
	while(1)
	
		{
				decimal = 0;
				varg =0; 
				power = 0;
					USARTWriteStr("Enter value=");
					
							for(a=0; a<=3; a++)
							{
				
											hexbuff[a] = USARTReadChar();	
											USARTWriteChar(hexbuff[a]);
							}
					

					USARTWriteStr("\n\r");
					USARTWriteStr("Value=");
					USARTWriteStr(hexbuff);
					USARTWriteStr("\n\r");
				


				for(i=3; i>=0; i--)
							{
  										
									
							/*search currect character in Digits array */
								for(j=0; j<=15; j++)
								{
 									if(hexbuff[i] == Digits[j])
									{
										
										varg =  pow(16,power);
										itoa(varg,vargData,10);
										USARTWriteStr("v=");
										USARTWriteStr(vargData);
										USARTWriteStr("    ");
										calcn = j*varg;
										itoa(calcn,stringOfnData,10);
										USARTWriteStr("c=");
										USARTWriteStr(stringOfnData);
										USARTWriteStr("   ");
										decimal = decimal +calcn;
										itoa(decimal,stringOfReadData,10);
										USARTWriteStr("D=");
										USARTWriteStr(stringOfReadData);
										USARTWriteStr("\n\r");

									 }
								}
								power++;
							}  

					
					b= 3;
					val = pow(16,b);
					itoa(val,testbuff,10);

					USARTWriteStr("\n\r test=");
					USARTWriteStr(testbuff);
					USARTWriteStr("\n\r");
					
					
					
		}
		
	return 0;
}

Thanks for your time.
 

check your use and initialisation's of the variable "power"
 

Why so complex? In addition to excessive complexity of the code, using "pow", that works with doubles, in 8-bit atmega will take enormous amount of processor time.
Use sprintf function instead.
 

pow function is working well outside for loop.I am getting error when i am using inside for loop.
 

i am getting -1 error in result

Do you mean a compilation error (-1), or that value is being shifted minus one unit ?
 

Thanks to all for reply.
I am able to convert 1 digit HEX to Decimal successfully.
As digit increases the final decimal result comes with -1 error.
for two digit HEX (00ff), i am getting 254 which is actual 255.
for three digit HEX (0fff), i am getting 4093 which is actual 4095.
In the first POST i have attached complete working code with METHOD-1 & Method-2.In Tera-Term(or Hyperterminal) we can check the output result.
Pow function is OK.But something is wrong in 'for' loop.I am wrapping code below.
Code:
#include 
.
.
char Digits[] ="0123456789abcdef";
char hexbuff[4];



for(i=3; i>=0; i--)
							{
  										
									
							/*search currect character in Digits array */
								for(j=0; j<=15; j++)
								{
 									if(hexbuff[i] == Digits[j])
									{
										
										decimal +=  j*pow(16,power);
										ltoa(decimal,stringOfData,10);
										USARTWriteStr("D=");
										USARTWriteStr(stringOfData);
										USARTWriteStr("\n\r");

									 }
								}
								power++;
							} 
.
.
 

Pow is not OK. Pow is a floating point function. Besides the fact that mega doesn't have dedicated FPU (that makes compiler to create a huge amount of code for mega's integer ALU, something about 2K of memory) the floating point numbers tend to be erroneous, so when you do 16^2 you get not 256, but 255.99997 (not exactly this, but similar), after converting it to int (that just truncates everything after the decimal point, but not rounds it) you get 255, and so forth with 16^3 etc. That is your -1 error. Important note, some powers will be with positive error, like 65536.0004 (again, not exactly the same, not exactly this number) and you will get correct result.
The solution may be this. As every iteration your power factor 16x bigger than previous, just replace pow with multiply by 16 (and 4-bit left shift is even better) like this:

Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int power_multiplier = 1;
for(i=3; i>=0; i--)
{
    
    /*search currect character in Digits array */
    for(j=0; j<=15; j++)
    {
        if(hexbuff[i] == Digits[j])
        {
            decimal +=  j*power_multiplier;
            ltoa(decimal,stringOfData,10);
         }
    }
//power_multiplier *= 16; //both examples are suitable
    power_multiplier <<= 4;
}



Or other soultion, single-line, I offered earlier (sorry not sprintf, but sscanf) will look like this:

Code C - [expand]
1
sscanf(hexbuff, "%x", &decimal);



Formatted input-output also takes significant amount of code, but at least it less error-prone, more readable.

And by the way, why all your variables are global?
 
P.S. I just can't stop thinking about optimization of this code.
My implementation. It doesn't have error-detection, just assuming the hex-string is correct and will fit the int. It uses 1 if-else branch instead of 16 in original, also it doesn't use consuming operations like multiply, only bit-wise, add/sub and shift operations. Also it may operate with uncompleted hex-strings (like "1F" instead of "001F") and is case-insensitive (even "FaCe" will be converted).

Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int alphahex_to_int(char *h)
{
    int result = 0;
    int tmp;
    while(*h)
    {
        if ((*h & 0xf0) == 0x30)
            tmp = *h - '0';
        else 
            tmp = (*h & 0x0f) + 9;
        result = (result << 4) + tmp;
        h++;
    }
    return result;
}

 

Thanks Altaero for your contribution to this post.
I have redefined all the variables in my project.


You might be right about issue of pow function & FPU.

I got the simple solution.
Actually HEX value is a temperature value in my project which after conversion to decimal i have to divide by 100.
During conversion i get error but after dividing by 100 i get the exact result with 2 digit precision.
I am getting the final result now.

Thanks for your time and description.
 

The adc value returned by the adc will be in binary format in the register or variable. if you want to divide a variable with decimal 100 then you can do it like this

Code:
var = var / 100;

or

var = var / 0x64;

or

var = var / 0b01100100;

They are all same. It doesn't matter whether you use binary or hex or decimal format to assign a value to the register or variable. It is only for your use different formats are provided.
 

If you want round to 5, then you should use something like:
Code:
unsigned var = 151;
unsigned newvar1 = var / 100;     //newvar1 will be 1
unsigned newvar2 = (var + 50) / 100; //newvar2 will be 2 (rounded up)

unsigned var2 = 149;
unsigned newvar3 = (var2 + 50)/100;  //newvar3 will be 1 (not rounded up)

if you want to understand the floating point arithmetic precision errors with an example, try to calculate this:
Code:
double a = 0.3;
double b = 3 * a + 0.1;
if(b == 1.0)
      printf("Ho ho ho, think again\n");
 

Are you using LCD to display ?
 

Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…