Skip to content

Commit 4a05e93

Browse files
committed
protomatter: validate pins to give better error message
The numbered error from the underlying library is not helpful for beginning users
1 parent e4c6b24 commit 4a05e93

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

shared-bindings/_protomatter/Protomatter.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,65 @@ STATIC void claim_and_never_reset_pins(mp_obj_t seq) {
7474
}
7575
}
7676

77+
STATIC void preflight_pins_or_throw(uint8_t clock_pin, uint8_t *rgb_pins, uint8_t rgb_pin_count, bool allow_inefficient) {
78+
uint32_t port = clock_pin / 32;
79+
uint32_t bit_mask = 1 << (clock_pin % 32);
80+
81+
for (uint8_t i = 0; i < rgb_pin_count; i++) {
82+
uint32_t pin_port = rgb_pins[i] / 32;
83+
84+
if (pin_port != port) {
85+
mp_raise_ValueError_varg(
86+
translate("rgb_pins[%d] is not on the same port as clock"), i);
87+
}
88+
89+
uint32_t pin_mask = 1 << (rgb_pins[i] % 32);
90+
if (pin_mask & bit_mask) {
91+
mp_raise_ValueError_varg(
92+
translate("rgb_pins[%d] duplicates another pin assignment"), i);
93+
}
94+
95+
bit_mask |= pin_mask;
96+
}
97+
98+
if (allow_inefficient) {
99+
return;
100+
}
101+
102+
uint8_t byte_mask = 0;
103+
if (bit_mask & 0x000000FF) byte_mask |= 0b0001;
104+
if (bit_mask & 0x0000FF00) byte_mask |= 0b0010;
105+
if (bit_mask & 0x00FF0000) byte_mask |= 0b0100;
106+
if (bit_mask & 0xFF000000) byte_mask |= 0b1000;
107+
108+
uint8_t bytes_per_element = 0xff;
109+
uint8_t ideal_bytes_per_element = (rgb_pin_count + 7) / 8;
110+
111+
switch(byte_mask) {
112+
case 0b0001:
113+
case 0b0010:
114+
case 0b0100:
115+
case 0b1000:
116+
bytes_per_element = 1;
117+
break;
118+
119+
case 0b0011:
120+
case 0b1100:
121+
bytes_per_element = 2;
122+
break;
123+
124+
default:
125+
bytes_per_element = 4;
126+
break;
127+
}
128+
129+
if (bytes_per_element != ideal_bytes_per_element) {
130+
mp_raise_ValueError_varg(
131+
translate("Pinout uses %d bytes per element, which consumes more than the ideal %d bytes. If this cannot be avoided, pass allow_inefficient=True to the constructor"),
132+
bytes_per_element, ideal_bytes_per_element);
133+
}
134+
}
135+
77136
//| :class:`~_protomatter.Protomatter` displays an in-memory framebuffer to an LED matrix.
78137
//|
79138
//| .. class:: Protomatter(width, bit_depth, rgb_pins, addr_pins, clock_pin, latch_pin, oe_pin, *, doublebuffer=True, framebuffer=None)
@@ -141,6 +200,8 @@ STATIC mp_obj_t protomatter_protomatter_make_new(const mp_obj_type_t *type, size
141200
validate_pins(MP_QSTR_rgb_pins, rgb_pins, MP_ARRAY_SIZE(self->rgb_pins), args[ARG_rgb_list].u_obj, &rgb_count);
142201
validate_pins(MP_QSTR_addr_pins, addr_pins, MP_ARRAY_SIZE(self->addr_pins), args[ARG_addr_list].u_obj, &addr_count);
143202

203+
preflight_pins_or_throw(clock_pin, rgb_pins, rgb_count, true);
204+
144205
mp_obj_t framebuffer = args[ARG_framebuffer].u_obj;
145206
if (framebuffer == mp_const_none) {
146207
int width = args[ARG_width].u_int;

0 commit comments

Comments
 (0)