23

我目前正在尝试使用简单的完整步骤来控制步进电机。这意味着我目前正在输出这样的一系列值:

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 位无符号整数,我最终会将 1 旋转到 MSB,这意味着 4 位我真正感兴趣的值,将变为 0000 几步。

我读过你可以使用结构和位域来解决这个问题,但我从中读到的大部分内容都告诉我这是一个非常糟糕的主意。

4

9 回答 9

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 );

在循环中执行此操作,将遍历所有四个可能的值。

方便的是,传递无效值将返回零,因此您可以编写一个 get 函数,该函数也断言 next != 0。您还应该在将值传递给数组之前断言 value < 9。

于 2016-10-26T11:07:16.003 回答
15

只需使用 anint来保存该值。当您进行旋转时,将最低有效位复制到第 4 位,然后将其右移 1:

int rotate(int value)
{
    value |= ((value & 1) << 4); // eg 1001 becomes 11001
    value >>= 1;                 // Now value is 1100
    return value;
}
于 2016-10-26T11:17:18.700 回答
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;
}
于 2016-10-26T15:59:45.567 回答
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 ] ) );
于 2016-10-26T11:13:45.120 回答
7

你可以使用10001000b和修改10000b

你可以01000100b 00100010b 00010001b 10001000b重复。

例如:

char x = 0x88;
Motor_Out(x & 0xf);
Rotate_Right(step, 1);
于 2016-10-26T11:11:56.870 回答
7

如果我使用 8 位无符号整数,我最终会将 1 旋转到 MSB

因此,当值变为零时,请使用移位并重新初始化所需的位。C 无论如何都没有旋转操作,因此您必须至少进行两次轮班。(而且我想 C++ 也没有旋转。)

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

简单,写起来很短,而且它的作用很明显。是的,它会编译成一个分支(除非处理器有条件移动操作),但是在你有分析器输出告诉你它很重要之前,你只是浪费了比那些处理器周期更多的时间来思考它。

于 2016-10-26T18:03:20.763 回答
4

使用 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;
}

现场演示

于 2016-10-26T11:07:07.653 回答
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];
于 2016-10-26T11:06:37.613 回答
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
于 2016-10-26T14:19:44.780 回答