More types of markers in Ju Matrix

This commit is contained in:
whowechina
2025-07-08 23:21:36 +08:00
parent 1411c7f432
commit e18221c2ca
6 changed files with 449 additions and 53 deletions
+2 -2
View File
@@ -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)
+35 -10
View File
@@ -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));
}
+45 -14
View File
@@ -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
+6 -27
View File
@@ -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");
+354
View File
@@ -0,0 +1,354 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#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();
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef MATRIX_H
#define MATRIX_H
void matrix_init();
void matrix_update();
#endif