- RED, GREEN, BLUE + WHITE 네 부분으로 구성된 RGBW 모듈
- 작동 전압 5V
- RGBW 각각의 값은 0~255 사이의 8비트 데이터로 구성됨
- VDD, DIN, DOUT, GND 네 부분으로 구성되며 DIN에 신호를 보내 컨트롤
- DIN에만 제어 신호를 전달하면 되므로 SPI통신을 이용할 경우 MOSI를 DIN에 연결하여 사용
- 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 설정]
- 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 데이터 마지막에 이어서 전송
- 데이터 시트의 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의 값으로 변한다
'STM32' 카테고리의 다른 글
STM32 - UART 기반 PMS7003 먼지 센서 제어 (17) | 2019.12.04 |
---|---|
P-NUCLEO-WB55 보드 무선 통신 사용 설정 (1) | 2019.10.16 |
STM32 - UART 통신 기반 SenseAir S8 LP CO2 감지 센서 (0) | 2019.10.11 |
STM32 - I2C 통신을 이용한 SSD1306 128x64 OLED 제어 (4) | 2019.09.17 |
STM32 - I2C를 이용한 DS3231 제어 (2) | 2019.08.20 |