logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

ec_switch_matrix.c (13748B)


  1. /* Copyright 2023 Cipulot
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "ec_switch_matrix.h"
  17. #include "analog.h"
  18. #include "atomic_util.h"
  19. #include "math.h"
  20. #include "print.h"
  21. #include "wait.h"
  22. #if defined(__AVR__)
  23. # error "AVR platforms not supported due to a variety of reasons. Among them there are limited memory, limited number of pins and ADC not being able to give satisfactory results."
  24. #endif
  25. #define OPEN_DRAIN_SUPPORT defined(PAL_MODE_OUTPUT_OPENDRAIN)
  26. eeprom_ec_config_t eeprom_ec_config;
  27. ec_config_t ec_config;
  28. // Pin and port array
  29. const pin_t row_pins[] = MATRIX_ROW_PINS;
  30. const pin_t amux_sel_pins[] = AMUX_SEL_PINS;
  31. const pin_t amux_en_pins[] = AMUX_EN_PINS;
  32. const pin_t amux_n_col_sizes[] = AMUX_COL_CHANNELS_SIZES;
  33. const pin_t amux_n_col_channels[][AMUX_MAX_COLS_COUNT] = {AMUX_COL_CHANNELS};
  34. #ifdef UNUSED_POSITIONS_LIST
  35. const uint8_t UNUSED_POSITIONS[][2] = UNUSED_POSITIONS_LIST;
  36. # define UNUSED_POSITIONS_COUNT (sizeof(UNUSED_POSITIONS) / sizeof(UNUSED_POSITIONS[0]))
  37. #endif
  38. #define AMUX_SEL_PINS_COUNT ARRAY_SIZE(amux_sel_pins)
  39. #define EXPECTED_AMUX_SEL_PINS_COUNT ceil(log2(AMUX_MAX_COLS_COUNT)
  40. // Checks for the correctness of the configuration
  41. _Static_assert(ARRAY_SIZE(amux_en_pins) == AMUX_COUNT, "AMUX_EN_PINS doesn't have the minimum number of bits required to enable all the multiplexers available");
  42. // Check that number of select pins is enough to select all the channels
  43. _Static_assert(AMUX_SEL_PINS_COUNT == EXPECTED_AMUX_SEL_PINS_COUNT), "AMUX_SEL_PINS doesn't have the minimum number of bits required address all the channels");
  44. // Check that number of elements in AMUX_COL_CHANNELS_SIZES is enough to specify the number of channels for all the multiplexers available
  45. _Static_assert(ARRAY_SIZE(amux_n_col_sizes) == AMUX_COUNT, "AMUX_COL_CHANNELS_SIZES doesn't have the minimum number of elements required to specify the number of channels for all the multiplexers available");
  46. static uint16_t sw_value[MATRIX_ROWS][MATRIX_COLS];
  47. static adc_mux adcMux;
  48. // Initialize the row pins
  49. void init_row(void) {
  50. // Set all row pins as output and low
  51. for (uint8_t idx = 0; idx < MATRIX_ROWS; idx++) {
  52. gpio_set_pin_output(row_pins[idx]);
  53. gpio_write_pin_low(row_pins[idx]);
  54. }
  55. }
  56. // Initialize the multiplexers
  57. void init_amux(void) {
  58. for (uint8_t idx = 0; idx < AMUX_COUNT; idx++) {
  59. gpio_set_pin_output(amux_en_pins[idx]);
  60. gpio_write_pin_low(amux_en_pins[idx]);
  61. }
  62. for (uint8_t idx = 0; idx < AMUX_SEL_PINS_COUNT; idx++) {
  63. gpio_set_pin_output(amux_sel_pins[idx]);
  64. }
  65. }
  66. // Disable all the unused rows
  67. void disable_unused_row(uint8_t row) {
  68. // disable all the other rows apart from the current selected one
  69. for (uint8_t idx = 0; idx < MATRIX_ROWS; idx++) {
  70. if (idx != row) {
  71. gpio_write_pin_low(row_pins[idx]);
  72. }
  73. }
  74. }
  75. // Select the multiplexer channel of the specified multiplexer
  76. void select_amux_channel(uint8_t channel, uint8_t col) {
  77. // Get the channel for the specified multiplexer
  78. uint8_t ch = amux_n_col_channels[channel][col];
  79. // momentarily disable specified multiplexer
  80. gpio_write_pin_high(amux_en_pins[channel]);
  81. // Select the multiplexer channel
  82. for (uint8_t i = 0; i < AMUX_SEL_PINS_COUNT; i++) {
  83. gpio_write_pin(amux_sel_pins[i], ch & (1 << i));
  84. }
  85. // re enable specified multiplexer
  86. gpio_write_pin_low(amux_en_pins[channel]);
  87. }
  88. // Disable all the unused multiplexers
  89. void disable_unused_amux(uint8_t channel) {
  90. // disable all the other multiplexers apart from the current selected one
  91. for (uint8_t idx = 0; idx < AMUX_COUNT; idx++) {
  92. if (idx != channel) {
  93. gpio_write_pin_high(amux_en_pins[idx]);
  94. }
  95. }
  96. }
  97. // Discharge the peak hold capacitor
  98. void discharge_capacitor(void) {
  99. #ifdef OPEN_DRAIN_SUPPORT
  100. gpio_write_pin_low(DISCHARGE_PIN);
  101. #else
  102. gpio_write_pin_low(DISCHARGE_PIN);
  103. gpio_set_pin_output(DISCHARGE_PIN);
  104. #endif
  105. }
  106. // Charge the peak hold capacitor
  107. void charge_capacitor(uint8_t row) {
  108. #ifdef OPEN_DRAIN_SUPPORT
  109. gpio_write_pin_high(DISCHARGE_PIN);
  110. #else
  111. gpio_set_pin_input(DISCHARGE_PIN);
  112. #endif
  113. gpio_write_pin_high(row_pins[row]);
  114. }
  115. // Initialize the peripherals pins
  116. int ec_init(void) {
  117. // Initialize ADC
  118. palSetLineMode(ANALOG_PORT, PAL_MODE_INPUT_ANALOG);
  119. adcMux = pinToMux(ANALOG_PORT);
  120. // Dummy call to make sure that adcStart() has been called in the appropriate state
  121. adc_read(adcMux);
  122. // Initialize discharge pin as discharge mode
  123. gpio_write_pin_low(DISCHARGE_PIN);
  124. #ifdef OPEN_DRAIN_SUPPORT
  125. gpio_set_pin_output_open_drain(DISCHARGE_PIN);
  126. #else
  127. gpio_set_pin_output(DISCHARGE_PIN);
  128. #endif
  129. // Initialize drive lines
  130. init_row();
  131. // Initialize AMUXs
  132. init_amux();
  133. return 0;
  134. }
  135. // Get the noise floor
  136. void ec_noise_floor(void) {
  137. // Initialize the noise floor
  138. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  139. for (uint8_t col = 0; col < MATRIX_COLS; col++) {
  140. ec_config.noise_floor[row][col] = 0;
  141. }
  142. }
  143. // Sample the noise floor
  144. for (uint8_t i = 0; i < DEFAULT_NOISE_FLOOR_SAMPLING_COUNT; i++) {
  145. for (uint8_t amux = 0; amux < AMUX_COUNT; amux++) {
  146. disable_unused_amux(amux);
  147. for (uint8_t col = 0; col < amux_n_col_sizes[amux]; col++) {
  148. uint8_t sum = 0;
  149. for (uint8_t i = 0; i < (amux > 0 ? amux : 0); i++)
  150. sum += amux_n_col_sizes[i];
  151. uint8_t adjusted_col = col + sum;
  152. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  153. #ifdef UNUSED_POSITIONS_LIST
  154. if (is_unused_position(row, adjusted_col)) continue;
  155. #endif
  156. disable_unused_row(row);
  157. ec_config.noise_floor[row][adjusted_col] += ec_readkey_raw(amux, row, col);
  158. }
  159. }
  160. }
  161. wait_ms(5);
  162. }
  163. // Average the noise floor
  164. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  165. for (uint8_t col = 0; col < MATRIX_COLS; col++) {
  166. ec_config.noise_floor[row][col] /= DEFAULT_NOISE_FLOOR_SAMPLING_COUNT;
  167. }
  168. }
  169. }
  170. // Scan key values and update matrix state
  171. bool ec_matrix_scan(matrix_row_t current_matrix[]) {
  172. bool updated = false;
  173. for (uint8_t amux = 0; amux < AMUX_COUNT; amux++) {
  174. disable_unused_amux(amux);
  175. for (uint8_t col = 0; col < amux_n_col_sizes[amux]; col++) {
  176. uint8_t sum = 0;
  177. for (uint8_t i = 0; i < (amux > 0 ? amux : 0); i++)
  178. sum += amux_n_col_sizes[i];
  179. uint8_t adjusted_col = col + sum;
  180. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  181. #ifdef UNUSED_POSITIONS_LIST
  182. if (is_unused_position(row, adjusted_col)) continue;
  183. #endif
  184. disable_unused_row(row);
  185. sw_value[row][adjusted_col] = ec_readkey_raw(amux, row, col);
  186. if (ec_config.bottoming_calibration) {
  187. if (ec_config.bottoming_calibration_starter[row][adjusted_col]) {
  188. ec_config.bottoming_reading[row][adjusted_col] = sw_value[row][adjusted_col];
  189. ec_config.bottoming_calibration_starter[row][adjusted_col] = false;
  190. } else if (sw_value[row][adjusted_col] > ec_config.bottoming_reading[row][adjusted_col]) {
  191. ec_config.bottoming_reading[row][adjusted_col] = sw_value[row][adjusted_col];
  192. }
  193. } else {
  194. updated |= ec_update_key(&current_matrix[row], row, adjusted_col, sw_value[row][adjusted_col]);
  195. }
  196. }
  197. }
  198. }
  199. return ec_config.bottoming_calibration ? false : updated;
  200. }
  201. // Read the capacitive sensor value
  202. uint16_t ec_readkey_raw(uint8_t channel, uint8_t row, uint8_t col) {
  203. uint16_t sw_value = 0;
  204. // Select the multiplexer
  205. select_amux_channel(channel, col);
  206. // Set the row pin to low state to avoid ghosting
  207. gpio_write_pin_low(row_pins[row]);
  208. ATOMIC_BLOCK_FORCEON {
  209. // Set the row pin to high state and have capacitor charge
  210. charge_capacitor(row);
  211. // Read the ADC value
  212. sw_value = adc_read(adcMux);
  213. }
  214. // Discharge peak hold capacitor
  215. discharge_capacitor();
  216. // Waiting for the ghost capacitor to discharge fully
  217. wait_us(DISCHARGE_TIME);
  218. return sw_value;
  219. }
  220. // Update press/release state of key
  221. bool ec_update_key(matrix_row_t *current_row, uint8_t row, uint8_t col, uint16_t sw_value) {
  222. bool current_state = (*current_row >> col) & 1;
  223. // Real Time Noise Floor Calibration
  224. if (sw_value < (ec_config.noise_floor[row][col] - NOISE_FLOOR_THRESHOLD)) {
  225. uprintf("Noise Floor Change: %d, %d, %d\n", row, col, sw_value);
  226. ec_config.noise_floor[row][col] = sw_value;
  227. ec_config.rescaled_mode_0_actuation_threshold[row][col] = rescale(ec_config.mode_0_actuation_threshold, 0, 1023, ec_config.noise_floor[row][col], eeprom_ec_config.bottoming_reading[row][col]);
  228. ec_config.rescaled_mode_0_release_threshold[row][col] = rescale(ec_config.mode_0_release_threshold, 0, 1023, ec_config.noise_floor[row][col], eeprom_ec_config.bottoming_reading[row][col]);
  229. ec_config.rescaled_mode_1_initial_deadzone_offset[row][col] = rescale(ec_config.mode_1_initial_deadzone_offset, 0, 1023, ec_config.noise_floor[row][col], eeprom_ec_config.bottoming_reading[row][col]);
  230. }
  231. // Normal board-wide APC
  232. if (ec_config.actuation_mode == 0) {
  233. if (current_state && sw_value < ec_config.rescaled_mode_0_release_threshold[row][col]) {
  234. *current_row &= ~(1 << col);
  235. uprintf("Key released: %d, %d, %d\n", row, col, sw_value);
  236. return true;
  237. }
  238. if ((!current_state) && sw_value > ec_config.rescaled_mode_0_actuation_threshold[row][col]) {
  239. *current_row |= (1 << col);
  240. uprintf("Key pressed: %d, %d, %d\n", row, col, sw_value);
  241. return true;
  242. }
  243. }
  244. // Rapid Trigger
  245. else if (ec_config.actuation_mode == 1) {
  246. // Is key in active zone?
  247. if (sw_value > ec_config.rescaled_mode_1_initial_deadzone_offset[row][col]) {
  248. // Is key pressed while in active zone?
  249. if (current_state) {
  250. // Is the key still moving down?
  251. if (sw_value > ec_config.extremum[row][col]) {
  252. ec_config.extremum[row][col] = sw_value;
  253. uprintf("Key pressed: %d, %d, %d\n", row, col, sw_value);
  254. }
  255. // Has key moved up enough to be released?
  256. else if (sw_value < ec_config.extremum[row][col] - ec_config.rescaled_mode_1_release_offset[row][col]) {
  257. ec_config.extremum[row][col] = sw_value;
  258. *current_row &= ~(1 << col);
  259. uprintf("Key released: %d, %d, %d\n", row, col, sw_value);
  260. return true;
  261. }
  262. }
  263. // Key is not pressed while in active zone
  264. else {
  265. // Is the key still moving up?
  266. if (sw_value < ec_config.extremum[row][col]) {
  267. ec_config.extremum[row][col] = sw_value;
  268. }
  269. // Has key moved down enough to be pressed?
  270. else if (sw_value > ec_config.extremum[row][col] + ec_config.rescaled_mode_1_actuation_offset[row][col]) {
  271. ec_config.extremum[row][col] = sw_value;
  272. *current_row |= (1 << col);
  273. uprintf("Key pressed: %d, %d, %d\n", row, col, sw_value);
  274. return true;
  275. }
  276. }
  277. }
  278. // Key is not in active zone
  279. else {
  280. // Check to avoid key being stuck in pressed state near the active zone threshold
  281. if (sw_value < ec_config.extremum[row][col]) {
  282. ec_config.extremum[row][col] = sw_value;
  283. *current_row &= ~(1 << col);
  284. return true;
  285. }
  286. }
  287. }
  288. return false;
  289. }
  290. // Print the matrix values
  291. void ec_print_matrix(void) {
  292. for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
  293. for (uint8_t col = 0; col < MATRIX_COLS - 1; col++) {
  294. uprintf("%4d,", sw_value[row][col]);
  295. }
  296. uprintf("%4d\n", sw_value[row][MATRIX_COLS - 1]);
  297. }
  298. print("\n");
  299. }
  300. // Check if the position is unused
  301. #ifdef UNUSED_POSITIONS_LIST
  302. bool is_unused_position(uint8_t row, uint8_t col) {
  303. for (uint8_t i = 0; i < UNUSED_POSITIONS_COUNT; i++) {
  304. if (UNUSED_POSITIONS[i][0] == row && UNUSED_POSITIONS[i][1] == col) {
  305. return true;
  306. }
  307. }
  308. return false;
  309. }
  310. #endif
  311. // Rescale the value to a different range
  312. uint16_t rescale(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max) {
  313. return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
  314. }