apa102.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include "lauxlib.h"
  4. #include "module.h"
  5. #include "platform.h"
  6. #include "user_interface.h"
  7. #include "pixbuf.h"
  8. #define NOP asm volatile(" nop \n\t")
  9. static inline void apa102_send_byte(uint32_t data_pin, uint32_t clock_pin, uint8_t byte) {
  10. int i;
  11. for (i = 0; i < 8; i++) {
  12. if (byte & 0x80) {
  13. GPIO_OUTPUT_SET(data_pin, PLATFORM_GPIO_HIGH); // Set pin high
  14. } else {
  15. GPIO_OUTPUT_SET(data_pin, PLATFORM_GPIO_LOW); // Set pin low
  16. }
  17. GPIO_OUTPUT_SET(clock_pin, PLATFORM_GPIO_HIGH); // Set pin high
  18. byte <<= 1;
  19. NOP;
  20. NOP;
  21. GPIO_OUTPUT_SET(clock_pin, PLATFORM_GPIO_LOW); // Set pin low
  22. NOP;
  23. NOP;
  24. }
  25. }
  26. static void apa102_send_buffer(uint32_t data_pin, uint32_t clock_pin, uint32_t *buf, uint32_t nbr_frames) {
  27. int i;
  28. // Send 32-bit Start Frame that's all 0x00
  29. apa102_send_byte(data_pin, clock_pin, 0x00);
  30. apa102_send_byte(data_pin, clock_pin, 0x00);
  31. apa102_send_byte(data_pin, clock_pin, 0x00);
  32. apa102_send_byte(data_pin, clock_pin, 0x00);
  33. // Send 32-bit LED Frames
  34. for (i = 0; i < nbr_frames; i++) {
  35. uint8_t *byte = (uint8_t *) buf++;
  36. // Set the first 3 bits of that byte to 1.
  37. // This makes the lua interface easier to use since you
  38. // don't have to worry about creating invalid LED Frames.
  39. byte[0] |= 0xE0;
  40. apa102_send_byte(data_pin, clock_pin, byte[0]);
  41. apa102_send_byte(data_pin, clock_pin, byte[1]);
  42. apa102_send_byte(data_pin, clock_pin, byte[2]);
  43. apa102_send_byte(data_pin, clock_pin, byte[3]);
  44. }
  45. // Send 32-bit End Frames
  46. uint32_t required_postamble_frames = (nbr_frames + 1) / 2;
  47. for (i = 0; i < required_postamble_frames; i++) {
  48. apa102_send_byte(data_pin, clock_pin, 0xFF);
  49. apa102_send_byte(data_pin, clock_pin, 0xFF);
  50. apa102_send_byte(data_pin, clock_pin, 0xFF);
  51. apa102_send_byte(data_pin, clock_pin, 0xFF);
  52. }
  53. }
  54. // Lua: apa102.write(data_pin, clock_pin, "string")
  55. // Byte quads in the string are interpreted as (brightness, B, G, R) values.
  56. // Only the first 5 bits of the brightness value is actually used (0-31).
  57. // This function does not corrupt your buffer.
  58. //
  59. // apa102.write(1, 3, string.char(31, 0, 255, 0)) uses GPIO5 for DATA and GPIO0 for CLOCK and sets the first LED green, with the brightness 31 (out of 0-32)
  60. // apa102.write(5, 6, string.char(255, 0, 0, 255):rep(10)) uses GPIO14 for DATA and GPIO12 for CLOCK and sets ten LED to red, with the brightness 31 (out of 0-32).
  61. // Brightness values are clamped to 0-31.
  62. static int apa102_write(lua_State* L) {
  63. uint8_t data_pin = luaL_checkinteger(L, 1);
  64. MOD_CHECK_ID(gpio, data_pin);
  65. uint32_t alt_data_pin = pin_num[data_pin];
  66. uint8_t clock_pin = luaL_checkinteger(L, 2);
  67. MOD_CHECK_ID(gpio, clock_pin);
  68. uint32_t alt_clock_pin = pin_num[clock_pin];
  69. const char *buf;
  70. uint32_t nbr_frames;
  71. switch(lua_type(L, 3)) {
  72. case LUA_TSTRING: {
  73. size_t buf_len;
  74. buf = luaL_checklstring(L, 3, &buf_len);
  75. nbr_frames = buf_len / 4;
  76. break;
  77. }
  78. case LUA_TUSERDATA: {
  79. pixbuf *buffer = pixbuf_from_lua_arg(L, 3);
  80. luaL_argcheck(L, buffer->nchan == 4, 3, "Pixbuf not 4-channel");
  81. buf = (const char *)buffer->values;
  82. nbr_frames = buffer->npix;
  83. break;
  84. }
  85. default:
  86. return luaL_argerror(L, 3, "String or pixbuf expected");
  87. }
  88. if (nbr_frames > 100000) {
  89. return luaL_error(L, "The supplied buffer is too long, and might cause the callback watchdog to bark.");
  90. }
  91. // Initialize the output pins
  92. platform_gpio_mode(data_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
  93. GPIO_OUTPUT_SET(alt_data_pin, PLATFORM_GPIO_HIGH); // Set pin high
  94. platform_gpio_mode(clock_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
  95. GPIO_OUTPUT_SET(alt_clock_pin, PLATFORM_GPIO_LOW); // Set pin low
  96. // Send the buffers
  97. apa102_send_buffer(alt_data_pin, alt_clock_pin, (uint32_t *) buf, (uint32_t) nbr_frames);
  98. return 0;
  99. }
  100. LROT_BEGIN(apa102, NULL, 0)
  101. LROT_FUNCENTRY( write, apa102_write )
  102. LROT_END(apa102, NULL, 0)
  103. NODEMCU_MODULE(APA102, "apa102", apa102, NULL);