aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/neopixel.c58
1 files changed, 38 insertions, 20 deletions
diff --git a/src/neopixel.c b/src/neopixel.c
index 6a5460c3..b799b516 100644
--- a/src/neopixel.c
+++ b/src/neopixel.c
@@ -28,12 +28,22 @@ command_config_neopixel(uint32_t *args)
DECL_COMMAND(command_config_neopixel, "config_neopixel oid=%c pin=%u");
#endif
-uint32_t
+static uint32_t
timer_from_ns(uint32_t ns)
{
return timer_from_us(ns * 1000) / 1000000;
}
+// The WS2812 uses a bit-banging protocol where each bit is
+// transmitted as a gpio high pulse of variable length. The various
+// specs are unclear, but it is believed the timing requirements are:
+// - A zero bit must have a high pulse less than 500ns.
+// - A one bit must have a high pulse longer than 650ns.
+// - The total bit time (gpio high to following gpio high) must not
+// exceed ~5000ns. The average bit time must be at least 1250ns.
+// - The specs generally indicate a minimum high pulse and low pulse
+// of 200ns, but the actual requirement might be smaller.
+
static int
send_data(struct neopixel_s *n, uint8_t *data, uint_fast8_t data_len)
{
@@ -51,47 +61,55 @@ send_data(struct neopixel_s *n, uint8_t *data, uint_fast8_t data_len)
uint_fast8_t bits = 8;
while (bits--) {
// Calculate pulse duration
- uint32_t on, off;
- if (byte & 0x80) {
- on = timer_from_ns(700 - 150);
- off = timer_from_ns(600 - 150);
- } else {
- on = timer_from_ns(350 - 150);
- off = timer_from_ns(800 - 150);
- }
- byte <<= 1;
+ uint32_t on;
+ if (byte & 0x80)
+ on = timer_from_ns(650);
+ else
+ on = timer_from_ns(200);
// Set output high
do {
irq_poll();
cur = timer_read_time();
} while (timer_is_before(cur, min_wait_time));
+ uint32_t off_end_time = cur;
gpio_out_write(pin, 1);
uint32_t on_start_time = timer_read_time();
- if (timer_is_before(max_wait_time, on_start_time))
- goto fail;
min_wait_time = on_start_time + on;
- max_wait_time = cur + on + timer_from_ns(300);
// Set output low
do {
irq_poll();
cur = timer_read_time();
} while (timer_is_before(cur, min_wait_time));
+ uint32_t on_end_time = cur;
gpio_out_write(pin, 0);
- uint32_t off_start_time = timer_read_time();
- if (timer_is_before(max_wait_time, off_start_time))
+ uint32_t off_start_time = cur = timer_read_time();
+ min_wait_time = on_start_time + timer_from_ns(1250);
+
+ // Check for faults
+ if (byte & 0x80) {
+ // Make sure off for at least 200ns
+ uint32_t min_off = off_start_time + timer_from_ns(200);
+ if (timer_is_before(min_wait_time, min_off))
+ min_wait_time = min_off;
+ } else {
+ // Make sure short on duration was no more than 500ns
+ uint32_t max_off = off_end_time + timer_from_ns(500);
+ if (timer_is_before(max_off, off_start_time))
+ goto fail;
+ }
+ byte <<= 1;
+ if (timer_is_before(max_wait_time, on_start_time))
goto fail;
- min_wait_time = off_start_time + off;
- max_wait_time = cur + off + timer_from_ns(300);
+ max_wait_time = on_end_time + timer_from_us(4);
}
}
- n->last_req_time = timer_read_time();
+ n->last_req_time = cur;
return 0;
fail:
// A hardware irq messed up the transmission - report a failure
- gpio_out_write(pin, 0);
- n->last_req_time = timer_read_time();
+ n->last_req_time = cur;
return -1;
}