SK6812 RGBW LED 스트립

  • 사용 보드 : nRF52840-PDK
  • 사용 IDE : SEGGER Embedded Studio for ARM (노르딕 제품 사용하는 경우 무료 라이센스 이용 가능)
  • 작동 전압 : 4.5~5.5V
  • 로직 인풋 레벨 (Din에 연결할) : -0.5~VDD+0.5
  • (3V 작동 전압 + 3V 로직 레벨로도 작동이 가능하다)
  • nRF52840-PDK 보드의 VDD(3V)를 통해 SK6812 전압 공급
  • 신호 전송에 사용될 GPIO Output high voltage = VDD 이므로 문제없이 구동된다
  • 특별히 사용 지정된 통신 방법은 없음
  • SDK 내 I2S 예제 프로젝트를 기반으로 작성 (\examples\peripheral\i2s)
  • (SDK 다운로드 : https://developer.nordicsemi.com/)

[동작]

<SPI를 통한 제어 시도>

<I2S를 통한 SK6812 RGBW LED 구동>

- SCK 설정

  • SK6812 모듈에 유효한 데이터 전송을 위해서는 0.3us, 0.6us, 0.9us (허용 오차 ±0.15us)의 크기를 갖는 데이터 블럭을 전송해야 한다
  • 데이터 1비트를 전송하는데 필요한 클럭(SCK)을 설정하기 위해선 다음과 같은 과정이 필요하다

nRF I2S

  1. 주어진 클럭(32Mhz)을 8~125 사이의 값으로 나눠서 Master Clock를 구함
    • MCK = 32Mhz / 8~125 (MCK : Master Clock)
  2. MCK를 32~512 사이의 값으로 나눠서 LRCK를 구함
    • LRCK = MCK / CONFIG.RATIO (32~512)
  3. 위의 과정을 통해 계산된 LRCK 값에 Sample Width 및 2를 곱한 값이 Serial Clock 값이 된다
    1. SCK = 2 * LRCK * CONFIG.SWIDTH (Sample Width)
  • 실제 사용 예시)
  • config.mck_setup = NRF_I2S_MCK_32MDIV10;
  • config.ratio = NRF_I2S_RATIO_32X;
  • config.sample_width = NRF_I2S_SWIDTH_16BIT;
    • MCK = 32 Mhz / 10 = 3.2 Mhz
    • LRCK = 3.2 / 32 = 0.1 Mhz
    • SCK = 2 * 0.1 * 16 = 3.2 Mhz
  • 따라서 1비트를 전송하는데는 0.3125us 시간이 걸린다

- 전송 데이터 구성

SK6812 데이터 인식 순서 (실제로는 RGBW가 아닌 GRBW 순으로 인식)

  • LED 각 색깔은 8개의 데이터(0~255 표현)로 구성되어있고 RGBW 네 개의 색을 가지고 있으므로 총 32개의 데이터가 전송되어야 한다
  • 하나의 데이터는 0 or 1로 표현할 수 있고 이 데이터를 표현하기 위해선 4개의 비트가 필요한다
  • 예) R0=0 이라면 1000 (0x8), R0=1이라면 1100 (0xC)로 구성됨

SK6812를 작동 시키는데 필요한 데이터 타이밍

  • 데이터의 0을 표현하기 위해선 0.3us 시간 동안 HIGH + 0.9 us 시간 동안 LOW 의 신호를 가져야 한다 (0x8, 1000)
  • 데이터의 1을 표현하기 위해선 0.6us 시간 동안 HIGH + 0.6 us 시간 동안 LOW 의 신호를 가져야 한다 (0xC, 1100)
  • 그러므로 하나의 색깔에 대한 전체 데이터는 4 bits * 8 = 32 bits 를 가지고 하나의 모듈을 구동하는데 필요한 데이터는 32 * 4(RGBW) = 128 bits 가 필요하다
  • 예) 빨간색 126 단계(0~255)를 표현하기 위해선 1000 0000 (R7 -> R0)의 데이터가 필요하고 이 데이터를 표현하기 위해서 0xC + 0x8 + 0x8 + ... + 0x8 (0xC + 0x8*7) 총 32비트를 사용

