0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ATmega328PBを使う (4) SPIでマイコン内蔵RGB LEDを点ける

Posted at

初めに

LEDをPWMで点灯させると明るさは調整できますが色見は調整できません。プラモデルを電飾する場合は、色見の調整を行いたい場合があります。RGBそれぞれのLEDの明るさを調整して色を作ることができます。マイコン内蔵RGB LEDであれば個々のLEDをPWMで明るさ調整する必要がありません。

作例はこちら
https://www.youtube.com/watch?v=kqoh1bq-qb4

マイコン内蔵RGB LED

マイコン内蔵RGB LEDは色々ありますが、ここでは秋月電子で販売しているsk6812-mini,SK6812 SIDEを対象にします。
制御に使う信号はデータのみで、複数個をカスケードに接続できます。
v01.png

このLEDは、RESET後に最初に受信した24bitのデータでLEDを点灯し、それ以降のデータはDOから出力します。色を変える場合はRESETを送ってからデータを送ることになるため、接続するLEDの数で更新周期に制約が出てきます。
v06.png

制御フォーマット/タイミング

1bitの通信タイミングは以下の通りです。
v02.png
v04.png
周期の最大値が規定されていないことから、H/Lの期間の比率でビットの判定をしているのではなく’H'の期間で判定していると思われます。
立上りから0.4us以内であれば'0'、1.0usであれば'1'となります。

データはMSBからGRBの順に送ります。
v03.png

制御方法

ソフト制御で1us以下のタイミングを作るには処理を占有させる必要があります。また送信中は割り込み等で中断させることもできません。それだと使いづらいのでSPIを使って実装します。
1bitのデータ送信の期間を1byteに割り当て、Hのパルスを作成します。
1bitの長さはMin1.2usなのでこの値を基にSPIのクロックを決めます。
1.2us/8=0.15us 
クロックにすると6.67MHzとなりますが、今回使うATmega328PB_Xplained_Miniのクロックは16MHzなのでこのクロックは作れません。近い値で16MHzを2分周した8MHzとします。
T0Hの長さは2bit幅とし0.25us、T1Hの長さは5bit幅とし0.625usとします。

SPIが転送していない期間の出力は’L'なので、外部でHC04等のインバータで反転させます。

SPI

コンポーネントの設定

v05.png
クロックは16MHzを2分周とし、Masterモードとします。
ポートが4本割り当てられますが、使用するのはMOSIだけです。

処理

10ms毎に転送を行います。
手順
1 RGBデータから転送データを作成
2 Resetを送信
3 LED数分データ転送

RGBデータから転送データを作成

# define RGB_LED_TRAN_NUM	(24)			/* RG  LED 1個当たりのデータ転送数 */
# define RGB_LED_L_DATA		(0b00111111)
# define RGB_LED_H_DATA		(0b00000111)
# define RGB_LED_RESET_LENGTH	(10)		/* Resetデータ長 */
# define RGB_CONV_COUNT		(8)

typedef enum {
	td_State_Reset = 0,
	td_State_Transfer,
	td_State_Idle,
} td_State;


static uint8_t grb[RGB_LED_MAX_NUM][RGB_COLOR_NUM];				/* RGB data */
static uint8_t sdLedData[RGB_LED_MAX_NUM * RGB_LED_TRAN_NUM];	/* SPI送信データ */
static td_State rgbLed_State;									/* 制御状態 */

static uint16_t rstTranCount;					/* reset送信データ数 */
static uint8_t rgbLedTranCount;					/* RGB LEDデータ数 */
static uint8_t rgbLedNumOfTran;					/* RGB LED転送数 */
static uint8_t rgbLedLenNum;					/* RGB LED数 */

static const uint8_t rgbLedResetData = 0xFF;	/* Reset Data */

/* RGB LED転送開始 */
/* 転送データ作成 */
/* 送信開始 */
void rgbLed_start(void)
{
	/* RGB LED転送データ作成 */
	rgbLed_makeSendData();
	/* SPIデータ送信開始 */
	rgbLed_StartTran();	
	
}

/* RGBデータから送信データを作成 */
static void rgbLed_makeSendData(void)
{
	uint8_t bitMask;
	uint8_t rgbData;
	uint8_t sendData;
	uint8_t sdPt;

	sdPt = 0;
	for (uint8_t ledNo = 0; ledNo < rgbLedLenNum; ledNo++) {					/* LED数繰り返し */
		for (uint8_t rgbNo = 0; rgbNo < RGB_COLOR_NUM; rgbNo++) {				/* RGB分繰り返し */
			bitMask = 0b10000000;
			rgbData = grb[ledNo][rgbNo];
			for (uint8_t cnvCount = 0; cnvCount < RGB_CONV_COUNT; cnvCount++) {	/* 8bit繰り返し */
				sendData = 0x00;
				if ((rgbData & bitMask) != 0x00) {
					sendData = RGB_LED_H_DATA;
				} else {
					sendData = RGB_LED_L_DATA;
				}
				sdLedData[sdPt] = sendData;
				bitMask = bitMask >> 1;
				sdPt++;
			}
		}
	}
}

関数 rgbLed_startは、転送データ作成を行った後、SPI転送を開始します。
rgbLed_makeSendDataでは、色データのgrb[][]の内容を1bit毎チェックし、1byteの転送データに変換します。それを転送用バッファのsdLedData[]に格納します。

送信処理

/* RGB LED送信開始処理 */
static void rgbLed_StartTran(void)
{
	rgbLed_State = td_State_Reset;
	rstTranCount = 0;
	SPI_MS_Transmit(rgbLedResetData);	/* transmit Reset Data */
}

/* SPI割り込み */
void rgbLed_spi_intr(void)
{
	switch (rgbLed_State)
	{
		case td_State_Reset:		/* Reset出力中 */
			if (rstTranCount > RGB_LED_RESET_LENGTH) {
				/* データ送信開始処理 */
				rgbLed_StartDataTran();
			} else {
				/* Reset送信処理 */
				rgbLed_ResetTran();
			}
			break;
		case td_State_Transfer:		/* データ送信処理 */
			rgbLed_DataTran();
			if (rgbLedTranCount >= rgbLedNumOfTran) {
				/* 送信終了 */
				rgbLedTranCount = 0;
				rgbLed_State = td_State_Idle;
			}
			break;
		default:
			/* 送信完了処理 */
			rgbLed_State = td_State_Idle;
			break;
	}	
}

/* Reset送信処理 */
static void rgbLed_ResetTran(void)
{
	SPI_MS_Transmit(rgbLedResetData);	/* transmit Reset Data */
	rstTranCount++;
}

/* データ送信開始処理 */
static void rgbLed_StartDataTran(void)
{
	rgbLed_State = td_State_Transfer;
	rgbLedTranCount = 0;
	rgbLed_DataTran();
}

/* データ送信処理 */
static void rgbLed_DataTran(void)
{
	SPI_MS_Transmit(sdLedData[rgbLedTranCount]);
	rgbLedTranCount++;
}

関数rgbLed_StartTranにより転送を開始します。
1byte転送終了後に割り込みが発生します。
割り込み処理ではReset送信中であればResetデータを送信し、データ送信中であればデータ送信を行います。送信データ数により終了判断を行います。

おわり

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?