web-dev-qa-db-ja.com

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になります。

構造体とビットフィールドを使用してこれを解決できることを読みましたが、これから読んだことの大部分は、それは非常に悪い考えだと言っています。

23
NT93

このための計算は非常に単純なので、テーブルアプローチよりも常に高速になります。

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つの関数に分割できます。1つはインデックスを「インクリメント」し、もう1つは実際に値を計算します。

constexpr unsigned decrement_mod4 ( unsigned index )
{
    return ( index - 1 ) & 3;
}

constexpr unsigned project ( unsigned index )
{
    return 1u << index;
}
10
KevinZ

可能な値が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 );

これをループで実行すると、4つの可能な値すべてがループされます。

便利なことに、無効な値を渡すとゼロが返されるため、次の!= 0もアサートするget関数を記述できます。値を配列に渡す前に、値<9もアサートする必要があります。

21
2501

intを使用して値を保持するだけです。ローテーションを実行するときは、最下位ビットをビット4にコピーしてから、右に1シフトします。

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

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 ] ) );
8
borisbn

8ビットのunsignedintを使用する場合、最終的に1をMSBにローテーションします

したがって、シフトを使用して、値がゼロになったときに必要なビットを再初期化します。 Cにはとにかく回転操作がないので、少なくとも2シフトする必要があります。 (そして、C++にも回転がないと思います。)

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

シンプルで、書くのが短く、その機能が明らかです。はい、それはブランチにコンパイルされます(プロセッサに条件付き移動操作がない限り)が、それが重要であることを示すプロファイラー出力が得られるまで、それらのプロセッササイクルがこれまでにないほど多くの時間を失ってしまいます。

7
ilkkachu

10001000bおよびmod10000bを使用できます

01000100b00100010b00010001b10001000bを繰り返すことができます。

例えば:

char x = 0x88;
Motor_Out(x & 0xf);
Rotate_Right(step, 1);
7
ningwei

8ビットのデータ型を使用します(例:uint8_t)。ゼロに初期化します。バイトの下位4ビットに設定するビットを設定します(例:value = 0x08)。

「ローテーション」ごとに、LSB(最下位ビット)を取得して保存します。右に1ステップシフトします。保存したビットで4番目のビットを上書きします。

このようなもの:

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

ライブデモンストレーション

必要な値で配列を作成し、配列から正しい値をロードします。それはあなたに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];
3
SurDin

1、2、4、および8を出力するだけで済みます。したがって、カウンタを使用して、どのビットをHighに設定するかをマークできます。

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
3
v7d8dpo4