mirror of
https://github.com/lupyuen/lupyuen.github.io.git
synced 2025-01-13 09:08:30 +08:00
1516 lines
No EOL
100 KiB
HTML
1516 lines
No EOL
100 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="generator" content="rustdoc">
|
||
<title>Rust on Apache NuttX OS</title>
|
||
|
||
|
||
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
|
||
<meta property="og:title"
|
||
content="Rust on Apache NuttX OS"
|
||
data-rh="true">
|
||
<meta property="og:description"
|
||
content="How we run Rust programs on Apache NuttX OS... And transmit a LoRa Message with Rust"
|
||
data-rh="true">
|
||
<meta property="og:image"
|
||
content="https://lupyuen.github.io/images/rust2-title.jpg">
|
||
<meta property="og:type"
|
||
content="article" data-rh="true">
|
||
<link rel="canonical" href="https://lupyuen.org/articles/rust2.html" />
|
||
<!-- End scripts/articles/*-header.html -->
|
||
<!-- 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="../dark.css">
|
||
<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");
|
||
}
|
||
</style>
|
||
<!-- End scripts/rustdoc-header.html -->
|
||
|
||
|
||
</head>
|
||
<body class="rustdoc">
|
||
<!--[if lte IE 8]>
|
||
<div class="warning">
|
||
This old browser is unsupported and will most likely display funky
|
||
things.
|
||
</div>
|
||
<![endif]-->
|
||
|
||
|
||
<!-- Begin scripts/rustdoc-before.html: Pre-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
|
||
|
||
<!-- Begin Theme Picker -->
|
||
<div class="theme-picker" style="left: 0"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg"
|
||
width="18" alt="Pick another theme!"></button>
|
||
<div id="theme-choices"></div>
|
||
</div>
|
||
<!-- Theme Picker -->
|
||
|
||
<!-- End scripts/rustdoc-before.html -->
|
||
|
||
|
||
<h1 class="title">Rust on Apache NuttX OS</h1>
|
||
<nav id="rustdoc"><ul>
|
||
<li><a href="#rust-meets-nuttx" title="Rust Meets NuttX">1 Rust Meets NuttX</a><ul></ul></li>
|
||
<li><a href="#putting-things-neatly" title="Putting Things Neatly">2 Putting Things Neatly</a><ul></ul></li>
|
||
<li><a href="#flipping-gpio" title="Flipping GPIO">3 Flipping GPIO</a><ul></ul></li>
|
||
<li><a href="#import-nuttx-functions" title="Import NuttX Functions">4 Import NuttX Functions</a><ul></ul></li>
|
||
<li><a href="#rust-embedded-hal" title="Rust Embedded HAL">5 Rust Embedded HAL</a><ul></ul></li>
|
||
<li><a href="#spi-transfer" title="SPI Transfer">6 SPI Transfer</a><ul></ul></li>
|
||
<li><a href="#rust-driver-for-lora-sx1262" title="Rust Driver for LoRa SX1262">7 Rust Driver for LoRa SX1262</a><ul></ul></li>
|
||
<li><a href="#transmit-lora-message" title="Transmit LoRa Message">8 Transmit LoRa Message</a><ul></ul></li>
|
||
<li><a href="#download-source-code" title="Download Source Code">9 Download Source Code</a><ul></ul></li>
|
||
<li><a href="#build-the-firmware" title="Build The Firmware">10 Build The Firmware</a><ul></ul></li>
|
||
<li><a href="#run-the-firmware" title="Run The Firmware">11 Run The Firmware</a><ul></ul></li>
|
||
<li><a href="#verify-lora-message" title="Verify LoRa Message">12 Verify LoRa Message</a><ul>
|
||
<li><a href="#spectrum-analyser" title="Spectrum Analyser">12.1 Spectrum Analyser</a><ul></ul></li>
|
||
<li><a href="#lora-receiver" title="LoRa Receiver">12.2 LoRa Receiver</a><ul></ul></li></ul></li>
|
||
<li><a href="#lorawan-support" title="LoRaWAN Support">13 LoRaWAN Support</a><ul></ul></li>
|
||
<li><a href="#whats-next" title="What’s Next">14 What’s Next</a><ul></ul></li>
|
||
<li><a href="#notes" title="Notes">15 Notes</a><ul></ul></li>
|
||
<li><a href="#appendix-rust-embedded-hal-for-nuttx" title="Appendix: Rust Embedded HAL for NuttX">16 Appendix: Rust Embedded HAL for NuttX</a><ul>
|
||
<li><a href="#gpio-hal" title="GPIO HAL">16.1 GPIO HAL</a><ul></ul></li>
|
||
<li><a href="#spi-hal" title="SPI HAL">16.2 SPI HAL</a><ul></ul></li>
|
||
<li><a href="#i2c-hal" title="I2C HAL">16.3 I2C HAL</a><ul></ul></li>
|
||
<li><a href="#delay-hal" title="Delay HAL">16.4 Delay HAL</a><ul></ul></li></ul></li>
|
||
<li><a href="#appendix-fix-sx1262-driver-for-nuttx" title="Appendix: Fix SX1262 Driver for NuttX">17 Appendix: Fix SX1262 Driver for NuttX</a><ul>
|
||
<li><a href="#merge-spi-requests" title="Merge SPI Requests">17.1 Merge SPI Requests</a><ul></ul></li>
|
||
<li><a href="#read-register" title="Read Register">17.2 Read Register</a><ul></ul></li>
|
||
<li><a href="#set-registers" title="Set Registers">17.3 Set Registers</a><ul></ul></li>
|
||
<li><a href="#adapt-for-risc-v" title="Adapt For RISC-V">17.4 Adapt For RISC-V</a><ul></ul></li></ul></li>
|
||
<li><a href="#appendix-rust-build-script-for-nuttx" title="Appendix: Rust Build Script for NuttX">18 Appendix: Rust Build Script for NuttX</a><ul>
|
||
<li><a href="#rust-target" title="Rust Target">18.1 Rust Target</a><ul></ul></li>
|
||
<li><a href="#rust-build-options" title="Rust Build Options">18.2 Rust Build Options</a><ul></ul></li>
|
||
<li><a href="#define-libraries" title="Define Libraries">18.3 Define Libraries</a><ul></ul></li>
|
||
<li><a href="#build-stub-library" title="Build Stub Library">18.4 Build Stub Library</a><ul></ul></li>
|
||
<li><a href="#build-rust-library" title="Build Rust Library">18.5 Build Rust Library</a><ul></ul></li>
|
||
<li><a href="#replace-stub-libary-by-rust-library" title="Replace Stub Libary by Rust Library">18.6 Replace Stub Libary by Rust Library</a><ul></ul></li>
|
||
<li><a href="#link-rust-library-into-firmware" title="Link Rust Library into Firmware">18.7 Link Rust Library into Firmware</a><ul></ul></li></ul></li>
|
||
<li><a href="#appendix-build-flash-and-run-nuttx" title="Appendix: Build, Flash and Run NuttX">19 Appendix: Build, Flash and Run NuttX</a><ul>
|
||
<li><a href="#build-nuttx" title="Build NuttX">19.1 Build NuttX</a><ul></ul></li>
|
||
<li><a href="#flash-nuttx" title="Flash NuttX">19.2 Flash NuttX</a><ul></ul></li>
|
||
<li><a href="#run-nuttx" title="Run NuttX">19.3 Run NuttX</a><ul></ul></li></ul></li></ul></nav><p>📝 <em>12 Jan 2022</em></p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-title.jpg" alt="PineDio Stack BL604 RISC-V Board" /></p>
|
||
<p><a href="https://lupyuen.github.io/articles/nuttx"><strong>Apache NuttX</strong></a> is an embedded operating system that’s portable across <strong>many platforms</strong> (8-bit to 64-bit) and works like a <strong>tiny version of Linux</strong> (because it’s POSIX Compliant).</p>
|
||
<p><em>Can we create (safer) Embedded Apps with <strong>Rust on NuttX</strong>?</em></p>
|
||
<p><em>Can we take a Device Driver from <a href="https://github.com/rust-embedded/awesome-embedded-rust#driver-crates"><strong>Rust Embedded</strong></a>… And run it on NuttX?</em></p>
|
||
<p>Today we shall…</p>
|
||
<ol>
|
||
<li>
|
||
<p>Build and run <strong>Rust programs</strong> on NuttX</p>
|
||
</li>
|
||
<li>
|
||
<p>Access <strong>GPIO and SPI ports</strong> with Rust Embedded HAL</p>
|
||
</li>
|
||
<li>
|
||
<p>Run the <strong>Semtech SX1262 LoRa Driver</strong> from Rust Embedded</p>
|
||
</li>
|
||
<li>
|
||
<p>And transmit a <a href="https://makezine.com/2021/05/24/go-long-with-lora-radio/"><strong>LoRa Message</strong></a> over the airwaves with Rust on NuttX!</p>
|
||
</li>
|
||
</ol>
|
||
<p>We tested Rust on NuttX with <a href="https://lupyuen.github.io/articles/pinedio2"><strong>PineDio Stack BL604</strong></a> RISC-V Board (pic above) and its onboard Semtech SX1262 Transceiver.</p>
|
||
<p>But it should work on ESP32, Arm and other NuttX platforms. (With some tweaking)</p>
|
||
<p><strong>Caution:</strong> Work in Progress! Some spots are rough and rocky, I’m hoping the NuttX and Rust Communities could help to fill in the potholes before release 🙏</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-run.png" alt="Rust running on NuttX" /></p>
|
||
<h1 id="rust-meets-nuttx"><a class="doc-anchor" href="#rust-meets-nuttx">§</a>1 Rust Meets NuttX</h1>
|
||
<p>This is the <strong>simplest Rust program</strong> that will run on NuttX and print <em>“Hello World!”</em>: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L22-L56">lib.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="attr">#![no_std] </span><span class="comment">// Use the Rust Core Library instead of the Rust Standard Library, which is not compatible with embedded systems
|
||
|
||
</span><span class="attr">#[no_mangle] </span><span class="comment">// Don't mangle the function name
|
||
</span><span class="kw">extern </span><span class="string">"C" </span><span class="kw">fn </span>rust_main() { <span class="comment">// Declare `extern "C"` because it will be called by NuttX
|
||
|
||
</span><span class="kw">extern </span><span class="string">"C" </span>{ <span class="comment">// Import C Function
|
||
</span><span class="doccomment">/// Print a message to the serial console (from C stdio library)
|
||
</span><span class="kw">fn </span>puts(s: <span class="kw-2">*const </span>u8) -> i32;
|
||
}
|
||
|
||
<span class="kw">unsafe </span>{ <span class="comment">// Mark as unsafe because we are calling C
|
||
// Print a message to the serial console
|
||
</span>puts(
|
||
<span class="string">b"Hello World!\0" </span><span class="comment">// Byte String terminated with null
|
||
</span>.as_ptr() <span class="comment">// Convert to pointer
|
||
</span>);
|
||
}
|
||
}</code></pre></div>
|
||
<p>Let’s break it down from the top…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Use the Rust Core Library instead of the Rust Standard Library,
|
||
// which is not compatible with embedded systems
|
||
</span><span class="attr">#![no_std]</span></code></pre></div>
|
||
<p>We select the <strong>Rust Core Library</strong> (for embedded platforms), which is a subset of the Rust Standard Library (for desktops and servers).</p>
|
||
<p>Next we declare the <strong>Rust Function</strong> that will be called by NuttX…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Don't mangle the function name
|
||
</span><span class="attr">#[no_mangle]
|
||
</span><span class="comment">// Declare `extern "C"` because it will be called by NuttX
|
||
</span><span class="kw">extern </span><span class="string">"C" </span><span class="kw">fn </span>rust_main() {</code></pre></div>
|
||
<p>(Why is it named <strong>“rust_main”</strong>? We’ll find out in a while)</p>
|
||
<p>NuttX provides the <strong>“puts”</strong> function because it’s POSIX Compliant (like Linux), so we import it from C…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Import C Function
|
||
</span><span class="kw">extern </span><span class="string">"C" </span>{
|
||
<span class="doccomment">/// Print a message to the serial console (from C stdio library)
|
||
</span><span class="kw">fn </span>puts(s: <span class="kw-2">*const </span>u8) -> i32;
|
||
}</code></pre></div>
|
||
<p>This declares that <strong>“puts”</strong>…</p>
|
||
<ul>
|
||
<li>
|
||
<p>Accepts a “<code>*const u8</code>” pointer</p>
|
||
<p>(Equivalent to “<code>const uint8_t *</code>” in C)</p>
|
||
</li>
|
||
<li>
|
||
<p>Returns an “<code>i32</code>” result</p>
|
||
<p>(Equivalent to “<code>int32_t</code>” in C)</p>
|
||
</li>
|
||
</ul>
|
||
<p>We call <strong>“puts”</strong> like so…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Mark as unsafe because we are calling C
|
||
</span><span class="kw">unsafe </span>{
|
||
<span class="comment">// Print a message to the serial console
|
||
</span>puts(
|
||
<span class="string">b"Hello World!\0" </span><span class="comment">// Byte String terminated with null
|
||
</span>.as_ptr() <span class="comment">// Convert to pointer
|
||
</span>);
|
||
}</code></pre></div>
|
||
<p>Passing a string from Rust to C looks rather cumbersome…</p>
|
||
<ul>
|
||
<li>
|
||
<p>Calls to C Functions must be marked as <strong>“<code>unsafe</code>”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>We construct a <strong>Byte String</strong> in Rust with the <code>b"..."</code> syntax</p>
|
||
</li>
|
||
<li>
|
||
<p>Rust Strings are not null-terminated! We add the <strong>Null Byte</strong> ourselves with “<code>\0</code>”</p>
|
||
</li>
|
||
<li>
|
||
<p>We call <strong>“<code>.as_ptr()</code>”</strong> to convert the Byte String to a pointer</p>
|
||
</li>
|
||
</ul>
|
||
<p>Though it looks messy, the Rust code above runs perfectly fine from the <strong>NuttX Shell</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>nsh> rust_test
|
||
|
||
Hello World!</code></pre></div>
|
||
<p>We’ll make it neater in the next chapter.</p>
|
||
<p><em>Is there anything we missed?</em></p>
|
||
<p>We need to define a <strong>Panic Handler</strong> that will be called when a Runtime Error or Assertion Failure occurs.</p>
|
||
<p><a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L218-L243">(Our Panic Handler is defined here)</a></p>
|
||
<h1 id="putting-things-neatly"><a class="doc-anchor" href="#putting-things-neatly">§</a>2 Putting Things Neatly</h1>
|
||
<p><em>Do we really need the cumbersome syntax for <strong>“puts”</strong> when we print things?</em></p>
|
||
<p>We can do better! Let’s wrap this cumbersome code…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Mark as unsafe because we are calling C
|
||
</span><span class="kw">unsafe </span>{
|
||
<span class="comment">// Print a message to the serial console
|
||
</span>puts(
|
||
<span class="string">b"Hello World!\0" </span><span class="comment">// Byte String terminated with null
|
||
</span>.as_ptr() <span class="comment">// Convert to pointer
|
||
</span>);
|
||
}</code></pre></div>
|
||
<p>…with a <strong>Rust Macro</strong>. And we’ll get this…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Print a message to the serial console
|
||
</span><span class="macro">println!</span>(<span class="string">"Hello World!"</span>);</code></pre></div>
|
||
<p>Much neater! We’ll see later that <strong>“println!”</strong> supports Formatted Output too.</p>
|
||
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/macros.rs">(<strong>println!</strong> is defined here. Thanks Huang Qi! 👍)</a></p>
|
||
<p><a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L175-L216">(<strong>puts</strong> is wrapped here)</a></p>
|
||
<p><em>Why is our Rust Function named <strong>rust_main</strong> instead of <strong>main</strong>?</em></p>
|
||
<p>Our Rust code (<strong>rust_main</strong>) is compiled into a <strong>Static Library</strong> that will be linked into the NuttX Firmware.</p>
|
||
<p>Our NuttX Firmware contains a NuttX App (<strong>rust_test</strong>) that calls <strong>rust_main</strong> from C: <a href="https://github.com/lupyuen/rust_test/blob/main/rust_test_main.c#L28-L37">rust_test_main.c</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>// Rust Function defined in rust/src/lib.rs
|
||
void rust_main(void);
|
||
|
||
// Our Main Function in C...
|
||
int main(int argc, FAR char *argv[]) {
|
||
// Calls the Rust Function
|
||
rust_main();
|
||
return 0;
|
||
}</code></pre></div>
|
||
<p>Thus it’s indeed possible to call Rust from C… And C from Rust!</p>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2#appendix-rust-build-script-for-nuttx">(More about the Rust build script in the Appendix)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-gpio.png" alt="Rust opening GPIO Ports on NuttX" /></p>
|
||
<h1 id="flipping-gpio"><a class="doc-anchor" href="#flipping-gpio">§</a>3 Flipping GPIO</h1>
|
||
<p>Since we can call NuttX Functions from Rust, let’s <strong>flip a GPIO High and Low</strong> the POSIX way: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L56-L133">lib.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Open GPIO Output
|
||
</span><span class="kw">let </span>cs = <span class="kw">unsafe </span>{
|
||
open(<span class="string">b"/dev/gpio1\0"</span>.as_ptr(), O_RDWR)
|
||
};
|
||
<span class="macro">assert!</span>(cs > <span class="number">0</span>);</code></pre></div>
|
||
<p>We open the GPIO Output at <strong>“/dev/gpio1”</strong> with read-write access.</p>
|
||
<p>Then we call <strong>ioctl</strong> to set the <strong>GPIO Output to Low</strong>…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Set GPIO Output to Low
|
||
</span><span class="kw">let </span>ret = <span class="kw">unsafe </span>{
|
||
ioctl(cs, GPIOC_WRITE, <span class="number">0</span>)
|
||
};
|
||
<span class="macro">assert!</span>(ret >= <span class="number">0</span>);</code></pre></div>
|
||
<p>We sleep for 1 second…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Sleep 1 second
|
||
</span><span class="kw">unsafe </span>{
|
||
sleep(<span class="number">1</span>);
|
||
}</code></pre></div>
|
||
<p>We set the <strong>GPIO Output to High</strong>…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Set GPIO Output to High
|
||
</span><span class="kw">let </span>ret = <span class="kw">unsafe </span>{
|
||
ioctl(cs, GPIOC_WRITE, <span class="number">1</span>)
|
||
};
|
||
<span class="macro">assert!</span>(ret >= <span class="number">0</span>);</code></pre></div>
|
||
<p>Finally we <strong>close the GPIO Output</strong>…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Close the GPIO Output
|
||
</span><span class="kw">unsafe </span>{
|
||
close(cs);
|
||
}</code></pre></div>
|
||
<p>This code works OK for <strong>blinking an LED</strong> on a GPIO pin, but we’ll do something more ambitious… Transfer data over SPI!</p>
|
||
<p><em>Won’t this code get really messy when we do lots of GPIO and SPI?</em></p>
|
||
<p>Yep it might get terribly messy! <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L61-L136">(Like this)</a></p>
|
||
<p>In a while we’ll mop this up with <strong>Rust Embedded HAL</strong>.</p>
|
||
<h1 id="import-nuttx-functions"><a class="doc-anchor" href="#import-nuttx-functions">§</a>4 Import NuttX Functions</h1>
|
||
<p><em>How did we import the NuttX Functions: open, ioctl, sleep, close, …?</em></p>
|
||
<p>We <strong>imported the NuttX Functions</strong> like so: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L248-L257">lib.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">extern </span><span class="string">"C" </span>{ <span class="comment">// Import NuttX Functions. TODO: Import with bindgen
|
||
</span><span class="kw">pub fn </span>open(path: <span class="kw-2">*const </span>u8, oflag: i32, ...) -> i32;
|
||
<span class="kw">pub fn </span>read(fd: i32, buf: <span class="kw-2">*mut </span>u8, count: u32) -> i32;
|
||
<span class="kw">pub fn </span>write(fd: i32, buf: <span class="kw-2">*const </span>u8, count: u32) -> i32;
|
||
<span class="kw">pub fn </span>close(fd: i32) -> i32;
|
||
<span class="kw">pub fn </span>ioctl(fd: i32, request: i32, ...) -> i32; <span class="comment">// On NuttX: request is i32, not u64 like Linux
|
||
</span><span class="kw">pub fn </span>sleep(secs: u32) -> u32;
|
||
<span class="kw">pub fn </span>usleep(usec: u32) -> u32;
|
||
<span class="kw">pub fn </span>exit(status: u32) -> !; <span class="comment">// Does not return
|
||
</span>}</code></pre></div>
|
||
<p>We (very carefully) <strong>imported the NuttX Constants</strong> as well: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L259-L277">lib.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Import NuttX Constants. TODO: Import with bindgen from https://github.com/lupyuen/nuttx/blob/rust/include/nuttx/ioexpander/gpio.h
|
||
</span><span class="kw">pub const </span>GPIOC_WRITE: i32 = _GPIOBASE | <span class="number">1</span>; <span class="comment">// _GPIOC(1)
|
||
</span><span class="kw">pub const </span>GPIOC_READ: i32 = _GPIOBASE | <span class="number">2</span>; <span class="comment">// _GPIOC(2)
|
||
</span><span class="kw">pub const </span>_GPIOBASE: i32 = <span class="number">0x2300</span>; <span class="comment">// GPIO driver commands
|
||
</span><span class="kw">pub const </span>O_RDWR: i32 = O_RDOK|O_WROK; <span class="comment">// Open for both read & write access</span></code></pre></div>
|
||
<p><a href="https://rust-lang.github.io/rust-bindgen/">(Someday we should auto-generate the Rust Bindings for NuttX with the <strong>bindgen</strong> tool)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-hal.png" alt="Rust Embedded HAL" /></p>
|
||
<h1 id="rust-embedded-hal"><a class="doc-anchor" href="#rust-embedded-hal">§</a>5 Rust Embedded HAL</h1>
|
||
<p><em>What is Rust Embedded HAL?</em></p>
|
||
<p><strong>Rust Embedded HAL</strong> (Hardware Abstraction Layer) defines a standard interface that’s used by <strong>Rust Embedded Device Drivers</strong> to access the hardware: GPIO, SPI, I2C, …</p>
|
||
<p><a href="https://github.com/rust-embedded/awesome-embedded-rust#driver-crates">(Check out the Rust Embedded Drivers)</a></p>
|
||
<p><em>What if we implement Rust Embedded HAL for NuttX: GPIO, SPI, I2C, …?</em></p>
|
||
<p>That would be super interesting… It means that we can pick <strong>any Rust Embedded Driver</strong> and run it on NuttX! (Theoretically)</p>
|
||
<p>In a while we’ll test the <strong>Semtech SX1262 LoRa Driver</strong> from Rust Embedded, and see if it works on NuttX!</p>
|
||
<p><em>How do we call Rust Embedded HAL from NuttX?</em></p>
|
||
<p>We have created a <strong>NuttX Embedded HAL</strong> that implements the Rust Embedded HAL on NuttX…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/lupyuen/nuttx-embedded-hal"><strong>lupyuen/nuttx-embedded-hal</strong></a></li>
|
||
</ul>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2#appendix-rust-embedded-hal-for-nuttx">(More details in the Appendix)</a></p>
|
||
<p>To call it, we add <strong>embedded-hal</strong> and <strong>nuttx-embedded-hal</strong> as dependencies to our <a href="https://github.com/lupyuen/rust_test/blob/main/rust/Cargo.toml#L8-L16"><strong>Cargo.toml</strong></a>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>## External Rust libraries used by this module. See crates.io.
|
||
[dependencies]
|
||
|
||
## Rust Embedded HAL: https://crates.io/crates/embedded-hal
|
||
embedded-hal = "0.2.7"
|
||
|
||
## NuttX Embedded HAL: https://crates.io/crates/nuttx-embedded-hal
|
||
nuttx-embedded-hal = "1.0.10"
|
||
|
||
## SX126x LoRa Radio Driver fixed for NuttX
|
||
sx126x = { git = "https://github.com/lupyuen/sx126x-rs-nuttx" } </code></pre></div>
|
||
<p><a href="https://crates.io/crates/nuttx-embedded-hal">(Always use the latest version of <strong>nuttx-embedded-hal</strong>)</a></p>
|
||
<p>(We’ll see the <strong>sx126x</strong> driver in a while)</p>
|
||
<p>We import the <strong>Rust Embedded Traits</strong> (GPIO, SPI and Delay) that we’ll call from our app: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L12-L18">lib.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Import Embedded Traits
|
||
</span><span class="kw">use </span>embedded_hal::{ <span class="comment">// Rust Embedded HAL
|
||
</span>digital::v2::OutputPin, <span class="comment">// GPIO Output
|
||
</span>blocking::{ <span class="comment">// Blocking I/O
|
||
</span>delay::DelayMs, <span class="comment">// Delay Interface
|
||
</span>spi::Transfer, <span class="comment">// SPI Transfer
|
||
</span>},
|
||
};</code></pre></div>
|
||
<p>To open GPIO Output <strong>“/dev/gpio1”</strong> we do this: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L133-L174">lib.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Open GPIO Output
|
||
</span><span class="kw">let </span><span class="kw-2">mut </span>cs = nuttx_embedded_hal::OutputPin
|
||
::new(<span class="string">"/dev/gpio1"</span>)
|
||
.expect(<span class="string">"open gpio failed"</span>);</code></pre></div>
|
||
<p>(This halts with an error if “/dev/gpio1” doesn’t exist)</p>
|
||
<p>We declare it as <strong>“<code>mut</code>”</strong> (mutable) because we expect its Internal State to change as we flip the GPIO.</p>
|
||
<p>Next we fetch the <strong>Delay Interface</strong> that we’ll call to sleep…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Get a Delay Interface
|
||
</span><span class="kw">let </span><span class="kw-2">mut </span>delay = nuttx_embedded_hal::Delay;</code></pre></div>
|
||
<p>Then we set the <strong>GPIO Output to Low</strong>…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Set GPIO Output to Low
|
||
</span>cs.set_low()
|
||
.expect(<span class="string">"cs failed"</span>);</code></pre></div>
|
||
<p>(“<code>expect</code>” works like an Assertion Check)</p>
|
||
<p>We sleep for 1 second…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Wait 1 second (1,000 milliseconds)
|
||
</span>delay.delay_ms(<span class="number">1000_u32</span>);</code></pre></div>
|
||
<p>(“<code>u32</code>” says that this is an unsigned 32-bit integer)</p>
|
||
<p>Finally we set the <strong>GPIO Output to High</strong>…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Set GPIO Output to High
|
||
</span>cs.set_high()
|
||
.expect(<span class="string">"cs failed"</span>);</code></pre></div>
|
||
<p>Rust Embedded HAL makes GPIO programming more fun! Let’s do SPI now.</p>
|
||
<p><img src="https://lupyuen.github.io/images/spi2-pinedio1.jpg" alt="Inside PineDio Stack BL604" /></p>
|
||
<h1 id="spi-transfer"><a class="doc-anchor" href="#spi-transfer">§</a>6 SPI Transfer</h1>
|
||
<p>Let’s test SPI Data Transfer to the <a href="https://www.semtech.com/products/wireless-rf/lora-core/sx1262"><strong>Semtech SX1262 LoRa Transceiver</strong></a>.</p>
|
||
<p>For PineDio Stack BL604 with its onboard SX1262 (pic above), we control <strong>SPI Chip Select</strong> ourselves via GPIO Output <strong>“/dev/gpio1”</strong></p>
|
||
<p>We begin by opening the <strong>GPIO Output</strong> for SPI Chip Select: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs#L133-L174">lib.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Test the NuttX Embedded HAL by reading SX1262 Register 8
|
||
</span><span class="kw">fn </span>test_hal() {
|
||
|
||
<span class="comment">// Open GPIO Output for SX1262 Chip Select
|
||
</span><span class="kw">let </span><span class="kw-2">mut </span>cs = nuttx_embedded_hal::OutputPin
|
||
::new(<span class="string">"/dev/gpio1"</span>)
|
||
.expect(<span class="string">"open gpio failed"</span>);</code></pre></div>
|
||
<p>Next we open the <strong>SPI Bus</strong>…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Open SPI Bus for SX1262
|
||
</span><span class="kw">let </span><span class="kw-2">mut </span>spi = nuttx_embedded_hal::Spi
|
||
::new(<span class="string">"/dev/spitest0"</span>)
|
||
.expect(<span class="string">"open spi failed"</span>);</code></pre></div>
|
||
<p><strong>“/dev/spitest0”</strong> is our <strong>SPI Test Driver</strong> that simplifies SPI programming. <a href="https://lupyuen.github.io/articles/spi2">(See this)</a></p>
|
||
<p>Before talking to SX1262, we set <strong>Chip Select to Low</strong>…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Set SX1262 Chip Select to Low
|
||
</span>cs.set_low()
|
||
.expect(<span class="string">"cs failed"</span>);</code></pre></div>
|
||
<p>We transmit <strong>5 bytes of data</strong> to SX1262 over SPI…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Define the SX1262 Command: Read Register 8
|
||
</span><span class="kw">let </span><span class="kw-2">mut </span>data: [ u8; <span class="number">5 </span>] = [ <span class="number">0x1d</span>, <span class="number">0x00</span>, <span class="number">0x08</span>, <span class="number">0x00</span>, <span class="number">0x00 </span>];
|
||
|
||
<span class="comment">// Transfer the command to SX1262 over SPI
|
||
</span>spi.transfer(<span class="kw-2">&mut </span>data)
|
||
.expect(<span class="string">"spi failed"</span>);</code></pre></div>
|
||
<p>The data transmitted over SPI is the <strong>SX1262 Command</strong> that will read <strong>SX1262 Register 8</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code> 1D 00 08 00 00</code></pre></div>
|
||
<p>We pass the data as a <strong>Mutable Reference</strong> “<code>&mut</code>” because we expect the contents to be changed during the SPI Transfer.</p>
|
||
<p>The value of SX1262 Register 8 is returned as the <strong>last byte</strong> of the SPI Response…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="macro">println!</span>(<span class="string">"test_hal: SX1262 Register 8 is 0x{:02x}"</span>, data[<span class="number">4</span>]);</code></pre></div>
|
||
<p>We set <strong>Chip Select to High</strong>…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Set SX1262 Chip Select to High
|
||
</span>cs.set_high()
|
||
.expect(<span class="string">"cs failed"</span>);</code></pre></div>
|
||
<p>And we’re done! Running this Rust code on NuttX shows…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>nsh> rust_test
|
||
...
|
||
test_hal: SX1262 Register 8 is 0x80</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/412cc8bef51c40236767e10693c738b5">(See the Output Log)</a></p>
|
||
<p>That’s the correct value of SX1262 Register 8: <strong><code>0x80</code></strong>!</p>
|
||
<p>(Later we’ll talk about building and flashing the NuttX Firmware)</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-hal2.png" alt="Calling the Rust Driver for LoRa SX1262" /></p>
|
||
<h1 id="rust-driver-for-lora-sx1262"><a class="doc-anchor" href="#rust-driver-for-lora-sx1262">§</a>7 Rust Driver for LoRa SX1262</h1>
|
||
<p><em>Can we pick ANY Device Driver from <a href="https://github.com/rust-embedded/awesome-embedded-rust#driver-crates"><strong>Rust Embedded</strong></a>…</em></p>
|
||
<p><em>And run it on NuttX?</em></p>
|
||
<p>Now that we have a (barebones) <strong>Rust Embedded HAL</strong> for NuttX, let’s find out!</p>
|
||
<p>We’ll test this Rust Embedded Driver for Semtech SX1262…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/lupyuen/sx126x-rs-nuttx"><strong>lupyuen/sx126x-rs-nuttx</strong></a></li>
|
||
</ul>
|
||
<p>That we tweaked slightly from <strong><a href="https://github.com/tweedegolf/sx126x-rs">tweedegolf/sx126x-rs</a></strong></p>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2#appendix-fix-sx1262-driver-for-nuttx">(Details in the Appendix. Thanks Tweede golf! 👍)</a></p>
|
||
<p>Let’s do the same test as last chapter: <strong>Read SX1262 Register 8</strong></p>
|
||
<p>We begin by opening the <strong>GPIO Input, Output and Interrupt Pins</strong> for SX1262: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/sx1262.rs#L21-L84">sx1262.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Test the SX1262 Driver by reading a register.
|
||
/// Based on https://github.com/tweedegolf/sx126x-rs/blob/master/examples/stm32f103-ping-pong.rs
|
||
</span><span class="kw">pub fn </span>test_sx1262() {
|
||
|
||
<span class="comment">// Open GPIO Input for SX1262 Busy Pin
|
||
</span><span class="kw">let </span>lora_busy = nuttx_embedded_hal::InputPin
|
||
::new(<span class="string">"/dev/gpio0"</span>)
|
||
.expect(<span class="string">"open gpio failed"</span>);
|
||
|
||
<span class="comment">// Open GPIO Output for SX1262 Chip Select
|
||
</span><span class="kw">let </span>lora_nss = nuttx_embedded_hal::OutputPin
|
||
::new(<span class="string">"/dev/gpio1"</span>)
|
||
.expect(<span class="string">"open gpio failed"</span>);
|
||
|
||
<span class="comment">// Open GPIO Interrupt for SX1262 DIO1 Pin
|
||
</span><span class="kw">let </span>lora_dio1 = nuttx_embedded_hal::InterruptPin
|
||
::new(<span class="string">"/dev/gpio2"</span>)
|
||
.expect(<span class="string">"open gpio failed"</span>);</code></pre></div>
|
||
<p>(We won’t handle interrupts today)</p>
|
||
<p>The <strong>NRESET and Antenna Pins</strong> are unused for now…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// TODO: Open GPIO Output for SX1262 NRESET Pin
|
||
</span><span class="kw">let </span>lora_nreset = nuttx_embedded_hal::UnusedPin
|
||
::new()
|
||
.expect(<span class="string">"open gpio failed"</span>);
|
||
|
||
<span class="comment">// TODO: Open GPIO Output for SX1262 Antenna Pin
|
||
</span><span class="kw">let </span>lora_ant = nuttx_embedded_hal::UnusedPin
|
||
::new()
|
||
.expect(<span class="string">"open gpio failed"</span>);
|
||
|
||
<span class="comment">// Open SPI Bus for SX1262
|
||
</span><span class="kw">let </span><span class="kw-2">mut </span>spi1 = nuttx_embedded_hal::Spi
|
||
::new(<span class="string">"/dev/spitest0"</span>)
|
||
.expect(<span class="string">"open spi failed"</span>);</code></pre></div>
|
||
<p>And we open the <strong>SPI Bus</strong> like before.</p>
|
||
<p>We <strong>define the pins</strong> for our SX1262 Driver…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Define the SX1262 Pins
|
||
</span><span class="kw">let </span>lora_pins = (
|
||
lora_nss, <span class="comment">// /dev/gpio1
|
||
</span>lora_nreset, <span class="comment">// TODO
|
||
</span>lora_busy, <span class="comment">// /dev/gpio0
|
||
</span>lora_ant, <span class="comment">// TODO
|
||
</span>lora_dio1, <span class="comment">// /dev/gpio2
|
||
</span>);
|
||
|
||
<span class="comment">// Init a busy-waiting delay
|
||
</span><span class="kw">let </span>delay = <span class="kw-2">&mut </span>nuttx_hal::Delay;</code></pre></div>
|
||
<p>We <strong>initialise the SX1262 Driver</strong>…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Build the SX1262 Configuration
|
||
</span><span class="kw">let </span>conf = build_config();
|
||
|
||
<span class="comment">// Construct the SX1262 Driver
|
||
</span><span class="kw">let </span><span class="kw-2">mut </span>lora = SX126x::new(lora_pins);
|
||
|
||
<span class="comment">// Init the SX1262 Driver
|
||
</span>lora.init(<span class="kw-2">&mut </span>spi1, delay, conf)
|
||
.expect(<span class="string">"sx1262 init failed"</span>);</code></pre></div>
|
||
<p><a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/sx1262.rs#L117-L157">(<strong>build_config</strong> is defined here)</a></p>
|
||
<p>Lastly we <strong>read SX1262 Register 8</strong> and print the result…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Init Result Buffer as 1 byte of 0x00
|
||
</span><span class="kw">let </span><span class="kw-2">mut </span>result: [ u8; <span class="number">1 </span>] = [ <span class="number">0</span>; <span class="number">1 </span>];
|
||
|
||
<span class="comment">// Read SX1262 Register 8 into Result Buffer
|
||
</span>lora.read_register(<span class="kw-2">&mut </span>spi1, delay, <span class="number">8</span>, <span class="kw-2">&mut </span>result)
|
||
.expect(<span class="string">"sx1262 read register failed"</span>);
|
||
|
||
<span class="comment">// Show the register value
|
||
</span><span class="macro">println!</span>(<span class="string">"test_sx1262: SX1262 Register 8 is 0x{:02x}"</span>, result[<span class="number">0</span>]);</code></pre></div>
|
||
<p>When we run the Rust code we’ll see…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>nsh> rust_test
|
||
...
|
||
test_sx1262: SX1262 Register 8 is 0x80</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/412cc8bef51c40236767e10693c738b5">(See the Output Log)</a></p>
|
||
<p>Which is the same result from the previous chapter. Yep the Rust Driver works OK with our NuttX Embedded HAL!</p>
|
||
<p>Let’s test the Rust Driver to the limit… And send a LoRa Message over the airwaves!</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-transmit2.png" alt="Transmit LoRa Message" /></p>
|
||
<h1 id="transmit-lora-message"><a class="doc-anchor" href="#transmit-lora-message">§</a>8 Transmit LoRa Message</h1>
|
||
<p>For our final test we shall transmit a <a href="https://makezine.com/2021/05/24/go-long-with-lora-radio/"><strong>LoRa Message</strong></a> with the Rust Driver for SX1262.</p>
|
||
<p>We configure the <strong>LoRa Frequency</strong> for our region like so: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/sx1262.rs#L14-L17">sx1262.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// TODO: Change this to your LoRa Frequency
|
||
</span><span class="comment">// const RF_FREQUENCY: u32 = 868_000_000; // 868 MHz (EU)
|
||
// const RF_FREQUENCY: u32 = 915_000_000; // 915 MHz (US)
|
||
</span><span class="kw">const </span>RF_FREQUENCY: u32 = <span class="number">923_000_000</span>; <span class="comment">// 923 MHz (Asia)</span></code></pre></div>
|
||
<p>We prepare for LoRa Transmission by <strong>setting some SX1262 Registers</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/sx1262.rs#L85-L115">sx1262.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Transmit a LoRa Message.
|
||
/// Based on https://github.com/tweedegolf/sx126x-rs/blob/master/examples/stm32f103-ping-pong.rs
|
||
</span><span class="kw">pub fn </span>test_sx1262() {
|
||
<span class="comment">// Omitted: Init the SX1262 Driver
|
||
</span>...
|
||
<span class="comment">// Write SX1262 Registers to prepare for transmitting LoRa message.
|
||
// Based on https://gist.github.com/lupyuen/5fdede131ad0e327478994872f190668
|
||
// and https://docs.google.com/spreadsheets/d/14Pczf2sP_Egnzi5_nikukauL2iTKA03Qgq715e50__0/edit?usp=sharing
|
||
|
||
// Write Register 0x889: 0x04 (TxModulation)
|
||
</span>lora.write_register(<span class="kw-2">&mut </span>spi1, delay, Register::TxModulaton, <span class="kw-2">&</span>[<span class="number">0x04</span>])
|
||
.expect(<span class="string">"write register failed"</span>);
|
||
|
||
<span class="comment">// Write Register 0x8D8: 0xFE (TxClampConfig)
|
||
</span>lora.write_register(<span class="kw-2">&mut </span>spi1, delay, Register::TxClampConfig, <span class="kw-2">&</span>[<span class="number">0xFE</span>])
|
||
.expect(<span class="string">"write register failed"</span>);
|
||
|
||
<span class="comment">// Write Register 0x8E7: 0x38 (Over Current Protection)
|
||
</span>lora.write_register(<span class="kw-2">&mut </span>spi1, delay, Register::OcpConfiguration, <span class="kw-2">&</span>[<span class="number">0x38</span>])
|
||
.expect(<span class="string">"write register failed"</span>);
|
||
|
||
<span class="comment">// Write Register 0x736: 0x0D (Inverted IQ)
|
||
</span>lora.write_register(<span class="kw-2">&mut </span>spi1, delay, Register::IqPolaritySetup, <span class="kw-2">&</span>[<span class="number">0x0D</span>])
|
||
.expect(<span class="string">"write register failed"</span>);</code></pre></div>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2#set-registers">(More about this)</a></p>
|
||
<p>Then we <strong>transmit a LoRa Message</strong> over the airwaves…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Send a LoRa message
|
||
</span>lora.write_bytes(
|
||
<span class="kw-2">&mut </span>spi1, <span class="comment">// SPI Interface
|
||
</span>delay, <span class="comment">// Delay Interface
|
||
</span><span class="string">b"Hello from Rust on NuttX!"</span>, <span class="comment">// Payload
|
||
</span><span class="number">0</span>.into(), <span class="comment">// Disable Transmit Timeout
|
||
</span><span class="number">8</span>, <span class="comment">// Preamble Length
|
||
</span>packet::lora::LoRaCrcType::CrcOn, <span class="comment">// Enable CRC
|
||
</span>).expect(<span class="string">"send failed"</span>);</code></pre></div>
|
||
<p>Containing the <strong>Message Payload</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>Hello from Rust on NuttX!</code></pre></div>
|
||
<p>And we’re done! We’ll see the results in a while. But first we run through the steps to build and flash our Rusty NuttX Firmware.</p>
|
||
<h1 id="download-source-code"><a class="doc-anchor" href="#download-source-code">§</a>9 Download Source Code</h1>
|
||
<p>To run Rust on NuttX, download the modified source code for <strong>NuttX OS and NuttX Apps</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>mkdir nuttx
|
||
cd nuttx
|
||
git clone --recursive --branch rusti2c https://github.com/lupyuen/nuttx nuttx
|
||
git clone --recursive --branch rusti2c https://github.com/lupyuen/nuttx-apps apps</code></pre></div>
|
||
<p>Or if we prefer to <strong>add the Rust Library and App</strong> to our NuttX Project, follow these instructions…</p>
|
||
<ol>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/rust-nuttx"><strong>“Install Rust Library”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/rust_test"><strong>“Install Rust Test App”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nuttx/tree/lorawan/drivers/rf"><strong>“Install SPI Test Driver”</strong></a></p>
|
||
</li>
|
||
</ol>
|
||
<p><a href="https://lupyuen.github.io/articles/pinedio2#appendix-bundled-features">(<strong>For PineDio Stack BL604:</strong> The Rust Library and App are already preinstalled)</a></p>
|
||
<h1 id="build-the-firmware"><a class="doc-anchor" href="#build-the-firmware">§</a>10 Build The Firmware</h1>
|
||
<p>Let’s build the NuttX Firmware that contains our <strong>Rust App</strong>…</p>
|
||
<ol>
|
||
<li>
|
||
<p>Install the build prerequisites…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/nuttx#install-prerequisites"><strong>“Install Prerequisites”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Assume that we have downloaded the <strong>NuttX Source Code</strong>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2#download-source-code"><strong>“Download Source Code”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Edit the <strong>Pin Definitions</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>## For BL602 and BL604:
|
||
nuttx/boards/risc-v/bl602/bl602evb/include/board.h
|
||
|
||
## For ESP32: Change "esp32-devkitc" to our ESP32 board
|
||
nuttx/boards/xtensa/esp32/esp32-devkitc/src/esp32_gpio.c</code></pre></div>
|
||
<p>Check that the <strong>Semtech SX1262 Pins</strong> are configured correctly in <a href="https://github.com/lupyuen/nuttx/blob/lorawan/boards/risc-v/bl602/bl602evb/include/board.h#L36-L95"><strong>board.h</strong></a> or <a href="https://github.com/lupyuen/nuttx/blob/lorawan/boards/xtensa/esp32/esp32-devkitc/src/esp32_gpio.c#L43-L67"><strong>esp32_gpio.c</strong></a>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/expander#pin-functions">(Which pins can be used? See this)</a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/sx1262#connect-sx1262-transceiver"><strong>“Connect SX1262 Transceiver”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Configure the build…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>cd nuttx
|
||
|
||
## For BL602: Configure the build for BL602
|
||
./tools/configure.sh bl602evb:nsh
|
||
|
||
## For PineDio Stack BL604: Configure the build for BL604
|
||
./tools/configure.sh bl602evb:pinedio
|
||
|
||
## For ESP32: Configure the build for ESP32.
|
||
## TODO: Change "esp32-devkitc" to our ESP32 board.
|
||
./tools/configure.sh esp32-devkitc:nsh
|
||
|
||
## Edit the Build Config
|
||
make menuconfig </code></pre></div></li>
|
||
<li>
|
||
<p>Enable the <strong>GPIO Driver</strong> in menuconfig…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/nuttx#enable-gpio-driver"><strong>“Enable GPIO Driver”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable the <strong>SPI Peripheral</strong>, <strong>SPI Character Driver</strong> and <strong>SPI Test Driver</strong>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/spi2#enable-spi"><strong>“Enable SPI”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable <strong>GPIO and SPI Logging</strong> for easier troubleshooting…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/spi2#enable-logging"><strong>“Enable Logging”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable <strong>Stack Canaries</strong> for stack checking…</p>
|
||
<p>Check the box for <strong>“Build Setup”</strong> → <strong>“Debug Options”</strong> → <strong>“Compiler Stack Canaries”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable <strong>Stack Backtrace</strong> for easier troubleshooting…</p>
|
||
<p>Check the box for <strong>“RTOS Features”</strong> → <strong>“Stack Backtrace”</strong></p>
|
||
<p><a href="https://lupyuen.github.io/images/lorawan3-config4.png">(See this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable our <strong>Rust Library</strong>…</p>
|
||
<p>Check the box for <strong>“Library Routines”</strong> → <strong>“Rust Library”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable our <strong>Rust Test App</strong>…</p>
|
||
<p>Check the box for <strong>“Application Configuration”</strong> → <strong>“Examples”</strong> → <strong>“Rust Test App”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Save the configuration and exit menuconfig</p>
|
||
<p><a href="https://gist.github.com/lupyuen/2857bdc21a4bcd5bb868eae78cf44826">(See the .config for BL602 and BL604)</a></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>For ESP32:</strong> Edit the function <strong>esp32_bringup</strong> in this file…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>## Change "esp32-devkitc" to our ESP32 board
|
||
nuttx/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c</code></pre></div>
|
||
<p>And call <strong>spi_test_driver_register</strong> to register our SPI Test Driver.</p>
|
||
<p><a href="https://lupyuen.github.io/articles/spi2#register-device-driver">(See this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Build, flash and run the NuttX Firmware on BL602 or ESP32…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2#appendix-build-flash-and-run-nuttx"><strong>“Build, Flash and Run NuttX”</strong></a></p>
|
||
</li>
|
||
</ol>
|
||
<p><img src="https://lupyuen.github.io/images/spi2-pinedio10a.jpg" alt="PineDio Stack BL604 with Antenna" /></p>
|
||
<h1 id="run-the-firmware"><a class="doc-anchor" href="#run-the-firmware">§</a>11 Run The Firmware</h1>
|
||
<p>We’re ready to run the NuttX Firmware and test our <strong>Rust App</strong>!</p>
|
||
<ol>
|
||
<li>
|
||
<p>Before testing, remember to connect the <strong>LoRa Antenna</strong>, as shown in the pic above.</p>
|
||
<p>(So we don’t fry the SX1262 Transceiver as we charge up the Power Amplifier)</p>
|
||
</li>
|
||
<li>
|
||
<p>In the NuttX Shell, list the <strong>NuttX Devices</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>ls /dev</code></pre></div></li>
|
||
<li>
|
||
<p>We should see…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>/dev:
|
||
gpio0
|
||
gpio1
|
||
gpio2
|
||
spi0
|
||
spitest0
|
||
...</code></pre></div>
|
||
<p>Our SPI Test Driver appears as <strong>“/dev/spitest0”</strong></p>
|
||
<p>The SX1262 Pins for Busy, Chip Select and DIO1 should appear as <strong>“/dev/gpio0”</strong> (GPIO Input), <strong>“gpio1”</strong> (GPIO Output) and <strong>“gpio2”</strong> (GPIO Interrupt) respectively.</p>
|
||
</li>
|
||
<li>
|
||
<p>In the NuttX Shell, run our <strong>Rust App</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>rust_test</code></pre></div></li>
|
||
<li>
|
||
<p>We should see Rust on NuttX <strong>transmitting our LoRa Message</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>Sending LoRa message...
|
||
Frequency: 923000000
|
||
...
|
||
Done!</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/412cc8bef51c40236767e10693c738b5">(See the Output Log)</a></p>
|
||
</li>
|
||
</ol>
|
||
<p>Let’s check whether Rust on NuttX has successfully transmitted our LoRa Message.</p>
|
||
<p><img src="https://lupyuen.github.io/images/sx1262-title.jpg" alt="PineDio Stack BL604 RISC-V Board with onboard Semtech SX1262 LoRa Transceiver (left)… Sniffed wirelessly with Airspy R2 Software Defined Radio (right)" /></p>
|
||
<p><em>PineDio Stack BL604 RISC-V Board with onboard Semtech SX1262 LoRa Transceiver (left)… Sniffed wirelessly with Airspy R2 Software Defined Radio (right)</em></p>
|
||
<h1 id="verify-lora-message"><a class="doc-anchor" href="#verify-lora-message">§</a>12 Verify LoRa Message</h1>
|
||
<p><em>Did Rust on NuttX transmit our LoRa Message successfully?</em></p>
|
||
<p>Let’s verify the LoRa Transmission in two ways…</p>
|
||
<ol>
|
||
<li>
|
||
<p>With a <strong>Spectrum Analyser</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>With a <strong>LoRa Receiver</strong></p>
|
||
</li>
|
||
</ol>
|
||
<h2 id="spectrum-analyser"><a class="doc-anchor" href="#spectrum-analyser">§</a>12.1 Spectrum Analyser</h2>
|
||
<p>We use a <strong>Spectrum Analyser</strong> (like Airspy R2, pic above) to sniff the airwaves…</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-chirp2.jpg" alt="LoRa Chirp recorded by Cubic SDR connected to Airspy R2 SDR" /></p>
|
||
<p>This shows that our LoRa Message was transmitted…</p>
|
||
<ol>
|
||
<li>
|
||
<p>At the right <strong>Radio Frequency</strong></p>
|
||
<p>(923 MHz)</p>
|
||
</li>
|
||
<li>
|
||
<p>With <strong>sufficient power</strong></p>
|
||
<p>(Because of the red bar)</p>
|
||
</li>
|
||
</ol>
|
||
<p>LoRa Messages have a characteristic criss-cross shape known as <strong>LoRa Chirp</strong>. More about this…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/lora#visualise-lora-with-software-defined-radio"><strong>“Visualise LoRa with Software Defined Radio”</strong></a></li>
|
||
</ul>
|
||
<p><img src="https://lupyuen.github.io/images/wisblock-title.jpg" alt="RAKwireless WisBlock LPWAN Module mounted on WisBlock Base Board" /></p>
|
||
<h2 id="lora-receiver"><a class="doc-anchor" href="#lora-receiver">§</a>12.2 LoRa Receiver</h2>
|
||
<p>Next we use <strong>RAKwireless WisBlock</strong> (pic above) as a LoRa Receiver. We run this Arduino code on WisBlock…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/lupyuen/wisblock-lora-receiver"><strong>wisblock-lora-receiver</strong></a></li>
|
||
</ul>
|
||
<p>Check that the <strong>LoRa Parameters</strong> are correct…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/lupyuen/wisblock-lora-receiver/blob/main/src/main.cpp#L37-L56"><strong>LoRa Parameters for WisBlock Receiver</strong></a></li>
|
||
</ul>
|
||
<p>In the NuttX Shell, enter this to transmit a LoRa Message…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>rust_test</code></pre></div>
|
||
<p>On WisBlock we should see the received <strong>LoRa Message</strong>…</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-receive.png" alt="RAKwireless WisBlock receives LoRa Message from Rust on NuttX" /></p>
|
||
<p>Which is ASCII for…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>Hello from Rust on NuttX!</code></pre></div>
|
||
<p>Our SX1262 Rust Driver has successfully transmitted a LoRa Message to RAKwireless WisBlock!</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-title.jpg" alt="PineDio Stack BL604 RISC-V Board (left) talking LoRaWAN to RAKwireless WisGate LoRaWAN Gateway (right)" /></p>
|
||
<p><em>PineDio Stack BL604 RISC-V Board (left) talking LoRaWAN to RAKwireless WisGate LoRaWAN Gateway (right)</em></p>
|
||
<h1 id="lorawan-support"><a class="doc-anchor" href="#lorawan-support">§</a>13 LoRaWAN Support</h1>
|
||
<p><em>What about LoRaWAN on Rust?</em></p>
|
||
<p>We need LoRaWAN if we wish to <strong>route LoRa Packets securely</strong> to a Local Area Network (ChirpStack) or to the internet (The Things Network).</p>
|
||
<p>Sadly we <strong>haven’t found a Complete LoRaWAN Stack</strong> for Rust yet.</p>
|
||
<p>(Probably because LoRaWAN is super complex… We need to sync up the Regional Parameters with the LoRaWAN Spec whenever LoRaWAN Regions are added or modified)</p>
|
||
<p>But we have a <strong>working LoRaWAN Stack for NuttX</strong> (in C) that’s based on the official LoRaWAN Stack by Semtech…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/lorawan3"><strong>“LoRaWAN on Apache NuttX OS”</strong></a></li>
|
||
</ul>
|
||
<p>So perhaps our Rust code could <strong>call out to the LoRaWAN Stack</strong> in C and interoperate.</p>
|
||
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>14 What’s Next</h1>
|
||
<p>In the next article we’ll talk about <strong>Rust and I2C</strong> on NuttX…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/rusti2c"><strong>“Rust talks I2C on Apache NuttX RTOS”</strong></a></li>
|
||
</ul>
|
||
<p>If you’re keen to make <strong>Rust on NuttX</strong> better, please lemme know! 🙏</p>
|
||
<p>Many Thanks to my <a href="https://lupyuen.github.io/articles/sponsor"><strong>GitHub Sponsors</strong></a> for supporting my work! This article wouldn’t have been possible without your support.</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/sponsor">Sponsor me a coffee</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://www.reddit.com/r/rust/comments/s1qojy/rust_on_apache_nuttx_os/">Discuss this article on Reddit</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/book">Read “The RISC-V BL602 / BL604 Book”</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>
|
||
<p><em>Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…</em></p>
|
||
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/src/rust2.md"><code>lupyuen.github.io/src/rust2.md</code></a></p>
|
||
<h1 id="notes"><a class="doc-anchor" href="#notes">§</a>15 Notes</h1>
|
||
<ol>
|
||
<li>
|
||
<p>This article is the expanded version of <a href="https://twitter.com/MisterTechBlog/status/1478959963930169345">this Twitter Thread</a></p>
|
||
</li>
|
||
<li>
|
||
<p>This article was inspired by Huang Qi’s Rust Wrapper for NuttX…</p>
|
||
<p><a href="https://github.com/no1wudi/nuttx.rs"><strong>no1wudi/nuttx.rs</strong></a></p>
|
||
<p>Which has many features that will be very useful for our implementation of Rust Embedded HAL.</p>
|
||
</li>
|
||
<li>
|
||
<p>Since NuttX behaves like Linux, can we use the <a href="https://crates.io/crates/libc"><strong><code>libc</code></strong></a> crate to import the POSIX Functions?</p>
|
||
<p>Possibly, if we extend <code>libc</code> to cover NuttX.</p>
|
||
<p>Note that the Function Signatures are slightly different: <code>libc</code> declares <strong>ioctl</strong> as…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">fn </span>ioctl(fd: i32, request: u64, ...) -> i32</code></pre></div>
|
||
<p><a href="https://docs.rs/libc/latest/libc/fn.ioctl.html">(Source)</a></p>
|
||
<p>Whereas NuttX declares <strong>ioctl</strong> as…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">fn </span>ioctl(fd: i32, request: i32, ...) -> i32</code></pre></div>
|
||
<p><a href="https://github.com/apache/nuttx/blob/master/include/sys/ioctl.h#L114">(Source)</a></p>
|
||
<p>The type of the <strong>request</strong> parameter is different: <strong><code>u64</code> vs <code>i32</code></strong>.</p>
|
||
<p>So beware!</p>
|
||
</li>
|
||
<li>
|
||
<p>What about the <a href="https://crates.io/crates/nix"><strong><code>nix</code></strong></a> crate?</p>
|
||
<p><code>nix</code> doesn’t support <code>no_std</code> yet, so sorry nope.</p>
|
||
<p><a href="https://github.com/nix-rust/nix/issues/281">(See this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Instead of <code>no_std</code>, can we run the Standard Rust Library on NuttX?</p>
|
||
<p>Sony worked on porting Standard Rust Library to NuttX, but it appears to be incomplete.</p>
|
||
<p><a href="https://speakerdeck.com/sgy/cortex-m4f-and-prototyping-a-simple-web-server">(See this)</a></p>
|
||
</li>
|
||
</ol>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-hal3.png" alt="GPIO HAL" /></p>
|
||
<h1 id="appendix-rust-embedded-hal-for-nuttx"><a class="doc-anchor" href="#appendix-rust-embedded-hal-for-nuttx">§</a>16 Appendix: Rust Embedded HAL for NuttX</h1>
|
||
<p>This section explains how we implemented the <strong>Rust Embedded HAL for NuttX</strong>…</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal"><strong>lupyuen/nuttx-embedded-hal</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://docs.rs/nuttx-embedded-hal"><strong>Documentation for nutt-embedded-hal</strong></a></p>
|
||
</li>
|
||
</ul>
|
||
<h2 id="gpio-hal"><a class="doc-anchor" href="#gpio-hal">§</a>16.1 GPIO HAL</h2>
|
||
<p>Let’s look at the HAL for <strong>GPIO Output</strong> (OutputPin), since GPIO Input (InputPin) and GPIO Interrupt (InterruptPin) are implemented the same way.</p>
|
||
<p>Our <strong>OutputPin Struct</strong> contains a <strong>NuttX File Descriptor</strong>: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L479-L485">nuttx-embedded-hal/src/hal.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX GPIO Output Struct
|
||
</span><span class="kw">pub struct </span>OutputPin {
|
||
<span class="doccomment">/// NuttX File Descriptor
|
||
</span>fd: i32,
|
||
}</code></pre></div>
|
||
<p>We set the File Descriptor when we <strong>create the OutputPin</strong>: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L381-L395">nuttx-embedded-hal/src/hal.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX Implementation of GPIO Output
|
||
</span><span class="kw">impl </span>OutputPin {
|
||
<span class="doccomment">/// Create a GPIO Output Pin from a Device Path (e.g. "/dev/gpio1")
|
||
</span><span class="kw">pub fn </span>new(path: <span class="kw-2">&</span>str) -> <span class="prelude-ty">Result</span><<span class="self">Self</span>, i32> {
|
||
<span class="comment">// Open the NuttX Device Path (e.g. "/dev/gpio1") for read-write
|
||
</span><span class="kw">let </span>fd = open(path, O_RDWR);
|
||
<span class="kw">if </span>fd < <span class="number">0 </span>{ <span class="kw">return </span><span class="prelude-val">Err</span>(fd) }
|
||
|
||
<span class="comment">// Return the pin
|
||
</span><span class="prelude-val">Ok</span>(<span class="self">Self </span>{ fd })
|
||
}
|
||
}</code></pre></div>
|
||
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L498-L522">(<strong>open</strong> is defined here)</a></p>
|
||
<p>To set the OutputPin High or Low, we call <strong>ioctl</strong> on the File Descriptor: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L201-L225">nuttx-embedded-hal/src/hal.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX Implementation of GPIO Output
|
||
</span><span class="kw">impl </span>v2::OutputPin <span class="kw">for </span>OutputPin {
|
||
<span class="doccomment">/// Error Type
|
||
</span><span class="kw">type </span>Error = i32;
|
||
|
||
<span class="doccomment">/// Set the GPIO Output to High
|
||
</span><span class="kw">fn </span>set_high(<span class="kw-2">&mut </span><span class="self">self</span>) -> <span class="prelude-ty">Result</span><(), <span class="self">Self</span>::Error> {
|
||
<span class="kw">let </span>ret = <span class="kw">unsafe </span>{
|
||
ioctl(<span class="self">self</span>.fd, GPIOC_WRITE, <span class="number">1</span>)
|
||
};
|
||
<span class="macro">assert!</span>(ret >= <span class="number">0</span>);
|
||
<span class="prelude-val">Ok</span>(())
|
||
}
|
||
|
||
<span class="doccomment">/// Set the GPIO Output to low
|
||
</span><span class="kw">fn </span>set_low(<span class="kw-2">&mut </span><span class="self">self</span>) -> <span class="prelude-ty">Result</span><(), <span class="self">Self</span>::Error> {
|
||
<span class="kw">let </span>ret = <span class="kw">unsafe </span>{
|
||
ioctl(<span class="self">self</span>.fd, GPIOC_WRITE, <span class="number">0</span>)
|
||
};
|
||
<span class="macro">assert!</span>(ret >= <span class="number">0</span>);
|
||
<span class="prelude-val">Ok</span>(())
|
||
}
|
||
}</code></pre></div>
|
||
<p>When we’re done with OutputPin, we <strong>close the File Descriptor</strong>: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L443-L451">nuttx-embedded-hal/src/hal.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX Implementation of GPIO Output
|
||
</span><span class="kw">impl </span>Drop <span class="kw">for </span>OutputPin {
|
||
<span class="doccomment">/// Close the GPIO Output
|
||
</span><span class="kw">fn </span>drop(<span class="kw-2">&mut </span><span class="self">self</span>) {
|
||
<span class="kw">unsafe </span>{ close(<span class="self">self</span>.fd) };
|
||
}
|
||
}</code></pre></div>
|
||
<p>Check out the GPIO demo and docs…</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal#gpio-output"><strong>GPIO Demo</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://docs.rs/nuttx-embedded-hal/latest/nuttx_embedded_hal/struct.OutputPin.html"><strong>GPIO Output Docs</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://docs.rs/nuttx-embedded-hal/latest/nuttx_embedded_hal/struct.InputPin.html"><strong>GPIO Input Docs</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://docs.rs/nuttx-embedded-hal/latest/nuttx_embedded_hal/struct.InterruptPin.html"><strong>GPIO Interrupt Docs</strong></a></p>
|
||
</li>
|
||
</ul>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-hal4.png" alt="SPI HAL" /></p>
|
||
<h2 id="spi-hal"><a class="doc-anchor" href="#spi-hal">§</a>16.2 SPI HAL</h2>
|
||
<p>Now we study the <strong>SPI HAL</strong> for NuttX.</p>
|
||
<p>Our <strong>Spi Struct</strong> also contains a <strong>File Descriptor</strong>: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L353-L473">nuttx-embedded-hal/src/hal.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX SPI Struct
|
||
</span><span class="kw">pub struct </span>Spi {
|
||
<span class="doccomment">/// NuttX File Descriptor
|
||
</span>fd: i32,
|
||
}
|
||
|
||
<span class="doccomment">/// NuttX Implementation of SPI Bus
|
||
</span><span class="kw">impl </span>Spi {
|
||
<span class="doccomment">/// Create an SPI Bus from a Device Path (e.g. "/dev/spitest0")
|
||
</span><span class="kw">pub fn </span>new(path: <span class="kw-2">&</span>str) -> <span class="prelude-ty">Result</span><<span class="self">Self</span>, i32> {
|
||
<span class="comment">// Open the NuttX Device Path (e.g. "/dev/spitest0") for read-write
|
||
</span><span class="kw">let </span>fd = open(path, O_RDWR);
|
||
<span class="kw">if </span>fd < <span class="number">0 </span>{ <span class="kw">return </span><span class="prelude-val">Err</span>(fd) }
|
||
|
||
<span class="comment">// Return the SPI Bus
|
||
</span><span class="prelude-val">Ok</span>(<span class="self">Self </span>{ fd })
|
||
}
|
||
}
|
||
|
||
<span class="doccomment">/// NuttX Implementation of SPI Bus
|
||
</span><span class="kw">impl </span>Drop <span class="kw">for </span>Spi {
|
||
<span class="doccomment">/// Close the SPI Bus
|
||
</span><span class="kw">fn </span>drop(<span class="kw-2">&mut </span><span class="self">self</span>) {
|
||
<span class="kw">unsafe </span>{ close(<span class="self">self</span>.fd) };
|
||
}
|
||
}</code></pre></div>
|
||
<p>We <strong>open and close</strong> the File Descriptor the same way as OutputPin.</p>
|
||
<p>To do SPI Write, we <strong>write to the File Descriptor</strong>: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L185-L201">nuttx-embedded-hal/src/hal.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX Implementation of SPI Write
|
||
</span><span class="kw">impl </span>spi::Write<u8> <span class="kw">for </span>Spi{
|
||
<span class="doccomment">/// Error Type
|
||
</span><span class="kw">type </span>Error = i32;
|
||
|
||
<span class="doccomment">/// Write SPI data
|
||
</span><span class="kw">fn </span>write(<span class="kw-2">&mut </span><span class="self">self</span>, words: <span class="kw-2">&</span>[u8]) -> <span class="prelude-ty">Result</span><(), <span class="self">Self</span>::Error> {
|
||
<span class="comment">// Transmit data
|
||
</span><span class="kw">let </span>bytes_written = <span class="kw">unsafe </span>{
|
||
write(<span class="self">self</span>.fd, words.as_ptr(), words.len() <span class="kw">as </span>u32)
|
||
};
|
||
<span class="macro">assert_eq!</span>(bytes_written, words.len() <span class="kw">as </span>i32);
|
||
<span class="prelude-val">Ok</span>(())
|
||
}
|
||
}</code></pre></div>
|
||
<p>SPI Transfer works the same way, except that we also <strong>copy the SPI Response</strong> and return it to the caller: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L161-L185">nuttx-embedded-hal/src/hal.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX Implementation of SPI Transfer
|
||
</span><span class="kw">impl </span>spi::Transfer<u8> <span class="kw">for </span>Spi {
|
||
<span class="doccomment">/// Error Type
|
||
</span><span class="kw">type </span>Error = i32;
|
||
|
||
<span class="doccomment">/// Transfer SPI data
|
||
</span><span class="kw">fn </span>transfer<<span class="lifetime">'w</span>>(<span class="kw-2">&mut </span><span class="self">self</span>, words: <span class="kw-2">&</span><span class="lifetime">'w </span><span class="kw-2">mut </span>[u8]) -> <span class="prelude-ty">Result</span><<span class="kw-2">&</span><span class="lifetime">'w </span>[u8], <span class="self">Self</span>::Error> {
|
||
<span class="comment">// Transmit data
|
||
</span><span class="kw">let </span>bytes_written = <span class="kw">unsafe </span>{
|
||
write(<span class="self">self</span>.fd, words.as_ptr(), words.len() <span class="kw">as </span>u32)
|
||
};
|
||
<span class="macro">assert_eq!</span>(bytes_written, words.len() <span class="kw">as </span>i32);
|
||
|
||
<span class="comment">// Read response
|
||
</span><span class="kw">let </span>bytes_read = <span class="kw">unsafe </span>{
|
||
read(<span class="self">self</span>.fd, words.as_mut_ptr(), words.len() <span class="kw">as </span>u32)
|
||
};
|
||
<span class="macro">assert_eq!</span>(bytes_read, words.len() <span class="kw">as </span>i32);
|
||
|
||
<span class="comment">// Return response
|
||
</span><span class="prelude-val">Ok</span>(words)
|
||
}
|
||
}</code></pre></div>
|
||
<p>Check out the SPI demo and docs…</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal#spi"><strong>SPI Demo</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://docs.rs/nuttx-embedded-hal/latest/nuttx_embedded_hal/struct.Spi.html"><strong>SPI Docs</strong></a></p>
|
||
</li>
|
||
</ul>
|
||
<h2 id="i2c-hal"><a class="doc-anchor" href="#i2c-hal">§</a>16.3 I2C HAL</h2>
|
||
<p>The implementation of I2C HAL for NuttX is described here…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/rusti2c#nuttx-embedded-hal"><strong>“NuttX Embedded HAL (I2C)”</strong></a></li>
|
||
</ul>
|
||
<p>Check out the I2C demo and docs…</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal#i2c"><strong>I2C Demo</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://docs.rs/nuttx-embedded-hal/latest/nuttx_embedded_hal/struct.I2c.html"><strong>I2C Docs</strong></a></p>
|
||
</li>
|
||
</ul>
|
||
<h2 id="delay-hal"><a class="doc-anchor" href="#delay-hal">§</a>16.4 Delay HAL</h2>
|
||
<p>We have also implemented the Delay HAL for NuttX…</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://docs.rs/nuttx-embedded-hal/latest/nuttx_embedded_hal/struct.Delay.html"><strong>Delay Docs</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal#delay"><strong>Delay Demo</strong></a></p>
|
||
</li>
|
||
</ul>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-driver.png" alt="Fixing SX1262 Driver for NuttX" /></p>
|
||
<h1 id="appendix-fix-sx1262-driver-for-nuttx"><a class="doc-anchor" href="#appendix-fix-sx1262-driver-for-nuttx">§</a>17 Appendix: Fix SX1262 Driver for NuttX</h1>
|
||
<p>In this article we used this Rust Embedded Driver for Semtech SX1262…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/lupyuen/sx126x-rs-nuttx"><strong>lupyuen/sx126x-rs-nuttx</strong></a></li>
|
||
</ul>
|
||
<p>That we tweaked slightly from…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/tweedegolf/sx126x-rs"><strong>tweedegolf/sx126x-rs</strong></a></li>
|
||
</ul>
|
||
<p>(Thanks Tweede golf! 👍)</p>
|
||
<p>Let’s look at the modifications that we made.</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-hal6.png" alt="SPI Transfers in small chunks" /></p>
|
||
<h2 id="merge-spi-requests"><a class="doc-anchor" href="#merge-spi-requests">§</a>17.1 Merge SPI Requests</h2>
|
||
<p>While testing <a href="https://github.com/tweedegolf/sx126x-rs"><strong>sx126x-rs</strong></a>, we discovered that the SPI Requests were split into <strong>1-byte or 2-byte chunks</strong>. (Pic above)</p>
|
||
<p>This fails on NuttX because the SPI Request needs to be in <strong>one contiguous block</strong> as Chip Select flips from High to Low and High.</p>
|
||
<p>To fix this, we buffer all SPI Requests in the Chip Select Guard: <a href="https://github.com/lupyuen/sx126x-rs-nuttx/blob/master/src/sx/slave_select.rs#L86-L126">sx126x-rs-nuttx/src/sx/slave_select.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">impl</span><<span class="lifetime">'nss</span>, <span class="lifetime">'spi</span>, TNSS, TSPI, TSPIERR> Transfer<u8> <span class="kw">for </span>SlaveSelectGuard<<span class="lifetime">'nss</span>, <span class="lifetime">'spi</span>, TNSS, TSPI>
|
||
<span class="kw">where
|
||
</span>TNSS: OutputPin,
|
||
TSPI: Write<u8, Error = TSPIERR> + Transfer<u8, Error = TSPIERR>,
|
||
{
|
||
<span class="kw">type </span>Error = SpiError<TSPIERR>;
|
||
<span class="kw">fn </span>transfer<<span class="lifetime">'w</span>>(<span class="kw-2">&mut </span><span class="self">self</span>, words: <span class="kw-2">&</span><span class="lifetime">'w </span><span class="kw-2">mut </span>[u8]) -> <span class="prelude-ty">Result</span><<span class="kw-2">&</span><span class="lifetime">'w </span>[u8], <span class="self">Self</span>::Error> {
|
||
<span class="kw">unsafe </span>{
|
||
<span class="comment">// Prevent a second transfer
|
||
</span><span class="macro">debug_assert!</span>(!TRANSFERRED);
|
||
|
||
<span class="comment">// Copy the transmit data to the buffer
|
||
</span>BUF[BUFLEN..(BUFLEN + words.len())]
|
||
.clone_from_slice(words);
|
||
BUFLEN += words.len();
|
||
|
||
<span class="comment">// Transfer the data over SPI
|
||
</span><span class="kw">let </span>res = <span class="self">self</span>.spi.transfer(<span class="kw-2">&mut </span>BUF[<span class="number">0</span>..BUFLEN])
|
||
.map_err(SpiError::Transfer);
|
||
|
||
<span class="comment">// Copy the result from SPI
|
||
</span>words.clone_from_slice(<span class="kw-2">&</span>BUF[BUFLEN - words.len()..BUFLEN]);
|
||
|
||
<span class="comment">// Empty the buffer
|
||
</span>BUFLEN = <span class="number">0</span>;
|
||
|
||
<span class="comment">// Prevent a second write or transfer
|
||
</span>TRANSFERRED = <span class="bool-val">true</span>;
|
||
res
|
||
}
|
||
}
|
||
}
|
||
|
||
<span class="doccomment">/// Buffer for SPI Transfer. Max packet size (256) + 2 bytes for Write Buffer Command.
|
||
</span><span class="kw">static </span><span class="kw-2">mut </span>BUF: [ u8; <span class="number">258 </span>] = [ <span class="number">0</span>; <span class="number">258 </span>];
|
||
|
||
<span class="doccomment">/// Length of buffer for SPI Transfer
|
||
</span><span class="kw">static </span><span class="kw-2">mut </span>BUFLEN: usize = <span class="number">0</span>;
|
||
|
||
<span class="doccomment">/// True if we have just executed an SPI Transfer
|
||
</span><span class="kw">static </span><span class="kw-2">mut </span>TRANSFERRED: bool = <span class="bool-val">false</span>;</code></pre></div>
|
||
<p>Then we patched the driver code to ensure that all SPI Request chains consist of…</p>
|
||
<ul>
|
||
<li>
|
||
<p>0 or more SPI Writes</p>
|
||
</li>
|
||
<li>
|
||
<p>Followed by 1 optional SPI Transfer</p>
|
||
</li>
|
||
</ul>
|
||
<p>Such that we flush the buffer of SPI Requests only after the final SPI Write or final SPI Transfer.</p>
|
||
<p>So this chain of SPI Requests…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code>spi.transfer(<span class="kw-2">&mut </span>[<span class="number">0x1D</span>])
|
||
.and_then(|<span class="kw">_</span>| spi.transfer(<span class="kw-2">&mut </span>start_addr))
|
||
.and_then(|<span class="kw">_</span>| spi.transfer(<span class="kw-2">&mut </span>[<span class="number">0x00</span>]))
|
||
.and_then(|<span class="kw">_</span>| spi.transfer(result))<span class="question-mark">?</span>;</code></pre></div>
|
||
<p>After patching becomes…</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code>spi.write(<span class="kw-2">&</span>[<span class="number">0x1D</span>]) <span class="comment">// Changed from `transfer` to `write`
|
||
</span>.and_then(|<span class="kw">_</span>| spi.write(<span class="kw-2">&</span>start_addr)) <span class="comment">// Changed from `transfer` to `write`
|
||
</span>.and_then(|<span class="kw">_</span>| spi.write(<span class="kw-2">&</span>[<span class="number">0x00</span>])) <span class="comment">// Changed from `transfer` to `write`
|
||
</span>.and_then(|<span class="kw">_</span>| spi.transfer(result))<span class="question-mark">?</span>; <span class="comment">// Final transfer is OK</span></code></pre></div>
|
||
<p><a href="https://github.com/lupyuen/sx126x-rs-nuttx/blob/master/src/sx/mod.rs#L241-L244">(Source)</a></p>
|
||
<p>The driver works OK on NuttX after merging the SPI Requests…</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-driver2.png" alt="SPI Transfers after merging" /></p>
|
||
<h2 id="read-register"><a class="doc-anchor" href="#read-register">§</a>17.2 Read Register</h2>
|
||
<p>We inserted a null byte for the Read Register command, because Read Requests should have minimum 5 bytes (instead of 4): <a href="https://github.com/lupyuen/sx126x-rs-nuttx/blob/master/src/sx/mod.rs#L229-L246">sx126x-rs-nuttx/src/sx/mod.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Read data from a register
|
||
</span><span class="kw">pub fn </span>read_register<<span class="lifetime">'spi</span>>(
|
||
<span class="kw-2">&</span><span class="lifetime">'spi </span><span class="kw-2">mut </span><span class="self">self</span>,
|
||
spi: <span class="kw-2">&</span><span class="lifetime">'spi </span><span class="kw-2">mut </span>TSPI,
|
||
delay: <span class="kw-2">&mut </span><span class="kw">impl </span>DelayUs<u32>,
|
||
start_addr: u16,
|
||
result: <span class="kw-2">&mut </span>[u8],
|
||
) -> <span class="prelude-ty">Result</span><(), SxError<TSPIERR, TPINERR>> {
|
||
<span class="macro">debug_assert!</span>(result.len() >= <span class="number">1</span>);
|
||
<span class="kw">let </span>start_addr = start_addr.to_be_bytes();
|
||
<span class="kw">let </span><span class="kw-2">mut </span>spi = <span class="self">self</span>.slave_select(spi, delay)<span class="question-mark">?</span>;
|
||
|
||
spi.write(<span class="kw-2">&</span>[<span class="number">0x1D</span>])
|
||
.and_then(|<span class="kw">_</span>| spi.write(<span class="kw-2">&</span>start_addr))
|
||
<span class="comment">// Inserted this null byte
|
||
</span>.and_then(|<span class="kw">_</span>| spi.write(<span class="kw-2">&</span>[<span class="number">0x00</span>]))
|
||
.and_then(|<span class="kw">_</span>| spi.transfer(result))<span class="question-mark">?</span>;
|
||
<span class="prelude-val">Ok</span>(())
|
||
}</code></pre></div>
|
||
<h2 id="set-registers"><a class="doc-anchor" href="#set-registers">§</a>17.3 Set Registers</h2>
|
||
<p>The following registers need to be set for the LoRa Transmission to work correctly: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/sx1262.rs#L73-L91">rust_test/rust/src/sx1262.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Write SX1262 Registers to prepare for transmitting LoRa message.
|
||
// Based on https://gist.github.com/lupyuen/5fdede131ad0e327478994872f190668
|
||
// and https://docs.google.com/spreadsheets/d/14Pczf2sP_Egnzi5_nikukauL2iTKA03Qgq715e50__0/edit?usp=sharing
|
||
|
||
// Write Register 0x889: 0x04 (TxModulation)
|
||
</span>lora.write_register(<span class="kw-2">&mut </span>spi1, delay, Register::TxModulaton, <span class="kw-2">&</span>[<span class="number">0x04</span>])
|
||
.expect(<span class="string">"write register failed"</span>);
|
||
|
||
<span class="comment">// Write Register 0x8D8: 0xFE (TxClampConfig)
|
||
</span>lora.write_register(<span class="kw-2">&mut </span>spi1, delay, Register::TxClampConfig, <span class="kw-2">&</span>[<span class="number">0xFE</span>])
|
||
.expect(<span class="string">"write register failed"</span>);
|
||
|
||
<span class="comment">// Write Register 0x8E7: 0x38 (Over Current Protection)
|
||
</span>lora.write_register(<span class="kw-2">&mut </span>spi1, delay, Register::OcpConfiguration, <span class="kw-2">&</span>[<span class="number">0x38</span>])
|
||
.expect(<span class="string">"write register failed"</span>);
|
||
|
||
<span class="comment">// Write Register 0x736: 0x0D (Inverted IQ)
|
||
</span>lora.write_register(<span class="kw-2">&mut </span>spi1, delay, Register::IqPolaritySetup, <span class="kw-2">&</span>[<span class="number">0x0D</span>])
|
||
.expect(<span class="string">"write register failed"</span>);</code></pre></div>
|
||
<p>We derived the registers from the log generated by the SX1262 driver in C…</p>
|
||
<ul>
|
||
<li><a href="https://gist.github.com/lupyuen/5fdede131ad0e327478994872f190668"><strong>Log from SX1262 Driver in C</strong></a></li>
|
||
</ul>
|
||
<p>And by comparing the SPI Output of the C and Rust Drivers…</p>
|
||
<ul>
|
||
<li><a href="https://docs.google.com/spreadsheets/d/14Pczf2sP_Egnzi5_nikukauL2iTKA03Qgq715e50__0/edit?usp=sharing"><strong>Compare SPI Output of C and Rust Drivers</strong></a></li>
|
||
</ul>
|
||
<p>The C Driver for SX1262 is described here…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/sx1262"><strong>“LoRa SX1262 on Apache NuttX OS”</strong></a></li>
|
||
</ul>
|
||
<h2 id="adapt-for-risc-v"><a class="doc-anchor" href="#adapt-for-risc-v">§</a>17.4 Adapt For RISC-V</h2>
|
||
<p>The <a href="https://github.com/tweedegolf/sx126x-rs"><strong>sx126x-rs</strong></a> crate depends on the <a href="https://crates.io/crates/cortex-m"><strong>cortex-m</strong></a> crate, which works only on Arm, not RISC-V (BL602).</p>
|
||
<p>We defined the following functions to fill in for the missing functions on RISC-V: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/sx1262.rs#L146-L168">rust_test/rust/src/sx1262.rs</a></p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Read Priority Mask Register. Missing function called by sx126x crate (Arm only, not RISC-V).
|
||
/// See https://github.com/rust-embedded/cortex-m/blob/master/src/register/primask.rs#L29
|
||
</span><span class="attr">#[cfg(not(target_arch = <span class="string">"arm"</span>))] </span><span class="comment">// If architecture is not Arm...
|
||
</span><span class="attr">#[no_mangle]
|
||
</span><span class="kw">extern </span><span class="string">"C" </span><span class="kw">fn </span>__primask_r() -> u32 { <span class="number">0 </span>}
|
||
|
||
<span class="doccomment">/// Disables all interrupts. Missing function called by sx126x crate (Arm only, not RISC-V).
|
||
/// See https://github.com/rust-embedded/cortex-m/blob/master/src/interrupt.rs#L29
|
||
</span><span class="attr">#[cfg(not(target_arch = <span class="string">"arm"</span>))] </span><span class="comment">// If architecture is not Arm...
|
||
</span><span class="attr">#[no_mangle]
|
||
</span><span class="kw">extern </span><span class="string">"C" </span><span class="kw">fn </span>__cpsid() {}
|
||
|
||
<span class="doccomment">/// Enables all interrupts. Missing function called by sx126x crate (Arm only, not RISC-V).
|
||
/// See https://github.com/rust-embedded/cortex-m/blob/master/src/interrupt.rs#L39
|
||
</span><span class="attr">#[cfg(not(target_arch = <span class="string">"arm"</span>))] </span><span class="comment">// If architecture is not Arm...
|
||
</span><span class="attr">#[no_mangle]
|
||
</span><span class="kw">extern </span><span class="string">"C" </span><span class="kw">fn </span>__cpsie() {}
|
||
|
||
<span class="doccomment">/// No operation. Missing function called by sx126x crate (Arm only, not RISC-V).
|
||
/// See https://github.com/rust-embedded/cortex-m/blob/master/src/asm.rs#L35
|
||
</span><span class="attr">#[cfg(not(target_arch = <span class="string">"arm"</span>))] </span><span class="comment">// If architecture is not Arm...
|
||
</span><span class="attr">#[no_mangle]
|
||
</span><span class="kw">extern </span><span class="string">"C" </span><span class="kw">fn </span>__nop() {}</code></pre></div>
|
||
<p>We haven’t tested the driver for receiving LoRa Messages, we might need more fixes for NuttX on RISC-V.</p>
|
||
<p>(But then again we might not need to receive LoRa Messages if we’re building a simple IoT Sensor)</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-build.png" alt="Rust Build Script for NuttX" /></p>
|
||
<h1 id="appendix-rust-build-script-for-nuttx"><a class="doc-anchor" href="#appendix-rust-build-script-for-nuttx">§</a>18 Appendix: Rust Build Script for NuttX</h1>
|
||
<p>Let’s study the Build Script for Rust on NuttX…</p>
|
||
<ul>
|
||
<li><strong>Build Script</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/run.sh">apps/examples/rust_test/run.sh</a></li>
|
||
</ul>
|
||
<p>And how it compiles the following into the NuttX Firmware…</p>
|
||
<ul>
|
||
<li>
|
||
<p><strong>Rust Project</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/Cargo.toml">apps/examples/rust_test/rust/Cargo.toml</a></p>
|
||
<p>(Rust Dependencies and Build Settings)</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Rust Source File</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/rust/src/lib.rs">apps/examples/rust_test/rust/src/lib.rs</a></p>
|
||
<p>(Defines the rust_main function)</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Rust Custom Target</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/riscv32imacf-unknown-none-elf.json">apps/examples/rust_test/riscv32imacf-unknown-none-elf.json</a></p>
|
||
<p>(Custom Rust Target for BL602 and BL604)</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Stub Library</strong>: <a href="https://github.com/lupyuen/rust-nuttx">nuttx/libs/librust</a></p>
|
||
<p>(Stub Library will be replaced by the compiled Rust Project)</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Test App</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/rust_test_main.c">apps/examples/rust_test/rust_test_main.c</a></p>
|
||
<p>(Main Function that calls rust_main)</p>
|
||
</li>
|
||
</ul>
|
||
<p>See also the Build Log for Rust on NuttX…</p>
|
||
<ul>
|
||
<li><a href="https://gist.github.com/lupyuen/9bfd71f7029bb66e327f89c8a58f450d"><strong>Build Log</strong></a></li>
|
||
</ul>
|
||
<h2 id="rust-target"><a class="doc-anchor" href="#rust-target">§</a>18.1 Rust Target</h2>
|
||
<p>Our Build Script begins by defining the <strong>Rust Target</strong> for the build: <a href="https://github.com/lupyuen/rust_test/blob/main/run.sh#L28-L39">rust_test/run.sh</a></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## Rust target: Custom target for BL602 and BL604
|
||
## https://docs.rust-embedded.org/embedonomicon/compiler-support.html#built-in-target
|
||
## https://docs.rust-embedded.org/embedonomicon/custom-target.html
|
||
rust_build_target=$PWD/riscv32imacf-unknown-none-elf.json
|
||
rust_build_target_folder=riscv32imacf-unknown-none-elf
|
||
|
||
## Rust target: Standard target
|
||
## rust_build_target=riscv32imac-unknown-none-elf
|
||
## rust_build_target_folder=riscv32imac-unknown-none-elf</code></pre></div>
|
||
<p><strong>For BL602 and BL604:</strong> We’re using the <strong>Custom Rust Target</strong> at…</p>
|
||
<p><a href="https://github.com/lupyuen/rust_test/blob/main/riscv32imacf-unknown-none-elf.json">apps/examples/rust_test/riscv32imacf-unknown-none-elf.json</a></p>
|
||
<p>This Custom Rust Target supports <strong>floating point</strong> on 32-bit RISC-V. (The standard 32-bit RISC-V target doesn’t support floating point)</p>
|
||
<p><a href="https://lupyuen.github.io/articles/rust#rust-targets">(More about Custom Rust Targets)</a></p>
|
||
<p><strong>For ESP32-C3 (RISC-V)</strong>: Set “rust_build_target” and “rust_build_target_folder” to the Standard Rust Target <strong>riscv32imc-unknown-none-elf</strong></p>
|
||
<p>Then run this command to install the Rust Target…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>rustup target add riscv32imc-unknown-none-elf</code></pre></div>
|
||
<p><a href="https://github.com/jessebraham/esp-hal/tree/main/esp32c3-hal">(See this)</a></p>
|
||
<p><strong>For ESP32 (Xtensa)</strong>: Set “rust_build_target” and “rust_build_target_folder” to the ESP32 Rust Target <strong>xtensa-esp32-none-elf</strong></p>
|
||
<p>We need to install the Rust compiler fork with Xtensa support. <a href="https://github.com/jessebraham/esp-hal/tree/main/esp32-hal">(See this)</a></p>
|
||
<h2 id="rust-build-options"><a class="doc-anchor" href="#rust-build-options">§</a>18.2 Rust Build Options</h2>
|
||
<p>Next we define the <strong>Rust Build Options</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/run.sh#L41-L48">rust_test/run.sh</a></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## Rust build options: Build the Rust Core Library for our custom target
|
||
rust_build_options="--target $rust_build_target -Z build-std=core"</code></pre></div>
|
||
<p><strong>For BL602 and BL604:</strong> Since we’re using a Custom Rust Target, we need to build the Rust Core Library for our target. That’s why we need “-Z build-std=core” for the Rust Build Options…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>--target nuttx/apps/examples/rust_test/riscv32imacf-unknown-none-elf.json \
|
||
-Z build-std=core</code></pre></div>
|
||
<p><a href="https://lupyuen.github.io/articles/rust#custom-rust-target-for-bl602">(More about building Rust Core Library)</a></p>
|
||
<p><strong>For ESP32 and ESP32-C3:</strong> Since we’re using a Standard Rust Target, remove “-Z build-std=core” from “rust_build_options”.</p>
|
||
<p>The Rust Build Options will look like…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>--target riscv32imc-unknown-none-elf</code></pre></div><h2 id="define-libraries"><a class="doc-anchor" href="#define-libraries">§</a>18.3 Define Libraries</h2>
|
||
<p>Next we define the <strong>libraries that will be modified</strong> during the build…</p>
|
||
<ul>
|
||
<li>
|
||
<p><strong>Stub Library:</strong> <a href="https://github.com/lupyuen/rust-nuttx">nuttx/libs/librust</a></p>
|
||
<p>This is an empty NuttX C Library that will be replaced by the Compiled Rust Library</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Rust Library:</strong> <a href="https://github.com/lupyuen/rust_test/blob/main/rust">apps/examples/rust_test/rust</a></p>
|
||
<p>This is the Rust Library (compiled as a Static Library) that will overwrite the Compiled Stub Library</p>
|
||
</li>
|
||
</ul>
|
||
<p>That’s how we <strong>inject our Rust Code</strong> into the NuttX Build: We overwrite the Compiled Stub Library by the Compiled Rust Library.</p>
|
||
<p>The Stub Library is defined like so: <a href="https://github.com/lupyuen/rust_test/blob/main/run.sh#L50-L53">rust_test/run.sh</a></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## Location of the Stub Library. We will replace this stub by the Rust Library
|
||
## rust_app_dest will be set to ../../../nuttx/staging/librust.a
|
||
rust_app_dir=$NUTTX_PATH/staging
|
||
rust_app_dest=$rust_app_dir/librust.a</code></pre></div>
|
||
<p>The Rust Library is defined below: <a href="https://github.com/lupyuen/rust_test/blob/main/run.sh#L55-L58">rust_test/run.sh</a></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## Location of the compiled Rust Library
|
||
## rust_app_build will be set to rust/target/riscv32imacf-unknown-none-elf/debug/libapp.a
|
||
rust_build_dir=$PWD/rust/target/$rust_build_target_folder/$rust_build_profile
|
||
rust_app_build=$rust_build_dir/libapp.a</code></pre></div><h2 id="build-stub-library"><a class="doc-anchor" href="#build-stub-library">§</a>18.4 Build Stub Library</h2>
|
||
<p>Our script <strong>builds NuttX twice.</strong></p>
|
||
<p>For the first build, we compile <strong>NuttX with the Stub Library</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/run.sh#L76-L83">rust_test/run.sh</a></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## Build the firmware with the Stub Library, ignoring references to the Rust Library
|
||
pushd $NUTTX_PATH
|
||
make || echo "----- Ignore undefined references to Rust Library"
|
||
popd</code></pre></div>
|
||
<p>Which fails to link because <strong>rust_main</strong> is undefined. Our script ignores the error and continues.</p>
|
||
<h2 id="build-rust-library"><a class="doc-anchor" href="#build-rust-library">§</a>18.5 Build Rust Library</h2>
|
||
<p>Now we build the Rust Library: <a href="https://github.com/lupyuen/rust_test/blob/main/run.sh#L89-L94">rust_test/run.sh</a></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## Build the Rust Library
|
||
pushd rust
|
||
rustup default nightly
|
||
cargo clippy $rust_build_options
|
||
cargo build $rust_build_options
|
||
popd</code></pre></div>
|
||
<p>Which expands to…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>cargo build \
|
||
--target nuttx/apps/examples/rust_test/riscv32imacf-unknown-none-elf.json \
|
||
-Z build-std=core</code></pre></div>
|
||
<p>(For BL602 and BL604)</p>
|
||
<p>This generates a <strong>Static Library</strong> at…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>apps/examples/rust_test/rust/target/riscv32imacf-unknown-none-elf/debug/libapp.a</code></pre></div>
|
||
<p>The Rust Build looks like this…</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-hal7.png" alt="Rust builds OK" /></p>
|
||
<h2 id="replace-stub-libary-by-rust-library"><a class="doc-anchor" href="#replace-stub-libary-by-rust-library">§</a>18.6 Replace Stub Libary by Rust Library</h2>
|
||
<p>We take the Static Library (generated by the Rust Compiler) and <strong>overwrite the Stub Library</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/run.sh#L96-L99">rust_test/run.sh</a></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## Replace the Stub Library by the compiled Rust Library
|
||
## Stub Library: ../../../nuttx/staging/librust.a
|
||
## Rust Library: rust/target/riscv32imacf-unknown-none-elf/debug/libapp.a
|
||
cp $rust_app_build $rust_app_dest</code></pre></div>
|
||
<p>Which is located at…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>nuttx/staging/librust.a</code></pre></div><h2 id="link-rust-library-into-firmware"><a class="doc-anchor" href="#link-rust-library-into-firmware">§</a>18.7 Link Rust Library into Firmware</h2>
|
||
<p>Finally we do the <strong>second NuttX build</strong>: <a href="https://github.com/lupyuen/rust_test/blob/main/run.sh#L105-L108">rust_test/run.sh</a></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## Link the Rust Library to the firmware
|
||
pushd $NUTTX_PATH
|
||
make
|
||
popd</code></pre></div>
|
||
<p>Which links the Rust Static Library (and <strong>rust_main</strong>) into the NuttX Firmware.</p>
|
||
<p>Our build for Rust on NuttX is complete! <strong>nuttx.bin</strong> contains our NuttX Firmware, with Rust embedded inside.</p>
|
||
<h1 id="appendix-build-flash-and-run-nuttx"><a class="doc-anchor" href="#appendix-build-flash-and-run-nuttx">§</a>19 Appendix: Build, Flash and Run NuttX</h1>
|
||
<p><em>(For BL602 and ESP32)</em></p>
|
||
<p>Below are the steps to build, flash and run NuttX on BL602 and ESP32.</p>
|
||
<p>The instructions below will work on <strong>Linux (Ubuntu)</strong>, <strong>WSL (Ubuntu)</strong> and <strong>macOS</strong>.</p>
|
||
<p><a href="https://nuttx.apache.org/docs/latest/quickstart/install.html">(Instructions for other platforms)</a></p>
|
||
<p><a href="https://popolon.org/gblog3/?p=1977&lang=en">(See this for Arch Linux)</a></p>
|
||
<h2 id="build-nuttx"><a class="doc-anchor" href="#build-nuttx">§</a>19.1 Build NuttX</h2>
|
||
<p>Follow these steps to build NuttX for BL602 or ESP32…</p>
|
||
<ol>
|
||
<li>
|
||
<p>Install the build prerequisites…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/nuttx#install-prerequisites"><strong>“Install Prerequisites”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Install Rust from <a href="https://rustup.rs"><strong>rustup.rs</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Assume that we have downloaded the <strong>NuttX Source Code</strong> and configured the build…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2#download-source-code"><strong>“Download Source Code”</strong></a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2#build-the-firmware"><strong>“Build the Firmware”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Edit the file…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>apps/examples/rust_test/rust/src/sx1262.rs</code></pre></div>
|
||
<p>And set the <strong>LoRa Frequency</strong>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2#transmit-lora-message"><strong>“Transmit LoRa Message”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>To build NuttX with Rust, enter this…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>pushd apps/examples/rust_test
|
||
./run.sh
|
||
popd</code></pre></div></li>
|
||
<li>
|
||
<p>We should see…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>LD: nuttx
|
||
CP: nuttx.hex
|
||
CP: nuttx.bin</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/9bfd71f7029bb66e327f89c8a58f450d">(See the complete log for BL602)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Ignore the errors at the <strong>“Flash NuttX”</strong> and <strong>“Run NuttX”</strong> steps</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>For WSL:</strong> Copy the <strong>NuttX Firmware</strong> to the <strong>c:\blflash</strong> directory in the Windows File System…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## /mnt/c/blflash refers to c:\blflash in Windows
|
||
mkdir /mnt/c/blflash
|
||
cp nuttx.bin /mnt/c/blflash</code></pre></div>
|
||
<p>For WSL we need to run <strong>blflash</strong> under plain old Windows CMD (not WSL) because it needs to access the COM port.</p>
|
||
</li>
|
||
<li>
|
||
<p>In case of problems, refer to the <strong>NuttX Docs</strong>…</p>
|
||
<p><a href="https://nuttx.apache.org/docs/latest/platforms/risc-v/bl602/index.html"><strong>“BL602 NuttX”</strong></a></p>
|
||
<p><a href="https://nuttx.apache.org/docs/latest/platforms/xtensa/esp32/index.html"><strong>“ESP32 NuttX”</strong></a></p>
|
||
<p><a href="https://nuttx.apache.org/docs/latest/quickstart/install.html"><strong>“Installing NuttX”</strong></a></p>
|
||
</li>
|
||
</ol>
|
||
<blockquote>
|
||
<p><img src="https://lupyuen.github.io/images/nuttx-build2.png" alt="Building NuttX" /></p>
|
||
</blockquote>
|
||
<h2 id="flash-nuttx"><a class="doc-anchor" href="#flash-nuttx">§</a>19.2 Flash NuttX</h2>
|
||
<p><strong>For ESP32:</strong> <a href="https://nuttx.apache.org/docs/latest/platforms/xtensa/esp32/index.html#flashing"><strong>See instructions here</strong></a> <a href="https://popolon.org/gblog3/?p=1977&lang=en">(Also check out this article)</a></p>
|
||
<p><strong>For BL602:</strong> Follow these steps to install <strong>blflash</strong>…</p>
|
||
<ol>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/flash#install-rustup"><strong>“Install rustup”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/flash#download-and-build-blflash"><strong>“Download and build blflash”</strong></a></p>
|
||
</li>
|
||
</ol>
|
||
<p>We assume that our Firmware Binary File <strong>nuttx.bin</strong> has been copied to the <strong>blflash</strong> folder.</p>
|
||
<p>Set BL602 / BL604 to <strong>Flashing Mode</strong> and restart the board…</p>
|
||
<p><strong>For PineDio Stack BL604:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Set the <strong>GPIO 8 Jumper</strong> to <strong>High</strong> <a href="https://lupyuen.github.io/images/pinedio-high.jpg">(Like this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Disconnect the USB cable and reconnect</p>
|
||
<p>Or use the Improvised Reset Button <a href="https://lupyuen.github.io/articles/pinedio#appendix-improvised-reset-button-for-pinedio-stack">(Here’s how)</a></p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For PineCone BL602:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Set the <strong>PineCone Jumper (IO 8)</strong> to the <strong><code>H</code> Position</strong> <a href="https://lupyuen.github.io/images/pinecone-jumperh.jpg">(Like this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Press the Reset Button</p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For BL10:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Connect BL10 to the USB port</p>
|
||
</li>
|
||
<li>
|
||
<p>Press and hold the <strong>D8 Button (GPIO 8)</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Press and release the <strong>EN Button (Reset)</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Release the D8 Button</p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For <a href="https://docs.ai-thinker.com/en/wb2">Ai-Thinker Ai-WB2</a>, Pinenut and MagicHome BL602:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Disconnect the board from the USB Port</p>
|
||
</li>
|
||
<li>
|
||
<p>Connect <strong>GPIO 8</strong> to <strong>3.3V</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Reconnect the board to the USB port</p>
|
||
</li>
|
||
</ol>
|
||
<p>Enter these commands to flash <strong>nuttx.bin</strong> to BL602 / BL604 over UART…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## For Linux: Change "/dev/ttyUSB0" to the BL602 / BL604 Serial Port
|
||
blflash flash nuttx.bin \
|
||
--port /dev/ttyUSB0
|
||
|
||
## For macOS: Change "/dev/tty.usbserial-1410" to the BL602 / BL604 Serial Port
|
||
blflash flash nuttx.bin \
|
||
--port /dev/tty.usbserial-1410 \
|
||
--initial-baud-rate 230400 \
|
||
--baud-rate 230400
|
||
|
||
## For Windows: Change "COM5" to the BL602 / BL604 Serial Port
|
||
blflash flash c:\blflash\nuttx.bin --port COM5</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/9c0dbd75bb6b8e810939a36ffb5c399f">(See the Output Log)</a></p>
|
||
<p>For WSL: Do this under plain old Windows CMD (not WSL) because <strong>blflash</strong> needs to access the COM port.</p>
|
||
<p><a href="https://github.com/apache/nuttx/issues/4336">(Flashing WiFi apps to BL602 / BL604? Remember to use <strong>bl_rfbin</strong>)</a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/flash#flash-the-firmware">(More details on flashing firmware)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/nuttx-flash2.png" alt="Flashing NuttX" /></p>
|
||
<h2 id="run-nuttx"><a class="doc-anchor" href="#run-nuttx">§</a>19.3 Run NuttX</h2>
|
||
<p><strong>For ESP32:</strong> Use Picocom to connect to ESP32 over UART…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>picocom -b 115200 /dev/ttyUSB0</code></pre></div>
|
||
<p><a href="https://popolon.org/gblog3/?p=1977&lang=en">(More about this)</a></p>
|
||
<p><strong>For BL602:</strong> Set BL602 / BL604 to <strong>Normal Mode</strong> (Non-Flashing) and restart the board…</p>
|
||
<p><strong>For PineDio Stack BL604:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Set the <strong>GPIO 8 Jumper</strong> to <strong>Low</strong> <a href="https://lupyuen.github.io/images/pinedio-low.jpg">(Like this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Disconnect the USB cable and reconnect</p>
|
||
<p>Or use the Improvised Reset Button <a href="https://lupyuen.github.io/articles/pinedio#appendix-improvised-reset-button-for-pinedio-stack">(Here’s how)</a></p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For PineCone BL602:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Set the <strong>PineCone Jumper (IO 8)</strong> to the <strong><code>L</code> Position</strong> <a href="https://lupyuen.github.io/images/pinecone-jumperl.jpg">(Like this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Press the Reset Button</p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For BL10:</strong></p>
|
||
<ol>
|
||
<li>Press and release the <strong>EN Button (Reset)</strong></li>
|
||
</ol>
|
||
<p><strong>For <a href="https://docs.ai-thinker.com/en/wb2">Ai-Thinker Ai-WB2</a>, Pinenut and MagicHome BL602:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Disconnect the board from the USB Port</p>
|
||
</li>
|
||
<li>
|
||
<p>Connect <strong>GPIO 8</strong> to <strong>GND</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Reconnect the board to the USB port</p>
|
||
</li>
|
||
</ol>
|
||
<p>After restarting, connect to BL602 / BL604’s UART Port at 2 Mbps like so…</p>
|
||
<p><strong>For Linux:</strong></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>screen /dev/ttyUSB0 2000000</code></pre></div>
|
||
<p><strong>For macOS:</strong> Use CoolTerm (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||
<p><strong>For Windows:</strong> Use <code>putty</code> (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||
<p><strong>Alternatively:</strong> Use the Web Serial Terminal (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||
<p>Press Enter to reveal the <strong>NuttX Shell</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>NuttShell (NSH) NuttX-10.2.0-RC0
|
||
nsh></code></pre></div>
|
||
<p>Congratulations NuttX is now running on BL602 / BL604!</p>
|
||
<p><a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">(More details on connecting to BL602 / BL604)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/nuttx-boot2.png" alt="Running NuttX" /></p>
|
||
<p><img src="https://lupyuen.github.io/images/rust2-pinedio.jpg" alt="Loads of fun with Rust, NuttX and LoRa on PineDio Stack BL604" /></p>
|
||
<p><em>Loads of fun with Rust, NuttX and LoRa on PineDio Stack BL604</em></p>
|
||
|
||
|
||
<!-- Begin scripts/rustdoc-after.html: Post-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
|
||
|
||
<!-- Begin Theme Picker and Prism Theme -->
|
||
<script src="../theme.js"></script>
|
||
<script src="../prism.js"></script>
|
||
<!-- Theme Picker and Prism Theme -->
|
||
|
||
<!-- End scripts/rustdoc-after.html -->
|
||
|
||
|
||
</body>
|
||
</html> |