2013-06-21 1 views
2

У меня есть приложение, в котором я получаю двоичный поток последовательных данных, и мне нужно разделить этот поток данных на отдельные переменные различных длина (uint16_t и uint32_t).Эффективный доступ к индивидуальным байтам в длинном C (на 8-битной платформе)

Прямо сейчас, я делаю ультра-простой:

#define OFFSET_iTOW 0 

volatile uint8_t temp[128]; 
volatile uint32_t recBytes; 

void main() 
{ 
    while (1) 
    { 
     recBytes = temp[OFFSET_iTOW+3]; 
     recBytes <<= 8; 
     recBytes |= temp[OFFSET_iTOW+2]; 
     recBytes <<= 8; 
     recBytes |= temp[OFFSET_iTOW+1]; 
     recBytes <<= 8; 
     recBytes |= temp[OFFSET_iTOW+0]; 

    } 
} 

(Данные передаются мало-Endian OFFSET_iTOW является одним из трех десятков смещений (определены в файле заголовка обычно) для. различные секции пакета данных)

Тем не менее, это приводит к довольно огромным выходом сборки (чуть усеченного раздел интереса):

void main() 
{ 

    recBytes = 0; 
12e: 10 92 04 02  sts 0x0204, r1 
132: 10 92 05 02  sts 0x0205, r1 
136: 10 92 06 02  sts 0x0206, r1 
13a: 10 92 07 02  sts 0x0207, r1 
    while (1) 
    { 



     recBytes = temp[OFFSET_iTOW+3]; 
13e: 80 91 03 02  lds r24, 0x0203 
142: 90 e0   ldi r25, 0x00 ; 0 
144: a0 e0   ldi r26, 0x00 ; 0 
146: b0 e0   ldi r27, 0x00 ; 0 
148: 80 93 04 02  sts 0x0204, r24 
14c: 90 93 05 02  sts 0x0205, r25 
150: a0 93 06 02  sts 0x0206, r26 
154: b0 93 07 02  sts 0x0207, r27 
     recBytes <<= 8; 
158: 80 91 04 02  lds r24, 0x0204 
15c: 90 91 05 02  lds r25, 0x0205 
160: a0 91 06 02  lds r26, 0x0206 
164: b0 91 07 02  lds r27, 0x0207 
168: ba 2f   mov r27, r26 
16a: a9 2f   mov r26, r25 
16c: 98 2f   mov r25, r24 
16e: 88 27   eor r24, r24 
170: 80 93 04 02  sts 0x0204, r24 
174: 90 93 05 02  sts 0x0205, r25 
178: a0 93 06 02  sts 0x0206, r26 
17c: b0 93 07 02  sts 0x0207, r27 
     recBytes |= temp[OFFSET_iTOW+2]; 
180: 20 91 04 02  lds r18, 0x0204 
184: 30 91 05 02  lds r19, 0x0205 
188: 40 91 06 02  lds r20, 0x0206 
18c: 50 91 07 02  lds r21, 0x0207 
190: 80 91 02 02  lds r24, 0x0202 
194: 90 e0   ldi r25, 0x00 ; 0 
196: a0 e0   ldi r26, 0x00 ; 0 
198: b0 e0   ldi r27, 0x00 ; 0 
19a: 82 2b   or r24, r18 
19c: 93 2b   or r25, r19 
19e: a4 2b   or r26, r20 
1a0: b5 2b   or r27, r21 
1a2: 80 93 04 02  sts 0x0204, r24 
1a6: 90 93 05 02  sts 0x0205, r25 
1aa: a0 93 06 02  sts 0x0206, r26 
1ae: b0 93 07 02  sts 0x0207, r27 
     recBytes <<= 8; 
1b2: 80 91 04 02  lds r24, 0x0204 
1b6: 90 91 05 02  lds r25, 0x0205 
1ba: a0 91 06 02  lds r26, 0x0206 
1be: b0 91 07 02  lds r27, 0x0207 
1c2: ba 2f   mov r27, r26 
1c4: a9 2f   mov r26, r25 
1c6: 98 2f   mov r25, r24 
1c8: 88 27   eor r24, r24 
1ca: 80 93 04 02  sts 0x0204, r24 
1ce: 90 93 05 02  sts 0x0205, r25 
1d2: a0 93 06 02  sts 0x0206, r26 
1d6: b0 93 07 02  sts 0x0207, r27 
     recBytes |= temp[OFFSET_iTOW+1]; 
1da: 20 91 04 02  lds r18, 0x0204 
1de: 30 91 05 02  lds r19, 0x0205 
1e2: 40 91 06 02  lds r20, 0x0206 
1e6: 50 91 07 02  lds r21, 0x0207 
1ea: 80 91 01 02  lds r24, 0x0201 
1ee: 90 e0   ldi r25, 0x00 ; 0 
1f0: a0 e0   ldi r26, 0x00 ; 0 
1f2: b0 e0   ldi r27, 0x00 ; 0 
1f4: 82 2b   or r24, r18 
1f6: 93 2b   or r25, r19 
1f8: a4 2b   or r26, r20 
1fa: b5 2b   or r27, r21 
1fc: 80 93 04 02  sts 0x0204, r24 
200: 90 93 05 02  sts 0x0205, r25 
204: a0 93 06 02  sts 0x0206, r26 
208: b0 93 07 02  sts 0x0207, r27 
     recBytes <<= 8; 
20c: 80 91 04 02  lds r24, 0x0204 
210: 90 91 05 02  lds r25, 0x0205 
214: a0 91 06 02  lds r26, 0x0206 
218: b0 91 07 02  lds r27, 0x0207 
21c: ba 2f   mov r27, r26 
21e: a9 2f   mov r26, r25 
220: 98 2f   mov r25, r24 
222: 88 27   eor r24, r24 
224: 80 93 04 02  sts 0x0204, r24 
228: 90 93 05 02  sts 0x0205, r25 
22c: a0 93 06 02  sts 0x0206, r26 
230: b0 93 07 02  sts 0x0207, r27 
     recBytes |= temp[OFFSET_iTOW+0]; 
234: 20 91 04 02  lds r18, 0x0204 
238: 30 91 05 02  lds r19, 0x0205 
23c: 40 91 06 02  lds r20, 0x0206 
240: 50 91 07 02  lds r21, 0x0207 
244: 80 91 00 02  lds r24, 0x0200 
248: 90 e0   ldi r25, 0x00 ; 0 
24a: a0 e0   ldi r26, 0x00 ; 0 
24c: b0 e0   ldi r27, 0x00 ; 0 
24e: 82 2b   or r24, r18 
250: 93 2b   or r25, r19 
252: a4 2b   or r26, r20 
254: b5 2b   or r27, r21 
256: 80 93 04 02  sts 0x0204, r24 
25a: 90 93 05 02  sts 0x0205, r25 
25e: a0 93 06 02  sts 0x0206, r26 
262: b0 93 07 02  sts 0x0207, r27 
266: 6b cf   rjmp .-298  ; 0x13e <loop+0x10> 

в этом является частью анализатора данных GPS в процедуре обслуживания прерываний, живущей на 8-битном микропроцессоре, работающем на частоте 16 МГц, и мне нужно сделать много этих преобразований, поэтому приведенный выше результат немного чрезмерен.

Поскольку это в ISR, я могу быть уверенным, что различные данные не будут меняться во время прерывания. В принципе, я хотел бы иметь возможность адресовать отдельные байты в длину. Поскольку это 8-битная архитектура, похоже, что компилятор должен иметь возможность оптимизировать до нескольких операций (возможно, 3-4 на строку C, поскольку байты в длине напрямую адресуются с точки зрения сборки).

Переменные объявлены volatile, поэтому они не оптимизированы до цикла, который ничего не делает. В фактическом приложении это extern ed structs, которые записываются из ISR, но считываются из цикла ожидания (с соответствующей защитой ISR для предотвращения прерывания чтения). Я не уверен, как создать компактный фрагмент, демонстрирующий это точное поведение.

+0

Прошу прощения, но на 8-битном микропроцессоре не просто загружать 32 бита данных в один регистр ... Компилятор уже хорошо выполняет свою работу. Если не оценивать, не беспокойтесь об эффективности. –

+0

@ H2CO3 - Я знаю, что это несколько регистров. У меня есть один массив символов и один длинный (это 4 8-битных регистра). Таким образом, операция копирования должна быть всего лишь 4 8-битными копиями. –

+0

Hm. Наложение указателя? UB, но работает: 'uint32_t foo = * (uint32_t *) addr;' –

ответ

3

Если вы используете союз, вы можете добраться до байтов частей длинного.

union Data 
{ 
    uint8_t bytes[4]; 
    uint32_t value; 
} recBytes; 

затем

recBytes.bytes[0] = temp[OFFSET_iTOW+3]; 
recBytes.bytes[1] = temp[OFFSET_iTOW+2]; 
recBytes.bytes[2] = temp[OFFSET_iTOW+1]; 
recBytes.bytes[3] = temp[OFFSET_iTOW]; 

тогда recBytes.value будет то, что вы хотите (хотя я не 100% об упорядочении байт, возможно, придется отменить его)

+0

Whoooo! Эндиантность весело! –

+0

да, поэтому он не переносится по разным Endiness, но это можно решить с помощью #define macro fun;) –

+0

Я все равно ориентируюсь на одну архитектуру. –

2

Ваш компилятор генерировать 32-разрядное хранилище для каждого входного байта и команды сдвига - это необходимо, поскольку recBytes нестабилен. Используйте временный, чтобы построить 32-битное значение, а затем сохранить его в летучее, или просто сделать все это в одном назначении:

#define OFFSET_iTOW 0 

volatile uint8_t temp[128]; 
volatile uint32_t recBytes; 

void main() 
{ 
    while (1) 
    { 
     recBytes = (uint32_t)temp[OFFSET_iTOW+3] << 24 | 
         (uint32_t)temp[OFFSET_iTOW+2] << 16 | 
         (uint32_t)temp[OFFSET_iTOW+1] << 8 | 
         (uint32_t)temp[OFFSET_iTOW+0]; 
    } 
} 
+0

Я действительно пробовал это, и он неправильно установил два верхних байта: 'ubxSol.ECEF_X \t = packetDat [OFFSET_ecefX + 0] | (packetDat [OFFSET_ecefX + 1] << 8) | (packetDat [OFFSET_ecefX + 2] << 16) | (packetDat [OFFSET_ecefX + 3] << 24); «Я понятия не имею, почему. –

+0

Hrrm, я идиот: элементы 'temp' -' uint8_t', и я слишком сильно их перемещаю - вам нужно перевести на 'uint32_t'. – Casey

+0

Исправление. Это значительно лучше. Это 43 операции ASM против ~ 92. Тем не менее, все еще кажется немного чрезмерным. –

1

Если temp буфер может быть заполнен в том же Endian порядке в качестве процессора, вы можете сформировать объединение 128 байт temp и 128/4 value. Не требуется движения.

#define N (128) 
union Data { 
    uint8_t temp[N]; 
    uint32_t value_u32[N/sizeof(uint32_t)]; 
    } recBytes; 

recBytes.value[OFFSET_iTOW/4]; 

[редактировать расширено для удовлетворения проблем Дополнительного Op в]

typedef struct { 
     uint32_t field1; 
     int32_t field2; 
     int16_t field3; // This and next 2 pack nicely into 4 bytes 
     uint8_t field4; 
     uint8_t field5; 
     int32_t field6; 
     int32_t field7; 
     int32_t field8; 
     uint32_t field9; 
     int32_t field10; 
     int32_t field11; 
     int32_t field12; 
     uint32_t field13; 
     uint16_t field14; // This and next 2 pack nicely into 4 bytes 
     uint8_t field15; 
     uint8_t field16; 
     uint32_t field17; 
} packet_t; 

union Data { 
    uint8_t temp[128]; 
    packet_t Packet; 
} recBytes; 

union может состоять из всех полой структуры пакета.После проверки контрольной суммы просто скопируйте структуру, а не поле за полем.

Working_var = recBytes.Packet; // or memcpy(&Working_var, &recBytes.Packet, sizeof(Working_var); 

Примечание: Ваш поставляется пакет определяет 52 байт.

+0

Это интересная идея, но проблема в том, что полученные данные неоднородны - это комбинация 'uint8_t',' uint16_t', 'uint32_t',' int16_t' и 'int32_t'. Можете ли вы создать союз, который является либо массивом символов, либо множеством различных типов в последовательности? –

+0

В частности, структура пакета: 'uint32_t',' int32_t', 'int16_t',' uint8_t', 'uint8_t',' int32_t', 'int32_t',' int32_t', 'uint32_t',' int32_t', 'int32_t ',' int32_t', 'uint32_t',' uint16_t', 'uint8_t',' uint8_t', 'uint32_t'. Go figure - я не реализовал его, мне просто нужно его разобрать. –

+0

Мне также нужно передать полученные данные в фактическую структуру, поскольку я фактически не копирую ее в рабочие переменные, пока не проведу проверку контрольной суммы. Я не думаю, что я могу уйти от по крайней мере * одной операции копирования за байт. –

Смежные вопросы