- I2S 전송 순서

CONFIG.FORMAT = Aligned, CONFIG.SWIDTH = 8Bit, CONFIG.CHANNELS = Stereo

  • L, R 데이터가 번갈아가며 전송된다 (그래프의 경우엔 SWIDTH=8 Bit로 L, R 각각은 8비트 데이터를 갖는다)
  • (SK6812에 전송할 신호 설정은 위 그래프 설정에서 SWIDTH만 16 Bit로 바꾼 상태)
  • 위의 설정(그래프 설정에서 SWIDTH만 16비트로 변경)에서 32 비트 신호를 전송하는 경우 15~0 비트가 LEFT, 31~16 비트가 RIGHT 채널에 해당한다
  • 예를 들어, Red=97을 설정하는 경우에 0110 0001 의 데이터가 필요하고 이 데이터는 0x8CC8 888C 로 구성된다
  • 그리고 이 데이터를 888C 8CC8 순서로 전송해야 제대로 된 밝기 단계가 설정된다
  • (만약 순서를 바꾸지 않고 전송한다면 R=22 로 설정된다)

- SK6812 내에서 데이터 처리

SK6812 LED에서 데이터를 받아들이는 방법

  • 그래프에서처럼 3개의 SK6812 LED에 데이터를 전송하는 경우, 첫 32개의 데이터는 SDOUT이 연결된 첫번째 LED에 그 다음 32개의 데이터는 두번째 LED, 마지막 32개 데이터는 세번째 LED에 전송된다
  • (LED에 전송된 데이터의 MSB부터 32개의 데이터(128Bit)만 남기고 나머지 부분만 다음 LED에 전송)
  • 때문에 특정 위치의 LED 색상을 바꾸기 위해 필요한 만큼의 데이터를 전송하는 것은 불가능하다
  • SK6812에 LED 데이터를 전송한 후, 다음 데이터를 전송하기 위해선 80us 시간 이상의 리셋 시간이 필요

[코드]

- 전역변수 및 정의

#define sk6812_led_num 1	//SK6812 LED 갯수 설정
#define sk6812_rst_buffer 8	//리셋, 32bit*8= 256bit = 0.3125us*256 = 80us
#define sk6812_buf_len sk6812_led_num*4+8	//리셋을 포함한 전송할 LED 데이터 총 길이(32비트 기준)

/*
하나의 색을 표현하는데 32비트 데이터가 필요하므로 unsigned 32bit integer 배열 선언
[0][0]=Green, [0][1]=Red, [0][2]=Blue, [0][3]=White 의 색상 정보를 저장
이중배열이지만 첫번째 행(sk6812_buffer[0][x])만 사용
*/
static uint32_t sk6812_buffer[2][sk6812_buf_len]={0};

static bool i2s_running=false;	//데이터 중복 전송을 피하는데 사용

- 구조체

/*
LED 색상 정보를 저장하고 버퍼에 반영하는데 사용
*/
typedef struct sk6812_rgbw{
  uint8_t red;
  uint8_t green;
  uint8_t blue;
  uint8_t white;
}sk6812_rgbw_struct;

- I2S 데이터 핸들러

