/* * This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo * * The MIT License (MIT) * * Copyright (c) 2018 LoBo (https://github.com/loboris) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include "driver/gpio.h" #include "driver/rmt.h" #include "libs/neopixel.h" #include "esp_log.h" static xSemaphoreHandle neopixel_sem = NULL; static intr_handle_t rmt_intr_handle = NULL; static rmt_channel_t RMTchannel = RMT_CHANNEL_0; static uint16_t neopixel_pos, neopixel_half, neopixel_bufIsDirty, neopixel_termsent; static uint16_t neopixel_buf_len = 0; static pixel_settings_t *neopixel_px; static uint8_t *neopixel_buffer = NULL; static uint8_t neopixel_brightness = 255; static uint8_t used_channels[RMT_CHANNEL_MAX] = {0}; // Get color value of RGB component //--------------------------------------------------- static uint8_t offset_color(char o, uint32_t color) { uint8_t clr = 0; switch(o) { case 'R': clr = (uint8_t)(color >> 24); break; case 'G': clr = (uint8_t)(color >> 16); break; case 'B': clr = (uint8_t)(color >> 8); break; case 'W': clr = (uint8_t)(color & 0xFF); break; default: clr = 0; } return clr; } // Set pixel color at buffer position from RGB color value //========================================================================= void np_set_pixel_color(pixel_settings_t *px, uint16_t idx, uint32_t color) { uint16_t ofs = idx * (px->nbits / 8); px->pixels[ofs] = offset_color(px->color_order[0], color); px->pixels[ofs+1] = offset_color(px->color_order[1], color); px->pixels[ofs+2] = offset_color(px->color_order[2], color); if (px->nbits == 32) px->pixels[ofs+3] = offset_color(px->color_order[3], color); } // Set pixel color at buffer position from HSB color value //============================================================================================================ void np_set_pixel_color_hsb(pixel_settings_t *px, uint16_t idx, float hue, float saturation, float brightness) { uint32_t color = hsb_to_rgb(hue, saturation, brightness); np_set_pixel_color(px, idx, color); } // Get RGB color value from RGB components corrected by brightness factor //============================================================================= uint32_t np_get_pixel_color(pixel_settings_t *px, uint16_t idx, uint8_t *white) { uint32_t clr = 0; uint32_t color = 0; uint8_t bpp = px->nbits/8; uint16_t ofs = idx * bpp; for (int i=0; i < bpp; i++) { clr = (uint16_t)px->pixels[ofs+i]; switch(px->color_order[i]) { case 'R': color |= (uint32_t)clr << 16; break; case 'G': color |= (uint32_t)clr << 8; break; case 'B': color |= (uint32_t)clr; break; case 'W': *white = px->pixels[ofs+i]; break; } } return color; } // Set two levels of RMT output to the Neopixel value for bit value "1". //------------------------------------------------------------------------------ static void IRAM_ATTR neopixel_mark(rmt_item32_t *pItem, pixel_settings_t *px) { pItem->level0 = px->timings.mark.level0; pItem->duration0 = px->timings.mark.duration0; pItem->level1 = px->timings.mark.level1; pItem->duration1 = px->timings.mark.duration1; } // Set two levels of RMT output to the Neopixel value for bit value "0". //------------------------------------------------------------------------------- static void IRAM_ATTR neopixel_space(rmt_item32_t *pItem, pixel_settings_t *px) { pItem->level0 = px->timings.space.level0; pItem->duration0 = px->timings.space.duration0; pItem->level1 = px->timings.space.level1; pItem->duration1 = px->timings.space.duration1; } // Set levels and duration of RMT output to the Neopixel value for Reset. //------------------------------------------------------------------------------ static void IRAM_ATTR rmt_terminate(rmt_item32_t *pItem, pixel_settings_t *px) { pItem->level0 = px->timings.reset.level0; pItem->duration0 = px->timings.reset.duration0; pItem->level1 = px->timings.reset.level1; pItem->duration1 = px->timings.reset.duration1; } // Transfer pixels from buffer to Neopixel strip //----------------------------------------- static void IRAM_ATTR copyToRmtBlock_half() { // This fills half an RMT block // When wrap around is happening, we want to keep the inactive half of the RMT block filled uint16_t i, offset, len, byteval; rmt_item32_t CurrentItem; offset = neopixel_half * MAX_PULSES; neopixel_half = !neopixel_half; // for next offset calculation int j; len = neopixel_buf_len - neopixel_pos; // remaining bytes in buffer if (len > (MAX_PULSES / 8)) len = (MAX_PULSES / 8); if (!len) { if (!neopixel_bufIsDirty) return; // Clear the channel's data block and return j = 0; if (!neopixel_termsent) { i++; rmt_terminate(&CurrentItem, neopixel_px); RMTMEM.chan[RMTchannel].data32[0].val = CurrentItem.val; neopixel_termsent = 1; j++; } for (i = j; i < MAX_PULSES; i++) { RMTMEM.chan[RMTchannel].data32[i + offset].val = 0; } neopixel_bufIsDirty = 0; return; } neopixel_bufIsDirty = 1; // Populate RMT bit buffer from 'neopixel_buffer' containing one byte for each RGB(W) value for (i = 0; i < len; i++) { byteval = (uint16_t)neopixel_buffer[i+neopixel_pos]; // Correct by brightness factor byteval = (byteval * neopixel_brightness) / 255; // Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the rmtPulsePair value corresponding to the buffered bit value for (j=7; j>=0; j--) { if (byteval & (1<pixel_count * (px->nbits / 8); if (neopixel_buffer == NULL) { neopixel_buffer = (uint8_t *)malloc(blen); if (neopixel_buffer == NULL) return; neopixel_buf_len = blen; } if (neopixel_buf_len < blen) { // larger buffer needed free(neopixel_buffer); neopixel_buffer = (uint8_t *)malloc(blen); if (neopixel_buffer == NULL) return; } memcpy(neopixel_buffer, px->pixels, blen); neopixel_buf_len = blen; neopixel_pos = 0; neopixel_half = 0; neopixel_px = px; neopixel_half = 0; neopixel_termsent = 0; neopixel_brightness = px->brightness; copyToRmtBlock_half(); if (neopixel_pos < neopixel_buf_len) { // Fill the other half of the buffer block copyToRmtBlock_half(); } // Start sending RMT.conf_ch[RMTchannel].conf1.mem_rd_rst = 1; RMT.conf_ch[RMTchannel].conf1.tx_start = 1; if (wait) { // Wait for operation to finish xSemaphoreTake(neopixel_sem, portMAX_DELAY); xSemaphoreGive(neopixel_sem); } } // Clear the Neopixel color buffer //================================= void np_clear(pixel_settings_t *px) { memset(px->pixels, 0, px->pixel_count * (px->nbits/8)); } //------------------------------------ static float Min(double a, double b) { return a <= b ? a : b; } //------------------------------------ static float Max(double a, double b) { return a >= b ? a : b; } // Convert 24-bit color to HSB representation //=================================================================== void rgb_to_hsb( uint32_t color, float *hue, float *sat, float *bri ) { float delta, min; float h = 0, s, v; uint8_t red = (color >> 16) & 0xFF; uint8_t green = (color >> 8) & 0xFF; uint8_t blue = color & 0xFF; min = Min(Min(red, green), blue); v = Max(Max(red, green), blue); delta = v - min; if (v == 0.0) s = 0; else s = delta / v; if (s == 0) h = 0.0; else { if (red == v) h = (green - blue) / delta; else if (green == v) h = 2 + (blue - red) / delta; else if (blue == v) h = 4 + (red - green) / delta; h *= 60; if (h < 0.0) h = h + 360; } *hue = h; *sat = s; *bri = v / 255; } // Convert HSB color to 24-bit color representation //============================================================ uint32_t hsb_to_rgb(float _hue, float _sat, float _brightness) { float red = 0.0; float green = 0.0; float blue = 0.0; if (_sat == 0.0) { red = _brightness; green = _brightness; blue = _brightness; } else { if (_hue >= 360.0) _hue = fmod(_hue, 360); int slice = (int)(_hue / 60.0); float hue_frac = (_hue / 60.0) - slice; float aa = _brightness * (1.0 - _sat); float bb = _brightness * (1.0 - _sat * hue_frac); float cc = _brightness * (1.0 - _sat * (1.0 - hue_frac)); switch(slice) { case 0: red = _brightness; green = cc; blue = aa; break; case 1: red = bb; green = _brightness; blue = aa; break; case 2: red = aa; green = _brightness; blue = cc; break; case 3: red = aa; green = bb; blue = _brightness; break; case 4: red = cc; green = aa; blue = _brightness; break; case 5: red = _brightness; green = aa; blue = bb; break; default: red = 0.0; green = 0.0; blue = 0.0; break; } } return (uint32_t)((uint8_t)(red * 255.0) << 16) | ((uint8_t)(green * 255.0) << 8) | ((uint8_t)(blue * 255.0)); } // Convert HSB color to 24-bit color representation // _hue: 0 ~ 359 // _sat: 0 ~ 255 // _bri: 0 ~ 255 //======================================================= uint32_t hsb_to_rgb_int(int hue, int sat, int brightness) { float _hue = (float)hue; float _sat = (float)((float)sat / 1000.0); float _brightness = (float)((float)brightness / 1000.0); float red = 0.0; float green = 0.0; float blue = 0.0; if (_sat == 0.0) { red = _brightness; green = _brightness; blue = _brightness; } else { if (_hue >= 360.0) _hue = fmod(_hue, 360); int slice = (int)(_hue / 60.0); float hue_frac = (_hue / 60.0) - slice; float aa = _brightness * (1.0 - _sat); float bb = _brightness * (1.0 - _sat * hue_frac); float cc = _brightness * (1.0 - _sat * (1.0 - hue_frac)); switch(slice) { case 0: red = _brightness; green = cc; blue = aa; break; case 1: red = bb; green = _brightness; blue = aa; break; case 2: red = aa; green = _brightness; blue = cc; break; case 3: red = aa; green = bb; blue = _brightness; break; case 4: red = cc; green = aa; blue = _brightness; break; case 5: red = _brightness; green = aa; blue = bb; break; default: red = 0.0; green = 0.0; blue = 0.0; break; } } return (uint32_t)((uint8_t)(red * 255.0) << 16) | ((uint8_t)(green * 255.0) << 8) | ((uint8_t)(blue * 255.0)); }