lp5562.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. #include "lp5562.h"
  2. #include "furi/common_defines.h"
  3. #include "lp5562_reg.h"
  4. #include <furi_hal.h>
  5. #include <stdio.h>
  6. void lp5562_reset(FuriHalI2cBusHandle* handle) {
  7. Reg0D_Reset reg = {.value = 0xFF};
  8. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x0D, *(uint8_t*)&reg, LP5562_I2C_TIMEOUT);
  9. }
  10. void lp5562_configure(FuriHalI2cBusHandle* handle) {
  11. Reg08_Config config = {.INT_CLK_EN = true, .PS_EN = true, .PWM_HF = true};
  12. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x08, *(uint8_t*)&config, LP5562_I2C_TIMEOUT);
  13. Reg70_LedMap map = {
  14. .red = EngSelectI2C,
  15. .green = EngSelectI2C,
  16. .blue = EngSelectI2C,
  17. .white = EngSelectI2C,
  18. };
  19. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, *(uint8_t*)&map, LP5562_I2C_TIMEOUT);
  20. }
  21. void lp5562_enable(FuriHalI2cBusHandle* handle) {
  22. Reg00_Enable reg = {.CHIP_EN = true, .LOG_EN = true};
  23. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, *(uint8_t*)&reg, LP5562_I2C_TIMEOUT);
  24. //>488μs delay is required after writing to 0x00 register, otherwise program engine will not work
  25. furi_hal_delay_us(500);
  26. }
  27. void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) {
  28. uint8_t reg_no;
  29. if(channel == LP5562ChannelRed) {
  30. reg_no = LP5562_CHANNEL_RED_CURRENT_REGISTER;
  31. } else if(channel == LP5562ChannelGreen) {
  32. reg_no = LP5562_CHANNEL_GREEN_CURRENT_REGISTER;
  33. } else if(channel == LP5562ChannelBlue) {
  34. reg_no = LP5562_CHANNEL_BLUE_CURRENT_REGISTER;
  35. } else if(channel == LP5562ChannelWhite) {
  36. reg_no = LP5562_CHANNEL_WHITE_CURRENT_REGISTER;
  37. } else {
  38. return;
  39. }
  40. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT);
  41. }
  42. void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) {
  43. uint8_t reg_no;
  44. if(channel == LP5562ChannelRed) {
  45. reg_no = LP5562_CHANNEL_RED_VALUE_REGISTER;
  46. } else if(channel == LP5562ChannelGreen) {
  47. reg_no = LP5562_CHANNEL_GREEN_VALUE_REGISTER;
  48. } else if(channel == LP5562ChannelBlue) {
  49. reg_no = LP5562_CHANNEL_BLUE_VALUE_REGISTER;
  50. } else if(channel == LP5562ChannelWhite) {
  51. reg_no = LP5562_CHANNEL_WHITE_VALUE_REGISTER;
  52. } else {
  53. return;
  54. }
  55. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT);
  56. }
  57. uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel) {
  58. uint8_t reg_no;
  59. uint8_t value;
  60. if(channel == LP5562ChannelRed) {
  61. reg_no = LP5562_CHANNEL_RED_VALUE_REGISTER;
  62. } else if(channel == LP5562ChannelGreen) {
  63. reg_no = LP5562_CHANNEL_GREEN_VALUE_REGISTER;
  64. } else if(channel == LP5562ChannelBlue) {
  65. reg_no = LP5562_CHANNEL_BLUE_VALUE_REGISTER;
  66. } else if(channel == LP5562ChannelWhite) {
  67. reg_no = LP5562_CHANNEL_WHITE_VALUE_REGISTER;
  68. } else {
  69. return 0;
  70. }
  71. furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, reg_no, &value, LP5562_I2C_TIMEOUT);
  72. return value;
  73. }
  74. void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, LP5562Engine src) {
  75. uint8_t reg_val = 0;
  76. uint8_t bit_offset = 0;
  77. do {
  78. if(channel & LP5562ChannelRed) {
  79. bit_offset = 4;
  80. channel &= ~LP5562ChannelRed;
  81. } else if(channel & LP5562ChannelGreen) {
  82. bit_offset = 2;
  83. channel &= ~LP5562ChannelGreen;
  84. } else if(channel & LP5562ChannelBlue) {
  85. bit_offset = 0;
  86. channel &= ~LP5562ChannelBlue;
  87. } else if(channel & LP5562ChannelWhite) {
  88. bit_offset = 6;
  89. channel &= ~LP5562ChannelWhite;
  90. } else {
  91. return;
  92. }
  93. furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x70, &reg_val, LP5562_I2C_TIMEOUT);
  94. reg_val &= ~(0x3 << bit_offset);
  95. reg_val |= ((src & 0x03) << bit_offset);
  96. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, reg_val, LP5562_I2C_TIMEOUT);
  97. } while(channel);
  98. }
  99. void lp5562_execute_program(
  100. FuriHalI2cBusHandle* handle,
  101. LP5562Engine eng,
  102. LP5562Channel ch,
  103. uint16_t* program) {
  104. if((eng < LP5562Engine1) || (eng > LP5562Engine3)) return;
  105. uint8_t reg_val = 0;
  106. uint8_t bit_offset = 0;
  107. uint8_t enable_reg = 0;
  108. // Read old value of enable register
  109. furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x00, &enable_reg, LP5562_I2C_TIMEOUT);
  110. // Engine configuration
  111. bit_offset = (3 - eng) * 2;
  112. furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, &reg_val, LP5562_I2C_TIMEOUT);
  113. reg_val &= ~(0x3 << bit_offset);
  114. reg_val |= (0x01 << bit_offset); // load
  115. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT);
  116. furi_hal_delay_us(100);
  117. // Program load
  118. for(uint8_t i = 0; i < 16; i++) {
  119. // Program words are big-endian, so reverse byte order before loading
  120. program[i] = __REV16(program[i]);
  121. }
  122. furi_hal_i2c_write_mem(
  123. handle,
  124. LP5562_ADDRESS,
  125. 0x10 + (0x20 * (eng - 1)),
  126. (uint8_t*)program,
  127. 16 * 2,
  128. LP5562_I2C_TIMEOUT);
  129. // Program start
  130. bit_offset = (3 - eng) * 2;
  131. furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, &reg_val, LP5562_I2C_TIMEOUT);
  132. reg_val &= ~(0x3 << bit_offset);
  133. reg_val |= (0x02 << bit_offset); // run
  134. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT);
  135. // Switch output to Execution Engine
  136. lp5562_set_channel_src(handle, ch, eng);
  137. enable_reg &= ~(0x3 << bit_offset);
  138. enable_reg |= (0x02 << bit_offset); // run
  139. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, enable_reg, LP5562_I2C_TIMEOUT);
  140. }
  141. void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng) {
  142. if((eng < LP5562Engine1) || (eng > LP5562Engine3)) return;
  143. uint8_t reg_val = 0;
  144. uint8_t bit_offset = 0;
  145. // Engine configuration
  146. bit_offset = (3 - eng) * 2;
  147. furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, &reg_val, LP5562_I2C_TIMEOUT);
  148. reg_val &= ~(0x3 << bit_offset);
  149. reg_val |= (0x00 << bit_offset); // Disabled
  150. furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT);
  151. }
  152. void lp5562_execute_ramp(
  153. FuriHalI2cBusHandle* handle,
  154. LP5562Engine eng,
  155. LP5562Channel ch,
  156. uint8_t val_start,
  157. uint8_t val_end,
  158. uint16_t time) {
  159. if(val_start == val_end) return;
  160. // Temporary switch to constant value from register
  161. lp5562_set_channel_src(handle, ch, LP5562Direct);
  162. // Prepare command sequence
  163. uint16_t program[16];
  164. uint8_t diff = (val_end > val_start) ? (val_end - val_start) : (val_start - val_end);
  165. uint16_t time_step = time * 2 / diff;
  166. uint8_t prescaller = 0;
  167. if(time_step > 0x3F) {
  168. time_step /= 32;
  169. prescaller = 1;
  170. }
  171. if(time_step == 0) {
  172. time_step = 1;
  173. } else if(time_step > 0x3F)
  174. time_step = 0x3F;
  175. program[0] = 0x4000 | val_start; // Set PWM
  176. if(val_end > val_start) {
  177. program[1] = (prescaller << 14) | (time_step << 8) | ((diff / 2) & 0x7F); // Ramp Up
  178. } else {
  179. program[1] = (prescaller << 14) | (time_step << 8) | 0x80 |
  180. ((diff / 2) & 0x7F); // Ramp Down
  181. }
  182. program[2] = 0xA001 | ((2 - 1) << 7); // Loop to step 1, repeat twice to get full 8-bit scale
  183. program[3] = 0xC000; // End
  184. // Execute program
  185. lp5562_execute_program(handle, eng, LP5562ChannelWhite, program);
  186. // Write end value to register
  187. lp5562_set_channel_value(handle, ch, val_end);
  188. }
  189. void lp5562_execute_blink(
  190. FuriHalI2cBusHandle* handle,
  191. LP5562Engine eng,
  192. LP5562Channel ch,
  193. uint16_t on_time,
  194. uint16_t period,
  195. uint8_t brightness) {
  196. // Temporary switch to constant value from register
  197. lp5562_set_channel_src(handle, ch, LP5562Direct);
  198. // Prepare command sequence
  199. uint16_t program[16];
  200. uint16_t time_step = 0;
  201. uint8_t prescaller = 0;
  202. program[0] = 0x4000 | brightness; // Set PWM
  203. time_step = on_time * 2;
  204. if(time_step > 0x3F) {
  205. time_step /= 32;
  206. prescaller = 1;
  207. } else {
  208. prescaller = 0;
  209. }
  210. if(time_step == 0) {
  211. time_step = 1;
  212. } else if(time_step > 0x3F)
  213. time_step = 0x3F;
  214. program[1] = (prescaller << 14) | (time_step << 8); // Delay
  215. program[2] = 0x4000 | 0; // Set PWM
  216. time_step = (period - on_time) * 2;
  217. if(time_step > 0x3F) {
  218. time_step /= 32;
  219. prescaller = 1;
  220. } else {
  221. prescaller = 0;
  222. }
  223. if(time_step == 0) {
  224. time_step = 1;
  225. } else if(time_step > 0x3F)
  226. time_step = 0x3F;
  227. program[3] = (prescaller << 14) | (time_step << 8); // Delay
  228. program[4] = 0x0000; // Go to start
  229. // Execute program
  230. lp5562_execute_program(handle, eng, ch, program);
  231. }