Pinball Wizard Code (Arduino)

This is just a snippet of our Arduino code that we wanted to highlight. It's the while loop that runs our code.

void loop() {
// put your main code here, to run repeatedly:
sensorValue1 = analogRead(interruptPin1);
sensorValue2 = analogRead(interruptPin2);
if (sensorValue1 > 200 && !scored) {
addScore();
scoreTime = 2000;
scored = true;
}
if (sensorValue2 > 200 && !loss) {
loseBall();
lossTime = 2000;
loss = true;
}
count--;
if (count == 0) {
count = goalie(on);
}
scoreTime--;
if (scoreTime == 0) {
digitalWrite(buzzer, LOW);
scored = false;
}
lossTime--;
if (lossTime == 0) {
loss = false;
}
}</code>

<code>


The interesting thing about this while loop is that since all the meat of the code is in functions that toggle certain pins high and low after a certain number of loops we do not have to worry as much about interrupts. We came up with this idea because the Arduino Uno only had two interrupt ports when we needed at least three to make our project run. Our functions were used to activate motors, and light LEDs quickly so that the loop could run faster allowing it to detect the sensors extremely quickly.

Pinball Wizard Schematic

WP_20160428_004

LED: We set one of the pins on the Arduino HIGH which outputs to a circuit which is an LED in line with a resistor to limit current.

Motor: We set up an NPN transistor as a relay. In this configuration we have power supplied to the motor and the path to ground is controlled by the transistor. Setting the port high allows the transistor to connect the motor to GND.

Buzzer: Set up in the same fashion as the motor.

Pressure Sensor: The pressure sensor acted like a potentiometer. While the sensor was not pressed it had so much resistance that it acted like an open circuit. When the sensor was pressed is lowered the resistance and acted like a short circuit which made the voltage on the port readable.

Pinball Wizard: Big Picture

WP_20160428_002

The goal of this project was to work together to create a pinball game. We chose a pinball machine because of the challenge and fun reward we would receive learning how to program and put one together. There were a few steps that had to be taken in order to meet this goal. First was to design the board and plan out what parts we needed. The theme of the board is a soccer themed pinball machine. So we needed two motors to act as the “goalie”. We also needed two pressure sensors in order to sense if a goal was made or a life was lost. Of course we needed wood, to build the board out of. Lastly we needed the parts that everyone recognizes from a pinball machine, the small silver ball, the plunger to shoot the ball into the playing field, the flippers that the player controls, and the score board. Once designed and all the pieces gathered, the next step was to build the board and program all the parts together. We did these steps simultaneously, with all of us bouncing back and forth (I see exactly what you did there-CKH) between helping build the board and working on getting all the motors and sensors working outside the board. Finally we were close enough finished with both of those steps that we put all the parts in the board and put the finishing touches on it.

Accelerometer Maze Game: The Big Picture

For our final lab assignment we were tasked with the most open ended objective possible, creating our own combination of hardware + software. We began throwing around ideas in labs 3 & 4, but nothing really stood out to us until Eugene suggested using an accelerometer to control some sort of game. With this idea in mind, we thought that a game where you navigate a cursor to the end of a maze to be interesting and challenging enough for us.

The game takes input from the accelerometer to move the position of a ball on the onboard LCD screen.  Once the ball reaches the end of the maze, the game won screen displays and the buzzer goes off.  The program then waits for the user to hit a button to start the game again.

 

Pictures of the project

IMG_1102

 

IMG_1103

RC Truck-Based Smart Car

Big Picture:

We built a smart car, this car is able to detect and avoid obstacles. Started off by purchasing an old RC Truck off of Ebay. We used the Arduino Uno and Arduino Motor Shield to help convert it into the smart car it is in the picture below.

rc smart

 

The Arduino helped us communicate between the car and the Parallax ping sensor. We used the output from the Arduino and routed it through the A3BU so that every time the car runs the LCD on the A3BU says “Vroom Vroom” and if we have enough battery/power the LEDS on the top light up too.

Our car detects objects within a 2 feet range. If an object is detected by the Ping Sensor, it sends a signal to work the motors in reverse (from the Arduino). The Truck then goes in reverse and turns right to avoid the obstacle in its path.

Asked: What is something you would change about your project to make it better?

Answer: We would take the power sources into account a bit more carefully. We started off with just 2 9 V batteries and later on realized that would not be enough with the weight of the car. We had to add a couple of portable/rechargeable batteries to help overcome the weight and get the car moving. It was used to power 2, 6 V motors.

Explain part of the code:

Here is the source code for the Atxmega256A3BU which would change text and flash when an object is detected. This module of code was used to denote the pin that was going to be used as an input(pin 32 in this case), we also used an if loop nested in a while loop to display one of the two messages on the LCD of the A3BU.

Capture1 Capture2

This code is used to send a ping to and from the sensor and detect the presence of an object within the vicinity of 2 feet. Its also used to control the motors and the LEDS. This implements the library NewPing which helps control and receive signals from the PING sensor. The Trigger_Pin sends the pulse and the Echo_Pin receives it.

Capture3

Schematic:

Here is a simple hardware schematic showing the Arduino board in connection with the motor shield and power supply.

Capture4

Here is the diagram of the whole Arduino motor shield.

Capture

And here is the Arduino Uno Rev 3 schematic – the Arduino we used for the communication to the ping sensor.

Capture

Accelerometer Maze Game Code

