SK6812 RGBW 모델

  • RED, GREEN, BLUE + WHITE 네 부분으로 구성된 RGBW 모듈
  • 작동 전압 5V
  • RGBW 각각의 값은 0~255 사이의 8비트 데이터로 구성됨
  • VDD, DIN, DOUT, GND 네 부분으로 구성되며 DIN에 신호를 보내 컨트롤
  • DIN에만 제어 신호를 전달하면 되므로 SPI통신을 이용할 경우 MOSI를 DIN에 연결하여 사용

RGBW 각각의 비트에 0, 1을 구현하기 위한 방법
SK6812 RGBW LED가 필요로 하는 데이터 구조 및 인식하는 순서 (위의 표와 다르게 실제로는 GRBW 순으로 인식함)

  • RGBW 각각은 8비트 데이터로 구성되고 각각의 비트는 T0H, T0L, T1H, T1L 조합으로 0과 1을 표현함
  • 0의 경우 : 0.3us(HIGH)+0.9us(LOW) 로 구성 (T0H + T0L + T0L +T0L)
  • 1의 경우 : 0.6us(HIGH)+0.6us(LOW) 로 구성 (T1H + T1H + T1L + T1L)
  • 총 길이 : 1.2us

구동 조건

  • 여러 개의 모듈을 구동하기 위해선 32bit * 구동할 LED의 갯수만큼의 데이터를 한번에 전송해야 함
  • LED 데이터 갱신이 필요한 경우, 이전 전송 이후 80us 이상의 리셋(LOW) 상태를 유지한 뒤에 데이터 재전송해야 함
  • 데이터 라인에 첫번째로 연결된 LED가 첫 32비트(first)를 받고 나머지 값은 그 다음 LED로 넘김

구동 방식

  • DIN으로 들어간 신호가 32비트 데이터(RGBW값)를 해당 순서의 LED에 전달된 뒤 나머지 값들은 DOUT으로 나가면서 계속 이어져가는 방식

[CUBE MX 설정]

데이터 사이즈 : 8 Mbits, 보드 레이트 3.0Mbits/s, NSSP Mode : 미사용

  • 3.0Mbit/s 의 경우, 하나의 신호당 요구되는 0.3us +-0.15 오차 안에 포함되는 0.333us의 길이를 갖음
  • 이 경우 LED 데이터의 0 or 1의 비트를 표현하기 위해 4개의 비트를 필요로 함 (0.333us*4)
  • 예)RED=1의 경우, 0000 0001 -> 0=0.333us(HIGH*1)+0.999us(LOW*3) -> 1000 *7(7~1비트)
  •                                       -> 1=0.666us(HIGH*2)+0.666us(LOW*2) -> 1100 *1(0비트)
  • 총 32(R8+G8+B8+W8)*4(0과 1 구현 위한 LOW와 HIGH 조합)*구동하고자 하는 LED의 갯수만큼의 비트가 필요
  • 예) LED 하나 사용하는 경우, 32*4*1=128비트로 총 16바이트이며 각 색상별로 4바이트를 사용
  • (4.0Mbits/s, 8.0Mbits/s, 16.0Mbits/s을 사용했을 때, 출력 형태가 오차 범위내에 포함 or 걸치는 형태였으나 정상  작동 X (ex: 0=0.25us+0.75us, 1=0.5us+0.5us))

  • STM32WB에서 SPI1의 경우 APB2에 속해있으므로 APB2의 클럭을 조절해줘야 함
  • 0.333us의 신호 길이를 갖기 위해 peripheral 클럭을 24MHz로 설정한 뒤, prescaler 값을 8로 설정 (1s/3.0M=0.333us)

  • 그 외, SPI1 Interrupt 체크 및 DMA 설정

[코드]

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

#define led_num 4
//led 하나당 16바이트 사용 (4bit*8(=하나의 색)*RGBW=16바이트),+4는 리셋을 위함
#define led_spi_buffer_bytes led_num*4*4+4

