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

Побитовое вращение справа от 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 за несколько шагов.

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

4b9b3361

Ответ 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