main.cpp
1 #include <iostream> 2 #include <pybind11/pybind11.h> 3 4 #include "piolib.h" 5 #include "utils/piolib/examples/ws2812.pio.h" 6 7 #define STRINGIFY(x) #x 8 #define MACRO_STRINGIFY(x) STRINGIFY(x) 9 10 namespace py = pybind11; 11 12 static PIO pio{}; 13 static int sm{-1}; 14 static int offset{-1}; 15 static int last_gpio{-1}; 16 static size_t last_size{}; 17 18 static void neopixel_write(py::object gpio_obj, py::buffer buf) { 19 int gpio = py::getattr(gpio_obj, "_pin", gpio_obj).attr("id").cast<int>(); 20 py::buffer_info info = buf.request(); 21 22 if (!pio || sm < 0) { 23 // (is safe to call twice) 24 if (pio_init()) { 25 throw std::runtime_error("pio_init() failed"); 26 } 27 28 // can't use `pio0` macro as it will call exit() on failure! 29 pio = pio_open(0); 30 if (PIO_IS_ERR(pio)) { 31 throw std::runtime_error( 32 py::str("Failed to open PIO device (error {})") 33 .attr("format")(PIO_ERR_VAL(pio)) 34 .cast<std::string>()); 35 } 36 37 sm = pio_claim_unused_sm(pio, true); 38 39 offset = pio_add_program(pio, &ws2812_program); 40 41 pio_sm_clear_fifos(pio, sm); 42 pio_sm_set_clkdiv(pio, sm, 1.0); 43 44 last_gpio = -1; 45 } 46 47 if (gpio != last_gpio) { 48 ws2812_program_init(pio, sm, offset, gpio, 800000.0, true); 49 last_gpio = gpio; 50 } 51 52 size_t size = info.size * info.itemsize; 53 54 // rp1pio can only DMA in 32-bit blocks. 55 // Copy the data into a temporary vector, with redundant zeros at the end 56 // then byteswap it so that the data comes out in the right order. 57 uint8_t *data = reinterpret_cast<uint8_t *>(info.ptr); 58 std::vector<uint32_t> vec; 59 vec.resize((size + 3) / 4); 60 size_t data_size = vec.size() * 4; 61 memcpy(&vec[0], data, size); 62 for (auto &i : vec) 63 i = __builtin_bswap32(i); 64 65 if (data_size > UINT16_MAX) { 66 throw py::value_error("Too much data"); 67 } 68 69 if (data_size != last_size) { 70 if (pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, data_size, 1)) { 71 throw std::runtime_error("pio_sm_config_xfer() failed"); 72 } 73 last_size = data_size; 74 } 75 if (pio_sm_xfer_data(pio, sm, PIO_DIR_TO_SM, data_size, &vec[0])) { 76 throw std::runtime_error("pio_sm_xfer_data() failed"); 77 } 78 } 79 80 static void free_pio(void) { 81 if (!pio) { 82 return; 83 } 84 if (offset <= 0) { 85 pio_remove_program(pio, &ws2812_program, offset); 86 }; 87 offset = -1; 88 if (sm >= 0) { 89 pio_sm_unclaim(pio, sm); 90 } 91 sm = -1; 92 pio_close(pio); 93 pio = nullptr; 94 } 95 96 PYBIND11_MODULE(adafruit_raspberry_pi5_neopixel_write, m) { 97 m.doc() = R"pbdoc( 98 neopixel_write for pi5 99 ---------------------- 100 101 .. currentmodule:: adafruit_raspberry_pi5_neopixel_write 102 103 .. autosummary:: 104 :toctree: _generate 105 106 neopixel_write 107 free_pio 108 )pbdoc"; 109 110 m.def("neopixel_write", &neopixel_write, py::arg("gpio"), py::arg("buf"), 111 R"pbdoc(NeoPixel writing function)pbdoc"); 112 113 m.def("free_pio", &free_pio, R"pbdoc(Release any held PIO resource)pbdoc"); 114 115 #ifdef VERSION_INFO 116 m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); 117 #else 118 m.attr("__version__") = "dev"; 119 #endif 120 }