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-12-20
- Revision:
- 46:2374900f8845
- Parent:
- 44:d89e5c59aeb0
- Child:
- 48:8eb4d886d28b
File content as of revision 46:2374900f8845:
/* WS281X.cpp (for LPC82X/STM32F0x/STM32F746xx)
* mbed Microcontroller Library
* Copyright (c) 2016 muetch, t.kuroki
* Allrights reserved.
*
* Rev 0.97 2016-09-07
* Rev 0.98 2016-09-08
*/
#include "WS281X.h"
#if defined(TARGET_STM)
#include "pinmap.h"
#endif
// 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
//----------------------------------------------------------------------------
WS281X::WS281X(PinName txPin, PinMode pinMode, int maxPixels, RGBOrder order)
: RGBPixels(maxPixels), _gpio()
{
pin_init(txPin, pinMode);
rgbOrder(order);
}
WS281X::WS281X(PinName txPin, PinMode pinMode,
RGBColor *buffer, int maxPixels, RGBOrder order)
: RGBPixels(buffer, maxPixels), _gpio()
{
pin_init(txPin, pinMode);
rgbOrder(order);
}
void WS281X::pin_init(PinName txPin, PinMode pinMode)
{
_txPin = txPin;
gpio_init_inout(&_gpio, txPin, PIN_OUTPUT, pinMode, 0);
#if defined(TARGET_STM)
pin_mode_ex(txPin, pinMode);
#endif
}
#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(RGBOrder order)
{
switch(_rgb_order = 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;
_rgb_order = GRB; // WS2812
break;
}
return _rgb_order;
}
//#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_F411RE)
// NUCLEO-F411RE (96MHz)
#define USE_DELAYFUNC 1
#define T0H (10)
#define T0L (31-T0H)
#define T1H (21)
#define T1L (31-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_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 + (_num_pixels * 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::show(const RGBColor color)
{
fill(color);
show();
}
//----------------------------------------------------------------------------