更具github上的QMK_Firware进行移植,理解其代码,选择需要的部分,移植到更为经济的芯片上。通过分析代码,学习到了很多
此博客的目的是移植ws2812部分
包含的代码为四个函数,分别为初始化(无),刷新数据,写入一个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"文件
这里有引用了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;
关于灯效参数的结构体
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;
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;
}
}