Подтвердить что ты не робот

Как найти следующий бит для изменения в коде Grey в постоянное время?

У меня есть небольшой 8-разрядный процессор, который имеет декодер N-to-M на некоторых выходных линиях - например, для случая с 5 по 32 бит, я записываю 00101 и состояние бит 5 изменяется. Единственным интерфейсом к выходу является состояние изменения, отсутствует обратная связь.

Устройство быстро подсчитывает (но случайно) происходящие события и должно предоставлять этот счет как код "однократные изменения" на другое устройство. Выходные штырьки считываются параллельно другим устройством и могут считываться так быстро или как экономно, как решает другое устройство, поэтому счет необходим.

Мне не нужно использовать стандартный код Binary Reflective Grey - я могу использовать любой однобитовый код изменения.

Однако я хочу иметь возможность отслеживать следующий бит для эффективного изменения.

У меня нет инструкции "LowestBitSet", и поиск младшего бита, установленного в четырех 8-битных регистрах, является потребляемым циклом - поэтому я не могу использовать этот "общий" подход:

  Keep binary counter A
  Find B as A XOR (A+1)
  Bit to change is LowestBitSet in B 

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

Любые предложения по алгоритмам?

4b9b3361

Ответ 1

Вам не нужно вычислять коды Gray и xor, вы можете просто использовать сам счетчик, а затем использовать 256-элементную таблицу поиска, чтобы подсчитать количество завершающих нулей. Вот так:

unsigned char bit_change(unsigned char counter[4]) {
  static const unsigned char ones[] = {
    0,0,0,1,0,1,1,2,0,1,1,2,1,2,2,3,0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
    0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
    0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
    1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
    0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
    1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
    1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
    2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,    
  };

  unsigned char i;
  for (i = 0; i < 4; i++) {
    unsigned char x = counter[i];
    if (x) {
      x ^= x - 1;
      return 8 * i + ones[x];
    }
  }
}

Если вы разворачиваете цикл, это не более 2 добавлений, 1 xors и 5 загрузок (но почти всегда меньше). Если у вас нет 256 байт для таблицы, вы можете использовать ту же стратегию на nibbles.

Ответ 3

Для двоичного рефлексивного серого кода см. этот ответ для эффективного способа вычисления кода N.
XOR с предыдущим кодом, чтобы получить значение, где установлен только бит для изменения.
Затем вы можете использовать этот бит Twiddling Hack (случай, когда "v - мощность 2" ), чтобы найти бит-индекс только с тремя операциями и таблицу с 32 входами.

Псевдокод выглядит примерно так:

  n = lastCode = 0
  increment:
     n+=1
     newCode = GrayCode(n)
     delta = newCode XOR oldCode
     bitToToggle = BitIndex(delta)
     old code = new code
     GOTO increment;

Ответ 4

LowestBitSet(A ^ (A+1)) всегда 0, если вы не работаете в IBM. Я думаю, вы имеете в виду HighestBitSet(), что примерно совпадает с log_2().

Сжатие бит сразу предшествует, тот, который был связан AShelly, будет намного более возможен на 8-битном микро.

Это должно сделать ваш оригинальный алгоритм достаточно практичным, создавая { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, ... }.

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

Ответ 5

Алгоритм, установленный OP, не генерирует никакого кода Grey.

Алгоритм в этом ответе: fooobar.com/questions/428822/... - это не постоянное время, так как условный тест if (x) может варьироваться от 1 до 4 исполнений в зависимости от значения counter[i]. Это изменяет время, необходимое для вычисления номеров бит. Будет 4 разных возможных времени выполнения, которые могут иметь один расчет.

См. (исключая коммиворатели груза) ссылку на обоснование следующего: это постоянное требование времени (нет огней, нет автомобиля, ни одной роскоши... даже не "таблица" ):

byte log2toN(byte b){ 
   return 7 - (byte) ( 0x10310200A0018380uLL >> ( (byte)(0x1D*b) >>2 ) ) ; }  
byte BRGC(byte n){ return n ^ n>>1; }
byte bit2flip(byte n){ return log2toN( BRGC(n) ^ BRGC(n+1) ); }

Тем не менее, существует намного лучший, более сукцинтный и целесообразный метод для соответствия критериям ОП.

Для целей культового кодирования грузов следующее удобно удовлетворяет условиям ОП минимально (максимально?).

Число бит для изменения определяется каждый раз только с помощью двух: модуль (что, если выполнено modulo 2^n, может быть таким же простым, как бит-мудрая операция & с битами n-1 то есть. константа 2^n - 1) и приращение.