This is the code for the maze game.  This program is pretty involved, and uses both C and Assembly.  The assembly files handle the conversion of the 10 bit signed integer position values from the accelerometer and turn them into signed 16 bit integers.  The rest of the code handles the bitmaps for the maze, the ball, and displaying the output to an LCD.  It also translates the coordinates into information about the acceleration of the ball and the direction.

(Instructor Note: This code uses Eugene Rockey’s serial I2C/TWI interface code, developed in spring 2016, which is going to lead to tons of great A3BU-based projects. The team did a lot of original work on getting the maze bitmaps to connect to accelerometer data and game actions.)

Here is the assembly file COMBINE.s


.global COMBINE

COMBINE: mov XL,r24 ;data_0
mov XH,r22 ;data_1
ldi r30,0x02 ;bit mask for testing sign bit
and r30,XH ;test sign bit
brbc 1,NEGATIVE ;if negative, jump to NEGATIVE routine
jmp END ;if positive, leave as is

NEGATIVE: ori XH,0xFC ;make X register a proper 16-bit two's complement

END: mov r24,XL ;move values to be stored into memory
mov r25,XH
ret

Here is the header file for the game_data.h

#ifndef GAME_DATA_H_
#define GAME_DATA_H_

#include <asf.h>

gfx_mono_color_t blank_data[5] = {
0x00,0x00,0x00,0x00,0x00
};

struct gfx_mono_bitmap blank = {
.width = 5 ,
.height = 8 ,
GFX_MONO_BITMAP_RAM ,
.data.pixmap = (gfx_mono_color_t *)blank_data
};

const gfx_mono_color_t ball_data[5] = {
0x04,0x0E,0x1F,0x0E,0x04
};

struct gfx_mono_bitmap ball = {
.width = 5 ,
.height = 8 ,
GFX_MONO_BITMAP_RAM ,
.data.pixmap = (gfx_mono_color_t *)ball_data
};

gfx_mono_color_t new_ball_data[5] = {
0x00,0x00,0x00,0x00,0x00
};

struct gfx_mono_bitmap new_ball = {
.width = 5 ,
.height = 8 ,
GFX_MONO_BITMAP_RAM ,
.data.pixmap = (gfx_mono_color_t *)new_ball_data
};

const gfx_mono_color_t maze_data[512] = {
//each page of data contains 128 bytes

//page1
0xFF,0x01,0x01,0x01,0x01,0x01,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0xC1,0x01,0x01,0x01,0x01,0x01,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0xC1,0x01,0x01,0x01,0x01,0x01,0xC1,0x01,0x01,0x01,0x01,0x01,0x7F,0x41,0x41,0x41,0x41,0x41,
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x01,0x01,0x01,0x01,0x01,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,
0xC1,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0xC1,0x01,0x01,0x01,0x01,0x01,0xFF,0x01,0x01,0x01,0x01,0x01,
0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
//page 2
0xFF,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00,0x00,0xFF,0x30,0x30,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3F,0x00,0x00,0x00,0x00,0x00,0x3F,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
0x30,0x30,0x30,0x30,0x30,0x30,0xF0,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xF0,0x00,0x00,0x00,0x00,0x00,
0xFF,0x00,0x00,0x00,0x00,0x00,0xF0,0x30,0x30,0x30,0x30,0x30,0xF0,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,
0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
//page 3
0xFF,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,
0xF8,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0xF8,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0xF8,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,
0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0xFF,0x08,0xE8,0xE8,0xE8,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
//page 4
0xFF,0x80,0x80,0x80,0x80,0x80,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x80,0x80,0x80,0x80,0x80,
0xFF,0x80,0x80,0x80,0x80,0x80,0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x80,0x80,0x80,0x80,0x80,
0xFF,0x80,0x80,0x80,0x80,0x80,0x83,0x82,0x82,0x82,0x82,0x82,0x82,0x80,0x80,0x80,0x80,0x80,0x83,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,
0x83,0x80,0x80,0x80,0x80,0x80,0xFF,0x80,0x80,0x80,0x80,0x80,0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x82,0x82,0x82,0x82,0x82,0x82,0x80,0x80,0x80,0x80,0x80,
0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

};

struct gfx_mono_bitmap maze = {
.width = 128 ,
.height = 32 ,
GFX_MONO_BITMAP_RAM ,
.data.pixmap = (gfx_mono_color_t *)maze_data
};
#endif /* GAME_DATA_H_ */

 

Here is another header file containing most of the custom functions


modified_functions.h

#include "gfx_mono_c12832_a1z.h"
#include "game_data.h"
#include <string.h>

#ifdef ST7565R_SERIAL_INTERFACE
# define CONFIG_ST7565R_FRAMEBUFFER
#endif

#ifdef CONFIG_ST7565R_FRAMEBUFFER
static uint8_t framebuffer[GFX_MONO_LCD_FRAMEBUFFER_SIZE];
#endif

#ifndef MODIFIED_FUNCTIONS_H_
#define MODIFIED_FUNCTIONS_H_

//prototypes
void twi_init(void);
void display_ball(struct gfx_mono_bitmap*, struct gfx_mono_bitmap*, struct gfx_mono_bitmap*,
gfx_coord_t, gfx_coord_t,int*);
void shift_ball_down(gfx_mono_color_t*,gfx_mono_color_t*,int);
void shift_ball_up(gfx_mono_color_t*,gfx_mono_color_t*,int);
void merge_graphics(gfx_mono_color_t*,gfx_mono_color_t*,gfx_coord_t,gfx_coord_t,int,int);
void clear_graphic_5v5(gfx_mono_color_t*);
void init_maze_array(bool[32][128],const gfx_mono_color_t*);
bool test_move_right(bool[32][128],int,int);
bool test_move_left(bool[32][128],int,int);
bool test_move_forward(bool[32][128],int,int);
bool test_move_backward(bool[32][128],int,int);
bool test_for_wall(bool[32][128],int,int);
void clearscreen(void);
void displaymenu(void);

