23

I'm currently trying to control a stepper motor using simple full steps. This means that I'm currently outputting a sequence of values like this:

1000
0100
0010
0001

I thought an easy way to do this was just take my 4-bit value and after each step, perform a rotate right operation. "Code" obviously isn't following any kind of syntax, it's simply there to illustrate my thoughts:

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

My problem is that there obviously isn't any 4-bit simple data types that I can use for this, and if I use an 8-bit unsigned int I will eventually rotate the 1 off to the MSB, which means the 4-bit value I'm actually interested in, will turn into 0000 for a few steps.

I've read that you can use structs and bit-fields to solve this, but the majority of things I read from this is telling me that it's a very bad idea.

NT93
  • 316
  • 2
  • 15
  • 1
    Are you going to have only 4 values or any 4 bit number? – Swanand Oct 26 '16 at 11:14
  • Well controlling the stepper motor using full-steps means that I simply output the above sequence as high/low values on 4 pins I have assigned to it. Reading the answers below seems to have given me an opportunity to solve it. I'm not quite sure why I didn't just think of having a set of 'X' char values that I just rotate through. – NT93 Oct 26 '16 at 11:20
  • http://stackoverflow.com/questions/15648116/bit-shifting-a-character-with-wrap-c – Matthew Whited Oct 26 '16 at 15:26

9 Answers9

21

With only 4 possible values you would use a table with 9 elements:

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

When you need the next value you simply use the current value as the index:

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

Doing this in a loop, will loop through all four possible values.

Conveniently, passing an invalid value, will return a zero, so you can write a get function that also asserts next != 0. You should also assert value < 9 before passing the value to the array.

2501
  • 25,460
  • 4
  • 47
  • 87
  • This seems really nifty - I'll definitely try that, I just have to wrap my head around how assert works. – NT93 Oct 26 '16 at 11:23
  • Note well that the other elements of the array still get initialised (which this answer does note), see the GCC docs: https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html – cat Oct 26 '16 at 14:34
  • You can get rid of the undefined behavior by doing `unsigned char table_right[8] = { 4, 8, 1, 0, 2, 0, 0, 0 };`. Then, have the next function do `return table_right[current & 7]`. This also makes the table fit in one QWORD. – KevinZ Oct 26 '16 at 17:42
  • 8
    Why on earth would you use a lookup table for this, when there are plenty of trivial arithmetic solutions like `next = ((current & 1) << 3) | (current >> 1)`? Not only are you wasting space with the lookup table, but on most systems a memory fetch is likely to be quite a bit slower than a couple of bitwise ops. – Ilmari Karonen Oct 26 '16 at 19:27
  • 3
    @IlmariKaronen Thank you for your opinion, that is a valid approach too. Please don't downvote a correct answer, rather go upvote others that use your approach just as I did, because I found them to be better than mine. – 2501 Oct 26 '16 at 19:37
15

Just use an int to hold the value. When you do the rotate copy the least significant bit to bit 4 and then shift it right by 1:

int rotate(int value)
{
    value |= ((value & 1) << 4); // eg 1001 becomes 11001
    value >>= 1;                 // Now value is 1100
    return value;
}
Sean
  • 60,939
  • 11
  • 97
  • 136
10

The arithmetic for this is simple enough that it will always be faster than the table approach:

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

This turns into 5 lines of branch-free x86 assembly:

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

Or, alternatively, if you actually like to see the indexes {3, 2, 1, 0}, then you can split them up into 2 functions, one that "increments" the index, and the other that actually computes the value:

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

