【问题标题】:How to send hex string to Serial Port?如何将十六进制字符串发送到串口?
【发布时间】:2019-01-05 09:01:50
【问题描述】:

我正在开发一个串行通信程序,用于在主机 PC(Linux)和微控制器之间发送和接收数据。现在我必须向 MCU 发送十六进制字节的数据。 那么如何在不使用 sprint/printf 的情况下将缓冲区字符串数据转换为十六进制字符串,以便用户将“AA12CDFF1000001201”而不是“\xAA\x12\xCD\xFF\x10\x00\x00\x12\x01”之类的字符串发送到 MCU。我也看到了Sending Hexadecimal to Serial Port,但它没有帮助,因为它说我可以使用 sprintf 将字符串转换为十六进制,但在我的情况下,我不想使用 sprintf。 基本上我想要的是: 如果用户输入 = "AA12CDFF1000001201" 我必须将其转换为以下格式 "\xAA\x12\xCD\xFF\x10\x00\x00\x12\x01" 然后将其写入串口。

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
void main()
{
int fd;
fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
char buff[]="\xAA\x12\xCD\xFF\x10\x00\x00\x12\x01";
write(fd, buff, sizeof(buff));
close(fd);
}    

【问题讨论】:

  • * 在我的情况下,由于某种原因我不想使用 sprintf。* 原因是什么?为什么还要使用18 bytes 长字符串发送9 bytes?
  • 我正在发送 9 字节的十六进制数据 = "\xAA\x12\xCD\xFF\x10\x00\x00\x12\x01" 这对于用户来说输入很乏味,所以我必须做到对用户来说很简单,所以有人只需键入“AA12CDFF1000001201”,就像普通串行终端应用程序一样,我们用户只需键入普通字符串。@tilz0R
  • 所以你的问题一开始就失败了。您想将字符串转换为字节,因为您根本不需要 sprintf。甚至不可能做到。
  • 好的,我改了。@tilz0R
  • 我在下面给了你答案。

标签: c linux serial-port


【解决方案1】:

经过与 Hessam 和 Arkku 的充分讨论,新版本的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* str2hexstr(char* inbuff) {

    char *outbuff;

    if (strlen(inbuff)%2) {
        outbuff=malloc((strlen(inbuff)*2+3)*sizeof(char));
        outbuff[strlen(inbuff)*2+2]='\0'; }
    else {
        outbuff=malloc(strlen(inbuff)*2+1*sizeof(char));
        outbuff[strlen(inbuff)*2]='\0'; }

    for (int strin=0,strout=0;strin<strlen(inbuff);) {
        outbuff[strout++]='\\';
        outbuff[strout++]='x';
        outbuff[strout++]=(!strin&&strlen(inbuff)%2)?'0':inbuff[strin++];
        outbuff[strout++]=inbuff[strin++];
    }
    return(outbuff);
}
int main()
{
    char inbuff1[]="123",inbuff2[]="1234";
    char *outbuff;

    outbuff=str2hexstr(inbuff1);
    printf("%s\n",outbuff);
    free(outbuff);
    outbuff=str2hexstr(inbuff2);
    printf("%s\n",outbuff);
    free(outbuff);
    return 0;
}

【讨论】:

  • 当我给出输入字符串:“12345678901”时,我在输出字符串末尾收到不需要的字符:\x01\x23\x45\x67\x89\x01。@Stef1611
  • 对于每 11 个字符长的输入字符串,它会在输出字符串的末尾显示不需要的字符 �。 @Stef1611
  • @raj123 :您需要进一步澄清您的问题。这会直接输出"\x01\x23\x45...";这似乎不是问题中所要求的。 \01 是一个字符常量,我希望所需的输出是位模式为 00000001 的单个字符,而不是 四个 个字符。
  • @Clifford 的问题是关于将十六进制字符串“\x01\xAA”发送到串口。
  • @raj123:足够公平(虽然不寻常),但您的说明需要添加到问题中,而不是在评论中。您问题中的代码片段具有误导性,因为这不是 buff[] 包含的内容。
【解决方案2】:

从字符串转换为字节

#include <stdio.h>
#include <string.h>
#include <stdint.h>

/* Dummy input */
char input[] = "11121314151617181920";
uint8_t output[32];

/* Convert input char to int */
uint8_t
char2byte(char c) {
    if (c >= '0' && c <= '9') {
        return c - '0';
    } else if (c >= 'A' && c <= 'F') {
        return c - 'A' + 10;
    } else if (c >= 'a' && c <= 'f') {
        return c - 'a' + 10;
    }
    return 0;
}

int
main() {
    size_t len;

    /* String must be even */
    len = strlen(input);
    if (len & 1 == 0) {

    }
    /* Process all inputs, 2 chars for single byte */
    for (size_t i = 0; i < len / 2; ++i) {
        output[i] = char2byte(input[2 * i]) << 4;
        output[i] |= char2byte(input[2 * i + 1]);
    }

    /* Test, expected is 0x13 */
    printf("Test output[2] = %02X\r\n", (unsigned)output[2]);

    return 0;
}

如果你运行代码:Test output[2] = 13

从字节转换为字符串

类似的东西。目标是将每个字节的每个半字节转换为字符。这意味着输出长度是初始缓冲区大小的两倍。

#include <stdio.h>
#include <string.h>
#include <stdint.h>

/* Output array */
char output[128];

/* Dummy input data array */
char data[] = {0x12, 0x13, 0x14, 0x00, 0x5A};

