haptic.c (10221B)
1 /* Copyright 2019 ishtob 2 * Driver for haptic feedback written for QMK 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "haptic.h" 19 #include "eeconfig.h" 20 #include "debug.h" 21 #include "usb_device_state.h" 22 #include "gpio.h" 23 #include "keyboard.h" 24 25 #ifdef HAPTIC_DRV2605L 26 # include "drv2605l.h" 27 #endif 28 #ifdef HAPTIC_SOLENOID 29 # include "solenoid.h" 30 #endif 31 32 #if defined(SPLIT_KEYBOARD) && defined(SPLIT_HAPTIC_ENABLE) 33 extern uint8_t split_haptic_play; 34 #endif 35 36 haptic_config_t haptic_config; 37 38 static void update_haptic_enable_gpios(void) { 39 if (haptic_config.enable && ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state_get_configure_state() == USB_DEVICE_STATE_CONFIGURED))) { 40 #if defined(HAPTIC_ENABLE_PIN) 41 HAPTIC_ENABLE_PIN_WRITE_ACTIVE(); 42 #endif 43 #if defined(HAPTIC_ENABLE_STATUS_LED) 44 HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE(); 45 #endif 46 } else { 47 #if defined(HAPTIC_ENABLE_PIN) 48 HAPTIC_ENABLE_PIN_WRITE_INACTIVE(); 49 #endif 50 #if defined(HAPTIC_ENABLE_STATUS_LED) 51 HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE(); 52 #endif 53 } 54 } 55 56 static void set_haptic_config_enable(bool enabled) { 57 haptic_config.enable = enabled; 58 update_haptic_enable_gpios(); 59 } 60 61 void haptic_init(void) { 62 // only initialize on secondary boards if the user desires 63 #if defined(SPLIT_KEYBOARD) && !defined(SPLIT_HAPTIC_ENABLE) 64 if (!is_keyboard_master()) return; 65 #endif 66 67 if (!eeconfig_is_enabled()) { 68 eeconfig_init(); 69 } 70 eeconfig_read_haptic(&haptic_config); 71 #ifdef HAPTIC_SOLENOID 72 solenoid_set_dwell(haptic_config.dwell); 73 #endif 74 if ((haptic_config.raw == 0) 75 #ifdef HAPTIC_SOLENOID 76 || (haptic_config.dwell == 0) 77 #endif 78 ) { 79 // this will be called, if the eeprom is not corrupt, 80 // but the previous firmware didn't have haptic enabled, 81 // or the previous firmware didn't have solenoid enabled, 82 // and the current one has solenoid enabled. 83 haptic_reset(); 84 } else { 85 // Haptic configuration has been loaded through the "raw" union item. 86 // This is to execute any side effects of the configuration. 87 set_haptic_config_enable(haptic_config.enable); 88 } 89 #ifdef HAPTIC_SOLENOID 90 solenoid_setup(); 91 dprintf("Solenoid driver initialized\n"); 92 #endif 93 #ifdef HAPTIC_DRV2605L 94 drv2605l_init(); 95 dprintf("DRV2605 driver initialized\n"); 96 #endif 97 eeconfig_debug_haptic(); 98 #ifdef HAPTIC_ENABLE_PIN 99 gpio_set_pin_output(HAPTIC_ENABLE_PIN); 100 #endif 101 #ifdef HAPTIC_ENABLE_STATUS_LED 102 gpio_set_pin_output(HAPTIC_ENABLE_STATUS_LED); 103 #endif 104 } 105 106 void haptic_task(void) { 107 #ifdef HAPTIC_SOLENOID 108 // Only run task on seconary boards if the user desires 109 # if defined(SPLIT_KEYBOARD) && !defined(SPLIT_HAPTIC_ENABLE) 110 if (!is_keyboard_master()) return; 111 # endif 112 solenoid_check(); 113 #endif // HAPTIC_SOLENOID 114 } 115 116 void eeconfig_debug_haptic(void) { 117 dprintf("haptic_config eeprom\n"); 118 dprintf("haptic_config.enable = %d\n", haptic_config.enable); 119 dprintf("haptic_config.mode = %d\n", haptic_config.mode); 120 } 121 122 void haptic_enable(void) { 123 set_haptic_config_enable(true); 124 dprintf("haptic_config.enable = %u\n", haptic_config.enable); 125 eeconfig_update_haptic(&haptic_config); 126 } 127 128 void haptic_disable(void) { 129 set_haptic_config_enable(false); 130 dprintf("haptic_config.enable = %u\n", haptic_config.enable); 131 eeconfig_update_haptic(&haptic_config); 132 } 133 134 void haptic_toggle(void) { 135 if (haptic_config.enable) { 136 haptic_disable(); 137 } else { 138 haptic_enable(); 139 } 140 eeconfig_update_haptic(&haptic_config); 141 } 142 143 void haptic_feedback_toggle(void) { 144 haptic_config.feedback++; 145 if (haptic_config.feedback >= HAPTIC_FEEDBACK_MAX) haptic_config.feedback = KEY_PRESS; 146 dprintf("haptic_config.feedback = %u\n", !haptic_config.feedback); 147 eeconfig_update_haptic(&haptic_config); 148 } 149 150 void haptic_buzz_toggle(void) { 151 bool buzz_stat = !haptic_config.buzz; 152 haptic_config.buzz = buzz_stat; 153 haptic_set_buzz(buzz_stat); 154 } 155 156 void haptic_mode_increase(void) { 157 uint8_t mode = haptic_config.mode + 1; 158 #ifdef HAPTIC_DRV2605L 159 if (haptic_config.mode >= DRV2605L_EFFECT_COUNT) { 160 mode = 1; 161 } 162 #endif 163 haptic_set_mode(mode); 164 } 165 166 void haptic_mode_decrease(void) { 167 uint8_t mode = haptic_config.mode - 1; 168 #ifdef HAPTIC_DRV2605L 169 if (haptic_config.mode < 1) { 170 mode = (DRV2605L_EFFECT_COUNT - 1); 171 } 172 #endif 173 haptic_set_mode(mode); 174 } 175 176 void haptic_dwell_increase(void) { 177 #ifdef HAPTIC_SOLENOID 178 int16_t next_dwell = ((int16_t)haptic_config.dwell) + SOLENOID_DWELL_STEP_SIZE; 179 if (haptic_config.dwell >= SOLENOID_MAX_DWELL) { 180 // if it's already at max, we wrap back to min 181 next_dwell = SOLENOID_MIN_DWELL; 182 } else if (next_dwell > SOLENOID_MAX_DWELL) { 183 // if we overshoot the max, then cap at max 184 next_dwell = SOLENOID_MAX_DWELL; 185 } 186 solenoid_set_dwell(next_dwell); 187 #else 188 int16_t next_dwell = ((int16_t)haptic_config.dwell) + 1; 189 #endif 190 haptic_set_dwell(next_dwell); 191 } 192 193 void haptic_dwell_decrease(void) { 194 #ifdef HAPTIC_SOLENOID 195 int16_t next_dwell = ((int16_t)haptic_config.dwell) - SOLENOID_DWELL_STEP_SIZE; 196 if (haptic_config.dwell <= SOLENOID_MIN_DWELL) { 197 // if it's already at min, we wrap to max 198 next_dwell = SOLENOID_MAX_DWELL; 199 } else if (next_dwell < SOLENOID_MIN_DWELL) { 200 // if we go below min, then we cap to min 201 next_dwell = SOLENOID_MIN_DWELL; 202 } 203 solenoid_set_dwell(next_dwell); 204 #else 205 int16_t next_dwell = ((int16_t)haptic_config.dwell) - 1; 206 #endif 207 haptic_set_dwell(next_dwell); 208 } 209 210 void haptic_reset(void) { 211 set_haptic_config_enable(true); 212 uint8_t feedback = HAPTIC_DEFAULT_FEEDBACK; 213 haptic_config.feedback = feedback; 214 #ifdef HAPTIC_DRV2605L 215 uint8_t mode = HAPTIC_DEFAULT_MODE; 216 haptic_config.mode = mode; 217 #endif 218 #ifdef HAPTIC_SOLENOID 219 uint8_t dwell = SOLENOID_DEFAULT_DWELL; 220 haptic_config.dwell = dwell; 221 haptic_config.buzz = SOLENOID_DEFAULT_BUZZ; 222 solenoid_set_dwell(dwell); 223 #else 224 // This is to trigger haptic_reset again, if solenoid is enabled in the future. 225 haptic_config.dwell = 0; 226 haptic_config.buzz = 0; 227 #endif 228 eeconfig_update_haptic(&haptic_config); 229 dprintf("haptic_config.feedback = %u\n", haptic_config.feedback); 230 dprintf("haptic_config.mode = %u\n", haptic_config.mode); 231 } 232 233 void haptic_set_feedback(uint8_t feedback) { 234 haptic_config.feedback = feedback; 235 eeconfig_update_haptic(&haptic_config); 236 dprintf("haptic_config.feedback = %u\n", haptic_config.feedback); 237 } 238 239 void haptic_set_mode(uint8_t mode) { 240 haptic_config.mode = mode; 241 eeconfig_update_haptic(&haptic_config); 242 dprintf("haptic_config.mode = %u\n", haptic_config.mode); 243 } 244 245 void haptic_set_amplitude(uint8_t amp) { 246 haptic_config.amplitude = amp; 247 eeconfig_update_haptic(&haptic_config); 248 dprintf("haptic_config.amplitude = %u\n", haptic_config.amplitude); 249 #ifdef HAPTIC_DRV2605L 250 drv2605l_amplitude(amp); 251 #endif 252 } 253 254 void haptic_set_buzz(uint8_t buzz) { 255 haptic_config.buzz = buzz; 256 eeconfig_update_haptic(&haptic_config); 257 dprintf("haptic_config.buzz = %u\n", haptic_config.buzz); 258 } 259 260 void haptic_set_dwell(uint8_t dwell) { 261 haptic_config.dwell = dwell; 262 eeconfig_update_haptic(&haptic_config); 263 dprintf("haptic_config.dwell = %u\n", haptic_config.dwell); 264 } 265 266 uint8_t haptic_get_enable(void) { 267 return haptic_config.enable; 268 } 269 270 uint8_t haptic_get_mode(void) { 271 if (!haptic_config.enable) { 272 return false; 273 } 274 return haptic_config.mode; 275 } 276 277 uint8_t haptic_get_feedback(void) { 278 if (!haptic_config.enable) { 279 return false; 280 } 281 return haptic_config.feedback; 282 } 283 284 uint8_t haptic_get_dwell(void) { 285 if (!haptic_config.enable) { 286 return false; 287 } 288 return haptic_config.dwell; 289 } 290 291 void haptic_enable_continuous(void) { 292 haptic_config.cont = 1; 293 dprintf("haptic_config.cont = %u\n", haptic_config.cont); 294 eeconfig_update_haptic(&haptic_config); 295 #ifdef HAPTIC_DRV2605L 296 drv2605l_rtp_init(); 297 #endif 298 } 299 300 void haptic_disable_continuous(void) { 301 haptic_config.cont = 0; 302 dprintf("haptic_config.cont = %u\n", haptic_config.cont); 303 eeconfig_update_haptic(&haptic_config); 304 #ifdef HAPTIC_DRV2605L 305 drv2605l_write(DRV2605L_REG_MODE, 0x00); 306 #endif 307 } 308 309 void haptic_toggle_continuous(void) { 310 if (haptic_config.cont) { 311 haptic_disable_continuous(); 312 } else { 313 haptic_enable_continuous(); 314 } 315 } 316 317 void haptic_cont_increase(void) { 318 uint8_t amp = haptic_config.amplitude + 10; 319 if (haptic_config.amplitude >= 120) { 320 amp = 120; 321 } 322 haptic_set_amplitude(amp); 323 } 324 325 void haptic_cont_decrease(void) { 326 uint8_t amp = haptic_config.amplitude - 10; 327 if (haptic_config.amplitude < 20) { 328 amp = 20; 329 } 330 haptic_set_amplitude(amp); 331 } 332 333 void haptic_play(void) { 334 #ifdef HAPTIC_DRV2605L 335 uint8_t play_eff = 0; 336 play_eff = haptic_config.mode; 337 drv2605l_pulse(play_eff); 338 # if defined(SPLIT_KEYBOARD) && defined(SPLIT_HAPTIC_ENABLE) 339 split_haptic_play = haptic_config.mode; 340 # endif 341 #endif 342 #ifdef HAPTIC_SOLENOID 343 solenoid_fire_handler(); 344 # if defined(SPLIT_KEYBOARD) && defined(SPLIT_HAPTIC_ENABLE) 345 split_haptic_play = 1; 346 # endif 347 #endif 348 } 349 350 void haptic_shutdown(void) { 351 #ifdef HAPTIC_SOLENOID 352 solenoid_shutdown(); 353 #endif 354 } 355 356 void haptic_notify_usb_device_state_change(void) { 357 update_haptic_enable_gpios(); 358 #if defined(HAPTIC_ENABLE_PIN) 359 gpio_set_pin_output(HAPTIC_ENABLE_PIN); 360 #endif 361 #if defined(HAPTIC_ENABLE_STATUS_LED) 362 gpio_set_pin_output(HAPTIC_ENABLE_STATUS_LED); 363 #endif 364 }