Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
WS281X.cpp
- Committer:
- mutech
- Date:
- 2016-11-04
- Revision:
- 35:dffa06d09fdc
- Parent:
- 34:5a141ed5d52a
- Child:
- 36:0fe7917a832a
File content as of revision 35:dffa06d09fdc:
/* WS281X.cpp (for LPC82X/STM32F0x/STM32F746xx)
 * mbed Microcontroller Library
 * Copyright (c) 2016 muetch, t.kuroki, MIT License
 *
 * 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.
 *
 * Rev 0.97 2016-09-07
 * Rev 0.98 2016-09-08
 */
#include "WS281X.h"
#if defined(TARGET_STM)
#include "pinmap.h"
#endif
#define USE_MALLOC          1   // 0:new, 1:malloc
// TARGET_STM32F7
// TARGET_DISCO_F746NG
// TARGET_NUCLEO_F746ZG
// TARGET_NUCLEO_F446RE
// TARGET_STM32F0
// TARGET_NUCLEO_F030R8
// TARGET_NUCLEO_F070RB
// TARGET_NUCLEO_F072RB
// TARGET_NUCLEO_F091RC
// TARGET_LPC82X
// TARGET_LPC824
//----------------------------------------------------------------------------
// 指定されたバッファの先頭からblock_size分をbuf_sizeが満杯になるまで繰り返しコピーする
template <class T>
static void repeat_buffer(T *buffer, int buf_size, int block_size = 1)
{
    if (buffer && block_size > 0 && (uint16_t)block_size < buf_size)
    {
        T *dest = buffer + block_size;
        int left = buf_size - block_size;
        while (left > block_size)
        {
            memcpy(dest, buffer, block_size * sizeof(T));
            dest += block_size;
            left -= block_size;
            block_size <<= 1;       // 次回は2倍のサイズの転送
        }
        memcpy(dest, buffer, left * sizeof(T));
    }
} 
//----------------------------------------------------------------------------
WS281X::WS281X(PinName wirePin, PinMode pinMode, int maxPixels, RGBOrder order)
    : _wirePin(wirePin), _gpio(), _owned_buffer(false)
{
    gpio_init_inout(&_gpio, wirePin, PIN_OUTPUT, pinMode, 0);
#if defined(TARGET_STM)
    pin_mode_ex(wirePin, pinMode);
#endif
    rgbOrder(order);
    _dummyPixel = 0;
    setPixelBuffer(0, maxPixels);
}
WS281X::WS281X(PinName wirePin, PinMode pinMode,
        RGBColor *buffer, int maxPixels, RGBOrder order)
    : _wirePin(wirePin), _gpio(), _owned_buffer(false)
{
    gpio_init_inout(&_gpio, wirePin, PIN_OUTPUT, pinMode, 0);
#if defined(TARGET_STM)
    pin_mode_ex(wirePin, pinMode);
#endif
    rgbOrder(order);
    _dummyPixel = 0;
    setPixelBuffer(buffer, maxPixels);
}
WS281X::~WS281X()
{
    setPixelBuffer(0, 0);
}
void WS281X::setPixelBuffer(RGBColor *buffer, int maxPixels)
{
    if (_owned_buffer && _pixels)
    {
#if USE_MALLOC
        free(_pixels);
#else
        delete[] _pixels;
#endif
    }
    _owned_buffer = false;
    _maxPixels = (maxPixels < 0) ? 0 : (maxPixels > MAX_PIXELS) ? MAX_PIXELS : maxPixels;
    _pixels = (!_maxPixels) ? NULL : buffer;
    if (!_pixels && _maxPixels > 0)
    {
#if USE_MALLOC
        _pixels = static_cast<RGBColor*>(malloc(sizeof(RGBColor)*_maxPixels));
        if (_pixels)
            _owned_buffer = true;
        else
            _maxPixels = 0;
#else
        _pixels = new RGBColor[_maxPixels];
        _owned_buffer = true;   
#endif
    }
    _numPixels = _maxPixels;
    clear();
}
int WS281X::numPixels(int value)
{
    if (value >= 0)
        _numPixels = (value > _maxPixels) ? _maxPixels : value;
    return _numPixels;
}
#if defined(TARGET_STM)
/**
 * Configure pin pull-up/pull-down/OpenDrain
 * typedef enum {
 *     PullNone  = 0,
 *     PullUp    = 1,
 *     PullDown  = 2,
 *     OpenDrain = 3,
 *     PullDefault = PullNone
 * } PinMode;
 */
