Arduino | 由八按键控制的俄罗斯方块

首先声明!请不要跳过!

1. 存在的问题:由于我硬件本身存在的问题,程序内的所有delay()以及与延时、时间有关的函数参数都是适配硬件的问题设置的,所有请在使用这些代码的时候根据自身硬件配置及开发情况重新设置有关时间的参数!!!(游戏逻辑与源码来自https://github.com/AJRussell/Tiny-Tetris,感谢开源。)

曾使用过joystick模块来进行开发,但手感实在是稀烂(虽然按键的手感也不怎么样),但摇杆x轴向参数区间实在过小,容易误判或响应过慢,所以使用了该8按键模块。

 

这篇文章算是为持有这个8按键模块或是想用成型按键的开发者编写,模块型号七星虫k3/k4物联网开发套件 - 8按键模块,为该模块较为特殊,电路不同于单个按键模块,8个按键只需要1根信号线便可运作,在没有头绪的情况下最好使用专属的代码控制。

如果您对游戏内容不感兴趣,可以直接到  5.  8按键模块控制程序 查看相应的控制代码。

2. 硬件连接图:

设备:Arduino UNO R3 极其扩展板,OLED显示屏,七星虫物联网开发套件8按键模块,无源蜂鸣器;

设备连接如图:

OLED <-> IC;

蜂鸣器 <-> digital 8;

8按键模块 <-> analog 0;

 

3. 主程序:

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#include <SPI.h>

#include <Wire.h>

#include "TetrisTheme.cpp"

#include "dpad.cpp"

//#define OLED_ADDRESS            0x3C //you may need to change this, this is the OLED I2C address.  

#define OLED_ADDRESS            0x3C//you may need to change this, this is the OLED I2C address.  

#define OLED_COMMAND                  0x80

#define OLED_DATA                 0x40

#define OLED_DISPLAY_OFF          0xAE

#define OLED_DISPLAY_ON                 0xAF

#define OLED_NORMAL_DISPLAY       0xA6

#define OLED_INVERSE_DISPLAY      0xA7

#define OLED_SET_BRIGHTNESS         0x81

#define OLED_SET_ADDRESSING         0x20

#define OLED_HORIZONTAL_ADDRESSING  0x00

#define OLED_VERTICAL_ADDRESSING  0x01

#define OLED_PAGE_ADDRESSING          0x02

#define OLED_SET_COLUMN                 0x21

#define OLED_SET_PAGE                 0x22

Adafruit_SSD1306 oled(128, 64, &Wire, -1);

// the tetris blocks

const byte Blocks[7][2] PROGMEM = {

  { 0B01000100, 0B01000100 },

  { 0B11000000, 0B01000100 },

  { 0B01100000, 0B01000100 },

  { 0B01100000, 0B00000110 },

  { 0B11000000, 0B00000110 },

  { 0B01000000, 0B00001110 },

  { 0B01100000, 0B00001100 }

};

// the numbers for score, To do: create letter fonts

const byte NumberFont[10][8] PROGMEM = {

  { 0x00, 0x1c, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x1c },

  { 0x00, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x0c, 0x08 },

  { 0x00, 0x3e, 0x02, 0x04, 0x18, 0x20, 0x22, 0x1c },

  { 0x00, 0x1c, 0x22, 0x20, 0x18, 0x20, 0x22, 0x1c },

  { 0x00, 0x10, 0x10, 0x3e, 0x12, 0x14, 0x18, 0x10 },

  { 0x00, 0x1c, 0x22, 0x20, 0x20, 0x1e, 0x02, 0x3e },

  { 0x00, 0x1c, 0x22, 0x22, 0x1e, 0x02, 0x04, 0x18 },

  { 0x00, 0x04, 0x04, 0x04, 0x08, 0x10, 0x20, 0x3e },

  { 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c },

  { 0x00, 0x0c, 0x10, 0x20, 0x3c, 0x22, 0x22, 0x1c }

};



 

  // "Tiny Tetris" upside-down text binarized from http://www.dcode.fr/binary-image

const byte welcomeScreen[16][5] PROGMEM = {

    B01110011, B10100010, B00100011, B11100010, B00000000,

    B10001001, B00100010, B00100000, B00100010, B00000000,

    B10000001, B00100010, B00100000, B00100010, B00000000,

    B01110001, B00011110, B00100001, B11100010, B00000000,

    B00001001, B00100010, B00100000, B00100010, B00000000,

    B10001001, B00100010, B00100000, B00100010, B00000000,

    B01110011, B10011110, B11111011, B11101111, B10000000,

    B00000000, B00000000, B00000000, B00000000, B00000000,

    B00000000, B00000000, B00000000, B00000000, B00000000,

    B00000000, B10001000, B10111000, B10000000, B00000000,

    B00000000, B10001100, B10010000, B10000000, B00000000,

    B00000000, B10001100, B10010000, B10000000, B00000000,

    B00000001, B01001010, B10010000, B10000000, B00000000,

    B00000010, B00101001, B10010000, B10000000, B00000000,

    B00000010, B00101001, B10010000, B10000000, B00000000,

    B00000010, B00101000, B10111011, B11100000, B00000000

   

};

// Tetris Illustration upside-down image binarized from http://www.dcode.fr/binary-image

const byte tetrisLogo[40][8] PROGMEM =  {

    B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111,

    B11101101, B10111111, B11111111, B11111111, B11111111, B01111111, B11111001, B11100111,

    B11101101, B00110100, B11111111, B11111111, B11111110, B01110011, B11110001, B11100111,

    B10111000, B01010101, B11111111, B11111111, B11111000, B01110011, B11100001, B11100111,

    B10011110, B10110011, B10110011, B11100011, B11100100, B00100011, B11100011, B11110011,

    B10001111, B00010011, B00110001, B11110001, B11110100, B00100011, B11100011, B11110011,

    B10001111, B00000111, B01110001, B11110000, B11110010, B00110011, B11100011, B11110001,

    B10001111, B00000110, B01100001, B11111000, B11111010, B00000001, B11000001, B11100001,

    B10000110, B00001110, B11100000, B11111000, B01111001, B00000001, B11000000, B11000001,

    B10000110, B00001100, B11100000, B11111100, B01111001, B00000001, B11000000, B00000001,

    B10000110, B00001100, B11110000, B11111100, B01111001, B00000000, B10000000, B00000001,

    B10000110, B00001100, B11110000, B01111100, B01111001, B00000000, B10000000, B00000001,

    B10000110, B00000110, B11110000, B01111100, B01111001, B00000000, B10000000, B00000001,

    B10000110, B00000111, B01111000, B01111000, B01110010, B00000000, B10000000, B00000001,

    B10001101, B00000011, B00111000, B01111000, B01110010, B00000000, B00000000, B00000001,

    B10011001, B10000011, B10111000, B01111000, B11110100, B00000000, B00000000, B00000001,

    B10011001, B10000001, B10011100, B01110001, B11101100, B00000000, B00000000, B00000001,

    B10001001, B00000000, B11111100, B01110001, B11011000, B00000000, B00000000, B00000001,

    B10001011, B00000000, B01111100, B01100011, B10110000, B00000000, B00000000, B00000001,

    B10000110, B00000000, B00110100, B11100111, B01100000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00011110, B11100110, B01000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00001110, B11001100, B10000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000110, B11011011, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000111, B11010010, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000011, B10100100, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000001, B11111000, B00000000, B00000000, B00110000, B00000001,

    B10000000, B00000000, B00000000, B11110000, B00000000, B00000000, B00110000, B00000001,

    B10000000, B00000000, B00000000, B11010000, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000000, B01110000, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B10000000, B01100000, B00000000, B00000000, B00000000, B00000001,

    B10000011, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,

    B10000011, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000000, B11110000, B00000000, B00000000, B00000000, B00010001,

    B10000000, B00000000, B00000000, B11001000, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000001, B10001000, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000001, B10001000, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000000, B10010000, B00000000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000000, B11110000, B00001000, B00000000, B00000000, B00000001

};

// Tetris Brick upside-down image binarized from http://www.dcode.fr/binary-image

const byte brickLogo[36][8] PROGMEM= {

    B10000000, B00000000, B00000000, B00000000, B00000000, B11111111, B11111100, B00000001,

    B10000000, B00000111, B11111100, B11111111, B11111110, B11111111, B11111100, B00000001,

    B10000011, B11111111, B11111110, B11111111, B11111111, B01111111, B11111110, B00000001,

    B10000011, B11111111, B11111110, B01111111, B11111111, B00111111, B11111111, B00000001,

    B10000011, B11111111, B11111111, B01111111, B11111111, B10111111, B11111111, B10000001,

    B10001001, B11111111, B11111111, B00111111, B11111111, B10011111, B11111111, B10000001,

    B10001101, B11111111, B11111111, B10111111, B11111111, B11001111, B11111111, B11000001,

    B10001101, B11111111, B11111111, B10011111, B11111111, B11101111, B11111111, B11100001,

    B10001100, B11111111, B11111111, B11011111, B11111111, B11100111, B11111111, B11110001,

    B10001110, B11111111, B11111111, B11001111, B11111111, B11110111, B11111111, B11110001,

    B10001110, B11111111, B11111111, B11101111, B11111111, B11111011, B11111111, B00000001,

    B10001110, B01111111, B11111111, B11101111, B11111111, B11100000, B00000000, B00010001,

    B10001111, B01111111, B11111111, B11100100, B00000000, B00000001, B11111111, B11110001,

    B10001111, B00111111, B10000000, B00000000, B00111111, B11111011, B11111111, B11110001,

    B10011111, B00000000, B00000111, B11110111, B11111111, B11110011, B11111111, B11100001,

    B10001111, B00111111, B11111111, B11100111, B11111111, B11110111, B11111111, B11000001,

    B10001111, B00111111, B11111111, B11101111, B11111111, B11100111, B11111111, B11000001,

    B10001111, B01111111, B11111111, B11101111, B11111111, B11101111, B11111111, B10000001,

    B10001111, B01111111, B11111111, B11001111, B11111111, B11001111, B11111111, B10000001,

    B10000111, B01111111, B11111111, B11011111, B11111111, B11011111, B11111111, B00000001,

    B10000110, B01111111, B11111111, B11011111, B11111111, B11011111, B11111111, B00000001,

    B10000110, B01111111, B11111111, B10011111, B11111111, B10111111, B11111110, B00000001,

    B10000010, B11111111, B11111111, B10111111, B11111111, B10111111, B11111000, B00000001,

    B10000010, B11111111, B11111111, B10111111, B11111111, B00110000, B00000000, B00000001,

    B10000010, B11111111, B11111111, B00111111, B11100000, B00000000, B00000000, B00000001,

    B10000000, B11111111, B11111111, B00000000, B00000110, B00000000, B00000000, B00000001,

    B10000000, B11111111, B11000000, B00000111, B11111110, B00000000, B00000000, B00000001,

    B10000000, B10000000, B00001110, B01111111, B11111100, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00111110, B11111111, B11111100, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00011110, B11111111, B11111100, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00011100, B11111111, B11111000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00011101, B11111111, B11111000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00001101, B11111111, B11110000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00001001, B11111111, B11110000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000011, B11111111, B11100000, B00000000, B00000000, B00000001,

    B10000000, B00000000, B00000011, B11110000, B00000000, B00000000, B00000000, B00000001

};


 

#define KEY_MIDDLE  0

#define KEY_LEFT    1

#define KEY_RIGHT   2

#define KEY_DOWN    3

#define KEY_ROTATE  4

#define PIEZO_PIN   8                          

#define LED_PIN     53

#define KEYPAD_PIN  A0

//struct for pieces

struct PieceSpace {

  byte umBlock[4][4];

  char Row;

  char Coloum;

};

//Globals, is a mess. To do: tidy up and reduce glogal use if possible

byte pageArray[8] = { 0 };

byte scoreDisplayBuffer[8][6] = { { 0 }, { 0 } };

byte nextBlockBuffer[8][2] = { { 0 }, { 0 } };

bool optomizePageArray[8] = { 0 };

byte blockColoum[10] = { 0 };

byte tetrisScreen[14][25] = { { 1 } , { 1 } };

PieceSpace currentPiece = { 0 };

PieceSpace oldPiece = { 0 };

byte nextPiece = 0;

//keyPress key = { 0 };

bool gameOver = false;

unsigned long moveTime = 0;

int pageStart = 0;

int pageEnd = 0;

int score = 0;

int acceleration = 0;

int level = 0;

int levellineCount = 0;

int dropDelay = 1000;

int lastKey = 0;

// I2C

void OLEDCommand(byte command) {

  Wire.beginTransmission(OLED_ADDRESS);

  Wire.write(OLED_COMMAND);

  Wire.write(command);

  Wire.endTransmission();

}


 

void OLEDData(byte data) {

  Wire.beginTransmission(OLED_ADDRESS);

  Wire.write(OLED_DATA);

  Wire.write(data);

  Wire.endTransmission();

}


 

void setup() {

  Serial.begin(9600);

  while (!Serial);

  Wire.begin();

  Wire.setClock(400000);

  pinMode(PIEZO_PIN, OUTPUT);

  pinMode(LED_PIN, OUTPUT);

  OLEDCommand(OLED_DISPLAY_OFF);

  delay(12500/1.5);

  OLEDCommand(OLED_DISPLAY_ON);

  delay(12500/1.5);

  OLEDCommand(OLED_NORMAL_DISPLAY);

  delay(12500/1.5);

  OLEDCommand(0x8C);

  delay(12500/1.5);

  OLEDCommand(0x14);

  delay(12500/1.5);

  OLEDCommand(OLED_NORMAL_DISPLAY);

  fillTetrisScreen(0);

  randomSeed(analogRead(7)); /// To do: create a decent random number generator.

  // blink led

  digitalWrite(LED_PIN, HIGH);

  delay(12500/1.5);

  digitalWrite(LED_PIN, LOW);

  delay(12500/1.5);

  digitalWrite(LED_PIN, HIGH);

  delay(12500/1.5);

  digitalWrite(LED_PIN, LOW);

}


 

void fillTetrisArray(byte value) {

  for (char r = 0; r < 24; r++) {

    for (char c = 0; c < 14; c++) {

      tetrisScreen[c][r] = value;

    }

  }

  for (char r = 21; r < 24; r++) {

    for (char c = 0; c < 14; c++) {

      tetrisScreen[c][r] = 0;

    }

  }

}


 

void fillTetrisScreen(byte value) {

  for (int r = 1; r < 21; r++) {

    for (int c = 2; c < 12; c++) {

      tetrisScreen[c][r] = value;

    }

  }

}


 

void drawTetrisScreen() {

  for (byte r = 1; r < 21; r++) {

    //loop through rows to see if there is data to be sent

    for (byte c = 2; c < 12; c++) {

      if ((tetrisScreen[c][r] == 2) | (tetrisScreen[c][r] == 3)) {

        //send line to screen

        for (byte i = 0; i < 10; i++) {

          blockColoum[i] = tetrisScreen[i + 2][r];

          //clear delete block

          if (tetrisScreen[i + 2][r] == 3) tetrisScreen[i + 2][r] = 0;

        }

        drawTetrisLine((r - 1) * 6);

        break;

      }

    }

  }

}


 

void drawTetrisTitle(bool blank = false) {

  byte byteval;

  //set Vertical addressing mode and column - page start end

  OLEDCommand(OLED_SET_ADDRESSING);

  OLEDCommand(OLED_VERTICAL_ADDRESSING);

  OLEDCommand(OLED_SET_COLUMN);

  OLEDCommand( 50 );                //Set column start

  OLEDCommand( 66 );              //Set column end

  OLEDCommand(OLED_SET_PAGE);

  OLEDCommand( 1 );               //Set page start

  OLEDCommand( 5 );               //Set page end

  for (int r = 0; r <16; r++) {

    for (int c = 4; c >=0; c--) {

      if(blank) {

        OLEDData(0);

      }else {

        byteval = pgm_read_byte(&welcomeScreen[r][c]);

        OLEDData(byteval);

      }

    }

  }

  OLEDCommand(OLED_SET_COLUMN);

  OLEDCommand( 1 );                //Set column start

  OLEDCommand( 42 );              //Set column end

  OLEDCommand(OLED_SET_PAGE);

  OLEDCommand( 0 );               //Set page start

  OLEDCommand( 7 );               //Set page end

  for (int r = 0; r <40; r++) {

    for (int c = 7; c >=0; c--) {

      if(blank) {

        OLEDData(0);

      }else {

        byteval = pgm_read_byte(&tetrisLogo[r][c]);

        OLEDData(byteval);

      }

    }

  }

  OLEDCommand(OLED_SET_COLUMN);

  OLEDCommand( 75 );                //Set column start

  OLEDCommand( 116 );              //Set column end

  OLEDCommand(OLED_SET_PAGE);

  OLEDCommand( 0 );               //Set page start

  OLEDCommand( 7 );               //Set page end

  for (int r = 0; r <36; r++) {

    for (int c = 7; c >=0; c--) {

      if(blank) {

        OLEDData(0);

      }else {

        byteval = pgm_read_byte(&brickLogo[r][c]);

        OLEDData(byteval);

      }

    }

  }

  //brickLogo[36][8]

}


 

void drawTetrisLine(byte x) {

  //fill array with blocks based on blockRow

  //clear page and Optimize array

  memset(optomizePageArray, 0, 8);   ///review this... declare them here? interesting question...

  memset(pageArray, 0, 8);

  x++; // up one

  //*********Column 0***********

  //draw block

  if (blockColoum[0] == 2 | blockColoum[0] == 1) {

    pageArray[0] = pageArray[0] | B11111001;

    optomizePageArray[0] = 1;

  }

  //delete block

  if (blockColoum[0] == 3) {

    pageArray[0] = pageArray[0] | B00000001; //create side wall

    pageArray[0] = pageArray[0] & B00000111;

    optomizePageArray[0] = 1;

  }

  //*********Column 1***********

  if (blockColoum[1] == 2 | blockColoum[1] == 1) {

    pageArray[1] = pageArray[1] | B00111110;

    optomizePageArray[1] = 1;

  }

  //delete block

  if (blockColoum[1] == 3) {

    pageArray[1] = pageArray[1] & B11000001;

    optomizePageArray[1] = 1;

  }

  //*********Column 2***********

  if (blockColoum[2] == 2 | blockColoum[2] == 1) {

    pageArray[1] = pageArray[1] | B10000000;

    optomizePageArray[1] = 1;

    pageArray[2] = pageArray[2] | B00001111;

    optomizePageArray[2] = 1;

  }

  //delete block

  if (blockColoum[2] == 3) {

    pageArray[1] = pageArray[1] & B01111111;

    optomizePageArray[1] = 1;

    pageArray[2] = pageArray[2] & B11110000;

    optomizePageArray[2] = 1;

  }

  //*********Column 3***********

  if (blockColoum[3] == 2 | blockColoum[3] == 1) {

    pageArray[2] = pageArray[2] | B11100000;

    optomizePageArray[2] = 1;

    pageArray[3] = pageArray[3] | B00000011;

    optomizePageArray[3] = 1;

  }

  //delete block

  if (blockColoum[3] == 3) {

    pageArray[2] = pageArray[2] & B00011111;

    optomizePageArray[2] = 1;

    pageArray[3] = pageArray[3] & B11111100;

    optomizePageArray[3] = 1;

  }

  //*********Column 4***********

  if (blockColoum[4] == 2 | blockColoum[4] == 1) {

    pageArray[3] = pageArray[3] | B11111000;

    optomizePageArray[3] = 1;

  }

  //delete block

  if (blockColoum[4] == 3) {

    pageArray[3] = pageArray[3] & B00000111;

    optomizePageArray[3] = 1;

  }

  //*********Column 5***********

  if (blockColoum[5] == 2 | blockColoum[5] == 1) {

    pageArray[4] = pageArray[4] | B00111110;

    optomizePageArray[4] = 1;

  }

  //delete block

  if (blockColoum[5] == 3) {

    pageArray[4] = pageArray[4] & B11000001;

    optomizePageArray[4] = 1;

  }

  //*********Column 6***********

  if (blockColoum[6] == 2 | blockColoum[6] == 1) {

    pageArray[4] = pageArray[4] | B10000000;

    optomizePageArray[4] = 1;

    pageArray[5] = pageArray[5] | B00001111;

    optomizePageArray[5] = 1;

  }

  //delete block

  if (blockColoum[6] == 3) {

    pageArray[4] = pageArray[4] & B01111111;

    optomizePageArray[4] = 1;

    pageArray[5] = pageArray[5] & B11110000;

    optomizePageArray[5] = 1;

  }

  //*********Column 7***********

  if (blockColoum[7] == 2 | blockColoum[7] == 1) {

    pageArray[5] = pageArray[5] | B11100000;

    optomizePageArray[5] = 1;

    pageArray[6] = pageArray[6] | B00000011;

    optomizePageArray[6] = 1;

  }

  if (blockColoum[7] == 3) {

    pageArray[5] = pageArray[5] & B00011111;

    optomizePageArray[5] = 1;

    pageArray[6] = pageArray[6] & B11111100;

    optomizePageArray[6] = 1;

  }

  //*********Column 8***********

  if (blockColoum[8] == 2 | blockColoum[8] == 1) {

    pageArray[6] = pageArray[6] | B11111000;

    optomizePageArray[6] = 1;

  }

  //delete block

  if (blockColoum[8] == 3) {

    pageArray[6] = pageArray[6] & B00000111;

    optomizePageArray[6] = 1;

  }

  //*********Column 9***********

  if (blockColoum[9] == 2 | blockColoum[9] == 1) {

    pageArray[7] = pageArray[7] | B10111110;

    optomizePageArray[7] = 1;

  }

  if (blockColoum[9] == 3) {

    pageArray[7] = pageArray[7] | B10000000;//create side wall

    pageArray[7] = pageArray[7] & B11000001;

    optomizePageArray[7] = 1;

  }

  //Optimize - figure out what page array has data

  for (int page = 0; page < 8; page++) {

    if (optomizePageArray[page]) {

      //block found set page start

      pageStart = page;

      break;

    }

  }

  for (int page = 7; page >= 0; page--) {

    if (optomizePageArray[page]) {

      //block found set page end

      pageEnd = page;

      break;

    }

  }

  //set Vertical addressing mode and column - page start end

  OLEDCommand(OLED_SET_ADDRESSING);

  OLEDCommand(OLED_VERTICAL_ADDRESSING);

  OLEDCommand(OLED_SET_COLUMN);

  OLEDCommand(x);

  OLEDCommand(x + 4);

  OLEDCommand(OLED_SET_PAGE);

  OLEDCommand(pageStart);

  OLEDCommand(pageEnd);

  //send the array 5 times

  for (int c = 0; c < 5; c++) {

    for (int p = pageStart; p <= pageEnd; p++) {

      OLEDData(pageArray[p]);

    }

  }

}


 

void loadPiece(byte pieceNumber, byte row, byte coloum, bool loadScreen) {

  //load the piece from piece array to screen

  byte pieceRow = 0;

  byte pieceColoum = 0;

  byte c = 0;

  // load piece from progmem

  byte byte_in;

  bool piece_out[4][4];

  byte piece_bit[2] = {0,0};

  for(int i=0;i<2;i++) {

   

    byte_in = pgm_read_byte(&Blocks[pieceNumber-1][i]);

   

    for( byte mask = 1; mask; mask <<=1) {

      if(mask & byte_in) {

        piece_out[piece_bit[0]][piece_bit[1]] = 1;

      } else {

        piece_out[piece_bit[0]][piece_bit[1]] = 0;

      }

      piece_bit[1]++;

      if(piece_bit[1]>=4) {

        piece_bit[1]=0;

        piece_bit[0]++;

      }

    }

  }

  memcpy(currentPiece.umBlock, piece_out, 16);

  currentPiece.Row = row;

  currentPiece.Coloum = coloum;

  if (loadScreen) {

    oldPiece = currentPiece;

    for (c = coloum; c < coloum + 4; c++) {

      for (int r = row; r < row + 4; r++) {

        if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 2;

        pieceRow++;

      }

      pieceRow = 0;

      pieceColoum++;

    }

  }

}


 

void drawPiece() {

  char coloum;

  char row;

  byte pieceRow = 0;

  byte pieceColoum = 0;

  char c = 0;

  // delete blocks first

  coloum = oldPiece.Coloum;

  row = oldPiece.Row;

  for (c = coloum; c < coloum + 4; c++) {

    for (char r = row; r < row + 4; r++) {

      if (oldPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 3;

      pieceRow++;

    }

    pieceRow = 0;

    pieceColoum++;

  }

  //draw new blocks

  pieceRow = 0;

  pieceColoum = 0;

  c = 0;

  coloum = currentPiece.Coloum;

  row = currentPiece.Row;

  for (c = coloum; c < coloum + 4; c++) {

    for (char r = row; r < row + 4; r++) {

      if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 2;

      pieceRow++;

    }

    pieceRow = 0;

    pieceColoum++;

  }

}


 

void drawLandedPiece() {

  char coloum;

  char row;

  byte pieceRow = 0;

  byte pieceColoum = 0;

  char c = 0;

  // Landed pieces are 1

  coloum = currentPiece.Coloum;

  row = currentPiece.Row;

  for (c = coloum; c < coloum + 4; c++) {

    for (int r = row; r < row + 4; r++) {

      if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 1;

      pieceRow++;

    }

    pieceRow = 0;

    pieceColoum++;

  }

  processCompletedLines();

}


 

bool led = true;


 

void RotatePiece() {

  byte i, j;

  byte umFig[4][4] = { 0 };

  memcpy(oldPiece.umBlock, currentPiece.umBlock, 16);

  oldPiece.Row = currentPiece.Row;

  oldPiece.Coloum = currentPiece.Coloum;

  for (i = 0; i < 4; ++i) {

    for (j = 0; j < 4; ++j) {

      umFig[j][i] = currentPiece.umBlock[4 - i - 1][j];

    }

  }

  oldPiece = currentPiece;

  memcpy(currentPiece.umBlock, umFig, 16);

  if (checkColloision()) currentPiece = oldPiece;

  // no need for this...

  if (led) {

    digitalWrite(LED_PIN, HIGH);

    led = false;

  }

  delay(1);

  digitalWrite(LED_PIN, LOW);

  if (led == false) {

    digitalWrite(LED_PIN, LOW);

    led = true;

  }

}


 

bool movePieceDown() {

  bool pieceLanded = false;

  char rndPiece = 0;

  oldPiece = currentPiece;

  currentPiece.Row = currentPiece.Row - 1;

  //check collision

  if (checkColloision()) {

    // its at the bottom make it a landed piece and start new piece

    currentPiece = oldPiece; // back to where it was

    drawLandedPiece();

    pieceLanded = true;

  }

  if (pieceLanded) {

    loadPiece(nextPiece, 19, 4, false);

    acceleration = 0;

    if (checkColloision()) {

      gameOver = true;

    } else {

      loadPiece(nextPiece, 19, 4, true);

      acceleration = 0;//reset acceleration as there is a new piece

    }

    nextPiece = random(1, 8);

    setNextBlock(nextPiece);

  }

}


 

void movePieceLeft() {

  oldPiece = currentPiece;

  currentPiece.Coloum = currentPiece.Coloum - 1;

  //check collision

  if (checkColloision())  {

    currentPiece = oldPiece; // back to where it was

  }

}


 

void movePieceRight() {

  oldPiece = currentPiece;

  currentPiece.Coloum = currentPiece.Coloum + 1;

  //check collision

  if (checkColloision())  {

    currentPiece = oldPiece; // back to where it was

  }

}


 

bool checkColloision() {

  byte pieceRow = 0;

  byte pieceColoum = 0;

  char c = 0;

  char coloum = currentPiece.Coloum;

  char row = currentPiece.Row;

  //scan across piece and translate to Tetris array and check Collisions.

  for (c = coloum; c < coloum + 4; c++) {

    for (char r = row; r < row + 4; r++) {

      if (currentPiece.umBlock[pieceColoum][pieceRow]) {

        if (tetrisScreen[c][r] == 1) return true; //is it on landed blocks?

      }

      pieceRow++;

    }

    pieceRow = 0;

    pieceColoum++;

  }

  return false;

}


 

void processCompletedLines() {

  char rowCheck = 0;

  char coloumCheck = 0;

  bool fullLine = false;

  bool noLine = true;

  char linesProcessed = 0;

  char clearedLines = 0;

  char topRow = 0;

  char bottomRow = 0;

  char currentRow = 0;

  int amountScored = 0;

  if (currentPiece.Row < 1)bottomRow = 1;

  else bottomRow = currentPiece.Row;

  for (int rowCheck = bottomRow; rowCheck < currentPiece.Row + 4; rowCheck++) {

    bool fullLine = true;

    for (coloumCheck = 2; coloumCheck < 12; coloumCheck++) {

      if (tetrisScreen[coloumCheck][rowCheck] == 0) {

        fullLine = false;

        break;

      }

    }

   

    if (fullLine) {

      //make line values 3's and render

      for (char c = 2; c < 12; c++) {

        tetrisScreen[c][rowCheck] = 3;

      }

      bottomRow = rowCheck + 1;

      //line is now all 0's

      linesProcessed++;

      delay(77); // animation :)

    }

    drawTetrisScreen();

  }

  //******all lines are 0's and have been removed from the screen

  if (linesProcessed) {

    clearedLines = linesProcessed;

    while (clearedLines) {

      for (currentRow = 1; currentRow < 20; currentRow++) {

        noLine = true;

        for (char c = 2; c < 12; c++) {

          if (tetrisScreen[c][currentRow])  noLine = false;

        }

        if (noLine) {

          //move all lines down

          for (int r = currentRow + 1; r < 20; r++) {

            for (char c = 2; c < 12; c++) {

              if (tetrisScreen[c][r]) tetrisScreen[c][r - 1] = 2;

              else tetrisScreen[c][r - 1] = 3;

            }

          }

        }

      }

      //make the 2's 1's

      for (char r = 1; r < 24; r++) {

        for (char c = 2; c < 12; c++) {

          if (tetrisScreen[c][r] == 2)tetrisScreen[c][r] = 1;

        }

      }

      clearedLines--;

      drawTetrisScreen();

      tone(PIEZO_PIN, 1000, 50);

      delay(12500/1.5);

      tone(PIEZO_PIN, 2000, 50);

      delay(12500/1.5);

      tone(PIEZO_PIN, 500, 50);

      delay(12500/1.5);

    }

  }

  // ************** process score *******************

  switch (linesProcessed) {

    case 1:   amountScored = 40 * (level + 1); break;

    case 2:   amountScored = 100 * (level + 1); break;

    case 3:   amountScored = 300 * (level + 1); break;

    case 4:   amountScored = 1200 * (level + 1);

      //do 4 line affect

      OLEDCommand(OLED_INVERSE_DISPLAY);

      delay(12500/1.5);

      OLEDCommand(OLED_NORMAL_DISPLAY);

    break;

  }

  //score animation

  for (long s = score; s < score + amountScored; s = s + (5 * (level + 1))) {

    setScore(s, false);

  }

  score = score + amountScored;

  setScore(score, false);

  //****update level line count

  levellineCount = levellineCount + linesProcessed;

  if (levellineCount > 10) {

    level++;

    levellineCount = 0;

    //do level up affect

    OLEDCommand(OLED_INVERSE_DISPLAY);

    delay(12500/1.5);

    OLEDCommand(OLED_NORMAL_DISPLAY);

    for (int i = 250; i < 2500; i += 100) {

      tone(PIEZO_PIN, i, 5);

      delay(12500/1.5);

      tone(PIEZO_PIN, i / 2, 5);

      delay(12500/1.5);

    }

    OLEDCommand(OLED_INVERSE_DISPLAY);

    delay(12500/1.5);

    OLEDCommand(OLED_NORMAL_DISPLAY);

  }

  //make the 2's 1's

  for (char r = bottomRow; r <= topRow; r++) {

    for (char c = 2; c < 12; c++) {

      if (tetrisScreen[c][r]) {

        tetrisScreen[c][r] = 1;

      }

    }

  }

}


 

void tetrisScreenToSerial() {

  //for debug

  for (int r = 0; r < 24; r++) {

    for (int c = 0; c < 14; c++) {

      Serial.print(tetrisScreen[c][r], DEC);

    }

    Serial.println();

  }

  Serial.println();

}


 

bool processKeys() {

  bool keypressed = true;

  int leftRight = 300 - acceleration;

  int rotate = 700;

  int down = 110 - acceleration;

  int dpadpos = Dpad::getPos();

  //Serial.println(dpadpos);

  switch(dpadpos) {

    case KEY_LEFT:

      if( Dpad::DoDebounce() ) {

        acceleration = Dpad::setAccel(acceleration, leftRight);

      }

      movePieceLeft();

    break;

    case KEY_RIGHT:

      if( Dpad::DoDebounce() ) {

        acceleration = Dpad::setAccel(acceleration, leftRight);

      }

      movePieceRight();

    break;

    case KEY_DOWN:

      if( Dpad::DoDebounce() ) {

        acceleration = Dpad::setAccel(acceleration, down);

      }

      movePieceDown();

    break;

    case KEY_ROTATE:

      if( Dpad::DoDebounce() ) {

        acceleration = Dpad::setAccel(acceleration, rotate);

      }

      RotatePiece();

    break;

    default:

      acceleration = 0;

      processKey = true;

      Debounce = 0;

      keypressed = false;

    break;

  }

  if (keypressed) {

    drawPiece();

    drawTetrisScreen();

  }

}


 

void setScore(long score, bool blank)

{

  // this is a kludge. To do: create a proper system for rendering numbers and letters.

  long ones = (score % 10);

  long tens = ((score / 10) % 10);

  long hundreds = ((score / 100) % 10);

  long thousands = ((score / 1000) % 10);

  long tenthousands = ((score / 10000) % 10);

  long hunderedthousands = ((score / 100000) % 10);


 

  //create the score in upper left part of the screen

  byte font = 0;

  char bytes_out[8];

  memset(scoreDisplayBuffer, 0, sizeof scoreDisplayBuffer);

  //****************score digit 6****************

  for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[hunderedthousands][v]);

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][0] = scoreDisplayBuffer[i][0] | bytes_out[i] >> 1;

  }

  //****************score digit 5****************

  for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[tenthousands][v]);

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][0] = scoreDisplayBuffer[i][0] | (bytes_out[i] << 6);

  }

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][1] = scoreDisplayBuffer[i][1] | bytes_out[i] >> 1;

  }

  //****************score digit 4****************

  for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[thousands][v]);


 

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][1] = scoreDisplayBuffer[i][1] | (bytes_out[i] << 6);

  }

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][2] = scoreDisplayBuffer[i][2] | bytes_out[i] >> 1;

  }

  //****************score digit 3****************

  for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[hundreds][v]);

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][2] = scoreDisplayBuffer[i][2] | (bytes_out[i] << 6);

  }

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][3] = scoreDisplayBuffer[i][3] | bytes_out[i] >> 1;

  }


 

  //****************score digit 2****************

  for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[tens][v]);

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][3] = scoreDisplayBuffer[i][3] | (bytes_out[i] << 6);

  }

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][4] = scoreDisplayBuffer[i][4] | bytes_out[i] >> 1;

  }


 

  //****************score digit 1****************

  for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[ones][v]);

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][4] = scoreDisplayBuffer[i][4] | (bytes_out[i] << 6);

  }

  //write the number to the Score buffer

  for (int i = 0; i < 8; i++)

  {

    scoreDisplayBuffer[i][5] = scoreDisplayBuffer[i][5] | bytes_out[i] >> 1;

  }

  //set Vertical addressing mode and column - page start end

  OLEDCommand(OLED_SET_ADDRESSING);

  OLEDCommand(OLED_VERTICAL_ADDRESSING);

  OLEDCommand(OLED_SET_COLUMN);

  OLEDCommand(120);                 //Set column start

  OLEDCommand(127);                 //Set column end

  OLEDCommand(OLED_SET_PAGE);

  OLEDCommand(0);                  //Set page start

  OLEDCommand(5);                  //Set page end

  for (int p = 0; p < 8; p++)

  {

    for (int c = 0; c <6; c++)

    {

      if (blank) OLEDData(0);

      else OLEDData(scoreDisplayBuffer[p][c]);

    }

  }

}


 

