我在这里做错了什么?- 位操作

huangapple go评论73阅读模式
英文:

What am I doing wrong here? - Bit Manipulation

问题

我有一个非常具体的问题。

我正在为学校做一个项目,简单地使用Arduino UNO上的5x7 LED矩阵制作一个乒乓球游戏。

问题出现在球的部分,我可以让它在X位置弹跳没有问题,但Y位置出现了一些奇怪的问题。

这里只是球的代码:

long prevMoveTime = millis(), currentMoveTime; // 异步drawBall方法的变量。
int ballPosX = B00100, ballPosY = B1110111; // 球的X和Y位置及其初始值。
bool ballDirX = true, ballDirY = true; // 球的方向(true = 左/上,false = 右/下)。

void setup() {
  for (int i = 0; i <= 13; i++) { pinMode(i, OUTPUT); }

  PORTB = ballPosX;
  PORTD = ballPosY;
}

void drawBall() {
  currentMoveTime = millis();

  switch (ballPosX) {
    case B10000: ballDirX = true; break;
    case B00001: ballDirX = false; break;
  }

  switch (ballPosY) {
    case B0111111: ballDirY = true; break;
    case B1111110: ballDirY = false; break;
  }

  if (currentMoveTime - prevMoveTime > 1000) {
    if (ballDirX) { ballPosX >>= 1; }
    else { ballPosX <<= 1; }

    PORTB = ballPosX;

    if (ballDirY) { ballPosY >>= 1; ballPosY |= B1000000; }
    else { ballPosY <<= 1; ballPosY |= B0000001; }

    PORTD = ballPosY;

    prevMoveTime = currentMoveTime;
  }
}

void loop() {
  drawBall();
}

基本上,有一些奇怪的问题,它似乎无法读取二进制值B0111111,因此ballDirY布尔值不会更改。

我尝试了几乎所有可能的方法:

  • 使用if而不是switch。
  • 交换案例。
  • 交换true/false分配。
  • 使用延迟而不是异步技巧。
  • 尝试使用串行监视器进行调试,但它显示给我与LED矩阵中所见无关的内容。
  • 尝试使用十进制数字而不是二进制。
  • 尝试使用'0b'而不是'B'来使用二进制值。

我得出的第一个结论是,B0111111值发生了某些变化,矩阵中的行明显接收到了信号,所以ballPosY = B0111111,但是switch要么无法读取它,要么ballDirY不会更改值。

我得出的第二个结论是,我可能只是愚蠢,可能有一些我没有注意到的东西lol。

提前感谢任何答案,如果我真的很蠢,请随时告诉我lmao。

英文:

I have a very specific issue.

I'm doing a project for school which is simply a Ping Pong game using a 5x7 Led Matrix in Arduino UNO.

The problem is with the part of the ball, I can make it bounce no problem in the X position, but there is something weird going in with the Y position.

Here is the code just for the ball:

long prevMoveTime = millis(), currentMoveTime; // Variables for asynchronous drawBall method.
int ballPosX = B00100, ballPosY = B1110111; // X &amp; Y positions of the ball with their initial values.
bool ballDirX = true, ballDirY = true; // Direction on the ball (true = left/up, false = right/down).

void setup() {
  for (int i = 0; i &lt;= 13; i++) { pinMode(i, OUTPUT); }

  PORTB = ballPosX;
  PORTD = ballPosY;
}

void drawBall() {
  currentMoveTime = millis();

  switch (ballPosX) {
    case B10000: ballDirX = true; break;
    case B00001: ballDirX = false; break;
  }

  switch (ballPosY) {
    case B0111111: ballDirY = true; break;
    case B1111110: ballDirY = false; break;
  }

  if (currentMoveTime - prevMoveTime &gt; 1000) {
    if (ballDirX) { ballPosX &gt;&gt;= 1; }
    else { ballPosX &lt;&lt;= 1; }

    PORTB = ballPosX;

    if (ballDirY) { ballPosY &gt;&gt;= 1; ballPosY |= B1000000; }
    else { ballPosY &lt;&lt;= 1; ballPosY |= B0000001; }

    PORTD = ballPosY;

    prevMoveTime = currentMoveTime;
  }
}