/*
I2S 초기화와 함께 설정된 데이터 핸들러는 전송 직후 및 전송 완료 후 호출된다
*/
static void i2s_data_handler(nrf_drv_i2s_buffers_t const * p_released,
                         uint32_t                      status)
{
    static uint8_t transfer_chk=0;

    /*
    p_released - 이전에 드라이버에 전달된 버퍼에 대한 포인터
    전송이 처음 실행되어 호출됐을 땐, 전송된 데이터가 없기 때문에 p_released->p_tx_buffer=0이다
    전송 이후엔 p_released->p_tx_buffer엔 마지막으로 전송된 데이터 버퍼가 존재
    */
    if(p_released->p_tx_buffer!=0)	//전송이 이뤄진 뒤엔 p_tx_buffer엔 데이터가 존재하므로 실행됨
    {
      transfer_chk++;	//데이터가 전송됐다면 값 증가해 데이터 전송이 일어났음을 표시
    }

    if(transfer_chk!=0)	//데이터 전송이 일어난 이후
    {
      nrf_drv_i2s_stop();	//I2S 전송 중단
      i2s_running=false;	//I2S 전송 중이 아님을 표시
      transfer_chk=0;		//데이터 전송 여부 
    }
}

- I2S 데이터 전송 함수

/*
I2S를 통한 데이터 전송에 사용되는 함수. 데이터는 DMA를 통해 전송된다
*/
void sk6812_send_buffer()
{
  uint32_t err_code = NRF_SUCCESS;
  
  //I2S 구조체 변수 선언 및 전송 버퍼에 전송할 데이터가 있는 SK6812 버퍼 주소 입력
  nrf_drv_i2s_buffers_t const i2s_buffers = {
      .p_tx_buffer=sk6812_buffer[0],
  };

  //I2S 통신이 사용 중이 아니라면 전송 실행
  if(i2s_running==false)
  {
    err_code = nrf_drv_i2s_start(&i2s_buffers, sk6812_buf_len, 0);
    //sk6812_buffer[0]부터 sk6812_buf_len 길이만큼 전송 (32bit 기준)
    
    APP_ERROR_CHECK(err_code);
    i2s_running==true;	//중복 전송이 일어나지 않게 하기 위해 전송 중임을 나타내는 true로 변경
  }
}

- SK6812 색상 설정 함수

/*
전달받은 색상 구조체 변수와 LED 위치(index)를 기반으로 
버퍼의 원하는 부분에 LED 색상 데이터를 설정함
*/
void sk6812_set_color(sk6812_rgbw_struct* set_color, uint8_t index)
{
  uint32_t temp_g=0, temp_r=0, temp_b=0, temp_w=0;
  //전달받은 구조체 색상값을 SK6812에서 인식하는 데이터로 변환 후 저장하는데 사용할 32비트 변수
  
  /*
  8비트 색상값의 MSB부터 0비트 자리로 비트 시프트 한 뒤에 0x1을 AND 연산
  나온 값이 1이면 0xC, 0이면 0x8을 32비트 temp_ 변수의 MSB부터 차례대로 입력
  */
  for(uint8_t i=0;i<8;i++)	//Green
  {
    if((set_color->green>>(7-i))&0x1==1)
    {
      temp_g|=0xC<<(7-i)*4;
    }
    else
    {
      temp_g|=0x8<<(7-i)*4;
    }

    if((set_color->red>>(7-i))&0x1==1)	//Red
    {
      temp_r|=0xC<<(7-i)*4;
    }
    else
    {
      temp_r|=0x8<<(7-i)*4;
    }

    if((set_color->blue>>(7-i))&0x1==1)	//Blue
    {
      temp_b|=0xC<<(7-i)*4;
    }
    else
    {
      temp_b|=0x8<<(7-i)*4;
    }

    if((set_color->white>>(7-i))&0x1==1)	//White
    {
      temp_w|=0xC<<(7-i)*4;
    }
    else
    {
      temp_w|=0x8<<(7-i)*4;
    }
  }
  
  /*
  I2S 통신은 L, R이 번갈아가면서 전송되고 L, R 각 크기는 SWIDTH 설정에 의해 결정됨
  SWIDTH=16 Bit 로 설정해 사용하므로 16비트 간격으로 L, R이 나뉘어 전송된다
  LEFT는 15~0비트, RIGHT는 31~16비트를 차지하는데 LEFT 먼저 전송이 시작되므로
  제대로 된 전송을 위해선 LEFT<->RIGHT 비트 위치 전환을 해야 한다 (MSB->LSB 순으로 전송)
  */
  sk6812_buffer[0][index*4]=((temp_g&0x0000FFFF)<<16)|((temp_g&0xFFFF0000)>>16);
  sk6812_buffer[0][index*4+1]=((temp_r&0x0000FFFF)<<16)|((temp_r&0xFFFF0000)>>16);
  sk6812_buffer[0][index*4+2]=((temp_b&0x0000FFFF)<<16)|((temp_b&0xFFFF0000)>>16);
  sk6812_buffer[0][index*4+3]=((temp_w&0x0000FFFF)<<16)|((temp_w&0xFFFF0000)>>16);

  sk6812_send_buffer();	//SK6812 버퍼 전송 함수 호출
  nrf_delay_ms(1);
}

