2

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

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

到目前为止,我在一个数组中有这个 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 << 32 上窒息。我不知道 sprintf 是否会处理这个大小数字。我承认我已经被 C# 和其他懒惰的语言宠坏了:)

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

4

2 回答 2

0

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

我误解了我看到的错误,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

于 2011-01-19T20:47:38.597 回答
0

转换为十进制在计算上是昂贵的——无论您是自己进行还是将其委托给诸如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类型(或在尝试进行除法时窒息),那么您将不得不自己实现该操作,这需要一些数学知识。最终,这篇文章可能有用——也可能没有。

于 2011-01-17T19:00:28.180 回答