Difference between revisions of "Ball Balancing Challenge"

From Mech
Jump to navigationJump to search
Line 78: Line 78:
==Software Design==
==Software Design==


The opening lines are necessary for establishing that we are using a PIC 18F4520, analog inputs, a clock, connecting with an ICD, and that we are using pin 36 instead of 16 for the second PWM output. This is also where we establish all of our variables and functions.
Source Code:


<pre>
<pre>

/*
touchscreen.c Mechatronics 333 Team 14 2/22/08
Reads the touchscreen individually as two axes. Actuates motor based on either touchscreen or joystick control.
PIC powers either the Upper Left (Y-axis reading) or the Lower Right (X-axis reading) and grounds what it doesn't power.
Output reading is the value on that axis.
Two channels of PWM output, with the PWM2 output moved from pin 16 to pin 36 to avoid conflict with Timer1 input
Upper Left (UL) corner is PIN_C1, and Lower Left (LR) corner is always its inverse.
Upper Right (UR) corner is always high, and lower right (LL) is always ground, its inverse.
Peak voltage: Top reading voltage=4V and bottom reading voltage=1.2V
Min xread,yread values: 62,70
Max xread,yread values: 189,177
*/

#include <18f4520.h>
#include <18f4520.h>
#DEVICE ADC=8 // set ADC to 8 bit accuracy
#DEVICE ADC=8 // set ADC to 8 bit accuracy
Line 109: Line 95:


void buzz();
void buzz();
</pre>


The main function starts with setting the correct initial values to the variables, and setting up the timers and analog ports.

