solenoid.c 4.9 KB

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