void WS281X::pin_mode_ex(PinName pin, PinMode mode)
{
    int port_index = STM_PORT(pin);
    int pin_index = STM_PIN(pin);
    int offset = pin_index << 1;
    GPIO_TypeDef * port_reg = ((GPIO_TypeDef *) (GPIOA_BASE + (port_index << 10)));
    // Configure pull-up/pull-down resistors
    uint32_t pupd = (uint32_t)mode & 3;
    if (pupd > 2)
        pupd = 0; // Open-drain = No pull-up/No pull-down
    if (mode == OpenDrain)
    {
        port_reg->PUPDR &= ~(0x3 << offset);    // Open-drain = No pull-up/No pull-down
        port_reg->OTYPER |= 1 << pin_index;
    }
    else
    {
        port_reg->OTYPER &= ~(1 << pin_index);
//      pin_mode(pin, mode);
        port_reg->PUPDR &= ~(0x3 << offset);
        port_reg->PUPDR |= (mode & 0x03) << offset;
    }
}
#endif
WS281X::RGBOrder WS281X::rgbOrder(WS281X::RGBOrder order)
{
    if (order != READ_ORDER)
    {
        _rgbOrder = order;
        switch(order)
        {
            case RGB: _1st = 0; _2nd = 1; _3rd = 2; break;  // WS2811
            case RBG: _1st = 0; _2nd = 2; _3rd = 1; break;
            case GRB: _1st = 1; _2nd = 0; _3rd = 2; break;  // WS2812
            case GBR: _1st = 2; _2nd = 0; _3rd = 1; break;
            case BRG: _1st = 1; _2nd = 2; _3rd = 0; break;
            case BGR: _1st = 2; _2nd = 1; _3rd = 0; break;
            default:
                _1st = 0; _2nd = 1; _3rd = 2;
                _rgbOrder = GRB;    // WS2812
                break;
        }
    }
    return _rgbOrder;
}
//#define _nop1()     __nop()
#define _nop1()     do {asm volatile ("nop"); } while(0)
#define _nop2()     _nop1(); _nop1()
#define _nop3()     _nop1(); _nop2()
#define _nop4()     _nop2(); _nop2()
#define _nop5()     _nop1(); _nop4()
#define _nop6()     _nop2(); _nop4()
#define _nop7()     _nop3(); _nop4()
#define _nop8()     _nop4(); _nop4()
#define _nop9()     _nop1(); _nop8()
#define _nop10()    _nop2(); _nop8()
#define _nop11()    _nop3(); _nop8()
#define _nop12()    _nop4(); _nop8()
#define _nop13()    _nop5(); _nop8()
#define _nop14()    _nop6(); _nop8()
#define _nop15()    _nop7(); _nop8()
#define _nop16()    _nop8(); _nop8()
#if defined(TARGET_LPC82X)
// LPCXpresso824-MAX (30MHz)
#define DELAY_T0H()     do{ _nop2(); }while(0)
#define DELAY_T1H()     do{ _nop6(); }while(0)
#define DELAY_TLOW()    do{ _nop6(); }while(0)
#define DELAY_TLOW2()   //do{ _nop2(); }while(0)
#define DELAY_SPACE()   do{ _nop4(); }while(0)
#define DELAY_NEXT()    //do{ _nop1(); }while(0)
#endif
#if defined(TARGET_STM32F0)
// NUCLEO-F030R8 (48MHz)
// NUCLEO-F070RB (48MHz)
// NUCLEO-F072RB (48MHz)
#define DELAY_T0H()     do{ _nop8(); _nop4(); }while(0)
#define DELAY_T1H()     do{ _nop8(); _nop8(); }while(0)
#define DELAY_TLOW()    do{ _nop16(); }while(0)
#define DELAY_TLOW2()   //do{ _nop8(); _nop4(); }while(0)
#define DELAY_SPACE()   do{ _nop8(); _nop6(); }while(0)
#define DELAY_NEXT()    do{ _nop8(); }while(0)
#endif
#if defined(TARGET_NUCLEO_F446RE)
// NUCLEO-F446RE (180MHz)
#define USE_DELAYFUNC   1
#define T0H             (18)
#define T0L             (58-T0H)
#define T1H             (40)
#define T1L             (58-T1H)
#define DELAY_T0H()     _delay(T0H)
#define DELAY_T1H()     _delay(T1H-T0H)
#define DELAY_TLOW()    _delay(T1L)
#define DELAY_TLOW2()   //DELAY_TLOW()
#define DELAY_SPACE()   _delay(T1L-2)
#define DELAY_NEXT()    _delay(16)
#endif
#if defined(TARGET_NUCLEO_F746ZG)
// NUCLEO-F746ZG (216MHz)
#define USE_DELAYFUNC   1
#define T0H             (35)
#define T0L             (130-T0H)
#define T1H             (75)
#define T1L             (130-T1H)
#define DELAY_T0H()     _delay(T0H)
#define DELAY_T1H()     _delay(T1H-T0H)
#define DELAY_TLOW()    _delay(T1L)
#define DELAY_TLOW2()   //DELAY_TLOW()
#define DELAY_SPACE()   _delay(T1L+20)
#define DELAY_NEXT()    _delay(50)
#endif
#if defined(TARGET_DISCO_F746NG)
// TARGET_DISCO_F746NG (216MHz)
#define USE_DELAYFUNC   1
#define T0H             (35)
#define T0L             (125-T0H)
#define T1H             (90)
#define T1L             (125-T1H)
#define DELAY_T0H()     _delay(T0H)
#define DELAY_T1H()     _delay(T1H-T0H)
#define DELAY_TLOW()    _delay(T1L)
#define DELAY_TLOW2()   //DELAY_TLOW()
#define DELAY_SPACE()   _delay(T1L-5)
#define DELAY_NEXT()    _delay(40)
#endif
#if defined(USE_DELAYFUNC) && (USE_DELAYFUNC != 0)
static inline __attribute__((always_inline))
void _delay(int value)
{
    do { _nop1(); } while (--value);
}
#endif
inline __attribute__((always_inline))
void WS281X::writeByte(__IO regsize_t *reg_set, __IO regsize_t *reg_clr, regsize_t *mask, uint8_t value)
{
    do
    {
#if 1
    // bit7
        *reg_set = mask[0];
        DELAY_T0H();
        *reg_clr = mask[(value >> 7) & 1];
        DELAY_T1H();
        *reg_clr = mask[0];
        DELAY_TLOW();
    // bit6
        *reg_set = mask[0];
        DELAY_T0H();
        *reg_clr = mask[(value >> 6) & 1];
        DELAY_T1H();
        *reg_clr = mask[0];
        DELAY_TLOW();
    // bit5
        *reg_set = mask[0];
        DELAY_T0H();
        *reg_clr = mask[(value >> 5) & 1];
        DELAY_T1H();
        *reg_clr = mask[0];
        DELAY_TLOW();
    // bit4
        *reg_set = mask[0];
        DELAY_T0H();
        *reg_clr = mask[(value >> 4) & 1];
        DELAY_T1H();
        *reg_clr = mask[0];
        DELAY_TLOW();
#endif
    // bit3
        *reg_set = mask[0];
        DELAY_T0H();
        *reg_clr = mask[(value >> 3) & 1];
        DELAY_T1H();
        *reg_clr = mask[0];
        DELAY_TLOW();
    // bit2
        *reg_set = mask[0];
        DELAY_T0H();
        *reg_clr = mask[(value >> 2) & 1];
        DELAY_T1H();
        *reg_clr = mask[0];
        DELAY_TLOW();
    // bit1
        *reg_set = mask[0];
        DELAY_T0H();
        *reg_clr = mask[(value >> 1) & 1];
        DELAY_T1H();
        *reg_clr = mask[0];
        DELAY_TLOW();
    // bit0
        *reg_set = mask[0];
        DELAY_T0H();
        *reg_clr = mask[(value >> 0) & 1];
        DELAY_T1H();
        *reg_clr = mask[0];
        DELAY_TLOW2();
    } while (0);
}
void WS281X::show()
{
// CPU_FREQ = 30MHz -> 0.0333us/cycle
// WS2811 0: 0.25us+1.0us, 1: 1.0us+0.25us
// WS2812 0: 0.45us+0.8us, 1: 0.8us+0.45us
    if (!_pixels)
        return;
#if defined(TARGET_NXP)
    __IO uint32_t *reg_set = _gpio.reg_set;
    __IO uint32_t *reg_clr = _gpio.reg_clr;
    uint32_t mask[2] = { _gpio.mask, 0 };
#elif defined(TARGET_STM32F0) || defined(TARGET_STM32F1)
    __IO uint32_t *reg_set = _gpio.reg_set;
    __IO uint32_t *reg_clr = _gpio.reg_clr;
    uint32_t mask[2] = { _gpio.mask, 0 };
#elif defined(TARGET_STM)
    __IO uint16_t *reg_set = (__IO uint16_t *)_gpio.reg_set_clr;
    __IO uint16_t *reg_clr = reg_set + 1;
    uint16_t mask[2] = { _gpio.mask, 0 };
#endif
    uint8_t *pix = (uint8_t *)_pixels;
    uint8_t *end = pix + (_numPixels * sizeof(_pixels[0]));
    __disable_irq();   // Disable interrupts temporarily because we don't want our pulse timing to be messed up.
    uint8_t value;
    do
    {
        value = pix[_1st];
        writeByte(reg_set, reg_clr, mask, value);
        DELAY_SPACE();
        value = pix[_2nd];
        writeByte(reg_set, reg_clr, mask, value);
        DELAY_SPACE();
        value = pix[_3rd];
        writeByte(reg_set, reg_clr, mask, value);
        pix += sizeof(_pixels[0]);
        DELAY_NEXT();
    } while (pix < end);
    __enable_irq();   // Re-enable interrupts now that we are done.
    wait_us(50);
}
// 指定位置のピクセルへ色配列を指定サイズ分をコピーする
void WS281X::setPixels(int index, RGBColor *color, int len)
{
    int numPixels = static_cast<int>(_numPixels);
    if (_pixels && len > 0 && index < numPixels && (index + len) >= 0)
    {
        if (index < 0)
		{
			len   += index;
			color -= index;
			index  = 0;
		}
        if (index + len > numPixels)
            len = numPixels - index;
        memcpy(&_pixels[index], color, len * sizeof(_pixels[0]));
    }
}
void WS281X::setPixels(int index, HSVColor *color, int len)
{
    int numPixels = static_cast<int>(_numPixels);
    if (_pixels && len > 0 && index < numPixels && (index + len) >= 0)
    {
        if (index < 0)
		{
			len   += index;
			color -= index;
			index  = 0;
		}
        if (index + len > numPixels)
            len = numPixels - index;
        RGBColor *dest = &_pixels[index];
        do
        {
            *dest++ = *color++;
        } while (--len);
    }
}
// 指定色を指定位置のピクセルから指定サイズ分書き込む
void WS281X::fillPixels(int index, const RGBColor color, int len)
{
    if (_pixels && len > 0 && (uint16_t)index < _numPixels)
    {
        if (index + len > _numPixels)
            len = _numPixels - index;
        _pixels[index] = color;
        repeat_buffer<RGBColor>(_pixels + index, len, 1);
    }
}
// 指定位置のピクセルから指定色を指定サイズ分書き込む
void WS281X::fillPixels(int index, const HSVColor color, int len)
{
    fillPixels(index, color, len);
}
// 先頭から指定サイズ分のブロックをバッファの最後までコピーする
void WS281X::repeatPixels(int block_size)
{
    if (_pixels && block_size > 0 && block_size < _numPixels)
    {
        repeat_buffer<RGBColor>(_pixels, _numPixels, block_size);
    }
}
void WS281X::repeatPixels(RGBColor *source, int size)
{
    if (_pixels && source && size > 0)
    {
        if (size > _numPixels)
            size = _numPixels;
        memcpy(_pixels, source, size * sizeof(_pixels[0]));
        repeat_buffer<RGBColor>(_pixels, _numPixels, size);
    }
}
void WS281X::repeatPixels(HSVColor *source, int size)
{
    if (_pixels && source && size > 0)
    {
        if (size > _numPixels)
            size = _numPixels;
        for (int i = 0; i < size; ++i)
            _pixels[i] = *source++;
        repeat_buffer<RGBColor>(_pixels, _numPixels, size);
    }
}
void WS281X::makeGradation(int index, RGBColor from, RGBColor to, int len)
{
    if (!_pixels || len < 1 || index >= _numPixels || (index + len) < 0)
        return;
    int end = len;
    if (index + end > _numPixels)
        end = _numPixels - index;
    RGBColor color;
    RGBColor *dest = _pixels;
    if (index > 0)
        dest += index;
    for (int i = (index < 0) ? -index : 0; i < end; ++i)
    {
        int j = len - i;
        color.red   = ((from.red   * j) + (to.red   * i)) / len;
        color.green = ((from.green * j) + (to.green * i)) / len;
        color.blue  = ((from.blue  * j) + (to.blue  * i)) / len;
        *dest++     = GammaColor(color);
    }
}
void WS281X::makeRainbow(int index, HSVColor color, int len, int direction)
{
    if (!_pixels || len < 1 || index >= _numPixels || (index + len) < 0)
        return;
    int end = len;
    if (index + end > _numPixels)
        end = _numPixels - index;
    HSVColor hsv(color);
    RGBColor *dest = _pixels;
    if (index > 0)
        dest += index;
    direction = (direction >= 0) ? -3600 : 3600;
    for (int i = (index < 0) ? -index : 0; i < end; ++i)
    {
        hsv.hue = color.hue + direction * i / len;
        *dest++ = GammaColor(hsv);
    }
}
// 指定色でバッファを埋めた後表示
void WS281X::show(const RGBColor color)
{
    clear(color);
    show();
}
//----------------------------------------------------------------------------