Need help explaining some functions - PONG GAME CODE

Go To Last Post
25 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone. Before anything, I want to say thank you to everyone in this forum. I've been a long time lurker here and this forum has helped me massively even though I never posted a question. Even though I'm not an expert on AVR topics (yet :p), I understand the basics and I'm studying to get better (hoping to, one day, work on this area). 

 

Anyways, my teacher asked us to create a project where we would talk about an already built code (MUST function properly) and suggest improvements (brief presentation + code the said improvements). One of the topics that caught my eye on this forum was the game PONG. I browsed online and found 2 identical projects (the code is mostly the same) and thought "Why not?! I loved this when I was younger". 

I downloaded the code and decided to rewrite it on my own way (even though the code core is the same, I used my variables, my functions, etc. and commented everything). I also built the circuitry according to the schematic offered in the said projects (both of them were not working properly so I decided to change the schematic according to my own code). With this, the schematic designed was:

 

 

With this, the code written was this: 

 

// -------------------------------Libraries used----------------------------------------------------//

	#include <avr/io.h>
	#include <stdlib.h>
	#include <stdio.h>
	#include <avr/interrupt.h>
	#include <stdint.h>
	#define F_CPU 8000000UL									// 8Mhz Internal Oscillator.
	#include <util/delay.h>
	#include <math.h>

// -------------------------------------------------------------------------------------------------//

// -------------------------------Blinking LED with 1s delay----------------------------------------//

	#define LED 1
	volatile unsigned char count = 15;						// In order to have a blinking led with 1s timer.
	volatile unsigned char led;							// Verifies if LED is turned ON or OFF.

// -------------------------------------------------------------------------------------------------//

// -------------------------------Global variables used---------------------------------------------//

	#define nrCol  8								// Number of columns (8x8 Led Matrix).
	#define PERIOD 10								// In milliseconds.
	#define DUTY_CYCLE 30								// Used in start_buzzer() in order to have sound.
	#define CLK   0									// PB0: Shift Register pin 11 (Clock).
	#define S_IN  1									// PB1: Shift Register pin 14 (Data).
	#define LATCH 2									// PB2: Shift Register pin 12 (Latch).

	uint8_t column[nrCol];								// Array used later on to display data on the Led Matrix.
	uint8_t line[12]={0,0,1,2,4,8,16,32,64,128,0,0};				// Line Mapping.
	uint8_t sw,sw1,sw2,score1,score2;						// sw: Counter for displaying purposes; sw1: Acts like a flag to know whether or not the player missed the ball ; sw2: Acts like a flag to know whether or not the player missed the ball ; score 1: Score of P1 ; score 2: Score of P2.
	uint8_t PSpeed=10, BSpeed=20;							// PSpeed: Players speed ; BSpeed: Ball speed.	

// -------------------------------------------------------------------------------------------------//

// -------------------------------Ball structure----------------------------------------------------//

	struct ball {
		int8_t x;								// New ball coord on x_axis.
		int8_t y;								// New ball coord on y_axis.

		int8_t old_x;								// Old ball coord on x_axis.
		int8_t old_y;								// Old ball coord on y_axis.

		int8_t speed_x;								// Ball speed on x_axis.
		int8_t speed_y;								// Ball speed on y_axis.

	} ball;

// -------------------------------------------------------------------------------------------------//

// -------------------------------Functions (prototypes)--------------------------------------------//

	void inic(void);								// General initializations.
	uint8_t readButton(uint8_t index);						// Reads current state of a button (1 -> Pressed; 0 -> Not pressed). Index = {1,2,3,4}.
	void start_buzzer();								// Enables the buzzer.
	void stop_buzzer();								// Disables the buzzer.
	void start_music();								// Plays a short melody before the game starts.
	void shift_data(uint8_t data); //used in update_score() funct.
	void update_score(uint8_t score_p1, uint8_t score_p2); //use this directly

// -------------------------------------------------------------------------------------------------//

// -------------------------------Interrupt routine (TimerCounter2)---------------------------------//

	ISR(TIMER2_COMP_vect) {								// Led blink interruption

		count--;								// Count = 15 by default. 32.7ms * 15 ~ 0,5s. Half a second on, half a second off.
		led = (PORTC & LED);							// Blinking LED.

		if (count==0 && led==0) {						// Comes here if led is turned off.
			PORTC |= LED;							// LED ON.
			count = 15;							// Resets counter.
		}

		if (count==0 && led==1) {						// Comes here if led is turned on.
			PORTC &= ~LED;							// LED OFF.
			count = 15;							// Resets counter.
		}
	}

// -------------------------------------------------------------------------------------------------//