/* Convert each nibble to char */
char num2char(uint8_t num) {
    if (num < 10) {
        return num + '0';
    } else if (num < 16) {
        return num - 10 + 'A';
    }
}

int
main() {
    size_t i;
    printf("Hello World\r\n");

    /* Process all bytes */
    for (i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
        /* Generate it twice, for high and low nibble */
        output[2 * i] = num2char((data[i] >> 4) & 0x0F);
        output[2 * i + 1] = num2char((data[i]) & 0x0F);
    }
    /* Trailing zero for string */
    output[2 * i] = 0;

    printf("Output: %s\r\n", output);

    return 0;
}

当你运行代码时:

Output: 121314005A  

【讨论】:

  • 您的字符串到字节不是我的代码中需要的。我必须将此命令写入串行端口“\xAA\x12\xCD\xFF\x10\x00\x00\x12\x01”而不是我需要像“AA12CDFF1000001201”这样的用户友好输入
  • 那你的问题已经成熟了,可以结束了。
【解决方案3】:

你想要这样的东西吗:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char inbuff[]="AA12CDFF1000001201";
    char *outbuff;

    if (strlen(inbuff)%2) {
            printf("error : strlen is odd");
            exit(1);}
    outbuff=malloc((strlen(inbuff)*2+1)*sizeof(char));
    for (int strin=0,strout=0;strin<strlen(inbuff);) {
        outbuff[strout++]='\\';
        outbuff[strout++]='x';
        outbuff[strout++]=inbuff[strin++];
        outbuff[strout++]=inbuff[strin++];
    }
    outbuff[strlen(inbuff)*2]='\0';
    printf("%s\n",outbuff);
    return 0;
}

【讨论】:

  • 为什么奇数字符串长度会有问题?
  • @hessamhedieh 如果每个字节应该由两个十六进制数字表示,奇数长度表示情况并非如此,并且不可能知道哪个字节缺少 nybble。 (您的回答假设它是最后一个,但如果这是用户输入的,他们可能在任何地方都犯了错误。)
  • 我认为字符数必须是偶数,因为 1 个字节是两个十六进制数字。偶数个字符,不知道在哪里多加一个0。在inbuff的开头还是结尾?也许,@raj123 可以精确。在您的代码中,使用偶数个字符,您将获得 \xAA\x1F\xE (例如)。 0E 或 E0 是什么意思?
  • @Arkku,当然你是对的,但是当user 发挥作用时,事情就变得连线了,如果用户只想将数字 5 发送到董事会,根据我的经验强迫他们写05 将导致用户抱怨应用程序无法运行或用户不友好的许多问题。
  • @Stef1611,如果您有任何导致我的代码失败的测试,请随时提供,但是 AFAICT,我的代码可以毫无问题地处理偶数和奇数场景。
【解决方案4】:

看看这个,不是一个干净的代码,但应该可以完成这项工作

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void hexify(const char *input, char *output)
{
    int i = 0;
    int o = 0;
    while(1)
    {
        if(input[i] == 0) break; 
        output[o++] = '\\';
        output[o++] = 'x';
        output[o++] = input[i++];
        if(input[i] == 0) break; 
        output[o++] = input[i++];
    }
    output[o] = 0;
}

 int main()
 {
     char test[]="112233AA"; //user input
     if((strlen(test) % 2) && strlen(test) != 1)
         printf("Input is not in correct format");
     else
     {
         char *out = malloc(2 * strlen(test) + 1);
         hexify(test, out);
         printf("%s", out);
         free(out);//dont forget this
     }
     return 0;
 }

【讨论】:

    【解决方案5】:

    如何将我的缓冲区字符串数据转换为十六进制字符串....所以用户发送...?

    give input = "AA12CDFF1000001201" 我必须将其转换为以下格式 "\xAA\x12\xCD\xFF\x10\x00\x00\x12\x01" 然后将其写入串口。

    当不需要缓冲区的中间存储时,这很简单。

    一次将原始字符串的2个字符1转换为一个字节,每次迭代写入每个字节。

    #include <ctype.h>
    
    void write_hex_string(const char *hs) {
      while (isxdigit(hs[0]) && isxdigit(hs[1])) {
        char buf[3] = { hs[0], hs[1], '\0' };
        unsigned char value = (unsigned char) strtol(buf, 0, 16);
        write(fd, &value, sizeof value);
        hs += 2;
      }
    }
    

    示例使用

    write_hex_string("AA12CDFF1000001201");
    

    如果代码可以将原始 字符串 作为流程的一部分进行更改,那么生活也很容易。因为有一个方便的位置来存储转换。

    void write_hex_string(char *hs) {
      char *pair  = hs;
      size_t length = 0;
      while (isxdigit(pair[0]) && isxdigit(pair[1])) {
        char buf[3] = { pair[0], pair[1], '\0' };
        hs[length++]  = (unsigned char) strtol(buf, 0, 16);
        pair += 2;
      }
      write(fd, hs, length);
    }
    

    1只要2个字符都是十六进制数字:['0'-'9', 'A'-'F', 'a'-'f']。

    【讨论】:

      猜你喜欢
      • 2015-07-19
      • 1970-01-01
      • 2019-02-21
      • 1970-01-01
      • 2023-03-19
      • 2013-08-08
      • 2021-01-28
      • 1970-01-01
      • 2010-10-04
      相关资源
      最近更新 更多