logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

ws2812_bitbang.c (5582B)


  1. /*
  2. * light weight WS2812 lib V2.0b
  3. *
  4. * Controls WS2811/WS2812/WS2812B RGB-LEDs
  5. * Author: Tim (cpldcpu@gmail.com)
  6. *
  7. * Jan 18th, 2014 v2.0b Initial Version
  8. * Nov 29th, 2015 v2.3 Added SK6812RGBW support
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation, either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. */
  23. #include <avr/interrupt.h>
  24. #include <avr/io.h>
  25. #include <util/delay.h>
  26. #include "ws2812.h"
  27. #include "pin_defs.h"
  28. #define pinmask(pin) (_BV((pin)&0xF))
  29. /*
  30. This routine writes an array of bytes with RGB values to the Dataout pin
  31. using the fast 800kHz clockless WS2811/2812 protocol.
  32. */
  33. // Fixed cycles used by the inner loop
  34. #define w_fixedlow 2
  35. #define w_fixedhigh 4
  36. #define w_fixedtotal 8
  37. // Insert NOPs to match the timing, if possible
  38. #define w_zerocycles (((F_CPU / 1000) * WS2812_T0H) / 1000000)
  39. #define w_onecycles (((F_CPU / 1000) * WS2812_T1H + 500000) / 1000000)
  40. #define w_totalcycles (((F_CPU / 1000) * WS2812_TIMING + 500000) / 1000000)
  41. // w1_nops - nops between rising edge and falling edge - low
  42. #if w_zerocycles >= w_fixedlow
  43. # define w1_nops (w_zerocycles - w_fixedlow)
  44. #else
  45. # define w1_nops 0
  46. #endif
  47. // w2_nops - nops between fe low and fe high
  48. #if w_onecycles >= (w_fixedhigh + w1_nops)
  49. # define w2_nops (w_onecycles - w_fixedhigh - w1_nops)
  50. #else
  51. # define w2_nops 0
  52. #endif
  53. // w3_nops - nops to complete loop
  54. #if w_totalcycles >= (w_fixedtotal + w1_nops + w2_nops)
  55. # define w3_nops (w_totalcycles - w_fixedtotal - w1_nops - w2_nops)
  56. #else
  57. # define w3_nops 0
  58. #endif
  59. // The only critical timing parameter is the minimum pulse length of the "0"
  60. // Warn or throw error if this timing can not be met with current F_CPU settings.
  61. #define w_lowtime ((w1_nops + w_fixedlow) * 1000000) / (F_CPU / 1000)
  62. #if w_lowtime > 550
  63. # error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
  64. #elif w_lowtime > 450
  65. # warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
  66. # warning "Please consider a higher clockspeed, if possible"
  67. #endif
  68. #define w_nop1 "nop \n\t"
  69. #define w_nop2 "rjmp .+0 \n\t"
  70. #define w_nop4 w_nop2 w_nop2
  71. #define w_nop8 w_nop4 w_nop4
  72. #define w_nop16 w_nop8 w_nop8
  73. static inline void ws2812_sendarray_mask(uint8_t *data, uint16_t datlen, uint8_t masklo, uint8_t maskhi) {
  74. uint8_t curbyte, ctr, sreg_prev;
  75. sreg_prev = SREG;
  76. cli();
  77. while (datlen--) {
  78. curbyte = (*data++);
  79. asm volatile(" ldi %0,8 \n\t"
  80. "loop%=: \n\t"
  81. " out %2,%3 \n\t" // '1' [01] '0' [01] - re
  82. #if (w1_nops & 1)
  83. w_nop1
  84. #endif
  85. #if (w1_nops & 2)
  86. w_nop2
  87. #endif
  88. #if (w1_nops & 4)
  89. w_nop4
  90. #endif
  91. #if (w1_nops & 8)
  92. w_nop8
  93. #endif
  94. #if (w1_nops & 16)
  95. w_nop16
  96. #endif
  97. " sbrs %1,7 \n\t" // '1' [03] '0' [02]
  98. " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low
  99. " lsl %1 \n\t" // '1' [04] '0' [04]
  100. #if (w2_nops & 1)
  101. w_nop1
  102. #endif
  103. #if (w2_nops & 2)
  104. w_nop2
  105. #endif
  106. #if (w2_nops & 4)
  107. w_nop4
  108. #endif
  109. #if (w2_nops & 8)
  110. w_nop8
  111. #endif
  112. #if (w2_nops & 16)
  113. w_nop16
  114. #endif
  115. " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high
  116. #if (w3_nops & 1)
  117. w_nop1
  118. #endif
  119. #if (w3_nops & 2)
  120. w_nop2
  121. #endif
  122. #if (w3_nops & 4)
  123. w_nop4
  124. #endif
  125. #if (w3_nops & 8)
  126. w_nop8
  127. #endif
  128. #if (w3_nops & 16)
  129. w_nop16
  130. #endif
  131. " dec %0 \n\t" // '1' [+2] '0' [+2]
  132. " brne loop%=\n\t" // '1' [+3] '0' [+4]
  133. : "=&d"(ctr)
  134. : "r"(curbyte), "I"(_SFR_IO_ADDR(PORTx_ADDRESS(WS2812_DI_PIN))), "r"(maskhi), "r"(masklo));
  135. }
  136. SREG = sreg_prev;
  137. }
  138. ws2812_led_t ws2812_leds[WS2812_LED_COUNT];
  139. void ws2812_init(void) {
  140. DDRx_ADDRESS(WS2812_DI_PIN) |= pinmask(WS2812_DI_PIN);
  141. }
  142. void ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
  143. ws2812_leds[index].r = red;
  144. ws2812_leds[index].g = green;
  145. ws2812_leds[index].b = blue;
  146. #if defined(WS2812_RGBW)
  147. ws2812_rgb_to_rgbw(&ws2812_leds[index]);
  148. #endif
  149. }
  150. void ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
  151. for (int i = 0; i < WS2812_LED_COUNT; i++) {
  152. ws2812_set_color(i, red, green, blue);
  153. }
  154. }
  155. void ws2812_flush(void) {
  156. uint8_t masklo = ~(pinmask(WS2812_DI_PIN)) & PORTx_ADDRESS(WS2812_DI_PIN);
  157. uint8_t maskhi = pinmask(WS2812_DI_PIN) | PORTx_ADDRESS(WS2812_DI_PIN);
  158. ws2812_sendarray_mask((uint8_t *)ws2812_leds, WS2812_LED_COUNT * sizeof(ws2812_led_t), masklo, maskhi);
  159. _delay_us(WS2812_TRST_US);
  160. }