<pre>
void main() {
void main() {
//Flash 3 times to let us know we're running
for (j=0;j<3;j++){
for (j=0;j<3;j++){
output_d(0b11111111);
output_d(0b11111111);
Line 140: Line 131:
set_pwm1_duty(39);
set_pwm1_duty(39);
set_pwm2_duty(39);
set_pwm2_duty(39);

</pre>

The following portion of code shows what happens when the game is continuously played. First the game waits for a coin, then it plays the game until the user strikes out, then it stops the motors from spinning and again waits for a coin.

<pre>
while (TRUE) {
while (TRUE) {
Line 154: Line 150:
//Gameplay begins. Controls go dead when strikes go too high;
//Gameplay begins. Controls go dead when strikes go too high;
while (coin==1) {
while (coin==1) {
</pre>
//TOUCHSCREEN READing, looks at the touchscreen readings

The touchscreen is read by having the PIC send alternating signals to 2 of the corners that are diagonal to each other while keeping the other two signals constant. This allows the touchscreen to read both axes each run through, and then display them in a manner where the x-axis reading corresponds to PIC LED outputs D0-D3, and the y-axis reading corresponds to outputs D4-D7. Although this gives visually a very poor resolution on the reading, it serves well enough as a debugging tool to make sure the reading is working.
<pre>
//Touchscreen reading, looks at the touchscreen readings
if (m==0) {
if (m==0) {
output_high(PIN_C1); //UL goes high, thus LR goes low; y-axis can be read
output_high(PIN_C1); //UL goes high, thus LR goes low; y-axis can be read
Line 169: Line 169:
m=0;
m=0;
}
}
//Check to see if the Ball is outide the allowed range of values to be "in the black" of the touchscreen
if ((yread< 90) | (yread >150) | (xread <90) | (xread > 150)) {
counter++;
if ((counter==3) && (strike==ball)) {
strike++; //If ball is "in the red" for a sufficiently long time, a strike is added.
}
}
else {
counter--;
//Check to see if the ball has returned from being "in the red."
if ((counter==-1) && (strike>ball)) {
ball++;
}
}
if ((counter==3) | (counter == -2)) counter=0;
if (strike==2) {
output_high(PIN_A3);
}
if (strike==3) {
output_high(PIN_A4);
}
if (strike==4) {
output_high(PIN_A5);
}
if (strike==5) {
output_high(PIN_E2);
}
if (strike==6) {
output_high(PIN_E0);
set_pwm1_duty(39);
set_pwm2_duty(39);
buzz();
}



//Joystick Control
if (input(PIN_C7) != 0) { // toggle switch that switches between Joystick control system and Touchscreen control system
set_adc_channel(2); // there's only one ADC so select which input to connect to it; here pin AN1
delay_us(10); // wait 10uS for ADC to settle to a newly selected input
read1 = read_adc(); //x-axis
if (read1<145 && read1>115) read1pwm=39;
else read1pwm=(read1*.3);
set_pwm1_duty(read1pwm);
set_adc_channel(1); // there's only one ADC so select which input to connect to it; here pin AN2
delay_us(10); // wait 10uS for ADC to settle to a newly selected input
read2 = read_adc(); //y-axis
if (read2<145 && read2>115) read2pwm=39;
else read2pwm=(read2*.3);
set_pwm2_duty(read2pwm);
}
else {
output_d(0);
set_pwm1_duty(39);
set_pwm2_duty(39);
}
//LED readings for XY locations of the touchscreen
//LED readings for XY locations of the touchscreen
if (xread<75) {
if (xread<75) {
Line 293: Line 231:
output_high(PIN_D6);
output_high(PIN_D6);
output_high(PIN_D7);
output_high(PIN_D7);
}
</pre>

To find the "winning" ranges of the black area on the touchscreen, we measured the voltage output on each axis on each boundary line, and converted it from the 0V to 5V scale to the quantized 8 bit (0 to 255) scale. Our code then checks to see if the ball is not in this zone before assigning a strike to the user. The counter variable is to ensure that the ball is outside the range enough times in a row, and a low reading is not simply the result of inconsistencies in the touchscreen reading.
To stop the machine from simply running up the strikes while the ball is in the red, the code ensures that a "strike" cannot be assigned until a "ball" is assigned before hand.

For each strike, a corresponding LED is lit up, and upon the last strike, the buzz function is set off.

<pre>
//Check to see if the Ball is outide the allowed range of values to be "in the black" of the touchscreen
if ((yread< 90) | (yread >150) | (xread <90) | (xread > 150)) {
counter++;
if ((counter==3) && (strike==ball)) {
strike++; //If ball is "in the red" for a sufficiently long time, a strike is added.
}
}
else {
counter--;
//Check to see if the ball has returned from being "in the red."
if ((counter==-1) && (strike>ball)) {
ball++;
}
}
if ((counter==3) | (counter == -2)) counter=0;


if (strike==2) {
output_high(PIN_A3);
}
if (strike==3) {
output_high(PIN_A4);
}
if (strike==4) {
output_high(PIN_A5);
}
if (strike==5) {
output_high(PIN_E2);
set_pwm1_duty(39);
set_pwm2_duty(39);
buzz();
}

</pre>
The PIC reads the joystick and quantizes the input from the analog 0V to 5V to the 8 bit quantized 0 to 255. It then scales that number to be between 0 and 78, the PWM extremes.
<pre>
//Joystick Control
if (input(PIN_C7) != 0) { // toggle switch that switches between Joystick control system and Touchscreen control system
set_adc_channel(2); // there's only one ADC so select which input to connect to it; here pin AN1
delay_us(10); // wait 10uS for ADC to settle to a newly selected input
read1 = read_adc(); //x-axis
if (read1<145 && read1>115) read1pwm=39;
else read1pwm=(read1*.3);
set_pwm1_duty(read1pwm);
set_adc_channel(1); // there's only one ADC so select which input to connect to it; here pin AN2
delay_us(10); // wait 10uS for ADC to settle to a newly selected input
read2 = read_adc(); //y-axis
if (read2<145 && read2>115) read2pwm=39;
else read2pwm=(read2*.3);
set_pwm2_duty(read2pwm);
}
else {
output_d(0);
set_pwm1_duty(39);
set_pwm2_duty(39);
}
}
}
}
}
}
}
}
</pre>


The buzz() function resets the counting variables to zero and sets the buzzer off by oscillating a PIC output into a basic buzzer speaker at an audible frequency.

<pre>
void buzz() {
void buzz() {


Line 303: Line 317:
strike=0;
strike=0;
ball=0;
ball=0;
counter = 0;
output_low(PIN_A3);
output_low(PIN_A3);
output_low(PIN_A4);
output_low(PIN_A4);

Revision as of 18:37, 20 March 2008

T14-project-action-08.jpg




Team Members

Left to right: JJ Darling, Alex Leung, Ben Schriesheim
  • JJ Darling - Electrical Engineering Class of 2009
  • Ben Schriesheim - Manufacturing Engineering Class of 2008
  • Alex Leung - Biomedical Engineering Graduate Student

Overview

We built a horizontal circular platform that is actuated from underneath by three speakers placed in an equilateral triangle. The vibration of the platform causes a small object (e.g., an IC socket or a coin) to act as an hour "hand" on top of the platform. The object slides around the circular platform, impelled by friction forces due to the vibration. By placing the speakers at different phases and amplitudes, we got the objects to move to desired positions. Due to the nodes created by the speaker vibrations, the object will move back to the correct hour if it is moved away. Our project was given to us by Professor Colgate and was based upon the research of Professor Lynch.

Mechanical Design

The Vibratory clock consists of a wooden base, held up by adjustable legs, three speakers, and a circular platform. The following material were used to create the design:

Base:

  • wood: 16" diameter, 0.75" thick
  • holes: 6" diameter (3 total)
  • adjustable legs: 3 rods: 3/8"-16; 4" long; 6 nuts total
Speaker supports.jpg

Speakers:

  • 3 Pyramid Power PW677X: 300W, 4 Ohms, 6.5" Chrome Subwoofer
    • previously adapted speakers replaced center of speaker with attachments 3" in height and 1/2" hole on top
    • 1/2" diameter PVC used to attach previous attachment to platform with a screw
      • held in place by 2 set screws each
    • centers placed 7.125" apart

Circular Platform

  • PVC/Acrylic: 11.75" diameter, 0.25" thick
    • machined on the LaserJet
    • screw holes counter-sunk at 7.125" apart
  • 2.875" above wooden base
  • Silver Sharpie used for numbers

Reasoning for Geometry of Design

Equilateral Triangle: By having the speakers equidistant from each other, we were able to create symmetry in our design, which made programming the various nodes much easier. For example, we were able to use the same theory to find the node at 12 o'clock, 4 o'clock, and 8 o'clock. If the speakers were not placed equidistant from each other, two speakers placed at equal amplitudes and opposite phases would not have created a sink in the exact middle of the two speakers.

Adjustable Legs:

We needed to make the legs adjustable since it is essential that the platform be perfectly level in order for the objects placed on the platform to move in expected patterns. For example, if one leg is slightly shorter than the other two, the objects placed on the platform would tend to move towards that leg and we could no longer rely on symmetry to program the various nodes due to the effects of gravity.

Height of Platform:

Through our own experimentation and from Professor Lynch's research, we found that we needed to have the pivot point (i.e. the speaker diaphragm) significantly below the platform in order to create sinks. At one point in the design process, we attempted to create a different pivot point by replacing the PVC with 1/2" outer diameter, 1/4" inner diameter Tygon 2001 tubing that only was able to bend in one point since it had stand-offs and screws inside the tube. However, this replacement in a sense created two pivot points (the speaker diaphragm and the Tygon), causing the platform to only create sources and not sinks. In addition, the different height of the pivot point may have also led to the plate acting differently.

Electrical Design

The Electical Design was fairly simple for this project. The inputs to the PIC were the joystick, coin slot, and the touchscreen. The outputs were the motors and the LED "strike" array. The touchscreen was also an output as it was controlled by the PIC.

The user would control the joystick that was simply two potentiometers that would output a value between 0V and 5V, corresponding to both the x-axis and the y-axis.


Circuit Board

Component List:

PartPart No.QtyVendorPrice (Total)
Microchip 8-bit PIC Microcontroller (U1)PIC18F45201N/AN/A
Quadruple Half-H DriversL293D 2Digi-Key$1.93
Hex InverterSN74HC041Digi-Key$0.47
TouchscreenBER237-ND1Digi-Key$62.00
JoystickN/A1N/AN/A

Circuit Diagram:

Ball Balancing Circuit Diagram

Software Design

The opening lines are necessary for establishing that we are using a PIC 18F4520, analog inputs, a clock, connecting with an ICD, and that we are using pin 36 instead of 16 for the second PWM output. This is also where we establish all of our variables and functions.

#include <18f4520.h>
#DEVICE ADC=8                      // set ADC to 8 bit accuracy
#fuses HS,NOLVP,NOWDT,NOPROTECT, CCP2B3      // CCP2B3 moves PWM2 output to pin 36 (RB3) rather than pin 16 (RC1)
#device icd=true
#use delay(clock=20000000)


int yread,xread,m,j=0;
int read1pwm,read2pwm=0;
int read1,read2=0;
int coin,strike,ball=0;
signed int16 counter=0;

void buzz();

The main function starts with setting the correct initial values to the variables, and setting up the timers and analog ports.

void main() {
   //Flash 3 times to let us know we're running
   for (j=0;j<3;j++){
      output_d(0b11111111);
      delay_ms(250);
      output_d(0);
      delay_ms(250);
   }
  
   coin=0;
   strike=0;
   ball=0;
   counter=0;
  
   output_high(PIN_C1);          //Start with UL high, LR low ***THESE WILL CHANGE***
   output_high(PIN_C0);          //This is the pin for UR. It will be run through an inverter for LL
                                 //the bottom left corner. ***THESE ARE PERMANENT***
  
   setup_adc_ports(AN0_TO_AN2);        // Enable analog inputs; choices run from just AN0, up to AN0_TO_AN11
   setup_adc(ADC_CLOCK_INTERNAL);      // the range selected has to start with AN0
  
   setup_timer_2(T2_DIV_BY_4, 77, 16);        // clock at 16KHz, interrupt every 4*50nS * 4 * (155+1) * 16 = 2.00mS

   enable_interrupts(INT_TIMER2);
   enable_interrupts(GLOBAL);  
 
   setup_ccp1(CCP_PWM);       // PWM output on CCP1/RC2, pin 17   this goes to y-axis motor
   setup_ccp2(CCP_PWM);       // PWM output on CCP2/RB3, pin 36   this goes to x-axis motor

   set_pwm1_duty(39);
   set_pwm2_duty(39);

The following portion of code shows what happens when the game is continuously played. First the game waits for a coin, then it plays the game until the user strikes out, then it stops the motors from spinning and again waits for a coin.

  
   while (TRUE) {
  
      set_pwm1_duty(39);
      set_pwm2_duty(39);
     
      //Wait for coin to be inserted
      while (coin==0) {                
         if (input(PIN_E1)) coin=1;
         else delay_ms(100);
      }
      
      //Gameplay begins. Controls go dead when strikes go too high; 
      while (coin==1) {

The touchscreen is read by having the PIC send alternating signals to 2 of the corners that are diagonal to each other while keeping the other two signals constant. This allows the touchscreen to read both axes each run through, and then display them in a manner where the x-axis reading corresponds to PIC LED outputs D0-D3, and the y-axis reading corresponds to outputs D4-D7. Although this gives visually a very poor resolution on the reading, it serves well enough as a debugging tool to make sure the reading is working.

         //Touchscreen reading, looks at the touchscreen readings
         if (m==0) {
            output_high(PIN_C1);       //UL goes high, thus LR goes low; y-axis can be read
            set_adc_channel(0);
            delay_us(10);          
            yread = read_adc();       //Read y axis           
            m++;
         }
         else {
            output_low(PIN_C1);        //UL goes low, thus LR goes high; x-axis can be read
            set_adc_channel(0);
            delay_us(10);          
            xread = read_adc();       //Read x axis
            m=0;
         }


         //LED readings for XY locations of the touchscreen
         if (xread<75) {                
            output_low(PIN_D0);     
            output_low(PIN_D1);
            output_low(PIN_D2);
            output_low(PIN_D3);
         }
         else if (xread<110) {
            output_high(PIN_D0);     
            output_low(PIN_D1);
            output_low(PIN_D2);
            output_low(PIN_D3);
         }
         else if (xread<145) {
            output_high(PIN_D0);     
            output_high(PIN_D1);
            output_low(PIN_D2);
            output_low(PIN_D3);
         }
         else if (xread<180) {
            output_high(PIN_D0);     
            output_high(PIN_D1);
            output_high(PIN_D2);
            output_low(PIN_D3);
         }
         else  {
            output_high(PIN_D0);     
            output_high(PIN_D1);
            output_high(PIN_D2);
            output_high(PIN_D3);
         }
         if (yread<75) {
            output_low(PIN_D4);     
            output_low(PIN_D5);
            output_low(PIN_D6);
            output_low(PIN_D7);
         }
         else if (yread<110) {
            output_high(PIN_D4);     
            output_low(PIN_D5);
            output_low(PIN_D6);
            output_low(PIN_D7);
         }
         else if (yread<145) {
            output_high(PIN_D4);     
            output_high(PIN_D5);
            output_low(PIN_D6);
            output_low(PIN_D7);
         }
         else if (yread<180) {
            output_high(PIN_D4);     
            output_high(PIN_D5);
            output_high(PIN_D6);
            output_low(PIN_D7);
         }
         else  {
            output_high(PIN_D4);     
            output_high(PIN_D5);
            output_high(PIN_D6);
            output_high(PIN_D7);
         }

To find the "winning" ranges of the black area on the touchscreen, we measured the voltage output on each axis on each boundary line, and converted it from the 0V to 5V scale to the quantized 8 bit (0 to 255) scale. Our code then checks to see if the ball is not in this zone before assigning a strike to the user. The counter variable is to ensure that the ball is outside the range enough times in a row, and a low reading is not simply the result of inconsistencies in the touchscreen reading. To stop the machine from simply running up the strikes while the ball is in the red, the code ensures that a "strike" cannot be assigned until a "ball" is assigned before hand.

For each strike, a corresponding LED is lit up, and upon the last strike, the buzz function is set off.

         //Check to see if the Ball is outide the allowed range of values to be "in the black" of the touchscreen
         if ((yread< 90) | (yread >150) | (xread <90) | (xread > 150)) {
            counter++;
            if ((counter==3) && (strike==ball)) {
               strike++;         //If ball is "in the red" for a sufficiently long time, a strike is added.
            }
         }       
         else {
            counter--;
            //Check to see if the ball has returned from being "in the red."
            if ((counter==-1)  && (strike>ball)) {
               ball++;
            }
         }
        
         if ((counter==3) | (counter == -2)) counter=0;


        
        
         if (strike==2) {
            output_high(PIN_A3);
         }
         if (strike==3) {
            output_high(PIN_A4);
         }
         if (strike==4) {
            output_high(PIN_A5);
         }
         if (strike==5) {
            output_high(PIN_E2);
            set_pwm1_duty(39);
            set_pwm2_duty(39);
            buzz();
         }



The PIC reads the joystick and quantizes the input from the analog 0V to 5V to the 8 bit quantized 0 to 255. It then scales that number to be between 0 and 78, the PWM extremes.

         //Joystick Control
         if (input(PIN_C7) != 0) {  // toggle switch that switches between Joystick control system and Touchscreen control system
            set_adc_channel(2);     // there's only one ADC so select which input to connect to it; here pin AN1
            delay_us(10);           // wait 10uS for ADC to settle to a newly selected input
            read1 = read_adc();                       //x-axis
            if (read1<145 && read1>115) read1pwm=39;
            else read1pwm=(read1*.3);
            set_pwm1_duty(read1pwm);
           
            set_adc_channel(1);     // there's only one ADC so select which input to connect to it; here pin AN2
            delay_us(10);           // wait 10uS for ADC to settle to a newly selected input
            read2 = read_adc();                      //y-axis
            if (read2<145 && read2>115) read2pwm=39;
            else read2pwm=(read2*.3);
            set_pwm2_duty(read2pwm);
         }
         else {
            output_d(0);
            set_pwm1_duty(39);
            set_pwm2_duty(39);
         }
      }
   }
}

The buzz() function resets the counting variables to zero and sets the buzzer off by oscillating a PIC output into a basic buzzer speaker at an audible frequency.

void buzz() {

   coin=0;
   strike=0;
   ball=0;
   counter = 0;
   output_low(PIN_A3);
   output_low(PIN_A4);  
   output_low(PIN_A5);
   output_low(PIN_E2);
   output_low(PIN_E0);

  
   while (input(PIN_C6)==0) {
      output_high(PIN_C4);
      delay_us(100);
      output_low(PIN_C4);
      delay_us(100);
   }
}

Results

VibratoryClock.jpg

We were able to get our project working so that the plate moves an object placed anywhere on the plate to the correct time and then moves the objects around the clock face, acting as the hour "hand" of a clock. However, we increased the speed so that it takes about 2 minutes for objects placed on the clock to move around so that it is easy to demonstrate. Check out the working Vibratory Clock here.

Reflections

Useful Resources

Vibration-Induced Frictional Force Fields for Part Manipulation

Universal Planar Manipulator