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.

367 lines
7.7 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  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 "ws2812.h"
  24. #include <avr/interrupt.h>
  25. #include <avr/io.h>
  26. #include <util/delay.h>
  27. #include "debug.h"
  28. #if !defined(LED_ARRAY) && defined(RGB_MATRIX_ENABLE)
  29. // LED color buffer
  30. LED_TYPE led[DRIVER_LED_TOTAL];
  31. #define LED_ARRAY led
  32. #endif
  33. #ifdef RGBW_BB_TWI
  34. // Port for the I2C
  35. #define I2C_DDR DDRD
  36. #define I2C_PIN PIND
  37. #define I2C_PORT PORTD
  38. // Pins to be used in the bit banging
  39. #define I2C_CLK 0
  40. #define I2C_DAT 1
  41. #define I2C_DATA_HI()\
  42. I2C_DDR &= ~ (1 << I2C_DAT);\
  43. I2C_PORT |= (1 << I2C_DAT);
  44. #define I2C_DATA_LO()\
  45. I2C_DDR |= (1 << I2C_DAT);\
  46. I2C_PORT &= ~ (1 << I2C_DAT);
  47. #define I2C_CLOCK_HI()\
  48. I2C_DDR &= ~ (1 << I2C_CLK);\
  49. I2C_PORT |= (1 << I2C_CLK);
  50. #define I2C_CLOCK_LO()\
  51. I2C_DDR |= (1 << I2C_CLK);\
  52. I2C_PORT &= ~ (1 << I2C_CLK);
  53. #define I2C_DELAY 1
  54. void I2C_WriteBit(unsigned char c)
  55. {
  56. if (c > 0)
  57. {
  58. I2C_DATA_HI();
  59. }
  60. else
  61. {
  62. I2C_DATA_LO();
  63. }
  64. I2C_CLOCK_HI();
  65. _delay_us(I2C_DELAY);
  66. I2C_CLOCK_LO();
  67. _delay_us(I2C_DELAY);
  68. if (c > 0)
  69. {
  70. I2C_DATA_LO();
  71. }
  72. _delay_us(I2C_DELAY);
  73. }
  74. // Inits bitbanging port, must be called before using the functions below
  75. //
  76. void I2C_Init(void)
  77. {
  78. I2C_PORT &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
  79. I2C_CLOCK_HI();
  80. I2C_DATA_HI();
  81. _delay_us(I2C_DELAY);
  82. }
  83. // Send a START Condition
  84. //
  85. void I2C_Start(void)
  86. {
  87. // set both to high at the same time
  88. I2C_DDR &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
  89. _delay_us(I2C_DELAY);
  90. I2C_DATA_LO();
  91. _delay_us(I2C_DELAY);
  92. I2C_CLOCK_LO();
  93. _delay_us(I2C_DELAY);
  94. }
  95. // Send a STOP Condition
  96. //
  97. void I2C_Stop(void)
  98. {
  99. I2C_CLOCK_HI();
  100. _delay_us(I2C_DELAY);
  101. I2C_DATA_HI();
  102. _delay_us(I2C_DELAY);
  103. }
  104. // write a byte to the I2C slave device
  105. //
  106. unsigned char I2C_Write(unsigned char c)
  107. {
  108. for (char i = 0; i < 8; i++)
  109. {
  110. I2C_WriteBit(c & 128);
  111. c <<= 1;
  112. }
  113. I2C_WriteBit(0);
  114. _delay_us(I2C_DELAY);
  115. _delay_us(I2C_DELAY);
  116. // _delay_us(I2C_DELAY);
  117. //return I2C_ReadBit();
  118. return 0;
  119. }
  120. #endif
  121. #ifdef RGB_MATRIX_ENABLE
  122. // Set an led in the buffer to a color
  123. void inline ws2812_setled(int i, uint8_t r, uint8_t g, uint8_t b)
  124. {
  125. led[i].r = r;
  126. led[i].g = g;
  127. led[i].b = b;
  128. }
  129. void ws2812_setled_all (uint8_t r, uint8_t g, uint8_t b)
  130. {
  131. for (int i = 0; i < RGBLED_NUM; i++) {
  132. led[i].r = r;
  133. led[i].g = g;
  134. led[i].b = b;
  135. }
  136. }
  137. #endif
  138. // Setleds for standard RGB
  139. void inline ws2812_setleds(LED_TYPE *ledarray, uint16_t leds)
  140. {
  141. // ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin));
  142. ws2812_setleds_pin(ledarray,leds, _BV(RGB_DI_PIN & 0xF));
  143. }
  144. void inline ws2812_setleds_pin(LED_TYPE *ledarray, uint16_t leds, uint8_t pinmask)
  145. {
  146. // ws2812_DDRREG |= pinmask; // Enable DDR
  147. // new universal format (DDR)
  148. _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= pinmask;
  149. ws2812_sendarray_mask((uint8_t*)ledarray,leds+leds+leds,pinmask);
  150. _delay_us(50);
  151. }
  152. // Setleds for SK6812RGBW
  153. void inline ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t leds)
  154. {
  155. #ifdef RGBW_BB_TWI
  156. uint8_t sreg_prev, twcr_prev;
  157. sreg_prev=SREG;
  158. twcr_prev=TWCR;
  159. cli();
  160. TWCR &= ~(1<<TWEN);
  161. I2C_Init();
  162. I2C_Start();
  163. I2C_Write(0x84);
  164. uint16_t datlen = leds<<2;
  165. uint8_t curbyte;
  166. uint8_t * data = (uint8_t*)ledarray;
  167. while (datlen--) {
  168. curbyte=*data++;
  169. I2C_Write(curbyte);
  170. }
  171. I2C_Stop();
  172. SREG=sreg_prev;
  173. TWCR=twcr_prev;
  174. #endif
  175. // ws2812_DDRREG |= _BV(ws2812_pin); // Enable DDR
  176. // new universal format (DDR)
  177. _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= _BV(RGB_DI_PIN & 0xF);
  178. ws2812_sendarray_mask((uint8_t*)ledarray,leds<<2,_BV(RGB_DI_PIN & 0xF));
  179. #ifndef RGBW_BB_TWI
  180. _delay_us(80);
  181. #endif
  182. }
  183. void ws2812_sendarray(uint8_t *data,uint16_t datlen)
  184. {
  185. ws2812_sendarray_mask(data,datlen,_BV(RGB_DI_PIN & 0xF));
  186. }
  187. /*
  188. This routine writes an array of bytes with RGB values to the Dataout pin
  189. using the fast 800kHz clockless WS2811/2812 protocol.
  190. */
  191. // Timing in ns
  192. #define w_zeropulse 350
  193. #define w_onepulse 900
  194. #define w_totalperiod 1250
  195. // Fixed cycles used by the inner loop
  196. #define w_fixedlow 2
  197. #define w_fixedhigh 4
  198. #define w_fixedtotal 8
  199. // Insert NOPs to match the timing, if possible
  200. #define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000)
  201. #define w_onecycles (((F_CPU/1000)*w_onepulse +500000)/1000000)
  202. #define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000)
  203. // w1 - nops between rising edge and falling edge - low
  204. #define w1 (w_zerocycles-w_fixedlow)
  205. // w2 nops between fe low and fe high
  206. #define w2 (w_onecycles-w_fixedhigh-w1)
  207. // w3 nops to complete loop
  208. #define w3 (w_totalcycles-w_fixedtotal-w1-w2)
  209. #if w1>0
  210. #define w1_nops w1
  211. #else
  212. #define w1_nops 0
  213. #endif
  214. // The only critical timing parameter is the minimum pulse length of the "0"
  215. // Warn or throw error if this timing can not be met with current F_CPU settings.
  216. #define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
  217. #if w_lowtime>550
  218. #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
  219. #elif w_lowtime>450
  220. #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
  221. #warning "Please consider a higher clockspeed, if possible"
  222. #endif
  223. #if w2>0
  224. #define w2_nops w2
  225. #else
  226. #define w2_nops 0
  227. #endif
  228. #if w3>0
  229. #define w3_nops w3
  230. #else
  231. #define w3_nops 0
  232. #endif
  233. #define w_nop1 "nop \n\t"
  234. #define w_nop2 "rjmp .+0 \n\t"
  235. #define w_nop4 w_nop2 w_nop2
  236. #define w_nop8 w_nop4 w_nop4
  237. #define w_nop16 w_nop8 w_nop8
  238. void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi)
  239. {
  240. uint8_t curbyte,ctr,masklo;
  241. uint8_t sreg_prev;
  242. // masklo =~maskhi&ws2812_PORTREG;
  243. // maskhi |= ws2812_PORTREG;
  244. masklo =~maskhi&_SFR_IO8((RGB_DI_PIN >> 4) + 2);
  245. maskhi |= _SFR_IO8((RGB_DI_PIN >> 4) + 2);
  246. sreg_prev=SREG;
  247. cli();
  248. while (datlen--) {
  249. curbyte=(*data++);
  250. asm volatile(
  251. " ldi %0,8 \n\t"
  252. "loop%=: \n\t"
  253. " out %2,%3 \n\t" // '1' [01] '0' [01] - re
  254. #if (w1_nops&1)
  255. w_nop1
  256. #endif
  257. #if (w1_nops&2)
  258. w_nop2
  259. #endif
  260. #if (w1_nops&4)
  261. w_nop4
  262. #endif
  263. #if (w1_nops&8)
  264. w_nop8
  265. #endif
  266. #if (w1_nops&16)
  267. w_nop16
  268. #endif
  269. " sbrs %1,7 \n\t" // '1' [03] '0' [02]
  270. " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low
  271. " lsl %1 \n\t" // '1' [04] '0' [04]
  272. #if (w2_nops&1)
  273. w_nop1
  274. #endif
  275. #if (w2_nops&2)
  276. w_nop2
  277. #endif
  278. #if (w2_nops&4)
  279. w_nop4
  280. #endif
  281. #if (w2_nops&8)
  282. w_nop8
  283. #endif
  284. #if (w2_nops&16)
  285. w_nop16
  286. #endif
  287. " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high
  288. #if (w3_nops&1)
  289. w_nop1
  290. #endif
  291. #if (w3_nops&2)
  292. w_nop2
  293. #endif
  294. #if (w3_nops&4)
  295. w_nop4
  296. #endif
  297. #if (w3_nops&8)
  298. w_nop8
  299. #endif
  300. #if (w3_nops&16)
  301. w_nop16
  302. #endif
  303. " dec %0 \n\t" // '1' [+2] '0' [+2]
  304. " brne loop%=\n\t" // '1' [+3] '0' [+4]
  305. : "=&d" (ctr)
  306. : "r" (curbyte), "I" (_SFR_IO_ADDR(_SFR_IO8((RGB_DI_PIN >> 4) + 2))), "r" (maskhi), "r" (masklo)
  307. );
  308. }
  309. SREG=sreg_prev;
  310. }