<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://blog.implrust.com/feed_style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <tabi:metadata xmlns:tabi="https://github.com/welpo/tabi">
        <tabi:base_url>https:&#x2F;&#x2F;blog.implrust.com</tabi:base_url>
        <tabi:separator>
            •
        </tabi:separator>
        <tabi:about_feeds>This is a web feed, also known as an Atom feed. Subscribe by copying the URL from the address bar into your newsreader. Visit About Feeds to learn more and get started. It&#x27;s free.</tabi:about_feeds>
        <tabi:visit_the_site>Visit website</tabi:visit_the_site>
        <tabi:recent_posts>Recent posts</tabi:recent_posts>
        <tabi:last_updated_on>Updated on $DATE</tabi:last_updated_on>
        <tabi:default_theme></tabi:default_theme>
        <tabi:post_listing_date>date</tabi:post_listing_date>
        <tabi:current_section>pio</tabi:current_section>
    </tabi:metadata><link rel="extra-stylesheet" href="https://blog.implrust.com/skins/lowcontrast_orange.css?h=43aaccb17d8ec616ace4" /><title>impl Blog for Rust - pio</title>
        <subtitle>impl Rust Blog Posts</subtitle>
    <link href="https://blog.implrust.com/tags/pio/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://blog.implrust.com/tags/pio/" rel="alternate" type="text/html"/>
    <generator uri="https://www.getzola.org/">Zola</generator><updated>2026-02-08T00:00:00+00:00</updated><id>https://blog.implrust.com/tags/pio/atom.xml</id><entry xml:lang="en">
        <title>Analyzing PIO Blink Using Logic Analyzer on Raspberry Pi Pico with Embedded Rust</title>
        <published>2026-02-08T00:00:00+00:00</published>
        <updated>2026-02-08T00:00:00+00:00</updated>
        <author>
            <name>implFerris</name>
        </author>
        <link rel="alternate" href="https://blog.implrust.com/posts/2026/02/logic-analyzer-raspberry-pi-pico-pio/" type="text/html"/>
        <id>https://blog.implrust.com/posts/2026/02/logic-analyzer-raspberry-pi-pico-pio/</id>
        
            <content type="html">&lt;p&gt;In earlier posts, we used a logic analyzer to inspect simple GPIO signals and UART communication. In this post, we will use the logic analyzer to inspect a signal generated by PIO, which is specific to the Raspberry Pi Pico variants (RP2040 and RP2350 chips).&lt;&#x2F;p&gt;
