Как преобразовать 38-битный байтовый массив в десятичный ASCII - PullRequest
2 голосов
/ 17 января 2011

Я пишу подпрограмму и AVR ATMEGA88 для чтения меток FDX RFID с использованием микросхемы TI TM3705A и передачи их по UART на другой процессор.Этот чип использует 15625 бод, в то время как другой процессор получит данные со скоростью 19200 бод.

Идея состоит в том, чтобы прочитать входящие данные (38 бит идентификационного номера - например, 00 11 E3 D6 7C), проверить CRC изатем выведите его в виде понятного 12-значного десятичного числа (000300144252), представляющего уникальный идентификатор тега.

Пока у меня есть 38-битное число в массиве:

Фактическое число IИнтересуюсь сидит в элементах 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 # и другими ленивыми языками для такого рода вещей:) </p>

Есть ли способ преобразовать в десятичную ", как вы идете" - другими словами, читать изнаиболее значимая цифра (6) и выводит десятичные ASCII-цифры на UART, затем 5,4,3,2 без больших промежуточных буферов и тому подобное?Память на этих чипах немного ограничена.

Ответы [ 2 ]

0 голосов
/ 19 января 2011

После долгих поисков, проб и ошибок я нашел решение.

Я неверно истолковал ошибку, которую увидел, и Томас прав. Функция была слишком большой для чипа, когда она добавлялась к моим собственным функциям.

Очевидные решения никуда не делись, но я перечислю их здесь, чтобы помочь другим новичкам, когда они столкнутся с этой проблемой.

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 генерируется почти 8 тысяч кода!

Вот рабочая альтернатива. Я изменил его, чтобы использовать только 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)

намного лучше :) 1053 *

Я провел большую часть своего времени с Дугласом Джонсом , но ответ наконец пришел от AVR Freaks .

0 голосов
/ 17 января 2011

Преобразование в десятичное является вычислительно дорогим - независимо от того, делаете ли вы это самостоятельно или делегируете библиотечную функцию, такую ​​как 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 (или задыхается при попытке сделать деление), то вам придется выполнить эту операцию самостоятельно, что потребует немного математических знаний. В конечном итоге, эта статья может быть полезна - или нет.

...