blob: 4041ac7f9466b41ab7a5697c3f14374182a6684b [file] [log] [blame]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>GPIO - The Embedded Rust Book</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../../favicon.svg">
<link rel="shortcut icon" href="../../favicon.png">
<link rel="stylesheet" href="../../css/variables.css">
<link rel="stylesheet" href="../../css/general.css">
<link rel="stylesheet" href="../../css/chrome.css">
<link rel="stylesheet" href="../../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../../toc.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">The Embedded Rust Book</h1>
<div class="right-buttons">
<a href="../../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/rust-embedded/book" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="recommendations-for-gpio-interfaces"><a class="header" href="#recommendations-for-gpio-interfaces">Recommendations for GPIO Interfaces</a></h1>
<p><a id="c-zst-pin"></a></p>
<h2 id="pin-types-are-zero-sized-by-default-c-zst-pin"><a class="header" href="#pin-types-are-zero-sized-by-default-c-zst-pin">Pin types are zero-sized by default (C-ZST-PIN)</a></h2>
<p>GPIO Interfaces exposed by the HAL should provide dedicated zero-sized types for
each pin on every interface or port, resulting in a zero-cost GPIO abstraction
when all pin assignments are statically known.</p>
<p>Each GPIO Interface or Port should implement a <code>split</code> method returning a
struct with every pin.</p>
<p>Example:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct PA0;
pub struct PA1;
// ...
pub struct PortA;
impl PortA {
pub fn split(self) -&gt; PortAPins {
PortAPins {
pa0: PA0,
pa1: PA1,
// ...
}
}
}
pub struct PortAPins {
pub pa0: PA0,
pub pa1: PA1,
// ...
}
<span class="boring">}</span></code></pre></pre>
<p><a id="c-erased-pin"></a></p>
<h2 id="pin-types-provide-methods-to-erase-pin-and-port-c-erased-pin"><a class="header" href="#pin-types-provide-methods-to-erase-pin-and-port-c-erased-pin">Pin types provide methods to erase pin and port (C-ERASED-PIN)</a></h2>
<p>Pins should provide type erasure methods that move their properties from
compile time to runtime, and allow more flexibility in applications.</p>
<p>Example:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>/// Port A, pin 0.
pub struct PA0;
impl PA0 {
pub fn erase_pin(self) -&gt; PA {
PA { pin: 0 }
}
}
/// A pin on port A.
pub struct PA {
/// The pin number.
pin: u8,
}
impl PA {
pub fn erase_port(self) -&gt; Pin {
Pin {
port: Port::A,
pin: self.pin,
}
}
}
pub struct Pin {
port: Port,
pin: u8,
// (these fields can be packed to reduce the memory footprint)
}
enum Port {
A,
B,
C,
D,
}
<span class="boring">}</span></code></pre></pre>
<p><a id="c-pin-state"></a></p>
<h2 id="pin-state-should-be-encoded-as-type-parameters-c-pin-state"><a class="header" href="#pin-state-should-be-encoded-as-type-parameters-c-pin-state">Pin state should be encoded as type parameters (C-PIN-STATE)</a></h2>
<p>Pins may be configured as input or output with different characteristics
depending on the chip or family. This state should be encoded in the type system
to prevent use of pins in incorrect states.</p>
<p>Additional, chip-specific state (eg. drive strength) may also be encoded in this
way, using additional type parameters.</p>
<p>Methods for changing the pin state should be provided as <code>into_input</code> and
<code>into_output</code> methods.</p>
<p>Additionally, <code>with_{input,output}_state</code> methods should be provided that
temporarily reconfigure a pin in a different state without moving it.</p>
<p>The following methods should be provided for every pin type (that is, both
erased and non-erased pin types should provide the same API):</p>
<ul>
<li><code>pub fn into_input&lt;N: InputState&gt;(self, input: N) -&gt; Pin&lt;N&gt;</code></li>
<li><code>pub fn into_output&lt;N: OutputState&gt;(self, output: N) -&gt; Pin&lt;N&gt;</code></li>
<li>
<pre><code class="language-ignore">pub fn with_input_state&lt;N: InputState, R&gt;(
&amp;mut self,
input: N,
f: impl FnOnce(&amp;mut PA1&lt;N&gt;) -&gt; R,
) -&gt; R
</code></pre>
</li>
<li>
<pre><code class="language-ignore">pub fn with_output_state&lt;N: OutputState, R&gt;(
&amp;mut self,
output: N,
f: impl FnOnce(&amp;mut PA1&lt;N&gt;) -&gt; R,
) -&gt; R
</code></pre>
</li>
</ul>
<p>Pin state should be bounded by sealed traits. Users of the HAL should have no
need to add their own state. The traits can provide HAL-specific methods
required to implement the pin state API.</p>
<p>Example:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span><span class="boring">use std::marker::PhantomData;
</span>mod sealed {
pub trait Sealed {}
}
pub trait PinState: sealed::Sealed {}
pub trait OutputState: sealed::Sealed {}
pub trait InputState: sealed::Sealed {
// ...
}
pub struct Output&lt;S: OutputState&gt; {
_p: PhantomData&lt;S&gt;,
}
impl&lt;S: OutputState&gt; PinState for Output&lt;S&gt; {}
impl&lt;S: OutputState&gt; sealed::Sealed for Output&lt;S&gt; {}
pub struct PushPull;
pub struct OpenDrain;
impl OutputState for PushPull {}
impl OutputState for OpenDrain {}
impl sealed::Sealed for PushPull {}
impl sealed::Sealed for OpenDrain {}
pub struct Input&lt;S: InputState&gt; {
_p: PhantomData&lt;S&gt;,
}
impl&lt;S: InputState&gt; PinState for Input&lt;S&gt; {}
impl&lt;S: InputState&gt; sealed::Sealed for Input&lt;S&gt; {}
pub struct Floating;
pub struct PullUp;
pub struct PullDown;
impl InputState for Floating {}
impl InputState for PullUp {}
impl InputState for PullDown {}
impl sealed::Sealed for Floating {}
impl sealed::Sealed for PullUp {}
impl sealed::Sealed for PullDown {}
pub struct PA1&lt;S: PinState&gt; {
_p: PhantomData&lt;S&gt;,
}
impl&lt;S: PinState&gt; PA1&lt;S&gt; {
pub fn into_input&lt;N: InputState&gt;(self, input: N) -&gt; PA1&lt;Input&lt;N&gt;&gt; {
todo!()
}
pub fn into_output&lt;N: OutputState&gt;(self, output: N) -&gt; PA1&lt;Output&lt;N&gt;&gt; {
todo!()
}
pub fn with_input_state&lt;N: InputState, R&gt;(
&amp;mut self,
input: N,
f: impl FnOnce(&amp;mut PA1&lt;N&gt;) -&gt; R,
) -&gt; R {
todo!()
}
pub fn with_output_state&lt;N: OutputState, R&gt;(
&amp;mut self,
output: N,
f: impl FnOnce(&amp;mut PA1&lt;N&gt;) -&gt; R,
) -&gt; R {
todo!()
}
}
// Same for `PA` and `Pin`, and other pin types.
<span class="boring">}</span></code></pre></pre>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../design-patterns/hal/predictability.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../c-tips/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../../design-patterns/hal/predictability.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../c-tips/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../../elasticlunr.min.js"></script>
<script src="../../mark.min.js"></script>
<script src="../../searcher.js"></script>
<script src="../../clipboard.min.js"></script>
<script src="../../highlight.js"></script>
<script src="../../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>