Pong for Gamepad2

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Gamepad.cpp Source File

Gamepad.cpp

00001 #include "Gamepad.h"
00002 
00003 #include "mbed.h"
00004 
00005 //////////// constructor/destructor ////////////
00006 Gamepad::Gamepad()
00007     :
00008     _led1(new PwmOut(PTA2)),
00009     _led2(new PwmOut(PTC2)),
00010     _led3(new PwmOut(PTC3)),
00011     _led4(new PwmOut(PTA1)),
00012     _led5(new PwmOut(PTC10)),
00013     _led6(new PwmOut(PTC11)),
00014 
00015     _button_A(new InterruptIn(PTC7)),
00016     _button_B(new InterruptIn(PTC9)),
00017     _button_X(new InterruptIn(PTC5)),
00018     _button_Y(new InterruptIn(PTC0)),
00019     _button_start(new InterruptIn(PTC8)),
00020 
00021     _vert(new AnalogIn(PTB11)),
00022     _horiz(new AnalogIn(PTB10)),
00023 
00024     _pot1(new AnalogIn(PTB2)),
00025     _pot2(new AnalogIn(PTB3)),
00026 
00027     dac(new AnalogOut(DAC0_OUT)),
00028     ticker(new Ticker),
00029     timeout(new Timeout),
00030     note_timeout(new Timeout),
00031 
00032     _x0(0),
00033     _y0(0)
00034 {}
00035 
00036 
00037 ///////////////// public methods /////////////////
00038 
00039 void Gamepad::init()
00040 {
00041     leds_off();
00042 
00043     // read centred values of joystick
00044     _x0 = _horiz->read();
00045     _y0 = _vert->read();
00046 
00047     // Set all buttons to PullUp
00048     _button_A->mode(PullUp);
00049     _button_B->mode(PullUp);
00050     _button_X->mode(PullUp);
00051     _button_Y->mode(PullUp);
00052     _button_start->mode(PullUp);
00053 
00054     // Set up interrupts for the fall of buttons
00055     _button_A->fall(callback(this,&Gamepad::A_fall_interrupt));
00056     _button_B->fall(callback(this,&Gamepad::B_fall_interrupt));
00057     _button_X->fall(callback(this,&Gamepad::X_fall_interrupt));
00058     _button_Y->fall(callback(this,&Gamepad::Y_fall_interrupt));
00059     _button_start->fall(callback(this,&Gamepad::start_fall_interrupt));
00060 
00061     // initalise button flags
00062     reset_buttons();
00063 
00064     // number of samples
00065     _n = 16;
00066     _sample_array = new float[_n];
00067 
00068     // create sample array for one period between 0.0 and 1.0
00069     for (int i = 0; i < _n ; i++) {
00070         _sample_array[i] = 0.5f + 0.5f*sin(i*2*PI/_n);
00071         //printf("y[%i] = %f\n",i,_sample_array[i]);
00072     }
00073 
00074 }
00075 
00076 void Gamepad::leds_off()
00077 {
00078     leds(0.0);
00079 }
00080 
00081 void Gamepad::leds_on()
00082 {
00083     leds(1.0);
00084 }
00085 
00086 void Gamepad::leds(float val) const
00087 {
00088     if (val < 0.0f) {
00089         val = 0.0f;
00090     }
00091     if (val > 1.0f) {
00092         val = 1.0f;
00093     }
00094 
00095     // leds are active-low, so subtract from 1.0
00096     // 0.0 corresponds to fully-off, 1.0 to fully-on
00097     val = 1.0f - val;
00098 
00099     _led1->write(val);
00100     _led2->write(val);
00101     _led3->write(val);
00102     _led4->write(val);
00103     _led5->write(val);
00104     _led6->write(val);
00105 }
00106 
00107 void Gamepad::led(int n,float val) const
00108 {
00109     // ensure they are within valid range
00110     if (val < 0.0f) {
00111         val = 0.0f;
00112     }
00113     if (val > 1.0f) {
00114         val = 1.0f;
00115     }
00116 
00117     switch (n) {
00118 
00119         // check for valid LED number and set value
00120 
00121         case 1:
00122             _led1->write(1.0f-val);   // active-low so subtract from 1
00123             break;
00124         case 2:
00125             _led2->write(1.0f-val);   // active-low so subtract from 1
00126             break;
00127         case 3:
00128             _led3->write(1.0f-val);   // active-low so subtract from 1
00129             break;
00130         case 4:
00131             _led4->write(1.0f-val);   // active-low so subtract from 1
00132             break;
00133         case 5:
00134             _led5->write(1.0f-val);   // active-low so subtract from 1
00135             break;
00136         case 6:
00137             _led6->write(1.0f-val);   // active-low so subtract from 1
00138             break;
00139 
00140     }
00141 }
00142 
00143 float Gamepad::read_pot1() const
00144 {
00145     return _pot1->read();
00146 }
00147 
00148 float Gamepad::read_pot2() const
00149 {
00150     return _pot2->read();
00151 }
00152 
00153 
00154 // this method gets the magnitude of the joystick movement
00155 float Gamepad::get_mag()
00156 {
00157     Polar p = get_polar();
00158     return p.mag;
00159 }
00160 
00161 // this method gets the angle of joystick movement (0 to 360, 0 North)
00162 float Gamepad::get_angle()
00163 {
00164     Polar p = get_polar();
00165     return p.angle;
00166 }
00167 
00168 Direction Gamepad::get_direction()
00169 {
00170     float angle = get_angle();  // 0 to 360, -1 for centred
00171 
00172     Direction d;
00173     // partition 360 into segments and check which segment the angle is in
00174     if (angle < 0.0f) {
00175         d = CENTRE;   // check for -1.0 angle
00176     } else if (angle < 22.5f) {  // then keep going in 45 degree increments
00177         d = N;
00178     } else if (angle < 67.5f) {
00179         d = NE;
00180     } else if (angle < 112.5f) {
00181         d = E;
00182     } else if (angle < 157.5f) {
00183         d = SE;
00184     } else if (angle < 202.5f) {
00185         d = S;
00186     } else if (angle < 247.5f) {
00187         d = SW;
00188     } else if (angle < 292.5f) {
00189         d = W;
00190     } else if (angle < 337.5f) {
00191         d = NW;
00192     } else {
00193         d = N;
00194     }
00195 
00196     return d;
00197 }
00198 
00199 void Gamepad::reset_buttons()
00200 {
00201     A_fall = B_fall = X_fall = Y_fall = start_fall = false;
00202 }
00203 
00204 bool Gamepad::A_pressed()
00205 {
00206     if (A_fall) {
00207         A_fall = false;
00208         return true;
00209     } else {
00210         return false;
00211     }
00212 }
00213 
00214 bool Gamepad::B_pressed()
00215 {
00216     if (B_fall) {
00217         B_fall = false;
00218         return true;
00219     } else {
00220         return false;
00221     }
00222 }
00223 
00224 bool Gamepad::X_pressed()
00225 {
00226     if (X_fall) {
00227         X_fall = false;
00228         return true;
00229     } else {
00230         return false;
00231     }
00232 }
00233 
00234 bool Gamepad::Y_pressed()
00235 {
00236     if (Y_fall) {
00237         Y_fall = false;
00238         return true;
00239     } else {
00240         return false;
00241     }
00242 }
00243 
00244 bool Gamepad::start_pressed()
00245 {
00246     if (start_fall) {
00247         start_fall = false;
00248         return true;
00249     } else {
00250         return false;
00251     }
00252 }
00253 
00254 bool Gamepad::A_held()
00255 {
00256     // Buttons are configured as PullUp hence the not
00257     return !_button_A->read();
00258 }
00259 
00260 bool Gamepad::B_held()
00261 {
00262     return !_button_B->read();
00263 }
00264 
00265 bool Gamepad::X_held()
00266 {
00267     return !_button_X->read();
00268 }
00269 
00270 bool Gamepad::Y_held()
00271 {
00272     return !_button_Y->read();
00273 }
00274 
00275 bool Gamepad::start_held()
00276 {
00277     return !_button_start->read();
00278 }
00279 
00280 ///////////////////// private methods ////////////////////////
00281 
00282 // get raw joystick coordinate in range -1 to 1
00283 // Direction (x,y)
00284 // North     (0,1)
00285 // East      (1,0)
00286 // South     (0,-1)
00287 // West      (-1,0)
00288 Vector2D Gamepad::get_coord()
00289 {
00290     // read() returns value in range 0.0 to 1.0 so is scaled and centre value
00291     // substracted to get values in the range -1.0 to 1.0
00292     float x = 2.0f*( _horiz->read() - _x0 );
00293     float y = 2.0f*( _vert->read()  - _y0 );
00294 
00295     // Note: the y value here is inverted to ensure the positive y is up
00296 
00297     Vector2D coord = {x,-y};
00298     return coord;
00299 }
00300 
00301 // This maps the raw x,y coord onto a circular grid.
00302 // See:  http://mathproofs.blogspot.co.uk/2005/07/mapping-square-to-circle.html
00303 Vector2D Gamepad::get_mapped_coord()
00304 {
00305     Vector2D coord = get_coord();
00306 
00307     // do the transformation
00308     float x = coord.x*sqrt(1.0f-pow(coord.y,2.0f)/2.0f);
00309     float y = coord.y*sqrt(1.0f-pow(coord.x,2.0f)/2.0f);
00310 
00311     Vector2D mapped_coord = {x,y};
00312     return mapped_coord;
00313 }
00314 
00315 // this function converts the mapped coordinates into polar form
00316 Polar Gamepad::get_polar()
00317 {
00318     // get the mapped coordinate
00319     Vector2D coord = get_mapped_coord();
00320 
00321     // at this point, 0 degrees (i.e. x-axis) will be defined to the East.
00322     // We want 0 degrees to correspond to North and increase clockwise to 359
00323     // like a compass heading, so we need to swap the axis and invert y
00324     float x = coord.y;
00325     float y = coord.x;
00326 
00327     float mag = sqrt(x*x+y*y);  // pythagoras
00328     float angle = RAD2DEG*atan2(y,x);
00329     // angle will be in range -180 to 180, so add 360 to negative angles to
00330     // move to 0 to 360 range
00331     if (angle < 0.0f) {
00332         angle+=360.0f;
00333     }
00334 
00335     // the noise on the ADC causes the values of x and y to fluctuate slightly
00336     // around the centred values. This causes the random angle values to get
00337     // calculated when the joystick is centred and untouched. This is also when
00338     // the magnitude is very small, so we can check for a small magnitude and then
00339     // set the angle to -1. This will inform us when the angle is invalid and the
00340     // joystick is centred
00341 
00342     if (mag < TOL) {
00343         mag = 0.0f;
00344         angle = -1.0f;
00345     }
00346 
00347     Polar p = {mag,angle};
00348     return p;
00349 }
00350 
00351 // ISRs for buttons
00352 void Gamepad::A_fall_interrupt()
00353 {
00354     A_fall = true;
00355 }
00356 void Gamepad::B_fall_interrupt()
00357 {
00358     B_fall = true;
00359 }
00360 void Gamepad::X_fall_interrupt()
00361 {
00362     X_fall = true;
00363 }
00364 void Gamepad::Y_fall_interrupt()
00365 {
00366     Y_fall = true;
00367 }
00368 void Gamepad::start_fall_interrupt()
00369 {
00370     start_fall = true;
00371 }
00372 
00373 void Gamepad::set_bpm(float bpm)
00374 {
00375     _bpm = bpm;
00376 }
00377 
00378 void Gamepad::tone(float frequency,float duration)
00379 {
00380     // calculate time step between samples
00381     float dt = 1.0f/(frequency*_n);
00382     // start from beginning of LUT
00383     _sample = 0;
00384 
00385     // setup ticker and timeout to stop ticker
00386 
00387     // the ticker repeats every dt to plat each sample in turn
00388     ticker->attach(callback(this, &Gamepad::ticker_isr), dt);
00389     // the timeout stops the ticker after the required duration
00390     timeout->attach(callback(this, &Gamepad::timeout_isr), duration );
00391 }
00392 
00393 void Gamepad::play_melody(int length,const int *notes,const int *durations,float bpm,bool repeat)
00394 {
00395     // copy arguments to member variables
00396     _bpm = bpm;
00397     _notes = notes;  // pointer for array
00398     _durations = durations;  // pointer for array
00399     _melody_length = length;
00400     _repeat = repeat;
00401 
00402     _note = 0;  // start from first note
00403 
00404     play_next_note(); // play the next note in the melody
00405 }
00406 
00407 void Gamepad::write_dac(float val)
00408 {
00409     if (val < 0.0f) {
00410         val = 0.0f;
00411     } else if (val > 1.0f) {
00412         val = 1.0f;
00413     }
00414     dac->write(val);
00415 }
00416 
00417 
00418 void Gamepad::play_next_note()
00419 {
00420     // _note is the note index to play
00421 
00422     // calculate the duration and frequency of the note
00423     float duration = 60.0f/(_bpm*_durations[_note]);
00424     float frequency = float(_notes[_note]);
00425     //printf("[%i] f = %f d = %f\n",_note,frequency,duration);
00426 
00427     // check if the note is not a space and if not then play the note
00428     if (frequency > 0) {
00429         tone(frequency,duration);
00430     }
00431 
00432     // the timeout goes to the next note in the melody
00433     // double the duration to leave a bit of a gap in between notes to be better
00434     // able to distinguish them
00435     note_timeout->attach(callback(this, &Gamepad::note_timeout_isr), duration*2.0f );
00436 }
00437 
00438 // called when the next note needs playing
00439 void Gamepad::note_timeout_isr()
00440 {
00441     _note++; // go onto next note
00442 
00443     // if in repeat mode then reset the note counter when get to end of melody
00444     if (_repeat && _note == _melody_length) {
00445         _note=0;
00446     }
00447 
00448     // check if note is within the melody
00449     if (_note < _melody_length) {
00450         play_next_note();
00451     }
00452 }
00453 
00454 void Gamepad::ticker_isr()
00455 {
00456     dac->write(_sample_array[_sample%_n]);  // use modulo to get index to play
00457     _sample++;  // increment the sample ready for next time
00458 }
00459 
00460 void Gamepad::timeout_isr()
00461 {
00462     // stops the ticker to end the note
00463     ticker->detach();
00464 }