macsbug

M5stack tetris with sound

with 3 comments

M5Stack TETRIS に BGM  を追加しました。          2019.05.04

DEMO MODE ( gif 画像にて 音はでません )

bitluni 氏より ULPSoundESP32 が Apr 7, 2019 に公開されました。
ESP32 の ULP ( Ultra Low Power Coprocessor ) を使用し BGM を出力します。
ULPの使用により メインプロセッサの負担無く スムーズに動作します。
スケッチは ULPSoundESP32 の ULPSoundMonoSamples を使用し
TETRIS に 組み込みました。

DEMO 又は GAME

を選択します。

OK で設定されます。

DEMO は

自動的に繰り返します。

BGMが流れる中

Volume の調整。

レベルは 0 – 63。

Down / Up で調整。

0 は 無音です。

OK で設定されます。


.
M5Stack と TETRIS:
1. ESP32 の TETRIS は MhageGH氏により作成されました。
_ 2017.06.20:esp32_ILI9328_Tetris by MhageGH:MhageGH 氏に感謝致します。
2. Tuan PM氏によりM5Stack用のTETRIS が動作。2017.12.02.ソースは未公開。
3. M5Stack 用に変換しました。
_ TETRIS with M5STACK : 2018.01.20 Transplant by macsbug
_ M5Stack発売時に GAME なき為 先人のスケッチを幾つか移植させて頂きました。
4. M5Stack Github に載せていただきました。
_ Tetris:2018.01.30:M5Stack Team 及び Jimmy氏に感謝致します。
5. shikarunochi 氏により機能追加が行われました。
_ スコアー と 次のブロック表示が追加されました。
_ M5Stack:テトリス改造:2018.05.03:しかるのち氏に感謝致します。
6. TETRIS_v2 : M5stack tetris with sound : 2019.05.03 Add sound by macsbug
_ bitluni 氏の ULPSoundMonoSamples を追加しました。bitluni 氏に感謝致します。
_ 機能が追加された しかるのち氏のスケッチを使用しました。
_ DEMO と GAME の選択、 Volume調整 (0-63, 0=無音)を 追加。
_ 操作は M5Stack の A,B,C ボタンとしました。Joystick UNIT等は各自で追加願います。


.
作成:


1. Arduino IDE 開発環境 と M5Stack ライブラリー を準備します。
2. TETRIS_v2.ino をコピーして「TETRIS_v2」フォルダーを作成します。
_ TETRIS_v2 は ブログの下に記載してあります。
_ SD_update 対応になっています。
3. tetris.h:サウンド データー。
_ ULPSoundESP32 の ULPSoundMonoSamples
_ tetris.h を DL し TETRIS_v2 フォルダーに入れます。
_ 注: DLは URL を前とかに たどり DL できる場所で 入手します。
4. Free_Fonts.h:フォントデーター。
_ M5Stack/examples/Advanced/Display/All_Free_Fonts_Demo/
_ Free_Fonts.h を DL し TETRIS_v2 フォルダーに入れます。
5. tetris_img.ctetris_img.c:TETRIS 背景画像。
_ tetris_img.c を DL し TETRIS_v2 フォルダーに入れます。
メモ:Github を使用していない(解らない)為、ブログだけのリストになります。