//definitions
void twi_init(void)
{
twi_master_options_t opt = {
.speed = 100000,
.chip = 0x53
};
twi_master_setup(&TWIC, &opt);
}
void display_ball(struct gfx_mono_bitmap *ball, struct gfx_mono_bitmap *new_ball, struct gfx_mono_bitmap *maze,
gfx_coord_t x, gfx_coord_t y, int *previous_page)
{
int current_page = y/8;
int page_starting_point = current_page * 8;
//clear stuff
clear_graphic_5v5(new_ball->data.pixmap);
clear_graphic_5v5(blank_data);

if(y<8){
if(x<5){
merge_graphics(blank_data,maze->data.pixmap,0,y,0,x); //clear left
gfx_mono_put_bitmap(&blank,0,0);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,0,y,1,x); //clear left, one page down
gfx_mono_put_bitmap(&blank,0,8);
clear_graphic_5v5(blank_data);

merge_graphics(blank_data,maze->data.pixmap,x,y,1,blank.width); //clear center-down
gfx_mono_put_bitmap(&blank,x,8);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x+5,y,0,blank.width); //clear right
gfx_mono_put_bitmap(&blank,x+5,0);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x+5,y,1,blank.width); //clear right, one page down
gfx_mono_put_bitmap(&blank,x+5,8);
clear_graphic_5v5(blank_data);
}
else if(x>4 && x<123){
merge_graphics(blank_data,maze->data.pixmap,x-5,y,0,blank.width); //clear left
gfx_mono_put_bitmap(&blank,x-5,0);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x+5,y,0,blank.width); //clear right
gfx_mono_put_bitmap(&blank,x+5,0);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x-5,y+8,1,blank.width); //clear left
gfx_mono_put_bitmap(&blank,x-5,8);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x+5,y+8,1,blank.width); //clear right
gfx_mono_put_bitmap(&blank,x+5,8);
clear_graphic_5v5(blank_data);
}
else{
merge_graphics(blank_data,maze->data.pixmap,x-5,y,0,blank.width); //clear left
gfx_mono_put_bitmap(&blank,x-5,0);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x-5,y,1,blank.width); //clear left
gfx_mono_put_bitmap(&blank,x-5,8);
clear_graphic_5v5(blank_data);
}
}
else if(y>7 && y<23){
if(x<5){
merge_graphics(blank_data,maze->data.pixmap,0,y,current_page,x); //clear left
gfx_mono_put_bitmap(&blank,0,page_starting_point);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,0,y,current_page+1,x); //clear left, one page down
gfx_mono_put_bitmap(&blank,0,page_starting_point+8);
clear_graphic_5v5(blank_data);

merge_graphics(blank_data,maze->data.pixmap,x+5,y,current_page,blank.width); //clear right
gfx_mono_put_bitmap(&blank,x+5,page_starting_point);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x+5,y,current_page+1,blank.width); //clear right, one page down
gfx_mono_put_bitmap(&blank,x+5,page_starting_point+8);
clear_graphic_5v5(blank_data);
}
else if(x>4 && x<123){
merge_graphics(blank_data,maze->data.pixmap,x-5,y,current_page,blank.width); //clear left
gfx_mono_put_bitmap(&blank,x-5,page_starting_point);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x+5,y,current_page,blank.width); //clear right
gfx_mono_put_bitmap(&blank,x+5,page_starting_point);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x-5,y+8,current_page+1,blank.width); //clear left, one page down
gfx_mono_put_bitmap(&blank,x-5,page_starting_point+8);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x+5,y+8,current_page+1,blank.width); //clear right, one page down
gfx_mono_put_bitmap(&blank,x+5,page_starting_point+8);
clear_graphic_5v5(blank_data);
}
else{
merge_graphics(blank_data,maze->data.pixmap,x-5,y,current_page,blank.width); //clear left
gfx_mono_put_bitmap(&blank,x-5,page_starting_point);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x-5,y,current_page+1,blank.width); //clear left, one page down
gfx_mono_put_bitmap(&blank,x-5,page_starting_point+8);
clear_graphic_5v5(blank_data);
}
}
else{
if(x<5){
merge_graphics(blank_data,maze->data.pixmap,0,y,current_page,x); //clear left
gfx_mono_put_bitmap(&blank,0,page_starting_point);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x+5,y,current_page,blank.width); //clear right
gfx_mono_put_bitmap(&blank,x+5,page_starting_point);
clear_graphic_5v5(blank_data);
}
else if(x>4 && x<123){
merge_graphics(blank_data,maze->data.pixmap,x-5,y,current_page,blank.width); //clear left
gfx_mono_put_bitmap(&blank,x-5,page_starting_point);
clear_graphic_5v5(blank_data);
merge_graphics(blank_data,maze->data.pixmap,x+5,y,current_page,blank.width); //clear right
gfx_mono_put_bitmap(&blank,x+5,page_starting_point);
clear_graphic_5v5(blank_data);
}
else{
merge_graphics(blank_data,maze->data.pixmap,x-5,y,current_page,blank.width); //clear left
gfx_mono_put_bitmap(&blank,x-5,page_starting_point);
clear_graphic_5v5(blank_data);
}
}
//display ball graphic window
if((y-page_starting_point) < 4){ //ball should display on one page (no overlap)
shift_ball_down(ball->data.pixmap,new_ball->data.pixmap,y-page_starting_point);
merge_graphics(new_ball->data.pixmap,maze->data.pixmap,x,y,y/8,new_ball->width); //merge maze and ball graphics
gfx_mono_put_bitmap(new_ball,x,y);
}
// else if((y-page_starting_point) > 3){
else{
shift_ball_down(ball->data.pixmap,new_ball->data.pixmap,y-page_starting_point);
merge_graphics(new_ball->data.pixmap,maze->data.pixmap,x,y,y/8,new_ball->width);
gfx_mono_put_bitmap(new_ball,x,y);

shift_ball_up(ball->data.pixmap,new_ball->data.pixmap,y-page_starting_point);
merge_graphics(new_ball->data.pixmap,maze->data.pixmap,x,y,y/8+1,new_ball->width);
gfx_mono_put_bitmap(new_ball,x,y+8);
}

