/* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "shared-bindings/neopixel_write/__init__.h" #include "bindings/rp2pio/StateMachine.h" #include "common-hal/rp2pio/StateMachine.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "supervisor/port.h" uint64_t next_start_raw_ticks = 0; // NeoPixels are 800khz bit streams. We are choosing zeros as <312ns hi, 936 lo> and ones // and ones as <700 ns hi, 556 ns lo>. // cycle. The first two instructions always run while only one of the two final // instructions run per bit. We start with the low period because it can be // longer while waiting for more data. const uint16_t neopixel_program[] = { // bitloop: // out x 1 side 0 [6]; Drive low. Side-set still takes place before instruction stalls. 0x6621, // jmp !x do_zero side 1 [3]; Branch on the bit we shifted out previous delay. Drive high. 0x1323, // do_one: // jmp bitloop side 1 [4]; Continue driving high, for a one (long pulse) 0x1400, // do_zero: // nop side 0 [4]; Or drive low, for a zero (short pulse) 0xa442 }; void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, uint8_t *pixels, uint32_t num_bytes) { // Set everything up. rp2pio_statemachine_obj_t state_machine; // TODO: Cache the state machine after we create it once. We'll need a way to // change the pins then though. uint32_t pins_we_use = 1 << digitalinout->pin->number; bool ok = rp2pio_statemachine_construct(&state_machine, neopixel_program, sizeof(neopixel_program) / sizeof(neopixel_program[0]), 12800000, // MHz, to get about appropriate sub-bit times in PIO program. NULL, 0, // init program NULL, 1, // out NULL, 1, // in 0, 0, // in pulls NULL, 1, // set digitalinout->pin, 1, // sideset 0, pins_we_use, // initial pin state NULL, // jump pin pins_we_use, true, false, true, 8, false, // TX, auto pull every 8 bits. shift left to output msb first true, // Wait for txstall. If we don't, then we'll deinit too quickly. false, 32, true, // RX setting we don't use false, // claim pins false, // Not user-interruptible. false, // No sideset enable 0, -1); // wrap if (!ok) { // Do nothing. Maybe bitbang? return; } // Wait to make sure we don't append onto the last transmission. This should only be a tick or // two. while (port_get_raw_ticks(NULL) < next_start_raw_ticks) { } common_hal_rp2pio_statemachine_write(&state_machine, pixels, num_bytes, 1 /* stride in bytes */); // Use a private deinit of the state machine that doesn't reset the pin. rp2pio_statemachine_deinit(&state_machine, true); // Reset the pin and release it from the PIO gpio_init(digitalinout->pin->number); common_hal_digitalio_digitalinout_switch_to_output((digitalio_digitalinout_obj_t *)digitalinout, false, DRIVE_MODE_PUSH_PULL); // Update the next start to +2 ticks. This ensures we give it at least 300us. next_start_raw_ticks = port_get_raw_ticks(NULL) + 2; }