&lt;p&gt;If you do not know what PIO is, I recommend checking the introduction in the &lt;a href=&quot;https:&#x2F;&#x2F;rp2040.implrust.com&#x2F;pio&#x2F;index.html&quot;&gt;“impl Rust for RP2040”&lt;&#x2F;a&gt; book. I will still give a short overview here so that we are all on the same page before looking at logic analyzer captures.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pio&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#pio&quot; aria-label=&quot;Anchor link for: pio&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
PIO&lt;&#x2F;h2&gt;
&lt;p&gt;PIO, short for Programmable I&#x2F;O, is a hardware block available on RP2040 and RP2350 microcontrollers. It allows us to write small programs that run independently of the main CPU and directly control GPIO pins.&lt;&#x2F;p&gt;
&lt;p&gt;Each PIO block contains a few state machines. A state machine executes instructions from a small instruction memory, one instruction at a time, and can drive pins, sample pins, and move data in and out through FIFOs.&lt;&#x2F;p&gt;
&lt;p&gt;The important part for this post is that PIO timing is instruction-driven. Unlike CPU code, where timing depends on compiler output, interrupts, and scheduling, PIO timing is deterministic. Each instruction takes a known number of cycles to execute.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pio-clock-divider&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#pio-clock-divider&quot; aria-label=&quot;Anchor link for: pio-clock-divider&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
PIO Clock Divider&lt;&#x2F;h3&gt;
&lt;p&gt;Each PIO state machine has its own clock divider. This divider controls how fast instructions are executed.&lt;&#x2F;p&gt;
&lt;p&gt;On RP2040, the PIO instruction clock is derived from the system clock like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;state_machine_clock&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; = clk_sys &#x2F; clock_divider&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In our setup:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;System clock = 125 MHz&lt;&#x2F;li&gt;
&lt;li&gt;PIO clock divider = 65535&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So the instruction execution rate becomes:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;125,000,000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &#x2F; 65,535 ≈ 1,907.38 instructions per second&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This means the PIO state machine executes roughly 1.9 thousand instructions per second.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;instruction-cycles-and-delays&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#instruction-cycles-and-delays&quot; aria-label=&quot;Anchor link for: instruction-cycles-and-delays&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Instruction Cycles and Delays&lt;&#x2F;h3&gt;
&lt;p&gt;By default, every PIO instruction takes exactly one instruction cycle to execute.&lt;&#x2F;p&gt;
&lt;p&gt;PIO also supports an instruction delay modifier, written using square brackets. For example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-set z-shell&quot;&gt;set&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; pins, 1 &lt;span class=&quot;z-keyword z-control z-regexp z-set z-begin z-shell&quot;&gt;[&lt;&#x2F;span&gt;31&lt;span class=&quot;z-keyword z-control z-regexp z-set z-end z-shell&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This means:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;1 cycle for the instruction itself&lt;&#x2F;li&gt;
&lt;li&gt;31 additional delay cycles&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So the total execution time for that instruction is 32 instruction cycles.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;time-per-instruction&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#time-per-instruction&quot; aria-label=&quot;Anchor link for: time-per-instruction&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Time Per Instruction&lt;&#x2F;h2&gt;
&lt;p&gt;Since we know the instruction execution rate, we can compute the time taken by one instruction cycle.&lt;&#x2F;p&gt;
&lt;p&gt;With a clock divider of 65535:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; &#x2F; 1,907.38 s ≈ 0.0005243 s&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That means approximately:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;0.524 ms (524,300 nanoseconds) per instruction cycle&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;project-source-code&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#project-source-code&quot; aria-label=&quot;Anchor link for: project-source-code&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Project Source Code&lt;&#x2F;h2&gt;
&lt;p&gt;You can clone the project used in this post from the following repository:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; clone https:&#x2F;&#x2F;github.com&#x2F;ImplFerris&#x2F;rp2040-projects&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-support z-function z-cd z-shell&quot;&gt;cd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; rp2040-projects&#x2F;embassy&#x2F;pio&#x2F;hello-blinky&#x2F;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This project contains the exact PIO program and Embassy setup used for the logic analyzer measurements shown in this post.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pio-program&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#pio-program&quot; aria-label=&quot;Anchor link for: pio-program&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Pio Program&lt;&#x2F;h3&gt;
&lt;p&gt;This is the PIO assembly program used to generate the blinking signal that we will analyze with the logic analyzer.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; prg &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-macro z-rust&quot;&gt;pio_asm!&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;    &lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-rust&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;    set pindirs, 1
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;    loop:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        set pins, 1 [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        set pins, 0 [30]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        nop [31]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;        jmp loop
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;    &lt;span class=&quot;z-punctuation z-definition z-string z-end z-rust&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This program runs in an infinite loop and drives a single GPIO pin HIGH and LOW.&lt;&#x2F;p&gt;
&lt;p&gt;The delays are added using the instruction delay modifier. Each instruction with [31] takes a total of 32 PIO cycles to execute. The one place where [30] is used is intentional. The jmp loop instruction takes one extra cycle while the pin is LOW, so reducing the delay on &lt;code&gt;set pins, 0&lt;&#x2F;code&gt; keeps the HIGH and LOW durations equal.&lt;&#x2F;p&gt;
&lt;p&gt;Because the program timing is entirely instruction-based, we can predict the exact HIGH time, LOW time, and output frequency before even connecting the logic analyzer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hardware-setup&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#hardware-setup&quot; aria-label=&quot;Anchor link for: hardware-setup&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Hardware Setup&lt;&#x2F;h2&gt;
&lt;p&gt;The hardware setup is very simple. We connect the logic analyzer to the same GPIO pin that drives the LED, so we can observe exactly what the LED sees.&lt;&#x2F;p&gt;
&lt;p&gt;The external LED is connected to GPIO15 through a resistor. The logic analyzer channel is connected in parallel to the same GPIO pin.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Source&lt;&#x2F;th&gt;&lt;th&gt;Connected To&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;GPIO15&lt;&#x2F;td&gt;&lt;td&gt;Resistor =&amp;gt; LED =&amp;gt; GND&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Logic Analyzer CH1&lt;&#x2F;td&gt;&lt;td&gt;GPIO15&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Logic Analyzer GND&lt;&#x2F;td&gt;&lt;td&gt;Pico GND&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;With this setup, the GPIO pin drives both the LED and the logic analyzer input at the same time. The LED gives us a visual confirmation of the blinking, while the logic analyzer lets us inspect the exact timing and waveform.&lt;&#x2F;p&gt;
&lt;p&gt;Make sure the logic analyzer ground and the Pico ground are connected. Without a common ground, the captured signal will be unreliable.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;start-the-capture&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#start-the-capture&quot; aria-label=&quot;Anchor link for: start-the-capture&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Start the Capture&lt;&#x2F;h2&gt;
&lt;p&gt;Open PulseView as usual. For this capture, I have configured the sample rate to 20 kHz and the number of samples to 1 million.&lt;&#x2F;p&gt;
&lt;p&gt;Once the settings are in place, click Run to start the capture. PulseView will record the signal and automatically stop once it reaches the configured number of samples.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;measuring-timing-with-cursors-in-pulseview&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#measuring-timing-with-cursors-in-pulseview&quot; aria-label=&quot;Anchor link for: measuring-timing-with-cursors-in-pulseview&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Measuring Timing with Cursors in PulseView&lt;&#x2F;h2&gt;
&lt;p&gt;Up to this point, we have been looking at captured waveforms mainly to understand their overall shape and behavior. To measure timing more precisely, PulseView provides a cursor feature that is very useful.&lt;&#x2F;p&gt;
&lt;p&gt;When you enable &lt;code&gt;Show Cursors&lt;&#x2F;code&gt; in PulseView, two movable time markers appear on the waveform. Each cursor represents a specific point in time on the capture. By placing the cursors on two edges or points of interest, PulseView shows the exact time difference between them.&lt;&#x2F;p&gt;
&lt;p&gt;This makes it easy to measure how long a signal stays HIGH, how long it stays LOW, or the full period of a repeating waveform.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;verifying-the-timing-on-the-logic-analyzer&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#verifying-the-timing-on-the-logic-analyzer&quot; aria-label=&quot;Anchor link for: verifying-the-timing-on-the-logic-analyzer&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Verifying the Timing on the Logic Analyzer&lt;&#x2F;h2&gt;
&lt;p&gt;With the capture complete, we can now use the cursors to measure the actual timing of the signal generated by PIO.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;img&#x2F;2026&#x2F;02&#x2F;logic-analyzer-show-cursors-pio.png&quot;  alt=&quot;logic-analyzer-show-cursors-pio&quot; style=&quot;width:400px; height:auto; display:block; margin:auto;&quot;&#x2F;&gt;
&lt;p&gt;In the capture above, the cursors are placed across one full HIGH-LOW cycle of the waveform. PulseView directly shows the measured period as roughly 268 ms, which corresponds to a frequency of about 3.7 Hz.&lt;&#x2F;p&gt;
&lt;p&gt;To understand why the measured period comes out to around 268 ms, let us now work through the timing of the PIO program step by step.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;high-phase-timing&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#high-phase-timing&quot; aria-label=&quot;Anchor link for: high-phase-timing&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
High Phase Timing&lt;&#x2F;h3&gt;
&lt;p&gt;The pin goes HIGH when the &lt;code&gt;set pins, 1&lt;&#x2F;code&gt; instruction executes and stays HIGH until &lt;code&gt;set pins, 0&lt;&#x2F;code&gt; executes.&lt;&#x2F;p&gt;
&lt;p&gt;While the pin is HIGH, the following instructions run:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;set pins, 1 [31]&lt;&#x2F;code&gt;&lt;br &#x2F;&gt;
The instruction itself takes 1 cycle, and the delay adds 31 more cycles.&lt;br &#x2F;&gt;
Total: 32 cycles&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;nop [31]&lt;&#x2F;code&gt; repeated 7 times&lt;br &#x2F;&gt;
Each &lt;code&gt;nop [31]&lt;&#x2F;code&gt; takes 32 cycles.&lt;br &#x2F;&gt;
Total: 7 x 32 = 224 cycles&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So the total number of instruction cycles while the pin is HIGH is:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;32&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; + 224 = 256 cycles&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With each instruction cycle taking about 0.524 ms, the total HIGH time becomes:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;256&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; x 0.524 ms ≈ 134 ms&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;low-phase-timing&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#low-phase-timing&quot; aria-label=&quot;Anchor link for: low-phase-timing&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Low Phase Timing&lt;&#x2F;h3&gt;
&lt;p&gt;The LOW phase starts when &lt;code&gt;set pins, 0&lt;&#x2F;code&gt; executes and lasts until the program loops back and sets the pin HIGH again.&lt;&#x2F;p&gt;
&lt;p&gt;While the pin is LOW, the following instructions run:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;set pins, 0 [30]&lt;&#x2F;code&gt;&lt;br &#x2F;&gt;
The instruction itself takes 1 cycle, and the delay adds 30 more cycles.&lt;br &#x2F;&gt;
Total: 31 cycles&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;nop [31]&lt;&#x2F;code&gt; repeated 7 times&lt;br &#x2F;&gt;
Total: 7 x 32 = 224 cycles&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;jmp loop&lt;&#x2F;code&gt;&lt;br &#x2F;&gt;
This instruction takes 1 cycle&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So the total number of instruction cycles while the pin is LOW is:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;31&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; + 224 + 1 = 256 cycles&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This again gives:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh z-code&quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;z-source z-shell z-bash&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-shell&quot;&gt;&lt;span class=&quot;z-variable z-function z-shell&quot;&gt;256&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-arguments z-shell&quot;&gt; x 0.524 ms ≈ 134 ms&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;putting-it-all-together&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#putting-it-all-together&quot; aria-label=&quot;Anchor link for: putting-it-all-together&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Putting It All Together&lt;&#x2F;h3&gt;
&lt;p&gt;From the instruction count, we end up with:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;HIGH time ≈ 134 ms&lt;&#x2F;li&gt;
&lt;li&gt;LOW time ≈ 134 ms&lt;&#x2F;li&gt;
&lt;li&gt;Full period ≈ 268 ms&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This matches approximately what we measured using the logic analyzer.&lt;&#x2F;p&gt;
</content>
        <summary type="html">In this post, we use a logic analyzer to study a GPIO signal generated by a PIO state machine on the RP2040. We derive the timing from instruction cycles and validate it on real hardware with Embedded Rust.</summary>
        </entry>
</feed>