/* USER CODE END PD */
  • 사용할 LED의 갯수와 그에 따른 버퍼의 크기를 define
  • MOSI의 신호가 데이터 전송 후 LOW로 떨어지지 않고 HIGH를 유지하여 LED 작동에 문제를 가져다주는 문제를 해결하기 위해 4바이트의 LOW신호를 LED 데이터 마지막에 이어서 전송

추가 LOW 신호가 없을 때, MOSI는 데이터 전송 이후 HIGH를 유지 (LED 3개 사용으로 총 48바이트의 길이)
추가로 2바이트 길이의 LOW 신호를 줬을 때, 2바이트 길이의 LOW신호 이후 MOSI 신호는 다시 HIGH로 바뀜
추가로 4바이트 길이의 LOW 신호를 줬을 때, 4바이트 길이의 LOW 신호 이후에도 LOW 상태를 유지 (정상 작동)

  • 데이터 시트의 80us 이상의 Reset(LOW) 신호를 준수하기 위해서는 4바이트가 아닌 30바이트 정도의 추가 LOW 신호를 사용 (80us/0.333us=240bits=30bytes)
  • Reset에 필요한 30바이트가 아닌 4바이트만을 사용한 이유는 단순히 개인적으로 80us 이하로 잦은 LED 색상 변경을 사용하는 경우가 없어 MOSI 신호를 LOW로 유지 시켜주는 최소한의 추가 바이트만 사용해도 괜찮겠다 생각했기 때문
/* USER CODE BEGIN PV */

struct sk_RGBW
{
	uint8_t red;
	uint8_t green;
	uint8_t blue;
	uint8_t white;
};
struct sk_RGBW sk6812_led[led_num]={0};		
//사용할 LED 갯수만큼 구조체 변수 생성
//ex) sk6812_led[0]의 경우, 첫번째 순서의 LED가 가질 색상들을 의미
uint8_t led_buffer[led_spi_buffer_bytes]={0};	//SPI를 이용해 LED에 전달될 데이터 배열

/* USER CODE END PV */
  • LED 값을 저장할 구조체와 배열을 생성