/*
merge_graphics(blank_data,maze->data.pixmap,x-5,y,y/8);
gfx_mono_put_bitmap(&blank,x-5,y);
*/
if(current_page>*previous_page){
clear_graphic_5v5(new_ball->data.pixmap);
merge_graphics(new_ball->data.pixmap,maze->data.pixmap,x,y-8,(y-8)/8,new_ball->width);
gfx_mono_put_bitmap(new_ball,x,y-8);
}
if((y-page_starting_point)==3){
clear_graphic_5v5(new_ball->data.pixmap);
merge_graphics(new_ball->data.pixmap,maze->data.pixmap,x,y+8,(y+8)/8,new_ball->width);
gfx_mono_put_bitmap(new_ball,x,y+8);
}

*previous_page = current_page;
}
void shift_ball_down(gfx_mono_color_t *ball_data, gfx_mono_color_t *new_ball_data, int relative_y)
{
int i;

for( i=0; i<5; i++ ){
new_ball_data[i] = ball_data[i] << relative_y;
}
}
void shift_ball_up(gfx_mono_color_t *ball_data, gfx_mono_color_t *new_ball_data, int relative_y)
{
int i;

for( i=0; i<5; i++ ){
new_ball_data[i] = ball_data[i] >> (8-relative_y);
}
}

void merge_graphics(gfx_mono_color_t *new_ball_data, gfx_mono_color_t *maze_data, gfx_coord_t x, gfx_coord_t y, int page, int width)
{
int i;

for(i=0;i<width;i++){
new_ball_data[i] = new_ball_data[i] | maze_data[i+x+(page*128)];
}
}

void clear_graphic_5v5(gfx_mono_color_t *graphic_data)
{
for(int i = 0;i <5; i++){
*(graphic_data + i) = 0;
}
}
void init_maze_array(bool maze_array[32][128], const gfx_mono_color_t *maze_data)
{
//converts data from maze bitmap into boolean array
//true==wall
//false==empty
uint8_t temp,bitmask,bitpos,row,column,page;

for(int i=0;i<512;i++){
bitpos = 0;
bitmask = 0x01;
page = i / 128;
column = i - (page * 128);
temp = *(maze_data + i); //stores byte of bitmap into variable

for(int x=0;x<8;x++){
row = bitpos + (page * 8);
maze_array[row][column] = (bool)((temp & bitmask) >> bitpos);

bitpos++;
bitmask = bitmask << 1;
}
temp = 0;
}

//clear winning cell
for(row=0;row<5;row++){
for(column=0;column<5;column++){
maze_array[row+20][column+109] = false;
}
}

}
bool test_for_wall(bool maze_array[32][128],int xpos,int ypos)
{
return maze_array[ypos][xpos];
}

bool test_move_right(bool maze_array[32][128],int xpos,int ypos)
{
bool test0,test1,test2,test3,test4;

test0 = !test_for_wall(maze_array,xpos+5,ypos);
test1 = !test_for_wall(maze_array,xpos+5,ypos+1);
test2 = !test_for_wall(maze_array,xpos+5,ypos+2);
test3 = !test_for_wall(maze_array,xpos+5,ypos+3);
test4 = !test_for_wall(maze_array,xpos+5,ypos+4);

if(test0 && test1 && test2 && test3 && test4){
return true;
}
else
return false;

}
bool test_move_left(bool maze_array[32][128],int xpos,int ypos)
{
bool test0,test1,test2,test3,test4;

test0 = !test_for_wall(maze_array,xpos-1,ypos);
test1 = !test_for_wall(maze_array,xpos-1,ypos+1);
test2 = !test_for_wall(maze_array,xpos-1,ypos+2);
test3 = !test_for_wall(maze_array,xpos-1,ypos+3);
test4 = !test_for_wall(maze_array,xpos-1,ypos+4);

if(test0 && test1 && test2 && test3 && test4){
return true;
}
else
return false;

}
bool test_move_forward(bool maze_array[32][128],int xpos,int ypos)
{
bool test0,test1,test2,test3,test4;

test0 = !test_for_wall(maze_array,xpos,ypos-1);
test1 = !test_for_wall(maze_array,xpos+1,ypos-1);
test2 = !test_for_wall(maze_array,xpos+2,ypos-1);
test3 = !test_for_wall(maze_array,xpos+3,ypos-1);
test4 = !test_for_wall(maze_array,xpos+4,ypos-1);

if(test0 && test1 && test2 && test3 && test4){
return true;
}
else
return false;

}
bool test_move_backward(bool maze_array[32][128],int xpos,int ypos)
{
bool test0,test1,test2,test3,test4;

test0 = !test_for_wall(maze_array,xpos,ypos+5);
test1 = !test_for_wall(maze_array,xpos+1,ypos+5);
test2 = !test_for_wall(maze_array,xpos+2,ypos+5);
test3 = !test_for_wall(maze_array,xpos+3,ypos+5);
test4 = !test_for_wall(maze_array,xpos+4,ypos+5);

if(test0 && test1 && test2 && test3 && test4){
return true;
}
else
return false;

}

