Arduboyで固定小数点と回転と

固定小数点で使用するsin, cos関数を作成した。
f:id:raohu69:20180321185009p:plain

#include <Arduboy2.h>

Arduboy2 arduboy;

#define SCREEN_CENTER_X  64
#define SCREEN_CENTER_Y  32
#define SCREEN_LEFT     -64
#define SCREEN_RIGHT     63
#define SCREEN_TOP      -32
#define SCREEN_BOTTOM    31

typedef struct WordPoint {
  uint16_t x;
  uint16_t y;
};

typedef struct BytePoint {
  uint8_t xl; // 小数部
  int8_t  xh; // 整数部
  uint8_t yl; // 小数部
  int8_t  yh; // 整数部
};

typedef union FixPoint {
  WordPoint w;
  BytePoint b;
};

#define X   w.x   // xの整数・小数部をひっくるめた値にアクセスする
#define Y   w.y   // yの整数・小数部をひっくるめた値にアクセスする
#define XH  b.xh  // xの整数部にアクセスする
#define XL  b.xl  // xの小数部にアクセスする
#define YH  b.yh  // yの整数部にアクセスする
#define YL  b.yl  // yの小数部にアクセスする

void setup()
{
  arduboy.begin();
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop()
{
  if (!arduboy.nextFrame()) return;
  arduboy.clear();

  static FixPoint pos;
  static uint8_t angle;

  if (arduboy.pressed(LEFT_BUTTON)) {
    pos.X -= (1 << 8);
    if (pos.XH < SCREEN_LEFT) pos.XH = SCREEN_LEFT;
  } else if (arduboy.pressed(RIGHT_BUTTON)) {
    pos.X += (1 << 8);
    if (pos.XH > SCREEN_RIGHT) pos.XH = SCREEN_RIGHT;
  }
  if (arduboy.pressed(UP_BUTTON)) {
    pos.Y -= (1 << 8);
    if (pos.YH < SCREEN_TOP) pos.YH = SCREEN_TOP;
  } else if (arduboy.pressed(DOWN_BUTTON)) {
    pos.Y += (1 << 8);
    if (pos.YH > SCREEN_BOTTOM) pos.YH = SCREEN_BOTTOM;
  }
  arduboy.fillCircle(pos.XH + SCREEN_CENTER_X, pos.YH + SCREEN_CENTER_Y, 2, WHITE);

  angle++;

  FixPoint c;
  c.X = pos.X + GetCos(angle) * 30;
  c.Y = pos.Y + GetSin(angle) * 30;
  arduboy.fillCircle(c.XH + SCREEN_CENTER_X, c.YH + SCREEN_CENTER_Y, 5, WHITE);

  arduboy.print("X="); arduboy.print(pos.XH); arduboy.print("\n");
  arduboy.print("Y="); arduboy.print(pos.YH); arduboy.print("\n");

  arduboy.display();
}

const uint8_t sinTbl[] PROGMEM = {
  0x00, 0x06, 0x0c, 0x12, 0x19, 0x1f, 0x25, 0x2b, 0x31, 0x38, 0x3e, 0x44, 0x4a, 0x50, 0x56, 0x5c,
  0x61, 0x67, 0x6d, 0x73, 0x78, 0x7e, 0x83, 0x88, 0x8e, 0x93, 0x98, 0x9d, 0xa2, 0xa7, 0xab, 0xb0,
  0xb5, 0xb9, 0xbd, 0xc1, 0xc5, 0xc9, 0xcd, 0xd1, 0xd4, 0xd8, 0xdb, 0xde, 0xe1, 0xe4, 0xe7, 0xea,
  0xec, 0xee, 0xf1, 0xf3, 0xf4, 0xf6, 0xf8, 0xf9, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xff,
};

int16_t GetSin(uint8_t angle)
{
  int16_t result = 256; // 1 << 8
  uint8_t n = angle & 0x7f;

  if (n < 64) {
    result = pgm_read_byte(sinTbl + n);
  } else if (n > 64) {
    result = pgm_read_byte(sinTbl + 128 - n);
  }

  return (angle > 128) ? -result : result;
}

int16_t GetCos(uint8_t angle)
{
  return GetSin(angle + 64);
}


sinテーブルの作成ツール
固定小数点に合わせるため、1周を256度とみなし、その4分の1(64個)のテーブルを作成する。

sintbl.py

# -*- coding: utf-8 -*-

import math

for angle in range(64):
    v = math.sin(angle * math.pi / 128) * 256
    print(format(int(v), '#04x') + ',', end='')

    if angle % 16 == 15:
        print('')