.
参考:
hackster.io:This Sound Driver Runs on the ESP32’s ULP Coprocessor
_ サウンドドライバはESP32のULPコプロセッサで動作します:
_  Bitluni氏は、コプロセッサ上で動作するサウンドドライバを開発し、
_  メインプロセッサの負担を取り除き、
_  より多くのリソースを必要とするタスクを自由に処理することができました。
bitluni:ULPSoundESP32:今回のスケッチがあります。
espressif:espressif/esp-iot-solution:ULPコプロセッサおよびアセンブリ環境設定の概要
espressif:esp32 ulp programming:esp32 ulpプログラミング
docs.espressif:ULP coprocessor instruction set:ULPコプロセッサ命令セット
{CODINGFIELD}:ULP (ESP32) : a simple example:ULP(ESP32):簡単な例。
Vida de Silício:Ultra Low Power coprocessor (ULP) – ESP32:ULPのプログラム方法。
Rabbit Note:ESP32 の ULP コプロセッサを使って超低消費電力 I2C 通信
mはげ(MhageGH):【ゆっくり解説】パラレル処理【電子工作】:ULP パラレル処理の説明。
mはげ(MhageGH):【ゆっくり解説】ディジタル音声の使い方 ~音の基礎からAIまで~:I2S の説明。
MhageGH/M5Stack_Tetris:2019.05.05 追記。
_ mはげ氏による TetrisにBGM追加:M5StackのExamplesのTetrisにBGMを付けました。
_  サンプリングレート8kHz、8bitのwaveファイルをコードに埋め込み、I2S+DACで鳴らしています。
MhageGH/M5Stack_Tetris:2019.05.09 追記。
_ パラレル化更新:LCD表示のSPIをポーリングからDMA割り込みトランザクション。


.
感想:
_ 画像表示:bitluni氏のお陰で 音を出力でき 画像表示の遅れも無く動作しています。
_ ULPの応用:ULPSoundESP32 の ULPSoundMonoSamples を参考に
_       Sound を用意すると 他の Sound も自在に出力できますね。
_ 音:M5Stack の アンプとスピーカーは ノイズが問題になる場合があります。
_   ノイズの要素は 1つでなく幾つかあります。( 詳細は省略 )
_   音量は DAC の内部を操作し無音か出力かにしています。
_    無音:dac_output_disable(DAC_CHANNEL_1);
_    出力:dac_output_enable(DAC_CHANNEL_1);
_
_ メモ:約5KHz+脈を打つノイズの原因は M-BUS DAC 25ピンの配置。
_    複雑な画像時に MOSI信号(23)の輻射波が GPIO-25(DAC OUT)からAMPへ入る。
_    これは PROTO Module の GPIO-25 PIN と 配線の 2つの影響を受けます。
_    NS4150B-4 pin (INN) – C44 の PGND は M5Stack BASIC と GRAY で Open。
_    ただし GRAY(2017.11 )でGNDもあり 基板の日付を述べる必要がある。
_    M5Stack FIRE(2018.2A) は GNDで C44(100nf) は 未装着です。
_    これに対し Jimmy氏は “we remove this cap for good sound.” と答えています。
_    結果 pin 4 (INN) は Openで良く GNDすると条件によりノイズが大きくなります。
_    
_    対策:DAC 25 ピン:削除する。
_       M-BUSの設計:MOSI ( 23 ) と DAC ( 25 ) は 距離を空ける。
_       PROTO基板の設計:DAC ( 25 ) は ピンを立てず 配線しない事。
_       ノイズ防止PROTO基板:25ピンの無い基板をスタックして使用する。


.
リスト:

// TETRIS_v2.ino : M5stack tetris with sound : 2019.05.03 Add sound by macsbug