// -------------------------------Display related functions-----------------------------------------//

	void init_matrix(uint8_t *column) {
		uint8_t i;
		for (i = 0; i < nrCol; i++)
			column[i] = 0xFF;						// Turns all LEDS ON (with the help of the column[] array).
	}

	void clear() {
		uint8_t i;
		for (i = 0; i < nrCol; i++)
			column[i] = 0;							// Turns all LEDS OFF (with the help of the column[] array).
	}

	void display(uint8_t *column) {
		uint8_t i;
		uint8_t seq;

		seq = 0x01;																													

		PORTA = line[ball.x+2];						        // Displaying the ball on the LED matrix.
		PORTD = line[ball.y+2];							// Displaying the ball on the LED matrix.
		_delay_ms(1);								// Takes 2 ms to execute.

		for (i = 0; i < nrCol; i++) {
			PORTA = seq;							// Outputs data to PORTA via the variable "seq".
			PORTD = column[i];						// Outputs data to PORTD via the array column[].
			_delay_ms(1);							// Takes 2 ms to execute.

			seq = seq << 1;							// Fast way to increase seq in order to display different lines {1,2,4,8,16,32,64,128}.
		}
	}

// -------------------------------------------------------------------------------------------------//

// -------------------------------Game related functions--------------------------------------------//

	void initBall() {
		int8_t v[2] = {-1,1};
		//int8_t v2[3] = {-1,0,1};

		ball.x = 3+rand()%2;							// Initializes the ball on x_axis (4th or 5th column): 3 -> 4th column ; rand()%2 -> Remainder after dividing a random LARGE number by 2 (will ALWAYS be 0 or 1).
		ball.y = 3+rand()%2;							// Initializes the ball on y_axis (4th or 5th row). 3 -> 4th row ; rand()%2 -> Remainder after dividing a random LARGE number by 2 (will ALWAYS be 0 or 1).
		ball.old_x = ball.x;							// Stores the new ball position (x_axis) as the old ball position (x_axis) --> Works as memory.
		ball.old_y = ball.y;							// Stores the new ball position (y_axis) as the old ball position (y_axis) --> Works as memory.
		ball.speed_x = v[rand()%2];						// Sets the ball speed (on the x_axis) as -1 or 1 according to the number obtained after the division of rand()%2 (always 0 or 1, therefore, v[0 or 1].
		ball.speed_y = 0;							// At the beginning, the ball only moves on the x_axis, therefore, there's no need to set the ball speed on the y_axis.

		column[0]=column[7]=24;							// Defines each player position (4th and 5th row LEDS ON). Each player consists of 2 leds. Needs to be here because, when the game resets, the players need to be defined again.
	}

	void move_ball() {
		ball.old_x=ball.x;							// Stores the new ball position (x_axis) as the old ball position (x_axis) --> Works as memory.
		ball.x=ball.x + ball.speed_x;						// If speed.x = 1, ball.x will add 1 to its current value (therefore, going in one direction). If speed.x = -1, ball.x will add -1 to its current value (opposite direction).

		ball.old_y=ball.y;							// Stores the new ball position (x_axis) as the old ball position (x_axis) --> Works as memory.
		ball.y=ball.y + ball.speed_y;						// If speed.y = 1, ball.y will add 1 to its current value (therefore, going in one direction). If speed.y = -1, ball.y will add -1 to its current value (opposite direction). If speed.y = 0, ball.y value will stay the same.

			//----------Wall Collision----------//

			if(ball.y==7 && ball.speed_y==1)				// If the ball touches the 8th row (array[7]) -> MATRIX TOP.
				ball.speed_y=-1;					// Its speed is inverted (from 1 to -1), forcing the ball to go in the opposite direction (like commented above) -> Bounces back.

			if(ball.y==0 && ball.speed_y==-1)				// If the ball touches the 1st row (array[0]) -> MATRIX BOTTOM.
				ball.speed_y=1;						// Its speed is inverted (from -1 to 1), forcing the ball to go in the opposite direction (like commented above) -> Bounces back.

			sw1=0;
			sw2=0;																													

			//----------Player Collision--------//

				//----------Collision with the right player----------//

				if(ball.x==6) {						// If the ball is in the 7th column (array[6]) -> Just before hitting the goalie (or not).
					if((line[2+ball.y]+line[2+ball.y-1])==column[7]) {		// ???
						ball.speed_x=-1;			// The goalie was positioned correctly, therefore, the ball bounces back (speed.x inverted).
						if(ball.speed_y!=1)
							ball.speed_y++;			// Forces the ball to move in a different direction (y_axis wise) by adding 1 to the variable ball.speed_y.
						sw2=1;					// Works as a flag. If sw2=1, then the right player defended the ball successfully.
					}

					if ((line[2+ball.y+1]+line[2+ball.y])==column[7]) {		// ????
						ball.speed_x=-1;			// The goalie was positioned correctly, therefore, the ball bounces back (speed.x inverted).
						if(ball.speed_y!=-1)
							ball.speed_y--;			// Forces the ball to move in a different direction (y_axis wise) by adding -1 to the variable ball.speed_y.
						sw2=1;					// Works as a flag. If sw2=1, then the right player defended the ball successfully.
					}

					if (((line[2+ball.y+1]+line[2+ball.y+2])==column[7]) && (ball.speed_y==1)) {			//????
						ball.speed_x=-1;			// The goalie was positioned correctly, therefore, the ball bounces back (speed.x inverted).
						ball.speed_y=-1;			// Forces the ball to move in the opposite direction (by inverting speed.y from 1 to -1).
						sw2=1;					// Works as a flag. If sw2=1, then the right player defended the ball successfully.
					}

					if (((line[2+ball.y-1]+line[2+ball.y-2])==column[7]) && (ball.speed_y==-1)) {		//???
						ball.speed_x=-1;			// The goalie was positioned correctly, therefore, the ball bounces back (speed.x inverted).
						ball.speed_y=1;				// Forces the ball to move in the opposite direction (by inverting speed.y from -1 to 1).
						sw2=1;					// Works as a flag. If sw2=1, then the right player defended the ball successfully.
					}
				}

				//----------------Left Player Scores-----------------//

				if (sw2==0 && ball.x==6) {				// If the right player missed the ball.
					score1++;					// Player 1 score goes up by 1.
					if (score1 == 10)
						update_score(0, 0);// Calls function update_score() and sends, by arguments, both players' scores. This will be used to display the numbers on the 7 segment displays.
					else
						update_score(score1, score2);// Calls function update_score() and sends, by arguments, both players' scores. This will be used to display the numbers on the 7 segment displays.

					column[7]=255;					// Column 8 (array[7]) turns every LED ON (acts as an animation because a goal was scored).
					column[6]=line[2+ball.y];			// Column 7 (array[6]) turns the led (row) where the ball entered "the net" ON.
					start_buzzer();					// Calls function start_buzzer(). The buzzer will play sound while the animation (code below) is displayed.
					for (sw=0;sw<200;sw++) {
						display(column);			// Calls function display() in order to show the goal animation.
					}
					stop_buzzer();					// When the animation ends (code above), the buzzer stops playing sound (game will restart).
					initBall();					// Resets the ball in the middle of the field. Game restarts.
				}

				//-----------Collision with the left player----------//

				if(ball.x==1) {						// If the ball is in the 2nd column (array[1]) -> Just before hitting the goalie (or not).
					if((line[2+ball.y]+line[2+ball.y-1])==column[0]) {		//????
						ball.speed_x=1;				// The goalie was positioned correctly, therefore, the ball bounces back (speed.x inverted).
						if(ball.speed_y!=1)
							ball.speed_y++;			// Forces the ball to move in a different direction (y_axis wise) by adding 1 to the variable ball.speed_y.
						sw1=1;					// Works as a flag. If sw1=1, then the left player defended the ball successfully.
					}

					if ((line[2+ball.y+1]+line[2+ball.y])==column[0]) {		// ????
						ball.speed_x=1;				// The goalie was positioned correctly, therefore, the ball bounces back (speed.x inverted).
						if(ball.speed_y!=-1)
							ball.speed_y--;			// Forces the ball to move in a different direction (y_axis wise) by adding -1 to the variable ball.speed_y.
						sw1=1;					// Works as a flag. If sw1=1, then the left player defended the ball successfully.
					}

					if (((line[2+ball.y+1]+line[2+ball.y+2])==column[0]) && (ball.speed_y==1)) {		//????
						ball.speed_x=1;				// The goalie was positioned correctly, therefore, the ball bounces back (speed.x inverted).
						ball.speed_y=-1;			// Forces the ball to move in the opposite direction (by inverting speed.y from -1 to 1).
						sw1=1;					// Works as a flag. If sw1=1, then the left player defended the ball successfully.
					}

					if (((line[2+ball.y-1]+line[2+ball.y-2])==column[0]) && (ball.speed_y==-1)) {		//????
						ball.speed_x=1;				// The goalie was positioned correctly, therefore, the ball bounces back (speed.x inverted).
						ball.speed_y=1;				// Forces the ball to move in the opposite direction (by inverting speed.y from 1 to -1).
						sw1=1;					// Works as a flag. If sw1=1, then the left player defended the ball successfully.
					}
				}	

				//----------------Right Player Scores----------------//

				if (sw1==0 && ball.x==1) {				// If the left player missed the ball.
					score2++;					// Player 2 score goes up by 1.
					if (score1 == 10)
						update_score(0, 0);// Calls function update_score() and sends, by arguments, both players' scores. This will be used to display the numbers on the 7 segment displays.
					else
						update_score(score1, score2);// Calls function update_score() and sends, by arguments, both players' scores. This will be used to display the numbers on the 7 segment displays.
					column[0]=255;					// Column 1 (array[0]) turns every LED ON (acts as an animation because a goal was scored).
					column[1]=line[2+ball.y];			// Column 2 (array[1]) turns the led (row) where the ball entered "the net" ON.
					start_buzzer();					// Calls function start_buzzer(). The buzzer will play sound while the animation (code below) is displayed.
					for (sw=0;sw<200;sw++){
						display(column);			// Calls function display() in order to show the goal animation.
					}
					stop_buzzer();					// When the animation ends (code above), the buzzer stops playing sound (game will restart).
					initBall();					// Resets the ball in the middle of the field. Game restarts.
				}

			//-----------Field Corners----------//

			if (ball.x==1 && ball.y==0 && ball.speed_y==-1)
				ball.speed_y=1;

			if (ball.x==6 && ball.y==0 && ball.speed_y==-1)
				ball.speed_y=1;

			if (ball.x==1 && ball.y==7 && ball.speed_y==1)
				ball.speed_y=-1;

			if (ball.x==6 && ball.y==7 && ball.speed_y==1)
				ball.speed_y=-1;

			//--------------Ball Led------------//

			column[ball.x]=line[2+ball.y];					// Gets the ball position on the x_axis (from 0 to 7) and writes the value of the corresponding y_axis position (by reading the array line[]).
	}

	void game() {
		uint8_t i;
		clear();								// Clears column[] array (therefore, resetting every value to 0, forcing the matrix to clear up).

		//column[0] = 24;							// Defines left player starting position (4th and 5th row).
		//column[7] = 24;							// Defines right player starting position (4th and 5th row).
		sw1=0; sw2=0;								// Initializes both variables to 0.
		score1=0; score2=0;							// The game starts, by default, 0-0.

		uint8_t btn1 = 0, btn2 = 0, btn3 = 0, btn4 = 0;				// Default state of buttons is 0. If pressed, the value changes.
		uint8_t counter = 0;			// Variable used to slow down the player movement. It has to hit 11 before a player moves (if this wasn't here, each players' goalie would rush from 1 side of the matrix to the other VERY quickly).
		uint8_t counter2 = 0;			// Variable used to slow down the ball movement. It has to hit 21 before it moves (if this wasn't here, the ball would move VERY quickly).

		initBall();								// Calls function initBall() (will define the ball starting pos, as well as the players).

		for(;;) {								// Infinite loop.

			//--------------Button read--------------//

			btn1 = readButton(1);					// Calls function readButton() using 1 as an argument and waits for its return. The value is stored on the variable btn1.
			btn2 = readButton(2);					// Calls function readButton() using 2 as an argument and waits for its return. The value is stored on the variable btn2.
			btn3 = readButton(3);					// Calls function readButton() using 3 as an argument and waits for its return. The value is stored on the variable btn3.
			btn4 = readButton(4);					// Calls function readButton() using 4 as an argument and waits for its return. The value is stored on the variable btn4.

			//-----------Players Movement------------//

			if(counter>PSpeed) {						// If the variable counter is higher than PSpeed (only true if 10+ > 10).

				//----------------Right player-----------------//

					if (btn1==1) {					// If btn1 == 1, then button 1 was pressed.
						if((column[7] << 1) <=192)		// If the value is equal or lower than 192, it means it hasn't hit the matrix top (8th row) yet.
							column[7] = column[7] << 1;	// Therefore, column[7] gets the new value (goes up one row).
					}

					if (btn2==1) {					// If btn2 == 1, then button 2 was pressed.
						if((column[7] >> 1) >= 3)		// If the value is equal or higher than 3, it means it hasn't hit the matrix bottom (1st row) yet.
							column[7] = column[7] >> 1;	// Therefore, column[7] gets the new value (goes down one row).
					}

				//----------------Left player------------------//

					if (btn3==1) {					// If btn3 == 1, then button 3 was pressed.
						if((column[0] << 1) <=192)		// If the value is equal or lower than 192, it means it hasn't hit the matrix top (8th row) yet.
							column[0] = column[0] << 1;	// Therefore, column[0] gets the new value (goes up one row).
					}

					if (btn4==1) {					// If btn4 == 1, then button 4 was pressed.
						if((column[0] >> 1) >= 3)		// If the value is equal or higher than 3, it means it hasn't hit the matrix bottom (1st row) yet.
							column[0] = column[0] >> 1;	// Therefore, column[0] gets the new value (goes down one row).
					}

				counter=0;						// Resets the variable counter.
			}
			counter++;							// Counter goes up by 1, meaning it has "looped" the above code (without doing anything) once again.

			//--------------Score Update-------------//

			if(score1>9 || score2>9) {					// If any player scores more than 9 goals, the game resets (forcing each players' score back to 0).

				score1=0;						// Resets score of player 1.
				score2=0;						// Resets score of player 2.
				update_score(0,0);					// Calls function update_score using both players scores (0,0) as arguments.
				PORTA = 0x00;						// Clears PORTA (if this wasn't here, the animation wouldn't be displayed correctly).
				PORTD = 0x00;						// Clears PORTD (if this wasn't here, the animation wouldn't be displayed correctly).
				_delay_ms(200);
				start_music();						// Since the game ended, calls start_music() function to play the initial music. Game will then be reset.
			}

			//--------------Ball Trail---------------//

			for(i=1; i<nrCol-1; i++)
				column[i]=0;			// Deletes the ball trail (resets every value of the array column[] back to 0). If this wasn't here, the led matrix would display the ball past position (on the past columns).

			//-------------Ball Movement-------------//

			if(counter2>BSpeed) {						// If the variable counter2 is higher than BSpeed (only true if 20+ > 20).
				move_ball();						// Calls function move_ball() in order to control the ball movement (collisions, etc.).
				counter2=0;						// Resets the variable counter2.
			}
			counter2++;							// Counter2 goes up by 1, meaning it has "looped" the above code (without doing anything) once again.

			//-------------Display Game--------------//

			display(column);			// Calls function display() using the variable / array column as argument. It will constantly display the game (code above) given that it's on a for (;;) loop.
		}
	}