void setNextBlock(byte pieceNumber) {

  memset(nextBlockBuffer, 0, sizeof nextBlockBuffer); //clear buffer

  switch (pieceNumber) {

    case 1:

      //************l piece - 1 *************

      for (int k = 2; k < 6; k++) {

        nextBlockBuffer[k][0] = B01110111;

        nextBlockBuffer[k][1] = B01110111;

      }

    break;

    case 2:

      //************J piece - 2 *************

      for (int k = 0; k < 3; k++) {

        nextBlockBuffer[k][0] = B01110000;

        nextBlockBuffer[k][1] = B01110111;

      }

      for (int k = 4; k < 7; k++) {

        nextBlockBuffer[k][0] = B01110000;

      }

    break;

    case 3:

      //************L piece - 3 *************

      for (int k = 0; k < 3; k++) {

        nextBlockBuffer[k][0] = B01110000;

      }

      for (int k = 4; k < 7; k++) {

        nextBlockBuffer[k][0] = B01110000;

        nextBlockBuffer[k][1] = B01110111;

      }

    break;

    case 4:

      //************O piece - 4 *************

      for (int k = 0; k < 3; k++) {

        nextBlockBuffer[k][0] = B01110000;

        nextBlockBuffer[k][1] = B00000111;

      }

      for (int k = 4; k < 7; k++) {

        nextBlockBuffer[k][0] = B01110000;

        nextBlockBuffer[k][1] = B00000111;

      }

    break;

    case 5:

      //************S piece - 5 *************

      for (int k = 0; k < 3; k++) {

        nextBlockBuffer[k][0] = B01110000;

        nextBlockBuffer[k][1] = B00000111;

      }

      for (int k = 4; k < 7; k++) {

        nextBlockBuffer[k][0] = B00000000;

        nextBlockBuffer[k][1] = B11101110;

      }

    break;

    case 6:

      //************T piece - 6 *************

      for (int k = 0; k < 3; k++) {

        nextBlockBuffer[k][0] = B01110000;

        nextBlockBuffer[k][1] = B01110111;

      }

      for (int k = 4; k < 7; k++) {

        nextBlockBuffer[k][0] = B00000000;

        nextBlockBuffer[k][1] = B00001110;

      }

    break;

    case 7:

      //************Z piece - 7 *************

      for (int k = 0; k < 3; k++) {

        nextBlockBuffer[k][0] = B01110000;

        nextBlockBuffer[k][1] = B00000111;

      }

      for (int k = 4; k < 7; k++) {

        nextBlockBuffer[k][0] = B11101110;

        nextBlockBuffer[k][1] = B00000000;

      }

    break;

  }

  //set Vertical addressing mode and column - page start end

  OLEDCommand(OLED_SET_ADDRESSING);

  OLEDCommand(OLED_VERTICAL_ADDRESSING);

  OLEDCommand(OLED_SET_COLUMN);

  OLEDCommand(120);                 //Set column start

  OLEDCommand(127);                 //Set column end

  OLEDCommand(OLED_SET_PAGE);

  OLEDCommand(6);                  //Set page start

  OLEDCommand(7);                  //Set page end

  for (int p = 0; p < 8; p++) {

    for (int c = 0; c < 2; c++) {

      OLEDData(nextBlockBuffer[p][c]);

    }

  }

}


 

