mirror of
https://github.com/lupyuen/lupyuen.github.io.git
synced 2025-01-13 09:08:30 +08:00
1277 lines
No EOL
86 KiB
HTML
1277 lines
No EOL
86 KiB
HTML
<!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 PineTime’s 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 PineTime’s Display Driver with Rust and Mynewt" />
|
||
<meta data-rh="true" property="og:title" content="Optimising PineTime’s Display Driver with Rust and Mynewt" />
|
||
<meta data-rh="true" property="twitter:title" content="Optimising PineTime’s 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 PineTime’s 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&redirect=https%3A%2F%2Fmedium.com%2F%40ly.lee%2Foptimising-pinetimes-display-driver-with-rust-and-mynewt-3ba269ea2f5c&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… You’ll realise that the rendering of graphics on
|
||
PineTime’s 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 PineTime’s
|
||
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 we’ll
|
||
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">Let’s 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 PineTime’s 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">That’s 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">That’s 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>! That’s 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 doesn’t 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 that’s 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> Let’s 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">Here’s 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">
|
||
PineTime’s 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)? Let’s 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">(Let’s 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, let’s 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">What’s 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 isn’t a lot of RAM, if we keep our
|
||
block size small. Also the Iterator will reuse the storage for each block returned, so we won’t 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">Here’s 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 here’s 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 we’ll 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 don’t
|
||
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 that’s <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 doesn’t work so well for a square that occupies the entire
|
||
240x240 PineTime screen. I’ll let you think…<em class="cd"> 1️⃣ </em>Why this doesn’t 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 won’t. 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 isn’t 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">Here’s 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>
|
||
doesn’t 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">Here’s 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 won’t
|
||
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>What’s <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. It’s 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 don’t 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 doesn’t 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 we’re 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">PineTime’s <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 let’s 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 don’t 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 PineTime’s 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">
|
||
Mynewt’s <strong class="gq ii">Mbuf Chains</strong> may be used to copy SPI requests easily into a
|
||
RAM space that’s 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">
|
||
Mynewt’s <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">Let’s 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 that’s 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 can’t be sure
|
||
how much RAM we’ll 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">Isn’t 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, here’s 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">Here’s how we
|
||
walk the Mbuf Chain to transmit each block of SPI data in the chain, and deallocate the chain when
|
||
we’re 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
|
||
don’t 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 PineTime’s ST7789 Display Controller. And with limited RAM,
|
||
it’s 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… It’s
|
||
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 it’s 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 PineTime’s 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, here’s 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 we’re 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 there’s 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">? It’s 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 it’s not hung…
|
||
That’s why it’s 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. Here’s 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 it’s 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">What’s 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 we’ll 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 it’s
|
||
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 PineTime’s 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
|
||
--> |