A Small Game "Snake" Controlled by Independent Buttons Based on 51 Single-chip Microcomputer and 8X8LED Dot Matrix Screen (MAX7219 Driver)

by Lisleapex Blog in Circuits > Arduino

7 Views, 0 Favorites, 0 Comments

A Small Game "Snake" Controlled by Independent Buttons Based on 51 Single-chip Microcomputer and 8X8LED Dot Matrix Screen (MAX7219 Driver)

FSEVOKNMANYB1MI.png

Snake, a classic, nostalgic game, is a must-write program for microcontrollers.


The 51 microcontroller minimum development board is used, and it is controlled by 8-bit self-made independent buttons. The microcontroller chip is STC89C52RC, and the crystal oscillator is @12.0000MHz. The 8X8LED dot matrix screen module is driven by MAX7219, which contains an automatic scanning circuit. You only need to send the data to be displayed into the chip register. It is very easy to use and recommended.

Supplies

STC89C52RC

51 microcontroller

crystal

Principle Analysis

1. Detection of matrix keyboard keys

Delay cannot be used to eliminate jitter, as it will block the program.


Use a timer to call the detection function in the interrupt function every 20ms (to skip the debounce), detect which key is pressed, and determine whether it is short-pressed, long-pressed, or released, and save the corresponding key code value to a variable, and then get the key code value in the main function to control the game operation. In the game, short-pressing and long-pressing the direction keys will change the direction of the snake head, so that the control will be more accurate.


2. Preservation of snake body data

Use one byte to save the position of a point on the snake body. The tens digit (1~8) indicates the column, which is 1~8 columns from left to right, and the ones digit (1~8) indicates the row, which is 1~8 rows from top to bottom. The 8X8 dot matrix screen has a total of 64 points, so a total of 64 unsigned character variables are required to save. A one-dimensional array SnakeBody can be defined to save the snake body data.


Use a variable SnakeHead to save the index of the corresponding data of the snake head in the array SnakeBody (range: 0~63).


Use another variable SnakeLength to save the length of the snake.


3. Movement and display of the snake

The dot matrix screen has 64 dots, and one byte can control 8 dots. A total of 8 bytes are required to store the data to be displayed. Define a display buffer array DisplayBuffer to save the data to be displayed. After the snake moves, or the snake eats food and produces new food, the data needs to be updated to the display buffer array DisplayBuffer, and then the data is written to the register of the MAX7219 chip. [The code also defines an intermediate buffer array MiddleBuffer (update the data to MiddleBuffer first, and then combine the 64 0 or 1 data in MiddleBuffer into 8 bytes). In fact, you don’t need to define this array. You can directly update the data to the display buffer array DisplayBuffer through operations such as AND, OR, and shift.] At the same time, the data in the array SnakeBody that saves the snake body data must also be updated synchronously.


4. Detection of the snake head hitting the wall and the body

Wall collision detection: If the data of the new snake head in the array SnakeBody after the move is less than 1 in the ones digit, less than 1 in the tens digit, greater than 8 in the ones digit, and greater than 8 in the tens digit, it means that it has hit the wall. The game is over.


Body collision detection: If the data of the corresponding bit in the cache array of the new snake head is 1, it means that it has hit the body. The game is over.


5. Creation of true random food

Each time a button is pressed, the lower eight bits of timer 0 (TL0) are used as seeds, and two random numbers are generated by the random function rand to determine a point on the dot matrix screen to determine whether this position is the snake body. If not, this is the location of the created food. If it is, search around this position until an empty position is found.


6. Control of the direction of the snake head

if((KeyNum==13 || KeyNum==29) && Direction!=1) //Detect both long and short presses to prevent short presses from being insensitive and causing the direction to not change

{ //If the "left" key (S13) is pressed short or long, and the original direction of the snake head is not right

Direction=3; //Then the direction of the snake head is changed to left

}

