Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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 Mon Jul 25 2022 00:33:51 by
