当前位置: 首页 > 工具软件 > QMK > 使用案例 >

qmk移植-WS2812灯效移植

墨承泽
2023-12-01

qmk移植-WS2812灯效移植

更具github上的QMK_Firware进行移植,理解其代码,选择需要的部分,移植到更为经济的芯片上。通过分析代码,学习到了很多
此博客的目的是移植ws2812部分

  1. rgb_matrix_drivers.c代码,定义了不同rgb芯片的驱动代码和结构体,其中针对2812的代码有:

包含的代码为四个函数,分别为初始化(无),刷新数据,写入一个rgb,写入全部rgb(同一个颜色)。并将它们作为一个结构体,用于通用处理,方便其他函数的调用(学习到了!)

#elif defined(WS2812)
#    if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_CUSTOM_DRIVER)
#        pragma message "Cannot use RGBLIGHT and RGB Matrix using WS2812 at the same time."
#        pragma message "You need to use a custom driver, or re-implement the WS2812 driver to use a different configuration."
#    endif

// LED color buffer
LED_TYPE rgb_matrix_ws2812_array[DRIVER_LED_TOTAL];

static void init(void) {}

static void flush(void) {
    // Assumes use of RGB_DI_PIN
    ws2812_setleds(rgb_matrix_ws2812_array, DRIVER_LED_TOTAL);
}

// Set an led in the buffer to a color
static inline void setled(int i, uint8_t r, uint8_t g, uint8_t b) {
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    const uint8_t k_rgb_matrix_split[2] = RGB_MATRIX_SPLIT;
    if (!is_keyboard_left()) {
        if (i >= k_rgb_matrix_split[0]) {
            i -= k_rgb_matrix_split[0];
        } else {
            return;
        }
    } else if (i >= k_rgb_matrix_split[0]) {
        return;
    }
#    endif

    rgb_matrix_ws2812_array[i].r = r;
    rgb_matrix_ws2812_array[i].g = g;
    rgb_matrix_ws2812_array[i].b = b;
#    ifdef RGBW
    convert_rgb_to_rgbw(&rgb_matrix_ws2812_array[i]);
#    endif
}

static void setled_all(uint8_t r, uint8_t g, uint8_t b) {
    for (int i = 0; i < sizeof(rgb_matrix_ws2812_array) / sizeof(rgb_matrix_ws2812_array[0]); i++) {
        setled(i, r, g, b);
    }
}

const rgb_matrix_driver_t rgb_matrix_driver = {
    .init          = init,
    .flush         = flush,
    .set_color     = setled,
    .set_color_all = setled_all,
};
#endif

代码中定义了一些结构体,主要为LED_TYPE rgb_matrix_ws2812_array[DRIVER_LED_TOTAL],通过include,我们继续分析#include "rgb_matrix.h"文件

  1. rgb_matrix.h代码,声明了全局定义、函数和结构体,有用部分:

这里有引用了include “ws2812.h”,可能上述的刷新数据实在该部分中,但是也可以自己写,我自己采用的是SPI+DMA的方式,计划使用PWM+DMA的方式进行

#include "rgb_matrix_types.h"
#include "color.h"
#include "quantum.h"

#elif defined(WS2812)
#    include "ws2812.h"
#endif

上述的函数结构体来了,很好的学习案例,结构体中均为指针,定义了函数的名称和输入变量,返回值。

typedef struct {
    /* Perform any initialisation required for the other driver functions to work. */
    void (*init)(void);
    /* Set the colour of a single LED in the buffer. */
    void (*set_color)(int index, uint8_t r, uint8_t g, uint8_t b);
    /* Set the colour of all LEDS on the keyboard in the buffer. */
    void (*set_color_all)(uint8_t r, uint8_t g, uint8_t b);
    /* Flush any buffered changes to the hardware. */
    void (*flush)(void);
} rgb_matrix_driver_t;

还包括一些外部变量引用,如下

extern const rgb_matrix_driver_t rgb_matrix_driver;

extern rgb_config_t rgb_matrix_config;

extern uint32_t     g_rgb_timer;
extern led_config_t g_led_config;
  1. 进一步查看rgb_matrix_types.h,这部分包含了定义和结构体,但是还不明了有啥用

关于灯效参数的结构体

typedef uint8_t led_flags_t;

typedef struct PACKED {
    uint8_t     iter;
    led_flags_t flags;
    bool        init;
} effect_params_t;

typedef struct PACKED {
    uint8_t x;
    uint8_t y;
} led_point_t;

关于LED标志位的定义,比如属于按键灯04,指示灯08等,同样可以参考手册

#define LED_FLAG_ALL 0xFF
#define LED_FLAG_NONE 0x00
#define LED_FLAG_MODIFIER 0x01
#define LED_FLAG_UNDERGLOW 0x02
#define LED_FLAG_KEYLIGHT 0x04
#define LED_FLAG_INDICATOR 0x08

