commit 438b31085f32f68586f015215eee2d1804825f89 Author: David Baranyai Date: Thu Aug 30 23:15:51 2018 +0200 Initial working version diff --git a/README.md b/README.md new file mode 100644 index 0000000..169685f --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Classic Snake game for the STM32 board +## Hardware prerequisities +* stm32duino (should work on generic Arduino boards) +* Philips PCD8544 (Nokia 3310) LCD + +## Software prerequisities +* Arduino Software +* [Arduino Core for STM32](https://github.com/stm32duino/Arduino_Core_STM32) +* [Adafruit PCD8544 Library](https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library) +* [Adafruit GFX Library](https://github.com/adafruit/Adafruit-GFX-Library) +* [SimpleTimer Library for Arduino](https://playground.arduino.cc/Code/SimpleTimer#Download) +* [Standard C++ for Arduino](https://github.com/maniacbug/StandardCplusplus) + +## Building and flashing +Just as with normal Arduino codes, build and flash it from the Arduino Software. +* Board: Generic STM32F103C series +* Variant: STM32F103C8 (20K RAM. 64K Flash) +* CPU Speed: 72MHz + +## Note +If using a different board, then change the configuration and modify the pins accordingly. +For the STM32 board the following changes are necessary: + +Adafruit_PCD8544.h: +``` +#define PCD8544_SPI_CLOCK_DIV SPI_CLOCK_DIV4 -> #define PCD8544_SPI_CLOCK_DIV SPI_CLOCK_DIV8 +``` + +``` +#ifdef __SAM3X8E__ +typedef volatile RwReg PortReg; +typedef uint8_t PortMask; -> typedef uint32_t PortMask; +#else +typedef volatile uint32_t PortReg; +typedef uint8_t PortMask; -> typedef uint32_t PortMask; +#endif +``` diff --git a/snake.ino b/snake.ino new file mode 100644 index 0000000..1337933 --- /dev/null +++ b/snake.ino @@ -0,0 +1,304 @@ +#include +#include +#include +#include + +#define clk PB13 +#define din PB15 +#define dc PB14 +#define cs PB12 +#define rst PA15 +// Software SPI (slower updates, more flexible pin options): +// pin 7 - Serial clock out (SCLK) +// pin 6 - Serial data out (DIN) +// pin 5 - Data/Command select (D/C) +// pin 4 - LCD chip select (CS) +// pin 3 - LCD reset (RST) +Adafruit_PCD8544 display = Adafruit_PCD8544(clk, din, dc, cs, rst); + +//speed in ms +#define SPEED 250 +SimpleTimer timer; + +#define SNAKE_INIT_LENGTH 10 //Initial length of snake +#define SNAKE_START_X 42 //Initial x position +#define SNAKE_START_Y 20 //Initial y position + +//Pin setup according to the board +#define left PA1 +#define right PB8 +#define up PA0 +#define down PB9 +#define center PC13 +#define left_led PB6 +#define right_led PB7 + +//Store the coordinates in a structure +struct coordinates +{ + int x; + int y; +}; + +std::vector snake; //store the snake points +coordinates snake_last_piece; //store the last point | needed for visualisation + +coordinates food; //food coordinates + +bool leds = true; //for LED blink + +int direction = 0; +int last_direction = 0; + +int points = 0; + +void setup() { + // put your setup code here, to run once: + //Serial port + Serial.begin(9600); + + //Set inputs and outputs + pinMode(left, INPUT_PULLUP); + pinMode(right, INPUT_PULLUP); + pinMode(up, INPUT_PULLUP); + pinMode(down, INPUT_PULLUP); + pinMode(center, INPUT_PULLUP); + pinMode(left_led, OUTPUT); + pinMode(right_led, OUTPUT); + + display.begin(); + display.setContrast(63); + + display.display(); //show splashscreen + delay(2000); + display.clearDisplay(); + + init_game(); + + timer.setInterval(SPEED, TimerCallbackFunc); //Timer for moving the snake +} + +void init_game() +{ + /* + * This function initializes the snake, clears the points and direction, and generate a new food. + * Can be used to reset the game. + */ + + //init the snake + direction = 0; + + points = 0; + + snake.clear(); //clear the vector + for(int i = 0; i < SNAKE_INIT_LENGTH; i++) + { + coordinates helper; + helper.x = SNAKE_START_X - i; + helper.y = SNAKE_START_Y; + snake.push_back(helper); + } + + snake_last_piece.x = 0; + snake_last_piece.y = 0; + + generate_food(); +} + +void TimerCallbackFunc() +{ + //blinks the 2 LED + digitalWrite(left_led, leds); + digitalWrite(right_led, !leds); + leds = !leds; + + //Moving in the given direction + snake_move(direction); +} + +void generate_food() +{ + /* + * Generates random x and y points until it's out of the snake's points. + */ + bool equal = true; + while(equal) + { + equal = false; + food.x = random(0, 83); + food.y = random(0,47); + for(int i = 0; i < snake.size(); i++) + { + if(snake[0].x == food.x && snake[0].y == food.y) + { + equal = true; + } + } + } + display.drawPixel(food.x, food.y, BLACK); + display.display(); +} + +void draw_snake() +{ + /* + * Draw the snake points. + */ + for(int i = 0; i < snake.size(); i++) + { + display.drawPixel(snake[i].x, snake[i].y, BLACK); + } + display.drawPixel(snake_last_piece.x, snake_last_piece.y, WHITE); + display.display(); +} + +bool check_crash() +{ + /* + * Returns true if the snake crashing into itself. + */ + for(int i = 1; i < snake.size(); i++) + { + if(snake[0].x == snake[i].x && snake[0].y == snake[i].y) + { + return true; + } + } + return false; +} + +void snake_move(int direction) +{ + /* + * Handling the moving directions and next points. + */ + last_direction = direction; + + snake_last_piece.x = snake[snake.size()-1].x; + snake_last_piece.y = snake[snake.size()-1].y; + + coordinates helper; + + if(direction == 0) //right + { + helper = snake[0]; + helper.x++; + if(helper.x > 83) + { + helper.x = 0; + } + } + else if(direction == 1) //left + { + helper = snake[0]; + helper.x--; + if(helper.x < 0) + { + helper.x = 83; + } + } + else if(direction == 2) //up + { + helper = snake[0]; + helper.y--; + if(helper.y < 0) + { + helper.y = 47; + } + } + else if(direction == 3) //down + { + helper = snake[0]; + helper.y++; + if(helper.y > 47) + { + helper.y = 0; + } + } + + //move forward every coordinate and replace the first with the new one + for(int i = snake.size(); i > 0; i--) + { + snake[i] = snake [i-1]; + } + snake[0] = helper; + draw_snake(); + + //if the snake ate the food, then increment by one and generate a new one + if(snake[0].x == food.x && snake[0].y == food.y) + { + increment_snake(); + generate_food(); + } +} + +void increment_snake() +{ + snake.push_back(snake[snake.size()]); + points++; +} + +void loop() { + // put your main code here, to run repeatedly: + timer.run(); + if(digitalRead(left) == 0) + { + Serial.print("Left button pressed"); + if(last_direction != 0) + { + direction = 1; + } + } + + if(digitalRead(right) == 0) + { + Serial.print("Right button pressed"); + if(last_direction != 1) + { + direction = 0; + } + } + + if(digitalRead(up) == 0) + { + Serial.print("Up button pressed"); + if(last_direction != 3) + { + direction = 2; + } + } + + if(digitalRead(down) == 0) + { + Serial.print("Down button pressed"); + if(last_direction != 2) + { + direction = 3; + } + } + + if(digitalRead(center) == 0) + { + Serial.print("Center button pressed"); + display.drawPixel(food.x, food.y, BLACK); + display.display(); + } + if(check_crash()) + { + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(BLACK); + display.setCursor(0,0); + display.println("Game over!"); + //display.setCursor(0,10); + display.println("Score: "); + display.print(points, DEC); + display.display(); + + while(digitalRead(center) != 0) + { + } + display.clearDisplay(); + init_game(); + } +}