As shown in the above code, after pressing the button, if the direction corresponding to the button is not the opposite direction of the snake head's movement, the value of the snake head direction variable Direction will be changed. However, there is a problem with this. If the button is pressed multiple times before the next move, the game may end. For example, before the next move, after the snake "turns right", if it continues to "turn right", it will hit the snake's body (the second point of the snake, that is, the next point of the snake's head).

if((KeyNum==13 || KeyNum==29) && LastDirection!=1) //Detect both long and short presses to prevent short presses from being insensitive and causing the direction to not change

{ //If the "left" key (S13) is pressed short or long, and the original direction of the snake head is not right

Direction=3; //Then the direction of the snake head is changed to left

}

The solution is to define another variable LastDirection to record the direction of the last movement. Note that the value of Direction should be assigned to LastDirection for update after the snake moves, which can solve the above problem.


7. Scrolling display up, down, left, and right

The input parameters of the scrolling display function are: the first address of the array (i.e. the array name) and the offset Offset. The data is processed by shifting, and and other operations and then written into the display cache array. If you are not familiar with it, it is recommended to learn about the application of pointers.

Module Codes

  1. Timer 0

#ifndef __TIMER0_H__

#define __TIMER0_H__


void Timer0_Init(void);


#endif

2. 8 independent buttons

#ifndef __KEYSCAN_8_H__

#define __KEYSCAN_8_H__


unsigned char Key(void);

void Key_Tick(void);


#endif

3. 8X8LED dot matrix screen (MAX7219 driver)

#ifndef __MATRIXLED_MAX7219_H__

#define __MATRIXLED_MAX7219_H__


void MAX7219_WriteByte(unsigned char Byte);

void MAX7219_SendData(unsigned char Address,Data);

void MatrixLED_Clear(void);

void MatrixLED_Init(void);

void MatrixLED_MoveLeft(unsigned char *Array,unsigned int Offset);

void MatrixLED_MoveUp(unsigned char *Array,unsigned int Offset);


#endif

4. Main function

/*

by Gan Tengsheng@20241202

Effect view/operation demonstration: You can search "Gan Tengsheng" or "gantengsheng" on B station to view

MCU: STC89C52RC

Crystal oscillator: 12T@12.0000MHz

Peripheral: self-made 8-bit independent button, 8X8LED dot matrix screen (MAX7219 driver)

Principle analysis: https://blog.csdn.net/gantengsheng/article/details/143581157


Operation instructions:


(1) Self-made independent button version


K7 K2 Up: K7

Down: K6

K8 K5 K4 K1 Left: K8

Right: K5

K6 K3 Start/Pause/Continue: K1

Return: K2


(2) Puzhong development board matrix button version


S1 S2 S3 S4 Up: S10

Down: S14

S5 S6 S7 S8 Left: S13

Right: S15

S9 S10 S11 S12 Start/Pause/Continue: S16

Return: S12

S13 S14 S15 S16


*/


#include <REGX52.H> //Header file containing register definitions in the installation directory

#include <STDLIB.H> //Header file containing random function declarations in the installation directory

#include "Timer0.h" //Header file in the project directory

#include "KeyScan_8.h"

#include "MatrixLED_MAX7219.h"


unsigned char KeyNum; //Store the obtained key code value

unsigned char Mode; //Game mode, 0: Display the English name of the game "<<SNAKE>>", 1: Display the English word "DIFFICULTY" for difficulty,

//2: Difficulty selection interface (range is 1~5), 3: Game in progress, 4: Full screen flashing after the game ends,

//5: Display the score in English "SCORE", 6: Circular scrolling display score

unsigned char MoveSnakeFlag; //Snake body movement flag, 1: move, 0: do not move

unsigned char NowDirection=1; //Direction of the snake head movement, 1: right, 2: up, 3: left, 4: down, the default is to move right when the game starts

unsigned char LastDirection=1; //Direction of the last movement of the snake head, 1: right, 2: up, 3: left, 4: down, the default is to move right when the game starts

unsigned char Length=2; //Length of the snake, initial value is 2

unsigned char Head=1; //In the array that saves the data of the entire snake (a total of 64 data), the index of the data corresponding to the snake head (0~63), the initial length of the snake is 2, and only two data are used initially

//(the first and second data of the array), the snake head corresponds to the second data, the index is 1, the range of Head: 0~63

unsigned char GameOverFlag; //Flag of the game ending, 1: game ending, 0: game not ending

unsigned char FlashFlag; //Flashing flag, 1: not displayed, 0: displayed

unsigned char Food; //Save the location of the created food

unsigned char Offset1; //Offset, used to control the scrolling display of English letters to the left (cleared after switching modes)

unsigned char Offset2; //Offset, used to control the scrolling display of numbers up and down (not cleared after switching modes)

unsigned char RollFlag1; //Flag of scrolling display, fast speed, 1: scrolling, 0: not scrolling

unsigned char RollFlag2; //Flag of scrolling display, moderate speed, 1: scrolling, 0: not scrolling

unsigned char RollFlag3; //Flag of scrolling display, slow speed, 1: scrolling, 0: not scrolling

unsigned char RollUpFlag; //Difficulty selection interface, flag of digital scrolling display up, 1: scrolling, 0: not scrolling

unsigned char RollDownFlag; //Difficulty selection interface, the flag for the number to scroll down, 1: scroll, 0: not scroll

unsigned char RollCount; //Count of up and down scrolling

unsigned char ExecuteOnceFlag; //The flag for executing only once in each mode, 1: execute, 0: not execute

unsigned char SnakeMoveSpeed=100; //Snake movement speed, the smaller the value, the faster the speed, the default is 1s after power-on (timer timing 10ms)

unsigned char T0Count0, T0Count1, T0Count2, T0Count3; //Timer counting variable

unsigned char PauseFlag; //Pause flag, 1: pause, 0: not pause


//Use a one-bit array to save the data of which points of the dot matrix screen should be lit, each data is 0 or 1, 0 is not lit, 1 is lit

//When the display needs to be updated, combine these 64 data into 8 bytes, update to the display buffer array, and then write to the MAX7219 register

//It is also OK not to define this middle buffer array, and the data in the display buffer array can be directly updated by AND, OR, shift and other operations

//It can also be defined as a two-dimensional array

//Use idata to modify the array variable because the lower 128 bits of the direct addressing of the STC89C52RC's on-chip RAM are not enough, and the upper 128 bits of indirect addressing are needed

unsigned char idata MiddleBuffer[]={

0,0,0,0,0,0,0,0,

0,1,1,0,0,0,0,0, //The initial position of the snake is in the second column, second row (tail) and the third column, second row (head)

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,

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,0,0,0,0,0,0,0,

0,0,0,0,0,0,0,0,

};


//Data of the snake body, the tens digit (1~8) indicates the column where the snake body is located, and the ones digit (1~8) indicates the row where the snake body is located

//The initial length is 2, the head corresponds to "32" (third column, second row), which is the first data in the array, so the initial value of the snake head Head is 1

//The tail corresponds to "22" (second column, second row), which is the 0th data in the array

unsigned char idata SnakeBody[]={

22,32,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,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,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,0,0,0,

};


//Screen display cache

//Requirement: vertical modulus, high bit at the top

unsigned char DisplayBuffer[]={

0x00,0x40,0x40,0x00,0x00,0x00,0x00,0x00,

};


unsigned char idata ScoreShow[]={ //Used to scroll and display the score

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //No display

0x00,0x00,0x00,0x00,0x00,0x00, //After the game, write the ten-digit digital font of the snake's length Length to this line

0x00,0x00,0x00,0x00,0x00,0x00, //After the game, write the one-digit digital font of the snake's length Length to this line

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //No display

};


//Negative code (bright spot is 1), vertical modulus, high bit on top, font size is 6*8

//Modified with code, indicating saving to ROM

unsigned char code Text_SNAKE[]={ //Game name "<<SNAKE>>"

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //No display

0x10,0x28,0x44,0x92,0x28,0x44,0x82,0x00, // <<

0x00,0x62,0x92,0x92,0x92,0x8C, // S

0x00,0xFE,0x20,0x10,0x08,0xFE, // N

0x00,0x3E,0x48,0x88,0x48,0x3E, // A

0x00,0xFE,0x10,0x28,0x44,0x82, // K

0x00,0xFE,0x92,0x92,0x92,0x82, // E

0x00,0x82,0x44,0x28,0x92,0x44,0x28,0x10, // >>

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //No display

};


unsigned char code Text_DIFFICULTY[]={ //English name of difficulty "<<DIFFICULTY>>"

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //No display

0x00,0xFE,0x82,0x82,0x44,0x38, // D

0x00,0x00,0x82,0xFE,0x82,0x00, // I

0x00,0xFE,0x90,0x90,0x90,0x80, // F

0x00,0xFE,0x90,0x90,0x90,0x80, // F

0x00,0x00,0x82,0xFE,0x82,0x00, // I

0x00,0x7C,0x82,0x82,0x82,0x44, // C

0x00,0xFC,0x02,0x02,0x02,0xFC, // U

0x00,0xFE,0x02,0x02,0x02,0x02, // L

0x00,0x80,0x80,0xFE,0x80,0x80, // T

0x00,0xE0,0x10,0x0E,0x10,0xE0, // Y

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // No display

0x00,0x00,0x00,0x42,0xFE,0x02,0x00,0x00, // 1 If you do not press the button to skip, it will automatically switch to the next mode after displaying "1"

};


unsigned char code DifficultySelect[]={ //Difficulty corresponding to the number, "1~5"

0x00,0x00,0x00,0x42,0xFE,0x02,0x00,0x00, // 1

0x00,0x00,0x42,0x86,0x8A,0x92,0x62,0x00, // 2

0x00,0x00,0x84,0x82,0xA2,0xD2,0x8C,0x00, // 3

0x00,0x00,0x18,0x28,0x48,0xFE,0x08,0x00, // 4

0x00,0x00,0xE4,0xA2,0xA2,0xA2,0x9C,0x00, // 5

0x00,0x00,0x00,0x42,0xFE,0x02,0x00,0x00, // 1 is the same as the first line because it is to be displayed in a circular scrolling manner

};


unsigned char code Text_SCORE[]={

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //No display

0x00,0x62,0x92,0x92,0x92,0x8C, // S

0x00,0x7C,0x82,0x82,0x82,0x44, // C

0x00,0x7C,0x82,0x82,0x82,0x7C, // O

0x00,0xFE,0x90,0x98,0x94,0x62, // R

0x00,0xFE,0x92,0x92,0x92,0x82, // E

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //No display

};


unsigned c

Effect Display

Snake.png

Conclusion

The key to this mini-game lies in the generation of true random numbers, the preservation of snake data, the movement of the snake body, collision detection, button detection, scanning and display of the LED dot matrix screen, scrolling display of characters, and the use of various signs.