lupyuen.org/articles/de3.html

736 lines
No EOL
44 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="rustdoc">
<title>NuttX RTOS for PinePhone: Display Engine</title>
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<meta property="og:title"
content="NuttX RTOS for PinePhone: Display Engine"
data-rh="true">
<meta property="og:description"
content="Apache NuttX Kernel now supports Allwinner A64 Display Engine on Pine64 PinePhone! Here's how we call it to render graphics on PinePhone's LCD Display"
data-rh="true">
<meta name="description"
content="Apache NuttX Kernel now supports Allwinner A64 Display Engine on Pine64 PinePhone! Here's how we call it to render graphics on PinePhone's LCD Display">
<meta property="og:image"
content="https://lupyuen.github.io/images/de3-title.jpg">
<meta property="og:type"
content="article" data-rh="true">
<link rel="canonical" href="https://lupyuen.org/articles/de3.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">NuttX RTOS for PinePhone: Display Engine</h1>
<nav id="rustdoc"><ul>
<li><a href="#allwinner-a64-display-engine" title="Allwinner A64 Display Engine">1 Allwinner A64 Display Engine</a><ul></ul></li>
<li><a href="#ui-channels" title="UI Channels">2 UI Channels</a><ul></ul></li>
<li><a href="#nuttx-framebuffer" title="NuttX Framebuffer">3 NuttX Framebuffer</a><ul></ul></li>
<li><a href="#render-framebuffers" title="Render Framebuffers">4 Render Framebuffers</a><ul>
<li><a href="#initialise-display-engine" title="Initialise Display Engine">4.1 Initialise Display Engine</a><ul></ul></li>
<li><a href="#initialise-ui-blender" title="Initialise UI Blender">4.2 Initialise UI Blender</a><ul></ul></li>
<li><a href="#initialise-ui-channels" title="Initialise UI Channels">4.3 Initialise UI Channels</a><ul></ul></li>
<li><a href="#enable-display-engine" title="Enable Display Engine">4.4 Enable Display Engine</a><ul></ul></li></ul></li>
<li><a href="#test-pattern" title="Test Pattern">5 Test Pattern</a><ul></ul></li>
<li><a href="#complete-display-driver" title="Complete Display Driver">6 Complete Display Driver</a><ul></ul></li>
<li><a href="#upcoming-drivers" title="Upcoming Drivers">7 Upcoming Drivers</a><ul></ul></li>
<li><a href="#whats-next" title="Whats Next">8 Whats Next</a><ul></ul></li>
<li><a href="#appendix-calibrate-nuttx-delay" title="Appendix: Calibrate NuttX Delay">9 Appendix: Calibrate NuttX Delay</a><ul></ul></li></ul></nav><p>📝 <em>23 Dec 2022</em></p>
<p><img src="https://lupyuen.github.io/images/de3-title.jpg" alt="Rendering graphics on PinePhone with Apache NuttX RTOS" /></p>
<p><a href="https://nuttx.apache.org/docs/latest/"><strong>Apache NuttX RTOS</strong></a> for <a href="https://wiki.pine64.org/index.php/PinePhone"><strong>Pine64 PinePhone</strong></a> (pic above) now supports <a href="https://lupyuen.github.io/articles/de"><strong>Allwinner A64 Display Engine</strong></a>!</p>
<p>Were one step closer to completing our <a href="https://lupyuen.github.io/articles/dsi3#complete-display-driver-for-pinephone"><strong>NuttX Display Driver</strong></a> for PinePhone.</p>
<p>Lets find out how our NuttX Display Driver will call A64 Display Engine to <strong>render graphics on PinePhones LCD Display</strong></p>
<p><img src="https://lupyuen.github.io/images/dsi3-steps.jpg" alt="Complete Display Driver for PinePhone" /></p>
<p><a href="https://lupyuen.github.io/articles/dsi3#complete-display-driver-for-pinephone"><em>Complete Display Driver for PinePhone</em></a></p>
<h1 id="allwinner-a64-display-engine"><a class="doc-anchor" href="#allwinner-a64-display-engine">§</a>1 Allwinner A64 Display Engine</h1>
<p>Inside PinePhones Allwinner A64 SoC (pic above) is the <strong>A64 Display Engine</strong> that…</p>
<ul>
<li>
<p>Pulls pixels from <strong>Multiple Framebuffers</strong> in RAM</p>
<p>(Up to 3 Framebuffers)</p>
</li>
<li>
<p><strong>Blends the pixels</strong> into a single image</p>
<p>(720 x 1440 for PinePhone)</p>
</li>
<li>
<p>Pushes the image to the <strong>A64 Timing Controller TCON0</strong></p>
<p>(Connected via MIPI Display Serial Interface to LCD Display)</p>
</li>
<li>
<p>Does all this automatically in Hardware via <strong>Direct Memory Access</strong> (DMA)</p>
<p>(No interrupts needed)</p>
</li>
</ul>
<p>Previously we talked about the A64 Display Engine and coding it with Zig…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/de"><strong>“Rendering PinePhones Display (DE and TCON0)”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/de2"><strong>“NuttX RTOS for PinePhone: Render Graphics in Zig”</strong></a></p>
</li>
</ul>
<p>Today well program it with the <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_de.c"><strong>NuttX Kernel Driver</strong></a> for the Display Engine.</p>
<p><img src="https://lupyuen.github.io/images/de2-overlay.jpg" alt="3 Framebuffers for 3 UI Channels" /></p>
<h1 id="ui-channels"><a class="doc-anchor" href="#ui-channels">§</a>2 UI Channels</h1>
<p>A64 Display Engine supports up to <strong>3 Framebuffers</strong> in RAM (pic above). Each pixel has <strong>32-bit ARGB 8888</strong> format.</p>
<p>The Display Engine renders the 3 Framebuffer as <strong>3 UI Channels</strong>, blended together into the displayed image…</p>
<p><img src="https://lupyuen.github.io/images/de2-blender.jpg" alt="Blending the UI Channels" /></p>
<p>Lets start with the <strong>3 Framebuffers</strong>: <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/test/test_a64_de.c#L5-L89">test_a64_de.c</a></p>
<ul>
<li>
<p><strong>Framebuffer 0</strong> (UI Channel 1) is a 720 x 1440 Fullscreen Framebuffer (pic below)…</p>
<div class="example-wrap"><pre class="language-c"><code>// PinePhone LCD Panel Width and Height (pixels)
#define PANEL_WIDTH 720
#define PANEL_HEIGHT 1440
// Framebuffer 0: (Base UI Channel)
// Fullscreen 720 x 1440 (4 bytes per XRGB 8888 pixel)
static uint32_t fb0[PANEL_WIDTH * PANEL_HEIGHT];</code></pre></div>
<p>Later well fill Framebuffer 0 with <strong>Blue, Green and Red</strong> blocks.</p>
</li>
<li>
<p><strong>Framebuffer 1</strong> (UI Channel 2) is a 600 x 600 Square…</p>
<div class="example-wrap"><pre class="language-c"><code>// Framebuffer 1: (First Overlay UI Channel)
// Square 600 x 600 (4 bytes per ARGB 8888 pixel)
#define FB1_WIDTH 600
#define FB1_HEIGHT 600
static uint32_t fb1[FB1_WIDTH * FB1_HEIGHT];</code></pre></div>
<p>Well fill it with <strong>Semi-Transparent White</strong> later.</p>
</li>
<li>
<p><strong>Framebuffer 2</strong> (UI Channel 3) is also a Fullscreen Framebuffer…</p>
<div class="example-wrap"><pre class="language-c"><code>// Framebuffer 2: (Second Overlay UI Channel)
// Fullscreen 720 x 1440 (4 bytes per ARGB 8888 pixel)
static uint32_t fb2[PANEL_WIDTH * PANEL_HEIGHT];</code></pre></div>
<p>Well fill it with a <strong>Semi-Transparent Green Circle</strong>.</p>
</li>
</ul>
<p>Lets wrap the 3 Framebuffers (<strong>fb0</strong>, <strong>fb1</strong> and <strong>fb2</strong>) with the NuttX Framebuffer Interface…</p>
<p><img src="https://lupyuen.github.io/images/de2-fb.jpg" alt="PinePhone Framebuffer" /></p>
<h1 id="nuttx-framebuffer"><a class="doc-anchor" href="#nuttx-framebuffer">§</a>3 NuttX Framebuffer</h1>
<p>NuttX expects our PinePhone Display Driver to provide a <a href="https://nuttx.apache.org/docs/latest/components/drivers/special/framebuffer.html"><strong>Framebuffer Interface</strong></a> for rendering graphics.</p>
<p>Lets define the <strong>NuttX Framebuffer</strong>: <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/test/test_a64_de.c#L5-L89">test_a64_de.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// TODO: Run `make menuconfig`
// Select &quot;System Type &gt; Allwinner A64 Peripheral Selection &gt; DE&quot;
// Select &quot;System Type &gt; Allwinner A64 Peripheral Selection &gt; RSB&quot;
// Select &quot;Build Setup &gt; Debug Options &gt; Graphics Debug Features &gt; Error + Warnings + Info&quot;
// Select &quot;Build Setup &gt; Debug Options &gt; Battery-related Debug Features &gt; Error + Warnings + Info&quot;
// Select &quot;Device Drivers &gt; Framebuffer Overlay Support&quot;
// Save config and exit menuconfig
// NuttX Framebuffer Interface
#include &lt;nuttx/video/fb.h&gt;
// 3 UI Channels: 1 Base Channel + 2 Overlay Channels
#define CHANNELS 3
// NuttX Video Controller for PinePhone (3 UI Channels)
static struct fb_videoinfo_s videoInfo = {
.fmt = FB_FMT_RGBA32, // Pixel format (XRGB 8888)
.xres = PANEL_WIDTH, // Horizontal resolution in pixel columns
.yres = PANEL_HEIGHT, // Vertical resolution in pixel rows
.nplanes = 1, // Number of color planes supported (Base UI Channel)
.noverlays = 2 // Number of overlays supported (2 Overlay UI Channels)
};</code></pre></div>
<p>The <a href="https://github.com/apache/nuttx/blob/master/include/nuttx/video/fb.h#L472-L487"><strong>fb_videoinfo_s</strong></a> struct defines the overall PinePhone Display Interface…</p>
<ul>
<li>720 x 1440 resolution</li>
<li>32-bit ARGB 8888 pixels</li>
<li>1 Base UI Channel (Framebuffer 0)</li>
<li>2 Overlay UI Channels (Framebuffers 1 and 2)</li>
</ul>
<p>This is how we define <strong>Framebuffer 0 (UI Channel 1)</strong>: <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/test/test_a64_de.c#L5-L89">test_a64_de.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// NuttX Color Plane for PinePhone (Base UI Channel):
// Fullscreen 720 x 1440 (4 bytes per XRGB 8888 pixel)
static struct fb_planeinfo_s planeInfo = {
.fbmem = &amp;fb0, // Start of frame buffer memory
.fblen = sizeof(fb0), // Length of frame buffer memory in bytes
.stride = PANEL_WIDTH * 4, // Length of a line in bytes (4 bytes per pixel)
.display = 0, // Display number (Unused)
.bpp = 32, // Bits per pixel (XRGB 8888)
.xres_virtual = PANEL_WIDTH, // Virtual Horizontal resolution in pixel columns
.yres_virtual = PANEL_HEIGHT, // Virtual Vertical resolution in pixel rows
.xoffset = 0, // Offset from virtual to visible resolution
.yoffset = 0 // Offset from virtual to visible resolution
};</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/include/nuttx/video/fb.h#L488-L504">(<strong>fb_planeinfo_s</strong> is defined here)</a></p>
<p>And <strong>Framebuffers 1 and 2</strong> (UI Channels 2 and 3): <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/test/test_a64_de.c#L5-L89">test_a64_de.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>/// NuttX Overlays for PinePhone (2 Overlay UI Channels)
static struct fb_overlayinfo_s overlayInfo[2] = {
// First Overlay UI Channel:
// Square 600 x 600 (4 bytes per ARGB 8888 pixel)
{
.fbmem = &amp;fb1, // Start of frame buffer memory
.fblen = sizeof(fb1), // Length of frame buffer memory in bytes
.stride = FB1_WIDTH * 4, // Length of a line in bytes
.overlay = 0, // Overlay number (First Overlay)
.bpp = 32, // Bits per pixel (ARGB 8888)
.blank = 0, // TODO: Blank or unblank
.chromakey = 0, // TODO: Chroma key argb8888 formatted
.color = 0, // TODO: Color argb8888 formatted
.transp = { .transp = 0, .transp_mode = 0 }, // TODO: Transparency
.sarea = { .x = 52, .y = 52, .w = FB1_WIDTH, .h = FB1_HEIGHT }, // Selected area within the overlay
.accl = 0 // TODO: Supported hardware acceleration
},
// Second Overlay UI Channel:
// Fullscreen 720 x 1440 (4 bytes per ARGB 8888 pixel)
{
.fbmem = &amp;fb2, // Start of frame buffer memory
.fblen = sizeof(fb2), // Length of frame buffer memory in bytes
.stride = PANEL_WIDTH * 4, // Length of a line in bytes
.overlay = 1, // Overlay number (Second Overlay)
.bpp = 32, // Bits per pixel (ARGB 8888)
.blank = 0, // TODO: Blank or unblank
.chromakey = 0, // TODO: Chroma key argb8888 formatted
.color = 0, // TODO: Color argb8888 formatted
.transp = { .transp = 0, .transp_mode = 0 }, // TODO: Transparency
.sarea = { .x = 0, .y = 0, .w = PANEL_WIDTH, .h = PANEL_HEIGHT }, // Selected area within the overlay
.accl = 0 // TODO: Supported hardware acceleration
},
};</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/include/nuttx/video/fb.h#L524-L540">(<strong>fb_overlayinfo_s</strong> is defined here)</a></p>
<p><em>Whats sarea?</em></p>
<div class="example-wrap"><pre class="language-c"><code>.sarea = {
.x = 52,
.y = 52,
.w = FB1_WIDTH, // Width is 600
.h = FB1_HEIGHT // Height is 600
}</code></pre></div>
<p>Remember that Framebuffer 1 is <strong>600 pixels</strong> wide… But the PinePhone Screen is <strong>720 pixels</strong> wide.</p>
<p>We use <strong>sarea</strong> to specify that Framebuffer 1 will be rendered <strong>52 pixels</strong> from the left (X Offset), <strong>52 pixels</strong> from the top (Y Offset).</p>
<p>(So it will be centered horizontally)</p>
<h1 id="render-framebuffers"><a class="doc-anchor" href="#render-framebuffers">§</a>4 Render Framebuffers</h1>
<p>Weve defined the NuttX Framebuffers… Lets <strong>render them with the Display Engine</strong>!</p>
<p>Well walk through the steps…</p>
<ol>
<li>
<p>Initialise Display Engine</p>
</li>
<li>
<p>Initialise UI Blender</p>
</li>
<li>
<p>Initialise UI Channels</p>
</li>
<li>
<p>Enable Display Engine</p>
</li>
</ol>
<h2 id="initialise-display-engine"><a class="doc-anchor" href="#initialise-display-engine">§</a>4.1 Initialise Display Engine</h2>
<p>We begin by <strong>initialising the Display Engine</strong></p>
<div class="example-wrap"><pre class="language-c"><code>// Init Display Engine
int ret = a64_de_init();
DEBUGASSERT(ret == OK);
// Wait 160 milliseconds
up_mdelay(160);
// Render Graphics with Display Engine
ret = pinephone_render_graphics();
DEBUGASSERT(ret == OK);</code></pre></div>
<p><a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/render.zig#L1146-L1196">(Source)</a></p>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_de.c#L386-L655"><strong>a64_de_init</strong></a> comes from our NuttX Kernel Driver for Display Engine.</p>
<p><a href="https://lupyuen.github.io/articles/de#appendix-initialising-the-allwinner-a64-display-engine">(How it works)</a></p>
<p>We call <a href="https://lupyuen.github.io/articles/de3#appendix-calibrate-nuttx-delay"><strong>up_mdelay</strong></a> to wait 160 milliseconds. <a href="https://lupyuen.github.io/articles/de3#appendix-calibrate-nuttx-delay">(Explained here)</a></p>
<p>Then we call <strong>pinephone_render_graphics</strong></p>
<h2 id="initialise-ui-blender"><a class="doc-anchor" href="#initialise-ui-blender">§</a>4.2 Initialise UI Blender</h2>
<p>Inside <strong>pinephone_render_graphics</strong>, we <strong>initialise the UI Blender</strong> that will blend our UI Channels into a single image: <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/test/test_a64_de.c#L91-L157">test_a64_de.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Render graphics with A64 Display Engine
int pinephone_render_graphics(void) {
// Init the UI Blender for A64 Display Engine
int ret = a64_de_blender_init();
DEBUGASSERT(ret == OK);</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_de.c#L655-L711">(<strong>a64_de_blender_init</strong> comes from our Display Engine Driver)</a></p>
<p><a href="https://lupyuen.github.io/articles/de#appendix-programming-the-allwinner-a64-display-engine">(How it works)</a></p>
<h2 id="initialise-ui-channels"><a class="doc-anchor" href="#initialise-ui-channels">§</a>4.3 Initialise UI Channels</h2>
<p>Next we <strong>initialise UI Channel 1</strong> with Framebuffer 0…</p>
<div class="example-wrap"><pre class="language-c"><code> // Init the Base UI Channel (Channel 1)
ret = a64_de_ui_channel_init(
1, // UI Channel Number (1 for Base UI Channel)
planeInfo.fbmem, // Start of Frame Buffer Memory (address should be 32-bit)
planeInfo.fblen, // Length of Frame Buffer Memory in bytes
planeInfo.xres_virtual, // Horizontal resolution in pixel columns
planeInfo.yres_virtual, // Vertical resolution in pixel rows
planeInfo.xoffset, // Horizontal offset in pixel columns
planeInfo.yoffset // Vertical offset in pixel rows
);
DEBUGASSERT(ret == OK);</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_de.c#L711-L927">(<strong>a64_de_ui_channel_init</strong> comes from our Display Engine Driver)</a></p>
<p><a href="https://lupyuen.github.io/articles/de2#configure-framebuffer">(How it works)</a></p>
<p>Then we <strong>initialise UI Channels 2 and 3</strong> (with Framebuffers 1 and 2)…</p>
<div class="example-wrap"><pre class="language-c"><code> // For each of the 2 Overlay UI Channels (Channels 2 and 3)...
for (int i = 0; i &lt; sizeof(overlayInfo) / sizeof(overlayInfo[0]); i++) {
// Get the NuttX Framebuffer for the UI Channel
const struct fb_overlayinfo_s *ov = &amp;overlayInfo[i];
// Init the UI Channel.
// We pass NULL if the UI Channel should be disabled.
ret = a64_de_ui_channel_init(
i + 2, // UI Channel Number (2 and 3 for Overlay UI Channels)
(CHANNELS == 3) ? ov-&gt;fbmem : NULL, // Start of Frame Buffer Memory (address should be 32-bit)
ov-&gt;fblen, // Length of Frame Buffer Memory in bytes
ov-&gt;sarea.w, // Horizontal resolution in pixel columns
ov-&gt;sarea.h, // Vertical resolution in pixel rows
ov-&gt;sarea.x, // Horizontal offset in pixel columns
ov-&gt;sarea.y // Vertical offset in pixel rows
);
DEBUGASSERT(ret == OK);
}</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_de.c#L711-L927">(<strong>a64_de_ui_channel_init</strong> comes from our Display Engine Driver)</a></p>
<p><a href="https://lupyuen.github.io/articles/de2#configure-framebuffer">(How it works)</a></p>
<h2 id="enable-display-engine"><a class="doc-anchor" href="#enable-display-engine">§</a>4.4 Enable Display Engine</h2>
<p>Finally we <strong>enable the Display Engine</strong></p>
<div class="example-wrap"><pre class="language-c"><code> // Set UI Blender Route, enable Blender Pipes
// and apply the settings
ret = a64_de_enable(CHANNELS);
DEBUGASSERT(ret == OK); </code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_de.c#L927-L1017">(<strong>a64_de_enable</strong> comes from our Display Engine Driver)</a></p>
<p><a href="https://lupyuen.github.io/articles/de2#configure-blender">(How it works)</a></p>
<p>The Display Engine starts <strong>pulling pixels from our Framebuffers</strong> over Direct Memory Access (DMA). And pushes the rendered image to PinePhones LCD Display.</p>
<p>But we wont see anything until we <strong>populate our 3 Framebuffers</strong> with a Test Pattern…</p>
<div class="example-wrap"><pre class="language-c"><code> // Fill Framebuffer with Test Pattern.
// Must be called after Display Engine is Enabled,
// or missing rows will appear.
test_pattern();
return OK;
}</code></pre></div>
<p>Lets do a simple Test Pattern…</p>
<p><img src="https://lupyuen.github.io/images/de2-overlay.jpg" alt="3 Framebuffers for 3 UI Channels" /></p>
<h1 id="test-pattern"><a class="doc-anchor" href="#test-pattern">§</a>5 Test Pattern</h1>
<p>We fill our 3 Framebuffers with a simple <strong>Test Pattern</strong> (pic above)…</p>
<ul>
<li>
<p><strong>Framebuffer 0:</strong> Blue, Green and Red Blocks</p>
<p>(720 x 1440 pixels)</p>
</li>
<li>
<p><strong>Framebuffer 1:</strong> Semi-Transparent White Square</p>
<p>(600 x 600 pixels)</p>
</li>
<li>
<p><strong>Framebuffer 2:</strong> Semi-Transparent Green Circle</p>
<p>(720 x 1440 pixels)</p>
</li>
</ul>
<p>Note that Framebuffers 1 and 2 are <strong>Semi-Transparent</strong>, to show that the UI Blender works correctly.</p>
<p>This is how we <strong>populate our 3 Framebuffers:</strong> <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/test/test_a64_de.c#L159-L243">test_a64_de.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Fill the Framebuffers with a Test Pattern.
// Must be called after Display Engine is Enabled,
// or missing rows will appear.
static void test_pattern(void) {
// Zero the Framebuffers
memset(fb0, 0, sizeof(fb0));
memset(fb1, 0, sizeof(fb1));
memset(fb2, 0, sizeof(fb2));</code></pre></div>
<p><strong>Framebuffer 0</strong> (UI Channel 1) will have Blue, Green and Red Blocks…</p>
<div class="example-wrap"><pre class="language-c"><code> // Init Framebuffer 0:
// Fill with Blue, Green and Red
const int fb0_len = sizeof(fb0) / sizeof(fb0[0]);
// For every pixel...
for (int i = 0; i &lt; fb0_len; i++) {
// Colours are in XRGB 8888 format
if (i &lt; fb0_len / 4) {
// Blue for top quarter
fb0[i] = 0x80000080;
} else if (i &lt; fb0_len / 2) {
// Green for next quarter
fb0[i] = 0x80008000;
} else {
// Red for lower half
fb0[i] = 0x80800000;
}
// Fixes the missing rows, not sure why
ARM64_DMB(); ARM64_DSB(); ARM64_ISB();
}</code></pre></div>
<p>(Well talk about <strong>ARM64_DMB</strong> later)</p>
<p><strong>Framebuffer 1</strong> (UI Channel 2) will be Semi-Transparent White…</p>
<div class="example-wrap"><pre class="language-c"><code> // Init Framebuffer 1:
// Fill with Semi-Transparent White
const int fb1_len = sizeof(fb1) / sizeof(fb1[0]);
// For every pixel...
for (int i = 0; i &lt; fb1_len; i++) {
// Set the pixel to Semi-Transparent White
fb1[i] = 0x40FFFFFF; // ARGB 8888 format
// Fixes the missing rows, not sure why
ARM64_DMB(); ARM64_DSB(); ARM64_ISB();
}</code></pre></div>
<p>And <strong>Framebuffer 2</strong> (UI Channel 3) will have a Semi-Transparent Green Circle…</p>
<div class="example-wrap"><pre class="language-c"><code> // Init Framebuffer 2:
// Fill with Semi-Transparent Green Circle
const int fb2_len = sizeof(fb2) / sizeof(fb2[0]);
// For every pixel row...
for (int y = 0; y &lt; PANEL_HEIGHT; y++) {
// For every pixel column...
for (int x = 0; x &lt; PANEL_WIDTH; x++) {
// Get pixel index
const int p = (y * PANEL_WIDTH) + x;
DEBUGASSERT(p &lt; fb2_len);
// Shift coordinates so that centre of screen is (0,0)
const int half_width = PANEL_WIDTH / 2;
const int half_height = PANEL_HEIGHT / 2;
const int x_shift = x - half_width;
const int y_shift = y - half_height;
// If x^2 + y^2 &lt; radius^2, set the pixel to Semi-Transparent Green
if (x_shift*x_shift + y_shift*y_shift &lt; half_width*half_width) {
fb2[p] = 0x80008000; // Semi-Transparent Green in ARGB 8888 Format
} else { // Otherwise set to Transparent Black
fb2[p] = 0x00000000; // Transparent Black in ARGB 8888 Format
}
// Fixes the missing rows, not sure why
ARM64_DMB(); ARM64_DSB(); ARM64_ISB();
}
}
}</code></pre></div>
<p>Were done with our Test Pattern! Lets talk about <strong>ARM64_DMB</strong></p>
<p><img src="https://lupyuen.github.io/images/de-rgb.jpg" alt="Missing Rows" /></p>
<p><em>Why the Arm Barriers?</em></p>
<div class="example-wrap"><pre class="language-c"><code>// Fixes the missing rows, not sure why
ARM64_DMB(); ARM64_DSB(); ARM64_ISB();</code></pre></div>
<p>These are <a href="https://developer.arm.com/documentation/dui0489/c/arm-and-thumb-instructions/miscellaneous-instructions/dmb--dsb--and-isb"><strong>Arm64 Barrier Instructions</strong></a> that prevent caching and out-of-order execution. <a href="https://developer.arm.com/documentation/dui0489/c/arm-and-thumb-instructions/miscellaneous-instructions/dmb--dsb--and-isb">(See this)</a></p>
<p>If we omit these Barrier Instructions, the rendered image will have <strong>missing rows</strong>. (Pic above)</p>
<p>Were not sure why this happens. Maybe its the CPU Cache? DMA? Framebuffer Alignment? Memory Corruption?</p>
<p>(Doesnt happen in the original Zig version)</p>
<p><em>Why do we fill the Framebuffers after enabling the Display Engine?</em></p>
<p>Since were running on DMA (Direct Memory Access), rightfully we can fill the Framebuffers (with our Test Pattern) <em>before</em> enabling the Display Engine…</p>
<p>But this creates mysterious missing rows (pic above). So we fill the Framebuffers <strong>after enabling the Display Engine</strong>.</p>
<p>Lets run our Test Code…</p>
<p><a href="https://lupyuen.github.io/images/de3-title.jpg">(Were still missing a row at the bottom of the circle)</a></p>
<p><img src="https://lupyuen.github.io/images/dsi3-steps.jpg" alt="Complete Display Driver for PinePhone" /></p>
<p><a href="https://lupyuen.github.io/articles/dsi3#complete-display-driver-for-pinephone"><em>Complete Display Driver for PinePhone</em></a></p>
<h1 id="complete-display-driver"><a class="doc-anchor" href="#complete-display-driver">§</a>6 Complete Display Driver</h1>
<p><em>Are we done yet with our Display Driver for PinePhone?</em></p>
<p>Not quite! PinePhone needs a <strong>super complex Display Driver</strong> that will handle 11 steps (pic above)…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/dsi3#complete-display-driver-for-pinephone"><strong>“Complete Display Driver for PinePhone”</strong></a></li>
</ul>
<p>Weve implemented most of this in the NuttX Kernel, were now converting the remaining bits <strong>from Zig to C</strong>.</p>
<p><em>So how do we test this hodgepodge of Zig and C?</em></p>
<p>We created a <strong>Zig Test Program</strong> that glues together the Zig and C bits for testing.</p>
<p>Here are <strong>all 11 steps</strong> of our upcoming Display Driver, hodgepodged with Zig: <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/render.zig#L1146-L1196">render.zig</a></p>
<div class="example-wrap"><pre class="language-zig"><code>// Zig Test Program that renders 3 UI Channels in Zig and C...
// Turn on PinePhone Display Backlight (in Zig)
backlight.backlight_enable(90); // 90% brightness
// Init A64 Timing Controller TCON0 (in C)
// PANEL_WIDTH is 720, PANEL_HEIGHT is 1440
_ = a64_tcon0_init(PANEL_WIDTH, PANEL_HEIGHT);
// Init PinePhone Power Management Integrated Circuit (in C)
_ = pinephone_pmic_init();
// Wait 15 milliseconds for power supply and power-on init
up_mdelay(15);</code></pre></div>
<p>In the code above, we do these steps…</p>
<ul>
<li>
<p>Turn on PinePhones <a href="https://lupyuen.github.io/articles/de#appendix-display-backlight"><strong>Display Backlight</strong></a></p>
<p><a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/backlight.zig">(<strong>backlight_enable</strong> is in Zig)</a></p>
</li>
<li>
<p>Initialise the A64 <a href="https://lupyuen.github.io/articles/de#appendix-timing-controller-tcon0"><strong>Timing Controller TCON0</strong></a></p>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_tcon0.c#L180-L474">(<strong>a64_tcon0_init</strong> comes from our NuttX Driver for Timing Controller TCON0)</a></p>
</li>
<li>
<p>Initialise PinePhones <a href="https://lupyuen.github.io/articles/de#appendix-power-management-integrated-circuit"><strong>Power Management Integrated Circuit (PMIC)</strong></a> to power on the LCD Panel</p>
<p><a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/test/test_a64_rsb.c">(<strong>pinephone_pmic_init</strong> will be added to NuttX Kernel)</a></p>
</li>
<li>
<p>Wait 15 milliseconds</p>
</li>
</ul>
<div class="example-wrap"><pre class="language-zig"><code>// Enable A64 MIPI Display Serial Interface (in C)
_ = a64_mipi_dsi_enable();
// Enable A64 MIPI Display Physical Layer (in C)
_ = a64_mipi_dphy_enable();</code></pre></div>
<p>Here we enable the A64 <a href="https://lupyuen.github.io/articles/dsi3"><strong>MIPI Display Serial Interface</strong></a> and <a href="https://lupyuen.github.io/articles/dsi3"><strong>MIPI Display Physical Layer</strong></a>.</p>
<p><a href="https://lupyuen.github.io/articles/dsi3#enable-mipi-dsi-and-d-phy">(<strong>a64_mipi_dsi_enable</strong> comes from our NuttX Driver for MIPI Display Serial Interface)</a></p>
<p><a href="https://lupyuen.github.io/articles/dsi3#enable-mipi-dsi-and-d-phy">(<strong>a64_mipi_dphy_enable</strong> too)</a></p>
<div class="example-wrap"><pre class="language-zig"><code>// Reset LCD Panel (in Zig)
panel.panel_reset();
// Wait 15 milliseconds for LCD Panel
up_mdelay(15);
// Init LCD Panel (in C)
_ = pinephone_panel_init();</code></pre></div>
<p>Next we reset the <a href="https://lupyuen.github.io/articles/de#appendix-reset-lcd-panel"><strong>LCD Panel</strong></a>, wait 15 milliseconds and send the <a href="https://lupyuen.github.io/articles/dsi3#send-mipi-dsi-packet"><strong>Initialisation Commands</strong></a> to the LCD Controller.</p>
<p><a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/panel.zig">(<strong>panel_reset</strong> is in Zig)</a></p>
<p><a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/test/test_a64_mipi_dsi.c#L43-L453">(<strong>pinephone_panel_init</strong> will be added to NuttX Kernel)</a></p>
<p><a href="https://lupyuen.github.io/articles/dsi3#send-mipi-dsi-packet">(Which calls <strong>a64_mipi_dsi_write</strong> from our NuttX Driver for MIPI Display Serial Interface)</a></p>
<div class="example-wrap"><pre class="language-zig"><code>// Start A64 MIPI Display Serial Interface (in C)
_ = a64_mipi_dsi_start();</code></pre></div>
<p>We start A64s <a href="https://lupyuen.github.io/articles/dsi3#enable-mipi-dsi-and-d-phy"><strong>MIPI Display Serial Interface</strong></a>.</p>
<p><a href="https://lupyuen.github.io/articles/dsi3#enable-mipi-dsi-and-d-phy">(<strong>a64_mipi_dsi_start</strong> comes from our NuttX Driver for MIPI Display Serial Interface)</a></p>
<div class="example-wrap"><pre class="language-zig"><code>// Init A64 Display Engine (in C)
_ = a64_de_init();
// Wait 160 milliseconds for Display Engine
up_mdelay(160);</code></pre></div>
<p>We <strong>initialise the Display Engine</strong>.</p>
<p><a href="https://lupyuen.github.io/articles/de3#initialise-display-engine">(Weve seen <strong>a64_de_init</strong> earlier)</a></p>
<div class="example-wrap"><pre class="language-zig"><code>// Render Graphics with Display Engine (in C)
_ = pinephone_render_graphics();</code></pre></div>
<p>Finally we <strong>render the framebuffers</strong> with the Display Engine.</p>
<p><a href="https://lupyuen.github.io/articles/de3#initialise-ui-blender">(Weve seen <strong>pinephone_render_graphics</strong> earlier)</a></p>
<p>This is how we compile our Zig Test Program…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen/pinephone-nuttx#test-mipi-dsi-for-nuttx-kernel"><strong>“Compile Zig Test Program”</strong></a></p>
<p><a href="https://github.com/lupyuen/pinephone-nuttx/releases/tag/v1.2.1">(Download the binaries here)</a></p>
</li>
</ul>
<p>We boot NuttX on PinePhone <a href="https://nuttx.apache.org/docs/latest/platforms/arm/a64/boards/pinephone/index.html">(with a microSD Card)</a> and run our Zig Test Program…</p>
<div class="example-wrap"><pre class="language-text"><code>NuttShell (NSH) NuttX-11.0.0-pinephone
nsh&gt; uname -a
NuttX 11.0.0-pinephone 64a54d2-dirty
Dec 21 2022 21:48:25 arm64 pinephone
nsh&gt; hello 0</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/bd943bea51c6f90debd05cd4f4a8585d">(Source)</a></p>
<p>PinePhone renders our Test Pattern on the LCD Display (pic below). Yep our (work-in-progress) PinePhone Display Driver has been tested successfully!</p>
<p>Heres the <strong>Debug Log</strong> from our Zig Test Program…</p>
<ul>
<li><a href="https://gist.github.com/lupyuen/bd943bea51c6f90debd05cd4f4a8585d"><strong>Test Log for Zig Test Program</strong></a></li>
</ul>
<p><em>Wont the Debug Logging create extra latency that might affect the driver?</em></p>
<p>Thats why we also test with <strong>Debug Logging disabled</strong></p>
<ul>
<li><a href="https://gist.github.com/lupyuen/df50aa74b33b39069e89eefda5957423"><strong>Test Log with Debug Logging Disabled</strong></a></li>
</ul>
<p>Lets talk about the upcoming drivers that were adding to NuttX Kernel…</p>
<p><img src="https://lupyuen.github.io/images/de3-title.jpg" alt="Rendering graphics on PinePhone with Apache NuttX RTOS" /></p>
<h1 id="upcoming-drivers"><a class="doc-anchor" href="#upcoming-drivers">§</a>7 Upcoming Drivers</h1>
<p><em>Which bits of our NuttX Display Driver are still in Zig?</em></p>
<p>These parts are still in Zig, <strong>pending conversion to C</strong></p>
<ul>
<li>
<p>Driver for PinePhone <a href="https://lupyuen.github.io/articles/de#appendix-display-backlight"><strong>Display Backlight</strong></a></p>
</li>
<li>
<p>Driver for PinePhone <a href="https://lupyuen.github.io/articles/de#appendix-reset-lcd-panel"><strong>LCD Panel</strong></a></p>
</li>
</ul>
<p>These have just been <strong>converted from Zig to C</strong>, now adding to NuttX Kernel…</p>
<ul>
<li>
<p>Driver for PinePhone <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/test/test_a64_rsb.c"><strong>Power Management Integrated Circuit (PMIC)</strong></a></p>
<p><a href="https://lupyuen.github.io/articles/de#appendix-power-management-integrated-circuit">(Which powers the LCD Panel)</a></p>
</li>
<li>
<p>Driver for A64 <a href="https://lupyuen.github.io/articles/de#appendix-reduced-serial-bus"><strong>Reduced Serial Bus (RSB)</strong></a></p>
<p>(Needed for PinePhone PMIC)</p>
</li>
</ul>
<p><em>Where will the new drivers live inside the NuttX Kernel?</em></p>
<p>The drivers for Display Backlight, LCD Panel and PMIC will go into the new <strong>PinePhone LCD Driver</strong>.</p>
<p>Which will follow the design of the <a href="https://github.com/apache/nuttx/blob/master/boards/arm/stm32f7/stm32f746g-disco/src/stm32_lcd.c"><strong>STM32F7 LCD Driver</strong></a> in NuttX…</p>
<ol>
<li>
<p>At startup, <strong>stm32_bringup</strong> calls <strong>fb_register</strong></p>
<p><a href="https://github.com/apache/nuttx/blob/master/boards/arm/stm32f7/stm32f746g-disco/src/stm32_bringup.c#L100">(<strong>stm32_bringup.c</strong>)</a></p>
</li>
<li>
<p>To initialise the Framebuffer, <strong>fb_register</strong> calls <strong>up_fbinitialize</strong></p>
<p><a href="https://github.com/apache/nuttx/blob/master/drivers/video/fb.c#L795-L805">(<strong>fb.c</strong>)</a></p>
</li>
<li>
<p>To initialise the Display Driver, <strong>up_fbinitialize</strong> calls <strong>stm32_ltdcinitialize</strong></p>
<p><a href="https://github.com/apache/nuttx/blob/master/boards/arm/stm32f7/stm32f746g-disco/src/stm32_lcd.c#L72">(<strong>stm32_lcd.c</strong>)</a></p>
</li>
<li>
<p>Inside the Display Driver, <strong>stm32_ltdcinitialize</strong> creates the NuttX Framebuffer</p>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm/src/stm32f7/stm32_ltdc.c#L2971">(<strong>stm32_ltdc.c</strong>)</a></p>
</li>
<li>
<p>NuttX Framebuffer is here: <a href="https://github.com/apache/nuttx/blob/master/arch/arm/src/stm32f7/stm32_ltdc.c#L864"><strong>stm32_ltdc.c</strong></a></p>
</li>
</ol>
<p>Our new PinePhone LCD Driver shall execute all 11 steps as described earlier…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/de3#complete-display-driver"><strong>“Complete Display Driver”</strong></a></li>
</ul>
<p>Probably inside our new implementation of <strong>up_fbinitialize</strong>. Work-in-progress…</p>
<ul>
<li><a href="https://github.com/lupyuen2/wip-nuttx/blob/lcd/boards/arm64/a64/pinephone/src/pinephone_display.c"><strong>boards/arm64/a64/pinephone/src/pinephone_display.c</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/de3-run.png" alt="Zig Test Program running on Apache NuttX RTOS for PinePhone" /></p>
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>8 Whats Next</h1>
<p>Very soon the official NuttX Kernel will be rendering graphics on PinePhones LCD Display… Stay tuned for updates!</p>
<p>Please check out the other articles on NuttX for PinePhone…</p>
<ul>
<li><a href="https://github.com/lupyuen/pinephone-nuttx"><strong>“Apache NuttX RTOS for PinePhone”</strong></a></li>
</ul>
<p>Many Thanks to my <a href="https://lupyuen.github.io/articles/sponsor"><strong>GitHub Sponsors</strong></a> for supporting my work! This article wouldnt have been possible without your support.</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/sponsor"><strong>Sponsor me a coffee</strong></a></p>
</li>
<li>
<p><a href="https://news.ycombinator.com/item?id=34100614"><strong>Discuss this article on Hacker News</strong></a></p>
</li>
<li>
<p><a href="https://www.reddit.com/r/PINE64official/comments/zt1181/nuttx_rtos_for_pinephone_display_engine/"><strong>Discuss this article on Reddit</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-ox64"><strong>My Current Project: “Apache NuttX RTOS for Ox64 BL808”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-star64"><strong>My Other Project: “NuttX for Star64 JH7110”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinephone-nuttx"><strong>Older Project: “NuttX for PinePhone”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io"><strong>Check out my articles</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/rss.xml"><strong>RSS Feed</strong></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/de3.md"><strong>lupyuen.github.io/src/de3.md</strong></a></p>
<h1 id="appendix-calibrate-nuttx-delay"><a class="doc-anchor" href="#appendix-calibrate-nuttx-delay">§</a>9 Appendix: Calibrate NuttX Delay</h1>
<p><em>Can we call sleep() or usleep() in our NuttX Display Driver?</em></p>
<p>Sorry Nope! Most of our Display Driver code runs in the NuttX Kernel at startup.</p>
<p>Calling <code>sleep()</code> or <code>usleep()</code> will <strong>crash the kernel</strong></p>
<p>Because the kernel is still starting up!</p>
<p><em>So how do we wait a while in our NuttX Display Driver?</em></p>
<p>We call <strong><code>up_mdelay()</code></strong> like so…</p>
<div class="example-wrap"><pre class="language-c"><code>// Wait 160 milliseconds
up_mdelay(160);</code></pre></div>
<p><em>How does up_mdelay() work?</em></p>
<p>Its a very simple loop: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/common/arm64_assert.c#L64-L75">arm64_assert.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Wait for the specified milliseconds
void up_mdelay(unsigned int milliseconds) {
volatile unsigned int i;
volatile unsigned int j;
for (i = 0; i &lt; milliseconds; i++) {
for (j = 0; j &lt; CONFIG_BOARD_LOOPSPERMSEC; j++) {
}
}
}</code></pre></div>
<p><em>Huh? Wont the compiler optimise the code and remove the loop?</em></p>
<p>It wont because we declared the variables as <strong><code>volatile</code></strong>.</p>
<p>The NuttX Disassembly shows that the loop is still intact: <a href="https://github.com/lupyuen/pinephone-nuttx/releases/download/v1.2.1/nuttx.S">nuttx.S</a></p>
<div class="example-wrap"><pre class="language-text"><code>arm64_assert.c:69 (discriminator 2)
for (i = 0; i &lt; milliseconds; i++)
40081830: b9400be1 ldr w1, [sp, #8]
40081834: 11000421 add w1, w1, #0x1
40081838: b9000be1 str w1, [sp, #8]
4008183c: 17fffff4 b 4008180c &lt;up_mdelay+0x10&gt;
arm64_assert.c:71 (discriminator 3)
for (j = 0; j &lt; CONFIG_BOARD_LOOPSPERMSEC; j++)
40081840: b9400fe1 ldr w1, [sp, #12]
40081844: 11000421 add w1, w1, #0x1
40081848: b9000fe1 str w1, [sp, #12]
4008184c: 17fffff6 b 40081824 &lt;up_mdelay+0x28&gt;</code></pre></div>
<p><em>Whats CONFIG_BOARD_LOOPSPERMSEC?</em></p>
<p>Thats a magic constant computed by the <strong>NuttX Calibration Tool For udelay</strong>.</p>
<p>To install the calibration tool…</p>
<div class="example-wrap"><pre class="language-bash"><code>make menuconfig</code></pre></div>
<p>Then select…</p>
<div class="example-wrap"><pre class="language-text"><code>Application Configuration &gt; Examples &gt; Calibration Tool For udelay </code></pre></div>
<p>And rebuild NuttX.</p>
<p>Boot NuttX on PinePhone and run <strong><code>calib_udelay</code></strong></p>
<div class="example-wrap"><pre class="language-text"><code>nsh&gt; calib_udelay
Calibrating timer for main calibration...
Performing main calibration for udelay.This will take approx. 17.280 seconds.
Calibration slope for udelay:
Y = m*X + b, where
X is loop iterations,
Y is time in nanoseconds,
b is base overhead,
m is nanoseconds per loop iteration.
m = 8.58195489 nsec/iter
b = -347067.66917297 nsec
Correlation coefficient, R² = 1.0000
Without overhead, 0.11652356 iterations per nanosecond and 116523.57 iterations per millisecond.
Recommended setting for CONFIG_BOARD_LOOPSPERMSEC:
CONFIG_BOARD_LOOPSPERMSEC=116524</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/5c7de17bfe6cd192a852182cdf217c43">(See the Complete Log)</a></p>
<p>We update the <strong>NuttX Board Configuration</strong> for PinePhone with the computed value: <a href="https://github.com/apache/nuttx/blob/master/boards/arm64/a64/pinephone/configs/nsh/defconfig">pinephone/configs/nsh/defconfig</a></p>
<div class="example-wrap"><pre class="language-text"><code>CONFIG_BOARD_LOOPSPERMSEC=116524</code></pre></div>
<p>(PinePhone is probably the fastest NuttX Board ever!)</p>
<p><em>What if our driver needs to wait a while AFTER the NuttX Kernel has been started?</em></p>
<p>Call <a href="https://github.com/apache/nuttx/blob/master/sched/signal/sig_usleep.c#L38-L86"><strong><code>nxsig_usleep()</code></strong></a> instead.</p>
<p>It suspends the current thread, instead of doing a busy-wait loop.</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>