From e18221c2ca3bb71a192f5e027fc1245046cf539f Mon Sep 17 00:00:00 2001 From: whowechina Date: Tue, 8 Jul 2025 23:21:36 +0800 Subject: [PATCH] More types of markers in Ju Matrix --- Matrix/firmware/src/CMakeLists.txt | 4 +- Matrix/firmware/src/hub75.c | 45 +++- Matrix/firmware/src/hub75.h | 59 +++-- Matrix/firmware/src/main.c | 33 +-- Matrix/firmware/src/matrix.c | 354 +++++++++++++++++++++++++++++ Matrix/firmware/src/matrix.h | 7 + 6 files changed, 449 insertions(+), 53 deletions(-) create mode 100644 Matrix/firmware/src/matrix.c create mode 100644 Matrix/firmware/src/matrix.h diff --git a/Matrix/firmware/src/CMakeLists.txt b/Matrix/firmware/src/CMakeLists.txt index d3cd519..2734637 100644 --- a/Matrix/firmware/src/CMakeLists.txt +++ b/Matrix/firmware/src/CMakeLists.txt @@ -5,8 +5,8 @@ add_compile_options(-Wall -Werror -Wfatal-errors -O3) function(make_firmware board board_def) add_executable(${board} - main.c button.c hub75.c save.c config.c cli.c commands.c hid.c - usb_descriptors.c) + main.c button.c hub75.c matrix.c save.c config.c + cli.c commands.c hid.c usb_descriptors.c) target_compile_definitions(${board} PUBLIC ${board_def}) pico_enable_stdio_usb(${board} 1) pico_enable_stdio_uart(${board} 0) diff --git a/Matrix/firmware/src/hub75.c b/Matrix/firmware/src/hub75.c index 2573f5a..873a14a 100644 --- a/Matrix/firmware/src/hub75.c +++ b/Matrix/firmware/src/hub75.c @@ -60,13 +60,37 @@ struct { .row = 0, .bit = 0, - .brightness = 6, + .brightness = 20, .clk_polarity = 1, .stb_polarity = 1, .oe_polarity = 0 }; +uint32_t hub75_hsv2rgb(uint8_t h, uint8_t s, uint8_t v) +{ + // Convert HSV to RGB + if (s == 0) { + return hub75_argb(0, v, v, v); // Grayscale + } + + uint8_t region = h / 43; + uint8_t remainder = (h - (region * 43)) * 6; + + uint8_t p = (v * (255 - s)) >> 8; + uint8_t q = (v * (255 - ((s * remainder) >> 8))) >> 8; + uint8_t t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; + + switch (region) { + case 0: return hub75_argb(0, v, t, p); + case 1: return hub75_argb(0, q, v, p); + case 2: return hub75_argb(0, p, v, t); + case 3: return hub75_argb(0, p, q, v); + case 4: return hub75_argb(0, t, p, v); + default: return hub75_argb(0, v, p, q); + } +} + static void fm6126a_write_register(uint16_t value, uint8_t position) { gpio_put(HUB75_CLK_PIN, !ctx.clk_polarity); @@ -123,12 +147,6 @@ void hub75_init(bool fm6126, bool inverted_stb) static void dma_complete() { - static uint64_t next_frame = 0; - - if (ctx.dma_complete) { - ctx.dma_complete(ctx.row, ctx.bit); - } - if (dma_channel_get_irq0_status(ctx.dma_channel)) { dma_channel_acknowledge_irq0(ctx.dma_channel); @@ -152,13 +170,14 @@ static void dma_complete() ctx.bit++; if (ctx.bit == BIT_DEPTH) { ctx.bit = 0; - sleep_until(next_frame); - next_frame += 8334; } } - hub75_data_rgb888_set_shift(ctx.pio, ctx.sm_data, ctx.data_prog, ctx.bit); + if (ctx.dma_complete) { + ctx.dma_complete(ctx.row, ctx.bit); + } + hub75_data_rgb888_set_shift(ctx.pio, ctx.sm_data, ctx.data_prog, ctx.bit); dma_channel_set_trans_count(ctx.dma_channel, WIDTH * 2, false); dma_channel_set_read_addr(ctx.dma_channel, &back_buffer[ctx.row], true); } @@ -221,3 +240,9 @@ void hub75_update() } } } + + +void hub75_clear() +{ + memset(canvas, 0, sizeof(canvas)); +} diff --git a/Matrix/firmware/src/hub75.h b/Matrix/firmware/src/hub75.h index ab1a053..d703e39 100644 --- a/Matrix/firmware/src/hub75.h +++ b/Matrix/firmware/src/hub75.h @@ -31,36 +31,67 @@ const static uint16_t GAMMA_10BIT[256] = { extern uint32_t canvas[64][64]; -void hub75_init(bool fm6126, bool invert_stb); +void hub75_init(bool fm6126, bool inverted_stb); void hub75_update(); -typedef void (*hub75_dma_complete_cb)(uint row, uint bit); +typedef void (*hub75_dma_complete_cb)(uint16_t row, uint16_t bit); void hub75_start(hub75_dma_complete_cb dma_complete); void hub75_stop(); uint16_t hub75_width(); uint16_t hub75_height(); -static inline void hub75_clear() +void hub75_clear(); + +static inline uint32_t hub75_argb(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { - memset(canvas, 0, sizeof(canvas)); + return (a << 24) | (r << 16) | (g << 8) | b; } static inline uint32_t hub75_rgb(uint8_t r, uint8_t g, uint8_t b) { - return (r << 16) | (g << 8) | b; + return hub75_argb(0xff, r, g, b); } -// no memory protecting, so use with care -static inline void hub75_pixel(int x, int y, uint32_t rgb888) +static inline void hub75_pixel(int x, int y, uint32_t rgb) { - uint32_t r = (rgb888 >> 16) & 0xff; - uint32_t g = (rgb888 >> 8) & 0xff; - uint32_t b = rgb888 & 0xff; - r = GAMMA_10BIT[r]; - g = GAMMA_10BIT[g]; - b = GAMMA_10BIT[b]; - canvas[y][x] = b << 20 | g << 10 | r; + uint32_t r = GAMMA_10BIT[(rgb >> 16) & 0xff]; + uint32_t g = GAMMA_10BIT[(rgb >> 8) & 0xff]; + uint32_t b = GAMMA_10BIT[rgb & 0xff]; + canvas[y][x] = (b << 20) | (g << 10) | r; } +uint32_t hub75_hsv2rgb(uint8_t h, uint8_t s, uint8_t v); + +static inline uint32_t hub75_alpha(uint8_t alpha, uint32_t rgb) +{ + return (alpha << 24) | (rgb & 0x00ffffff); +} + +static inline void hub75_blend(int x, int y, uint32_t argb) +{ + uint32_t r1 = GAMMA_10BIT[(argb >> 16) & 0xff]; + uint32_t g1 = GAMMA_10BIT[(argb >> 8) & 0xff]; + uint32_t b1 = GAMMA_10BIT[argb & 0xff]; + + uint32_t alpha = (argb >> 24) & 0xff; + + if (alpha == 0) { + return; + } else if (alpha == 255) { + canvas[y][x] = (b1 << 20) | (g1 << 10) | r1; + return; + } + + uint32_t r0 = canvas[y][x] & 0x3ff; + uint32_t g0 = (canvas[y][x] >> 10) & 0x3ff; + uint32_t b0 = (canvas[y][x] >> 20) & 0x3ff; + + uint32_t r_mix = (r1 * alpha + r0 * (255 - alpha)) >> 8; + uint32_t g_mix = (g1 * alpha + g0 * (255 - alpha)) >> 8; + uint32_t b_mix = (b1 * alpha + b0 * (255 - alpha)) >> 8; + canvas[y][x] = (b_mix << 20) | (g_mix << 10) | r_mix; +} + + #endif diff --git a/Matrix/firmware/src/main.c b/Matrix/firmware/src/main.c index fc85afa..ec2f3a9 100644 --- a/Matrix/firmware/src/main.c +++ b/Matrix/firmware/src/main.c @@ -17,6 +17,7 @@ #include "hardware/sync.h" #include "hardware/structs/ioqspi.h" #include "hardware/structs/sio.h" +#include "hardware/clocks.h" #include "tusb.h" #include "usb_descriptors.h" @@ -30,29 +31,7 @@ #include "cli.h" #include "commands.h" #include "hid.h" -#include "hub75.h" - -static void draw() -{ - for (int y = 31; y < 33; y++) { - for (int x = 0; x < 64; x++) { - int r = x * 4 + 10; - int g = y * 4 + 10; - int b = x * 4; - if (r > 255) r = 255; - if (g > 255) g = 255; - if (b > 255) b = 255; - hub75_pixel(x, y, hub75_rgb(r, g, b)); - } - } -} - -static void dma_complete(uint row, uint bit) -{ - if (row == 0 && bit == 0) { - cli_fps_count(2); - } -} +#include "matrix.h" static mutex_t core1_io_lock; static void core1_loop() @@ -60,8 +39,7 @@ static void core1_loop() while (1) { if (mutex_try_enter(&core1_io_lock, NULL)) { mutex_exit(&core1_io_lock); - draw(); - hub75_update(); + matrix_update(); } cli_fps_count(1); } @@ -126,6 +104,8 @@ void init() update_check(); + set_sys_clock_khz(200000, false); + tusb_init(); stdio_init_all(); @@ -136,9 +116,8 @@ void init() button_init(); - hub75_init(true, false); cli_fps_label(2, "LED"); - hub75_start(dma_complete); + matrix_init(); cli_init("ju_matrix>", "\n << Ju Matrix >>\n" " https://github.com/whowechina\n\n"); diff --git a/Matrix/firmware/src/matrix.c b/Matrix/firmware/src/matrix.c new file mode 100644 index 0000000..434b242 --- /dev/null +++ b/Matrix/firmware/src/matrix.c @@ -0,0 +1,354 @@ +#include +#include +#include + +#include "pico/time.h" + +#include "cli.h" +#include "matrix.h" +#include "hub75.h" + +static void dma_complete(uint16_t row, uint16_t bit) +{ + if (row == 0 && bit == 0) { + cli_fps_count(2); + } +} + +void matrix_init() +{ + hub75_init(true, false); + hub75_start(dma_complete); +} + +void draw_button(uint8_t x, uint8_t y, uint32_t border, uint32_t solid) +{ + int col = x * 17; + int row = y * 17; + for (int i = 0; i < 13; i++) { + hub75_pixel(col + i, row, border); + hub75_pixel(col + i, row + 12, border); + hub75_pixel(col, row + i, border); + hub75_pixel(col + 12, row + i, border); + } + + for (int i = 1; i < 12; i++) { + for (int j = 1; j < 12; j++) { + hub75_pixel(col + i, row + j, solid); + } + } +} + +static void draw_door(uint8_t x, uint8_t y, uint32_t progress, bool arrow) +{ + const uint32_t solid = hub75_rgb(0, 140, 140); + int col = x * 17; + int row = y * 17; + for (int i = 0; i < 13; i++) { + int len = arrow ? abs(i - 6) + 1 : 0; + int bottom = len + 11; + + int prog = progress * 255 * 12 / 1000; + + int split = prog / 255; + int extra = prog % 255; + for (int j = 0; j < 13; j++) { + uint32_t color = 0; + if (j < len) { + color = solid; + } else if (j > bottom - split) { + color = solid; + } + hub75_pixel(col + i, row + j, color); + } + if (bottom - split < 13) { + hub75_blend(col + i, row + bottom - split, hub75_alpha(extra, solid)); + } + } +} + +static void draw_sector_fast(uint8_t x, uint8_t y, uint32_t progress) +{ + const uint32_t solid = hub75_rgb(255, 100, 50); + int col = x * 17; + int row = y * 17; + + int center = 6; + + // 进度转换为角度 (0-360度,用整数避免浮点运算) + int angle_max = (progress * 360) / 1000; + + for (int i = 0; i < 13; i++) { + for (int j = 0; j < 13; j++) { + int dx = i - center; + int dy = j - center; + + // 计算角度(用整数近似,从12点方向开始顺时针) + int angle; + if (dx == 0 && dy < 0) angle = 0; // 正北 + else if (dx > 0 && dy < 0) angle = 45; // 东北 + else if (dx > 0 && dy == 0) angle = 90; // 正东 + else if (dx > 0 && dy > 0) angle = 135; // 东南 + else if (dx == 0 && dy > 0) angle = 180; // 正南 + else if (dx < 0 && dy > 0) angle = 225; // 西南 + else if (dx < 0 && dy == 0) angle = 270; // 正西 + else if (dx < 0 && dy < 0) angle = 315; // 西北 + else angle = 0; // 中心点 + + // 更精细的角度计算(可选) + if (dx != 0 || dy != 0) { + // 简化的角度计算,用查表法或线性插值 + if (abs(dx) > abs(dy)) { + // 主要在东西方向 + if (dx > 0) { + angle = 90 + (dy * 45) / abs(dx); + } else { + angle = 270 - (dy * 45) / abs(dx); + } + } else { + // 主要在南北方向 + if (dy < 0) { + angle = 0 + (dx * 45) / abs(dy); + } else { + angle = 180 - (dx * 45) / abs(dy); + } + } + if (angle < 0) angle += 360; + } + + bool in_sector = (angle <= angle_max) || (angle_max >= 360); + + // 边缘抗锯齿 + if (!in_sector && angle_max < 360) { + int angle_diff = angle - angle_max; + if (angle_diff < 10 && angle_diff > 0) { // 10度的渐变区域 + uint8_t alpha = 255 * (10 - angle_diff) / 10; + hub75_blend(col + i, row + j, hub75_alpha(alpha, solid)); + } + } else if (in_sector) { + hub75_pixel(col + i, row + j, solid); + } + } + } +} + +static void draw_closing_door_smooth(uint8_t x, uint8_t y, uint32_t progress, bool invert) +{ + const uint32_t solid = hub75_rgb(100, 255, 100); + int col = x * 17; + int row = y * 17; + + // 精确的关门进度 (0-6.5的浮点数效果,用整数模拟) + int close_distance_x10 = (progress * 65) / 1000; // 0-65 (代表0-6.5) + int full_close = close_distance_x10 / 10; + int partial_close = close_distance_x10 % 10; + + for (int i = 0; i < 13; i++) { + for (int j = 0; j < 13; j++) { + // 计算到中心或边缘的距离 + int distance = invert ? + ((j <= 6) ? j : (12 - j)) : // 距离边缘 + abs(j - 6); // 距离中心 + + // 直接计算并绘制 + if (distance < full_close) { + hub75_pixel(col + i, row + j, solid); + } else if (distance == full_close && full_close <= 6) { + uint8_t alpha = (partial_close * 255) / 10; + hub75_blend(col + i, row + j, hub75_alpha(alpha, solid)); + } + } + } +} + +// 简化的整数三角函数表 (256个值,0-360度) +static const int sin_table[] = { + 0, 4, 9, 13, 18, 22, 27, 31, 36, 40, 44, 49, 53, 58, 62, 66, 71, 75, 79, 83, + 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 131, 135, 139, 143, 146, 150, 153, 157, 160, + 164, 167, 171, 174, 177, 181, 184, 187, 190, 193, 196, 199, 202, 205, 208, 211, 213, 216, 219, 221, + 224, 226, 229, 231, 233, 235, 238, 240, 242, 244, 246, 247, 249, 251, 252, 254, 255, 257, 258, 259, + 260, 262, 263, 264, 265, 266, 267, 267, 268, 269, 269, 270 +}; + +// 获取sin值 (输入0-360度,输出-270到270的定点数) +static int get_sin(int deg) { + deg = deg % 360; + if (deg < 0) deg += 360; + + if (deg <= 90) return sin_table[deg]; + else if (deg <= 180) return sin_table[180 - deg]; + else if (deg <= 270) return -sin_table[deg - 180]; + else return -sin_table[360 - deg]; +} + +// 获取cos值 (cos(x) = sin(x + 90)) +static int get_cos(int deg) { + return get_sin(deg + 90); +} + +// 旋转函数:将canvas中(x,y)位置的13x13格子按指定角度旋转 +void rotate_13x13(uint32_t canvas[][64], int x, int y, int deg) { + // 临时缓冲区存储旋转后的结果 + uint32_t temp[13][13]; + + // 清空临时缓冲区 + for (int i = 0; i < 13; i++) { + for (int j = 0; j < 13; j++) { + temp[i][j] = 0; + } + } + + // 获取旋转矩阵的整数值 (放大270倍) + int cos_deg = get_cos(deg); + int sin_deg = get_sin(deg); + + // 中心点 (6, 6) + int center = 6; + + // 对每个输出像素进行反向变换采样 + for (int out_i = 0; out_i < 13; out_i++) { + for (int out_j = 0; out_j < 13; out_j++) { + // 输出像素相对于中心的坐标 + int out_x = (out_i - center) * 270; + int out_y = (out_j - center) * 270; + + // 反向旋转得到源坐标 (使用-deg) + int src_x = (out_x * cos_deg + out_y * sin_deg) / 270 + center * 270; + int src_y = (-out_x * sin_deg + out_y * cos_deg) / 270 + center * 270; + + // 转换为像素坐标 (270倍放大) + int src_i = src_x / 270; + int src_j = src_y / 270; + + // 检查边界 + if (src_i >= 0 && src_i < 12 && src_j >= 0 && src_j < 12) { + // 计算小数部分用于双线性插值 + int frac_x = src_x % 270; + int frac_y = src_y % 270; + + // 获取四个相邻像素的颜色值 (从canvas中读取) + uint32_t c00 = canvas[x + src_i][y + src_j]; + uint32_t c10 = canvas[x + src_i + 1][y + src_j]; + uint32_t c01 = canvas[x + src_i][y + src_j + 1]; + uint32_t c11 = canvas[x + src_i + 1][y + src_j + 1]; + + // 双线性插值计算最终颜色 + // 简化版本:如果有任何相邻像素有颜色,就使用最亮的那个 + uint32_t result_color = 0; + if (c00 || c10 || c01 || c11) { + // 选择最亮的颜色作为结果 + result_color = c00; + if ((c10 & 0x3FF3FF3FF) > (result_color & 0x3FF3FF3FF)) result_color = c10; + if ((c01 & 0x3FF3FF3FF) > (result_color & 0x3FF3FF3FF)) result_color = c01; + if ((c11 & 0x3FF3FF3FF) > (result_color & 0x3FF3FF3FF)) result_color = c11; + + // 根据距离调整亮度来实现抗锯齿 + int weight = 270 - ((frac_x + frac_y) / 2); + if (weight < 270) { + uint8_t alpha = (weight * 255) / 270; + result_color = hub75_alpha(alpha, result_color); + } + } + + temp[out_i][out_j] = result_color; + } + } + } + + // 将旋转结果写回canvas + for (int i = 0; i < 13; i++) { + for (int j = 0; j < 13; j++) { + canvas[x + i][y + j] = temp[i][j]; + } + } +} + +static void rotate_90(int x, int y, int times) +{ + times = times % 4; + + if (times == 0) { + return; + } + + int col = x * 17; + int row = y * 17; + + uint32_t temp[13][13]; + for (int i = 0; i < 13; i++) { + for (int j = 0; j < 13; j++) { + temp[i][j] = canvas[col + i][row + j]; + } + } + + for (int i = 0; i < 13; i++) { + for (int j = 0; j < 13; j++) { + int src_i; + int src_j; + + if (times == 1) { + src_i = 12 - j; + src_j = i; + } else if (times == 2) { + src_i = 12 - i; + src_j = 12 - j; + } else { + src_i = j; + src_j = 12 - i; + } + + canvas[col + i][row + j] = temp[src_i][src_j]; + } + } +} + +static void draw() +{ + int phase = (time_us_64() / 1000) % 1001; + for (int i = 0; i < 16; i++) { + int col = i % 4; + int row = i / 4; + int progress = (phase + (i * 1000 / 15)) % 1100; + if (progress > 1000) { + progress = 1000; + } + int choice = i % 6; + if (choice == 0) { + draw_sector_fast(col, row, progress); + } else if (choice == 1) { + draw_door(col, row, progress, false); + } else if (choice == 2) { + draw_door(col, row, progress, true); + } else if (choice == 3) { + // 正常关门效果 - 上下 + draw_closing_door_smooth(col, row, progress, false); + } else if (choice == 4) { + // 反转关门效果 + draw_closing_door_smooth(col, row, progress, true); + } else { + // 扇形展开(重复使用) + draw_sector_fast(col, row, progress); + } + + rotate_90(col, row, col + row); + } +} + +void overlay() +{ + int off = abs((time_us_32() / 100000 % 80) - 40); + uint32_t color = 0x20000000 | hub75_hsv2rgb(time_us_32() / 20000, 200, 255); + for (int i = 0; i < 23; i++) { + for (int j = 0; j < 23; j++) { + hub75_blend(off + i, off + j, color); + } + } +} + +void matrix_update() +{ + hub75_clear(); + draw(); + overlay(); + hub75_update(); +} diff --git a/Matrix/firmware/src/matrix.h b/Matrix/firmware/src/matrix.h new file mode 100644 index 0000000..9c5b71f --- /dev/null +++ b/Matrix/firmware/src/matrix.h @@ -0,0 +1,7 @@ +#ifndef MATRIX_H +#define MATRIX_H + +void matrix_init(); +void matrix_update(); + +#endif \ No newline at end of file