【问题标题】:How to convert 38bit Byte Array to ASCII Decimal如何将 38 位字节数组转换为 ASCII 十进制
【发布时间】:2011-01-17 15:05:14
【问题描述】:

我正在编写一个例程和一个 AVR ATMEGA88,以使用 TI TM3705A 芯片读取 FDX RFID 标签,并通过 UART 将其传输到另一个处理器。该芯片使用 15625 波特,而其他处理器将以 19200 波特接收数据。

这个想法是读取传入的数据(ID 号的 38 位 - 例如 00 11 E3 D6 7C),对其进行 CRC 校验,然后将其输出为一个友好的 12 位十进制数(000300144252),它表示唯一的 ID标记。

到目前为止,我在一个数组中有这个 38 位数字:

我感兴趣的实际数字位于元素 2:6 中。没有的 2 个 MSB。 6 应该被忽略,因为它们是下一个数据块的开始。

i   Dec Hex Bin
0   80  50  01010000
1   126 7E  01111110
2   124 7C  01111100
3   214 D6  11010110
4   227 E3  11100011
5   17  11  00010001
6   192 C0  11000000
7   237 ED  11101101
8   0   00  00000000
9   128 80  10000000
10  97  61  01100001
11  103 67  01100111
12  126 7E  01111110
13  0   00  00000000
14  0   00  00000000

我正在寻找一种将数组中的字节输出为十进制“000300144252”的有效方法。

我尝试将它打包成 long long 类型,然后使用 sprintf %d 但它似乎在 temp = data

有没有办法“随时”转换为十进制 - 换句话说,从最高有效位 (6) 读取并在 UART 上输出十进制 ASCII 数字,然后是 5、4、3、2 没有大中间缓冲区之类的?这些芯片的内存有点受限。

【问题讨论】:

    标签: arrays gcc decimal avr


    【解决方案1】:

    转换为十进制的计算成本很高——无论您自己进行还是将其委托给诸如sprintf() 之类的库函数都不会改变它。在这里更复杂的是它是一个相对较大的值:38 位,比 32 位大。

    对于sprintf(),使用"%lld" 打印long long。如果编译器支持long long类型,而sprintf()不支持,那么你可以手动完成:

    static void
    convert_to_decimal(char[] dst, unsigned long long src)
    {
        int i;
    
        for (i = 0; i < 12; i ++) {
            dst[11 - i] = '0' + (int)(src % 10);
            src /= 10;
        }
        dst[12] = 0;
    }
    

    此函数将 12 字符的结果写入 dst[](带有终止 NUL)。请注意,这意味着除以 10,编译器会将其转换为包含一个相对复杂的函数。

    如果您的编译器不支持 long long 类型(或在尝试进行除法时窒息),那么您将不得不自己实现该操作,这需要一些数学知识。最终,this article 可能有用——也可能没有。

    【讨论】:

    • 非常优雅的解决方案 - 谢谢!但是它不适用于我的编译器(WinAVR - GCC) - 它确实支持 long long。我收到此错误:c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/bin/ld.exe: 区域文本溢出通过 1214 字节,我与另一个 sprintf 端口相同。我认为正在发生的事情是编译器可以看到 dst[11 - i] 可以超出设备物理内存的范围。我不知道如何说服它相信我们这个:)
    • “区域文本溢出”意味着总编译代码大小超过了设备上可以容纳的最大代码大小——至少就链接器所知的这种大小而言。 'long long' 类型的除法代码可能很长……因为您只需要除以常数 10,因此可能可以实现更短的优化版本,但需要一些工作。
    【解决方案2】:

    经过大量搜索和反复试验,我找到了解决方案。

    我误解了我看到的错误,Thomas 是对的。添加到我自己的功能时,该功能对于芯片来说太大了。

    明显的选择没有去任何地方,但我会在这里列出它们以帮助其他新手遇到这个问题。

    itoa() - 16 位和 ultoa() - 32 位已实现,但太小了。

    sprintf(%d) 太小,sprintf(%lld) 未在 WinAVR (AVR-GCC) 中实现。

    此代码有效(有警告):

    void main()
    {
        unsigned long long tagid;
        char tagid_str[12];
    
        tagid = 109876543210ull
    
        convert_to_decimal(tagid_str, tagid);
    }
    
    void convert_to_decimal(char* dst, unsigned long long src) 
    {     
        int i;
        for (i = 0; i < 12; i ++) 
        {         
            dst[11 - i] = '0' + (int)(src % 10);
            src /= 10;     
        }     
    
        dst[12] = 0; 
    } 
    

    但是看看统计数据:

    程序:7358 字节(89.8% 已满) (.text + .data + .bootloader)

    数据:256 字节(25.0% 已满) (.data + .bss + .noinit)

    罪魁祸首是 % 运算符。我无法解释为什么使用 long long 会生成近 8k 的代码!

    这是一个可行的选择。我将其修改为仅使用 unsigned long long(64 位)最多 12 个十进制数字以适应我正在使用的 RFID 阅读器格式。

    void main()
    {
        unsigned long long tagid;
        char tagid_str[12];
    
        tagid = 000000000000ull;
        ulltostr((unsigned long long)tagid, tagid_str);
    
        tagid = 000000000001ull;
        ulltostr((unsigned long long)tagid, tagid_str);
    
        tagid = 109876543210ull;
        ulltostr((unsigned long long)tagid, tagid_str);
    
        tagid = 900000000000ull;
        ulltostr((unsigned long long)tagid, tagid_str);
    
        tagid = 999999999999ull;
        ulltostr((unsigned long long)tagid, tagid_str);
    }
    
    //http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=31199
    
    void ulltostr(unsigned long long val, char *s ) 
    { 
      char *p; 
      unsigned char d, i; 
      unsigned char zero; 
      unsigned long long test; 
      unsigned long long uval = val; 
    
      p = s; 
    
      zero = 1; 
    
      i = 12; 
      do{ 
        i--;    
        if ( i==0)   test =10; 
        else if ( i==1) test =100; 
        else if ( i==2) test =1000; 
        else if ( i==3) test =10000; 
        else if ( i==4) test =100000; 
        else if ( i==5) test =1000000; 
        else if ( i==6) test =10000000; 
        else if ( i==7) test =100000000; 
        else if ( i==8) test =1000000000; 
        else if ( i==9) test =10000000000; 
        else if ( i==10) test=100000000000; 
        else if ( i==11) test=1000000000000; 
        else if ( i==12) test=10000000000000; 
    
        for( d = '0'; uval >= test; uval -= test ) 
        { 
          d++; 
          zero = 0; 
        } 
        if( zero == 0 ) 
          *p++ = d ; 
      }while( i ); 
    
      *p++ = (unsigned char)uval + '0'; 
    }
    

    还有统计数据:

    程序:758 字节(9.3% 已满) (.text + .data + .bootloader)

    数据:0 字节(0.0% 已满) (.data + .bss + .noinit)

    好多了 :)

    我大部分时间都在Douglas Jones上度过,但答案终于来自AVR Freaks

    【讨论】:

    • (不过看起来最多 13 位。)
    猜你喜欢
    • 1970-01-01
    • 2013-05-22
    • 1970-01-01
    • 1970-01-01
    • 2018-05-04
    • 2010-12-03
    • 2022-01-09
    • 2022-01-09
    • 2014-10-10
    相关资源
    最近更新 更多