#define NO_LED 255

将上述的,组成一个通用LED结构体,包含三个部分,分别为 Key Matrix to LED Index、 LED Index to Physical Position和LED Index to Flag

typedef struct PACKED {
    uint8_t     matrix_co[MATRIX_ROWS][MATRIX_COLS];
    led_point_t point[DRIVER_LED_TOTAL];
    uint8_t     flags[DRIVER_LED_TOTAL];
} led_config_t;

举例参考为

led_config_t g_led_config = { {
  // Key Matrix to LED Index
  {   5, NO_LED, NO_LED,   0 },
  { NO_LED, NO_LED, NO_LED, NO_LED },
  {   4, NO_LED, NO_LED,   1 },
  {   3, NO_LED, NO_LED,   2 }
}, {
  // LED Index to Physical Position
  { 188,  16 }, { 187,  48 }, { 149,  64 }, { 112,  64 }, {  37,  48 }, {  38,  16 }
}, {
  // LED Index to Flag
  1, 4, 4, 4, 4, 1
} };

接下来定义rgb_config_t结构体,使用的union共用体,区别在于:结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。即union中的成员列表其实是一个,调用不同的数据,得到是同一个数据的不同表示。

typedef union {
    uint32_t raw;
    struct PACKED {
        uint8_t     enable : 2;
        uint8_t     mode : 6;
        HSV         hsv;
        uint8_t     speed; // EECONFIG needs to be increased to support this
        led_flags_t flags;
    };
} rgb_config_t;
  1. 最后是rgb_matrix.c,定义了一些比较重要的函数

hsv转rgb的函数,但这个是weak,即声明了但是没有定义,所以得找找定义在哪里

__attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) {
    return hsv_to_rgb(hsv);
}

全局定义

// globals
rgb_config_t rgb_matrix_config; // TODO: would like to prefix this with g_ for global consistancy, do this in another pr
uint32_t     g_rgb_timer;
#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS
uint8_t g_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};
#endif // RGB_MATRIX_FRAMEBUFFER_EFFECTS
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
last_hit_t g_last_hit_tracker;
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
// 初始化结构体
void eeconfig_update_rgb_matrix_default(void) {
    dprintf("eeconfig_update_rgb_matrix_default\n");
    rgb_matrix_config.enable = 1;
    rgb_matrix_config.mode   = RGB_MATRIX_STARTUP_MODE;
    rgb_matrix_config.hsv    = (HSV){RGB_MATRIX_STARTUP_HUE, RGB_MATRIX_STARTUP_SAT, RGB_MATRIX_STARTUP_VAL};
    rgb_matrix_config.speed  = RGB_MATRIX_STARTUP_SPD;
    rgb_matrix_config.flags  = LED_FLAG_ALL;
    eeconfig_flush_rgb_matrix(true);
}

函数结构体的几个函数,又再次定义了一下

void rgb_matrix_update_pwm_buffers(void) {
    rgb_matrix_driver.flush();
}

void rgb_matrix_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
    rgb_matrix_driver.set_color(index, red, green, blue);
}

void rgb_matrix_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++)
        rgb_matrix_set_color(i, red, green, blue);
#else
    rgb_matrix_driver.set_color_all(red, green, blue);
#endif
}

从矩阵led的行和列,得到对应的led编号函数

uint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i) {
    uint8_t led_count = rgb_matrix_map_row_column_to_led_kb(row, column, led_i);
    uint8_t led_index = g_led_config.matrix_co[row][column];
    if (led_index != NO_LED) {
        led_i[led_count] = led_index;
        led_count++;
    }
    return led_count;
}

一个rgb任务函数的组成部分函数

void rgb_matrix_task(void) {
    rgb_task_timers();

    // Ideally we would also stop sending zeros to the LED driver PWM buffers
    // while suspended and just do a software shutdown. This is a cheap hack for now.
    bool suspend_backlight = suspend_state ||
#if RGB_DISABLE_TIMEOUT > 0
                             (rgb_anykey_timer > (uint32_t)RGB_DISABLE_TIMEOUT) ||
#endif // RGB_DISABLE_TIMEOUT > 0
                             false;

    uint8_t effect = suspend_backlight || !rgb_matrix_config.enable ? 0 : rgb_matrix_config.mode;

    switch (rgb_task_state) {
        case STARTING:
            rgb_task_start();
            break;
        case RENDERING:
            rgb_task_render(effect);
            if (effect) {
                rgb_matrix_indicators();
                rgb_matrix_indicators_advanced(&rgb_effect_params);
            }
            break;
        case FLUSHING:
            rgb_task_flush(effect);
            break;
        case SYNCING:
            rgb_task_sync();
            break;
    }
}
 类似资料: