ckled2001.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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.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_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
  131. ckled2001_led led;
  132. if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
  133. memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
  134. g_pwm_buffer[led.driver][led.r] = red;
  135. g_pwm_buffer[led.driver][led.g] = green;
  136. g_pwm_buffer[led.driver][led.b] = blue;
  137. g_pwm_buffer_update_required[led.driver] = true;
  138. }
  139. }
  140. void CKLED2001_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
  141. for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
  142. CKLED2001_set_color(i, red, green, blue);
  143. }
  144. }
  145. void CKLED2001_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
  146. ckled2001_led led;
  147. memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
  148. uint8_t control_register_r = led.r / 8;
  149. uint8_t control_register_g = led.g / 8;
  150. uint8_t control_register_b = led.b / 8;
  151. uint8_t bit_r = led.r % 8;
  152. uint8_t bit_g = led.g % 8;
  153. uint8_t bit_b = led.b % 8;
  154. if (red) {
  155. g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
  156. } else {
  157. g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
  158. }
  159. if (green) {
  160. g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
  161. } else {
  162. g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
  163. }
  164. if (blue) {
  165. g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
  166. } else {
  167. g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
  168. }
  169. g_led_control_registers_update_required[led.driver] = true;
  170. }
  171. void CKLED2001_update_pwm_buffers(uint8_t addr, uint8_t index) {
  172. if (g_pwm_buffer_update_required[index]) {
  173. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
  174. // If any of the transactions fail we risk writing dirty PG0,
  175. // refresh page 0 just in case.
  176. if (!CKLED2001_write_pwm_buffer(addr, g_pwm_buffer[index])) {
  177. g_led_control_registers_update_required[index] = true;
  178. }
  179. }
  180. g_pwm_buffer_update_required[index] = false;
  181. }
  182. void CKLED2001_update_led_control_registers(uint8_t addr, uint8_t index) {
  183. if (g_led_control_registers_update_required[index]) {
  184. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
  185. for (int i = 0; i < 24; i++) {
  186. CKLED2001_write_register(addr, i, g_led_control_registers[index][i]);
  187. }
  188. }
  189. g_led_control_registers_update_required[index] = false;
  190. }
  191. void CKLED2001_sw_return_normal(uint8_t addr) {
  192. // Select to function page
  193. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  194. // Setting LED driver to normal mode
  195. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
  196. }
  197. void CKLED2001_sw_shutdown(uint8_t addr) {
  198. // Select to function page
  199. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  200. // Setting LED driver to shutdown mode
  201. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
  202. // Write SW Sleep Register
  203. CKLED2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE);
  204. }