solenoid.c (5009B)
1 /* Copyright 2018 mtdjr - modified by ishtob 2 * Driver for solenoid 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 "timer.h" 19 #include "solenoid.h" 20 #include "haptic.h" 21 #include "gpio.h" 22 #include "usb_device_state.h" 23 #include "util.h" 24 #include <stdlib.h> 25 26 static pin_t solenoid_pads[] = SOLENOID_PINS; 27 #define NUMBER_OF_SOLENOIDS ARRAY_SIZE(solenoid_pads) 28 bool solenoid_on[NUMBER_OF_SOLENOIDS] = {false}; 29 bool solenoid_buzzing[NUMBER_OF_SOLENOIDS] = {false}; 30 uint16_t solenoid_start[NUMBER_OF_SOLENOIDS] = {0}; 31 #ifdef SOLENOID_PIN_ACTIVE_LOW 32 # define low true 33 # define high false 34 #else 35 # define low false 36 # define high true 37 #endif 38 static bool solenoid_active_state[NUMBER_OF_SOLENOIDS]; 39 40 extern haptic_config_t haptic_config; 41 42 void solenoid_buzz_on(void) { 43 haptic_set_buzz(1); 44 } 45 46 void solenoid_buzz_off(void) { 47 haptic_set_buzz(0); 48 } 49 50 void solenoid_set_buzz(uint8_t buzz) { 51 haptic_set_buzz(buzz); 52 } 53 54 void solenoid_set_dwell(uint8_t dwell) { 55 haptic_set_dwell(dwell); 56 } 57 58 /** 59 * @brief Stops a specific solenoid 60 * 61 * @param index select which solenoid to check/stop 62 */ 63 void solenoid_stop(uint8_t index) { 64 gpio_write_pin(solenoid_pads[index], !solenoid_active_state[index]); 65 solenoid_on[index] = false; 66 solenoid_buzzing[index] = false; 67 } 68 69 /** 70 * @brief Fires off a specific solenoid 71 * 72 * @param index Selects which solenoid to fire 73 */ 74 void solenoid_fire(uint8_t index) { 75 if (!haptic_config.buzz && solenoid_on[index]) return; 76 if (haptic_config.buzz && solenoid_buzzing[index]) return; 77 78 solenoid_on[index] = true; 79 solenoid_buzzing[index] = true; 80 solenoid_start[index] = timer_read(); 81 gpio_write_pin(solenoid_pads[index], solenoid_active_state[index]); 82 } 83 84 /** 85 * @brief Handles selecting a non-active solenoid, and firing it. 86 * 87 */ 88 void solenoid_fire_handler(void) { 89 #ifndef SOLENOID_RANDOM_FIRE 90 if (NUMBER_OF_SOLENOIDS > 1) { 91 uint8_t i = rand() % NUMBER_OF_SOLENOIDS; 92 if (!solenoid_on[i]) { 93 solenoid_fire(i); 94 } 95 } else { 96 solenoid_fire(0); 97 } 98 #else 99 for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) { 100 if (!solenoid_on[i]) { 101 solenoid_fire(i); 102 break; 103 } 104 } 105 #endif 106 } 107 108 /** 109 * @brief Checks active solenoid to stop them, and to handle buzz mode 110 * 111 */ 112 void solenoid_check(void) { 113 uint16_t elapsed[NUMBER_OF_SOLENOIDS] = {0}; 114 115 for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) { 116 if (!solenoid_on[i]) continue; 117 118 elapsed[i] = timer_elapsed(solenoid_start[i]); 119 120 // Check if it's time to finish this solenoid click cycle 121 if (elapsed[i] > haptic_config.dwell) { 122 solenoid_stop(i); 123 continue; 124 } 125 126 // Check whether to buzz the solenoid on and off 127 if (haptic_config.buzz) { 128 if ((elapsed[i] % (SOLENOID_BUZZ_ACTUATED + SOLENOID_BUZZ_NONACTUATED)) < SOLENOID_BUZZ_ACTUATED) { 129 if (!solenoid_buzzing[i]) { 130 solenoid_buzzing[i] = true; 131 gpio_write_pin(solenoid_pads[i], solenoid_active_state[i]); 132 } 133 } else { 134 if (solenoid_buzzing[i]) { 135 solenoid_buzzing[i] = false; 136 gpio_write_pin(solenoid_pads[i], !solenoid_active_state[i]); 137 } 138 } 139 } 140 } 141 } 142 143 /** 144 * @brief Initial configuration for solenoids 145 * 146 */ 147 void solenoid_setup(void) { 148 #ifdef SOLENOID_PINS_ACTIVE_STATE 149 bool state_temp[] = SOLENOID_PINS_ACTIVE_STATE; 150 uint8_t bound_check = ARRAY_SIZE(state_temp); 151 #endif 152 153 for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) { 154 #ifdef SOLENOID_PINS_ACTIVE_STATE 155 solenoid_active_state[i] = (bound_check - i) ? state_temp[i] : high; 156 #else 157 solenoid_active_state[i] = high; 158 #endif 159 gpio_write_pin(solenoid_pads[i], !solenoid_active_state[i]); 160 gpio_set_pin_output(solenoid_pads[i]); 161 if ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state_get_configure_state() == USB_DEVICE_STATE_CONFIGURED)) { 162 solenoid_fire(i); 163 } 164 } 165 } 166 167 /** 168 * @brief stops solenoids prior to device reboot, to prevent them from being locked on 169 * 170 */ 171 void solenoid_shutdown(void) { 172 for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) { 173 gpio_write_pin(solenoid_pads[i], !solenoid_active_state[i]); 174 } 175 }