lupyuen.org/articles/optimising-pinetimes-display-driver-with-rust-and-mynewt.html

1277 lines
No EOL
86 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="/_static/css/banner-styles.css?v=bsmaklHF" />
<link rel="stylesheet" type="text/css" href="/_static/css/iconochive.css?v=qtvMKcIJ" />
<link rel="canonical" href="https://lupyuen.org/articles/optimising-pinetimes-display-driver-with-rust-and-mynewt.html" />
<!-- End Wayback Rewrite JS Include -->
<title data-rh="true">Optimising PineTimes Display Driver with Rust and Mynewt</title>
<meta data-rh="true" charset="utf-8" />
<meta data-rh="true" name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
<meta data-rh="true" name="theme-color" content="#000000" />
<meta data-rh="true" property="og:type" content="article" />
<meta data-rh="true" property="article:published_time" content="2020-03-05T04:58:31.032Z" />
<meta data-rh="true" name="title" content="Optimising PineTimes Display Driver with Rust and Mynewt" />
<meta data-rh="true" property="og:title" content="Optimising PineTimes Display Driver with Rust and Mynewt" />
<meta data-rh="true" property="twitter:title" content="Optimising PineTimes Display Driver with Rust and Mynewt" />
<meta data-rh="true" name="description"
content="Simple tweaks like Batched Updates and Non-Blocking SPI can have a huge impact on rendering performance… PineTime Smart Watch has been an awesome educational tool for teaching embedded coding with…" />
<meta data-rh="true" property="og:description"
content="Simple tweaks like Batched Updates and Non-Blocking SPI can have a huge impact on rendering performance…" />
<meta data-rh="true" property="twitter:description"
content="Simple tweaks like Batched Updates and Non-Blocking SPI can have a huge impact on rendering performance…" />
<meta data-rh="true" name="twitter:card" content="summary_large_image" />
<meta data-rh="true" name="twitter:creator" content="@MisterTechBlog" />
<meta data-rh="true" name="author" content="Lup Yuen Lee 李立源" />
<meta data-rh="true" name="robots" content="index,follow" />
<meta data-rh="true" name="referrer" content="unsafe-url" />
<meta data-rh="true" name="twitter:label1" value="Reading time" />
<meta data-rh="true" name="twitter:data1" value="15 min read" />
<meta property="og:image"
content="https://lupyuen.github.io/images/legacy/f1.png">
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
<link rel="stylesheet" type="text/css" href="../normalize.css">
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
<link rel="stylesheet" type="text/css" href="../prism.css">
<script src="../storage.js"></script><noscript>
<link rel="stylesheet" href="../noscript.css"></noscript>
<link rel="shortcut icon" href="../favicon.ico">
<style type="text/css">
#crate-search {
background-image: url("../down-arrow.svg");
}
a {
color: #77d;
}
</style>
<!-- End scripts/rustdoc-header.html -->
</head>
<body>
<div id="root">
<div class="a b c">
<article>
<section class="cl cm cn co ai cp cq r"></section><span class="r"></span>
<div>
<div class="s u cr cs ct cu"></div>
<section class="cv cw cx cy cz">
<div class="da ai">
<p><img src="https://lupyuen.github.io/images/legacy/f1.png" /></p>
</div>
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<div>
<div id="4cb4" class="du dv ap cd dw b dx dy dz ea eb ec ed ee ef eg eh">
<h1 class="dw b dx ei dz ej eb ek ed el ef em ap">Optimising PineTimes Display Driver with Rust and
Mynewt</h1>
</div>
<div class="en">
<div class="n eo ep eq er">
<div class="o n">
<div><a rel="noopener"
href="https://lupyuen.github.io">
<div class="de es et">
<div class="bf n eu o p s ev ew ex ey ez cu"></div>
</div>
</a></div>
<div class="fb ai r">
<div class="n">
<div style="flex:1"><span class="cc b cd ce cf cg r ap q">
<div class="fc n o fd"><span class="cc fe ff ce av fg fh as at au ap"><a
class="bw bx bg bh bi bj bk bl bm bn fi bq ca cb" rel="noopener"
href="https://lupyuen.github.io">Lup
Yuen Lee 李立源</a></span>
</div>
</span></div>
</div><span class="cc b cd ce cf cg r ch ci"><span class="cc fe ff ce av fg fh as at au ch">
<div><a class="bw bx bg bh bi bj bk bl bm bn fi bq ca cb" rel="noopener"
href="https://lupyuen.github.io">
28 Dec 2019</a> <!-- -->·
<!-- -->
<!-- -->15
<!-- --> min read
</div>
</span></span>
</div>
</div>
<div class="n gd ge gf gg gh gi gj gk y">
<div class="n o">
<div class="gm r">
<div class="q"><a
href="https://web.archive.org/web/20200612094614/https://medium.com/m/signin?operation=register&amp;redirect=https%3A%2F%2Fmedium.com%2F%40ly.lee%2Foptimising-pinetimes-display-driver-with-rust-and-mynewt-3ba269ea2f5c&amp;source=post_actions_header--------------------------bookmark_header-"
class="bw bx bg bh bi bj bk bl bm bn by bz bq ca cb" rel="noopener"></a></div>
</div>
<div class="gn r am"></div>
</div>
</div>
</div>
</div>
</div>
<p id="7fd8" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Simple tweaks like Batched Updates and Non-Blocking SPI can have a huge impact on
rendering performance…</em></p>
<p id="fbba" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><a
class="bw gc hj hk hl hm" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">PineTime
Smart Watch</a> has been an awesome educational tool for teaching embedded coding with <a
href="https://doc.rust-lang.org/book/title-page.html"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">Rust</a> and <a
href="https://mynewt.apache.org/"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">Mynewt OS</a>… Check out PineTime
articles <a class="bw gc hj hk hl hm" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">#1</a>,
<a class="bw gc hj hk hl hm" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/building-a-rust-driver-for-pinetimes-touch-controller">#2</a>
and <a class="bw gc hj hk hl hm" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/porting-druid-rust-widgets-to-pinetime-smart-watch">#3</a>
</p>
<p id="6bd8" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">But stare
closely at the video demos in the articles… Youll realise that the rendering of graphics on
PineTimes LCD display looks sluggish.</p>
<p id="2bbf" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Can we expect speedy screen updates from a </em><a
href="https://store.pine64.org/?product=pinetime-dev-kit"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">$20 smart
watch</em></a><em class="hi">… Powered by a </em><a
href="https://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.0.pdf"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">Nordic nRF52832
Microcontroller</em></a><em class="hi"> that drives an </em><a
href="https://wiki.pine64.org/images/5/54/ST7789V_v1.6.pdf"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">ST7789 Display
Controller</em></a><em class="hi"> over </em><a
href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">SPI</em></a><em
class="hi">?</em></p>
<p id="7774" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Yes we can!</em> Check the rendering performance of Rust and Mynewt OS on PineTime,
before and after optimisation…</p>
</div>
</div>
<div class="da">
<div class="n p">
<div class="hn ho hp hq hr hs ae ht af hu ah ai">
<p><a href="https://youtu.be/_x6B-L5KOtU">[Watch the video on YouTube]</a></p>
<p><em>Before and after optimising PineTimes
display driver</em></p>
</div>
</div>
</div>
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<p id="aa7f" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Today well
learn how we optimised the PineTime Display Driver to render text and graphics in sub-seconds…</p>
<ol class="">
<li id="1e34" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb if ig ih">
<strong class="gq ii">We group the pixels to be rendered into rows and blocks.</strong> This allows
graphics and text to be rendered in fewer SPI operations.</li>
<li id="b32b" class="go hc ap cd gq b gr ij hd gt ik he gv il hf gx im hg gz in hh hb if ig ih">
<strong class="gq ii">We changed Blocking SPI operations to Non-Blocking SPI operations.
</strong>This enables the Rust rendering functions to be executed while SPI operations are running
concurrently. <em class="hi">(</em><a
href="https://en.wikipedia.org/wiki/Graphics_pipeline"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">Think graphics
rendering pipeline</em></a><em class="hi">)</em></li>
</ol>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="b1c9" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Rendering PineTime Graphics
Pixel by Pixel</h1>
<p id="3957" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv">Lets look at a
simple example to understand how the [<a
href="https://crates.io/crates/embedded-graphics"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">embedded-graphics</a>] and [<a
href="https://crates.io/crates/st7735-lcd"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">st7735-lcd</a>] crates work
together to render graphics on PineTimes LCD display. This code creates a rectangle with
[embedded-graphics] and renders the rectangle to the [st7735-lcd] display…</p>
<p><script src="https://gist.github.com/lupyuen/b8ef2fabc603fea3d0e2c736d30ba03e.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/display.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/display.rs</a>
</em></p>
<p id="246f" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">When we trace
the SPI requests generated by the [st7735-lcd] driver, we see lots of repetition…</p>
<p><script src="https://gist.github.com/lupyuen/e55ba5da32ba586f5a6728d624f2b880.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/spi-blocking.log"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/spi-blocking.log</a>
</em></p>
<p id="39a3" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">(</em><a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs#L183-L188"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">The SPI log was
obtained by uncommenting this code</em></a><em class="hi">)</em></p>
<p id="fb87" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">For each pixel
in the rectangle, the display driver is setting the X and Y coordinates of each pixel and setting the
colour of each pixel… <em class="hi">Pixel by pixel! (0, 0), (0, 1), (0, 2), …</em></p>
<p id="3932" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Thats not
efficient for rendering graphics, pixel by pixel… <em class="hi">Why are [embedded-graphics] and
[st7735-lcd] doing that?</em></p>
<p id="7847" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Thats because
[embedded-graphics] was designed to run on <strong class="gq ii">highly-constrained
microcontrollers</strong> with very little RAM… Think STM32 Blue Pill, which has only <strong
class="gq ii">20 KB RAM</strong>! Thats too little RAM for rendering rectangles and other graphics
into RAM and copying the rendered RAM bitmap to the display. <em class="hi">How does
[embedded-graphics] render graphics?</em></p>
<p id="0532" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">By using <a
href="https://doc.rust-lang.org/book/ch13-02-iterators.html"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><strong class="gq ii">Rust
Iterators</strong></a>! Every graphic object to be rendered (rectangles, circles, even text) is
transformed by [embedded-graphics] into a <strong class="gq ii">Rust Iterator that returns the (X, Y)
coordinates of each pixel and its colour</strong>. This requires very little RAM because the pixel
information is computed on the fly, only when the Iterator needs to return the next pixel.</p>
<p id="9b66" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Rendering a
Pixel Iterator to the display is really easy and doesnt need much RAM, like this…</p>
<p><script src="https://gist.github.com/lupyuen/876da278160e96738d35ca23db895ad6.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/src/lib.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/src/lib.rs</a>
</em></p>
<p id="ee99" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Upon inspecting
the <code class="dm jq jr js jt b">set_pixel</code> function thats called for each pixel, we see
this…</p>
<p><script src="https://gist.github.com/lupyuen/f5da8b5db5cf1784f83acb70d012e0af.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/src/lib.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/src/lib.rs</a>
</em></p>
<p id="4b14" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">A-ha!</em> We have discovered the code that creates all the repeated SPI requests for
setting the (X, Y) coordinates and colour of each pixel!</p>
<p id="4d53" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Instead of
updating the LED display pixel by pixel, <em class="hi">can we batch the pixels together and blast the
entire batch of pixels in a single SPI request?</em></p>
<p id="7e43" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Digging into
the [st7735-lcd] display driver code, we see this clue…</p>
<p><script src="https://gist.github.com/lupyuen/286ea307d1877627f2e45685af14b8f0.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/src/lib.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/src/lib.rs</a>
</em></p>
<p id="3079" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">See the
difference? The function <code class="dm jq jr js jt b">set_pixels</code> sets the pixel window to the
region from <code class="dm jq jr js jt b">(X Start, Y Start)</code> to <code
class="dm jq jr js jt b">(X End, Y End)</code>… Then it <strong class="gq ii">blasts a list of pixel
colours</strong> to populate that entire window region!</p>
<p id="7936" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">When we call
<code class="dm jq jr js jt b">set_pixels</code> the SPI requests generated by the display driver
would look like this… <em class="hi">(Note the long lists of pixel colours)</em></p>
<p><script src="https://gist.github.com/lupyuen/04ed78a7671da9d65f4c5cc796c8dec5.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/spi-non-blocking.log"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/spi-non-blocking.log</a>
</em></p>
<p id="9799" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">But will this really improve rendering performance?</em> Lets test this hypothesis the
<a class="bw gc hj hk hl hm" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/my-5-year-iot-mission">Lean
and Agile Way</a> by batching the pixels (in the simplest way possible) without disturbing too much
[embedded-graphics] and [st7735-lcd] code…</p>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="8d93" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Batching PineTime Pixels into
Rows and Blocks</h1>
<p id="c037" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv">Heres our
situation…</p>
<ol class="">
<li id="ce78" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb if ig ih">
[embedded-graphics] creates Rust Iterators for rendering graphic objects. Works with minimal RAM,
but generates excessive SPI requests.</li>
<li id="19b3" class="go hc ap cd gq b gr ij hd gt ik he gv il hf gx im hg gz in hh hb if ig ih">
PineTimes Nordic nRF52832 microcontroller has 64 KB of RAM… Not quite sufficient to render the
entire 240x240 screen into RAM. (2 bytes of colour per pixel ✖ 240 rows ✖ 240 columns = <strong
class="gq ii">112.5 KB</strong>) RAM-based bitmap rendering is no go.</li>
</ol>
<p id="a21c" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Is there a Middle Way…</em> Keeping the RAM-efficient Rust Iterators... But get the
Iterators to <em class="hi">return small batches of pixels</em> (instead of returning individual
pixels)? Lets experiment with two very simple Rust Iterators: <strong class="gq ii">Pixel Row
Iterator and Pixel Block Iterator!</strong></p>
<p id="77ce" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Suppose we ask
[embedded-graphics] to render this trapezoid shape with 10 pixels…</p>
<p><img src="https://lupyuen.github.io/images/legacy/f2.png" /></p>
<p><em>10 pixels from the rendered letter K
</em></p>
<p id="5925" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">
[embedded-graphics] returns a Pixel Iterator that generates the 10 pixels from left to right, top to
bottom…</p>
<p><img src="https://lupyuen.github.io/images/legacy/f3.png" /></p>
<p><em>Zig-zag Pixel Iterator returned by
[embedded-graphics]</em></p>
<p id="6153" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Which needs
<strong class="gq ii">10 SPI requests</strong> to render, 1 pixel per SPI request. <em
class="hi">(Lets count only the set colour requests)</em></p>
<p id="6b71" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Since the Pixel
Iterator produces pixels row by row, lets create a <strong class="gq ii">Pixel Row Iterator</strong>
that returns pixels grouped by row…</p>
<p><img src="https://lupyuen.github.io/images/legacy/f4.png" /></p>
<p><em>Our Pixel Row Iterator returns 3 rows
</em></p>
<p id="fd97" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Awesome!</em> When we group the pixels into rows, we only need to make <strong
class="gq ii">3 SPI requests</strong> to render all 10 pixels!</p>
<p id="5705" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Can we do better?</em> What if we group consecutive rows of the same width into
rectangular blocks… Creating a <strong class="gq ii">Pixel Block Iterator</strong></p>
<p><img src="https://lupyuen.github.io/images/legacy/f5.png" /></p>
<p><em>Our Pixel Block Iterator returns 2 blocks
</em></p>
<p id="2ed4" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Yay!</em> We have grouped 10 pixels into 2 blocks… Only <strong class="gq ii">2 SPI
requests</strong> to render all 10 pixels!</p>
<p id="f417" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Whats the catch? How did we optimise 10 SPI requests into 2 SPI requests… Without
sacrificing anything?</em></p>
<p id="32fb" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">While grouping
the pixels into rows and blocks, we actually use more RAM. Every time the Pixel Row Iterator returns
the next row, it needs up to <strong class="gq ii">8 bytes</strong> of temporary RAM storage (4 pixels
with 2 colour bytes each).</p>
<p id="5c5e" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">And every time
the Pixel Block Iterator returns the next block (max 8 pixels), it needs up to <strong
class="gq ii">16 bytes</strong> of temporary RAM storage. Which isnt a lot of RAM, if we keep our
block size small. Also the Iterator will reuse the storage for each block returned, so we wont need
to worry about storing 2 or more blocks returned by the Iterator.</p>
<p id="3814" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">This is the
classical <a
href="https://en.wikipedia.org/wiki/Space%E2%80%93time_tradeoff"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><strong class="gq ii">Space-Time
Tradeoff</strong></a> in Computer Science… Sacrificing some storage space (RAM) to make things run
faster.</p>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="1326" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Pixel Row and Pixel Block
Iterators</h1>
<p id="98b3" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv">Heres the code
for the Pixel Row Iterator that returns the next row of contiguous pixels…</p>
<p><script src="https://gist.github.com/lupyuen/0ed60fad6e539e1522e1fbcdb6fb54f4.js"></script></p>
<p><em>Pixel Row Iterator. From <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs</a>
</em></p>
<p id="68f8" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">And heres the
code for the Pixel Block Iterator that returns the next block of contiguous rows of the same width.
Turns out we only need to tweak the code above slightly to get what we need… Instead of iterating over
pixels, we now iterate over rows…</p>
<p><script src="https://gist.github.com/lupyuen/8def4257e80edb0964b16b23b599a0f5.js"></script></p>
<p><em>Pixel Block Iterator. From <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs</a>
</em></p>
<p id="c9b4" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Combining the
Pixel Row Iterator and the Pixel Block Iterator, we get the <code
class="dm jq jr js jt b">draw_blocks</code> function that renders any [embedded-graphics] graphic
object (including text) as pixel blocks…</p>
<p><script src="https://gist.github.com/lupyuen/294e899b288612f509230225a6ad03c2.js"></script></p>
<p><em>Rendering a graphic object as Pixel Blocks.
From <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs</a>
</em></p>
<p id="b84b" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Thus we now
render graphic objects as RAM-efficient chunks of pixels, instead of individual pixels. <em
class="hi">Middle Way found!</em></p>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="20f4" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Test the Pixel Row and Pixel
Block Iterators</h1>
<p id="b629" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv"><em
class="hi"></em><a
href="https://en.wikipedia.org/wiki/Space%E2%80%93time_tradeoff"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">Space-Time
Tradeoff</em></a><em class="hi"> called and wants to know how much space well be allocating to
make things run faster…”</em></p>
<p id="4d29" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">The more RAM
storage we allocate for batching pixels into rows and blocks, the fewer SPI requests we need to make.
The code currently sets the limits at <strong class="gq ii">100 pixels per row, 200 pixels per
block</strong></p>
<p><script src="https://gist.github.com/lupyuen/111875b0f5bc3b51b86fe12457373e51.js"></script></p>
<p><em>Pixel Row and Pixel Block Sizes. From <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs</a>
</em></p>
<p id="c26d" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Note that the
rows and blocks are returned by the Iterators as <strong class="gq ii">[</strong><a
href="https://crates.io/crates/heapless"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><strong
class="gq ii">heapless</strong></a><strong class="gq ii">] </strong><a
href="https://docs.rs/heapless/0.5.1/heapless/struct.Vec.html"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><strong
class="gq ii">Vectors</strong></a>, which use fixed-size arrays to store Vectors. So that we dont
rely on Heap Memory, which is harder to manage on embedded devices like PineTime.</p>
<p id="2f26" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Any graphic
object thats <strong class="gq ii">100 pixels wide</strong> (or smaller) will be batched efficiently
into pixels rows and blocks. Like this square of width 90 pixels created with [embedded-graphics]…</p>
<p><script src="https://gist.github.com/lupyuen/7d9d802fa284e35ec4e6bcea345d8bf6.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/display.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/display.rs</a>
</em></p>
<p><img src="https://lupyuen.github.io/images/legacy/f6.png" /></p>
<p><em>Square of width 90 pixels from the render demo
</em></p>
<p id="f669" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">When we trace
the rendering of the square, we see this log of pixel blocks…</p>
<p><script src="https://gist.github.com/lupyuen/07a8af1319fbbcc5a5d3ff1c97e83ac6.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/pixel-block.log"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/pixel-block.log</a>
</em></p>
<p id="f639" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">(</em><a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs#L122-L125"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">The log was created
by uncommenting this code</em></a><em class="hi">)</em></p>
<p id="cd78" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Which means
that we are indeed deconstructing the <strong class="gq ii">90x90 square</strong> into <strong
class="gq ii">90x2 pixel blocks</strong> for efficient rendering.</p>
<blockquote class="kq kr ks">
<p id="eacb" class="go hc ap hi gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="cd">💎</em> This deconstruction doesnt work so well for a square that occupies the entire
240x240 PineTime screen. Ill let you think…<em class="cd"> 1</em>Why this doesnt work <em
class="cd">2</em>A solution for rendering the huge square efficiently <em class="cd">😀</em>
</p>
</blockquote>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="b211" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Non-Blocking SPI on PineTime
with Mynewt OS</h1>
<p id="2695" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv">We could go
ahead and run the Pixel Row and Pixel Block Iterators to measure the rendering time… But we wont. We
are now rendering the screen as chunks of pixels, transmitting a long string of pixel colours in
<strong class="gq ii">a single SPI request</strong></p>
<p id="6188" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">However our SPI
code in PineTime isnt optimised to handle large SPI requests… Whenever it transmits an SPI request,
<em class="hi">it waits for the entire request to be transmitted</em> before returning to the caller.
This is known as <strong class="gq ii">Blocking SPI</strong>.</p>
<p id="4a2c" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Heres how we
call <code
class="dm jq jr js jt b"><a href="https://mynewt.apache.org/latest/os/modules/hal/hal_spi/hal_spi.html#c.hal_spi_txrx" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">hal_spi_txrx</a></code>
to transmit a Blocking SPI request in Rust with Mynewt OS…</p>
<p><script src="https://gist.github.com/lupyuen/1379fd70dc7a1e8ca08b964f7618652a.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs</a>
</em></p>
<p id="8e2b" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Mynewt OS
provides an efficient way to transmit SPI requests: <strong class="gq ii">Non-Blocking SPI</strong>.
<code
class="dm jq jr js jt b"><a href="https://mynewt.apache.org/latest/os/modules/hal/hal_spi/hal_spi.html#c.hal_spi_txrx_noblock" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">hal_spi_txrx_noblock</a></code>
doesnt hold up the caller while transmitting the request. Instead, Mynewt calls our Callback Function
when the request has been completed.</p>
<p id="a8ba" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Heres how we
set up Non-Blocking SPI and call <code class="dm jq jr js jt b">hal_spi_txrx_noblock</code></p>
<p><script src="https://gist.github.com/lupyuen/3e0fd1d9e26f2692534d1592a1a7d194.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs</a>
</em></p>
<p id="53a7" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><code
class="dm jq jr js jt b">spi_noblock_handler</code> is our Callback Function in Rust. Mynewt wont
let us transmit a Non-Blocking SPI request while another is in progress, so our Callback Function
needs to ensure that never happens. More about <code
class="dm jq jr js jt b">spi_noblock_handler</code> in a while.</p>
<blockquote class="kq kr ks">
<p id="fc4d" class="go hc ap hi gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="cd">💎 </em>Whats <code
class="dm jq jr js jt b"><a href="https://doc.rust-lang.org/core/mem/fn.transmute.html" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">core::mem::transmute</a></code>?
We use this function from the <a
href="https://doc.rust-lang.org/core/index.html"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">Rust Core Library</a> to cast
pointer types when passing pointers and references from Rust to C. Its similar to casting <code
class="dm jq jr js jt b">char *</code> to <code class="dm jq jr js jt b">void *</code> in C.</p>
<p id="ab06" class="go hc ap hi gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Why dont we
need to specify the pointer type that we are casting to? Because the Rust Compiler performs <a
href="https://doc.rust-lang.org/stable/rust-by-example/types/inference.html"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">Type Inference</a> to deduce the
pointer type.</p>
</blockquote>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="7ad3" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Work Around an SPI Quirk</h1>
<p id="d6bc" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv"><em
class="hi">Bad News:</em> Non-Blocking SPI doesnt work 100% as advertised for Nordic nRF52832
Microcontroller, the heart of PineTime. <a
href="https://github.com/apache/mynewt-core/blob/master/hw/mcu/nordic/nrf52xxx/src/hal_spi.c#L1106-L1118"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">According to this note in Mynewt
OS</a>, <strong class="gq ii">Non-Blocking SPI on nRF52832 fails if were sending a single byte over
SPI</strong>.</p>
<p id="f6f1" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">But why would we send single-byte SPI requests anyway?</em></p>
<p id="614d" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Remember this
SPI log that we captured earlier? We seem to be sending single bytes very often: <code
class="dm jq jr js jt b">2a</code>, <code class="dm jq jr js jt b">2b</code> and <code
class="dm jq jr js jt b">2c</code>, which are <strong class="gq ii">Command Bytes</strong></p>
<p><script src="https://gist.github.com/lupyuen/04ed78a7671da9d65f4c5cc796c8dec5.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/spi-non-blocking.log"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/spi-non-blocking.log</a>
</em></p>
<p id="6ca6" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">PineTimes <a
href="https://wiki.pine64.org/images/5/54/ST7789V_v1.6.pdf"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">ST7789 Display Controller</a> has
an unusual SPI interface with a special pin: the <strong class="gq ii">Data/Command (DC) Pin</strong>.
The display controller expects our microcontroller to set the <strong class="gq ii">DC Pin to Low when
sending the Command Byte</strong>, and set the <strong class="gq ii">DC Pin to High when sending
Data Bytes</strong></p>
<p><script src="https://gist.github.com/lupyuen/50f71f2130ab37a203217072748668bd.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs</a>
</em></p>
<p id="26f7" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Unfortunately
our <strong class="gq ii">Command Bytes are single bytes</strong>, hence we see plenty of single-byte
SPI requests. <em class="hi">All because of the need to flip the DC Pin!</em></p>
<p id="bddf" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">This
complicates our SPI design but lets overcome this microcontroller hardware defect with good firmware…
<strong class="gq ii">All single-byte SPI requests are now sent the Blocking way</strong>, other
requests are sent the Non-Blocking way…</p>
<p><script src="https://gist.github.com/lupyuen/e92582b44be77bdd538a6e602b9838eb.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs</a>
</em></p>
<p id="8b9a" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">The code uses a
Semaphore <code class="dm jq jr js jt b">SPI_SEM</code> to wait for the Non-Blocking SPI operation to
complete before proceeding. <code class="dm jq jr js jt b">SPI_SEM</code> is signalled by our Callback
Function <code class="dm jq jr js jt b">spi_noblock_handler</code> like this…</p>
<p><script src="https://gist.github.com/lupyuen/48857b90ad0199c75f3589e99440a1f2.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs</a>
</em></p>
<p id="747b" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Something smells fishy…</em> <em class="hi">Why are we now waiting for a Non-Blocking SPI
request to complete?</em></p>
<p id="4fa2" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Well this
happens when we do things the <a class="bw gc hj hk hl hm" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/my-5-year-iot-mission">Lean
and Agile Way</a>… When we hit problems (like the single-byte SPI issue), we assess various simple
solutions before we select and implement the right permanent fix. <em class="hi">(And I dont think we
have found the right fix yet)</em></p>
<p id="4706" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">This Semaphore
workaround also makes the function <code class="dm jq jr js jt b">internal_spi_noblock_write</code>
easier to troubleshoot… Whether the SPI request consists of a single byte or multiple bytes, <code
class="dm jq jr js jt b">internal_spi_noblock_write</code> will always wait for the SPI request to
complete, instead of having diverging paths.</p>
<p id="5a97" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">This story also
highlights the benefit of building our Rust firmware on top of an established Real Time Operating
System like Mynewt OS… We quickly discover platform quirks that others have experienced, so that we
can avoid the same trap.</p>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="7688" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Render Graphics and Send SPI
Requests Simultaneously on PineTime</h1>
<p id="1041" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv">Now we can send
large SPI requests efficiently to PineTimes LCD display. We are blocking on a Semaphore while waiting
for the SPI request to be completed, which means that our CPU is actually free to do some other tasks
while blocking.</p>
<p id="c77b" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Can we do some [embedded-graphics] rendering while waiting for the SPI requests to be
completed?</em></p>
<p id="079d" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Two problems
with that…</p>
<ol class="">
<li id="e4fd" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb if ig ih">
[embedded-graphics] creates Rust Iterators and SPI requests in temporary RAM storage. To let
[embedded-graphics] continue working, we need to <strong class="gq ii">copy the generated SPI
requests</strong> into RAM before sending the requests</li>
<li id="bfe8" class="go hc ap cd gq b gr ij hd gt ik he gv il hf gx im hg gz in hh hb if ig ih">To
perform [embedded-graphics] rendering independently from the SPI request transmission, we need a
<strong class="gq ii">background task</strong>. The main task will render graphics with
[embedded-graphics] (which is our current design), the background task will transmit SPI requests
(this part is new).</li>
</ol>
<p><img src="https://lupyuen.github.io/images/legacy/f7.jpeg" /></p>
<p><em>Rendering graphics and transmitting SPI
requests at the same time on PineTime. Yes this is the Producer-Consumer Pattern found in many
programs.</em></p>
<p id="79f7" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Fortunately
Mynewt OS has everything we need to experiment with this multitasking…</p>
<ol class="">
<li id="6c8a" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb if ig ih">
Mynewts <strong class="gq ii">Mbuf Chains</strong> may be used to copy SPI requests easily into a
RAM space thats specially managed by Mynewt OS</li>
<li id="cfe5" class="go hc ap cd gq b gr ij hd gt ik he gv il hf gx im hg gz in hh hb if ig ih">
Mynewts <strong class="gq ii">Mbuf Queues</strong> may be used to enqueue the SPI requests for
transmission by the background task</li>
<li id="b503" class="go hc ap cd gq b gr ij hd gt ik he gv il hf gx im hg gz in hh hb if ig ih">Mynewt
lets us create a <strong class="gq ii">background task</strong> to send SPI requests from the Mbuf
Queue</li>
</ol>
<p id="17b0" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Lets look at
Mbuf Chains, Mbuf Queues and Multitasking in Mynewt OS.</p>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="3cd0" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Buffer SPI Requests with Mbuf
Chains in Mynewt OS</h1>
<p id="16d8" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv">In the Unix
world of Network Drivers, <a
href="https://mynewt.apache.org/latest/os/core_os/mbuf/mbuf.html"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><strong
class="gq ii">Mbufs</strong></a> (short for Memory Buffers) are often used to store network
packets. Mbufs were created to make common networking stack operations (like stripping and adding
protocol headers) efficient and as copy-free as possible. <em class="hi">(Mbufs are also used by the
NimBLE Bluetooth Stack, which we have seen in the </em><a class="bw gc hj hk hl hm" target="_blank"
rel="noopener"
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot"><em
class="hi">first PineTime article</em></a><em class="hi">)</em></p>
<p id="6fc8" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">What makes Mbufs so versatile? How are they different from Heap Storage?</em></p>
<p id="b66c" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">When handling
Network Packets (and SPI Requests), we need a quick way to allocate and deallocate buffers of varying
sizes. When we request memory from Heap Storage, we get a contiguous block of RAM thats exactly what
we need (or maybe more). But it causes our Heap Storage to become <em class="hi">fragmented and poorly
utilised.</em></p>
<p><img src="https://lupyuen.github.io/images/legacy/f8.png" /></p>
<p><em>Chain of Mbufs. From <a
href="https://mynewt.apache.org/latest/os/core_os/mbuf/mbuf.html"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://mynewt.apache.org/latest/os/core_os/mbuf/mbuf.html</a>
</em></p>
<p id="845c" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">With Mbufs, we
get a <strong class="gq ii">chain (linked list) of memory blocks</strong> instead. We cant be sure
how much RAM well get in each block, but we can be sure that the total RAM in the entire chain meets
what we need. <em class="hi">(The diagram above shows how Mynewt OS allocates Mbuf Chains in a compact
way using fixed-size Mbuf blocks)</em></p>
<p id="78df" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Isnt it harder to code with a chain of memory blocks?</em> Yes, it makes coding more
cumbersome, but Mbuf Chains will utilise our tiny pool of RAM on PineTime much better than a Heap
Storage allocator.</p>
<p id="afff" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">With Rust and
Mynewt OS, heres how we allocate an Mbuf Chain and append our SPI request to the Mbuf Chain…</p>
<p><script src="https://gist.github.com/lupyuen/c314f5341dbb315fdff853b70fe80505.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs</a>
</em></p>
<p id="b180" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">We may call
<code
class="dm jq jr js jt b"><a href="https://mynewt.apache.org/latest/os/core_os/mbuf/mbuf.html#c.os_mbuf_append" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">os_mbuf_append</a></code>
as often as we like to append data to our Mbuf Chain, which keeps growing and growing… (Unlike Heap
Storage blocks which are fixed-size). <em class="hi">So cool!</em></p>
<p id="51e0" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Heres how we
walk the Mbuf Chain to transmit each block of SPI data in the chain, and deallocate the chain when
were done…</p>
<p><script src="https://gist.github.com/lupyuen/c49d675e9bb8922691e09c331cc1d9bd.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs</a>
</em></p>
<p id="15ec" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Note that we
dont transmit the entire Mbuf Chain of SPI data in a single SPI operation… We transmit the SPI data
one Mbuf at a time. This works fine for PineTimes ST7789 Display Controller. And with limited RAM,
its best not to make an extra copy of the entire Mbuf Chain before transmitting.</p>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="e4e5" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Enqueue SPI Requests with Mbuf
Queues in Mynewt OS</h1>
<p id="b733" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv">After
[embedded-graphics] has completed its rendering, we get an Mbuf Chain that contains the SPI request
that will be transmitted to the PineTime Display Controller by the background task. Now we need a way
to enqueue the SPI requests (Mbuf Chains) produced by [embedded-graphics]…</p>
<p><img src="https://lupyuen.github.io/images/legacy/f9.png" /></p>
<p><em>Enqueuing SPI requests in an MBuf Queue before
transmitting</em></p>
<p id="c367" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">When we use
Mbuf Chains in Mynewt OS, <em class="hi">we get Mbuf Queues for free!</em></p>
<p id="80b7" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Check the
function <code class="dm jq jr js jt b">spi_event_callback</code> from the last code snippet… Its
actually calling <code
class="dm jq jr js jt b"><a href="https://mynewt.apache.org/latest/os/core_os/mbuf/mbuf.html#c.os_mqueue_get" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">os_mqueue_get</a></code>
to read SPI requests (Mbuf Chains) from an Mbuf Queue named <code
class="dm jq jr js jt b">SPI_DATA_QUEUE</code>.</p>
<p id="a9ea" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Adding an SPI
request to an Mbuf Queue is done by calling <code class="dm jq jr js jt b">os_mqueue_put</code> in
Rust like this…</p>
<p><script src="https://gist.github.com/lupyuen/9fca8f06a5d3afd4457079fa89587d13.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs</a>
</em></p>
<p id="e602" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><code
class="dm jq jr js jt b">spi_noblock_write</code> is the complete Rust function we use in our
PineTime firmware to 1⃣ Allocate an Mbuf Chain 2⃣ Append the SPI request to the Mbuf Chain 3⃣ Add
the Mbuf Chain to the Mbuf Queue. <em class="hi">Yep its that easy to use Mbuf Chains and Mbuf Queues
in Mynewt OS!</em></p>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="8904" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Transmit Enqueued SPI Requests
with Mynewt Background Task</h1>
<p id="56b6" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv">Here comes the
final part of our quick experiment… Create a background task in Mynewt to read the Mbuf Queue and
transmit each SPI request to PineTimes Display Controller…</p>
<p><img src="https://lupyuen.github.io/images/legacy/f10.png" /></p>
<p><em>Transmitting SPI Requests enqueued in an Mbuf
Queue</em></p>
<p id="a326" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">With Rust and
Mynewt OS, heres how we create a background task <code class="dm jq jr js jt b">SPI_TASK</code> that
runs the neverending function <code class="dm jq jr js jt b">spi_task_func</code></p>
<p><script src="https://gist.github.com/lupyuen/f68b6f8eb8d7b0e18a586106c6ff12bc.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs</a>
</em></p>
<p id="17a5" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">(Note that were calling Mynewt to create background tasks instead of using </em><a
href="https://doc.rust-lang.org/book/ch16-01-threads.html"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">Rust
multitasking</em></a><em class="hi">, because Mynewt controls all our tasks on PineTime)</em></p>
<p id="1c59" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><code
class="dm jq jr js jt b">spi_task_func</code> runs forever, blocking until theres a request in the
Mbuf Queue, and executes the request. The request is handled by the function <code
class="dm jq jr js jt b">spi_event_callback</code> that we have seen earlier. <em class="hi">(How
does Mynewt know that it should invoke </em><code
class="dm jq jr js jt b">spi_event_callback</code><em class="hi">? Its defined in the call to
</em><code
class="dm jq jr js jt b"><a href="https://mynewt.apache.org/latest/os/core_os/mbuf/mbuf.html#c.os_mqueue_init" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">os_mqueue_init</a></code><em
class="hi"> above.)</em></p>
<p id="2e59" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><code
class="dm jq jr js jt b"><a href="https://mynewt.apache.org/latest/os/modules/hal/hal_watchdog/hal_watchdog.html#c.hal_watchdog_tickle" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">hal_watchdog_tickle</a></code>
<em class="hi">appears oddly in the code… What is that?</em></p>
<p id="7560" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Mynewt
helpfully pings our background task every couple of milliseconds, to make sure that its not hung…
Thats why its called a <a
href="https://mynewt.apache.org/latest/os/modules/hal/hal_watchdog/hal_watchdog.html"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><strong
class="gq ii">Watchdog</strong></a>.</p>
<p id="3458" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">To prevent
Mynewt from raising a Watchdog Exception, we need to tell the Watchdog every couple of milliseconds
that we are OK… By calling <code
class="dm jq jr js jt b"><a href="https://mynewt.apache.org/latest/os/modules/hal/hal_watchdog/hal_watchdog.html#c.hal_watchdog_tickle" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">hal_watchdog_tickle</a></code>
</p>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="40dd" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Optimised PineTime Display
Driver… Assemble!</h1>
<p id="9b7e" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv">This has been a
lengthy but quick <em class="hi">(two-week)</em> experiment in optimising the display rendering for
PineTime. Heres how we put everything together…</p>
<p id="5e60" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">1⃣ We have
<strong class="gq ii">batched the rendering of pixels by rows and by blocks</strong>. This batching
code has been added to the [<a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">piet-embedded</a>] crate that
calls [embedded-graphics] to render 2D graphics and text on our PineTime.</p>
<p id="fcca" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">2<a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/display.rs"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">The code that demos the batching
of pixels</a> is also in the [piet-embedded] crate. Batching is enabled when we enable the <code
class="dm jq jr js jt b">noblock_spi</code> feature in [piet-embedded]s <code
class="dm jq jr js jt b"><a href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/Cargo.toml" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">Cargo.toml</a></code>
like this…</p>
<p><script src="https://gist.github.com/lupyuen/71fed75e0a9f25ebe1ee94a0d5fb8a82.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/Cargo.toml"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/Cargo.toml</a>
</em></p>
<p id="0788" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">3<code
class="dm jq jr js jt b">noblock_spi</code> is referenced in the demo code like this…</p>
<p><script src="https://gist.github.com/lupyuen/163c6899b9aaaf54edd81de645c10ac7.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/display.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/display.rs</a>
</em></p>
<p id="dd93" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">4⃣ We have
implemented <strong class="gq ii">Non-Blocking SPI with Mbuf Chains and Mbuf Queues</strong> (plus a
background task). The code is located in the [<a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">mynewt</a>] crate.</p>
<p id="b3e4" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">5⃣ We have
forked the original [st7735-lcd] display driver into [<a
href="https://github.com/lupyuen/st7735-lcd-batch-rs"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">st7735-lcd-batch</a>] to test
Non-Blocking SPI. Non-Blocking SPI is enabled when we enable the <code
class="dm jq jr js jt b">noblock_spi</code> feature in [st7735-lcd-batch]s <code
class="dm jq jr js jt b"><a href="https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/Cargo.toml" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">Cargo.toml</a></code>
</p>
<p><script src="https://gist.github.com/lupyuen/ae438ce7592785b3e597d41c4fccf4f7.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/Cargo.toml"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/Cargo.toml</a>
</em></p>
<p id="93b7" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">6<code
class="dm jq jr js jt b">noblock_spi</code> is referenced by [st7735-lcd-batch] like this…</p>
<p><script src="https://gist.github.com/lupyuen/88978a532d172dc080b81c81b5b6cc74.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/src/lib.rs"
class="bw gc hj hk hl hm" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/st7735-lcd-batch-rs/blob/master/src/lib.rs</a>
</em></p>
<p id="f603" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">(Plus a few other spots in that file)</em></p>
<p id="2256" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">We have
attempted to optimise the display driver for PineTime… <em class="hi">But its far from optimal!</em>
</p>
<p id="6e00" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">There are a few
parameters that we may tweak to make PineTime render faster… Just be mindful that some of these tweaks
will take up precious RAM…</p>
<p id="c2e2" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">1<code
class="dm jq jr js jt b"><a href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs#L26" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">MaxRowSize</a></code>:
Maximum number of pixels per batched row. Currently set to 100.</p>
<p id="d305" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">2<code
class="dm jq jr js jt b"><a href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/batch.rs#L28" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">MaxBlockSize</a></code>:
Maximum number of pixels per batched block. Currently set to 200.</p>
<p id="447c" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">3<code
class="dm jq jr js jt b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/mynewt/src/spi.rs#L109" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">SPI_THROTTLE_SEM</a></code>:
How many SPI requests are allowed to be enqueued before blocking the rendering task. Currently set to
2.</p>
<p id="05bc" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">4<code
class="dm jq jr js jt b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/apps/my_sensor_app/syscfg.yml#L100" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">OS_MAIN_STACK_SIZE</a></code>:
Stack Size for the main task. Currently set to 16 KB</p>
<p id="85d7" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">5<code
class="dm jq jr js jt b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/apps/my_sensor_app/syscfg.yml#L102" class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">MSYS_1_BLOCK_COUNT</a></code>:
Number of Mbuf blocks available. Currently set to 64.</p>
<p id="8534" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><a
href="https://gist.github.com/lupyuen/35d8471a200bebd4bd6de8696d3eeff0"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow"><em class="hi">Is it possible to
render PineTime graphics at the theoretical maximum speed of the SPI bus? Read this</em></a></p>
</div>
</div>
</section>
<hr class="io fe ip iq ir is ic it iu iv iw ix" />
<section class="cv cw cx cy cz">
<div class="n p">
<div class="z ab ac ae af dt ah ai">
<h1 id="bef2" class="iy iz ap cd cc ja dx jb dz jc jd je jf jg jh ji jj">Whats Next?</h1>
<p id="1d10" class="go hc ap cd gq b gr jk hd gt jl he gv jm hf gx jn hg gz jo hh hb cv"><a
href="https://store.pine64.org/?product=pinetime-dev-kit"
class="bw gc hj hk hl hm" target="_blank" rel="noopener nofollow">PineTime is available for purchase
by general public</a>! Check this article for updated instructions to build and flash PineTime
firmware…</p>
<div class="lb lc ld le lf lg"><a target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/build-and-flash-rust-mynewt-firmware-for-pinetime-smart-watch">
<div class="lh n bv">
<div class="li n lj p lk ll">
<h2 class="cc ja lm ce av ln fh as lo au ap">Build and Flash Rust+Mynewt Firmware for PineTime
Smart Watch</h2>
</div>
<div class="lr r">
<div class="ls r lt lu lv lr lw lx ly"></div>
</div>
</div>
</a></div>
<p id="ac85" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">In the next
article well have…</p>
<p id="858c" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">1⃣ The <strong
class="gq ii">prebuilt Rust + Mynewt OS firmware</strong> that we may download and install on
PineTime</p>
<p id="dfea" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">2<strong
class="gq ii">Instructions for flashing the firmware</strong> to PineTime with Raspberry Pi (or ST
Link)</p>
<p id="6a1a" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">3<strong
class="gq ii">Instructions for developing our own Watch Apps</strong> with the <a
class="bw gc hj hk hl hm" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/porting-druid-rust-widgets-to-pinetime-smart-watch">druid
Rust UI Framework</a></p>
<p id="3927" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv">Stay tuned!</p>
<p id="023f" class="go hc ap cd gq b gr gs hd gt gu he gv gw hf gx gy hg gz ha hh hb cv"><em
class="hi">Here are the other articles in the PineTime series…</em></p>
<div class="lb lc ld le lf lg"><a target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">
<div class="lh n bv">
<div class="li n lj p lk ll">
<h2 class="cc ja lm ce av ln fh as lo au ap">Sneak Peek of PineTime Smart Watch… And why its
perfect for teaching IoT</h2>
</div>
<div class="lr r">
<div class="lz r lt lu lv lr lw lx ly"></div>
</div>
</div>
</a></div>
<div class="lb lc ld le lf lg"><a target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/building-a-rust-driver-for-pinetimes-touch-controller">
<div class="lh n bv">
<div class="li n lj p lk ll">
<h2 class="cc ja lm ce av ln fh as lo au ap">Building a Rust Driver for PineTimes Touch
Controller</h2>
</div>
<div class="lr r">
<div class="ma r lt lu lv lr lw lx ly"></div>
</div>
</div>
</a></div>
<div class="lb lc ld le lf lg"><a target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/porting-druid-rust-widgets-to-pinetime-smart-watch">
<div class="lh n bv">
<div class="li n lj p lk ll">
<h2 class="cc ja lm ce av ln fh as lo au ap">Porting [druid] Rust Widgets to PineTime Smart
Watch</h2>
</div>
<div class="lr r">
<div class="mb r lt lu lv lr lw lx ly"></div>
</div>
</div>
</a></div>
<div class="lb lc ld le lf lg"><a target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/build-and-flash-rust-mynewt-firmware-for-pinetime-smart-watch">
<div class="lh n bv">
<div class="li n lj p lk ll">
<h2 class="cc ja lm ce av ln fh as lo au ap">Build and Flash Rust+Mynewt Firmware for PineTime
Smart Watch</h2>
</div>
<div class="lr r">
<div class="ls r lt lu lv lr lw lx ly"></div>
</div>
</div>
</a></div>
<div class="lb lc ld le lf lg"><a target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/debug-rust-mynewt-firmware-for-pinetime-on-raspberry-pi">
<div class="lh n bv">
<div class="li n lj p lk ll">
<h2 class="cc ja lm ce av ln fh as lo au ap">Debug Rust+Mynewt Firmware for PineTime on
Raspberry Pi</h2>
</div>
<div class="lr r">
<div class="mc r lt lu lv lr lw lx ly"></div>
</div>
</div>
</a></div>
<div class="lb lc ld le lf lg"><a target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/my-first-week-as-embedded-foss-advocate">
<div class="lh n bv">
<div class="li n lj p lk ll">
<h2 class="cc ja lm ce av ln fh as lo au ap">My First Week As Embedded FOSS Advocate</h2>
</div>
<div class="lr r">
<div class="md r lt lu lv lr lw lx ly"></div>
</div>
</div>
</a></div>
<div class="lb lc ld le lf lg"><a
href="https://marketplace.visualstudio.com/items?itemName=LeeLupYuen.visual-embedded-rust"
target="_blank" rel="noopener nofollow">
<div class="lh n bv">
<div class="li n lj p lk ll">
<h2 class="cc ja lm ce av ln fh as lo au ap">Visual Embedded Rust</h2>
</div>
<div class="lr r">
<div class="me r lt lu lv lr lw lx ly"></div>
</div>
</div>
</a></div>
<div class="lb lc ld le lf lg"><a
href="https://lupyuen.github.io/pinetime-rust-mynewt/articles/chip8"
target="_blank" rel="noopener nofollow">
<div class="lh n bv">
<div class="li n lj p lk ll">
<h2 class="cc ja lm ce av ln fh as lo au ap">chip8.rs: CHIP-8 Game Emulator in Rust for PineTime
Smart Watch</h2>
</div>
<div class="lr r">
<div class="mf r lt lu lv lr lw lx ly"></div>
</div>
</div>
</a></div>
</div>
</div>
</section>
</div>
</article>
</div>
</div>
<ul>
<li>
<p><a href="https://github.com/sponsors/lupyuen">Sponsor me a coffee</a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io">Check out my articles</a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/rss.xml">RSS Feed</a></p>
</li>
</ul>
</body>
</html>
<!--
FILE ARCHIVED ON 09:46:14 Jun 12, 2020 AND RETRIEVED FROM THE
INTERNET ARCHIVE ON 10:45:36 Feb 21, 2021.
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
SECTION 108(a)(3)).
-->
<!--
playback timings (ms):
captures_list: 159.551
exclusion.robots: 0.292
exclusion.robots.policy: 0.279
RedisCDXSource: 2.3
esindex: 0.012
LoadShardBlock: 122.905 (3)
PetaboxLoader3.datanode: 129.513 (4)
CDXLines.iter: 30.297 (3)
load_resource: 80.342
PetaboxLoader3.resolve: 22.986
-->