/* USER CODE BEGIN PFP */
void set_color(struct sk_RGBW *led_arr, uint8_t led_index)
{
	uint8_t i;
	uint32_t led_temp_buff[4]={0};
    //하나의 배열당 하나의 색을 담당 ex)led_temp_buff[0]=Green, led_temp_buff[1]=Red
    //LED 데이터 하나의 비트를 표현하기 위해 4개의 H, L 비트가 필요하고 총 8자리의 LED 데이터가 필요하므로
    //unsigned int 32bits 배열 사용 (4*8=32)
    
    //LED 비트 0을 표현하기 위해선 1000=0x8, 1을 표현하기 위해선 1100=0xc
    //예) Red=128일 때(1000 0000), led_temp_buff[1]=1100 1000 1000 ... 1000
	
    /*
    MSB부터 출력되므로 led_temp_buff의 MSB부터 전달받은 색 데이터의 0, 1을 구분한 뒤 채워나감
    전달받은 포인터 구조체 주소를 참조해 LED 배열의 MSB부터 데이터를 채워나감
    예) led_arr->green=1100 1000일 때 (Green=200), 
    첫번째(i=0) 루프에서 0만큼 MSB 방향으로 전체 비트를 시프트하고
    1000 0000을 곱한 뒤, 나온 값이 0이냐 1이냐에 따라 0x8(0일 때) or 0xC(1일 때)를
    (7-0)*4만큼 MSB 방향으로 시프트 시킨 뒤 32비트 배열에 저장
    i=0일 때, led_temp_buff[0]=(31,MSB) 1100 0000 0000 .... 0000 (0,LSB)
    i=1일 때, led_temp_buff[0]=(31,MSB) 1100 1100 0000 .... 0000 (0,LSB)
    i=7일 때, led_temp_buff[0]=(31,MSB) 1100 1100 1000 1000 1100 1000 1000 1000 (0,LSB)
    
    SK6812는 데이터를 RGBW순이 아닌 GRBW순으로 인식함
    */
	for(i=0;i<8;i++)
	{
		if((led_arr->green<<i)&0x80)	//Green
			led_temp_buff[0]+=(0xc<<(7-i)*4);
		else
			led_temp_buff[0]+=(0x8<<(7-i)*4);
		if((led_arr->red<<i)&0x80)	//Red
			led_temp_buff[1]+=(0xc<<(7-i)*4);
		else
			led_temp_buff[1]+=(0x8<<(7-i)*4);
		if((led_arr->blue<<i)&0x80)	//Blue
			led_temp_buff[2]+=(0xc<<(7-i)*4);
		else
			led_temp_buff[2]+=(0x8<<(7-i)*4);
		if((led_arr->white<<i)&0x80)	//WHITE
			led_temp_buff[3]+=(0xc<<(7-i)*4);
		else
			led_temp_buff[3]+=(0x8<<(7-i)*4);
	}


	/*
    led_index는 실제 LED의 순서를 의미
    ex) led_index=0 : MOSI에 연결된 첫번째 LED
    
    led_index*16에서 *16은 하나의 LED당 16바이트의 색상 데이터를 사용하기 때문
    ex) led_index=2일 경우(실제로는 세번째에 위치한 LED),
    전역변수로 선언된 led_buffer[] 배열 값 중에서 led_index[32]~led_index[47]까지의 배열만 값이 바뀌고
    나머지 배열들의 값은 유지된 채로 출력됨
    
	led_buffer 배열에 LED 데이터를 입력한 후, DMA를 이용해 LED에 출력
    각 색깔별로 4(LOW+HIGH)*8(LED 데이터(=LED 강도))=32bits=4bytes의 데이터 크기를 갖음
    ex) GREEN=200=1100 1000=1100 1100 1000 1000 ... 1000 일 때,
    led_buffer[0]=led_temp_buff[0] 31~24 비트값 1100 1100
    led_buffer[1]=led_temp_buff[0] 24~16 비트값 1000 1000
    led_buffer[2]=led_temp_buff[0] 15~8 비트값 1100 1000
    led_buffer[3]=led_temp_buff[0] 7~0 비트값 1000 1000
    (MSB부터 출력되므로 MSB를 [0]번 배열에 저장)
    
    led_buffer[4]~[7]=RED, led_buffer[8]~[11]=BLUE, led_buffer[12]~[15]=WHITE
    */
    
	for(i=0;i<4;i++)
	{
		led_buffer[(i+led_index*16)]=(led_temp_buff[0]>>(3-i)*8);	//GREEN
		led_buffer[(i+led_index*16+4)]=(led_temp_buff[1]>>(3-i)*8);	//RED
		led_buffer[(i+led_index*16+8)]=(led_temp_buff[2]>>(3-i)*8);	//BLUE
		led_buffer[(i+led_index*16+12)]=(led_temp_buff[3]>>(3-i)*8);	//WHITE
	}
	HAL_SPI_Transmit_DMA(&hspi1,led_buffer,led_spi_buffer_bytes);
    //DMA를 이용해 데이터 보냄, SPI1 사용, led_buffer 보낼 데이터, led_spi_buffer_bytes 전송할 byte의 크기
}
/* USER CODE END PFP */
  • SPI 통신을 사용하고 DMA를 이용해 RGBW 데이터를 출력하는 함수