void drawBottom() {

  //set Vertical addressing mode and column - page start end

  OLEDCommand(OLED_SET_ADDRESSING);

  OLEDCommand(OLED_VERTICAL_ADDRESSING);

  OLEDCommand(OLED_SET_COLUMN);

  OLEDCommand(0);              //Set column start

  OLEDCommand(0);              //Set column end

  OLEDCommand(OLED_SET_PAGE);

  OLEDCommand(0);              //Set page start

  OLEDCommand(7);              //Set page end

  for (int c = 0; c < 8; c++) {

    OLEDData(255);

  }

}


 

void drawSides() {

  //set Vertical addressing mode and column - page start end

  OLEDCommand(OLED_SET_ADDRESSING);

  OLEDCommand(OLED_VERTICAL_ADDRESSING);

  OLEDCommand(OLED_SET_COLUMN);

  OLEDCommand(0);                //Set column start

  OLEDCommand(127);              //Set column end

  OLEDCommand(OLED_SET_PAGE);

  OLEDCommand(0);               //Set page start

  OLEDCommand(7);               //Set page end

  for (int r = 0; r < 128; r++) {

    for (int c = 0; c < 8; c++) {

      if (c == 0) OLEDData(1);

      else if (c == 7) OLEDData(128);

      else OLEDData(0);

    }

  }

}


 