- 메인

/*
SDK Example 폴더 내 I2S 프로젝트 기반
*/
int main(void)
{
    uint32_t err_code = NRF_SUCCESS;
    APP_ERROR_CHECK(err_code);

    //I2S 설정
    nrf_drv_i2s_config_t config = NRF_DRV_I2S_DEFAULT_CONFIG;	//I2S 환경설정 구조체 변수 선언
    config.sdout_pin = I2S_SDOUT_PIN;	//SDOUT PIN 설정 (예제 기준 P0.27)
    config.mck_setup = NRF_I2S_MCK_32MDIV10;	(MasterClock = 32Mhz / 10)
    config.ratio     = NRF_I2S_RATIO_32X;
    config.sample_width=NRF_I2S_SWIDTH_16BIT;	(L, R 각 채널이 갖는 데이터 비트 크기)
    config.channels  = NRF_I2S_CHANNELS_STEREO;	(스테레오 설정)
    
    //I2S 환경설정 변수와 데이터 핸들러를 기반으로 I2S 통신 초기화
    err_code = nrf_drv_i2s_init(&config, i2s_data_handler);	
    APP_ERROR_CHECK(err_code);

    while(true)
    {  

    }
}

- 단순 사용 예시) 8개의 LED를 R->G->B->W->R 순서로 색상 변환 (각 색상의 밝기는 128 단계로 설정)

#define sk6812_led_num 8

int main(void)
{
    uint32_t err_code = NRF_SUCCESS;
    APP_ERROR_CHECK(err_code);

    //I2S 설정
    nrf_drv_i2s_config_t config = NRF_DRV_I2S_DEFAULT_CONFIG;	//I2S 환경설정 구조체 변수 선언
    config.sdout_pin = I2S_SDOUT_PIN;	//SDOUT PIN 설정 (예제 기준 P0.27)
    config.mck_setup = NRF_I2S_MCK_32MDIV10;	(MasterClock = 32Mhz / 10)
    config.ratio     = NRF_I2S_RATIO_32X;
    config.sample_width=NRF_I2S_SWIDTH_16BIT;	(L, R 각 채널이 갖는 데이터 비트 크기)
    config.channels  = NRF_I2S_CHANNELS_STEREO;	(스테레오 설정)
    
    //I2S 환경설정 변수와 데이터 핸들러를 기반으로 I2S 통신 초기화
    err_code = nrf_drv_i2s_init(&config, i2s_data_handler);	
    APP_ERROR_CHECK(err_code);

    //SK6812 색상 설정에 사용할 색상 구조체 변수 선언
    sk6812_rgbw_struct temp_rgbw;
    
    //모든 색상에 대해 0의 값을 설정
    temp_rgbw.red=0;
    temp_rgbw.green=0;
    temp_rgbw.blue=0;
    temp_rgbw.white=0;
    
    //모든 LED 버퍼에 0의 색상값 입력 후 전송, i=LED 순서(SDOUT이 연결된 LED가 시작점)
    for(uint8_t i=0;i<sk6812_led_num;i++)
    {
      sk6812_set_color(&temp_rgbw,i);
    }

    int PAUSE_TIME=1000;
    
    /*
    모든 LED에 동일한 색상을 지정한 뒤 전송하고 PAUSE_TIME 시간동안 정지된 후 
    다음 색상으로 변환 (빨강 -> 초록 -> 파랑 -> 흰색 순으로 무한 루프)
    */
    while(true)
    {  
        temp_rgbw.red=128;
        temp_rgbw.green=0;
        temp_rgbw.blue=0;
        temp_rgbw.white=0;
        for(uint8_t i=0;i<sk6812_led_num;i++)
        {
          sk6812_set_color(&temp_rgbw,i);
        }
        nrf_delay_ms(PAUSE_TIME);

        temp_rgbw.red=0;
        temp_rgbw.green=128;
        temp_rgbw.blue=0;
        temp_rgbw.white=0;
        for(uint8_t i=0;i<sk6812_led_num;i++)
        {
          sk6812_set_color(&temp_rgbw,i);
        }
        nrf_delay_ms(PAUSE_TIME);

        temp_rgbw.red=0;
        temp_rgbw.green=0;
        temp_rgbw.blue=128;
        temp_rgbw.white=0;
        for(uint8_t i=0;i<sk6812_led_num;i++)
        {
          sk6812_set_color(&temp_rgbw,i);
        }
        nrf_delay_ms(PAUSE_TIME);

        temp_rgbw.red=0;
        temp_rgbw.green=0;
        temp_rgbw.blue=0;
        temp_rgbw.white=128;
        for(uint8_t i=0;i<sk6812_led_num;i++)
        {
          sk6812_set_color(&temp_rgbw,i);
        }
        nrf_delay_ms(PAUSE_TIME);
    }
}