void loop() {
  drawBall();
}

So basically, there is something weird going on where it won't read the binary value B0111111 and thus the ballDirY bool won't change value.

I've tried about everything I could:

  • Using an if instead of a switch.
  • Swapping the cases.
  • Swapping the true/false assignments.
  • Using a delay instead of the asynchronous trick.
  • Tried using the serial monitor to debug but what it shows me has nothing to do with what I see in the led matrix.
  • Tried using decimal numbers instead of binary.
  • Tried using '0b' instead of 'B' to use the binary values.

The first conclusion I've come to is that something is happening with the B0111111 value, the row in the matrix does receive the signal so clearly ballPosY = B0111111, but the switch either won't read it or ballDirY won't change value.

The second conclusion I've come to is that I'm simply stupid and there is something I haven't noticed lol.

Thanks in advance for any answers and fell free to let me know if I'm just dumb lmao.

答案1

得分: 0

当您进行左移操作时,不会清除未使用的位(7-15)。在Arduino上,int 是16位的,因此,例如,当您进行 1000001 << 1 的左移操作时,结果是 10000010 而不是您所期望的 0000010

要清除这些位,您需要创建一个掩码,其中将要使用的位设置为1,而多余的位设置为0。在您的代码中,这可以是 B0000000001111111,然后使用 &(与)运算符与您的值进行按位与操作。

以下是一个示例程序,用于演示不清除位的情况:

#include <iostream>
#include <bitset>
#include <cstdint>

enum {UP, DOWN};
const uint16_t ALL_ROWS        = 0b1111111;
const uint16_t TOP_ROW_MASK    = 0b0111111;
const uint16_t BOTTOM_ROW_MASK = 0b1111110;
const uint16_t TOP_ROW         = 0b1000000;
const uint16_t BOTTOM_ROW      = 0b0000001;

int main()
{
    uint16_t ballPosY = 0b1110111;
    uint8_t ballDirY = UP;
    
    for (int i = 0; i < 20; i++) {
        if (ballPosY == TOP_ROW_MASK) {
            ballDirY = DOWN;
        }
        else if (ballPosY == BOTTOM_ROW_MASK) {
            ballDirY = UP;
        }
        
        if (ballDirY == DOWN) {
            ballPosY = (ballPosY >> 1) | TOP_ROW;
            std::cout << "DOWN: ";
        }
        else {
            ballPosY = ((ballPosY << 1) | BOTTOM_ROW);
            std::cout << "UP  : ";
        }
        
        // 使用 bitset 进行打印
        std::bitset<16> bits(ballPosY);
        std::cout << bits << '\n';
    }

    return 0;
}

输出结果为:

UP  : 0000000011101111
UP  : 0000000111011111
UP  : 0000001110111111
UP  : 0000011101111111
UP  : 0000111011111111
UP  : 0001110111111111
UP  : 0011101111111111
UP  : 0111011111111111
UP  : 1110111111111111
UP  : 1101111111111111
UP  : 1011111111111111
UP  : 0111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111

如果您将上面的代码中的以下部分:

ballPosY = ((ballPosY << 1) | BOTTOM_ROW);

替换为:

ballPosY = ((ballPosY << 1) | BOTTOM_ROW) & ALL_ROWS;

则输出将正确:

UP  : 0000000001101111
UP  : 0000000001011111
UP  : 0000000000111111
DOWN: 0000000001011111
DOWN: 0000000001101111
DOWN: 0000000001110111
DOWN: 0000000001111011
DOWN: 0000000001111101
DOWN: 0000000001111110
UP  : 0000000001111101
UP  : 0000000001111011
UP  : 0000000001110111
UP  : 0000000001101111
UP  : 0000000001011111
UP  : 0000000000111111
DOWN: 0000000001011111
DOWN: 0000000001101111
DOWN: 0000000001110111
DOWN: 0000000001111011
DOWN: 0000000001111101