void loop() {

  #include <Adafruit_GFX.h>

  #include <Adafruit_SSD1306.h>

  #include <SPI.h>

  //main loop code

  //To do: create high score system that savees to EEprom

  gameOver = false;

  score = 0;

  fillTetrisArray(1); //fill with 1's to make border

  fillTetrisScreen(2);

  drawTetrisScreen();

  delay(12500/1.5);

  fillTetrisScreen(3);

  drawTetrisScreen();

  delay(12500/1.5);

  drawSides();

  drawBottom();

  // tetrisScreenToSerial();

  OLEDCommand(OLED_INVERSE_DISPLAY);

  delay(12500/1.5);

  OLEDCommand(OLED_NORMAL_DISPLAY);

  loadPiece(random(1, 8), 20, 5, true);

  drawTetrisScreen();

  nextPiece = random(1, 8);

  setNextBlock(nextPiece);

  setScore(0, false);

  delay(12500/1.5);

  setScore(0, true);

  delay(12500/1.5);

  setScore(0, false);

  byte rnd = 0;

  drawTetrisTitle(false);

  TetrisTheme::start();

  while(songOn) TetrisTheme::tetrisThemePlay();

  drawTetrisTitle(true);

  drawSides();

  drawBottom();

  setScore(0, false);

  for(int i=1;i<10;i++) {

    nextPiece = random(1, 8);

    setNextBlock(nextPiece);

    delay(12500/1.5);

  }

  while (!gameOver) {

    movePieceDown();

    drawPiece();

    drawTetrisScreen();

    moveTime = millis();

    while (millis() - moveTime < (dropDelay - (level * 50))) {

      processKeys();

    }

  }

}

