├── .gitignore ├── library.json ├── src ├── esp_iomux.c ├── debug.h ├── iomux.h ├── i2s_dma.h ├── interrupts.h ├── common_macros.h ├── i2s_regs.h ├── iomux_regs.h ├── i2s_dma.c ├── ws2812_i2s.c └── slc_regs.h ├── LICENSE-MIT.txt ├── LICENSE-BSD.txt ├── include └── ws2812_i2s.h ├── README.md └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | libws2812_i2s.a 2 | build 3 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk_dependencies": [], 3 | "name": "ws2812_i2s", 4 | "license": "MIT", 5 | "author": "Johannes Schriewer ", 6 | "url": "git+https://github.com/esp8266-setup/ws2812-i2s.git@master", 7 | "version": "1.0.0", 8 | "dependencies": [], 9 | "extra_includes": "", 10 | "extra_cflags": "-DLED_TYPE=LED_TYPE_WS2812 -DLED_MODE=LED_MODE_RGB", 11 | "extra_ldflags": "" 12 | } -------------------------------------------------------------------------------- /src/esp_iomux.c: -------------------------------------------------------------------------------- 1 | /* Compiler-level implementation for esp/iomux.h and esp/iomux_private.h 2 | * 3 | * Part of esp-open-rtos 4 | * Copyright (C) 2015 Superhouse Automation Pty Ltd 5 | * BSD Licensed as described in the file LICENSE 6 | */ 7 | #include "iomux.h" 8 | #include "common_macros.h" 9 | 10 | const static IRAM_DATA uint32_t IOMUX_TO_GPIO[] = { 12, 13, 14, 15, 3, 1, 6, 7, 8, 9, 10, 11, 0, 2, 4, 5 }; 11 | const static IRAM_DATA uint32_t GPIO_TO_IOMUX[] = { 12, 5, 13, 4, 14, 15, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3 }; 12 | 13 | uint8_t IRAM gpio_to_iomux(const uint8_t gpio_number) { 14 | return GPIO_TO_IOMUX[gpio_number]; 15 | } 16 | 17 | uint8_t IRAM iomux_to_gpio(const uint8_t iomux_number) { 18 | return IOMUX_TO_GPIO[iomux_number]; 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | I2S code is licensed under the MIT license: 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2016 sheinz (https://github.com/sheinz) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /LICENSE-BSD.txt: -------------------------------------------------------------------------------- 1 | ESP open RTOS is BSD licensed: 2 | 3 | Copyright (C) 2015 Superhouse Automation Pty Ltd 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef debug_h__included 2 | #define debug_h__included 3 | 4 | #include 5 | #include 6 | 7 | // Debug levels 8 | #define TRACE 0 9 | #define DEBUG 1 10 | #define INFO 2 11 | #define WARN 3 12 | #define ERROR 4 13 | #define FATAL 5 14 | 15 | // minimum level of messages to print, 16 | // may be overridden by defining it before including the header 17 | #ifndef DEBUG_LEVEL 18 | #define DEBUG_LEVEL INFO 19 | #endif 20 | 21 | // Debug logging macro: 22 | // Usage: LOG(INFO, "Hello %s", name) 23 | // 24 | // You may leave out any parameters after _format to just log a string 25 | // a newline is appended automatically 26 | #define LOG(_level, _format, args...) { \ 27 | if (_level >= DEBUG_LEVEL) { \ 28 | static const char flash_str[] ICACHE_RODATA_ATTR STORE_ATTR = _format "\n"; \ 29 | printf(flash_str, ## args); \ 30 | } \ 31 | } 32 | 33 | 34 | // Log a JSON object 35 | // Usage: LOG_JSON(INFO, "MyObject", myObject) 36 | // 37 | // this pretty prints the json object and logs it 38 | #define LOG_JSON(_level, _msg, _json_obj, args...) { \ 39 | if (_level >= DEBUG_LEVEL) { \ 40 | static const char flash_str[] ICACHE_RODATA_ATTR STORE_ATTR = _msg ": %s\n"; \ 41 | char *out = cJSON_Print(_json_obj); \ 42 | printf(flash_str, ## args, out); \ 43 | free(out); \ 44 | } \ 45 | } 46 | 47 | #if DEBUG_LEVEL <= DEBUG 48 | #define LOG_HEAP(_msg) { \ 49 | static const char flash_str[] ICACHE_RODATA_ATTR STORE_ATTR = _msg " - Free heap: %d\n"; \ 50 | printf(flash_str, system_get_free_heap_size()); \ 51 | } 52 | #else 53 | #define LOG_HEAP(_msg) {} 54 | #endif 55 | 56 | #define HEXDUMP(_level, _title, _data, _len) { \ 57 | if (_level >= DEBUG_LEVEL) { \ 58 | os_printf(_title "\n"); \ 59 | for (uint16_t i = 0; i < (_len); i++) { \ 60 | os_printf("%02x ", (_data)[i]); \ 61 | if (i % 16 == 0) { \ 62 | os_printf("\n"); \ 63 | } \ 64 | } \ 65 | if ((_len) % 16 != 0) { \ 66 | os_printf("\n"); \ 67 | } \ 68 | } \ 69 | } 70 | 71 | #endif -------------------------------------------------------------------------------- /include/ws2812_i2s.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 sheinz (https://github.com/sheinz) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef __WS2812_I2S_H__ 25 | #define __WS2812_I2S_H__ 26 | 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | /** Use this one if you are using WS2811 or WS2812 neopixels */ 35 | #define LED_TYPE_WS2812 0 36 | 37 | /** Use this one if you are using the SK6812 type neopixels */ 38 | #define LED_TYPE_SK6812 1 39 | 40 | /** define this macro on the cmdline (-DLED_TYPE=) to select 41 | * which timings to use */ 42 | #ifndef LED_TYPE 43 | #define LED_TYPE LED_TYPE_WS2812 44 | #endif 45 | 46 | /** Your LEDs have three color components */ 47 | #define LED_MODE_RGB 3 48 | 49 | /** Your LEDs have four color components */ 50 | #define LED_MODE_RGBW 4 51 | 52 | /** Select how many color components are in your pixels by 53 | * defining it on the cmdline (-DLED_MODE=) */ 54 | #ifndef LED_MODE 55 | #define LED_MODE LED_MODE_RGB 56 | #endif 57 | 58 | typedef struct { 59 | uint8_t red; 60 | uint8_t green; 61 | uint8_t blue; 62 | #if LED_MODE == LED_MODE_RGBW 63 | uint8_t white; 64 | #endif 65 | } ws2812_pixel_t; 66 | 67 | /** 68 | * Initialize i2s and dma subsystems to work with ws2812 led strip. 69 | * 70 | * Please note that each pixel will take 12 bytes of RAM. 71 | * 72 | * @param pixels_number Number of pixels in the strip. 73 | */ 74 | void ws2812_i2s_init(uint32_t pixels_number); 75 | 76 | /** 77 | * Update ws2812 pixels. 78 | * 79 | * @param pixels Array of 'pixels_number' pixels. The array must contain all 80 | * the pixels. 81 | */ 82 | void ws2812_i2s_update(ws2812_pixel_t *pixels); 83 | 84 | #ifdef __cplusplus 85 | } 86 | #endif 87 | 88 | #endif // __WS2812_I2S_H__ 89 | -------------------------------------------------------------------------------- /src/iomux.h: -------------------------------------------------------------------------------- 1 | /** esp/iomux.h 2 | * 3 | * Configuration of iomux registers. 4 | * 5 | * Part of esp-open-rtos 6 | * Copyright (C) 2015 Superhouse Automation Pty Ltd 7 | * BSD Licensed as described in the file LICENSE 8 | */ 9 | #ifndef _ESP_IOMUX_H 10 | #define _ESP_IOMUX_H 11 | 12 | #include 13 | 14 | typedef volatile uint32_t *esp_reg_t; 15 | 16 | #include "iomux_regs.h" 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | /** 23 | * Convert a GPIO pin number to an iomux register index. 24 | * 25 | * This function should evaluate to a constant if the gpio_number is 26 | * known at compile time, or return the result from a lookup table if not. 27 | * 28 | */ 29 | uint8_t IRAM gpio_to_iomux(const uint8_t gpio_number); 30 | 31 | /** 32 | * Convert an iomux register index to a GPIO pin number. 33 | * 34 | * This function should evaluate to a constant if the iomux_num is 35 | * known at compile time, or return the result from a lookup table if not. 36 | * 37 | */ 38 | uint8_t IRAM iomux_to_gpio(const uint8_t iomux_num); 39 | 40 | /** 41 | * Directly get the IOMUX register for a particular gpio number 42 | * 43 | * ie *gpio_iomux_reg(3) is equivalent to IOMUX_GPIO3 44 | */ 45 | inline static esp_reg_t gpio_iomux_reg(const uint8_t gpio_number) { 46 | return &(IOMUX.PIN[gpio_to_iomux(gpio_number)]); 47 | } 48 | 49 | /** 50 | * Set IOMUX function. 51 | * 52 | * @param iomux_num Index of IOMUX register. Can be converted from GPIO number 53 | * with gpio_to_iomux. 54 | * @param iomux_func GPIO function definition IOMUX_GPIOn_FUNC_* 55 | */ 56 | inline static void iomux_set_function(uint8_t iomux_num, uint32_t iomux_func) { 57 | uint32_t prev = IOMUX.PIN[iomux_num] & ~IOMUX_PIN_FUNC_MASK; 58 | IOMUX.PIN[iomux_num] = iomux_func | prev; 59 | } 60 | 61 | inline static void iomux_set_direction_flags(uint8_t iomux_num, uint32_t dir_flags) { 62 | uint32_t mask = IOMUX_PIN_OUTPUT_ENABLE | IOMUX_PIN_OUTPUT_ENABLE_SLEEP; 63 | uint32_t prev = IOMUX.PIN[iomux_num] & ~mask; 64 | IOMUX.PIN[iomux_num] = dir_flags | prev; 65 | } 66 | 67 | inline static void iomux_set_pullup_flags(uint8_t iomux_num, uint32_t pullup_flags) { 68 | uint32_t mask = IOMUX_PIN_PULLUP | IOMUX_PIN_PULLDOWN | IOMUX_PIN_PULLUP_SLEEP | IOMUX_PIN_PULLDOWN_SLEEP; 69 | uint32_t prev = IOMUX.PIN[iomux_num] & ~mask; 70 | IOMUX.PIN[iomux_num] = pullup_flags | prev; 71 | } 72 | 73 | /** 74 | * Set a pin to the GPIO function. 75 | * 76 | * This allows you to set pins to GPIO without knowing in advance the 77 | * exact register masks to use. 78 | * 79 | * Sets the function and direction, but leaves the pullup configuration the 80 | * same as before. 81 | */ 82 | inline static void iomux_set_gpio_function(uint8_t gpio_number, bool output_enable) { 83 | const uint8_t iomux_num = gpio_to_iomux(gpio_number); 84 | const uint32_t func = iomux_num > 11 ? IOMUX_FUNC(0) : IOMUX_FUNC(3); 85 | iomux_set_function(iomux_num, func); 86 | iomux_set_direction_flags(iomux_num, output_enable ? IOMUX_PIN_OUTPUT_ENABLE : 0); 87 | } 88 | 89 | #ifdef __cplusplus 90 | } 91 | #endif 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /src/i2s_dma.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 sheinz (https://github.com/sheinz) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef __I2S_DMA_H__ 25 | #define __I2S_DMA_H__ 26 | 27 | #include 28 | #include 29 | #include "slc_regs.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | typedef void (*i2s_dma_isr_t)(void *); 36 | 37 | typedef struct dma_descriptor { 38 | uint32_t blocksize:12; 39 | uint32_t datalen:12; 40 | uint32_t unused:5; 41 | uint32_t sub_sof:1; 42 | uint32_t eof:1; 43 | uint32_t owner:1; 44 | 45 | void* buf_ptr; 46 | struct dma_descriptor *next_link_ptr; 47 | } dma_descriptor_t; 48 | 49 | typedef struct { 50 | uint8_t bclk_div; 51 | uint8_t clkm_div; 52 | } i2s_clock_div_t; 53 | 54 | typedef struct { 55 | bool data; 56 | bool clock; 57 | bool ws; 58 | } i2s_pins_t; 59 | 60 | /** 61 | * Initialize I2S and DMA subsystems. 62 | * 63 | * @param isr ISR handler. Can be NULL if interrupt handling is not needed. 64 | * @param arg ISR handler arg. 65 | * @param clock_div I2S clock configuration. 66 | * @param pins I2S pin configuration. Specifies which pins are enabled in I2S. 67 | */ 68 | void i2s_dma_init(i2s_dma_isr_t isr, void *arg, i2s_clock_div_t clock_div, i2s_pins_t pins); 69 | 70 | /** 71 | * Calculate I2S dividers for the specified frequency. 72 | * 73 | * I2S_FREQ = 160000000 / (bclk_div * clkm_div) 74 | * Base frequency is independent from the CPU frequency. 75 | */ 76 | i2s_clock_div_t i2s_get_clock_div(int32_t freq); 77 | 78 | /** 79 | * Start I2S transmittion. 80 | * 81 | * @param descr Pointer to the first descriptor in the linked list of descriptors. 82 | */ 83 | void i2s_dma_start(dma_descriptor_t *descr); 84 | 85 | /** 86 | * Stop I2S transmittion. 87 | */ 88 | void i2s_dma_stop(); 89 | 90 | /** 91 | * Clear interrupt in the I2S ISR handler. 92 | * 93 | * It is intended to be called from ISR. 94 | */ 95 | inline void i2s_dma_clear_interrupt() { 96 | SLC.INT_CLEAR = 0xFFFFFFFF; 97 | } 98 | 99 | /** 100 | * Check if it is EOF interrupt. 101 | * 102 | * It is intended to be called from ISR. 103 | */ 104 | inline bool i2s_dma_is_eof_interrupt() { 105 | return (SLC.INT_STATUS & SLC_INT_STATUS_RX_EOF); 106 | } 107 | 108 | /** 109 | * Get pointer to a descriptor that caused EOF interrupt. 110 | * It is the last processed descriptor. 111 | * 112 | * It is intended to be called from ISR. 113 | */ 114 | inline dma_descriptor_t *i2s_dma_get_eof_descriptor() { 115 | return (dma_descriptor_t*)SLC.RX_EOF_DESCRIPTOR_ADDR; 116 | } 117 | 118 | #ifdef __cplusplus 119 | } 120 | #endif 121 | 122 | #endif // __I2S_DMA_H__ 123 | -------------------------------------------------------------------------------- /src/interrupts.h: -------------------------------------------------------------------------------- 1 | /* ESP8266 Xtensa interrupt management functions 2 | * 3 | * Some (w/ sdk_ prefix) are implemented in binary libs, rest are 4 | * inlines replacing functions in the binary libraries. 5 | * 6 | * Part of esp-open-rtos 7 | * Copyright (C) 2015 Superhouse Automation Pty Ltd 8 | * BSD Licensed as described in the file LICENSE 9 | */ 10 | #ifndef _XTENSA_INTERRUPTS_H 11 | #define _XTENSA_INTERRUPTS_H 12 | #include 13 | #include 14 | #include 15 | #include "common_macros.h" 16 | 17 | 18 | /* Interrupt numbers for level 1 exception handler. */ 19 | typedef enum { 20 | INUM_WDEV_FIQ = 0, 21 | INUM_SLC = 1, 22 | INUM_SPI = 2, 23 | INUM_RTC = 3, 24 | INUM_GPIO = 4, 25 | INUM_UART = 5, 26 | INUM_TICK = 6, /* RTOS timer tick, possibly xtensa CPU CCOMPARE0(?) */ 27 | INUM_SOFT = 7, 28 | INUM_WDT = 8, 29 | INUM_TIMER_FRC1 = 9, 30 | 31 | /* FRC2 default handler. Configured by sdk_ets_timer_init, which 32 | runs as part of default libmain.a startup code, assigns 33 | interrupt handler to sdk_vApplicationTickHook+0x68 34 | */ 35 | INUM_TIMER_FRC2 = 10, 36 | } xt_isr_num_t; 37 | 38 | void _xt_user_exit(void); 39 | 40 | /* The normal running level is 0. 41 | * The system tick isr, timer frc2_isr, sv_isr etc run at level 1. 42 | * Debug exceptions run at level 2? 43 | * The wdev nmi runs at level 3. 44 | */ 45 | static inline uint32_t _xt_get_intlevel(void) { 46 | uint32_t level; 47 | __asm__ volatile("rsr %0, ps" : "=a"(level)); 48 | return level & 0xf; 49 | } 50 | 51 | /* 52 | * There are conflicting definitions for XCHAL_EXCM_LEVEL. Newlib 53 | * defines it to be 1 and xtensa_rtos.h defines it to be 3. Don't want 54 | * 3 as that is for the NMI and might want to check that the OS apis 55 | * are not entered in level 3. Setting the interrupt level to 3 does 56 | * not disable the NMI anyway. So set the level to 2. 57 | */ 58 | 59 | #ifdef XCHAL_EXCM_LEVEL 60 | #undef XCHAL_EXCM_LEVEL 61 | #define XCHAL_EXCM_LEVEL 2 62 | #endif 63 | 64 | /* Disable interrupts and return the old ps value, to pass into 65 | _xt_restore_interrupts later. 66 | 67 | This is desirable to use in place of 68 | portDISABLE_INTERRUPTS/portENABLE_INTERRUPTS for 69 | non-FreeRTOS & non-portable code. 70 | */ 71 | static inline uint32_t _xt_disable_interrupts(void) { 72 | uint32_t old_level; 73 | __asm__ volatile ("rsil %0, " XTSTR(XCHAL_EXCM_LEVEL) : "=a" (old_level)); 74 | return old_level; 75 | } 76 | 77 | /* Restore PS level. Intended to be used with _xt_disable_interrupts */ 78 | static inline void _xt_restore_interrupts(uint32_t new_ps) { 79 | __asm__ volatile ("wsr %0, ps; rsync" :: "a" (new_ps)); 80 | } 81 | 82 | static inline uint32_t _xt_isr_unmask(uint32_t unmask) { 83 | uint32_t old_level = _xt_disable_interrupts(); 84 | uint32_t intenable; 85 | __asm__ volatile ("rsr %0, intenable" : "=a" (intenable)); 86 | __asm__ volatile ("wsr %0, intenable;" :: "a" (intenable | unmask)); 87 | _xt_restore_interrupts(old_level); 88 | return intenable; 89 | } 90 | 91 | static inline uint32_t _xt_isr_mask(uint32_t mask) { 92 | uint32_t old_level = _xt_disable_interrupts(); 93 | uint32_t intenable; 94 | __asm__ volatile ("rsr %0, intenable" : "=a" (intenable)); 95 | __asm__ volatile ("wsr %0, intenable;" :: "a" (intenable & ~mask)); 96 | _xt_restore_interrupts(old_level); 97 | return intenable; 98 | } 99 | 100 | static inline uint32_t _xt_read_ints(void) { 101 | uint32_t interrupt; 102 | __asm__ volatile ("rsr %0, interrupt" : "=a" (interrupt)); 103 | return interrupt; 104 | } 105 | 106 | static inline void _xt_clear_ints(uint32_t mask) { 107 | __asm__ volatile ("wsr %0, intclear; esync" :: "a" (mask)); 108 | } 109 | 110 | typedef void (* _xt_isr)(void *arg); 111 | void _xt_isr_attach (uint8_t i, _xt_isr func, void *arg); 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ws2812-i2s library 2 | 3 | This a library to be used in firmware for the ESP8266. 4 | 5 | ## What is this 6 | 7 | This library is a I2S Interface to drive WS2811/WS2812 and SK6812 LED strips. 8 | The code is lifted out of the [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos) project and 9 | has been modified to work with SK6812 LEDs and compile with the original Espressif RTOS SDK. 10 | 11 | The communication with the LEDs is over I2S and uses DMA to offload timing critical stuff off the CPU. 12 | You will need a framebuffer (8bit per color) for each of your LEDs and the library internally needs 13 | DMA buffers which are 4 bytes per LED per color. 14 | 15 | The I2S pin is shared with UART0 RxD so you will not be able to send anything to the ESP over serial anymore. 16 | 17 | ## API 18 | 19 | ### Hardware selection 20 | 21 | To select which type of LED you want to connect to your ESP module you may have to edit the Makefile: 22 | 23 | ```make 24 | CFLAGS += -DLED_TYPE=LED_TYPE_WS2812 -DLED_MODE=LED_MODE_RGB 25 | ``` 26 | 27 | #### LED_TYPE 28 | 29 | There are two possible values for `LED_TYPE`: 30 | 31 | - `LED_TYPE_WS2812` use this one for WS2811 or WS2812 LEDs 32 | - `LED_TYPE_SK6812` use this one for the SK6812 types (mostly used by RGBW strips) 33 | 34 | #### LED_MODE 35 | 36 | This setting defines how many color components are in your LEDs 37 | 38 | - `LED_MODE_RGB` this is the usual default, 3 colors. They are sent in GRB order. 39 | - `LED_MODE_RGBW` use this if your LEDs have 4 color components, like the RGBW strips 40 | with a dedicated white LED in addition to the red, green and blue ones. 41 | 42 | ### C API 43 | 44 | ```c 45 | void ws2812_i2s_init(uint32_t pixels_number); 46 | ``` 47 | 48 | Call this one with the pixel count before starting to send out data. This will initalize 49 | all needed buffers and set the IOMUX for GPIO 3 from the default UART0 RxD to I2S. 50 | 51 | ```c 52 | void ws2812_i2s_update(ws2812_pixel_t *pixels); 53 | ``` 54 | 55 | Update the LED strip with new pixel data. The library assumes the number of pixels in this 56 | array are the same as with the init call. 57 | 58 | One pixel looks like this: 59 | 60 | ```c 61 | typedef struct { 62 | uint8_t red; 63 | uint8_t green; 64 | uint8_t blue; 65 | #if LED_MODE == LED_MODE_RGBW 66 | uint8_t white; 67 | #endif 68 | } ws2812_pixel_t; 69 | ``` 70 | 71 | so as you can see the `white` component is only available when configured for a RGBW strip. 72 | 73 | ## Usage instructions 74 | 75 | This library is built with the [esp8266-setup](http://github.com/esp8266-setup/esp8266-setup) tool in mind. 76 | 77 | If you are already using the `esp8266-setup` build system just issue the following command in your project dir: 78 | 79 | ```bash 80 | esp8266-setup add-library git+https://github.com/esp8266-setup/ws2812_i2s.git@master 81 | ``` 82 | 83 | If you do not want to use the `esp8266-setup` build system just grab the files from the `src` and `include` directories and add them to your project. 84 | 85 | Be aware that most libraries built with this build system use the `C99` standard, so you may have to add `--std=c99` to your `CFLAGS`. 86 | 87 | ## Build instructions 88 | 89 | - Install the ESP8266 Toolchain 90 | - Download the ESP8266 RTOS SDK 91 | - Compile the library: 92 | ```bash 93 | make \ 94 | XTENSA_TOOLS_ROOT=/path/to/compiler/bin \ 95 | SDK_PATH=/path/to/ESP8266_RTOS_SDK 96 | ``` 97 | 98 | - The finished library will be placed in the current directory under the name 99 | of `libws2812-i2s.a` 100 | - Corresponding include files are in `include/` 101 | 102 | If you installed the ESP SDK and toolchain to a default location (see below) you may just type `make` to build. 103 | 104 | ### Default locations 105 | 106 | #### Windows 107 | 108 | - **XTENSA\_TOOLS\_ROOT**: `c:\ESP8266\xtensa-lx106-elf\bin` 109 | - **SDK_PATH**: `c:\ESP8266\ESP8266_RTOS_SDK` 110 | 111 | #### MacOS X 112 | 113 | We assume that your default file system is not case sensitive so you will have created a sparse bundle with a case sensitive filesystem which is named `ESP8266`: 114 | 115 | - **XTENSA\_TOOLS\_ROOT**: `/Volumes/ESP8266/esp-open-sdk/xtensa-lx106-elf/bin` 116 | - **SDK_PATH**: `/Volumes/ESP8266/ESP8266_RTOS_SDK` 117 | 118 | #### Linux 119 | 120 | - **XTENSA\_TOOLS\_ROOT**: `/opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf/bin` 121 | - **SDK_PATH**: `/opt/Espressif/ESP8266_RTOS_SDK` 122 | -------------------------------------------------------------------------------- /src/common_macros.h: -------------------------------------------------------------------------------- 1 | /* Some common compiler macros 2 | * 3 | * Not esp8266-specific. 4 | * 5 | * Part of esp-open-rtos 6 | * Copyright (C) 2015 Superhouse Automation Pty Ltd 7 | * BSD Licensed as described in the file LICENSE 8 | */ 9 | 10 | #ifndef _COMMON_MACROS_H 11 | #define _COMMON_MACROS_H 12 | 13 | #include 14 | 15 | #define UNUSED __attributed((unused)) 16 | 17 | #ifndef BIT 18 | #define BIT(X) (1<<(X)) 19 | #endif 20 | 21 | /* These macros convert values to/from bitfields specified by *_M and *_S (mask 22 | * and shift) constants. Used primarily with ESP8266 register access. 23 | */ 24 | 25 | #define VAL2FIELD(fieldname, value) ((value) << fieldname##_S) 26 | #define FIELD2VAL(fieldname, regbits) (((regbits) >> fieldname##_S) & fieldname##_M) 27 | 28 | #define FIELD_MASK(fieldname) (fieldname##_M << fieldname##_S) 29 | #define SET_FIELD(regbits, fieldname, value) (((regbits) & ~FIELD_MASK(fieldname)) | VAL2FIELD(fieldname, value)) 30 | 31 | /* VAL2FIELD/SET_FIELD do not normally check to make sure that the passed value 32 | * will fit in the specified field (without clobbering other bits). This makes 33 | * them faster and is usually fine. If you do need to make sure that the value 34 | * will not overflow the field, use VAL2FIELD_M or SET_FIELD_M (which will 35 | * first mask the supplied value to only the allowed number of bits) instead. 36 | */ 37 | #define VAL2FIELD_M(fieldname, value) (((value) & fieldname##_M) << fieldname##_S) 38 | #define SET_FIELD_M(regbits, fieldname, value) (((regbits) & ~FIELD_MASK(fieldname)) | VAL2FIELD_M(fieldname, value)) 39 | 40 | /* Set bits in reg with specified mask. 41 | */ 42 | #define SET_MASK_BITS(reg, mask) (reg) |= (mask) 43 | 44 | /* Clear bits in reg with specified mask 45 | */ 46 | #define CLEAR_MASK_BITS(reg, mask) (reg) &= ~(mask) 47 | 48 | /* Use the IRAM macro to place functions into Instruction RAM (IRAM) 49 | instead of flash (aka irom). 50 | 51 | (This is the opposite to the Espressif SDK, where functions default 52 | to being placed in IRAM but the ICACHE_FLASH_ATTR attribute will 53 | place them in flash.) 54 | 55 | Use the IRAM attribute for functions which are called when the 56 | flash may not be available (for example during NMI exceptions), or 57 | for functions which are called very frequently and need high 58 | performance. 59 | 60 | Usage example: 61 | 62 | void IRAM high_performance_function(void) 63 | { 64 | // do important thing here 65 | } 66 | 67 | Bear in mind IRAM is limited (32KB), compared to up to 1MB of flash. 68 | */ 69 | #define IRAM __attribute__((section(".iram1.text"))) 70 | 71 | /* Use the RAM macro to place constant data (rodata) into RAM (data 72 | RAM) instead of the default placement in flash. This is useful for 73 | constant data which needs high performance access. 74 | 75 | Usage example: 76 | 77 | const RAM uint8_t constants[] = { 1, 2, 3, 7 }; 78 | 79 | When placing string literals in RAM, they need to be declared with 80 | the type "const char[]" not "const char *" 81 | 82 | Usage example: 83 | 84 | const RAM char hello_world[] = "Hello World"; 85 | */ 86 | #define RAM __attribute__((section(".data"))) 87 | 88 | /* Use the IRAM_DATA macro to place data into Instruction RAM (IRAM) 89 | instead of the default of flash (for constant data) or data RAM 90 | (for non-constant data). 91 | 92 | This may be useful to free up data RAM. However all data read from 93 | any instruction space (either IRAM or Flash) must be 32-bit aligned 94 | word reads. Reading unaligned data stored with IRAM_DATA will be 95 | slower than reading data stored in RAM. You can't perform unaligned 96 | writes to IRAM. 97 | */ 98 | #define IRAM_DATA __attribute__((section(".iram1.data"))) 99 | 100 | /* Use the IROM macro to store constant values in IROM flash. In 101 | esp-open-rtos this is already the default location for most constant 102 | data (rodata), so you don't need this attribute in 99% of cases. 103 | 104 | The exceptions are to mark data in the core & freertos libraries, 105 | where the default for constant data storage is RAM. 106 | 107 | (Unlike the Espressif SDK you don't need to use an attribute like 108 | ICACHE_FLASH_ATTR for functions, they go into flash by default.) 109 | 110 | Important to note: IROM flash is accessed via 32-bit word aligned 111 | reads. esp-open-rtos does some magic to "fix" unaligned reads, but 112 | performance is reduced. 113 | */ 114 | #ifdef __cplusplus 115 | #define IROM __attribute__((section(".irom0.literal"))) 116 | #else 117 | #define IROM __attribute__((section(".irom0.literal"))) const 118 | #endif 119 | 120 | #ifndef XTSTR 121 | #define _XTSTR(x) # x 122 | #define XTSTR(x) _XTSTR(x) 123 | #endif 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Johannes Schriewer 2 | # 3 | # Redistribution and use in source and binary forms, with or without 4 | # modification, are permitted provided that the following conditions 5 | # are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 10 | # 2. Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in 12 | # the documentation and/or other materials provided with the 13 | # distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | # POSSIBILITY OF SUCH DAMAGE. 27 | 28 | # name for the target project 29 | PROJECT := ws2812_i2s 30 | TARGET := $(addsuffix .a,$(addprefix lib,$(PROJECT))) 31 | 32 | BUILD_DIR ?= build 33 | 34 | # source code to compile 35 | SRC := $(wildcard src/*.c) 36 | OBJ := $(patsubst %.c,$(BUILD_DIR)/%.o,$(SRC)) 37 | INCDIR := -I./include -I./src 38 | INCDIR += 39 | 40 | LIB_SDK_INCDIR ?= include include/espressif extra_include 41 | 42 | CFLAGS ?= -Os -Wpointer-arith -Wundef -fno-inline-functions -Werror 43 | CFLAGS += -DLED_TYPE=LED_TYPE_WS2812 -DLED_MODE=LED_MODE_RGB 44 | CFLAGS += -Wl,-EL -nostdlib -mlongcalls -mtext-section-literals -D__ets__ \ 45 | -DICACHE_FLASH -ffunction-sections -fdata-sections -fno-builtin-printf \ 46 | -fno-jump-tables --std=c99 47 | 48 | # OS-Detection 49 | ifeq ($(OS),Windows_NT) 50 | OS = Windows 51 | else 52 | UNAME_S := $(shell uname -s) 53 | ifeq ($(UNAME_S),Linux) 54 | OS = Linux 55 | endif 56 | ifeq ($(UNAME_S),Darwin) 57 | OS = Darwin 58 | endif 59 | endif 60 | 61 | # base directory for the compiler 62 | # base directory of the ESP8266 SDK package, absolute 63 | # serial port to use for flashing 64 | # esptool.py path 65 | ifeq ($(OS),Windows) 66 | XTENSA_TOOLS_ROOT ?= /c/ESP8266/xtensa-lx106-elf/bin 67 | SDK_PATH ?= /c/ESP8266/ESP8266_RTOS_SDK 68 | endif 69 | ifeq ($(OS),Darwin) 70 | XTENSA_TOOLS_ROOT ?= /Volumes/ESP8266/esp-open-sdk/xtensa-lx106-elf/bin 71 | SDK_PATH ?= /Volumes/ESP8266/ESP8266_RTOS_SDK 72 | endif 73 | ifeq ($(OS),Linux) 74 | XTENSA_TOOLS_ROOT ?= /opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf/bin 75 | SDK_PATH ?= /opt/Espressif/ESP8266_RTOS_SDK 76 | endif 77 | 78 | # select which tools to use as compiler, librarian and linker 79 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 80 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 81 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 82 | 83 | #### 84 | #### no user configurable options below here 85 | #### 86 | 87 | TARGET := $(addprefix $(BUILD_DIR)/../,$(TARGET)) 88 | DEP := $(patsubst %.c,$(BUILD_DIR)/%.d,$(SRC)) 89 | LIB_SDK_INCDIR := $(addprefix -I$(SDK_PATH)/,$(LIB_SDK_INCDIR)) 90 | 91 | V ?= $(VERBOSE) 92 | ifeq ("$(V)","1") 93 | Q := 94 | vecho := @true 95 | else 96 | Q := @ 97 | vecho := @echo 98 | endif 99 | 100 | vpath %.c $(SRC_DIR) 101 | 102 | .PHONY: all checkdirs flash clean 103 | 104 | all: checkdirs $(TARGET) 105 | 106 | $(TARGET): $(OBJ) 107 | $(vecho) "AR $@" 108 | $(Q) $(AR) cru $@ $^ 109 | 110 | checkdirs: $(BUILD_DIR) $(BUILD_DIR)/src 111 | 112 | $(BUILD_DIR)/src: 113 | $(Q) mkdir -p $(BUILD_DIR)/src 114 | 115 | $(BUILD_DIR): 116 | $(Q) mkdir -p $@ 117 | 118 | clean: 119 | $(vecho) "Clean $(abspath $(TARGET))" 120 | $(Q) rm -f $(abspath $(TARGET)) 121 | $(vecho) "Clean $(BUILD_DIR)" 122 | $(Q) rm -rf $(BUILD_DIR) 123 | 124 | $(BUILD_DIR)/%.o: %.c 125 | $(vecho) "CC $<" 126 | $(Q) $(CC) $(INCDIR) $(LIB_SDK_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $< -o $@ 127 | 128 | $(BUILD_DIR)/%.d: %.c $(BUILD_DIR)/src 129 | $(vecho) "Depend $<" 130 | $(Q) set -e; rm -f $@; \ 131 | $(CC) -M $(INCDIR) $(LIB_SDK_INCDIR) $(SDK_INCDIR) $(CPPFLAGS) $< > $@.$$$$; \ 132 | sed 's,\(.*\)\.o[ :]*,$(BUILD_DIR)/$(dir $<)\1.o $@: ,g' < $@.$$$$ > $@; \ 133 | rm -f $@.$$$$ 134 | 135 | -include $(DEP) -------------------------------------------------------------------------------- /src/i2s_regs.h: -------------------------------------------------------------------------------- 1 | /* esp/i2s_regs.h 2 | * 3 | * ESP8266 I2S register definitions 4 | * 5 | * Not compatible with ESP SDK register access code. 6 | */ 7 | 8 | #ifndef _ESP_I2S_REGS_H 9 | #define _ESP_I2S_REGS_H 10 | 11 | #include 12 | #include 13 | #include "common_macros.h" 14 | 15 | 16 | #define I2S_BASE 0x60000e00 17 | #define I2S (*(struct I2S_REGS *)I2S_BASE) 18 | 19 | struct I2S_REGS { 20 | uint32_t volatile TXFIFO; // 0x00 21 | uint32_t volatile RXFIFO; // 0x04 22 | uint32_t volatile CONF; // 0x08 23 | uint32_t volatile INT_RAW; // 0x0c 24 | uint32_t volatile INT_STATUS; // 0x10 25 | uint32_t volatile INT_ENABLE; // 0x14 26 | uint32_t volatile INT_CLEAR; // 0x18 27 | uint32_t volatile TIMING; // 0x1c 28 | uint32_t volatile FIFO_CONF; // 0x20 29 | uint32_t volatile RX_EOF_NUM; // 0x24 30 | uint32_t volatile CONF_SINGLE_DATA; // 0x28 31 | uint32_t volatile CONF_CHANNELS; // 0x2c 32 | }; 33 | 34 | _Static_assert(sizeof(struct I2S_REGS) == 0x30, "I2S_REGS is the wrong size"); 35 | 36 | /* Details for CONF register */ 37 | 38 | #define I2S_CONF_BCK_DIV_M 0x0000003f 39 | #define I2S_CONF_BCK_DIV_S 22 40 | #define I2S_CONF_CLKM_DIV_M 0x0000003f 41 | #define I2S_CONF_CLKM_DIV_S 16 42 | #define I2S_CONF_BITS_MOD_M 0x0000000f 43 | #define I2S_CONF_BITS_MOD_S 12 44 | #define I2S_CONF_RX_MSB_SHIFT BIT(11) 45 | #define I2S_CONF_TX_MSB_SHIFT BIT(10) 46 | #define I2S_CONF_RX_START BIT(9) 47 | #define I2S_CONF_TX_START BIT(8) 48 | #define I2S_CONF_MSB_RIGHT BIT(7) 49 | #define I2S_CONF_RIGHT_FIRST BIT(6) 50 | #define I2S_CONF_RX_SLAVE_MOD BIT(5) 51 | #define I2S_CONF_TX_SLAVE_MOD BIT(4) 52 | #define I2S_CONF_RX_FIFO_RESET BIT(3) 53 | #define I2S_CONF_TX_FIFO_RESET BIT(2) 54 | #define I2S_CONF_RX_RESET BIT(1) 55 | #define I2S_CONF_TX_RESET BIT(0) 56 | #define I2S_CONF_RESET_MASK 0xf 57 | 58 | /* Details for INT_RAW register */ 59 | 60 | #define I2S_INT_RAW_TX_REMPTY BIT(5) 61 | #define I2S_INT_RAW_TX_WFULL BIT(4) 62 | #define I2S_INT_RAW_RX_REMPTY BIT(3) 63 | #define I2S_INT_RAW_RX_WFULL BIT(2) 64 | #define I2S_INT_RAW_TX_PUT_DATA BIT(1) 65 | #define I2S_INT_RAW_RX_TAKE_DATA BIT(0) 66 | 67 | /* Details for INT_STATUS register */ 68 | 69 | #define I2S_INT_STATUS_TX_REMPTY BIT(5) 70 | #define I2S_INT_STATUS_TX_WFULL BIT(4) 71 | #define I2S_INT_STATUS_RX_REMPTY BIT(3) 72 | #define I2S_INT_STATUS_RX_WFULL BIT(2) 73 | #define I2S_INT_STATUS_TX_PUT_DATA BIT(1) 74 | #define I2S_INT_STATUS_RX_TAKE_DATA BIT(0) 75 | 76 | /* Details for INT_ENABLE register */ 77 | 78 | #define I2S_INT_ENABLE_TX_REMPTY BIT(5) 79 | #define I2S_INT_ENABLE_TX_WFULL BIT(4) 80 | #define I2S_INT_ENABLE_RX_REMPTY BIT(3) 81 | #define I2S_INT_ENABLE_RX_WFULL BIT(2) 82 | #define I2S_INT_ENABLE_TX_PUT_DATA BIT(1) 83 | #define I2S_INT_ENABLE_RX_TAKE_DATA BIT(0) 84 | 85 | /* Details for INT_CLEAR register */ 86 | 87 | #define I2S_INT_CLEAR_TX_REMPTY BIT(5) 88 | #define I2S_INT_CLEAR_TX_WFULL BIT(4) 89 | #define I2S_INT_CLEAR_RX_REMPTY BIT(3) 90 | #define I2S_INT_CLEAR_RX_WFULL BIT(2) 91 | #define I2S_INT_CLEAR_TX_PUT_DATA BIT(1) 92 | #define I2S_INT_CLEAR_RX_TAKE_DATA BIT(0) 93 | 94 | /* Details for TIMING register */ 95 | 96 | #define I2S_TIMING_TX_BCK_IN_INV BIT(22) 97 | #define I2S_TIMING_RX_DSYNC_SW BIT(21) 98 | #define I2S_TIMING_TX_DSYNC_SW BIT(20) 99 | #define I2S_TIMING_RX_BCK_OUT_DELAY_M 0x00000003 100 | #define I2S_TIMING_RX_BCK_OUT_DELAY_S 18 101 | #define I2S_TIMING_RX_WS_OUT_DELAY_M 0x00000003 102 | #define I2S_TIMING_RX_WS_OUT_DELAY_S 16 103 | #define I2S_TIMING_TX_SD_OUT_DELAY_M 0x00000003 104 | #define I2S_TIMING_TX_SD_OUT_DELAY_S 14 105 | #define I2S_TIMING_TX_WS_OUT_DELAY_M 0x00000003 106 | #define I2S_TIMING_TX_WS_OUT_DELAY_S 12 107 | #define I2S_TIMING_TX_BCK_OUT_DELAY_M 0x00000003 108 | #define I2S_TIMING_TX_BCK_OUT_DELAY_S 10 109 | #define I2S_TIMING_RX_SD_IN_DELAY_M 0x00000003 110 | #define I2S_TIMING_RX_SD_IN_DELAY_S 8 111 | #define I2S_TIMING_RX_WS_IN_DELAY 0x00000003 112 | #define I2S_TIMING_RX_WS_IN_DELAY_S 6 113 | #define I2S_TIMING_RX_BCK_IN_DELAY_M 0x00000003 114 | #define I2S_TIMING_RX_BCK_IN_DELAY_S 4 115 | #define I2S_TIMING_TX_WS_IN_DELAY_M 0x00000003 116 | #define I2S_TIMING_TX_WS_IN_DELAY_S 2 117 | #define I2S_TIMING_TX_BCK_IN_DELAY_M 0x00000003 118 | #define I2S_TIMING_TX_BCK_IN_DELAY_S 0 119 | 120 | /* Details for FIFO_CONF register */ 121 | 122 | #define I2S_FIFO_CONF_RX_FIFO_MOD_M 0x00000007 123 | #define I2S_FIFO_CONF_RX_FIFO_MOD_S 16 124 | #define I2S_FIFO_CONF_TX_FIFO_MOD_M 0x00000007 125 | #define I2S_FIFO_CONF_TX_FIFO_MOD_S 13 126 | #define I2S_FIFO_CONF_DESCRIPTOR_ENABLE BIT(12) 127 | #define I2S_FIFO_CONF_TX_DATA_NUM_M 0x0000003f 128 | #define I2S_FIFO_CONF_TX_DATA_NUM_S 6 129 | #define I2S_FIFO_CONF_RX_DATA_NUM_M 0x0000003f 130 | #define I2S_FIFO_CONF_RX_DATA_NUM_S 0 131 | 132 | /* Details for CONF_CHANNEL register */ 133 | 134 | #define I2S_CONF_CHANNELS_RX_CHANNEL_MOD_M 0x00000003 135 | #define I2S_CONF_CHANNELS_RX_CHANNEL_MOD_S 3 136 | #define I2S_CONF_CHANNELS_TX_CHANNEL_MOD_M 0x00000007 137 | #define I2S_CONF_CHANNELS_TX_CHANNEL_MOD_S 0 138 | 139 | #endif /* _ESP_I2S_REGS_H */ 140 | -------------------------------------------------------------------------------- /src/iomux_regs.h: -------------------------------------------------------------------------------- 1 | /* esp/iomux_regs.h 2 | * 3 | * ESP8266 IOMUX register definitions 4 | * 5 | * Not compatible with ESP SDK register access code. 6 | * 7 | * Note that IOMUX register order is _not_ the same as GPIO order. See 8 | * esp/iomux.h for programmer-friendly IOMUX configuration options. 9 | */ 10 | 11 | #ifndef _ESP_IOMUX_REGS_H 12 | #define _ESP_IOMUX_REGS_H 13 | 14 | #include 15 | #include 16 | #include "common_macros.h" 17 | 18 | #define IOMUX_BASE 0x60000800 19 | #define IOMUX (*(struct IOMUX_REGS *)(IOMUX_BASE)) 20 | 21 | struct IOMUX_REGS { 22 | uint32_t volatile CONF; // 0x00 23 | uint32_t volatile PIN[16]; // 0x04 - 0x40 24 | }; 25 | 26 | _Static_assert(sizeof(struct IOMUX_REGS) == 0x44, "IOMUX_REGS is the wrong size"); 27 | 28 | /* Details for CONF register */ 29 | 30 | #define IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK BIT(8) 31 | #define IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK BIT(9) 32 | 33 | /* Details for PIN registers */ 34 | 35 | #define IOMUX_PIN_OUTPUT_ENABLE BIT(0) 36 | #define IOMUX_PIN_OUTPUT_ENABLE_SLEEP BIT(1) 37 | #define IOMUX_PIN_PULLDOWN_SLEEP BIT(2) 38 | #define IOMUX_PIN_PULLUP_SLEEP BIT(3) 39 | #define IOMUX_PIN_FUNC_LOW_M 0x00000003 40 | #define IOMUX_PIN_FUNC_LOW_S 4 41 | #define IOMUX_PIN_PULLDOWN BIT(6) 42 | #define IOMUX_PIN_PULLUP BIT(7) 43 | #define IOMUX_PIN_FUNC_HIGH_M 0x00000004 44 | #define IOMUX_PIN_FUNC_HIGH_S 6 45 | 46 | #define IOMUX_PIN_FUNC_MASK 0x00000130 47 | 48 | /* WARNING: Macro evaluates argument twice */ 49 | #define IOMUX_FUNC(val) (VAL2FIELD_M(IOMUX_PIN_FUNC_LOW, val) | VAL2FIELD_M(IOMUX_PIN_FUNC_HIGH, val)) 50 | 51 | #define IOMUX_GPIO0 IOMUX.PIN[12] 52 | #define IOMUX_GPIO1 IOMUX.PIN[5] 53 | #define IOMUX_GPIO2 IOMUX.PIN[13] 54 | #define IOMUX_GPIO3 IOMUX.PIN[4] 55 | #define IOMUX_GPIO4 IOMUX.PIN[14] 56 | #define IOMUX_GPIO5 IOMUX.PIN[15] 57 | #define IOMUX_GPIO6 IOMUX.PIN[6] 58 | #define IOMUX_GPIO7 IOMUX.PIN[7] 59 | #define IOMUX_GPIO8 IOMUX.PIN[8] 60 | #define IOMUX_GPIO9 IOMUX.PIN[9] 61 | #define IOMUX_GPIO10 IOMUX.PIN[10] 62 | #define IOMUX_GPIO11 IOMUX.PIN[11] 63 | #define IOMUX_GPIO12 IOMUX.PIN[0] 64 | #define IOMUX_GPIO13 IOMUX.PIN[1] 65 | #define IOMUX_GPIO14 IOMUX.PIN[2] 66 | #define IOMUX_GPIO15 IOMUX.PIN[3] 67 | 68 | #define IOMUX_GPIO0_FUNC_GPIO IOMUX_FUNC(0) 69 | #define IOMUX_GPIO0_FUNC_SPI0_CS2 IOMUX_FUNC(1) 70 | #define IOMUX_GPIO0_FUNC_CLOCK_OUT IOMUX_FUNC(4) 71 | 72 | #define IOMUX_GPIO1_FUNC_UART0_TXD IOMUX_FUNC(0) 73 | #define IOMUX_GPIO1_FUNC_SPI0_CS1 IOMUX_FUNC(1) 74 | #define IOMUX_GPIO1_FUNC_GPIO IOMUX_FUNC(3) 75 | #define IOMUX_GPIO1_FUNC_CLOCK_RTC_BLINK IOMUX_FUNC(4) 76 | 77 | #define IOMUX_GPIO2_FUNC_GPIO IOMUX_FUNC(0) 78 | #define IOMUX_GPIO2_FUNC_I2SO_WS IOMUX_FUNC(1) 79 | #define IOMUX_GPIO2_FUNC_UART1_TXD IOMUX_FUNC(2) 80 | #define IOMUX_GPIO2_FUNC_UART0_TXD IOMUX_FUNC(4) 81 | 82 | #define IOMUX_GPIO3_FUNC_UART0_RXD IOMUX_FUNC(0) 83 | #define IOMUX_GPIO3_FUNC_I2SO_DATA IOMUX_FUNC(1) 84 | #define IOMUX_GPIO3_FUNC_GPIO IOMUX_FUNC(3) 85 | #define IOMUX_GPIO3_FUNC_CLOCK_XTAL_BLINK IOMUX_FUNC(4) 86 | 87 | #define IOMUX_GPIO4_FUNC_GPIO IOMUX_FUNC(0) 88 | #define IOMUX_GPIO4_FUNC_CLOCK_XTAL IOMUX_FUNC(1) 89 | 90 | #define IOMUX_GPIO5_FUNC_GPIO IOMUX_FUNC(0) 91 | #define IOMUX_GPIO5_FUNC_CLOCK_RTC IOMUX_FUNC(1) 92 | 93 | #define IOMUX_GPIO6_FUNC_SD_CLK IOMUX_FUNC(0) 94 | #define IOMUX_GPIO6_FUNC_SPI0_CLK IOMUX_FUNC(1) 95 | #define IOMUX_GPIO6_FUNC_GPIO IOMUX_FUNC(3) 96 | #define IOMUX_GPIO6_FUNC_UART1_CTS IOMUX_FUNC(4) 97 | 98 | #define IOMUX_GPIO7_FUNC_SD_DATA0 IOMUX_FUNC(0) 99 | #define IOMUX_GPIO7_FUNC_SPI0_Q_MISO IOMUX_FUNC(1) 100 | #define IOMUX_GPIO7_FUNC_GPIO IOMUX_FUNC(3) 101 | #define IOMUX_GPIO7_FUNC_UART1_TXD IOMUX_FUNC(4) 102 | 103 | #define IOMUX_GPIO8_FUNC_SD_DATA1 IOMUX_FUNC(0) 104 | #define IOMUX_GPIO8_FUNC_SPI0_D_MOSI IOMUX_FUNC(1) 105 | #define IOMUX_GPIO8_FUNC_GPIO IOMUX_FUNC(3) 106 | #define IOMUX_GPIO8_FUNC_UART1_RXD IOMUX_FUNC(4) 107 | 108 | #define IOMUX_GPIO9_FUNC_SD_DATA2 IOMUX_FUNC(0) 109 | #define IOMUX_GPIO9_FUNC_SPI0_HD IOMUX_FUNC(1) 110 | #define IOMUX_GPIO9_FUNC_GPIO IOMUX_FUNC(3) 111 | #define IOMUX_GPIO9_FUNC_SPI1_HD IOMUX_FUNC(4) 112 | 113 | #define IOMUX_GPIO10_FUNC_SD_DATA3 IOMUX_FUNC(0) 114 | #define IOMUX_GPIO10_FUNC_SPI0_WP IOMUX_FUNC(1) 115 | #define IOMUX_GPIO10_FUNC_GPIO IOMUX_FUNC(3) 116 | #define IOMUX_GPIO10_FUNC_SPI1_WP IOMUX_FUNC(4) 117 | 118 | #define IOMUX_GPIO11_FUNC_SD_CMD IOMUX_FUNC(0) 119 | #define IOMUX_GPIO11_FUNC_SPI0_CS0 IOMUX_FUNC(1) 120 | #define IOMUX_GPIO11_FUNC_GPIO IOMUX_FUNC(3) 121 | #define IOMUX_GPIO11_FUNC_UART1_RTS IOMUX_FUNC(4) 122 | 123 | #define IOMUX_GPIO12_FUNC_MTDI IOMUX_FUNC(0) 124 | #define IOMUX_GPIO12_FUNC_I2SI_DATA IOMUX_FUNC(1) 125 | #define IOMUX_GPIO12_FUNC_SPI1_Q_MISO IOMUX_FUNC(2) 126 | #define IOMUX_GPIO12_FUNC_GPIO IOMUX_FUNC(3) 127 | #define IOMUX_GPIO12_FUNC_UART0_DTR IOMUX_FUNC(4) 128 | 129 | #define IOMUX_GPIO13_FUNC_MTCK IOMUX_FUNC(0) 130 | #define IOMUX_GPIO13_FUNC_I2SI_BCK IOMUX_FUNC(1) 131 | #define IOMUX_GPIO13_FUNC_SPI1_D_MOSI IOMUX_FUNC(2) 132 | #define IOMUX_GPIO13_FUNC_GPIO IOMUX_FUNC(3) 133 | #define IOMUX_GPIO13_FUNC_UART0_CTS IOMUX_FUNC(4) 134 | 135 | #define IOMUX_GPIO14_FUNC_MTMS IOMUX_FUNC(0) 136 | #define IOMUX_GPIO14_FUNC_I2SI_WS IOMUX_FUNC(1) 137 | #define IOMUX_GPIO14_FUNC_SPI1_CLK IOMUX_FUNC(2) 138 | #define IOMUX_GPIO14_FUNC_GPIO IOMUX_FUNC(3) 139 | #define IOMUX_GPIO14_FUNC_UART0_DSR IOMUX_FUNC(4) 140 | 141 | #define IOMUX_GPIO15_FUNC_MTDO IOMUX_FUNC(0) 142 | #define IOMUX_GPIO15_FUNC_I2SO_BCK IOMUX_FUNC(1) 143 | #define IOMUX_GPIO15_FUNC_SPI1_CS0 IOMUX_FUNC(2) 144 | #define IOMUX_GPIO15_FUNC_GPIO IOMUX_FUNC(3) 145 | #define IOMUX_GPIO15_FUNC_UART0_RTS IOMUX_FUNC(4) 146 | 147 | #endif /* _ESP_IOMUX_REGS_H */ 148 | -------------------------------------------------------------------------------- /src/i2s_dma.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2016 sheinz (https://github.com/sheinz) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #include "debug.h" 26 | #include "common_macros.h" 27 | #include "i2s_dma.h" 28 | #include "i2s_regs.h" 29 | #include "interrupts.h" 30 | #include "iomux.h" 31 | 32 | #include 33 | 34 | // The following definitions is taken from ESP8266_MP3_DECODER demo 35 | // https://github.com/espressif/ESP8266_MP3_DECODER/blob/master/mp3/driver/i2s_freertos.c 36 | // It is requred to set clock to I2S subsystem 37 | void rom_i2c_writeReg_Mask(uint32_t block, uint32_t host_id, 38 | uint32_t reg_add, uint32_t Msb, uint32_t Lsb, uint32_t indata); 39 | 40 | #ifndef i2c_bbpll 41 | #define i2c_bbpll 0x67 42 | #define i2c_bbpll_en_audio_clock_out 4 43 | #define i2c_bbpll_en_audio_clock_out_msb 7 44 | #define i2c_bbpll_en_audio_clock_out_lsb 7 45 | #define i2c_bbpll_hostid 4 46 | #endif 47 | 48 | #define i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata) \ 49 | rom_i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata) 50 | 51 | #define i2c_writeReg_Mask_def(block, reg_add, indata) \ 52 | i2c_writeReg_Mask(block, block##_hostid, reg_add, reg_add##_msb, \ 53 | reg_add##_lsb, indata) 54 | 55 | 56 | void i2s_dma_init(i2s_dma_isr_t isr, void *arg, i2s_clock_div_t clock_div, i2s_pins_t pins) { 57 | LOG(TRACE, "i2s: Initializing i2s DMA"); 58 | 59 | // reset DMA 60 | SET_MASK_BITS(SLC.CONF0, SLC_CONF0_RX_LINK_RESET); 61 | CLEAR_MASK_BITS(SLC.CONF0, SLC_CONF0_RX_LINK_RESET); 62 | 63 | // clear DMA int flags 64 | SLC.INT_CLEAR = 0xFFFFFFFF; 65 | SLC.INT_CLEAR = 0; 66 | 67 | // Enable and configure DMA 68 | SLC.CONF0 = SET_FIELD(SLC.CONF0, SLC_CONF0_MODE, 0); // does it really needed? 69 | SLC.CONF0 = SET_FIELD(SLC.CONF0, SLC_CONF0_MODE, 1); 70 | 71 | // Do we really need to set and clear? 72 | SET_MASK_BITS(SLC.RX_DESCRIPTOR_CONF, SLC_RX_DESCRIPTOR_CONF_INFOR_NO_REPLACE | 73 | SLC_RX_DESCRIPTOR_CONF_TOKEN_NO_REPLACE); 74 | CLEAR_MASK_BITS(SLC.RX_DESCRIPTOR_CONF, SLC_RX_DESCRIPTOR_CONF_RX_FILL_ENABLE | 75 | SLC_RX_DESCRIPTOR_CONF_RX_EOF_MODE | SLC_RX_DESCRIPTOR_CONF_RX_FILL_MODE); 76 | 77 | LOG(TRACE, "i2s: Attaching ISR"); 78 | 79 | if (isr) { 80 | _xt_isr_attach(INUM_SLC, isr, arg); 81 | SET_MASK_BITS(SLC.INT_ENABLE, SLC_INT_ENABLE_RX_EOF); 82 | SLC.INT_CLEAR = 0xFFFFFFFF; 83 | _xt_isr_unmask(1< 30 | #include 31 | 32 | #define IRAM_DATA __attribute__((section(".iram1.data"))) 33 | 34 | #define MAX_DMA_BLOCK_SIZE 4095 35 | #define DMA_PIXEL_SIZE (LED_MODE * 4) // each colour takes 4 bytes 36 | 37 | /** 38 | * Amount of zero data to produce WS2812 reset condition. 39 | * DMA data must be multiple of 4 40 | * 16 bytes of 0 gives ~50 microseconds of low pulse 41 | */ 42 | #define WS2812_ZEROES_LENGTH 16 43 | 44 | static uint8_t i2s_dma_zero_buf[WS2812_ZEROES_LENGTH] = {0}; 45 | 46 | static dma_descriptor_t *dma_block_list; 47 | static uint32_t dma_block_list_size; 48 | 49 | static void *dma_buffer; 50 | static uint32_t dma_buffer_size; 51 | 52 | static volatile bool i2s_dma_processing = false; 53 | 54 | static void dma_isr_handler(void *arg) { 55 | if (i2s_dma_is_eof_interrupt()) { 56 | i2s_dma_processing = false; 57 | } 58 | i2s_dma_clear_interrupt(); 59 | } 60 | 61 | /** 62 | * Form a linked list of descriptors (dma blocks). 63 | * The last two blocks are zero block and stop block. 64 | * The last block is a stop terminal block. It has no data and no next block. 65 | */ 66 | static inline void init_descriptors_list(uint8_t *buf, uint32_t total_dma_data_size) { 67 | for (int i = 0; i < dma_block_list_size; i++) { 68 | dma_block_list[i].owner = 1; 69 | dma_block_list[i].eof = 0; 70 | dma_block_list[i].sub_sof = 0; 71 | dma_block_list[i].unused = 0; 72 | dma_block_list[i].buf_ptr = buf; 73 | 74 | if (total_dma_data_size >= MAX_DMA_BLOCK_SIZE) { 75 | dma_block_list[i].datalen = MAX_DMA_BLOCK_SIZE; 76 | dma_block_list[i].blocksize = MAX_DMA_BLOCK_SIZE; 77 | total_dma_data_size -= MAX_DMA_BLOCK_SIZE; 78 | buf += MAX_DMA_BLOCK_SIZE; 79 | } else { 80 | dma_block_list[i].datalen = total_dma_data_size; 81 | dma_block_list[i].blocksize = total_dma_data_size; 82 | total_dma_data_size = 0; 83 | } 84 | 85 | if (i == (dma_block_list_size - 2)) { // zero block 86 | dma_block_list[i].buf_ptr = i2s_dma_zero_buf; 87 | dma_block_list[i].datalen = WS2812_ZEROES_LENGTH; 88 | dma_block_list[i].blocksize = WS2812_ZEROES_LENGTH; 89 | } 90 | 91 | if (i == (dma_block_list_size - 1)) { // stop block 92 | // it needs a valid buffer even if no data to output 93 | dma_block_list[i].buf_ptr = i2s_dma_zero_buf; 94 | dma_block_list[i].datalen = 0; 95 | dma_block_list[i].blocksize = WS2812_ZEROES_LENGTH; 96 | dma_block_list[i].next_link_ptr = 0; 97 | 98 | // the last stop block should trigger interrupt 99 | dma_block_list[i].eof = 1; 100 | } else { 101 | dma_block_list[i].next_link_ptr = &dma_block_list[i + 1]; 102 | } 103 | } 104 | } 105 | 106 | void ws2812_i2s_init(uint32_t pixels_number) { 107 | LOG(TRACE, "ws2812: Initializing for %d pixels", pixels_number); 108 | 109 | dma_buffer_size = pixels_number * DMA_PIXEL_SIZE; 110 | dma_block_list_size = dma_buffer_size / MAX_DMA_BLOCK_SIZE; 111 | 112 | if (dma_buffer_size % MAX_DMA_BLOCK_SIZE) { 113 | dma_block_list_size += 1; 114 | } 115 | 116 | dma_block_list_size += 2; // zero block and stop block 117 | 118 | LOG(DEBUG, "allocating %d dma blocks\n", dma_block_list_size); 119 | 120 | dma_block_list = (dma_descriptor_t*)malloc( 121 | dma_block_list_size * sizeof(dma_descriptor_t)); 122 | 123 | LOG(DEBUG, "allocating %d bytes for DMA buffer\n", dma_buffer_size); 124 | dma_buffer = malloc(dma_buffer_size); 125 | memset(dma_buffer, 0xFA, dma_buffer_size); 126 | 127 | init_descriptors_list(dma_buffer, dma_buffer_size); 128 | 129 | i2s_clock_div_t clock_div = i2s_get_clock_div(3333333); 130 | i2s_pins_t i2s_pins = {.data = true, .clock = false, .ws = false}; 131 | 132 | LOG(DEBUG, "i2s clock dividers, bclk=%d, clkm=%d\n", 133 | clock_div.bclk_div, clock_div.clkm_div); 134 | 135 | i2s_dma_init(dma_isr_handler, NULL, clock_div, i2s_pins); 136 | } 137 | 138 | #if LED_TYPE == LED_TYPE_WS2812 139 | const IRAM_DATA int16_t bitpatterns[16] = { 140 | 0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110, 141 | 0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110, 142 | 0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110, 143 | 0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110, 144 | }; 145 | #endif 146 | 147 | #if LED_TYPE == LED_TYPE_SK6812 148 | static const uint16_t bitpatterns[16] = { 149 | 0b1000100010001000, 0b1000100010001100, 0b1000100011001000, 0b1000100011001100, 150 | 0b1000110010001000, 0b1000110010001100, 0b1000110011001000, 0b1000110011001100, 151 | 0b1100100010001000, 0b1100100010001100, 0b1100100011001000, 0b1100100011001100, 152 | 0b1100110010001000, 0b1100110010001100, 0b1100110011001000, 0b1100110011001100, 153 | }; 154 | #endif 155 | 156 | void ws2812_i2s_update(ws2812_pixel_t *pixels) { 157 | LOG(TRACE, "ws2812: Updating DMA buffers"); 158 | 159 | while (i2s_dma_processing) {}; 160 | 161 | uint16_t *p_dma_buf = dma_buffer; 162 | 163 | for (uint32_t i = 0; i < (dma_buffer_size / DMA_PIXEL_SIZE); i++) { 164 | // green 165 | *p_dma_buf++ = bitpatterns[pixels[i].green & 0x0F]; 166 | *p_dma_buf++ = bitpatterns[pixels[i].green >> 4]; 167 | 168 | // red 169 | *p_dma_buf++ = bitpatterns[pixels[i].red & 0x0F]; 170 | *p_dma_buf++ = bitpatterns[pixels[i].red >> 4]; 171 | 172 | // blue 173 | *p_dma_buf++ = bitpatterns[pixels[i].blue & 0x0F]; 174 | *p_dma_buf++ = bitpatterns[pixels[i].blue >> 4]; 175 | 176 | #if LED_MODE == LED_MODE_RGBW 177 | // white 178 | *p_dma_buf++ = bitpatterns[pixels[i].white & 0x0F]; 179 | *p_dma_buf++ = bitpatterns[pixels[i].white >> 4]; 180 | #endif 181 | } 182 | 183 | i2s_dma_processing = true; 184 | i2s_dma_start(dma_block_list); 185 | } 186 | -------------------------------------------------------------------------------- /src/slc_regs.h: -------------------------------------------------------------------------------- 1 | /* esp/slc_regs.h 2 | * 3 | * ESP8266 SLC register definitions 4 | * 5 | * Not compatible with ESP SDK register access code. 6 | */ 7 | 8 | #ifndef _ESP_SLC_REGS_H 9 | #define _ESP_SLC_REGS_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include "common_macros.h" 15 | 16 | #define SLC_BASE 0x60000b00 17 | #define SLC (*(struct SLC_REGS *)SLC_BASE) 18 | 19 | struct SLC_REGS { 20 | uint32_t volatile CONF0; // 0x00 21 | uint32_t volatile INT_RAW; // 0x04 22 | uint32_t volatile INT_STATUS; // 0x08 23 | uint32_t volatile INT_ENABLE; // 0x0c 24 | uint32_t volatile INT_CLEAR; // 0x10 25 | uint32_t volatile RX_STATUS; // 0x14 26 | uint32_t volatile RX_FIFO_PUSH; // 0x18 27 | uint32_t volatile TX_STATUS; // 0x1c 28 | uint32_t volatile TX_FIFO_POP; // 0x20 29 | uint32_t volatile RX_LINK; // 0x24 30 | uint32_t volatile TX_LINK; // 0x28 31 | uint32_t volatile INTVEC_TO_HOST; // 0x2c 32 | uint32_t volatile TOKEN0; // 0x30 33 | uint32_t volatile TOKEN1; // 0x34 34 | uint32_t volatile CONF1; // 0x38 35 | uint32_t volatile STATE0; // 0x3c 36 | uint32_t volatile STATE1; // 0x40 37 | uint32_t volatile BRIDGE_CONF; // 0x44 38 | uint32_t volatile RX_EOF_DESCRIPTOR_ADDR; // 0x48 39 | uint32_t volatile TX_EOF_DESCRIPTOR_ADDR; // 0x4c 40 | uint32_t volatile RX_EOF_BUFFER_DESCRIPTOR_ADDR; // 0x50 - Naming uncertain 41 | uint32_t volatile AHB_TEST; // 0x54 42 | uint32_t volatile SDIO_STATUS; // 0x58 43 | uint32_t volatile RX_DESCRIPTOR_CONF; // 0x5c 44 | uint32_t volatile TX_LINK_DESCRIPTOR; // 0x60 45 | uint32_t volatile TX_LINK_DESCRIPTOR_BF0; // 0x64 46 | uint32_t volatile TX_LINK_DESCRIPTOR_BF1; // 0x68 47 | uint32_t volatile RX_LINK_DESCRIPTOR; // 0x6c 48 | uint32_t volatile RX_LINK_DESCRIPTOR_BF0; // 0x70 49 | uint32_t volatile RX_LINK_DESCRIPTOR_BF1; // 0x74 50 | uint32_t volatile DATE; // 0x78 51 | uint32_t volatile ID; // 0x7c 52 | uint32_t volatile UNKNOWN_80; // 0x80 53 | uint32_t volatile UNKNOWN_84; // 0x84 54 | uint32_t volatile HOST_INT_RAW; // 0x88 55 | uint32_t volatile UNKNOWN_8C; // 0x8c 56 | uint32_t volatile UNKNOWN_90; // 0x90 57 | uint32_t volatile HOST_CONF_W0; // 0x94 58 | uint32_t volatile HOST_CONF_W1; // 0x98 59 | uint32_t volatile HOST_INT_STATUS; // 0x9c 60 | uint32_t volatile HOST_CONF_W2; // 0xa0 61 | uint32_t volatile HOST_CONF_W3; // 0xa4 62 | uint32_t volatile HOST_CONF_W4; // 0xa8 63 | uint32_t volatile UNKNOWN_AC; // 0xac 64 | uint32_t volatile HOST_INT_CLEAR; // 0xb0 65 | uint32_t volatile HOST_INT_ENABLE; // 0xb4 66 | uint32_t volatile UNKNOWN_B8; // 0xb8 67 | uint32_t volatile HOST_CONF_W5; // 0xbc 68 | }; 69 | 70 | _Static_assert(sizeof(struct SLC_REGS) == 0xc0, "SLC_REGS is the wrong size"); 71 | 72 | /* Details for CONF0 register */ 73 | 74 | #define SLC_CONF0_MODE_M 0x00000003 75 | #define SLC_CONF0_MODE_S 12 76 | #define SLC_CONF0_DATA_BURST_ENABLE BIT(9) 77 | #define SLC_CONF0_DESCRIPTOR_BURST_ENABLE BIT(8) 78 | #define SLC_CONF0_RX_NO_RESTART_CLEAR BIT(7) 79 | #define SLC_CONF0_RX_AUTO_WRITE_BACK BIT(6) 80 | #define SLC_CONF0_RX_LOOP_TEST BIT(5) 81 | #define SLC_CONF0_TX_LOOP_TEST BIT(4) 82 | #define SLC_CONF0_AHBM_RESET BIT(3) 83 | #define SLC_CONF0_AHBM_FIFO_RESET BIT(2) 84 | #define SLC_CONF0_RX_LINK_RESET BIT(1) 85 | #define SLC_CONF0_TX_LINK_RESET BIT(0) 86 | 87 | /* Details for INT_RAW register */ 88 | 89 | #define SLC_INT_RAW_TX_DSCR_EMPTY BIT(21) 90 | #define SLC_INT_RAW_RX_DSCR_ERROR BIT(20) 91 | #define SLC_INT_RAW_TX_DSCR_ERROR BIT(19) 92 | #define SLC_INT_RAW_TO_HOST BIT(18) 93 | #define SLC_INT_RAW_RX_EOF BIT(17) 94 | #define SLC_INT_RAW_RX_DONE BIT(16) 95 | #define SLC_INT_RAW_TX_EOF BIT(15) 96 | #define SLC_INT_RAW_TX_DONE BIT(14) 97 | #define SLC_INT_RAW_TOKEN1_1TO0 BIT(13) 98 | #define SLC_INT_RAW_TOKEN0_1TO0 BIT(12) 99 | #define SLC_INT_RAW_TX_OVERFLOW BIT(11) 100 | #define SLC_INT_RAW_RX_UNDEFLOW BIT(10) 101 | #define SLC_INT_RAW_TX_START BIT(9) 102 | #define SLC_INT_RAW_RX_START BIT(8) 103 | #define SLC_INT_RAW_FROM_HOST_BIT7 BIT(7) 104 | #define SLC_INT_RAW_FROM_HOST_BIT6 BIT(6) 105 | #define SLC_INT_RAW_FROM_HOST_BIT5 BIT(5) 106 | #define SLC_INT_RAW_FROM_HOST_BIT4 BIT(4) 107 | #define SLC_INT_RAW_FROM_HOST_BIT3 BIT(3) 108 | #define SLC_INT_RAW_FROM_HOST_BIT2 BIT(2) 109 | #define SLC_INT_RAW_FROM_HOST_BIT1 BIT(1) 110 | #define SLC_INT_RAW_FROM_HOST_BIT0 BIT(0) 111 | 112 | /* Details for INT_STATUS register */ 113 | 114 | #define SLC_INT_STATUS_TX_DSCR_EMPTY BIT(21) 115 | #define SLC_INT_STATUS_RX_DSCR_ERROR BIT(20) 116 | #define SLC_INT_STATUS_TX_DSCR_ERROR BIT(19) 117 | #define SLC_INT_STATUS_TO_HOST BIT(18) 118 | #define SLC_INT_STATUS_RX_EOF BIT(17) 119 | #define SLC_INT_STATUS_RX_DONE BIT(16) 120 | #define SLC_INT_STATUS_TX_EOF BIT(15) 121 | #define SLC_INT_STATUS_TX_DONE BIT(14) 122 | #define SLC_INT_STATUS_TOKEN1_1TO0 BIT(13) 123 | #define SLC_INT_STATUS_TOKEN0_1TO0 BIT(12) 124 | #define SLC_INT_STATUS_TX_OVERFLOW BIT(11) 125 | #define SLC_INT_STATUS_RX_UNDEFLOW BIT(10) 126 | #define SLC_INT_STATUS_TX_START BIT(9) 127 | #define SLC_INT_STATUS_RX_START BIT(8) 128 | #define SLC_INT_STATUS_FROM_HOST_BIT7 BIT(7) 129 | #define SLC_INT_STATUS_FROM_HOST_BIT6 BIT(6) 130 | #define SLC_INT_STATUS_FROM_HOST_BIT5 BIT(5) 131 | #define SLC_INT_STATUS_FROM_HOST_BIT4 BIT(4) 132 | #define SLC_INT_STATUS_FROM_HOST_BIT3 BIT(3) 133 | #define SLC_INT_STATUS_FROM_HOST_BIT2 BIT(2) 134 | #define SLC_INT_STATUS_FROM_HOST_BIT1 BIT(1) 135 | #define SLC_INT_STATUS_FROM_HOST_BIT0 BIT(0) 136 | 137 | /* Details for INT_ENABLE register */ 138 | 139 | #define SLC_INT_ENABLE_TX_DSCR_EMPTY BIT(21) 140 | #define SLC_INT_ENABLE_RX_DSCR_ERROR BIT(20) 141 | #define SLC_INT_ENABLE_TX_DSCR_ERROR BIT(19) 142 | #define SLC_INT_ENABLE_TO_HOST BIT(18) 143 | #define SLC_INT_ENABLE_RX_EOF BIT(17) 144 | #define SLC_INT_ENABLE_RX_DONE BIT(16) 145 | #define SLC_INT_ENABLE_TX_EOF BIT(15) 146 | #define SLC_INT_ENABLE_TX_DONE BIT(14) 147 | #define SLC_INT_ENABLE_TOKEN1_1TO0 BIT(13) 148 | #define SLC_INT_ENABLE_TOKEN0_1TO0 BIT(12) 149 | #define SLC_INT_ENABLE_TX_OVERFLOW BIT(11) 150 | #define SLC_INT_ENABLE_RX_UNDEFLOW BIT(10) 151 | #define SLC_INT_ENABLE_TX_START BIT(9) 152 | #define SLC_INT_ENABLE_RX_START BIT(8) 153 | #define SLC_INT_ENABLE_FROM_HOST_BIT7 BIT(7) 154 | #define SLC_INT_ENABLE_FROM_HOST_BIT6 BIT(6) 155 | #define SLC_INT_ENABLE_FROM_HOST_BIT5 BIT(5) 156 | #define SLC_INT_ENABLE_FROM_HOST_BIT4 BIT(4) 157 | #define SLC_INT_ENABLE_FROM_HOST_BIT3 BIT(3) 158 | #define SLC_INT_ENABLE_FROM_HOST_BIT2 BIT(2) 159 | #define SLC_INT_ENABLE_FROM_HOST_BIT1 BIT(1) 160 | #define SLC_INT_ENABLE_FROM_HOST_BIT0 BIT(0) 161 | 162 | #define SLC_INT_ENABLE_FROM_HOST_BIT_ALL 0xff 163 | 164 | /* Details for INT_CLEAR register */ 165 | 166 | #define SLC_INT_CLEAR_TX_DSCR_EMPTY BIT(21) 167 | #define SLC_INT_CLEAR_RX_DSCR_ERROR BIT(20) 168 | #define SLC_INT_CLEAR_TX_DSCR_ERROR BIT(19) 169 | #define SLC_INT_CLEAR_TO_HOST BIT(18) 170 | #define SLC_INT_CLEAR_RX_EOF BIT(17) 171 | #define SLC_INT_CLEAR_RX_DONE BIT(16) 172 | #define SLC_INT_CLEAR_TX_EOF BIT(15) 173 | #define SLC_INT_CLEAR_TX_DONE BIT(14) 174 | #define SLC_INT_CLEAR_TOKEN1_1TO0 BIT(13) 175 | #define SLC_INT_CLEAR_TOKEN0_1TO0 BIT(12) 176 | #define SLC_INT_CLEAR_TX_OVERFLOW BIT(11) 177 | #define SLC_INT_CLEAR_RX_UNDEFLOW BIT(10) 178 | #define SLC_INT_CLEAR_TX_START BIT(9) 179 | #define SLC_INT_CLEAR_RX_START BIT(8) 180 | #define SLC_INT_CLEAR_FROM_HOST_BIT7 BIT(7) 181 | #define SLC_INT_CLEAR_FROM_HOST_BIT6 BIT(6) 182 | #define SLC_INT_CLEAR_FROM_HOST_BIT5 BIT(5) 183 | #define SLC_INT_CLEAR_FROM_HOST_BIT4 BIT(4) 184 | #define SLC_INT_CLEAR_FROM_HOST_BIT3 BIT(3) 185 | #define SLC_INT_CLEAR_FROM_HOST_BIT2 BIT(2) 186 | #define SLC_INT_CLEAR_FROM_HOST_BIT1 BIT(1) 187 | #define SLC_INT_CLEAR_FROM_HOST_BIT0 BIT(0) 188 | 189 | /* Details for RX_STATUS register */ 190 | 191 | #define SLC_RX_STATUS_EMPTY BIT(1) 192 | #define SLC_RX_STATUS_FULL BIT(0) 193 | 194 | /* Details for RX_FIFO_PUSH register */ 195 | 196 | #define SLC_RX_FIFO_PUSH_FLAG BIT(16) 197 | #define SLC_RX_FIFO_PUSH_DATA_M 0x000001ff 198 | #define SLC_RX_FIFO_PUSH_DATA_S 0 199 | 200 | /* Details for TX_STATUS register */ 201 | 202 | #define SLC_TX_STATUS_EMPTY BIT(1) 203 | #define SLC_TX_STATUS_FULL BIT(0) 204 | 205 | /* Details for TX_FIFO_POP register */ 206 | 207 | #define SLC_TX_FIFO_POP_FLAG BIT(16) 208 | #define SLC_TX_FIFO_POP_DATA_M 0x000007ff 209 | #define SLC_TX_FIFO_POP_DATA_S 0 210 | 211 | /* Details for RX_LINK register */ 212 | 213 | #define SLC_RX_LINK_PARK BIT(31) 214 | #define SLC_RX_LINK_RESTART BIT(30) 215 | #define SLC_RX_LINK_START BIT(29) 216 | #define SLC_RX_LINK_STOP BIT(28) 217 | #define SLC_RX_LINK_DESCRIPTOR_ADDR_M 0x000fffff 218 | #define SLC_RX_LINK_DESCRIPTOR_ADDR_S 0 219 | 220 | /* Details for TX_LINK register */ 221 | 222 | #define SLC_TX_LINK_PARK BIT(31) 223 | #define SLC_TX_LINK_RESTART BIT(30) 224 | #define SLC_TX_LINK_START BIT(29) 225 | #define SLC_TX_LINK_STOP BIT(28) 226 | #define SLC_TX_LINK_DESCRIPTOR_ADDR_M 0x000fffff 227 | #define SLC_TX_LINK_DESCRIPTOR_ADDR_S 0 228 | 229 | /* Details for INTVEC_TO_HOST register */ 230 | 231 | #define SLC_INTVEC_TO_HOST_INTVEC_M 0x000000ff 232 | #define SLC_INTVEC_TO_HOST_INTVEC_S 0 233 | 234 | /* Details for TOKEN0 register */ 235 | 236 | #define SLC_TOKEN0_M 0x00000fff 237 | #define SLC_TOKEN0_S 16 238 | #define SLC_TOKEN0_LOCAL_INC_MORE BIT(14) 239 | #define SLC_TOKEN0_LOCAL_INC BIT(13) 240 | #define SLC_TOKEN0_LOCAL_WRITE BIT(12) 241 | #define SLC_TOKEN0_LOCAL_DATA_M 0x00000FFF 242 | #define SLC_TOKEN0_LOCAL_DATA_S 0 243 | 244 | /* Details for TOKEN1 register */ 245 | 246 | #define SLC_TOKEN1_MASK 0x00000fff 247 | #define SLC_TOKEN1_S 16 248 | #define SLC_TOKEN1_LOCAL_INC_MORE BIT(14) 249 | #define SLC_TOKEN1_LOCAL_INC BIT(13) 250 | #define SLC_TOKEN1_LOCAL_WRITE BIT(12) 251 | #define SLC_TOKEN1_LOCAL_DATA_M 0x00000fff 252 | #define SLC_TOKEN1_LOCAL_DATA_S 0 253 | 254 | /* Details for BRIDGE_CONF register */ 255 | 256 | #define SLC_BRIDGE_CONF_TX_PUSH_IDLE_M 0x0000ffff 257 | #define SLC_BRIDGE_CONF_TX_PUSH_IDLE_S 16 258 | #define SLC_BRIDGE_CONF_TX_DUMMY_MODE BIT(12) 259 | #define SLC_BRIDGE_CONF_FIFO_MAP_ENABLE_M 0x0000000f 260 | #define SLC_BRIDGE_CONF_FIFO_MAP_ENABLE_S 8 261 | #define SLC_BRIDGE_CONF_TX_EOF_ENABLE_M 0x0000003f 262 | #define SLC_BRIDGE_CONF_TX_EOF_ENABLE_S 0 263 | 264 | /* Details for AHB_TEST register */ 265 | 266 | #define SLC_AHB_TEST_ADDR_M 0x00000003 267 | #define SLC_AHB_TEST_ADDR_S 4 268 | #define SLC_AHB_TEST_MODE_M 0x00000007 269 | #define SLC_AHB_TEST_MODE_S 0 270 | 271 | /* Details for SDIO_STATUS register */ 272 | 273 | #define SLC_SDIO_STATUS_BUS_M 0x00000007 274 | #define SLC_SDIO_STATUS_BUS_S 12 275 | #define SLC_SDIO_STATUS_WAKEUP BIT(8) 276 | #define SLC_SDIO_STATUS_FUNC_M 0x0000000f 277 | #define SLC_SDIO_STATUS_FUNC_S 4 278 | #define SLC_SDIO_STATUS_COMMAND_M 0x00000007 279 | #define SLC_SDIO_STATUS_COMMAND_S 0 280 | 281 | /* Details for RX_DESCRIPTOR_CONF register */ 282 | 283 | #define SLC_RX_DESCRIPTOR_CONF_RX_FILL_ENABLE BIT(20) 284 | #define SLC_RX_DESCRIPTOR_CONF_RX_EOF_MODE BIT(19) 285 | #define SLC_RX_DESCRIPTOR_CONF_RX_FILL_MODE BIT(18) 286 | #define SLC_RX_DESCRIPTOR_CONF_INFOR_NO_REPLACE BIT(17) 287 | #define SLC_RX_DESCRIPTOR_CONF_TOKEN_NO_REPLACE BIT(16) 288 | #define SLC_RX_DESCRIPTOR_CONF_POP_IDLE_COUNT_M 0x0000ffff 289 | #define SLC_RX_DESCRIPTOR_CONF_POP_IDLE_COUNT_S 0 290 | 291 | #endif /* _ESP_SLC_REGS_H */ 292 | --------------------------------------------------------------------------------