void clearscreen(void)
{
for(int page=0;page<5;page++){
st7565r_set_page_address(page);
for(int column=0;column<128;column++){
st7565r_set_column_address(column);
st7565r_write_data(0x00);
}
}
}
void displaymenu(void)
{
clearscreen();
char string[22]="Welcome to The Maze";
gfx_mono_draw_string(string,0,0,&sysfont);
gfx_mono_draw_horizontal_line(0,7,strlen(string)*6,GFX_PIXEL_CLR);

strcpy(string,"Press any button...");
gfx_mono_draw_string(string,0,16,&sysfont);
gfx_mono_draw_horizontal_line(0,23,114,GFX_PIXEL_CLR);

}

#endif /* MODIFIED_FUNCTIONS_H_ */

 

 

Here is the main function file main.c


#include <asf.h>
#include <board.h>
#include <sysclk.h>
#include "game_data.h"
#include "modified_functions.h"
#include "keyboard.h"
//variables
int8_t data_x0=0,data_x1=0,data_y0=0,data_y1=0,data_z0=0,data_z1=0;
int16_t x_combined, y_combined, z_combined;
char mystring[20];
uint8_t bit_pos;
int ball_xpos=1, ball_ypos=14, ball_previous_page=0;
bool temp,maze_array[32][128];
struct pwm_config mypwm;

#define MOVEMENT_THRESHOLD_POS 40 //Minimum threshold values
#define MOVEMENT_THRESHOLD_NEG -20

#define WINNING_POS_X 109 //Winning x and y coordinates
#define WINNING_POS_Y 20
//prototypes
extern int16_t COMBINE(int8_t,int8_t); //assembly function that combines output register data and
//converts it to a 16-bit value