Фактическая последовательность кода Джонсона Грея (JGC) генерируется постепенно с помощью XOR предыдущего кода с желаемым битом, выбранным сдвигом слева от 1 до позиции номера бита. Этот расчет НЕ необходим в соответствии с требованиями OP.

Кодекс Джонсона
-------------------------

Фактическое серое кодирование не имеет значения, поэтому использование кода Gray Gray от Johnson исключительно тривиально.

Обратите внимание, что плотность кода Johnson Gray (JGC) является линейной и не логарифмической, как основание 2 или двоичный отраженный серый код (BRGC).

С 32 битами в 4 байта последовательность может отсчитываться от 0 до 63 до сброса.

/.//////////////////////////////////// ///////////////////////////////////////.//strong >

byte  bitCtr=-1;               //   for 4 x 8 bits use 32 instead of 5
int JohnsonCode(){ static byte GCbits = 0; 
                       return  GCbits ^=  1u << ( bitCtr = ++bitCtr %5 ); }

/.//////////////////////////////////// ///////////////////////////////////////.//strong >

Тестируемый выход:

  Johnson counter   Bit
     Gray code:   Flipped:
       ....1         0
       ...11         1
       ..111         2
       .1111         3
       11111         4
       1111.         0
       111..         1
       11...         2
       1....         3
       .....         4
       ....1         0
       ...11         1   etc.   

частично сгенерированный с помощью этого кода:

void testJohnson(){           
   Serial.println("\n\tJohnson counter\t   Bit\n\t   Gray code:\t Flipped:");

   for( int intifiny=31; --intifiny;  )
      Serial.println( "\t     " + cut( 5, "....." +  

// s.replace(...) returns zip, 
//                    so VVV use lambda function      invoke via VVV      
//  _____________________ V ____________________   ______________ V _____________ 
   [](String  s){ s.replace("0","."); return s; } ( String( JohnsonCode(), BIN ) )

         ) + "\t    " + bitCtr
      );
}  

/*

К сожалению, этот ответ не объясняет: "Как вы (я, я,...) находил...".

Подробнее о методологии нахождения таких решений и использовании BRGC аналогично...
см. предыдущий номер ссылки: fooobar.com/questions/428822/...

Ответ 6

/*
Как ранее сообщал этот ответ, fooobar.com/questions/428822/... с использованием кода Gray Gray от Johnson, очень просто:

Number_of_Bit_To_Flip = ++Number_of_Bit_To_Flip % Total_Number_of_Counter_Bits

который выполняется при каждом возникновении события.

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

Метод 1 - используя таблицу */

static const byte twos[ ] = { //  note pattern    V       V       V V V V
  0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,  6,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,    7,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,  6,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      8,
};

//  operation count worst case    3 logic   4 index     1 addition
//            for 7/8 of calls    2         3           1

byte bit_change(byte ctr[4]) {
 return  
  (byte[]){ 
    (byte[]){  16 + twos[ctr[2]]               ,
      (byte[]){24 + twos[ctr[3]] ,
               31                } [ !ctr[3] ] } [ !ctr[2] ] ,
    (byte[]){   0 + twos[ctr[0]]               ,
                8 + twos[ctr[1]]               } [ !ctr[0] ] }
                                                         [ctr[0] || ctr[1]];

// --------  NB. orphaned, included for pedagogic purposes  --------

  return  (byte[]){ 0 + twos[ctr[0]] ,   //    this IS totally time constant
                    8 + twos[ctr[1]] ,   // NB. 31 + time "constantator" 
                   16 + twos[ctr[2]] ,   // case ctr[i]==0 for all i
                   24 + twos[ctr[3]] ,
                   31 + twos[ctr[0]] } [ !ctr[0]*( 1+ 
                                         !ctr[1]*( 1+
                                         !ctr[2]*( 1+
                                         !ctr[3]       ) ) ) ];  
 }

/ Метод 2 - нет таблиц */

byte bin2toN(byte b){  
   return 
      (byte []) {(byte []) {(byte []) {7,6} [b < 128 ] , 
                            (byte []) {5,4} [b <  32 ] } [b < 64 ] ,
                 (byte []) {(byte []) {3,2} [b <   8 ] , 
                            (byte []) {1,0} [b <   2 ] } [b <  4 ] } [b < 16 ] ;
} 

