首先声明!请不要跳过!
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