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 namespace { 13 14 PIO pio_open_check() { 15 if (pio_init()) { 16 throw std::runtime_error("PIO not available"); 17 } 18 19 PIO pio = pio_open(0); 20 if(PIO_IS_ERR(pio)) { 21 throw std::runtime_error( 22 py::str("Failed to open PIO device (error {})").attr("format")(PIO_ERR_VAL(pio)).cast<std::string>()); 23 } 24 return pio; 25 } 26 27 int pio_sm_claim_unused_sm_check(PIO pio) { 28 int sm = pio_claim_unused_sm(pio, false); 29 if (sm < 0) { 30 throw std::runtime_error("No state machine available"); 31 } 32 return sm; 33 } 34 35 int pio_add_program_check(PIO pio, const struct pio_program *program) { 36 int offset = pio_add_program(pio, program); 37 if (offset < 0) { 38 throw std::runtime_error("Could not load program"); 39 } 40 return offset; 41 } 42 43 int get_pin_number(py::object gpio_obj) { 44 return py::getattr(gpio_obj, "_pin", gpio_obj).attr("id").cast<int>(); 45 } 46 47 template<class T> 48 int get_default(py::object o, T default_value) { 49 if (o.is_none()) { return default_value; } 50 return o.cast<T>(); 51 } 52 53 class StateMachine { 54 PIO pio{}; 55 int sm{-1}; 56 int offset{-1}; 57 58 public: 59 StateMachine(py::buffer assembled, 60 double frequency, 61 py::object first_sideset_pin, 62 int sideset_pin_count, 63 bool auto_pull, 64 bool out_shift_right, 65 int pull_threshold) { 66 pio = pio_open_check(); 67 sm = pio_sm_claim_unused_sm_check(pio); 68 py::buffer_info info = assembled.request(); 69 if (info.itemsize != 2) { 70 throw py::value_error("assembled: Expected array of type `h`"); 71 } 72 if (info.size >= 32) { 73 throw py::value_error("assembled: Exceeds maximum program length (32)"); 74 } 75 76 struct pio_program program = { 77 .instructions = reinterpret_cast<uint16_t*>(info.ptr), 78 .length = static_cast<uint8_t>(info.size), 79 .origin = -1, 80 }; 81 offset = pio_add_program_check(pio, &program); 82 83 pio_sm_config c = {0, 0, 0}; 84 sm_config_set_wrap(&c, offset, offset + info.size - 1); 85 86 if (!first_sideset_pin.is_none()) { 87 if (sideset_pin_count < 1 || sideset_pin_count > 5) { 88 throw py::value_error("sideset_pin_count out of range (must be 1 to 5)"); 89 } 90 auto first_sideset_pin_number = get_pin_number(first_sideset_pin); 91 for(int i=0; i<sideset_pin_count; i++) { 92 pio_gpio_init(pio, first_sideset_pin_number + i); 93 } 94 sm_config_set_sideset(&c, sideset_pin_count, /* optional */ false, /* pindirs */ false); 95 sm_config_set_sideset_pins(&c, first_sideset_pin_number); 96 } 97 98 sm_config_set_out_shift(&c, out_shift_right, auto_pull, pull_threshold); 99 100 double div = clock_get_hz(clk_sys) / frequency; 101 sm_config_set_clkdiv(&c, div); 102 103 pio_sm_init(pio, sm, offset, &c); 104 pio_sm_set_enabled(pio, sm, true); 105 106 } 107 108 ~StateMachine() { 109 if(!PIO_IS_ERR(pio)) pio_close(pio); 110 } 111 112 void write(py::buffer b) { 113 py::buffer_info info = b.request(); 114 uint32_t *ptr = reinterpret_cast<uint32_t*>(info.ptr); 115 std::vector<uint32_t> vec; 116 // the DMA controller doesn't replicate 8- and 16-bit values like on rp2, so we have to do it ourselves 117 if (info.itemsize != 4) { 118 vec.reserve(info.size); 119 switch(info.itemsize) { 120 case 1: 121 { 122 auto *buf = reinterpret_cast<uint8_t*>(info.ptr); 123 for(pybind11::ssize_t i=0; i<info.size; i++) { 124 vec.push_back(buf[i] * 0x01010101); 125 } 126 break; 127 } 128 case 2: 129 { 130 auto *buf = reinterpret_cast<uint16_t*>(info.ptr); 131 for(pybind11::ssize_t i=0; i<info.size; i++) { 132 vec.push_back(buf[i] * 0x00010001); 133 } 134 } 135 break; 136 default: 137 throw py::value_error("buffer must contain items of 1, 2, or 4 bytes"); 138 } 139 ptr = &vec[0]; 140 } 141 size_t size = info.size * sizeof(uint32_t); 142 if (pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, size, 1)) { 143 throw std::runtime_error("pio_sm_config_xfer() failed"); 144 } 145 if (pio_sm_xfer_data(pio, sm, PIO_DIR_TO_SM, size, ptr)) { 146 throw std::runtime_error("pio_sm_xfer_data() failed"); 147 } 148 } 149 }; 150 151 PYBIND11_MODULE(adafruit_rp1pio, m) { 152 m.doc() = R"pbdoc( 153 Hardware interface to RP1 series’ programmable IO (PIO) peripheral. 154 ------------------------------------------------------------------- 155 156 Except as noted, this is intended to be a subset of the functionality 157 in CircuitPython's ``rp2pio`` module. 158 159 .. currentmodule:: adafruit_rp1pio 160 161 .. autosummary:: 162 :toctree: _generate 163 164 StateMachine 165 )pbdoc"; 166 167 168 py::class_<StateMachine>(m, "StateMachine", "A single PIO StateMachine") 169 .def(py::init<py::buffer /* assembled */, 170 double /* frequency */, 171 py::object /* first_sideset_pin */, 172 int /* sideset_pin_count */, 173 bool /* auto_pull */, 174 bool /* out_shift_right */, 175 int /* pull_threshold */>(), 176 "Construct a StateMachine", 177 py::arg("assembled"), 178 py::arg("frequency"), 179 py::kw_only(), 180 py::arg("first_sideset_pin") = py::none(), 181 py::arg("sideset_pin_count") = 1, 182 py::arg("auto_pull") = false, 183 py::arg("out_shift_right") = true, 184 py::arg("pull_threshold") = 32 185 ) 186 .def("write", &StateMachine::write, "Write the data contained in buffer to the state machine", py::arg("buffer")); 187 188 #ifdef VERSION_INFO 189 m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); 190 #else 191 m.attr("__version__") = "dev"; 192 #endif 193 } 194 }