byte flip_bit(byte n[4]){  
return    
  (byte []){ 
    (byte []){   16 + bin2toN(  n[2] & -n[2] )            ,
      (byte []){ 24 + bin2toN(  n[3] & -n[3] ),
                 31                           } [ !n[3] ] } [ !n[2] ] ,
    (byte []){    0 + bin2toN(  n[0] & -n[0] )            ,
                  8 + bin2toN(  n[1] & -n[1] )            } [ !n[0] ] } 
                                                               [ n[0] || n[1] ] ;

 // - - - - - - - - - - - - ORPHANED, fails on zero - - - - - - - - - - - -

  return                             //  included for pedagogic purposes
    (byte []) {                         
      (byte []) { bin2toN(  n[2] & -n[2] ) + 16 ,
                  bin2toN(  n[3] & -n[3] ) + 24 } [ !n[2] ] ,
      (byte []) { bin2toN(  n[0] & -n[0] ) +  0 ,
                  bin2toN(  n[1] & -n[1] ) +  8 } [ !n[0] ] } [ n[0] || n[1] ] ;
}

/*
Бит Bunnies и Cargo Cult Coders не нужно читать дальше.

Эффективность выполнения вышеуказанного кода зависит от того, что n[0], n[1], ... вычисляются во время компиляции как фиксированные адреса, которые довольно условны. Кроме того, использование компилятора оптимизации по требованию будет ускорять содержимое массива поэтому нужно вычислить только одно индексированное значение. Эта сложность компилятора, скорее всего, отсутствует, но ее легко собрать вручную исходный машинный код для этого (в основном коммутатор, вычисленный goto и т.д.).

Анализ вышеуказанных алгоритмов с использованием осиротевшего кода показывает, что каждая функция вызов будет выполнять точно такую ​​же последовательность команд, оптимизирован или нет.

В обоих методах не-осиротевшие возвраты требуют обработки случая, когда счетчик опрокидывается на 0, следовательно, используя дополнительный индекс и логический (!). Это дополнительно происходит для 1/2 1/2 1/2 или 1/8 от общего количества, и для одного счета в этом 1/8 ничего не остается, кроме как вернуть 31.

Первый метод требует 2 логических операций (! ||), 1 дополнение и 3 индекса расчеты для 7/8 от общего количества. При одном отсчете нуля, 3 логических и Необходимы 3 операции индекса, а остальные остальные 1/8 нуждаются в 3 логических, 1 дополнение и 4 операции индекса.

Окончательный код при выполнении метода 2 (скомпилирован оптимально), для 7/8 вычислений, использует 7 логических операций (|| & ! < - последнее представляет собой два дополнения), 1 арифметика (+) и 5 ​​расчетных операций индекса. Другой 1/8, но один экземпляр, требуется 8 логических, 1 сложение и 6 вычисленных операций индекса.

К сожалению, ни одна вспышка божественного вдохновения не проявила никакого кода груза. Это сокращенная история , как это авторство композиции.

Как это было сделано, было связано с критическим предварительным расследованием, которое было задокументировано: fooobar.com/questions/428822/.... Затем код был получен с использованием процесса последовательной обработки, начинающегося с оценки алгоритмов в этом сообщении.
В частности, этот ответ: fooobar.com/questions/428822/....

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

*/

byte bit_change(byte ctr[4]) {
  static byte ones[256]; //  this sparse RA is precomputed once
    for (byte i = 255; --i; ones[i]=0) ;
    ones[ 0] =    ones[ 1] = 0; ones[ 3] = 1; ones[  7] = 2; 
    ones[15] = 3; ones[31] = 4; ones[63] = 5; ones[127] = 6; ones[255] = 7;

//   { ie. this very sparse array is completely adequate for original code
//    0,0, ,1, , , ,2, , , , , , , ,3, , , , , , , , , , , , , , , ,4,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,5,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,6,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
//     , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,7,  }

//  1/2 of count uses 2 index 2 conditionals 0 increment 1 logic 2 +/-  1 x
//  1/4               3       4              1           1       2      1
//  1/8               4       6              2           1       2      1
//  1/16              5       8              3           1       2      1
//     average  14 = 3.5      5             1.5          1       2      1  

unsigned char i;  for (i = 0; i < 4; i++) {         //  original code
                    unsigned char x = counter[i];   //  "works" but
                         if (x) {                   //  still fails on 
                           x ^= x - 1;              //  count 0 rollover
                           return 8 * i + ones[x];
                   }     }

//  .............................  refinement .............................
byte x;             for (byte i = 0; i < 4; i++)         //
                         if (x = counter[i]) 
                              return  i<<3 + ones[x ^ x - 1];
}

