#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
//定義個別連接的PIN腳
#define TFT_CS 8
#define TFT_RST 9
#define TFT_DC 10
#define TFT_MOSI 11
#define TFT_MISO 12
#define TFT_CLK 13
#define leftButton 2
#define rightButton 3
//定義音符類型的enum
enum noteType{RED , BLUE , NONE};
//定義一個資料結構儲存分數資訊
struct score{
int count; //音符出現的次數
int hit; //擊中音符的次數
};
//建立LCD class
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);
//建立紅色和藍色分數的陣列
score scores[2];
//建立音符的變數
volatile noteType note;
//音符出現的間隔時間
int interval = 10000;
//紀錄上一次音符出現的時間
unsigned long previousTime = millis();
void setup() {
Serial.begin(9600);
tft.begin(); //啟動LCD
setBackground(); //設定LCD背景
//設定leftButton和rightButton腳位為INPUT_PULLUP模式
pinMode(leftButton , INPUT_PULLUP);
pinMode(rightButton , INPUT_PULLUP);
//設定按鈕著中斷函式
attachInterrupt(digitalPinToInterrupt(leftButton), leftButtonISR , FALLING);
attachInterrupt(digitalPinToInterrupt(rightButton) , rightButtonISR , FALLING);
//設定隨機數種子
randomSeed(millis());
}
void loop() {
unsigned long currentTime = millis(); //紀錄現在的時間
if(currentTime - previousTime >= interval){ //當間隔時間大於interval
interval = random(1000 , 2000); //隨機產生介於500~2000間interval的值
note = (noteType)random(0,2); //隨機產生0 , 1 ,2
switch(note){ //使用switch-case判斷音符類型
case RED: //當note為RED時,為紅色音符
scores[RED].count++; //紅色音符出現次數加一
redPattern(); //在LCD上顯示紅色音符
redScore(); //更新LCD上紅色音符的分數
break;
case BLUE:
scores[BLUE].count++; //當note為BLUE時,為藍色音符
bluePattern(); //在LCD上顯示藍色音符
blueScore(); //更新LCD上籃色音符的分數
break;
case NONE: //當note為NONE時
break; //不做任何事
}
previousTime = currentTime; //將現在時間記錄在previousTime
}else if(currentTime - previousTime >= interval / 2){ //當間隔時間大於interval的一半
noPattern(); //LCD上不顯示音符
}
}
boolean debounce(int pin){ //使用軟體防止硬體彈跳的問題
boolean state;
boolean previousState;
previousState = digitalRead(pin);
for(int counter = 0 ; counter < 100 ; counter++){
delay(1);
state = digitalRead(pin);
if(state != previousState){
counter = 0;
previousState = state;
}
}
return state;
}
void leftButtonISR(){ //處理左按鈕的中斷
if(!debounce(leftButton) && note == RED){//如果此時顯示的音符為紅色音符
Serial.println("press red");
scores[RED].hit++;//紅色音符打擊次數加一
noPattern(); //LCD上不顯示音符
redScore(); //更新LCD上紅色音符的分數
}
}
void rightButtonISR(){ //處理右按鈕的中斷
if(!debounce(rightButton) && note == BLUE){//如果此時顯示的音符為藍色音符
Serial.println("press blue");
scores[BLUE].hit++; //藍色音符打擊次數加一
noPattern(); //LCD上不顯示音符
blueScore(); //更新LCD上籃色音符的分數
}
}
void setBackground(){ //設定背景
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
tft.drawLine(0 , 49 , tft.width() , 49 ,ILI9341_WHITE );
tft.drawLine(0 , 101 , tft.width() , 101 ,ILI9341_WHITE );
tft.fillRect(0 , 49 , 75 , 53 , ILI9341_ORANGE);
tft.fillRect(75 , 50 , tft.width() , 50 , ILI9341_DARKGREY);
redScore();
blueScore();
}
void setText(int x , int y ,int w , int h ,uint8_t size , uint16_t color , char* str){ //定義一個函式,方便設定置中的文字
tft.setCursor(x + (w - strlen(str) * 6 * size) / 2, y + (h - 6 * size) / 2);
tft.setTextColor(color);
tft.setTextSize(size);
tft.println(str);
}
void redScore(){ //顯示紅色音符的總出現次數和擊中次數
char *score = malloc(10);
sprintf(score, "%d / %d", scores[RED].hit , scores[RED].count);
tft.fillRoundRect(75 , 5 , 100 , 40 , 10 , ILI9341_RED);
setText(75 , 5 , 100 , 40 , 1 , ILI9341_BLACK , score);
free(score);
}
void blueScore(){ //顯示藍色音符的總出現次數和擊中次數
char *score = malloc(10);
sprintf(score, "%d / %d", scores[BLUE].hit , scores[BLUE].count);
tft.fillRoundRect(75 , 105 , 100 , 40 , 10 , ILI9341_CYAN);
setText(75 , 105 , 100 , 40 , 1 , ILI9341_BLACK , score);
free(score);
}
void redPattern(){ //劃出紅色音符
tft.fillCircle(100 , 75 , 15 , ILI9341_RED);
}
void bluePattern(){ //劃出藍色音符
tft.fillCircle(100 , 75 , 15 , ILI9341_CYAN);
}
void noPattern(){ //讓紅、藍色音符消失
tft.fillCircle(100 , 75 , 15 , ILI9341_DARKGREY);
note = NONE;
}