D .gitmodules => .gitmodules +0 -3
@@ 1,3 0,0 @@
-[submodule "mzapo-sdl"]
- path = mzapo-sdl
- url = git@gitlab.fel.cvut.cz:bohacfr2/mzapo_sdl.git
D mzapo-sdl => mzapo-sdl +0 -1
@@ 1,1 0,0 @@
-Subproject commit fee42a20ea8c95e81865fddfb392851836a5b689
A mzapo-sdl/.gitignore => mzapo-sdl/.gitignore +6 -0
@@ 0,0 1,6 @@
+connect.gdb
+
+obj/
+bin/
+
+.cache/
A mzapo-sdl/Makefile => mzapo-sdl/Makefile +36 -0
@@ 0,0 1,36 @@
+SRC_DIR=./src
+OBJ_DIR=./obj
+BIN_DIR?=./bin
+INC_DIR=-I./include
+
+NAME=mzapo_sdl
+BINARY=$(BIN_DIR)/lib$(NAME).so
+
+SRC=$(wildcard $(SRC_DIR)/*.c)
+OBJ=$(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC))
+
+CFLAGS=$(INC_DIR) -Wall -Werror -g -O0
+CFLAGS+=$(shell sdl2-config --cflags)
+
+.PHONY: all clean
+
+all: $(BINARY)
+ cp img/mzapo.png $(BIN_DIR)
+
+$(BINARY): $(OBJ) | $(BIN_DIR)
+ $(CC) -shared $(LDFLAGS) $^ $(LDLIBS) -o $@
+
+$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
+ $(CC) $(CFLAGS) -c -fPIC $< -o $@
+
+$(BIN_DIR) $(OBJ_DIR):
+ echo $(BIN_DIR)
+ mkdir -p $@
+
+dep: depend
+
+clean:
+ $(RM) -rv $(BIN_DIR) $(OBJ_DIR)
+ $(RM) -rv .cache
+
+-include $(OBJ:.o=.d)
A mzapo-sdl/img/mzapo.png => mzapo-sdl/img/mzapo.png +0 -0
A mzapo-sdl/include/image_load.h => mzapo-sdl/include/image_load.h +20 -0
@@ 0,0 1,20 @@
+#ifndef __IMAGE_LOAD_H__
+#define __IMAGE_LOAD_H__
+
+#include <stdint.h>
+
+typedef struct {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+} __attribute__((__packed__)) buffer_pixel_t;
+
+typedef struct {
+ uint32_t width;
+ uint32_t height;
+ buffer_pixel_t *data;
+} image_t;
+
+image_t image_load_png(char *path);
+
+#endif // __IMAGE_LOAD_H__
A mzapo-sdl/include/mzapo_regs.h => mzapo-sdl/include/mzapo_regs.h +101 -0
@@ 0,0 1,101 @@
+/*******************************************************************
+ Simple program to check LCD functionality on MicroZed
+ based MZ_APO board designed by Petr Porazil at PiKRON
+
+ mzapo_regs.h - definition of the MZ_APO design registers
+
+ (C) Copyright 2017 by Pavel Pisa
+ e-mail: pisa@cmp.felk.cvut.cz
+ homepage: http://cmp.felk.cvut.cz/~pisa
+ company: http://www.pikron.com/
+ license: any combination of GPL, LGPL, MPL or BSD licenses
+
+ *******************************************************************/
+
+#ifndef MZAPO_REGS_H
+#define MZAPO_REGS_H
+
+/*
+ Complete description of the educational MZ_APO design registers
+ can be found at
+
+ https://cw.fel.cvut.cz/wiki/courses/b35apo/documentation/mz_apo/start
+
+ The peripherals VHDL sources can be found in the repository
+
+ http://rtime.felk.cvut.cz/gitweb/fpga/zynq/canbench-sw.git/tree/refs/heads/microzed_apo:/system/ip
+
+*/
+
+/* SPI connected knobs and LEDs registers and keyboard */
+
+#define SPILED_REG_BASE_PHYS 0x43c40000
+#define SPILED_REG_SIZE 0x00004000
+
+#define SPILED_REG_LED_LINE_o 0x004
+#define SPILED_REG_LED_RGB1_o 0x010
+#define SPILED_REG_LED_RGB2_o 0x014
+#define SPILED_REG_LED_KBDWR_DIRECT_o 0x018
+
+#define SPILED_REG_KBDRD_KNOBS_DIRECT_o 0x020
+#define SPILED_REG_KNOBS_8BIT_o 0x024
+
+/* Parallel LCD registers */
+
+#define PARLCD_REG_BASE_PHYS 0x43c00000
+#define PARLCD_REG_SIZE 0x00004000
+
+#define PARLCD_REG_CR_o 0x0000
+#define PARLCD_REG_CR_RESET_m 0x00000002
+#define PARLCD_REG_CMD_o 0x0008
+#define PARLCD_REG_DATA_o 0x000C
+
+/* RC model servos and optional PS2 peripheral */
+
+#define SERVOPS2_REG_BASE_PHYS 0x43c50000
+#define SERVOPS2_REG_SIZE 0x4000
+
+#define SERVOPS2_REG_CR_o 0x0000
+#define SERVOPS2_REG_PWMPER_o 0x000C
+#define SERVOPS2_REG_PWM1_o 0x0010
+#define SERVOPS2_REG_PWM2_o 0x0014
+#define SERVOPS2_REG_PWM3_o 0x0018
+#define SERVOPS2_REG_PWM4_o 0x001C
+
+/* Simple audio PWM output */
+
+#define AUDIOPWM_REG_BASE_PHYS 0x43c60000
+#define AUDIOPWM_REG_SIZE 0x4000
+
+#define AUDIOPWM_REG_CR_o 0x0000
+#define AUDIOPWM_REG_PWMPER_o 0x0008
+#define AUDIOPWM_REG_PWM_o 0x000C
+
+/* Optional DC Motor Simple Driver Peripherals for PSR Subject */
+
+#define DCSPDRV_REG_BASE_PHYS_0 0x43c20000
+#define DCSPDRV_REG_BASE_PHYS_1 0x43c30000
+#define DCSPDRV_REG_SIZE 0x4000
+
+#define DCSPDRV_REG_CR_o 0x0000
+#define DCSPDRV_REG_CR_PWM_A_DIRECT_m 0x00000010
+#define DCSPDRV_REG_CR_PWM_B_DIRECT_m 0x00000020
+#define DCSPDRV_REG_CR_PWM_ENABLE_m 0x00000040
+#define DCSPDRV_REG_CR_IRC_RESET_m 0x00000100
+
+#define DCSPDRV_REG_SR_o 0x0004
+#define DCSPDRV_REG_SR_IRC_A_MON_m 0x00000100
+#define DCSPDRV_REG_SR_IRC_B_MON_m 0x00000200
+#define DCSPDRV_REG_SR_IRC_IRQ_MON_m 0x00000400
+
+#define DCSPDRV_REG_PERIOD_o 0x0008
+#define DCSPDRV_REG_PERIOD_MASK_m 0x3fffffff
+
+#define DCSPDRV_REG_DUTY_o 0x000C
+#define DCSPDRV_REG_DUTY_MASK_m 0x3fffffff
+#define DCSPDRV_REG_DUTY_DIR_A_m 0x40000000
+#define DCSPDRV_REG_DUTY_DIR_B_m 0x80000000
+
+#define DCSPDRV_REG_IRC_o 0x0010
+
+#endif /*MZAPO_REGS_H*/
A mzapo-sdl/include/mzapo_sdl.h => mzapo-sdl/include/mzapo_sdl.h +48 -0
@@ 0,0 1,48 @@
+#ifndef __MZAPO_SDL_H__
+#define __MZAPO_SDL_H__
+
+#include <stdint.h>
+
+#define RGB_LEDS_COUNT 2
+
+typedef struct {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+} __attribute__((__packed__)) mzapo_sdl_rgb_led;
+
+typedef struct {
+ mzapo_sdl_rgb_led left;
+ uint8_t offset;
+ mzapo_sdl_rgb_led right;
+} __attribute__((__packed__)) mzapo_sdl_rgb_leds;
+
+typedef uint32_t mzapo_sdl_ledstrip;
+
+typedef void (*display_command_fn)(uint16_t cmd);
+typedef void (*display_data_fn)(uint16_t data);
+
+typedef struct {
+ display_command_fn cmd;
+ display_data_fn data;
+} __attribute__((__packed__)) mzapo_sdl_display;
+
+typedef struct {
+ uint32_t offset1;
+
+ mzapo_sdl_ledstrip ledstrip;
+ uint64_t offset2;
+ mzapo_sdl_rgb_leds leds;
+ uint8_t offset3;
+ uint32_t reg_kbdrd_direct;
+ uint64_t offset4;
+ uint32_t reg_knobs_direct;
+ uint32_t reg_knobs_8bit;
+} __attribute__((__packed__)) mzapo_sdl_knobs_keyboard_rgb;
+
+void mzapo_sdl_init();
+void mzapo_sdl_deinit();
+
+void *mzapo_sdl_map_phys(uint64_t region_base, uint64_t region_size);
+
+#endif // __MZAPO_SDL_H__
A mzapo-sdl/include/sdl.h => mzapo-sdl/include/sdl.h +21 -0
@@ 0,0 1,21 @@
+#ifndef __SDL_H__
+#define __SDL_H__
+
+#include <assert.h>
+
+#include <SDL.h>
+#include <SDL_image.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "SDL_surface.h"
+#include "SDL_video.h"
+
+
+int sdl_init(uint16_t w, uint16_t h);
+void sdl_close();
+
+void sdl_redraw(uint16_t w, uint16_t h, uint8_t *img);
+
+bool sdl_poll_event(SDL_Event *event);
+#endif // __SDL_H__
A mzapo-sdl/include/sdl_utils.h => mzapo-sdl/include/sdl_utils.h +48 -0
@@ 0,0 1,48 @@
+#ifndef __SDL_UTILS_H__
+#define __SDL_UTILS_H__
+
+#include "image_load.h"
+#include <stdint.h>
+
+#define DISPLAY_WIDTH 480
+#define DISPLAY_HEIGHT 320
+
+#define WINDOW_WIDTH 760
+#define WINDOW_HEIGHT 1000
+
+#define LCD_START_X 114
+#define LCD_START_Y 346
+
+#define RGB_LEFT_LED_START_X 136
+#define RGB_LEFT_LED_START_Y 817
+
+#define RGB_RIGHT_LED_START_X 587
+#define RGB_RIGHT_LED_START_Y 811
+
+#define RGB_LED_WIDTH 25
+#define RGB_LED_HEIGHT 25
+
+#define LEDSTRIP_START_X 103
+#define LEDSTRIP_START_Y 968
+
+#define LEDSTRIP_WIDTH 10
+#define LEDSTRIP_HEIGHT 10
+
+#define LEDSTRIP_STEP_X 16
+#define LEDSTRIP_STEP_Y -5.0/31.0
+
+extern buffer_pixel_t buffer[WINDOW_WIDTH * WINDOW_HEIGHT];
+
+void sdl_utils_init();
+void sdl_utils_deinit();
+
+void sdl_utils_render_buffer();
+
+void sdl_utils_clear_buffer();
+
+void sdl_utils_draw_image();
+void sdl_utils_draw_ledstrip(uint32_t values);
+void sdl_utils_draw_display(uint16_t *buffer);
+void sdl_utils_draw_rgb_leds(uint8_t r1, uint8_t g1, uint8_t b1, uint8_t r2, uint8_t g2, uint8_t b2);
+
+#endif // __SDL_UTILS_H__
A mzapo-sdl/src/image_load.c => mzapo-sdl/src/image_load.c +95 -0
@@ 0,0 1,95 @@
+#include "image_load.h"
+#include "sdl.h"
+#include <png.h>
+
+image_t image_load_png(char *path) {
+ FILE *infile = fopen(path, "r");
+ image_t empty = {0, 0, NULL};
+ if (infile == NULL) {
+ return empty;
+ }
+
+ png_structp png =
+ png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png == NULL) {
+ return empty;
+ }
+
+ png_infop info = png_create_info_struct(png);
+ if (info == NULL) {
+ return empty;
+ }
+
+ png_init_io(png, infile);
+ png_read_info(png, info);
+
+ uint32_t width = png_get_image_width(png, info);
+ uint32_t height = png_get_image_height(png, info);
+ png_byte color_type = png_get_color_type(png, info);
+ png_byte bit_depth = png_get_bit_depth(png, info);
+
+ if (bit_depth == 16) {
+ png_set_strip_16(png);
+ }
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(png);
+ }
+
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+ png_set_expand_gray_1_2_4_to_8(png);
+ }
+
+ if (png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png);
+
+ if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
+ }
+
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(png);
+ }
+
+ png_read_update_info(png, info);
+
+ png_bytep *row_pointers = malloc(sizeof(png_bytep) * height);
+ if (row_pointers == NULL) {
+ png_destroy_read_struct(&png, &info, NULL);
+ fclose(infile);
+ return empty;
+ }
+
+ for (int i = 0; i < height; i++) {
+ row_pointers[i] = malloc(png_get_rowbytes(png, info));
+ }
+
+ png_read_image(png, row_pointers);
+
+ fclose(infile);
+ png_destroy_read_struct(&png, &info, NULL);
+
+ buffer_pixel_t *pixels =
+ malloc(sizeof(buffer_pixel_t) * width * height);
+ if (pixels == NULL) {
+ png_destroy_read_struct(&png, &info, NULL);
+ free(row_pointers);
+ fclose(infile);
+ return empty;
+ }
+
+ for (int y = 0; y < height; y++) {
+ png_bytep row = row_pointers[y];
+ for (int x = 0; x < width; x++) {
+ png_bytep px = &(row[x * 4]);
+ pixels[y * width + x].r = px[0];
+ pixels[y * width + x].g = px[1];
+ pixels[y * width + x].b = px[2];
+ }
+ }
+
+ image_t image = {.width = width, .height = height, .data = pixels};
+ return image;
+}
A mzapo-sdl/src/mzapo_sdl.c => mzapo-sdl/src/mzapo_sdl.c +105 -0
@@ 0,0 1,105 @@
+#include "mzapo_sdl.h"
+#include "mzapo_regs.h"
+#include "sdl_utils.h"
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+static pthread_t sdl_thread;
+static bool running;
+
+static bool display_draw = false;
+static uint16_t display_x = 0, display_y = 0;
+
+static uint16_t display_buffer[DISPLAY_WIDTH * DISPLAY_HEIGHT];
+
+static void *mzapo_sdl_thread_init(void *state);
+
+static void mzapo_display_cmd(uint16_t cmd);
+static void mzapo_display_data(uint16_t data);
+
+mzapo_sdl_display display = {.cmd = mzapo_display_cmd, .data = mzapo_display_data};
+mzapo_sdl_knobs_keyboard_rgb sdl_knobs_keyboard_rgb = {
+ .reg_knobs_direct = 0, .reg_kbdrd_direct = 0,
+ .reg_knobs_8bit = 0, .ledstrip = 0 };
+
+void mzapo_sdl_init() {
+ running = true;
+
+ sdl_knobs_keyboard_rgb.leds.left.r = 0;
+ sdl_knobs_keyboard_rgb.leds.left.g = 0;
+ sdl_knobs_keyboard_rgb.leds.left.b = 0;
+
+ sdl_knobs_keyboard_rgb.leds.right.r = 0;
+ sdl_knobs_keyboard_rgb.leds.right.g = 0;
+ sdl_knobs_keyboard_rgb.leds.right.b = 0;
+
+ for (int i = 0; i < DISPLAY_HEIGHT * DISPLAY_WIDTH; i++) {
+ display_buffer[i] = 0;
+ }
+
+ pthread_create(&sdl_thread, NULL, mzapo_sdl_thread_init, NULL);
+}
+
+void mzapo_sdl_deinit() {
+ running = false;
+ pthread_join(sdl_thread, NULL);
+}
+
+void *mzapo_sdl_map_phys(uint64_t region_base, uint64_t region_size) {
+ switch (region_base) {
+ case PARLCD_REG_BASE_PHYS:
+ return &display;
+ case SPILED_REG_BASE_PHYS:
+ return &sdl_knobs_keyboard_rgb;
+ default:
+ return NULL;
+ }
+}
+
+static void *mzapo_sdl_thread_init(void *state) {
+ sdl_utils_init();
+ while (running) {
+ sdl_utils_clear_buffer();
+
+ sdl_utils_draw_image();
+
+ sdl_utils_draw_rgb_leds(sdl_knobs_keyboard_rgb.leds.left.r,
+ sdl_knobs_keyboard_rgb.leds.left.g,
+ sdl_knobs_keyboard_rgb.leds.left.b,
+ sdl_knobs_keyboard_rgb.leds.right.r,
+ sdl_knobs_keyboard_rgb.leds.right.g,
+ sdl_knobs_keyboard_rgb.leds.right.b);
+
+ sdl_utils_draw_ledstrip(sdl_knobs_keyboard_rgb.ledstrip);
+
+ sdl_utils_draw_display(display_buffer);
+
+ sdl_utils_render_buffer();
+ }
+
+ return NULL;
+}
+
+static void mzapo_display_cmd(uint16_t cmd) {
+ switch(cmd) {
+ case 0x2C:
+ display_x = 0;
+ display_y = 0;
+ display_draw = true;
+ break;
+ }
+}
+
+static void mzapo_display_data(uint16_t data) {
+ if (display_draw && display_y < DISPLAY_HEIGHT) {
+ display_buffer[display_y * DISPLAY_WIDTH + display_x] = data;
+
+ display_x ++;
+
+ if (display_x >= DISPLAY_WIDTH) {
+ display_x = 0;
+ display_y ++;
+ }
+ }
+}
A mzapo-sdl/src/sdl.c => mzapo-sdl/src/sdl.c +48 -0
@@ 0,0 1,48 @@
+#include "sdl.h"
+
+static SDL_Window *win = NULL;
+
+int sdl_init(uint16_t w, uint16_t h) {
+ int r = 0;
+ r = SDL_Init(SDL_INIT_VIDEO);
+ assert(win == NULL);
+ win = SDL_CreateWindow("MZAPO SDL", SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_SHOWN);
+ assert(win != NULL);
+ SDL_SetWindowTitle(win, "MZAPO SDL");
+ SDL_Surface *surface =
+ SDL_CreateRGBSurface(32, 32, 24, 32 * 3, 0xff, 0xff00, 0xff0000, 0x0000);
+ SDL_SetWindowIcon(win, surface);
+ SDL_FreeSurface(surface);
+
+ SDL_SetWindowResizable(win, SDL_FALSE);
+ return r;
+}
+
+void sdl_close() {
+ assert(win != NULL);
+ SDL_DestroyWindow(win);
+ SDL_Quit();
+}
+
+void sdl_redraw(uint16_t w, uint16_t h, uint8_t *img) {
+ assert(img && win);
+ SDL_Surface *scr = SDL_GetWindowSurface(win);
+
+ for (int y = 0; y < scr->h && y < h; ++y) {
+ for (int x = 0; x < scr->w && x < w; ++x) {
+ const int idx = (y * scr->w + x) * scr->format->BytesPerPixel;
+ Uint8 *px = (Uint8 *)scr->pixels + idx;
+ uint64_t position_in_img = (y * w + x) * 3;
+ *(px + scr->format->Rshift / 8) = *(img + position_in_img);
+ *(px + scr->format->Gshift / 8) = *(img + position_in_img + 1);
+ *(px + scr->format->Bshift / 8) = *(img + position_in_img + 2);
+ }
+ }
+ SDL_UpdateWindowSurface(win);
+ SDL_FreeSurface(scr);
+}
+
+bool sdl_poll_event(SDL_Event *event) {
+ return SDL_PollEvent(event);
+}
A mzapo-sdl/src/sdl_utils.c => mzapo-sdl/src/sdl_utils.c +104 -0
@@ 0,0 1,104 @@
+#include "sdl_utils.h"
+#include "image_load.h"
+#include "sdl.h"
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+
+#define IMG_PATH "./bin/mzapo.png"
+
+buffer_pixel_t buffer[WINDOW_WIDTH * WINDOW_HEIGHT];
+image_t image;
+
+#define DISPLAY_MAX_RED 31u
+#define DISPLAY_MAX_GREEN 63u
+#define DISPLAY_MAX_BLUE 31u
+
+void sdl_utils_init() {
+ sdl_init(WINDOW_WIDTH, WINDOW_HEIGHT);
+ printf(IMG_PATH);
+ image = image_load_png(IMG_PATH);
+
+ sdl_utils_clear_buffer();
+}
+
+void sdl_utils_deinit() {
+ sdl_close();
+}
+
+void sdl_utils_render_buffer() {
+ sdl_redraw(WINDOW_WIDTH, WINDOW_HEIGHT, (uint8_t*)buffer);
+}
+
+void sdl_utils_clear_buffer() {
+ buffer_pixel_t black = {0, 0, 0};
+ for (int i = 0; i < WINDOW_WIDTH * WINDOW_HEIGHT; i++) {
+ buffer[i] = black;
+ }
+}
+
+void sdl_utils_draw_image() {
+ for (uint16_t y = 0; y < WINDOW_HEIGHT && y < image.height; y++) {
+ for (uint16_t x = 0; x < WINDOW_WIDTH && x < image.width; x++) {
+ buffer[WINDOW_WIDTH * y + x] = image.data[image.width * y + x];
+ }
+ }
+}
+
+static void sdl_utils_rectangle(uint16_t bx, uint16_t by, uint16_t w, uint16_t h, buffer_pixel_t color) {
+ for (uint16_t iy = 0; iy < h; iy++) {
+ for (uint16_t ix = 0; ix < w; ix++) {
+ uint16_t x = ix + bx;
+ uint16_t y = iy + by;
+
+ buffer[y * WINDOW_WIDTH + x] = color;
+ }
+ }
+}
+
+void sdl_utils_draw_ledstrip(uint32_t values) {
+ float x = LEDSTRIP_START_X;
+ float y = LEDSTRIP_START_Y;
+
+ buffer_pixel_t color = {.r = 95, .g = 63, .b = 25};
+ for (int i = 0; i < 32; i++) {
+ bool on = (values >> (31 - i)) & 1;
+
+ if (on) {
+ sdl_utils_rectangle(x, y, LEDSTRIP_WIDTH, LEDSTRIP_HEIGHT, color);
+ }
+
+ x += LEDSTRIP_STEP_X;
+ y += LEDSTRIP_STEP_Y;
+ }
+}
+
+void sdl_utils_draw_display(uint16_t *data) {
+ for (int y = 0; y < DISPLAY_HEIGHT; y++) {
+ for (int x = 0; x < DISPLAY_WIDTH; x++) {
+ uint16_t pixel = data[y * DISPLAY_WIDTH + x];
+
+ uint8_t r = (pixel >> 11) & 0x1F;
+ uint8_t g = (pixel >> 5) & 0x3F;
+ uint8_t b = (pixel) & 0x1F;
+
+ buffer_pixel_t convert = {
+ .r = ((double)r / DISPLAY_MAX_RED) * 255,
+ .g = ((double)g / DISPLAY_MAX_GREEN) * 255,
+ .b = ((double)b / DISPLAY_MAX_BLUE) * 255,
+ };
+
+ buffer[(LCD_START_Y + y) * WINDOW_WIDTH + LCD_START_X + x] = convert;
+ }
+ }
+}
+
+void sdl_utils_draw_rgb_leds(uint8_t r1, uint8_t g1, uint8_t b1, uint8_t r2,
+ uint8_t g2, uint8_t b2) {
+ buffer_pixel_t left = {.r = r1, .g = g1, .b = b1};
+ buffer_pixel_t right = {.r = r2, .g = g2, .b = b2};
+ sdl_utils_rectangle(RGB_LEFT_LED_START_X, RGB_LEFT_LED_START_Y, RGB_LED_WIDTH,
+ RGB_LED_HEIGHT, left);
+ sdl_utils_rectangle(RGB_RIGHT_LED_START_X, RGB_RIGHT_LED_START_Y,
+ RGB_LED_WIDTH, RGB_LED_HEIGHT, right);
+}