Mohamed Moawya / SnakeGame

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