4. 音乐程序:

#ifndef TETRISTHEMECPP

#define TETRISTHEMECPP

#include <Arduino.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#include <SPI.h>

#include <Wire.h>

#ifndef PIEZO_PIN

#define PIEZO_PIN  (8)

#endif

// A rest

#define _R     (0)

// Note frequencies based on http://www.phy.mtu.edu/~suits/notefreqs.html

#define _C0    (16.35)

#define _CS0   (17.32)

#define _D0    (18.35)

#define _DS0   (19.45)

#define _E0    (20.60)

#define _F0    (21.83)

#define _FS0   (23.12)

#define _G0    (24.50)

#define _GS0   (25.96)

#define _A0    (27.50)

#define _AS0   (29.14)

#define _B0    (30.87)

#define _C1    (32.70)

#define _CS1   (34.65)

#define _D1    (36.71)

#define _DS1   (38.89)

#define _E1    (41.20)

#define _F1    (43.65)

#define _FS1   (46.25)

#define _G1    (49.00)

#define _GS1   (51.91)

#define _A1    (55.00)

#define _AS1   (58.27)

#define _B1    (61.74)

#define _C2    (65.41)

#define _CS2   (69.30)

#define _D2    (73.42)

#define _DS2   (77.78)

#define _E2    (82.41)

