Craig Evans
/
Pong2
Pong for Gamepad2
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Sat Jul 16 2022 18:01:16 by 1.7.2