Skip to content
Snippets Groups Projects
labyrinth.c 8.87 KiB
/**
 * Name        : tp1_labyrinth.c
 * Author      : VP
 * Date        : 23.10.2017
 * Description : Labyrinth (TP1). Master implementation must contain:
 *               - 5 balls management, 1 of the master controlled by the local accelerometer
 *               - 1 ball controlled by the slave (accelerometer of the slave board)
 *               - 3 balls with random accelerations
 *               - FreeRTOS used in cooperative mode
 *               - use of global variables allowed
 *               Only the slave acceleration is given by the slave. All ball positions are
 *               managed by the master.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "lcd.h"
#include "accelerometer.h"
#include "ethernet_mgt.h"
#include "tools.h"
#include "custom_rand.h"
#include "traces_ref.h"

#define NUMBER_OF_BALLS  5 				// min 1
#define BALL_DISP_PERIOD 20				// ms
#define BOT1_BALL_DISP_PERIOD 30		// ms
#define BOT2_BALL_DISP_PERIOD 50		// ms
#define BOT3_BALL_DISP_PERIOD 45		// ms
#define SPEED_MAX 5.
#define BALL_RADIUS 5
#define BOT1_BALL_RADIUS 7
#define BOT2_BALL_RADIUS 8
#define BOT3_BALL_RADIUS 8
#define REBOUND_FACTOR 0.9				// speed ratio after a rebound
#define PI 3.141592
#define LCD_YELLOW (LCD_GREEN | LCD_RED)

#define GOAL_H_WALL (LCD_MAX_WIDTH-(2*BALL_RADIUS+1+WALL_WIDTH))
#define GOAL_V_WALL (LCD_MAX_HEIGHT-(2*BALL_RADIUS+1+WALL_WIDTH)-1)

#define NUM_THREADS 5

// Ball identifiers
enum {
	NONE = -1,			// NONE is used if ball has reached the goal (winner field)
	MASTER_BALL,
	SLAVE_BALL,
	BOT1_BALL,
	BOT2_BALL,
	BOT3_BALL
};

// ball descriptor
typedef struct {
	int thread_id;
	object_t ball;
} ball_param_th_t;

static coord_fx_t labyrinth_points[]={
		{80,280},{40,280},{40,190},{END_OF_LINE,LCD_WHITE},
		{40,100},{150,100},{150,160},{70,160},{END_OF_LINE,LCD_WHITE},
		{0,160},{40,160},{END_OF_LINE,LCD_WHITE},
		{70,190},{180,190},{180,80},{END_OF_LINE,LCD_WHITE},
		{210,100},{238,100},{END_OF_LINE,LCD_WHITE},
		{180,220},{238,220},{END_OF_LINE,LCD_WHITE},
		{0,40},{100,40},{END_OF_LINE,LCD_WHITE},
		{140,40},{237,40},{END_OF_LINE,LCD_WHITE},
		{120,220},{120,300},{END_OF_LINE,LCD_GREEN},
		{GOAL_H_WALL-2*BALL_RADIUS,GOAL_V_WALL},{LCD_MAX_WIDTH-1,GOAL_V_WALL},{END_OF_LINE,LCD_GREEN},
		{GOAL_H_WALL-4*BALL_RADIUS-WALL_WIDTH-1,LCD_MAX_HEIGHT-WALL_WIDTH},{GOAL_H_WALL-4*BALL_RADIUS-WALL_WIDTH-1,GOAL_V_WALL},{END_OF_WALLS,END_OF_WALLS}
};


/* Description: Callback of ethernet module. This function is called when a message is received
 * Parameters: data: pointer on data received
 *             len: length of the data [bytes]
 */
void slave_info_rx(void *data, int len)
{
	// code executed when the slave sends an ethernet frame
}

void Algo_main_ball(void *params);
void Algo_bots(void *params);
void RandAccel(accel_t *accel);

