/ src / main.cpp
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  }