// -------------------------------------------------------------------------------------------------//

// -------------------------------Main function (core of the program)-------------------------------//

	int main(void) {
		inic();									// Calls function inic() so it can define the ATMega's pins and configure Timer/Counters.
		start_music();								// Calls function start_music() to play a short melody before the game starts.
		init_matrix(column);				// Calls function init_matrix() so it can turn all the matrix leds on (we won't see all leds on since we will define PORTA with specific values on display().
		update_score(0, 0);							// Calls function update_score() so it initializes both players' scores with 0.
		game();						// Calls function game(). The code will always loop back inside this function, so there's no need to use while(1) on main(). It only leaves this function if the reset button is pressed.
		return 0;
	}

// -------------------------------------------------------------------------------------------------//

// --------------------------Defining ATMega32 pins and registers-----------------------------------//

	void inic (void) {
		DDRA = 0xFF;								// Defined as outputs (matrix).
		DDRD = 0xFF;								// Defined as outputs (matrix).
		DDRB = 0x0F;								// PB0 - PB3 as outputs (buzzer + shift register) and last 4 pins defined as inputs (switches).
		DDRC = 0x01;								// Blinking LED.

		TCCR2 |= (1<<WGM21)|(0<<COM21)|(0<<COM20)|(1<<CS22)|(1<<CS21)|(1<<CS20);	         // CTC Mode with normal port operation. T = [1024/8MHz] * (OCR2 + 1).
		OCR2 = 255;

		TCCR0 |= (1<<WGM01) | (1<<WGM00) | (1<<COM01) | (0<<CS02) | (1<<CS01) | (1<<CS00);	// Fast PWM mode. Non inverting. Prescaler = 64.
		TCCR0 &= ~((1<<COM01) | (1<<COM00));							// For now deactivate output to buzzer.

		TIMSK = (1<<OCIE2);							// Timer/Counter2 Compare Match interrupt enabled.
		sei();									// Global interrupts activated.
	}

