{"id":2434,"date":"2025-03-15T07:03:03","date_gmt":"2025-03-15T07:03:03","guid":{"rendered":"https:\/\/mailitics.com\/index.php\/2025\/03\/15\/nine-pico-pio-wats-with-rust-part-2\/"},"modified":"2025-03-15T07:03:03","modified_gmt":"2025-03-15T07:03:03","slug":"nine-pico-pio-wats-with-rust-part-2","status":"publish","type":"post","link":"https:\/\/mailitics.com\/index.php\/2025\/03\/15\/nine-pico-pio-wats-with-rust-part-2\/","title":{"rendered":"Nine Pico PIO Wats with Rust (Part 2)"},"content":{"rendered":"<p>    Nine Pico PIO Wats with Rust (Part 2)<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<p class=\"wp-block-paragraph\">This is Part 2 of an exploration into the unexpected quirks of programming the Raspberry Pi Pico PIO with <a href=\"https:\/\/towardsdatascience.com\/tag\/micropython\/\" title=\"Micropython\">Micropython<\/a>. If you missed <a href=\"https:\/\/towardsdatascience.com\/nine-pico-pio-wats-with-rust-part-1-9d062067dc25\/\">Part 1<\/a>, we uncovered four <em>Wats<\/em> that challenge assumptions about register count, instruction slots, the behavior of <code>pull noblock<\/code>, and smart yet cheap hardware.<\/p>\n<p class=\"wp-block-paragraph\">Now, we continue our journey toward crafting a theremin-like musical instrument\u200a\u2014\u200aa project that reveals some of the quirks and perplexities of PIO programming. Prepare to challenge your understanding of constants in a way that brings to mind a Shakespearean tragedy.<\/p>\n<h2 class=\"wp-block-heading\">Wat 5: Inconstant constants<\/h2>\n<p class=\"wp-block-paragraph\">In the world of PIO programming, constants should be reliable, steadfast, and, well, <em>constant<\/em>. But what if they\u2019re not? This brings us to a puzzling Wat about how the set instruction in PIO works\u2014or doesn\u2019t\u2014when handling larger constants.<\/p>\n<p class=\"wp-block-paragraph\">Much like Juliet doubting Romeo\u2019s constancy, you might find yourself wondering if PIO constants will, as she says, \u201cprove likewise variable.\u201d<\/p>\n<h2 class=\"wp-block-heading\">The problem: Constants are not as big as they seem<\/h2>\n<p class=\"wp-block-paragraph\">Imagine you\u2019re programming an ultrasonic range finder and need to count down from 500 while waiting for the Echo signal to drop from high to low. To set up this wait time in PIO, you might na\u00efvely try to load the constant value directly using <code>set<\/code>:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">; In Rust, be sure 'config.shift_in.direction = ShiftDirection::Left;'\nset y, 15       ; Load upper 5 bits (0b01111)\nmov isr, y      ; Transfer to ISR (clears ISR)\nset y, 20       ; Load lower 5 bits (0b10100)\nin y, 5         ; Shift in lower bits to form 500 in ISR\nmov y, isr      ; Transfer back to y<\/code><\/pre>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><em>Aside: Don\u2019t try to understand the crazy <\/em><em>jmp<\/em><em> operations here. We\u2019ll discuss those next in <\/em><strong><em>Wat 6<\/em><\/strong><em>.<\/em><\/p>\n<\/blockquote>\n<p class=\"wp-block-paragraph\">But here\u2019s the tragic twist: the set instruction in PIO is limited to constants between 0 and 31. Moreover, the star-crossed set instruction doesn\u2019t report an error. Instead, <a href=\"https:\/\/forums.raspberrypi.com\/viewtopic.php?p=2289656#p2289173\">it silently corrupts the entire PIO instruction<\/a>. This produces a nonsense result.<\/p>\n<h2 class=\"wp-block-heading\">Workarounds for inconstant constants<\/h2>\n<p class=\"wp-block-paragraph\">To address this limitation, consider the following approaches:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Read Values and Store Them in a Register: <\/strong>We saw this approach in <strong>Wat 1<\/strong>. You can load your constant in the <code>osr<\/code> register, then transfer it to y. For example:<\/li>\n<\/ul>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\"># Read the max echo wait into OSR.\npull                    ; same as pull block\nmov y, osr              ; Load max echo wait into Y<\/code><\/pre>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Shift and Combine Smaller Values: <\/strong>Using the isr register and the in instruction, you can build up a constant of any size. This, however, consumes time and operations from your 32-operation budget (see <a href=\"https:\/\/towardsdatascience.com\/nine-pico-pio-wats-with-rust-part-1-9d062067dc25\/\">Part 1<\/a>, <strong>Wat 2<\/strong>).<\/li>\n<\/ul>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">; In Rust, be sure 'config.shift_in.direction = ShiftDirection::Left;'\n\nset y, 15       ; Load upper 5 bits (0b01111)\nmov isr, y      ; Transfer to ISR (clears ISR)\nset y, 20       ; Load lower 5 bits (0b10100)\nin y, 5         ; Shift in lower bits to form 500 in ISR\nmov y, isr      ; Transfer back to y<\/code><\/pre>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Slow Down the Timing<\/strong>: Reduce the frequency of the state machine to stretch delays over more system clock cycles. For example, lowering the state machine speed from 125 MHz to 343 kHz reduces the timeout constant <code>182<\/code>,<code>216<\/code> to <code>500<\/code>.\u00a0<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Use Extra Delays and (Nested) Loops:<\/strong> All instructions support an optional delay, allowing you to add up to 31 extra cycles. (To generate even longer delays, use loops\u200a\u2014\u200aor even nested loops.)<\/li>\n<\/ul>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">; Generate 10\u03bcs trigger pulse (4 cycles at 343_000Hz)\nset pins, 1 [3]       ; Set trigger pin to high, add delay of 3\nset pins, 0           ; Set trigger pin to low voltage<\/code><\/pre>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Use the \u201cSubtraction Trick\u201d to Generate the Maximum 32-bit Integer<\/strong>:<strong> <\/strong>In <strong>Wat 7<\/strong>, we\u2019ll explore a way to generate <code>4,294,967,295<\/code> (the maximum unsigned 32-bit integer) via subtraction.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">Much like Juliet cautioning against swearing by the inconstant moon, we\u2019ve discovered that PIO constants are not always as steadfast as they seem. Yet, just as their story takes unexpected turns, so too does ours, moving from the inconstancy of constants to the uneven nature of conditionals. In the next Wat, we\u2019ll explore how PIO\u2019s handling of conditional jumps can leave you questioning its loyalty to logic.<\/p>\n<h2 class=\"wp-block-heading\">Wat 6: Conditionals through the looking-glass<\/h2>\n<p class=\"wp-block-paragraph\">In most programming environments, logical conditionals feel balanced: you can test if a pin is high or low, or check registers for equality or inequality. In PIO, this symmetry breaks down. You can jump on pin high, but not pin low, and on <code>x!=y<\/code>, but not <code>x==y<\/code>. The rules are whimsical\u200a\u2014\u200alike Humpty Dumpty in <em>Through the Looking-Glass<\/em>: \u201cWhen I define a conditional, it means just what I choose it to mean\u200a\u2014\u200aneither more nor less.\u201d<\/p>\n<p class=\"wp-block-paragraph\">These quirks force us to rewrite our code to fit the lopsided logic, creating a gulf between how we wish the code could be written and how we must write it.<\/p>\n<h2 class=\"wp-block-heading\">The problem: Lopsided conditionals in action<\/h2>\n<p class=\"wp-block-paragraph\">Consider a simple scenario: using a range finder, you want to count down from a maximum wait time (y) until the ultrasonic echo pin goes low. Intuitively, you might write the logic like this:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">measure_echo_loop:\n jmp !pin measurement_complete   ; If echo voltage is low, measurement is complete\n jmp y-- measure_echo_loop       ; Continue counting down unless timeout<\/code><\/pre>\n<p class=\"wp-block-paragraph\">And when processing the measurement, if we only wish to output values that differ from the previous value, we would write:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">measurement_complete:\n jmp x==y cooldown             ; If measurement is the same, skip to cool down\n mov isr, y                    ; Store measurement in ISR\n push                          ; Output ISR\n mov x, y                      ; Save the measurement in X<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Unfortunately, PIO doesn\u2019t let you test <code>!pin<\/code> or <code>x==y<\/code> directly. You must restructure your logic to accommodate the available conditionals, such as <code>pin<\/code> and <code>x!=y<\/code>.<\/p>\n<h2 class=\"wp-block-heading\">The solution: The way it must be<\/h2>\n<p class=\"wp-block-paragraph\">Given PIO\u2019s limitations, we adapt our logic with a two-step approach that ensures the desired behavior despite the missing conditionals:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">Jump on the opposite conditional to skip two instructions forward.<\/li>\n<li class=\"wp-block-list-item\">Next, use an unconditional jump to reach the desired target.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">This workaround adds one extra jump (affecting the instruction limit), but the additional label is cost-free.<\/p>\n<p class=\"wp-block-paragraph\">Here is the rewritten code for counting down until the pin goes low:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">measure_echo_loop:\n   jmp pin echo_active     ; if echo voltage is high continue count down\n   jmp measurement_complete ; if echo voltage is low, measurement is complete\necho_active:\n   jmp y-- measure_echo_loop ; Continue counting down unless timeout<\/code><\/pre>\n<p class=\"wp-block-paragraph\">And here is the code for processing the measurement such that it will only output differing values:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">measurement_complete:\n   jmp x!=y send_result    ; if measurement is different, then send it.\n   jmp cooldown            ; If measurement is the same, don't send.\n\nsend_result:\n   mov isr, y              ; Store measurement in ISR\n   push                    ; Output ISR\n   mov x, y               ; Save the measurement in X<\/code><\/pre>\n<h2 class=\"wp-block-heading\">Lessons from Humpty Dumpty\u2019s conditionals<\/h2>\n<p class=\"wp-block-paragraph\">In <em>Through the Looking-Glass<\/em>, Alice learns to navigate Humpty Dumpty\u2019s peculiar world\u200a\u2014\u200ajust as you\u2019ll learn to navigate PIO\u2019s Wonderland of lopsided conditions.<\/p>\n<p class=\"wp-block-paragraph\">But as soon as you master one quirk, another reveals itself. In the next Wat, we\u2019ll uncover a surprising behavior of jmp that, if it were an athlete, would shatter world records.<\/p>\n<h1 class=\"wp-block-heading\">Wat 7: Overshooting jumps<\/h1>\n<p class=\"wp-block-paragraph\">In <a href=\"https:\/\/towardsdatascience.com\/nine-pico-pio-wats-with-rust-part-1-9d062067dc25\/\">Part 1<\/a>\u2019s <strong>Wat 1<\/strong> and <strong>Wat 3<\/strong>, we saw how <code>jmp x--<\/code> or <code>jmp y--<\/code> is often used to loop a fixed number of times by decrementing a register until it reaches 0. Straightforward enough, right? But what happens when y is 0 and we run the following instruction?<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">jmp y-- measure_echo_loop<\/code><\/pre>\n<p class=\"wp-block-paragraph\">If you guessed that it does <strong>not<\/strong> jump to <code>measure_echo_loop<\/code> and instead falls through to the next instruction, you\u2019re absolutely correct. But for full credit, answer this: <strong>What value does y have after the instruction?<\/strong><\/p>\n<p class=\"wp-block-paragraph\">The answer: <strong>4,294,967,295.<\/strong> Why? Because y is decremented <strong>after<\/strong> it is tested for zero. <em>Wat!?<\/em><\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Aside: If this doesn\u2019t surprise you, you likely have experience with C or C++ which distinguish between pre-increment (e.g., <code>++x<\/code>) and post-increment (e.g., x++) operations. The behavior of <code>jmp y--<\/code> is equivalent to a post-decrement, where the value is tested <em>before<\/em> being decremented.<\/p>\n<\/blockquote>\n<p class=\"wp-block-paragraph\">This value, 4,294,967,295, is the maximum for a 32-bit unsigned integer. It\u2019s as if a track-and-field long jumper launches off the takeoff board but, instead of landing in the sandpit, overshoots and ends up on another continent.<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Aside:<strong> <\/strong>As foreshadowed in <strong>Wat 5<\/strong>, we can use this behavior intentionally to set a register to the value 4,294,967,295.<\/p>\n<\/blockquote>\n<p class=\"wp-block-paragraph\">Now that we\u2019ve learned how to stick the landing with <code>jmp<\/code>, let\u2019s see if we can avoid getting stuck by the pins that PIO reads and sets.<\/p>\n<h1 class=\"wp-block-heading\">Wat 8: Too many \u201cpins\u201d<\/h1>\n<p class=\"wp-block-paragraph\">In Dr. Seuss\u2019s <em>Too Many Daves<\/em>, Mrs. McCave had 23 sons, all named Dave, leading to endless confusion whenever she called out their name. In PIO programming, <code>pin<\/code> and <code>pins<\/code> can refer to completely different ranges of pins depending on the context. It\u2019s hard to know which Dave or Daves you\u2019re talking to.<\/p>\n<h2 class=\"wp-block-heading\">The problem: Pin ranges and subranges<\/h2>\n<p class=\"wp-block-paragraph\">In PIO, both <code>pin<\/code> and <code>pins<\/code> instructions depend on <strong>pin ranges<\/strong> defined in Rust, outside of PIO. However, individual instructions often operate on a <strong>subrange<\/strong> of those pin ranges. The behavior varies depending on the command: the subrange could be the first <em>n<\/em> pins of the range, all the pins, or just a specific pin given by an index. To clarify PIO\u2019s behavior, I created the following table:<\/p>\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/03\/Pico-1.png?ssl=1\" alt=\"\" class=\"wp-image-599656\"><\/figure>\n<p class=\"wp-block-paragraph\">This table shows how PIO interprets the terms <code>pin<\/code> and <code>pins<\/code> in different instructions, along with their associated contexts and configurations.<\/p>\n<h2 class=\"wp-block-heading\">Example: Distance program for the range finder<\/h2>\n<p class=\"wp-block-paragraph\">Here\u2019s a PIO program for measuring the distance to an object using <strong>Trigger<\/strong> and <strong>Echo<\/strong> pins. The key features of this program are:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<strong>Continuous Operation<\/strong>: The range finder runs in a loop as fast as possible.<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Maximum Range Limit<\/strong>: Measurements are capped at a given distance, with a return value of <code>4,294,967,295<\/code> if no object is detected.<\/li>\n<li class=\"wp-block-list-item\">\n<strong>Filtered Outputs<\/strong>: Only measurements that differ from their immediate predecessor are sent, reducing the output rate.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">Glance over the program and notice that although it is working with two pins\u200a\u2014\u200a<strong>Trigger<\/strong> and <strong>Echo<\/strong>\u200a\u2014\u200athroughout the program we only see <code>pin<\/code> and <code>pins<\/code>.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">.program distance\n\n; X is the last value sent. Initialize it to\n; u32::MAX which means 'echo timeout'\n; (Set X to u32::MAX by subtracting 1 from 0)\n   set x, 0\nsubtraction_trick:\n   jmp x-- subtraction_trick\n\n; Read the max echo wait into OSR\n   pull                         ; same as pull block\n\n; Main loop\n.wrap_target\n   ; Generate 10\u03bcs trigger pulse (4 cycles at 343_000Hz)\n   set pins, 0b1 [3]       ; Set trigger pin to high, add delay of 3\n   set pins, 0b0           ; Set trigger pin to low voltage\n\n   ; When the trigger goes high, start counting down until it goes low\n   wait 1 pin 0            ; Wait for echo pin to be high voltage\n   mov y, osr              ; Load max echo wait into Y\n\nmeasure_echo_loop:\n   jmp pin echo_active     ; if echo voltage is high continue count down\n   jmp measurement_complete ; if echo voltage is low, measurement is complete\necho_active:\n   jmp y-- measure_echo_loop ; Continue counting down unless timeout\n\n; Y tells where the echo countdown stopped. It\n; will be u32::MAX if the echo timed out.\nmeasurement_complete:\n   jmp x!=y send_result    ; if measurement is different, then sent it.\n   jmp cooldown            ; If measurement is the same, don't send.\n\nsend_result:\n   mov isr, y              ; Store measurement in ISR\n   push                    ; Output ISR\n   mov x, y               ; Save the measurement in X\n\n; Cool down period before next measurement\ncooldown:\n   wait 0 pin 0           ; Wait for echo pin to be low\n.wrap                      ; Restart the measurement loop<\/code><\/pre>\n<h4 class=\"wp-block-heading\"><strong>Configuring Pins<\/strong><\/h4>\n<p class=\"wp-block-paragraph\">To ensure the PIO program behaves as intended:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<code>set pins, 0b1<\/code> should control the <strong>Trigger<\/strong> pin.<\/li>\n<li class=\"wp-block-list-item\">\n<code>wait 1 pin 0<\/code> should monitor the <strong>Echo<\/strong> pin.<\/li>\n<li class=\"wp-block-list-item\">\n<code>jmp pin echo_active<\/code> should also monitor the <strong>Echo<\/strong> pin.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">Here\u2019s how you can configure this in Rust (followed by an explanation):<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">let mut distance_state_machine = pio1.sm0;\nlet trigger_pio = pio1.common.make_pio_pin(hardware.trigger);\nlet echo_pio = pio1.common.make_pio_pin(hardware.echo);\ndistance_state_machine.set_pin_dirs(Direction::Out, &amp;[&amp;trigger_pio]);\ndistance_state_machine.set_pin_dirs(Direction::In, &amp;[&amp;echo_pio]);\ndistance_state_machine.set_config(&amp;{\n   let mut config = Config::default();\n   config.set_set_pins(&amp;[&amp;trigger_pio]); \/\/ For set instruction\n   config.set_in_pins(&amp;[&amp;echo_pio]); \/\/ For wait instruction\n   config.set_jmp_pin(&amp;echo_pio); \/\/ For jmp instruction\n   let program_with_defines = pio_file!(\"examples\/distance.pio\");\n   let program = pio1.common.load_program(&amp;program_with_defines.program);\n   config.use_program(&amp;program, &amp;[]); \/\/ No side-set pins\n   config\n});<\/code><\/pre>\n<p class=\"wp-block-paragraph\">The keys here are the <code>&lt;strong&gt;set_set_pins&lt;\/strong&gt;<\/code>, <code>&lt;strong&gt;set_in_pins&lt;\/strong&gt;<\/code>, and <strong><code>set_jmp_pin<\/code> <\/strong>methods on the Config struct.<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<code>set_in_pins<\/code>: Specifies the pins for <strong>input operations<\/strong>, such as wait(1, pin, \u2026). The \u201cin\u201d pins must be consecutive.<\/li>\n<li class=\"wp-block-list-item\">\n<code>set_set_pins<\/code>: Configures the pin for <strong>set operations<\/strong>, like set(pins, 1). The \u201cset\u201d pins must also be consecutive.<\/li>\n<li class=\"wp-block-list-item\">\n<code>set_jmp_pin<\/code>: Defines the single pin used in <strong>conditional jumps<\/strong>, such as <code>jmp(pin, ...)<\/code>.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">As described in the table, other optional inputs include:<\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\">\n<code>set_out_pins<\/code>: Sets the consecutive pins for <strong>output operations<\/strong>, such as out(pins, \u2026).<\/li>\n<li class=\"wp-block-list-item\">\n<code>use_program<\/code>: Sets a) the loaded program and b) consecutive pins for <strong>sideset operations. <\/strong>Sideset operations allow simultaneous pin toggling during other instructions.<\/li>\n<\/ul>\n<h4 class=\"wp-block-heading\"><strong>Configuring Multiple Pins<\/strong><\/h4>\n<p class=\"wp-block-paragraph\">Although not required for this program, you can configure a range of pins in PIO by providing a slice of consecutive pins. For example, suppose we had two ultrasonic range finders:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">let trigger_a_pio = pio1.common.make_pio_pin(hardware.trigger_a);\nlet trigger_b_pio = pio1.common.make_pio_pin(hardware.trigger_b);\nconfig.set_set_pins(&amp;[&amp;trigger_a_pio, &amp;trigger_b_pio]);<\/code><\/pre>\n<p class=\"wp-block-paragraph\">A single instruction can then control both pins:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">set pins, 0b11 [3]  # Sets both trigger pins (17, 18) high, adds delay\nset pins, 0b00      # Sets both trigger pins low<\/code><\/pre>\n<p class=\"wp-block-paragraph\">This approach lets you efficiently apply bit patterns to multiple pins simultaneously, streamlining control for applications involving multiple outputs.<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong><em>Aside: The Word \u201cSet\u201d in Programming<\/em><\/strong><\/p>\n<p class=\"wp-block-paragraph\"><em>In programming, the word \u201cset\u201d is notoriously overloaded with multiple meanings. In the context of PIO, \u201cset\u201d refers to something to which you can assign a value\u200a\u2014\u200asuch as a pin\u2019s state. It does <\/em><strong><em>not<\/em><\/strong><em> mean a collection of things, as it often does in other programming contexts. When PIO refers to a collection, it usually uses the term \u201crange\u201d instead. This distinction is crucial for avoiding confusion as you work with PIO.<\/em><\/p>\n<\/blockquote>\n<h2 class=\"wp-block-heading\">Lessons from Mrs. McCave<\/h2>\n<p class=\"wp-block-paragraph\">In <em>Too Many Daves<\/em>, Mrs. McCave lamented not giving her 23 Daves more distinct names. You can avoid her mistake by clearly documenting your pins with meaningful names\u200a\u2014\u200alike <strong>Trigger<\/strong> and <strong>Echo<\/strong>\u200a\u2014\u200ain your comments.<\/p>\n<p class=\"wp-block-paragraph\">But if you think handling these pin ranges is tricky, debugging a PIO program adds an entirely new layer of challenge. In the next Wat, we\u2019ll dive into the kludgy debugging methods available. Let\u2019s see just how far we can push them.<\/p>\n<h1 class=\"wp-block-heading\">Wat 9: Kludgy debugging<\/h1>\n<p class=\"wp-block-paragraph\">I like to debug with interactive breakpoints in VS Code. I also do <strong>print debugging<\/strong>, where you insert temporary info statements to see what the code is doing and the values of variables. Using the <a href=\"https:\/\/www.raspberrypi.com\/documentation\/microcontrollers\/debug-probe.html\"><strong>Raspberry Pi Debug Probe<\/strong><\/a> and <a href=\"https:\/\/probe.rs\/\"><strong>probe-rs<\/strong><\/a>, I can do both of these with regular Rust code on the Pico.<\/p>\n<p class=\"wp-block-paragraph\">With PIO programming, however, I can do neither.<\/p>\n<p class=\"wp-block-paragraph\">The fallback is <strong>push-to-print debugging<\/strong>. In PIO, you temporarily output integer values of interest. Then, in Rust, you use <code>info!<\/code> to print those values for inspection.<\/p>\n<p class=\"wp-block-paragraph\">For example, in the following PIO program, we temporarily add instructions to push the value of <code>x<\/code> for debugging. We also include <code>set<\/code> and <code>out<\/code> to push a constant value, such as 7, which must be between 0 and 31 inclusive.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">.program distance\n\n; X is the last value sent. Initialize it to\n; u32::MAX which means 'echo timeout'\n; (Set X to u32::MAX by subtracting 1 from 0)\n   set x, 0\nsubtraction_trick:\n   jmp x-- subtraction_trick\n\n; DEBUG: See the value of x\n   mov isr, x\n   push\n\n; Read the max echo wait into OSR\n   pull                         ; same as pull block\n\n; DEBUG: Send constant value\n   set y, 7           ; Push '7' so that we know we've reached this point\n   mov isr, y\n   push\n; ...<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Back in Rust, you can read and print these values to help understand what\u2019s happening in the PIO code (<a href=\"https:\/\/github.com\/CarlKCarlK\/pico_pio\/blob\/main\/examples\/distance_debug.rs\">full code<\/a> and <a href=\"https:\/\/github.com\/CarlKCarlK\/pico_pio\/\">project<\/a>):<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">  \/\/ ...\n   distance_state_machine.set_enable(true);\n   distance_state_machine.tx().wait_push(MAX_LOOPS).await;\n   loop {\n       let end_loops = distance_state_machine.rx().wait_pull().await;\n       info!(\"end_loops: {}\", end_loops);\n   }\n  \/\/ ...\n<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Outputs:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\">INFO  Hello, debug!\n\u2514\u2500 distance_debug::inner_main::{async_fn#0} @ examplesdistance_debug.rs:27\nINFO  end_loops: 4294967295\n\u2514\u2500 distance_debug::inner_main::{async_fn#0} @ examplesdistance_debug.rs:57\nINFO  end_loops: 7\n\u2514\u2500 distance_debug::inner_main::{async_fn#0} @ examplesdistance_debug.rs:57<\/code><\/pre>\n<p class=\"wp-block-paragraph\">When push-to-print debugging isn\u2019t enough, you can turn to hardware tools. I bought my first oscilloscope (a <strong>FNIRSI DSO152<\/strong>, for $37). With it, I was able to confirm the <strong>Echo<\/strong> signal was working. The <strong>Trigger<\/strong> signal, however, was too fast for this inexpensive oscilloscope to capture clearly.<\/p>\n<p class=\"wp-block-paragraph\">Using these methods\u200a\u2014\u200aespecially push-to-print debugging\u200a\u2014\u200ayou can trace the flow of your PIO program, even without a traditional debugger.<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><em>Aside<\/em><strong><em>:<\/em><\/strong><em> In C\/C++ (and potentially Rust), you can get closer to a full debugging experience for PIO, for example, by using the <\/em><a href=\"https:\/\/github.com\/PaulAccisano\/piodebug\"><strong><em>piodebug<\/em><\/strong><em> project<\/em><\/a><em>.<\/em><\/p>\n<\/blockquote>\n<p class=\"wp-block-paragraph\">That concludes the nine Wats, but let\u2019s bring everything together in a bonus Wat.<\/p>\n<h1 class=\"wp-block-heading\">Bonus Wat 10: Putting it all together<\/h1>\n<p class=\"wp-block-paragraph\">Now that all the components are ready, it\u2019s time to combine them into a working theremin-like musical instrument. We need a Rust monitor program. This program starts both PIO state machines\u200a\u2014\u200aone for measuring distance and the other for generating tones. It then waits for a new distance measurement, maps that distance to a tone, and sends the corresponding tone frequency to the tone-playing state machine. If the distance is out of range, it stops the tone.<\/p>\n<p class=\"wp-block-paragraph\"><strong>Rust\u2019s Place<\/strong>: At the heart of this system is a function that maps distances (from 0 to 50 cm) to tones (approximately <strong>B2<\/strong> to <strong>F5<\/strong>). This function is simple to write in Rust, leveraging Rust\u2019s floating-point math and exponential operations. Implementing this in PIO would be virtually impossible due to its limited instruction set and lack of floating-point support.<\/p>\n<p class=\"wp-block-paragraph\">Here\u2019s the core monitor program to run the theremin (<a href=\"https:\/\/github.com\/CarlKCarlK\/pico_pio\/blob\/main\/examples\/theremin.rs\">full file<\/a> and<a href=\"https:\/\/github.com\/CarlKCarlK\/pico_pio\/\"> project<\/a>):<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">sound_state_machine.set_enable(true);\ndistance_state_machine.set_enable(true);\ndistance_state_machine.tx().wait_push(MAX_LOOPS).await;\nloop {\n   let end_loops = distance_state_machine.rx().wait_pull().await;\n   match loop_difference_to_distance_cm(end_loops) {\n       None =&gt; {\n           info!(\"Distance: out of range\");\n           sound_state_machine.tx().wait_push(0).await;\n       }\n       Some(distance_cm) =&gt; {\n           let tone_frequency = distance_to_tone_frequency(distance_cm);\n           let half_period = sound_state_machine_frequency \/ tone_frequency as u32 \/ 2;\n           info!(\"Distance: {} cm, tone: {} Hz\", distance_cm, tone_frequency);\n           sound_state_machine.tx().push(half_period); \/\/ non-blocking push\n           Timer::after(Duration::from_millis(50)).await;\n       }\n   }\n}<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Using two PIO state machines alongside a Rust monitor program lets you literally run three programs at once. This setup is convenient on its own and is essential when strict timing or very high-frequency I\/O operations are required.<\/p>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong><em>Aside:<\/em><\/strong><em> Alternatively, Rust Embassy\u2019s async tasks let you implement cooperative multitasking directly on a single main processor. You code in Rust rather than a mixture of Rust and PIO. Although Embassy tasks don\u2019t literally run in parallel, they switch quickly enough to handle applications like a theremin. Here\u2019s a snippet from <\/em><a href=\"https:\/\/github.com\/CarlKCarlK\/pico_pio\/blob\/main\/examples\/theremin_no_pio.rs\"><em>theremin_no_pio.rs<\/em><\/a><em> showing a similar core loop:<\/em><\/p>\n<\/blockquote>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-rust\">loop {\n       match distance.measure().await {\n           None =&gt; {\n               info!(\"Distance: out of range\");\n               sound.rest().await;\n           }\n           Some(distance_cm) =&gt; {\n               let tone_frequency = distance_to_tone_frequency(distance_cm);\n               info!(\"Distance: {} cm, tone: {} Hz\", distance_cm, tone_frequency);\n               sound.play(tone_frequency).await;\n               Timer::after(Duration::from_millis(50)).await;\n           }\n       }\n   }<\/code><\/pre>\n<p class=\"wp-block-paragraph\"><em>See our <\/em><a href=\"https:\/\/medium.com\/@carlmkadie\/how-rust-embassy-shine-on-embedded-devices-part-1-9f4911c92007\"><em>recent article on Rust Embassy programming<\/em><\/a><em> for more details.<\/em><\/p>\n<p class=\"wp-block-paragraph\">Now that we\u2019ve assembled all the components, let\u2019s watch the video again of me \u201cplaying\u201d the musical instrument. On the monitor screen, you can see the debugging prints displaying the distance measurements and the corresponding tones. This visual connection highlights how the system responds in real time.<\/p>\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\">\n<div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"rust theremin\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/lS6P9WNzuOY?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div>\n<\/figure>\n<h3 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h3>\n<p class=\"wp-block-paragraph\">PIO programming on the <a href=\"https:\/\/towardsdatascience.com\/tag\/raspberry-pi\/\" title=\"Raspberry Pi\">Raspberry Pi<\/a> Pico is a captivating blend of simplicity and complexity, offering unparalleled hardware control while demanding a shift in mindset for developers accustomed to higher-level programming. Through the nine Wats we\u2019ve explored, PIO has both surprised us with its limitations and impressed us with its raw efficiency.<\/p>\n<p class=\"wp-block-paragraph\">While we\u2019ve covered significant ground\u200a\u2014\u200amanaging state machines, pin assignments, timing intricacies, and debugging\u200a\u2014\u200athere\u2019s still much more you can learn as needed: DMA, IRQ, side-set pins, differences between PIO on the Pico 1 and Pico 2, autopush and autopull, FIFO join, and more.<\/p>\n<p class=\"wp-block-paragraph\"><strong>Recommended Resources<\/strong><\/p>\n<ul class=\"wp-block-list\">\n<li class=\"wp-block-list-item\"><a href=\"https:\/\/github.com\/CarlKCarlK\/pico_pio\/\">Code for this project on GitHub<\/a><\/li>\n<li class=\"wp-block-list-item\">\n<a href=\"https:\/\/datasheets.raspberrypi.com\/pico\/raspberry-pi-pico-c-sdk.pdf\">The Pico SDK manual<\/a>, Chapter 3<\/li>\n<li class=\"wp-block-list-item\"><a href=\"https:\/\/docs.embassy.dev\/embassy-rp\/git\/rp2040\/pio\/index.html\">Embassy PIO API Documentation<\/a><\/li>\n<li class=\"wp-block-list-item\">The<a href=\"https:\/\/datasheets.raspberrypi.com\/rp2040\/rp2040-datasheet.pdf\"> RP2040<\/a> (Pico 1) and<a href=\"https:\/\/datasheets.raspberrypi.com\/rp2350\/rp2350-datasheet.pdf\"> RP2350<\/a> (Pico 2) datasheets<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">At its core, PIO\u2019s quirks reflect a design philosophy that prioritizes low-level hardware control with minimal overhead. By embracing these characteristics, PIO will not only meet your project\u2019s demands but also open doors to new possibilities in embedded systems programming.<\/p>\n<p class=\"wp-block-paragraph\"><em>Please<\/em><a href=\"https:\/\/towardsdatascience.com\/author\/carlmkadie\/\"><em> follow Carl on Towards Data Science<\/em><\/a><em> and on <\/em><a href=\"https:\/\/bsky.app\/profile\/carlkadie.bsky.social\">@carlkadie.bsky.social<\/a><em>. I write on scientific programming in <a href=\"https:\/\/towardsdatascience.com\/tag\/rust\/\" title=\"Rust\">Rust<\/a> and Python, machine learning, and statistics. I tend to write about one article per month.<\/em><\/p>\n<p class=\"wp-block-paragraph\">\n<p>The post <a href=\"https:\/\/towardsdatascience.com\/nine-pico-pio-wats-with-rust-part-2\/\">Nine Pico PIO Wats with Rust (Part 2)<\/a> appeared first on <a href=\"https:\/\/towardsdatascience.com\/\">Towards Data Science<\/a>.<\/p>\n<\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><br \/>\n    Carl M. Kadie<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/towardsdatascience.com\/nine-pico-pio-wats-with-rust-part-2\/\">Go to original source<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nine Pico PIO Wats with Rust (Part 2) This is Part 2 of an exploration into the unexpected quirks of programming the Raspberry Pi Pico PIO with Micropython. If you missed Part 1, we uncovered four Wats that challenge assumptions about register count, instruction slots, the behavior of pull noblock, and smart yet cheap hardware. [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[62,67,1263,160,2042,1870,302],"tags":[2044,2043,883],"class_list":["post-2434","post","type-post","status-publish","format-standard","hentry","category-aimldsaimlds","category-deep-dives","category-micropython","category-programming","category-raspberry-pi","category-rust","category-software-engineering","tag-constants","tag-pio","tag-set"],"_links":{"self":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/2434"}],"collection":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/comments?post=2434"}],"version-history":[{"count":0,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/2434\/revisions"}],"wp:attachment":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/media?parent=2434"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/categories?post=2434"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/tags?post=2434"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}