//========================================================================
// TETRIS with M5STACK : 
//            : 2017.06.20 esp32_ILI9328_Tetris by MhageGH
//            : 2018.01.20 Transplant by macsbug
//            : 2018.05.03 Modified by @shiakrunochi
// TETRIS_v2  : M5stack tetris with sound
//            : 2019.05.03 Add sound by macsbug
//            : https://macsbug.wordpress.com/2019/05/04/m5stack-tetris-with-sound/
// Controller : Buttons A = LEFT, B = RIGHT, C = START, ROTATE
// Display    : Left = 100x240, Center = 120x240, Right = 100x240
// Block      : 8ea, 12x12 pixel
// SD         : tetris.jpg : BackGround Image : R.G.B 320x240 pixel
// Github     : (reference) https://github.com/MhageGH/esp32_ILI9328_Tetris
// Github     : (Original) https://macsbug.wordpress.com/2018/01/20/tetris-with-m5stack/
// Github     : (M5Stack) https://github.com/m5stack/M5Stack/tree/master/examples/Games/Tetris
// Github     : (revision) http://shikarunochi.matrix.jp/?p=2296
// How to     : How to make tetris https://www.nicovideo.jp/watch/sm17983957
// Sound      : Sound processing with ULP co-processor
// Github     : bitluni/ULPSoundESP32:https://github.com/bitluni/ULPSoundESP32
// tetris.h   : Sound : 2.4MB
//========================================================================
#include <M5Stack.h>                                       // M5STACK
#include "M5StackUpdater.h"                                //
#include "Free_Fonts.h"                                    // Font 12
uint16_t BlockImage[8][12][12];                            // Block
uint16_t backBuffer[240][120];                             // GAME AREA
uint16_t nextBlockBuffer[60][48];                          // NEXT BLOCK AREA
const int Length = 12;     // the number of pixels for a side of a block
const int Width  = 10;     // the number of horizontal blocks
const int Height = 20;     // the number of vertical blocks
int screen[Width][Height] = {0}; //it shows color-numbers of all positions
struct Point {int X, Y;};
struct Block {Point square[4][4]; int numRotate, color;};
Point pos; Block block;
int nextBlockType = -1;
long score = 0;
Block nextBlock;
int rot, fall_cnt = 0;
bool started = false, gameover = false;
boolean but_A = false, but_LEFT = false, but_RIGHT = false, but_DOWN = false;
int game_speed = 25; // 25msec
Block blocks[7] = {
  {{{{-1,0},{0,0},{1,0},{2,0}},{{0,-1},{0,0},{0,1},{0,2}},
  {{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}}},2,1},
  {{{{0,-1},{1,-1},{0,0},{1,0}},{{0,0},{0,0},{0,0},{0,0}},
  {{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}}},1,2},
  {{{{-1,-1},{-1,0},{0,0},{1,0}},{{-1,1},{0,1},{0,0},{0,-1}},
  {{-1,0},{0,0},{1,0},{1,1}},{{1,-1},{0,-1},{0,0},{0,1}}},4,3},
  {{{{-1,0},{0,0},{0,1},{1,1}},{{0,-1},{0,0},{-1,0},{-1,1}},
  {{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}}},2,4},
  {{{{-1,0},{0,0},{1,0},{1,-1}},{{-1,-1},{0,-1},{0,0},{0,1}},
  {{-1,1},{-1,0},{0,0},{1,0}},{{0,-1},{0,0},{0,1},{1,1}}},4,5},
  {{{{-1,1},{0,1},{0,0},{1,0}},{{0,-1},{0,0},{1,0},{1,1}},
  {{0,0},{0,0},{0,0},{0,0}},{{0,0},{0,0},{0,0},{0,0}}},2,6},
  {{{{-1,0},{0,0},{1,0},{0,-1}},{{0,-1},{0,0},{0,1},{-1,0}},
  {{-1,0},{0,0},{1,0},{0,1}},{{0,-1},{0,0},{0,1},{1,0}}},4,7}
};
extern uint8_t tetris_img[];

// - ULP -
#include <esp32/ulp.h>
#include <driver/rtc_io.h>
#include <driver/dac.h>
#include <soc/rtc.h>
#include <math.h>
#include "tetris.h"
String mode_ = "GAME";
unsigned long samplingRate = 44100;
const int opcodeCount = 17;
const int dacTableStart1 = 2048 - 512;
const int dacTableStart2 = dacTableStart1 - 512;
const int totalSampleWords = 2048 - 512 - (opcodeCount + 1);
const int totalSamples = totalSampleWords * 2;
const int indexAddress = opcodeCount;
const int bufferStart = indexAddress + 1;
int v = 24;// volume

void startULPSound(){
  //calculate the actual ULP clock
  unsigned long rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 1000);
  unsigned long rtc_fast_freq_hz = 
      1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period;
  dac_output_enable(DAC_CHANNEL_1);      //initialize DACs
  dac_output_enable(DAC_CHANNEL_2);
  dac_output_voltage(DAC_CHANNEL_1, 128);
  dac_output_voltage(DAC_CHANNEL_2, 128);
  int retAddress1 = 13;
  int loopCycles  = 84;
  Serial.print("Real RTC clock: ");
  Serial.println(rtc_fast_freq_hz);
  int dt = (rtc_fast_freq_hz / samplingRate) - loopCycles;
  if(dt < 0)
    Serial.println("Sampling rate too high"); 
    Serial.print("dt: ");Serial.println(dt);