int main(void){

board_init();
sysclk_init();
gfx_mono_init();
twi_init(); //initialize I2C interface
pwm_init(&mypwm, PWM_TCC0, PWM_CH_C, 100); //initialize PWM channel
gpio_set_pin_high(NHD_C12832A1Z_BACKLIGHT); //turn on LCD backlight

int8_t data; //data variable to store info to write to ADXL
int8_t data_received[10]; //data buffer array for I2C
int8_t* ptr = &data;
twi_package_t packet_write={ //I2C data write structure
.addr[0] = 0x31, //DATA_FORMAT register location on ADXL
.addr_length=sizeof(uint8_t),
.chip=0x53, //ADXL address
.buffer=(void*)ptr,
.length=sizeof(data)
};
data = 0x00;
while (twi_master_write(&TWIC, &packet_write) != TWI_SUCCESS); //sets the AXDL resolution to 2g resolution

//set AXDL to measure
packet_write.addr[0] = 0x2D; //PWER_CTL register location on ADXL
data=0x08;
while (twi_master_write(&TWIC, &packet_write) != TWI_SUCCESS); //sets AXDL to "measure mode"

twi_package_t packet_read = { //I2C data write structure
.addr = {0x32,0x33,0x34,0x35,0x36,0x37}, // array of data registers that will be read from ADXL
.addr_length = sizeof(int8_t),
.chip = 0x53, // slave address
.buffer = data_received, // transfer data destination buffer
.length = 6 // transfer data size (bytes) / one for each register
};

init_maze_array(maze_array,maze_data); //creates boolean array for boundaries from bitmap data

//gfx_mono_draw_string("X=",0,0,&sysfont); //Display the axis on LCD
//gfx_mono_draw_string("Y=",0,8,&sysfont);
//gfx_mono_draw_string("Z=",0,16,&sysfont);

int duty; //variable to set PWM duty cycle
struct keyboard_event input; //struct for button operation on menu screen

while(1)
{
displaymenu(); //display start menu
do{
keyboard_get_key_state(&input);
}while(input.type != KEYBOARD_RELEASE); //when button is pressed and released, begin game

clearscreen(); //Display instructions
strcpy(mystring,"Tilt the breadboard");
gfx_mono_draw_string(mystring,0,0,&sysfont);
gfx_mono_draw_horizontal_line(0,7,strlen(mystring)*6,GFX_PIXEL_CLR);
strcpy(mystring,"to move the ball");
gfx_mono_draw_string(mystring,0,8,&sysfont);
gfx_mono_draw_horizontal_line(0,15,strlen(mystring)*6,GFX_PIXEL_CLR);
strcpy(mystring,"to the end");
gfx_mono_draw_string(mystring,0,16,&sysfont);
gfx_mono_draw_horizontal_line(0,23,strlen(mystring)*6,GFX_PIXEL_CLR);
delay_s(15);

/* BEGIN GAME */

while(true){

gfx_mono_put_bitmap(&maze,0,0); //print maze and ball
display_ball(&ball,&new_ball,&maze,ball_xpos,
ball_ypos,&ball_previous_page);

if(ball_xpos== WINNING_POS_X //check to see if ball is winning position
&& ball_ypos == WINNING_POS_Y){

clearscreen(); //print winning message
gfx_mono_draw_string("You win!!",35,8,&sysfont);
gfx_mono_draw_horizontal_line(35,15,9,GFX_PIXEL_CLR);

int loop=0; //trigger winning sound
pwm_start(&mypwm,50);

while(loop<6){
for(duty=35;duty<=65;duty++){
pwm_set_duty_cycle_percent(&mypwm,duty);
delay_ms(10);
}
for(duty=65;duty>=35;duty--){
pwm_set_duty_cycle_percent(&mypwm,duty);
delay_ms(10);

}
loop++;
}
pwm_set_duty_cycle_percent(&mypwm,0);

ball_xpos = 1; //reset ball to starting position
ball_ypos = 14;

break;
}

for(int i=0;i<10;i++){ //initialize buffer array before reading
data_received[i]=0;
}//end for

if(twi_master_read(&TWIC, &packet_read) == TWI_SUCCESS){
data_x0 = data_received[0]; //Read data from AXDL and store values
data_x1 = data_received[1];
data_y0 = data_received[2];
data_y1 = data_received[3];
data_z0 = data_received[4];
data_z1 = data_received[5];
}//end if

x_combined = 0; //clear previous values
y_combined = 0;
z_combined = 0;

x_combined = COMBINE(data_x0,data_x1); //combine results into a 16-bit signed value
y_combined = COMBINE(data_y0,data_y1);
z_combined = COMBINE(data_z0,data_z1);

if((int)x_combined >= MOVEMENT_THRESHOLD_POS //test for x axis tilt
|| (int)x_combined <= MOVEMENT_THRESHOLD_NEG){
if((int)x_combined >= MOVEMENT_THRESHOLD_POS){
gfx_mono_st7565r_put_byte(ball_ypos/8,ball_xpos,0x00);
if(test_move_right(maze_array,ball_xpos,ball_ypos)){
ball_xpos++; //increment ball if it clear path
if(ball_xpos>123){
ball_xpos=123; //if ball is at edge of screen, don't increment
}//end if
}
}//end if
else if((int)x_combined <= MOVEMENT_THRESHOLD_NEG){ //test for x axis tilt
gfx_mono_st7565r_put_byte(ball_ypos/8,ball_xpos+4,0x00);
if(test_move_left(maze_array,ball_xpos,ball_ypos)){
ball_xpos--; //decrement ball if it clear path
if(ball_xpos<0){
ball_xpos=0; //if ball is at edge of screen, don't decrement
}
}
}
}

if((int)y_combined >= MOVEMENT_THRESHOLD_POS
|| (int)y_combined <= MOVEMENT_THRESHOLD_NEG){
if ((int)y_combined >= MOVEMENT_THRESHOLD_POS){ //test for y axis tilt
if(test_move_forward(maze_array,ball_xpos,ball_ypos)){
ball_ypos--; //decrement ball if it clear path
if(ball_ypos<0){
ball_ypos=0; //if ball is at edge of screen, don't decrement
}//end if
}
}//end if
else if((int)y_combined <= MOVEMENT_THRESHOLD_NEG){ //test for y axis tilt
if(test_move_backward(maze_array,ball_xpos,ball_ypos)){
ball_ypos++; //increment ball if it clear path
if(ball_ypos>27){
ball_ypos=27; //if ball is at edge of screen, don't increment
}
}
}
}

display_ball(&ball,&new_ball,&maze,
ball_xpos,ball_ypos,&ball_previous_page); //draw ball with new position

delay_ms(15); //wait for a bit...
}
}
}//end main

Accelerometer Maze Game: Schematic

This is the schematic for the Maze Game.  This project was not as involved on the hardware side as a lot of other projects were.  It only required an accelerometer, 3 220-ohm resistors, a piezo buzzer, and several wires for the I2C communication from the accelerometer and the A3BU.

Image

(Instructor note: hmmm. this link does not work off campus. The Speedometer project uses the same accelerometer, here’s their schematic)

Crosswalk Simulator/Stoplight Code Snippet

You can see the commented code we used below:



/**
 * \file
 *
 * \brief Empty user application template
 *
 */

/**
 * \mainpage User Application template doxygen documentation
 *
 * \par Empty user application template
 *
 * Bare minimum empty user application template
 *
 * \par Content
 *
 * -# Include the ASF header files (through asf.h)
 * -# "Insert system clock initialization code here" comment
 * -# Minimal main function that starts with a call to board_init()
 * -# "Insert application code here" comment
 *
 */

/*
 * Include header files for all drivers that have been imported from
 * Atmel Software Framework (ASF).
 */
/*
 * Support and FAQ: visit &lt;a href="http://www.atmel.com/design-support/"&gt;Atmel Support&lt;/a&gt;
 */
#include &lt;asf.h&gt;
#include &lt;delay.h&gt;
#include &lt;avr/io.h&gt;
#include &lt;stdio.h&gt;
#include &lt;conf_example.h&gt;

int adc_result;
//Function to get the result from the ADC pin and save it to the above global variable
static void adc_handler(ADC_t *adc, uint8_t ch_mask, adc_result_t result)
{ 
 adc_result = 0;
 #ifdef CONF_BOARD_OLED_UG_2832HSWEG04
 gfx_mono_draw_filled_rect(0,0,128,32,GFX_PIXEL_CLR);
 #endif

 adc_result = (uint8_t)result;
}

