LCD freezes after pressing RESET button

I want to write a script about snake game, if i press RESET button, the snake will spin. I'm wondering why my display freezes after I press the RESET button, but it's actually running? How can I solve it?

I am using ESP32 and 1602A LCD

#include <LiquidCrystal_I2C.h>   //LCD library
#include <stdlib.h>
#include <limits.h>
#include "Arduino_1602_Snake.h"
#include <Preferences.h>

#define GRAPHIC_WIDTH 16
#define GRAPHIC_HEIGHT 4

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
LiquidCrystal_I2C lcd(0x27,16,2);  //0x27 I2c address
Preferences prefs; // 声明Preferences对象

byte block[3] = {
  B01110,
  B01110,
  B01110,
};

byte apple[3] = {
  B00100,
  B01010,
  B00100,
};

struct Pos {
  uint8_t x = 0, y = 0;
};



int8_t snakePosHistory[GRAPHIC_HEIGHT * GRAPHIC_WIDTH][2] = {{0}}; //first element is the head.
size_t snakeLength = 0;
enum Sd {SNAKE_LEFT, SNAKE_UP, SNAKE_RIGHT, SNAKE_DOWN};
Sd snakeDirection;
int8_t applePos[2];
unsigned long lastGameUpdateTick = 0;
unsigned long gameUpdateInterval = 1000;
bool thisFrameControlUpdated = false;
enum Gs {GAME_MENU, GAME_PLAY, GAME_LOSE, GAME_WIN};
Gs gameState;

uint8_t graphicRam[GRAPHIC_WIDTH * 2 / 8][GRAPHIC_HEIGHT];

void graphic_generate_characters()
{
  /*
    space: 0 0
    0: 0 A
    1: 0 B
    2: A 0
    3: A A
    4: A B
    5: B 0
    6: B A
    7: B B
  */
  for (size_t i = 0; i < 8; i++) {
    byte glyph[8];
    int upperIcon = (i + 1) / 3;
    int lowerIcon = (i + 1) % 3;

    memset(glyph, 0, sizeof(glyph));
    if (upperIcon == 1)
      memcpy(&glyph[0], &block[0], 3);
    else if (upperIcon == 2)
      memcpy(&glyph[0], &apple[0], 3);

    if (lowerIcon == 1)
      memcpy(&glyph[4], &block[0], 3);
    else if (lowerIcon == 2)
      memcpy(&glyph[4], &apple[0], 3);

    lcd.createChar(i, glyph);
  }
  delay(1); //Wait for the CGRAM to be written
}

void graphic_clear() {
  memset(graphicRam, 0, sizeof(graphicRam));
}

void graphic_add_item(uint8_t x, uint8_t y, enum DisplayItem item) {
  graphicRam[x / (8 / 2)][y] |= (uint8_t)item * (1 << ((x % (8 / 2)) * 2));
}


void graphic_flush() {
  lcd.clear();
  for (size_t x = 0; x < 16; x++) {
    for (size_t y = 0; y < 2; y++) {
      enum DisplayItem upperItem = (DisplayItem)((graphicRam[x / (8 / 2)][y * 2] >> ((x % (8 / 2)) * 2)) & 0x3);
      enum DisplayItem lowerItem = (DisplayItem)((graphicRam[x / (8 / 2)][y * 2 + 1] >> ((x % (8 / 2)) * 2)) & 0x3);
      if (upperItem >= GRAPHIC_ITEM_NUM)
        upperItem = GRAPHIC_ITEM_B;
      if (lowerItem >= GRAPHIC_ITEM_NUM)
        lowerItem = GRAPHIC_ITEM_B;
      lcd.setCursor(x, y);
      if (upperItem == 0 && lowerItem == 0)
        lcd.write(' ');
      else
        lcd.write(byte((uint8_t)upperItem * 3 + (uint8_t)lowerItem - 1));
    }
  }
}


void game_new_apple_pos()
{
  bool validApple = true;
  do {
    applePos[0] = rand() % GRAPHIC_WIDTH;
    applePos[1] = rand() % GRAPHIC_HEIGHT;
    validApple = true;

    for (size_t i = 0; i < snakeLength; i++)
    {
      if (applePos[0] == snakePosHistory[i][0] && applePos[1] == snakePosHistory[i][1]) {
        validApple = false;
        break;
      }
    }
  } while (!validApple);

}

