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 // 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 }
Generated on Wed Jul 13 2022 01:33:24 by
1.7.2