ATtiny202事始め (2)WS2812Bを光らせてみる

# NeoPixel互換マイコン内蔵フルカラーLED
数多くの互換品が存在しているもよう。
入手性が良いのはWS2812B、sk6812あたりか？
### 秋月電子さんいつもありがとうございます
https://akizukidenshi.com/catalog/c/ccsmdled/
## 制御方法
T0H,T0L,T1H,T1Lのタイミングをどうやって制御するかにかかっている。
T0L,T1Lは比較的自由度が高い。
Trstは最後にL出力でほかっておけば良いのだが、連続して色を変える場合にはTrstを確保する必要がある。
https://akizukidenshi.com/download/ds/opscoled/sk6812-mini_rev09_cn.pdf
から抜粋
![AAA](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121242/4d7ffbf1-896f-849f-3c12-5252ca6c1f07.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121242/55ba8065-035e-142d-9db5-ad58cf93a1b2.png)
https://akizukidenshi.com/download/ds/worldsemi/WS2812B_20200225.pdf
から抜粋
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121242/e83e339b-7849-d824-8caf-af712a89803d.png)
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();
}
```
タイミングを見てみると
CODE1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121242/86627a6e-a319-403b-14a7-3b48243b7b5a.png)
CODE0
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121242/090a49c7-caf3-7a30-f55d-2c016c7bb4fd.png)

### ちなみに各色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);
	}
}
```
## 実行結果
静止画ですみません
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121242/168af634-bf65-1b34-622f-c71915393fd6.png)

## 今後の展開
### ライブラリ化してArduinoのmegaTinyCoreとかからも簡単に使えるようにしたい。