void game_init() {
  srand(micros());
  gameUpdateInterval = 1000;
  gameState = GAME_PLAY;

  snakePosHistory[0][0] = 3; snakePosHistory[0][1] = 1;
  snakePosHistory[1][0] = 2; snakePosHistory[1][1] = 1;
  snakePosHistory[2][0] = 1; snakePosHistory[2][1] = 1;
  snakePosHistory[3][0] = 0; snakePosHistory[3][1] = 1;
  snakeLength = 4;
  snakeDirection = SNAKE_RIGHT;
  game_new_apple_pos();
  thisFrameControlUpdated = false;
}

void game_calculate_logic() {
  if (gameState != GAME_PLAY) //Game over. Don't bother calculating game logic.
    return;

  //Calculate the movement of the tail
  for (size_t i = snakeLength; i >= 1; i--) { //We intentionally use i=snakeLength instead of i=snakeLength-1 so that the snake will be lengthened right after it eats the apple
    memcpy(snakePosHistory[i], snakePosHistory[i-1], sizeof(snakePosHistory[i]));
  }
  //Calculate the head movement
  memcpy(snakePosHistory[0], snakePosHistory[1], sizeof(snakePosHistory[0]));
  switch (snakeDirection) {
    case SNAKE_LEFT:  snakePosHistory[0][0]--; break;
    case SNAKE_UP:    snakePosHistory[0][1]--; break;
    case SNAKE_RIGHT: snakePosHistory[0][0]++; break;
    case SNAKE_DOWN:  snakePosHistory[0][1]++; break;
  }

  //Look for wall collision
  if (snakePosHistory[0][0] < 0 || snakePosHistory[0][0] >= GRAPHIC_WIDTH || snakePosHistory[0][1] < 0 || snakePosHistory[0][1] >= GRAPHIC_HEIGHT) {
    gameState = GAME_LOSE;
    return;
  }

  //Look for self collision
  for (size_t i = 1; i < snakeLength; i++) {
    if (snakePosHistory[0][0] == snakePosHistory[i][0] && snakePosHistory[0][1] == snakePosHistory[i][1]) {
      gameState = GAME_LOSE;
      return;
    }
  }

  if (snakePosHistory[0][0] == applePos[0] && snakePosHistory[0][1] == applePos[1]) {
    snakeLength++;
    gameUpdateInterval = gameUpdateInterval * 9 / 10;
    if (snakeLength >= sizeof(snakePosHistory) / sizeof(*snakePosHistory))
      gameState = GAME_WIN;
    else
      game_new_apple_pos();
  }


}

void game_calculate_display() {
  graphic_clear();
  switch (gameState) {
    case GAME_LOSE:
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Boo! You lose!");
      lcd.setCursor(0, 1);
      lcd.print("Length: ");
      lcd.setCursor(8, 1);
      lcd.print(snakeLength);
      break;
    case GAME_WIN:
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("You won. Congratz!");
      lcd.setCursor(0, 1);
      lcd.print("Length: ");
      lcd.setCursor(8, 1);
      lcd.print(snakeLength);
      break;
    case GAME_PLAY:
      for (size_t i = 0; i < snakeLength; i++)
        graphic_add_item(snakePosHistory[i][0], snakePosHistory[i][1], GRAPHIC_ITEM_A);
      graphic_add_item(applePos[0], applePos[1], GRAPHIC_ITEM_B);
      graphic_flush();
      break;
    case GAME_MENU:
      //Do nothing
      break;
  }
}