//this function checks if a "pedestrian" is at the intersection
//if the result from the ADC pin is between 100 and 200, then return true
//otherwise return false
bool checkForPedestrian(ADC_t *adc, uint8_t ch_mask)
{
 adc_start_conversion(adc, ch_mask);
 if(adc_result &gt; 100 &amp;&amp; adc_result &lt; 200)
 return true;
 else
 return false;
}

//draw a walk sign using ASCII characters on the screen
void drawWalkSign()
{
 gfx_mono_draw_string(" O", 10, 10, &amp;sysfont);
 gfx_mono_draw_string(" v|\\", 10, 17, &amp;sysfont);
 gfx_mono_draw_string(" / \\", 10, 25, &amp;sysfont);
}

//draw a don't walk hand on the screen using ASCII characters
void drawHand()
{
 gfx_mono_draw_string("^^^^", 30, 0, &amp;sysfont);
 gfx_mono_draw_string("|||||^", 28, 7, &amp;sysfont);
 gfx_mono_draw_string("||||| |", 28, 14, &amp;sysfont);
 gfx_mono_draw_string("\\____/", 30, 23, &amp;sysfont);
}

//draw a timer bar on the screen and also make the speaker beep
void drawBar(struct pwm_config buzzer)
{
 int count = 0;
 int count_2 = 0;
 //this will delay the whole process by 14 seconds
 while(count &lt; 140)
 {
 //draw a rectangle that grows across the screen
 gfx_mono_draw_rect(0, 0, count, 6, GFX_PIXEL_SET);
 count += 10;
 count_2++;
 //turn the speaker on
 pwm_start(&amp;buzzer,100);
 //wait for a second
 delay_ms(1000);
 //turn the speaker off
 pwm_start(&amp;buzzer, 50);
 }
 //turn the speaker completely off
 pwm_start(&amp;buzzer, 0);
 //reset the ADC result
 adc_result = 0;
}

