2016-10-26 11 views
22

В настоящее время я пытаюсь управлять шаговым двигателем, используя простые полные шаги. Это означает, что я в данный момент вывожусь последовательность значений, как это:Побитовое вращение справа от 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-битный unsigned int, я в конечном итоге поверну его 1 к MSB, а это означает, что 4-битное значение, которое меня действительно интересует, превратится в 0000 за несколько шагов.

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

+1

У вас будет только 4 значения или любое 4-битное число? – Swanand

+0

Хорошо управляя шаговым двигателем с помощью полных шагов, я просто выводил вышеприведенную последовательность как высокие/низкие значения на 4 контакта, которые я назначил. Чтение ответов ниже, похоже, дало мне возможность решить эту проблему. Я не совсем уверен, почему я не просто думал о наборе значений символов «X», которые я просто вращаю. – NT93

+0

http://stackoverflow.com/questions/15648116/bit-shifting-a-character-with-wrap-c –

ответ

10

Арифметика для этого достаточно просто, что он всегда будет быстрее, чем подход таблицы:

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; 
} 
+0

Это приятное решение, но использование таблицы - это всего лишь две команды mov. – 2501

+0

@ 2501 Мне тоже нравится ваш подход к столу. И теоретически ваш может быть довольно компактным (см. Мой комментарий к вашему решению), но вы действительно посмотрели на сгенерированную сборку? – KevinZ

+0

Да, используя неподписанный тип, генерирует два mov * s * – 2501

3

Я бы сделал массив со значениями, которые вам нужны, и загрузите правильное значение из массива. Это займет у вас 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]; 
+2

Обратите внимание, что это использует переменную с переменным именем 'current_value' в качестве индекса в таблице фактических значений двигателя. – unwind

4

использовать тип данных 8-битовый (как, например uint8_t). Инициализируйте его до нуля. Установите бит, который вы хотите установить в младших четырех битах байта (например, value = 0x08).

Для каждого «поворота» возьмите младший бит (младший значащий бит) и сохраните его. Сдвиньте один шаг вправо. Перезапишите четвертый бит бит, который вы сохранили.

Что-то вроде этого:

#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; 
} 

Live demonstration.

21

С только 4 возможных значений, которые вы бы использовать таблицу с 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); 

Doing это в цикле, проведет все четыре возможных значения.

Удобно, передавая недопустимое значение, возвращает ноль, так что вы можете написать функцию get, которая также утверждает следующее! = 0. Вы должны также подтвердить значение < 9 перед передачей значения массиву.

+0

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

+0

Обратите внимание, что другие элементы массива все еще инициализируются (что этот ответ замечает), см. Документы GCC: https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html – cat

+0

Вы можете избавиться неопределенного поведения, выполнив 'unsigned char table_right [8] = {4, 8, 1, 0, 2, 0, 0, 0};'. Затем выполните следующую функцию: return table_right [current & 7] '. Это также делает таблицу подходящей в одном QWORD. – KevinZ

7

вы можете использовать 10001000b и мод 10000b

и вы можете получить 01000100b00100010b00010001b10001000b повторить.

, например:

char x = 0x88; 
Motor_Out(x & 0xf); 
Rotate_Right(step, 1); 
+3

Пожалуйста, не оставляйте ответ только с кодом. Попробуйте объяснить это. – user694733

8

IMO самый простой способ:

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 ])); 
+0

Недостатком этого является то, что если это длинное приложение, stepIdx ++ в конечном итоге приведет к UB – UKMonkey

+5

@UKMonkey: no it not. Посмотрите последнюю строку, которая зажимает «stepsIdx» в диапазон? (Лично я бы назвал его «stepIdx », но, скорее всего, хороший оптимизирующий компилятор сделает это за вас.) – usr2564301

+0

@RadLexus: если для следующего двигателя потребуется 5 (или 7) шагов, операция напоминания будет лучшая идея. И вы правы: пусть компилятор думает о скорости)) – borisbn

15

Просто используйте int для хранения значения. Когда вы делаете вращатьс скопировать значащий бит в бит 4, а затем переместить его вправо на 1:

int rotate(int value) 
{ 
    value |= ((value & 1) << 4); // eg 1001 becomes 11001 
    value >>= 1;     // Now value is 1100 
    return value; 
} 
3

Вам нужно только выход 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 
+1

, что '4 >> i' - это опечатка? – ilkkachu

+0

@ilkkachu Да. Спасибо, что указали это. – v7d8dpo4

6

, если я использую 8-битовое беззнаковое INT я в конечном итоге будет вращать 1 прочь к MSB

Так использовать сдвиг и переинициализировать бит вы хотите, когда значение переходит в нуль. В любом случае, C не имеет операции поворота, поэтому вам нужно будет сделать как минимум две смены. (И я полагаю, C++ не вращается либо.)

x >>= 1; 
if (! x) x = 0x08; 

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