NeoPixelAvr.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. /*-------------------------------------------------------------------------
  2. Arduino library to control a wide variety of WS2811- and WS2812-based RGB
  3. LED devices such as Adafruit FLORA RGB Smart Pixels and NeoPixel strips.
  4. Currently handles 400 and 800 KHz bitstreams on 8, 12 and 16 MHz ATmega
  5. MCUs, with LEDs wired for various color orders. 8 MHz MCUs provide
  6. output on PORTB and PORTD, while 16 MHz chips can handle most output pins
  7. (possible exception with upper PORT registers on the Arduino Mega).
  8. Written by Phil Burgess / Paint Your Dragon for Adafruit Industries,
  9. contributions by PJRC, Michael Miller and other members of the open
  10. source community.
  11. Adafruit invests time and resources providing this open source code,
  12. please support Adafruit and open-source hardware by purchasing products
  13. from Adafruit!
  14. -------------------------------------------------------------------------
  15. The contents of this file were taken from the Adafruit NeoPixel library
  16. and modified only to fit within individual calling functions.
  17. NeoPixel is free software: you can redistribute it and/or modify
  18. it under the terms of the GNU Lesser General Public License as
  19. published by the Free Software Foundation, either version 3 of
  20. the License, or (at your option) any later version.
  21. NeoPixel is distributed in the hope that it will be useful,
  22. but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. GNU Lesser General Public License for more details.
  25. You should have received a copy of the GNU Lesser General Public
  26. License along with NeoPixel. If not, see
  27. <http://www.gnu.org/licenses/>.
  28. -------------------------------------------------------------------------*/
  29. // must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
  30. #if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
  31. #include <Arduino.h>
  32. // Hand-tuned assembly code issues data to the LED drivers at a specific
  33. // rate. There's separate code for different CPU speeds (8, 12, 16 MHz)
  34. // for both the WS2811 (400 KHz) and WS2812 (800 KHz) drivers. The
  35. // datastream timing for the LED drivers allows a little wiggle room each
  36. // way (listed in the datasheets), so the conditions for compiling each
  37. // case are set up for a range of frequencies rather than just the exact
  38. // 8, 12 or 16 MHz values, permitting use with some close-but-not-spot-on
  39. // devices (e.g. 16.5 MHz DigiSpark). The ranges were arrived at based
  40. // on the datasheet figures and have not been extensively tested outside
  41. // the canonical 8/12/16 MHz speeds; there's no guarantee these will work
  42. // close to the extremes (or possibly they could be pushed further).
  43. // Keep in mind only one CPU speed case actually gets compiled; the
  44. // resulting program isn't as massive as it might look from source here.
  45. #if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
  46. #ifdef PORTD // PORTD isn't present on ATtiny85, etc.
  47. void send_pixels_8mhz_800_PortD(uint8_t* pixels, size_t sizePixels, uint8_t pinMask)
  48. {
  49. volatile size_t i = sizePixels; // Loop counter
  50. volatile uint8_t* ptr = pixels; // Pointer to next byte
  51. volatile uint8_t b = *ptr++; // Current byte value
  52. volatile uint8_t hi; // PORT w/output bit set high
  53. volatile uint8_t lo; // PORT w/output bit set low
  54. volatile uint8_t n1;
  55. volatile n2 = 0; // First, next bits out
  56. // Squeezing an 800 KHz stream out of an 8 MHz chip requires code
  57. // specific to each PORT register. At present this is only written
  58. // to work with pins on PORTD or PORTB, the most likely use case --
  59. // this covers all the pins on the Adafruit Flora and the bulk of
  60. // digital pins on the Arduino Pro 8 MHz (keep in mind, this code
  61. // doesn't even get compiled for 16 MHz boards like the Uno, Mega,
  62. // Leonardo, etc., so don't bother extending this out of hand).
  63. // Additional PORTs could be added if you really need them, just
  64. // duplicate the else and loop and change the PORT. Each add'l
  65. // PORT will require about 150(ish) bytes of program space.
  66. // 10 instruction clocks per bit: HHxxxxxLLL
  67. // OUT instructions: ^ ^ ^ (T=0,2,7)
  68. hi = PORTD | pinMask;
  69. lo = PORTD & ~pinMask;
  70. n1 = lo;
  71. if (b & 0x80)
  72. {
  73. n1 = hi;
  74. }
  75. // Dirty trick: RJMPs proceeding to the next instruction are used
  76. // to delay two clock cycles in one instruction word (rather than
  77. // using two NOPs). This was necessary in order to squeeze the
  78. // loop down to exactly 64 words -- the maximum possible for a
  79. // relative branch.
  80. asm volatile(
  81. "headD:" "\n\t" // Clk Pseudocode
  82. // Bit 7:
  83. "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
  84. "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
  85. "out %[port] , %[n1]" "\n\t" // 1 PORT = n1
  86. "rjmp .+0" "\n\t" // 2 nop nop
  87. "sbrc %[byte] , 6" "\n\t" // 1-2 if(b & 0x40)
  88. "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
  89. "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
  90. "rjmp .+0" "\n\t" // 2 nop nop
  91. // Bit 6:
  92. "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
  93. "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
  94. "out %[port] , %[n2]" "\n\t" // 1 PORT = n2
  95. "rjmp .+0" "\n\t" // 2 nop nop
  96. "sbrc %[byte] , 5" "\n\t" // 1-2 if(b & 0x20)
  97. "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
  98. "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
  99. "rjmp .+0" "\n\t" // 2 nop nop
  100. // Bit 5:
  101. "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
  102. "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
  103. "out %[port] , %[n1]" "\n\t" // 1 PORT = n1
  104. "rjmp .+0" "\n\t" // 2 nop nop
  105. "sbrc %[byte] , 4" "\n\t" // 1-2 if(b & 0x10)
  106. "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
  107. "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
  108. "rjmp .+0" "\n\t" // 2 nop nop
  109. // Bit 4:
  110. "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
  111. "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
  112. "out %[port] , %[n2]" "\n\t" // 1 PORT = n2
  113. "rjmp .+0" "\n\t" // 2 nop nop
  114. "sbrc %[byte] , 3" "\n\t" // 1-2 if(b & 0x08)
  115. "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
  116. "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
  117. "rjmp .+0" "\n\t" // 2 nop nop
  118. // Bit 3:
  119. "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
  120. "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
  121. "out %[port] , %[n1]" "\n\t" // 1 PORT = n1
  122. "rjmp .+0" "\n\t" // 2 nop nop
  123. "sbrc %[byte] , 2" "\n\t" // 1-2 if(b & 0x04)
  124. "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
  125. "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
  126. "rjmp .+0" "\n\t" // 2 nop nop
  127. // Bit 2:
  128. "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
  129. "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
  130. "out %[port] , %[n2]" "\n\t" // 1 PORT = n2
  131. "rjmp .+0" "\n\t" // 2 nop nop
  132. "sbrc %[byte] , 1" "\n\t" // 1-2 if(b & 0x02)
  133. "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
  134. "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
  135. "rjmp .+0" "\n\t" // 2 nop nop
  136. // Bit 1:
  137. "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
  138. "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo
  139. "out %[port] , %[n1]" "\n\t" // 1 PORT = n1
  140. "rjmp .+0" "\n\t" // 2 nop nop
  141. "sbrc %[byte] , 0" "\n\t" // 1-2 if(b & 0x01)
  142. "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi
  143. "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
  144. "sbiw %[count], 1" "\n\t" // 2 i-- (don't act on Z flag yet)
  145. // Bit 0:
  146. "out %[port] , %[hi]" "\n\t" // 1 PORT = hi
  147. "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo
  148. "out %[port] , %[n2]" "\n\t" // 1 PORT = n2
  149. "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++
  150. "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80)
  151. "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi
  152. "out %[port] , %[lo]" "\n\t" // 1 PORT = lo
  153. "brne headD" "\n" // 2 while(i) (Z flag set above)
  154. : [byte] "+r" (b),
  155. [n1] "+r" (n1),
  156. [n2] "+r" (n2),
  157. [count] "+w" (i)
  158. : [port] "I" (_SFR_IO_ADDR(PORTD)),
  159. [ptr] "e" (ptr),
  160. [hi] "r" (hi),
  161. [lo] "r" (lo) );
  162. }
  163. #endif
  164. void send_pixels_8mhz_800_PortB(uint8_t* pixels, size_t sizePixels, uint8_t pinMask)
  165. {
  166. volatile size_t i = sizePixels; // Loop counter
  167. volatile uint8_t* ptr = pixels; // Pointer to next byte
  168. volatile uint8_t b = *ptr++; // Current byte value
  169. volatile uint8_t hi; // PORT w/output bit set high
  170. volatile uint8_t lo; // PORT w/output bit set low
  171. volatile uint8_t n1;
  172. volatile n2 = 0; // First, next bits out
  173. // Same as above, just switched to PORTB and stripped of comments.
  174. hi = PORTB | pinMask;
  175. lo = PORTB & ~pinMask;
  176. n1 = lo;
  177. if (b & 0x80)
  178. {
  179. n1 = hi;
  180. }
  181. asm volatile(
  182. "headB:" "\n\t"
  183. "out %[port] , %[hi]" "\n\t"
  184. "mov %[n2] , %[lo]" "\n\t"
  185. "out %[port] , %[n1]" "\n\t"
  186. "rjmp .+0" "\n\t"
  187. "sbrc %[byte] , 6" "\n\t"
  188. "mov %[n2] , %[hi]" "\n\t"
  189. "out %[port] , %[lo]" "\n\t"
  190. "rjmp .+0" "\n\t"
  191. "out %[port] , %[hi]" "\n\t"
  192. "mov %[n1] , %[lo]" "\n\t"
  193. "out %[port] , %[n2]" "\n\t"
  194. "rjmp .+0" "\n\t"
  195. "sbrc %[byte] , 5" "\n\t"
  196. "mov %[n1] , %[hi]" "\n\t"
  197. "out %[port] , %[lo]" "\n\t"
  198. "rjmp .+0" "\n\t"
  199. "out %[port] , %[hi]" "\n\t"
  200. "mov %[n2] , %[lo]" "\n\t"
  201. "out %[port] , %[n1]" "\n\t"
  202. "rjmp .+0" "\n\t"
  203. "sbrc %[byte] , 4" "\n\t"
  204. "mov %[n2] , %[hi]" "\n\t"
  205. "out %[port] , %[lo]" "\n\t"
  206. "rjmp .+0" "\n\t"
  207. "out %[port] , %[hi]" "\n\t"
  208. "mov %[n1] , %[lo]" "\n\t"
  209. "out %[port] , %[n2]" "\n\t"
  210. "rjmp .+0" "\n\t"
  211. "sbrc %[byte] , 3" "\n\t"
  212. "mov %[n1] , %[hi]" "\n\t"
  213. "out %[port] , %[lo]" "\n\t"
  214. "rjmp .+0" "\n\t"
  215. "out %[port] , %[hi]" "\n\t"
  216. "mov %[n2] , %[lo]" "\n\t"
  217. "out %[port] , %[n1]" "\n\t"
  218. "rjmp .+0" "\n\t"
  219. "sbrc %[byte] , 2" "\n\t"
  220. "mov %[n2] , %[hi]" "\n\t"
  221. "out %[port] , %[lo]" "\n\t"
  222. "rjmp .+0" "\n\t"
  223. "out %[port] , %[hi]" "\n\t"
  224. "mov %[n1] , %[lo]" "\n\t"
  225. "out %[port] , %[n2]" "\n\t"
  226. "rjmp .+0" "\n\t"
  227. "sbrc %[byte] , 1" "\n\t"
  228. "mov %[n1] , %[hi]" "\n\t"
  229. "out %[port] , %[lo]" "\n\t"
  230. "rjmp .+0" "\n\t"
  231. "out %[port] , %[hi]" "\n\t"
  232. "mov %[n2] , %[lo]" "\n\t"
  233. "out %[port] , %[n1]" "\n\t"
  234. "rjmp .+0" "\n\t"
  235. "sbrc %[byte] , 0" "\n\t"
  236. "mov %[n2] , %[hi]" "\n\t"
  237. "out %[port] , %[lo]" "\n\t"
  238. "sbiw %[count], 1" "\n\t"
  239. "out %[port] , %[hi]" "\n\t"
  240. "mov %[n1] , %[lo]" "\n\t"
  241. "out %[port] , %[n2]" "\n\t"
  242. "ld %[byte] , %a[ptr]+" "\n\t"
  243. "sbrc %[byte] , 7" "\n\t"
  244. "mov %[n1] , %[hi]" "\n\t"
  245. "out %[port] , %[lo]" "\n\t"
  246. "brne headB" "\n"
  247. : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i)
  248. : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi),
  249. [lo] "r" (lo));
  250. }
  251. void send_pixels_8mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
  252. {
  253. volatile size_t i = sizePixels; // Loop counter
  254. volatile uint8_t* ptr = pixels; // Pointer to next byte
  255. volatile uint8_t b = *ptr++; // Current byte value
  256. volatile uint8_t hi; // PORT w/output bit set high
  257. volatile uint8_t lo; // PORT w/output bit set low
  258. // Timing is more relaxed; unrolling the inner loop for each bit is
  259. // not necessary. Still using the peculiar RJMPs as 2X NOPs, not out
  260. // of need but just to trim the code size down a little.
  261. // This 400-KHz-datastream-on-8-MHz-CPU code is not quite identical
  262. // to the 800-on-16 code later -- the hi/lo timing between WS2811 and
  263. // WS2812 is not simply a 2:1 scale!
  264. // 20 inst. clocks per bit: HHHHxxxxxxLLLLLLLLLL
  265. // ST instructions: ^ ^ ^ (T=0,4,10)
  266. volatile uint8_t next, bit;
  267. hi = *port | pinMask;
  268. lo = *port & ~pinMask;
  269. next = lo;
  270. bit = 8;
  271. asm volatile(
  272. "head20:" "\n\t" // Clk Pseudocode (T = 0)
  273. "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
  274. "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
  275. "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
  276. "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 6)
  277. "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7)
  278. "dec %[bit]" "\n\t" // 1 bit-- (T = 8)
  279. "breq nextbyte20" "\n\t" // 1-2 if(bit == 0)
  280. "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10)
  281. "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12)
  282. "rjmp .+0" "\n\t" // 2 nop nop (T = 14)
  283. "rjmp .+0" "\n\t" // 2 nop nop (T = 16)
  284. "rjmp .+0" "\n\t" // 2 nop nop (T = 18)
  285. "rjmp head20" "\n\t" // 2 -> head20 (next bit out)
  286. "nextbyte20:" "\n\t" // (T = 10)
  287. "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12)
  288. "nop" "\n\t" // 1 nop (T = 13)
  289. "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 14)
  290. "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 16)
  291. "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18)
  292. "brne head20" "\n" // 2 if(i != 0) -> (next byte)
  293. : [port] "+e" (port),
  294. [byte] "+r" (b),
  295. [bit] "+r" (bit),
  296. [next] "+r" (next),
  297. [count] "+w" (i)
  298. : [hi] "r" (hi),
  299. [lo] "r" (lo),
  300. [ptr] "e" (ptr));
  301. }
  302. #elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
  303. #ifdef PORTD // PORTD isn't present on ATtiny85, etc.
  304. void send_pixels_12mhz_800_PortD(uint8_t* pixels, size_t sizePixels, uint8_t pinMask)
  305. {
  306. volatile size_t i = sizePixels; // Loop counter
  307. volatile uint8_t* ptr = pixels; // Pointer to next byte
  308. volatile uint8_t b = *ptr++; // Current byte value
  309. volatile uint8_t hi; // PORT w/output bit set high
  310. volatile uint8_t lo; // PORT w/output bit set low
  311. // In the 12 MHz case, an optimized 800 KHz datastream (no dead time
  312. // between bytes) requires a PORT-specific loop similar to the 8 MHz
  313. // code (but a little more relaxed in this case).
  314. // 15 instruction clocks per bit: HHHHxxxxxxLLLLL
  315. // OUT instructions: ^ ^ ^ (T=0,4,10)
  316. volatile uint8_t next;
  317. hi = PORTD | pinMask;
  318. lo = PORTD & ~pinMask;
  319. next = lo;
  320. if (b & 0x80) next = hi;
  321. // Don't "optimize" the OUT calls into the bitTime subroutine;
  322. // we're exploiting the RCALL and RET as 3- and 4-cycle NOPs!
  323. asm volatile(
  324. "headD:" "\n\t" // (T = 0)
  325. "out %[port], %[hi]" "\n\t" // (T = 1)
  326. "rcall bitTimeD" "\n\t" // Bit 7 (T = 15)
  327. "out %[port], %[hi]" "\n\t"
  328. "rcall bitTimeD" "\n\t" // Bit 6
  329. "out %[port], %[hi]" "\n\t"
  330. "rcall bitTimeD" "\n\t" // Bit 5
  331. "out %[port], %[hi]" "\n\t"
  332. "rcall bitTimeD" "\n\t" // Bit 4
  333. "out %[port], %[hi]" "\n\t"
  334. "rcall bitTimeD" "\n\t" // Bit 3
  335. "out %[port], %[hi]" "\n\t"
  336. "rcall bitTimeD" "\n\t" // Bit 2
  337. "out %[port], %[hi]" "\n\t"
  338. "rcall bitTimeD" "\n\t" // Bit 1
  339. // Bit 0:
  340. "out %[port] , %[hi]" "\n\t" // 1 PORT = hi (T = 1)
  341. "rjmp .+0" "\n\t" // 2 nop nop (T = 3)
  342. "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 5)
  343. "out %[port] , %[next]" "\n\t" // 1 PORT = next (T = 6)
  344. "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7)
  345. "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) (T = 8)
  346. "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 9)
  347. "nop" "\n\t" // 1 (T = 10)
  348. "out %[port] , %[lo]" "\n\t" // 1 PORT = lo (T = 11)
  349. "sbiw %[count], 1" "\n\t" // 2 i-- (T = 13)
  350. "brne headD" "\n\t" // 2 if(i != 0) -> (next byte)
  351. "rjmp doneD" "\n\t"
  352. "bitTimeD:" "\n\t" // nop nop nop (T = 4)
  353. "out %[port], %[next]" "\n\t" // 1 PORT = next (T = 5)
  354. "mov %[next], %[lo]" "\n\t" // 1 next = lo (T = 6)
  355. "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 7)
  356. "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 0x80) (T = 8)
  357. "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 9)
  358. "nop" "\n\t" // 1 (T = 10)
  359. "out %[port], %[lo]" "\n\t" // 1 PORT = lo (T = 11)
  360. "ret" "\n\t" // 4 nop nop nop nop (T = 15)
  361. "doneD:" "\n"
  362. : [byte] "+r" (b),
  363. [next] "+r" (next),
  364. [count] "+w" (i)
  365. : [port] "I" (_SFR_IO_ADDR(PORTD)),
  366. [ptr] "e" (ptr),
  367. [hi] "r" (hi),
  368. [lo] "r" (lo));
  369. }
  370. #endif
  371. void send_pixels_12mhz_800_PortB(uint8_t* pixels, size_t sizePixels, uint8_t pinMask)
  372. {
  373. volatile uint16_t i = (uint16_t)sizePixels; // Loop counter
  374. volatile uint8_t* ptr = pixels; // Pointer to next byte
  375. volatile uint8_t b = *ptr++; // Current byte value
  376. volatile uint8_t hi; // PORT w/output bit set high
  377. volatile uint8_t lo; // PORT w/output bit set low
  378. volatile uint8_t next;
  379. hi = PORTB | pinMask;
  380. lo = PORTB & ~pinMask;
  381. next = lo;
  382. if (b & 0x80)
  383. {
  384. next = hi;
  385. }
  386. // Same as above, just set for PORTB & stripped of comments
  387. asm volatile(
  388. "headB:" "\n\t"
  389. "out %[port], %[hi]" "\n\t"
  390. "rcall bitTimeB" "\n\t"
  391. "out %[port], %[hi]" "\n\t"
  392. "rcall bitTimeB" "\n\t"
  393. "out %[port], %[hi]" "\n\t"
  394. "rcall bitTimeB" "\n\t"
  395. "out %[port], %[hi]" "\n\t"
  396. "rcall bitTimeB" "\n\t"
  397. "out %[port], %[hi]" "\n\t"
  398. "rcall bitTimeB" "\n\t"
  399. "out %[port], %[hi]" "\n\t"
  400. "rcall bitTimeB" "\n\t"
  401. "out %[port], %[hi]" "\n\t"
  402. "rcall bitTimeB" "\n\t"
  403. "out %[port] , %[hi]" "\n\t"
  404. "rjmp .+0" "\n\t"
  405. "ld %[byte] , %a[ptr]+" "\n\t"
  406. "out %[port] , %[next]" "\n\t"
  407. "mov %[next] , %[lo]" "\n\t"
  408. "sbrc %[byte] , 7" "\n\t"
  409. "mov %[next] , %[hi]" "\n\t"
  410. "nop" "\n\t"
  411. "out %[port] , %[lo]" "\n\t"
  412. "sbiw %[count], 1" "\n\t"
  413. "brne headB" "\n\t"
  414. "rjmp doneB" "\n\t"
  415. "bitTimeB:" "\n\t"
  416. "out %[port], %[next]" "\n\t"
  417. "mov %[next], %[lo]" "\n\t"
  418. "rol %[byte]" "\n\t"
  419. "sbrc %[byte], 7" "\n\t"
  420. "mov %[next], %[hi]" "\n\t"
  421. "nop" "\n\t"
  422. "out %[port], %[lo]" "\n\t"
  423. "ret" "\n\t"
  424. "doneB:" "\n"
  425. : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i)
  426. : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi),
  427. [lo] "r" (lo));
  428. }
  429. void send_pixels_12mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
  430. {
  431. volatile uint16_t i = (uint16_t)sizePixels; // Loop counter
  432. volatile uint8_t* ptr = pixels; // Pointer to next byte
  433. volatile uint8_t b = *ptr++; // Current byte value
  434. volatile uint8_t hi; // PORT w/output bit set high
  435. volatile uint8_t lo; // PORT w/output bit set low
  436. // 30 instruction clocks per bit: HHHHHHxxxxxxxxxLLLLLLLLLLLLLLL
  437. // ST instructions: ^ ^ ^ (T=0,6,15)
  438. volatile uint8_t next, bit;
  439. hi = *port | pinMask;
  440. lo = *port & ~pinMask;
  441. next = lo;
  442. bit = 8;
  443. asm volatile(
  444. "head30:" "\n\t" // Clk Pseudocode (T = 0)
  445. "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
  446. "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
  447. "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
  448. "rjmp .+0" "\n\t" // 2 nop nop (T = 6)
  449. "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8)
  450. "rjmp .+0" "\n\t" // 2 nop nop (T = 10)
  451. "rjmp .+0" "\n\t" // 2 nop nop (T = 12)
  452. "rjmp .+0" "\n\t" // 2 nop nop (T = 14)
  453. "nop" "\n\t" // 1 nop (T = 15)
  454. "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 17)
  455. "rjmp .+0" "\n\t" // 2 nop nop (T = 19)
  456. "dec %[bit]" "\n\t" // 1 bit-- (T = 20)
  457. "breq nextbyte30" "\n\t" // 1-2 if(bit == 0)
  458. "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 22)
  459. "rjmp .+0" "\n\t" // 2 nop nop (T = 24)
  460. "rjmp .+0" "\n\t" // 2 nop nop (T = 26)
  461. "rjmp .+0" "\n\t" // 2 nop nop (T = 28)
  462. "rjmp head30" "\n\t" // 2 -> head30 (next bit out)
  463. "nextbyte30:" "\n\t" // (T = 22)
  464. "nop" "\n\t" // 1 nop (T = 23)
  465. "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 24)
  466. "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 26)
  467. "sbiw %[count], 1" "\n\t" // 2 i-- (T = 28)
  468. "brne head30" "\n" // 1-2 if(i != 0) -> (next byte)
  469. : [port] "+e" (port),
  470. [byte] "+r" (b),
  471. [bit] "+r" (bit),
  472. [next] "+r" (next),
  473. [count] "+w" (i)
  474. : [hi] "r" (hi),
  475. [lo] "r" (lo),
  476. [ptr] "e" (ptr));
  477. }
  478. #elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L) // 16Mhz CPU
  479. void send_pixels_16mhz_800(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
  480. {
  481. volatile uint16_t i = (uint16_t)sizePixels; // Loop counter
  482. volatile uint8_t* ptr = pixels; // Pointer to next byte
  483. volatile uint8_t b = *ptr++; // Current byte value
  484. volatile uint8_t hi; // PORT w/output bit set high
  485. volatile uint8_t lo; // PORT w/output bit set low
  486. // WS2811 and WS2812 have different hi/lo duty cycles; this is
  487. // similar but NOT an exact copy of the prior 400-on-8 code.
  488. // 20 inst. clocks per bit: HHHHHxxxxxxxxLLLLLLL
  489. // ST instructions: ^ ^ ^ (T=0,5,13)
  490. volatile uint8_t next;
  491. volatile uint8_t bit;
  492. hi = *port | pinMask;
  493. lo = *port & ~pinMask;
  494. next = lo;
  495. bit = 8;
  496. asm volatile(
  497. "head20:" "\n\t" // Clk Pseudocode (T = 0)
  498. "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
  499. "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128)
  500. "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4)
  501. "dec %[bit]" "\n\t" // 1 bit-- (T = 5)
  502. "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7)
  503. "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8)
  504. "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above)
  505. "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10)
  506. "rjmp .+0" "\n\t" // 2 nop nop (T = 12)
  507. "nop" "\n\t" // 1 nop (T = 13)
  508. "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
  509. "nop" "\n\t" // 1 nop (T = 16)
  510. "rjmp .+0" "\n\t" // 2 nop nop (T = 18)
  511. "rjmp head20" "\n\t" // 2 -> head20 (next bit out)
  512. "nextbyte20:" "\n\t" // (T = 10)
  513. "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11)
  514. "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13)
  515. "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15)
  516. "nop" "\n\t" // 1 nop (T = 16)
  517. "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18)
  518. "brne head20" "\n" // 2 if(i != 0) -> (next byte)
  519. : [port] "+e" (port),
  520. [byte] "+r" (b),
  521. [bit] "+r" (bit),
  522. [next] "+r" (next),
  523. [count] "+w" (i)
  524. : [ptr] "e" (ptr),
  525. [hi] "r" (hi),
  526. [lo] "r" (lo));
  527. }
  528. void send_pixels_16mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* port, uint8_t pinMask)
  529. {
  530. volatile size_t i = sizePixels; // Loop counter
  531. volatile uint8_t* ptr = pixels; // Pointer to next byte
  532. volatile uint8_t b = *ptr++; // Current byte value
  533. volatile uint8_t hi; // PORT w/output bit set high
  534. volatile uint8_t lo; // PORT w/output bit set low
  535. // The 400 KHz clock on 16 MHz MCU is the most 'relaxed' version.
  536. // 40 inst. clocks per bit: HHHHHHHHxxxxxxxxxxxxLLLLLLLLLLLLLLLLLLLL
  537. // ST instructions: ^ ^ ^ (T=0,8,20)
  538. volatile uint8_t next, bit;
  539. hi = *port | pinMask;
  540. lo = *port & ~pinMask;
  541. next = lo;
  542. bit = 8;
  543. asm volatile(
  544. "head40:" "\n\t" // Clk Pseudocode (T = 0)
  545. "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
  546. "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128)
  547. "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4)
  548. "rjmp .+0" "\n\t" // 2 nop nop (T = 6)
  549. "rjmp .+0" "\n\t" // 2 nop nop (T = 8)
  550. "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 10)
  551. "rjmp .+0" "\n\t" // 2 nop nop (T = 12)
  552. "rjmp .+0" "\n\t" // 2 nop nop (T = 14)
  553. "rjmp .+0" "\n\t" // 2 nop nop (T = 16)
  554. "rjmp .+0" "\n\t" // 2 nop nop (T = 18)
  555. "rjmp .+0" "\n\t" // 2 nop nop (T = 20)
  556. "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 22)
  557. "nop" "\n\t" // 1 nop (T = 23)
  558. "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 24)
  559. "dec %[bit]" "\n\t" // 1 bit-- (T = 25)
  560. "breq nextbyte40" "\n\t" // 1-2 if(bit == 0)
  561. "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 27)
  562. "nop" "\n\t" // 1 nop (T = 28)
  563. "rjmp .+0" "\n\t" // 2 nop nop (T = 30)
  564. "rjmp .+0" "\n\t" // 2 nop nop (T = 32)
  565. "rjmp .+0" "\n\t" // 2 nop nop (T = 34)
  566. "rjmp .+0" "\n\t" // 2 nop nop (T = 36)
  567. "rjmp .+0" "\n\t" // 2 nop nop (T = 38)
  568. "rjmp head40" "\n\t" // 2 -> head40 (next bit out)
  569. "nextbyte40:" "\n\t" // (T = 27)
  570. "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 28)
  571. "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 30)
  572. "rjmp .+0" "\n\t" // 2 nop nop (T = 32)
  573. "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 34)
  574. "rjmp .+0" "\n\t" // 2 nop nop (T = 36)
  575. "sbiw %[count], 1" "\n\t" // 2 i-- (T = 38)
  576. "brne head40" "\n" // 1-2 if(i != 0) -> (next byte)
  577. : [port] "+e" (port),
  578. [byte] "+r" (b),
  579. [bit] "+r" (bit),
  580. [next] "+r" (next),
  581. [count] "+w" (i)
  582. : [ptr] "e" (ptr),
  583. [hi] "r" (hi),
  584. [lo] "r" (lo));
  585. }
  586. #else
  587. #error "CPU SPEED NOT SUPPORTED"
  588. #endif
  589. #endif