#define _F2    (87.31)

#define _FS2   (92.50)

#define _G2    (98.00)

#define _GS2   (103.83)

#define _A2    (110.00)

#define _AS2   (116.54)

#define _B2    (123.47)

#define _C3    (130.81)

#define _CS3   (138.59)

#define _D3    (146.83)

#define _DS3   (155.56)

#define _E3    (164.81)

#define _F3    (174.61)

#define _FS3   (185.00)

#define _G3    (196.00)

#define _GS3   (207.65)

#define _A3    (220.00)

#define _AS3   (233.08)

#define _B3    (246.94)

#define _C4    (261.63)

#define _CS4   (277.18)

#define _D4    (293.66)

#define _DS4   (311.13)

#define _E4    (329.63)

#define _F4    (349.23)

#define _FS4   (369.99)

#define _G4    (392.00)

#define _GS4   (415.30)

#define _A4    (440.00)

#define _AS4   (466.16)

#define _B4    (493.88)

#define _C5    (523.25)

#define _CS5   (554.37)

#define _D5    (587.33)

#define _DS5   (622.25)

#define _E5    (659.25)

#define _F5    (698.46)

#define _FS5   (739.99)

#define _G5    (783.99)

#define _GS5   (830.61)

#define _A5    (880.00)

#define _AS5   (932.33)

