How to split an ASCII character hex byte
What basically I want to do is
For example, "a" hex-equivalent is equal 0x61
, it is possible to divide 61
on 6
and 1
and save them as '6'
and '1'
?
The buffer receives data as follows:
rx_dataframe.data[0] is H'00,'.'// H' is Hex equivalant and '' is ASCII value
rx_dataframe.data[0] is H'31,'1'
rx_dataframe.data[0] is H'32,'2'
rx_dataframe.data[0] is H'33,'3'
I need to convert hex values 0x00,0x31,0x32,0x33
to char value '0','0','3','1','3','2';'3','3'
and store them in placestx_buff_data[];
I want mine to tx_buff_data
look like this:
tx_buff_data[0] have H'30,'0'
tx_buff_data[1] have H'30,'0'
tx_buff_data[2] have H'33,'3'
tx_buff_data[3] have H'31,'1'
tx_buff_data[4] have H'33,'3'
tx_buff_data[5] have H'32,'2'
tx_buff_data[6] have H'33,'3'
tx_buff_data[7] have H'33,'3'
source to share
You can split each byte into two chunks (4-bit values) using AND + bitwise shifts:
unsigned char lo = byte & 0x0f;
unsigned char hi = (byte >> 4) & 0x0f;
Then you can convert each half to a hexadecimal character by looking up the array (since strings are only character arrays):
char loChar = "0123456789ABCDEF"[lo];
char hiChar = "0123456789ABCDEF"[hi];
source to share
Here is a sample program that can be used as a template for your code
#include <stdio.h>
int main(void)
{
char in[] = { '\0', '1', '2', '3' };
char out[2 * sizeof( in )];
size_t i;
char *p;
p = out;
for ( i = 0; i < sizeof( in ) / sizeof( *in ); i++ )
{
*p = ( ( unsigned char )in[i] & 0xF0 ) >> 4;
*p +=( *p < 10 ) ? '0' : 'A' - 10;
++p;
*p = ( ( unsigned char )in[i] & 0x0F );
*p +=( *p < 10 ) ? '0' : 'A' - 10;
++p;
}
for ( i = 0; i < sizeof( out ) / sizeof( *out ); i++ )
{
printf( "%c", out[i] );
}
puts( "" );
return 0;
}
Output signal
00313233
source to share
Using the ASCII character definition results in an extremely fast and economical solution. ASCII table shows that
values 0... 9 <=> figures '0'...'9' <=> 0x30...0x39 in ASCII code;
values 10...15 <=> figures 'A'...'F' <=> 0x41...0x46 in ASCII code;
or 'a'...'f' <=> 0x61...0x66 in ASCII code;
Sometimes, lowercase letters are used for "a" - "f".
So, if the value of our nibble is below 10 (from 0000b to 1001b), the representation of the character will be 0x30 + n or in c syntax '0' + n.
If n is between 10 and 15 (1010b to 1111b) 0x41 + n - 10 or "A" + n-10.
Using unsigned int 8bit instead of char type:
uint8_t nibble2char( uint8_t n ) {
// make sure that n e {0x00,...,0x0F}
if( n<10 ) return '0'+n;
else return 'A'+n-10;
}
or shorter:
uint8_t nibble2char( uint8_t n ) {
// make sure that n e {0x00,...,0x0F}
return n<10 ? '0'+n : 'A'+n-10;
}
or as a macro (thanks to chqrlie for (n)):
#define NIBBLE_TO_CHAR(n) ((n)<10 ? '0'+(n) : 'A'+(n)0) // n <= 15 !
If lowercase letters are to be used, replace "A" with "a".
Convert a 2-chunk byte to a 2-byte 0-terminated string that you can use:
void byte2str( uint8_t* s, uint8_t n ) {
// s points to a string of at least 3 Bytes length !
uint8_t hi = ( n >> 4 ) & 0x0F ;
uint8_t lo = n & 0x0F ;
s[0] = NIBBLE_TO_CHAR( hi ); // high byte
s[1] = NIBBLE_TO_CHAR( lo ); // low byte
s[2] = 0;
}
EDIT:
With the chqrlie fix for the macro, the function becomes more compact:
void byte2str( uint8_t* s, uint8_t n ) {
// s points to a string of at least 3 Bytes length !
s[0] = NIBBLE_TO_CHAR( ( n >> 4 ) & 0x0F ); // high byte
s[1] = NIBBLE_TO_CHAR( n & 0x0F ); // low byte
s[2] = 0;
}
source to share
I suggest you another option, doing a fixed size input conversion and checking the size of the output:
void convert(char *output, int size_out, char *input, int size_in)
{
static char hexdigit[] = "0123456789ABCDEF";
while (size_in-- && size_out>2) { // if not enough space, truncate
*output++ = hexdigit[(*input >> 4) & 0x0F]; // because char could be signed
*output++ = hexdigit[*input & 0x0F];
input++;
size_out -= 2;
}
*output++ = '\0';
}
As with the microcontroller, any overhead such as sprintf () is removed. You can call it like this:
convert(tx_buff_data, sizeof(tx_buff_data), your_input_buffer, number_of_bytes);
source to share