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
Arduino | 由八按键控制的俄罗斯方块相关推荐
- 零基础无实物一步一步学PLCS7-1200仿真(八)-按键控制数码管显示
项目要求:按照项目的功能完成按键控制数码管的技术显示. 项目目的:学习比较指令.数学函数指令和加减计数指令和数码管的显示原理. 项目功能:数码管可以显示两位数00-99,然后可通过四个按键完成数码管的 ...
- 利用Arduino在米思齐上实现按键控制led灯
一.实验目的及要求 button开关led,当按键的次数为奇数时灯为亮的状态,当按键的次数为偶数时灯为关闭状态. 二.实验原理与内容 实现米思齐按键控制LED 三.实验软硬件环境 硬件:Arduino ...
- Arduino UNO+TB6600驱动器按键控制步进电机启动停止
Arduino UNO+TB6600驱动器按键控制步进电机启动停止 步进电机 TB6600驱动器 **接线说明:**重点内容!!!! //本次实验电机驱动采用共阴极接线方式 1. PUL-, DIR- ...
- Arduino程序设计(二) 按键控制LED灯
按键控制LED灯程序设计 前言 一.按键控制LED灯--内部上拉(基础) 二.按键控制LED灯--外部上拉(基础) 三.按键控制LED灯(进阶) 总结 参考文献 前言 本文主要介绍三种按键控制LED灯 ...
- VSM Studio uln2003驱动步进电机+单按键启保停正反转八拍控制
VSM Studio uln2003驱动步进电机+单按键启保停正反转八拍控制 Proteus仿真演示 基于51单片机,步进电机采用8节拍控制 VSM Studio代码 /* Main.c file g ...
- arduino按键控制LED 灯(按一下切换一次状态)
物联网实验一 2021.3.24 arduino按键控制LED 灯(按一下切换一次状态) #define led 13 //定义led灯为13引脚 #define button 3 //定义butto ...
- Arduino基础入门篇07—按键控制LED灯
前面介绍了Arduino数字I/O,通过控制数字引脚输出来控制LED灯亮灭.本篇将介绍数字I/O的输入功能,通过检测按键状态来控制LED灯亮灭,把LED的亮灭变成人为可控制的. 1. 实验材料 Uno ...
- 最简单DIY基于STM32的远程控制电脑系统②(无线遥杆+按键控制)
STM32库函数开发系列文章目录 第一篇:STM32F103ZET6单片机双串口互发程序设计与实现 第二篇:最简单DIY基于STM32单片机的蓝牙智能小车设计方案 第三篇:最简单DIY基于STM32F ...
- Ardino基础教程 5_按键控制
实验五:按键控制 I/O 口的意思即为INPUT 接口和OUTPUT 接口,到目前为止我们设计的小灯实验都还只是应用到Arduino 的I/O 口的输出功能,这个实验我们来尝试一下使用Arduino的 ...
- 玩转Mixly – 3、Arduino AVR编程 之 控制
以下内容源自Mixly官方技术文档: https://mixly.readthedocs.io/zh_CN/latest/Arduino/AVR/02Control.html 控制 控制类别中包括了时 ...
最新文章
- NLP高阶:一文走遍完整自然语言处理流程
- potplayer 多个进程_操作系统进程与线程基本概念理解
- 09、HTLM中直接写get请求和模板标签分片功能
- 数据结构第二章线性表学习笔记
- 【Python】反转列表 list 的几种方法
- python聚类分析结果很差_python中的聚类分析:value错误:x和y的大小必须相同
- python可以代替plc吗_PLC可以代替安全控制器吗?电气工程师告诉你
- Java笔记-对称加密AES的使用
- 计算机仿真技术实际,计算机仿真技术详解.doc
- html百度地图导航起点默认定位位置,百度地图API的自动定位路线查询
- 有效解决鼠标问题的驱动工具-罗技G502游戏鼠标驱动程序提供下载
- 啤酒与尿布:数据分析相关性分析案例一
- 指针分配和释放空间(转)
- 2000元 组装车 思路
- STM32MP157系列教程连载-Linux应用开发篇1:STM32MP1微处理器之Ubuntu安装与体验
- 解决error C2059: 语法错误:“::”问题
- 论文解读:多层肽 - 蛋白质相互作用预测的深度学习框架
- 顶点从观察空间变换到裁剪空间
- Visual Studio代码:高级用户指南
- c++语言程序设计(第四版)郑莉链表的实现源码