const ulp_insn_t mono[] = {
  I_MOVI(R3, 0),             //reset offset register
  I_DELAY(dt),               //delay to get the right sampling rate // 6 + dt
  I_MOVI(R0, 0),             //reset sample index         // 6
  I_ST(R0, R3, indexAddress),//write the index back to memory for the main cpu // 8
  I_RSHI(R2, R0, 1),         //divide index by two since we store two samples in each dword // 6
  I_LD(R1, R2, bufferStart), //load the samples           // 8
  I_ANDI(R2, R0, 1),         //get if odd or even sample  // 6
  I_LSHI(R2, R2, 3),         //multiply by 8              // 6
  I_RSHR(R1, R1, R2),        //shift the bits to have the right sample in the lower 8 bits // 6
  I_ANDI(R1, R1, 255),       //mask the lower 8 bits      // 6
  I_LSHI(R1, R1, 1),         //multiply by 2              // 6
  I_ADDI(R1, R1, dacTableStart1),//add start position     // 6
  I_BXR(R1),                 //jump to the dac opcode     // 4
                             //here we get back from writing a sample
  I_ADDI(R0, R0, 1),         //increment the sample index // 6
  I_BGE(-13, totalSamples),  //if reached end of the buffer, jump relative to index reset // 4
  //wait to get the right sample rate (2 cycles more to compensate the index reset)
  I_DELAY((unsigned int)dt + 2), // 8 + dt
  I_BXI(3)};                 //if not, jump absolute to where index is written to memory // 4
// write io and jump back another 12 + 4

  size_t load_addr = 0;
  size_t size = sizeof(mono)/sizeof(ulp_insn_t);
  ulp_process_macros_and_load(load_addr, mono, &size);
//  this is how to get the opcodes
//  for(int i = 0; i < size; i++)
//    Serial.println(RTC_SLOW_MEM[i], HEX);
  for(int i = 0; i < 256; i++){   //create DAC opcode tables
    RTC_SLOW_MEM[dacTableStart1 + i * 2] = 0x1D4C0121 | (i << 10); //dac0
    RTC_SLOW_MEM[dacTableStart1 + 1 + i * 2] = 0x80000000 + retAddress1 * 4;
  }
  for(int i = 0; i < totalSampleWords; i++) //set all samples to 128 (silence) RTC_SLOW_MEM[bufferStart + i] = 0x8080; RTC_SLOW_MEM[indexAddress] = 0; //start ulp_run(0); while(RTC_SLOW_MEM[indexAddress] == 0) delay(1); } long currentSample = 0; unsigned char nextSample(){ static long pos = 0; if(pos >= sampleCount)
    pos = 0;
  return (unsigned char)((int)(samples[pos++] + 128)/v); // add v
}

int lastFilledWord = 0;

