我目前正试图使用简单的全步骤控制步进电机。这意味着我当前输出的值序列如下所示:
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的几个步骤。
我读到过你可以使用结构体和位域来解决这个问题,但是我从这里读到的大部分东西都告诉我这是一个非常糟糕的主意。
只有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 <</p>仅使用int
来保存值。当你旋转时,将最低有效位复制到第4位,然后右移1位:
int rotate(int value)
{
value |= ((value & 1) << 4); // eg 1001 becomes 11001
value >>= 1; // Now value is 1100
return value;
}
这个算法很简单,它总是比表方法快:
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}
,那么您可以将它们拆分为两个函数,一个将索引"加1",另一个实际计算值:
constexpr unsigned decrement_mod4 ( unsigned index )
{
return ( index - 1 ) & 3;
}
constexpr unsigned project ( unsigned index )
{
return 1u << index;
}
我觉得最简单的方法是:
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 ] ) );
可以使用10001000b
和mod 10000b
你可以得到01000100b
00100010b
00010001b
10001000b
重复。
char x = 0x88;
Motor_Out(x & 0xf);
Rotate_Right(step, 1);
如果我使用8位无符号整型,我最终会将1旋转到MSB
因此,当值变为0时,使用移位并重新初始化您想要的位。C没有旋转操作,所以你至少要做两次移位。(我想c++也没有旋转)
x >>= 1;
if (! x) x = 0x08;
简单,简短,作用明显。是的,它将编译成一个分支(除非处理器有一个条件移动操作),但是直到您有分析器输出告诉您它很重要之前,您只是浪费了比处理器周期更多的时间来思考它。
使用8位数据类型(例如uint8_t
)。将其初始化为0。设置你想要设置的位在字节的后四位(例如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("%02hhxn", value); // Will print 08
value = rotate_one_right(value);
printf("%02hhxn", value); // Will print 04
value = rotate_one_right(value);
printf("%02hhxn", value); // Will print 02
value = rotate_one_right(value);
printf("%02hhxn", value); // Will print 01
value = rotate_one_right(value);
printf("%02hhxn", 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];
您只需输出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