Побитовое вращение справа от 4-битного значения
В настоящее время я пытаюсь управлять шаговым двигателем, используя простые полные шаги. Это означает, что в настоящее время я выводил последовательность таких значений:
1000
0100
0010
0001
Я думал, что простой способ сделать это - просто взять мое 4-битное значение и после каждого шага выполнить операцию поворота вправо. "Код", очевидно, не следует никакому синтаксису, он просто там, чтобы проиллюстрировать мои мысли:
step = 1000;
//Looping
Motor_Out(step)
//Rotate my step variable right by 1 bit
Rotate_Right(step, 1)
Моя проблема заключается в том, что для этого я не могу использовать 4-битные простые типы данных, и если я использую 8-битный беззнаковый int, я в конечном итоге поверну его 1 к MSB, что означает 4-битное значение, которое меня действительно интересует, превратится в 0000 за несколько шагов.
Я читал, что вы можете использовать структуры и бит-поля для решения этой проблемы, но большинство из того, что я читаю, говорит мне, что это очень плохая идея.
Ответы
Ответ 1
Арифметика для этого достаточно проста, что она всегда будет быстрее, чем табличный подход:
constexpr unsigned rotate_right_4bit ( unsigned value )
{
return ( value >> 1 ) | ( ( value << 3 ) & 15 );
}
Это превращается в 5 строк сборки x86 без ветвей:
lea eax, [0+rdi*8]
shr edi
and eax, 15
or eax, edi
ret
Или, наоборот, если вам действительно нравится индексы {3, 2, 1, 0}
, вы можете разбить их на 2 функции, которые "увеличивают" индекс, а другой, который фактически вычисляет значение:
constexpr unsigned decrement_mod4 ( unsigned index )
{
return ( index - 1 ) & 3;
}
constexpr unsigned project ( unsigned index )
{
return 1u << index;
}
Ответ 2
Только с четырьмя возможными значениями вы должны использовать таблицу с 9 элементами:
unsigned char table_right[] = { [0x1] = 0x8 , [0x2] = 0x1 , [0x4] = 0x2 , [0x8] = 0x4 };
Когда вам нужно следующее значение, вы просто используете текущее значение в качестве индекса:
unsigned char current = 0x4; //value is: 0b0100
unsigned char next = table_right[current]; //returns: 0b0010
assert( next == 0x2 );
Выполняя это в цикле, будет проходить все четыре возможных значения.
Удобно, передав недопустимое значение, вернет нуль, чтобы вы могли написать функцию get, которая также утверждает следующее!= 0. Вы также должны утверждать значение < 9 перед передачей значения в массив.
Ответ 3
Просто используйте int
, чтобы сохранить значение. Когда вы производите копирование с наименьшим значащим битом в бит 4, а затем сдвиньте его вправо на 1:
int rotate(int value)
{
value |= ((value & 1) << 4); // eg 1001 becomes 11001
value >>= 1; // Now value is 1100
return value;
}
Ответ 4
ИМО самый простой способ:
const unsigned char steps[ 4 ] = { 0x08, 0x04, 0x02, 0x01 };
int stepsIdx = 0;
...
const unsigned char step = steps[ stepsIdx++ ];
stepsIdx = stepsIdx % ( sizeof( steps ) / sizeof( steps[ 0 ] ) );
Ответ 5
вы можете использовать 10001000b
и mod 10000b
и вы можете получить 01000100b
00100010b
00010001b
10001000b
repeat.
например:
char x = 0x88;
Motor_Out(x & 0xf);
Rotate_Right(step, 1);
Ответ 6
если я использую 8-битный беззнаковый int, я в конечном итоге поверну его 1 в MSB
Итак, используйте сдвиг и повторно инициализируйте бит, который вы хотите, когда значение достигнет нуля. В любом случае, C не имеет операции поворота, поэтому вам нужно будет сделать как минимум две смены. (И я полагаю, что С++ тоже не вращается.)
x >>= 1;
if (! x) x = 0x08;
Простой, короткий для написания и очевидный в том, что он делает. Да, он будет скомпилирован в ветвь (если только процессор не имеет условной операции перемещения), но пока вы не получите отчет о профайлере, чтобы сказать вам, что это важно, вы просто теряли больше времени, думая об этом, чем те, которые будут выполняться в цикле процессора.
Ответ 7
Используйте 8-битный тип данных (например, uint8_t
). Инициализируйте его до нуля. Установите бит, который вы хотите установить в младших четырех битах байта (например, value = 0x08
).
Для каждого "вращения" возьмите LSB (младший бит) и сохраните его. Сдвиньте один шаг вправо. Перезапишите четвертый бит бит, который вы сохранили.
Что-то вроде этого:
#include <stdio.h>
#include <stdint.h>
uint8_t rotate_one_right(uint8_t value)
{
unsigned saved_bit = value & 1; // Save the LSB
value >>= 1; // Shift right
value |= saved_bit << 3; // Make the saved bit the nibble MSB
return value;
}
int main(void)
{
uint8_t value = 0x08; // Set the high bit in the low nibble
printf("%02hhx\n", value); // Will print 08
value = rotate_one_right(value);
printf("%02hhx\n", value); // Will print 04
value = rotate_one_right(value);
printf("%02hhx\n", value); // Will print 02
value = rotate_one_right(value);
printf("%02hhx\n", value); // Will print 01
value = rotate_one_right(value);
printf("%02hhx\n", value); // Will print 08 again
return 0;
}
Живая демонстрация.
Ответ 8
Я бы сделал массив со значениями, которые вам нужны, и загрузите правильное значение из массива. Это займет у вас 4 байта, оно будет быстрым и решит ваши проблемы, даже если вы начнете использовать другой тип двигателя.
for example:
const char values[4]={1,2,4,8};
int current_value = 0;
....
if(++current_value>=4)current_value=0;
motor = values[current_value];
Ответ 9
Вам нужно только вывести 1, 2, 4 и 8. Таким образом, вы можете использовать счетчик, чтобы отметить, какой бит установить высокий.
Motor_Out(8 >> i);
i = (i + 1) & 3;
Если вы хотите управлять двигателем на полшага, вы можете использовать массив для хранения нужных вам чисел.
const unsigned char out[] = {0x8, 0xc, 0x4, 0x6, 0x2, 0x3, 0x1, 0x9};
Motor_out(out[i]);
i = (i + 1) & 7;
И вы можете повернуть 4-битное целое число, подобное этому.
((i * 0x11) >> 1) & 0xf