Class used to interface with the handheld gamepad.

Fork of Gamepad by Craig Evans

Committer:
valavanisalex
Date:
Fri Mar 03 12:51:13 2017 +0000
Revision:
13:ef5fc9f58805
Parent:
12:1b0b6355da4f
Child:
14:688bdcbdd3a5
Use forward declaration of mbed classes so that we can avoid pulling the whole of the mbed header into our own API

Who changed what in which revision?

UserRevisionLine numberNew contents of line
eencae 0:a6288c29b936 1 #include "Gamepad.h"
eencae 0:a6288c29b936 2
valavanisalex 13:ef5fc9f58805 3 #include "mbed.h"
valavanisalex 13:ef5fc9f58805 4
eencae 1:6d25cd49059b 5 //////////// constructor/destructor ////////////
eencae 0:a6288c29b936 6 Gamepad::Gamepad()
eencae 0:a6288c29b936 7 {
eencae 10:a13d2f9d8a14 8 _led1 = new PwmOut(PTA1);
eencae 10:a13d2f9d8a14 9 _led2 = new PwmOut(PTA2);
eencae 10:a13d2f9d8a14 10 _led3 = new PwmOut(PTC2);
eencae 10:a13d2f9d8a14 11 _led4 = new PwmOut(PTC3);
eencae 10:a13d2f9d8a14 12 _led5 = new PwmOut(PTC4);
eencae 10:a13d2f9d8a14 13 _led6 = new PwmOut(PTD3);
eencae 0:a6288c29b936 14
valavanisalex 11:ff86b2ffce01 15 _button_A = new InterruptIn(PTB9);
valavanisalex 11:ff86b2ffce01 16 _button_B = new InterruptIn(PTD0);
valavanisalex 11:ff86b2ffce01 17 _button_X = new InterruptIn(PTC17);
valavanisalex 11:ff86b2ffce01 18 _button_Y = new InterruptIn(PTC12);
valavanisalex 11:ff86b2ffce01 19 _button_back = new InterruptIn(PTB19);
valavanisalex 11:ff86b2ffce01 20 _button_start = new InterruptIn(PTC5);
valavanisalex 11:ff86b2ffce01 21 _button_L = new InterruptIn(PTB18);
valavanisalex 11:ff86b2ffce01 22 _button_R = new InterruptIn(PTB3);
valavanisalex 11:ff86b2ffce01 23 _button_joystick = new InterruptIn(PTC16);
eencae 3:964a6d95acdd 24
valavanisalex 11:ff86b2ffce01 25 _vert = new AnalogIn(PTB10);
valavanisalex 11:ff86b2ffce01 26 _horiz = new AnalogIn(PTB11);
eencae 0:a6288c29b936 27
valavanisalex 11:ff86b2ffce01 28 _buzzer = new PwmOut(PTC10);
valavanisalex 11:ff86b2ffce01 29 _pot = new AnalogIn(PTB2);
eencae 0:a6288c29b936 30
valavanisalex 11:ff86b2ffce01 31 _timeout = new Timeout();
eencae 1:6d25cd49059b 32
eencae 0:a6288c29b936 33 }
eencae 0:a6288c29b936 34
eencae 1:6d25cd49059b 35 Gamepad::~Gamepad()
eencae 1:6d25cd49059b 36 {
eencae 10:a13d2f9d8a14 37 delete _led1,_led2,_led3,_led4,_led5,_led6;
valavanisalex 11:ff86b2ffce01 38 delete _button_A,_button_B,_button_joystick,_vert,_horiz;
valavanisalex 11:ff86b2ffce01 39 delete _button_X,_button_Y,_button_back,_button_start;
valavanisalex 11:ff86b2ffce01 40 delete _button_L,_button_R, _buzzer, _pot, _timeout;
eencae 1:6d25cd49059b 41 }
eencae 1:6d25cd49059b 42
eencae 1:6d25cd49059b 43 ///////////////// public methods /////////////////
eencae 1:6d25cd49059b 44
eencae 0:a6288c29b936 45 void Gamepad::init()
eencae 0:a6288c29b936 46 {
eencae 1:6d25cd49059b 47 leds_off();
eencae 7:019671f7bd83 48 init_buttons();
eencae 3:964a6d95acdd 49
eencae 3:964a6d95acdd 50 // read centred values of joystick
valavanisalex 11:ff86b2ffce01 51 _x0 = _horiz->read();
valavanisalex 11:ff86b2ffce01 52 _y0 = _vert->read();
eencae 1:6d25cd49059b 53
eencae 1:6d25cd49059b 54 // clear all flags
valavanisalex 12:1b0b6355da4f 55 _event_state = 0;
eencae 0:a6288c29b936 56 }
eencae 0:a6288c29b936 57
eencae 0:a6288c29b936 58 void Gamepad::leds_off()
eencae 0:a6288c29b936 59 {
eencae 10:a13d2f9d8a14 60 leds(0.0);
eencae 0:a6288c29b936 61 }
eencae 0:a6288c29b936 62
eencae 0:a6288c29b936 63 void Gamepad::leds_on()
eencae 0:a6288c29b936 64 {
eencae 10:a13d2f9d8a14 65 leds(1.0);
eencae 1:6d25cd49059b 66 }
eencae 1:6d25cd49059b 67
valavanisalex 11:ff86b2ffce01 68 void Gamepad::leds(float val) const
eencae 1:6d25cd49059b 69 {
eencae 1:6d25cd49059b 70 if (val < 0.0f) {
eencae 1:6d25cd49059b 71 val = 0.0f;
eencae 1:6d25cd49059b 72 }
eencae 1:6d25cd49059b 73 if (val > 1.0f) {
eencae 1:6d25cd49059b 74 val = 1.0f;
eencae 1:6d25cd49059b 75 }
eencae 1:6d25cd49059b 76
eencae 1:6d25cd49059b 77 // leds are active-low, so subtract from 1.0
eencae 1:6d25cd49059b 78 // 0.0 corresponds to fully-off, 1.0 to fully-on
eencae 1:6d25cd49059b 79 val = 1.0f - val;
eencae 1:6d25cd49059b 80
eencae 10:a13d2f9d8a14 81 _led1->write(val);
eencae 10:a13d2f9d8a14 82 _led2->write(val);
eencae 10:a13d2f9d8a14 83 _led3->write(val);
eencae 10:a13d2f9d8a14 84 _led4->write(val);
eencae 10:a13d2f9d8a14 85 _led5->write(val);
eencae 10:a13d2f9d8a14 86 _led6->write(val);
eencae 9:893189072e89 87 }
eencae 9:893189072e89 88
eencae 10:a13d2f9d8a14 89 void Gamepad::led1(float val) {
eencae 10:a13d2f9d8a14 90 _led1->write(1.0f-val); // active-low so subtract from 1
eencae 9:893189072e89 91 }
eencae 10:a13d2f9d8a14 92 void Gamepad::led2(float val) {
eencae 10:a13d2f9d8a14 93 _led2->write(1.0f-val); // active-low so subtract from 1
eencae 9:893189072e89 94 }
eencae 10:a13d2f9d8a14 95 void Gamepad::led3(float val) {
eencae 10:a13d2f9d8a14 96 _led3->write(1.0f-val); // active-low so subtract from 1
eencae 9:893189072e89 97 }
eencae 10:a13d2f9d8a14 98 void Gamepad::led4(float val) {
eencae 10:a13d2f9d8a14 99 _led4->write(1.0f-val); // active-low so subtract from 1
eencae 9:893189072e89 100 }
eencae 10:a13d2f9d8a14 101 void Gamepad::led5(float val) {
eencae 10:a13d2f9d8a14 102 _led5->write(1.0f-val); // active-low so subtract from 1
eencae 9:893189072e89 103 }
eencae 10:a13d2f9d8a14 104 void Gamepad::led6(float val) {
eencae 10:a13d2f9d8a14 105 _led6->write(1.0f-val); // active-low so subtract from 1
eencae 1:6d25cd49059b 106 }
eencae 1:6d25cd49059b 107
valavanisalex 11:ff86b2ffce01 108 float Gamepad::read_pot() const
eencae 1:6d25cd49059b 109 {
valavanisalex 11:ff86b2ffce01 110 return _pot->read();
eencae 1:6d25cd49059b 111 }
eencae 1:6d25cd49059b 112
eencae 1:6d25cd49059b 113 void Gamepad::tone(float frequency, float duration)
eencae 1:6d25cd49059b 114 {
valavanisalex 11:ff86b2ffce01 115 _buzzer->period(1.0f/frequency);
valavanisalex 11:ff86b2ffce01 116 _buzzer->write(0.5); // 50% duty cycle - square wave
valavanisalex 11:ff86b2ffce01 117 _timeout->attach(callback(this, &Gamepad::tone_off), duration );
eencae 1:6d25cd49059b 118 }
eencae 1:6d25cd49059b 119
valavanisalex 12:1b0b6355da4f 120 bool Gamepad::check_event(GamepadEvent const id)
eencae 1:6d25cd49059b 121 {
valavanisalex 12:1b0b6355da4f 122 // Check whether event flag is set
valavanisalex 12:1b0b6355da4f 123 if (_event_state[id]) {
valavanisalex 12:1b0b6355da4f 124 _event_state.reset(id); // clear flag
eencae 3:964a6d95acdd 125 return true;
eencae 3:964a6d95acdd 126 } else {
eencae 3:964a6d95acdd 127 return false;
eencae 3:964a6d95acdd 128 }
eencae 3:964a6d95acdd 129 }
eencae 3:964a6d95acdd 130
eencae 3:964a6d95acdd 131 // this method gets the magnitude of the joystick movement
eencae 3:964a6d95acdd 132 float Gamepad::get_mag()
eencae 3:964a6d95acdd 133 {
eencae 3:964a6d95acdd 134 Polar p = get_polar();
eencae 3:964a6d95acdd 135 return p.mag;
eencae 3:964a6d95acdd 136 }
eencae 3:964a6d95acdd 137
eencae 3:964a6d95acdd 138 // this method gets the angle of joystick movement (0 to 360, 0 North)
eencae 3:964a6d95acdd 139 float Gamepad::get_angle()
eencae 3:964a6d95acdd 140 {
eencae 3:964a6d95acdd 141 Polar p = get_polar();
eencae 3:964a6d95acdd 142 return p.angle;
eencae 3:964a6d95acdd 143 }
eencae 3:964a6d95acdd 144
eencae 4:bafb7f483e93 145 Direction Gamepad::get_direction()
eencae 3:964a6d95acdd 146 {
eencae 3:964a6d95acdd 147 float angle = get_angle(); // 0 to 360, -1 for centred
eencae 3:964a6d95acdd 148
eencae 3:964a6d95acdd 149 Direction d;
eencae 3:964a6d95acdd 150 // partition 360 into segments and check which segment the angle is in
eencae 3:964a6d95acdd 151 if (angle < 0.0f) {
eencae 3:964a6d95acdd 152 d = CENTRE; // check for -1.0 angle
eencae 3:964a6d95acdd 153 } else if (angle < 22.5f) { // then keep going in 45 degree increments
eencae 3:964a6d95acdd 154 d = N;
eencae 3:964a6d95acdd 155 } else if (angle < 67.5f) {
eencae 3:964a6d95acdd 156 d = NE;
eencae 3:964a6d95acdd 157 } else if (angle < 112.5f) {
eencae 3:964a6d95acdd 158 d = E;
eencae 3:964a6d95acdd 159 } else if (angle < 157.5f) {
eencae 3:964a6d95acdd 160 d = SE;
eencae 3:964a6d95acdd 161 } else if (angle < 202.5f) {
eencae 3:964a6d95acdd 162 d = S;
eencae 3:964a6d95acdd 163 } else if (angle < 247.5f) {
eencae 3:964a6d95acdd 164 d = SW;
eencae 3:964a6d95acdd 165 } else if (angle < 292.5f) {
eencae 3:964a6d95acdd 166 d = W;
eencae 3:964a6d95acdd 167 } else if (angle < 337.5f) {
eencae 3:964a6d95acdd 168 d = NW;
eencae 3:964a6d95acdd 169 } else {
eencae 3:964a6d95acdd 170 d = N;
eencae 3:964a6d95acdd 171 }
eencae 3:964a6d95acdd 172
eencae 3:964a6d95acdd 173 return d;
eencae 3:964a6d95acdd 174 }
eencae 3:964a6d95acdd 175
eencae 1:6d25cd49059b 176 ///////////////////// private methods ////////////////////////
eencae 1:6d25cd49059b 177
eencae 1:6d25cd49059b 178 void Gamepad::tone_off()
eencae 1:6d25cd49059b 179 {
valavanisalex 11:ff86b2ffce01 180 _buzzer->write(0.0);
eencae 1:6d25cd49059b 181 }
eencae 1:6d25cd49059b 182
eencae 1:6d25cd49059b 183 void Gamepad::init_buttons()
eencae 1:6d25cd49059b 184 {
eencae 1:6d25cd49059b 185 // turn on pull-downs as other side of button is connected to 3V3
eencae 1:6d25cd49059b 186 // button is 0 when not pressed and 1 when pressed
valavanisalex 11:ff86b2ffce01 187 _button_A->mode(PullDown);
valavanisalex 11:ff86b2ffce01 188 _button_B->mode(PullDown);
valavanisalex 11:ff86b2ffce01 189 _button_X->mode(PullDown);
valavanisalex 11:ff86b2ffce01 190 _button_Y->mode(PullDown);
valavanisalex 11:ff86b2ffce01 191 _button_back->mode(PullDown);
valavanisalex 11:ff86b2ffce01 192 _button_start->mode(PullDown);
valavanisalex 11:ff86b2ffce01 193 _button_L->mode(PullDown);
valavanisalex 11:ff86b2ffce01 194 _button_R->mode(PullDown);
valavanisalex 11:ff86b2ffce01 195 _button_joystick->mode(PullDown);
eencae 1:6d25cd49059b 196 // therefore setup rising edge interrupts
valavanisalex 11:ff86b2ffce01 197 _button_A->rise(callback(this,&Gamepad::a_isr));
valavanisalex 11:ff86b2ffce01 198 _button_B->rise(callback(this,&Gamepad::b_isr));
valavanisalex 11:ff86b2ffce01 199 _button_X->rise(callback(this,&Gamepad::x_isr));
valavanisalex 11:ff86b2ffce01 200 _button_Y->rise(callback(this,&Gamepad::y_isr));
valavanisalex 11:ff86b2ffce01 201 _button_L->rise(callback(this,&Gamepad::l_isr));
valavanisalex 11:ff86b2ffce01 202 _button_R->rise(callback(this,&Gamepad::r_isr));
valavanisalex 11:ff86b2ffce01 203 _button_start->rise(callback(this,&Gamepad::start_isr));
valavanisalex 11:ff86b2ffce01 204 _button_back->rise(callback(this,&Gamepad::back_isr));
valavanisalex 11:ff86b2ffce01 205 _button_joystick->rise(callback(this,&Gamepad::joy_isr));
eencae 1:6d25cd49059b 206 }
eencae 1:6d25cd49059b 207
eencae 1:6d25cd49059b 208 // button interrupts ISRs
valavanisalex 12:1b0b6355da4f 209 // Each of these simply sets the appropriate event bit in the _event_state
valavanisalex 12:1b0b6355da4f 210 // variable
eencae 1:6d25cd49059b 211 void Gamepad::a_isr()
eencae 1:6d25cd49059b 212 {
valavanisalex 12:1b0b6355da4f 213 _event_state.set(EVENT_A_PRESSED);
eencae 1:6d25cd49059b 214 }
eencae 1:6d25cd49059b 215 void Gamepad::b_isr()
eencae 1:6d25cd49059b 216 {
valavanisalex 12:1b0b6355da4f 217 _event_state.set(EVENT_B_PRESSED);
eencae 1:6d25cd49059b 218 }
eencae 1:6d25cd49059b 219 void Gamepad::x_isr()
eencae 1:6d25cd49059b 220 {
valavanisalex 12:1b0b6355da4f 221 _event_state.set(EVENT_X_PRESSED);
eencae 1:6d25cd49059b 222 }
eencae 1:6d25cd49059b 223 void Gamepad::y_isr()
eencae 1:6d25cd49059b 224 {
valavanisalex 12:1b0b6355da4f 225 _event_state.set(EVENT_Y_PRESSED);
eencae 1:6d25cd49059b 226 }
eencae 1:6d25cd49059b 227 void Gamepad::l_isr()
eencae 1:6d25cd49059b 228 {
valavanisalex 12:1b0b6355da4f 229 _event_state.set(EVENT_L_PRESSED);
eencae 1:6d25cd49059b 230 }
eencae 1:6d25cd49059b 231 void Gamepad::r_isr()
eencae 1:6d25cd49059b 232 {
valavanisalex 12:1b0b6355da4f 233 _event_state.set(EVENT_R_PRESSED);
eencae 1:6d25cd49059b 234 }
eencae 1:6d25cd49059b 235 void Gamepad::back_isr()
eencae 1:6d25cd49059b 236 {
valavanisalex 12:1b0b6355da4f 237 _event_state.set(EVENT_BACK_PRESSED);
eencae 1:6d25cd49059b 238 }
eencae 1:6d25cd49059b 239 void Gamepad::start_isr()
eencae 1:6d25cd49059b 240 {
valavanisalex 12:1b0b6355da4f 241 _event_state.set(EVENT_START_PRESSED);
eencae 3:964a6d95acdd 242 }
eencae 3:964a6d95acdd 243 void Gamepad::joy_isr()
eencae 3:964a6d95acdd 244 {
valavanisalex 12:1b0b6355da4f 245 _event_state.set(EVENT_JOY_PRESSED);
eencae 3:964a6d95acdd 246 }
eencae 3:964a6d95acdd 247
eencae 3:964a6d95acdd 248 // get raw joystick coordinate in range -1 to 1
eencae 3:964a6d95acdd 249 // Direction (x,y)
eencae 3:964a6d95acdd 250 // North (0,1)
eencae 3:964a6d95acdd 251 // East (1,0)
eencae 3:964a6d95acdd 252 // South (0,-1)
eencae 3:964a6d95acdd 253 // West (-1,0)
eencae 3:964a6d95acdd 254 Vector2D Gamepad::get_coord()
eencae 3:964a6d95acdd 255 {
eencae 3:964a6d95acdd 256 // read() returns value in range 0.0 to 1.0 so is scaled and centre value
eencae 3:964a6d95acdd 257 // substracted to get values in the range -1.0 to 1.0
valavanisalex 11:ff86b2ffce01 258 float x = 2.0f*( _horiz->read() - _x0 );
valavanisalex 11:ff86b2ffce01 259 float y = 2.0f*( _vert->read() - _y0 );
eencae 3:964a6d95acdd 260
eencae 3:964a6d95acdd 261 // Note: the x value here is inverted to ensure the positive x is to the
eencae 3:964a6d95acdd 262 // right. This is simply due to how the potentiometer on the joystick
eencae 3:964a6d95acdd 263 // I was using was connected up. It could have been corrected in hardware
eencae 3:964a6d95acdd 264 // by swapping the power supply pins. Instead it is done in software so may
eencae 3:964a6d95acdd 265 // need to be changed depending on your wiring setup
eencae 3:964a6d95acdd 266
eencae 3:964a6d95acdd 267 Vector2D coord = {-x,y};
eencae 3:964a6d95acdd 268 return coord;
eencae 3:964a6d95acdd 269 }
eencae 3:964a6d95acdd 270
eencae 3:964a6d95acdd 271 // This maps the raw x,y coord onto a circular grid.
eencae 3:964a6d95acdd 272 // See: http://mathproofs.blogspot.co.uk/2005/07/mapping-square-to-circle.html
eencae 3:964a6d95acdd 273 Vector2D Gamepad::get_mapped_coord()
eencae 3:964a6d95acdd 274 {
eencae 3:964a6d95acdd 275 Vector2D coord = get_coord();
eencae 3:964a6d95acdd 276
eencae 3:964a6d95acdd 277 // do the transformation
eencae 3:964a6d95acdd 278 float x = coord.x*sqrt(1.0f-pow(coord.y,2.0f)/2.0f);
eencae 3:964a6d95acdd 279 float y = coord.y*sqrt(1.0f-pow(coord.x,2.0f)/2.0f);
eencae 3:964a6d95acdd 280
eencae 3:964a6d95acdd 281 Vector2D mapped_coord = {x,y};
eencae 3:964a6d95acdd 282 return mapped_coord;
eencae 3:964a6d95acdd 283 }
eencae 3:964a6d95acdd 284
eencae 3:964a6d95acdd 285 // this function converts the mapped coordinates into polar form
eencae 3:964a6d95acdd 286 Polar Gamepad::get_polar()
eencae 3:964a6d95acdd 287 {
eencae 3:964a6d95acdd 288 // get the mapped coordinate
eencae 3:964a6d95acdd 289 Vector2D coord = get_mapped_coord();
eencae 3:964a6d95acdd 290
eencae 3:964a6d95acdd 291 // at this point, 0 degrees (i.e. x-axis) will be defined to the East.
eencae 3:964a6d95acdd 292 // We want 0 degrees to correspond to North and increase clockwise to 359
eencae 3:964a6d95acdd 293 // like a compass heading, so we need to swap the axis and invert y
eencae 3:964a6d95acdd 294 float x = coord.y;
eencae 3:964a6d95acdd 295 float y = coord.x;
eencae 3:964a6d95acdd 296
eencae 3:964a6d95acdd 297 float mag = sqrt(x*x+y*y); // pythagoras
eencae 3:964a6d95acdd 298 float angle = RAD2DEG*atan2(y,x);
eencae 3:964a6d95acdd 299 // angle will be in range -180 to 180, so add 360 to negative angles to
eencae 3:964a6d95acdd 300 // move to 0 to 360 range
eencae 3:964a6d95acdd 301 if (angle < 0.0f) {
eencae 3:964a6d95acdd 302 angle+=360.0f;
eencae 3:964a6d95acdd 303 }
eencae 3:964a6d95acdd 304
eencae 3:964a6d95acdd 305 // the noise on the ADC causes the values of x and y to fluctuate slightly
eencae 3:964a6d95acdd 306 // around the centred values. This causes the random angle values to get
eencae 3:964a6d95acdd 307 // calculated when the joystick is centred and untouched. This is also when
eencae 3:964a6d95acdd 308 // the magnitude is very small, so we can check for a small magnitude and then
eencae 3:964a6d95acdd 309 // set the angle to -1. This will inform us when the angle is invalid and the
eencae 3:964a6d95acdd 310 // joystick is centred
eencae 3:964a6d95acdd 311
eencae 3:964a6d95acdd 312 if (mag < TOL) {
eencae 3:964a6d95acdd 313 mag = 0.0f;
eencae 3:964a6d95acdd 314 angle = -1.0f;
eencae 3:964a6d95acdd 315 }
eencae 3:964a6d95acdd 316
eencae 3:964a6d95acdd 317 Polar p = {mag,angle};
eencae 3:964a6d95acdd 318 return p;
eencae 1:6d25cd49059b 319 }