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

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,195 @@
#define _POSIX_C_SOURCE 200809L
#include "h618_hub75.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
enum {
PI_R1 = 1u << 0,
PI_G1 = 1u << 1,
PI_B1 = 1u << 2,
PI_R2 = 1u << 3,
PI_G2 = 1u << 4,
PI_B2 = 1u << 5,
};
static size_t word_index(unsigned row, unsigned plane, unsigned column) {
return ((size_t)row * HUB75_PWM_BITS + plane) * HUB75_WIDTH + column;
}
int main(void) {
assert(strcmp(hub75_hardware_mapping(), HUB75_HARDWARE_MAPPING) == 0);
assert(HUB75_INTERNAL_PWM_BITS == 11);
assert(HUB75_PWM_PLANE_OFFSET == 4);
assert(HUB75_SCANS_PER_FRAME == 1);
assert(hub75_scans_per_frame_for_brightness(1) == 1);
assert(hub75_scans_per_frame_for_brightness(20) == 1);
assert(hub75_scans_per_frame_for_brightness(100) == 1);
assert(hub75_scans_per_frame_for_brightness(0) == -1);
assert(hub75_scans_per_frame_for_brightness(101) == -1);
uint16_t previous_mapped = 0;
for (int brightness = 1; brightness <= 100; ++brightness) {
uint16_t mapped = hub75_map_channel_for_brightness(255, brightness);
assert(mapped >= previous_mapped);
previous_mapped = mapped;
for (unsigned plane = 0; plane < HUB75_PWM_BITS; ++plane) {
uint64_t remainder = 123u;
assert(hub75_oe_ticks_for_plane(
10250000, brightness, plane, &remainder
) == (1u << (plane + HUB75_PWM_PLANE_OFFSET)));
assert(remainder == 0);
}
}
assert(previous_mapped == 2047u);
assert(hub75_map_channel_for_brightness(255, 0) == UINT16_MAX);
uint64_t invalid_remainder = 0;
assert(hub75_oe_ticks_for_plane(5000000, 0, 0, &invalid_remainder) == UINT32_MAX);
assert(hub75_oe_ticks_for_plane(5000000, 1, HUB75_PWM_BITS, &invalid_remainder) == UINT32_MAX);
uint64_t full_scale_remainder = 0;
assert(hub75_oe_ticks_for_plane(
10250000, 100, HUB75_PWM_BITS - 1, &full_scale_remainder
) == 1024u);
uint8_t *frame = calloc(1, HUB75_FRAME_BYTES);
uint16_t *planes = calloc(HUB75_BITPLANE_WORDS, sizeof(*planes));
assert(frame != NULL);
assert(planes != NULL);
/* Compare extracted planes with the Raspberry-compatible 11-bit map. */
frame[0] = 0xff;
frame[1] = 0x80;
frame[2] = 0x40;
size_t bottom = (size_t)HUB75_SCAN_ROWS * HUB75_WIDTH * 3u;
frame[bottom] = 0x20;
frame[bottom + 1] = 0x10;
frame[bottom + 2] = 0x08;
assert(hub75_build_bitplanes(
frame,
HUB75_FRAME_BYTES,
planes,
HUB75_BITPLANE_WORDS
) == 0);
const uint8_t values[] = {0xff, 0x80, 0x40, 0x20, 0x10, 0x08};
const uint16_t bits[] = {PI_R1, PI_G1, PI_B1, PI_R2, PI_G2, PI_B2};
for (unsigned plane = 0; plane < HUB75_PWM_BITS; ++plane) {
uint16_t expected = 0;
for (unsigned component = 0; component < 6; ++component) {
uint16_t mapped = hub75_map_channel_for_brightness(values[component], 100);
if ((mapped >> (plane + HUB75_PWM_PLANE_OFFSET)) & 1u) {
expected |= bits[component];
}
}
assert(planes[word_index(0, plane, 0)] == expected);
}
memset(frame, 0xff, HUB75_FRAME_BYTES);
assert(hub75_build_bitplanes(
frame,
HUB75_FRAME_BYTES,
planes,
HUB75_BITPLANE_WORDS
) == 0);
for (size_t index = 0; index < HUB75_BITPLANE_WORDS; ++index) {
assert(planes[index] == (PI_R1 | PI_G1 | PI_B1 | PI_R2 | PI_G2 | PI_B2));
}
/* Brightness changes bitplane data rather than OE pulse duration. */
memset(frame, 0, HUB75_FRAME_BYTES);
frame[0] = 0xff;
frame[1] = 0x4f;
frame[2] = 0xa3;
uint16_t low_planes[HUB75_BITPLANE_WORDS];
assert(hub75_build_bitplanes_for_brightness(
frame, HUB75_FRAME_BYTES, 100, planes, HUB75_BITPLANE_WORDS
) == 0);
assert(hub75_build_bitplanes_for_brightness(
frame, HUB75_FRAME_BYTES, 20, low_planes, HUB75_BITPLANE_WORDS
) == 0);
assert(memcmp(low_planes, planes, sizeof(low_planes)) != 0);
assert(hub75_build_bitplanes(frame, 1, planes, HUB75_BITPLANE_WORDS) == -1);
assert(hub75_build_bitplanes(frame, HUB75_FRAME_BYTES, planes, 1) == -1);
hub75_config config = {
.brightness = 40,
.refresh_rate_hz = 100,
.cpu_affinity = 0,
.realtime_priority = 1,
.use_dev_mem = 0,
};
char error[512] = {0};
hub75_context *context = hub75_create(&config, error, sizeof(error));
assert(context != NULL);
hub75_fake_registers registers;
assert(hub75_get_fake_registers(context, &registers) == 0);
assert(((registers.mux1 >> ((14u - 8u) * 4u)) & 0xfu) == 0x5u);
assert(((registers.mux1 >> ((15u - 8u) * 4u)) & 0xfu) == 0x1u);
assert((registers.pwm_pccr45 & (1u << 4)) != 0);
assert((registers.pwm_pccr45 & ((0xfu) | (1u << 5) | (0x3u << 7))) == 0);
assert((registers.pwm_per & (1u << 4)) != 0);
assert((registers.pwm_pcr4 & 0x1ffu) == 2u);
assert((registers.pwm_pcr4 & (1u << 9)) != 0);
assert(registers.pwm_ppr4 == (65535u << 16));
hub75_stats stats;
assert(hub75_get_stats(context, &stats) == 0);
assert(strcmp(stats.oe_timing_backend, HUB75_OE_TIMING_BACKEND) == 0);
assert(stats.oe_pulse_faults == 0);
memset(frame, 0xff, HUB75_FRAME_BYTES);
assert(hub75_submit_rgb(context, frame, HUB75_FRAME_BYTES) == 0);
assert(hub75_set_brightness(context, 100) == 0);
assert(hub75_start(context) == 0);
struct timespec pulse_test_delay = {.tv_sec = 0, .tv_nsec = 100000000};
nanosleep(&pulse_test_delay, NULL);
assert(hub75_get_stats(context, &stats) == 0);
assert(stats.max_programmed_oe_ns == 128000u);
assert(stats.oe_pulse_faults == 0);
assert(hub75_get_fake_registers(context, &registers) == 0);
assert((registers.pwm_pcr4 & (1u << 9)) != 0);
assert((registers.pwm_pcr4 & (1u << 10)) == 0);
assert((registers.pwm_per & (1u << 4)) != 0);
assert((registers.pwm_ppr4 & 0xffffu) != 0);
assert((registers.pwm_ppr4 >> 16) + 1u ==
(registers.pwm_ppr4 & 0xffffu));
hub75_destroy(context);
config.fake_pwm4_stuck = 1;
context = hub75_create(&config, error, sizeof(error));
assert(context != NULL);
assert(hub75_start(context) == 0);
struct timespec stuck_test_delay = {.tv_sec = 0, .tv_nsec = 100000000};
nanosleep(&stuck_test_delay, NULL);
assert(hub75_get_stats(context, &stats) == 0);
assert(stats.oe_pulse_faults == 1);
assert(stats.oe_forced_blanks == 1);
assert(strstr(stats.oe_timing_error, "start bit did not clear") != NULL);
assert(hub75_get_fake_registers(context, &registers) == 0);
assert((registers.pwm_per & (1u << 4)) == 0);
assert(((registers.mux1 >> ((14u - 8u) * 4u)) & 0xfu) == 0x1u);
hub75_destroy(context);
config.fake_pwm4_stuck = 0;
config.fake_pwm5_mode = 1;
context = hub75_create(&config, error, sizeof(error));
assert(context != NULL);
assert(hub75_get_fake_registers(context, &registers) == 0);
assert((registers.pwm_per & (1u << 5)) != 0);
assert((registers.pwm_pccr45 & (1u << 6)) != 0);
assert(registers.pwm_pcr5 == 0x100u);
assert(registers.pwm_ppr5 == 0x000b0006u);
hub75_destroy(context);
config.fake_pwm5_mode = 2;
context = hub75_create(&config, error, sizeof(error));
assert(context == NULL);
assert(strstr(error, "incompatible shared PWM4/5 clock") != NULL);
assert(hub75_force_safeoff(0, error, sizeof(error)) == 0);
free(planes);
free(frame);
puts("native_bitplane_tests=PASS");
return 0;
}