#define _B5    (987.77)

#define _C6    (1046.50)

#define _CS6   (1108.73)

#define _D6    (1174.66)

#define _DS6   (1244.51)

#define _E6    (1318.51)

#define _F6    (1396.91)

#define _FS6   (1479.98)

#define _G6    (1567.98)

#define _GS6   (1661.22)

#define _A6    (1760.00)

#define _AS6   (1864.66)

#define _B6    (1975.53)

#define _C7    (2093.00)

#define _CS7   (2217.46)

#define _D7    (2349.32)

#define _DS7   (2489.02)

#define _E7    (2637.02)

#define _F7    (2793.83)

#define _FS7   (2959.96)

#define _G7    (3135.96)

#define _GS7   (3322.44)

#define _A7    (3520.00)

#define _AS7   (3729.31)

#define _B7    (3951.07)

#define _C8    (4186.01)

#define _CS8   (4434.92)

#define _D8    (4698.63)

#define _DS8   (4978.03)

#define _E8    (5274.04)

#define _F8    (5587.65)

#define _FS8   (5919.91)

#define _G8    (6271.93)

#define _GS8   (6644.88)

#define _A8    (7040.00)

#define _AS8   (7458.62)

#define _B8    (7902.13)

// beats per minute

#define BPM   (120.0)

// Time (in microseconds) to spend on each note while simulating polyphony

// If this is too small, low frequency notes will be inaudible.

#define POLY_DELTA (14400)

static const float lead_notes[] PROGMEM = {

  // part 1

  _E5, _B4, _C5, _D5, _C5, _B4, _A4, _A4, _C5, _E5, _D5, _C5, _B4, _B4, _C5, _D5, _E5, _C5, _A4, _A4, _R,

  _D5, _F5, _A5, _G5, _F5, _E5, _C5, _E5, _D5, _C5, _B4, _B4, _C5, _D5, _E5, _C5, _A4, _A4, _R,

  // part 2

  _E4, _C4, _D4, _B3, _C4, _A3, _GS3, _B3,

  _E4, _C4, _D4, _B3, _C4, _E4, _A4, _A4, _GS4, _R

};

static const float lead_times[] PROGMEM = {

  // part 1

  1.0, 0.5, 0.5, 1.0, 0.5, 0.5, 1.0, 0.5, 0.5, 1.0, 0.5, 0.5, 1.0, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,

  1.5, 0.5, 1.0, 0.5, 0.5, 1.5, 0.5, 1.0, 0.5, 0.5, 1.0, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,

  // part 2

  2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,

  2.0, 2.0, 2.0, 2.0, 1.0, 1.0, 1.0, 1.0, 3.0, 1.0

};