int main (void)
{ 
 //initialize everything
 board_init();
 sysclk_init();
 gfx_mono_init();
 ioport_set_pin_high(LCD_BACKLIGHT_ENABLE_PIN);
 
 //PWM configs for the various LEDs
 struct pwm_config green;
 struct pwm_config red;
 struct pwm_config yellow;
 struct pwm_config walk;

 struct pwm_config green_2;
 struct pwm_config red_2;
 struct pwm_config yellow_2;
 struct pwm_config buzzer;
 
 /* Set up a PWM channel with 500 Hz frequency. Here we will output 4 different pulse trains on 4 pins of the A3BU Xplained.*/

 pwm_init(&amp;red, PWM_TCC0, PWM_CH_A, 500); //this is SDA on J1 on the A3BU Xplained 
 pwm_init(&amp;green, PWM_TCC0, PWM_CH_B, 500); //On A3BU Xplained board, SCL on J1 
 pwm_init(&amp;yellow, PWM_TCC0, PWM_CH_C, 500);//On A3BU Xplained board, RXD on J1 
 pwm_init(&amp;walk, PWM_TCC0, PWM_CH_D, 500); //On A3BU Xplained board, TXD on J1
 
 pwm_init(&amp;red_2, PWM_TCE0, PWM_CH_A, 500); //this is SDA on J1 on the A3BU Xplained 
 pwm_init(&amp;green_2, PWM_TCE0, PWM_CH_B, 500); //On A3BU Xplained board, SCL on J1 
 pwm_init(&amp;yellow_2, PWM_TCE0, PWM_CH_C, 500);//On A3BU Xplained board, RXD on J1 
 pwm_init(&amp;buzzer, PWM_TCE0, PWM_CH_D, 2000); //On A3BU Xplained board, TXD on J1
 
 // Set each of the 4 PWM channel duty cycles independently, and start them going 
 pwm_start(&amp;red,100);
 pwm_start(&amp;green,100);
 pwm_start(&amp;yellow,100); 
 pwm_start(&amp;walk,100);
 
 pwm_start(&amp;red_2,100);
 pwm_start(&amp;green_2,100);
 pwm_start(&amp;yellow_2,100); 
 pwm_start(&amp;buzzer,0);
 
 /*************************/
 //ADC initialization steps
 /*************************/
 struct adc_config adc_conf;
 struct adc_channel_config adcch_conf;

 sleepmgr_init();
 irq_initialize_vectors();
 cpu_irq_enable();

 // Initialize configuration structures.
 adc_read_configuration(&amp;ADCB, &amp;adc_conf);
 adcch_read_configuration(&amp;ADCB, ADC_CH0, &amp;adcch_conf);

 /* Configure the ADC module:
 * - unsigned, 12-bit results
 * - VCC voltage reference
 * - 200 kHz maximum clock rate
 * - manual conversion triggering
 * - temperature sensor enabled
 * - callback function
 */
 adc_set_conversion_parameters(&amp;adc_conf, ADC_SIGN_ON, ADC_RES_12,
 ADC_REF_VCC);
 adc_set_clock_rate(&amp;adc_conf, 200000UL);
 adc_set_conversion_trigger(&amp;adc_conf, ADC_TRIG_MANUAL, 1, 0);
 adc_enable_internal_input(&amp;adc_conf, ADC_INT_TEMPSENSE);

 adc_write_configuration(&amp;ADCB, &amp;adc_conf);
 adc_set_callback(&amp;ADCB, &amp;adc_handler);

 /* Configure ADC channel 0:
 * - single-ended measurement from temperature sensor
 * - interrupt flag set on completed conversion
 * - interrupts disabled
 */
 adcch_set_input(&amp;adcch_conf, ADCCH_POS_PIN1, ADCCH_NEG_NONE,
 1);
 adcch_set_interrupt_mode(&amp;adcch_conf, ADCCH_MODE_COMPLETE);
 adcch_enable_interrupt(&amp;adcch_conf);

 adcch_write_configuration(&amp;ADCB, ADC_CH0, &amp;adcch_conf);

 // Enable the ADC
 adc_enable(&amp;ADCB);
 
 //ON and OFF duty cycle values
 int ON = 100;
 int OFF = 0;
 //bool to check for pedestrians in the intersection
 bool pedestrian = false;
 //loop forever
 while(1) {
 //as long as there is no pedestrian at the intersection
 //leave the green and red_2 LEDs on, and the rest off
 while(!pedestrian)
 {
 pwm_start(&amp;red,OFF);
 pwm_start(&amp;green,ON);
 pwm_start(&amp;yellow,OFF);
 
 pwm_start(&amp;red_2,ON);
 pwm_start(&amp;green_2,OFF);
 pwm_start(&amp;yellow_2,OFF);
 
 //check the ADC pin to see if a pedestrian is at the intersection
 pedestrian = checkForPedestrian(&amp;ADCB, ADC_CH0);
 }
 
 /***a pedestrian has pressed the button, now transition***/
 //reset pedestrian value
 pedestrian = false;
 //wait for a half second
 delay_ms(500);
 //turn green off and turn yellow on
 pwm_start(&amp;green,OFF);
 pwm_start(&amp;yellow,ON);
 //wait for 3 seconds
 delay_ms(3000);
 
 //turn yellow off and turn red on
 pwm_start(&amp;yellow,OFF);
 pwm_start(&amp;red,ON);
 //wait for 2 seconds
 delay_ms(2000);
 
 //turn red_2 off and turn green_2 on
 pwm_start(&amp;green_2,ON);
 pwm_start(&amp;red_2,OFF);
 //clear the LCD screen
 gfx_mono_init();
 //draw the walk signs and the timer bar, and start the speaker
 drawWalkSign();
 drawBar(buzzer);
 //clear the LCD screen
 gfx_mono_init();
 //draw the Don't Walk hand
 drawHand();
 //wait for 5 seconds to prevent a pedestrian from immediately pushing the button again
 delay_ms(5000);
 
 //as long as there is no pedestrian at the intersection
 //leave the green_2 and red LEDs on, and the rest off
 while(!pedestrian)
 {
 pwm_start(&amp;red_2,OFF);
 pwm_start(&amp;green_2,ON);
 pwm_start(&amp;yellow_2,OFF);
 
 pwm_start(&amp;red,ON);
 pwm_start(&amp;green,OFF);
 pwm_start(&amp;yellow,OFF);
 
 //check the ADC pin to see if a pedestrian is at the intersection
 pedestrian = checkForPedestrian(&amp;ADCB, ADC_CH0);
 }
 
 /***a pedestrian has pressed the button, now transition***/
 //reset pedestrian value
 pedestrian = false;
 //wait for a half second
 delay_ms(500);
 //turn green_2 off and turn yellow_2 on
 pwm_start(&amp;green_2,OFF);
 pwm_start(&amp;yellow_2,ON);
 //wait for 3 seconds
 delay_ms(3000);
 
 //turn yellow_2 off and turn red_2 on
 pwm_start(&amp;yellow_2,OFF);
 pwm_start(&amp;red_2,ON);
 //wait for 2 seconds
 delay_ms(2000);
 //turn red off and turn green on
 pwm_start(&amp;green,ON);
 pwm_start(&amp;red,OFF);
 //clear the LCD screen
 gfx_mono_init();
 //draw the walk signs and the timer bar, and start the speaker
 drawWalkSign();
 drawBar(buzzer);
 //clear the LCD screen
 gfx_mono_init();
 //draw the Don't Walk hand
 drawHand();
 //wait for 5 seconds to prevent a pedestrian from immediately pushing the button again
 delay_ms(5000);
 
 /***Repeat the whole process again***/
 }
}

C Code created in Atmel Studio 7 that dictates the actions of our LCD and circuit

Crosswalk Simulator/Stoplight: The Big Picture

The fundamental goal of this project was to recreate the same type of crosswalk that could be found at the intersection of Eastern Parkway and Speed School. The scope of the project contained two stop lights (6 LEDs Total), walk/don’t walk graphics, battery powered crosswalk button, a sounding buzzer for echolocation,  and a “time remaining” indicator.

The A3BU board worked well for all functions that we needed to complete this project. The board provided us with enough GPIO pins to provide power control for 6 of the 3V LED’s, as well as frequency pulse with modulation to control the buzzer. An analog -to-digital converter (ADC) pin was also utilized to detect digital-hi’s when the crosswalk button was pressed. The battery provided a voltage that would then be tested against a certain range, and if the value was in that range, the crosswalk logic would trigger.

LED Layout with A3BU, buzzer, and button.

Final Project pic 2

The LCD display served a large purpose as it displayed all of our crosswalk functions. When the crosswalk button was pressed and the light turns red, a ASCII graphic of a “walkman” appears letting you know its safe to cross. A incremented bar also appears, gradually growing larger allowing the walker to see how much time they have left to cross. Once this timer ends, an ASCII “stop-hand” appears, letting the user know it’s no longer safe to cross.

 

LCD displaying the ‘walkman’ and the timer bar at the top

Final Project pic 1

The buzzer was turned off and on by varying the duty cycle on the GPIO output, while the pitched was changed by editing the frequency of the pulses. Here is a video displaying all the functions.