Skip to content

Commit

Permalink
Support for ESP32C3 IDF4 neopixels
Browse files Browse the repository at this point in the history
  • Loading branch information
gfwilliams committed May 16, 2024
1 parent f0fee9d commit e3e68c7
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 15 deletions.
3 changes: 2 additions & 1 deletion boards/ESP32C3_IDF4.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@
# 'TELNET',
# 'FILESYSTEM',
# 'FLASHFS',
'BLUETOOTH'
'BLUETOOTH',
'NEOPIXEL'
],
'makefile' : [
'DEFINES+=-DESP_PLATFORM -DESP32=1',
Expand Down
110 changes: 96 additions & 14 deletions targets/esp32/esp32_neopixel.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/*
/*
This file is part of Espruino, a JavaScript interpreter for Microcontrollers
* adapted from source written by Chris Osborn <[email protected]>
* http://insentricity.com
* and https://github.com/cashoefman/ESP32-C3-Rainbow-LED-Strip/blob/master/components/led_strip for C3 port
*
* Copyright (C) 2013 Gordon Williams <[email protected]>
*
Expand All @@ -13,17 +14,21 @@ This file is part of Espruino, a JavaScript interpreter for Microcontrollers
* ESP32 specific exposed components for neopixel.
* ----------------------------------------------------------------------------
*/

#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <soc/rmt_struct.h>
#include <soc/dport_reg.h>
#include <driver/gpio.h>
#include <soc/gpio_sig_map.h>
#include <esp_intr.h>
#include <driver/rmt.h>

#if ESP_IDF_VERSION_MAJOR>=4
#else
#include <soc/dport_reg.h>
#endif

#include "esp32_neopixel.h"

#define RMTCHANNEL 0
Expand All @@ -32,10 +37,16 @@ This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#define DIVIDER 4 /* Above 4, timings start to deviate*/
#define DURATION 12.5 /* minimum time of a single RMT duration */

#define PULSE_T0H ( 350 / (DURATION * DIVIDER))
#define PULSE_T1H ( 900 / (DURATION * DIVIDER))
#define PULSE_T0L ( 900 / (DURATION * DIVIDER))
#define PULSE_T1L ( 350 / (DURATION * DIVIDER))
#define WS2812_T0H_NS (350)
#define WS2812_T0L_NS (900)
#define WS2812_T1H_NS (900)
#define WS2812_T1L_NS (350)
#define WS2812_RESET_US (280)

#define PULSE_T0H ( WS2812_T0H_NS / (DURATION * DIVIDER))
#define PULSE_T1H ( WS2812_T1H_NS / (DURATION * DIVIDER))
#define PULSE_T0L ( WS2812_T0L_NS / (DURATION * DIVIDER))
#define PULSE_T1L ( WS2812_T1L_NS / (DURATION * DIVIDER))
#define PULSE_TRS (50000 / (DURATION * DIVIDER))

typedef union {
Expand All @@ -57,6 +68,47 @@ static rmtPulsePair neopixel_bits[2]= {
{{PULSE_T1H,1,PULSE_T1L,0}}
};

#if ESP_IDF_VERSION_MAJOR>=4

int neopixelConfiguredGPIO = -1;

/**
* @brief Convert RGB data to RMT format.
*
* @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
*
* @param[in] src: source data, to converted to RMT format
* @param[in] dest: place where to store the convert result
* @param[in] src_size: size of source data
* @param[in] wanted_num: number of RMT items that want to get
* @param[out] translated_size: number of source data that got converted
* @param[out] item_num: number of RMT items which are converted from source data
*/
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
size_t wanted_num, size_t *translated_size, size_t *item_num) {
if (src == NULL || dest == NULL) {
*translated_size = 0;
*item_num = 0;
return;
}
size_t size = 0;
size_t num = 0;
uint8_t *psrc = (uint8_t *)src;
rmt_item32_t *pdest = dest;
while (size < src_size && num < wanted_num) {
for (int i = 7; i >= 0; i--) {
// MSB first
pdest->val = neopixel_bits[(*psrc>>i) & 1].val;
pdest++;
}
num+=8;
size++;
psrc++;
}
*translated_size = size;
*item_num = num;
}
#else
void neopixel_initRMTChannel(int rmtChannel){
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode.
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
Expand All @@ -72,9 +124,8 @@ void neopixel_initRMTChannel(int rmtChannel){
RMT.conf_ch[rmtChannel].conf1.ref_always_on = 1; // use apb clock: 80M
RMT.conf_ch[rmtChannel].conf1.idle_out_en = 1;
RMT.conf_ch[rmtChannel].conf1.idle_out_lv = 0;

return;
}
#endif

void neopixel_copy(){
unsigned int i, j, offset, offset2, len, bit;
Expand Down Expand Up @@ -115,8 +166,31 @@ void neopixel_handleInterrupt(void *arg){
}

void neopixel_init(int gpioNum){
#if ESP_IDF_VERSION_MAJOR>=4
if (neopixelConfiguredGPIO != gpioNum) {
if (neopixelConfiguredGPIO)
rmt_driver_uninstall(RMTCHANNEL);
neopixelConfiguredGPIO = gpioNum;
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpioNum, RMTCHANNEL);
// set counter clock to 40MHz
config.clk_div = 2;
ESP_ERROR_CHECK(rmt_config(&config));
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
uint32_t counter_clk_hz = 0;
if (rmt_get_counter_clock(config.channel, &counter_clk_hz) != ESP_OK)
printf("get rmt counter clock failed\n");
float ratio = (float)counter_clk_hz / 1e9;
neopixel_bits[0].duration0 = (uint32_t)(ratio * WS2812_T0H_NS);
neopixel_bits[0].duration1 = (uint32_t)(ratio * WS2812_T0L_NS);
neopixel_bits[1].duration0 = (uint32_t)(ratio * WS2812_T1H_NS);
neopixel_bits[1].duration1 = (uint32_t)(ratio * WS2812_T1L_NS);
// set ws2812 to rmt adapter
rmt_translator_init(config.channel, ws2812_rmt_adapter);
}
#else
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN);
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST);

// PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpioNum], 2);
// gpio_matrix_out(gpioNum, RMT_SIG_OUT0_IDX + RMTCHANNEL, 0, 0);
// gpio_set_direction(gpioNum, GPIO_MODE_OUTPUT);
Expand All @@ -128,19 +202,26 @@ void neopixel_init(int gpioNum){
RMT.int_ena.ch0_tx_thr_event = 1;
RMT.int_ena.ch0_tx_end = 1;
esp_intr_alloc(ETS_RMT_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED, neopixel_handleInterrupt, NULL, &rmt_intr_handle);

return;
#endif
}

bool esp32_neopixelWrite(Pin pin,unsigned char *rgbData, size_t rgbSize){
if(rgbSize) {
neopixel_init(pin);
neopixel_init(pin);
#if ESP_IDF_VERSION_MAJOR>=4
if (rmt_write_sample(RMTCHANNEL, rgbData, rgbSize, true) != ESP_OK)
printf("transmit RMT samples failed\n");
int timeout_ms = 100;
return rmt_wait_tx_done(RMTCHANNEL, pdMS_TO_TICKS(timeout_ms));
#else
neopixel_buffer = rgbData;
neopixel_len = rgbSize;
neopixel_pos = 0;
neopixel_bufpart = 0;
neopixel_sem = xSemaphoreCreateBinary();
for(int i=0;i<BUFFERS-1;i++) if (neopixel_pos < neopixel_len) neopixel_copy();
for(int i=0;i<BUFFERS-1;i++)
if (neopixel_pos < neopixel_len)
neopixel_copy();
RMT.conf_ch[RMTCHANNEL].conf1.mem_rd_rst = 1;
esp_intr_enable(rmt_intr_handle);
RMT.conf_ch[RMTCHANNEL].conf1.tx_start = 1;
Expand All @@ -149,6 +230,7 @@ bool esp32_neopixelWrite(Pin pin,unsigned char *rgbData, size_t rgbSize){
esp_intr_free(rmt_intr_handle);
neopixel_sem = NULL;
rmt_intr_handle=0;
#endif
}
return true;
}
}

0 comments on commit e3e68c7

Please sign in to comment.