You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
4.9 KiB

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