Переменная uint64_t с операциями & - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть функция, которая ищет серии из девяти «1» в 64-битной переменной (номер метки RFID) и, если она найдена, перемещает их в MSB. У меня огромная проблема с пониманием, почему он не работает должным образом.

Это моя переменная

uint64_t volatile RFID_data2= 0b1111111110000000000000000000000000000000000000000000000000000000;

и я посылаю его как указатель на функцию

test_flag= header_align(&RFID_data2);


uint8_t header_align(uint64_t *data){

uint64_t bit_mask=(uint64_t) 
0b1111111110000000000000000000000000000000000000000000000000000000;


if((*data&bit_mask)==bit_mask){
PORTA ^= (1<<PORTA2);
return 1;
}

Это условие никогда не выполняется, но если я изменил условие на это:

if((*data==bit_mask){
PORTA ^= (1<<PORTA2);
return 1;
    }

похоже, что он работает хорошо.

Более того, я пишу еще одно условие - оно работает.

if((*data&bit_mask)>(bit_mask-1) && (*data&bit_mask)<(bit_mask+1 )){
    PORTA ^= (1<<PORTA2);
    return 1

Как я вижу, это проблема с операцией AND '&'. Кроме того, нет никаких проблем, когда я изменяю RFID_data на 32-битную переменную. Я работаю с Attiny441 и компилятором GCC, Atmel Studio Есть ли способ заставить его работать на 64 битах?

Я изменил функцию на uint64t (без указателя), но проблема все еще сохраняется. Я также пытался создать глобальный переменный и удалить изменчивый модификатор, но он все еще не работает должным образом. Использование макроса UINT64_C также не помогает. Похоже:

uint64_t RFID_data;// global var

int main(void)
{
  RFID_data=(0xFF80000000000000);// write into global var
 uint64_t RFID_data2=(0xFF80000000000000);
   while(1)
   {
    test_flag=header_align(RFID_data2);// send local variable
   }
}


 uint8_t header_align(uint64_t data){
  uint64_t bit_mask = UINT64_C(0xFF80000000000000);


    if((data&bit_mask)==bit_mask){
     PORTA ^= (1<<PORTA2);//nothink
        return 1;
    }

Я также пытался проверить условие-глобальным переменным:

     if((RFID_data&bit_mask)==bit_mask){
     PORTA ^= (1<<PORTA2);///nothink
        return 1;
    }

В обоих случаях он не возвращает 1 и не изменяет состояние PORTA2.

Это работает только когда я создаю новую локальную переменную в header_allgin, например:

 uint8_t header_align(uint64_t data){
  uint64_t RFID_data3 = UINT64_C(0xFF80000000000000);
  uint64_t bit_mask = UINT64_C(0xFF80000000000000);


    if((RFID_data3&bit_mask)==bit_mask){
     PORTA ^= (1<<PORTA2);// here i can see signal
        return 1;
    }}

Можно ли заставить его работать с помощью глобальной переменной или аргумента функции?

Ответы [ 3 ]

0 голосов
/ 30 апреля 2018

Просто используйте правильные суффиксы.

L для int32

LL для int64

UL для uint32

ULL для uint64

0 голосов
/ 02 мая 2018

Похоже, вы обнаружили здесь реальную ошибку компилятора!

Версия GCC:

avr-gcc (GCC) 4.8.1

(с включенной оптимизацией -O1 или -O2, при -O0 проблема, по-видимому, отсутствует)

Сокращенный контрольный пример:

#include <stdint.h>


uint8_t volatile tmp;


__attribute__((noinline)) void test_64(uint64_t d64)
{
 if ((d64 & 0xFF800000UL) == 0xFF800000UL){
  tmp ++;
 }
}

__attribute__((noinline)) void test_32(uint32_t d32)
{
 if ((d32 & 0xFF800000UL) == 0xFF800000UL){
  tmp ++;
 }
}


int main(void)
{
 test_64(0);
 test_32(0);

 while(1);
}

Выход ассемблера для критической части:

00000228 <test_64>:
 228:   08 95           ret

0000022a <test_32>:
 22a:   66 27           eor r22, r22
 22c:   77 27           eor r23, r23
 22e:   80 78           andi    r24, 0x80   ; 128
 230:   61 15           cp  r22, r1
 232:   71 05           cpc r23, r1
 234:   80 48           sbci    r24, 0x80   ; 128
 236:   9f 4f           sbci    r25, 0xFF   ; 255
 238:   09 f0           breq    .+2         ; 0x23c <test_32+0x12>
 23a:   08 95           ret
 23c:   80 91 00 20     lds r24, 0x2000
 240:   8f 5f           subi    r24, 0xFF   ; 255
 242:   80 93 00 20     sts 0x2000, r24
 246:   08 95           ret

00000248 <main>:
 248:   20 e0           ldi r18, 0x00   ; 0
 24a:   30 e0           ldi r19, 0x00   ; 0
 24c:   40 e0           ldi r20, 0x00   ; 0
 24e:   50 e0           ldi r21, 0x00   ; 0
 250:   60 e0           ldi r22, 0x00   ; 0
 252:   70 e0           ldi r23, 0x00   ; 0
 254:   80 e0           ldi r24, 0x00   ; 0
 256:   90 e0           ldi r25, 0x00   ; 0
 258:   0e 94 14 01     call    0x228   ; 0x228 <test_64>
 25c:   60 e0           ldi r22, 0x00   ; 0
 25e:   70 e0           ldi r23, 0x00   ; 0
 260:   cb 01           movw    r24, r22
 262:   0e 94 15 01     call    0x22a   ; 0x22a <test_32>
 266:   ff cf           rjmp    .-2         ; 0x266 <main+0x1e>

1024 * Наблюдение *

Для 32 битов генерируется правильный код. Для 64 битов сравнение не выполняется вообще, код компилируется, как будто результат if всегда был ложным. Собственный GCC правильно компилирует обе функции.

Возможно, вам следует избегать использования 64-битных переменных в вашем коде.

Ошибка теперь подтверждена на багтрекере GCC, вы можете следить за ней здесь:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85805

Таким образом, на момент написания статьи все достаточно современные версии avr-gcc были затронуты, пока это не будет исправлено.

0 голосов
/ 30 апреля 2018

Преобразование константы в uint64_t не является правильным способом сделать буквальным uint64_t. Вы должны использовать макрос UINT64_C вместо:

uint64_t bit_mask = UINT64_C(0b1111111110000000000000000000000000000000000000000000000000000000);

или если вы предпочитаете гекс

uint64_t bit_mask = UINT64_C(0xFF80000000000000);
...