void setup() {
  Serial.begin(115200);
  prefs.begin("snake1"); // 打开命名空间snake
  Serial.print(prefs.getInt("gameState", 5));
  if (prefs.getInt("gameState", 0) == 1) {
    Serial.println("turn");
    prefs.getBytes("snakePosHistory", &snakePosHistory, prefs.getBytesLength("snakePosHistory"));
    snakeLength = (size_t) prefs.getUChar("snakeLength", 0);
    snakeDirection = Sd(prefs.getInt("snakeDirection"));
    prefs.getBytes("applePos", &applePos, prefs.getBytesLength("applePos"));
    lastGameUpdateTick = prefs.getULong("lastGameUpdateTick", 0);
    gameUpdateInterval = prefs.getULong("gameUpdateInterval", 1000);
    gameState = Gs(prefs.getInt("gameState", 0));
    prefs.getBytes("graphicRam", &graphicRam, prefs.getBytesLength("graphicRam"));
    thisFrameControlUpdated = prefs.getBool("thisFrameControlUpdated");
    switch (gameState) {
      case GAME_PLAY:
        switch (snakeDirection) {
          case SNAKE_LEFT:   snakeDirection = SNAKE_DOWN;  break;
          case SNAKE_UP:     snakeDirection = SNAKE_LEFT;  break;
          case SNAKE_RIGHT:  snakeDirection = SNAKE_UP;    break;
          case SNAKE_DOWN:   snakeDirection = SNAKE_RIGHT; break;
        }
        thisFrameControlUpdated = true;
        break;
      case GAME_MENU:
        game_init();
        break;
    graphic_generate_characters();
    }
  } else {
      Wire.begin(0,21); //LCD init SDA(IO0) SCL(IO21)
      lcd.begin(16, 2);
      lcd.backlight();
      lcd.clear();
      lcd.home();
    lcd.print("1602 LCD Snake");
    lcd.setCursor(0, 1);
    lcd.print("Press RESET to rotate snake");
    gameState = GAME_MENU;
    delay(1000);
    Serial.println("init");
 graphic_generate_characters();
     game_init();
  }
 
  
}

void loop() {
  lcd.setCursor(0, 0);


  lcd.setCursor(8, 0);

  if (millis() - lastGameUpdateTick > gameUpdateInterval) {
    Serial.println("loop");
    game_calculate_logic();
    game_calculate_display();
    lastGameUpdateTick = millis();
    thisFrameControlUpdated = false;
    prefs.putBytes("snakePosHistory", &snakePosHistory, sizeof("snakePosHistory"));
    prefs.putUChar("snakeLength", (uint8_t) snakeLength);
    prefs.putInt("snakeDirection", (int)snakeDirection);
    prefs.putBytes("applePos", &applePos, sizeof("applePos"));
    prefs.putULong("lastGameUpdateTick", lastGameUpdateTick);
    prefs.putULong("gameUpdateInterval", gameUpdateInterval);
    Serial.println((int)gameState);
    prefs.putInt("gameState", (int)gameState);
    prefs.putBytes("graphicRam", &graphicRam, sizeof("graphicRam"));
    prefs.putBool("thisFrameControlUpdated", thisFrameControlUpdated);
  }
}

This says 16x4 LCD

This says 16x2 LCD

Maybe the characters are off the page?

Popular belief is when resetting the processor the display gets reset. This is not true, when the system gets rebooted with the processor reset the LCD does not get reset. If it is waiting for something on the I2C it will be stuck there. You need to power down the display controller to reset it or modify the LCD so you can reset it via hardware. Glitches on the VCC can also cause it to lockup.

When I looked into it many years back the controller has a reset pin that is triggered by the VCC and is not brought out for user usage. I by practice expect this on any I2C device. When resetting the display will generally show what was written before reset and until initialized again.

The code will but the hardware will not be reset unless the VCC is cycled causing a hardware reset to the display controller. If the display controller is latched up in some mode to my knowledge software will not get it reset.

I found that I forgot to add lcd begin in setup() when gameState == 1 now the snake can move

void setup() {
  Serial.begin(115200);
  prefs.begin("snake1");
  Serial.print(prefs.getInt("gameState", 5));
  if (prefs.getInt("gameState", 0) == 1) {
    Wire.begin(0, 21); //LCD init SDA(IO0) SCL(IO21)
    lcd.begin(16, 2);
    lcd.backlight();
    ...

But now sometimes it will say

rst:0x1 (POWERON_RESET),boot:0x1 (DOWNLOAD_BOOT(UART0/UART1/SDIO_FEI_REO_V2))]

after pressing the RESET button, and then the LCD and ESP32 will freezes, and the RESET button will not return to normal unless the power is turned off and reconnected, how to solve it

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.