// -------------------------------------------------------------------------------------------------//

// -------------------------------Button state function---------------------------------------------//

	uint8_t readButton(uint8_t index) {
								// Taking into account the index value obtained via argument (1 to 4), this function will return a value (according to the statement below).
		return !(PINB & (16 << (index - 1)));		// PINB = {16,32,64,128} according to which button was pressed (btn1 -> PB4; btn2 -> PB5; btn3 -> PB6; btn4 -> PB7). 16 * 2^(index-1) = {16,32,64,128}.
	}							// Bitwise AND (&) does the logical AND of both numbers (PINB & ...).

// -------------------------------------------------------------------------------------------------//

// -------------------------------Buzzer related functions------------------------------------------//

	void start_buzzer() {
		TCCR0 |= (1<<COM01);							// Activate output to buzzer.
		OCR0 = DUTY_CYCLE;							// Signal applied to the buzzer. Vout = Duty_Cycle /256 * 2.
	}

	void stop_buzzer() {
		TCCR0 &= ~((1<<COM01) | (1<<COM00));					// Deactivates output to buzzer.
		OCR0 = 0;								// Just making sure the buzzer will produce no sound by resetting the OCR0 value to 0 (Vout = 0).
	}

	void start_music() {
		uint8_t j;
		uint8_t pos = 0;							// Variable used to rotate between PORTD pins (columns).

		for (j=0; j<100; j++) {							// Takes nearly 6 seconds to finish. Inaccurate time measurement.

			PORTD |= (1<<pos);						// Turns all LEDS connected to PORTD ON (1 by 1, with the help of the variable pos).
			PORTA = 0xFF;							// If we want the matrix leds to turn ON, PORTA has to be set to HIGH as well.

			start_buzzer();							// Calls function start_buzzer() in order to play sound.
			_delay_ms(60);
			stop_buzzer();							// Calls function stop_buzzer() in order to disable sound.

			pos++;								// Increments variable (next column).
			if (pos == 8) {							// If pos = 8, then all 8 columns are ON (every PORTD pin ON).
				pos = 0;						// Resets variable pos to zero so it can make the animation once again.
				PORTD = 0x00;						// Clears the matrix (so it can make the animation again).
			}
		}
	 }