int main(void)
{
	accel_t ball_accel;
	static ball_param_th_t ball_param_th[NUMBER_OF_BALLS]={			// balls description
		{MASTER_BALL,{{10,10}, {0, 0}, BALL_RADIUS, LCD_YELLOW, BALL_DISP_PERIOD}},
		{SLAVE_BALL,{{30,10}, {0, 0}, BALL_RADIUS, LCD_GREEN, BALL_DISP_PERIOD}},
		{BOT1_BALL,{{200,10}, {0, 0}, BOT1_BALL_RADIUS, LCD_RED| (LCD_BLUE>>2), BOT1_BALL_DISP_PERIOD}},
		{BOT2_BALL,{{70,70}, {0, 0}, BOT2_BALL_RADIUS, LCD_RED | (LCD_BLUE>>1), BOT2_BALL_DISP_PERIOD}},
		{BOT3_BALL,{{200,280}, {0, 0}, BOT3_BALL_RADIUS, LCD_RED | LCD_BLUE, BOT3_BALL_DISP_PERIOD}}
	};

	init_rnd32(0);			// initialise random generator
	init_lcd();				// initialise screen
	if(accel_init() != 0)	// initialise accelerometer
	{
		printf("Accelerometer initialisation failed!\n");
		return 1;
	}
	//ethernet_init(..., slave_info_rx); // initialise ethernet and determine its callback function

	draw_labyrinth(labyrinth_points);
	init_traces(115200, 1, true); // initialise traces. Line to be removed if you implement your own traces

	accel_read(&ball_accel);	 // example: reading of the accelerometer

	// creating tasks...
	/*for(int i = 0; i<NUM_THREADS; i++){

	}*/
	xTaskCreate(Algo_main_ball, (signed portCHAR *)"Masterball",
					configMINIMAL_STACK_SIZE, &ball_param_th[0], tskIDLE_PRIORITY+1,
					NULL);
	xTaskCreate(Algo_bots, (signed portCHAR *)"libreball1",
						configMINIMAL_STACK_SIZE, &ball_param_th[2], tskIDLE_PRIORITY+1,
						NULL);
	vTaskStartScheduler();		// launch scheduler
	LPC_TIM0->TCR = 1;

	while(1);

	return 1;
}

void Algo_main_ball(void *params){
	ball_param_th_t *ball = (ball_param_th_t *)params;
	accel_t ball_acc;
	int un_ou_moins_un_x = 1, un_ou_moins_un_y = 1;
	while(1){
		lcd_filled_circle(ball->ball.pos.x, ball->ball.pos.y, ball->ball.radius, LCD_BLACK);
		int CollisionState = test_collision(&ball->ball);
		if(CollisionState != NO_COLLISION){
			//ralentissement de 10% de la vitesse de la balle
			ball->ball.speed.x = ball->ball.speed.x - (ball->ball.speed.x/10);
			ball->ball.speed.y = ball->ball.speed.y - (ball->ball.speed.y/10);

			if(CollisionState & TOUCH_LEFT){
				un_ou_moins_un_x = -1;
				ball->ball.pos.x += ball->ball.radius/2;
			}
			if(CollisionState & TOUCH_RIGHT){
				un_ou_moins_un_x = -1;
				ball->ball.pos.x -= ball->ball.radius/2;
			}
			if(CollisionState & TOUCH_UP){
				un_ou_moins_un_y = -1;
				ball->ball.pos.y += ball->ball.radius/2;
			}
			if(CollisionState & TOUCH_DOWN){
				un_ou_moins_un_y = -1;
				ball->ball.pos.y -= ball->ball.radius/2;
			}
		}else{
			accel_read(&ball_acc);
			ball->ball.speed.x = ((ball_acc.accel_x*0.1)+ball->ball.speed.x)*(un_ou_moins_un_x);
			ball->ball.speed.y = ((ball_acc.accel_y*0.1)+ball->ball.speed.y)*(un_ou_moins_un_y);

			ball->ball.pos.x = ball->ball.pos.x + ball->ball.speed.x*(un_ou_moins_un_x);
			ball->ball.pos.y = ball->ball.pos.y + ball->ball.speed.y*(un_ou_moins_un_y);
			un_ou_moins_un_y = 1;
			un_ou_moins_un_x = 1;
		}

		//détection des collisions sur les bords de l'ecran LCD
		if(ball->ball.pos.x <= ball->ball.radius){
			ball->ball.pos.x = ball->ball.radius+1;
		}
		if(ball->ball.pos.y <= ball->ball.radius){
			ball->ball.pos.y = ball->ball.radius+1;
		}
		if(ball->ball.pos.x >= LCD_MAX_WIDTH-ball->ball.radius){
			ball->ball.pos.x = LCD_MAX_WIDTH-ball->ball.radius;
		}
		if(ball->ball.pos.y >= LCD_MAX_HEIGHT-ball->ball.radius){
			ball->ball.pos.y = LCD_MAX_HEIGHT-ball->ball.radius;
		}
		lcd_filled_circle(ball->ball.pos.x, ball->ball.pos.y, ball->ball.radius, ball->ball.color);
		vTaskDelay((portTickType)20/portTICK_RATE_MS);

	}
}