/ ------------------------------------------- ------------------- --------------------------------/

//              for (byte i = 255; --i; twos[i] == ones[i ^ i-1] ) ;
// ones[ ] uses only 9 of 1/4K inefficient,  twos[ ] uses all 1/4K

static const byte twos[ ] = {  
 0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,  6,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,    7,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,  6,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
   0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      8,
};


//  fix count 0 rollover failure, make time absolutely constant as per OP

byte f0(byte ctr[4]) {
  byte ansr=31;
  for (byte i = 0; i < 4; i++) 
   if (ctr[i]) {
       ansr=(byte[]){0,8,16,24}[i] + twos[ctr[i]];    //  i<<3 faster?
       break;
    }
  for (; i < 4; i++) if (ctr[i]) ;
  return ansr;
}

//..................................................

// loop ops (average):  1.5 ++   2.5 []  5 if
// result calculation:   1  +     2  []       significant loop overhead

byte f1(byte counter[4]) {
  for (byte i = 0; i < 4; i++) 
    if (counter[i]) 
      return  (byte[]){  0 + twos[counter[0]], 
                         8 + twos[counter[1]],                           
                        16 + twos[counter[2]], 
                        24 + twos[counter[3]]  } [i];
  return 31;
}

//..................................................

//    5 +/++    6 []     10 if

byte f2(byte counter[4]){  
  byte i, ansr=31;
  for (i = 0; i < 4; i++) {      //  definite loop overhead
    if (counter[i]) {
       ansr= (byte[]){   0 + twos[counter[0]], 
                         8 + twos[counter[1]],                           
                        16 + twos[counter[2]], 
                        24 + twos[counter[3]]  } [i];
       break;
  } }
  for (; i < 4; i++)   if (counter[i]);  //   time "constantator"
  return ansr;
}

//..................................................

//   4 +    4 !    3 x    1 []    1 computed goto/switch

byte f3(byte counter[4]){          // default: time "constantator"
  switch (!counter[0]*( 1 +        //           counter[0]==0 !!
          !counter[1]*( 1 +
          !counter[2]*( 1 +
          !counter[3]        ) ) ) ){
              case 0:  return   0 +  twos[ counter[0] ] ;
              case 1:  return   8 +  twos[ counter[1] ] ;
              case 2:  return  16 +  twos[ counter[2] ] ;
              case 3:  return  24 +  twos[ counter[3] ] ;
              default: return  31 +  twos[ counter[0] ] ;
}         }

/*

Существует сравнимая хронология для метода 2.

Эта последовательность была радикально ослаблена и сокращена до промежуточного примера:

Не случайно код, отправленный в fooobar.com/questions/428822/..., был а не только размер байта по размеру. Предложенный код находится в этом сообщении. */

byte log2toN(byte b){ return    ( b & 0xF0 ? 4:0 )  +   //    4444....
                                ( b & 0xCC ? 2:0 )  +   //    22..22..
                                ( b & 0xAA ? 1:0 )  ;   //    1.1.1.1.
} 
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
byte BRGC(byte n){ return n ^ n>>1; }

byte bitNbyte(byte n){ return log2toN( BRGC(n) ^ BRGC(n+1) ); }