FOR 문 하나에 해당하는 데이터 전송 신호 (8번의 LED 전체 색상 데이터 전송)
8개의 LED 색상 데이터에 해당하는 데이터 신호 (32*4*8=1024 Bit)
LED 8개의 색상 데이터 전송 중 첫번째 LED에 Blue=128가 입력

  • I2S 전송 함수(sk6812_send_buffer())를 색상 설정 함수(sk6812_set_color) 내부에 넣었기 때문에 하나의 FOR문이 실행될 때 LED 갯수인 8개만큼의 전체 버퍼 전송이 이루어진다
  • 단순히 사용의 편의를 위해 I2S 전송 함수를 색상 설정 함수 내부에 넣은 것이므로 전송 함수를 색상 설정 함수에서 제외하고 LED 색상 버퍼의 데이터를 전부 바꾼 뒤에 따로 전송 함수를 호출하는 식으로 사용해도 된다
/*
사용 LED 4개, sk6812_set_color() 함수 내의 I2S 전송 함수 제외한 뒤 따로 사용 예
SK6812에 전송할 데이터 버퍼의 색상 정보 전체를 원하는 값으로 바꾼 후에 I2S 전송 함수 호출
첫번째 LED : Red 128, 두번째 LED : Green 128, 세번째 LED : Blue 128, 네번째 LED : White 128 의 값을 가짐
*/
temp_rgbw.red=128;
temp_rgbw.green=0;
temp_rgbw.blue=0;
temp_rgbw.white=0;
sk6812_set_color(&temp_rgbw,0);	//첫번째 LED 색상 버퍼 변경

temp_rgbw.red=0;
temp_rgbw.green=128;
temp_rgbw.blue=0;
temp_rgbw.white=0;
sk6812_set_color(&temp_rgbw,1);	//두번째 LED 색상 버퍼 변경

temp_rgbw.red=0;
temp_rgbw.green=0;
temp_rgbw.blue=128;
temp_rgbw.white=0;
sk6812_set_color(&temp_rgbw,2);	//세번째 LED 색상 버퍼 변경

temp_rgbw.red=0;
temp_rgbw.green=0;
temp_rgbw.blue=0;
temp_rgbw.white=128;
sk6812_set_color(&temp_rgbw,3);	//네번째 LED 색상 버퍼 변경

sk6812_send_buffer();

 

+ Recent posts