NeoPixel互換マイコン内蔵フルカラーLED
数多くの互換品が存在しているもよう。
入手性が良いのはWS2812B、sk6812あたりか?
秋月電子さんいつもありがとうございます
制御方法
T0H,T0L,T1H,T1Lのタイミングをどうやって制御するかにかかっている。
T0L,T1Lは比較的自由度が高い。
Trstは最後にL出力でほかっておけば良いのだが、連続して色を変える場合にはTrstを確保する必要がある。
https://akizukidenshi.com/download/ds/opscoled/sk6812-mini_rev09_cn.pdf
から抜粋
https://akizukidenshi.com/download/ds/worldsemi/WS2812B_20200225.pdf
から抜粋
WS2812Bとsk6812で若干違う部分もあるが基本的に同じタイミングで制御可能
IOポートを使用してタイミングを生成
SPIを使う方法もあるようだがそちらは今後の課題としたい。
今回はプログラムでIOポートの出力を変化させるという原始的な方法を取った。
単純にポートをたたいて後はNOPでタイミングを調整した。
T0L,T1L期間に関しては多少伸びても問題ないようなので、ビット、カラー、LEDポジションは普通にforループで回して選択している。
WS281B制御コード抜粋.cpp
void SendPixData() {
cli();
for(uint8_t iLED=0; iLED<NUM_LED; iLED++) {
for(uint8_t iRGB=0; iRGB<3; iRGB++) {
uint8_t txByte = PixData[iLED][iRGB];
for(uint8_t iBIT=0; iBIT<8; iBIT++) {
uint8_t mbData = txByte & 0x80;
if(!mbData) { // Send 1
VPORTA.OUT |= mbPORTA;
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
::
);
VPORTA.OUT &= ~mbPORTA;
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
::
);
} else { // Send 0:
VPORTA.OUT |= mbPORTA;
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
::
);
VPORTA.OUT &= ~mbPORTA;
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
::
);
}
txByte = txByte << 1;
}
}
}
sei();
}
ちなみに各色8ビットの計24ビットデータの並びはG7~G0,R7~R0,B7~B0
サンプルコード
main.cpp
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define PA 7 // PORTA #7 : PKG PIN #3
#define NUM_LED 8
#define mbPORTA 0x01 << PA
uint8_t PixData[NUM_LED][3];
void SendPixData() {
cli();
for(uint8_t iLED=0; iLED<NUM_LED; iLED++) {
for(uint8_t iRGB=0; iRGB<3; iRGB++) {
uint8_t txByte = PixData[iLED][iRGB];
for(uint8_t iBIT=0; iBIT<8; iBIT++) {
uint8_t mbData = txByte & 0x80;
if(!mbData) { // Send 1
VPORTA.OUT |= mbPORTA;
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
::
);
VPORTA.OUT &= ~mbPORTA;
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
::
);
} else { // Send 0:
VPORTA.OUT |= mbPORTA;
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
::
);
VPORTA.OUT &= ~mbPORTA;
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
::
);
}
txByte = txByte << 1;
}
}
}
sei();
}
void SetRGB(uint8_t i, uint8_t R, uint8_t G, uint8_t B) {
PixData[i][0] = G;
PixData[i][1] = R;
PixData[i][2] = B;
}
void ClrALL(void) {
for(uint8_t i=0; i<NUM_LED; i++) {
SetRGB(i, 0x00, 0x00, 0x00);
}
}
void SetPAT8(void) {
SetRGB(0, 0x80, 0x00, 0x00);
SetRGB(1, 0x40, 0x40, 0x00);
SetRGB(2, 0x00, 0x80, 0x00);
SetRGB(3, 0x00, 0x40, 0x40);
SetRGB(4, 0x00, 0x00, 0x80);
SetRGB(5, 0x40, 0x00, 0x40);
SetRGB(6, 0x10, 0x10, 0x10);
SetRGB(7, 0x80, 0x80, 0x80);
}
int main(void)
{
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x00);
VPORTA.DIR |= mbPORTA;
while(1)
{
ClrALL();
SendPixData();
_delay_ms(500);
SetPAT8();
SendPixData();
_delay_ms(500);
}
}