constexpr unsigned project ( unsigned index )
{
    return 1u << index;
}
KevinZ
  • 3,036
  • 1
  • 18
  • 26
  • This is a nice solution, but using a table is just two mov instructions. – 2501 Oct 26 '16 at 16:25
  • @2501 I like your table approach too. And in theory yours could be pretty compact (see my comment on your solution), but have you actually looked at the generated assembly? – KevinZ Oct 26 '16 at 17:47
  • Yes, using unsigned type generates two mov*s* – 2501 Oct 26 '16 at 18:10
  • It's tagged as C, C doesn't have `constexpr`. – Hatted Rooster Oct 27 '16 at 09:17
  • @2501: two `mov`s *and* a table, which is not shorter and also not necessarily faster than these pure arithmetic instructions. But it makes no sense to look at this function in isolation, it’ll get inlined into the caller’s context, usually. – Holger Oct 27 '16 at 10:34
  • @Holger Table is static. It is true that a mov might be slower due to cache effects. When did I comment about the entire function in isolation? – 2501 Oct 27 '16 at 10:35
  • @2501: the five instructions mentioned in this answer are from compiling this function in isolation. If you’re not comparing with them, with what are you comparing otherwise? Of course, *your* two `mov` instructions are not from looking at a function, otherwise, there was a `ret` instruction too. After inlining, the optimizer may reduce the remaining four instruction to less. Ironically, if inlined to an unrolled loop, a straight-forward `x>>=1; if(!x) x=8;` implementation might benefit even better from an optimizer’s prediction, to a net result of a single arithmetic instruction. – Holger Oct 27 '16 at 10:48
  • @Holger I simply stated that a table is two instructions and complemented the answer. Your argument is a strawman. – 2501 Oct 27 '16 at 10:53
8

IMO the easiest way is:

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 ] ) );
borisbn
  • 4,988
  • 25
  • 42
  • The down side of this is that if it's a long running application, stepsIdx++ will eventually result in UB – UKMonkey Oct 26 '16 at 11:26
  • 5
    @UKMonkey: no it will not. See that last line that clips `stepsIdx` into range? (Personally I'd write it as `stepsIdx &= 3;` but most likely, a good optimizing compiler will do that for you.) – Jongware Oct 26 '16 at 11:33
  • @RadLexus: if the next motor will need 5 (or 7) steps, a reminder operation would be a better idea. And you're right: let compiler think about speed )) – borisbn Oct 26 '16 at 12:11
  • 2
    and to make it future proof: `stepsIdx = stepsIdx % sizeof(steps);` (possibly adding a `/sizeof(steps[0])` if you ever change the type) – ratchet freak Oct 26 '16 at 15:38
  • I like _countof even though it isn't standard instead of sizeof(a) / sizeof(a[0]) – paulm Oct 27 '16 at 12:18
7

you can use 10001000b and mod 10000b

and you can get 01000100b 00100010b 00010001b 10001000b repeat.

for example:

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

if I use an 8-bit unsigned int I will eventually rotate the 1 off to the MSB

So use a shift and reinitialize the bit you want when the value goes to zero. C doesn't have a rotate operation anyway, so you'll have to do at least two shifts. (And I suppose C++ doesn't have rotates either.)

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

Simple, short to write, and obvious in what it does. Yes, it'll compile into a branch (unless the processor has a conditional move operation), but until you have the profiler output to tell you it's important, you just lost more time thinking about it than those processor cycles will ever amount to.

ilkkachu
  • 6,221
  • 16
  • 30
4

Use an 8-bit data type (like e.g. uint8_t). Initialize it to zero. Set the bit you want to set in the lower four bits of the byte (e.g. value = 0x08).

For each "rotation" take the LSB (least significant bit) and save it. Shift one step right. Overwrite the fourth bit with the bit you saved.

Something like this:

#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.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
3

I would make an array with the values you need and load the correct value from the array. It will take you 4 bytes, it will be fast, and solve your problems even if you start using a different motor type.

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];
SurDin
  • 3,281
  • 4
  • 26
  • 28
  • 2
    Note that this uses the confusingly-named `current_value` variable as an index into the table of actual motor values. – unwind Oct 26 '16 at 11:15
3

You only need to output 1, 2, 4, and 8. So you can use a counter to mark which bit to set high.

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

If you want to drive the motor at half steps, you can use an array to store the numbers you need.

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

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

And you can rotate a 4-bit integer like this.

((i * 0x11) >> 1) & 0xf
v7d8dpo4
  • 1,399
  • 8
  • 9