void fillSamples(){
  int currentSample = RTC_SLOW_MEM[indexAddress] & 0xffff;
  int currentWord = currentSample >> 1;
  while(lastFilledWord != currentWord){
    unsigned int w = nextSample();
    w |= nextSample() << 8; RTC_SLOW_MEM[bufferStart + lastFilledWord] = w; lastFilledWord++; if(lastFilledWord == totalSampleWords) lastFilledWord = 0; } } // - ULP - void mode_set(){ M5.Lcd.setFreeFont(FM12); M5.lcd.setCursor(68,85);M5.lcd.setTextColor(WHITE); M5.lcd.printf("Demo or Game"); M5.lcd.setCursor(68,125);M5.lcd.printf("Please select"); M5.lcd.setCursor(37,232);M5.lcd.setTextColor(WHITE); M5.lcd.printf("DEMO"); M5.lcd.drawRect(35,212,60,28, BLACK); M5.lcd.setCursor(146,232);M5.lcd.setTextColor(GREEN); M5.lcd.printf("OK"); M5.lcd.drawRect(130,212,60,28,GREEN); M5.lcd.setCursor(228,232);M5.lcd.setTextColor(WHITE); M5.lcd.printf("GAME"); M5.lcd.drawRect(225,212,60,28,BLACK); b: M5.update(); fillSamples(); if (M5.BtnA.isPressed()) { mode_ = "DEMO";} if (M5.BtnC.wasPressed()){ mode_ = "GAME";} if (M5.BtnB.wasPressed()){ M5.lcd.setCursor(135,165);M5.lcd.setTextColor(BLACK); M5.lcd.print(" "); M5.lcd.setCursor(135,165);M5.lcd.setTextColor(RED); M5.lcd.print(mode_); delay(250);return;} M5.lcd.setCursor(135,165);M5.lcd.setTextColor(WHITE); M5.lcd.print(mode_); delay(25); M5.lcd.setCursor(135,165);M5.lcd.setTextColor(BLACK); M5.lcd.print(mode_); goto b; } void volume_set(){ M5.Lcd.setFreeFont(FM12); M5.lcd.setTextColor(WHITE); M5.lcd.setCursor(68,85); M5.lcd.printf("Adjust volume"); M5.lcd.setCursor(120,125);M5.lcd.printf("0 - 63"); M5.lcd.setCursor(37,232); M5.lcd.printf("Down"); M5.lcd.drawRect(35,212,60,28,BLACK); M5.lcd.setCursor(146,232);M5.lcd.setTextColor(GREEN); M5.lcd.printf("OK"); M5.lcd.drawRect(130,212,60,28,GREEN); M5.lcd.setCursor(242,232);M5.lcd.setTextColor(WHITE); M5.lcd.printf("Up"); M5.lcd.drawRect(225,212,60,28,BLACK); b: M5.update(); fillSamples(); if (M5.BtnA.isPressed()) {if(v> 0 && v<64){v++;};} // Down if (M5.BtnC.wasPressed()){if(v> 1 && v<65){v--;};} // Up
  if (M5.BtnB.wasPressed()){M5.lcd.setCursor(50,30); // OK 
      M5.lcd.setTextColor(WHITE);M5.lcd.printf("Volume complete");
      if (v == 64){dac_output_disable(DAC_CHANNEL_1);}// Volume OFF
      ;return;}
  M5.lcd.setCursor(145,165);M5.lcd.setTextColor(WHITE);
  M5.lcd.print(64-v); delay(25);
  M5.lcd.setCursor(145,165);M5.lcd.setTextColor(BLACK);
  M5.lcd.print(64-v);
  goto b;
}

void restart_() {
  score = 0;                                    // Clear Score
  M5.Lcd.drawJpg(tetris_img, 34215); // Load background from file data
  PutStartPos();                                // Start Position
  for (int i=0;i<Width;++i){for(int j=0;j<Height;++j){
    if (screen[i][j] != 0) screen[i][j] = 0;}   // Clear data
  }
  gameover = false;
  if (v != 64){dac_output_enable(DAC_CHANNEL_1);// Sound ON
               startULPSound();}                // ULP co-processor
}

void gameover_display(){
  dac_output_disable(DAC_CHANNEL_1);            // Sound OFF
  M5.Lcd.setFreeFont(FM24); 
  M5.lcd.setCursor(40,130);M5.lcd.setTextColor(WHITE);
  M5.lcd.printf("GAME OVER");delay(6000);
  M5.Lcd.setFreeFont(FM12); 
}

