开发者

Is there an easy way to convert a number to hexidecimal ASCII chars in C?

I am working a C firmware program for an embedded device. I want to send an array of hex char values over the serial port. Is there a simple way to convert a value to ASCII hex?

For example if the array contains 0xFF, I want to send out the ASCII string "FF", or for a hex value of 0x3B I want to send out "3B".

How is this typically done?

I already have the serial send functions in place so that I can do this...

char msg[] = "Send this message";
SendString(msg);

and the SendString function calls this function for each element in the passed array:

// This function sends out a single character over the UART
int SendU( int c)
{
    while(U1STAbits.UTXBF);
    U1TXREG = c;
    return c;
}

I am looking for a function that will allow me to do this...

char HexArray[5] = {0x4D, 0xFF, 0xE3, 0xAA, 0xC4};
SendHexArray(HexArray);

//Output "4D, FF开发者_JAVA百科, E3, AA, C4"


Write the number to a string using sprintf and then use your existing SendString function to send that over the UART. You can, of course, do this one number at a time:

char num_str[3];
sprintf( num_str, "%02X", 0xFF );
SendString(num_str);

%02X is a format string for the printf family of functions, it says pad the element with 0s until width 2 and format the element as a hexadecimal number.

The 02 part ensures that when you want to print 0x0F that you get 0F instead of just F in the output stream. If you use a lowercase x you'll get lowercase characters (e.g. ff instead of FF).


The classic trick from the 8-bit micro in assembly language era is to break the conversion of a nybble into two segments. The value from 0 to 9 and the value from 10 to 15. Then simple arithmetic saves the 16-byte lookup table.

void SendDigit(int c) {
    c &= 0x0f;
    c += (c <= 9) ? '0' : 'A'-10;
    SendU(c);
}

void SendArray(const unsigned char *msg, size_t len) {
    while (len--) {
        unsigned char c = *msg++;
        SendDigit(c>>4);
        SendDigit(c);
    }
}

A couple of side notes are in order. First, this works because the digits and letters are each in contiguous spans in ASCII. If you are unfortunate enough to want EBCDIC, this still works as the letters 'A' through 'F' are contiguous there as well (but the rest of the alphabet is broken into three spans).

Second, I've changed the signature of SendArray(). My version is careful to make the buffer be unsigned, which is generally safer when planning to promote the bytes to some larger integral type to do arithmetic. If they are signed, then code like nibble[(*msg)>>4] might try to use a negative index into the array and the result won't be at all useful.

Finally, I added a length parameter. For a general binary dump, you probably don't have any byte value that makes sense to use as an end sentinel. For that, a count is much more effective.

Edit: Fixed a bug: for digits over 10, the arithmetic is c + 'A' - 10, not c + 'A'. Thanks to Brooks Moses for catching that.


I'd start with an array lookup.

char *asciihex[] = {
    "00", "01", "02", ..., "0F",
    "10", "11", "12", ..., "1F",
    ...,
    "F0", "F1", "F2", ..., "FF"
};

and then simply look it up ...

SendString(asciihex[val]);

Edit

Incorporating Dysaster's nibbles idea:

void SendString(const char *msg) {
    static const char nibble[] = {'0', '1', '2', ..., 'F'};
    while (*msg) {
        /* cast to unsigned char before bit operations (thanks RBerteig) */
        SendU(nibble[(((unsigned char)*msg)&0xf0)>>4]); /* mask 4 top bits too, in case CHAR_BIT > 8 */
        SendU(nibble[((unsigned char)*msg)&0x0f]);
        msg++;
    }
}


sprintf will do it.

sprintf (dest, "%X", src);


If your compiler supports it, you can use itoa. Otherwise, I'd use sprintf as in Nathan's & Mark's answers. If itoa is supported and performance is an issue, try some testing to determine which is faster (past experience leads me to expect itoa to be faster, but YMMV).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