注意:在我的代码中,我使用了一些常量来使代码更易读。我还使用 0b 来指定二进制数,因为我的编译器不支持 B。另外,我使用了 uint16_t 而不是 int 来确保值是16位,就像在Arduino上一样。它还是无符号的。我建议始终使用无符号类型来进行位掩码操作。

英文:

When you shift left, you are not clearing out unused bits (7-15). On Arduino, int is 16 bits, so, for example, when you shift 1000001 &lt;&lt; 1 you get 10000010 and not 0000010, as you seem to be expecting.

To clear the bits, make a mask with the bits you want to use set and the extra bits clear. In your code this would be B0000000001111111 Then, &amp; (AND) that with your value.

Here's an example program that DOES NOT clear the bits, to demonstrate the error:

#include &lt;iostream&gt;
#include &lt;bitset&gt;
#include &lt;cstdint&gt;

enum {UP, DOWN};
const uint16_t ALL_ROWS        = 0b1111111;
const uint16_t TOP_ROW_MASK    = 0b0111111;
const uint16_t BOTTOM_ROW_MASK = 0b1111110;
const uint16_t TOP_ROW         = 0b1000000;
const uint16_t BOTTOM_ROW      = 0b0000001;

int main()
{
    uint16_t ballPosY = 0b1110111;
    uint8_t ballDirY = UP;
    
    for (int i = 0; i &lt; 20; i++) {
        if (ballPosY == TOP_ROW_MASK) {
            ballDirY = DOWN;
        }
        else if (ballPosY == BOTTOM_ROW_MASK) {
            ballDirY = UP;
        }
        
        if (ballDirY == DOWN) {
            ballPosY = (ballPosY &gt;&gt; 1) | TOP_ROW;
            std::cout &lt;&lt; &quot;DOWN: &quot;;
        }
        else {
            ballPosY = ((ballPosY &lt;&lt; 1) | BOTTOM_ROW);
            std::cout &lt;&lt; &quot;UP  : &quot;;
        }
        
        // Use a bitset for printing
        std::bitset&lt;16&gt; bits(ballPosY);
        std::cout &lt;&lt; bits &lt;&lt; &#39;\n&#39;;
    }

    return 0;
}

And the output is:

UP  : 0000000011101111
UP  : 0000000111011111
UP  : 0000001110111111
UP  : 0000011101111111
UP  : 0000111011111111
UP  : 0001110111111111
UP  : 0011101111111111
UP  : 0111011111111111
UP  : 1110111111111111
UP  : 1101111111111111
UP  : 1011111111111111
UP  : 0111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111
UP  : 1111111111111111

If you take the code above and replace

ballPosY = ((ballPosY &lt;&lt; 1) | BOTTOM_ROW);

With

ballPosY = ((ballPosY &lt;&lt; 1) | BOTTOM_ROW) &amp; ALL_ROWS;

the output will be correct:

UP  : 0000000001101111
UP  : 0000000001011111
UP  : 0000000000111111
DOWN: 0000000001011111
DOWN: 0000000001101111
DOWN: 0000000001110111
DOWN: 0000000001111011
DOWN: 0000000001111101
DOWN: 0000000001111110
UP  : 0000000001111101
UP  : 0000000001111011
UP  : 0000000001110111
UP  : 0000000001101111
UP  : 0000000001011111
UP  : 0000000000111111
DOWN: 0000000001011111
DOWN: 0000000001101111
DOWN: 0000000001110111
DOWN: 0000000001111011
DOWN: 0000000001111101

Note: in my code I use a bunch of constants to make the code easier to read. I also use 0b to designate a binary number since my compiler does not support B.

Also, I use uint16_t instead of int to ensure the value is 16 bits, like on the Arduino. It is also unsigned. I recommend always using unsigned for bit masks.

huangapple
  • 本文由 发表于 2023年7月18日 00:14:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76706342.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定