Files
matrix-screen-controller/核桃派软件源代码/tests/test_walnutpi_native_driver.py
T

137 lines
4.6 KiB
Python

from __future__ import annotations
import pytest
from app.display.matrix_driver import MatrixDriverOptions, validate_driver_options
from app.display.native_model import (
HEIGHT,
HARDWARE_MAPPING,
HUB75_PINS,
PI_BANK_OFFSET,
PI_DATA_OFFSET,
PIO_BASE,
PWM_BASE,
PWM_BITS,
REFRESH_RATE_LIMITS,
RESERVED_PI_BITS,
SCAN_ROWS,
WIDTH,
address_mask,
bitplane_index,
build_bitplanes,
map_channel,
oe_ticks_for_plane,
)
def test_walnutpi_pin_mapping_matches_fixed_hub75_contract():
assert [pin.signal for pin in HUB75_PINS] == [
"R1", "G1", "B1", "R2", "G2", "B2", "A", "B", "C", "D", "E", "CLK", "OE", "LAT",
]
assert [pin.physical for pin in HUB75_PINS] == [29, 31, 33, 35, 37, 8, 10, 28, 27, 15, 16, 38, 40, 36]
assert [pin.bit for pin in HUB75_PINS] == [0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15]
assert not ({pin.bit for pin in HUB75_PINS} & RESERVED_PI_BITS)
assert RESERVED_PI_BITS == {7, 8, 16}
assert HUB75_PINS[-2].gpio == "PI14/PWM4"
assert HUB75_PINS[-1].gpio == "PI15"
assert HARDWARE_MAPPING == "walnutpi-pi-bank-pwm-oe-v2"
def test_h618_pi_register_addresses_are_fixed():
assert PIO_BASE == 0x0300B000
assert PWM_BASE == 0x0300A000
assert PI_BANK_OFFSET == 0x120
assert PI_DATA_OFFSET == 0x10
assert PIO_BASE + PI_BANK_OFFSET + PI_DATA_OFFSET == 0x0300B130
def test_row_address_uses_abcde_bits_in_pi_bank():
assert address_mask(0) == 0
assert address_mask(1) == 1 << 6
assert address_mask(2) == 1 << 9
assert address_mask(16) == 1 << 12
assert address_mask(31) == sum(1 << bit for bit in (6, 9, 10, 11, 12))
with pytest.raises(ValueError):
address_mask(32)
def test_bitplanes_pair_top_and_bottom_rows_without_address_or_control_bits():
frame = bytearray(WIDTH * HEIGHT * 3)
frame[0:3] = bytes((0xFF, 0x80, 0x40))
bottom = SCAN_ROWS * WIDTH * 3
frame[bottom:bottom + 3] = bytes((0x20, 0x10, 0x08))
planes = build_bitplanes(bytes(frame))
expected = [0] * PWM_BITS
values = (0xFF, 0x80, 0x40, 0x20, 0x10, 0x08)
for component, value in enumerate(values):
mapped = map_channel(value, 100) >> 4
for plane in range(PWM_BITS):
if mapped & (1 << plane):
expected[plane] |= 1 << component
assert [planes[bitplane_index(0, plane, 0)] for plane in range(PWM_BITS)] == expected
assert all(word & ~0x3F == 0 for word in planes)
def test_white_frame_sets_all_six_color_bits_in_all_seven_planes():
planes = build_bitplanes(bytes([0xFF]) * (WIDTH * HEIGHT * 3))
assert len(planes) == SCAN_ROWS * PWM_BITS * WIDTH
assert set(planes) == {0x3F}
def test_bitplane_model_rejects_wrong_frame_size_and_coordinates():
with pytest.raises(ValueError, match="64x64"):
build_bitplanes(b"bad")
with pytest.raises(ValueError, match="out of range"):
bitplane_index(0, PWM_BITS, 0)
@pytest.mark.parametrize("refresh_rate", sorted(REFRESH_RATE_LIMITS))
def test_native_driver_accepts_only_documented_refresh_rates(refresh_rate):
validate_driver_options(MatrixDriverOptions(limit_refresh_rate_hz=refresh_rate))
def test_native_driver_rejects_geometry_pwm_mapping_and_unknown_rate_changes():
for options in (
MatrixDriverOptions(rows=32),
MatrixDriverOptions(cols=128),
MatrixDriverOptions(scan_rows=16),
MatrixDriverOptions(row_address_bits=4),
MatrixDriverOptions(pwm_bits=6),
MatrixDriverOptions(hardware_mapping="other"),
MatrixDriverOptions(limit_refresh_rate_hz=70),
):
with pytest.raises(ValueError):
validate_driver_options(options)
def test_cie_brightness_is_monotonic_and_pwm4_weights_are_fixed():
mapped = [map_channel(255, brightness) for brightness in range(1, 101)]
assert mapped == sorted(mapped)
assert mapped[-1] == 2047
for brightness in range(1, 101):
for plane in range(PWM_BITS):
ticks, remainder = oe_ticks_for_plane(10_250_000, brightness, plane, 999)
assert ticks == 1 << (plane + 4)
assert remainder == 0
def test_brightness_changes_bitplanes_instead_of_temporally_dithering_oe():
white = bytes([0xFF]) * (WIDTH * HEIGHT * 3)
low = build_bitplanes(white, brightness=20)
high = build_bitplanes(white, brightness=100)
assert low != high
assert set(high) == {0x3F}
@pytest.mark.parametrize("arguments", [
(0, 1, 0, 0),
(5_000_000, 0, 0, 0),
(5_000_000, 1, PWM_BITS, 0),
(5_000_000, 1, 0, -1),
])
def test_pwm4_fixed_point_rejects_invalid_inputs(arguments):
with pytest.raises(ValueError):
oe_ticks_for_plane(*arguments)