ckled2001-simple.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /* Copyright 2021 @ Keychron (https://www.keychron.com)
  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 2 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 "ckled2001-simple.h"
  17. #include "i2c_master.h"
  18. #include "wait.h"
  19. #ifndef CKLED2001_TIMEOUT
  20. # define CKLED2001_TIMEOUT 100
  21. #endif
  22. #ifndef CKLED2001_PERSISTENCE
  23. # define CKLED2001_PERSISTENCE 0
  24. #endif
  25. #ifndef PHASE_CHANNEL
  26. # define PHASE_CHANNEL MSKPHASE_12CHANNEL
  27. #endif
  28. #ifndef CKLED2001_CURRENT_TUNE
  29. # define CKLED2001_CURRENT_TUNE \
  30. { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
  31. #endif
  32. // Transfer buffer for TWITransmitData()
  33. uint8_t g_twi_transfer_buffer[20];
  34. // These buffers match the CKLED2001 PWM registers.
  35. // The control buffers match the PG0 LED On/Off registers.
  36. // Storing them like this is optimal for I2C transfers to the registers.
  37. // We could optimize this and take out the unused registers from these
  38. // buffers and the transfers in CKLED2001_write_pwm_buffer() but it's
  39. // probably not worth the extra complexity.
  40. uint8_t g_pwm_buffer[DRIVER_COUNT][192];
  41. bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
  42. uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
  43. bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
  44. bool CKLED2001_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
  45. // If the transaction fails function returns false.
  46. g_twi_transfer_buffer[0] = reg;
  47. g_twi_transfer_buffer[1] = data;
  48. #if CKLED2001_PERSISTENCE > 0
  49. for (uint8_t i = 0; i < CKLED2001_PERSISTENCE; i++) {
  50. if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, CKLED2001_TIMEOUT) != 0) {
  51. return false;
  52. }
  53. }
  54. #else
  55. if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, CKLED2001_TIMEOUT) != 0) {
  56. return false;
  57. }
  58. #endif
  59. return true;
  60. }
  61. bool CKLED2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
  62. // Assumes PG1 is already selected.
  63. // If any of the transactions fails function returns false.
  64. // Transmit PWM registers in 12 transfers of 16 bytes.
  65. // g_twi_transfer_buffer[] is 20 bytes
  66. // Iterate over the pwm_buffer contents at 16 byte intervals.
  67. for (int i = 0; i < 192; i += 16) {
  68. g_twi_transfer_buffer[0] = i;
  69. // Copy the data from i to i+15.
  70. // Device will auto-increment register for data after the first byte
  71. // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
  72. for (int j = 0; j < 16; j++) {
  73. g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
  74. }
  75. #if CKLED2001_PERSISTENCE > 0
  76. for (uint8_t i = 0; i < CKLED2001_PERSISTENCE; i++) {
  77. if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, CKLED2001_TIMEOUT) != 0) {
  78. return false;
  79. }
  80. }
  81. #else
  82. if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, CKLED2001_TIMEOUT) != 0) {
  83. return false;
  84. }
  85. #endif
  86. }
  87. return true;
  88. }
  89. void CKLED2001_init(uint8_t addr) {
  90. // Select to function page
  91. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  92. // Setting LED driver to shutdown mode
  93. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
  94. // Setting internal channel pulldown/pullup
  95. CKLED2001_write_register(addr, PDU_REG, MSKSET_CA_CB_CHANNEL);
  96. // Select number of scan phase
  97. CKLED2001_write_register(addr, SCAN_PHASE_REG, PHASE_CHANNEL);
  98. // Setting PWM Delay Phase
  99. CKLED2001_write_register(addr, SLEW_RATE_CONTROL_MODE1_REG, MSKPWM_DELAY_PHASE_ENABLE);
  100. // Setting Driving/Sinking Channel Slew Rate
  101. CKLED2001_write_register(addr, SLEW_RATE_CONTROL_MODE2_REG, MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE);
  102. // Setting Iref
  103. CKLED2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_DISABLE);
  104. // Set LED CONTROL PAGE (Page 0)
  105. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
  106. for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
  107. CKLED2001_write_register(addr, i, 0x00);
  108. }
  109. // Set PWM PAGE (Page 1)
  110. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
  111. for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; i++) {
  112. CKLED2001_write_register(addr, i, 0x00);
  113. }
  114. // Set CURRENT PAGE (Page 4)
  115. uint8_t current_tuen_reg_list[LED_CURRENT_TUNE_LENGTH] = CKLED2001_CURRENT_TUNE;
  116. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, CURRENT_TUNE_PAGE);
  117. for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; i++) {
  118. CKLED2001_write_register(addr, i, current_tuen_reg_list[i]);
  119. }
  120. // Enable LEDs ON/OFF
  121. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
  122. for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
  123. CKLED2001_write_register(addr, i, 0xFF);
  124. }
  125. // Select to function page
  126. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  127. // Setting LED driver to normal mode
  128. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
  129. }
  130. void CKLED2001_set_value(int index, uint8_t value) {
  131. ckled2001_led led;
  132. if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
  133. memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
  134. g_pwm_buffer[led.driver][led.v] = value;
  135. g_pwm_buffer_update_required[led.driver] = true;
  136. }
  137. }
  138. void CKLED2001_set_value_all(uint8_t value) {
  139. for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
  140. CKLED2001_set_value(i, value);
  141. }
  142. }
  143. void CKLED2001_set_led_control_register(uint8_t index, bool value) {
  144. ckled2001_led led;
  145. memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
  146. uint8_t control_register = led.v / 8;
  147. uint8_t bit_value = led.v % 8;
  148. if (value) {
  149. g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
  150. } else {
  151. g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
  152. }
  153. g_led_control_registers_update_required[led.driver] = true;
  154. }
  155. void CKLED2001_update_pwm_buffers(uint8_t addr, uint8_t index) {
  156. if (g_pwm_buffer_update_required[index]) {
  157. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
  158. // If any of the transactions fail we risk writing dirty PG0,
  159. // refresh page 0 just in case.
  160. if (!CKLED2001_write_pwm_buffer(addr, g_pwm_buffer[index])) {
  161. g_led_control_registers_update_required[index] = true;
  162. }
  163. }
  164. g_pwm_buffer_update_required[index] = false;
  165. }
  166. void CKLED2001_update_led_control_registers(uint8_t addr, uint8_t index) {
  167. if (g_led_control_registers_update_required[index]) {
  168. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
  169. for (int i = 0; i < 24; i++) {
  170. CKLED2001_write_register(addr, i, g_led_control_registers[index][i]);
  171. }
  172. }
  173. g_led_control_registers_update_required[index] = false;
  174. }
  175. void CKLED2001_sw_return_normal(uint8_t addr) {
  176. // Select to function page
  177. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  178. // Setting LED driver to normal mode
  179. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
  180. }
  181. void CKLED2001_sw_shutdown(uint8_t addr) {
  182. // Select to function page
  183. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  184. // Setting LED driver to shutdown mode
  185. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
  186. // Write SW Sleep Register
  187. CKLED2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE);
  188. }