void set_rgb_value(struct sk_RGBW *current_rgb,uint8_t red,uint8_t green,uint8_t blue,uint8_t white)
{
	current_rgb->red=red;
	current_rgb->green=green;
	current_rgb->blue=blue;
	current_rgb->white=white;
}
  • 구조체 배열 변수 주소를 전달받아 해당 배열의 RGBW색상을 설정하는 함수
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    
	  set_rgb_value(&sk6812_led[0],0,0,0,0);	//구조체 배열 변수(sk6812_led[0])에 RGBW값 설정
	  for(i=0;i<led_num;i++)
	  {
		  set_color(&sk6812_led[0],i);	//설정된 LED 갯수만큼 sk6812_led[0]에 저장된 RGBW값을 출력
	  }
	  HAL_Delay(1000);

	  set_rgb_value(&sk6812_led[0],255,0,0,0);	//RED=255, 나머지는 0으로 설정
	  for(i=0;i<led_num;i++)
	  {
		  set_color(&sk6812_led[0],i);
          //ex) LED 갯수 3, sk6812_led[0]=RED 255를 제외한 나머지 GBW값이 0일 때, 
          //	세 개의 LED가 동일한 RED=255만큼의 빛 세기를 가진채로 켜짐
	  }
	  HAL_Delay(1000);
      
	  set_rgb_value(&sk6812_led[1],0,255,0,0);	//sk6812_led[1]에 GREEN=255, 나머지=0 입력
	  set_color(&sk6812_led[1],1);	
      /*
      sk6812_led[1]에 저장된 RGBW값을 2번째 LED에 전달 
      led_index값이 0부터 시작되므로 led_index=1은 두번째 LED를 의미
      sk6812_led[1]의 값을 두번째 LED에 할당한 이유는 단순히 
      sk6812_led[0]은 첫번째 LED, sk6812_led[1]은 두번째 LED의 값을 갖는다는 순서를 맞춰준것일뿐
      중요한 부분은 set_color(&sk6812_led[1],1); 에서 LED 순서를 의미하는 led_index=1
      */
	  HAL_Delay(1000);
   }
  • 사용할 LED의 갯수를 네 개라 가정 (led_num=4)
  • 처음엔 네 개의 LED가 꺼진 상태로 시작 (RGBW=0,0,0,0)
  • 1초의 딜레이 후, 출력에 이용될 LED 배열에 RGBW 값을 입력 (RED=255, GBW=0)
  • FOR문이 네 차례 반복되며 SK6812 RGBW LED에 led_buffer[0]~led_buffer[68]까지의 버퍼를 출력
  • i=0일 때, led_buffer 중 led_buffer[0]~led_buffer[15]만 값을 가진채로 출력되므로 첫번째 LED만 켜짐 (나머지 배열은 초기화 값인 0 유지)
  • i=1일 때, led_buffer 중 led_buffer[16]~led_buffer[31]의 값이 입력되어 led_buffer[0]~led_buffer[31]까지 유효한 LED 데이터 값을 가지므로 두 번째 LED까지 켜짐
  • i=2,3 도 위의 상황이 반복되므로 결국 네 개의 LED가 켜짐
  • 출력에 사용될 RGBW 배열을 sk6812_led[0]으로 고정시켰기 때문에 네 개의 LED가 동일하게 RED=255 값의 밝기를 가진다
  • 1초의 딜레이 후, sk6812_led[1]의 값을 GREEN=255, RBW=0으로 변경
  • sk6812_led[1]의 값을 두 번째 LED(led_index=1)에 출력하기 위해 출력 함수인 set_color 사용
  • led_buffer 배열 중 두 번째 LED값에 해당하는 led_buffer[16]~led_buffer[31]의 값만이 sk6812_led[1]의 값으로 치환되고 HAL_SPI_Transmit_DMA 함수를 통해 led_buffer[0]~led_buffer[64] 네 개의 LED 전체 RGBW값을 출력함
  • 첫번째와 세번째, 네번째 LED의 값은 RED=255의 값을 유지하고 두 번째 LED만이 GREEN=255, RBW=0의 값으로 변한다

+ Recent posts