qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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 }