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 17 static void neopixel_write(py::object gpio_obj, py::buffer buf) { 18 int gpio = py::getattr(gpio_obj, "_pin", gpio_obj).attr("id").cast<int>(); 19 py::buffer_info info = buf.request(); 20 21 if (!pio || sm < 0) { 22 // (is safe to call twice) 23 if (pio_init()) { 24 throw std::runtime_error("pio_init() failed"); 25 } 26 27 // can't use `pio0` macro as it will call exit() on failure! 28 pio = pio_open(0); 29 if(PIO_IS_ERR(pio)) { 30 throw std::runtime_error( 31 py::str("Failed to open PIO device (error {})").attr("format")(PIO_ERR_VAL(pio)).cast<std::string>()); 32 } 33 34 sm = pio_claim_unused_sm(pio, true); 35 36 offset = pio_add_program(pio, &ws2812_program); 37 38 pio_sm_clear_fifos(pio, sm); 39 pio_sm_set_clkdiv(pio, sm, 1.0); 40 41 printf("Loaded program at %d, using sm %d\n", offset, sm, gpio); 42 last_gpio = -1; 43 } 44 45 if (gpio != last_gpio) { 46 ws2812_program_init(pio, sm, offset, gpio, 800000.0, false); 47 printf("Initialized program at %d, using sm %d, gpio %d\n", offset, sm, gpio); 48 last_gpio = gpio; 49 } 50 51 size_t size = info.size * info.itemsize; 52 53 if(size > UINT16_MAX) { 54 throw py::value_error("Too much data"); 55 } 56 57 if (pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, size, 1)) { 58 throw std::runtime_error("pio_sm_config_xfer() failed"); 59 } 60 if (pio_sm_xfer_data(pio, sm, PIO_DIR_TO_SM, size, info.ptr)) { 61 throw std::runtime_error("pio_sm_xfer_data() failed"); 62 } 63 64 } 65 66 static void free_pio(void) { 67 if (!pio) { return; } 68 if (offset <= 0) { pio_remove_program(pio, &ws2812_program, offset); }; 69 offset = -1; 70 if (sm >= 0) { pio_sm_unclaim(pio, sm); } 71 sm = -1; 72 pio_close(pio); 73 pio = nullptr; 74 } 75 76 PYBIND11_MODULE(neopixel_write_pi5, m) { 77 m.doc() = R"pbdoc( 78 neopixel_write for pi5 79 ----------------------- 80 81 .. currentmodule:: neopixel_write_pi5 82 83 .. autosummary:: 84 :toctree: _generate 85 86 neopixel_write 87 )pbdoc"; 88 89 m.def("neopixel_write", &neopixel_write, 90 py::arg("gpio"), 91 py::arg("buf"), 92 R"pbdoc(NeoPixel writing function)pbdoc"); 93 94 m.def("_free_pio", &free_pio, 95 R"pbdoc(Release any held PIO resource)pbdoc"); 96 97 #ifdef VERSION_INFO 98 m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); 99 #else 100 m.attr("__version__") = "dev"; 101 #endif 102 }