You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
8.6 KiB

  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 "snled27351-mono.h"
  17. #include "i2c_master.h"
  18. #define SNLED27351_PWM_REGISTER_COUNT 192
  19. #define SNLED27351_LED_CONTROL_REGISTER_COUNT 24
  20. #ifndef SNLED27351_I2C_TIMEOUT
  21. # define SNLED27351_I2C_TIMEOUT 100
  22. #endif
  23. #ifndef SNLED27351_I2C_PERSISTENCE
  24. # define SNLED27351_I2C_PERSISTENCE 0
  25. #endif
  26. #ifndef SNLED27351_PHASE_CHANNEL
  27. # define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL
  28. #endif
  29. #ifndef SNLED27351_CURRENT_TUNE
  30. # define SNLED27351_CURRENT_TUNE \
  31. { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
  32. #endif
  33. const uint8_t i2c_addresses[SNLED27351_DRIVER_COUNT] = {
  34. SNLED27351_I2C_ADDRESS_1,
  35. #ifdef SNLED27351_I2C_ADDRESS_2
  36. SNLED27351_I2C_ADDRESS_2,
  37. # ifdef SNLED27351_I2C_ADDRESS_3
  38. SNLED27351_I2C_ADDRESS_3,
  39. # ifdef SNLED27351_I2C_ADDRESS_4
  40. SNLED27351_I2C_ADDRESS_4,
  41. # endif
  42. # endif
  43. #endif
  44. };
  45. // These buffers match the SNLED27351 PWM registers.
  46. // The control buffers match the PG0 LED On/Off registers.
  47. // Storing them like this is optimal for I2C transfers to the registers.
  48. // We could optimize this and take out the unused registers from these
  49. // buffers and the transfers in snled27351_write_pwm_buffer() but it's
  50. // probably not worth the extra complexity.
  51. uint8_t g_pwm_buffer[SNLED27351_DRIVER_COUNT][SNLED27351_PWM_REGISTER_COUNT];
  52. bool g_pwm_buffer_update_required[SNLED27351_DRIVER_COUNT] = {false};
  53. uint8_t g_led_control_registers[SNLED27351_DRIVER_COUNT][SNLED27351_LED_CONTROL_REGISTER_COUNT] = {0};
  54. bool g_led_control_registers_update_required[SNLED27351_DRIVER_COUNT] = {false};
  55. void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data) {
  56. #if SNLED27351_I2C_PERSISTENCE > 0
  57. for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
  58. if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
  59. }
  60. #else
  61. i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT);
  62. #endif
  63. }
  64. void snled27351_select_page(uint8_t index, uint8_t page) {
  65. snled27351_write_register(index, SNLED27351_REG_COMMAND, page);
  66. }
  67. void snled27351_write_pwm_buffer(uint8_t index) {
  68. // Assumes PG1 is already selected.
  69. // Transmit PWM registers in 12 transfers of 16 bytes.
  70. // Iterate over the pwm_buffer contents at 16 byte intervals.
  71. for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) {
  72. #if SNLED27351_I2C_PERSISTENCE > 0
  73. for (uint8_t j = 0; j < SNLED27351_I2C_PERSISTENCE; j++) {
  74. if (i2c_write_register(i2c_addresses[index] << 1, i, g_pwm_buffer[index] + i, 16, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;
  75. }
  76. #else
  77. i2c_write_register(i2c_addresses[index] << 1, i, g_pwm_buffer[index] + i, 16, SNLED27351_I2C_TIMEOUT);
  78. #endif
  79. }
  80. }
  81. void snled27351_init_drivers(void) {
  82. i2c_init();
  83. for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {
  84. snled27351_init(i);
  85. }
  86. for (int i = 0; i < SNLED27351_LED_COUNT; i++) {
  87. snled27351_set_led_control_register(i, true);
  88. }
  89. for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {
  90. snled27351_update_led_control_registers(i);
  91. }
  92. }
  93. void snled27351_init(uint8_t index) {
  94. snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
  95. // Setting LED driver to shutdown mode
  96. snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
  97. // Setting internal channel pulldown/pullup
  98. snled27351_write_register(index, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED);
  99. // Select number of scan phase
  100. snled27351_write_register(index, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL);
  101. // Setting PWM Delay Phase
  102. snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE);
  103. // Setting Driving/Sinking Channel Slew Rate
  104. snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE);
  105. // Setting Iref
  106. snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0);
  107. snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);
  108. for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
  109. snled27351_write_register(index, i, 0x00);
  110. }
  111. snled27351_select_page(index, SNLED27351_COMMAND_PWM);
  112. for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
  113. snled27351_write_register(index, i, 0x00);
  114. }
  115. snled27351_select_page(index, SNLED27351_COMMAND_CURRENT_TUNE);
  116. uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE;
  117. for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
  118. snled27351_write_register(index, i, current_tune_reg_list[i]);
  119. }
  120. snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);
  121. for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
  122. snled27351_write_register(index, i, 0xFF);
  123. }
  124. snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
  125. // Setting LED driver to normal mode
  126. snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
  127. }
  128. void snled27351_set_value(int index, uint8_t value) {
  129. snled27351_led_t led;
  130. if (index >= 0 && index < SNLED27351_LED_COUNT) {
  131. memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));
  132. if (g_pwm_buffer[led.driver][led.v] == value) {
  133. return;
  134. }
  135. g_pwm_buffer[led.driver][led.v] = value;
  136. g_pwm_buffer_update_required[led.driver] = true;
  137. }
  138. }
  139. void snled27351_set_value_all(uint8_t value) {
  140. for (int i = 0; i < SNLED27351_LED_COUNT; i++) {
  141. snled27351_set_value(i, value);
  142. }
  143. }
  144. void snled27351_set_led_control_register(uint8_t index, bool value) {
  145. snled27351_led_t led;
  146. memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));
  147. uint8_t control_register = led.v / 8;
  148. uint8_t bit_value = led.v % 8;
  149. if (value) {
  150. g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
  151. } else {
  152. g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
  153. }
  154. g_led_control_registers_update_required[led.driver] = true;
  155. }
  156. void snled27351_update_pwm_buffers(uint8_t index) {
  157. if (g_pwm_buffer_update_required[index]) {
  158. snled27351_select_page(index, SNLED27351_COMMAND_PWM);
  159. snled27351_write_pwm_buffer(index);
  160. g_pwm_buffer_update_required[index] = false;
  161. }
  162. }
  163. void snled27351_update_led_control_registers(uint8_t index) {
  164. if (g_led_control_registers_update_required[index]) {
  165. snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);
  166. for (uint8_t i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) {
  167. snled27351_write_register(index, i, g_led_control_registers[index][i]);
  168. }
  169. g_led_control_registers_update_required[index] = false;
  170. }
  171. }
  172. void snled27351_flush(void) {
  173. for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {
  174. snled27351_update_pwm_buffers(i);
  175. }
  176. }
  177. void snled27351_sw_return_normal(uint8_t index) {
  178. snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
  179. // Setting LED driver to normal mode
  180. snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);
  181. }
  182. void snled27351_sw_shutdown(uint8_t index) {
  183. snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);
  184. // Setting LED driver to shutdown mode
  185. snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);
  186. // Write SW Sleep Register
  187. snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE);
  188. }