static const float bass_notes[] PROGMEM = {

  // part 1

  _E2, _E3, _E2, _E3, _E2, _E3, _E2, _E3, _A1, _A2, _A1, _A2, _A1, _A2, _A1, _A2, _GS1, _GS2, _GS1, _GS2, _GS1, _GS2, _GS1, _GS2, _A1, _A2, _A1, _A2, _A1, _B2, _C3, _E3,

  _D2, _D3, _D2, _D3, _D2, _D3, _D2, _D3, _C2, _C3, _C2, _C3, _C2, _C3, _C2, _C3, _B1, _B2, _B1, _B2, _B1, _B2, _B1, _B2, _A1, _A2, _A1, _A2, _A1, _A2, _A1, _A2,

  // part 2

  _A1, _E2, _A1, _E2, _A1, _E2, _A1, _E2, _GS1, _E2, _GS1, _E2, _GS1, _E2, _GS1, _E2, _A1, _E2, _A1, _E2, _A1, _E2, _A1, _E2, _GS1, _E2, _GS1, _E2, _GS1, _E2, _GS1, _E2,

  _A1, _E2, _A1, _E2, _A1, _E2, _A1, _E2, _GS1, _E2, _GS1, _E2, _GS1, _E2, _GS1, _E2, _A1, _E2, _A1, _E2, _A1, _E2, _A1, _E2, _GS1, _E2, _GS1, _E2, _GS1, _E2, _GS1, _E2

};

/*

// all values are 0.5 so let's hardcode it and save some memory

static const float bass_times[] = {

  // part 1

  0.5,  0.5,  0.5,  0.5,  0.5,  0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,

  0.5,  0.5,  0.5,  0.5,  0.5,  0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,

  // part 2

  0.5,  0.5,  0.5,  0.5,  0.5,  0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,

  0.5,  0.5,  0.5,  0.5,  0.5,  0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5

};*/

static const int lead_note_count = sizeof(lead_notes) / sizeof(float);

static const int bass_note_count = sizeof(bass_notes) / sizeof(float);

static volatile boolean songOn;

class TetrisTheme {

   

  public:

    static void init() {

      /*

      // enable timer, used to play music sequencer async

      TCCR1A = 0; // No options in control register A

      TCCR1B = (1 << CS10); // Set prescaler to divide by 8

      TIMSK1 = (1 << OCIE1A); // Call ISR when TCNT2 = OCRA2

      OCR1A = 0;// Set frequency of generated wave

      sei(); // Enable interrupts to generate waveform!*/

      songOn = false;

    }

    static void start() {

      init();

      songOn = true;

    }

    static void stop() {

      songOn = false;

    }

    static void tetrisThemePlay() {

     

      if (!songOn) {

        return;

      }

     

      int curr_lead_note = 0;

      int curr_bass_note = 0;

      float curr_lead_note_time_remaining = pgm_read_float(&lead_times[curr_lead_note]);

      float curr_bass_note_time_remaining = 0.5;// bass_times[curr_bass_note]; // hardcoded

      float lead_freq, bass_freq, note_value;

      unsigned long duration;

   

      while (curr_lead_note < lead_note_count && curr_bass_note < bass_note_count && songOn) {

        lead_freq = pgm_read_float(&lead_notes[curr_lead_note]);

        bass_freq = pgm_read_float(&bass_notes[curr_bass_note]);

        note_value = min(curr_lead_note_time_remaining, curr_bass_note_time_remaining);

        duration = note_value * 1000000 * (60.0/BPM);

   

        if (lead_freq > 0 && bass_freq > 0) {

          TetrisTheme::play_two_notes(lead_freq, bass_freq, duration);

        } else if (lead_freq > 0) {

          TetrisTheme::play_one_note(lead_freq, duration);

        } else if (bass_freq > 0) {

          TetrisTheme::play_one_note(bass_freq, duration);

        } else {

          delay( duration/10);

        }

   

        // Advance lead note

        curr_lead_note_time_remaining -= note_value;

        if (curr_lead_note_time_remaining < 0.001) {

          curr_lead_note++;

          curr_lead_note_time_remaining = pgm_read_float(&lead_times[curr_lead_note]);

        }

   

        // Advance bass note

        curr_bass_note_time_remaining -= note_value;

        if (curr_bass_note_time_remaining < 0.001) {

          curr_bass_note++;

          curr_bass_note_time_remaining = 0.5;// bass_times[curr_bass_note]; // hardcoded

        }

       

        if(digitalRead(A0) == LOW) {

          songOn = false;

        }

      }

    }

  private:

    static void play_one_note(float frequency, unsigned long duration) {

      unsigned long period = 1000000.0/frequency;

   

      for (unsigned int cycles=duration/period; cycles>0; cycles--) {

        // half the time on

        digitalWrite(PIEZO_PIN, HIGH);

        delay(4000);

   

        // half the time off

        digitalWrite(PIEZO_PIN, LOW);

        delay(4000);

      }

   

      // If the duration wasn't a multiple of the period, delay the remainder

      delay(duration % period);

    }

   

   

    // duration is in microseconds

    static void play_two_notes(float freq1, float freq2, unsigned long duration) {

        for (unsigned long t=0; t<duration; t+=2*POLY_DELTA) {

          play_one_note(freq1, POLY_DELTA);

          play_one_note(freq2, POLY_DELTA);

        }

    }

   

   

};

#endif

5. 8按键模块控制程序:

#include "pins_arduino.h"

#ifndef DPADCPP

#define DPADCPP

#include <Arduino.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#include <SPI.h>

#include <Wire.h>

#ifndef KEYPAD_PIN

#define KEYPAD_PIN  A0  

#endif

const int dpad[5][2] = {

  {540, 580}, //KEY_MIDDLE 0

  {200, 240}, //KEY_LEFT 1

  {430, 470}, //KEY_RIGHT 2

  {320, 360}, //KEY_DOWN 3

  {770, 810}  //KEY_ROTATE 4

};

static int dpadwarp[5] = { 0,0,0,0,0 };

static volatile int Debounce = 0;

static volatile bool processKey = true;

static volatile int currentPos;


 

class Dpad

{

  static const int DebounceMax = 10;

  public:

    static int getPos() {

     

      currentPos = getPosition(KEYPAD_PIN)*1;

      delay(12500);

     

      for(int i=0;i<5;i++) {

        if(currentPos > dpad[i][0] && currentPos < dpad[i][1]) {

          return i;  

        }

      }

      return -1;

    }

    static boolean DoDebounce() {

      Debounce++;

      if(Debounce > DebounceMax) {

        return true;

      }

      return false;

    }

    static int setAccel(int acceleration, int offset) {

      if(processKey) {

        dpadwarp[currentPos] = millis();

      }

      if(millis() < dpadwarp[currentPos] + offset) {

        processKey = false;  

      } else {

        processKey = true;

        acceleration = acceleration + 70;

        if (acceleration > offset) {

          acceleration = offset;

        }

      }

      return acceleration;

    }

  private:

    static int getPosition(int pin) {

      return analogRead(pin);

    }

};

#endif

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值