ckled2001.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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[65];
  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 3 transfers of 64 bytes.
  65. // Iterate over the pwm_buffer contents at 64 byte intervals.
  66. for (uint8_t i = 0; i < 192; i += 64) {
  67. g_twi_transfer_buffer[0] = i;
  68. // Copy the data from i to i+63.
  69. // Device will auto-increment register for data after the first byte
  70. // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
  71. for (uint8_t j = 0; j < 64; j++) {
  72. g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
  73. }
  74. #if CKLED2001_PERSISTENCE > 0
  75. for (uint8_t i = 0; i < CKLED2001_PERSISTENCE; i++) {
  76. if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, CKLED2001_TIMEOUT) != 0) {
  77. return false;
  78. }
  79. }
  80. #else
  81. if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, CKLED2001_TIMEOUT) != 0) {
  82. return false;
  83. }
  84. #endif
  85. }
  86. return true;
  87. }
  88. void CKLED2001_init(uint8_t addr) {
  89. // Select to function page
  90. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  91. // Setting LED driver to shutdown mode
  92. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
  93. // Setting internal channel pulldown/pullup
  94. CKLED2001_write_register(addr, PDU_REG, MSKSET_CA_CB_CHANNEL);
  95. // Select number of scan phase
  96. CKLED2001_write_register(addr, SCAN_PHASE_REG, PHASE_CHANNEL);
  97. // Setting PWM Delay Phase
  98. CKLED2001_write_register(addr, SLEW_RATE_CONTROL_MODE1_REG, MSKPWM_DELAY_PHASE_ENABLE);
  99. // Setting Driving/Sinking Channel Slew Rate
  100. CKLED2001_write_register(addr, SLEW_RATE_CONTROL_MODE2_REG, MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE);
  101. // Setting Iref
  102. CKLED2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_DISABLE);
  103. // Set LED CONTROL PAGE (Page 0)
  104. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
  105. for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
  106. CKLED2001_write_register(addr, i, 0x00);
  107. }
  108. // Set PWM PAGE (Page 1)
  109. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
  110. for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; i++) {
  111. CKLED2001_write_register(addr, i, 0x00);
  112. }
  113. // Set CURRENT PAGE (Page 4)
  114. uint8_t current_tuen_reg_list[LED_CURRENT_TUNE_LENGTH] = CKLED2001_CURRENT_TUNE;
  115. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, CURRENT_TUNE_PAGE);
  116. for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; i++) {
  117. CKLED2001_write_register(addr, i, current_tuen_reg_list[i]);
  118. }
  119. // Enable LEDs ON/OFF
  120. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
  121. for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
  122. CKLED2001_write_register(addr, i, 0xFF);
  123. }
  124. // Select to function page
  125. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  126. // Setting LED driver to normal mode
  127. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
  128. }
  129. void CKLED2001_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
  130. ckled2001_led led;
  131. if (index >= 0 && index < DRIVER_LED_TOTAL) {
  132. memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
  133. g_pwm_buffer[led.driver][led.r] = red;
  134. g_pwm_buffer[led.driver][led.g] = green;
  135. g_pwm_buffer[led.driver][led.b] = blue;
  136. g_pwm_buffer_update_required[led.driver] = true;
  137. }
  138. }
  139. void CKLED2001_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
  140. for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
  141. CKLED2001_set_color(i, red, green, blue);
  142. }
  143. }
  144. void CKLED2001_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
  145. ckled2001_led led;
  146. memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
  147. uint8_t control_register_r = led.r / 8;
  148. uint8_t control_register_g = led.g / 8;
  149. uint8_t control_register_b = led.b / 8;
  150. uint8_t bit_r = led.r % 8;
  151. uint8_t bit_g = led.g % 8;
  152. uint8_t bit_b = led.b % 8;
  153. if (red) {
  154. g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
  155. } else {
  156. g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
  157. }
  158. if (green) {
  159. g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
  160. } else {
  161. g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
  162. }
  163. if (blue) {
  164. g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
  165. } else {
  166. g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
  167. }
  168. g_led_control_registers_update_required[led.driver] = true;
  169. }
  170. void CKLED2001_update_pwm_buffers(uint8_t addr, uint8_t index) {
  171. if (g_pwm_buffer_update_required[index]) {
  172. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
  173. // If any of the transactions fail we risk writing dirty PG0,
  174. // refresh page 0 just in case.
  175. if (!CKLED2001_write_pwm_buffer(addr, g_pwm_buffer[index])) {
  176. g_led_control_registers_update_required[index] = true;
  177. }
  178. }
  179. g_pwm_buffer_update_required[index] = false;
  180. }
  181. void CKLED2001_update_led_control_registers(uint8_t addr, uint8_t index) {
  182. if (g_led_control_registers_update_required[index]) {
  183. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
  184. for (int i = 0; i < 24; i++) {
  185. CKLED2001_write_register(addr, i, g_led_control_registers[index][i]);
  186. }
  187. }
  188. g_led_control_registers_update_required[index] = false;
  189. }
  190. void CKLED2001_sw_return_normal(uint8_t addr) {
  191. // Select to function page
  192. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  193. // Setting LED driver to normal mode
  194. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
  195. }
  196. void CKLED2001_sw_shutdown(uint8_t addr) {
  197. // Select to function page
  198. CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
  199. // Setting LED driver to shutdown mode
  200. CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
  201. // Write SW Sleep Register
  202. CKLED2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE);
  203. }