neopixel: Update to more flexible bit-banging timing
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
46041f5411
commit
4e5ddff00b
|
@ -28,12 +28,22 @@ command_config_neopixel(uint32_t *args)
|
||||||
DECL_COMMAND(command_config_neopixel, "config_neopixel oid=%c pin=%u");
|
DECL_COMMAND(command_config_neopixel, "config_neopixel oid=%c pin=%u");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t
|
static uint32_t
|
||||||
timer_from_ns(uint32_t ns)
|
timer_from_ns(uint32_t ns)
|
||||||
{
|
{
|
||||||
return timer_from_us(ns * 1000) / 1000000;
|
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
|
static int
|
||||||
send_data(struct neopixel_s *n, uint8_t *data, uint_fast8_t data_len)
|
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;
|
uint_fast8_t bits = 8;
|
||||||
while (bits--) {
|
while (bits--) {
|
||||||
// Calculate pulse duration
|
// Calculate pulse duration
|
||||||
uint32_t on, off;
|
uint32_t on;
|
||||||
if (byte & 0x80) {
|
if (byte & 0x80)
|
||||||
on = timer_from_ns(700 - 150);
|
on = timer_from_ns(650);
|
||||||
off = timer_from_ns(600 - 150);
|
else
|
||||||
} else {
|
on = timer_from_ns(200);
|
||||||
on = timer_from_ns(350 - 150);
|
|
||||||
off = timer_from_ns(800 - 150);
|
|
||||||
}
|
|
||||||
byte <<= 1;
|
|
||||||
|
|
||||||
// Set output high
|
// Set output high
|
||||||
do {
|
do {
|
||||||
irq_poll();
|
irq_poll();
|
||||||
cur = timer_read_time();
|
cur = timer_read_time();
|
||||||
} while (timer_is_before(cur, min_wait_time));
|
} while (timer_is_before(cur, min_wait_time));
|
||||||
|
uint32_t off_end_time = cur;
|
||||||
gpio_out_write(pin, 1);
|
gpio_out_write(pin, 1);
|
||||||
uint32_t on_start_time = timer_read_time();
|
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;
|
min_wait_time = on_start_time + on;
|
||||||
max_wait_time = cur + on + timer_from_ns(300);
|
|
||||||
|
|
||||||
// Set output low
|
// Set output low
|
||||||
do {
|
do {
|
||||||
irq_poll();
|
irq_poll();
|
||||||
cur = timer_read_time();
|
cur = timer_read_time();
|
||||||
} while (timer_is_before(cur, min_wait_time));
|
} while (timer_is_before(cur, min_wait_time));
|
||||||
|
uint32_t on_end_time = cur;
|
||||||
gpio_out_write(pin, 0);
|
gpio_out_write(pin, 0);
|
||||||
uint32_t off_start_time = timer_read_time();
|
uint32_t off_start_time = cur = timer_read_time();
|
||||||
if (timer_is_before(max_wait_time, off_start_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;
|
goto fail;
|
||||||
min_wait_time = off_start_time + off;
|
max_wait_time = on_end_time + timer_from_us(4);
|
||||||
max_wait_time = cur + off + timer_from_ns(300);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n->last_req_time = timer_read_time();
|
n->last_req_time = cur;
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
// A hardware irq messed up the transmission - report a failure
|
// A hardware irq messed up the transmission - report a failure
|
||||||
gpio_out_write(pin, 0);
|
n->last_req_time = cur;
|
||||||
n->last_req_time = timer_read_time();
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue