haptic.c
10263 bytes
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)
33extern uint8_t split_haptic_play;
34#endif
35
36haptic_config_t haptic_config;
37
38static 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
56static void set_haptic_config_enable(bool enabled) {
57 haptic_config.enable = enabled;
58 update_haptic_enable_gpios();
59}
60
61void 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 haptic_config.raw = eeconfig_read_haptic();
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
106void 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
116void 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
122void 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.raw);
126}
127
128void 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.raw);
132}
133
134void haptic_toggle(void) {
135 if (haptic_config.enable) {
136 haptic_disable();
137 } else {
138 haptic_enable();
139 }
140 eeconfig_update_haptic(haptic_config.raw);
141}
142
143void 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.raw);
148}
149
150void 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
156void 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
166void 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
176void 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
193void 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
210void 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.raw);
229 dprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
230 dprintf("haptic_config.mode = %u\n", haptic_config.mode);
231}
232
233void haptic_set_feedback(uint8_t feedback) {
234 haptic_config.feedback = feedback;
235 eeconfig_update_haptic(haptic_config.raw);
236 dprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
237}
238
239void haptic_set_mode(uint8_t mode) {
240 haptic_config.mode = mode;
241 eeconfig_update_haptic(haptic_config.raw);
242 dprintf("haptic_config.mode = %u\n", haptic_config.mode);
243}
244
245void haptic_set_amplitude(uint8_t amp) {
246 haptic_config.amplitude = amp;
247 eeconfig_update_haptic(haptic_config.raw);
248 dprintf("haptic_config.amplitude = %u\n", haptic_config.amplitude);
249#ifdef HAPTIC_DRV2605L
250 drv2605l_amplitude(amp);
251#endif
252}
253
254void haptic_set_buzz(uint8_t buzz) {
255 haptic_config.buzz = buzz;
256 eeconfig_update_haptic(haptic_config.raw);
257 dprintf("haptic_config.buzz = %u\n", haptic_config.buzz);
258}
259
260void haptic_set_dwell(uint8_t dwell) {
261 haptic_config.dwell = dwell;
262 eeconfig_update_haptic(haptic_config.raw);
263 dprintf("haptic_config.dwell = %u\n", haptic_config.dwell);
264}
265
266uint8_t haptic_get_enable(void) {
267 return haptic_config.enable;
268}
269
270uint8_t haptic_get_mode(void) {
271 if (!haptic_config.enable) {
272 return false;
273 }
274 return haptic_config.mode;
275}
276
277uint8_t haptic_get_feedback(void) {
278 if (!haptic_config.enable) {
279 return false;
280 }
281 return haptic_config.feedback;
282}
283
284uint8_t haptic_get_dwell(void) {
285 if (!haptic_config.enable) {
286 return false;
287 }
288 return haptic_config.dwell;
289}
290
291void 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.raw);
295#ifdef HAPTIC_DRV2605L
296 drv2605l_rtp_init();
297#endif
298}
299
300void 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.raw);
304#ifdef HAPTIC_DRV2605L
305 drv2605l_write(DRV2605L_REG_MODE, 0x00);
306#endif
307}
308
309void haptic_toggle_continuous(void) {
310 if (haptic_config.cont) {
311 haptic_disable_continuous();
312 } else {
313 haptic_enable_continuous();
314 }
315}
316
317void 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
325void 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
333void 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
350void haptic_shutdown(void) {
351#ifdef HAPTIC_SOLENOID
352 solenoid_shutdown();
353#endif
354}
355
356void 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}