ckled2001.c 8.3 KB

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