// -------------------------------------------------------------------------------------------------//

// -------------------------------7 segment display related function--------------------------------//

void shift_data(uint8_t data)
{
	uint8_t i;
	uint8_t bit;

	PORTB &= ~(1 << LATCH); //deactivate latch output

	for (i = 0; i < 8; i++)
	{
		PORTB &= ~(1 << S_IN);
		PORTB &= ~(1 << CLK);

		if (data & (1 << i))
		bit = 0x01;
		else
		bit = 0x00;

		PORTB |= (bit << S_IN);
		PORTB |= (1 << CLK);
	}

	PORTB &= ~(1 << CLK);  //set clock to zero
	PORTB |= (1 << LATCH); //activate latch output
}

void update_score(uint8_t score_p1, uint8_t score_p2)
{
	shift_data((score_p2 & 0x0F) | ((score_p1 & 0x0F) << 4));
}

// -------------------------------------------------------------------------------------------------//

(I'm sorry if the code is a bit messy given that I commented everything I could. Some comments occupy 2 lines).

 

With this, my idea is to first understand / be able to explain the code and only then think about how to code the improvements (on the top of my head, I already thought about adding a potentiometer to reduce brightness, a volume controller, a menu / interface where people could select different difficulties (the ball would move faster / slower), etc. 

 

With this, I'm having trouble explaining some ball collisions (for example, when it hits a player) - lines which are commented with "// ?????": if((line[2+ball.y]+line[2+ball.y-1])==column[0]) {"  etc.

 

I also have some doubts regarding the explanation of display() function but I would like to take care of things one by one. I understand it more or less, I just don't know if my explanation is perfect or if there are any flaws.

 

EDIT1: I forgot to add a video showing the results. Everything is working perfectly. I added some goal animations and buzzer sound as well. Video attached!

 

Attachment(s): 

Last Edited: Mon. Jan 6, 2020 - 07:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

flash17 wrote:
Some comments occupy 2 lines

Some comments add nothing to the code; eg

			start_buzzer();							// Calls function start_buzzer() in order to play sound.
			_delay_ms(60);
			stop_buzzer();							// Calls function stop_buzzer() in order to disable sound.

 

Perhaps it might have been useful to just say

// Sound the buzzer for 60ms			
start_buzzer();
_delay_ms(60);
stop_buzzer();

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
// Sound the buzzer for 60ms			
start_buzzer();
_delay_ms(60);
stop_buzzer();

Got it, will re-do those comments. I just write them for guiding purposes (since I have to write a report and make a presentation). Any idea about the ones marked with // ??? (the ones about the ball collision)?  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just took a fast look at your HW. Please add a 100nF decoupling capacitor Vcc to GND and you should place it as close as possible to the microcontroller. For the buzzer add a small series resistor (33R), and if you want to make the sound of the buzzer more stronger you could consider driving the other pin also from the MCU, this gives you the advantage of having a higher voice.

 

instead of using a wait blocking function you could drive the pins via PWM then you have a more "efficient" way of driving your buzzer.

 

AVCC pin should not be left floating, for this check the recommended HW design note for your MCU.

 

what is your configuration for unused pins ? it cant be left floating as you may know, but you can configure the as outputs, this was you may save some additional m/uA.

 

Apart from the abovementioned suggestions, the project looks fine. Best luck!

 

 

 

Last Edited: Tue. Jan 7, 2020 - 11:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Will try out the said additions! The unused pins were left floating yes (mostly PORTC since It only set PC0 and PC1 as outputs).

Last Edited: Tue. Jan 7, 2020 - 11:57 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The unused pins can be configured as outputs to save power

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Got it. Will do so. Is the display function correct also (idk if you looked at the software part)? I am having trouble understsnding the use of a display function instead of a interrupt routine.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
Will try out the said additions! The unused pins were left floating yes (mostly PORTC since It only set PC0 and PC1 as outputs).

 

Correct, only the 2 first PORTC pins were defined. The other ones were unset. Will change the HW accordingly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am having trouble understsnding the use of a display function instead of a interrupt routine.

 

IMO the function is more or less a routine. It's only called "once" at the end of the game() function but since the MCU is running at a High Frequency (8MHz) you basically can't see the delay inbetween both calls (display(column); ....... display(column);). With this, since column[] is inserted as an argument, the function simply displays whatever values are on the column[] array via the for function (column[i] in order to rotate between the 8 numbers). 

 

After that, it simply goes back to the game() function and repeats. At least that's what I think is a good explanation. Perhaps someone else can step in and add something if needed! 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

flash17 wrote:
the function is more or less a routine.

Pardon ?

 

AFAIK, "function" and "routine" are pretty much synonyms ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pfff...awneil..then try to explain it yourself for the students here instead of criticizing ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

flash17 wrote:
Perhaps someone else can step in and add something if needed! 

@awniel

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Correct. My bad, poor use of words blush. Anyways, what I said is more or less what I understood from the function. After the values of the array column[] are changed (on the function game(), initball() and moveball()), the display() function is called and displays the said array (I suppose the use of a pointer [*column] is needed in order to not mess with the array values itself (?)).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Moe123 wrote:
awneil..then try to explain it 

Just wondering if there is some (subtle) difference between "function" and "routine" of which I was not previously aware ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


It's quite tricky to capture a frame of that animation using the PrtScrn key but I captured it just as it got to this point:

 

 

The first thing you may notice is that I rotated this through 90 degrees. That is because in code such as:

				//-----------Collision with the left player----------//

				if(ball.x==1) {						// If the ball is in the 2nd column (array[1]) -> Just before hitting the goalie (or not).
					if((line[2+ball.y]+line[2+ball.y-1])==column[0]) {		//????
						ball.speed_x=1;				// The goalie was positioned correctly, therefore, the ball bounces back (speed.x inverted).
						if(ball.speed_y!=1)
							ball.speed_y++;			// Forces the ball to move in a different direction (y_axis wise) by adding 1 to the variable ball.speed_y.
						sw1=1;					// Works as a flag. If sw1=1, then the left player defended the ball successfully.
					}

it talks about "left" and "right" players (not "top" and "bottom") and equally that would imply that "ball.x" increases across and "ball.y" increases down in the sense that I have rotated the picture - not as you presented. If you try to introduce a 90 degree turn so that columns becomes rows and x becomes y and so on then trying to interpret the code.

 

Anyway I think you need to start by understanding what line[] actually is. In fact it is this:

	uint8_t line[12]={0,0,1,2,4,8,16,32,64,128,0,0};				// Line Mapping.

and is principally used in code such as this:

		PORTA = line[ball.x+2];						        // Displaying the ball on the LED matrix.
		PORTD = line[ball.y+2];							// Displaying the ball on the LED matrix.

So the +2 seems to be because the "bat" is 2 pixels wide and it can "drive" right off the left or right hand end and so the line[] array has the bit positions (1<<0, 1<<,1, 1<<2 etc) with a +2 buffer on either side.

 

Therefore assume the (left) bat is in the position seen in the picture above. It is therefore maybe occupying the bit positions shown as:

line[2+ball.y]+line[2+ball.y-1]

Then if ball.y is 4 this is possibly adding line[2+4] and line[2+5] which are 16 and 32 or 0b00010000 and 0b00100000 to make 48 - that is 0b00110000 which represent the two pixels of the bat.

 

What I cannot quite grasp (and maybe this is also your issue?) is how it can be testing this against:

==column[0]

because if it is 48 (or whatever) that suggests TWO bits are set but surely column[] will only contain a single bit set.

 

But I think that in analysing code such as this the key thing is to get a mental picture of how each data element is being used. So maybe investigate further as to what is being held in column[]. I presume that these are the columns so in my picture

 

00000000

00000000

00000000

10000001

10000001

01000000

00000000

00000000

 

then you would read this vertically so column[0] is 0b00011000 column[1] is 0b00000100 and so on. In which case maybe that test against column[0] does make sense?

 

But this would be the general process of analysis. Sketch out some grids/arrays of 0's and 1's and see what you would expect to be in column[N] and line[X] or whatever. As I say line[] just seems to be a "cheap" way of doing binary multiplications by table look up.

 

 

More than anything make sure you know the data ordering. Where the x,y (0,0) origin is and in which direction .x and .y increment (still not sure if origin is top left or bottom left in my picture or, for that matter if it might actually be top/bottom right rather than left with bit numbers increasing to the left ??).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's a nice explanation. Thanks for taking that much time to answer man! 

What I cannot quite grasp (and maybe this is also your issue?) is how it can be testing this against:

==column[0]

It is indeed my problem. Although, in my mind, I always thought about those conditions beeing like "If the ball [line..............] is in the same position (==) as the bat in column[0] aka, if the value of line[........] (value to display the ball) is the same (==) as the value in column[0] / [7] (value to display the bat), then it would mean the ball is going to hit the bat (given that both the ball AND the bat are on the same position - though in different columns / x coords). 

 

Perhaps a little confusing but that's exactly why I'm sharing this question since I want to explain it well. Anyways, will do what you suggested (sketching each position etc.). 

 

Once again, thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

flash17, do you use the same account "Jonaas" ? because it seems that you are same person

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No but he's indeed one of the authors of a thread that I saw while choosing my project (one of the projects I found lead me to his thread). From what I understood he was developing the game itself using the project found online, I'm just using the project in order to present improvements.

 

Edit: The said thread:  https://www.avrfreaks.net/forum/creating-gaming-console-scratch-avr-c-c

 

Edit2: Since we're here, I also found the same block of code on this thread:  https://www.avrfreaks.net/forum/trouble-using-74hc595-shift-register-display-numbers-2x-7-segment-displays#comment-2821896 by user takashi83 regarding the data shifting to the 7 segment displays. The thread helped me understand how the data is sent to the 7 segment displays through the Shift Register and the decoders. I'll also link the said project (bookmarked on my home PC) in case someone wants to follow / try out.

Last Edited: Tue. Jan 7, 2020 - 02:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the other thread (is it Pong season?) I advised Jonaas18 to implement the matrix display as an ISR + framebuffer. I really think it's a better approach that keeps thinks more modular. Just my 2 cents.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My teacher advised us to choose either PONG or a led game in which some leds would blink in a certain sequence and we would then need to press the corresponding buttons to imitate the sequence (kinda like a "what was the sequence" game). Thought PONG was funnier and, like explained, found out multiple threads about it so laugh.

 

Are there lots of differences (performance wise and display capacity wise) between using a display() function like used in the code above or an ISR? 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another possible approach is to forget the existing code all together. Look at the problem and work out how you would have implemented this thing yourself if you were given a completely free hand. This will cause you to analyse the problem in greater depth. Having done that if you then come back to his code you will probably read it with a clearer view of what's going on, probably reading some bits as "oh I see, he chose to do XXXX like that did he?" etc.

 

Ultimately it's all about 0's and 1's - it's just working out how to represent ball/bat and how to detect their presence. For the purposes of the display it obviously needs the 1's and 0's packed into single bytes that can be output to PORTA or PORTD or whatever but maybe an easier approach (for the purpose of indexing the array with x and y coordinates) is to use a whole uint8_t to simply hold a single 0/1 state. I know this is wasteful (8 times as much storage) but the indexing is much easier and you don't need something like line[] to map 0..7 to 1,2,4,8... etc.

 

If you took that approach you could either have a complete grid[8 * 8] and then a particular element is grid[(y * 8) + x] or you could even go for grid[8][8] and then access as grid[y][x] or grid[x][y] (whichever you prefer). That gives a much simpler approach to the collision logic and when you understand that THEN you can consider packing the bits.

 

Yet another thing you could do is build a PC simulation of this. The "core logic" would be the same but the input/output would be done differently. You could just do a simple text display frame by frame:

"        "
"*       "
"*      *"
"   *   *"
"        "
etc.

 

Last Edited: Tue. Jan 7, 2020 - 02:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Got it. I am on exams season so time is a little bit short. Nontheless, if I have the time, I will try to do as suggested and then look back to this code to figure out what's different and why is different. Thanks for the idea!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

flash17 wrote:
Are there lots of differences (performance wise and display capacity wise) between using a display() function like used in the code above or an ISR?

 

In this case, not really. It's just a way to keep things decoupled and separated, instead of having a massive game() function that handles buttons, screen timing and almost everything else at a low level.

 

edit: I prefer to have low level stuff tuck away in ISRs (if possible), while the main code only sees a kind of hardware abstraction.

Last Edited: Tue. Jan 7, 2020 - 03:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:
while the main code only sees a kind of hardware abstraction.
Which then makes it much easier to create a simulation ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Which then makes it much easier to create a simulation ;-)

 

Exactly. Which then allows, if this was a collaborative work, for one person to work on the game logic, while another works on the hardware interface (once they have agreed on the hardware abstraction to use).