byte bit2flip(byte n[4]){  
   boolean n3=n[3]<255, n2=n[2]<255, n1=n[1]<255, n0=n[0]<255;
           return n0*( 0 + bitNbyte( n[0] ) ) + !n0*( 
                  n1*( 8 + bitNbyte( n[1] ) ) + !n1*( 
                  n2*(16 + bitNbyte( n[2] ) ) + !n2*( 
                  n3*(24 + bitNbyte( n[3] ) ) + !n3*( 0 ) ) ) );
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
byte bit_flip(byte n[4]){  
//switch(     ( !!n[0] << 3 ) | ( !!n[1] << 2 ) | ( !!n[2] << 1 )  |  !!n[3] )
  switch( 15 ^ ( !n[0] << 3 ) ^  ( !n[1] << 2 ) ^  ( !n[2] << 1 )  ^   !n[3] ) { 
      case 15: case 14: case 13: case 12:
      case 11: case 10: case  9: case  8:  return  0 + log2toN(  n[0] & -n[0]  );
      case  7: case  6: case  5: case  4:  return  8 + log2toN(  n[1] & -n[1]  );
                        case  3: case  2:  return 16 + log2toN(  n[2] & -n[2]  );
                                 case  1:  return 24 + log2toN(  n[3] & -n[3]  );
      default:                             return 31 + log2toN(  n[0] & -n[0]  );
}           }

/*
Риторически, ответ на "Как мне найти..." можно получить только в явном виде в личном смысле (см. этот ответ: fooobar.com/questions/428822/...) как нельзя говорить о когнитивных способностях других людей.

Содержание fooobar.com/questions/428822/... имеет решающее значение для фона информации и отражает педантичный механизм, необходимый для умственной гимнастики для решения этой проблемы. Несомненно, окружающая среда и множество материалов сложны, но это точно личный подход к получению достаточной проницательности, репертуару, анекдотическому прецеденты и т.д., чтобы экстраполировать и интерполировать ответ на конкретный ответ, какая именно программа соответствует критериям. Кроме того, именно эта "преследование" возбуждает и мотивирует (возможно, патологическую) психику вкладывать время и силы чтобы насытить любопытство любопытным поиском. */

void setup() {    }

void loop() {     }

Ответ 7

/*
Невозможно отредактировать предыдущий ответ в соответствии с комментарием, поэтому переписывайте запись:

Слишком нетерпеливый?
Для немедленного удовлетворения и минимального назидания, прервите погоню и преследуйте эту ссылку, где был опубликован только конечный результат:
C-код для генерации следующего бита для перевода в серый код

РЭС:
Код C для генерации следующего бита для перевода в серый код
Как найти следующий бит для изменения кода Grey в постоянное время?

Получение n-го кода Grey из (n-1) -го серого кода
Функция увеличения цвета серого
Эффективный способ перебора позиций смены кода Grey
Создание серых кодов.

https://en.wikipedia.org/wiki/The_C_Programming_Language  
https://en.wikipedia.org/wiki/The_Elements_of_Programming_Style  

ПРЕДУПРЕЖДЕНИЕ:
Для целей кодирования целесообразности и доказуемого функционального исполнения, были использованы некоторые нетривиальные методы программирования. Однако это надеемся, смягчить для изложения концепции зачатки представляя сущность как можно тривиально и минимально, с помощью выделение ////. Тщательное чтение, учеба и эксперимент избегать кодирования, надзора и совершения ошибок.

Этот ответ проявляется в основной среде кодирования Arduino IDE ESP8266.

Алгоритм, установленный OP, не совсем корректен (как если бы это;).

Кодекс Джонсона
-------------------------

Так как фактическое серое кодирование не имеет значения, использование кода серого Johnson Gray очень просто и острый способ когнитивно и вычислительно считать и бит для изменения, и следующий код.

Обратите внимание на то, что плотность кода Юнга счетчика Грина является линейной, а не логарифмической.
С 32 битами в 4 байта последовательность может отсчитываться от 0 до 63 до сброса.

Необходимо тщательно проверить функциональную пригодность следующего кода, модифицируя его по мере необходимости.

СОВЕТ: Проверка является ДОЛЖНЫ, особенно для "двоичного отраженного" кода Grey (BRGC)!

/.//////////////////////////////////// //////////////////////////////////////.//strong > /

byte  bitCtr=-1;                //   for 4 x 8 bits use 32 instead of 5
byte JohnsonCode(){ static byte GCbits = 0; 
                        return  GCbits ^=  1u << ( bitCtr = ++bitCtr %5 ); }

/.//////////////////////////////////// //////////////////////////////////////.//strong > /

void testJohnson(){           
   Serial.println("\n\tJohnson counter\t   Bit\n\t   Gray code:\t Flipped:");

   for( int intifiny=31; --intifiny;  )
      Serial.println( "\t     " + cut( 5, "....." +  

// s.replace(...) returns zip, 
//                    so VVV use lambda function      invoke via VVV      
//  _____________________ V ____________________   ______________ V _____________ 
   [](String  s){ s.replace("0","."); return s; } ( String( JohnsonCode(), BIN ) )

         ) + "\t    " + bitCtr
      );
}  

/*
 Выходы:

  Johnson counter   Bit
     Gray code:   Flipped:
       ....1         0
       ...11         1
       ..111         2
       .1111         3
       11111         4
       1111.         0
       111..         1
       11...         2
       1....         3
       .....         4
       ....1         0
       ...11         1   etc.   

Некоторые справочные материалы о двоичном отраженном сером коде (BRGC)
----------------------------------------------- ------------------------------------------------
CONVERSIONS:
---------------------
REF: Code Golf: серый код

// These javascript scURIples may run by copy and paste to the URI browser bar.

// convert base 2  to  BRGC:   n^=n>>1  
//     get base 2 from BRGC:   n^=n>>1   n^=n>>2   n^=n>>4  ...

javascript:      n=16; s="<pre>";  
                      function f(i,n){ return i?f(i>>1,n^n>>i):n}
                                  while(n--) s +=  f(4,n^n>>1) .toString(2)+"\n";

javascript:      n=16; s="<pre>"; while(n--) s +=     (n^n>>1) .toString(2)+"\n";

javascript: c=0; n=16; s="<pre>"; while(n--) s +=(c^(c=n^n>>1)).toString(2)+"\n";

СЧЕТА:
-----------------
Следующее (как указано выше) произвольно получает как предыдущий, так и следующий BRGC для кода.
NB! Порядок n1 и n2 определяется четностью и не соответствует в противном случае. Порядок может быть n1, gray, n2 ИЛИ может быть n2, gray, n1, поэтому eo (четность) различает.

unsigned n1 = gray ^ 1;
unsigned n2 = gray ^ ((gray & -gray) << 1); 
gray = eo=!eo ? n1 : n2;                   // eo (or parity) gets Every Other  

т.

bitToFlip =  eo=!eo ? 1 : (gray & -gray) << 1;   gray ^= bitToFlip;  

следовательно,

    gray ^=  eo=!eo ? 1 : (gray & -gray) << 1;    

/.////////////////////////////////// ////////////////////////////////////.//strong > /

byte tooGray( byte (*f)(byte) ){ 
   static byte BRGC=0, base2=0;
   static boolean eo=false;              
   return  

       (*f)(  BRGC ^= (eo=!eo) ? (BRGC & -BRGC) <<1 : 1 )  & 0x3 |
   //  count  ^---------------------------------------^  in raw BRGC   


       (*f)(           base2   ^   base2++ >>1          )  & 0xC ; }
   //  count in base 2 ^---------------------^ and convert to BRGC

/.////////////////////////////////// ////////////////////////////////////.//strong >

REF: Соседи в коде Grey

http://www.graphics.stanford.edu/~seander/bithacks.html   
http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html  
https://en.wikipedia.org/wiki/Ring_counter#Johnson_counter   

О да,... подсчитайте бит в A ^ A+1, который будет иметь бит-шаблон, такой как 000... 0111..1 Докажите.

Как получить позицию бита для мощности 2 - параметры n должны иметь один бит.

Метод 1
*/

byte naive1(byte n){ return  bitNumber(n-1);   }  

byte bitNumber(byte m){  // can use A ^ A+1  ...  BUT >> 1 first OR -1 after
 return ( m & 1  ?1:0 ) + ( m &  2 ?1:0 ) + ( m &  4 ?1:0 ) + ( m &   8 ?1:0 ) +
        ( m & 16 ?1:0 ) + ( m & 32 ?1:0 ) + ( m & 64 ?1:0 ) + ( m & 128 ?1:0 );}
       //    256               512              1024               2048  ...

/*
 Способ 2
*/

byte naively2(byte n){
 return ( n & 1  ?0:0 ) + ( n &  2 ?1:0 ) + ( n &  4 ?2:0 ) + ( n &   8 ?3:0 ) +
        ( n & 16 ?4:0 ) + ( n & 32 ?5:0 ) + ( n & 64 ?6:0 ) + ( n & 128 ?7:0 );}

/*
 Способ 3
*/

byte powerOf2(byte n){ return ( n & 0xF0 ? 4:0 )  +   //    4444....
                              ( n & 0xCC ? 2:0 )  +   //    22..22..
                              ( n & 0xAA ? 1:0 )  ; } //    1.1.1.1.

/*
 Способ 4
*/

// and the penultimate,
//    http://www.graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn

byte fastNof2up(byte b){ return (byte []){0,1,6,2,7,5,4,3}[(byte)(b*0x1Du)>>5];}
//                                        7,0,5,1,6,4,3,2           0x3Au
//              b==2^N                    0,1,2,4,7,3,6,5           0x17u
//                                        7,0,1,3,6,2,5,4           0x2Eu
//     |------|                 |------|        
//     .....00011101         ........1101....     
//    ......0011101.        .........101.....     
//   .......011101..       ..........01......   
//  ........11101...      ...........1.......       
//     |------|                 |------|

/*
 Способ 5
 Подробности в конце.
 Может ли разумный выбор констант сократить это только до 2 операций?
*/

byte log2toN(byte b){ return 7 - (byte) ( 0x10310200A0018380uLL >> ( (byte)(0x1D*b) >>2 ) ) ; }  

/*
 Испытательная среда
*/

void setup() {Serial.begin(115200);  testJohnson();  test(); fastLog2upN(0);  }

void loop() {   delay(250);  // return ;
  [](byte GrayX2){  Serial.print( GrayX2 ^ 0x0F ? "" : baq(GrayX2)+"\n"); 
   analogWrite( D5, (int []) { 0, 1200,    0, 300 }  [ GrayX2      & 0x3 ] );
   analogWrite( D6, (int []) { 0,    0, 1200, 300 }  [ GrayX2      & 0x3 ] );
   analogWrite( D7, (int []) { 0, 1200,    0, 300 }  [ GrayX2 >> 2 & 0x3 ] );
   analogWrite( D8, (int []) { 0,    0, 1200, 300 }  [ GrayX2 >> 2 & 0x3 ] ); } 
//                                    (  tooGray(           b              )  );
                                      (  tooGray( [](byte n) { return n; } )  );
}

/ =========================================== ===========================
Оговорка:
-----------
Алгоритм OP не эффективен.

  Keep binary counter A
  Find B as A XOR (A+1)
  Bit to change is LowestBitSet in B

как видно при кодировании как:
*/

void test(){
 static byte C=0, bits=0; 
 Serial.println((String)"\n  "+(3^2+1)+"  "+(3^(2+1))+"  "+((3^2)+1) );
 Serial.println(
  "\n                                         manifested by an        actual               "
  "\n                                        obstinate perverse       bit to               " 
  "\n                                              psyche              flip           check" 
  "\n      A            A+1         A ^ A+1       B^=A^A+1         (A^A+1)+1>>1            ");
 for(int intifiny=32, A=0; intifiny--; A=++A%15)  // sort a go infinite ... an epiphany!
  Serial.println( (String) pad(  b(  bits ^=  b( b(A) ^ b(A+1) )  )   ) +"    "+ 
                    baq( (A^A+1)+1>>1 ) +"    "+ baq( C^=(A^A+1)+1>>1 )  +"    "
                                              //       "| "+   pad(A)+" "+ pad(bits)
                    );
  Serial.println(
   "                                      |                                    "
   "\n                                     BITS:                                 " 
   "\n                              Bit Isn't This Silly                         " 
   "\n                               Bit Is Totally Set    (A ^ A+1) & -(A ^ A+1) == 1 ALWAYS " 
 "\n\n non-binary Gray codes?                                                    "
   "\n  {-1,0,1} balanced ternary, factoroid (factoradic), {0,-1} negated binary \n");
}  //  https://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm  

//   some service routines  ...

String cut(byte sz, String str) { return     str.substring(str.length()-sz)   ; }
String pad(byte n             ) { return cut( 4, "         " + String(n,DEC) ); }
String baq(byte n             ) { return cut( 9, "........." + String(n,BIN) ); }
void   q  (                   ) {          /*  PDQ QED PQ */         } 
void   p  (         String str) { Serial.print( "  " + str + "   " ) ; }
byte   d  (byte n             ) {  p(pad(n));         return n       ; }   
byte   b  (byte n             ) {  p(baq(n));         return n       ; }  

/*
 Пример вывода:

                                                                flip  bit                      
                                                               "correctly"    confirm?           
      A            A+1         A ^ A+1       B^=A^A+1         (A^A+1)+1>>1                 
  ........0     ........1     ........1     ........1      1    ........1    ........1   |    0    1
  ........1     .......10     .......11     .......10      2    .......10    .......11   |    1    2
  .......10     .......11     ........1     .......11      3    ........1    .......10   |    2    3
  .......11     ......100     ......111     ......100      4    ......100    ......110   |    3    4
  ......100     ......101     ........1     ......101      5    ........1    ......111   |    4    5
  ......101     ......110     .......11     ......110      6    .......10    ......101   |    5    6
  ......110     ......111     ........1     ......111      7    ........1    ......100   |    6    7
  ......111     .....1000     .....1111     .....1000      8    .....1000    .....1100   |    7    8
  .....1000     .....1001     ........1     .....1001      9    ........1    .....1101   |    8    9
  .....1001     .....1010     .......11     .....1010     10    .......10    .....1111   |    9   10
  .....1010     .....1011     ........1     .....1011     11    ........1    .....1110   |   10   11
     etc.                             |                                    
                                     BITS:                
                              Bit Isn't This Silly             Houston; We have a (an-other) problem    
                               Bit Is Totally Set                    
                        (A ^ A+1) & -(A ^ A+1) == 1 ALWAYS           

Любопытный?
-----------
Следующий код - это очень грубый метод, который может ускорить охоту за подходящими кандидатами на подсчет бит для вычисления log 2^n (в базе 2, т.е. n).
*/

byte fastLog2upN(byte b){                            //  b==2^N
    for(long long int i=8, b=1; --i;    )
        Serial.println((int)(0x0706050403020100uLL / (b*=0x100)),HEX)  ; 
    for( int i=9, b=1; --i;b*=2)        //   3A = 1D*2
        Serial.println( 
          (String)      (                           (b>>4 | b>>2 | b>>1) & 7   )   +   "    \t" + 
                        (                                   (0xB8*b) >>8 & 7   )   +   "    \t" + 
                        (                                   (0xB8*b) >>7 & 7   )   +   "    \t" + 
                        (                                   (0x1D*b) >>4 & 7   )   +   "    \t" + 
                        (                                   (0x0D*b) >>3 & 7   )   +   "   |\t" + 
                        (                            ((byte)(0x9E*b) >>2       ) ) +   "    \t" + 
                 (byte) ( 0x07070301C0038007uLL >>   ((byte)(0x9E*b) >>2       ) ) +   "    \t" + 
                        (                            ((byte)(0x1E*b) >>2       ) ) +   "    \t" + 
                 (byte) (  0x7070001C0038307uLL >>   ((byte)(0x1E*b) >>2       ) ) +   "    \t" + 
                        (                            ((byte)(0x5E*b) >>2       ) ) +   "    \t" + 
                 (byte) (  0x703800183838307uLL >>   ((byte)(0x5E*b) >>2       ) ) +   "  \t| " + 
                        (                            ((byte)(0x3A*b))>>5       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>4       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>3       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>2       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>1       )   +   "    \t" + 
                        (                            ((byte)(0x3A*b))>>0       )   +   "  \t| " + 
                 (byte) ( 0x0203040601050007uLL >> 8*((byte)(0x3A*b) >>5       ) ) +   "    \t" + 
          String((byte) ( 0x0706050403020100uLL >>   ((byte)(0x3A*b) >>4       ) ),HEX ) + "\t" + 
          String((byte) (       0x0020010307uLL >>   ((byte)(0x3A*b) >>3       ) ),HEX ) + "\t" + 
          String((byte) ( 0x10300200A0018007uLL >>   ((byte)(0x3A*b) >>2       ) ),HEX ) + "\t|" + 
                        (                            ((byte)(0x1D*b))>>2       )   +   "    \t" + 
                 (byte) ( 0x10710700E0018380uLL >>   ((byte)(0x1D*b) >>2       ) ) +   "    \t" + 
                 (byte) ( 0x10310200A0018380uLL >>   ((byte)(0x1D*b) >>2       ) ) +   "  \t| " + 
       "") ;
   } 

/*

javascript: x=6; y=4; n=511; ra=[]; s="<pre>x"; 
   while(n--)for(i=5;i;i=i==3?2:--i){ 
      j=0; 
      for(b=1;b<129;b*=2) ra[j++]=((n*b)&0xFF)>>i;
      ra.sort(function(a, b){return a-b});
      if ( tf=ra[--j] < 64 &&  ra[1]>ra[0]+x ) 
         while( --j && ( tf = (ra[j]>ra[j-1]+x) || ( ra[j]<ra[j-1]+y  && ra[j+1]>ra[j]+x) ) );
      if(tf) s+="\n "+n.toString(16)+" >> "+i+" \t"+ra.join("  "); 
   }; 
  s;

// many >>2    to do: sledge hammer creation of acceptable bit pattern solutions
// only want btf - Bits That Fit (in 8 bytes): (tf=ra[j-1] < 64)
// program proximity control so no BS (Brain Strain!): tf = (ra[j]<ra[j-1]+x) || (ra[j]<ra[j-2]+y) 
// for debug: s+="\n "+n.toString(16)+" >> "+i; 
// for debug: s+="\n" +tf+"\t"+ra[j]+"\t"+ra[j-1]+"\t"+ra[j-2];  

*/