//========================================================================
void setup(void) {
  M5.begin();                   // M5STACK INITIALIZE
  Wire.begin();if(digitalRead(39)==0){updateFromFS(SD);ESP.restart();}//SD UPdate
  M5.Lcd.setBrightness(200);    // BRIGHTNESS = MAX 255
  M5.Lcd.fillScreen(BLACK);     // CLEAR SCREEN
  M5.Lcd.setRotation(1);        // SCREEN ROTATION = 0
  //----------------------------// Make Block ----------------------------
  make_block( 0, BLACK);        // Type No, Color
  make_block( 1, 0x00F0);       // DDDD     RED
  make_block( 2, 0xFBE4);       // DD,DD    PUPLE 
  make_block( 3, 0xFF00);       // D__,DDD  BLUE
  make_block( 4, 0xFF87);       // DD_,_DD  GREEN 
  make_block( 5, 0x87FF);       // __D,DDD  YELLO
  make_block( 6, 0xF00F);       // _DD,DD_  LIGHT GREEN
  make_block( 7, 0xF8FC);       // _D_,DDD  PINK
  //----------------------------------------------------------------------
  // M5.Lcd.drawJpgFile(SD,"/tetris.jpg");  // Load background from SD
  M5.Lcd.drawJpg(tetris_img, 34215); // Load background from file data
  startULPSound();                           // ULP co-processor
  mode_set();                                // demo or manual game
  M5.Lcd.drawJpg(tetris_img, 34215); // Load background from file data
  volume_set();                              // volume setup
  M5.Lcd.drawJpg(tetris_img, 34215); // Load background from file data
  PutStartPos();                             // Start Position
  for (int i = 0; i < 4; ++i) screen[pos.X + 
   block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
  Draw();                                    // Draw block
  DrawNextBlock();
}

//========================================================================
void loop() {
  fillSamples();
  if (gameover) restart_();
  Point next_pos;
  int next_rot = rot;
  GetNextPosRot(&next_pos, &next_rot);
  ReviseScreen(next_pos, next_rot);
  M5.update();
  delay(game_speed);                                      // SPEED ADJUST
}
//========================================================================
void Draw() {                               // Draw 120x240 in the center
  for (int i = 0; i < Width; ++i) for (int j = 0; j < Height; ++j)
   for (int k = 0; k < Length; ++k) for (int l = 0; l < Length; ++l)
    backBuffer[j * Length + l][i * Length + k] = BlockImage[screen[i][j]][k][l];
    M5.Lcd.pushImage(100, 0, 120, 240, (uint16_t *)backBuffer);
}
void DrawNextBlock() {  
  for(int x = 0; x < 48;x++) {
    for(int y = 0; y < 60;y++){
      nextBlockBuffer[y][x]=0;
    }
  }
  nextBlock = blocks[nextBlockType];
  int offset = 6 + 12;
  
  for (int i = 0; i < 4; ++i) {
      for (int k = 0; k < Length; ++k) for (int l = 0; l < Length; ++l){
        nextBlockBuffer[60 - (nextBlock.square[0][i].X * Length + l + 
        offset)][nextBlock.square[0][i].Y * Length + k + offset] = 
        BlockImage[nextBlockType + 1][k][l];
      }
  }
  M5.Lcd.pushImage(26, 100, 48, 60, (uint16_t *)nextBlockBuffer);
  M5.Lcd.fillRect(2, 76, 96, 19, BLACK);
  M5.Lcd.setCursor(0, 92);//M5.Lcd.setCursor(10, 78);
  M5.Lcd.printf("%7d",score);
}
//========================================================================
void PutStartPos() {
  pos.X = 4; pos.Y = 1;
  if (nextBlockType == -1){
    block = blocks[random(7)];
  }else{
    block = blocks[nextBlockType];
  }
  nextBlockType = random(7);
  rot = random(block.numRotate);
}
//========================================================================
bool GetSquares(Block block, Point pos, int rot, Point* squares) {
  bool overlap = false;
  for (int i = 0; i < 4; ++i) {
    Point p;
    p.X = pos.X + block.square[rot][i].X;
    p.Y = pos.Y + block.square[rot][i].Y;
    overlap |= p.X < 0 || p.X >= Width || p.Y < 0 || p.Y >= 
      Height || screen[p.X][p.Y] != 0;
    squares[i] = p;
  }
  return !overlap;
}
//========================================================================
void GameOver() {
  for (int i = 0; i < Width; ++i) for (int j = 0; j < Height; ++j) if (screen[i][j] != 0) screen[i][j] = 4; gameover = true; gameover_display(); } //======================================================================== void ClearKeys() { but_A=false; but_LEFT=false; but_RIGHT=false; but_DOWN=false;} //======================================================================== bool KeyPadLoop(){ if ( mode_ == "GAME" ){ if(M5.BtnA.wasPressed()){ClearKeys();but_LEFT =true;return true;} if(M5.BtnB.wasPressed()){ClearKeys();but_RIGHT=true;return true;} if(M5.BtnC.wasPressed()){ClearKeys();but_A =true;return true;} if (Serial.available()) { char r = Serial.read(); while(Serial.read() != -1); if (r == 'z') { ClearKeys(); but_A=true; } //else but_A=false; if (r == '2') { ClearKeys(); but_DOWN=true; } //else but_DOWN=false; if (r == '4') { ClearKeys(); but_LEFT=true; } //else but_LEFT=false; if (r == '6') { ClearKeys(); but_RIGHT=true; } //else but_RIGHT=false; return true; } return false; } // RANDOM int rnd_key = random(3*8); if (rnd_key == 0){ClearKeys();but_LEFT =true;return true;} if (rnd_key == 1){ClearKeys();but_RIGHT=true;return true;} if (rnd_key == 2){ClearKeys();but_A =true;return true;} } //======================================================================== void GetNextPosRot(Point* pnext_pos, int* pnext_rot) { bool received = KeyPadLoop(); if (but_A) started = true; if (!started) return; pnext_pos->X = pos.X;
  pnext_pos->Y = pos.Y;
  if ((fall_cnt = (fall_cnt + 1) % 10) == 0) pnext_pos->Y += 1;
  else if (received) {
    if (but_LEFT) { but_LEFT = false; pnext_pos->X -= 1;}
    else if (but_RIGHT) { but_RIGHT = false; pnext_pos->X += 1;}
    else if (but_DOWN) { but_DOWN = false; pnext_pos->Y += 1;}
    else if (but_A) { but_A = false;
      *pnext_rot = (*pnext_rot + block.numRotate - 1)%block.numRotate; 
    }
  }
}
//========================================================================
void DeleteLine() {
  int deleteCount = 0;
  for (int j = 0; j < Height; ++j) {
    bool Delete = true;
    for (int i = 0; i < Width; ++i) if (screen[i][j] == 0) Delete = false; if (Delete) { for (int k = j; k >= 1; --k) {
        for (int i = 0; i < Width; ++i) { screen[i][k] = screen[i][k - 1]; } } deleteCount++; } } switch (deleteCount){ case 1:score = score + 40;break; case 2:score = score + 100;break; case 3:score = score + 300;break; case 4:score = score + 1200;break; } if(score > 9999999){score = 9999999;}
}
//========================================================================
void ReviseScreen(Point next_pos, int next_rot) {
  if (!started) return;
  Point next_squares[4];
  for (int i = 0; i < 4; ++i) screen[pos.X + 
    block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = 0;
  if (GetSquares(block, next_pos, next_rot, next_squares)) {
   for (int i = 0; i < 4; ++i){
     screen[next_squares[i].X][next_squares[i].Y] = block.color;
   }
   pos = next_pos; rot = next_rot;
  }
  else {
   for (int i = 0; i < 4; ++i) screen[pos.X + 
    block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
   if (next_pos.Y == pos.Y + 1) {
    DeleteLine(); PutStartPos();DrawNextBlock();
    if (!GetSquares(block, pos, rot, next_squares)) {
     for (int i = 0; i < 4; ++i) screen[pos.X + 
      block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
      GameOver();
    }
   }
  }
  Draw();
}
//========================================================================
void make_block( int n , uint16_t color ){            // Make Block color       
  for ( int i =0 ; i < 12; i++ ) for ( int j =0 ; j < 12; j++ ){
    BlockImage[n][i][j] = color;                           // Block color
    if ( i == 0 || j == 0 ) BlockImage[n][i][j] = 0;       // BLACK Line
  } 
}
//========================================================================

**************************************************************************************************************
M5Stack , macsbug , M5stack tetris with sound , tetris , ESP32 , ULP , テトリス ,


Written by macsbug

5月 4, 2019 @ 12:00 pm

カテゴリー: M5STACK

3件のフィードバック

Subscribe to comments with RSS.

  1. 情報公開ありがとうございます!
    M5StackのBasic, GrayとFireでC44の配線が違うのですか。以前確認したはずなのですが見落としていました。自分の持っているハードでは明らかにBasicよりFireの方が音質が良いです。それが原因なのかもしれません。確認してみます。
    ちなみに【ゆっくり解説】パラレル処理【電子工作】の音声再生ではULP Coprocessor(超低消費電力コプロセッサ)ではなくI2Sという音声用のペリフェラルを利用しています。

    mはげ

    5月 4, 2019 at 6:58 pm

    • mはげ様、サイトへの訪問をありがとうございます。
      いつもTwitterを見させて頂いています。(私は Twitterはしていません)
      そして 氏のスケッチをお断りも無く勝手ながらM5stackに移植させて頂きました。
      改めて此処で感謝する次第です。
      ゆっくり解説も大変勉強になっています。特に今回のパラレルは何度も見ています。

      >M5StackのBasic, GrayとFireでC44の配線が違うのですか。
      M5Stackは 10台程持っていますが 区分けはしていません。
      とりあえず、周囲のM5Stackを見てみました。
      PCB は基板 BAT Connector 横の日付(DATE CODE)です。
      BASIC(PCB=2017.6 );NS4150B_4 – C44 – open。
      BASIC(PCB=2018.3 );NS4150B_4 – C44 – open:*open をGNDするとノイズが大きくなる。
      GRAY (PCB=2017.6 );NS4150B_4 – C44 – open。
      GRAY (PCB=2017.11 );NS4150B_4 – C44 – GND。
      FIRE (PCB=2018.2A);NS4150B_4 – none – GND。
      * open をGNDするとノイズが大きくなる件は スケッチによっても異なる場合があり 測定条件を決める必要があると感じました。

      >BASICよりFIREの方が音質が良いです。
      その通りです。これはヒントになりそうで注目していますが 原因は追求出来ていません。
      MOSI(23) からの輻射の確認方法は MBUS-23と25(DAC)間を絶縁の線を近づけると発生します。
      複雑な画像を表示すると MOSI(23)の信号(on/off) は多数でて 25(DAC)に対し輻射ノイズが増えます。
      MBUS-25のピンが長い物や 25のパターンが長いPROTO基板を接続すると輻射ノイズが増えます。
      SCK(MBUS-18)と25(DAC)を絶縁の線を近づける場合はノイズはでません。
      ノイズの件は力量がたりませんが なんとかして原因と対策を知りたく思っています。

      不確定で未発表ですが 入力端子30Kohm 内蔵と思われるNS4148(番号が消してあった)を Ali から購入し BASIC の NS4150B と交換してみました。しかし pin 5 に 3.3v がでて pin 8が gnd状態で Speaker が 発熱し中断しました。
      NS4150B -> NS4148_5 = 3.3V -> Speaker hot !
      何か方法が間違っていたかは解明出来ていません。
      この件、たしか mはげ様は NS4150B の入力に 30Kohm を接続し変化なしと聞いています。

      >ちなみに【ゆっくり解説】パラレル処理【電子工作】、、I2Sという音声用のペリフェラルを利用しています。
      情報をありがとうございます。今回の記事の方法かと想像していました。なるほど、I2S信号からの出力だったのですね。素晴らしいです。

      macsbug

      5月 4, 2019 at 8:44 pm

      • なるほど。色々教えて頂きありがとうございます。
        C44側は同じGrayでもロットによって回路が異なるのですね。推奨回路通りにした方がノイズが大きくなるとか基板設計が良くないのでしょうか。GNDの電位が安定していないのかも知れません。
        GPIO25側は外部にも出力出来るように分岐していて、それがアンテナとなってSPIのディジタルノイズを拾ってしまうという推測ですね。そうだとすればおっしゃる通り分岐させずに切ってしまえばノイズが抑えられるかも知れませんね。

        mはげ

        5月 4, 2019 at 10:16 pm


コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。