void Algo_bots(void *params){
	ball_param_th_t *ball = (ball_param_th_t *)params;
	accel_t ball_acc;
	int un_ou_moins_un_x = 1, un_ou_moins_un_y = 1;
	while(1){
		lcd_filled_circle(ball->ball.pos.x, ball->ball.pos.y, ball->ball.radius, LCD_BLACK);
		int CollisionState = test_collision(&ball->ball);
		if(CollisionState != NO_COLLISION){
			//ralentissement de 10% de la vitesse de la balle
			ball->ball.speed.x = ball->ball.speed.x - (ball->ball.speed.x/10);
			ball->ball.speed.y = ball->ball.speed.y - (ball->ball.speed.y/10);

			if(CollisionState & TOUCH_LEFT){
				un_ou_moins_un_x = -1;
				ball->ball.pos.x += ball->ball.radius/2;
			}
			if(CollisionState & TOUCH_UP){
				un_ou_moins_un_y = -1;
				ball->ball.pos.y += ball->ball.radius/2;
			}
			if(CollisionState & TOUCH_RIGHT){
				un_ou_moins_un_x = -1;
				ball->ball.pos.x -= ball->ball.radius/2;
			}
			if(CollisionState & TOUCH_DOWN){
				un_ou_moins_un_y = -1;
				ball->ball.pos.y -= ball->ball.radius/2;
			}
		}else{
			RandAccel(&ball_acc);
			ball->ball.speed.x = ((ball_acc.accel_x*0.1)+ball->ball.speed.x)*(un_ou_moins_un_x);
			ball->ball.speed.y = ((ball_acc.accel_y*0.1)+ball->ball.speed.y)*(un_ou_moins_un_y);

			ball->ball.pos.x = ball->ball.pos.x + ball->ball.speed.x*(un_ou_moins_un_x);
			ball->ball.pos.y = ball->ball.pos.y + ball->ball.speed.y*(un_ou_moins_un_y);
			un_ou_moins_un_y = 1;
			un_ou_moins_un_x = 1;
		}
		//}
		if(ball->ball.pos.x <= ball->ball.radius){
			ball->ball.pos.x = ball->ball.radius+1;
		}
		if(ball->ball.pos.y <= ball->ball.radius){
			ball->ball.pos.y = ball->ball.radius+1;
		}
		if(ball->ball.pos.x >= LCD_MAX_WIDTH-ball->ball.radius){
			ball->ball.pos.x = LCD_MAX_WIDTH-ball->ball.radius;
		}
		if(ball->ball.pos.y >= LCD_MAX_HEIGHT-ball->ball.radius){
			ball->ball.pos.y = LCD_MAX_HEIGHT-ball->ball.radius;
		}
		lcd_filled_circle(ball->ball.pos.x, ball->ball.pos.y, ball->ball.radius, ball->ball.color);
		vTaskDelay((portTickType)20/portTICK_RATE_MS);

	}
}


//random d'accélérations
void RandAccel(accel_t *accel){
	float x = (float)(rnd32()%100)-50;
	float y = (float)(rnd32()%100)-50;
	accel->accel_x = x/100;
	accel->accel_y = y/100;
	//accel->accel_z = (float)(rnd32()%500)/100;

	accel->magneto_x = 0;
	accel->magneto_y = 0;
	accel->magneto_z = 0;
	accel->temperature = 0;
}