본문 바로가기

Nordic/nRF54

[nRF54 Xbee EVM] PWM 테스트 - SK6812 NexPixel LED 제어

 

nRF54 Xbee의 PWM 테스트를 위해 Xbee 키보드 인터페이스 보드의 SK6812 LED를 제어하는 테스트를 해 보자.

 

 

SK6812  NexPixel LED를 제어 하는 방법은 여러가지가 있지만 간단하게 PWM으로 제어할 수 있다. 우선 nRF54의 PWM 초기화 함수를 작성한다.

#include <hal/nrf_pwm.h>
#include <nrfx_pwm.h>
#include <nrfx.h>

#define SK6812_PIN          NRF_GPIO_PIN_MAP(1, 8)

#define NUM_LEDS            4
#define BITS_PER_LED        24                     // GRB 24비트
#define TOTAL_DATA_BITS     (NUM_LEDS * BITS_PER_LED)
#define RESET_BITS          80                     // >80us Low Latch 신호

// 16MHz 클럭 기준, 주기 20 Ticks = 1.25us (800kHz)
#define PWM_COUNTER_TOP     20
#define T0H_TICKS           5                      // Bit 0 High: 0.3125us
#define T1H_TICKS           11                     // Bit 1 High: 0.6875us

// EasyDMA 전송 버퍼
static uint16_t pwm_seq_values[TOTAL_DATA_BITS + RESET_BITS];

typedef struct {
    uint8_t r;
    uint8_t g;
    uint8_t b;
} ColorGRB;

static ColorGRB leds[NUM_LEDS];

// HAL API를 이용한 SK6812 PWM 초기화 (Assertion/IRQ 충돌 및 리셋 완벽 방지)
void sk6812_init(void)
{
    // 1. P1.08 핀을 GPIO 출력으로 설정
    nrf_gpio_cfg_output(SK6812_PIN);
    nrf_gpio_pin_clear(SK6812_PIN);

    // 2. PWM20 비활성화 후 재설정
    nrf_pwm_disable(NRF_PWM20);

    // 3. P1.08 핀을 PWM20 채널 0에 직접 할당
    uint32_t pins[NRF_PWM_CHANNEL_COUNT] = {
        SK6812_PIN,
        NRF_PWM_PIN_NOT_CONNECTED,
        NRF_PWM_PIN_NOT_CONNECTED,
        NRF_PWM_PIN_NOT_CONNECTED
    };
    nrf_pwm_pins_set(NRF_PWM20, pins);

    // 4. PWM 기본 동작 설정 (16MHz, Up Counter, Top = 20)
    nrf_pwm_configure(NRF_PWM20, NRF_PWM_CLK_16MHz, NRF_PWM_MODE_UP, PWM_COUNTER_TOP);

    // 5. 로드 모드 및 스텝 모드 설정
    nrf_pwm_decoder_set(NRF_PWM20, NRF_PWM_LOAD_COMMON, NRF_PWM_STEP_AUTO);

    // 6. PWM20 하드웨어 활성화
    nrf_pwm_enable(NRF_PWM20);

    printk("SK6812 PWM20 HAL Initialized successfully!\n");
}

 

 

 

SK6812  NexPixel LED를 제어 하는 함수를 작성한다.

// LED 색상 지정
void sk6812_set_pixel(uint8_t index, uint8_t r, uint8_t g, uint8_t b)
{
    if (index < NUM_LEDS) {
        leds[index].r = r;
        leds[index].g = g;
        leds[index].b = b;
    }
}

// HAL EasyDMA 직접 트리거 전송
void sk6812_show(void)
{
    uint32_t bit_idx = 0;

    for (int i = 0; i < NUM_LEDS; i++) {
        // SK6812 GRB 순서 전송 (MSB First)
        uint32_t color = ((uint32_t)leds[i].g << 16) |
                         ((uint32_t)leds[i].r << 8)  |
                         ((uint32_t)leds[i].b);

        for (int b = 23; b >= 0; b--) {
            bool is_one = (color >> b) & 0x01;
            // 0x8000 비트 설정으로 Duty Cycle 제어
            pwm_seq_values[bit_idx++] = (is_one ? T1H_TICKS : T0H_TICKS) | 0x8000;
        }
    }

    // Latch/Reset 구간 (Low 유지)
    for (int i = 0; i < RESET_BITS; i++) {
        pwm_seq_values[bit_idx++] = 0x8000;
    }

    // HAL 시퀀스 버퍼 등록 및 재생
    nrf_pwm_seq_ptr_set(NRF_PWM20, 0, pwm_seq_values);
    nrf_pwm_seq_cnt_set(NRF_PWM20, 0, TOTAL_DATA_BITS + RESET_BITS);
    nrf_pwm_seq_refresh_set(NRF_PWM20, 0, 0);
    nrf_pwm_seq_end_delay_set(NRF_PWM20, 0, 0);

    // EasyDMA 전송 시작 (하드웨어 자동 전송)
    nrf_pwm_task_trigger(NRF_PWM20, NRF_PWM_TASK_SEQSTART0);
}

// 모든 LED 끄기
void sk6812_clear(void)
{
    for (int i = 0; i < NUM_LEDS; i++) {
        sk6812_set_pixel(i, 0, 0, 0);
    }
    sk6812_show();
}

 

 

 

SK6812 LED 테스트 코드

int main(void)
{
    int err;
    int64_t previous_time = k_uptime_get();
    int flag[3] = {0,};

    int sw1_prev_state = 1;
    int sw2_prev_state = 1;    

    printk("nRF54 PWM Test - SK6812 NeoPixel Control\n");

    /* 1. 하드웨어 GPIO 초기화 */
    InitGpio();
    init_gpio_hal();

    Led1Off();
    Led2On();

    nrf_gpio_pin_write(PIN_OUT_P1_12, 1);
    nrf_gpio_pin_write(PIN_OUT_P1_14, 1);

    printk("start\n");

    sk6812_init();


    while (1) {

        int sw1_val = GetSwtich1();
        int sw2_val = GetSwtich2();

        if (sw1_val == 1) {
            if (!flag[0]) {
                flag[0] = 1;
                
                printk("1\n");

                sk6812_set_pixel(0, 255, 0, 0);   // Red
                sk6812_set_pixel(1, 0, 255, 0);   // Green
                sk6812_set_pixel(2, 0, 0, 255);   // Blue
                sk6812_set_pixel(3, 255, 255, 0); // Yellow
                sk6812_show();
                
            }
        } else {
            flag[0] = 0;
        }
        
      
        int64_t current_time = k_uptime_get();        
        if (current_time - previous_time >= 500) {
            previous_time = current_time;

            printk("current_time= %lld\n", current_time);
            
            Led2Toggle();
        }        
        k_sleep(K_MSEC(10));
    }

 

 

nRF54 PWM Test - SK6812 테스트 결과