
George Sykes ELEC2645 project
Dependencies: mbed
GHOST HUNTER
In a world of ghostly horrors there is much money to be made in underground ghost fighting rings. You've managed to get hold of a Ghostbuster, a special piece of equipment that allows you to catch, train and fight ghosts.
Instructions
Below you will find the instructions for the game. Please note that due to COVID-19 a large part of the game (fighting ghosts) could not be added as it would have required access to a second gamepad which i could not acquire.
Welcome screen
When first started you will be presented with a welcome screen
- Pot 1 to adjust the contrast on the screen
- Press A to continue.
Main menu
You have three options, catch ghosts (add ghosts to your inventory), inventory (sell ghosts) or settings(adjust the games settings).
- Press X and B to move the selection up and down respectively
- Press A to enter the selected submenu
Catch Ghost
Will now be presented with two challenges. In the first you need to find a ghost, in the second you catch it. Theses stages will start automatically.
Find ghost
Rotate the gamepad on its roll and pitch axis until all the LED's turn on. The ones on the left indicate roll and the right pitch.
- Rotate the gamepad on it roll and pitch to light up the LED's
Catch ghost
Return the gamepad to a comfortable position and use the joystick to move the crosshairs onto the ghost sprite. When ready press the A button to catch the ghost. You will be told what kind of ghost you have captured and it will be added to your inventory.
- Press A to catch the ghost
- Move the joystick to move the crosshairs
Inventory
The inventory allows you to view your ghosts and sell them.
- Use Pot 1 to scroll through the ghosts
- Pot 2 to scroll up and down the details of the individual ghosts
- Press X to prepare to sell a ghost and press again to confirm, if you don't press again the sale screen will disappear after 5 seconds
- Press Start to return to the main menu
Settings
This menu allows you to adjust some of the settings of the game.
- Press X to go up one option
- Press B to go down one option
- Press A to enter the selected submenu
- Press Start to return to the main menu
Contrast
Set the contrast of the LCD screen, the contrast will adjust on this screen so you can see the effect (contrast is bounded between 0.4 and 0.6).
- Pot 1 to increase or decrease the contrast
- Press A to set the contrast
Button Delay
Set the minimum time between button presses; if this is too low the game will detect two button presses when there was only one, too high and the buttons will seem unresponsive. So as to ensure these issues do not occur while changing the setting button X temporarily operates on the new delay but none of the others will until A is pressed.
- Pot 1 to increase or decrease the delay
- Press X to test the new delay, this will toggle the small circle to be filled in or unfilled
- Press A to save the setting
Gamepad2/Gamepad.cpp@17:3ebcf7bba112, 2020-05-26 (annotated)
- Committer:
- el18gs
- Date:
- Tue May 26 13:37:32 2020 +0000
- Revision:
- 17:3ebcf7bba112
- Parent:
- 2:eaf245af2aae
Final Submission. I have read and agreed with Statement of Academic Integrity.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
el18gs | 1:8d14be858ca0 | 1 | #include "Gamepad.h" |
el18gs | 1:8d14be858ca0 | 2 | |
el18gs | 1:8d14be858ca0 | 3 | #include "mbed.h" |
el18gs | 1:8d14be858ca0 | 4 | |
el18gs | 1:8d14be858ca0 | 5 | //////////// constructor/destructor //////////// |
el18gs | 1:8d14be858ca0 | 6 | Gamepad::Gamepad() |
el18gs | 1:8d14be858ca0 | 7 | : |
el18gs | 1:8d14be858ca0 | 8 | _led1(new PwmOut(PTA2)), |
el18gs | 1:8d14be858ca0 | 9 | _led2(new PwmOut(PTC2)), |
el18gs | 1:8d14be858ca0 | 10 | _led3(new PwmOut(PTC3)), |
el18gs | 1:8d14be858ca0 | 11 | _led4(new PwmOut(PTA1)), |
el18gs | 1:8d14be858ca0 | 12 | _led5(new PwmOut(PTC10)), |
el18gs | 1:8d14be858ca0 | 13 | _led6(new PwmOut(PTC11)), |
el18gs | 1:8d14be858ca0 | 14 | |
el18gs | 1:8d14be858ca0 | 15 | _button_A(new InterruptIn(PTC7)), |
el18gs | 1:8d14be858ca0 | 16 | _button_B(new InterruptIn(PTC9)), |
el18gs | 1:8d14be858ca0 | 17 | _button_X(new InterruptIn(PTC5)), |
el18gs | 1:8d14be858ca0 | 18 | _button_Y(new InterruptIn(PTC0)), |
el18gs | 1:8d14be858ca0 | 19 | _button_start(new InterruptIn(PTC8)), |
el18gs | 1:8d14be858ca0 | 20 | |
el18gs | 1:8d14be858ca0 | 21 | _vert(new AnalogIn(PTB11)), |
el18gs | 1:8d14be858ca0 | 22 | _horiz(new AnalogIn(PTB10)), |
el18gs | 1:8d14be858ca0 | 23 | |
el18gs | 1:8d14be858ca0 | 24 | _pot1(new AnalogIn(PTB2)), |
el18gs | 1:8d14be858ca0 | 25 | _pot2(new AnalogIn(PTB3)), |
el18gs | 1:8d14be858ca0 | 26 | |
el18gs | 1:8d14be858ca0 | 27 | dac(new AnalogOut(DAC0_OUT)), |
el18gs | 1:8d14be858ca0 | 28 | ticker(new Ticker), |
el18gs | 1:8d14be858ca0 | 29 | timeout(new Timeout), |
el18gs | 1:8d14be858ca0 | 30 | note_timeout(new Timeout), |
el18gs | 1:8d14be858ca0 | 31 | |
el18gs | 1:8d14be858ca0 | 32 | _x0(0), |
el18gs | 1:8d14be858ca0 | 33 | _y0(0) |
el18gs | 1:8d14be858ca0 | 34 | {} |
el18gs | 1:8d14be858ca0 | 35 | |
el18gs | 1:8d14be858ca0 | 36 | |
el18gs | 1:8d14be858ca0 | 37 | ///////////////// public methods ///////////////// |
el18gs | 1:8d14be858ca0 | 38 | |
el18gs | 1:8d14be858ca0 | 39 | void Gamepad::init() |
el18gs | 1:8d14be858ca0 | 40 | { |
el18gs | 1:8d14be858ca0 | 41 | leds_off(); |
el18gs | 1:8d14be858ca0 | 42 | |
el18gs | 1:8d14be858ca0 | 43 | // read centred values of joystick |
el18gs | 1:8d14be858ca0 | 44 | _x0 = _horiz->read(); |
el18gs | 1:8d14be858ca0 | 45 | _y0 = _vert->read(); |
el18gs | 1:8d14be858ca0 | 46 | |
el18gs | 1:8d14be858ca0 | 47 | // Set all buttons to PullUp |
el18gs | 1:8d14be858ca0 | 48 | _button_A->mode(PullUp); |
el18gs | 1:8d14be858ca0 | 49 | _button_B->mode(PullUp); |
el18gs | 1:8d14be858ca0 | 50 | _button_X->mode(PullUp); |
el18gs | 1:8d14be858ca0 | 51 | _button_Y->mode(PullUp); |
el18gs | 1:8d14be858ca0 | 52 | _button_start->mode(PullUp); |
el18gs | 1:8d14be858ca0 | 53 | |
el18gs | 1:8d14be858ca0 | 54 | // Set up interrupts for the fall of buttons |
el18gs | 1:8d14be858ca0 | 55 | _button_A->fall(callback(this,&Gamepad::A_fall_interrupt)); |
el18gs | 1:8d14be858ca0 | 56 | _button_B->fall(callback(this,&Gamepad::B_fall_interrupt)); |
el18gs | 1:8d14be858ca0 | 57 | _button_X->fall(callback(this,&Gamepad::X_fall_interrupt)); |
el18gs | 1:8d14be858ca0 | 58 | _button_Y->fall(callback(this,&Gamepad::Y_fall_interrupt)); |
el18gs | 1:8d14be858ca0 | 59 | _button_start->fall(callback(this,&Gamepad::start_fall_interrupt)); |
el18gs | 1:8d14be858ca0 | 60 | |
el18gs | 1:8d14be858ca0 | 61 | // initalise button flags |
el18gs | 1:8d14be858ca0 | 62 | reset_buttons(); |
el18gs | 1:8d14be858ca0 | 63 | |
el18gs | 1:8d14be858ca0 | 64 | // number of samples |
el18gs | 1:8d14be858ca0 | 65 | _n = 16; |
el18gs | 1:8d14be858ca0 | 66 | _sample_array = new float[_n]; |
el18gs | 1:8d14be858ca0 | 67 | |
el18gs | 1:8d14be858ca0 | 68 | // create sample array for one period between 0.0 and 1.0 |
el18gs | 1:8d14be858ca0 | 69 | for (int i = 0; i < _n ; i++) { |
el18gs | 1:8d14be858ca0 | 70 | _sample_array[i] = 0.5f + 0.5f*sin(i*2*PI/_n); |
el18gs | 1:8d14be858ca0 | 71 | //printf("y[%i] = %f\n",i,_sample_array[i]); |
el18gs | 1:8d14be858ca0 | 72 | } |
el18gs | 1:8d14be858ca0 | 73 | |
el18gs | 2:eaf245af2aae | 74 | // Initialise random number generator |
el18gs | 2:eaf245af2aae | 75 | float seed = read_pot1() + read_pot2() + get_angle() + get_mag(); |
el18gs | 2:eaf245af2aae | 76 | printf("Seed: %f\n", seed); |
el18gs | 2:eaf245af2aae | 77 | srand(seed * 100); |
el18gs | 2:eaf245af2aae | 78 | |
el18gs | 1:8d14be858ca0 | 79 | } |
el18gs | 1:8d14be858ca0 | 80 | |
el18gs | 1:8d14be858ca0 | 81 | void Gamepad::leds_off() |
el18gs | 1:8d14be858ca0 | 82 | { |
el18gs | 1:8d14be858ca0 | 83 | leds(0.0); |
el18gs | 1:8d14be858ca0 | 84 | } |
el18gs | 1:8d14be858ca0 | 85 | |
el18gs | 1:8d14be858ca0 | 86 | void Gamepad::leds_on() |
el18gs | 1:8d14be858ca0 | 87 | { |
el18gs | 1:8d14be858ca0 | 88 | leds(1.0); |
el18gs | 1:8d14be858ca0 | 89 | } |
el18gs | 1:8d14be858ca0 | 90 | |
el18gs | 1:8d14be858ca0 | 91 | void Gamepad::leds(float val) const |
el18gs | 1:8d14be858ca0 | 92 | { |
el18gs | 1:8d14be858ca0 | 93 | if (val < 0.0f) { |
el18gs | 1:8d14be858ca0 | 94 | val = 0.0f; |
el18gs | 1:8d14be858ca0 | 95 | } |
el18gs | 1:8d14be858ca0 | 96 | if (val > 1.0f) { |
el18gs | 1:8d14be858ca0 | 97 | val = 1.0f; |
el18gs | 1:8d14be858ca0 | 98 | } |
el18gs | 1:8d14be858ca0 | 99 | |
el18gs | 1:8d14be858ca0 | 100 | // leds are active-low, so subtract from 1.0 |
el18gs | 1:8d14be858ca0 | 101 | // 0.0 corresponds to fully-off, 1.0 to fully-on |
el18gs | 1:8d14be858ca0 | 102 | val = 1.0f - val; |
el18gs | 1:8d14be858ca0 | 103 | |
el18gs | 1:8d14be858ca0 | 104 | _led1->write(val); |
el18gs | 1:8d14be858ca0 | 105 | _led2->write(val); |
el18gs | 1:8d14be858ca0 | 106 | _led3->write(val); |
el18gs | 1:8d14be858ca0 | 107 | _led4->write(val); |
el18gs | 1:8d14be858ca0 | 108 | _led5->write(val); |
el18gs | 1:8d14be858ca0 | 109 | _led6->write(val); |
el18gs | 1:8d14be858ca0 | 110 | } |
el18gs | 1:8d14be858ca0 | 111 | |
el18gs | 1:8d14be858ca0 | 112 | void Gamepad::led(int n,float val) const |
el18gs | 1:8d14be858ca0 | 113 | { |
el18gs | 1:8d14be858ca0 | 114 | // ensure they are within valid range |
el18gs | 1:8d14be858ca0 | 115 | if (val < 0.0f) { |
el18gs | 1:8d14be858ca0 | 116 | val = 0.0f; |
el18gs | 1:8d14be858ca0 | 117 | } |
el18gs | 1:8d14be858ca0 | 118 | if (val > 1.0f) { |
el18gs | 1:8d14be858ca0 | 119 | val = 1.0f; |
el18gs | 1:8d14be858ca0 | 120 | } |
el18gs | 1:8d14be858ca0 | 121 | |
el18gs | 1:8d14be858ca0 | 122 | switch (n) { |
el18gs | 1:8d14be858ca0 | 123 | |
el18gs | 1:8d14be858ca0 | 124 | // check for valid LED number and set value |
el18gs | 1:8d14be858ca0 | 125 | |
el18gs | 1:8d14be858ca0 | 126 | case 1: |
el18gs | 1:8d14be858ca0 | 127 | _led1->write(1.0f-val); // active-low so subtract from 1 |
el18gs | 1:8d14be858ca0 | 128 | break; |
el18gs | 1:8d14be858ca0 | 129 | case 2: |
el18gs | 1:8d14be858ca0 | 130 | _led2->write(1.0f-val); // active-low so subtract from 1 |
el18gs | 1:8d14be858ca0 | 131 | break; |
el18gs | 1:8d14be858ca0 | 132 | case 3: |
el18gs | 1:8d14be858ca0 | 133 | _led3->write(1.0f-val); // active-low so subtract from 1 |
el18gs | 1:8d14be858ca0 | 134 | break; |
el18gs | 1:8d14be858ca0 | 135 | case 4: |
el18gs | 1:8d14be858ca0 | 136 | _led4->write(1.0f-val); // active-low so subtract from 1 |
el18gs | 1:8d14be858ca0 | 137 | break; |
el18gs | 1:8d14be858ca0 | 138 | case 5: |
el18gs | 1:8d14be858ca0 | 139 | _led5->write(1.0f-val); // active-low so subtract from 1 |
el18gs | 1:8d14be858ca0 | 140 | break; |
el18gs | 1:8d14be858ca0 | 141 | case 6: |
el18gs | 1:8d14be858ca0 | 142 | _led6->write(1.0f-val); // active-low so subtract from 1 |
el18gs | 1:8d14be858ca0 | 143 | break; |
el18gs | 1:8d14be858ca0 | 144 | |
el18gs | 1:8d14be858ca0 | 145 | } |
el18gs | 1:8d14be858ca0 | 146 | } |
el18gs | 1:8d14be858ca0 | 147 | |
el18gs | 1:8d14be858ca0 | 148 | float Gamepad::read_pot1() const |
el18gs | 1:8d14be858ca0 | 149 | { |
el18gs | 1:8d14be858ca0 | 150 | return _pot1->read(); |
el18gs | 1:8d14be858ca0 | 151 | } |
el18gs | 1:8d14be858ca0 | 152 | |
el18gs | 1:8d14be858ca0 | 153 | float Gamepad::read_pot2() const |
el18gs | 1:8d14be858ca0 | 154 | { |
el18gs | 1:8d14be858ca0 | 155 | return _pot2->read(); |
el18gs | 1:8d14be858ca0 | 156 | } |
el18gs | 1:8d14be858ca0 | 157 | |
el18gs | 1:8d14be858ca0 | 158 | |
el18gs | 1:8d14be858ca0 | 159 | // this method gets the magnitude of the joystick movement |
el18gs | 1:8d14be858ca0 | 160 | float Gamepad::get_mag() |
el18gs | 1:8d14be858ca0 | 161 | { |
el18gs | 1:8d14be858ca0 | 162 | Polar p = get_polar(); |
el18gs | 1:8d14be858ca0 | 163 | return p.mag; |
el18gs | 1:8d14be858ca0 | 164 | } |
el18gs | 1:8d14be858ca0 | 165 | |
el18gs | 1:8d14be858ca0 | 166 | // this method gets the angle of joystick movement (0 to 360, 0 North) |
el18gs | 1:8d14be858ca0 | 167 | float Gamepad::get_angle() |
el18gs | 1:8d14be858ca0 | 168 | { |
el18gs | 1:8d14be858ca0 | 169 | Polar p = get_polar(); |
el18gs | 1:8d14be858ca0 | 170 | return p.angle; |
el18gs | 1:8d14be858ca0 | 171 | } |
el18gs | 1:8d14be858ca0 | 172 | |
el18gs | 1:8d14be858ca0 | 173 | Direction Gamepad::get_direction() |
el18gs | 1:8d14be858ca0 | 174 | { |
el18gs | 1:8d14be858ca0 | 175 | float angle = get_angle(); // 0 to 360, -1 for centred |
el18gs | 1:8d14be858ca0 | 176 | |
el18gs | 1:8d14be858ca0 | 177 | Direction d; |
el18gs | 1:8d14be858ca0 | 178 | // partition 360 into segments and check which segment the angle is in |
el18gs | 1:8d14be858ca0 | 179 | if (angle < 0.0f) { |
el18gs | 1:8d14be858ca0 | 180 | d = CENTRE; // check for -1.0 angle |
el18gs | 1:8d14be858ca0 | 181 | } else if (angle < 22.5f) { // then keep going in 45 degree increments |
el18gs | 1:8d14be858ca0 | 182 | d = N; |
el18gs | 1:8d14be858ca0 | 183 | } else if (angle < 67.5f) { |
el18gs | 1:8d14be858ca0 | 184 | d = NE; |
el18gs | 1:8d14be858ca0 | 185 | } else if (angle < 112.5f) { |
el18gs | 1:8d14be858ca0 | 186 | d = E; |
el18gs | 1:8d14be858ca0 | 187 | } else if (angle < 157.5f) { |
el18gs | 1:8d14be858ca0 | 188 | d = SE; |
el18gs | 1:8d14be858ca0 | 189 | } else if (angle < 202.5f) { |
el18gs | 1:8d14be858ca0 | 190 | d = S; |
el18gs | 1:8d14be858ca0 | 191 | } else if (angle < 247.5f) { |
el18gs | 1:8d14be858ca0 | 192 | d = SW; |
el18gs | 1:8d14be858ca0 | 193 | } else if (angle < 292.5f) { |
el18gs | 1:8d14be858ca0 | 194 | d = W; |
el18gs | 1:8d14be858ca0 | 195 | } else if (angle < 337.5f) { |
el18gs | 1:8d14be858ca0 | 196 | d = NW; |
el18gs | 1:8d14be858ca0 | 197 | } else { |
el18gs | 1:8d14be858ca0 | 198 | d = N; |
el18gs | 1:8d14be858ca0 | 199 | } |
el18gs | 1:8d14be858ca0 | 200 | |
el18gs | 1:8d14be858ca0 | 201 | return d; |
el18gs | 1:8d14be858ca0 | 202 | } |
el18gs | 1:8d14be858ca0 | 203 | |
el18gs | 1:8d14be858ca0 | 204 | void Gamepad::reset_buttons() |
el18gs | 1:8d14be858ca0 | 205 | { |
el18gs | 1:8d14be858ca0 | 206 | A_fall = B_fall = X_fall = Y_fall = start_fall = false; |
el18gs | 1:8d14be858ca0 | 207 | } |
el18gs | 1:8d14be858ca0 | 208 | |
el18gs | 1:8d14be858ca0 | 209 | bool Gamepad::A_pressed() |
el18gs | 1:8d14be858ca0 | 210 | { |
el18gs | 1:8d14be858ca0 | 211 | if (A_fall) { |
el18gs | 1:8d14be858ca0 | 212 | A_fall = false; |
el18gs | 1:8d14be858ca0 | 213 | return true; |
el18gs | 1:8d14be858ca0 | 214 | } else { |
el18gs | 1:8d14be858ca0 | 215 | return false; |
el18gs | 1:8d14be858ca0 | 216 | } |
el18gs | 1:8d14be858ca0 | 217 | } |
el18gs | 1:8d14be858ca0 | 218 | |
el18gs | 1:8d14be858ca0 | 219 | bool Gamepad::B_pressed() |
el18gs | 1:8d14be858ca0 | 220 | { |
el18gs | 1:8d14be858ca0 | 221 | if (B_fall) { |
el18gs | 1:8d14be858ca0 | 222 | B_fall = false; |
el18gs | 1:8d14be858ca0 | 223 | return true; |
el18gs | 1:8d14be858ca0 | 224 | } else { |
el18gs | 1:8d14be858ca0 | 225 | return false; |
el18gs | 1:8d14be858ca0 | 226 | } |
el18gs | 1:8d14be858ca0 | 227 | } |
el18gs | 1:8d14be858ca0 | 228 | |
el18gs | 1:8d14be858ca0 | 229 | bool Gamepad::X_pressed() |
el18gs | 1:8d14be858ca0 | 230 | { |
el18gs | 1:8d14be858ca0 | 231 | if (X_fall) { |
el18gs | 1:8d14be858ca0 | 232 | X_fall = false; |
el18gs | 1:8d14be858ca0 | 233 | return true; |
el18gs | 1:8d14be858ca0 | 234 | } else { |
el18gs | 1:8d14be858ca0 | 235 | return false; |
el18gs | 1:8d14be858ca0 | 236 | } |
el18gs | 1:8d14be858ca0 | 237 | } |
el18gs | 1:8d14be858ca0 | 238 | |
el18gs | 1:8d14be858ca0 | 239 | bool Gamepad::Y_pressed() |
el18gs | 1:8d14be858ca0 | 240 | { |
el18gs | 1:8d14be858ca0 | 241 | if (Y_fall) { |
el18gs | 1:8d14be858ca0 | 242 | Y_fall = false; |
el18gs | 1:8d14be858ca0 | 243 | return true; |
el18gs | 1:8d14be858ca0 | 244 | } else { |
el18gs | 1:8d14be858ca0 | 245 | return false; |
el18gs | 1:8d14be858ca0 | 246 | } |
el18gs | 1:8d14be858ca0 | 247 | } |
el18gs | 1:8d14be858ca0 | 248 | |
el18gs | 1:8d14be858ca0 | 249 | bool Gamepad::start_pressed() |
el18gs | 1:8d14be858ca0 | 250 | { |
el18gs | 1:8d14be858ca0 | 251 | if (start_fall) { |
el18gs | 1:8d14be858ca0 | 252 | start_fall = false; |
el18gs | 1:8d14be858ca0 | 253 | return true; |
el18gs | 1:8d14be858ca0 | 254 | } else { |
el18gs | 1:8d14be858ca0 | 255 | return false; |
el18gs | 1:8d14be858ca0 | 256 | } |
el18gs | 1:8d14be858ca0 | 257 | } |
el18gs | 1:8d14be858ca0 | 258 | |
el18gs | 1:8d14be858ca0 | 259 | bool Gamepad::A_held() |
el18gs | 1:8d14be858ca0 | 260 | { |
el18gs | 1:8d14be858ca0 | 261 | // Buttons are configured as PullUp hence the not |
el18gs | 1:8d14be858ca0 | 262 | return !_button_A->read(); |
el18gs | 1:8d14be858ca0 | 263 | } |
el18gs | 1:8d14be858ca0 | 264 | |
el18gs | 1:8d14be858ca0 | 265 | bool Gamepad::B_held() |
el18gs | 1:8d14be858ca0 | 266 | { |
el18gs | 1:8d14be858ca0 | 267 | return !_button_B->read(); |
el18gs | 1:8d14be858ca0 | 268 | } |
el18gs | 1:8d14be858ca0 | 269 | |
el18gs | 1:8d14be858ca0 | 270 | bool Gamepad::X_held() |
el18gs | 1:8d14be858ca0 | 271 | { |
el18gs | 1:8d14be858ca0 | 272 | return !_button_X->read(); |
el18gs | 1:8d14be858ca0 | 273 | } |
el18gs | 1:8d14be858ca0 | 274 | |
el18gs | 1:8d14be858ca0 | 275 | bool Gamepad::Y_held() |
el18gs | 1:8d14be858ca0 | 276 | { |
el18gs | 1:8d14be858ca0 | 277 | return !_button_Y->read(); |
el18gs | 1:8d14be858ca0 | 278 | } |
el18gs | 1:8d14be858ca0 | 279 | |
el18gs | 1:8d14be858ca0 | 280 | bool Gamepad::start_held() |
el18gs | 1:8d14be858ca0 | 281 | { |
el18gs | 1:8d14be858ca0 | 282 | return !_button_start->read(); |
el18gs | 1:8d14be858ca0 | 283 | } |
el18gs | 1:8d14be858ca0 | 284 | |
el18gs | 1:8d14be858ca0 | 285 | ///////////////////// private methods //////////////////////// |
el18gs | 1:8d14be858ca0 | 286 | |
el18gs | 1:8d14be858ca0 | 287 | // get raw joystick coordinate in range -1 to 1 |
el18gs | 1:8d14be858ca0 | 288 | // Direction (x,y) |
el18gs | 1:8d14be858ca0 | 289 | // North (0,1) |
el18gs | 1:8d14be858ca0 | 290 | // East (1,0) |
el18gs | 1:8d14be858ca0 | 291 | // South (0,-1) |
el18gs | 1:8d14be858ca0 | 292 | // West (-1,0) |
el18gs | 1:8d14be858ca0 | 293 | Vector2D Gamepad::get_coord() |
el18gs | 1:8d14be858ca0 | 294 | { |
el18gs | 1:8d14be858ca0 | 295 | // read() returns value in range 0.0 to 1.0 so is scaled and centre value |
el18gs | 1:8d14be858ca0 | 296 | // substracted to get values in the range -1.0 to 1.0 |
el18gs | 1:8d14be858ca0 | 297 | float x = 2.0f*( _horiz->read() - _x0 ); |
el18gs | 1:8d14be858ca0 | 298 | float y = 2.0f*( _vert->read() - _y0 ); |
el18gs | 1:8d14be858ca0 | 299 | |
el18gs | 1:8d14be858ca0 | 300 | // Note: the y value here is inverted to ensure the positive y is up |
el18gs | 1:8d14be858ca0 | 301 | |
el18gs | 1:8d14be858ca0 | 302 | Vector2D coord = {x,-y}; |
el18gs | 1:8d14be858ca0 | 303 | return coord; |
el18gs | 1:8d14be858ca0 | 304 | } |
el18gs | 1:8d14be858ca0 | 305 | |
el18gs | 1:8d14be858ca0 | 306 | // This maps the raw x,y coord onto a circular grid. |
el18gs | 1:8d14be858ca0 | 307 | // See: http://mathproofs.blogspot.co.uk/2005/07/mapping-square-to-circle.html |
el18gs | 1:8d14be858ca0 | 308 | Vector2D Gamepad::get_mapped_coord() |
el18gs | 1:8d14be858ca0 | 309 | { |
el18gs | 1:8d14be858ca0 | 310 | Vector2D coord = get_coord(); |
el18gs | 1:8d14be858ca0 | 311 | |
el18gs | 1:8d14be858ca0 | 312 | // do the transformation |
el18gs | 1:8d14be858ca0 | 313 | float x = coord.x*sqrt(1.0f-pow(coord.y,2.0f)/2.0f); |
el18gs | 1:8d14be858ca0 | 314 | float y = coord.y*sqrt(1.0f-pow(coord.x,2.0f)/2.0f); |
el18gs | 1:8d14be858ca0 | 315 | |
el18gs | 1:8d14be858ca0 | 316 | Vector2D mapped_coord = {x,y}; |
el18gs | 1:8d14be858ca0 | 317 | return mapped_coord; |
el18gs | 1:8d14be858ca0 | 318 | } |
el18gs | 1:8d14be858ca0 | 319 | |
el18gs | 1:8d14be858ca0 | 320 | // this function converts the mapped coordinates into polar form |
el18gs | 1:8d14be858ca0 | 321 | Polar Gamepad::get_polar() |
el18gs | 1:8d14be858ca0 | 322 | { |
el18gs | 1:8d14be858ca0 | 323 | // get the mapped coordinate |
el18gs | 1:8d14be858ca0 | 324 | Vector2D coord = get_mapped_coord(); |
el18gs | 1:8d14be858ca0 | 325 | |
el18gs | 1:8d14be858ca0 | 326 | // at this point, 0 degrees (i.e. x-axis) will be defined to the East. |
el18gs | 1:8d14be858ca0 | 327 | // We want 0 degrees to correspond to North and increase clockwise to 359 |
el18gs | 1:8d14be858ca0 | 328 | // like a compass heading, so we need to swap the axis and invert y |
el18gs | 1:8d14be858ca0 | 329 | float x = coord.y; |
el18gs | 1:8d14be858ca0 | 330 | float y = coord.x; |
el18gs | 1:8d14be858ca0 | 331 | |
el18gs | 1:8d14be858ca0 | 332 | float mag = sqrt(x*x+y*y); // pythagoras |
el18gs | 1:8d14be858ca0 | 333 | float angle = RAD2DEG*atan2(y,x); |
el18gs | 1:8d14be858ca0 | 334 | // angle will be in range -180 to 180, so add 360 to negative angles to |
el18gs | 1:8d14be858ca0 | 335 | // move to 0 to 360 range |
el18gs | 1:8d14be858ca0 | 336 | if (angle < 0.0f) { |
el18gs | 1:8d14be858ca0 | 337 | angle+=360.0f; |
el18gs | 1:8d14be858ca0 | 338 | } |
el18gs | 1:8d14be858ca0 | 339 | |
el18gs | 1:8d14be858ca0 | 340 | // the noise on the ADC causes the values of x and y to fluctuate slightly |
el18gs | 1:8d14be858ca0 | 341 | // around the centred values. This causes the random angle values to get |
el18gs | 1:8d14be858ca0 | 342 | // calculated when the joystick is centred and untouched. This is also when |
el18gs | 1:8d14be858ca0 | 343 | // the magnitude is very small, so we can check for a small magnitude and then |
el18gs | 1:8d14be858ca0 | 344 | // set the angle to -1. This will inform us when the angle is invalid and the |
el18gs | 1:8d14be858ca0 | 345 | // joystick is centred |
el18gs | 1:8d14be858ca0 | 346 | |
el18gs | 1:8d14be858ca0 | 347 | if (mag < TOL) { |
el18gs | 1:8d14be858ca0 | 348 | mag = 0.0f; |
el18gs | 1:8d14be858ca0 | 349 | angle = -1.0f; |
el18gs | 1:8d14be858ca0 | 350 | } |
el18gs | 1:8d14be858ca0 | 351 | |
el18gs | 1:8d14be858ca0 | 352 | Polar p = {mag,angle}; |
el18gs | 1:8d14be858ca0 | 353 | return p; |
el18gs | 1:8d14be858ca0 | 354 | } |
el18gs | 1:8d14be858ca0 | 355 | |
el18gs | 1:8d14be858ca0 | 356 | // ISRs for buttons |
el18gs | 1:8d14be858ca0 | 357 | void Gamepad::A_fall_interrupt() |
el18gs | 1:8d14be858ca0 | 358 | { |
el18gs | 1:8d14be858ca0 | 359 | A_fall = true; |
el18gs | 1:8d14be858ca0 | 360 | } |
el18gs | 1:8d14be858ca0 | 361 | void Gamepad::B_fall_interrupt() |
el18gs | 1:8d14be858ca0 | 362 | { |
el18gs | 1:8d14be858ca0 | 363 | B_fall = true; |
el18gs | 1:8d14be858ca0 | 364 | } |
el18gs | 1:8d14be858ca0 | 365 | void Gamepad::X_fall_interrupt() |
el18gs | 1:8d14be858ca0 | 366 | { |
el18gs | 1:8d14be858ca0 | 367 | X_fall = true; |
el18gs | 1:8d14be858ca0 | 368 | } |
el18gs | 1:8d14be858ca0 | 369 | void Gamepad::Y_fall_interrupt() |
el18gs | 1:8d14be858ca0 | 370 | { |
el18gs | 1:8d14be858ca0 | 371 | Y_fall = true; |
el18gs | 1:8d14be858ca0 | 372 | } |
el18gs | 1:8d14be858ca0 | 373 | void Gamepad::start_fall_interrupt() |
el18gs | 1:8d14be858ca0 | 374 | { |
el18gs | 1:8d14be858ca0 | 375 | start_fall = true; |
el18gs | 1:8d14be858ca0 | 376 | } |
el18gs | 1:8d14be858ca0 | 377 | |
el18gs | 1:8d14be858ca0 | 378 | void Gamepad::set_bpm(float bpm) |
el18gs | 1:8d14be858ca0 | 379 | { |
el18gs | 1:8d14be858ca0 | 380 | _bpm = bpm; |
el18gs | 1:8d14be858ca0 | 381 | } |
el18gs | 1:8d14be858ca0 | 382 | |
el18gs | 1:8d14be858ca0 | 383 | void Gamepad::tone(float frequency,float duration) |
el18gs | 1:8d14be858ca0 | 384 | { |
el18gs | 1:8d14be858ca0 | 385 | // calculate time step between samples |
el18gs | 1:8d14be858ca0 | 386 | float dt = 1.0f/(frequency*_n); |
el18gs | 1:8d14be858ca0 | 387 | // start from beginning of LUT |
el18gs | 1:8d14be858ca0 | 388 | _sample = 0; |
el18gs | 1:8d14be858ca0 | 389 | |
el18gs | 1:8d14be858ca0 | 390 | // setup ticker and timeout to stop ticker |
el18gs | 1:8d14be858ca0 | 391 | |
el18gs | 1:8d14be858ca0 | 392 | // the ticker repeats every dt to plat each sample in turn |
el18gs | 1:8d14be858ca0 | 393 | ticker->attach(callback(this, &Gamepad::ticker_isr), dt); |
el18gs | 1:8d14be858ca0 | 394 | // the timeout stops the ticker after the required duration |
el18gs | 1:8d14be858ca0 | 395 | timeout->attach(callback(this, &Gamepad::timeout_isr), duration ); |
el18gs | 1:8d14be858ca0 | 396 | } |
el18gs | 1:8d14be858ca0 | 397 | |
el18gs | 1:8d14be858ca0 | 398 | void Gamepad::play_melody(int length,const int *notes,const int *durations,float bpm,bool repeat) |
el18gs | 1:8d14be858ca0 | 399 | { |
el18gs | 1:8d14be858ca0 | 400 | // copy arguments to member variables |
el18gs | 1:8d14be858ca0 | 401 | _bpm = bpm; |
el18gs | 1:8d14be858ca0 | 402 | _notes = notes; // pointer for array |
el18gs | 1:8d14be858ca0 | 403 | _durations = durations; // pointer for array |
el18gs | 1:8d14be858ca0 | 404 | _melody_length = length; |
el18gs | 1:8d14be858ca0 | 405 | _repeat = repeat; |
el18gs | 1:8d14be858ca0 | 406 | |
el18gs | 1:8d14be858ca0 | 407 | _note = 0; // start from first note |
el18gs | 1:8d14be858ca0 | 408 | |
el18gs | 1:8d14be858ca0 | 409 | play_next_note(); // play the next note in the melody |
el18gs | 1:8d14be858ca0 | 410 | } |
el18gs | 1:8d14be858ca0 | 411 | |
el18gs | 1:8d14be858ca0 | 412 | void Gamepad::write_dac(float val) |
el18gs | 1:8d14be858ca0 | 413 | { |
el18gs | 1:8d14be858ca0 | 414 | if (val < 0.0f) { |
el18gs | 1:8d14be858ca0 | 415 | val = 0.0f; |
el18gs | 1:8d14be858ca0 | 416 | } else if (val > 1.0f) { |
el18gs | 1:8d14be858ca0 | 417 | val = 1.0f; |
el18gs | 1:8d14be858ca0 | 418 | } |
el18gs | 1:8d14be858ca0 | 419 | dac->write(val); |
el18gs | 1:8d14be858ca0 | 420 | } |
el18gs | 1:8d14be858ca0 | 421 | |
el18gs | 1:8d14be858ca0 | 422 | |
el18gs | 1:8d14be858ca0 | 423 | void Gamepad::play_next_note() |
el18gs | 1:8d14be858ca0 | 424 | { |
el18gs | 1:8d14be858ca0 | 425 | // _note is the note index to play |
el18gs | 1:8d14be858ca0 | 426 | |
el18gs | 1:8d14be858ca0 | 427 | // calculate the duration and frequency of the note |
el18gs | 1:8d14be858ca0 | 428 | float duration = 60.0f/(_bpm*_durations[_note]); |
el18gs | 1:8d14be858ca0 | 429 | float frequency = float(_notes[_note]); |
el18gs | 1:8d14be858ca0 | 430 | //printf("[%i] f = %f d = %f\n",_note,frequency,duration); |
el18gs | 1:8d14be858ca0 | 431 | |
el18gs | 1:8d14be858ca0 | 432 | // check if the note is not a space and if not then play the note |
el18gs | 1:8d14be858ca0 | 433 | if (frequency > 0) { |
el18gs | 1:8d14be858ca0 | 434 | tone(frequency,duration); |
el18gs | 1:8d14be858ca0 | 435 | } |
el18gs | 1:8d14be858ca0 | 436 | |
el18gs | 1:8d14be858ca0 | 437 | // the timeout goes to the next note in the melody |
el18gs | 1:8d14be858ca0 | 438 | // double the duration to leave a bit of a gap in between notes to be better |
el18gs | 1:8d14be858ca0 | 439 | // able to distinguish them |
el18gs | 1:8d14be858ca0 | 440 | note_timeout->attach(callback(this, &Gamepad::note_timeout_isr), duration*2.0f ); |
el18gs | 1:8d14be858ca0 | 441 | } |
el18gs | 1:8d14be858ca0 | 442 | |
el18gs | 1:8d14be858ca0 | 443 | // called when the next note needs playing |
el18gs | 1:8d14be858ca0 | 444 | void Gamepad::note_timeout_isr() |
el18gs | 1:8d14be858ca0 | 445 | { |
el18gs | 1:8d14be858ca0 | 446 | _note++; // go onto next note |
el18gs | 1:8d14be858ca0 | 447 | |
el18gs | 1:8d14be858ca0 | 448 | // if in repeat mode then reset the note counter when get to end of melody |
el18gs | 1:8d14be858ca0 | 449 | if (_repeat && _note == _melody_length) { |
el18gs | 1:8d14be858ca0 | 450 | _note=0; |
el18gs | 1:8d14be858ca0 | 451 | } |
el18gs | 1:8d14be858ca0 | 452 | |
el18gs | 1:8d14be858ca0 | 453 | // check if note is within the melody |
el18gs | 1:8d14be858ca0 | 454 | if (_note < _melody_length) { |
el18gs | 1:8d14be858ca0 | 455 | play_next_note(); |
el18gs | 1:8d14be858ca0 | 456 | } |
el18gs | 1:8d14be858ca0 | 457 | } |
el18gs | 1:8d14be858ca0 | 458 | |
el18gs | 1:8d14be858ca0 | 459 | void Gamepad::ticker_isr() |
el18gs | 1:8d14be858ca0 | 460 | { |
el18gs | 1:8d14be858ca0 | 461 | dac->write(_sample_array[_sample%_n]); // use modulo to get index to play |
el18gs | 1:8d14be858ca0 | 462 | _sample++; // increment the sample ready for next time |
el18gs | 1:8d14be858ca0 | 463 | } |
el18gs | 1:8d14be858ca0 | 464 | |
el18gs | 1:8d14be858ca0 | 465 | void Gamepad::timeout_isr() |
el18gs | 1:8d14be858ca0 | 466 | { |
el18gs | 1:8d14be858ca0 | 467 | // stops the ticker to end the note |
el18gs | 1:8d14be858ca0 | 468 | ticker->detach(); |
el18gs | 1:8d14be858ca0 | 469 | } |
el18gs | 2:eaf245af2aae | 470 | |
el18gs | 2:eaf245af2aae | 471 | int Gamepad::random_gen(int upper, int lower){ |
el18gs | 2:eaf245af2aae | 472 | return (int)(rand() % (upper - lower + 1)); |
el18gs | 2:eaf245af2aae | 473 | } |