初始化奇妙小屏幕控制器项目

This commit is contained in:
2026-09-08 22:56:52 +08:00
commit 8d368de3b5
491 changed files with 67678 additions and 0 deletions
@@ -0,0 +1,188 @@
#define _POSIX_C_SOURCE 200809L
#include "h618_hub75.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static double monotonic_seconds(void) {
struct timespec value;
clock_gettime(CLOCK_MONOTONIC, &value);
return (double)value.tv_sec + (double)value.tv_nsec / 1000000000.0;
}
static int integer_argument(const char *value, const char *name) {
char *end = NULL;
long parsed = strtol(value, &end, 10);
if (value[0] == '\0' || end == NULL || *end != '\0' || parsed < 0 || parsed > 1000000) {
fprintf(stderr, "invalid %s: %s\n", name, value);
exit(2);
}
return (int)parsed;
}
static int hex_nibble(char value) {
if (value >= '0' && value <= '9') return value - '0';
if (value >= 'a' && value <= 'f') return value - 'a' + 10;
if (value >= 'A' && value <= 'F') return value - 'A' + 10;
return -1;
}
static void color_argument(const char *value, uint8_t output[3]) {
if (strlen(value) != 6) {
fprintf(stderr, "invalid color: %s (expected RRGGBB)\n", value);
exit(2);
}
for (unsigned channel = 0; channel < 3; ++channel) {
int high = hex_nibble(value[channel * 2]);
int low = hex_nibble(value[channel * 2 + 1]);
if (high < 0 || low < 0) {
fprintf(stderr, "invalid color: %s (expected RRGGBB)\n", value);
exit(2);
}
output[channel] = (uint8_t)((high << 4) | low);
}
}
int main(int argc, char **argv) {
int duration = 10;
int refresh_rate = 100;
int brightness = 40;
int use_dev_mem = 0;
int use_static_color = 0;
uint8_t static_color[3] = {0};
for (int index = 1; index < argc; ++index) {
if (strcmp(argv[index], "--duration") == 0 && index + 1 < argc) {
duration = integer_argument(argv[++index], "duration");
} else if (strcmp(argv[index], "--refresh-rate") == 0 && index + 1 < argc) {
refresh_rate = integer_argument(argv[++index], "refresh rate");
} else if (strcmp(argv[index], "--brightness") == 0 && index + 1 < argc) {
brightness = integer_argument(argv[++index], "brightness");
} else if (strcmp(argv[index], "--dev-mem") == 0) {
use_dev_mem = 1;
} else if (strcmp(argv[index], "--color") == 0 && index + 1 < argc) {
color_argument(argv[++index], static_color);
use_static_color = 1;
} else {
fprintf(stderr, "usage: %s [--duration N] [--refresh-rate N] [--brightness N] [--color RRGGBB] [--dev-mem]\n", argv[0]);
return 2;
}
}
if (duration < 1) {
fputs("duration must be at least one second\n", stderr);
return 2;
}
hub75_config config = {
.brightness = brightness,
.refresh_rate_hz = refresh_rate,
.cpu_affinity = 3,
.realtime_priority = 50,
.use_dev_mem = use_dev_mem,
};
char error[512] = "";
hub75_context *context = hub75_create(&config, error, sizeof(error));
if (context == NULL) {
fprintf(stderr, "create failed: %s\n", error);
return 1;
}
if (hub75_start(context) != 0) {
fputs("start failed\n", stderr);
hub75_destroy(context);
return 1;
}
uint8_t frame[HUB75_FRAME_BYTES];
if (use_static_color) {
for (unsigned pixel = 0; pixel < HUB75_WIDTH * HUB75_HEIGHT; ++pixel) {
memcpy(frame + pixel * 3u, static_color, sizeof(static_color));
}
if (hub75_submit_rgb(context, frame, sizeof(frame)) != 0) {
fputs("initial static submit failed\n", stderr);
hub75_destroy(context);
return 1;
}
}
double started = monotonic_seconds();
unsigned submitted = 0;
while (monotonic_seconds() - started < duration) {
if (!use_static_color) {
for (unsigned pixel = 0; pixel < HUB75_WIDTH * HUB75_HEIGHT; ++pixel) {
frame[pixel * 3] = (uint8_t)(pixel + submitted);
frame[pixel * 3 + 1] = (uint8_t)(pixel * 3u + submitted);
frame[pixel * 3 + 2] = (uint8_t)(pixel * 7u + submitted);
}
if (hub75_submit_rgb(context, frame, sizeof(frame)) != 0) {
fputs("submit failed\n", stderr);
hub75_destroy(context);
return 1;
}
++submitted;
}
struct timespec delay = {.tv_sec = 0, .tv_nsec = 20000000};
nanosleep(&delay, NULL);
}
hub75_stats stats;
if (hub75_get_stats(context, &stats) != 0) {
fputs("stats failed\n", stderr);
hub75_destroy(context);
return 1;
}
hub75_destroy(context);
int expected_scans = hub75_scans_per_frame_for_brightness(brightness);
double miss_rate = stats.completed_frames == 0 ? 100.0 :
(double)stats.deadline_misses * 100.0 / (double)stats.completed_frames;
printf(
"{\"mode\":\"%s\",\"configured_hz\":%d,\"actual_hz\":%.3f,"
"\"panel_scan_hz\":%.3f,\"completed_frames\":%llu,"
"\"completed_scans\":%llu,\"scans_per_frame\":%d,"
"\"deadline_misses\":%llu,"
"\"deadline_miss_percent\":%.6f,\"buffer_swaps\":%llu,"
"\"submitted_frames\":%llu,\"oe_timing_backend\":\"%s\","
"\"oe_pulse_faults\":%llu,\"oe_forced_blanks\":%llu,"
"\"max_programmed_oe_ns\":%llu,\"oe_timing_error\":\"%s\","
"\"cpu_affinity_active\":%s,"
"\"realtime_priority_active\":%s,\"memory_locked\":%s,"
"\"governor\":\"%s\",\"affinity_error\":\"%s\","
"\"realtime_error\":\"%s\",\"memory_lock_error\":\"%s\","
"\"last_error\":\"%s\"}\n",
use_dev_mem ? "dev-mem" : "fake-registers",
refresh_rate,
stats.actual_refresh_rate_hz,
stats.panel_scan_rate_hz,
(unsigned long long)stats.completed_frames,
(unsigned long long)stats.completed_scans,
stats.scans_per_frame,
(unsigned long long)stats.deadline_misses,
miss_rate,
(unsigned long long)stats.buffer_swaps,
(unsigned long long)stats.submitted_frames,
stats.oe_timing_backend,
(unsigned long long)stats.oe_pulse_faults,
(unsigned long long)stats.oe_forced_blanks,
(unsigned long long)stats.max_programmed_oe_ns,
stats.oe_timing_error,
stats.cpu_affinity_active ? "true" : "false",
stats.realtime_priority_active ? "true" : "false",
stats.memory_locked ? "true" : "false",
stats.governor,
stats.affinity_error,
stats.realtime_error,
stats.memory_lock_error,
stats.last_error
);
if (
stats.completed_frames == 0 ||
stats.actual_refresh_rate_hz <= 0.0 ||
stats.panel_scan_rate_hz <= 0.0 ||
stats.scans_per_frame != expected_scans ||
stats.completed_scans != stats.completed_frames * (uint64_t)expected_scans ||
stats.oe_pulse_faults != 0 ||
strcmp(stats.oe_timing_backend, HUB75_OE_TIMING_BACKEND) != 0
) return 1;
return 0;
}