[
  {
    "path": "Button-debouncer/CMakeLists.txt",
    "content": "add_executable(pio_button_debounce)\n\npico_generate_pio_header(pio_button_debounce ${CMAKE_CURRENT_LIST_DIR}/button_debounce.pio)\n\ntarget_sources(pio_button_debounce PRIVATE button_debounce.cpp main.cpp)\n\ntarget_link_libraries(pio_button_debounce PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(pio_button_debounce)\n\n# add url via pico_set_program_url\nexample_auto_set_url(pio_button_debounce)\n\n\n"
  },
  {
    "path": "Button-debouncer/README.md",
    "content": "# Button debouncer using the Raspberry Pico PIO \n\n## Update \n\nThe new version allows the use of all 8 PIO state machines, and thus debounce up to 8 gpio.\n\nIt also allows setting of the debounce time between 0.5 to 30 ms.\n\n\n## Original text\n\nWhen using a GPIO to read noisy input, such as a mechanical button, it may happen that the signal read by the microcontroller rapidly switches back and forth, which may lead to false detection of several button presses where only one was intended. To prevent this, some hardware solutions exist as well as software solutions. This project is a software debouncer that makes sure that only after the input signal has stabilized, the code will read the new value. The downside of debouncers is that they usually cost some processing time to function. For Arduino a simple debouncer can be found [here](https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce).\n\nThe nice thing about the [Raspberry Pico](https://www.raspberrypi.org/documentation/pico/getting-started) is that it has 8 programmable IO (PIO) blocks that work independent of the main cores. I wanted to try my hand at programming the PIO and decided to do something I hadn't already seen as an example: a debouncer. The debouncer runs on one of the state machines of a PIO instance (there are two PIO instances and each has 4 state machines.)\n\nThe c++ code contains a class that starts the PIO code and lets the user code read the debounced pin state. The PIO code is as follows:\n\n        jmp pin isone     ; executed only once: is the pin currently 0 or 1?\n    iszero:\n        wait 1 pin 0      ; the pin is 0, wait for it to become 1\n        set x 31          ; prepare to test the pin for 31 times\n    checkzero:\n        jmp pin stillone  ; check if the pin is still 1\n        jmp iszero        ; if the pin has returned to 0, start over\n    stillone:\n        jmp x-- checkzero ; decrease the time to wait, or the pin has definitively become 1\n    isone:\n        wait 0 pin 0      ; the pin is 1, wait for it to become 0\n        set x 31          ; prepare to test the pin for 31 times\n    checkone:\n        jmp pin isone     ; if the pin has returned to 1, start over\n        jmp x-- checkone  ; decrease the time to wait\n        jmp iszero        ; the pin has definitively become 0\n\n\n## Explanation of PIO code\n* Start with the assumption that the pin is in a steady state.\n  If it is currently 1, then go to 'isone'; if it is currently 0, then go to 'iszero'\n* The code after 'isone' works as follows:\n  * Since the pin is 1 wait for a change to 0\n  * If that happens, set 31 into the x scratch register\n    * This is the amount of 'time' the debouncer will wait before switching over. The actual amount of time is also dependent on the clock divisor, and the fact that two jmp statements are executed for a test. Edit: in the updated version the debounce time can be set in ms.\n  * The program keeps checking if the input changes back to 1; and if so, start over at 'isone'\n  * If the input does not change back, complete the loop of counting down from 31\n  * If the x scratch register becomes 0, the signal has definitively switched to 0; start from 'iszero'\n* The branch of 'iszero' works similarly, but is structured a little bit differently because the `jmp pin` statement jumps on 1, not 0\n\nThere is one more important aspect to consider: the user code needs to read the debounced pin. So, somehow information from the PIO state machine has to go to the user code. I have considered several options:\n* Use the FIFO much like the [uart_rx](https://github.com/raspberrypi/pico-examples/tree/master/pio/uart_rx) example code\n* Use the FIFO, but from the PIO code make it empty (IN NULL) for a 0 and fill it with something to indicate a 1, and test the FIFO content with pio_sm_get_rx_fifo_level, or pio_sm_is_rx_fifo_empty\n* Use an interrupt, e.g. PIO0_IRQ_0 for a 0, and PIO0_IRQ_1 for a 1\n* Use an interesting approach where no explicit communication between the user code and PIO code is used at all!\n\nThe option I chose is based on the fact that the user code can know where the PIO state machine program counter is during execution via `pio_sm_get_pc`. The debouncer code has a clear split between the part where the debounced value is 0 and the part where it is 1: If (offset+1 <= pc < offset+isone) the value is 0, if (pc >= offset+isone) the value is 1, where offset is the starting point of the program in the PIO memory, and pc is the program counter.\n\nThis approach keeps the code very small, and reading the debounced state is very quick.\n\n## Does it actually work\nYes, it does. To show that the debouncer works, I have used another microcontroller to generate a repeating signal that bounces up and down a couple of times. In the figure below I have used a slightly different PIO code that sets a GPIO to 0 or 1 depending on the debounced state.\n\n![](debounce_test.png)\n\nThe blue line is the raw input signal. It bounces up and down a couple of times between point (a) and (b). The yellow line is the debounced pin state. It clearly follows the blue line, but skips over the bounces.\n\nIf the debouncing time is chosen too small the yellow line simply follows the blue line including the bounces, but with a slight delay.\n"
  },
  {
    "path": "Button-debouncer/button_debounce.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n\n#include \"button_debounce.pio.h\"\n#include \"button_debounce.h\"\n\n// indicate if errors and warnings should print a message\n// comment out for no messages\n#define PRINT_ERRORS\n\n// indicator that something is not used or not set\n#define UNUSED -10\n\n/* \n * class that debounces gpio using the PIO state machines.\n * up to 8 gpios can be debounced at any one time.\n * the debounce time for each gpio can be set individually, default it is set a 10ms.\n */\nDebounce::Debounce(void)\n{\n    // indicate that currently there are no gpios debounced\n    for (int i = 0; i < 32; i++)\n    {\n        gpio_debounced[i] = UNUSED;\n        pio_debounced[i] = (PIO)NULL;\n        sm_debounced[i] = UNUSED;\n        offset[i] = UNUSED;\n        // conf[i] = (pio_sm_config) 0;\n    }\n    num_of_debounced = 0;\n    pio0_already_set = UNUSED;\n    pio1_already_set = UNUSED;\n}\n\n/* \n * Request to debounce the gpio\n * @param gpio: the gpio that needs to be debounced\n *              the value must be [0, 28] excluding 23, 24 and 25. \n */\nint Debounce::debounce_gpio(uint gpio)\n{\n\n    // check if the gpio is valid\n    if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\\n\");\n#endif\n        return -1;\n    }\n    // check that the gpio is unused\n    if (gpio_debounced[gpio] != UNUSED)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce warning: gpio is already debounced\\n\");\n#endif\n        return -1;\n    }\n    // check if there are still sm available (there are 8, but other programs could also be using sm, which is checked later)\n    if (num_of_debounced == 8)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: max 8 gpios can be debounced (no state machine available)\\n\");\n#endif\n        return -1;\n    }\n\n    // Find a pio and sm:\n    // start with trying to use pio0\n    PIO pio = pio0;\n    // claim a state machine, no panic if non is available\n    uint sm = pio_claim_unused_sm(pio, false);\n    // check if this is a valid sm\n    if (sm == -1)\n    {\n        // pio0 did not deliver a sm, try pio1\n        pio = pio1;\n        // claim a state machine, no panic if non is available\n        sm = pio_claim_unused_sm(pio, false);\n        // check if this is a valid sm\n        if (sm == -1)\n        {\n            // also no sm from pio1, return an error\n#ifdef PRINT_ERRORS\n            printf(\"debounce error: no state machine available\\n\");\n#endif\n            return -1;\n        }\n    }\n\n    pio_debounced[gpio] = pio;\n    sm_debounced[gpio] = sm;\n    gpio_debounced[gpio] = gpio;\n    num_of_debounced += 1;\n\n    // check if the pio program has already been loaded:\n    if ((pio_debounced[gpio] == pio0) && (pio0_already_set != UNUSED))\n    {\n        offset[gpio] = offset[pio0_already_set];\n        conf[gpio] = conf[pio0_already_set];\n    }\n    else if ((pio_debounced[gpio] == pio1) && (pio1_already_set != UNUSED))\n    {\n        offset[gpio] = offset[pio1_already_set];\n        conf[gpio] = conf[pio1_already_set];\n    }\n    else\n    {\n        // load the pio program into the pio memory\n        offset[gpio] = pio_add_program(pio_debounced[gpio], &button_debounce_program);\n        // make a sm config\n        conf[gpio] = button_debounce_program_get_default_config(offset[gpio]);\n        // set the initial clkdiv to 10ms\n        sm_config_set_clkdiv(&conf[gpio], 10.);\n        if (pio_debounced[gpio] == pio0)\n            pio0_already_set = gpio;\n        else\n            pio1_already_set = gpio;\n    }\n    // set the 'wait' gpios\n    sm_config_set_in_pins(&conf[gpio], gpio); // for WAIT, IN\n    // set the 'jmp' gpios\n    sm_config_set_jmp_pin(&conf[gpio], gpio); // for JMP\n    // init the pio sm with the config\n    pio_sm_init(pio_debounced[gpio], sm_debounced[gpio], offset[gpio], &conf[gpio]);\n\n    // enable the sm\n    pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], true);\n    return 0;\n};\n\n/* \n * Request to debounce the gpio\n * @param gpio: the gpio that needs to be debounced\n *              the value must be a uint in the range [0, 28] excluding 23, 24 and 25. \n * @param debounce_time: the float debounce_time in milliseconds in the range [0.5, 30.]\n */\n\nint Debounce::set_debounce_time(uint gpio, float debounce_time)\n{\n    // check if the gpio is valid\n    if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\\n\");\n#endif\n        return -1;\n    }\n    if (debounce_time < 0.5)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: debounce time must be > 0 ms\\n\");\n#endif\n        return -1;\n    }\n    if (debounce_time > 30)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: the maximum is 30 ms, if you need longer debounce times: instructions are in the code\\n\");\n#endif\n        return -1;\n    }\n\n    /* \n        calculate clkdiv based on debounce time:\n        Note: the resulting debounce time will not be very precise, but probably within 5 to 10%\n\n        In the pio code it becomes clear that the debounce time is about 31*2 = 62 instructions.\n        The time in seconds when a clkdiv is applied then becomes: clkdiv * 62 / 125000000\n        In microseconds this is: clkdiv * 62/125, which is about half the clkdiv value.\n        Conversely: the clkdiv for a given debounce_time in miliseconds is: 2 * 1000 * debounce_time\n        The minimum clkdiv value is 1, the corresponding debounce time is about 500 microseconds\n        The maximum clkdiv value is 65535, the corresponding debounce time is about 33 milliseconds\n        \n        If a longer debounce time is required, the pio code must be adapted to add some pauses. This is\n        indicated in the pio code.\n     */\n\n    // stop the sm\n    pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], false);\n    // calculate the clkdiv (see explanation above)\n    float clkdiv = 2. * debounce_time * 1000.;\n    // check that the clkdiv has a valid value\n    if (clkdiv < 1.0)\n        clkdiv = 1.0;\n    else if (clkdiv > 65535.)\n        clkdiv = 65535.;\n    // set the clkdiv for both pio\n    sm_config_set_clkdiv(&conf[gpio], clkdiv);\n    // do the init of the pio/sm\n    pio_sm_init(pio_debounced[gpio], sm_debounced[gpio], offset[gpio], &conf[gpio]);\n    // enable the sm\n    pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], true);\n    return 0;\n};\n\n/* \n * Read the current value of the debounced the gpio\n * @param gpio: the gpio whose value (low, high) is read\n *              the gpio must have previously been debounced using debounce_gpio()\n */\nint Debounce::read(uint gpio)\n{\n    // check if the gpio is valid\n    if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\\n\");\n#endif\n        return -1;\n    }\n    // check that this gpio is indeed being debounced\n    if (gpio_debounced[gpio] == UNUSED)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio is not debounced\\n\");\n#endif\n        return -1;\n    }\n    // read the program counter\n    uint pc = pio_sm_get_pc(pio_debounced[gpio], sm_debounced[gpio]);\n    // if it is at or beyond the \"wait 0 pin 0\" it has value 1, else 0\n    // in the pio code a public define called 'border' is set at that position\n    if (pc >= (offset[gpio] + button_debounce_border))\n        return 1;\n    else\n        return 0;\n};\n\n/* \n * undebounce a previously debounced gpio\n * @param gpio: the gpio that is no longer going to be debounced\n *              the gpio must have previously been debounced using debounce_gpio()\n */\nint Debounce::undebounce_gpio(uint gpio)\n{\n    // check if the gpio is valid\n    if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\\n\");\n#endif\n        return -1;\n    }\n    // check that this gpio is indeed being debounced\n    if (gpio_debounced[gpio] == UNUSED)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio is not debounced\\n\");\n#endif\n        return -1;\n    }\n\n    // disable the pio\n    pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], false);\n    // save pio, sm and offset to - if possible - unclaim the sm and remove the program from pio memory\n    PIO pio_used = pio_debounced[gpio];\n    uint sm_used = sm_debounced[gpio];\n    int offset_used = offset[gpio];\n    // indicate that the gpio is not debounced\n    gpio_debounced[gpio] = UNUSED;\n    pio_debounced[gpio] = (PIO)NULL;\n    sm_debounced[gpio] = UNUSED;\n    offset[gpio] = UNUSED;\n\n    // unclaim the sm\n    pio_sm_unclaim(pio_used, sm_used);\n\n    // if this is the last gpio of a pio: remove the program, set pioX_already_set to UNUSED\n    int i;\n    for (i = 0; i < 32; i++)\n    {\n        // check if the pio is still in use (i.e. one of the sm belongs to this pio)\n        if (pio_debounced[i] == pio_used)\n            break;\n    }\n    // if i==32 it means that no other debounced gpio uses this pio\n    if (i == 32)\n    {\n        // remove the program\n        pio_remove_program(pio_used, &button_debounce_program, offset_used);\n        // indicate that the pio (either pio0 or pio1) is not set\n        if (pio_used == pio0)\n            pio0_already_set = UNUSED;\n        else\n            pio1_already_set = UNUSED;\n    }\n    // there is one less gpio being debounced\n    num_of_debounced--;\n\n    return 0;\n}\n"
  },
  {
    "path": "Button-debouncer/button_debounce.h",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n\n#include \"button_debounce.pio.h\"\n\n\n/* \n * class that debounces gpio using the PIO state machines.\n * up to 8 gpios can be debounced at any one time.\n * the debounce time for each gpio can be set individually, default it is set a 10ms.\n */\nclass Debounce\n{\npublic:\n    /* \n     * constructor to debounce gpios\n     */\n    Debounce(void);\n\n    /* \n     * Request to debounce the gpio\n     * @param gpio: the gpio that needs to be debounced\n     *              the value must be [0, 28] excluding 23, 24 and 25. \n     */\n    int debounce_gpio(uint gpio);\n\n    /* \n     * set the debounce time for a gpio\n     * @param gpio: the gpio for which the debounce time will be set\n     *              the gpio must have previously been debounced using debounce_gpio()\n     * @param debounce_time: the float debounce_time in milliseconds in the range [0.5, 30.]\n     */\n    int set_debounce_time(uint gpio, float debounce_time);\n\n    /* \n     * Read the current value of the debounced the gpio\n     * @param gpio: the gpio whose value (low, high) is read\n     *              the gpio must have previously been debounced using debounce_gpio()\n     */\n    int read(uint gpio);\n\n    /* \n     * undebounce (rebounce?) a previously debounced gpio\n     * @param gpio: the gpio that is no longer going to be debounced\n     *              the gpio must have previously been debounced using debounce_gpio()\n     */\n    int undebounce_gpio(uint gpio);\n\nprivate:\n    // the number of instantiated buttons to debounce\n    uint num_of_debounced = 0;\n\n    // Note: yes, the below way of doing things is somewhat wastefull, but handy\n    // for each gpio indicate if it is debounced\n    int gpio_debounced[32];\n    // for each gpio the PIO used to debounce\n    PIO pio_debounced[32];\n    // for each gpio the sm used to debounce\n    int sm_debounced[32];\n    // for each gpio the location of the pio program in the memory\n    int offset[32];\n    // for each gpio the configurations of the pio sm\n    pio_sm_config conf[32];\n    // flags to indicate the pio has already been set (and by which gpio)\n    int pio0_already_set, pio1_already_set;\n};\n"
  },
  {
    "path": "Button-debouncer/button_debounce.pio",
    "content": "; Debounce a gpio\n\n\n; Explanation:\n; - start with the assumption that the gpio is in a steady state. \n;   If it is currently 1, then go to 'isone'; if it is currently 0, then go to 'iszero'\n; - the branch of 'isone' works as follows:\n;     wait for a change to 0\n;     if that happens, set 31 into the x scratch register\n;         this is the amount of 'time' the debouncer will wait before switching over\n;         the actual amount of time is also dependent on the clock divisor\n;     the program keeps checking if the input changes back to 1, if so, start over at 'isone'\n;     if the input does not change back, complete the loop of counting down from 31\n;     if the x scratch register becomes 0, the signal has definitively switched to 0:\n;         start from 'iszero'\n; - the branch of 'iszero' works similarly, but note that a jmp pin statement always jumps on 1, not 0\n; - if (offset+1 <= pc < offset+isone) the value is 0, if (pc >= offset+isone) the value is 1\n; - The border between 0 and 1 in the code is taken as 'isone' which is made public as 'button_debounce_border'\n\n.program button_debounce\n\n    jmp pin isone   ; executed only once: is the gpio currently 0 or 1?\niszero:\n    wait 1 pin 0    ; the gpio is 0, wait for it to become 1\n    set x 31        ; prepare to test the gpio for 31 * 2 clock cycles\ncheckzero:\n    ; nop [31]      ; possible location to add some pauses if longer debounce times are needed\n                    ; Note: also insert a pause right after 'checkone' below\n\n    jmp pin stillone; check if the gpio is still 1\n    jmp iszero      ; if the gpio has returned to 0, start over\nstillone:\n    jmp x-- checkzero; the decrease the time to wait, or decide it has definitively become 1\nisone:\n    wait 0 pin 0    ; the gpio is 1, wait for it to become 0\n    set x 31        ; prepare to test the gpio for 31 * 2 clock cycles\ncheckone:\n    ; nop [31]      ; possible location to add some pauses if longer debounce times are needed\n                    ; Note: also insert a pause right after 'checkzero' above\n\n    jmp pin isone   ; if the gpio has returned to 1, start over\n    jmp x-- checkone; decrease the time to wait\n    jmp iszero      ; the gpio has definitively become 0\n\n; the c-code must know where the border between 0 and 1 is in the code:\n.define public border isone"
  },
  {
    "path": "Button-debouncer/main.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"button_debounce.h\"\n\n/*\n  This code shows how to use the button debouncer that uses the PIO state machines.\n  It shows all functionality:\n \n  Instantiate the debouncer, e.g.: Debounce debouncer;\n  Request to debounce the gpio, e.g. gpio 3: debouncer.debounce_gpio(3)\n  set the debounce time for a gpio, e.g. set to 1ms: debouncer.set_debounce_time(3, 1);\n  Read the current value of the debounced the gpio, e.g. gpio 3: int v = debouncer.read(3);\n  undebounce (rebounce?) a previously debounced gpio, e.g. gpio 3: debouncer.undebounce_gpio(3);\n\n  This example code first debounces gpio 3 to 10, then in an infinite loop reads the current \n  value of the debounced gpios. During this loop one by one the gpios are undebounced and then \n  one by one debounced again.\n */\n\nint main()\n{\n    // necessary for printf\n    stdio_init_all();\n    printf(\"Start of debounce example\\n\");\n    // instantiate the debouncer\n    Debounce debouncer;\n\n    // debounce 8 gpio\n    debouncer.debounce_gpio(3);\n    debouncer.debounce_gpio(4);\n    debouncer.debounce_gpio(5);\n    debouncer.debounce_gpio(6);\n    debouncer.debounce_gpio(7);\n    debouncer.debounce_gpio(8);\n    debouncer.debounce_gpio(9);\n    debouncer.debounce_gpio(10);\n\n    // set different debounce times\n    // Note: an external puls generator that can vary the puls widts and an logic analyser \n    // was used during testing to verify that this indeed works.\n    // debouncer.set_debounce_time(3, 1);\n    // debouncer.set_debounce_time(4, 2);\n    // debouncer.set_debounce_time(5, 3);\n    // debouncer.set_debounce_time(6, 4);\n    // debouncer.set_debounce_time(7, 5);\n    // debouncer.set_debounce_time(8, 6);\n    // debouncer.set_debounce_time(9, 7);\n    // debouncer.set_debounce_time(10, 8);\n\n    // infinite loop that continues to show the debounced value of the gpios\n    // in the first part of the loop the debounced gpios are one by one UNdebounced (rebounced?)\n    // in the second part of the loop the gpios are again all debounced (redebounced?)\n    int sm;\n    while (true)\n    {\n        tight_loop_contents();\n        // loop that reads and prints the current values of the debounced gpios (or print an X if not debounced)\n        // one by one the gpios are undebounced\n        for (int stop_debounce = 3; stop_debounce <= 10; stop_debounce++)\n        {\n            printf(\"\\nValue:\\t\");\n            for (int gpio = 3; gpio <= 10; gpio++)\n            {\n                int v = debouncer.read(gpio);\n                if (v != -1)\n                    printf(\"%d\\t\", v);\n                else \n                    printf(\"X\\t\");\n            }\n            sleep_ms(250);\n            // one by one UNdebounce the gpios\n            debouncer.undebounce_gpio(stop_debounce);\n        }\n\n        // loop that reads and prints the current values of the debounced gpios (or print an X if not debounced)\n        // one by one the gpios are debounced again\n        for (int debounce_again = 3; debounce_again <= 10; debounce_again++)\n        {\n            printf(\"\\nValue:\\t\");\n            for (int gpio = 3; gpio <= 10; gpio++)\n            {\n                int v = debouncer.read(gpio);\n                if (v != -1)\n                    printf(\"%d\\t\", v);\n                else \n                    printf(\"X\\t\");\n            }\n            sleep_ms(250);\n            // one by one debounce the gpios\n            debouncer.debounce_gpio(debounce_again);\n        }\n    }\n}\n"
  },
  {
    "path": "Button-debouncer/micropython_integrated/README.md",
    "content": "# Button debouncer using the Raspberry Pico PIO integrated into MicroPython\n\n## Note\nThis is a version of the button debounce program that has to be compiled into MicroPython. \nThis is not what I want, but this worked and I'm still working on a version that is an external .mpy file that can be imported.\n\n## How-to compile this code into MicroPython\nThis is a how-to for integrating cpp programs such as the button_debounce class into MicroPython.\n\nStarting point:\n- there is a working micropython environment for the RPI Pico\n- there is a working cpp program (in this case: button_debounce)\n\nGo to the rp2 port in the micropython directory:\n```\ncd /YOUR_PATH/micropython/ports/rp2\n```\n\nTest that compiling it produces a working micropython binary:\n```\nmake clean\nmake\nls -l build-PICO/\n```\nThe file 'firmware.uf2' should be present (and just built)\n\nThe module to be integrated in micropython will be located in the examples directory\n```\ncd /YOUR_PATH/micropython/examples/usercmodule\nmkdir button_debounce\ncd button_debounce\n```\n**The next text isn't needed if you use the files in this github repository! Skip to 'Add to overall cmake file for user-c-modules'**\n\nCopy the cpp files to this directory. Do not forget to include the generated .pio.h file if needed:\n```\ncp /PATH_TO_ORIGINAL_CPP_CODE/button_debounce.* .\n```\n\nNow make the following files:\n```\ntouch debounce.cpp\ntouch debouncemodule.h\ntouch debouncemodule.c\ntouch micropython.cmake\n```\nThe content of these files is:\ndebounce.cpp:\n```\nextern \"C\" {\n#include <debouncemodule.h>\n#include \"button_debounce.h\"\n\nDebounce debounce;\n\nmp_obj_t debounce_gpio(mp_obj_t mp_gpio) {\n    const auto gpio = mp_obj_get_int(mp_gpio);\n    return mp_obj_new_int(debounce.debounce_gpio(gpio));\n}\n\nmp_obj_t set_debounce_time(mp_obj_t mp_gpio, mp_obj_t mp_debounce_time) {\n    const auto gpio = mp_obj_get_int(mp_gpio);\n    const auto debounce_time = mp_obj_get_float(mp_debounce_time);\n    return mp_obj_new_int(debounce.set_debounce_time(gpio, debounce_time));\n}\n\nmp_obj_t read(mp_obj_t mp_gpio) {\n    const auto gpio = mp_obj_get_int(mp_gpio);\n    return mp_obj_new_int(debounce.read(gpio));\n}\n\nmp_obj_t undebounce_gpio(mp_obj_t mp_gpio) {\n    const auto gpio = mp_obj_get_int(mp_gpio);\n    return mp_obj_new_int(debounce.undebounce_gpio(gpio));\n}\n}\n```\ndebouncemodule.h:\n```\n// Include MicroPython API.\n#include \"py/runtime.h\"\n\n// Declare the function we'll make available in Python as cppexample.cppfunc().\nextern mp_obj_t debounce_gpio(mp_obj_t mp_gpio);\nextern mp_obj_t set_debounce_time(mp_obj_t mp_gpio, mp_obj_t mp_debounce_time);\nextern mp_obj_t read(mp_obj_t mp_gpio);\nextern mp_obj_t undebounce_gpio(mp_obj_t mp_gpio);\n```\ndebouncemodule.c:\n```\n#include <debouncemodule.h>\n\n// Define a Python reference to the function we'll make available.\n// See example.cpp for the definition.\nSTATIC MP_DEFINE_CONST_FUN_OBJ_1(debounce_gpio_obj, debounce_gpio);\nSTATIC MP_DEFINE_CONST_FUN_OBJ_2(set_debounce_time_obj, set_debounce_time);\nSTATIC MP_DEFINE_CONST_FUN_OBJ_1(read_obj, read);\nSTATIC MP_DEFINE_CONST_FUN_OBJ_1(undebounce_gpio_obj, undebounce_gpio);\n\n// Define all properties of the module.\n// Table entries are key/value pairs of the attribute name (a string)\n// and the MicroPython object reference.\n// All identifiers and strings are written as MP_QSTR_xxx and will be\n// optimized to word-sized integers by the build system (interned strings).\nSTATIC const mp_rom_map_elem_t debounce_module_globals_table[] = {\n    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_debounce) },\n    { MP_ROM_QSTR(MP_QSTR_debounce_gpio), MP_ROM_PTR(&debounce_gpio_obj) },\n    { MP_ROM_QSTR(MP_QSTR_set_debounce_time), MP_ROM_PTR(&set_debounce_time_obj) },\n    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&read_obj) },\n    { MP_ROM_QSTR(MP_QSTR_undebounce_gpio), MP_ROM_PTR(&undebounce_gpio_obj) },\n};\nSTATIC MP_DEFINE_CONST_DICT(debounce_module_globals, debounce_module_globals_table);\n\n// Define module object.\nconst mp_obj_module_t debounce_user_cmodule = {\n    .base = { &mp_type_module },\n    .globals = (mp_obj_dict_t *)&debounce_module_globals,\n};\n\n// Register the module to make it available in Python.\n// Note: the \"1\" in the third argument means this module is always enabled.\n// This \"1\" can be optionally replaced with a macro like MODULE_CPPEXAMPLE_ENABLED\n// which can then be used to conditionally enable this module.\nMP_REGISTER_MODULE(MP_QSTR_debounce, debounce_user_cmodule, 1);\n```\nmicropython.cmake:\n```\nadd_library(usermod_debounce INTERFACE)\ntarget_sources(usermod_debounce INTERFACE\n    ${CMAKE_CURRENT_LIST_DIR}/button_debounce.cpp\n    ${CMAKE_CURRENT_LIST_DIR}/debounce.cpp\n    ${CMAKE_CURRENT_LIST_DIR}/debouncemodule.c\n)\ntarget_include_directories(usermod_debounce INTERFACE\n    ${CMAKE_CURRENT_LIST_DIR}\n)\ntarget_link_libraries(usermod INTERFACE usermod_debounce)\n```\n## Add to overall cmake file for user-c-modules\nIn the directory with usercmodules add the button_debounce to the micropython.cmake file (you can use any editor, I used vi):\n```\nvi /YOUR_PATH/micropython/examples/usercmodule/micropython.cmake\n```\nThis file now looks like:\n```\n# This top-level micropython.cmake is responsible for listing\n# the individual modules we want to include.\n# Paths are absolute, and ${CMAKE_CURRENT_LIST_DIR} can be\n# used to prefix subdirectories.\n\n# Add the C example.\n# include(${CMAKE_CURRENT_LIST_DIR}/cexample/micropython.cmake)\n# Add the CPP example.\n# include(${CMAKE_CURRENT_LIST_DIR}/cppexample/micropython.cmake)\n# Add the module.\ninclude(${CMAKE_CURRENT_LIST_DIR}/button_debounce/micropython.cmake)\n```\n## Compiling\nGo to the rp2 port in the micropython directory and make with the option to include the user_c_modules:\n```\ncd /YOUR_PATH/micropython/ports/rp2\nmake USER_C_MODULES=../../examples/usercmodule/micropython.cmake\n```\n\nThis should result in a firmware.uf2 that includes the button_debounce module. \n\n## Running\nThe firmware must be uploaded to the pico (turn off, button pressed, turn on, file explorer pops up, move firmware.uf2 into it).\nThonny can be used to interact with micropython. \n\nI made the following script and uploaded it to the pico:\n```\nimport debounce\nimport time\ndebounce.start()\ndebounce.debounce_gpio(15)\nwhile True:\n    print(debounce.read(15))\n    time.sleep(0.1)\n```\nNote the 'debounce.start'. This is due to a problem I haven't solved yet: micropython loads the module with 'import debounce' but at a restart of a script using it, it doesn't run the constructor of the Debounce class again, therefore not initializing the variables. Thus, I've made a function 'start' to explicitly initialize the class variables.\n\n"
  },
  {
    "path": "Button-debouncer/micropython_integrated/button_debounce.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n\n#include \"button_debounce.pio.h\"\n#include \"button_debounce.h\"\n\n// FIXME: micropython doesn't let the printf go through, so might as well not print\n// indicate if errors and warnings should print a message\n// comment out for no messages\n// #define PRINT_ERRORS\n\n// indicator that something is not used or not set\n#define UNUSED -10\n\n/* \n * class that debounces gpio using the PIO state machines.\n * up to 8 gpios can be debounced at any one time.\n * the debounce time for each gpio can be set individually, default it is set a 10ms.\n */\n// FIXME: micropython caused problems with the constructor, so now I've an\n// empty constructor and the user needs to explicitly start the debounce \n// with 'start'\nDebounce::Debounce(void)\n{}\n\n/*\n * start must be used to initialize the variables\n */\n\nvoid Debounce::start(void)\n{\n    // indicate that currently there are no gpios debounced\n    for (int i = 0; i < 32; i++)\n    {\n        gpio_debounced[i] = UNUSED;\n        pio_debounced[i] = (PIO)NULL;\n        sm_debounced[i] = UNUSED;\n        offset[i] = UNUSED;\n        // conf[i] = (pio_sm_config) 0;\n    }\n    num_of_debounced = 0;\n    pio0_already_set = UNUSED;\n    pio1_already_set = UNUSED;\n    has_started = true;\n}\n\n/* \n * Request to debounce the gpio\n * @param gpio: the gpio that needs to be debounced\n *              the value must be [0, 28] excluding 23, 24 and 25. \n */\nint Debounce::debounce_gpio(uint gpio)\n{\n    if (has_started==false) \n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: the module needs to be started first with <module name>.start()\\n\");\n#endif\n        return -1;\n    }\n        \n    // check if the gpio is valid\n    if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\\n\");\n#endif\n        return -1;\n    }\n    // check that the gpio is unused\n    if (gpio_debounced[gpio] != UNUSED)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce warning: gpio is already debounced\\n\");\n#endif\n        return -1;\n    }\n    // check if there are still sm available (there are 8, but other programs could also be using sm, which is checked later)\n    if (num_of_debounced == 8)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: max 8 gpios can be debounced (no state machine available)\\n\");\n#endif\n        return -1;\n    }\n\n    // Find a pio and sm:\n    // start with trying to use pio0\n    PIO pio = pio0;\n    // claim a state machine, no panic if non is available\n    int sm = pio_claim_unused_sm(pio, false);\n    // check if this is a valid sm\n    if (sm == -1)\n    {\n        // pio0 did not deliver a sm, try pio1\n        pio = pio1;\n        // claim a state machine, no panic if non is available\n        sm = pio_claim_unused_sm(pio, false);\n        // check if this is a valid sm\n        if (sm == -1)\n        {\n            // also no sm from pio1, return an error\n#ifdef PRINT_ERRORS\n            printf(\"debounce error: no state machine available\\n\");\n#endif\n            return -1;\n        }\n    }\n\n    pio_debounced[gpio] = pio;\n    sm_debounced[gpio] = sm;\n    gpio_debounced[gpio] = gpio;\n    num_of_debounced += 1;\n\n    // check if the pio program has already been loaded:\n    if ((pio_debounced[gpio] == pio0) && (pio0_already_set != UNUSED))\n    {\n        offset[gpio] = offset[pio0_already_set];\n        conf[gpio] = conf[pio0_already_set];\n    }\n    else if ((pio_debounced[gpio] == pio1) && (pio1_already_set != UNUSED))\n    {\n        offset[gpio] = offset[pio1_already_set];\n        conf[gpio] = conf[pio1_already_set];\n    }\n    else\n    {\n        // load the pio program into the pio memory\n        offset[gpio] = pio_add_program(pio_debounced[gpio], &button_debounce_program);\n        // make a sm config\n        conf[gpio] = button_debounce_program_get_default_config(offset[gpio]);\n        // set the initial clkdiv to 10ms\n        sm_config_set_clkdiv(&conf[gpio], 10.);\n        if (pio_debounced[gpio] == pio0)\n            pio0_already_set = gpio;\n        else\n            pio1_already_set = gpio;\n    }\n    // set the 'wait' gpios\n    sm_config_set_in_pins(&conf[gpio], gpio); // for WAIT, IN\n    // set the 'jmp' gpios\n    sm_config_set_jmp_pin(&conf[gpio], gpio); // for JMP\n    // init the pio sm with the config\n    pio_sm_init(pio_debounced[gpio], sm_debounced[gpio], offset[gpio], &conf[gpio]);\n\n    // enable the sm\n    pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], true);\n    return 0;\n};\n\n/* \n * Request to debounce the gpio\n * @param gpio: the gpio that needs to be debounced\n *              the value must be a uint in the range [0, 28] excluding 23, 24 and 25. \n * @param debounce_time: the float debounce_time in milliseconds in the range [0.5, 30.]\n */\n\nint Debounce::set_debounce_time(uint gpio, float debounce_time)\n{\n    // check that the module has been started\n    if (has_started==false) \n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: the module needs to be started first with <module name>.start()\\n\");\n#endif\n        return -1;\n    }\n\n    // check if the gpio is valid\n    if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\\n\");\n#endif\n        return -1;\n    }\n    if (debounce_time < 0.5)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: debounce time must be > 0 ms\\n\");\n#endif\n        return -1;\n    }\n    if (debounce_time > 30)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: the maximum is 30 ms, if you need longer debounce times: instructions are in the code\\n\");\n#endif\n        return -1;\n    }\n\n    /* \n        calculate clkdiv based on debounce time:\n        Note: the resulting debounce time will not be very precise, but probably within 5 to 10%\n\n        In the pio code it becomes clear that the debounce time is about 31*2 = 62 instructions.\n        The time in seconds when a clkdiv is applied then becomes: clkdiv * 62 / 125000000\n        In microseconds this is: clkdiv * 62/125, which is about half the clkdiv value.\n        Conversely: the clkdiv for a given debounce_time in miliseconds is: 2 * 1000 * debounce_time\n        The minimum clkdiv value is 1, the corresponding debounce time is about 500 microseconds\n        The maximum clkdiv value is 65535, the corresponding debounce time is about 33 milliseconds\n        \n        If a longer debounce time is required, the pio code must be adapted to add some pauses. This is\n        indicated in the pio code.\n     */\n\n    // stop the sm\n    pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], false);\n    // calculate the clkdiv (see explanation above)\n    float clkdiv = 2. * debounce_time * 1000.;\n    // check that the clkdiv has a valid value\n    if (clkdiv < 1.0)\n        clkdiv = 1.0;\n    else if (clkdiv > 65535.)\n        clkdiv = 65535.;\n    // set the clkdiv for both pio\n    sm_config_set_clkdiv(&conf[gpio], clkdiv);\n    // do the init of the pio/sm\n    pio_sm_init(pio_debounced[gpio], sm_debounced[gpio], offset[gpio], &conf[gpio]);\n    // enable the sm\n    pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], true);\n    return 0;\n};\n\n/* \n * Read the current value of the debounced the gpio\n * @param gpio: the gpio whose value (low, high) is read\n *              the gpio must have previously been debounced using debounce_gpio()\n */\nint Debounce::read(uint gpio)\n{\n    // check that the module has been started\n    if (has_started==false) \n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: the module needs to be started first with <module name>.start()\\n\");\n#endif\n        return -1;\n    }\n    // check if the gpio is valid\n    if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\\n\");\n#endif\n        return -1;\n    }\n    // check that this gpio is indeed being debounced\n    if (gpio_debounced[gpio] == UNUSED)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio is not debounced\\n\");\n#endif\n        return -1;\n    }\n    // read the program counter\n    int pc = pio_sm_get_pc(pio_debounced[gpio], sm_debounced[gpio]);\n    // if it is at or beyond the \"wait 0 pin 0\" it has value 1, else 0\n    // in the pio code a public define called 'border' is set at that position\n    if (pc >= (offset[gpio] + button_debounce_border))\n        return 1;\n    else\n        return 0;\n};\n\n/* \n * undebounce a previously debounced gpio\n * @param gpio: the gpio that is no longer going to be debounced\n *              the gpio must have previously been debounced using debounce_gpio()\n */\nint Debounce::undebounce_gpio(uint gpio)\n{\n    // check that the module has been started\n    if (has_started==false) {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: the module needs to be started first with <module name>.start()\\n\");\n#endif\n        return -1;\n    }\n\n    // check if the gpio is valid\n    if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\\n\");\n#endif\n        return -1;\n    }\n    // check that this gpio is indeed being debounced\n    if (gpio_debounced[gpio] == UNUSED)\n    {\n#ifdef PRINT_ERRORS\n        printf(\"debounce error: gpio is not debounced\\n\");\n#endif\n        return -1;\n    }\n\n    // disable the pio\n    pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], false);\n    // save pio, sm and offset to - if possible - unclaim the sm and remove the program from pio memory\n    PIO pio_used = pio_debounced[gpio];\n    uint sm_used = sm_debounced[gpio];\n    int offset_used = offset[gpio];\n    // indicate that the gpio is not debounced\n    gpio_debounced[gpio] = UNUSED;\n    pio_debounced[gpio] = (PIO)NULL;\n    sm_debounced[gpio] = UNUSED;\n    offset[gpio] = UNUSED;\n\n    // unclaim the sm\n    pio_sm_unclaim(pio_used, sm_used);\n\n    // if this is the last gpio of a pio: remove the program, set pioX_already_set to UNUSED\n    int i;\n    for (i = 0; i < 32; i++)\n    {\n        // check if the pio is still in use (i.e. one of the sm belongs to this pio)\n        if (pio_debounced[i] == pio_used)\n            break;\n    }\n    // if i==32 it means that no other debounced gpio uses this pio\n    if (i == 32)\n    {\n        // remove the program\n        pio_remove_program(pio_used, &button_debounce_program, offset_used);\n        // indicate that the pio (either pio0 or pio1) is not set\n        if (pio_used == pio0)\n            pio0_already_set = UNUSED;\n        else\n            pio1_already_set = UNUSED;\n    }\n    // there is one less gpio being debounced\n    num_of_debounced--;\n\n    return 0;\n}\n"
  },
  {
    "path": "Button-debouncer/micropython_integrated/button_debounce.h",
    "content": "//#include \"py/runtime.h\"\n\n#include \"button_debounce.pio.h\"\n\n/* \n * class that debounces gpio using the PIO state machines.\n * up to 8 gpios can be debounced at any one time.\n * the debounce time for each gpio can be set individually, default it is set a 10ms.\n */\nclass Debounce\n{\npublic:\n    /* \n     * constructor to debounce gpios\n     */\n    Debounce(void);\n\n    /* \n     * start the debounce class\n     * FIXME: This is due to the way micropython starts a module (when\n     * restarting it doesn't run the constructor again)\n     */\n    void start(void);\n\n    /* \n     * Request to debounce the gpio\n     * @param gpio: the gpio that needs to be debounced\n     *              the value must be [0, 28] excluding 23, 24 and 25. \n     */\n    int debounce_gpio(uint gpio);\n\n    /* \n     * set the debounce time for a gpio\n     * @param gpio: the gpio for which the debounce time will be set\n     *              the gpio must have previously been debounced using debounce_gpio()\n     * @param debounce_time: the float debounce_time in milliseconds in the range [0.5, 30.]\n     */\n    int set_debounce_time(uint gpio, float debounce_time);\n\n    /* \n     * Read the current value of the debounced the gpio\n     * @param gpio: the gpio whose value (low, high) is read\n     *              the gpio must have previously been debounced using debounce_gpio()\n     */\n    int read(uint gpio);\n\n    /* \n     * undebounce (rebounce?) a previously debounced gpio\n     * @param gpio: the gpio that is no longer going to be debounced\n     *              the gpio must have previously been debounced using debounce_gpio()\n     */\n    int undebounce_gpio(uint gpio);\n\nprivate:\n    // indicate that the instantiated class has been started\n    // FIXME: this due to the micropython problem with starting a module and not running the constructor again\n    bool has_started = false;\n    // the number of instantiated buttons to debounce\n    uint num_of_debounced = 0;\n\n    // Note: yes, the below way of doing things is somewhat wastefull, but handy\n    // for each gpio indicate if it is debounced\n    int gpio_debounced[32];\n    // for each gpio the PIO used to debounce\n    PIO pio_debounced[32];\n    // for each gpio the sm used to debounce\n    int sm_debounced[32];\n    // for each gpio the location of the pio program in the memory\n    int offset[32];\n    // for each gpio the configurations of the pio sm\n    pio_sm_config conf[32];\n    // flags to indicate the pio has already been set (and by which gpio)\n    int pio0_already_set, pio1_already_set;\n};\n\n"
  },
  {
    "path": "Button-debouncer/micropython_integrated/button_debounce.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#pragma once\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// --------------- //\n// button_debounce //\n// --------------- //\n\n#define button_debounce_wrap_target 0\n#define button_debounce_wrap 10\n\n#define button_debounce_border 6\n\nstatic const uint16_t button_debounce_program_instructions[] = {\n            //     .wrap_target\n    0x00c6, //  0: jmp    pin, 6                     \n    0x20a0, //  1: wait   1 pin, 0                   \n    0xe03f, //  2: set    x, 31                      \n    0x00c5, //  3: jmp    pin, 5                     \n    0x0001, //  4: jmp    1                          \n    0x0043, //  5: jmp    x--, 3                     \n    0x2020, //  6: wait   0 pin, 0                   \n    0xe03f, //  7: set    x, 31                      \n    0x00c6, //  8: jmp    pin, 6                     \n    0x0048, //  9: jmp    x--, 8                     \n    0x0001, // 10: jmp    1                          \n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program button_debounce_program = {\n    .instructions = button_debounce_program_instructions,\n    .length = 11,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config button_debounce_program_get_default_config(uint offset) {\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + button_debounce_wrap_target, offset + button_debounce_wrap);\n    return c;\n}\n#endif\n\n"
  },
  {
    "path": "Button-debouncer/micropython_integrated/debounce.cpp",
    "content": "extern \"C\" {\n#include <debouncemodule.h>\n#include \"button_debounce.h\"\n#include <stdio.h>\n#include \"py/obj.h\"\n\nDebounce debounce;\n\n// FIXME: this is due to micropython not running the constructor again at re-importing the module\nmp_obj_t start() {\n    // mp_printf(&mp_plat_print, \"start\\n\");\n    debounce.start();\n    return mp_obj_new_int(1);\n}\n\nmp_obj_t debounce_gpio(mp_obj_t mp_gpio) {\n    const auto gpio = mp_obj_get_int(mp_gpio);\n    // mp_printf(&mp_plat_print, \"debounce_gpio\\n\");\n    return mp_obj_new_int(debounce.debounce_gpio(gpio));\n}\n\nmp_obj_t set_debounce_time(mp_obj_t mp_gpio, mp_obj_t mp_debounce_time) {\n    const auto gpio = mp_obj_get_int(mp_gpio);\n    const auto debounce_time = mp_obj_get_float(mp_debounce_time);\n    // mp_printf(&mp_plat_print, \"set_debounce_time\\n\");\n    return mp_obj_new_int(debounce.set_debounce_time(gpio, debounce_time));\n}\n\nmp_obj_t read(mp_obj_t mp_gpio) {\n    const auto gpio = mp_obj_get_int(mp_gpio);\n    // mp_printf(&mp_plat_print, \"read\\n\");\n    return mp_obj_new_int(debounce.read(gpio));\n}\n\nmp_obj_t undebounce_gpio(mp_obj_t mp_gpio) {\n    const auto gpio = mp_obj_get_int(mp_gpio);\n    // mp_printf(&mp_plat_print, \"undebounce_gpio\\n\");\n    return mp_obj_new_int(debounce.undebounce_gpio(gpio));\n}\n\n\n}\n"
  },
  {
    "path": "Button-debouncer/micropython_integrated/debouncemodule.c",
    "content": "#include <debouncemodule.h>\n\n// Define a Python reference to the function we'll make available.\n// See example.cpp for the definition.\n// FIXME: start shouldn't be necessary (see other files)\nSTATIC MP_DEFINE_CONST_FUN_OBJ_0(start_obj, start);\nSTATIC MP_DEFINE_CONST_FUN_OBJ_1(debounce_gpio_obj, debounce_gpio);\nSTATIC MP_DEFINE_CONST_FUN_OBJ_2(set_debounce_time_obj, set_debounce_time);\nSTATIC MP_DEFINE_CONST_FUN_OBJ_1(read_obj, read);\nSTATIC MP_DEFINE_CONST_FUN_OBJ_1(undebounce_gpio_obj, undebounce_gpio);\n\n// Define all properties of the module.\n// Table entries are key/value pairs of the attribute name (a string)\n// and the MicroPython object reference.\n// All identifiers and strings are written as MP_QSTR_xxx and will be\n// optimized to word-sized integers by the build system (interned strings).\nSTATIC const mp_rom_map_elem_t debounce_module_globals_table[] = {\n    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_debounce) },\n// FIXME: start shouldn't be necessary (see other files)\n    { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&start_obj) },\n    { MP_ROM_QSTR(MP_QSTR_debounce_gpio), MP_ROM_PTR(&debounce_gpio_obj) },\n    { MP_ROM_QSTR(MP_QSTR_set_debounce_time), MP_ROM_PTR(&set_debounce_time_obj) },\n    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&read_obj) },\n    { MP_ROM_QSTR(MP_QSTR_undebounce_gpio), MP_ROM_PTR(&undebounce_gpio_obj) },\n};\nSTATIC MP_DEFINE_CONST_DICT(debounce_module_globals, debounce_module_globals_table);\n\n// Define module object.\nconst mp_obj_module_t debounce_user_cmodule = {\n    .base = { &mp_type_module },\n    .globals = (mp_obj_dict_t *)&debounce_module_globals,\n};\n\n// Register the module to make it available in Python.\n// Note: the \"1\" in the third argument means this module is always enabled.\n// This \"1\" can be optionally replaced with a macro like MODULE_CPPEXAMPLE_ENABLED\n// which can then be used to conditionally enable this module.\nMP_REGISTER_MODULE(MP_QSTR_debounce, debounce_user_cmodule, 1);\n\n"
  },
  {
    "path": "Button-debouncer/micropython_integrated/debouncemodule.h",
    "content": "// Include MicroPython API.\n#include \"py/runtime.h\"\n\n// Declare the function we'll make available in Python as cppexample.cppfunc().\n// FIXME: start shouldn't be necessary (see other files)\nextern mp_obj_t start();\nextern mp_obj_t debounce_gpio(mp_obj_t mp_gpio);\nextern mp_obj_t set_debounce_time(mp_obj_t mp_gpio, mp_obj_t mp_debounce_time);\nextern mp_obj_t read(mp_obj_t mp_gpio);\nextern mp_obj_t undebounce_gpio(mp_obj_t mp_gpio);\n"
  },
  {
    "path": "Button-debouncer/micropython_integrated/micropython.cmake",
    "content": "add_library(usermod_debounce INTERFACE)\n\ntarget_sources(usermod_debounce INTERFACE\n    ${CMAKE_CURRENT_LIST_DIR}/button_debounce.cpp\n    ${CMAKE_CURRENT_LIST_DIR}/debounce.cpp\n    ${CMAKE_CURRENT_LIST_DIR}/debouncemodule.c\n)\n\ntarget_include_directories(usermod_debounce INTERFACE\n    ${CMAKE_CURRENT_LIST_DIR}\n)\n\ntarget_link_libraries(usermod INTERFACE usermod_debounce) \n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.12)\n\n# Pull in SDK (must be before project)\ninclude(pico_sdk_import.cmake)\n\nproject(pico_examples C CXX ASM)\nset(CMAKE_C_STANDARD 11)\nset(CMAKE_CXX_STANDARD 17)\n\nset(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})\n\n# Initialize the SDK\npico_sdk_init()\n\ninclude(example_auto_set_url.cmake)\nadd_subdirectory(blow_out_a_LED)\nadd_subdirectory(Button-debouncer)\nadd_subdirectory(button_matrix_4x4)\nadd_subdirectory(count_pulses_with_pause)\nadd_subdirectory(HCSR04)\nadd_subdirectory(ledpanel)\nadd_subdirectory(Limited_1_wire)\nadd_subdirectory(multiplication)\nadd_subdirectory(PwmIn)\nadd_subdirectory(Rotary_encoder)\nadd_subdirectory(Rotational_shift_ISR)\nadd_subdirectory(SBUS)\nadd_subdirectory(sm_to_dma_to_buffer)\nadd_subdirectory(sm_to_dma_to_sm_to_dma_to_buffer)\nadd_subdirectory(subroutines)\nadd_subdirectory(two_pio_programs_one_file)\nadd_subdirectory(Two_sm_one_disabled)\nadd_subdirectory(Two_sm_one_disabled_with_irq)\nadd_subdirectory(Two_sm_simple)\nadd_subdirectory(Value_communication_between_two_sm_via_pins)\nadd_subdirectory(ws2812_led_strip_120)\n"
  },
  {
    "path": "HCSR04/CMakeLists.txt",
    "content": "add_executable(HCSR04)\n\npico_generate_pio_header(HCSR04 ${CMAKE_CURRENT_LIST_DIR}/HCSR04.pio)\n\ntarget_sources(HCSR04 PRIVATE HCSR04.cpp)\n\ntarget_link_libraries(HCSR04 PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(HCSR04)\n\n# add url via pico_set_program_url\nexample_auto_set_url(HCSR04)\n\n\n"
  },
  {
    "path": "HCSR04/HCSR04.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"HCSR04.pio.h\"\n\n// class that sets up and reads the HCSR04\n// The HCSR04 works by giving it a 10 us pulse on its Trigger pin\n// The distance to an object is represented by the length of the pulse on its Echo pin\nclass HCSR04\n{\npublic:\n    // constructor\n    // input = pin connected to the 'Echo' pin of the HCSR04.\n    // ! NOTE: USE A VOLTAGE DIVIDER FOR THE INPUT (i.e. the Echo pin of the HCSR04)\n    //         to go from 5V (which is needed by the HCSR04 module) to 3.3V\n    // output = pin connected to the 'Trig' pin of the HCSR04.\n    HCSR04(uint input, uint output)\n    {\n        // pio 0 is used\n        pio = pio0;\n        // state machine 0\n        sm = 0;\n        // configure the used pins\n        pio_gpio_init(pio, input);\n        pio_gpio_init(pio, output);\n        // load the pio program into the pio memory\n        uint offset = pio_add_program(pio, &HCSR04_program);\n        // make a sm config\n        pio_sm_config c = HCSR04_program_get_default_config(offset);\n        // set the 'in' pins, also used for 'wait'\n        sm_config_set_in_pins(&c, input);\n        // set the 'jmp' pin\n        sm_config_set_jmp_pin(&c, input);\n        // set the output pin to output\n        pio_sm_set_consecutive_pindirs(pio, sm, output, 1, true);\n        // set the 'set' pins\n        sm_config_set_set_pins(&c, output, 1);\n        // set shift direction\n        sm_config_set_in_shift(&c, false, false, 0);\n        // init the pio sm with the config\n        pio_sm_init(pio, sm, offset, &c);\n        // enable the sm\n        pio_sm_set_enabled(pio, sm, true);\n    }\n\n    // read the distance to an object in cm (0 cm means invalid measurement)\n    float read(void)\n    {\n        // value is used to read from the sm RX FIFO\n        uint32_t clock_cycles = 0;\n        // clear the FIFO: do a new measurement\n        pio_sm_clear_fifos(pio, sm);\n        // give the sm some time to do a measurement and place it in the FIFO\n        sleep_ms(100);\n        // check that the FIFO isn't empty\n        if (pio_sm_is_rx_fifo_empty(pio, sm))\n        {\n            return 0;\n        }\n        // read one data item from the FIFO\n        // Note: every test for the end of the echo puls takes 2 pio clock ticks,\n        //       but changes the 'timer' by only one\n        clock_cycles = 2 * pio_sm_get(pio, sm);\n        // using\n        // - the time for 1 pio clock tick (1/125000000 s)\n        // - speed of sound in air is about 340 m/s\n        // - the sound travels from the HCSR04 to the object and back (twice the distance)\n        // we can calculate the distance in cm by multiplying with 0.000136\n        float cm = (float)clock_cycles * 0.000136;\n        return (cm);\n    }\n\nprivate:\n    // the pio instance\n    PIO pio;\n    // the state machine\n    uint sm;\n};\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // the instance of the HCSR04 (Echo pin = 14, Trig pin = 15)\n    HCSR04 my_HCSR04(14, 15);\n    // infinite loop to print distance measurements\n    while (true)\n    {\n        // read the distance sensor and print the result\n        float cm = my_HCSR04.read();\n        printf(\"cm = %f\\n\", cm);\n        sleep_ms(100);\n    }\n}"
  },
  {
    "path": "HCSR04/HCSR04.pio",
    "content": "\n; Description of the used algorithm\n;\n; start:\n;\n;   Give a puls to the HCSR04 Trig pin to start the measurement\n;       According to the datasheet the pulse should be 10us long.\n;       Assume the Trig pin is currently 0, set it to 1, wait 10 us, set it to 0\n;       A length of 10 us, with a clock of 125MHz this is 1250 ticks, or binary: 10011100010. \n;       Assuming that the 10 us doesn't have to be very precise, round it down to 10011000000. \n;       The delay can be achieved by:\n;       \n;           set x 19        ; set x to 10011 (and clear the higher bits)\n;           mov ISR x       ; copy x to ISR \n;           in NULL 6       ; shift in 6 more 0 bits\n;           mov x ISR       ; move the ISR to x (which now contains 10011000000)\n;       delay1:\n;           jmp x-- delay1  ; count down to 0: a delay of (about) 10 us\n;\n;       This results in a delay of about 10us.\n;   \n;   The distance to the object is encoded in the length of the pulse on the Echo pin\n;       Read the Echo pin (USE A VOLTAGE DIVIDER) wait until the input pulse becomes high\n;       Set the value 0xFFFFFFFF in x; this is the start of the 'timer'. This can be achieved by\n;       mov x ~NULL \n;\n;       Now the value in x is decremented in a loop and each time the Echo pin is tested. \n;       If the Echo pin is 0, the value (0xFFFFFFFF - x) represents the length of the echo pulse.\n;       Note: each decrement of x and a test of the Echo pin is 2 pio clock cycles.\n;   Push the bit-inversed value of x, i.e. (0xFFFFFFFF - x) into the Rx FIFO\n;\n;   According to the HCSR04 datasheet, we have to wait for 60ms before starting another measurement\n;       Use the same trick as above to create a delay. But now with values:\n;       7500000 clock cycles = 11100100111000011100000, round down to 11100 + 18 * 0\n;\n; Go back to start\n\n\n.program HCSR04\n\n.wrap_target\n                    ; give a puls to the HCSR04 Trigger pin\n    set pins 1      ; set the trigger to 1 \n                    ; delay for 10 us (the length of the trigger pulse)\n    set x 19        ; set x to 10011 (and clear the higher bits)\n    mov ISR x       ; copy x to ISR \n    in NULL 6       ; shift in 6 more 0 bits\n    mov x ISR       ; move the ISR to x (which now contains 10011000000)\ndelay1:\n    jmp x-- delay1  ; count down to 0: a delay of (about) 10 us\n\n    set pins 0      ; make the trigger 0 again, completing the trigger pulse\n                    ; \n    wait 1 pin 0    ; wait for the echo pin to rise\n                    ; start a counting loop to measure the length of the echo pulse\n    mov x ~NULL     ; start with the value 0xFFFFFFFF\ntimer:\n    jmp x-- test    ; count down\n    jmp timerstop   ; timer has reached 0, stop count down\ntest:\n    jmp pin timer   ; test if the echo pin is still 1, if so, continue counting down\ntimerstop:          ; echo pulse is over (or timer has reached 0)\n    mov ISR ~x      ; move the bit-inversed value in x to the ISR\n    push noblock    ; push the ISR into the Rx FIFO\n                    ; delay for 60ms (advice from datasheet to prevent triggering on echos)\n    set x 28        ; set x to 11100\n    mov ISR x       ; copy x to ISR\n    in NULL 18      ; shift in 18 more bits\n    mov x ISR       ; move the ISR to x \ndelay2:\n    jmp x-- delay2  ; delay (about) 60 ms\n.wrap               ; start over"
  },
  {
    "path": "HCSR04/README.md",
    "content": "# HC-SR04 using the PIO code\nThe HC-SR04 is an ultrasonic distance measurement unit ([datasheet](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf)). Basically, it works by sending a puls to the Trig pin, and measuring the pulse on the Echo pin. The length of the Echo pulse represents the distance to an object in front of the device.\n\nThe HC-SR04 uses 5V while the Pico uses 3V3. Thus the Echo pin requires a [resistive divider](https://hackaday.com/2016/12/05/taking-it-to-another-level-making-3-3v-and-5v-logic-communicate-with-level-shifters/) to be save. \n\n## The algorithm\nThe pio code to read the HC-SR04 has the following steps:\n* Give a pulse on the Trig pin of the HC-SR04 to start the measurement. The datasheet indicates that the length of this pulse should be 10 us\n* Measure the length of the pulse on the Echo pin\n* Wait 60 ms before making a new measurement to be sure that no echos from previous measurements are found\n\nSetting and reading pins are (if you've done it before) straight forward, but making and reading pulses of specific length weren't for me.\n\nMaking the HC-SR04 algorithm work involves two types of timing:\n* a delay to make the trigger pulse\n* measuring the length of the echo pulse\n* a delay to ensure no echos of a previous measurement are received\n\nThe PIO doesn't seem to have timers, but each instruction takes one pio clock cycle, and these can be used to measure time.\n\n## Delay in PIO\nAccording to the datasheet, the trigger pulse should be 10 us. The pio clock runs at 125 MHz, so, the pulse should be 1250 cycles long. Here it is assumed that the clock divider is set to 1. If this has a different value, some of the timing calculations below will change. Creating the required delay can be achieved in the following way:\n* 1250 cycles in binary is 10011100010\n* assuming the pulse width doesn't need to be very precise, this can be rounded down to 10011000000\n* this can be split into the 5 most significant digits (10011) and 6 additional 0 bits\n* place this number in the x register in the following way (note that for this to work correctly, the shift direction needs to be set with`sm_config_set_in_shift(&c, false, false, 0);`):\n``` pio\n    set x 19        ; set x to 10011 (and clear the higher bits)\n    mov ISR x       ; copy x to ISR \n    in NULL 6       ; shift in 6 more 0 bits\n    mov x ISR       ; move the ISR to x (which now contains 10011000000)\n``` \n* Count down to 0 in a tight loop using:\n``` pio\ndelay1:\n    jmp x-- delay1\n```\nThis results in a delay of almost 10 us.\n\nThis same approach can be used to create a clock-cycle precise delay for any number between 0x00000000 and 0xFFFFFFFF. This may involve setting x and shifting it into the ISR, 5 bits at a time, several times. If the timing doesn't have to be precise, rounding down (or up) after the 5 most significant bits and shifting in further 0's, as done above, can save instructions.\n\nThe second delay that is needed is 60 ms. The same trick as above is used to create this delay. But now with the following values:\n7500000 clock cycles are needed. In binary this is 11100100111000011100000. This is rounded down to 11100 + 18 * 0.\n\n## Measuring duration of a pulse\n\nTo measure the duration of some event, in this case the length of the Echo pulse which represents the distance to an object, can be done by starting the x (or y) scratch register with 0xFFFFFFFF and counting down, testing for the stop criterion each iteration. \n\nThe counting down loop with test for the stop criterion looks like this:\n```pio\ntimer:\n    jmp x-- test    ; count down\n    jmp timerstop   ; timer has reached 0, stop count down\ntest:\n    jmp pin timer   ; test if the echo pin is still 1, if so, continue counting down\ntimerstop:          ; echo pulse is over (or timer has reached 0)\n    mov ISR ~x      ; move the bit-inverted value of x to the ISR\n    push noblock    ; push the ISR into the RX FIFO\n```\nIf x isn't 0, the statement `jmp x-- test` jumps to testing for the stop criterion (the Echo pin going low). If the stop criterion is true, or if the x register is 0 when arriving at the jmp statement, the bit-inverted value of x is moved to the ISR, which makes it positive, and is then pushed to the Rx FIFO. \n\n## Calculation of distance\nIn the C/C++ code the value in the x scratch register is received. Each loop has two instructions (`jmp` with decrement, and `jmp` testing the pin) the amount of pio clock cycles becomes `2 * pio_sm_get(pio, sm)`.\n\nFor the calculation of the distance three more things are needed:\n* one pio clock cycle takes 1 / 125 MHz = 0.000000008 s\n* The speed of sound in air is about 340 m/s = 34000 cm/s\n* The sound travels from the HC-SR04 to the object and back (twice the distance)\n\nIf those are factored in, the code to calculate the distance in cm becomes:\n``` c\n    clock_cycles = 2*pio_sm_get(pio, sm);\n    cm = (float)clock_cycles * 0.000136;\n```\n\n\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 GitJer\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Limited_1_wire/CMakeLists.txt",
    "content": "add_executable(onewire)\n\npico_generate_pio_header(onewire ${CMAKE_CURRENT_LIST_DIR}/onewire.pio)\n\ntarget_sources(onewire PRIVATE onewire.cpp)\n\ntarget_link_libraries(onewire PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(onewire)\n\n# add url via pico_set_program_url\nexample_auto_set_url(onewire)\n\n\n"
  },
  {
    "path": "Limited_1_wire/README.md",
    "content": "# 1-wire protocol for one device \n\nThis code is a pio implementation of the [1-wire protocol](https://en.wikipedia.org/wiki/1-Wire). The C++ implementation is rather limited: it is meant for only one device (the DS18B20 temperature sensor) and uses the default settings (12-bit conversion). \nTo make this I have used the [datasheet](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf) as well as the implementation of [Paul Stoffregen](https://github.com/PaulStoffregen/OneWire) and [this pio implementation](https://www.raspberrypi.org/forums/viewtopic.php?t=304511#p1851030).\n\nOne of the interesting elements is that it uses the same pin for set, out, sideset, out, and in! (I should have included a 'jmp pin' somewhere. :)\n\n"
  },
  {
    "path": "Limited_1_wire/onewire.cpp",
    "content": "/*\n\nThis code uses pio code to read a single DS18B20 temperature sensor.\nIt is not suited to read more than one sensor: it does not use the parts\nof the 1-wire protocol that allowes more than one sensor.\n\n*/\n\n#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"onewire.pio.h\"\n\n// the pin to which the sensor is connected.\n// Note that an external pullup resistor of 4.7k is needed on this pin\n#define OW_PIN 15\n\n// ---------------------------------------------------------------------------------\n// The 1-Wire CRC scheme is described in Maxim Application Note 27:\n// \"Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products\"\n//\n// Dow-CRC using polynomial X^8 + X^5 + X^4 + X^0\n// Tiny 2x16 entry CRC table created by Arjen Lentz\n// See http://lentz.com.au/blog/calculating-crc-with-a-tiny-32-entry-lookup-table\n//\n// Copied from https://github.com/PaulStoffregen/OneWire/blob/master/OneWire.cpp\nstatic const uint8_t dscrc2x16_table[] = {\n    0x00, 0x5E, 0xBC, 0xE2, 0x61, 0x3F, 0xDD, 0x83,\n    0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41,\n    0x00, 0x9D, 0x23, 0xBE, 0x46, 0xDB, 0x65, 0xF8,\n    0x8C, 0x11, 0xAF, 0x32, 0xCA, 0x57, 0xE9, 0x74};\n// ---------------------------------------------------------------------------------\n\nclass OneWire\n{\npublic:\n    OneWire(uint onewire_pin)\n    {\n        pio = pio0;\n        // state machine 0\n        sm = 0;\n        // configure the used pins\n        pio_gpio_init(pio, OW_PIN);\n        // load the pio programs into the pio memory\n        offset_wait = pio_add_program(pio, &onewire_wait_program);\n        offset_reset = pio_add_program(pio, &onewire_reset_program);\n        offset_write_byte = pio_add_program(pio, &onewire_write_byte_program);\n        offset_read_byte = pio_add_program(pio, &onewire_read_byte_program);\n\n        // make a sm config\n        pio_sm_config c = pio_get_default_sm_config();\n        // set the set pin\n        sm_config_set_set_pins(&c, OW_PIN, 1);\n        // set the out pin\n        sm_config_set_out_pins(&c, OW_PIN, 1);\n        // configure side set to be 1 pin, optional (hence 2 bits) and controls pindirs\n        sm_config_set_sideset(&c, 2, true, true);\n        sm_config_set_sideset_pins(&c, OW_PIN);\n        // set the in pin\n        sm_config_set_in_pins(&c, OW_PIN);\n        // set shift to right: bits shifted by 'in' are ordered as least\n        // significant bit (LSB) first, no autopush/pull\n        sm_config_set_in_shift(&c, true, false, 0);\n        sm_config_set_out_shift(&c, true, false, 0);\n        // one clock cycle is 10 us\n        sm_config_set_clkdiv(&c, 1250);\n        // init the pio sm with the config, start with the wait program\n        pio_sm_init(pio, sm, offset_wait, &c);\n        // enable the sm\n        pio_sm_set_enabled(pio, sm, true);\n    }\n\n    int reset()\n    {\n        // start the reset program and check if a sensor (worker) responds\n        pio_sm_exec(pio, sm, offset_reset);\n        // read the return value: 0 means there are at least one workers\n        int no_workders_detected = pio_sm_get_blocking(pio, sm);\n        if (no_workders_detected == 1)\n            // indeed: no workers detected\n            return -1;\n        return 1;\n    }\n\n    int reset_and_check()\n    {\n        // this method checks if there are workers (i.e. DS18B20 sensors)\n        // and then - assuming only one is present - checks if it can read\n        // this sensor properly by asking for its unique ID and doing a CRC.\n\n        // start the reset program\n        pio_sm_exec(pio, sm, offset_reset);\n        // read the return value: 0 means there are (at least one) workers\n        int no_workders_detected = pio_sm_get_blocking(pio, sm);\n        if (no_workders_detected == 1)\n            // indeed: no workers detected\n            return -1;\n        else\n        {\n            // for stability reasons give it some time\n            sleep_ms(10);\n            // send the command to get the address of the worker\n            send_byte(0x33);\n            // for stability reasons give it some time\n            sleep_ms(10);\n            // read the results\n            read_bytes(8);\n            // the eighth byte is the crc\n            if (crc8(results, 7) != results[7])\n            {\n                printf(\"crc of ROM is wrong!!!!!!!!!!!\\n\");\n                return -1;\n            }\n            return 1;\n        }\n    }\n\n    void send_byte(uint32_t to_send)\n    {\n        // put the byte in the TxFIFO\n        pio_sm_put(pio, sm, to_send);\n        // start the sm program that writes a byte \n        pio_sm_exec(pio, sm, offset_write_byte);\n    }\n\n    uint32_t read_byte()\n    {\n        // start the sm program that reads a byte\n        pio_sm_exec(pio, sm, offset_read_byte);\n        // wait for the result\n        return pio_sm_get_blocking(pio, sm) >> 24;\n    }\n\n    void read_bytes(int n)\n    {\n        // read n bytes from the sensor\n        for (int i = 0; i < n; i++)\n            results[i] = (uint8_t)read_byte() & 0xFF;\n    }\n\n    float read_temperature()\n    {\n        // put the sensor in a known state\n        int workers = reset();\n        if (workers < 0)\n            return -1;\n        // for stability reasons give it some time\n        sleep_ms(10);\n        // address the family (not one specific sensor)\n        send_byte(0xCC);\n        // for stability reasons give it some time\n        sleep_ms(5);\n        // ask for a temperature conversion\n        send_byte(0x44);\n        // a temperature conversion at 12 bit resolution takes 750ms\n        sleep_ms(800);\n        // put the sensor in a known state\n        workers = reset();\n        if (workers < 0)\n            return -1;\n        // for stability reasons give it some time\n        sleep_ms(10);\n        // address the family (not one specific sensor)\n        send_byte(0xCC);\n        // for stability reasons give it some time\n        sleep_ms(5);\n        // ask for the results\n        send_byte(0xBE);\n        // for stability reasons give it some time\n        sleep_ms(10);\n        // read the results\n        read_bytes(9);\n        // the ninth byte is the crc\n        if (crc8(results, 8) != results[8])\n        {\n            printf(\"crc of data is wrong!!!!!!!!!!!\\n\");\n            return -1;\n        }\n        // convert the temperature results to a float\n        // the bits in results[0] indicate temperature as follows:\n        // bit 7 to 0: 2^3, 2^2, 2^1, 2^0, 2^-1, 2^-2, 2^-3, 2^-4\n        uint8_t Tlow = results[0];\n        float T = 0;\n        for (int bit = 0; bit < 8; bit++)\n            if ((Tlow & 1 << bit) > 0)\n                T += 1 << bit;\n        // above counting started as if 2^-4 is 1, so correct by dividing by 16\n        T /= 16.;\n        // the bits in results[1] indicate temperature as follows:\n        // bit 7 to 0: S, S, S, S, S, 2^6, 2^5, 2^4; where S = sign\n        // note that negative numbers start at -128\n        uint8_t Thigh = results[1];\n        for (int bit = 0; bit < 3; bit++)\n            if ((Thigh & 1 << bit) > 0)\n                T += 1 << (bit + 4);\n        // check the sign using bit 3\n        if ((Thigh & 1 << 3) > 0)\n            T = -128.+T;\n        return T;\n    }\n\n    // ---------------------------------------------------------------------------------\n    // Copied (and adapted) from https://github.com/PaulStoffregen/OneWire/blob/master/OneWire.cpp\n    uint8_t crc8(const uint8_t *addr, uint8_t len)\n    {\n        uint8_t crc = 0;\n        while (len--)\n        {\n            crc = *addr++ ^ crc; // just re-using crc as intermediate\n            crc = dscrc2x16_table[(crc & 0x0f)] ^\n                  dscrc2x16_table[16 + ((crc >> 4) & 0x0f)];\n        }\n        return crc;\n    }\n    // ---------------------------------------------------------------------------------\n\nprivate:\n    // the pio instance\n    PIO pio;\n    // the state machine\n    uint sm;\n    // the offsets (origins of the PIO programs)\n    uint offset_wait;\n    uint offset_reset;\n    uint offset_write_byte;\n    uint offset_read_byte;\n    // the result of reading the sensor\n    uint8_t results[9];\n};\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // the instance of the OneWire\n    OneWire DS18B20(OW_PIN);\n    // reset and determine if there are workers\n    int workers = DS18B20.reset_and_check();\n\n    if (workers > 0)\n        while (true)\n            printf(\"Temperature = %f\\n\", DS18B20.read_temperature());\n    else\n        while (true)\n            ;\n}"
  },
  {
    "path": "Limited_1_wire/onewire.pio",
    "content": "\n;\n; It is assumed that an external pull up resistor (4.7k) is present on the pin with the OneWire sensor\n;\n; ------------------------------------------------------\n        ; WAIT\n        ; When starting the sm, it should do nothing.\n.program onewire_wait\n\nonewire_wait:\n    jmp onewire_wait\n\n; ------------------------------------------------------\n        ; RESET\n.program onewire_reset\n        ; side-set controls the direction\n.side_set 1 opt pindirs\n\nonewire_reset:\n        ; side_set to input and wait until the line goes high, just in case it isn't already 1\n    wait 1 PIN 0 side 0\n        ; side_set to output and pull line low and delay for > 480 us\n    set PINS 0 [7] side 1\n        ; wait > 480 us (at 10us per step), here use 490 us\n    set x 4 [5]\nreset_wait_loop:\n    jmp x-- reset_wait_loop [6]\n        ; side_set to input, delay for 80us\n    nop [7] side 0\n        ; read the value of the line (1 = no slaves, 0 = at least one slave)\n    in PINS 1\n    push\n        ; wait until the line goes high after the slaves release the line\n    wait 1 PIN 0\n        ; end by doing nothing in a loop\nreset_stop:\n    jmp reset_stop\n\n; ------------------------------------------------------\n        ; WRITE BYTE\n.program onewire_write_byte\n.side_set 1 opt pindirs\n\n        ; get the byte to send\n    pull\n        ; set counter to 8 bits\n    set x 7\nwrite_byte_loop:\n        ; master pulls low for 10 us\n    set PINS 0 side 1\n        ; write whatever bit is shifted out of the OSR, keep it for 40us\n    out PINS 1 [3]\n        ; the last part (10 us) must be high TODO: jmp also takes 10 us (would not be needed)\n    set PINS 1\n        ; do 8 bits in total\n    jmp x-- write_byte_loop\n        ; end by doing nothing in a loop\nwrite_byte_stop:\n    jmp x-- write_byte_stop\n\n; ------------------------------------------------------\n        ; READ BYTE\n.program onewire_read_byte\n.side_set 1 opt pindirs\n\n        ; set counter to 8 bits\n    set x 7\n        ; clear the ISR\n    mov ISR NULL\nread_byte_loop:\n        ; master pulls low for 10 us\n    set PINS 0 side 1\n        ; set master to read (instruction takes 10 us)\n    nop side 0\n        ; sample the line and shift right into the ISR (LSB is read first)\n    in PINS 1\n        ; there is still some time to wait: about 50 us\n    nop [4]\n        ; do 8 bits in total\n    jmp x-- read_byte_loop\n        ; push it in the RxFIFO\n    push\n        ; end by doing nothing in a loop\nread_byte_stop:\n    jmp read_byte_stop\n"
  },
  {
    "path": "PwmIn/CMakeLists.txt",
    "content": "add_executable(PwmIn)\n\npico_generate_pio_header(PwmIn ${CMAKE_CURRENT_LIST_DIR}/PwmIn.pio)\n\ntarget_sources(PwmIn PRIVATE PwmIn.cpp)\n\ntarget_link_libraries(PwmIn PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(PwmIn)\n\n# add url via pico_set_program_url\nexample_auto_set_url(PwmIn)"
  },
  {
    "path": "PwmIn/PwmIn.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"PwmIn.pio.h\"\n\n// class that sets up and reads PWM pulses: PwmIn. It has three functions:\n// read_period (in seconds)\n// read_pulsewidth (in seconds)\n// read_dutycycle (between 0 and 1)\n// read pulsewidth, period, and calculate the dutycycle\n\nclass PwmIn\n{\npublic:\n    // constructor\n    // input = pin that receives the PWM pulses.\n    PwmIn(uint input)\n    {\n        // pio 0 is used\n        pio = pio0;\n        // state machine 0\n        sm = 0;\n        // configure the used pins\n        pio_gpio_init(pio, input);\n        // load the pio program into the pio memory\n        uint offset = pio_add_program(pio, &PwmIn_program);\n        // make a sm config\n        pio_sm_config c = PwmIn_program_get_default_config(offset);\n        // set the 'jmp' pin\n        sm_config_set_jmp_pin(&c, input);\n        // set the 'wait' pin (uses 'in' pins)\n        sm_config_set_in_pins(&c, input);\n        // set shift direction\n        sm_config_set_in_shift(&c, false, false, 0);\n        // init the pio sm with the config\n        pio_sm_init(pio, sm, offset, &c);\n        // enable the sm\n        pio_sm_set_enabled(pio, sm, true);\n    }\n\n    // read_period (in seconds)\n    float read_period(void)\n    {\n        read();\n        // one clock cycle is 1/125000000 seconds\n        return (period * 0.000000008);\n    }\n\n    // read_pulsewidth (in seconds)\n    float read_pulsewidth(void)\n    {\n        read();\n        // one clock cycle is 1/125000000 seconds\n        return (pulsewidth * 0.000000008);\n    }\n\n    // read_dutycycle (between 0 and 1)\n    float read_dutycycle(void)\n    {\n        read();\n        return ((float)pulsewidth / (float)period);\n    }\n\n    // read pulsewidth and period for one pulse\n    void read_PWM(float *readings)\n    {\n        read();\n        *(readings + 0) = (float)pulsewidth * 0.000000008;\n        *(readings + 1) = (float)period * 0.000000008;\n        *(readings + 2) = ((float)pulsewidth / (float)period);\n    }\n\nprivate:\n    // read the period and pulsewidth\n    void read(void)\n    {\n        // clear the FIFO: do a new measurement\n        pio_sm_clear_fifos(pio, sm);\n        // wait for the FIFO to contain two data items: pulsewidth and period\n        while (pio_sm_get_rx_fifo_level(pio, sm) < 2)\n            ;\n        // read pulse width from the FIFO\n        pulsewidth = pio_sm_get(pio, sm);\n        // read period from the FIFO\n        period = pio_sm_get(pio, sm) + pulsewidth;\n        // the measurements are taken with 2 clock cycles per timer tick\n        pulsewidth = 2 * pulsewidth;\n        // calculate the period in clock cycles:\n        period = 2 * period;\n    }\n    // the pio instance\n    PIO pio;\n    // the state machine\n    uint sm;\n    // data about the PWM input measured in pio clock cycles\n    uint32_t pulsewidth, period;\n};\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // the instance of the PwmIn\n    PwmIn my_PwmIn(14);\n    // the array with the results\n    float pwm_reading[3];\n    // infinite loop to print PWM measurements\n    while (true)\n    {\n        my_PwmIn.read_PWM(pwm_reading);\n        if (pwm_reading[0] >= 0.)\n        {\n            printf(\"pw=%.8f \\tp=%.8f \\tdc=%.8f\\n\", pwm_reading[0], pwm_reading[1], pwm_reading[2]);\n        }\n        sleep_ms(100);\n    }\n}\n"
  },
  {
    "path": "PwmIn/PwmIn.pio",
    "content": "\n.program PwmIn\n\n; algorithm:\n\n; loop:\n;    reset y, the 'timer' for the pulsewidth (high period)\n;    reset x, the 'timer' for the low period. (high + low = period)\n;    wait for a 0, then wait for a 1: this is the rising edge\n;    loop: \n;       decrement y timer\n;       test for falling edge \n;    y timer value is the pulse width (actually, (0xFFFFFFFF - y)*2/125MHz is the pulse width)\n;    loop:\n;       test for rising edge\n;       decrement x timer\n;    x timer is the low period (actually, (0xFFFFFFFF - x)*2/125MHz is the low period)\n;    push both y and x to the Rx FIFO\n\n.wrap_target\nstart:\n    mov y ~NULL         ; start with the value 0xFFFFFFFF\n    mov x ~NULL         ; start with the value 0xFFFFFFFF\n    wait 0 pin 0        ; wait for a 0\n    wait 1 pin 0        ; wait for a 1, now we really have the rising edge\ntimer_hp:               ; loop for high period\n    jmp y-- test        ; count down for pulse width\n    jmp start           ; timer has reached 0, stop count down of pulse, restart\ntest:\n    jmp pin timer_hp    ; test if the pin is still 1, if so, continue counting down\ntimer_lp:               ; loop for low period\n    jmp pin timerstop   ; if the pin has become 1, the period is over, stop count down\n    jmp x-- timer_lp    ; if not: count down\n    jmp start           ; timer has reached 0, stop count down of low period, restart\ntimerstop:\n    mov ISR ~y          ; move the value ~y to the ISR: the high period (pulsewidth) (0xFFFFFFFF-y)\n    push noblock        ; push the ISR into the Rx FIFO\n    mov ISR ~x          ; move the value ~x to the ISR: the low period (0xFFFFFFFF-x)\n    push noblock        ; push the ISR into the Rx FIFO\n.wrap\n"
  },
  {
    "path": "PwmIn/PwmIn_4pins/CMakeLists.txt",
    "content": "add_executable(PWM4)\n\npico_generate_pio_header(PWM4 ${CMAKE_CURRENT_LIST_DIR}/PwmIn.pio)\n\ntarget_sources(PWM4 PRIVATE PWM4.cpp PwmIn.cpp)\n\ntarget_link_libraries(PWM4 PRIVATE\n        pico_stdlib\n        hardware_pio\n        hardware_pwm\n        hardware_gpio\n        )\n\npico_add_extra_outputs(PWM4)\n\n\n\n\n"
  },
  {
    "path": "PwmIn/PwmIn_4pins/PWM4.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"pico/time.h\"\n\n#include \"PwmIn.h\"\n\n#define NUM_OF_PINS 4\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    printf(\"PwmIn on 4 pins\\n\");\n\n    // set PwmIn\n    uint pin_list[NUM_OF_PINS] = {14, 15, 18, 19};\n    PwmIn my_PwmIn(pin_list, NUM_OF_PINS);\n\n    while (true)\n    {\n        // adviced empty (for now) function of sdk\n        tight_loop_contents();\n\n        // translate pwm of input to output\n        float PW_0 = my_PwmIn.read_PW(0);\n        float PW_1 = my_PwmIn.read_PW(1);\n        float PW_2 = my_PwmIn.read_PW(2);\n        float PW_3 = my_PwmIn.read_PW(3);\n        printf(\"PW_0=%f PW_1=%f PW_2=%f PW_3=%f\\n\", PW_0, PW_1, PW_2, PW_3);\n        sleep_ms(100);\n    }\n}\n\n"
  },
  {
    "path": "PwmIn/PwmIn_4pins/PwmIn.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/irq.h\"\n\n#include \"PwmIn.h\"\n#include \"PwmIn.pio.h\"\n\n// class that reads PWM pulses from up to 4 pins\nPwmIn::PwmIn(uint *pin_list, uint num_of_pins)\n{\n    _num_of_pins = num_of_pins;\n    // pio 0 is used\n    pio = pio0;\n    // load the pio program into the pio memory\n    uint offset = pio_add_program(pio, &PwmIn_program);\n    // start num_of_pins state machines\n    for (int i = 0; i < num_of_pins; i++)\n    {\n        // prepare state machine i\n        pulsewidth[i] = 0;\n        period[i] = 0;\n\n        // configure the used pins (pull down, controlled by PIO)\n        gpio_pull_down(pin_list[i]);\n        pio_gpio_init(pio, pin_list[i]);\n        // make a sm config\n        pio_sm_config c = PwmIn_program_get_default_config(offset);\n        // set the 'jmp' pin\n        sm_config_set_jmp_pin(&c, pin_list[i]);\n        // set the 'wait' pin (uses 'in' pins)\n        sm_config_set_in_pins(&c, pin_list[i]);\n        // set shift direction\n        sm_config_set_in_shift(&c, false, false, 0);\n        // init the pio sm with the config\n        pio_sm_init(pio, i, offset, &c);\n        // enable the sm\n        pio_sm_set_enabled(pio, i, true);\n    }\n    // set the IRQ handler\n    irq_set_exclusive_handler(PIO0_IRQ_0, pio_irq_handler);\n    // enable the IRQ\n    irq_set_enabled(PIO0_IRQ_0, true);\n    // allow irqs from the low 4 state machines\n    pio0_hw->inte0 = PIO_IRQ0_INTE_SM0_BITS | PIO_IRQ0_INTE_SM1_BITS | PIO_IRQ0_INTE_SM2_BITS | PIO_IRQ0_INTE_SM3_BITS ;\n};\n\n// read the period and pulsewidth\nvoid PwmIn::read_PWM(float *readings, uint pin)\n{\n    if (pin < _num_of_pins)\n    {\n\n        // determine whole period\n        period[pin] += pulsewidth[pin];\n        // the measurements are taken with 2 clock cycles per timer tick\n        // hence, it is 2*0.000000008\n        *(readings + 0) = (float)pulsewidth[pin] * 2 * 0.000000008;\n        *(readings + 1) = (float)period[pin] * 2 * 0.000000008;\n        *(readings + 2) = ((float)pulsewidth[pin] / (float)period[pin]);\n        pulsewidth[pin] = 0;\n        period[pin] = 0;\n    }\n};\n\n// read only the duty cycle\nfloat PwmIn::read_DC(uint pin)\n{\n    return ((float)pulsewidth[pin] / (float)period[pin]);\n}\n\n// read only the period\nfloat PwmIn::read_P(uint pin)\n{\n    // the measurements are taken with 2 clock cycles per timer tick\n    // hence, it is 2*0.000000008\n    return ((float)period[pin] * 0.000000016);\n}\n\nfloat PwmIn::read_PW(uint pin)\n{\n    // the measurements are taken with 2 clock cycles per timer tick\n    // hence, it is 2*0.000000008\n    return ((float)pulsewidth[pin] * 0.000000016);\n};\n\nuint32_t PwmIn::pulsewidth[4];\nuint32_t PwmIn::period[4];\nPIO PwmIn::pio;\n"
  },
  {
    "path": "PwmIn/PwmIn_4pins/PwmIn.h",
    "content": "#ifndef PwmIn_H\n#define PwmIn_H\n\n#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"PwmIn.h\"\n#include \"PwmIn.pio.h\"\n\n// class that reads PWM pulses on max 4 pins\nclass PwmIn\n{\npublic:\n    // constructor\n    PwmIn(uint *pin_list, uint num_of_pin);\n    // read pulsewidth and period for one pulse\n    void read_PWM(float *readings, uint pin);\n    // read only the pulsewidth\n    float read_PW(uint pin);\n    // read only the duty cycle\n    float read_DC(uint pin);\n    // read only the period\n    float read_P(uint pin);\n\nprivate:\n    // set the irq handler\n    static void pio_irq_handler()\n    {\n        int state_machine = -1;\n        // check which IRQ was raised:\n        for (int i = 0; i < 4; i++)\n        {\n            if (pio0_hw->irq & 1<<i)\n            {\n                // clear interrupt\n                pio0_hw->irq = 1 << i;\n                // read pulse width from the FIFO\n                pulsewidth[i] = pio_sm_get(pio, i);\n                // read low period from the FIFO\n                period[i] = pio_sm_get(pio, i);\n                // clear interrupt\n                pio0_hw->irq = 1 << i;\n            }\n        }\n    }\n    // the pio instance\n    static PIO pio;\n    // the pins and number of pins\n    uint _num_of_pins;\n    // data about the PWM input measured in pio clock cycles\n    static uint32_t pulsewidth[4], period[4];\n};\n\n#endif"
  },
  {
    "path": "PwmIn/PwmIn_4pins/PwmIn.pio",
    "content": "\n.program PwmIn\n\n; algorithm:\n\n; loop:\n;    reset y, the 'timer' for the pulsewidth (high period)\n;    reset x, the 'timer' for the low period. (high + low = period)\n;    wait for a 0, then wait for a 1: this is the rising edge\n;    loop: \n;       decrement y timer\n;       test for falling edge \n;    y timer value is the pulse width (actually, (0xFFFFFFFF - x)*2/125MHz is the pulse width)\n;    loop:\n;       test for rising edge\n;       decrement x timer\n;    x timer is the low period (actually, (0xFFFFFFFF - x)*2/125MHz is the low period)\n;    push both y and x to the Rx FIFO\n;    notify via relative IRQ\n\n.wrap_target\nstart:\n    mov y ~NULL         ; start with the value 0xFFFFFFFF\n    mov x ~NULL         ; start with the value 0xFFFFFFFF\n    wait 0 pin 0        ; wait for a 0\n    wait 1 pin 0        ; wait for a 1, now we really have the rising edge\ntimer_hp:               ; loop for high period\n    jmp y-- test        ; count down for pulse width\n    jmp start           ; timer has reached 0, stop count down of pulse, restart\ntest:\n    jmp pin timer_hp    ; test if the pin is still 1, if so, continue counting down\ntimer_lp:               ; loop for low period\n    jmp pin timerstop   ; if the pin has become 1, the period is over, stop count down\n    jmp x-- timer_lp    ; if not: count down\n    jmp start           ; timer has reached 0, stop count down of low period, restart\ntimerstop:\n    mov ISR ~y          ; move the value ~y to the ISR: the high period (pulsewidth) (0xFFFFFFFF-x)\n    push noblock        ; push the ISR into the Rx FIFO\n    mov ISR ~x          ; move the value ~x to the ISR: the low period (0xFFFFFFFF-x)\n    push noblock        ; push the ISR into the Rx FIFO\n    irq 0 rel           ; notify the c-program via IRQ\n.wrap\n"
  },
  {
    "path": "PwmIn/README.md",
    "content": "# PWM input using the Raspberry Pi Pico PIO \n\n# UPDATE:\nThere was a problem with getting the PwmIn to read more than one pin. So, I've made an update. This update can read pwm signals from up to 4 pins (you could probably go up to 8 if pio1 is also used). It uses (relative) irq in the pio code to signal the c-code that new data is available. See the directory PwmIn_4pins.\n\n# ORIGINAL:\n\nMost microcontrollers have hardware to produce Pulse Width Modulation (PWM) signals. But sometimes it is useful to be able to read PWM signals and determine the period, pulse width and duty cycle.\n\n(Warning: questionable statement) Although the RP2040 seems to be capable of measuring pulse width and period, this takes more CPU cycles than letting a PIO state machine take care of it.\n\nBased on the method to measure pulses with PIO code as described [here](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/HCSR04), a PWM Input can be made.\n\n## Algorithm\n\nIn pseudo-code the algorithm is as follows:\n\n```\n    loop:\n        reset y, the 'timer' for the pulsewidth (high period)\n        reset x, the 'timer' for the low period. (high + low = period)\n        wait for a 0, then wait for a 1: this is the rising edge\n        loop: \n           decrement y timer\n           test for falling edge \n        y timer value is the pulse width (actually, (0xFFFFFFFF - y)*2/125MHz is the pulse width)\n        loop:\n           test for rising edge\n           decrement x timer\n        x timer is the low period (actually, (0xFFFFFFFF - x)*2/125MHz is the low period)\n        push both y and x to the Rx FIFO\n```\n"
  },
  {
    "path": "README.md",
    "content": "# Content\n\nThis repository contains bits and pieces that I made while trying to figure out how the Raspberry Pi Pico state machines and the Programmable Input/Output (PIO) work.\n\n[Here](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/handy_bits_and_pieces) I have written down the reusable 'tricks' I use in several of the projects listed below.\n\nThe following projects are contained in this repository:\n\n## State machine emulator\nThe problem with the state machines is that debuggers do not give the insight I need when writing code for a sm. I typically write some code, upload it to the pico and find that it doesn't do what I want it to do. Instead of guessing what I do wrong, I would like to see the values of e.g. the registers when the sm is executing. So, I made an [emulator](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/state_machine_emulator).\n\n## Two independently running state machines \n[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Two_sm_simple) is just an example of two state machines running independently. Nothing special about it, but I had to do it.\n\n## Two independently running state machines, one gets disabled temporarily\n[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Two_sm_one_disabled) is an example of two state machines running independently, but one gets disabled temporarily.\n\n## Two independently running state machines, synchronized via irq, one gets disabled temporarily\n[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Two_sm_one_disabled_with_irq) is an example of two state machines synchronized via setting and clearing an irq, one gets disabled temporarily.\n\n## State machine writes into a buffer via DMA\n[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/sm_to_dma_to_buffer) is an example of a state machine using DMA (Direct Memory Access) to write into a buffer.\n\n## State Machine -> DMA -> State Machine -> DMA -> Buffer\n[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/sm_to_dma_to_sm_to_dma_to_buffer) is an example where one state machine writes via DMA to another state machine whose output is put into a buffer via another DMA channel.\n\n## Communicating values between state machines \nThe [RP2040 Datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) states that \"State machines can not communicate data\". Or can they ... Yes they can, in several ways, [including via GPIO pins](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Value_communication_between_two_sm_via_pins).\n\n## Use the ISR for rotational shifting\nNormally if the ISR shifts via the `IN` instruction, the bits that come out of the ISR go to cyber space, never to be heard from again. Sometimes it is handy to have rotational shifting. [Right shifting works fine, but left shifting needs some trickery](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Rotational_shift_ISR).\n\n## 4x4 button matrix using PIO code\n[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/button_matrix_4x4) reads a 4x4 button matrix using PIO code for the Raspberry Pico and returns the button pressed.\n\n## Button debouncer using PIO code\nWhen using a GPIO to read noisy input, such as a mechanical button, a [software debouncer](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Button-debouncer) makes sure that only after the input signal has stabilized, the code will read the new value. \n\n## PWM input using PIO code\nMost microcontrollers have hardware to produce Pulse Width Modulation (PWM) signals. But sometimes it is useful to be able to [read PWM signals](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/PwmIn) and determine the period, pulse width and duty cycle.\n\n## Rotary encoder using PIO code\n[This software](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Rotary_encoder) reads an optical rotary encoder with very clean signals on its output using PIO code.\n\n## HC-SR04 using the PIO code\n[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/HCSR04) reads the HC-SR04, an ultrasonic distance measurement unit.\n\n## Ws2812 led strip with 120 pixels \n[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/ws2812_led_strip_120) is my take on how to control a ws2812 led strip with 120 pixels\n\n## multiply two numbers \n[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/multiplication) multiplies two numbers.\n\n## Two pio programs in one file\nI wanted to see how I could use two pio programs in one file and use them from within the c/c++ program, see [here.](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/two_pio_programs_one_file) \n\n## 1-wire protocol for one device \n[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Limited_1_wire) is a pio implementation of the 1-wire protocol.\n\n## Blow out a(n) LED\n[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/blow_out_a_LED) is a remake of the wonderfull little thingy made by Paul Dietz: blow on a LED to make it go out! Really!\n\n## Counting pulses in a pulse train separated by a pause \n[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/count_pulses_with_pause) can be used for protocols where the data is encoded by a number of pulses in a pulse train followed by a pause.\nE.g. the LMT01 temperature sensor uses this, [see](https://www.reddit.com/r/raspberrypipico/comments/nis1ew/made_a_pulse_counter_for_the_lmt01_temperature/).\n\n## Subroutines in pioasm\n[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/subroutines) shows that subroutines in pioasm can be a thing and can - in some cases - be used to do more with the limited memory space than is possible with just writing the code in one program.\n\n## Read the SBUS protocol with (and without!) pio code\nThe SBUS protocol is typically used in Radio Controlled cars, drones, etc. If you want to read this protocol from a RC receiver in order to manipulate the data before setting motors and servos, you can use [this code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/SBUS).\n\n## LED panel using PIO state machine and Direct Memory Access\n[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/ledpanel) shows how a pio state machine can drive led panels. It is made for\ntwo 64x64 led panels connected to form a 64 row x 128 column panel. There are 16 brightness levels for each Red, Green and Blue of each pixel, allowing many \ncolors. In its present form it updates the panel at about 144 Hz at standard clock settings (=125MHz.)\n"
  },
  {
    "path": "Rotary_encoder/CMakeLists.txt",
    "content": "add_executable(pio_rotary_encoder)\n\npico_generate_pio_header(pio_rotary_encoder ${CMAKE_CURRENT_LIST_DIR}/pio_rotary_encoder.pio)\n\ntarget_sources(pio_rotary_encoder PRIVATE pio_rotary_encoder.cpp)\n\ntarget_link_libraries(pio_rotary_encoder PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_enable_stdio_usb(pio_rotary_encoder 0)\npico_enable_stdio_uart(pio_rotary_encoder 1)\n\npico_add_extra_outputs(pio_rotary_encoder)\n\n# add url via pico_set_program_url\nexample_auto_set_url(pio_rotary_encoder)\n\n\n"
  },
  {
    "path": "Rotary_encoder/README.md",
    "content": "# Rotary encoder for Raspberry Pi Pico using PIO code\n\nThis software reads a rotary encoder with the Raspberry Pi Pico using PIO code. \nThe rotary encoder that is used is an optical encoder with very clean signals on its output, called A and B, so debouncing of these signals is unnecessary.\n\n<img src=\"rotary_encoder.jpg\" width=\"300\">\n\nThe specific encoder I use gives 600 pulses per 360 degrees rotation. For each pulse, 4 signal changes are measured, see the next figure from [wikipedia](https://en.wikipedia.org/wiki/Rotary_encoder#/media/File:Quadrature_Diagram.svg), so in total 2400 transitions are made for a full rotation.\n\n<img src=\"Quadrature_Diagram.png\" width=\"300\">\n\nI have also tried a cheap simple rotary encoder, but only when turning very slowly does the result make any sense.\n\n## Other code\nThis isn't the first code to read a rotary encoder using the PIO, see e.g. [pimoroni-pico](https://github.com/pimoroni/pimoroni-pico/blob/encoder-pio/drivers/encoder-pio/encoder.pio), which does timing and rudimentary debouncing. And it lets you choose non-consecutive pins. \n\nBut this code uses interrupts in the PIO code to signal rotations to the C++ code. And maybe this can best be considered as an exercise on how to make something useful with:\n- 19 `jmp` instructions (one hidden in a `mov exec`)\n- 3 `in` instructions\n- 2 `irq` instructions\n- 1 `out` instruction\n- 1 `mov` instruction (well ... technically 2)\n\nBut seriously, I wanted to play with a jump table in PIO code. This means setting the `.origin` to make sure the jump table is at a fixed position in instruction memory (0), and setting the initial program counter with `pio_sm_init(pio, sm, 16, &c)` to start at instruction location 16, i.e. after the jump table. It also means that the `mov exec` instruction is used to make a jump to the jump table.\n\n## Explanation of PIO code\nThe first 15 addresses are a list of jumps, forming a jump table, that is later used to raise either an IRQ that signals a clockwise rotation or an IRQ to signal a counter clockwise rotation. See below.\n\nThe important steps are explained in the figure below. There the program line number, the contents of the Output Shift Register (OSR) and the Input Shift Register (ISR) are shown.\n\nThe program starts at line 16, which is only used as an initialization of the program: the two pins (whose values are denoted as A and B) of the rotary encoder are read into the ISR. The two rightmost bits are now AB. The other bits in the ISR do not play a role at this moment and are denoted as 'x'. \n\nThe next step, line 17, is the actual start of the program: the content of the ISR is copied to the OSR. The two bits AB are to be used as the previous values, and are therefore denoted as A' and B'. \n\nLine 18 clears the ISR because the zero valued bits are needed later. Then, in two steps, the ISR gets shifted in the old values, A'B' from the OSR, and reads in two new readings of the rotary encoder (AB). \n\nNow, at line 20, the 16 left most bits of the ISR contain: 000000000000A'B'AB.\nAccording to the [datasheet of the rp2040](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) an unconditional jmp instruction without delay or side-set is, apart from the address to jump to, all zeros. And that is exactly what is now contained in the ISR: a jmp instruction to address 0A'B'AB. This is always an address in the first 16 program addresses (0-15). The PIO has an instruction to interpret the content of the ISR as an instruction and execute it: on line 20 the `mov exec ISR` does exactly this.\n\n<img src=\"code_explanation.png\" width=\"800\">\n\n## Jump table\n\nThe first 16 instructions form a jump table. The addresses represent the 12 legal transitions and 4 error transitions that can be found in the output of the rotary encoder. As described above, the address that is jumped to is represented by the two old values and the two current values of the output: A'B'AB. The 16 transitions are:\n\n```\nA'B'A B = meaning;                                                action\n--------------------------------------------------------------------------------------\n0 0 0 0 = transition from 00 to 00 = no change in reading;        do a jump to line 17\n0 0 0 1 = transition from 00 to 01 = clockwise rotation;          do a jump to CW  \n0 0 1 0 = transition from 00 to 10 = counter clockwise rotation;  do a jump to CCW\n0 0 1 1 = transition from 00 to 11 = error;                       do a jump to line 17\n0 1 0 0 = transition from 01 to 00 = counter clockwise rotation;  do a jump to CCW \n0 1 0 1 = transition from 01 to 01 = no change in reading;        do a jump to line 17\n0 1 1 0 = transition from 01 to 10 = error;                       do a jump to line 17\n0 1 1 1 = transition from 01 to 11 = clockwise rotation;          do a jump to CW  \n1 0 0 0 = transition from 10 to 00 = clockwise rotation;          do a jump to CW  \n1 0 0 1 = transition from 10 to 01 = error;                       do a jump to line 17\n1 0 1 0 = transition from 10 to 10 = no change in reading;        do a jump to line 17\n1 0 1 1 = transition from 10 to 11 = counter clockwise rotation;  do a jump to CCW\n1 1 0 0 = transition from 11 to 00 = error;                       do a jump to line 17\n1 1 0 1 = transition from 11 to 01 = counter clockwise rotation;  do a jump to CCW\n1 1 1 0 = transition from 11 to 10 = clockwise rotation;          do a jump to CW  \n1 1 1 1 = transition from 11 to 11 = no change in reading;        do a jump to line 17\n```\nThe jump to `CW` is a piece of code that sets `irq 0` and then jumps to line 17, the jump to `CCW` sets `irq 1` and then jumps to line 17.\nIn the C++ code, the `irq 0` causes a counter called `rotation` to be increased, the `irq 1` causes it to be decreased.\n"
  },
  {
    "path": "Rotary_encoder/pio_rotary_encoder.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/irq.h\"\n\n#include \"pio_rotary_encoder.pio.h\"\n\n// class to read the rotation of the rotary encoder\nclass RotaryEncoder\n{\npublic:\n    // constructor\n    // rotary_encoder_A is the pin for the A of the rotary encoder.\n    // The B of the rotary encoder has to be connected to the next GPIO.\n    RotaryEncoder(uint rotary_encoder_A)\n    {\n        uint8_t rotary_encoder_B = rotary_encoder_A + 1;\n        // pio 0 is used\n        PIO pio = pio0;\n        // state machine 0\n        uint8_t sm = 0;\n        // configure the used pins as input with pull up\n        pio_gpio_init(pio, rotary_encoder_A);\n        gpio_set_pulls(rotary_encoder_A, true, false);\n        pio_gpio_init(pio, rotary_encoder_B);\n        gpio_set_pulls(rotary_encoder_B, true, false);\n        // load the pio program into the pio memory\n        uint offset = pio_add_program(pio, &pio_rotary_encoder_program);\n        // make a sm config\n        pio_sm_config c = pio_rotary_encoder_program_get_default_config(offset);\n        // set the 'in' pins\n        sm_config_set_in_pins(&c, rotary_encoder_A);\n        // set shift to left: bits shifted by 'in' enter at the least\n        // significant bit (LSB), no autopush\n        sm_config_set_in_shift(&c, false, false, 0);\n        // set the IRQ handler\n        irq_set_exclusive_handler(PIO0_IRQ_0, pio_irq_handler);\n        // enable the IRQ\n        irq_set_enabled(PIO0_IRQ_0, true);\n        pio0_hw->inte0 = PIO_IRQ0_INTE_SM0_BITS | PIO_IRQ0_INTE_SM1_BITS;\n        // init the sm.\n        // Note: the program starts after the jump table -> initial_pc = 16\n        pio_sm_init(pio, sm, 16, &c);\n        // enable the sm\n        pio_sm_set_enabled(pio, sm, true);\n    }\n\n    // set the current rotation to a specific value\n    void set_rotation(int _rotation)\n    {\n        rotation = _rotation;\n    }\n\n    // get the current rotation\n    int get_rotation(void)\n    {\n        return rotation;\n    }\n\nprivate:\n    static void pio_irq_handler()\n    {\n        // test if irq 0 was raised\n        if (pio0_hw->irq & 1)\n        {\n            rotation = rotation - 1;\n        }\n        // test if irq 1 was raised\n        if (pio0_hw->irq & 2)\n        {\n            rotation = rotation + 1;\n        }\n        // clear both interrupts\n        pio0_hw->irq = 3;\n    }\n\n    // the pio instance\n    PIO pio;\n    // the state machine\n    uint sm;\n    // the current location of rotation\n    static int rotation;\n};\n\n// Initialize static member of class Rotary_encoder\nint RotaryEncoder::rotation = 0;\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // the A of the rotary encoder is connected to GPIO 16, B to GPIO 17\n    RotaryEncoder my_encoder(16);\n    // initialize the rotatry encoder rotation as 0\n    my_encoder.set_rotation(0);\n    // infinite loop to print the current rotation\n    while (true)\n    {\n        printf(\"rotation=%d\\n\", my_encoder.get_rotation());\n    }\n}"
  },
  {
    "path": "Rotary_encoder/pio_rotary_encoder.pio",
    "content": "\n.program pio_rotary_encoder\n.wrap_target\n.origin 0        ; The jump table has to start at 0\n                 ; it contains the correct jumps for each of the 16  \n                 ; combination of 4 bits formed by A'B'AB\n                 ; A = current reading of pin_A of the rotary encoder\n                 ; A' = previous reading of pin_A of the rotary encoder\n                 ; B = current reading of pin_B of the rotary encoder\n                 ; B' = previous reading of pin_B of the rotary encoder\n    jmp read     ; 0000 = from 00 to 00 = no change in reading\n    jmp CW       ; 0001 = from 00 to 01 = clockwise rotation\n    jmp CCW      ; 0010 = from 00 to 10 = counter clockwise rotation\n    jmp read     ; 0011 = from 00 to 11 = error\n\n    jmp CCW      ; 0100 = from 01 to 00 = counter clockwise rotation\n    jmp read     ; 0101 = from 01 to 01 = no change in reading \n    jmp read     ; 0110 = from 01 to 10 = error\n    jmp CW       ; 0111 = from 01 to 11 = clockwise rotation\n \n    jmp CW       ; 1000 = from 10 to 00 = clockwise rotation\n    jmp read     ; 1001 = from 10 to 01 = error\n    jmp read     ; 1010 = from 10 to 10 = no change in reading \n    jmp CCW      ; 1011 = from 10 to 11 = counter clockwise rotation\n \n    jmp read     ; 1100 = from 11 to 00 = error\n    jmp CCW      ; 1101 = from 11 to 01 = counter clockwise rotation\n    jmp CW       ; 1110 = from 11 to 10 = clockwise rotation\n    jmp read     ; 1111 = from 11 to 11 = no change in reading \n\npc_start:        ; this is the entry point for the program\n    in pins 2    ; read the current values of A and B and use \n                 ; them to initialize the previous values (A'B')\nread:\n    mov OSR ISR  ; the OSR is (after the next instruction) used to shift \n                 ; the two bits with the previous values into the ISR\n    out ISR 2    ; shift the previous value into the ISR. This also sets\n                 ; all other bits in the ISR to 0\n    in pins 2    ; shift the current value into the ISR\n                 ; the 16 LSB of the ISR now contain 000000000000A'B'AB\n                 ; this represents a jmp instruction to the address A'B'AB \n    mov exec ISR ; do the jmp encoded in the ISR\nCW:              ; a clockwise rotation was detected\n    irq 0        ; signal a clockwise rotation via an IRQ\n    jmp read     ; jump to reading the current values of A and B\nCCW:             ; a counter clockwise rotation was detected\n    irq 1        ; signal a counter clockwise rotation via an IRQ\n;    jmp read    ; jump to reading the current values of A and B.\n                 ; the jmp isn't needed because of the .wrap, and the first \n                 ; statement of the program happens to be a jmp read\n.wrap"
  },
  {
    "path": "Rotational_shift_ISR/CMakeLists.txt",
    "content": "add_executable(rotational_shift_ISR)\n\npico_generate_pio_header(rotational_shift_ISR ${CMAKE_CURRENT_LIST_DIR}/rotational_shift_ISR.pio)\n\ntarget_sources(rotational_shift_ISR PRIVATE rotational_shift_ISR.cpp)\n\ntarget_link_libraries(rotational_shift_ISR PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(rotational_shift_ISR)\n\n# add url via pico_set_program_url\nexample_auto_set_url(rotational_shift_ISR)\n\n\n"
  },
  {
    "path": "Rotational_shift_ISR/README.md",
    "content": "# Use the ISR for rotational shifting\nNormally if the ISR shifts via the `IN` instruction, the bits that come out of the ISR go to cyber space, never to be heard from again. Sometimes it is handy to have rotational shifting. The [figure below](https://en.wikipedia.org/wiki/Circular_shift#Applications) shows a rotational right shift of one bit. Note that the Least Significant Bit (LSB) shifts to the Most Significant Bit (MSB).\n\n![](rotational_shifting.png)\n\nIn PIO code the ISR can be used to shift bits in the direction given by the c-command `sm_config_set_in_shift(&c, direction, false, 0)` where direction=true means shift to the right, and direction=false means shift to the left. The `IN` instruction has an option to shift bits into the ISR from the ISR itself via `IN ISR <Bit count>` instruction. This opens up the possibility of making it shift rotationally. The algorithm used by `IN ISR <Bit count>` is as follows:\n\nshift the ISR for `<Bit count>` bits in the direction specified by `sm_config_set_in_shift`, and shift in the LSBs of the original ISR value.\n\n## Right shifting\nFor right shifting this works just fine, it basically comes down to every bit that would normally go to cyber space is saved and shifted in as the MSB. In the figure below the two steps are shown for eight bits (the ISR has 32) and the instruction `IN ISR 2`:\n\n![](right_shift.png)\n\nOn the top the ISR has the bit value 00110010, in the middle it has shifted two bits, and on the bottom the two LSBs of the original ISR are shifted in the same order as the MSBs (these are the same as the two bits shifted out in the blue circle). This results in a bit value 10001100, which is exactly a two bits rotational shift of the ISR.\n\n## Left shifting\nFor left-shifting, however, this does not work well, consider the following example:\n\n![](left_shift.png)\n\nThe ISR at the start has a bit value of 01010010, and after shifting to the left, the two LSBs of the original ISR are shifted in on the left. These are not the two bits that were shifted out (blue circle)! So, no rotational shifting to the left.\n\nIt is possible to obtain true rotational left shifting. The trick is that the direction of shifting must be set to right shifting: `sm_config_set_in_shift(&c, true, false, 0)` and the following PIO code can then be used to left shift the ISR for two bits (other `<Bit counts>` are of course also possible):\n```\nmov ISR :: ISR\nin ISR 2\nmov ISR :: ISR\n```\nIn the above PIO code, the `::` symbols reverses the bit order in the ISR, effectively turning the correctly working right shift into a left-shift."
  },
  {
    "path": "Rotational_shift_ISR/rotational_shift_ISR.cpp",
    "content": "\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"rotational_shift_ISR.pio.h\"\n\n// handy function to print the value of 'number' in bits\nvoid printBits(uint32_t number)\n{ // go through each of the bits, starting with the Most Significant Bit (MSB)\n    for (int i = 31; i >= 0; i--)\n    {\n        // test of that bit is set, print \"1\" if set, or \"0\" if not\n        if ((number & (1 << i)) > 0)\n        {\n            printf(\"1\");\n        }\n        else\n        {\n            printf(\"0\");\n        }\n    }\n}\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // pio 0 is used\n    PIO pio = pio0;\n    // state machine 0 and 1\n    uint sm = 0;\n    // load the sm0 program into the pio memory\n    uint offset = pio_add_program(pio, &rotational_shift_ISR_program);\n    // make a sm config\n    pio_sm_config c = rotational_shift_ISR_program_get_default_config(offset);\n    // set shift direction\n    // NOTE: IT IS SET TO SHIFT RIGHT! EVEN THOUGH THE PIO CODE SHIFTS RIGHT AND LEFT\n    sm_config_set_in_shift(&c, true, false, 0);\n    // init the pio sm with the config\n    pio_sm_init(pio, sm, offset, &c);\n    // enable the sm\n    pio_sm_set_enabled(pio, sm, true);\n\n    while (true)\n    {\n        // push a random number into the Tx FIFO\n        pio_sm_put(pio, sm, rand());\n        // read it back (first wait until something is written into the Rx FIFO)\n        while (pio_sm_is_rx_fifo_empty(pio, sm))\n            ;\n        printf(\"S)\\t\");\n        printBits(pio_sm_get(pio, sm));\n        printf(\"\\n\");\n\n        // let the PIO code do 32 shifts to the right\n        for (int i = 1; i <= 32; i++)\n        {\n            while (pio_sm_is_rx_fifo_empty(pio, sm))\n                ;\n            printf(\"R%d)\\t\", i);\n            printBits(pio_sm_get(pio, sm));\n            printf(\"\\n\");\n        }\n\n        // let the PIO code do 32 shifts to the left\n        for (int i = 1; i <= 32; i++)\n        {\n            while (pio_sm_is_rx_fifo_empty(pio, sm))\n                ;\n            printf(\"L%d)\\t\", i);\n            printBits(pio_sm_get(pio, sm));\n            printf(\"\\n\");\n        }\n\n        printf(\"----------------------------------------------------------\\n\");\n    }\n}"
  },
  {
    "path": "Rotational_shift_ISR/rotational_shift_ISR.pio",
    "content": "/*\n    I was playing with the instruction `IN ISR <Bit count>` and wanted to make\n    a rotational shifter. For right-shifting this works fine, but for left-shifting\n    two instructions `mov ISR :: ISR` are required.\n*/\n\n.program rotational_shift_ISR\n.wrap_target\n    pull block        ; get a value to shift around\n    mov ISR OSR       ; put it in the ISR\n    mov y ISR         ; save the ISR\n    push block        ; push the starting value back to the user program\n    mov ISR y         ; restore the ISR\n \n    set x 31          ; prepare to do a full rotation to the right\nloop_right:\n    in ISR 1          ; shift to right\n    mov y ISR         ; save the ISR\n    push block        ; the ISR is cleared\n    mov ISR y         ; restore the ISR\n    jmp x-- loop_right\n\n    set x 31          ; prepare to do a full rotation to the left\nloop_left:\n    mov ISR :: ISR    ; reverse bit order in ISR\n    in ISR 1          ; shift right (but after the next reverse its actually left)\n    mov ISR :: ISR    ; reverse bit order in ISR\n    mov y ISR         ; save the ISR\n    push block        ; the ISR is cleared\n    mov ISR y         ; restore the ISR\n    jmp x-- loop_left    \n.wrap\n"
  },
  {
    "path": "SBUS/CMakeLists.txt",
    "content": "add_executable(SBUS)\n\npico_generate_pio_header(SBUS ${CMAKE_CURRENT_LIST_DIR}/SBUS.pio)\n\ntarget_sources(SBUS PRIVATE SBUS.cpp)\n\ntarget_link_libraries(SBUS PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(SBUS)\n\n\n"
  },
  {
    "path": "SBUS/README.md",
    "content": "# Read the SBUS protocol with (and without!) pio code\n\nThe SBUS protocol is typically used in Radio Controlled cars, drones, etc.\nIf you want to read this protocol from a RC receiver in order to manipulate the data before setting motors and servos, you can use this code.\n\nThe SBUS protocol is basically an uart Rx with inverted input, 100000 baud rate, a parity bit, and two stop bits, see [here](https://github.com/bolderflight/sbus).\nThe basis for the PIO code is the RPI pico example for [pio rx](https://github.com/raspberrypi/pico-examples/blob/master/pio/uart_rx/uart_rx.pio).\nThe parsing of the received data is done following to [this](https://platformio.org/lib/show/5622/Bolder%20Flight%20Systems%20SBUS).\nIt even does parity checking in the pio code!\n\n## Update\n\nSince the SBUS protocol is \"normal\" uart I originally started looking at using the hardware uart, but couldn't find an invert option. Turns out you have to invert the GPIO used for input or output. The updated code that doesn't use pio but uart hardware is [here](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/SBUS/gpio_invert).\n"
  },
  {
    "path": "SBUS/SBUS.cpp",
    "content": "/*\n    Read SBUS protocol, typically used in Radio Controlled cars, drones, etc.\n    SBUS data packets consists of 25 8-bit bytes starting with 0x0F and ending with two 0x00\n    The signal is inverted, has an even parity bit and two stop-bits, \n    see: https://github.com/bolderflight/sbus\n*/\n#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n\n#include \"SBUS.pio.h\"\n\n// The baud rate for the SBUS protocol is 100000\n#define SERIAL_BAUD 100000\n// The RC receiver is attached to pin 5\n#define PIO_RX_PIN 5\n// the max amount of data in a SBUS data packet\n#define MAX_DATA_ITEMS 25\n\n// function to decode the received SBUS data to actual steering commands\nvoid decode(uint8_t *data)\n{\n    uint16_t channels[16];\n    // see https://platformio.org/lib/show/5622/Bolder%20Flight%20Systems%20SBUS\n    channels[0] = (uint16_t)((data[0] | data[1] << 8) & 0x07FF);\n    channels[1] = (uint16_t)((data[1] >> 3 | data[2] << 5) & 0x07FF);\n    channels[2] = (uint16_t)((data[2] >> 6 | data[3] << 2 | data[4] << 10) & 0x07FF);\n    channels[3] = (uint16_t)((data[4] >> 1 | data[5] << 7) & 0x07FF);\n    channels[4] = (uint16_t)((data[5] >> 4 | data[6] << 4) & 0x07FF);\n    channels[5] = (uint16_t)((data[6] >> 7 | data[7] << 1 | data[8] << 9) & 0x07FF);\n    channels[6] = (uint16_t)((data[8] >> 2 | data[9] << 6) & 0x07FF);\n    channels[7] = (uint16_t)((data[9] >> 5 | data[10] << 3) & 0x07FF);\n    channels[8] = (uint16_t)((data[11] | data[12] << 8) & 0x07FF);\n    channels[9] = (uint16_t)((data[12] >> 3 | data[13] << 5) & 0x07FF);\n    channels[10] = (uint16_t)((data[13] >> 6 | data[14] << 2 | data[15] << 10) & 0x07FF);\n    channels[11] = (uint16_t)((data[15] >> 1 | data[16] << 7) & 0x07FF);\n    channels[12] = (uint16_t)((data[16] >> 4 | data[17] << 4) & 0x07FF);\n    channels[13] = (uint16_t)((data[17] >> 7 | data[18] << 1 | data[19] << 9) & 0x07FF);\n    channels[14] = (uint16_t)((data[19] >> 2 | data[20] << 6) & 0x07FF);\n    channels[15] = (uint16_t)((data[20] >> 5 | data[21] << 3) & 0x07FF);\n    for (int i = 0; i < 16; i++)\n    {\n        printf(\"%d \\t\", channels[i]);\n    }\n    printf(\"\\n\");\n}\n\n// Main function\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n\n    // Set up the state machine to receive RC SBUS data\n    PIO pio = pio0;\n    uint sm = 0;\n    uint offset = pio_add_program(pio, &sbus_program);\n    pio_sm_config c = sbus_program_get_default_config(offset);\n\n    // configure the pin to receive the SBUS data\n    pio_sm_set_consecutive_pindirs(pio, sm, PIO_RX_PIN, 1, false);\n    pio_gpio_init(pio, PIO_RX_PIN);\n    gpio_pull_down(PIO_RX_PIN);\n    sm_config_set_in_pins(&c, PIO_RX_PIN); // for WAIT, IN\n    sm_config_set_jmp_pin(&c, PIO_RX_PIN); // for JMP\n    // Shift to right, autopull disabled\n    sm_config_set_in_shift(&c, true, false, 32);\n    // Shift to left, autopull disabled\n    sm_config_set_out_shift(&c, false, false, 32);\n    // Deeper FIFO as we're not doing any TX\n    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n    // SM transmits 1 bit per 8 execution cycles.\n    float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_BAUD);\n    sm_config_set_clkdiv(&c, div);\n    // init and enable the sm\n    pio_sm_init(pio, sm, offset, &c);\n    pio_sm_set_enabled(pio, sm, true);\n\n    uint8_t index = 0;\n    uint8_t data[MAX_DATA_ITEMS];\n\n    // continuously get the SBUS data from the pio and decode it\n    while (true)\n    {\n        // Note: \n        // Although there is sufficient time for receiving and decoding because the clkdiv \n        // and join (see above), too much printing will cause data loss\n        while (pio_sm_is_rx_fifo_empty(pio, sm))\n            tight_loop_contents();\n        uint8_t data_item = pio_sm_get(pio, sm) >> 24;\n        // search for 0x0f: the start marker\n        if (data_item == 0x0f)\n            index = 0;\n        else if (index < MAX_DATA_ITEMS)\n        {\n            // test if the first end marker is read\n            if (data_item == 0)\n            {\n                // read the second end marker\n                while (pio_sm_is_rx_fifo_empty(pio, sm))\n                    tight_loop_contents();\n                data_item = pio_sm_get(pio, sm) >> 24;\n                // the second end marker should also be 0\n                if (data_item != 0)\n                    printf(\"Error, second end marker not found\\n\");\n                else\n                    // if all data is received, decode it\n                    if (index == 22)\n                        decode(data);\n            }\n            else\n                // if not start or end marker, add it to the received data\n                data[index++] = data_item;\n        }\n        else\n            printf(\"error\\n\");\n    }\n}\n"
  },
  {
    "path": "SBUS/SBUS.pio",
    "content": ".program sbus\n\n; Start with the situation that the number of ones received is zero (an even number)\n; Read the 8 data bits and the parity bit (so, 9 bits in total)\n; The even parity bit has to make the total number of ones odd!\n; The code starts in the top half (the wrong parity part); if a one is observed it switches \n; to the bottom half (the correct parity part). If again a one is observed, it switches back to the top half. \n; Etc, switching back and forth every time a one is observed.\n\n; the bits arrive LSB first, thus right-shift it into the ISR\n\n.wrap_target\nstart:\n    wait 1 pin 0            ; Stall until start bit is asserted\n    set x, 8 [10]           ; read 8 data bits plus the parity bit. The parity bit should make a odd total of ones.\n                            ; delay 12 cycles incl wait, set (8 cycles for the start bit, then 4 to get halfway the first data bit)\neven_ones:\n    in pins, 1              ; Shift data bit into ISR\n    mov OSR ISR             ; test if it is a one or zero: copy it to the OSR and left-shift one bit out\n    out y 1\n    jmp !y even_ones_1      ; jump if y == 0, the number of ones doesn't change\n    jmp odd_ones_2          ; the number of ones did change! Go to the bottom part (correct parity)\neven_ones_1:\n    jmp x-- even_ones [3]   ; Loop 9 times (8 data bits + the parity bit), each loop iteration is 8 cycles\n    ; no need to wait for the stop bits, just stop already, the parity is wrong\n    jmp end_and_restart\neven_ones_2:                ; the only reason this is here is the [2] compared to [3] a couple of lines back\n                            ; because after the \"jmp !y\" an extra jmp is made, which costs 1 cycle\n    jmp x-- even_ones [2]   ; Loop 9 times (8 data bits + the parity bit), each loop iteration is 8 cycles\n    ; no need to wait for the stop bits, just stop already, the parity is wrong\n    jmp end_and_restart\n;---------------------------------------------------------\n; If the reading of 9 bits ends above: the parity is wrong\n;\n; If the reading of 9 bits ends below: the parity is correct\n;---------------------------------------------------------\nodd_ones:\n    in pins, 1              ; Shift data bit into ISR\n    mov OSR ISR             ; copy it to the OSR to get that bit into the y register\n    out y 1                 ; \n    jmp !y odd_ones_1       ; jump if y == 0, the number of ones doesn't change\n    jmp even_ones_2         ; the number of ones did change! Go to the top part (wrong parity)\nodd_ones_1:\n    jmp x-- odd_ones [3]    ; Loop 9 times (8 data bits + the parity bit), each loop iteration is 8 cycles\n    ; all data has been read, no need to wait for the stop bits. The parity is correct\ncorrect:\n    mov OSR ISR             ; the parity bit is still in ISR, remove it: copy to OSR, left-shift the parity bit out\n    out NULL 1\n    mov ISR ~OSR            ; invert the data in the OSR and copy it to the ISR\n    push                    ; push the ISR to the Rx FIFO\nend_and_restart:\n    wait 0 pin 0            ; wait for line to return to idle state.\n    jmp start               ; Don't push data if we didn't see correct framing.\nodd_ones_2:\n    jmp x-- odd_ones [2]    ; Loop 9 times (8 data bits + the parity bit), each loop iteration is 8 cycles\n    jmp correct             ; all data has been read, no need to wait for the stop bits. The parity is correct\n\n"
  },
  {
    "path": "SBUS/gpio_invert/CMakeLists.txt",
    "content": "add_executable(SBUS)\n\ntarget_sources(SBUS PRIVATE SBUS.cpp)\n\ntarget_link_libraries(SBUS PRIVATE\n        pico_stdlib\n        hardware_uart\n        )\n\npico_add_extra_outputs(SBUS)\n\n\n"
  },
  {
    "path": "SBUS/gpio_invert/SBUS.cpp",
    "content": "/*\n    Read SBUS protocol, typically used in Radio Controlled cars, drones, etc.\n    SBUS data packets consists of 25 8-bit bytes starting with 0x0F and ending with two 0x00\n    The signal is inverted, has an even parity bit and two stop-bits, \n    see: https://github.com/bolderflight/sbus\n*/\n#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/uart.h\"\n\n// the max amount of data in a SBUS data packet (including start marker 0x0F and end marker 0x00)\n#define MAX_DATA_ITEMS 25\n// the baud rate for the SBUS protocol is 100000\n#define SERIAL_BAUD 100000\n// the RC receiver is attached to pin 5\n#define RX_PIN 5\n// the UART output (not used)\n// #define TX_PIN 4\n// the uart (there are two: uart0 and uart1, pin5 belongs to uart1)\n#define UART_ID uart1\n// number of data bits; SBUS has 8\n#define DATA_BITS 8\n// number of stop bits; SBUS has 2\n#define STOP_BITS 2\n// parity setting; SBUS has even parity\n#define PARITY UART_PARITY_EVEN\n\n// function to decode the received SBUS data to actual steering commands\nvoid decode(uint8_t *data)\n{\n    // the SBUS data encodes 16 channels\n    uint16_t channels[16];\n    // see https://platformio.org/lib/show/5622/Bolder%20Flight%20Systems%20SBUS\n    channels[0] = (uint16_t)((data[0] | data[1] << 8) & 0x07FF);\n    channels[1] = (uint16_t)((data[1] >> 3 | data[2] << 5) & 0x07FF);\n    channels[2] = (uint16_t)((data[2] >> 6 | data[3] << 2 | data[4] << 10) & 0x07FF);\n    channels[3] = (uint16_t)((data[4] >> 1 | data[5] << 7) & 0x07FF);\n    channels[4] = (uint16_t)((data[5] >> 4 | data[6] << 4) & 0x07FF);\n    channels[5] = (uint16_t)((data[6] >> 7 | data[7] << 1 | data[8] << 9) & 0x07FF);\n    channels[6] = (uint16_t)((data[8] >> 2 | data[9] << 6) & 0x07FF);\n    channels[7] = (uint16_t)((data[9] >> 5 | data[10] << 3) & 0x07FF);\n    channels[8] = (uint16_t)((data[11] | data[12] << 8) & 0x07FF);\n    channels[9] = (uint16_t)((data[12] >> 3 | data[13] << 5) & 0x07FF);\n    channels[10] = (uint16_t)((data[13] >> 6 | data[14] << 2 | data[15] << 10) & 0x07FF);\n    channels[11] = (uint16_t)((data[15] >> 1 | data[16] << 7) & 0x07FF);\n    channels[12] = (uint16_t)((data[16] >> 4 | data[17] << 4) & 0x07FF);\n    channels[13] = (uint16_t)((data[17] >> 7 | data[18] << 1 | data[19] << 9) & 0x07FF);\n    channels[14] = (uint16_t)((data[19] >> 2 | data[20] << 6) & 0x07FF);\n    channels[15] = (uint16_t)((data[20] >> 5 | data[21] << 3) & 0x07FF);\n    for (int i = 0; i < 16; i++)\n    {\n        printf(\"%d \\t\", channels[i]);\n    }\n    printf(\"\\n\");\n}\n\n// Main function\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // initialize the uart\n    uart_init(UART_ID, SERIAL_BAUD);\n    // set the gpio function to uart\n    gpio_set_function(RX_PIN, GPIO_FUNC_UART);\n    // set data bits, stop_bits and the parity of the uart\n    uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY);\n    // set the input gpio pin to inverted\n    gpio_set_inover(RX_PIN, GPIO_OVERRIDE_INVERT);\n\n    // index of read SBUS data item\n    uint8_t index = 0;\n    // array to store read SBUS data\n    uint8_t data[MAX_DATA_ITEMS];\n\n    // continuously get the SBUS data from the pio and decode it\n    while (true)\n    {   // read a SBUS data item\n        uint8_t data_item = (uint8_t)uart_getc(UART_ID);\n        // store it\n        data[index++] = data_item;\n        // check for the start markter\n        if (data_item == 0x0F)\n        {\n            // if start marker has been found: decode the existing SBUS data\n            decode(data);\n            // start over\n            index = 0;\n        }\n    }\n}\n"
  },
  {
    "path": "Two_sm_one_disabled/CMakeLists.txt",
    "content": "add_executable(two_sm_one_disabled)\n\npico_generate_pio_header(two_sm_one_disabled ${CMAKE_CURRENT_LIST_DIR}/two_sm_one_disabled.pio)\n\ntarget_sources(two_sm_one_disabled PRIVATE two_sm_one_disabled.cpp)\n\ntarget_link_libraries(two_sm_one_disabled PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(two_sm_one_disabled)\n\n# add url via pico_set_program_url\nexample_auto_set_url(two_sm_one_disabled)\n\n\n"
  },
  {
    "path": "Two_sm_one_disabled/README.md",
    "content": "# Two independently running state machines, one gets disabled temporarily\n\nThis is an example of two state machines (sm) running independently, but one, sm1, gets disabled temporarily.\n\nThe sm0 repeatedly counts up from 0 to 0xFFFFFFFF (actually, it counts down!), the other repeatedly counts down from 31. They both send their current values to the c-program via their Rx FIFOs.\n\nIn the c-program, sm1 gets disabled for a number of iterations. The output is somewhat interesting:\n```\n0 = 293088      1 = 31          ; \n0 = 293089      1 = 30          ; \n0 = 293090      1 = 29          ; \n0 = 293091      1 = 28  <---    ; Here the sm1 is disabled, but it still has 4 words in the Rx FIFO\n0 = 293092      1 = 27  <---    ; \n0 = 293093      1 = 26  <---    ; \n0 = 293094      1 = 25  <---    ; Last valid Rx FIFO data\n0 = 293095      1 = 28  <---    ; Here the Rx FIFO of sm1 is empty. Oddly, we get 28, not 25 \n0 = 293096      1 = 28  <---    ; \n0 = 293097      1 = 28  <---    ; \n0 = 293098      1 = 28  <---    ; \n0 = 293099      1 = 28  <---    ; \n0 = 293100      1 = 28  <---    ; \n0 = 293101      1 = 24          ; The sm is started again, and as expected continues where it was stopped\n0 = 293102      1 = 23          ; \n0 = 293103      1 = 22          ; \n0 = 293104      1 = 21          ; \n```\nWhile sm0 keeps counting up, sm1 is disabled. At that moment there are still 4 data items in the FIFO. After these 4 have been read and printed, reading the FIFO keeps giving data (28, the entry at the time of disabling sm1). I know you should always check whether there is still valid data in the FIFO before reading it, but hey now we know what happens.\n"
  },
  {
    "path": "Two_sm_one_disabled/two_sm_one_disabled.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"two_sm_one_disabled.pio.h\"\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n\n    // pio 0 is used\n    PIO pio = pio0;\n    // state machine 0 and 1\n    uint sm0 = 0;\n    uint sm1 = 1;\n    // load the sm0 program into the pio memory\n    uint offset0 = pio_add_program(pio, &tester0_program);\n    // load the sm1 program into the pio memory\n    uint offset1 = pio_add_program(pio, &tester1_program);\n    // make a sm config\n    pio_sm_config c0 = tester0_program_get_default_config(offset0);\n    pio_sm_config c1 = tester1_program_get_default_config(offset1);\n    // init the pio sm0 with the config\n    pio_sm_init(pio, sm0, offset0, &c0);\n    pio_sm_init(pio, sm1, offset1, &c1);\n    // enable the sm\n    pio_sm_set_enabled(pio, sm0, true);\n    pio_sm_set_enabled(pio, sm1, true);\n\n    // infinite loop. But after 100 times normal printf, sm1 is disabled for 10 iterations and then enabled again\n    int i = 0;\n    while (true)\n    {\n        i++;\n        if (i < 100)\n        {\n            // normal printing 100 times\n            printf(\"0 = %d \\t\\t1 = %d\\n\", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));\n        }\n        else if (i < 110)\n        {\n            // sm1 is disabled, printing for 10 times\n            pio_sm_set_enabled(pio, sm1, false);\n            printf(\"0 = %d \\t\\t1 = %d  <---\\n\", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));\n        }\n        else\n        {\n            // enable sm1 again, and repeat counting\n            pio_sm_set_enabled(pio, sm1, true);\n            i = 0;\n        }\n    }\n}"
  },
  {
    "path": "Two_sm_one_disabled/two_sm_one_disabled.pio",
    "content": "\n.program tester0\n\nstart0:\n    mov x ~NULL         ; start with 0xFFFFFFFF\npush_it0:\n    mov ISR ~x          ; push the value into the Rx FIFO \n                        ; (make it look like counting up)\n    push block\n    jmp x-- push_it0    ; count down\n    jmp start0\n\n.program tester1\n\nstart1:\n    set x 31            ; start with 31\npush_it1:\n    mov ISR x           ; push the x value into the Rx FIFO\n    push block\n    jmp x-- push_it1    ; count down\n    jmp start1\n\n\n\n "
  },
  {
    "path": "Two_sm_one_disabled_with_irq/CMakeLists.txt",
    "content": "add_executable(two_sm_one_disabled_with_irq)\n\npico_generate_pio_header(two_sm_one_disabled_with_irq ${CMAKE_CURRENT_LIST_DIR}/two_sm_one_disabled_with_irq.pio)\n\ntarget_sources(two_sm_one_disabled_with_irq PRIVATE two_sm_one_disabled_with_irq.cpp)\n\ntarget_link_libraries(two_sm_one_disabled_with_irq PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(two_sm_one_disabled_with_irq)\n\n# add url via pico_set_program_url\nexample_auto_set_url(two_sm_one_disabled_with_irq)\n\n\n"
  },
  {
    "path": "Two_sm_one_disabled_with_irq/README.md",
    "content": "# Two independently running state machines, synchronized via irq, one is temporarily disabled\n\nThis is an example of two state machines (sm) synchronizing their execution via setting and clearing an irq. At some point sm1 gets disabled. Because they are synchronized, sm0 also stops.\n\nOne sm repeatedly counts up from 0 to 0xFFFFFFFF (actually, it counts down!), the other repeatedly counts down from 31. They both send their current values to the c-program via their Rx FIFOs.\n\n```\n0 = 198         1 = 24          ;\n0 = 199         1 = 23          ;\n0 = 200         1 = 22          ; \n0 = 201         1 = 21  <---    ; The sm1 is disabled\n0 = 202         1 = 20  <---    ; The sm0 stops producing output (irq wait)\n0 = 199         1 = 19  <---    ;\n0 = 199         1 = 18  <---    ; Last valid sm1 Rx FIFO data\n0 = 199         1 = 21  <---    ;\n0 = 199         1 = 21  <---    ;\n0 = 199         1 = 21  <---    ;\n0 = 199         1 = 21  <---    ;\n0 = 199         1 = 21  <---    ;\n0 = 199         1 = 21  <---    ;\n0 = 203         1 = 17          ; The sm1 started again, sm0 follows\n0 = 204         1 = 16          ;\n0 = 205         1 = 15          ;\n```"
  },
  {
    "path": "Two_sm_one_disabled_with_irq/two_sm_one_disabled_with_irq.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"two_sm_one_disabled_with_irq.pio.h\"\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n\n    // pio 0 is used\n    PIO pio = pio0;\n    // state machine 0 and 1\n    uint sm0 = 0;\n    uint sm1 = 1;\n    // load the sm0 program into the pio memory\n    uint offset0 = pio_add_program(pio, &tester0_program);\n    // load the sm1 program into the pio memory\n    uint offset1 = pio_add_program(pio, &tester1_program);\n    // make a sm config\n    pio_sm_config c0 = tester0_program_get_default_config(offset0);\n    pio_sm_config c1 = tester1_program_get_default_config(offset1);\n    // init the pio sm0 with the config\n    pio_sm_init(pio, sm0, offset0, &c0);\n    pio_sm_init(pio, sm1, offset1, &c1);\n    // enable the sm\n    pio_sm_set_enabled(pio, sm0, true);\n    pio_sm_set_enabled(pio, sm1, true);\n\n    // infinite loop. But after 100 times normal printf, sm1 is disabled for 10 iterations and then enabled again\n    int i = 0;\n    while (true)\n    {\n        i++;\n        if (i < 100)\n        {\n            // normal printing 100 times\n            printf(\"0 = %d \\t\\t1 = %d\\n\", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));\n        }\n        else if (i < 110)\n        {\n            // sm1 is disabled, printing for 10 times\n            pio_sm_set_enabled(pio, sm1, false);\n            printf(\"0 = %d \\t\\t1 = %d  <---\\n\", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));\n        }\n        else\n        {\n            // enable sm1 again, and repeat counting\n            pio_sm_set_enabled(pio, sm1, true);\n            i = 0;\n        }\n    }\n}"
  },
  {
    "path": "Two_sm_one_disabled_with_irq/two_sm_one_disabled_with_irq.pio",
    "content": "\n.program tester0\n\nstart0:\n    mov x ~NULL         ; start with 0xFFFFFFFF\npush_it0:\n    mov ISR ~x          ; push the value into the Rx FIFO (make it look like counting up)\n    push block\n    irq wait 0          ; set irq 0 and wait for it to clear\n    jmp x-- push_it0    ; count down\n    jmp start0\n\n\n.program tester1\n\nstart1:\n    set x 31            ; start with 31\npush_it1:\n    mov ISR x           ; push the value into the Rx FIFO\n    push block\n    irq clear 0         ; clear irq 0\n    jmp x-- push_it1    ; count down\n    jmp start1\n\n\n\n "
  },
  {
    "path": "Two_sm_simple/CMakeLists.txt",
    "content": "add_executable(two_sm_simple)\n\npico_generate_pio_header(two_sm_simple ${CMAKE_CURRENT_LIST_DIR}/two_sm_simple.pio)\n\ntarget_sources(two_sm_simple PRIVATE two_sm_simple.cpp)\n\ntarget_link_libraries(two_sm_simple PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(two_sm_simple)\n\n# add url via pico_set_program_url\nexample_auto_set_url(two_sm_simple)\n\n\n"
  },
  {
    "path": "Two_sm_simple/README.md",
    "content": "# Two independently running state machines \n\nThis is just an example of two state machines (sm) running independently. Nothing special about it, but I had to do it.\n\nOne sm repeatedly counts up from 0 to 0xFFFFFFFF (actually, it counts down!), the other repeatedly counts down from 31. They both send their current values to the c-program via their Rx FIFOs."
  },
  {
    "path": "Two_sm_simple/two_sm_simple.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"two_sm_simple.pio.h\"\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n\n    // pio 0 is used\n    PIO pio = pio0;\n    // state machine 0 and 1\n    uint sm0 = 0;\n    uint sm1 = 1;\n    // load the sm0 program into the pio memory\n    uint offset0 = pio_add_program(pio, &tester0_program);\n    // load the sm1 program into the pio memory\n    uint offset1 = pio_add_program(pio, &tester1_program);\n    // make a sm config\n    pio_sm_config c0 = tester0_program_get_default_config(offset0);\n    pio_sm_config c1 = tester1_program_get_default_config(offset1);\n    // init the pio sm0 with the config\n    pio_sm_init(pio, sm0, offset0, &c0);\n    pio_sm_init(pio, sm1, offset1, &c1);\n    // enable the sm\n    pio_sm_set_enabled(pio, sm0, true);\n    pio_sm_set_enabled(pio, sm1, true);\n\n    while (true)\n    {\n        printf(\"0 = %d \\t\\t1 = %d\\n\", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));\n    }\n}"
  },
  {
    "path": "Two_sm_simple/two_sm_simple.pio",
    "content": "\n.program tester0\n\nstart0:\n    mov x ~NULL         ; start at 0xFFFFFFFF and count down\npush_it0:\n    mov ISR ~x          ; make it appear that it counts up\n    push block          ; push the current value into the Rx FIFO\n    jmp x-- push_it0\n    jmp start0\n\n.program tester1\n\nstart1:\n    set x 31            ; start at 31\npush_it1:\n    mov ISR x\n    push block          ; push the current value into the Rx FIFO\n    jmp x-- push_it1\n    jmp start1\n\n\n\n "
  },
  {
    "path": "Value_communication_between_two_sm_via_pins/CMakeLists.txt",
    "content": "add_executable(value_communication_between_two_sm_via_pins)\n\npico_generate_pio_header(value_communication_between_two_sm_via_pins ${CMAKE_CURRENT_LIST_DIR}/value_communication_between_two_sm_via_pins.pio)\n\ntarget_sources(value_communication_between_two_sm_via_pins PRIVATE value_communication_between_two_sm_via_pins.cpp)\n\ntarget_link_libraries(value_communication_between_two_sm_via_pins PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(value_communication_between_two_sm_via_pins)\n\n# add url via pico_set_program_url\nexample_auto_set_url(value_communication_between_two_sm_via_pins)\n\n\n"
  },
  {
    "path": "Value_communication_between_two_sm_via_pins/README.md",
    "content": "# Sending values between state machines \n\nThe [RP2040 Datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) states that \"State machines can not communicate data\". Or can they ...\n\nOf course they can, but not directly from within the state machines (sm). They can communicate via their FIFOs if the c program reads the Rx FIFO of one sm and puts it into the Tx FIFO of the other sm. They can even do this via DMA, which is [another example](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/sm_to_dma_to_sm_to_dma_to_buffer) in this repository.\n\nBut they can also communicate using the GPIO pins.\n\nIn this example, one sm sets a GPIO pin to a value, and the other reads that pin to obtain the value. They use an irq to make sure that they work synchronized. In this example only one GPIO pin is used, but they could of course use more pins to communicate more bits at one time.\n"
  },
  {
    "path": "Value_communication_between_two_sm_via_pins/value_communication_between_two_sm_via_pins.cpp",
    "content": "\n#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"value_communication_between_two_sm_via_pins.pio.h\"\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // pio 0 is used\n    PIO pio = pio0;\n    // state machine 0 and 1\n    uint sm0 = 0;\n    uint sm1 = 1;\n    // use pin 15 for both output of sm0 and input to sm1\n    uint pin = 15;\n    pio_gpio_init(pio, pin);\n    pio_sm_set_consecutive_pindirs(pio, sm0, pin, 1, true);\n    // These pins are used by sm0 and sm1.\n    // As it turns out, the pin needs to be set once: to output!\n    // even though sm1 uses them as input.\n\n    // load the sm0 program into the pio memory\n    uint offset0 = pio_add_program(pio, &tester0_program);\n    // load the sm1 program into the pio memory\n    uint offset1 = pio_add_program(pio, &tester1_program);\n    // make a sm config\n    pio_sm_config c0 = tester0_program_get_default_config(offset0);\n    pio_sm_config c1 = tester1_program_get_default_config(offset1);\n\n    // set shift direction\n    sm_config_set_in_shift(&c0, false, false, 0);\n    // set shift direction\n    sm_config_set_in_shift(&c1, false, false, 0);\n\n    // sm0 produces output using 'set', sm1 reads it using 'in'\n    // set the 'set' pins for sm0\n    sm_config_set_set_pins(&c0, pin, 1);\n    // set the 'in' pins for sm1\n    sm_config_set_in_pins(&c1, pin);\n\n    // init the pio sm0 with config c0\n    pio_sm_init(pio, sm0, offset0, &c0);\n    // init the pio sm1 with config c1\n    pio_sm_init(pio, sm1, offset1, &c1);\n    // enable the state machines\n    pio_sm_set_enabled(pio, sm0, true);\n    pio_sm_set_enabled(pio, sm1, true);\n\n    // end with infinite loop of printing what is send by sm0 and received by sm1\n    while (true)\n    {\n        printf(\"value send by sm0 = %d\\n\", pio_sm_get(pio, sm0));\n        printf(\"value received by sm1 = %d\\n\", pio_sm_get(pio, sm1));\n    }\n}"
  },
  {
    "path": "Value_communication_between_two_sm_via_pins/value_communication_between_two_sm_via_pins.pio",
    "content": "\n.program tester0\n\n.wrap_target\n    set pins 0      ; set output on pin to 0 (this is read by tester1, see below)\n    mov ISR NULL    ; signal this to the c++ program via the Rx FIFO\n    push block      \n    irq wait 0      ; wait for the other sm to read the value of the pin\n    \n    set pins 1      ; the same as above, but now for the value 1\n    set x 1         ; push 1 into the ISR and then Rx FIFO\n    mov ISR x\n    push block\n    irq wait 0\n.wrap\n\n\n.program tester1\n\n.wrap_target\n    wait irq 0      ; wait for the other sm to set a value on the pin\n    in pins 1       ; read the value\n    push block      ; signal this to the c++ program via the FIFO\n    irq clear 0     ; clear the irq\n.wrap\n\n\n\n "
  },
  {
    "path": "Z80/CMakeLists.txt",
    "content": "add_executable(Z80)\n\npico_generate_pio_header(Z80 ${CMAKE_CURRENT_LIST_DIR}/Z80.pio)\n\ntarget_sources(Z80 PRIVATE Z80.c)\n\ntarget_link_libraries(Z80 PRIVATE\n        pico_stdlib\n        hardware_pio\n        hardware_irq\n        hardware_vreg\n        )\n\n\n"
  },
  {
    "path": "Z80/README.md",
    "content": "# Z80 read and write from bus \n\nThis is a WIP.\n\nIt uses pio code to read and write to a Z80 bus.\n\nThe information needed to write this code was provided by [siriokds](https://github.com/siriokds)."
  },
  {
    "path": "Z80/Z80.c",
    "content": "#include \"stdio.h\"\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/vreg.h\"\n// the .pio.h file also defines (in this order): D0 (8 bits), A0 (8 bits), RW, WR, DIR, OE\n#include \"Z80.pio.h\"\n\n// PIO and state machine\nPIO pio;\nuint sm_rd;\nuint sm_wr;\nuint offset_rd;\nuint offset_wr;\n// Configure the pio state machine\nvoid configure_pio_sm()\n{\n    // pio 0 is used\n    pio = pio0;\n    // state machine 0 is used.\n    sm_rd = 0;\n    sm_wr = 1;\n\n    // load the sm_rd program into the pio memory\n    offset_rd = pio_add_program(pio, &Z80_read_program);\n    // load the sm program into the pio memory\n    offset_wr = pio_add_program(pio, &Z80_write_program);\n    // make a sm_rd config\n    pio_sm_config smc_rd = Z80_read_program_get_default_config(offset_rd);\n    // make a sm_wr config\n    pio_sm_config smc_wr = Z80_write_program_get_default_config(offset_wr);\n\n    // function select: this allows pio to set output on a gpio\n    for (int i = D0; i <= OE; i++)\n        pio_gpio_init(pio, i);\n\n    // TODO: for testing purposes, set data and address lines to 0 via pull down, normally this would be set externally\n    for (int i = D0; i < RD; i++)\n        gpio_set_pulls(i, false, true);\n\n    // set initial pindirs: D0 - D7 are (also) output\n    pio_sm_set_consecutive_pindirs(pio, sm_rd, D0, 8, true);\n    pio_sm_set_consecutive_pindirs(pio, sm_wr, D0, 8, true);\n    // set initial pindirs: A0 - A7 are input\n    pio_sm_set_consecutive_pindirs(pio, sm_rd, A0, 8, false);\n    pio_sm_set_consecutive_pindirs(pio, sm_wr, A0, 8, false);\n    // set initial pindirs: RD, WR are input\n    pio_sm_set_consecutive_pindirs(pio, sm_rd, RD, 1, false);\n    pio_sm_set_consecutive_pindirs(pio, sm_wr, RD, 1, false);\n    pio_sm_set_consecutive_pindirs(pio, sm_rd, WR, 1, false);\n    pio_sm_set_consecutive_pindirs(pio, sm_wr, WR, 1, false);\n    // set initial pindirs: DIR and OE are output\n    pio_sm_set_consecutive_pindirs(pio, sm_rd, DIR, 1, true);\n    pio_sm_set_consecutive_pindirs(pio, sm_wr, DIR, 1, true);\n    pio_sm_set_consecutive_pindirs(pio, sm_rd, OE, 1, true);\n    pio_sm_set_consecutive_pindirs(pio, sm_wr, OE, 1, true);\n\n    // pio 'in' pins: inputs start at the first data bit (D0)\n    sm_config_set_in_pins(&smc_rd, D0);\n    // pio 'out' pins: data D0-D7 can also be output\n    sm_config_set_out_pins(&smc_rd, D0, 8);\n    // pio 'set' pins: DIR (LSB) and OE (MSB)\n    sm_config_set_set_pins(&smc_rd, DIR, 2);\n    // Reading from RxFIFO: Shift to left, autopull disabled\n    sm_config_set_in_shift(&smc_rd, false, false, 32);\n    // Writing to TxFIFO: Shift to right, autopull disabled\n    sm_config_set_out_shift(&smc_rd, true, false, 32);\n    // pio 'in' pins: inputs start at the first data bit (D0)\n    sm_config_set_in_pins(&smc_wr, D0);\n    // pio 'out' pins: data D0-D7 can also be output\n    sm_config_set_out_pins(&smc_wr, D0, 8);\n    // pio 'set' pins: DIR (LSB) and OE (MSB)\n    sm_config_set_set_pins(&smc_wr, DIR, 2);\n    // Reading from RxFIFO: Shift to left, autopull disabled\n    sm_config_set_in_shift(&smc_wr, false, false, 32);\n    // Writing to TxFIFO: Shift to right, autopull disabled\n    sm_config_set_out_shift(&smc_wr, true, false, 32);\n    // set clock to about 4Mhz TODO: set correct frequency\n    // sm_config_set_clkdiv(&smc, 31);\n    // sm_config_set_clkdiv(&smc_rd, 16);\n    // sm_config_set_clkdiv(&smc_wr, 16);\n    // init the pio sm with the config\n    pio_sm_init(pio, sm_rd, offset_rd, &smc_rd);\n    pio_sm_init(pio, sm_wr, offset_wr, &smc_wr);\n    // enable the sm\n    pio_sm_set_enabled(pio, sm_rd, true);\n    pio_sm_set_enabled(pio, sm_wr, true);\n}\n\n// in the current version the address bus is only 8 bits\n// the first half is ROM, the second half is RAM\nuint8_t ROM[128];\nuint8_t RAM[128];\n\nint main()\n{\n    // set the voltage a bit higher than default\n    vreg_set_voltage(0b1100); // 1.15v\n    // overclock to 270MHz\n    set_sys_clock_khz(270000, true);\n\n    // fill the ROM and RAM with numbers\n    uint8_t num;\n    for (num=0; num<128; num++) {\n        ROM[num] = num;\n        RAM[num] = 128+num;\n    }\n\n    // needed for printf\n    stdio_init_all();\n\n    // initialize the state machine\n    configure_pio_sm();\n\n    // print the gpio assignments\n    printf(\"D0=%d\\n\", D0);\n    printf(\"A0=%d\\n\", A0);\n    printf(\"RD=%d\\n\", RD);\n    printf(\"WR=%d\\n\", WR);\n    printf(\"DIR=%d\\n\", DIR);\n    printf(\"OE=%d\\n\", OE);\n\n    // a counter to simulate the data in memory (or after some calculations)\n    uint16_t counter = 0;\n    // the received data from pio is a combination of the data and the address\n    uint32_t addr_data;\n    // the data part\n    uint8_t data;\n    // the address part\n    uint16_t address;\n    // used for checking whether the sm has returned to the default state\n    uint8_t current_pc;\n\n    // start in the default situation\n    // Note: if this is in reality set by an external system (e.g. via pullup/down resistors, ths can be removed)\n    pio_sm_exec(pio, sm_rd, offset_rd + Z80_read_offset_set_default);\n    pio_sm_exec(pio, sm_wr, offset_wr + Z80_write_offset_set_default);\n\n    uint bitoffs;\n    const uint32_t mask = PIO_FLEVEL_RX0_BITS >> PIO_FLEVEL_RX0_LSB;\n\n    while (1)\n    {\n        // test if there is something in RxFIFO of sm0 (a read)\n        bitoffs = PIO_FLEVEL_RX0_LSB + sm_rd * (PIO_FLEVEL_RX1_LSB - PIO_FLEVEL_RX0_LSB);\n        if ((pio->flevel >> bitoffs) & mask > 0)\n        {\n            // get the data from the RxFIFO\n            addr_data = pio->rxf[sm_rd];\n            // the lowest 8 bits are the data and the next 8 bits are the address\n            // data = addr_data & 0xFF;\n            address = addr_data >> 8;\n\n            // determine if it is ROM or RAM, then get the value from ROM or RAM\n            if (address<128) {\n                data = ROM[address];\n                printf(\"read ROM address %d results in %d\\n\", address, data);\n            } else {\n                data = RAM[address-128];\n                printf(\"read RAM address %d results in %d\\n\", address-128, data);\n            }\n            // send the data to the pio for writing it to the data bits\n            pio->txf[sm_rd] = data;\n        }\n\n        // test if there is something in RxFIFO of sm1 (a write)\n        bitoffs = PIO_FLEVEL_RX0_LSB + sm_wr * (PIO_FLEVEL_RX1_LSB - PIO_FLEVEL_RX0_LSB);\n        if ((pio->flevel >> bitoffs) & mask > 0)\n        {\n            // get the data from the RxFIFO\n            addr_data = pio->rxf[sm_wr];\n            data = addr_data & 0xFF;\n            address = addr_data >> 8;\n            if (address<128) {\n                printf(\"ROM not writable\\n\");\n            } else {\n                RAM[address-128] = data;\n                printf(\"Wrote %d to RAM address %d\\n\", data, address-128);\n            }\n\n        }\n    }\n}"
  },
  {
    "path": "Z80/Z80.pio",
    "content": "; This pio programm allows a Z80 bus to:\n; - read from the RPI pico by providing an address (read_data)\n; - write a value to the RPI pico to a memory address (write_data)\n\n\n    ; set the pin assignment:\n    ; The starting point of the 8 bit Data D0 - D7\n.define PUBLIC D0 2\n    ; The starting point of the 16 bit Address A0 - A15\n.define PUBLIC A0 (D0 + 8)\n    ; Read bus flag\n.define PUBLIC RD (A0 + 8 + 0)\n    ; Write bus flag\n.define PUBLIC WR (A0 + 8 + 1)\n    ; Direction of level shifter\n.define PUBLIC DIR (A0 + 8 + 2)\n    ; Output enable of level shifter\n.define PUBLIC OE (A0 + 8 + 3)\n\n\n.program Z80_read\n\npublic set_default:\n    ; set pins:  MSB = OE, LSB = DIR, so 0b10 is OE=1 and DIR=0\n    ; set the default: OE=1 and DIR=0\n    set pins 0b10\n    ; wait for RD to become 0\n    wait 0 GPIO RD \n    ; read the data (8 bits) and addres (8 bits)\n    in pins 16\n    ; push the data and address to the RxFIFO\n    push\n    ; get the new data to set as output\n    pull block\n    ; set the data bits on the bus \n    out pins 8\n    ; set OE=0 and DIR=0\n    set pins 0b00\n    ; wait for RD to become 1, then wait 3 cycles\n    wait 1 GPIO RD [3]\n    ; go back to the default state\n    jmp set_default\n\n.program Z80_write\n\npublic set_default:\n    ; set pins:  MSB = OE, LSB = DIR, so 0b10 is OE=1 and DIR=0\n    ; set the default: OE=1 and DIR=0\n    set pins 0b10\n    ; wait for WR to become 0\n    wait 0 GPIO WR\n    ; set OE=0 and DIR=0\n    set pins 0b00\n    ; read the data (8 bits) and addres (8 bits)\n    in pins 16\n    ; push the data and address to the RxFIFO\n    push\n    ; wait for WR to become 1, then wait 3 cycles\n    wait 1 GPIO WR [3]\n    ; go back to the default state\n    jmp set_default\n"
  },
  {
    "path": "blow_out_a_LED/CMakeLists.txt",
    "content": "add_executable(blow_led)\n\ntarget_sources(blow_led PRIVATE blow_led.cpp)\n\ntarget_link_libraries(blow_led PRIVATE\n        pico_stdlib\n        hardware_adc\n        hardware_dma\n        )\n        \n\n"
  },
  {
    "path": "blow_out_a_LED/README.md",
    "content": "# Blow out a(n) LED\nThis is remake of the wonderful little thingy made by Paul Dietz. See this [Hackaday article](https://hackaday.com/2018/08/21/an-led-you-can-blow-out-with-no-added-sensor/) and the [github code](https://github.com/paulhdietz/LEDSensors/blob/master/_07_BlowOutLED/_07_BlowOutLED.ino).\n\nIt lights a LED and when you blow on it, it switches off for one second, then lights again. Imagine blowing out a LED!\n\nFrom the hackaday article:\n\"Turning the LED on warms it up and blowing on it cools it off, causing measurable \nchanges in the voltage drop across the device. The change isn’t much — only a \nhandful of millivolts — but the effect is consistent and can be measured. \"\n\nWhere his code is rather simple and elegant, mine is convoluted.\nWhy? Because I wanted to play with the DMA sniffer!\n\nThe DMA sniffer allows summing of all values that pass through the DMA.\nSo, there is no need to sum the values yourself afterwards.\n\nThe code does the following:\n- it turns the LED on for at least 1s to warm it up\n- it starts a repeat timer to call \"repeating_timer_callback\"\n- in that callback the adc is read NUM_ADC_SAMPLES times through dma, \n  the samples are summed by the dma sniffer\n- if the read summed value deviates sufficiently from the average, a flag is set to switch the LED off\n- an array of obtained summed values is kept up to date to determine the average of the sums\n- in the main loop the flag is checked and when set the LED goes off for 1s and then 1s on to warm it up\n"
  },
  {
    "path": "blow_out_a_LED/blow_led.cpp",
    "content": "/*\n\nThis is remake of the wonderful little thingy made by Paul Dietz. See:\nhttps://hackaday.com/2018/08/21/an-led-you-can-blow-out-with-no-added-sensor/\nhttps://github.com/paulhdietz/LEDSensors/blob/master/_07_BlowOutLED/_07_BlowOutLED.ino\n\nFrom the hackaday article:\n\"Turning the LED on warms it up and blowing on it cools it off, causing measurable \nchanges in the voltage drop across the device. The change isn’t much — only a \nhandful of millivolts — but the effect is consistent and can be measured. \"\n\nWhere his code is rather simple and elegant, mine is convoluted.\nWhy? Because I wanted to play with the DMA sniffer!\n\nThe DMA sniffer allows summing of all values that pass through the DMA.\nSo, there is no need to sum the values yourself afterwards.\n\nThe code does the following:\n- it turns the LED on for at least 1s to warm it up\n- it starts a repeat timer to call \"repeating_timer_callback\"\n- in that callback the adc is read NUM_ADC_SAMPLES times through dma, \n  the samples are summed by the dma sniffer\n- an array of the obtained sums is kept up to date to determine the average of the sums\n- if the read summed value deviates sufficiently from the average, a flag is set to switch the LED off\n- in the main loop the flag is checked and when set the LED goes off for 1s and then 1s on to warm it up\n*/\n\n#include <stdio.h>\n#include <math.h>\n#include \"pico/stdlib.h\"\n#include \"hardware/gpio.h\"\n#include \"hardware/adc.h\"\n#include \"hardware/dma.h\"\n\n// LED Connections: PLUS pin - resistor - ADC pin - LED - GND\n#define PLUS 22\n// pin for ADC goes between resistor and LED\n#define MEASURE 26\n// Number of adc samples to take. Only used in dma_channel_configure\n#define NUM_ADC_SAMPLES 512\n// Number of samples to determine the average\n#define NUM_AV_SAMPLES 64\n// Minimum jump for blow out\n// depends on the resistor value and type of LED\n#define BLOWN 400\n\n// the array with NUM_ADC_SAMPLES summed adc samples\nfloat summed_adc_values[NUM_AV_SAMPLES];\n// keeps track of where in the summed_adc_values array the new value is to be written\nuint8_t sample_num;\n// dma channel number\nuint dma_chan;\n// the total of the summed_adc_values array\nfloat total;\n// flag to indicate it the led should be turned off\nbool light_out;\n// the variable into which the dma channel dumps all adc readings!\nuint32_t temp;\n\n// the callback function that is called automatically (see add_repeating_timer_ms below)\nbool repeating_timer_callback(struct repeating_timer *t)\n{\n    // start the sniff sum at 0\n    dma_hw->sniff_data = 0;\n    // start the dma_channel. NOTE: it doesn't write to a buffer, it writes to a single variable\n    // the samples are summed by the sniffer functionality!\n    dma_channel_set_write_addr(dma_chan, &temp, true);\n    // wait for it to finish\n    dma_channel_wait_for_finish_blocking(dma_chan);\n\n    // check whether the deviations of the current sample deviates more than BLOWN from the average\n    // the value of BLOWN depends on many things, e.g. resistor, type of LED, NUM_AV_SAMPLES, and NUM_ADC_SAMPLES\n    // the print statement can be used to determine which BLOWN value will work for you\n    // printf(\"%f\\n\", (total / 64.) - summed_adc_values[sample_num]);\n    if (((total / 64.) - dma_hw->sniff_data) > BLOWN)\n        light_out = true;\n\n    // save the summed adc values in the summed_adc_values array\n    // total is the sum of all elements in that array\n    total -= summed_adc_values[sample_num];\n    summed_adc_values[sample_num] = dma_hw->sniff_data;\n    total += summed_adc_values[sample_num];\n\n    // make the array a ring\n    if (++sample_num == NUM_AV_SAMPLES)\n        sample_num = 0;\n\n    return true;\n}\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n\n    // switch on the LED and let it warm up\n    gpio_init(PLUS);\n    gpio_set_dir(PLUS, GPIO_OUT);\n    gpio_put(PLUS, 1);\n    sleep_ms(1000);\n\n    // initialize some variables\n    light_out = false;\n    sample_num = 0;\n    total = 0;\n    for (int i = 0; i < NUM_AV_SAMPLES; i++)\n        summed_adc_values[i] = 0;\n\n    // ========== ADC setup ===================\n    adc_init();\n    adc_fifo_setup(\n        true,  // Write each completed conversion to the sample FIFO\n        true,  // Enable DMA data request (DREQ)\n        1,     // DREQ (and IRQ) asserted when at least 1 sample present\n        false, // disable ERR bit\n        false  // Do not shift each sample to 8 bits when pushing to FIFO, i.e. keep it 12 bit resolution\n    );\n    // Divisor of 0 -> full speed.\n    adc_set_clkdiv(0);\n    // start the adc\n    adc_run(true);\n\n    // ========== DMA setup ===================\n    // Set up the DMA to start transferring data as soon as it appears in FIFO\n    dma_chan = dma_claim_unused_channel(true);\n    dma_channel_config cfg = dma_channel_get_default_config(dma_chan);\n    // data size is 32 bits (it should be at least 12)\n    channel_config_set_transfer_data_size(&cfg, DMA_SIZE_32);\n    // Reading from AND WRITING to a constant address\n    channel_config_set_read_increment(&cfg, false);\n    channel_config_set_write_increment(&cfg, false);\n    // Pace transfers based on availability of ADC samples\n    channel_config_set_dreq(&cfg, DREQ_ADC);\n    // configure sniff to automatically sum (mode=0xf) all the data\n    dma_sniffer_enable(dma_chan, 0xf, true);\n    // enable sniff\n    channel_config_set_sniff_enable(&cfg, true);\n    // configure the channel\n    dma_channel_configure(dma_chan, &cfg,\n                          NULL,            // dst\n                          &adc_hw->fifo,   // src\n                          NUM_ADC_SAMPLES, // transfer count\n                          false            // start immediately\n    );\n\n    // ===========TIMER setup =================\n    struct repeating_timer timer;\n    add_repeating_timer_ms(10, repeating_timer_callback, NULL, &timer);\n    // ========================================\n\n    while (true)\n    {\n        if (light_out)\n        { // switch the LED off\n            gpio_put(PLUS, 0);\n            sleep_ms(1000);\n            // switch it back on and let it warm up\n            gpio_put(PLUS, 1);\n            sleep_ms(1000);\n            // clear flag\n            light_out = false;\n        }\n    }\n}\n"
  },
  {
    "path": "button_matrix_4x4/4x4_button_matrix.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"4x4_button_matrix.pio.h\"\n\n// class that sets up and reads the 4x4 button matrix\nclass button_matrix_4x4\n{\npublic:\n    // constructor\n    // base_input is the starting gpio for the 4 input pins\n    // base_output is the starting gpio for the 4 output pins\n    button_matrix_4x4(uint base_input, uint base_output)\n    {\n        // pio 0 is used\n        pio = pio0;\n        // state machine 0\n        sm = 0;\n        // configure the used pins\n        for (int i = 0; i < 4; i++)\n        {\n            // output pins\n            pio_gpio_init(pio, base_output + i);\n            // input pins with pull down\n            pio_gpio_init(pio, base_input + i);\n            gpio_pull_down(base_input + i);\n        }\n        // load the pio program into the pio memory\n        uint offset = pio_add_program(pio, &button_matrix_program);\n        // make a sm config\n        pio_sm_config c = button_matrix_program_get_default_config(offset);\n        // set the 'in' pins\n        sm_config_set_in_pins(&c, base_input);\n        // set the 4 output pins to output\n        pio_sm_set_consecutive_pindirs(pio, sm, base_output, 4, true);\n        // set the 'set' pins\n        sm_config_set_set_pins(&c, base_output, 4);\n        // set shift such that bits shifted by 'in' end up in the lower 16 bits\n        sm_config_set_in_shift(&c, 0, 0, 0);\n        // init the pio sm with the config\n        pio_sm_init(pio, sm, offset, &c);\n        // enable the sm\n        pio_sm_set_enabled(pio, sm, true);\n    }\n\n    // read the 4x4 matrix\n    int read(void)\n    {\n        // value is used to read from the fifo\n        uint32_t value = 0;\n        // clear the FIFO, we only want a currently pressed key\n        pio_sm_clear_fifos(pio, sm);\n        // give the sm some time to fill the FIFO if a key is being pressed\n        sleep_ms(1);\n        // check that the FIFO isn't empty\n        if (pio_sm_is_rx_fifo_empty(pio, sm))\n        {\n            return -1;\n        }\n        // read one data item from the FIFO\n        value = pio_sm_get(pio, sm);\n        // translate from a bit position in value to a key number from 0 to 15.\n        for (int i = 0; i < 16; i++)\n        {\n            // test a bit to see if it is set\n            if ((value & (0x1 << i)) != 0)\n            {\n                //the bit is set -> return 'i' as the key number\n                return i;\n            }\n        }\n        return -1;\n    }\n\nprivate:\n    // the pio instance\n    PIO pio;\n    // the state machine\n    uint sm;\n};\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // the instance of the button matrix: input gpio: 10, 11, 12, and 13, output gpio: 18, 19, 20, and 21\n    button_matrix_4x4 my_matrix(10, 18);\n    // infinite loop to print pressed keys\n    while (true)\n    {\n        // read the matrix of buttons\n        int key = my_matrix.read();\n        if (key >= 0)\n        { // a key was pressed: print its number\n            printf(\"key pressed = %d\\n\", key);\n        }\n        sleep_ms(1000);\n    }\n}"
  },
  {
    "path": "button_matrix_4x4/4x4_button_matrix.pio",
    "content": "; in the c-program the 'set' pins are the 4 output pins to the 4x4 button matrix\n; in the c-program the 'in' pins are the 4 input pins from the 4x4 button matrix, these are pulled_down\n\n.program button_matrix\n\nstart:\n    set pins 1 [31]  ; set 0001 on the 4 output pins (activate first row) and wait for the signal to stabilize\n    in pins 4        ; shift the input pins into the ISR\n    set pins 2 [31]  ; set 0010 on the 4 output pins (activate second row) and wait for the signal to stabilize\n    in pins 4        ; shift the input pins into the ISR\n    set pins 4 [31]  ; set 0100 on the 4 output pins (activate third row) and wait for the signal to stabilize\n    in pins 4        ; shift the input pins into the ISR\n    set pins 8 [31]  ; set 1000 on the 4 output pins (activate fourth row) and wait for the signal to stabilize\n    in pins 4        ; shift the input pins into the ISR\n    mov x ISR        ; copy the ISR into the x scratch register\n    jmp !x start     ; if the x contains 0, no key was pressed, start over\n    push noblock     ; a key was pressed, push the ISR into the RX FIFO\n    jmp start        ; start over\n"
  },
  {
    "path": "button_matrix_4x4/CMakeLists.txt",
    "content": "add_executable(4x4_button_matrix)\n\npico_generate_pio_header(4x4_button_matrix ${CMAKE_CURRENT_LIST_DIR}/4x4_button_matrix.pio)\n\ntarget_sources(4x4_button_matrix PRIVATE 4x4_button_matrix.cpp)\n\ntarget_link_libraries(4x4_button_matrix PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(4x4_button_matrix)\n\n# add url via pico_set_program_url\nexample_auto_set_url(4x4_button_matrix)\n\n\n"
  },
  {
    "path": "button_matrix_4x4/README.md",
    "content": "# 4x4 button matrix\n\nThis code reads a 4x4 button matrix using PIO code for the Raspberry Pico. In the image below a simple 4x4 button matrix is shown. Four of its pins are connected to the rows (from the PIO code perspective these are outputs) and the other 4 are connected to its columns (used as pulled down inputs).\n\n![](4x4_button_matrix.jpg)\n\nThe PIO code alternatingly sets one row to HIGH and the others to LOW. The four column pins are read by the 'in pins 4' instruction. If no button is pressed, all columns read LOW, i.e. '0000' is shifted into the ISR. If a button is pressed in the row that is HIGH, the corresponding column is also read as HIGH. \n\nIf, after all 4 rows have had their turn of being set to HIGH, the ISR contains only zeros, no key was pressed. If it isn't zero, a button was pressed (at least one), and the ISR is pushed into the state machine's RX FIFO.\n\nIn the C/C++ code, when reading the RX FIFO it first needs to be cleared in order to not read old key presses. The consequence is that the state machine needs a little time (1 ms in the code) to fill the FIFO if a key is being pressed. If the FIFO is still empty, no key is pressed. If the FIFO does contain a value, the key is available as a bit in some position in the lower 16 bits. This is translated into a key value from 0 to 15.\n"
  },
  {
    "path": "count_pulses_with_pause/CMakeLists.txt",
    "content": "add_executable(count_pulses_with_pause)\n\npico_generate_pio_header(count_pulses_with_pause ${CMAKE_CURRENT_LIST_DIR}/count_pulses_with_pause.pio)\n\ntarget_sources(count_pulses_with_pause PRIVATE count_pulses_with_pause.cpp)\n\ntarget_link_libraries(count_pulses_with_pause PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(count_pulses_with_pause)\n\n# add url via pico_set_program_url\n# example_auto_set_url(count_pulses_with_pause)"
  },
  {
    "path": "count_pulses_with_pause/README.md",
    "content": "# Counting pulses in a pulse train separated by a pause \n\nThis class can be used for protocols where the data is encoded by a number of pulses in a pulse train followed by a pause.\nE.g. the LMT01 temperature sensor uses this, [see](https://www.reddit.com/r/raspberrypipico/comments/nis1ew/made_a_pulse_counter_for_the_lmt01_temperature/).\n\n"
  },
  {
    "path": "count_pulses_with_pause/count_pulses_with_pause.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/pio.h\"\n\n#include \"count_pulses_with_pause.pio.h\"\n\n/*\nThis class can be used for protocols where the data is encoded by a number of pulses in a pulse train followed by a pause.\nE.g. the LMT01 temperature sensor uses this (see https://www.reddit.com/r/raspberrypipico/comments/nis1ew/made_a_pulse_counter_for_the_lmt01_temperature/)\n\nThe class itself only starts the state machine and, when called, read_pulses() gives the data the state machine has put in the Rx FIFO\n*/\n\nclass count_pulses_with_pause\n{\npublic:\n    // input = pin that receives the pulses.\n    count_pulses_with_pause(uint input)\n    {\n        // pio 0 is used\n        pio = pio0;\n        // state machine 0\n        sm = 0;\n        // configure the used pin\n        pio_gpio_init(pio, input);\n        // load the pio program into the pio memory\n        uint offset = pio_add_program(pio, &count_pulses_with_pause_program);\n        // make a sm config\n        pio_sm_config c = count_pulses_with_pause_program_get_default_config(offset);\n        // set the 'jmp' pin\n        sm_config_set_jmp_pin(&c, input);\n        // set the 'wait' pin (uses 'in' pins)\n        sm_config_set_in_pins(&c, input);\n        // set shift direction\n        sm_config_set_in_shift(&c, false, false, 0);\n        // init the pio sm with the config\n        pio_sm_init(pio, sm, offset, &c);\n        // enable the sm\n        pio_sm_set_enabled(pio, sm, true);\n    }\n\n    // read the number of pulses in a pulse train\n    uint32_t read_pulses(void)\n    {\n        // clear the FIFO: do a new measurement\n        pio_sm_clear_fifos(pio, sm);\n        // wait for the FIFO to contain a data item\n        while (pio_sm_get_rx_fifo_level(pio, sm) < 1)\n            ;\n        // read the Rx FIFO and return the value: the number of pulses measured\n        return (pio_sm_get(pio, sm));\n    }\n\nprivate:\n    // the pio instance\n    PIO pio;\n    // the state machine\n    uint sm;\n};\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // the instance of the count_pulses_with_pause. Note the input pin is 28 in this example\n    count_pulses_with_pause my_count_pulses_with_pause(28);\n    // infinite loop to print pulse measurements\n    while (true)\n    {\n        int num_pulses = my_count_pulses_with_pause.read_pulses();\n        printf(\"number of pulses = %zu\\n\", num_pulses);\n        sleep_ms(100);\n    }\n}\n"
  },
  {
    "path": "count_pulses_with_pause/count_pulses_with_pause.pio",
    "content": "\n.program count_pulses_with_pause\n\n; algorithm:\n\n; This PIO program counts the number of pulses in a pulse train.\n; In the settings used below, two pulse trains must be separated by at least 40ms\n; \n; There are two counters:\n; One counts the number of pulses observed in a pulse train (the pulse_counter, placed in the y-register)\n; One counts how much time has passed after a pulse (the time_counter, placed in the x-register)\n; \n; If the time_counter has measured 40ms it is assumed the pulse train has ended. \n; Then pulse_counter is placed in the Rx FIFO and the pio program starts over.\n; \n; 'Measuring' the 40ms is done by counting how many instructions have been executed\n; in the loop that measures if a pulse is present on the input pin.\n; The RPI pico is assumed to run at 125MHz, so 40ms is 5000000 instruction steps.\n; The test loop is 2 instructions (jmp PIN and jmp x--), so the time_counter must be 2500000.\n; Since the 40ms doesn't need to be precise, this can be approximated by placing 19 in the x and shifting in 17 zeros.\n; 2500000 in binary is 1001100010010110100000, which is approximately 10011 with 17 zeros, 10011 is 19 in decimal.\n\nstart:\n        ; set pulse_counter to 0 (counting negatively!)\n    mov y ~NULL\nstart_time_counter:\n        ; set time_counter to 10011 << 17\n    set x 19\n    mov ISR x\n    in NULL 17\n    mov x ISR\ntest:\n        ; test if there is a pulse (a 1 on the pin)\n    jmp PIN during_pulse\n        ;  no pulse is currently observed: decrement time_counter and test again\n    jmp x-- test\n        ; time_counter has reached 0: a pause has happened\n        ; place the pulse counter in the Rx FIFO\n    mov ISR ~y\n    push\n        ; start over\n    jmp start\n\nduring_pulse:\n        ;  a pulse is in progress, wait till the pulse is over\n    wait 0 PIN 0 \n        ;  increase the number of pulses observed\n    jmp y-- during_pulse2\n        ; assume y never reaches 0\nduring_pulse2:\n        ;  restart the time counter\n    jmp start_time_counter\n\n"
  },
  {
    "path": "example_auto_set_url.cmake",
    "content": "set(PICO_EXAMPLE_URL_BASE \"https://github.com/raspberrypi/pico-examples/tree/HEAD\")\nmacro(example_auto_set_url TARGET)\n    file(RELATIVE_PATH URL_REL_PATH \"${PICO_EXAMPLES_PATH}\" \"${CMAKE_CURRENT_LIST_DIR}\")\n    pico_set_program_url(${TARGET} \"${PICO_EXAMPLE_URL_BASE}/${URL_REL_PATH}\")\nendmacro()"
  },
  {
    "path": "handy_bits_and_pieces/README.md",
    "content": "# Handy bits and pieces of PIO code \n\nOn this page I list some of the things that I found out while making the projects. \n\nIf you know of better ways of doing things, or if you have additional 'tricks', please let me know!\n\n## Setting the x (or y) scratch registers to 0xFFFFFFFF\n\nThe x register can be used for counting, but PIO code only has one instruction (`jmp x-- label`) that can actually subtract one from the value in x. \nSetting the x (or y) registers to a value from 0 to 31 can be done with the `set` command. That command has 5 bits for a number. \nFor counters it is handy to have much higher values to start with. One approach is to start the x with the highest value available in 32 bits: 0xFFFFFFFF and count down.\n\nThis can be achieved with the instruction:\n```\nmov x ~NULL\n```\nIf the C-program needs a number counting down from 0xFFFFFFFF to 0, you can use:\n```\nstart:\n    mov x ~NULL\t    ; set the x register to 0xFFFFFFFF\npush_it:\n    mov ISR x\t\t; copy x to the ISR\n    push block\t\t; wait for the C-program to read it from the Rx FIFO\n    jmp x-- push_it\t; decrement x and loop if not zero\n    jmp start\t\t; x = 0 -> start over\n```\n\n## Counting up instead of down\nAs stated above, PIO only has one instruction to count down: `jmp x-- label` (which can also use y). Counting up isn't possible. However, bitwise inverting the value in x results in its complement. Thus we can start a loop at 0xFFFFFFFF and count down, but use the inverted value in x, which results in counting from 0 to 0xFFFFFFFF.\n\nIf the C-program needs a number counting up from 0 to 0xFFFFFFFF, you can use:\n```\nstart:\n    mov x ~NULL\t    ; set the x register to 0xFFFFFFFF\npush_it:\n    mov ISR ~x\t\t; copy its complement to the ISR\n    push block\t\t; wait for the C-program to read it from the Rx FIFO\n    jmp x-- push_it\t; decrement x and loop if not zero\n    jmp start\t\t; x = 0 -> start over\n```\nNote the tilde '~' before the x in the line `mov ISR ~x`.\n\n## Delay\nIf you need to have a small delay in PIO code the delay_value can be set to a number of cycles between 0 and 31. If, however, a (much) larger delay is required, the following can be used:\n\nA counter in the x (or y) register can be started at the required number of delay cycles, and then counted down in a tight loop. Note that you need to subtract the amount of cycles that setting up the counter takes.\n\nThis same approach can be used to create a clock-cycle precise delay for any number between 0x00000000 and 0xFFFFFFFF. This can be done by setting x and shifting it into the ISR, 5 bits at a time, several times. For example, a delay loop of 682 instructions is needed (in binary: 1010101010), this can be achieved in the following way:\n```\n    set x 21             ; in binary 10101, the first 5 bits of 1010101010\n    mov ISR x            ; copy x into the ISR\n    set x 10             ; in binary 01010, the 5 least significant bits of 1010101010\n    in x 5               ; shift the least significant 5 bits of x into the ISR\n    mov x ISR            ; place the ISR in x as the start value of the counter\n            \ndelay_loop:              ; the actual delay loop\n    jmp x-- delay_loop\n```\nNote that setting up the starting value in the x register took 5 instructions, so in total this will result in a delay of 682+5 = 687 clock cycles. Also note that left-shifting is used.\n\nIf the timing doesn't have to be precise, rounding down (or up) after the 5 most significant bits and shifting in further 0's can save instructions.\nFor example if a delay of approximately 7500000 clock cycles is needed (in binary 11100100111000011100000), using only the first 5 most significant digits and setting the rest (18 bits) to 0, may suffice. This results in:\n```\n    set x 28             ; set x to 11100\n    mov ISR x            ; copy x into the ISR\n    in NULL 18           ; shift in 18 more 0 bits\n    mov x ISR            ; move the ISR to x \n\ndelay_loop:              ; the delay loop\n    jmp x-- delay_loop   \n```\n\n## Measuring 'time'\nTo measure the duration of some event, can be done by starting the x (or y) scratch register with 0xFFFFFFFF and counting down, testing for the stop criterion each iteration. \n\nThe counting down loop with test for the stop criterion looks like this:\n```pio\n    mov x ~NULL     ; start with 0xFFFFFFFF\ntimer:\n    jmp x-- test    ; count down, and jump to testing\n    jmp timerstop   ; timer has reached 0, stop count down\ntest:\n    jmp pin timer   ; <---- insert the test criterion here\ntimerstop:          ; The test criterion was met (or timer has reached 0)\n    mov ISR ~x      ; move the bit inverted value of x to the ISR\n    push noblock    ; push the ISR into the RX FIFO\n```\nThe `jmp x-- test` tests if x is zero, and if not decrements it and jumps to testing for the stop criterion (here the `jmp` pin going low). If the criterion fails, a jmp is  made to `timer`. If the criterion is met, or if the x register becomes 0, the bit-inverted value of x is moved to the ISR, and is then pushed to the Rx FIFO. \n\nHere the counting down and the test itself only take two instructions (`jmp x-- test` and `jmp pin timer`). Therefore the resolution of measuring the time with this code is 2 * pio clock tick, which for the standard clock is `2 / 125 MHz = 0.000000016` seconds. If the test takes more than one instruction, the resolution becomes lower.\n\n\n## Change shifting direction in PIO code\nWhen configuring a sm from c-code, you can configure the shift direction of the ISR:\nfor left shifting:\n```\nsm_config_set_in_shift(&c, false, false, 0);\n```\nfor right shifting:\n```\nsm_config_set_in_shift(&c, true, false, 0);\n```\nIf you need to change the shifting direction in PIO code, you can use:\n```\nmov ISR :: ISR\nin ISR 1\nmov ISR :: ISR\n```\nThe `::` symbol reverses the bit order. In this example, shifting is only for one bit, but other bit counts are also possible: `in ISR Bit_count`, with Bit_count between 1 and 32, 32 is encoded as 00000.\n\n"
  },
  {
    "path": "ledpanel/CMakeLists.txt",
    "content": "add_executable(ledpanel)\n\npico_generate_pio_header(ledpanel ${CMAKE_CURRENT_LIST_DIR}/ledpanel.pio)\n\ntarget_sources(ledpanel PRIVATE ledpanel.c ledpanel_worker.c)\n\ntarget_link_libraries(ledpanel PRIVATE\n        pico_stdlib\n        hardware_pio\n        hardware_dma\n        hardware_irq\n        hardware_interp\n        pico_multicore\n        )\n"
  },
  {
    "path": "ledpanel/README.md",
    "content": "# LED panel using PIO state machine and Direct Memory Access\n\nThis code shows how a pio state machine can drive led panels. It is made for\ntwo 64x64 led panels connected to form a 64 row x 128 column panel. There are 16\nbrightness levels for each Red, Green and Blue of each pixel, allowing many \ncolors. In its present form it updates the panel at about 144 Hz at standard\nclock settings (=125MHz.)\n\nThe main function has code for three test patterns.\n\nThere are many, many concepts that require explanation, and I do a poor\njob of it in the code and header file.\nSome of the things that I would like to (but do not) explain in detail here are:\n* it uses two cores: core 0 generates images, core 1 transcodes it into a data format suitable for\nsending it to the PIO state machine (sm).\n* after transcoding, the sm continuously sends the data to the ledpanel. It uses Direct Memory Access (DMA) for this.\n* double buffering is used for generating images (but not for the transcoded data structure!)\n* care has been taken that every generated image is displayed.\n* the images have 4 bits for each color (green, blue, red), it uses a sort of Pulse Width Modulation (PWM) to get different brightness levels for the colors and thus each pixel can have 16 * 16 * 16 = 4096 colors. See [here](https://www.galliumstudio.com/2020/04/07/stm32-episode-3-color-tricks-with-led-panels/) for an explanation of the PWM approach.\n* there is also an overall brightness setting. It takes values from 0 (dimmest) to 7 (brightest).\n* brightness ultimately comes down to how long a delay loop is in the sm code. \n* the file 'ledpanel.c' contains the code for core 0. It mostly consists of some simple (and simplistic) functions to make the images. In the main function three example functions are given.\n* the file 'ledpanel_worker.c' contains the code for core 1: transcoding and controlling the sm via DMA.\n* the sm outputs the address and color bits to the ledpanel and executes the delay loop that determines the brightness. \n* I used the interpolator hardware! This must be one of the few examples that uses it. And I do not use it for what it was intended for: it just reorders some bits, see the (somewhat vague) explanation in 'ledpanel_worker.c'.\n\nThis image shows the first of the three example animations in the main function: a skewed rainbow that shifts position with time:\n![](ledpanels.jpg)\n\n## Some words about the timing of the various functions\n\nIn the figure below the data of three logical analyzer channels are shown (many thanks to [Saleae](https://www.saleae.com/)) for the first example in the main function (the skewed rainbow):\n\nThe top line shows when the DMA interrupt is busy. This interrupt restarts the DMA channel such that the sm can send data to the ledpanel. The restarting of the DMA takes very little time, so only a peak is visible in the figure. The important thing is the time between those peaks (the 1 in the figure). At the highest brightness level that I use, the peaks are 6.93 ms apart, this translates into a display update frequency of 144 Hz.\n\nThe bottom line is the process generating the images on core 0. It is clear that generating images is the slowest process, see 3 in the figure. Generating the rainbow image costs more than 55ms.\n\nThe channel in the middle is the transcoding function on core 1. It takes almost 7ms to transcode an image, see 2 in the figure.\n\n![](ledpanel_timing_1.png)\n\nIn the figure below the same channels are shown for the second example in the main function: red and blue gradients overlapping for 8 overall brightness levels. At the lowest brightness level the updating of the ledpanel (top channel)\ntakes 590 us, at 1 in the figure, while one brightness level higher takes 640 us (at 2 in the figure). At the highest brightness level it is 6.93 ms.\nGenerating the figure takes almost 6 ms, the pulse at 4 in the figure. Note that in the code there is a 1 second pause between generating images at different brightness levels.\n\n![](ledpanel_timing_2.png)\n\nIn the figure below the timing for the third example in the main function (three moving lines) is shown. Generating a new image takes little time: 1.15 ms, see 1 in the figure. The transcoding, 2 in the figure, is continuously busy. The code has been made such that all images are displayed: core 0 has to wait for core 1 to finish transcoding.\n\n![](ledpanel_timing_3.png)\n\n\n"
  },
  {
    "path": "ledpanel/ledpanel.c",
    "content": "\n#include \"stdio.h\"\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/interp.h\"\n#include \"ledpanel.pio.h\"\n#include \"pico/multicore.h\"\n#include \"ledpanel.h\"\n\n// Overall brightness: a number from 0 (dim) to 7 (brightest)\nuint overall_brightness = 7;\n\n// the image to be displayed (a pointer) and the two actual variables for double buffering\nuint currently_drawing = 1;\nuint (*image)[num_of_displays * columns_of_display];\nuint image1[rows_of_display][num_of_displays * columns_of_display];\nuint image2[rows_of_display][num_of_displays * columns_of_display];\n\n// 0 = no image ready or processing finished\n// 1 = producer (core 0, in this file) has finished producing image 1\n// 2 = producer (core 0)) has finished producing image 2\nuint image_ready = 0;\nuint image_processing = 0;\n\n/******************************************************************************\n * some simple routines to draw the image for the test patterns\n *****************************************************************************/\n\n// set the color + brightness for a pixel\nvoid set_pixel(uint row, uint column, uint c, uint brightness)\n{\n    // leave the other two colors untouched to allow mixing\n    if (c == R) // Red: do not shift\n        image[row][column] |= brightness;\n    else if (c == G) // Green: shift over 8 bits\n        image[row][column] |= brightness << 8;\n    else // Blue: shift 4 bits\n        image[row][column] |= brightness << 4;\n}\n\n// set the individual colors + brightnesses for a pixel\nvoid set_pixel_c(uint row, uint column, uint R_brightness, uint G_brightness, uint B_brightness)\n{\n    image[row][column] = R_brightness | G_brightness << 8 | B_brightness << 4;\n}\n\n// draw a horizontal line\nvoid row_line(uint row, uint column_start, uint column_end, uint c, uint brightness)\n{\n    for (uint column = column_start; column < column_end; column++)\n        set_pixel(row, column, c, brightness);\n}\n\n// draw a vertical line\nvoid column_line(uint column, uint row_start, uint row_end, uint c, uint brightness)\n{\n    for (uint row = row_start; row < row_end; row++)\n        set_pixel(row, column, c, brightness);\n}\n\n// make an empty image\nvoid clear_image()\n{\n    for (uint x = 0; x < rows_of_display; x++)\n        for (uint y = 0; y < num_of_displays * columns_of_display; y++)\n            image[x][y] = 0;\n}\n\n// color wheel (from Adafruit strand test)\n//      Input a value 0 to 255 to get a color value.\n//      The colors returned are a transition r - g - b - back to r.\nvoid wheel(uint wheel_pos, uint *RC, uint *GC, uint *BC)\n{\n    if (wheel_pos < 85)\n    {\n        *RC = wheel_pos * 3;\n        *GC = 255 - wheel_pos * 3;\n        *BC = 0;\n    }\n    else if (wheel_pos < 170)\n    {\n        wheel_pos -= 85;\n        *BC = wheel_pos * 3;\n        *RC = 255 - wheel_pos * 3;\n        *GC = 0;\n    }\n    else\n    {\n        wheel_pos -= 170;\n        *GC = wheel_pos * 3;\n        *BC = 255 - wheel_pos * 3;\n        *RC = 0;\n    }\n}\n\n// switch image buffers to be drawn (dubbelbuffering)\n// However, wait if the process on core1 is still busy with a buffer\nvoid switch_buffer()\n{\n    image_ready = currently_drawing;\n    if (currently_drawing == 1)\n    {\n        while (image_processing == 2)\n            ;\n        image = image2;\n        currently_drawing = 2;\n    }\n    else\n    {\n        while (image_processing == 1)\n            ;\n        image = image1;\n        currently_drawing = 1;\n    }\n}\n\n/******************************************************************************\n * the main function: initializes everything and starts a loop of test patterns\n *****************************************************************************/\n\nint main()\n{\n\n    // start the dubbel buffer with image1\n    image = image1;\n    currently_drawing = 1;\n\n    // TODO: remove (only for testing purposes)\n    gpio_init(timing_pin_build_image);\n    gpio_set_dir(timing_pin_build_image, GPIO_OUT);\n\n    // needed for printf\n    stdio_init_all();\n\n    // start with an empty image\n    clear_image();\n\n    // Start core 1. Core 1 continuously transcodes the image\n    // to a format that can be used with the pio code on the\n    // state machine..\n    multicore_launch_core1(core1_worker);\n\n    // show the test patterns forever\n    while (true)\n    {\n\n        // /*\n        //\n        // Test pattern 3: make the rainbow pattern that shifts with time\n        //\n\n        for (int current_t = 0; current_t < 255; current_t++)\n        {\n            gpio_put(timing_pin_build_image, 1); // TODO: remove (only for testing purposes)\n            uint r, g, b;\n            // make a rainbow pattern\n            for (uint row = 0; row < rows_of_display; row++)\n                for (uint column = 0; column < num_of_displays * columns_of_display; column++)\n                {\n                    wheel((current_t + row + column) % 256, &r, &g, &b);\n                    // Note: green is a bit too strong on my panels -> lower the multiplication factor\n                    set_pixel_c(row, column, (int)(r * 0.0627), (int)(g * 0.04), (int)(b * 0.0627));\n                }\n\n            gpio_put(timing_pin_build_image, 0); // TODO: remove (only for testing purposes)\n            // signal to core 1 that the image is ready, and switch image buffer\n            switch_buffer();\n        }\n        // */\n\n        // /*\n        //\n        // Test pattern 2: draw red and blue planes with 16 different intensity levels and change the overall brightness\n        //\n\n        // change the overall brightness\n        for (uint b = 0; b <= 7; b++)\n        {\n            gpio_put(timing_pin_build_image, 1); // TODO: remove (only for testing purposes)\n\n            clear_image();\n\n            uint prev_fraction = 0;\n            uint next_fraction = 0;\n            // Red color planes with brightness in 16 levels (4 bits)\n            for (uint fraction = 1; fraction <= 16; fraction++)\n            {\n                next_fraction = (int)((float)fraction / 16. * (num_of_displays * columns_of_display));\n                for (uint current_y = prev_fraction; current_y < next_fraction; current_y++)\n                    column_line(current_y, 0, rows_of_display, R, fraction - 1);\n                prev_fraction = next_fraction;\n            }\n\n            prev_fraction = 0;\n            // Blue color planes with brightness in 16 levels (4 bits)\n            for (uint fraction = 1; fraction <= 16; fraction++)\n            {\n                next_fraction = (int)((float)fraction / 16. * rows_of_display);\n                for (uint current_x = prev_fraction; current_x < next_fraction; current_x++)\n                    row_line(current_x, 0, num_of_displays * columns_of_display, B, fraction - 1);\n                prev_fraction = next_fraction;\n            }\n\n            overall_brightness = b;\n            gpio_put(timing_pin_build_image, 0); // TODO: remove (only for testing purposes)\n            // signal to core 1 that the image is ready, and switch image buffer\n            switch_buffer();\n            // pause 1 seconds\n            sleep_ms(1000);\n        }\n        //    */\n\n        //   /*\n        //\n        // Test pattern 1: moving lines\n        //\n\n        // repeat this test pattern a number of times\n        for (int count = 0; count < 5; count++)\n        {\n            for (uint current_t = 0; current_t < num_of_displays * columns_of_display; current_t++)\n            {\n                gpio_put(timing_pin_build_image, 1); // TODO: remove (only for testing purposes)\n                uint current_x = current_t % rows_of_display;\n                uint current_y = current_t;\n                // prepare an empty image\n                clear_image();\n                // draw red, green and blue lines at high brightness\n                column_line(current_y, 0, rows_of_display, R, 15);\n                column_line((current_y + 64) % (num_of_displays * columns_of_display), 0, rows_of_display, G, 15);\n                row_line(current_x, 0, num_of_displays * columns_of_display, B, 15);\n                gpio_put(timing_pin_build_image, 0); // TODO: remove (only for testing purposes)\n                // signal to core 1 that the image is ready, and switch image buffer\n                switch_buffer();\n            }\n        }\n        // */\n    }\n}\n"
  },
  {
    "path": "ledpanel/ledpanel.h",
    "content": "#ifndef LEDPANELH\n#define LEDPANELH\n\n/*\n\nThis code shows how a pio state machine can work with led panels. It is made for\ntwo 64x64 led panels connected to form a 64 row x 128 column panel. There are 16\nbrightness levels for each Red, Green and Blue of each pixel, allowing many \ncolors. In addition there is a 8 level overall brightness. \nIn its present form it updates the panel at about 144 Hz at standard\nclock settings (=125MHz.)\n\nConcept of operation\nThe user prepares an image. In this code this happens on core 0. \nThe main function has code for three test patterns, that each produce several \nimages to make a small animation.\nThese images are transcoded on core 1 to a datastructure suitable for the pio \nstate machine (sm) to control the display. The pio state machine (sm) \ncontinuously updates the display via direct memory access (DMA).\n\n\n*/\n\n#include \"stdio.h\"\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/irq.h\"\n#include \"ledpanel.pio.h\"\n#include \"pico/multicore.h\"\n\n/******************************************************************************\n * Display settings\n *****************************************************************************/\n\n// The display configuration:\n//      I use two 64x64 ledpanels. to use a smaller configuration: just let part\n//      of the image blank (0 brightness)\n// Do not change (the PIO code assumes this configuration hard coded):\n#define num_of_displays 2\n#define columns_of_display 64\n#define rows_of_display 64\n#define half_rows_of_display 32\n\n/* \n  The pin assignment has been chosen such that the PIO sm can use 'out PINS' \n  to immediately set both the pixel data and address data to the pins\n\n  panel:            pin assignment:     cable (=mirror of pin assignment):\n  -------------     -------------       -------------\n  | R1  | G1  |     |  2  |  3  |       |  3  |  2  |\n  -------------     -------------       -------------\n  | B1  | GND |     |  4  | GND |       | GND |  4  |\n  -------------     -------------       -------------\n  | R2  | G2  |     |  5  |  6  |       |  6  |  5  |\n  -------------     -------------       -------------\n    B2  |  E  |        7  | 12  |       | 12  |  7  ||\n  -------------     -------------       -------------\n     A  |  B  |        8  |  9  |       |  9  |  8  ||\n  -------------     -------------       -------------\n  |  C  |  D  |     | 10  | 11  |       | 11  | 10  |\n  -------------     -------------       -------------\n  | CLK | LAT |     | 14  | 13  |       | 13  | 14  |\n  -------------     -------------       -------------\n  | OE  | GND |     | 15  | GND |       | GND | 15  |\n  -------------     -------------       -------------\n\n  Note: OE = output enable = inverse of blank\n*/\n\n// The pin assignments\n#define panel_r1 2\n#define panel_g1 3\n#define panel_b1 4\n#define panel_r2 5\n#define panel_g2 6\n#define panel_b2 7\n#define address_a 8\n#define address_b 9\n#define address_c 10\n#define address_d 11\n#define address_e 12\n#define latch 13\n#define clock_p 14\n#define blank 15\n\n// TODO: remove (only for testing purposes)\n#define timing_pin_DMA 16\n#define timing_pin_convert 17\n#define timing_pin_build_image 18\n\n/******************************************************************************\n * Image and its encoding for the state machine\n *****************************************************************************/\n\n/* \nColor and brightness \n\nEach pixel in a ledpanel has three LEDS: a red, green and blue LED. The panel \nworks by setting a 0 or 1 for each RGB LED for each pixel for one row and letting \nthem shine for a little bit of time. Each color for each pixel in this code has \n4 bits. This means that each row is written 4 times, where the 4th bit of each \ncolor is written first, then the 3th, etc. This results in a (sort of) 'Pulse \nWidth Modulation' (PWM), \nsee https://www.galliumstudio.com/2020/04/07/stm32-episode-3-color-tricks-with-led-panels/.\nNote that here I've done the 'pwm' per row and not for a whole frame. \nThe time the LEDs are on is the longest for the 4th bit and the shortest for\nthe 1st bit.\n\n\nSetting the overall brightness:\nThe variable 'overall_brightness' controls overall brightness in addition to the \n16 brightness levels of each red, green, and blue pixel values. It does this by\nbit-shifting the delay time to higher values. It can take on values from 0 to 7.\nTaking the 4 bits encoding of the color, and shifting it by a maximum of 7 bits \nresults in a 11-bit number. This number is used as the counter for the delay loop.\n\nIf a higher brightness value is needed, the pio program can be changed: it has\na delay loop with three 'nop [3]' statements (do nothing for 4 clock cycles).\nMore 'nop' statements can be added. But this will also lower the update rate\nof the display.\n\n*/\nextern uint overall_brightness;\n\n// Just for ease of use: assign numbers 0, 1 and 2 to Red, Green, and Blue\n#define R 0\n#define G 1\n#define B 2\n\n/*\nThe image to be displayed\n\nThe RGB values for each pixel are stored in an image variable:\nimage[row][column]=RGB_brightness \nFor each R, G and B 4 bits are used: Red (no shift), Blue (shift 4 bits), \nGreen (shift 8 bits). So the values in image look like: \ng4, g3, g2, g1, b4, b3, b2, b1, r4, r3, r2, r1\nwhere g4 is the highest brightness bit of green, g1 is the lowest brightness \nbit of green, similar for blue and red\n\nDouble buffering is used, so two image variables:\n*/\nextern uint image1[rows_of_display][num_of_displays * columns_of_display];\nextern uint image2[rows_of_display][num_of_displays * columns_of_display];\n// the pointer to one of the two above image variables\nextern uint (*image)[num_of_displays * columns_of_display];\n\n/*\nBoth cores of the Pico are used:\n    core 0: generates images\n    core 1: processes images to be send to the PIO state machine (SM), and controls the DMA to the SM\nSome coordination is required to make sure that all images that are prepared by\ncore 0 are actually displayed by core 1. Also some coordination is needed to \nmake sure that the image data is only overwritten by core 0 after it has been \nprocessed for displaying by core 1. \n*/\n// function to be called from core 0\nextern void core1_worker();\n// indicates which image is ready (set by core 0)\nextern uint image_ready;\n// indicates which image is being processed on core 1\nextern uint image_processing;\n\n#endif \n"
  },
  {
    "path": "ledpanel/ledpanel.pio",
    "content": ".program ledpanel\n        ; side_set PINS: \n        ;   MSB:    blank (0=screen on, 1=screen off)\n        ;           clock \n        ;   LSB:    latch\n.side_set 3\n        ; I have two panels of 64 columns -> there are 128 pixels per row\n        ; output 128 address/pixel data items (set x to 127)\n    mov ISR ~NULL side 0b100\n    \n        ; shift in 25 0s from left to right (i.e. right shift has to be set in c-program)\n    in NULL 25 side 0b100\n        ; ISR is now 127\n        \n.wrap_target\n        ; start x at 127\n    mov x ISR side 0b000\nget_data:\n        ; set the data on the pins (autopull for 'out' is enabled)\n        ; data = 5 address bits + RGB (row) + RGB (row+32) = 11 bits\n        ; start the clock pulse, screen off (needs 2 clock cycles, determined experimentally)\n    out PINS 11 side 0b110 [2]\n\n        ; end clock pulse, screen off\n        ; check if all items for this row have been done\n    jmp x-- get_data side 0b100 \n    \n        ; start the latch pulse, screen on (needs 2 clock cycles, determined experimentally)\n        ; set x to the number of delay loops = brightness level\n        ; note: to not disturb the autopull the brightness level (11 bits) is set twice\n        ;       which is a nice coincidence because the 0b001 needs to be set for two clock cycles!\n    out x 11 side 0b001 \n    out x 11 side 0b001 \n\n        ; start the delay loop and end the latch pulse, screen on\ndelay:\n    nop [3] side 0b000 ; note: three nops with 3 delay cycles seems sufficient for my panels\n    nop [3] side 0b000 ;       if more brightness is needed you can add more nops here, but\n    nop [3] side 0b000 ;       that will lower the update rate (now 144 Hz)\n        ; decrement x for another round of delays\n    jmp x-- delay side 0b000\n        ; the delay loop has finished: start a new row\n.wrap\n"
  },
  {
    "path": "ledpanel/ledpanel_worker.c",
    "content": "#include \"stdio.h\"\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/irq.h\"\n#include \"ledpanel.pio.h\"\n#include \"pico/multicore.h\"\n#include \"hardware/interp.h\"\n\n#include \"ledpanel.h\"\n\n// local (i.e. core 1) pointer to the image variable that contains the image information\nuint (*image_to_encode)[num_of_displays * columns_of_display];\n\n// the image is encoded for output to the sm in the variable \"encoded_image\"\n// Because of the construction of the led panel, you always send (x,y) and (x+32, y) pixels.\n// Additionally, with each set of pixels the address is given.\n// So, from MSB to LSB: E, F, D, C, B, A, g(row+32), b(row+32), r(row+32), g(row), b(row), r(row)\n//      where E,F,D,C,B and A encode the row address.\n//      and where g, b and r are bits for a brightness level, each color is encoded with 4 bits. See the\n//      loop 'for (int b = 3; b >= 0; b--)' in the code below.\n// This is 11 bits, so: two of these fit in a uint32_t (with room to spare: 10 bits)\n\n// The size of the transcoded image for tranmission to the sm follows from:\n//      128 lines\n//      32 rows (two rows are transmitted simultaneously)\n//      4 brightness level\n//      2 pixels (and the address) per uint32_t\n// -> 128*32*4 / 2 = 8192\n//      additionally the overall brightness is set for each of the 32 rows and each of the 4 brightness levels\n// -> 8192 + 32*4 = 8320\n#define MAX_ITEMS 8320\nuint32_t encoded_image[MAX_ITEMS];\n\n// number of items to send to the sm via dma\nuint num_of_items_to_dma;\n\n// encode the image to be suitable for sending it to the sm\nvoid encode_image()\n{\n    gpio_put(timing_pin_convert, 1);// TODO: remove (only for testing purposes)\n\n    // variable to hold the data to be send to the sm\n    uint32_t address_and_pixels;\n    // counter to keep track of the number of items to be send\n    uint num_of_items = 0;\n\n    /* \n\n    YES YES YES! I finally found a good use for the interpolator hardware!!!!\n\n    It may not be what it was intended for, but hey: it works!\n    And it is faster than a lot of 'and', 'bit shift' and 'or' operations!\n\n    The following bit shifting/reordering is needed to transcode the RGB values as\n    stored in the image variables to the format required by the PIO sm to send\n    to the ledpanel:\n    The images contain pixel information in the format:\n        g4, g3, g2, g1, b4, b3, b2, b1, r4, r3, r2, r1\n    but the sm needs the bits to be in a different order: \n    for the highest brightness bits (b=3): \n        g4, b4, r4\n    down to the lowest brightness color information (b=0):\n        g1, b1, r1\n\n    So, the interpolator is configured as follows:\n    - the input is the pixel color information shifted over b bits\n    - The Base 2 variable contains the b'th bit for red\n        see the lines in the code below:\n            uint value = image_to_encode[row][i] >> b;\n            interp0->base[2] = value & 0x01;\n        so, the first bit in interpolator's 'Result 2' is the b'th bit for red\n    - Lane 0 shifts the pixel info three bits right and masks the result in\n      such a way that the b'th bit of blue is now the second bit in 'Result 2'\n    - Lane 1 shifts the pixel info 6 bits right and masks the result in such\n      a way that the b'th bit of green is now the third bit in 'Result 2'\n    */\n\n    // Initialise lane 0 on interp0 on this core\n    interp_config cfg0 = interp_default_config();\n    // shift right 3 bits\n    interp_config_set_shift(&cfg0, 3);\n    // mask lets bit 1 (the 2nd bit) pass\n    interp_config_set_mask(&cfg0, 1, 1);\n    interp_set_config(interp0, 0, &cfg0);\n\n    // Initialise lane 1 on interp0 on this core\n    interp_config cfg1 = interp_default_config();\n    // shift right 6 bits\n    interp_config_set_shift(&cfg1, 6);\n    // mask lets bit 2 (the 3rd bit) pass\n    interp_config_set_mask(&cfg1, 2, 2);\n    interp_set_config(interp0, 1, &cfg1);\n\n    // 64 rows, but row i and i+1 are drawn simultaneously -> 32 rows\n    for (uint8_t row = 0; row < half_rows_of_display; row++)\n    {\n        // 4 brightness level bits for color in each pixel\n        for (int b = 3; b >= 0; b--)\n        {\n            // all columns\n            for (uint8_t i = 0; i < num_of_displays * columns_of_display; i += 2)\n            {\n                // ledpanel displays put the x and x+32 rows on the display at the same time.\n                // Additionally, two pixels fit in one byte to be displayed. So, code the y and y+1 pixels\n\n                // pixel (x,y)\n                uint value = image_to_encode[row][i] >> b;\n                // printf(\"1 image_to_encode=%d value=%d\\n\", image_to_encode[row][i], value);\n                interp0->base[2] = value & 0x01;\n                interp0->accum[0] = value;\n                interp0->accum[1] = value;\n                address_and_pixels = interp0->peek[2];\n                // pixel (x+32, y)\n                value = image_to_encode[row + 32][i] >> b;\n                // printf(\"2 image_to_encode=%d value=%d\\n\", image_to_encode[row + 32][i], value);\n                interp0->base[2] = value & 0x01;\n                interp0->accum[0] = value;\n                interp0->accum[1] = value;\n                address_and_pixels |= interp0->peek[2] << 3;\n                // add the row address\n                address_and_pixels |= row << 6;\n                // pixel (x,y+1)\n                value = image_to_encode[row][i + 1] >> b;\n                // printf(\"3 image_to_encode=%d value=%d\\n\",image_to_encode[row][i + 1], value);\n                interp0->base[2] = value & 0x01;\n                interp0->accum[0] = value;\n                interp0->accum[1] = value;\n                address_and_pixels |= interp0->peek[2] << 11;\n                // pixel (x+32, y+1)\n                value = image_to_encode[row + 32][i + 1] >> b;\n                // printf(\"4 image_to_encode=%d value=%d\\n\",image_to_encode[row + 32][i + 1], value);\n                interp0->base[2] = value & 0x01;\n                interp0->accum[0] = value;\n                interp0->accum[1] = value;\n                address_and_pixels |= interp0->peek[2] << 14;\n                // add the row address\n                address_and_pixels |= row << 17;\n                // add the data to the encoded image array to be sent to the pio sm\n                if (num_of_items < MAX_ITEMS)\n                    encoded_image[num_of_items++] = address_and_pixels;\n            }\n            // This controlls the overall brightness of the panels\n            // In order not to disturb the 22 bit autopull, it is sent twice\n            address_and_pixels = 1 << (overall_brightness + b);\n            address_and_pixels |= address_and_pixels << 11;\n            if (num_of_items < MAX_ITEMS)\n                encoded_image[num_of_items++] = address_and_pixels;\n        }\n    }\n    // set the number of dataitems to be sent to the sm via dma\n    num_of_items_to_dma = num_of_items;\n\n    gpio_put(timing_pin_convert, 0);// TODO: remove (only for testing purposes)\n}\n\n/******************************************************************************\n * configuration of the sm and dma\n *****************************************************************************/\n\n// variables for the pio and state machine (sm) to be used\nPIO pio;\nuint sm;\n// the sm program offset and sm configuration\nuint sm_offset;\npio_sm_config smc;\n// the dma channel\nuint dma_chan;\n\n// configure the state machine\nvoid configure_pio_sm()\n{\n    // pio to use\n    pio = pio0;\n    // set the GPIO to be used by the pio\n    for (uint pin = panel_r1; pin <= blank; pin++)\n        pio_gpio_init(pio, pin);\n    // state machine to use\n    sm = 0;\n    // load the sm program into the pio memory\n    sm_offset = pio_add_program(pio, &ledpanel_program);\n    // make a sm config\n    smc = ledpanel_program_get_default_config(sm_offset);\n    // set pindirs all pins are under control of the PIO sm as output\n    pio_sm_set_consecutive_pindirs(pio, sm, panel_r1, 14, true);\n    // set out pin (r1, g1, b1, r2, g2, b2, a, b, c, d, e)\n    sm_config_set_out_pins(&smc, panel_r1, 11);\n    // set side-set pins (latch, clock, blank)\n    sm_config_set_sideset_pins(&smc, latch);\n    // set the correct ISR shift direction (see the .pio file)\n    sm_config_set_in_shift(&smc, true, false, 0);\n    // set autopull for out: NOTE: one set of pixels is 11 bits,\n    // there are two sets per doubleword (32 bits) send to the Tx FIFO\n    sm_config_set_out_shift(&smc, true, true, 22);\n    // init the pio sm with the config\n    pio_sm_init(pio, sm, sm_offset, &smc);\n    // enable the state machines\n    pio_sm_set_enabled(pio, sm, true);\n}\n\n// interrupt handler for the direct memory access\nvoid dma_handler()\n{\n    gpio_put(timing_pin_DMA, 1);// TODO: remove (only for testing purposes)\n    // Clear the interrupt request\n    dma_hw->ints0 = 1u << dma_chan;\n    // set the number of data items to transfer\n    dma_channel_set_trans_count(dma_chan, num_of_items_to_dma, false);\n    // set the read address, this will trigger the DMA to start again\n    dma_channel_set_read_addr(dma_chan, encoded_image, true);\n    gpio_put(timing_pin_DMA, 0);// TODO: remove (only for testing purposes)\n}\n\n// configure the direct memory access\nvoid configure_dma()\n{\n    // init variable to hold the number of items to send via dma\n    num_of_items_to_dma = 0;\n    // Get free DMA channels, panic() if there are none\n    dma_chan = dma_claim_unused_channel(true);\n    // make DMA configs\n    dma_channel_config dma_conf = dma_channel_get_default_config(dma_chan);\n    // transfer uint32_t\n    channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);\n    // the buffer increment of read pointer\n    channel_config_set_read_increment(&dma_conf, true);\n    // the TxFIFO is fixed in memory -> no increment write pointer\n    channel_config_set_write_increment(&dma_conf, false);\n    // let the sm of pio determine the speed\n    channel_config_set_dreq(&dma_conf, pio_get_dreq(pio, sm, true));\n    // configure the dma channel to write to sm TxFIFO from buffer\n    dma_channel_configure(dma_chan, &dma_conf, &pio0_hw->txf[0], NULL, 0, false);\n    // enable IRQ for the channel\n    dma_channel_set_irq0_enabled(dma_chan, true);\n    // set the handler\n    irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);\n    // enable IRQ\n    irq_set_enabled(DMA_IRQ_0, true);\n}\n\nvoid core1_worker()\n{\n    // TODO: remove (only for testing purposes)\n    gpio_init(timing_pin_DMA);\n    gpio_set_dir(timing_pin_DMA, GPIO_OUT);\n    gpio_init(timing_pin_convert);\n    gpio_set_dir(timing_pin_convert, GPIO_OUT);\n\n    // wait for an image to be finished by core 0\n    while (image_ready == 0)\n        ;\n    if (image_ready == 1)\n    {\n        // the image in variable image1 is ready for transcoding\n        image_to_encode = image1;\n        // indicate that image1 is what this core is working on\n        image_processing = image_ready;\n    }\n    else\n    {\n        // the image in variable image2 is ready for transcoding\n        image_to_encode = image2;\n        // indicate that image2 is what this core is working on\n        image_processing = image_ready;\n    }\n\n    // prepare the sm\n    configure_pio_sm();\n    // prepare the dma\n    configure_dma();\n    // convert the image to data for the pio sm\n    encode_image();\n    // start the dma\n    dma_handler();\n    image_processing = 0;\n\n    while (true)\n    {\n        // if image1 is ready\n        if (image_ready == 1)\n        {   \n            // the image in variable image1 is ready for transcoding\n            // indicate that image1 is what this core is working on\n            image_processing = image_ready;\n            // indicate that core 0 can continue with generating a new image\n            image_ready = 0;\n            image_to_encode = image1;\n            // do the encoding\n            encode_image();\n            // indicate transcoding has finished\n            image_processing = 0;\n        }\n        else if (image_ready == 2)\n        {\n            // the image in variable image2 is ready for transcoding\n            // indicate that image2 is what this core is working on\n            image_processing = image_ready;\n            image_ready = 0;\n            // indicate that core 0 can continue with generating a new image\n            image_to_encode = image2;\n            // do the encoding\n            encode_image();\n            // indicate transcoding has finished\n            image_processing = 0;\n        }\n    }\n}\n"
  },
  {
    "path": "multiplication/CMakeLists.txt",
    "content": "add_executable(multiplier)\n\npico_generate_pio_header(multiplier ${CMAKE_CURRENT_LIST_DIR}/multiplier.pio)\n\ntarget_sources(multiplier PRIVATE multiplier.cpp)\n\ntarget_link_libraries(multiplier PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(multiplier)\n\n# add url via pico_set_program_url\nexample_auto_set_url(multiplier)\n\n\n"
  },
  {
    "path": "multiplication/README.md",
    "content": "# multiply two numbers \n\nIn the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) there is an example of how a sm can be used to add two numbers. If you can add, you can multiply! So, this is a pio program that can multiply. Not very useful, but interesting nonetheless. \n\nIf the two numbers to be multiplied are called m1 and m2, this multiplication works by adding (actually subtracting) one for m2 times m1 times.\n\nA possibly better implementation would perform the multiplication in a different way: by shifting and adding."
  },
  {
    "path": "multiplication/multiplier.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"multiplier.pio.h\"\n\n// the pio instance\nPIO pio;\n// the state machine\nuint sm;\n\nvoid pio_mul(int a, int b)\n{\n    pio_sm_put(pio, sm, a);\n    pio_sm_put(pio, sm, b);\n    printf(\"%d * %d = %d\\n\", a, b, pio_sm_get_blocking(pio, sm));\n}\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n\n    // pio 0 is used\n    pio = pio0;\n    // state machine 0\n    sm = 0;\n    // load the pio program into the pio memory\n    uint offset = pio_add_program(pio, &multiplier_program);\n    // make a sm config\n    pio_sm_config c = multiplier_program_get_default_config(offset);\n    // init the pio sm with the config\n    pio_sm_init(pio, sm, offset, &c);\n    // enable the sm\n    pio_sm_set_enabled(pio, sm, true);\n\n    pio_mul(1, 1);\n    pio_mul(0, 1);\n    pio_mul(5, 0);\n    pio_mul(1, 2);\n    pio_mul(2, 1);\n    pio_mul(3, 2);\n    pio_mul(2, 3);\n    pio_mul(4, 5);\n    pio_mul(5, 4);\n    pio_mul(1, 10);\n    pio_mul(5, 100);\n    pio_mul(12, 12);\n    pio_mul(100, 101);\n    pio_mul(1001, 1000);\n\n    while (true)\n    {\n    }\n}"
  },
  {
    "path": "multiplication/multiplier.pio",
    "content": "\n.program multiplier\n\n; overall approach:\n    ; get the two numbers to be multiplied, called m1 and m2\n    ; m2 is saved in OSR, m1 is saved in ISR\n    ; use the (negative) y as counter\n    ; repeat m2 times: \n    ;    repeat m1 times:\n    ;       subtract one from y\n\n \n.wrap_target\nstart:\n    pull block      ; wait until data is available\n    mov x OSR       ; get m1\n    mov ISR x       ; save m1 in the ISR; this value is used m2 times\n    pull block      ; get m2 \n    mov y OSR\n                    ; test if m2 = 0, if so -> return a 0\n    jmp y-- not_zero; if m2 > 0, subtract 1 and go to not_zero\n    mov ISR NULL    ; m2 == 0, return a 0 as answer\n    push\n    jmp start       ; restart\n\nnot_zero:\n    mov OSR y       ; write back m2-1 in the OSR\n    mov y ~NULL\t    ; initialize the y register to 0xFFFFFFFF\n\nnext_x:             ; start the actual multiplication by counting m2 times m1 times subtracting one from y\n    jmp x-- sub_one ; count down x, and if still x > 0 - also decrement y\n                    ; if x==0, m1 has been counted down, see if OSR > 0 (i.e. m2), if so, go again\n    mov x OSR       ; get the saved value of m2 from the OSR\n    jmp x-- save_m2 ; if x>0 save the new value of m2 and start subtracting x (and also y)\n    ; nothing more to do, m2 has been counted down to 0, return the answer\n    mov ISR ~y\n    push\n    .wrap\n\nsub_one:            ; subtract one from y\n    jmp y--\tnext_x  ; y is (as it should) > 0 -> continue the multiplication\n    mov ISR ~NULL   ; y has become 0, only happens for large numbers -> give 0xFFFFFFFF as answer\n    push\n    jmp start\n\nsave_m2:            ; save m2 and switch to m1 in x\n    mov OSR x       ; save the x (m2) in the OSR\n    mov x ISR       ; start over with m1\n    jmp next_x\n\n"
  },
  {
    "path": "pico_sdk_import.cmake",
    "content": "# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake\n\n# This can be dropped into an external project to help locate this SDK\n# It should be include()ed prior to project()\n\nif (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))\n    set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})\n    message(\"Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))\n    set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))\n    set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')\")\nendif ()\n\nset(PICO_SDK_PATH \"${PICO_SDK_PATH}\" CACHE PATH \"Path to the Raspberry Pi Pico SDK\")\nset(PICO_SDK_FETCH_FROM_GIT \"${PICO_SDK_FETCH_FROM_GIT}\" CACHE BOOL \"Set to ON to fetch copy of SDK from git if not otherwise locatable\")\nset(PICO_SDK_FETCH_FROM_GIT_PATH \"${PICO_SDK_FETCH_FROM_GIT_PATH}\" CACHE FILEPATH \"location to download SDK\")\n\nif (NOT PICO_SDK_PATH)\n    if (PICO_SDK_FETCH_FROM_GIT)\n        include(FetchContent)\n        set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})\n        if (PICO_SDK_FETCH_FROM_GIT_PATH)\n            get_filename_component(FETCHCONTENT_BASE_DIR \"${PICO_SDK_FETCH_FROM_GIT_PATH}\" REALPATH BASE_DIR \"${CMAKE_SOURCE_DIR}\")\n        endif ()\n        FetchContent_Declare(\n                pico_sdk\n                GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk\n                GIT_TAG master\n        )\n        if (NOT pico_sdk)\n            message(\"Downloading Raspberry Pi Pico SDK\")\n            FetchContent_Populate(pico_sdk)\n            set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})\n        endif ()\n        set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})\n    else ()\n        message(FATAL_ERROR\n                \"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git.\"\n                )\n    endif ()\nendif ()\n\nget_filename_component(PICO_SDK_PATH \"${PICO_SDK_PATH}\" REALPATH BASE_DIR \"${CMAKE_BINARY_DIR}\")\nif (NOT EXISTS ${PICO_SDK_PATH})\n    message(FATAL_ERROR \"Directory '${PICO_SDK_PATH}' not found\")\nendif ()\n\nset(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)\nif (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})\n    message(FATAL_ERROR \"Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK\")\nendif ()\n\nset(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH \"Path to the Raspberry Pi Pico SDK\" FORCE)\n\ninclude(${PICO_SDK_INIT_CMAKE_FILE})\n"
  },
  {
    "path": "sm_to_dma_to_buffer/CMakeLists.txt",
    "content": "add_executable(sm_to_dma_to_buffer)\n\npico_generate_pio_header(sm_to_dma_to_buffer ${CMAKE_CURRENT_LIST_DIR}/sm_to_dma_to_buffer.pio)\n\ntarget_sources(sm_to_dma_to_buffer PRIVATE sm_to_dma_to_buffer.cpp)\n\ntarget_link_libraries(sm_to_dma_to_buffer PRIVATE\n        pico_stdlib\n        hardware_pio\n        hardware_dma\n        )\n\npico_add_extra_outputs(sm_to_dma_to_buffer)\n\n# add url via pico_set_program_url\nexample_auto_set_url(sm_to_dma_to_buffer)\n\n\n"
  },
  {
    "path": "sm_to_dma_to_buffer/README.md",
    "content": "# State machine writes into a buffer via DMA\n\nThis is an example of a state machine (sm) using DMA (Direct Memory Access) to write into a buffer.\n\nThe sm0 counts up from 0, and sends its current value into the Rx FIFO. A DMA channel reads the Rx FIFO and puts it into a buffer (of length 100).\n\n"
  },
  {
    "path": "sm_to_dma_to_buffer/sm_to_dma_to_buffer.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/dma.h\"\n\n#include \"sm_to_dma_to_buffer.pio.h\"\n\nint main()\n{\n    // buffer to write to\n    uint32_t buffer[100];\n\n    // needed for printf\n    stdio_init_all();\n\n    // pio 0 is used\n    PIO pio = pio0;\n    // state machine 0\n    uint sm0 = 0;\n    // load the sm0 program into the pio memory\n    uint offset0 = pio_add_program(pio, &sm_to_dma_to_buffer_program);\n    // make a sm config\n    pio_sm_config smc0 = sm_to_dma_to_buffer_program_get_default_config(offset0);\n    // init the pio sm0 with the config\n    pio_sm_init(pio, sm0, offset0, &smc0);\n    // disable the sm\n    pio_sm_set_enabled(pio, sm0, false);\n    // make sure the FIFOs are empty\n    pio_sm_clear_fifos(pio, sm0);\n\n    // Get a free DMA channel, panic() if there are none\n    int dma_chan = dma_claim_unused_channel(true);\n    // make a default dma config\n    dma_channel_config dma_conf = dma_channel_get_default_config(dma_chan);\n    // transfer uint32_t\n    channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);\n    // a FIFO is read -> no increment of read pointer\n    channel_config_set_read_increment(&dma_conf, false);\n    // a buffer in memory is written to -> increment write pointer\n    channel_config_set_write_increment(&dma_conf, true);\n    // let the sm0 of pio determine the speed\n    channel_config_set_dreq(&dma_conf, pio_get_dreq(pio, sm0, false));\n    // configure the dma channel to read 100 uint32_t from sm0 to the buffer\n    dma_channel_configure(dma_chan, &dma_conf,\n                          buffer,         // Destinatinon pointer: the buffer in memory\n                          &pio->rxf[sm0], // Source pointer: the output of sm0\n                          100,            // Number of transfers\n                          true            // Start immediately\n    );\n    // enable the sm\n    pio_sm_set_enabled(pio, sm0, true);\n    // let the dma channel do its stuff\n    dma_channel_wait_for_finish_blocking(dma_chan);\n\n    // print the result in the buffer\n    for (int i = 0; i < 100; i++)\n    {\n        printf(\"buffer = %d\\n\", buffer[i]);\n    }\n    // endless loop to end this program\n    while (true)\n        ;\n}"
  },
  {
    "path": "sm_to_dma_to_buffer/sm_to_dma_to_buffer.pio",
    "content": "\n.program sm_to_dma_to_buffer\n\nstart0:\n    mov x ~NULL         ; start with 0xFFFFFFFF\npush_it0:\n    mov ISR ~x          ; push the value into the Rx FIFO \n                        ; (make it look like counting up)\n    push \n    jmp x-- push_it0    ; count down\n    jmp start0\n\n "
  },
  {
    "path": "sm_to_dma_to_sm_to_dma_to_buffer/CMakeLists.txt",
    "content": "add_executable(sm_to_dma_to_sm_to_dma_to_buffer)\n\npico_generate_pio_header(sm_to_dma_to_sm_to_dma_to_buffer ${CMAKE_CURRENT_LIST_DIR}/sm_to_dma_to_sm_to_dma_to_buffer.pio)\n\ntarget_sources(sm_to_dma_to_sm_to_dma_to_buffer PRIVATE sm_to_dma_to_sm_to_dma_to_buffer.cpp)\n\ntarget_link_libraries(sm_to_dma_to_sm_to_dma_to_buffer PRIVATE\n        pico_stdlib\n        hardware_pio\n        hardware_dma\n        )\n\npico_add_extra_outputs(sm_to_dma_to_sm_to_dma_to_buffer)\n\n# add url via pico_set_program_url\nexample_auto_set_url(sm_to_dma_to_sm_to_dma_to_buffer)\n\n\n"
  },
  {
    "path": "sm_to_dma_to_sm_to_dma_to_buffer/README.md",
    "content": "# State Machine -> DMA -> State Machine -> DMA -> Buffer\nThis is an example where one state machine writes via DMA to another state machine whose output is put into a buffer via another DMA channel.\n\nThe sm0 counts up from 0, and sends its current value into its Rx FIFO. A DMA channel reads this Rx FIFO and puts it into the Tx FIFO of sm1. The sm1 does nothing more than put what it receives into its Rx FIFO. A DMA channel reads the sm1 Rx FIFO and puts the values into a buffer (of length 100).\n\nIn the code the chain is as follows:\n```\nsm0 -> dma_chan_2 -> sm1 -> dma_chan_1 -> buffer\n```\n\nI had some problems getting this to work. The main reason was to set the data request (DREQ) signal correctly. It turns out to depend on which sm is the slowest. That one has to determine the speed.\n\nIn the PIO code sm0 runs a program (tester0) that is 3 instructions long while sm1 runs a program (tester1) that is 4 instructions. Therefore, sm1 should determine the DREQ:\n``` \nchannel_config_set_dreq(&dma_conf_2, pio_get_dreq(pio, sm1, true)); \n```\nIf I artificially make tester0 longer, e.g. by uncommenting the ';[31]', then sm0 should determine the DREQ:\n```\nchannel_config_set_dreq(&dma_conf_2, pio_get_dreq(pio, sm0, false));\n\n```\nIn addition, starting the state machines and DMA channels in the correct order is important. In this case:\n```\n// enable the state machines\npio_sm_set_enabled(pio, sm1, true);\npio_sm_set_enabled(pio, sm0, true);\n\n// start the dma channel from sm0 to sm1: start producing data\ndma_channel_start(dma_chan_2);\n\n// wait for dma channel from sm1 to the buffer to finish\ndma_channel_wait_for_finish_blocking(dma_chan_1);\n```"
  },
  {
    "path": "sm_to_dma_to_sm_to_dma_to_buffer/sm_to_dma_to_sm_to_dma_to_buffer.cpp",
    "content": "/*\n\nThe goal of this program is to show how DMA can be used to transfer data from one state machine to the next and from that second state machine to a buffer.\n\nstate machine 0 (sm0) produces numbers from 0 counting up.\nstate machien 1 (sm1) accepts input and immedeately pushes it as output\n\nIn between sm0 and sm1 there is a DMA channel, and between sm1 and the buffere there is another DMA channel: \n\nsm0 -> dma_chan_2 -> sm1 -> dma_chan_1 -> buffer\n\n*/\n\n#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/dma.h\"\n\n#include \"sm_to_dma_to_sm_to_dma_to_buffer.pio.h\"\n\nint main()\n{\n    uint32_t num_samples = 1000;\n    // buffer to write to\n    uint32_t buffer[num_samples];\n\n    // needed for printf\n    stdio_init_all();\n\n    // pio 0 is used\n    PIO pio = pio0;\n    // state machines 0 and 1\n    uint sm0 = 0;\n    uint sm1 = 1;\n    // load the sm0 program into the pio memory\n    uint offset0 = pio_add_program(pio, &tester0_program);\n    // load the sm1 program into the pio memory\n    uint offset1 = pio_add_program(pio, &tester1_program);\n    // make a sm config\n    pio_sm_config smc0 = tester0_program_get_default_config(offset0);\n    pio_sm_config smc1 = tester1_program_get_default_config(offset1);\n    // init the pio sm0 with the config\n    pio_sm_init(pio, sm0, offset0, &smc0);\n    pio_sm_init(pio, sm1, offset1, &smc1);\n    // disable the state machines\n    pio_sm_set_enabled(pio, sm0, false);\n    pio_sm_set_enabled(pio, sm1, false);\n    // clear the FIFOs\n    pio_sm_clear_fifos(pio, sm0);\n    pio_sm_clear_fifos(pio, sm1);\n\n    // DMA channel from sm1 to a buffer in memory\n    // NOTES:\n    // - this channel is paced (dreq) by reading output from sm1 (is_tx is false)\n    // - this channel may start immediately\n\n    // Get a free DMA channel, panic() if there are none\n    int dma_chan_1 = dma_claim_unused_channel(true);\n    // make a dma config\n    dma_channel_config dma_conf_1 = dma_channel_get_default_config(dma_chan_1);\n    // transfer uint32_t\n    channel_config_set_transfer_data_size(&dma_conf_1, DMA_SIZE_32);\n    // a FIFO is read -> no increment of read pointer\n    channel_config_set_read_increment(&dma_conf_1, false);\n    // a buffer in memory is written to -> increment write pointer\n    channel_config_set_write_increment(&dma_conf_1, true);\n    // let the sm0 of pio determine the speed\n    channel_config_set_dreq(&dma_conf_1, pio_get_dreq(pio, sm1, false));\n    // configure the dma channel to read num_samples uint32_t from sm1 to the buffer\n    dma_channel_configure(dma_chan_1, &dma_conf_1,\n                          buffer,         // Destinatinon pointer\n                          &pio->rxf[sm1], // Source pointer\n                          num_samples,    // Number of transfers\n                          true            // Start immediately\n    );\n\n    // DMA channel from sm0 output to sm1 input\n    // NOTES:\n    // - this channel is paced (dreq) by writing to sm1 (is_tx is true).\n    //   The PIO program of sm1 is 4 instructions, sm0 is 3 instructions\n    //   If sm0 is made slower than sm1, then dreq should depend on sm0 (is_tx is false)\n    // - this channel should not start immediately. First enable the state machines\n\n    // Get a free channel, panic() if there are none\n    int dma_chan_2 = dma_claim_unused_channel(true);\n    // make a dma config\n    dma_channel_config dma_conf_2 = dma_channel_get_default_config(dma_chan_2);\n    // transfer uint32_t\n    channel_config_set_transfer_data_size(&dma_conf_2, DMA_SIZE_32);\n    // a FIFO is read -> no increment of read pointer\n    channel_config_set_read_increment(&dma_conf_2, false);\n    // a FIFO is written to -> no increment of write pointer\n    channel_config_set_write_increment(&dma_conf_2, false);\n    // let writing to sm1 determine the speed if sm1 is slower than sm0\n    channel_config_set_dreq(&dma_conf_2, pio_get_dreq(pio, sm1, true));\n    // let writing to sm0 determine the speed if sm0 is slower than sm1\n    // channel_config_set_dreq(&dma_conf_2, pio_get_dreq(pio, sm0, false));\n    // configure the dma channel to read num_samples uint32_t from sm0 to sm1\n    dma_channel_configure(dma_chan_2, &dma_conf_2,\n                          &pio->txf[sm1], // Destinatinon pointer\n                          &pio->rxf[sm0], // Source pointer\n                          num_samples,    // Number of transfers\n                          false           // Start immediately\n    );\n\n    // enable the state machines\n    pio_sm_set_enabled(pio, sm1, true);\n    pio_sm_set_enabled(pio, sm0, true);\n\n    // start the dma channel from sm0 to sm1: start producing data\n    dma_channel_start(dma_chan_2);\n    // wait for dma channel from sm1 to the buffer to finish\n    dma_channel_wait_for_finish_blocking(dma_chan_1);\n\n    // print the result in the buffer\n    for (int i = 0; i < num_samples; i++)\n    {\n        printf(\"buffer = %d\\n\", buffer[i]);\n    }\n    while (true)\n        ;\n}"
  },
  {
    "path": "sm_to_dma_to_sm_to_dma_to_buffer/sm_to_dma_to_sm_to_dma_to_buffer.pio",
    "content": "// Note: \n// program tester1 is 4 instructions (intentionally not using .wrap) while program tester0 is only 3 instructions per loop.\n// These program lengths influences the dreq settings of the c program\n// If tester1 is slower than tester0:\n//        // let writing to sm1 determine the speed\n//        channel_config_set_dreq(&dma_conf_2, pio_get_dreq(pio, sm1, true));\n// If tester0 is slower than tester1 (remove the ';' before [31] in tester0):\n//        // let writing to sm0 determine the speed if sm0 is slower than sm1\n//        channel_config_set_dreq(&dma_conf_2, pio_get_dreq(pio, sm0, false));\n\n\n\n.program tester0\n// a simple counter producing output starting from 0\nstart0:\n    mov x ~NULL         ; set x to 0xFFFFFFFF\npush_it0:\n    mov ISR ~x          ; set the ISR to ~x (bitwise invert)\n    push        ;[31]   ; push to FIFO (optionally make tester0 slower than tester1, the dreq needs to change)\n    jmp x-- push_it0    ; decrease x and loop\n    jmp start0\n\n\n.program tester1\n// pull and immediately push. Indeed, it does nothing!\nstart1:\n    pull                ; pull the Tx FIFO\n    mov ISR OSR         ; do nothing but copy the OSR to the ISR\n    push                ; push the Rx FIFO\n    jmp start1          ; loop\n\n\n "
  },
  {
    "path": "state_machine_emulator/README.md",
    "content": "# Emulator of a Raspberry Pi Pico PIO state machine\n\n## What does it do\nThis emulator allows you to step through the workings of a RPI Pico PIO state machine running PIO code. It is intended for running test cases and gain insight into how the code works.\n\nThis code takes a .pio.h file generated by pioasm as input and emulates how a PIO state machine (sm) would execute it.  \n\nBesides the .pio.h file there are two more input files:\n* a file describing the 'C'-statements (called c_program) such as configuring of pins and putting data into the Tx FIFO. Note that these are not the actual C/C++ statements, but much simplified versions of them. And not all of them, only some (see below.)\n* a file describing the externally driven GPIO pins (called pin_program, see below)\n\nThe user can obtain insights into the workings of the pio code through a GUI which shows all (?) the relevant sm internal information.\n\n##### Why?\nThe problem with the RPi Pico PIO state machines (sm) is that the debugger supplied with the vscode distribution for the Pico does not give the insight I need when writing code for a sm. I typically write some code, upload it to the Pico and find that it doesn't do what I want it to do. Instead of guessing what I do wrong, I want to see the values of e.g. the registers when the sm is executing.\n\nAnd I liked making the emulator.\n\n## What does it not do\nSince this emulator is meant for studying how pio code is executed for a given test case (the combination of c_program and pin_program), it only emulates one sm. You can set which of the 8 sm it is (in config.py) because interrupts use this number. It also uses only one PIO (i.e. 32 instruction memory locations) running one program. If you want to study several PIO programs, you'll have to make separate test cases. \n\nOther things you might expect, but aren't implemented or used:\n* It only uses non-blocking 'C'-statements to 'put' data into the TxFIFO and and 'get' data from the RxFIFO,\n* 'out exec' and 'mov exec' aren't implemented,\n* No FIFO joining,\n* 'origin' is not used; all pio code starts at memory location 0,\n* It (mostly) doesn't use the registers ([RP2040 datasheet](htts://rptl.io/rp2040-datasheet) section 3.7). \n\n\n## Is this the only means to study pio code? \nNo, there are several options.\n\n* Use explicit statements in PIO code to export values (e.g. via MOV ISR X, PUSH) but this influences your code,\n* You could insert 'EXEC' commands from C/C++, but this still won't give you a deep insight. \n\nOr you can program the pio, look at its output (GPIO or your print messages) and try to figure out why it doesn't do what you want it to do. This is hard work and the exact reason why I built this emulator.\n\nThere are other debuggers and emulators:\n* You can use GDB which can give some sm parameters, but not all (e.g. no X- and Y-registers)\n* [VisualGDB](https://visualgdb.com/tutorials/raspberry/pico/pio/debugger/) looks awesome but seems to involve a bit of work (I haven't tried it)\n* [WOKWI](https://wokwi.com/tools/pioasm), see e.g. the [HackadayU video](https://www.youtube.com/watch?v=LIA9wpt7N60) where it is used.\n* [soundpaint rp2040pio](https://github.com/soundpaint/rp2040pio)\n* [NathanY3G rp2040-pio-emulator](https://github.com/NathanY3G/rp2040-pio-emulator), installable via [pip](https://pypi.org/project/rp2040-pio-emulator/)\n\nThere may be others that I don't know of.\nEven though it is a lot of work, building this emulator is quite instructive and fun (if you're into such things.)\n\n## Workflow\nWhen working on a project, I typically have an IDE (in my case vscode) with the project files, the pin_program and c_program files opened. Additionally, I have the emulator open with the .pio.h file of the project \n\nWhen changing the .pio code, I use the IDE function 'build' to have pioasm generate a new .pio.h file. Then I press reLoad in the emulator, and study the emulation output.\nWhen changing the C/C++ code that uses the pio code, I have to decide if the c_program also needs to change. Often, this will not be the case because the c_program and pin_program act more like test cases than that they mimic the real C/C++ code or real signals applied to the pins.\n\n\n## How does it work\nUsing python3, after reading and parsing the input files, it emulates the sm (and the c-statements, and the externally driven GPIOs) for 500 steps, which can of course be changed in the file config.py, and stores all the state variables at each step into a variable. Then the GUI is started which allows you to step back and forth through the results.\n\nRun it with: ```python main.py pio_program.pio.h pin_program c_program```\nFor example (1 line): ```python main.py examples/multiplication/pio_program.pio.h examples/multiplication/pin_program examples/multiplication/c_program```\n\nOr simply with:\n```python main.py examples/multiplication```\n\nNote: at a given time step, first the c-program and pin-program for that time step are executed, then the PIO statement. So, if the pin-program sets a pin, the PIO sees the pin as set.\n\nAfter the emulation, the GUI looks similar to the image below.\n![](emulator_screenshot_annotations.png)\n\n## GUI\n\nThe GUI consists of serveral sections:\n\n##### Externally driven pins\nIn this section the pin-program is shown. The highlighted text has just been executed. The statements should be placed in a file called pin_program. The format of the statements is: ```timestep, GPIOs, state```\nwhere:\n* ```timestep``` is the timestep at which an externally driven pin is changed\n* ```GPIOs``` can be either 'GPIOx' with x from 0 to 31, or 'all'\n* ```state``` can be '-1' for not driven externally, '0' for externally driven low, or '1' for externally driven high.\n\nFor example, a pin_program file can look like this:\n```\n0, all, -1      # GPIOs are not externally driven (=default, so not necessary)\n10, GPIO2, 1    # starting at t=10 GPIO2 is externally driven high\n15, GPIO2, 0    # starting at t=15 GPIO2 is externally driven low\n20, GPIO2, -1   # starting at t=20 GPIO2 is no longer externally driven\n```\n\nThe just executed pin statement is highlighted.\n\n##### State machine status\nThis section shows variables from the internals of the sm:\n* 'clock' is the current time step (actually, this is not strictly sm internal)\n* 'pc' is the program counter of the just executed pio statement\n* 'delay' is the amount of time steps still to be delayed due to a pio code delay option\n* 'status' is the fill state of the RxFIFO or TxFIFO\n* 'OSR shift counter' indicates when an autopull will occur\n* 'ISR shift counter' indicates when an autopush will occur\n* 'OSR' is the value in the Output Shift Register\n* 'ISR' is the value in the Input Shift Register\n* 'X' is the value in the X Register\n* 'Y' is the value in the Y Register\n* 'IRQ (sm=5)' indicates which IRQ is set. For some pio statements the IRQ depends on the sm (the Pico has 8), in this example it is sm 5, the default is sm 0.\n\n##### Toolbar\nThe toolbar presents a number of buttons to control the emulator:\n* 'reLoad' reloads all the files and restarts the emulation. This is handy for repeated compiling-emulating sessions: after compiling the pio code, just hit reLoad (or the 'l' or 'L' key)\n* 'Restart' goes back to t=0 (keybindings: 'r' or 'R')\n* 'back 50' goes back 50 time steps\n* 'back 10' goes back 10 time steps (keybinding: down arrow)\n* 'back' goes back one time step (keybinding: left arrow)\n* 'step' goes forward one time step (keybinding: right arrow)\n* 'step 10' goes forward 10 time steps (keybinding: up arrow)\n* 'step 50' goes forward 50 time steps\n* 'Quit' stops the emulator and GUI (keybindings: 'q' or 'Q')\n\n##### PIO code\nThis is the pio code that was obtained from the '*.pio.h' file (the * indicates that the name before .pio.h doesn't matter). This file should be generated by the pioasm tool, not hand-crafted!\n\nThe just executed line of pio code is highlighted.\n\nIn the .pio.h files generated by pioasm some statements are placed that are picked up by the emulator. The following information is used:\n* ```#define tester_wrap_target```\n* ```#define tester_wrap```\n* the compiled statements in ```..._program_instructions[]```\n* the .length in ```..._program[]```\n* the  '```sm_config_set_sideset```' statement in ```..._program_get_default_config[]```\n  * note: offset is not supported\n  * setting wrap parameters is done using the #define statements mentioned above\n* sideset: sideset_base is supported, the other settings (sideset_count, sideset_opt, and sideset_pindirs) are set by parsing the .pio.h file\n\n\n##### Output (get RxFIFO)\nFrom the 'C' program you can issue a 'get' statement. This removes one item from the RxFIFO and writes it as output. The just produced output is highlighted.\n\n##### FIFOs\nThese are the two FIFOs, each with 4 data items. The 'C' program can 'put' data in the TxFIFO, and 'get' it out of the RxFIFO.\n\n##### 'C'-statements\nThe important C/C++ statements for controlling the sm are supported in a simplified form. The statements should be placed in a file called 'c_program'. The format of the statements is as follows:\n```timestep, command, argument```\nwhere:\n* ```timestep``` is the timestep at which the command should be executed\n* ```command``` see below\n* ```argument``` the argument for the command\n\nHave a look at the examples to see how these commands work\n\nThe currently supported c-statements in the file c_program are:\n* set_base, set_count\nsets the 'set' pins to use, see [C/C++ SDK](https://rptl.io/pico-c-sdk) 4.1.15.5.55.\n* out_base, out_count\nsets the 'out' pins to use, see [C/C++ SDK](https://rptl.io/pico-c-sdk) 4.1.15.5.51.\n* sideset_base\nsets the 'sideset' pins to use, see [C/C++ SDK](https://rptl.io/pico-c-sdk) 4.1.15.5.56, and 4.1.16.3.13. Note that sideset_count , sideset_pindirs, and sideset_opt are set by parsing the .pio.h file (.side_set directive in pio code), but sideset_base isn't, you must set it in the c_program file.\n* in_base\nsets the 'in' pins to use, see [C/C++ SDK](https://rptl.io/pico-c-sdk) 4.1.15.5.50. \n* jmp_pin\nsets the 'jmp' pin to use, see [C/C++ SDK](https://rptl.io/pico-c-sdk) 4.1.16.3.7. \n* out_shift_right, out_shift_autopull, pull_threshold\nsets how the out shifting is handled, see [C/C++ SDK](https://rptl.io/pico-c-sdk) 4.1.16.3.10.\n* in_shift_right, in_shift_autopush, push_threshold\nsets how the in shifting is handled, see [C/C++ SDK](https://rptl.io/pico-c-sdk) 4.1.16.3.6.\n* put, get\nput data into the TxFIFO (non-blocking) or get data from the RxFIFO and show it in the output frame of the GUI. See [C/C++ SDK](https://rptl.io/pico-c-sdk) 4.1.15.5.42. and 4.1.15.5.30.\n* get_pc\nget the program counter and put it in the output frame of the GUI, see [C/C++ SDK](https://rptl.io/pico-c-sdk) 4.1.15.5.32.\n* set_N\nsets a register that allows monitoring the RxFIFO and TxFIFO levels via the mov statement, see [RP2040 datasheet](https://rptl.io/rp2040-datasheet) at section 3.4.8 (the mov statement) and section  3.7 (Registers). \n* status_sel\nselects which FIFO's level should be checked (0 = TxFIFO, 1 = RxFIFO)\n* dir_out\nsets the pindir of a pin to output\n* dir_in\nsets the pindir of a pin to input\n* dir_non\nunset the pindir of a pin\n\n##### Settings\nThese are some of the register settings that can influence how the sm functions.\n\n##### GPIO status\nThe GPIO can be changed and used by the sm in different ways. In the section 'Pin configuration' the following information is found:\n* GPIO\nThis shows the the GPIOs on the outside of the Pico. It is determined in the following way:\n  * the lowest priority are the 'out' and 'set' values (if the pindir indicates the GPIO is an output)\n  * mid priority is the sideset (if sideset is used for pins, and, if the pindir is set to output) \n  * highest priority are the externally driven pins. These win. If an external input is provided to a pin that is configured as output, a warning is given. This situation may potentially destroy the Pico.\n\n   The state of the pins is given as follows:\n  * . means not set\n  * 0 means low\n  * 1 means high \n\n* GPIO ext\nThis indicates if a pin is externally driven (configured by the pin-program). A '.' means not driven, '0' means driven low, and '1' means driven high.\n* pindir\nIndicates whether the pin is configured as Input '1', or as Output '0'.\n* sideset vals\nGives the values of the pins set by sideset.\n* sideset pins\nGives the pins that are going to be set via sideset. 'B' means the base, 'C' means count. So, if sideset is configured with pin 3 as base and a count of 4, this would look like: ```00000000000000000000000CCCB000```\n* out vals\nGives the values of the pins set by out\n* out pins\nGives the pins that are going to be set via out. 'B' means the base, 'C' means count. So, if out is configured with pin 3 as base and a count of 4, this would look like: ```00000000000000000000000CCCB000```\n* set vals\nGives the values of the pins set by set\n* set pins\nGives the pins that are going to be set via set. 'B' means the base, 'C' means count. So, if set is configured with pin 3 as base and a count of 4, this would look like: ```00000000000000000000000CCCB000```\n* in pins\nIndicates with a 'B' which pin is the base for the in operation\n* jmp pin\nIndicates with a 'B' which pin is the jmp-pin\n\n##### Warning and errors\nIn this part of the GUI warning will appear at the time step they were observed during the emulation. \n\n## What could be improved\nSeveral things, you tell me!\n\nSome things that might be improved:\n* not all functionality is implemented (search for TODO in the code), and what is implemented may be buggy and inefficient \n* there are 30 accessible GPIOs on the Pico, but in the emulator there are 32\n* the emulator can't be started without pin_program, c_program, or - of course - the *.pio.h files\n\n## Does it contain bugs?\nYes, be warned!\nIf you find any, please let met know so I can improve the code.\n\n\n## Examples\nI have included a number of examples to show that it works. For an explanation I refer to:\n* [button debounce](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Button-debouncer)\n* [multiplication](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/multiplication)\n* [rotational shift](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Rotational_shift_ISR)\n* [led panel](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/ledpanel)\n* [square wave](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), from the RP2040 Datasheet\n* [stepper motor](https://www.youtube.com/watch?v=UJ4JjeCLuaI) by Tinker Tech Trove\n* side_step\n* in_shift\n"
  },
  {
    "path": "state_machine_emulator/config.py",
    "content": "SAVE_TEST_DATA = False\n\nEMULATION_STEPS = 500\nSTATEMACHINE_NUMBER = 0\n"
  },
  {
    "path": "state_machine_emulator/emulation.py",
    "content": "from copy import deepcopy\n\n\nclass emulation:\n    \"\"\" This class controls the emulation of the sm of a RP2040 \"\"\"\n\n    def __init__(self, state_machine, pin_program, c_program):\n        \"\"\" init makes the list with the emulation results (output) and\n            the variables needed for highlights in the GUI\n        \"\"\"\n        # the emulator for the RP2040:\n        self.state_machine = state_machine\n        # the (user) changes made to the pins:\n        self.pin_program = pin_program\n        # the user c-program that influences the PIO and sm\n        self.c_program = c_program\n        # the output the emulator produces based on the c_program; this is used for the GUI\n        self.output = []\n        # lines to be highlighted in the GUI parts for e.g. the c-statements and pin-settings\n        self.emulation_highlight_pin_program = []\n        self.emulation_highlight_c_program = []\n        self.emulation_output_c_program = []\n        self.emulation_highlight_output_c_program = []\n\n    def emulate(self, number_of_steps):\n        \"\"\" emulate a number of steps \"\"\"\n        for step in range(number_of_steps):\n            # prepare for warning messages\n            self.warning_messages = []\n            # first execute the pin and c_program statements\n            warnings = self.execute_pin_and_c_program()\n            if warnings:\n                self.warning_messages.extend(warnings)\n            # then run the PIO code in the emulator\n            warnings = self.state_machine.time_step()\n            if warnings:\n                self.warning_messages.extend(warnings)\n            # copy the current state and append it to the list with all data\n            self.output.append((deepcopy(self.state_machine.GPIO_data),\n                                deepcopy(self.state_machine.vars),\n                                deepcopy(self.state_machine.settings),\n                                deepcopy(self.state_machine.sm_irq),\n                                deepcopy(self.emulation_highlight_pin_program),\n                                deepcopy(self.emulation_highlight_c_program),\n                                deepcopy(self.emulation_highlight_output_c_program),\n                                deepcopy(self.warning_messages)\n                                ))\n            # start with a clean list of to-be-highlighted parts\n            self.emulation_highlight_pin_program = []\n            self.emulation_highlight_c_program = []\n            self.emulation_highlight_output_c_program = []\n\n\n    def bit_string(self, value):\n        \"\"\" function to produce a string with the binary representation of a value \"\"\"\n        return str().join([\"0\" if (value & (1 << i)) == 0 else \"1\" for i in reversed(range(32))])\n\n\n    def execute_pin_and_c_program(self):\n        \"\"\" executes the c-program and pin settings as specified by the c- and pin-program \"\"\"\n        # get the current time from the state_machine (= system level clock)\n        time = self.state_machine.clock\n        # warnings\n        warning_messages = []\n\n        # set all the GPIO's according to the pin states\n        for index, g in enumerate(self.pin_program):\n            if g[0] == time:  # TODO? wasteful to go through the whole list each time. Keep an index or something (but then again, 500 steps takes almost no time ... so why bother)\n                # all pin_statements for this time step are to be highlighted\n                self.emulation_highlight_pin_program.append(index)\n                # handle the GPIO and 'all' statements\n                if 'GPIO' in g[1]:\n                    gpio = int(g[1].replace('GPIO', ''))\n                    self.state_machine.GPIO_data[\"GPIO_external\"][gpio] = int(g[2])\n                    # also set the real GPIO since external always wins!\n                    self.state_machine.GPIO_data[\"GPIO\"][gpio] = int(g[2])\n                elif 'all' in g[1]:\n                    for gpio in range(32):\n                        self.state_machine.GPIO_data[\"GPIO_external\"][gpio] = int(g[2])\n                        # also set the real GPIO since external always wins!\n                        self.state_machine.GPIO_data[\"GPIO\"][gpio] = int(g[2])\n                else:\n                    # should already have been filtered out when parsing the file, but anyway:\n                    warning_messages.append(\"Warning: unknown pin_program statement\"+str(g[0])+str(g[1])+str(g[2])+\"\\n\")\n\n        # set c_program settings\n        # go through the c-program, and at the right time, execute the statements\n        for index, c in enumerate(self.c_program):\n            if c[0] == time:  # TODO? wasteful to go through the whole list each time. Keep an index or something (but then again, 500 steps takes almost no time ... so why bother)\n                # all c_statements for this time step are to be highlighted in the GUI\n                self.emulation_highlight_c_program.append(index)\n                # handle all possible c_statements\n                if c[1] == 'put':\n                    # place a value in the Tx FIFO\n                    if self.state_machine.vars[\"TxFIFO_count\"] < 4:\n                        # there is still room in the TxFIFO: add the value c[2] and increase the number of items in the TxFIFO\n                        self.state_machine.vars[\"TxFIFO\"][self.state_machine.vars[\"TxFIFO_count\"]] = c[2]\n                        self.state_machine.vars[\"TxFIFO_count\"] += 1\n                        # make sure the 'status' is correct\n                        if self.state_machine.settings['status_sel'] == 0:\n                            if self.state_machine.vars[\"TxFIFO_count\"] < self.state_machine.settings['FIFO_level_N']:\n                                self.state_machine.vars[\"status\"] = 0xFFFFFFFF; # binary all ones \n                            else:\n                                self.state_machine.vars[\"status\"] = 0;  # binary all zeroes\n\n                elif c[1] == 'get':\n                    # get a value from the Rx FIFO, and put it in output\n                    if self.state_machine.vars[\"RxFIFO_count\"] > 0:\n                        # there are items in RxFIFO\n                        self.emulation_highlight_output_c_program.append(\n                            len(self.emulation_output_c_program))\n                        self.emulation_output_c_program.append(\n                            str(time) + \" : \" + str(self.state_machine.vars[\"RxFIFO\"][0]) + \" = \" + self.bit_string(self.state_machine.vars[\"RxFIFO\"][0]))\n                        # shift FIFO entries 1 to 4 back one place\n                        for i in range(0, 3):\n                            self.state_machine.vars[\"RxFIFO\"][i] = self.state_machine.vars[\"RxFIFO\"][i+1]\n                        # set the last entry to 0 (this may not happen in reality!)\n                        self.state_machine.vars[\"RxFIFO\"][3] = 0\n                        # there is now one less item in the Rx FIFO\n                        self.state_machine.vars[\"RxFIFO_count\"] -= 1\n                        # make sure the 'status' is correct\n                        if self.state_machine.settings['status_sel'] == 1:\n                            if self.state_machine.vars[\"RxFIFO_count\"] < self.state_machine.settings['FIFO_level_N']:\n                                self.state_machine.vars[\"status\"] = 0xFFFFFFFF; # binary all ones\n                            else:\n                                self.state_machine.vars[\"status\"] = 0; # binary all zeroes\n                elif c[1] in ['set_base', 'set_count', 'in_base', 'jmp_pin', 'sideset_base', 'sideset_count', 'sideset_opt', 'sideset_pindirs', 'out_base', 'out_count', 'out_shift_right', 'out_shift_autopull', 'pull_threshold', 'in_shift_right', 'in_shift_autopush', 'push_threshold']:\n                    self.state_machine.settings[c[1]] = c[2]\n                elif c[1] == 'get_pc':\n                    # note: the c_program is executed before an emulation step. Thus get_pc shows the previous pc in the gui\n                    self.emulation_highlight_output_c_program.append(len(self.emulation_output_c_program))\n                    self.emulation_output_c_program.append(str(time) + \" : \" + \"pc=\" + str(self.state_machine.vars[\"pc\"]))\n                elif c[1] == 'set_pc':\n                    # note: this should (maybe) only be used at t=0, the c_program is executed before an emulation step. Thus set_pc can set the starting point\n                    # note: If not set explicitly, pc = -1 at the start, so the pc first adds 1 to start at 0. Here the 1 must first be subtracted\n                    self.state_machine.vars[\"pc\"] = c[2]-1 \n                elif c[1] == 'irq':\n                    # clear the bit in c[2]. Note that in c++, when clearing an irq, you have to set the corresponding bit with:\n                    # pio0_hw->irq = 1<<irq\n                    # Also note that here all irq are visible to the c-program, normally only irq 0-3 are visible!\n                    if self.state_machine.sm_irq[c[2]] == 0:\n                        # if the irq bit was not set, set it\n                        self.state_machine.sm_irq[c[2]] = 1\n                    else:\n                        # if the irq bit was set, clear it\n                        self.state_machine.sm_irq[c[2]] = 0\n                elif c[1] == 'set_N':\n                    # set the # items in FIFOs that sets status (used in 'mov' instruction)  \n                    self.state_machine.settings['FIFO_level_N'] = c[2]\n                elif c[1] == 'status_sel':\n                    # set the # items in FIFOs that sets status (used in 'mov' instruction)  \n                    self.state_machine.settings['status_sel'] = c[2]\n                elif c[1] == 'dir_out':\n                    # set the pin to output \n                    self.state_machine.GPIO_data[\"GPIO_pindirs\"][c[2]] = 0\n                elif c[1] == 'dir_in':\n                    # set the pin direction to in \n                    self.state_machine.GPIO_data[\"GPIO_pindirs\"][c[2]] = 1\n                elif c[1] == 'dir_non':\n                    # unset the pin direction  \n                    self.state_machine.GPIO_data[\"GPIO_pindirs\"][c[2]] = -1\n                else:\n                    # should already have been filtered out when parsing the file, but anyway:\n                    warning_messages.append(\"Warning: unknown c-program statement: \" + str(c[0]) + str(c[1]) + str(c[2])+\"\\n\")\n\n        # if out_base and out_count are set, then make the associated pin an output in GPIO_pindirs\n        if self.state_machine.settings[\"out_base\"] != -1 and self.state_machine.settings[\"out_count\"] != 0:\n            for i in range(self.state_machine.settings[\"out_base\"], self.state_machine.settings[\"out_base\"] + self.state_machine.settings[\"out_count\"]):\n                self.state_machine.GPIO_data[\"GPIO_pindirs\"][i] = 0 # indicate this pin is an output\n        # if set_base and set_count are set, then make the associated pin an output in GPIO_pindirs\n        if self.state_machine.settings[\"set_base\"] != -1 and self.state_machine.settings[\"set_count\"] != 0:\n            for i in range(self.state_machine.settings[\"set_base\"], self.state_machine.settings[\"set_base\"] + self.state_machine.settings[\"set_count\"]):\n                self.state_machine.GPIO_data[\"GPIO_pindirs\"][i] = 0 # indicate this pin is an output\n        # if sideset_base and sideset_count are set, then make the associated pin(s) an output in GPIO_pindirs\n        if self.state_machine.settings[\"sideset_base\"] != -1 and self.state_machine.settings[\"sideset_count\"] != 0:\n            num_of_bits = self.state_machine.settings[\"sideset_count\"] - 1 if self.state_machine.settings[\"sideset_opt\"] else 0\n            for i in range(self.state_machine.settings[\"sideset_base\"], self.state_machine.settings[\"sideset_base\"] + num_of_bits):\n                self.state_machine.GPIO_data[\"GPIO_pindirs\"][i] = 0 # indicate this pin is an output\n\n        return warning_messages"
  },
  {
    "path": "state_machine_emulator/examples/button_debounce/c_program",
    "content": "# timestamp, command, arguments of command\n0, in_base, 0\n0, jmp_pin, 0\n5, get_pc\n10, get_pc\n15, get_pc\n20, get_pc\n21, get_pc\n22, get_pc\n23, get_pc\n25, get_pc\n30, get_pc\n35, get_pc\n40, get_pc\n45, get_pc\n50, get_pc\n55, get_pc\n60, get_pc\n65, get_pc\n70, get_pc\n75, get_pc\n80, get_pc\n85, get_pc\n110, get_pc\n115, get_pc\n120, get_pc\n125, get_pc\n130, get_pc\n135, get_pc\n140, get_pc\n145, get_pc\n150, get_pc\n155, get_pc\n160, get_pc\n165, get_pc\n170, get_pc\n175, get_pc\n180, get_pc\n185, get_pc\n210, get_pc\n215, get_pc\n220, get_pc\n225, get_pc\n230, get_pc\n235, get_pc\n240, get_pc\n245, get_pc\n250, get_pc\n255, get_pc\n260, get_pc\n265, get_pc\n270, get_pc\n275, get_pc\n280, get_pc\n285, get_pc\n310, get_pc\n315, get_pc\n320, get_pc\n325, get_pc\n330, get_pc\n335, get_pc\n340, get_pc\n345, get_pc\n350, get_pc\n355, get_pc\n360, get_pc\n365, get_pc\n370, get_pc\n375, get_pc\n380, get_pc\n385, get_pc\n410, get_pc\n415, get_pc\n420, get_pc\n425, get_pc\n430, get_pc\n435, get_pc\n440, get_pc\n445, get_pc\n450, get_pc\n455, get_pc\n460, get_pc\n465, get_pc\n470, get_pc\n475, get_pc\n480, get_pc\n485, get_pc"
  },
  {
    "path": "state_machine_emulator/examples/button_debounce/pin_program",
    "content": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx means pin x\n# state = -1 means not driven externally\n# state = 0 means driven low externally\n# state = 1 means driven high externally\n0, all, -1\n# the first couple of changes are skipped by the debouncing pc<6 meaning a 0\n0, GPIO0, 0\n20, GPIO0, 1\n40, GPIO0, 0\n60, GPIO0, 1\n80, GPIO0, 0\n100, GPIO0, 1\n# around this point the switch to 1 is made, pc>=6\n240, GPIO0, 0\n280, GPIO0, 1\n320, GPIO0, 0\n360, GPIO0, 1\n"
  },
  {
    "path": "state_machine_emulator/examples/button_debounce/pio_program.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// --------------- //\n// button_debounce //\n// --------------- //\n\n#define button_debounce_wrap_target 0\n#define button_debounce_wrap 10\n\nstatic const uint16_t button_debounce_program_instructions[] = {\n    //     .wrap_target\n    0x00c6, //  0: jmp    pin, 6\n    0x20a0, //  1: wait   1 pin, 0\n    0xe03f, //  2: set    x, 31\n    0x00c5, //  3: jmp    pin, 5\n    0x0001, //  4: jmp    1\n    0x0043, //  5: jmp    x--, 3\n    0x2020, //  6: wait   0 pin, 0\n    0xe03f, //  7: set    x, 31\n    0x00c6, //  8: jmp    pin, 6\n    0x0048, //  9: jmp    x--, 8\n    0x0001, // 10: jmp    1\n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program button_debounce_program = {\n    .instructions = button_debounce_program_instructions,\n    .length = 11,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config button_debounce_program_get_default_config(uint offset)\n{\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + button_debounce_wrap_target, offset + button_debounce_wrap);\n    return c;\n}\n#endif\n"
  },
  {
    "path": "state_machine_emulator/examples/in_shift/c_program",
    "content": "# timestamp, command, arguments of command\n0, in_base,  0\n0, in_shift_right,  0\n9, in_base,  10\n9, in_shift_right,  1\n"
  },
  {
    "path": "state_machine_emulator/examples/in_shift/pin_program",
    "content": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx means pin x\n# state = -1 means not driven externally\n# state = 0 means driven low externally\n# state = 1 means driven high externally\n0, all, -1\n0, GPIO0, 1\n0, GPIO1, 1\n0, GPIO2, 1\n0, GPIO3, 1\n1, GPIO0, 0\n1, GPIO1, 0\n1, GPIO2, 0\n1, GPIO3, 0\n2, GPIO0, 1\n2, GPIO1, 1\n2, GPIO2, 1\n2, GPIO3, 0\n3, GPIO0, 0\n3, GPIO1, 0\n3, GPIO2, 0\n3, GPIO3, 1\n10, GPIO10, 0\n10, GPIO11, 0\n10, GPIO12, 0\n10, GPIO13, 0\n11, GPIO10, 1\n11, GPIO11, 1\n11, GPIO12, 1\n11, GPIO13, 1\n12, GPIO10, 1\n12, GPIO11, 0\n12, GPIO12, 0\n12, GPIO13, 0\n13, GPIO10, 0\n13, GPIO11, 1\n13, GPIO12, 1\n13, GPIO13, 1"
  },
  {
    "path": "state_machine_emulator/examples/in_shift/pio_program.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#pragma once\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// ------ //\n// tester //\n// ------ //\n\n#define tester_wrap_target 0\n#define tester_wrap 0\n\nstatic const uint16_t tester_program_instructions[] = {\n            //     .wrap_target\n    0x4004, //  0: in     pins, 4                    \n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program tester_program = {\n    .instructions = tester_program_instructions,\n    .length = 1,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config tester_program_get_default_config(uint offset) {\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + tester_wrap_target, offset + tester_wrap);\n    return c;\n}\n#endif\n\n"
  },
  {
    "path": "state_machine_emulator/examples/irq_set_and_clear/c_program",
    "content": "# timestamp, command, arguments of command\n10, irq, 0\n12, irq, 1\n14, irq, 2\n16, irq, 3\n18, irq, 4\n20, irq, 5\n22, irq, 6\n24, irq, 7"
  },
  {
    "path": "state_machine_emulator/examples/irq_set_and_clear/pin_program",
    "content": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx means pin x\n# state = -1 means not driven externally\n# state = 0 means driven low externally\n# state = 1 means driven high externally\n0, all, -1"
  },
  {
    "path": "state_machine_emulator/examples/irq_set_and_clear/pio_program.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#pragma once\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// ------ //\n// tester //\n// ------ //\n\n#define tester_wrap_target 0\n#define tester_wrap 15\n\nstatic const uint16_t tester_program_instructions[] = {\n    //     .wrap_target\n    0xc000, //  0: irq    nowait 0\n    0xc001, //  1: irq    nowait 1\n    0xc002, //  2: irq    nowait 2\n    0xc003, //  3: irq    nowait 3\n    0xc004, //  4: irq    nowait 4\n    0xc005, //  5: irq    nowait 5\n    0xc006, //  6: irq    nowait 6\n    0xc007, //  7: irq    nowait 7\n    0xc020, //  8: irq    wait 0\n    0xc021, //  9: irq    wait 1\n    0xc022, //  10: irq    wait 2\n    0xc023, //  11: irq    wait 3\n    0xc024, //  12: irq    wait 4\n    0xc025, //  13: irq    wait 5\n    0xc026, //  14: irq    wait 6\n    0xc027, //  15: irq    wait 7\n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program tester_program = {\n    .instructions = tester_program_instructions,\n    .length = 16,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config tester_program_get_default_config(uint offset)\n{\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + tester_wrap_target, offset + tester_wrap);\n    return c;\n}\n#endif\n"
  },
  {
    "path": "state_machine_emulator/examples/ledpanel/c_program",
    "content": "# timestamp, command [, argument of command]\n0, sideset_base, 13\n0, out_shift_right, True\n0, out_shift_autopull, True\n0, pull_threshold, 22\n0, out_base, 0\n0, out_count, 11\n0, put, 4194303\n8, put, 4194303\n16, put, 4194303\n24, put, 4194303\n32, put, 4194303\n40, put, 4194303\n48, put, 4194303\n56, put, 4194303\n64, put, 4194303\n72, put, 4194303\n80, put, 4194303\n88, put, 4194303\n96, put, 4194303\n104, put, 4194303\n112, put, 4194303\n120, put, 4194303\n140, put, 4194303\n160, put, 4194303\n180, put, 4194303\n181, put, 4194303\n182, put, 4194303\n183, put, 4194303\n184, put, 4194303\n185, put, 4194303\n186, put, 4194303\n187, put, 4194303\n188, put, 4194303\n189, put, 4194303\n"
  },
  {
    "path": "state_machine_emulator/examples/ledpanel/ledpanel.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#pragma once\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// -------- //\n// ledpanel //\n// -------- //\n\n#define ledpanel_wrap_target 2\n#define ledpanel_wrap 10\n\nstatic const uint16_t ledpanel_program_instructions[] = {\n    0xb0cb, //  0: mov    isr, !null      side 4     \n    0x5079, //  1: in     null, 25        side 4     \n            //     .wrap_target\n    0xa026, //  2: mov    x, isr          side 0     \n    0x7a0b, //  3: out    pins, 11        side 6 [2] \n    0x1043, //  4: jmp    x--, 3          side 4     \n    0x642b, //  5: out    x, 11           side 1     \n    0x642b, //  6: out    x, 11           side 1     \n    0xa342, //  7: nop                    side 0 [3] \n    0xa342, //  8: nop                    side 0 [3] \n    0xa342, //  9: nop                    side 0 [3] \n    0x0047, // 10: jmp    x--, 7          side 0     \n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program ledpanel_program = {\n    .instructions = ledpanel_program_instructions,\n    .length = 11,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config ledpanel_program_get_default_config(uint offset) {\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + ledpanel_wrap_target, offset + ledpanel_wrap);\n    sm_config_set_sideset(&c, 3, false, false);\n    return c;\n}\n#endif\n\n"
  },
  {
    "path": "state_machine_emulator/examples/ledpanel/pin_program",
    "content": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx means pin x\n# state = -1 means not driven externally\n# state = 0 means driven low externally\n# state = 1 means driven high externally\n0, all, -1"
  },
  {
    "path": "state_machine_emulator/examples/multiplication/c_program",
    "content": "# timestamp, command, arguments of command\n0, put, 5\n0, put, 3\n55, get\n56, put, 7\n56, put, 8\n225, get"
  },
  {
    "path": "state_machine_emulator/examples/multiplication/pin_program",
    "content": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx means pin x\n# state = -1 means not driven externally\n# state = 0 means driven low externally\n# state = 1 means driven high externally\n0, all, -1"
  },
  {
    "path": "state_machine_emulator/examples/multiplication/pio_program.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// ------ //\n// tester //\n// ------ //\n\n#define tester_wrap_target 0\n#define tester_wrap 15\n\nstatic const uint16_t tester_program_instructions[] = {\n    //     .wrap_target\n    0x80a0, //  0: pull   block\n    0xa027, //  1: mov    x, osr\n    0xa0c1, //  2: mov    isr, x\n    0x80a0, //  3: pull   block\n    0xa047, //  4: mov    y, osr\n    0x0089, //  5: jmp    y--, 9\n    0xa0c3, //  6: mov    isr, null\n    0x8020, //  7: push   block\n    0x0000, //  8: jmp    0\n    0xa0e2, //  9: mov    osr, y\n    0xa04b, // 10: mov    y, !null\n    0x0050, // 11: jmp    x--, 16\n    0xa027, // 12: mov    x, osr\n    0x0054, // 13: jmp    x--, 20\n    0xa0ca, // 14: mov    isr, !y\n    0x8020, // 15: push   block\n            //     .wrap\n    0x008b, // 16: jmp    y--, 11\n    0xa0cb, // 17: mov    isr, !null\n    0x8020, // 18: push   block\n    0x0000, // 19: jmp    0\n    0xa0e1, // 20: mov    osr, x\n    0xa026, // 21: mov    x, isr\n    0x000b, // 22: jmp    11\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program tester_program = {\n    .instructions = tester_program_instructions,\n    .length = 23,\n    .origin = -1,\n};\n\n// sm_config_set_sideset(&c, 2, true, true);\nstatic inline pio_sm_config tester_program_get_default_config(uint offset)\n{\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + tester_wrap_target, offset + tester_wrap);\n    return c;\n}\n#endif\n"
  },
  {
    "path": "state_machine_emulator/examples/push_pull_auto/c_program",
    "content": "0, out_shift_right, True\n0, out_shift_autopull, True\n0, pull_threshold, 10\n0, in_shift_right, False\n0, in_shift_autopush, True\n0, push_threshold, 3\n20, put, 31\n30, get\n31, get\n40, put, 63\n50, get\n51, get\n60, put, 95\n70, get\n71, get\n80, put, 127\n90, get\n91, get\n100, put, 159\n105, get\n106, get\n110, put, 191\n115, get\n116, get\n120, put, 223\n125, get\n126, get\n140, put, 225\n145, get\n146, get\n160, put, 287\n165, get\n166, get\n180, put, 319\n185, get\n186, get\n200, put, 351\n205, get\n206, get\n"
  },
  {
    "path": "state_machine_emulator/examples/push_pull_auto/pin_program",
    "content": "0, all, 0\n"
  },
  {
    "path": "state_machine_emulator/examples/push_pull_auto/push_pull.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#pragma once\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// --------- //\n// push_pull //\n// --------- //\n\n#define push_pull_wrap_target 0\n#define push_pull_wrap 1\n\nstatic const uint16_t push_pull_program_instructions[] = {\n            //     .wrap_target\n    0x6025, //  0: out    x, 5                       \n    0x4023, //  1: in     x, 3                       \n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program push_pull_program = {\n    .instructions = push_pull_program_instructions,\n    .length = 2,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config push_pull_program_get_default_config(uint offset) {\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + push_pull_wrap_target, offset + push_pull_wrap);\n    return c;\n}\n#endif\n\n"
  },
  {
    "path": "state_machine_emulator/examples/rotational_shift/c_program",
    "content": "# timestamp, command, arguments of command\n0, put, 1234567890\n10, get\n15, get\n20, get\n25, get\n30, get\n35, get\n40, get\n45, get\n50, get\n60, get\n65, get\n70, get\n75, get\n80, get\n85, get\n90, get\n95, get\n100, get\n105, get\n110, get\n115, get\n120, get\n125, get\n130, get\n135, get\n140, get\n145, get\n150, get\n160, get\n165, get\n170, get\n175, get\n180, get\n185, get\n190, get\n195, get\n200, get\n205, get\n210, get\n215, get\n220, get\n225, get\n230, get\n235, get\n240, get\n245, get\n250, get\n260, get\n265, get\n270, get\n275, get\n280, get\n285, get\n290, get\n295, get\n300, get\n305, get\n310, get\n315, get\n320, get\n325, get\n330, get\n335, get\n340, get\n345, get\n350, get\n360, get\n365, get\n370, get\n375, get\n380, get\n385, get\n390, get"
  },
  {
    "path": "state_machine_emulator/examples/rotational_shift/pin_program",
    "content": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx means pin x\n# state = -1 means not driven externally\n# state = 0 means driven low externally\n# state = 1 means driven high externally\n0, all, -1"
  },
  {
    "path": "state_machine_emulator/examples/rotational_shift/pio_program.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// -------------------- //\n// rotational_shift_ISR //\n// -------------------- //\n\n#define rotational_shift_ISR_wrap_target 0\n#define rotational_shift_ISR_wrap 18\n\nstatic const uint16_t rotational_shift_ISR_program_instructions[] = {\n    //     .wrap_target\n    0x80a0, //  0: pull   block\n    0xa0c7, //  1: mov    isr, osr\n    0xa046, //  2: mov    y, isr\n    0x8020, //  3: push   block\n    0xa0c2, //  4: mov    isr, y\n    0xe03f, //  5: set    x, 31\n    0x40c1, //  6: in     isr, 1\n    0xa046, //  7: mov    y, isr\n    0x8020, //  8: push   block\n    0xa0c2, //  9: mov    isr, y\n    0x0046, // 10: jmp    x--, 6\n    0xe03f, // 11: set    x, 31\n    0xa0d6, // 12: mov    isr, ::isr\n    0x40c1, // 13: in     isr, 1\n    0xa0d6, // 14: mov    isr, ::isr\n    0xa046, // 15: mov    y, isr\n    0x8020, // 16: push   block\n    0xa0c2, // 17: mov    isr, y\n    0x004c, // 18: jmp    x--, 12\n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program rotational_shift_ISR_program = {\n    .instructions = rotational_shift_ISR_program_instructions,\n    .length = 19,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config rotational_shift_ISR_program_get_default_config(uint offset)\n{\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + rotational_shift_ISR_wrap_target, offset + rotational_shift_ISR_wrap);\n    return c;\n}\n#endif\n"
  },
  {
    "path": "state_machine_emulator/examples/side_step/README.md",
    "content": "# The sidestep also works with two GPIO as output:\n\n## pio program:\n```\n.program tester\n \n.side_set 2 opt\n\n.wrap_target\n    nop side 0b01\n    nop\n    nop side 0b10\n.wrap\n```\n## c program:\n```\n0, sideset_base, 4\n```\n\n"
  },
  {
    "path": "state_machine_emulator/examples/side_step/c_program",
    "content": "# timestamp, command, arguments of command\n0, sideset_base, 4"
  },
  {
    "path": "state_machine_emulator/examples/side_step/pin_program",
    "content": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx means pin x\n# state = -1 means not driven externally\n# state = 0 means driven low externally\n# state = 1 means driven high externally\n0, all, -1"
  },
  {
    "path": "state_machine_emulator/examples/side_step/pio_program.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#pragma once\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// ------ //\n// tester //\n// ------ //\n\n#define tester_wrap_target 0\n#define tester_wrap 2\n\nstatic const uint16_t tester_program_instructions[] = {\n            //     .wrap_target\n    0xb042, //  0: nop                    side 0     \n    0xa042, //  1: nop                               \n    0xb842, //  2: nop                    side 1     \n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program tester_program = {\n    .instructions = tester_program_instructions,\n    .length = 3,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config tester_program_get_default_config(uint offset) {\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + tester_wrap_target, offset + tester_wrap);\n    sm_config_set_sideset(&c, 2, true, false);\n    return c;\n}\n#endif\n\n"
  },
  {
    "path": "state_machine_emulator/examples/square_wave/c_program",
    "content": "# timestamp, command, arguments of command (typically pio, sm, arguments)\n0, set_base, 0\n0, set_count, 1\n"
  },
  {
    "path": "state_machine_emulator/examples/square_wave/pin_program",
    "content": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx means pin x\n# state = -1 means not driven externally\n# state = 0 means driven low externally\n# state = 1 means driven high externally\n0, all, -1"
  },
  {
    "path": "state_machine_emulator/examples/square_wave/pio_program.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// ---------- //\n// squarewave //\n// ---------- //\n\n#define squarewave_wrap_target 0\n#define squarewave_wrap 3\n\nstatic const uint16_t squarewave_program_instructions[] = {\n            //     .wrap_target\n    0xe081, //  0: set    pindirs, 1                 \n    0xe101, //  1: set    pins, 1                [1] \n    0xe000, //  2: set    pins, 0                    \n    0x0001, //  3: jmp    1                          \n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program squarewave_program = {\n    .instructions = squarewave_program_instructions,\n    .length = 4,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config squarewave_program_get_default_config(uint offset) {\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + squarewave_wrap_target, offset + squarewave_wrap);\n    return c;\n}\n#endif\n\n"
  },
  {
    "path": "state_machine_emulator/examples/stepper/c_program",
    "content": "# timestamp, command, arguments of command\n0, out_base, 0\n0, out_count, 4\n0, put, 100\n0, put, 2216789025\n0, out_shift_right, 1\n"
  },
  {
    "path": "state_machine_emulator/examples/stepper/pin_program",
    "content": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx means pin x\n# state = -1 means not driven externally\n# state = 0 means driven low externally\n# state = 1 means driven high externally\n0, all, -1"
  },
  {
    "path": "state_machine_emulator/examples/stepper/pio_program.pio.h",
    "content": "// This example is taken from:\n// https://github.com/tinkertechtrove/pico-pi-playing/blob/main/pio-steppers/test_motor4.py\n\n// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#pragma once\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// ------ //\n// tester //\n// ------ //\n\n#define tester_wrap_target 0\n#define tester_wrap 9\n\nstatic const uint16_t tester_program_instructions[] = {\n    //     .wrap_target\n    0x80a0, //  0: pull   block\n    0xa027, //  1: mov    x, osr\n    0x80a0, //  2: pull   block\n    0xa047, //  3: mov    y, osr\n    0x0029, //  4: jmp    !x, 9\n    0x00e7, //  5: jmp    !osre, 7\n    0xa0e2, //  6: mov    osr, y\n    0x7f04, //  7: out    pins, 4                [31]\n    0x0045, //  8: jmp    x--, 5\n    0xc000, //  9: irq    nowait 0\n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program tester_program = {\n    .instructions = tester_program_instructions,\n    .length = 10,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config tester_program_get_default_config(uint offset)\n{\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + tester_wrap_target, offset + tester_wrap);\n    return c;\n}\n#endif\n"
  },
  {
    "path": "state_machine_emulator/interface/__init__.py",
    "content": "from tkinter import Tk\nfrom threading import Thread\n\nclass Emulator_Interface(Thread):\n    \"\"\" This class builds the GUI to show the results of the emulation of the sm of a RP2040 \"\"\"\n\n    # Imported methods\n    from ._toolbar import build_toolbar, step_callback, quit_callback, step_10_callback, step_50_callback, restart_callback, step_10_back_callback, step_50_back_callback, step_back_callback, reload_callback, enable_disable_buttons\n    from ._left_frame import build_left_frame, update_left_frame\n    from ._mid_frame import build_mid_frame, update_mid_frame\n    from ._right_frame import build_right_frame, update_right_frame\n    from ._output_frame import build_output_frame, update_output_frame\n\n    def __init__(self, program_definitions, pin_program, c_program, emulation_results, emulation_output_c_program):\n        \"\"\" build and run the GUI \"\"\"\n        # the flag that can signal to the main function to reload all files\n        self.reload_flag = False\n        # the data that needs to be displayed\n        self.program_definitions = program_definitions\n        self.pio_program = program_definitions['pio_program']\n        self.pin_program = pin_program\n        self.c_program = c_program\n        self.emulation_results = emulation_results\n        self.emulation_output_c_program = emulation_output_c_program\n        # the current time and max time (for checking jumps of +1, +10 and +50)\n        self.current_clock = 0\n        self.max_clock = len(self.emulation_results)\n        # Make the window\n        self.root = Tk()\n        # Make the title that will appear in the top left\n        self.root.wm_title(\"Raspberry PI PICO PIO Emulation\")\n        # set background color to white\n        self.root.config(background=\"#FFFFFF\")\n\n        # this is just a simple activity to allow ctrl-c in a terminal to function\n        # TODO: doesn't work?\n        self.after_id = self.root.after(50, self.check)\n\n        # make the frames\n        self.build_toolbar()\n        self.build_left_frame()\n        self.build_mid_frame()\n        self.build_right_frame()\n        self.build_output_frame()\n\n        # update the display to reflect the data of self.current_clock=0\n        self.update_display()\n        # start the monitoring and updating\n        self.root.mainloop()\n\n    def check(self):\n        \"\"\" this is just a simple activity to allow ctrl-c to function \"\"\"\n        self.after_id = self.root.after(50, self.check)  # 50 stands for 50 ms.\n\n    def __del__(self):\n        \"\"\" clean up at exit \"\"\"\n        self.root.after_cancel(self.after_id)\n        try:\n            self.root.quit()\n            self.root.destroy()\n        except:\n            pass\n\n    def get_reload_flag(self):\n        return self.reload_flag\n\n    def update_display(self):\n        \"\"\" update the display at every step to show the emulation results for the new step \"\"\"\n        self.update_left_frame()\n        self.update_mid_frame()\n        self.update_right_frame()\n        self.update_output_frame()\n"
  },
  {
    "path": "state_machine_emulator/interface/_interface_item.py",
    "content": "from tkinter import Text, Label, Listbox\nfrom interface._tooltips import CreateToolTip\n\n\"\"\"\nAll the classes here are an element of the GUI. They all have a setup (__init__), a method to make a string to be used and update.\n\"\"\"\n\nclass Interface_Item:\n    def __init__(self, frame, display_name, row, col, width):\n        self.display_name = display_name\n        self.row = row\n        self.col = col\n        self.width = width\n        # make the Text widget for the name of the variable to be displayed\n        label = Text(frame, height=1, width=12)\n        label.insert(\"end\", self.display_name)\n        label.configure(font=\"TkFixedFont\", state=\"disabled\")\n        label.grid(row=self.row, column=self.col, padx=(5, 5), sticky='W')\n        # make the Text widget for the value of the variable\n        self.value_label = Text(frame, height=1, width=self.width)\n        self.value_label.insert(\"end\", self.value_string(0))\n        self.value_label.configure(font=\"TkFixedFont\", state=\"disabled\")\n        self.value_label.grid(row=self.row, column=self.col, padx=(5, 5), sticky='E')\n        # make tags to be used in showing which characters have changed, and which have not\n        self.value_label.tag_config(\"changed\", foreground=\"Blue\")\n        self.value_label.tag_config(\"unchanged\", foreground=\"Black\")\n\n    def update(self, clock):\n        # save the current value\n        old_value = self.value_label.get(\"1.0\", \"end\").strip()\n        # allow the widget text to be changed, and delete the existing content\n        self.value_label.configure(state=\"normal\")\n        self.value_label.delete(\"1.0\", \"end\")\n        # determine the new text for the widget, and insert it\n        new_value = self.value_string(clock)\n        self.value_label.insert(\"end\", new_value)\n        # disallow changing the text, justify right\n        self.value_label.configure(state=\"disabled\")\n        self.value_label.tag_configure(\"j_right\", justify='right')\n        self.value_label.tag_add(\"j_right\", 1.0, \"end\")\n        # now apply colors, using tags \"changed and \"unchanged\", e.g. to the last 32 characters (the binary representation)\n        len_string = min(len(new_value), len(old_value), 32)\n        for i in range(-1,-len_string-1, -1):\n            if old_value[i] != new_value[i]:\n                self.value_label.tag_add(\"changed\", f\"{1}.{len(new_value)+i}\") \n            else:\n                self.value_label.tag_add(\"unchanged\", f\"{1}.{len(new_value)+i}\") \n\n            \nclass Var_Bits_32(Interface_Item):\n    def __init__(self, display_name, var_name, frame, row, col, var, var_index):\n        self.var_name = var_name\n        self.var = var\n        self.var_index = var_index\n        super().__init__(frame, display_name, row, col, 45)\n\n    def value_string(self, clock):\n        value_string = str(self.var[clock][self.var_index][self.var_name] & 0xFFFFFFFF) + \" = \"\n        value = self.var[clock][self.var_index][self.var_name]\n        # extend the value_string based on bits in 'value' \n        for i in reversed(range(32)):\n            value_string += \"0\" if (value & (1 << i)) == 0 else \"1\"\n        return value_string\n\n\nclass Pin_Settings_32(Interface_Item):\n    def __init__(self, display_name, base_name, count_name, frame, row, col, var, var_index):\n        self.base_name = base_name\n        self.count_name = count_name\n        self.var = var\n        self.var_index = var_index\n        super().__init__(frame, display_name, row, col, 40)\n\n    def value_string(self, clock):\n        base = self.var[clock][self.var_index][self.base_name]\n        count = 0\n        if self.count_name:\n            count = self.var[clock][self.var_index][self.count_name]\n            # dirty hack: sideset_count includes the sideset_opt bit, but this does not set a pin!\n            if self.count_name == \"sideset_count\" and self.var[clock][self.var_index][\"sideset_opt\"]:\n                count -= 1\n        value_string_list = ['.' for i in range(32)]\n        if base >= 0:\n            value_string_list[31-base] = 'B'\n        for i in range(count-1):\n            value_string_list[31-(base+1+i) % 32] = 'C'\n        value_string = ''.join(value_string_list)\n        return value_string\n\n\nclass Var_List_IRQ(Interface_Item):\n    def __init__(self, display_name, frame, row, col, var, var_index):\n        self.var = var\n        self.var_index = var_index\n        super().__init__(frame, display_name, row, col, 40)\n\n    def value_string(self, clock):\n        value_string = \"\"\n        for v in reversed(self.var[clock][self.var_index]):\n            value_string += \"1\" if v==1 else \"0\" if v==0 else \".\"\n        return value_string\n\nclass Var_List(Interface_Item):\n    def __init__(self, display_name, frame, row, col, var, var_index):\n        self.var = var\n        self.var_index = var_index\n        super().__init__(frame, display_name, row, col, 40)\n\n    def value_string(self, clock):\n        value_string = \"\"\n        for v in reversed(self.var[clock][0][self.var_index]):\n            value_string += \"1\" if v==1 else \"0\" if v==0 else \".\"\n        return value_string\n\n\nclass Interface_Item_Listbox_Bits:\n    def __init__(self, display_name, var_name, frame, row, col, var, clock):\n        self.display_name = display_name\n        self.var_name = var_name\n        self.var = var\n        label = Label(frame, text=display_name + ' \\u24D8')\n        CreateToolTip(label, \\\n            \"The data in TxFIFO is transmitted from the normal core to the state machine.\\n\"\n            \"The data in RxFIFO is transmitted from the state machine to the normal core. \")\n        label.grid(row=row, column=col, padx=(5, 5), sticky='W')\n        self.value_listbox = Listbox(frame, height=4, width=45, justify=\"right\", exportselection=0)\n        for index in range(4):\n            self.value_listbox.insert(\"end\", self.value_string(index, clock))\n        self.value_listbox.grid(row=row+1, column=0, padx=(5, 5))\n\n    def update(self, clock):\n        for index in range(4):\n            self.value_listbox.delete(0)\n        for index in range(4):\n            self.value_listbox.insert(\"end\", self.value_string(index, clock))\n\n    def value_string(self, index, clock):\n        value_string = str(self.var[clock][1][self.var_name][index] & 0xFFFFFFFF) + \" = \"\n        value = self.var[clock][1][self.var_name][index]\n        for i in reversed(range(32)):\n            value_string += \"0\" if (value & (1 << i)) == 0 else \"1\"\n        return value_string\n\n\nclass Interface_Item_Listbox_Time:\n    def __init__(self, display_name, frame, row, col, var):\n        self.display_name = display_name\n        self.var = var\n        label = Label(frame, text=display_name)\n        label.grid(row=row, column=col, padx=(5, 5), sticky='W')\n        self.value_listbox = Listbox(frame, height=13, width=45, exportselection=0)\n        for index in range(len(var)):\n            self.value_listbox.insert(\"end\", self.value_string(index))\n        self.value_listbox.grid(row=row+1, column=0, padx=(5, 5))\n\n    def update(self):\n        for index in range(4):\n            self.value_listbox.delete(0)\n        for index in range(4):\n            self.value_listbox.insert(\"end\", self.value_string(index))\n\n    def value_string(self, index):\n        value_string = str(self.var[index][0]) + \" : \" + self.var[index][1]\n        first = True\n        for l in self.var[index][2:]:\n            value_string += \"=\" if first else \", \"\n            first = False\n            value_string += str(l)\n        return value_string\n"
  },
  {
    "path": "state_machine_emulator/interface/_left_frame.py",
    "content": "from tkinter import Frame\n\n\ndef build_left_frame(self):\n    \"\"\" build the panel on the left with the c-program, pin_program, and the TxFIFO and RxFIFO \"\"\"\n    from interface._interface_item import Interface_Item_Listbox_Bits, Interface_Item_Listbox_Time\n\n    # on the left side\n    self.left_frame = Frame(self.root, width=375, height=725)\n    self.left_frame.grid(row=1, column=0, padx=0, pady=2)\n    self.left_frame.grid_propagate(0)\n\n    # start on row=2 with placing widgets\n    grid_row = 2\n\n    # pin states program\n    self.pin_program_listbox = Interface_Item_Listbox_Time(\"Highlight is just executed pin-program\", self.left_frame, grid_row, 0, self.pin_program)\n    grid_row += 2\n\n    # c program\n    self.c_program_listbox = Interface_Item_Listbox_Time(\"Highlight is just executed C-program\", self.left_frame, grid_row, 0, self.c_program)\n    grid_row += 2\n\n    # TxFIFO\n    self.TxFIFO_listbox = Interface_Item_Listbox_Bits(\"TxFIFO\", \"TxFIFO\", self.left_frame, grid_row, 0, self.emulation_results, self.current_clock)\n    grid_row += 2\n\n    # RxFIFO\n    self.RxFIFO_listbox = Interface_Item_Listbox_Bits(\"RxFIFO\", \"RxFIFO\", self.left_frame, grid_row, 0, self.emulation_results, self.current_clock)\n\n\ndef update_left_frame(self):\n    \"\"\" update the left frame \"\"\"\n    # update the Tx and Rx FIFOs\n    self.TxFIFO_listbox.update(self.current_clock)\n    self.RxFIFO_listbox.update(self.current_clock)\n\n    # highlight the just executed pin_program statements\n    self.pin_program_listbox.value_listbox.selection_clear(0, \"end\")\n    # make sure the first highlighted item is visible by using 'see'\n    first = True\n    for index in self.emulation_results[self.current_clock][4]:\n        self.pin_program_listbox.value_listbox.selection_set(index)\n        if first:\n            self.pin_program_listbox.value_listbox.see(index)\n            first = False\n\n    # highlight the just executed c_program statements\n    self.c_program_listbox.value_listbox.selection_clear(0, \"end\")\n    # make sure the first highlighted item is visible by using 'see'\n    first = True\n    for index in self.emulation_results[self.current_clock][5]:\n        self.c_program_listbox.value_listbox.selection_set(index)\n        if first:\n            self.c_program_listbox.value_listbox.see(index)\n            first = False\n"
  },
  {
    "path": "state_machine_emulator/interface/_mid_frame.py",
    "content": "from tkinter import Frame, Label, Text\nfrom config import STATEMACHINE_NUMBER\n\ndef build_mid_frame(self):\n    \"\"\" build the panel with the sm variables and settings \"\"\"\n    from interface._interface_item import Var_Bits_32, Var_List_IRQ, Var_List, Pin_Settings_32\n    from interface._tooltips import CreateToolTip\n\n    # In the middle\n    self.mid_frame = Frame(self.root, width=460, height=725)\n    self.mid_frame.grid(row=1, column=1, padx=0, pady=2)\n    self.mid_frame.grid_propagate(0)\n\n    grid_row = 2\n\n    # make a separate frame for some data items (clock, pc, delay, status)\n    self.clk_pc_delay_stat_frame = Frame(self.mid_frame, width=460, height=25)\n    self.clk_pc_delay_stat_frame.grid(row=grid_row, column=1, padx=0, pady=5)\n    self.clk_pc_delay_stat_frame.grid_propagate(0)\n    self.clk_pc_delay_stat_frame.grid_columnconfigure(index=0, weight=1)\n    self.clk_pc_delay_stat_frame.grid_columnconfigure(index=1, weight=1)\n    self.clk_pc_delay_stat_frame.grid_columnconfigure(index=2, weight=1)\n    self.clk_pc_delay_stat_frame.grid_columnconfigure(index=3, weight=1)\n    inset_col=0\n    # clock\n    self.clock_label = Label(self.clk_pc_delay_stat_frame, text=\"clock=0\", font=(\"Arial\", 14))\n    self.clock_label.grid(row=1, column=inset_col, padx=(5, 5), sticky='W')\n    inset_col += 1\n    # PIO pc\n    self.pc_label = Label(self.clk_pc_delay_stat_frame, text=\"pc=0\")\n    self.pc_label.grid(row=1, column=inset_col, padx=(5, 5), sticky='W')\n    inset_col += 1\n    # Wait cycles to complete\n    self.delay_label = Label(self.clk_pc_delay_stat_frame, text=\"delay=0\")\n    self.delay_label.grid(row=1, column=inset_col, padx=(5, 5), sticky='W')\n    inset_col += 1\n    # status\n    self.status_label = Label(self.clk_pc_delay_stat_frame, text=\"status=0\")\n    self.status_label.grid(row=1, column=inset_col, padx=(5, 5), sticky='W')\n\n    grid_row += 1\n\n    # make a separate frame for some data items (OSR and ISR shift counters)\n    self.OSR_ISR_shift_counters_frame = Frame(self.mid_frame, width=460, height=25)\n    self.OSR_ISR_shift_counters_frame.grid(row=grid_row, column=1, padx=0, pady=0)\n    self.OSR_ISR_shift_counters_frame.grid_propagate(0)\n    self.OSR_ISR_shift_counters_frame.grid_columnconfigure(index=0, weight=1)\n    self.OSR_ISR_shift_counters_frame.grid_columnconfigure(index=1, weight=1)\n    self.OSR_ISR_shift_counters_frame.grid_columnconfigure(index=2, weight=0)\n    self.OSR_ISR_shift_counters_frame.grid_columnconfigure(index=3, weight=0)\n    inset_col=0\n    # OSR shift counter\n    self.OSR_shift_counter_label = Label(self.OSR_ISR_shift_counters_frame, text=\"OSR shift counter=0\")\n    self.OSR_shift_counter_label.grid(row=1, column=inset_col, padx=(5, 5), sticky='W')\n    inset_col += 1\n    # ISR shift counter\n    self.ISR_shift_counter_label = Label(self.OSR_ISR_shift_counters_frame, text=\"ISR shift counter=0\")\n    self.ISR_shift_counter_label.grid(row=1, column=inset_col, padx=(5, 5), sticky='W')\n    \n    # Now place the other data items in rows\n    grid_row += 1\n    # OSR\n    self.OSR_label = Var_Bits_32(\"OSR\", \"OSR\", self.mid_frame, grid_row, 1, self.emulation_results, 1)\n    grid_row += 1\n    # ISR\n    self.ISR_label = Var_Bits_32(\"ISR\", \"ISR\", self.mid_frame, grid_row, 1, self.emulation_results, 1)\n    grid_row += 1\n    # x\n    self.X_label = Var_Bits_32(\"X\", \"x\", self.mid_frame, grid_row, 1, self.emulation_results, 1)\n    grid_row += 1\n    # y\n    self.Y_label = Var_Bits_32(\"Y\", \"y\", self.mid_frame, grid_row, 1, self.emulation_results, 1)\n    grid_row += 1\n    # empty row\n    self.empty_label1 = Label(self.mid_frame)\n    self.empty_label1.grid(row=grid_row, column=1, padx=(5, 5), sticky='W')\n    grid_row += 1\n    # IRQ\n    self.IRQ_label = Var_List_IRQ(\"IRQ (sm=\"+str(STATEMACHINE_NUMBER)+\")\", self.mid_frame, grid_row, 1, self.emulation_results, 3)\n    grid_row += 1\n\n    # pin configuration section label\n    self.pin_config_label = Label(self.mid_frame, text=\"\\nPin configuration: \\u24D8\")\n    self.pin_config_label.grid(row=grid_row, column=1, padx=(5, 5), sticky='W')\n    self.pin_config_label_ttp = CreateToolTip(self.pin_config_label, \\\n        \"The GPIO output (first row below) is determined by (lowest priority) 'out' and 'set', or (mid priority) 'sideset'.\\n\"\n        \"The 'out', 'set' and 'sideset' also require pindir to be an output (a '0' in the third row below).\\n\"\n        \"The highest priority is an external signal (GPIO ext). This wins from any internal signal (and may damage the RPI pico!)\")\n    grid_row += 1\n    # GPIO \n    self.GPIO_label = Var_List(\"GPIO\", self.mid_frame, grid_row, 1, self.emulation_results, \"GPIO\")\n    grid_row += 1\n    # GPIO_external \n    self.GPIO_ext_label = Var_List(\"GPIO ext\", self.mid_frame, grid_row, 1, self.emulation_results, \"GPIO_external\")\n    grid_row += 1\n    # GPIO pindirs\n    self.GPIO_pindirs_label = Var_List(\"pindir\", self.mid_frame, grid_row, 1, self.emulation_results, \"GPIO_pindirs\")\n    grid_row += 1\n    # SIDESET_pins\n    self.sideset_vals_label = Var_List(\"sideset vals\", self.mid_frame, grid_row, 1, self.emulation_results, \"GPIO_sideset\")\n    grid_row += 1\n    self.sideset_pins_label = Pin_Settings_32(\"sideset pins\", \"sideset_base\", \"sideset_count\", self.mid_frame, grid_row, 1, self.emulation_results, 2)\n    grid_row += 1\n    # OUT_pins\n    self.out_vals_label = Var_List(\"out vals\", self.mid_frame, grid_row, 1, self.emulation_results, \"GPIO_out\")\n    grid_row += 1\n    self.out_pins_label = Pin_Settings_32(\"out pins\", \"out_base\", \"out_count\", self.mid_frame, grid_row, 1, self.emulation_results, 2)\n    grid_row += 1\n    # SET_pins\n    self.set_vals_label = Var_List(\"set vals\", self.mid_frame, grid_row, 1, self.emulation_results, \"GPIO_set\")\n    grid_row += 1\n    self.set_pins_label = Pin_Settings_32(\"set pins\", \"set_base\", \"set_count\", self.mid_frame, grid_row, 1, self.emulation_results, 2)\n    grid_row += 1\n    # IN_pins\n    self.in_pins_label = Pin_Settings_32(\"in pins\", \"in_base\", None, self.mid_frame, grid_row, 1, self.emulation_results, 2)\n    grid_row += 1\n    # JMP_PIN\n    self.jmp_pin_label = Pin_Settings_32(\"jmp pin\", \"jmp_pin\", None, self.mid_frame, grid_row, 1, self.emulation_results, 2)\n    grid_row += 1\n    # settings\n    temp = Label(self.mid_frame, text=\"\\nSettings:\")\n    temp.grid(row=grid_row, column=1, padx=(5, 5), sticky='W')\n    grid_row += 1\n    # place the settings that are to be displayed\n    settings = self.emulation_results[0][2]\n    self.list_of_settings_to_be_displayed = [\"in_shift_right\", \"in_shift_autopush\", \"push_threshold\", \"out_shift_right\", \"out_shift_autopull\", \"pull_threshold\", \"sideset_opt\", \"sideset_pindirs\"]\n    self.settings_labels = [None]*len(self.list_of_settings_to_be_displayed)\n    for i, s in enumerate(self.list_of_settings_to_be_displayed):\n        label = Text(self.mid_frame, height=1, width=20)\n        label.insert(\"end\", s)\n        label.configure(font=\"TkFixedFont\", state=\"disabled\")\n        label.grid(row=grid_row, column=1, padx=(5, 5), sticky='W')\n        self.settings_labels[i] = Text(self.mid_frame, height=1, width=35)\n        self.settings_labels[i].insert(\"end\", str(settings[s]))\n        self.settings_labels[i].configure(font=\"TkFixedFont\", state=\"disabled\")\n        self.settings_labels[i].grid(row=grid_row, column=1, padx=(5, 5), sticky='E')\n        # make tags to be used in showing which characters have changed, and which have not\n        self.settings_labels[i].tag_config(\"changed\", foreground=\"Blue\")\n        self.settings_labels[i].tag_config(\"unchanged\", foreground=\"Black\")\n        grid_row += 1\n\n\ndef update_mid_frame(self):\n    \"\"\" update the frame with the sm variables and settings \"\"\"\n    vars = self.emulation_results[self.current_clock][1]\n\n    self.clock_label['text'] = \"clock=\" + str(self.current_clock)\n    self.pc_label['text'] = \"pc=\" + str(vars['pc'])\n    self.delay_label['text'] = \"delay=\" + str(vars['delay'])\n    self.status_label['text'] = \"status=\" + str(vars['status'])\n    self.OSR_shift_counter_label['text'] = \"OSR shift counter=\"+str(vars['OSR_shift_counter'])\n    self.ISR_shift_counter_label['text'] = \"ISR shift counter=\"+str(vars['ISR_shift_counter'])\n\n    self.OSR_label.update(self.current_clock)\n    self.ISR_label.update(self.current_clock)\n    self.X_label.update(self.current_clock)\n    self.Y_label.update(self.current_clock)\n    self.GPIO_label.update(self.current_clock)\n    self.GPIO_ext_label.update(self.current_clock)\n    self.GPIO_pindirs_label.update(self.current_clock)\n    self.IRQ_label.update(self.current_clock)\n    self.in_pins_label.update(self.current_clock)\n    self.out_pins_label.update(self.current_clock)\n    self.out_vals_label.update(self.current_clock)\n    self.set_vals_label.update(self.current_clock)\n    self.set_pins_label.update(self.current_clock)\n    self.sideset_pins_label.update(self.current_clock)\n    self.sideset_vals_label.update(self.current_clock)\n    self.jmp_pin_label.update(self.current_clock)\n\n\n    # update settings\n    settings = self.emulation_results[self.current_clock][2]\n    for i, s in enumerate(self.list_of_settings_to_be_displayed):\n        # save the current value for comparison with the new value\n        old_value = self.settings_labels[i].get(\"1.0\", \"end\").strip()\n        # allow the widget text to be changed, and delete the existing content\n        self.settings_labels[i].configure(state=\"normal\")\n        self.settings_labels[i].delete(\"1.0\", \"end\")\n        # determine the new text for the widget, and insert it\n        self.settings_labels[i].insert(\"end\", str(settings[s]))\n        # disallow changing the text\n        self.settings_labels[i].configure(state=\"disabled\")\n\n        # apply tags to give color to the changed data items\n        if old_value != str(settings[s]):\n            self.settings_labels[i].tag_add(\"changed\", '1.0', '1.'+str(len(str(settings[s])))) \n        else:\n            self.settings_labels[i].tag_add(\"unchanged\", '1.0', '1.'+str(len(str(settings[s]))))\n"
  },
  {
    "path": "state_machine_emulator/interface/_output_frame.py",
    "content": "from tkinter import Frame, Label, Listbox, Text\n\n\ndef build_output_frame(self):\n    \"\"\" build the frame with the output (of c-statements) of the emulation \"\"\"\n    # the output frame\n    self.output_frame = Frame(self.root, width=425, height=725)\n    self.output_frame.grid(row=1, column=3, padx=0, pady=2)\n    self.output_frame.grid_propagate(0)\n\n    # program output label\n    self.program_label = Label(self.output_frame, text=\"Highlight is just produced output from RxFIFO:\")\n    self.program_label.grid(row=2, column=3, padx=(5, 5), sticky='W')\n\n    # program output is in a listbox\n    self.output_listbox = Listbox(self.output_frame, height=26, width=50, justify=\"right\", exportselection=0)\n    self.output_listbox.grid(row=3, column=3, padx=(5, 5), sticky='E')\n\n    # put the c-program into the listbox (in update_output_frame the current line will be highlighted (selected) in the GUI)\n    for line in self.emulation_output_c_program:\n        self.output_listbox.insert(\"end\", line)\n\n    # messages label\n    self.program_label = Label(self.output_frame, text=\"Messages\")\n    self.program_label.grid(row=4, column=3, padx=(5, 5), sticky='W')\n\n    # messages\n    self.messages_text = Text(self.output_frame, height=11, width=50, exportselection=0)\n    self.messages_text.grid(row=5, column=3, padx=(5, 5), sticky='E')\n\n\ndef update_output_frame(self):\n    \"\"\" update the panel with the output (of c-statements) \"\"\"\n    # highlight the just executed output\n    self.output_listbox.selection_clear(0, \"end\")\n    for index in self.emulation_results[self.current_clock][6]:\n        self.output_listbox.selection_set(index)\n        self.output_listbox.see(index)\n\n    # put the messages produced at the current clock here\n    self.messages_text.delete(\"1.0\", \"end\")\n    for line in self.emulation_results[self.current_clock][7]:\n        self.messages_text.insert(\"end\", \"-> \" + line)\n"
  },
  {
    "path": "state_machine_emulator/interface/_right_frame.py",
    "content": "from tkinter import Frame, Label, Listbox\n\n\ndef build_right_frame(self):\n    \"\"\" build the panel with the pio program \"\"\"\n    # on the right side\n    self.right_frame = Frame(self.root, width=305, height=725)\n    # self.right_frame = Frame(self.root, width=345, height=675)\n    self.right_frame.grid(row=1, column=2, padx=0, pady=2)\n    self.right_frame.grid_propagate(0)\n    # self.right_frame.grid_columnconfigure(1, weight=1)\n\n    self.program_label = Label(self.right_frame, text=\"Highlight is just executed pio-code:\")\n    self.program_label.grid(row=1, column=2, padx=(5, 5), sticky='W')\n    # PIO program\n    self.code_listbox = Listbox(self.right_frame, height=38, width=40)\n    for index, p in enumerate(self.pio_program):\n        if index == self.program_definitions['pio_program_wrap']:\n            self.code_listbox.insert(\"end\", p[1] + ' (wrap)')\n        elif index == self.program_definitions['pio_program_wrap_target']:\n            self.code_listbox.insert(\"end\", p[1] + ' (wrap target)')\n        elif index == self.program_definitions['pio_program_origin']:\n            self.code_listbox.insert(\"end\", p[1] + ' (origin)')\n        else:\n            self.code_listbox.insert(\"end\", p[1])\n    self.code_listbox.grid(row=2, column=2, padx=(5, 5), sticky='E')\n\n\ndef update_right_frame(self):\n    \"\"\" update the panel on the right with the pio program \"\"\"\n    self.code_listbox.selection_clear(0, \"end\")\n    self.code_listbox.selection_set(self.emulation_results[self.current_clock][1]['pc'])\n"
  },
  {
    "path": "state_machine_emulator/interface/_toolbar.py",
    "content": "from tkinter import Frame, Button\n\n\"\"\"\n    build the toolbar with associated call back functions and key bindings\n\"\"\"\n\ndef enable_disable_buttons(self):\n    \"\"\" enable or disable buttons and keybindings depending on time step and max time step \"\"\"\n\n    \n    # disable 50 step back button if not possible\n    self.step_50_back_button[\"state\"] = \"disabled\" if self.current_clock < 50 else \"normal\"\n\n    # disable 10 step back button and keybinding if not possible, otherwise enable\n    self.step_10_back_button[\"state\"] = \"disabled\" if self.current_clock < 10 else \"normal\"\n\n    # disable step back button and keybinding if not possible, otherwise enable \n    self.step_back_button[\"state\"] = \"disabled\" if self.current_clock == 0 else \"normal\"\n\n    # disable step button and keybinding if not possible, otherwise enable \n    self.step_button[\"state\"] = \"normal\" if self.current_clock < self.max_clock-1 else \"disabled\"\n\n    # disable 10 step button if not possible\n    self.step_10_button[\"state\"] = \"normal\" if self.current_clock < self.max_clock-10 else \"disabled\"\n\n    # disable 50 step button if not possible\n    self.step_50_button[\"state\"] = \"normal\" if self.current_clock < self.max_clock-51 else \"disabled\"\n\n\ndef reload_callback(self, event):\n    \"\"\" if the 'reload' button is pressed, start from the beginning, reloading all files \"\"\"\n    self.reload_flag = True\n    self.__del__()\n\n\ndef restart_callback(self, event):\n    \"\"\" if the 'restart' button is pressed, start from t=0 \"\"\"\n    self.current_clock = 0\n    self.enable_disable_buttons()\n    self.update_display()\n\n\ndef quit_callback(self, event):\n    \"\"\" if the 'quit' button is pressed, quit the program \"\"\"\n    self.__del__()\n\n\ndef step_50_back_callback(self):\n    \"\"\" take 50 steps back in time (if possible) \"\"\"\n    if self.current_clock >= 50:\n        self.current_clock -= 50\n    else:\n        self.current_clock = 0\n    self.enable_disable_buttons()\n    self.update_display()\n\n\ndef step_10_back_callback(self, event):\n    \"\"\" take 10 steps back in time (if possible) \"\"\"\n    if self.current_clock >= 10:\n        self.current_clock -= 10\n    else:\n        self.current_clock = 0\n    self.enable_disable_buttons()\n    self.update_display()\n\n\ndef step_back_callback(self, event):\n    \"\"\" take one step back in time (if possible) \"\"\"\n    self.current_clock -= 1 if self.current_clock > 0 else 0 \n    self.enable_disable_buttons()\n    self.update_display()\n\n\ndef step_callback(self, event):\n    \"\"\" take one step forward in time (if possible) \"\"\"\n    self.current_clock += 1 if self.current_clock < self.max_clock-1 else 0\n    self.enable_disable_buttons()\n    self.update_display()\n\n\ndef step_10_callback(self, event):\n    \"\"\" take 10 steps forward in time (if possible) \"\"\"\n    if self.current_clock < self.max_clock-10:\n        self.current_clock += 10\n    else:\n        self.current_clock = self.max_clock-1\n    self.enable_disable_buttons()\n    self.update_display()\n\n\ndef step_50_callback(self):\n    \"\"\" take 50 steps forward in time (if possible) \"\"\"\n    if self.current_clock < self.max_clock-50:\n        self.current_clock += 50\n    else:\n        self.current_clock = self.max_clock-1\n    self.enable_disable_buttons()\n    self.update_display()\n\n\ndef build_toolbar(self):\n    \"\"\" build the toolbar \"\"\"\n\n    # build the toolbar\n    self.toolbar = Frame(self.root, borderwidth=2, relief='raised')\n    self.toolbar.grid(row=0, columnspan=4, padx=0, pady=2, sticky=\"EW\")\n\n    # build the buttons in the toolbar\n    self.reload_button = Button(self.toolbar, text=\"reLoad\", command=lambda: self.reload_callback(None))\n    self.reload_button.pack(side=\"left\", fill=\"none\")\n\n    self.restart_button = Button(self.toolbar, text=\"Restart\", command=lambda: self.restart_callback(None))\n    self.restart_button.pack(side=\"left\", fill=\"none\")\n\n    self.step_50_back_button = Button(self.toolbar, text=\"back 50\", command=lambda: self.step_50_back_callback())\n    self.step_50_back_button.pack(side=\"left\", fill=\"none\")\n\n    self.step_10_back_button = Button(self.toolbar, text=\"back 10\\u2193\", command=lambda: self.step_10_back_callback(None))\n    self.step_10_back_button.pack(side=\"left\", fill=\"none\")\n\n    self.step_back_button = Button(self.toolbar, text=\"back\\u2190\", command=lambda: self.step_back_callback(None))\n    self.step_back_button.pack(side=\"left\", fill=\"none\")\n\n    self.step_button = Button(self.toolbar, text=\"step\\u2192\", command=lambda: self.step_callback(None))\n    self.step_button.pack(side=\"left\", fill=\"none\")\n\n    self.step_10_button = Button(self.toolbar, text=\"step 10\\u2191\", command=lambda: self.step_10_callback(None))\n    self.step_10_button.pack(side=\"left\", fill=\"none\")\n\n    self.step_50_button = Button(self.toolbar, text=\"step 50\", command=lambda: self.step_50_callback())\n    self.step_50_button.pack(side=\"left\", fill=\"none\")\n\n    self.quit_button = Button(self.toolbar, text=\"Quit\", command=lambda: self.quit_callback(None))\n    self.quit_button.pack(side=\"right\", fill=\"none\")\n\n    # prepare key binding and unbinding of back button and step button (in 'enable_disable_buttons()')\n    self.root.bind('<Down>', self.step_10_back_callback)\n    self.root.bind('<Left>', self.step_back_callback)\n    self.root.bind('<Right>', self.step_callback)\n    self.root.bind('<Up>', self.step_10_callback)\n\n    # bind key 'q' and 'Q' to quit\n    self.root.bind('q', self.quit_callback)\n    self.root.bind('Q', self.quit_callback)\n    # bind key 'r' and 'R' to restart\n    self.root.bind('r', self.restart_callback)\n    self.root.bind('R', self.restart_callback)\n    # bind key 'l' and 'L' to reload\n    self.root.bind('l', self.reload_callback)\n    self.root.bind('L', self.reload_callback)\n\n    # enable or disable buttons depending on the current time step and max time step\n    self.enable_disable_buttons()"
  },
  {
    "path": "state_machine_emulator/interface/_tooltips.py",
    "content": "# thanks to crxguy52 and Stevoisiak:\n# https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter\nfrom tkinter import Toplevel, Label\n\nclass CreateToolTip(object):\n    \"\"\"\n    create a tooltip for a given widget\n    \"\"\"\n    def __init__(self, widget, text='widget info'):\n        self.waittime = 500     #milliseconds\n        self.wraplength = 180   #pixels\n        self.widget = widget\n        self.text = text\n        self.widget.bind(\"<Enter>\", self.enter)\n        self.widget.bind(\"<Leave>\", self.leave)\n        self.widget.bind(\"<ButtonPress>\", self.leave)\n        self.id = None\n        self.tw = None\n\n    def enter(self, event=None):\n        self.schedule()\n\n    def leave(self, event=None):\n        self.unschedule()\n        self.hidetip()\n\n    def schedule(self):\n        self.unschedule()\n        self.id = self.widget.after(self.waittime, self.showtip)\n\n    def unschedule(self):\n        id = self.id\n        self.id = None\n        if id:\n            self.widget.after_cancel(id)\n\n    def showtip(self, event=None):\n        x = y = 0\n        x, y, cx, cy = self.widget.bbox(\"insert\")\n        x += self.widget.winfo_rootx() + 150\n        y += self.widget.winfo_rooty() + 20\n        # creates a toplevel window\n        self.tw = Toplevel(self.widget)\n        # Leaves only the label and removes the app window\n        self.tw.wm_overrideredirect(True)\n        self.tw.wm_geometry(\"+%d+%d\" % (x, y))\n        label = Label(self.tw, text=self.text, justify='left',\n                       background=\"#ffffff\", relief='solid', borderwidth=1,\n                       wraplength = self.wraplength)\n        label.pack(ipadx=1)\n\n    def hidetip(self):\n        tw = self.tw\n        self.tw= None\n        if tw:\n            tw.destroy()"
  },
  {
    "path": "state_machine_emulator/main.py",
    "content": "\"\"\"\nTODO:\n- check the 'status' with 'set_N' and 'status_sel' with the actual hardware!\n    Wait! not just the status, but many, many things should be tested with the pico hardware\n- \"FIFO IRQs\" Figure 38. in rp2040-datasheet.pdf\n- start without c-program or pin-program\n- try except around emulation\n- make the way statements in c_program are made exactly the same as how they appear in the GUI\n- use the pio assembler to read raw programs and compile them (i.e. read prog.pio instead of prog.pio.h)\n\"\"\"\n\nfrom sys import argv, exc_info\nfrom os import getcwd, path, chdir\nfrom glob import glob\n\nfrom interface import Emulator_Interface\nfrom emulation import emulation\nfrom config import EMULATION_STEPS, SAVE_TEST_DATA\nfrom state_machine import state_machine\n\n\"\"\"\nThis code emulates a state machine of a RP4020\nIt provides a GUI to step through the emulation results.\n\"\"\"\n\ndef process_file_pio_h(filename, c_program):\n    \"\"\" read and parse a pioasm generated header file \"\"\"\n    pio_program = list()\n    pio_program_length = None\n    pio_program_origin = -1 # note: not actually used\n    pio_program_wrap_target = 0\n    pio_program_wrap = None\n\n    try:\n        with open(filename, 'r') as pio_file:\n            line = pio_file.readline()\n            while line:\n                if line[0] == '/' and line[1] == '/':\n                    pass\n                elif \".length\" in line:\n                    d = line.strip().split('=')\n                    pio_program_length = int(d[1].replace(',', ''))\n                elif \".origin\" in line: # note: origin is not actually used\n                    d = line.strip().split('=')\n                    pio_program_origin = int(d[1].replace(',', ''))\n                elif \"#define\" in line:\n                    if \"wrap_target\" in line:\n                        d = line.strip().split(' ')\n                        pio_program_wrap_target = int(d[2])\n                    elif \"wrap\" in line:\n                        d = line.strip().split(' ')\n                        pio_program_wrap = int(d[2])\n                elif \"static const uint16_t\" in line:\n                    line = pio_file.readline()\n                    while '};' not in line:\n                        d = line.strip().split(', //')\n                        if len(d) == 2:\n                            pio_program.append(d)\n                        line = pio_file.readline()\n                elif \"static inline pio_sm_config\" in line:\n                    # add to the c-program at t=0\n                    line2 = pio_file.readline()\n                    while '}' not in line2:\n                        if 'sm_config_set_sideset' in line2:\n                            parts = line2.split(',')\n                            a1 = int(parts[1].strip())\n                            c_program.append([0, 'sideset_count', a1])\n                            a2 = parts[2].strip().lower() == 'true'\n                            c_program.append([0, 'sideset_opt', a2])\n                            a3 = parts[3].split(')')[0].strip().lower() == 'true'\n                            c_program.append([0, 'sideset_pindirs', a3])\n                        line2 = pio_file.readline()\n                line = pio_file.readline()\n        if len(pio_program) != pio_program_length:\n            print(\"Warning: length specification in pio file doesn't match actual length, continuing anyway\")\n        if len(pio_program) > 32:\n            print(\"Warning: program too long, continuing anyway\")\n    except IOError as e:\n        print(\"I/O Error reading pio program file:\", e.errno, e.strerror)\n    except:\n        print(\"Error reading pio program file:\", exc_info()[0])\n    return pio_program, pio_program_length, pio_program_origin, pio_program_wrap_target, pio_program_wrap\n\n\ndef process_file_pin_program(filename, pin_program):\n    \"\"\" read the pin program file and parse it \"\"\"\n    try:\n        allowed_parts1 = ['GPIO'+str(i) for i in range(32)]\n        allowed_parts1.append('all')\n        with open(filename, 'r') as pin_program_file:\n            for line in pin_program_file:\n                # remove all # characters and all text after it (i.e. comments)\n                line = line.split(\"#\", 1)[0]\n                # check if the line still has some content\n                if line.strip():\n                    parts = line.split(',')\n                    parts[1] = parts[1].strip()\n                    if parts[1] in allowed_parts1:\n                        parts[0] = int(parts[0])   # time at which the command should be run\n                        parts[2] = int(parts[2])   # argument of the command\n                        pin_program.append(parts)\n                    else:\n                        print(\"Warning: Unknown command in pin_program: \", parts, 'continuing anyway')\n    except IOError as e:\n        print(\"I/O Error reading pin program file:\", e.errno, e.strerror)\n    except:\n        print(\"Error reading pin program file:\", exc_info()[0])\n\n\ndef process_file_c_program(filename, c_program):\n    \"\"\" read the c program file and parse it \"\"\"\n    try:\n        with open(filename, 'r') as c_program_file:\n            for line in c_program_file:\n                # remove all # characters and all text after it (i.e. comments)\n                line = line.split(\"#\", 1)[0]\n                # check if the line still has some content\n                if line.strip():\n                    # a \"c-instruction\" has two or three parts:\n                    # timestamp, command, and possibly an argument of the command\n                    # the timestamp and argument must be integers/bool, the command is a (stripped) string\n                    parts = line.strip().split(',')\n                    parts[1] = parts[1].strip()\n                    # check if the command is valid \n                    if parts[1].strip() in ['put', 'get', 'set_base', 'set_count', 'in_base', 'jmp_pin', 'sideset_base', 'out_base', 'out_count', 'out_shift_right', 'out_shift_autopull', 'pull_threshold', 'in_shift_right', 'in_shift_autopush', 'push_threshold', 'get_pc', 'set_pc', 'irq', 'set_N', 'status_sel', 'dir_out', 'dir_in', 'dir_non']:\n                        parts[0] = int(parts[0])\n                        parts[1] = parts[1]\n                        if len(parts) == 3:\n                            parts[2] = parts[2].strip()\n                            # convert strings \"True\" and \"False\" to boolean, otherwise it is an int\n                            if parts[2] in [\"True\", \"true\", \"Yes\", \"yes\"]:\n                                parts[2] = True\n                            elif parts[2] in [\"False\", \"false\", \"No\", \"no\"]:\n                                parts[2] = False\n                            else:\n                                parts[2] = int(parts[2])\n                        c_program.append(parts)\n                    else:\n                        print(\"Warning: Unknown command in c_program: \", parts, \"continuing anyway\")\n    except IOError as e:\n        print(\"I/O Error reading c program file:\", e.errno, e.strerror)\n    except:\n        print(\"Error reading c program file:\", exc_info()[0])\n\ndef print_usage():\n    \"\"\" print the usage of this program \"\"\"\n    print(\"Usage:\")\n    print(\"python3\", argv[0], \"file.pio.h pin_program c_program\")\n    print(\"or:\")\n    print(\"python3\", argv[0], \"directory_of_files\")\n    exit()\n\n\nif __name__ == \"__main__\":\n\n    # process arguments\n    if len(argv) == 4:\n        # read the three arguments: .pio.h pin-program c-program\n        pio_h_filename = argv[1]\n        pin_program_filename = argv[2]\n        c_program_filename = argv[3]\n    elif len(argv) == 2:\n        # read one argument: the directory with the files .pio.h pin-program c-program\n        dir_of_files = argv[1]\n        # if the first character isn't a '/', add the full path\n        if dir_of_files[0] != '/':\n            dirname=getcwd()\n            dir_of_files = path.join(dirname, dir_of_files)\n        # if the last character isn't a '/', add it\n        if dir_of_files[-1] != '/':\n            dir_of_files += '/'\n        chdir(dir_of_files)\n        pio_file = glob(\"*.pio.h\")\n        pin_file = glob(\"pin_program\")\n        c_file = glob(\"c_program\")\n        if (len(pio_file)==1 and len(pin_file)==1 and len(c_file)==1):\n            pio_h_filename = dir_of_files + pio_file[0]\n            pin_program_filename = dir_of_files + pin_file[0]\n            c_program_filename = dir_of_files + c_file[0]\n        else:\n            print_usage()\n    else:\n        print_usage()\n        \n    # flag to indicate that files need to be (re)loaded. This is used when the user pushes the reload button in the GUI\n    load_files = True\n    while load_files:\n        load_files = False\n\n        # the pio program (with associated data) is a dict\n        program_definitions = dict()\n        # the c_program and pin_program are lists\n        c_program = list()\n        pin_program = list()\n\n        # process the pio.h file (which may already contribute to the c_program)\n        pio_program, pio_program_length, pio_program_origin, pio_program_wrap_target, pio_program_wrap = process_file_pio_h(pio_h_filename, c_program)\n        program_definitions['pio_program'] = pio_program\n        program_definitions['pio_program_length'] = pio_program_length\n        program_definitions['pio_program_origin'] = pio_program_origin  # note: not used\n        program_definitions['pio_program_wrap_target'] = pio_program_wrap_target\n        program_definitions['pio_program_wrap'] = pio_program_wrap\n        # process the c_program\n        process_file_c_program(c_program_filename, c_program)\n        # process the pin_program\n        process_file_pin_program(pin_program_filename, pin_program)\n\n        # make the RP2040 emulation (and it will make the PIO and sm)\n        my_state_machine = state_machine(program_definitions)\n        my_emulation = emulation(my_state_machine, pin_program, c_program)\n\n        # do the emulation\n        my_emulation.emulate(EMULATION_STEPS)\n        \n        if SAVE_TEST_DATA:\n            print(\"program_definitions:\")\n            print(program_definitions)\n            print(\"pin_program:\")\n            print(pin_program)\n            print(\"c_program:\")\n            print(c_program)\n            print(\"my_emulation.output:\")\n            print(my_emulation.output)\n            print(\"my_emulation.emulation_output_c_program:\")\n            print(my_emulation.emulation_output_c_program)\n        else:\n            # show the interface\n            my_Emulator_Interface = Emulator_Interface(program_definitions, pin_program, c_program, my_emulation.output, my_emulation.emulation_output_c_program)\n\n            # check if a reload was requested\n            load_files = my_Emulator_Interface.get_reload_flag()\n"
  },
  {
    "path": "state_machine_emulator/state_machine/__init__.py",
    "content": "from config import STATEMACHINE_NUMBER\n\nclass state_machine:\n    \"\"\" this class emulates a state machine (sm) \"\"\"\n\n    from ._time_step import time_step\n    from ._push_pull import push_to_RxFIFO, pull_from_TxFIFO\n    from ._do_sideset import do_sideset\n    from ._set_all_GPIO import set_all_GPIO\n    from ._execute_instructions import execute_instruction, execute_jmp, execute_wait, execute_in, execute_out, execute_push, execute_pull, execute_mov, execute_irq, execute_set\n\n\n    def __init__(self, program_definitions):\n        \"\"\" make the variables (such as x and y registers) and settings\n            and make some class variables needed for correct execution\n        \"\"\"\n        # Note: the Pico has two PIOs with 4 state machines each, but this emulator only has one state machine!\n        # You can still set the state_machine_number (in config.py) because it is used in some irq operations\n\n        # the sm number (used in irq calculations)\n        self.sm_number = STATEMACHINE_NUMBER\n        # the pio program and important parameters\n        self.program = program_definitions['pio_program']\n        self.wrap_target = program_definitions['pio_program_wrap_target']\n        self.wrap = program_definitions['pio_program_wrap']\n        # RP2040 clock\n        self.clock = 0\n        # define the irq\n        self.sm_irq = [0 for _ in range(8)]\n\n        # data related to the GPIO (the GPIO themselves, the pindir, external driven GPIO, out, set and sideset)\n        self.GPIO_data = {}\n        # the Pico has 32 GPIO\n        self.GPIO_data[\"GPIO\"] = [-1 for _ in range(32)]\n        # pindir = 1 means Input (default); the '1' looks like an 'I' from 'Input'  \n        # pindir = 0 means output; the '0' looks like an 'O' from 'Output'\n        self.GPIO_data[\"GPIO_pindirs\"] = [1 for _ in range(32)]\n        # externally driven pins\n        self.GPIO_data[\"GPIO_external\"] = [-1 for _ in range(32)]\n        # pins driven by 'out'\n        self.GPIO_data[\"GPIO_out\"] = [-1 for _ in range(32)]\n        # pins driven by 'set'\n        self.GPIO_data[\"GPIO_set\"] = [-1 for _ in range(32)]\n        # pins driven by 'sideset'\n        self.GPIO_data[\"GPIO_sideset\"] = [-1 for _ in range(32)]\n        \n        # the variables used by the sm\n        self.vars = {}\n        self.vars[\"RxFIFO\"] = [0 for _ in range(4)]\n        self.vars[\"RxFIFO_count\"] = 0  \n        self.vars[\"TxFIFO\"] = [0 for _ in range(4)]\n        self.vars[\"TxFIFO_count\"] = 0 \n        self.vars[\"x\"] = 0\n        self.vars[\"y\"] = 0\n        self.vars[\"ISR\"] = 0\n        self.vars[\"ISR_shift_counter\"] = 0\n        self.vars[\"OSR\"] = 0\n        self.vars[\"OSR_shift_counter\"] = 32\n        self.vars[\"pc\"] = -1 # before executing any instruction a '+= 1' is done in time_step()\n        self.vars[\"delay\"] = 0\n        self.vars[\"status\"] = -1\n\n        # settings that control how sm functions are executed\n        self.settings = {}\n        self.settings[\"in_shift_right\"] = True      # right\n        self.settings[\"in_shift_autopush\"] = False\n        self.settings[\"push_threshold\"] = 32        # false\n        self.settings[\"out_shift_right\"] = False    # left (True = right)\n        self.settings[\"out_shift_autopull\"] = False\n        self.settings[\"pull_threshold\"] = 32        # false\n        self.settings[\"in_base\"] = -1       # indicate it is not set\n        self.settings[\"jmp_pin\"] = -1       # indicate it is not set\n        self.settings[\"set_base\"] = -1      # indicate it is not set\n        self.settings[\"set_count\"] = 0      # no pins assigned\n        self.settings[\"out_base\"] = -1      # indicate it is not set\n        self.settings[\"out_count\"] = 0      # no pins assigned\n        self.settings[\"sideset_base\"] = -1  # indicate it is not set\n        self.settings[\"sideset_count\"] = 0  # no pins assigned\n        self.settings[\"sideset_opt\"] = False\n        self.settings[\"sideset_pindirs\"] = False\n        self.settings[\"FIFO_level_N\"] = 0\n        self.settings[\"status_sel\"] = 0 # 0 => status ~0x0 if TxFIFO_count < FIFO_LEVEL_N\n                                        # 1 => status ~0x0 if RxFIFO_count < FIFO_LEVEL_N\n\n        # for e.g. jmp the pc should not be updated after an execution step since it is set by the jmp\n        # also for e.g. wait statements the pc should remain unchanged\n        self.skip_increase_pc = False # clear flag\n        # for some statements the delay should be postponed to after the instruction (e.g. wait) has finished\n        self.delay_delay = False\n        # the jmp statement changes the vars[\"pc\"] immediately when issued. But I want to keep the 'old' pc until it is time to change the pc in 'time_step()'\n        self.jmp_to = -1 # not set\n        # flags that indicate whether the sm is waiting because either pull or push is stalling (TxFIFO empty / RxFIFO full)\n        self.pull_is_stalling = False\n        self.push_is_stalling = False\n        # indicates if the irq instruction is already waiting for clearing of the irq\n        self.irq_is_waiting = False"
  },
  {
    "path": "state_machine_emulator/state_machine/_do_sideset.py",
    "content": "def do_sideset(self, instruction_delay_sideset):\n    \"\"\" handle sideset \"\"\"\n    # TODO: is 3.5.1. fully covered? Especially the 4 points at the end of the section.\n    # determine and execute the (optional) sideset\n    # if sideset is optional the MSB is set when sideset should be executed:\n    if self.settings[\"sideset_opt\"]:\n        # check if the MSB is set\n        if instruction_delay_sideset & 0x10:\n            # do the side step for sideset_count bits after the MSB\n            # note: sideset_count includes the optional bit!\n            for i in range(self.settings[\"sideset_count\"]-1):\n                test_ss_bit = 4-i-1\n                value = 1 if instruction_delay_sideset & (1 << test_ss_bit) else 0\n                # do the side step for sideset_base + i\n                if self.settings[\"sideset_pindirs\"]:\n                    self.GPIO_data[\"GPIO_pindirs\"][(self.settings[\"sideset_base\"] + i) % 32] = value\n                else:\n                    self.GPIO_data[\"GPIO_sideset\"][(self.settings[\"sideset_base\"] + i) % 32] = value\n    else:  # sideset is mandatory\n        for i in range(self.settings[\"sideset_count\"]):\n            test_ss_bit = 5-i-1\n            # if the bit is set, the GPIO (or pindir) has to be set; if not: clear the GPIO/pindir\n            value = 1 if instruction_delay_sideset & (1 << test_ss_bit) else 0\n            # do the side step for sideset_base + i\n            if self.settings[\"sideset_pindirs\"]:\n                self.GPIO_data[\"GPIO_pindirs\"][(self.settings[\"sideset_base\"] + i) % 32] = value\n            else:\n                self.GPIO_data[\"GPIO_sideset\"][(self.settings[\"sideset_base\"] + i) % 32] = value\n    self.set_all_GPIO() # TODO: is this correct? the datasheet says that the sideset is done before the instruction."
  },
  {
    "path": "state_machine_emulator/state_machine/_execute_instructions.py",
    "content": "def execute_instruction(self, instruction):\n    \"\"\" Execute the PIO instruction \"\"\"\n    # get the three bits that encode the instruction type\n    instruction_type = (instruction & 0xE000) >> 13\n    # get the five delay/side-set bits\n    instruction_delay_sideset = (instruction & 0x1F00) >> 8\n\n    # determine the (optional) delay\n    # the bits for delay is 5 minus the number of sideset pins\n    # NOTE: the number of sideset pins specified in the pio.h file includes the opt-bit\n    bits_for_delay = 5 - self.settings[\"sideset_count\"]\n    # delay is the bits_for_delay LSB of instruction_del_ss\n    self.vars[\"delay\"] = instruction_delay_sideset & ((1 << bits_for_delay) - 1)\n    self.do_sideset(instruction_delay_sideset)\n\n    is_pull = 1 if (instruction & (1 << 7)) > 0 else 0\n    # determine which function to execute based on the instruction_type\n    if instruction_type == 0:                   # its a 'jmp'\n        self.execute_jmp(instruction)\n    elif instruction_type == 1:                 # its a 'wait'\n        self.execute_wait(instruction)\n    elif instruction_type == 2:                 # its a 'in'\n        self.execute_in(instruction)\n    elif instruction_type == 3:                 # its a 'out'\n        self.execute_out(instruction)\n    elif instruction_type == 4 and is_pull:     # its a 'pull'\n        self.execute_pull(instruction)\n    elif instruction_type == 4 and not is_pull:  # its a 'push'\n        self.execute_push(instruction)\n    elif instruction_type == 5:                 # its a 'mov'\n        self.execute_mov(instruction)\n    elif instruction_type == 6:                 # its a 'irq'\n        self.execute_irq(instruction)\n    elif instruction_type == 7:                 # its a 'set'\n        self.execute_set(instruction)\n    else:                                       # its an error!\n        self.sm_warning_messages.append(\"Warning: unknown instruction, continuing\\n\")\n\n    # when not pull or mov, do autopull (if enabled) (datasheet 3.5.4):\n    if self.settings[\"out_shift_autopull\"] and not ((instruction_type == 4 and is_pull) or (instruction_type == 5)):\n        if self.vars[\"OSR_shift_counter\"] >= self.settings[\"pull_threshold\"]:\n            if self.vars[\"TxFIFO_count\"] > 0:\n                self.pull_from_TxFIFO()\n                self.pull_is_stalling = False\n\n\ndef execute_jmp(self, instruction):\n    \"\"\" execute a jmp instruction \"\"\"\n    # get instruction parameters\n    jmp_condition = (instruction & 0x00E0) >> 5\n    addr = instruction & 0x001F\n\n    do_jump = False\n    if jmp_condition == 0:      # always\n        do_jump = True\n    elif jmp_condition == 1:    # !x\n        if self.vars[\"x\"] == 0:\n            do_jump = True\n    elif jmp_condition == 2:    # x--\n        if self.vars[\"x\"] != 0:\n            do_jump = True\n        # x--\n        self.vars[\"x\"] = (self.vars[\"x\"] - 1) & 0xffffffff\n    elif jmp_condition == 3:    # !y\n        if self.vars[\"y\"] == 0:\n            do_jump = True\n    elif jmp_condition == 4:    # y--\n        if self.vars[\"y\"] != 0:\n            do_jump = True\n        # y--\n        self.vars[\"y\"] = (self.vars[\"y\"] - 1) & 0xffffffff\n    elif jmp_condition == 5:    # x!=y\n        if self.vars[\"y\"] != self.vars[\"x\"]:\n            do_jump = True\n    elif jmp_condition == 6:    # pin\n        if self.settings[\"jmp_pin\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'jmp_pin' isn't set before use in JMP instruction, continuing\\n\")\n        if self.GPIO_data[\"GPIO\"][self.settings[\"jmp_pin\"]] == 1:\n            do_jump = True\n    elif jmp_condition == 7:    # !OSRE\n        if self.vars[\"OSR_shift_counter\"] < 32:\n            do_jump = True\n    if do_jump:\n        # save the address to jump to for when the pc is set for the new instruction\n        self.jmp_to = addr\n        # the address is given by self.jmp_to, so, no increase of the pc\n        self.skip_increase_pc = True\n\n\ndef execute_wait(self, instruction):\n    \"\"\" execute a wait instruction \"\"\"\n    # get instruction parameters\n    polarity = 1 if (instruction & (1 << 7)) > 0 else 0\n    source = (instruction & 0x0060) >> 5\n    index = instruction & 0x1F\n    \n    is_not_met = False\n    if source == 0:             # GPIO\n        if self.GPIO_data[\"GPIO\"][index] != polarity:\n            is_not_met = True\n    elif source == 1:           # pin\n        if self.settings[\"in_base\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'in_base' isn't set before use in WAIT instruction, continuing\\n\")\n        if self.GPIO_data[\"GPIO\"][(self.settings[\"in_base\"]+index) % 32] != polarity:\n            is_not_met = True\n    elif source == 2:           # IRQ\n        MSB = 1 if (instruction & (1 << 4)) > 0 else 0\n        if MSB:\n            irq = (instruction & 0x07 + self.sm_number) % 4\n        else:\n            irq = instruction & 0x07\n        if self.sm_irq[irq] != polarity:\n            is_not_met = True\n    else:\n        self.sm_warning_messages.append(\"Warning: WAIT has unknown source, continuing\\n\")\n\n    if is_not_met:\n        # condition has not been met, keep waiting\n        self.skip_increase_pc = True\n        # wait with the delay until condition has been met\n        self.delay_delay = True\n\n\ndef execute_in(self, instruction):\n    \"\"\" execute an in instruction \"\"\"\n\n    # rp2040-datasheet.pdf 3.2.4 Stalling: An IN instruction when autopush is enabled, ISR reaches its shift threshold, and the RxFIFO is full\n    if self.push_is_stalling:\n        self.sm_warning_messages.append(\"Warning: push is stalling in IN\\n\")\n        return\n\n    # get instruction parameters\n    source = (instruction & 0x00E0) >> 5\n    bit_count = instruction & 0x001F\n\n    if bit_count == 0:\n        bit_count = 32\n    value = 0\n    mask = (1 << bit_count) - 1\n\n    # get data from the source\n    if source == 0:     # PINS\n        if self.settings[\"in_base\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'in_base' isn't set before use in IN instruction, continuing\\n\")\n        for pin in range(bit_count):\n            value |= (self.GPIO_data[\"GPIO\"][(self.settings[\"in_base\"] + pin) % 32] << pin)\n    elif source == 1:   # X\n        value = self.vars[\"x\"] & mask\n    elif source == 2:   # Y\n        value = self.vars[\"y\"] & mask\n    elif source == 3:   # NULL\n        value = 0\n    elif source == 4:   # reserved\n        self.sm_warning_messages.append(\"Warning: IN has unknown source, continuing\\n\")\n        return\n    elif source == 5:   # reserved\n        self.sm_warning_messages.append(\"Warning: IN has unknown source, continuing\\n\")\n        return\n    elif source == 6:   # ISR\n        value = self.vars[\"ISR\"] & mask\n    elif source == 7:   # OSR\n        value = self.vars[\"OSR\"] & mask\n    else:               # Error\n        self.sm_warning_messages.append(\"Warning: IN has unknown source, continuing\\n\")\n        return\n\n    # shift data into the ISR\n    if self.settings[\"in_shift_right\"]:  # shift right\n        self.vars[\"ISR\"] >>= bit_count\n        self.vars[\"ISR\"] |= value << (32-bit_count)\n    else:  # shift left\n        self.vars[\"ISR\"] <<= bit_count\n        self.vars[\"ISR\"] |= value\n    # make sure it the ISR stays 32 bit\n    self.vars[\"ISR\"] &= 0xFFFFFFFF\n    # adjust the shift counter\n    self.vars[\"ISR_shift_counter\"] += bit_count\n    if (self.vars[\"ISR_shift_counter\"]) > 32:\n        self.vars[\"ISR_shift_counter\"] = 32\n\n    # (if enabled) autopush or stall\n    if self.settings[\"in_shift_autopush\"] and (self.vars[\"ISR_shift_counter\"] >= self.settings[\"push_threshold\"]):\n        if self.vars[\"RxFIFO_count\"] < 4:\n            self.push_to_RxFIFO()\n            self.push_is_stalling = False\n        else:\n            # block: do not go to next instruction\n            self.skip_increase_pc = True\n            self.delay_delay = True\n            self.push_is_stalling = True\n\ndef execute_out(self, instruction):\n    \"\"\" execute an out instruction \"\"\"\n\n    # do autopull (datasheet 3.5.4):\n    if self.settings[\"out_shift_autopull\"]:\n        if self.vars[\"OSR_shift_counter\"] >= self.settings[\"pull_threshold\"]:\n            if self.vars[\"TxFIFO_count\"] > 0:\n                self.pull_from_TxFIFO()\n            # stall\n            self.skip_increase_pc = True\n            self.delay_delay = True\n            self.pull_is_stalling = True\n            self.sm_warning_messages.append(\"Warning: pull is stalling in OUT\\n\")\n            return\n    \n    # get instruction parameters\n    destination = (instruction & 0x00E0) >> 5\n    bit_count = instruction & 0x001F\n\n    if bit_count == 0:\n        bit_count = 32\n\n    # shift to the right\n    if self.settings[\"out_shift_right\"]:\n        # take the bit_count LSB\n        mask = (1 << bit_count)-1\n        value = self.vars[\"OSR\"] & mask\n        # shift the OSR bit_count to the right\n        self.vars[\"OSR\"] >>= bit_count\n    else:   # shift to the left\n        # take the bit_count MSB by making a mask and shifting it left\n        mask = (1 << bit_count)-1\n        # shift them (32-bit_count) to the left\n        mask <<= (32-bit_count)\n        # get the bits from the OSR\n        value = self.vars[\"OSR\"] & mask\n        # shift value back\n        value >>= (32-bit_count)\n        # shift the OSR bit_count to the left\n        self.vars[\"OSR\"] <<= bit_count\n    # make sure it the OSR stays 32 bit\n    self.vars[\"OSR\"] &= 0xFFFFFFFF\n    # adjust the shift counter\n    self.vars[\"OSR_shift_counter\"] += bit_count\n\n    # put the result in the destination\n    if destination == 0:     # PINS\n        if self.settings[\"out_base\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'out_base' isn't set before use in OUT instruction, continuing\\n\")\n        for pin in range(bit_count):\n            self.GPIO_data[\"GPIO_out\"][(self.settings[\"out_base\"] + pin) % 32] = 1 if value & (1 << pin) else 0\n    elif destination == 1:   # X\n        self.vars[\"x\"] = value\n    elif destination == 2:   # Y\n        self.vars[\"y\"] = value\n    elif destination == 3:   # NULL\n        pass\n    elif destination == 4:   # PINDIRS\n        if self.settings[\"out_base\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'out_base' isn't set before use in OUT instruction, continuing\\n\")\n        for pin in range(bit_count):\n            self.GPIO_data[\"GPIO_pindirs\"][(self.settings[\"out_base\"] + pin) % 32] = 1 if value & (1 << pin) else 0\n    elif destination == 5:   # PC\n        # save the address to jump to for when the pc is set for the new instruction\n        self.jmp_to = value & 0x1F\n        # the address is given, so, no increase of one for the pc\n        self.skip_increase_pc = True\n    elif destination == 6:   # ISR\n        self.vars[\"ISR\"] = value\n        self.vars[\"ISR_shift_counter\"] += bit_count\n    elif destination == 7:   # EXEC\n        # TODO?\n        self.sm_warning_messages.append(\"Warning: OUT EXEC functionality isn't implemented, continuing\\n\")\n    else:                   # Error\n        self.sm_warning_messages.append(\"Warning: OUT has unknown destination, continuing\\n\")\n        return\n\n\n\n\n\n\ndef execute_push(self, instruction):\n    \"\"\" execute a push instruction \"\"\"\n    # get instruction parameters\n    ifF = 1 if (instruction & (1 << 6)) > 0 else 0\n    Blk = 1 if (instruction & (1 << 5)) > 0 else 0\n\n    # check if there is space in the FIFO\n    if self.vars[\"RxFIFO_count\"] < 4:\n        if ifF:\n            # only push if counter >= the threshold\n            if (self.vars[\"ISR_shift_counter\"] >= self.settings[\"push_threshold\"]):\n                self.push_to_RxFIFO()\n        else:\n            # push independent of counter and threshold\n            self.push_to_RxFIFO()\n    else:\n        # there's no space to push: block or continue?\n        if Blk:\n            # blocking: do not go to next instruction\n            self.skip_increase_pc = True\n            self.delay_delay = True\n            self.push_is_stalling = True\n        else:\n            # continue, but clear ISR\n            self.push_is_stalling = False\n            self.vars[\"ISR\"] = 0\n\n\ndef execute_pull(self, instruction):\n    \"\"\" execute a pull instruction \"\"\"\n    # get instruction parameters\n    ifE = 1 if (instruction & (1 << 6)) > 0 else 0\n    Blk = 1 if (instruction & (1 << 5)) > 0 else 0\n\n    if self.vars[\"TxFIFO_count\"] != 0:\n        if ifE:\n            # only pull if counter is larger than threshold\n            if (self.vars[\"OSR_shift_counter\"] >= self.settings[\"pull_threshold\"]):\n                self.pull_from_TxFIFO()\n        else:\n            # pull independent of counter and threshold\n            self.pull_from_TxFIFO()\n    else:\n        # there's no data to pull: block or continue\n        if Blk:\n            # blocking: do not go to next instruction\n            self.skip_increase_pc = True\n            self.delay_delay = True\n            self.pull_is_stalling = True\n        else:\n            # \"A non-blocking PULL on an empty FIFO has\n            # the same effect as MOV OSR, X\"\n            self.vars[\"OSR\"] = self.vars[\"x\"]\n            self.pull_is_stalling = False\n            self.sm_warning_messages.append(\"Note: a non-blocking PULL on an empty FIFO has the same effect as 'MOV OSR, X', continuing\\n\")\n\n\n\ndef execute_mov(self, instruction):\n    \"\"\" execute a mov instruction \"\"\"\n    # get instruction parameters\n    destination = (instruction & 0x00E0) >> 5\n    operation = (instruction & 0x0018) >> 3\n    source = (instruction & 0x0007)\n\n    # the value to be moved\n    value = -1\n    # get the source (i.e. set 'value')\n    if source == 0:     # PINS\n        if self.settings[\"in_base\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'in_base' isn't set before use in MOV instruction, continuing\\n\")\n        value = 0\n        for pin in range(32):\n            if self.GPIO_data[\"GPIO\"][(self.settings[\"in_base\"] + pin) % 32] == -1:\n                self.sm_warning_messages.append(str(\"Warning: a pin (\"+str((self.settings[\"in_base\"] + pin) % 32)+\") with undefined state is read, 0 is used, continuing\\n\"))\n                # not necessary to 'or' value with a 0\n            else:\n                value |= (self.GPIO_data[\"GPIO\"][(self.settings[\"in_base\"] + pin) % 32] << pin)\n    elif source == 1:   # X\n        value = self.vars[\"x\"]\n    elif source == 2:   # Y\n        value = self.vars[\"y\"]\n    elif source == 3:   # NULL\n        value = 0\n    elif source == 4:   # reserved\n        self.sm_warning_messages.append(\"Warning: MOV has unknown source, continuing\\n\")\n        return\n    elif source == 5:   # status \n        value = self.vars[\"status\"]\n    elif source == 6:   # ISR\n        value = self.vars[\"ISR\"]\n    elif source == 7:   # OSR\n        value = self.vars[\"OSR\"]\n    else:               # Error\n        self.sm_warning_messages.append(\"Warning: MOV has unknown source, continuing\\n\")\n\n    # apply the operation on value\n    if operation == 1:      # invert (bitwise complement)\n        value = 0xFFFFFFFF - value\n    elif operation == 2:    # bit-reversed\n        reversed_value = 0\n        for i in range(32):\n            reversed_value <<= 1\n            reversed_value |= value & 1\n            value >>= 1\n        value = reversed_value\n\n    # place value in destination\n    # get the source (i.e. set 'value')\n    # PINS TODO: check if it is correct that only out_count bits are output (not 32)\n    if destination == 0:\n        if self.settings[\"out_base\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'out_base' isn't set before use in MOV instruction, continuing\\n\")\n        if self.settings[\"out_count\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'out_count' isn't set before use in MOV instruction, continuing\\n\")\n        for pin in range(self.settings[\"out_count\"]):\n            self.GPIO_data[\"GPIO_out\"][(self.settings[\"out_base\"] + pin) % 32] = 1 if value & (1 << pin) else 0\n            # self.set_GPIO('out', (self.settings[\"out_base\"] + pin) % 32, value & (1 << pin))\n    elif destination == 1:   # X\n        self.vars[\"x\"] = value\n    elif destination == 2:   # Y\n        self.vars[\"y\"] = value\n    elif destination == 3:   # reserved\n        self.sm_warning_messages.append(\"Warning: MOV has unknown destination, continuing\\n\")\n        return\n    elif destination == 4:   # EXEC\n        # TODO?\n        self.sm_warning_messages.append(\"Warning: MOV EXEC functionality isn't implemented, continuing\\n\")\n        pass\n    elif destination == 5:   # PC\n        # save the address to jump to for when the pc is set for the new instruction\n        self.jmp_to = value & 0x1F\n        # the address is given, so, no increase of one for the pc\n        self.skip_increase_pc = True\n    elif destination == 6:   # ISR\n        self.vars[\"ISR\"] = value\n        self.vars[\"ISR_shift_counter\"] = 0\n    elif destination == 7:   # OSR\n        self.vars[\"OSR\"] = value\n        self.vars[\"OSR_shift_counter\"] = 0\n    else:                    # Error\n        self.sm_warning_messages.append(\"Warning: MOV has unknown destination, continuing\\n\")\n        return\n\n\ndef execute_irq(self, instruction):\n    \"\"\" execute an irq instruction \"\"\"\n    # get instruction parameters\n    Clr = 1 if (instruction & (1 << 6)) > 0 else 0\n    Wait = 1 if (instruction & (1 << 5)) > 0 else 0\n    MSB = 1 if (instruction & (1 << 4)) > 0 else 0\n\n    # add sm number and do modulo 4 if MSB is set\n    if MSB:\n        irq = (instruction & 0x07 + self.sm_number) % 4\n    else:\n        irq = instruction & 0x07\n    \n    # the irq statement is already waiting for clearing \n    if self.irq_is_waiting:\n        # check if irq is set, if so, wait\n        if self.sm_irq[irq] == 1:\n            self.skip_increase_pc = True\n            self.delay_delay = True\n        else:\n            self.irq_is_waiting = False\n            return\n    \n    if Clr:\n        # clear the irq\n        self.sm_irq[irq] = 0\n    else:\n        # set the irq\n        self.sm_irq[irq] = 1\n        if Wait:\n            self.irq_is_waiting = True\n            self.skip_increase_pc = True\n            self.delay_delay = True\n\ndef execute_set(self, instruction):\n    \"\"\" execute a set instruction \"\"\"\n    # get instruction parameters\n    destination = (instruction & 0x00E0) >> 5\n    data = instruction & 0x001F\n\n    if destination == 0:        # PINS\n        if self.settings[\"set_base\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'set_base' isn't set before use in SET instruction, continuing\\n\")\n        if self.settings[\"set_count\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'set_count' isn't set before use in SET instruction, continuing\\n\")\n        else:\n            for pin in range(self.settings[\"set_count\"]):\n                self.GPIO_data[\"GPIO_set\"][(self.settings[\"set_base\"] + pin) % 32] = 1 if data & (1 << pin) else 0\n                # self.set_GPIO('set', ((self.settings[\"set_base\"] + pin) % 32), data & (1 << pin))\n    elif destination == 1:      # X\n        self.vars[\"x\"] = data\n    elif destination == 2:      # Y\n        self.vars[\"y\"] = data\n    elif destination == 4:      # PINDIRS\n        if self.settings[\"set_base\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'set_base' isn't set before use in SET instruction, continuing\\n\")\n        if self.settings[\"set_count\"] == -1:\n            self.sm_warning_messages.append(\"Warning: 'set_count' isn't set before use in SET instruction, continuing\\n\")\n        for pin in range(self.settings[\"set_count\"]):\n            self.GPIO_data[\"GPIO_pindirs\"][(self.settings[\"set_base\"] + pin) % 32] = 1 if data & (1 << pin) else 0\n    else:\n        self.sm_warning_messages.append(\"Warning: SET has unknown destination, continuing\\n\")\n"
  },
  {
    "path": "state_machine_emulator/state_machine/_push_pull.py",
    "content": "def push_to_RxFIFO(self):\n    \"\"\" push data to the RxFIFO (it has already been checked that there is space!) \"\"\"\n    # place the new item after the data already in the FIFO\n    self.vars[\"RxFIFO\"][self.vars[\"RxFIFO_count\"]] = self.vars[\"ISR\"]\n    # there is now one more item\n    self.vars[\"RxFIFO_count\"] += 1 \n    # clear the shift counter and the ISR itself\n    self.vars[\"ISR_shift_counter\"] = 0\n    self.vars[\"ISR\"] = 0\n\n\ndef pull_from_TxFIFO(self):\n    \"\"\" pull data from TxFIFO (it has already been checked that there is data) \"\"\"\n    # pull the first item in the TxFIFO and place it in the OSR\n    self.vars[\"OSR\"] = self.vars[\"TxFIFO\"][0]\n    # shift the whole TxFIFO\n    for t in range(self.vars[\"TxFIFO_count\"]-1):\n        self.vars[\"TxFIFO\"][t] = self.vars[\"TxFIFO\"][t+1]\n    # set the now open space in the TxFIFO to 0\n    self.vars[\"TxFIFO\"][self.vars[\"TxFIFO_count\"]-1] = 0\n    # there is now one less data item in the TxFIFO\n    self.vars[\"TxFIFO_count\"] -= 1\n    # the number of bits shifted out of the OSR is 0\n    self.vars[\"OSR_shift_counter\"] = 0\n"
  },
  {
    "path": "state_machine_emulator/state_machine/_set_all_GPIO.py",
    "content": "def set_all_GPIO(self):\n    \"\"\" sets the GPIO according to the changes in the time step; there is a specific priority order to out, set, sideset and external \"\"\"\n    # first 'out' and 'set' values (lowest priority)\n    for pin in range(self.settings[\"out_base\"], self.settings[\"out_count\"] + self.settings[\"out_base\"]):\n        if self.GPIO_data[\"GPIO_out\"][pin] != -1:\n            if self.GPIO_data[\"GPIO_pindirs\"][pin] == 0: # pindir must be an output (0)\n                self.GPIO_data[\"GPIO\"][pin] = self.GPIO_data[\"GPIO_out\"][pin]\n            else:\n                self.sm_warning_messages.append(\"Warning: GPIO \"+str(pin)+\" set by 'out' is not an output, continuing\\n\")\n\n    for pin in range(self.settings[\"set_base\"], self.settings[\"set_count\"]+self.settings[\"set_base\"]):\n        if self.GPIO_data[\"GPIO_set\"][pin] != -1:\n            if self.GPIO_data[\"GPIO_pindirs\"][pin] == 0: # pindir must be an output (0)\n                self.GPIO_data[\"GPIO\"][pin] = self.GPIO_data[\"GPIO_set\"][pin]\n            else:\n                self.sm_warning_messages.append(\"Warning: GPIO \"+str(pin)+\" set by 'set' is not an output, continuing\\n\")\n\n    # now sideset, medium priority\n    # first check if the GPIO or the pindirs have to be set (here only GPIO)\n    if not self.settings[\"sideset_pindirs\"]:\n        # dirty hack: sideset_count includes the sideset_opt bit, but this does not set a pin!\n        count_sideset = self.settings[\"sideset_count\"]\n        if self.settings[\"sideset_opt\"]:\n            count_sideset -= 1\n        # now set the pins\n        for pin in range(self.settings[\"sideset_base\"], count_sideset + self.settings[\"sideset_base\"]):\n            if self.GPIO_data[\"GPIO_sideset\"][pin] != -1:\n                if self.GPIO_data[\"GPIO_pindirs\"][pin] == 0: # pindir must be an output (0)\n                    self.GPIO_data[\"GPIO\"][pin] = self.GPIO_data[\"GPIO_sideset\"][pin]\n                else:\n                    self.sm_warning_messages.append(\"Warning: GPIO \"+str(pin)+\" set by 'sideset' is not an output, continuing\\n\")\n\n    # finally, externally driven pins, highest priority\n    for pin in range(32):\n        if self.GPIO_data[\"GPIO_external\"][pin] != -1:\n            # external input always wins\n            self.GPIO_data[\"GPIO\"][pin] = self.GPIO_data[\"GPIO_external\"][pin]\n            if self.GPIO_data[\"GPIO_pindirs\"][pin] != 1:\n                # the pin is configured as an output! Warning!\n                self.sm_warning_messages.append(\"Warning: external input applied to GPIO \"+str(pin)+\" but it is configured as output (external wins!), continuing\\n\")"
  },
  {
    "path": "state_machine_emulator/state_machine/_time_step.py",
    "content": "def time_step(self):\n    \"\"\" emulate one time step \"\"\"\n    # prepare for warning messages\n    self.sm_warning_messages = []\n    # flag to indicate we're dealing with a delayed delay\n    skip_due_to_delay_delay = False\n    # check if delay is active: for some instructions (wait, irq) delay needs to wait till after the instruction has finished\n    if not self.delay_delay:\n        # check if a delay is being executed: if so, do nothing\n        if self.vars[\"delay\"] > 0:\n            self.vars[\"delay\"] -= 1\n            skip_due_to_delay_delay = True\n    \n    # if delayed delay: skip normal pc increase and instruction execution\n    if skip_due_to_delay_delay == False:\n        self.delay_delay = False\n        # check if (e.g. due to a jmp or wait) the pc does not need to be changed\n        if self.skip_increase_pc:\n            # yes: do not increase the pc\n            self.skip_increase_pc = False # clear flag\n            if self.jmp_to >= 0:\n                # the skip is due to a jmp, set pc to the address to jmp to\n                self.vars[\"pc\"] = self.jmp_to\n                self.jmp_to = -1 #  not set\n        else:\n            # add one to the program counter\n            self.vars[\"pc\"] += 1\n            # check if the pc should wrap\n            if self.vars[\"pc\"] == self.wrap+1:\n                self.vars[\"pc\"] = self.wrap_target\n        # get the new instruction and execute it\n        instruction = int(self.program[self.vars[\"pc\"]][0], 16)\n        # execute the instruction\n        self.execute_instruction(instruction)\n        # set the 'status' depending on RxFIFO or TxFIFO count\n        if self.settings['status_sel'] == 0:\n            # check TxFIFO level\n            if self.vars[\"TxFIFO_count\"] < self.settings['FIFO_level_N']:\n                self.vars[\"status\"] = -1; # binary all ones \n            else:\n                self.vars[\"status\"] = 0;  # binary all zeroes\n        else: # self.settings['status_sel'] == 1\n            # check RxFIFO level\n            if self.vars[\"RxFIFO_count\"] < self.settings['FIFO_level_N']:\n                self.vars[\"status\"] = -1; # binary all ones\n            else:\n                self.vars[\"status\"] = 0; # binary all zeroes\n    # set the GPIOs according to the changes in this time step\n    # note: even when skip_due_to_delay_delay is True, there may still be \n    # changes due to the pin_ or c_program\n    self.set_all_GPIO()\n    # increase the system level clock\n    self.clock += 1\n    # return warnings\n    return self.sm_warning_messages\n"
  },
  {
    "path": "subroutines/CMakeLists.txt",
    "content": "add_executable(subroutine)\n\npico_generate_pio_header(subroutine ${CMAKE_CURRENT_LIST_DIR}/subroutine.pio)\n\ntarget_sources(subroutine PRIVATE subroutine.cpp)\n\ntarget_link_libraries(subroutine PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(subroutine)\n"
  },
  {
    "path": "subroutines/README.md",
    "content": "# Subroutines in PIO code\n\n## What? Subroutines? There's no such thing in pioasm\nThat is true, but you can make something that works like subroutines.\n\nWhat is needed is a way to specify a return address, and for the subroutine to actually jump to that return address.\nSpecifying a return address can be achieved by using the labels in the pio code. For this to work it must be made sure that the pio program starts at address 0 in the pio memory. This is achieved by starting with `.origin 0`. The return address can be stored in any of the registers, in the example below x is used, in the [example code](https://github.com/GitJer/Some_RPI-Pico_stuff/blob/main/subroutines/subroutine.pio) the ISR is used, but OSR and y are just as good. You only need to remember to not use the register with the return address in the subroutine. At the end of the subroutine the return address is used to jump back to the main code. This can be done in several ways (assuming the OSR is used for the return address): \n- In the example below the property is used that a jmp to an absolute address is all 0's plus the return address in the last 5 bits. Then `mov exec OSR` can be used to execute that jump.\n- In the same way `out exec 32` can be used.\n- It is also possible to directly jump to the return address by placing the return address in the program counter using `mov PC OSR`.\n- In the same way `out PC 5` can be used.\n\nIn the example below two subroutines are used: sub_set_one and sub_set_zero. The return address is in x and jumping to that address is done by `mov PC x`. \n```\n.program subroutine\n    ; START THE PROGRAM AT ADDRESS 0\n.origin 0\n\nstart:\n        ; set the return address in x, then in OSR\n    set x ret1\n        ; jump to subroutine to set the pin to 1\n    jmp sub_set_one\nret1:\n        ; set the return address in x, then OSR\n    set x start\n        ; jump to subroutine to set the pin to 0\n    jmp sub_set_zero\n\nsub_set_one:\n        ; set pin to 1\n    set PINS 1\n        ; return to the address set in OSR\n    mov PC x\nsub_set_zero:\n        ; set pin to 0\n    set PINS 0\n        ; return to the address set in OSR\n    mov PC x\n```\n\n## Is it useful?\nIn the PIO memory there is only room for 32 instructions, so using subroutines which require some overhead in the number of instructions (setting up the return address, jumping to the subroutine, jumping to the return address) quickly costs more than just writing what you want as one piece of code. In the example above it would clearly be better to just make a loop that toggles a pin.\n\nIn the [example code](https://github.com/GitJer/Some_RPI-Pico_stuff/blob/main/subroutines/subroutine.pio) a somewhat more elaborate subroutine is used: a (nonsensical) protocol that sends 5 bits and then pauses for a specified number of instructions before returning to the main program. It produces:\n\n<img src=\"code_output.png\" width=\"900\">\n\nIn that code, the subroutine consists of 7 instructions and it is called 5 times. Setting up and jumping to it costs 5 instructions. In total all 32 instruction memory locations are used. If one would program the functional parts (sending 5 bits and pausing) directly it would cost about 41 instructions, which would not fit in memory. \n\nSo, this is a proof of principle that using subroutines can - in some cases - be used to do more with the limited memory space than is possible with just writing the code in one program.\n"
  },
  {
    "path": "subroutines/subroutine.cpp",
    "content": "/*\n    This is the C SDK code needed to start the accompanying pio code.\n    It only defines a pin (for out and set) as output, and starts the sm\n*/\n\n#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"subroutine.pio.h\"\n\nint main()\n{\n    // the pio instance\n    PIO pio = pio0;\n    // the state machine\n    uint sm = 0;\n\n    // needed for printf\n    stdio_init_all();\n\n    int tx_pin = 16;\n\n    // let pio control tx_pin\n    pio_gpio_init(pio, tx_pin);\n    // load the pio program into the pio memory\n    uint offset = pio_add_program(pio, &subroutine_program);\n    // make a sm config\n    pio_sm_config c = subroutine_program_get_default_config(offset);\n    // set the 'out' pin\n    sm_config_set_out_pins(&c, tx_pin, 1);\n    // set the 'set' pin\n    sm_config_set_set_pins(&c, tx_pin, 1);\n    // set the pin to output\n    pio_sm_set_consecutive_pindirs(pio, sm, tx_pin, 1, true);\n    // OUT shifts to right, no autopull\n    sm_config_set_out_shift(&c, true, false, 32);\n    // just for testing purposes (I have a crappy logic analyzer): slow down the clock\n    sm_config_set_clkdiv(&c, 10);\n    // init the pio sm with the config\n    pio_sm_init(pio, sm, offset, &c);\n    // enable the sm\n    pio_sm_set_enabled(pio, sm, true);\n    // do nothing\n    while (1)\n        ;\n}"
  },
  {
    "path": "subroutines/subroutine.pio",
    "content": "; This is a proof of principle of using subroutines in PIO code.\n\n.program subroutine\n    ; START THE PROGRAM AT ADDRESS 0\n.origin 0\n\nstart:\n        ; set the return address in x, then in ISR\n    set x ret1\n    mov ISR x\n        ; set data to send in x\n    set x 0b11111\n        ; set pause time after sending bits in y\n    set y 6\n        ; jump to subroutine send_data\n    jmp send_data\nret1:\n        ; set the return address in x, then in ISR\n    set x ret2\n    mov ISR x\n        ; set data to send in x\n    set x 0b11011\n        ; set pause time after sending bits in y\n    set y 12\n        ; jump to subroutine send_data\n    jmp send_data\nret2:\n        ; set the return address in x, then in ISR\n    set x ret3\n    mov ISR x\n        ; set data to send in x\n    set x 0b10101\n        ; set pause time after sending bits in y\n    set y 18\n        ; jump to subroutine send_data\n    jmp send_data\nret3:\n        ; set the return address in x, then in ISR\n    set x ret4\n    mov ISR x\n        ; set data to send in x\n    set x 0b01110\n        ; set pause time after sending bits in y\n    set y 24\n        ; jump to subroutine send_data\n    jmp send_data\nret4:\n        ; set the return address in x, then in ISR\n    set x start\n    mov ISR x\n        ; set data to send in x\n    set x 0b10001\n        ; set pause time after sending bits in y\n    set y 30\n        ; jump to subroutine send_data\n    jmp send_data\n\n\n; The subroutine to send bits and pause for some time\n;\n; The return address used is in ISR\n; The data to be send is in x\n; The pause time is in y\n\nsend_data: \n        ; data to send is put in the OSR for shifting\n    mov OSR x\n        ; send 5 bits, x is a counter\n    set x, 4\nbitloop:\n        ; shift out 1 bit from OSR to pin\n    out pins 1\n        ; afterwards make the pin 0\n    set pins 0\n        ; do all 5 bits\n    jmp x-- bitloop\n        ; pause y+1 steps after sending the bits\npause:  \n    jmp y-- pause\n        ; return to the address set in ISR\n    mov exec ISR\n\n"
  },
  {
    "path": "two_pio_programs_one_file/CMakeLists.txt",
    "content": "add_executable(two_p_one_f)\n\npico_generate_pio_header(two_p_one_f ${CMAKE_CURRENT_LIST_DIR}/two_p_one_f.pio)\n\ntarget_sources(two_p_one_f PRIVATE two_p_one_f.cpp)\n\ntarget_link_libraries(two_p_one_f PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(two_p_one_f)\n\n# add url via pico_set_program_url\nexample_auto_set_url(two_p_one_f)\n\n\n"
  },
  {
    "path": "two_pio_programs_one_file/README.md",
    "content": "This example contains two pio programs.\nIt was actually part of getting the 1-wire protocol working, and that requires an external pull up resistor (e.g. 10k) on the pin used by both set and side-set.\nBut for now, this is mostly an example of how two pio programs in one .pio file can be used.\n\nThe first program produces a short 0 (two cycles) and a long 1 (nine cycles) on the output\nthe second program produces a short 1 (two cycles) and a long 0 (nine cycles) on the output\n\nNote that in the generated file two_p_one_f.pio.h file the two programs are \nconsidered completely separate and independent of where in memory they will end up!\nEven the 'jmp again1' and 'jmp again2' are both encoded as a 'jmp 0'. This means\nthat when loading a program into pio memory, some trans-coding is necessary to make\nthe jmp statements point to the right memory address.\n"
  },
  {
    "path": "two_pio_programs_one_file/two_p_one_f.cpp",
    "content": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"two_p_one_f.pio.h\"\n\n#define TEST_PIN 16\n\n/*\nThis program shows how you can use two pio programs in one file (two_p_one_f)\nFor this test it is assumed that the TEST_PIN is externally pulled high via e.g. a 10k resistor\n*/\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // the pio instance is 0\n    PIO pio;\n    pio = pio0;\n    // the state machine instance is 0\n    uint sm;\n    sm = 0;\n    // configure the used pin\n    pio_gpio_init(pio, TEST_PIN);\n\n    // load the pio programs into the pio memory\n    uint offset_1 = pio_add_program(pio, &two_p_one_f_1_program);\n    uint offset_2 = pio_add_program(pio, &two_p_one_f_2_program);\n    // make the sm config for both programs\n    pio_sm_config c = pio_get_default_sm_config();\n    // set the pin used by set\n    sm_config_set_set_pins(&c, TEST_PIN, 1);\n    // sideset (bitcount=2: one bit + one bit because sideset is optional, optional=true, pindirs=true)\n    sm_config_set_sideset(&c, 2, true, true);\n    // set the pin used by sideset (same pin as for SET, two lines above)\n    sm_config_set_sideset_pins(&c, TEST_PIN);\n    // one clock cycle is 10 us\n    sm_config_set_clkdiv(&c, 1250);\n\n    // init the pio sm with the config, start with two_p_one_f_1\n    pio_sm_init(pio, sm, offset_1, &c);\n    // enable the sm\n    pio_sm_set_enabled(pio, sm, true);\n\n    while (true)\n    {\n        // switch to two_p_one_f_1\n        pio_sm_exec(pio, sm, 0x0000 + offset_1);\n        sleep_ms(1000);\n        // switch to two_p_one_f_2\n        pio_sm_exec(pio, sm, 0x0000 + offset_2);\n        sleep_ms(1000);\n    }\n}\n"
  },
  {
    "path": "two_pio_programs_one_file/two_p_one_f.pio",
    "content": "\n;\n; This file contains two pio programs.\n; It is assumed that an external pull up resistor is present on the pin used by both set and side-set\n;\n; The first program produces a short 0 (two cycles) and a long 1 (nine cycles) on the output\n; the second program produces a short 1 (two cycles) and a long 0 (nine cycles) on the output\n;\n; Note that in the generated file two_p_one_f.pio.h file the two programs are \n; considered completely separate and independent of where in memory they will end up!\n; Even the 'jmp again1' and 'jmp again2' are both encoded as a 'jmp 0'. This means\n; that when loading a program into pio memory, some trans-coding is necessary to make\n; the jmp statements point to the right memory address.\n\n\n        ; first program\n.program two_p_one_f_1\n        ; side-set controls the direction\n.side_set 1 opt pindirs\n\nagain1:\n        ; set direction to output (side 1), and set output value to 0 (set pins 0)\n    set pins 0 side 1 [1]\n        ; set direction to input, if external pull-up is used, pin will float to 1\n    nop side 0\n        ; keep input for some time\n    nop [6]\n        ; start over\n    jmp again1\n\n\n        ; second program\n.program two_p_one_f_2\n        ; side-set controls the direction\n.side_set 1 opt pindirs\n\nagain2:\n        ; set to output, and set the output to 0\n    set pins 0 side 1\n    nop [7]\n        ; set to input, if external pullup is used, expect line to float up\n    nop side 0\n    jmp again2\n"
  },
  {
    "path": "two_pio_programs_one_file/two_p_one_f.pio.h",
    "content": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// -------------------------------------------------- //\n\n#pragma once\n\n#if !PICO_NO_HARDWARE\n#include \"hardware/pio.h\"\n#endif\n\n// ------------- //\n// two_p_one_f_1 //\n// ------------- //\n\n#define two_p_one_f_1_wrap_target 0\n#define two_p_one_f_1_wrap 3\n\nstatic const uint16_t two_p_one_f_1_program_instructions[] = {\n            //     .wrap_target\n    0xf900, //  0: set    pins, 0         side 1 [1] \n    0xb042, //  1: nop                    side 0     \n    0xa642, //  2: nop                           [6] \n    0x0000, //  3: jmp    0                          \n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program two_p_one_f_1_program = {\n    .instructions = two_p_one_f_1_program_instructions,\n    .length = 4,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config two_p_one_f_1_program_get_default_config(uint offset) {\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + two_p_one_f_1_wrap_target, offset + two_p_one_f_1_wrap);\n    sm_config_set_sideset(&c, 2, true, true);\n    return c;\n}\n#endif\n\n// ------------- //\n// two_p_one_f_2 //\n// ------------- //\n\n#define two_p_one_f_2_wrap_target 0\n#define two_p_one_f_2_wrap 3\n\nstatic const uint16_t two_p_one_f_2_program_instructions[] = {\n            //     .wrap_target\n    0xf800, //  0: set    pins, 0         side 1     \n    0xa742, //  1: nop                           [7] \n    0xb042, //  2: nop                    side 0     \n    0x0000, //  3: jmp    0                          \n            //     .wrap\n};\n\n#if !PICO_NO_HARDWARE\nstatic const struct pio_program two_p_one_f_2_program = {\n    .instructions = two_p_one_f_2_program_instructions,\n    .length = 4,\n    .origin = -1,\n};\n\nstatic inline pio_sm_config two_p_one_f_2_program_get_default_config(uint offset) {\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + two_p_one_f_2_wrap_target, offset + two_p_one_f_2_wrap);\n    sm_config_set_sideset(&c, 2, true, true);\n    return c;\n}\n#endif\n\n"
  },
  {
    "path": "ws2812_led_strip_120/CMakeLists.txt",
    "content": "add_executable(ws2812_led_strip_120)\n\npico_generate_pio_header(ws2812_led_strip_120 ${CMAKE_CURRENT_LIST_DIR}/ws2812_led_strip_120.pio)\n\ntarget_sources(ws2812_led_strip_120 PRIVATE ws2812_led_strip_120.c)\n\ntarget_link_libraries(ws2812_led_strip_120 PRIVATE\n        pico_stdlib\n        hardware_pio\n        )\n\npico_add_extra_outputs(ws2812_led_strip_120)\n\n# add url via pico_set_program_url\nexample_auto_set_url(ws2812_led_strip_120)\n\n\n"
  },
  {
    "path": "ws2812_led_strip_120/README.md",
    "content": "# Ws2812 led strip with 120 pixels \n\nThis is my take on how to control a ws2812 led strip with 120 pixels. It differs from the [pio example code](https://github.com/raspberrypi/pico-examples/tree/master/pio/ws2812) for the ws2812 in two ways.\n\n## clkdiv\nI wanted to have the sm run at its normal speed (125 MHz). The timing restrictions of the ws2812 chip then requires to use delay cycles. It happens that the maximum (31) is just enough delay to make it work. In the pio example code, a side set is used to toggle the GPIO that drives the signal to the ws2812 pixels. This results in neat code, but if side set is used, however, less bits are left over to specify a delay. In this case not enough to make it work.\n\n## reset period\nTo make the ws2812 accept a new set of pixel data, a reset period has to be used. In my code I count how many pixel RGB values have been sent out and then start a delay loop. The led strip I use has 120 pixels, so, the pio code is specific to that amount of pixels. The delay loop is such that it waits long enough for the reset of the ws2812.\n\n## Dithering\nI wanted to try dithering for low brightness values to make the transitions more smooth. In the code I have used a simple table with values to use for dithering. I have no idea how others do this, so maybe there are more clever ways. In the code first normal brightness changes are programmed, followed by dithered brightness changes.\n"
  },
  {
    "path": "ws2812_led_strip_120/ws2812_led_strip_120.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n#include \"ws2812_led_strip_120.pio.h\"\n\nstatic inline void put_pixel(uint32_t pixel_grb)\n{\n    pio_sm_put_blocking(pio0, 0, pixel_grb << 8u);\n}\n\nstatic inline uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b)\n{\n    return ((uint32_t)(r) << 8) |\n           ((uint32_t)(g) << 16) |\n           (uint32_t)(b);\n}\n\nvoid all_red()\n{\n    for (uint i = 0; i < 120; ++i)\n        put_pixel(urgb_u32(0x80, 0, 0));\n}\n\nvoid all_green()\n{\n    for (uint i = 0; i < 120; ++i)\n        put_pixel(urgb_u32(0, 0x80, 0));\n}\n\nvoid all_blue()\n{\n    for (uint i = 0; i < 120; ++i)\n        put_pixel(urgb_u32(0, 0, 0x80));\n}\n\nvoid white(int level)\n{\n    for (uint i = 0; i < 120; ++i)\n        put_pixel(urgb_u32(level, level, level));\n}\n\nconst int PIN_TX = 2;\n\nint main()\n{\n    // needed for printf\n    stdio_init_all();\n    // the pio instance\n    PIO pio;\n    // the state machine\n    uint sm;\n    // pio 0 is used\n    pio = pio0;\n    // state machine 0\n    sm = 0;\n    // configure the used pins\n    pio_gpio_init(pio, PIN_TX);\n    // load the pio program into the pio memory\n    uint offset = pio_add_program(pio, &ws2812_led_strip_120_program);\n    // make a sm config\n    pio_sm_config c = ws2812_led_strip_120_program_get_default_config(offset);\n    // set the 'set' pin\n    sm_config_set_set_pins(&c, PIN_TX, 1);\n    // set the pindirs to output\n    pio_sm_set_consecutive_pindirs(pio, sm, PIN_TX, 1, true);\n    // set out shift direction\n    sm_config_set_out_shift(&c, false, true, 24);\n    // set in shift direction\n    sm_config_set_in_shift(&c, false, false, 0);\n    // join the FIFOs\n    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);\n    // init the pio sm with the config\n    pio_sm_init(pio, sm, offset, &c);\n    // enable the sm\n    pio_sm_set_enabled(pio, sm, true);\n\n    // int maxRows = 11;\n    // int maxCols = 10;\n    //     bool transition[11][10] = {\n    //         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 10\n    //         {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, // 9\n    //         {0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, // 8\n    //         {0, 0, 0, 1, 0, 0, 1, 0, 0, 1}, // 7\n    //         {0, 0, 1, 0, 1, 0, 0, 1, 0, 1}, // 6\n    //         {0, 1, 0, 1, 0, 1, 0, 1, 0, 1}, // 5\n    //         {0, 1, 0, 1, 1, 0, 1, 0, 1, 1}, // 4\n    //         {0, 1, 1, 0, 1, 1, 0, 1, 1, 1}, // 3\n    //         {0, 1, 1, 1, 1, 0, 1, 1, 1, 1}, // 2\n    //         {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 1\n    //         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 0\n    //     };\n\n    int maxRows = 13;\n    int maxCols = 12;\n    bool transition[13][12] = {\n        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 12\n        {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 11\n        {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, // 10\n        {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, // 9\n        {1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, // 8\n        {1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0}, // 7\n        {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, // 6\n        {0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1}, // 5\n        {0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1}, // 4\n        {0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1}, // 3\n        {0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}, // 2\n        {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 1\n        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 0\n    };\n\n    int maxLevel = 20;\n    while (1)\n    {\n        // without dithering\n        for (int level = 0; level < maxLevel; level++)\n            // next loop is just to make it equal to the dithered version below\n            for (int row = 0; row < maxRows * maxCols; row++) \n                white(level);\n        for (int level = maxLevel; level > 0; level--)\n            // next loop is just to make it equal to the dithered version below\n            for (int row = maxRows * maxCols - 1; row >= 0; row--) \n                white(level);\n        // with dithering\n        for (int level = 0; level < maxLevel; level++)\n            for (int row = 0; row < maxRows; row++)\n                for (int col = 0; col < maxCols; col++)\n                    white(level + transition[row][col]);\n        for (int level = maxLevel; level > 0; level--)\n            for (int row = maxRows - 1; row >= 0; row--)\n                for (int col = 0; col < maxCols; col++)\n                    white(level + transition[row][col]);\n    }\n}"
  },
  {
    "path": "ws2812_led_strip_120/ws2812_led_strip_120.pio",
    "content": "; This code sends pixel color data for ws2812 leds for a led strip of 120 leds.\n; Note: the data send from the c-code must contain 120 * RGB data.\n; It assumes the out-shift is set to right, such that the MSB of the RGB data is send first\n; It assumes the in-shift is set to left, such that the counters work correctly\n; \n;\n; for the ws2812 a zero bit looks like this:\n;\n; |---|\n; |   |  \n; --------------\n;\n; a one bit looks like:\n;\n; |-------|\n; |       |  \n; --------------\n;\n; So, a zero consists of 1/3 high and then 2/3 low; a one consists of 2/3 high and then 1/3 low\n;\n; One pixel requires 24 bits to encode RGB with 8 bits each\n; the led strip I use has 120 pixels\n; after all pixels have been sent, wait a reset period to make the strip start again\n\n; the code below works as follows:\n; 1) make an outer loop that runs 2880 times (120 pixels * 24 bits)\n; 2) get the bytes with RGB data from the c-program, \n;    shift them in one by one and send the correct ws2812 bit-encoding\n; 3) after all 2880 bits have been send to the pixels, wait a reset time (393216 * 1/125000000) = 3ms\n\n.program ws2812_led_strip_120\n\n.define T0 30\nstart:\n        ; 120 times 24 (my led strip has 120 leds, 24 bits per pixel) = 2880\n        ; but note that the '0'th loop is also executed -> 2879 is needed\n        ; fill y with 2880 (101101000000) and subtract one to get 2879 (101100111111)\n    set y 22        ; set y to 10110 (the 5 MSB of 2880)\n    mov ISR y       ; copy them to the ISR\n    set y 16        ; set y to 10000 (the next 5 MSB of 2880)\n    in y 5          ; shift them in the ISR (note: left_in_shift is required)\n    in NULL 2       ; shift 2 0 into the ISR, now it contains 2880\n    mov y ISR       ; copy it into y\n    jmp y-- loop    ; decrease one to get 2879\nloop:\n    out x, 1        ; shift in one bit in x; note: this is blocking, \n                    ; it must be blocking while the pins is 0.\n    set pins 1 [T0] ; pins is set to high; this is the first 1/3, always high\n    jmp !x send_zero  ; if a low must be sent, jump. \n    jmp end_with_zero [T0+1] ; keep high for 31 c (now 2/3 of the time frame for one bit is high)\nsend_zero:\n    set pins 0 [T0+1] ; set low for 31 c (the middle 1/3 is low)\nend_with_zero:\n    set pins 0 [T0] ; set low for 31 c (including the out x, 1 = 31c). The last 1/3 is always low\n    jmp y-- loop    ; if not all of the 2880 bits have been sent, send another\n\n    ; delay 12288 clock cycles (about 100 us)\n    set y 24        ; binary = 11000\n    mov ISR y\n    in NULL 9       ; shift in 9 zero bits\n    mov y ISR       ; y now contains 11000000000000 = 12288\ndelay_loop:         ; the actual delay loop\n    jmp y-- delay_loop \n    jmp start       ; start again for a new set of pixel data\n"
  }
]