Rotation à droite de la valeur de 4 bits

J'essaie actuellement de contrôler un moteur pas à pas en utilisant des étapes complètes simples. Cela signifie que je génère actuellement une séquence de valeurs comme ceci:

1000
0100
0010
0001

Je pensais qu'un moyen facile de le faire était de prendre ma valeur de 4 bits et après chaque étape, effectuez une opération de rotation à droite. "Code" ne suit évidemment aucun type de syntaxe, il est simplement là pour illustrer mes pensées:

step = 1000;
//Looping
Motor_Out(step)
//Rotate my step variable right by 1 bit
Rotate_Right(step, 1)

Mon problème est qu'il n'y a évidemment pas de types de données simples 4 bits pour lesquels je peux utiliser ceci, et si j'utilise un int non signé 8 bits, je finirai par faire pivoter le 1 vers le MSB, ce qui signifie que la valeur 4 bits qui m'intéresse réellement, se transformera en 0000 pour quelques étapes.

J'ai lu que vous pouvez utiliser des structures et des champs de bits pour résoudre cela, mais la majorité des choses que je lis de ceci me dit que c'est une très mauvaise idée.

22
demandé sur NT93 2016-10-26 14:01:33

9 réponses

L'arithmétique pour cela est assez simple qu'il sera toujours plus rapide que l'approche de table:

constexpr unsigned rotate_right_4bit ( unsigned value )
{
    return ( value >> 1 ) | ( ( value << 3 ) & 15 );
}

Cela se transforme en 5 lignes d'assemblage x86 sans branche:

lea     eax, [0+rdi*8]
shr     edi
and     eax, 15
or      eax, edi
ret

Ou, alternativement, si vous aimez réellement voir les index {3, 2, 1, 0}, vous pouvez les diviser en 2 fonctions, l'une qui "incrémente" l'index, et l'autre qui calcule réellement la valeur:

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

constexpr unsigned project ( unsigned index )
{
    return 1u << index;
}
10
répondu KevinZ 2016-10-26 15:59:45

, Avec seulement 4 valeurs possibles vous utilisez une table avec 9 éléments:

unsigned char table_right[] = { [0x1] = 0x8 , [0x2] = 0x1 , [0x4] = 0x2 , [0x8] = 0x4 };

Lorsque vous avez besoin de la valeur suivante, vous utilisez simplement la valeur actuelle de l'indice:

unsigned char current = 0x4;    //value is: 0b0100
unsigned char next = table_right[current];  //returns: 0b0010
assert( next == 0x2 );

En faisant cela dans une boucle, parcourra les quatre valeurs possibles.

Commodément, en passant une valeur invalide, retournera un zéro, de sorte que vous pouvez écrire une fonction get qui affirme également next != 0. Vous devez également affirmer la valeur

21
répondu 2501 2016-10-26 19:55:39

Il suffit d'utiliser un int pour contenir la valeur. Lorsque vous faites la rotation, Copiez le bit le moins significatif sur le bit 4, puis déplacez-le de 1:

int rotate(int value)
{
    value |= ((value & 1) << 4); // eg 1001 becomes 11001
    value >>= 1;                 // Now value is 1100
    return value;
}
15
répondu Sean 2016-10-26 11:17:18

IMO le moyen le plus simple est:

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
répondu borisbn 2016-10-27 11:52:05

, Vous pouvez utiliser 10001000b et mod 10000b

Et vous pouvez obtenir 01000100b 00100010b 00010001b 10001000b répétez.

Par exemple:

char x = 0x88;
Motor_Out(x & 0xf);
Rotate_Right(step, 1);
7
répondu ningwei 2016-10-26 11:30:08

Si j'utilise un int non signé 8 bits, je finirai par faire pivoter le 1 vers le MSB

Utilisez donc un décalage et réinitialisez le bit que vous voulez lorsque la valeur passe à zéro. C n'a pas d'opération de rotation de toute façon, vous devrez donc faire au moins deux quarts de travail. (Et je suppose que C++ n'a pas non plus de rotation.)

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

Simple, court à écrire, et évident dans ce qu'il fait. Oui, il compilera dans une branche (sauf si le processeur a une opération de déplacement conditionnelle), mais jusqu'à ce que vous avoir la sortie du profileur pour vous dire que c'est important, vous venez de perdre plus de temps à y penser que ces cycles de processeur ne le feront jamais.

7
répondu ilkkachu 2016-10-26 19:28:27

Utilisez un type de données 8 bits (comme par exemple uint8_t). Initialisez-le à zéro. Définissez le bit que vous voulez définir dans les quatre bits inférieurs de l'octet (par exemple value = 0x08).

Pour chaque "rotation", prenez le LSB (bit le moins significatif) et enregistrez-le. Un décalage vers la droite. Écrasez le quatrième bit avec le bit que vous avez enregistré.

Quelque Chose comme ceci:

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

Démonstration en Direct.

4
répondu Some programmer dude 2016-10-26 11:14:00

Je ferais un tableau avec les valeurs dont vous avez besoin et chargerais la valeur correcte du tableau. Il vous faudra 4 octets, ce sera rapide et résoudra vos problèmes même si vous commencez à utiliser un type de moteur différent.

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
répondu SurDin 2016-10-26 11:06:37

Il vous suffit de sortir 1, 2, 4 et 8. Vous pouvez donc utiliser un compteur pour marquer le bit à définir haut.

Motor_Out(8 >> i);
i = (i + 1) & 3;

Si vous voulez conduire le moteur à demi-pas, vous pouvez utiliser un tableau pour stocker les nombres dont vous avez besoin.

const unsigned char out[] = {0x8, 0xc, 0x4, 0x6, 0x2, 0x3, 0x1, 0x9};

Motor_out(out[i]);
i = (i + 1) & 7;

Et vous pouvez faire pivoter un entier de 4 bits comme ceci.

((i * 0x11) >> 1) & 0xf
3
répondu v7d8dpo4 2016-10-27 03:48:27