lupyuen.org/articles/serial.html

854 lines
No EOL
47 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: UART Driver</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: UART Driver"
data-rh="true">
<meta property="og:description"
content="Our PinePhone Operating System will be awfully quiet if we don't implement UART Input and Output... Here's how we implemented the UART Driver for Apache NuttX RTOS"
data-rh="true">
<meta name="description"
content="Our PinePhone Operating System will be awfully quiet if we don't implement UART Input and Output... Here's how we implemented the UART Driver for Apache NuttX RTOS">
<meta property="og:image"
content="https://lupyuen.github.io/images/serial-title.jpg">
<meta property="og:type"
content="article" data-rh="true">
<link rel="canonical" href="https://lupyuen.org/articles/serial.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: UART Driver</h1>
<nav id="rustdoc"><ul>
<li><a href="#uart-controller" title="UART Controller">1 UART Controller</a><ul></ul></li>
<li><a href="#uart-with-polling" title="UART With Polling">2 UART With Polling</a><ul>
<li><a href="#transmit-uart" title="Transmit UART">2.1 Transmit UART</a><ul></ul></li>
<li><a href="#wait-to-transmit" title="Wait To Transmit">2.2 Wait To Transmit</a><ul></ul></li>
<li><a href="#receive-uart" title="Receive UART">2.3 Receive UART</a><ul></ul></li>
<li><a href="#wait-to-receive" title="Wait To Receive">2.4 Wait To Receive</a><ul></ul></li>
<li><a href="#arm64-assembly" title="Arm64 Assembly">2.5 Arm64 Assembly</a><ul></ul></li></ul></li>
<li><a href="#uart-with-interrupts" title="UART With Interrupts">3 UART With Interrupts</a><ul>
<li><a href="#attach-interrupt-handler" title="Attach Interrupt Handler">3.1 Attach Interrupt Handler</a><ul></ul></li>
<li><a href="#enable-interrupt" title="Enable Interrupt">3.2 Enable Interrupt</a><ul></ul></li>
<li><a href="#handle-interrupt" title="Handle Interrupt">3.3 Handle Interrupt</a><ul></ul></li>
<li><a href="#uart-transmit" title="UART Transmit">3.4 UART Transmit</a><ul></ul></li>
<li><a href="#uart-receive" title="UART Receive">3.5 UART Receive</a><ul></ul></li></ul></li>
<li><a href="#initialise-uart" title="Initialise UART">4 Initialise UART</a><ul></ul></li>
<li><a href="#nuttx-uart-driver" title="NuttX UART Driver">5 NuttX UART Driver</a><ul></ul></li>
<li><a href="#uart-in-action" title="UART In Action">6 UART In Action</a><ul></ul></li>
<li><a href="#whats-next" title="Whats Next">7 Whats Next</a><ul></ul></li>
<li><a href="#appendix-uart-ports-on-pinephone" title="Appendix: UART Ports on PinePhone">8 Appendix: UART Ports on PinePhone</a><ul></ul></li></ul></nav><p>📝 <em>9 Sep 2022</em></p>
<p><img src="https://lupyuen.github.io/images/serial-title.jpg" alt="PinePhone Hacking with Pinebook Pro and BLÅHAJ" /></p>
<p><em>PinePhone Hacking with Pinebook Pro and BLÅHAJ</em></p>
<p><strong>UPDATE:</strong> PinePhone is now officially supported by Apache NuttX RTOS <a href="https://lupyuen.github.io/articles/what">(See this)</a></p>
<p>Last week we spoke about creating our own <strong>Operating System</strong> for <a href="https://wiki.pine64.org/index.php/PinePhone"><strong>Pine64 PinePhone</strong></a></p>
<ul>
<li><a href="https://lupyuen.github.io/articles/uboot"><strong>“PinePhone boots Apache NuttX RTOS”</strong></a></li>
</ul>
<p>Our PinePhone OS will be awfully quiet until we implement <strong>UART Input and Output</strong>. (For the Serial Debug Console)</p>
<p>Today well learn about the <strong>UART Controller</strong> for the Allwinner A64 SoC inside PinePhone…</p>
<ul>
<li>
<p>Transmit and receive UART Data the Polling Way</p>
</li>
<li>
<p>Also the Interrupt-Driven Way</p>
</li>
<li>
<p>Enabling UART Interrupts</p>
</li>
<li>
<p>Handling UART Interrupts</p>
</li>
</ul>
<p>And how we implemented PinePhones <strong>UART Driver</strong> for <a href="https://lupyuen.github.io/articles/uboot"><strong>Apache NuttX RTOS</strong></a>.</p>
<p>Lets dive into our <strong>NuttX Porting Journal</strong> and learn how we made PinePhone chatty over UART…</p>
<ul>
<li><a href="https://github.com/lupyuen/pinephone-nuttx"><strong>lupyuen/pinephone-nuttx</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/uboot-uart1.png" alt="Allwinner A64 UART Controller Registers" /></p>
<p><a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><em>Allwinner A64 UART Controller Registers</em></a></p>
<h1 id="uart-controller"><a class="doc-anchor" href="#uart-controller">§</a>1 UART Controller</h1>
<p>Our operating system will print some output on PinePhones <strong>Serial Debug Console</strong> as it runs. (And receive input too)</p>
<p>To do that, well talk to the <strong>UART Controller</strong> on the Allwinner A64 SoC…</p>
<ul>
<li><a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><strong>Allwinner A64 User Manual</strong></a></li>
</ul>
<p>Flip the <a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><strong>A64 User Manual</strong></a> to page 562 (“UART”) and well see the <strong>UART Registers</strong>. (Pic above)</p>
<p>PinePhones Serial Console is connected to <strong>UART0</strong> at Base Address <strong><code>0x01C2</code> <code>8000</code></strong></p>
<p>Which we define like so: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/hardware/a64_memorymap.h#L42-L46">a64_memorymap.h</a></p>
<div class="example-wrap"><pre class="language-c"><code>/* A64 UART0 Base Address */
#define A64_UART0_ADDR 0x1C28000
/* A64 UART0 IRQ */
#define A64_UART0_IRQ 32 </code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.h#L53-L59">(<code>A64_UART0_IRQ</code> is defined here)</a></p>
<p>(Well talk about <code>A64_UART0_IRQ</code> in a while)</p>
<p><img src="https://lupyuen.github.io/images/arm-uart2.jpg" alt="PinePhone connected to USB Serial Debug Cable" /></p>
<p>Check that PinePhone is connected to our computer with the <strong>USB Serial Debug Cable</strong> (pic above) at 115.2 kbps…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/uboot#usb-serial-debug-cable"><strong>“USB Serial Debug Cable”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/uboot#boot-log"><strong>“Boot Log”</strong></a></p>
</li>
</ul>
<p>Lets read and write UART Data the easier (inefficient) way, via Polling…</p>
<p><img src="https://lupyuen.github.io/images/uboot-uart2.png" alt="A64 UART Receive and Transmit Registers UART_RBR and UART_THR" /></p>
<p><a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><em>A64 UART Receive and Transmit Registers UART_RBR and UART_THR</em></a></p>
<h1 id="uart-with-polling"><a class="doc-anchor" href="#uart-with-polling">§</a>2 UART With Polling</h1>
<p>Page 563 of the <a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><strong>Allwinner A64 User Manual</strong></a> tells us the UART Registers for <strong>reading and writing UART Data</strong> (pic above)…</p>
<ul>
<li>
<p><strong>Receiver Buffer Register (RBR)</strong></p>
<p>(At Offset <code>0x00</code>)</p>
</li>
<li>
<p><strong>Transmit Holding Register (THR)</strong></p>
<p>(Also at Offset <code>0x00</code>)</p>
</li>
</ul>
<p>Lets write some UART Data…</p>
<h2 id="transmit-uart"><a class="doc-anchor" href="#transmit-uart">§</a>2.1 Transmit UART</h2>
<p>The <strong>Transmit Holding Register (THR)</strong> is at address <strong><code>0x01C2</code> <code>8000</code></strong>. (Since Offset is 0)</p>
<p>Well write our output data to <strong><code>0x01C2</code> <code>8000</code></strong>, byte by byte, and the data will appear in the Serial Console: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L815-L838">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Send one byte to PinePhone Allwinner A64 UART
static void a64_uart_send(struct uart_dev_s *dev, int ch)
{
// Write to UART Transmit Holding Register (UART_THR)
// Offset: 0x0000
uint8_t *uart_thr = (uint8_t *)
(A64_UART0_ADDR + 0x0);
// Bits 7 to 0: Transmit Holding Register (THR)
// Data to be transmitted on the serial output port . Data should only be
// written to the THR when the THR Empty (THRE) bit (UART_LSR[5]) is set.
// If in FIFO mode and FIFOs are enabled (UART_FCR[0] = 1) and THRE is set,
// 16 number of characters of data may be written to the THR before the
// FIFO is full. Any attempt to write data when the FIFO is full results in the
// write data being lost.
*uart_thr = ch;
}</code></pre></div>
<p>So this code…</p>
<div class="example-wrap"><pre class="language-c"><code>a64_uart_send(NULL, &#39;H&#39;);
a64_uart_send(NULL, &#39;E&#39;);
a64_uart_send(NULL, &#39;Y&#39;);</code></pre></div>
<p>Will print this to PinePhones Serial Console…</p>
<div class="example-wrap"><pre class="language-text"><code>HEY</code></pre></div>
<p><em>Will this work if we send a huge chunk of text?</em></p>
<p>Nope, well overflow the <strong>Transmit FIFO Buffer</strong>!</p>
<p>The pic below shows what happens if we print too much… The overflow characters <strong>will get dropped</strong>. (Hence the solitary “<code>f</code>”)</p>
<p>To fix this, we <strong>wait for the UART Port</strong> to be ready before we transmit. Well see how in the next section.</p>
<p><em>Whats <code>uart_dev_s</code>?</em></p>
<p>Thats the convention that NuttX RTOS expects for UART Drivers.</p>
<p>We may drop the parameter if were not on NuttX.</p>
<p><img src="https://lupyuen.github.io/images/uboot-title.png" alt="Why we wait for the UART Port before we transmit" /></p>
<p><em>Why we wait for the UART Port before we transmit</em></p>
<h2 id="wait-to-transmit"><a class="doc-anchor" href="#wait-to-transmit">§</a>2.2 Wait To Transmit</h2>
<p>Lets check if the UART Port is <strong>ready to accept output data</strong> for transmission.</p>
<p>We read Bit 5 of the <strong>Line Status Register (UART_LSR)</strong> at Offset <code>0x14</code>: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L876-L898">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Return true if Transmit FIFO is not full for PinePhone Allwinner A64 UART
static bool a64_uart_txready(struct uart_dev_s *dev)
{
// Read from UART Line Status Register (UART_LSR)
// Offset: 0x0014
const uint8_t *uart_lsr = (const uint8_t *)
(A64_UART0_ADDR + 0x14);
// Bit 5: TX Holding Register Empty (THRE)
// If the FIFOs are disabled, this bit is set to &quot;1&quot; whenever the TX Holding
// Register is empty and ready to accept new data and it is cleared when the
// CPU writes to the TX Holding Register.
// If the FIFOs are enabled, this bit is set to &quot;1&quot; whenever the TX FIFO is
// empty and it is cleared when at least one byte is written
// to the TX FIFO.
return (*uart_lsr &amp; 0b100000) != 0; // Transmit FIFO is ready if THRE=1 (Bit 5)
}</code></pre></div>
<p>Now we can print to the Serial Console <strong>without dropping characters</strong></p>
<div class="example-wrap"><pre class="language-c"><code>// Wait for UART Port to be ready
while (!a64_uart_txready(NULL)) {}
// Send one byte of data
a64_uart_send(NULL, &#39;A&#39;);</code></pre></div>
<p><em>Busy Wait in an Empty Loop? Thats wasteful aint it?</em></p>
<p>Yes were wasting CPU Cycles waiting for UART.</p>
<p>Thats why NuttX and other Operating Systems will insist that we implement <strong>UART with Interrupts</strong> (instead of Polling).</p>
<p>Well cover this in a while.</p>
<p>Also note that PinePhones UART Port has a <strong>Transmit FIFO Buffer</strong> of 16 characters.</p>
<p>Our UART Driver doesnt check for the available space in the Transmit FIFO Buffer.</p>
<p>For efficiency, we should probably fix this: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L900-L919">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Return true if Transmit FIFO is empty for PinePhone Allwinner A64 UART
static bool a64_uart_txempty(struct uart_dev_s *dev)
{
// Transmit FIFO is empty if Transmit FIFO is not full (for now)
return a64_uart_txready(dev);
}</code></pre></div>
<p>Moving on from UART Transmit to Receive…</p>
<p><img src="https://lupyuen.github.io/images/uboot-uart2.png" alt="A64 UART Registers UART_RBR and UART_THR" /></p>
<p><a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><em>A64 UART Registers UART_RBR and UART_THR</em></a></p>
<h2 id="receive-uart"><a class="doc-anchor" href="#receive-uart">§</a>2.3 Receive UART</h2>
<p>Now that PinePhone can talk to us, lets make sure we can talk back!</p>
<p>Anything that we type into PinePhones Serial Console will appear in the <strong>Receiver Buffer Register (RBR)</strong>, byte by byte.</p>
<p>The Receiver Buffer Register is at address <strong><code>0x01C2</code> <code>8000</code></strong>. (Since Offset is 0). This how we read it: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L727-L753">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Receive data from PinePhone Allwinner A64 UART
static int a64_uart_receive(struct uart_dev_s *dev, unsigned int *status)
{
// Status is always OK
*status = 0;
// Read from UART Receiver Buffer Register (UART_RBR)
// Offset: 0x0000
const uint8_t *uart_rbr = (const uint8_t *)
(A64_UART0_ADDR + 0x00);
// Bits 7 to 0: Receiver Buffer Register (RBR)
// Data byte received on the serial input port . The data in this register is
// valid only if the Data Ready (DR) bit in the UART Line Status Register
// (UART_LCR) is set.
//
// If in FIFO mode and FIFOs are enabled (UART_FCR[0] set to one), this
// register accesses the head of the receive FIFO. If the receive FIFO is full
// and this register is not read before the next data character arrives, then
// the data already in the FIFO is preserved, but any incoming data are lost
// and an overrun error occurs.
return *uart_rbr;
}</code></pre></div>
<p>(We may drop the <strong><code>dev</code></strong> and <strong><code>status</code></strong> parameters if were not on NuttX)</p>
<p>But dont read the UART Input yet! We need to wait for the UART Input to be available…</p>
<h2 id="wait-to-receive"><a class="doc-anchor" href="#wait-to-receive">§</a>2.4 Wait To Receive</h2>
<p>Lets check if theres <strong>UART Input</strong> ready to be read from the UART Port.</p>
<p>We read Bit 0 of the <strong>Line Status Register (UART_LSR)</strong> at Offset <code>0x14</code>: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L791-L813">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Return true if Receive FIFO is not empty for PinePhone Allwinner A64 UART
static bool a64_uart_rxavailable(struct uart_dev_s *dev)
{
// Read from UART Line Status Register (UART_LSR)
// Offset: 0x0014
const uint8_t *uart_lsr = (const uint8_t *)
(A64_UART0_ADDR + 0x14);
// Bit 0: Data Ready (DR)
// This is used to indicate that the receiver contains at least one character in
// the RBR or the receiver FIFO.
// 0: no data ready
// 1: data ready
// This bit is cleared when the RBR is read in non-FIFO mode, or when the
// receiver FIFO is empty, in FIFO mode.
return (*uart_lsr) &amp; 1; // DR=1 if data is ready
}</code></pre></div>
<p>Now were ready to read UART Input…</p>
<div class="example-wrap"><pre class="language-c"><code>// Wait for UART Input to be ready
while (!a64_uart_rxavailable(NULL)) {}
// Read one byte of data
int status;
int ch = a64_uart_receive(NULL, &amp;status);</code></pre></div>
<p><em>Again… This looks like a waste of CPU Cycles?</em></p>
<p>Indeed, UART Input wont work well on multitasking operating systems unless we do it with Interrupts. (Coming up in a sec!)</p>
<h2 id="arm64-assembly"><a class="doc-anchor" href="#arm64-assembly">§</a>2.5 Arm64 Assembly</h2>
<p><em>Is it safe to do UART Output when our PinePhone OS is booting?</em></p>
<p>Yep we may call <a href="https://lupyuen.github.io/articles/serial#transmit-uart"><strong><code>a64_uart_send</code></strong></a> and <a href="https://lupyuen.github.io/articles/serial#wait-to-transmit"><strong><code>a64_uart_txready</code></strong></a> when our OS is booting.</p>
<p>For Arm64 Assembly we have something similar: This <strong>Arm64 Assembly Macro</strong> is super helpful for printing debug messages in our Arm64 Startup Code…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/uboot#nuttx-uart-macros"><strong>“NuttX UART Macros”</strong></a></li>
</ul>
<p><em>Dont we need to set the Baud Rate for the UART Port?</em></p>
<p>Right now we dont initialise the UART Port because U-Boot has kindly done it for us. (At 115.2 kbps)</p>
<p>Well come back to this in a while.</p>
<h1 id="uart-with-interrupts"><a class="doc-anchor" href="#uart-with-interrupts">§</a>3 UART With Interrupts</h1>
<p>Earlier we saw UART with Polling, and how inefficient it can get. Now we talk about <strong>UART with Interrupts</strong> and how we…</p>
<ul>
<li>
<p>Attach a UART Interrupt Handler</p>
</li>
<li>
<p>Enable UART Interrupts</p>
</li>
<li>
<p>Handle UART Interrupts</p>
</li>
</ul>
<p><em>Does NuttX use UART Polling or Interrupts?</em></p>
<p>NuttX uses both Polling-based UART and Interrupt-driven UART. NuttX OS writes <strong>System Logs</strong> (<code>syslog</code>) the UART Polling way…</p>
<div class="example-wrap"><pre class="language-c"><code>sinfo(&quot;This is printed on UART with Polling\n&quot;);</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L1400-L1432">(By calling <strong><code>up_putc</code></strong>)</a></p>
<p>And NuttX Apps print <strong>App Messages</strong> the UART Interrupt Way…</p>
<div class="example-wrap"><pre class="language-c"><code>printf(&quot;This is printed on UART with Interrupts\n&quot;);</code></pre></div>
<p>So if we dont see any App Messages in NuttX, check that the <strong>UART Interrupts</strong> are OK.</p>
<p><img src="https://lupyuen.github.io/images/interrupt-peripheral.jpg" alt="Shared Peripheral Interrupts for Allwinner A64s Generic Interrupt Controller" /></p>
<p><a href="https://lupyuen.github.io/articles/interrupt#generic-interrupt-controller"><em>Shared Peripheral Interrupts for Allwinner A64s Generic Interrupt Controller</em></a></p>
<h2 id="attach-interrupt-handler"><a class="doc-anchor" href="#attach-interrupt-handler">§</a>3.1 Attach Interrupt Handler</h2>
<p>PinePhones UART Controller will trigger an Interrupt for <strong>Transmit and Receive Events</strong> when…</p>
<ul>
<li>
<p>Transmit Buffer becomes empty</p>
</li>
<li>
<p>Received Data becomes available</p>
</li>
</ul>
<p>The <a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><strong>Allwinner A64 User Manual</strong></a> (page 211, “GIC”) reveals that UART0 Interrupts will be triggered at <strong>Interrupt Number 32</strong>. (Pic above)</p>
<p>Lets <strong>attach our Interrupt Handler</strong> to handle the UART Interrupts: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L607-L657">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// UART0 IRQ Number for PinePhone Allwinner A64 UART
#define UART_IRQ 32
// Attach Interrupt Handler for PinePhone Allwinner A64 UART
static int a64_uart_attach(struct uart_dev_s *dev)
{
// Attach UART Interrupt Handler
int ret = irq_attach(
UART_IRQ, // Interrupt Number
a64_uart_irq_handler, // Interrupt Handler
dev // NuttX Device
);
// Set Interrupt Priority in
// Generic Interrupt Controller version 2
arm64_gic_irq_set_priority(
UART_IRQ, // Interrupt Number
0, // Interrupt Flags
IRQ_TYPE_LEVEL // Trigger Interrupt on High
);
// Enable UART Interrupt
if (ret == OK) {
up_enable_irq(UART_IRQ);
} else {
sinfo(&quot;error ret=%d\n&quot;, ret);
}
return ret;
}</code></pre></div>
<p><strong>a64_uart_irq_handler</strong> is our UART Interrupt Handler, well explain in a while.</p>
<p><em>Whats irq_attach?</em></p>
<div class="example-wrap"><pre class="language-c"><code>// Attach UART Interrupt Handler
int ret = irq_attach(
UART_IRQ, // Interrupt Number
a64_uart_irq_handler, // Interrupt Handler
dev // NuttX Device
);</code></pre></div>
<p>On NuttX, we call <strong>irq_attach</strong> to attach an Interrupt Handler to the UART Controller.</p>
<p><em>Whats arm64_gic_irq_set_priority?</em></p>
<div class="example-wrap"><pre class="language-c"><code>// Set Interrupt Priority in
// Generic Interrupt Controller version 2
arm64_gic_irq_set_priority(
UART_IRQ, // Interrupt Number
0, // Interrupt Flags
IRQ_TYPE_LEVEL // Trigger Interrupt on High
);</code></pre></div>
<p>Arm64 Interrupts are managed on PinePhone by the <strong>Generic Interrupt Controller</strong> in Allwinner A64…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/interrupt#generic-interrupt-controller"><strong>“Generic Interrupt Controller”</strong></a></li>
</ul>
<p>The code above calls the Generic Interrupt Controller to set the priority of the UART Interrupt.</p>
<p>Later when were done with UART Interrupts, we should <strong>detach the Interrupt Handler</strong>: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L659-L688">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Detach Interrupt Handler for PinePhone Allwinner A64 UART
static void a64_uart_detach(struct uart_dev_s *dev)
{
// Disable UART Interrupt
up_disable_irq(UART_IRQ);
// Detach UART Interrupt Handler
irq_detach(UART_IRQ);
}</code></pre></div>
<p><img src="https://lupyuen.github.io/images/serial-enable.jpg" alt="A64 UART Interrupt Enable Register UART_IER" /></p>
<p><a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><em>A64 UART Interrupt Enable Register UART_IER</em></a></p>
<h2 id="enable-interrupt"><a class="doc-anchor" href="#enable-interrupt">§</a>3.2 Enable Interrupt</h2>
<p>UART Interupts wont happen until we <strong>enable UART Interrupts</strong>.</p>
<p>Page 565 of the <a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><strong>Allwinner A64 User Manual</strong></a> tells us the UART Register for enabling UART Interrupts (pic above)…</p>
<ul>
<li>
<p><strong>Interrupt Enable Register (UART_IER)</strong></p>
<p>(At Offset <code>0x04</code>)</p>
</li>
</ul>
<p>This is how we enable (or disable) <strong>UART Receive Interrupts</strong>: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L755-L789">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Enable or disable Receive Interrupts for PinePhone Allwinner A64 UART
static void a64_uart_rxint(struct uart_dev_s *dev, bool enable)
{
// Write to UART Interrupt Enable Register (UART_IER)
// Offset: 0x0004
uint8_t *uart_ier = (uint8_t *)
(A64_UART0_ADDR + 0x04);
// Bit 0: Enable Received Data Available Interrupt (ERBFI)
// This is used to enable/disable the generation of Received Data Available Interrupt and the Character Timeout Interrupt (if in FIFO mode and FIFOs enabled). These are the second highest priority interrupts.
// 0: Disable
// 1: Enable
if (enable) { *uart_ier |= 0b00000001; }
else { *uart_ier &amp;= 0b11111110; }
}</code></pre></div>
<p>And this is how we enable (or disable) <strong>UART Transmit Interrupts</strong>: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L840-L874">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Enable or disable Transmit Interrupts for PinePhone Allwinner A64 UART
static void a64_uart_txint(struct uart_dev_s *dev, bool enable)
{
// Write to UART Interrupt Enable Register (UART_IER)
// Offset: 0x0004
uint8_t *uart_ier = (uint8_t *)
(A64_UART0_ADDR + 0x04);
// Bit 1: Enable Transmit Holding Register Empty Interrupt (ETBEI)
// This is used to enable/disable the generation of Transmitter Holding Register Empty Interrupt. This is the third highest priority interrupt.
// 0: Disable
// 1: Enable
if (enable) { *uart_ier |= 0b00000010; }
else { *uart_ier &amp;= 0b11111101; }
}</code></pre></div><h2 id="handle-interrupt"><a class="doc-anchor" href="#handle-interrupt">§</a>3.3 Handle Interrupt</h2>
<p>Earlier weve attached <strong><code>a64_uart_irq_handler</code></strong> as our Interrupt Handler for UART Interrupts…</p>
<div class="example-wrap"><pre class="language-c"><code>// Attach UART Interrupt Handler
int ret = irq_attach(
UART_IRQ, // Interrupt Number
a64_uart_irq_handler, // Interrupt Handler
dev // NuttX Device
);</code></pre></div>
<p>Lets look inside the Interrupt Handler.</p>
<p>When UART triggers an Interrupt, it stores the cause of the Interrupt in the <strong>Interrupt Identity Register (UART_IIR)</strong>, Offset <code>0x08</code>.</p>
<p><strong>Bits 0 to 3</strong> of the Interrupt Identity Register are…</p>
<ul>
<li>
<p><strong>Binary <code>0010</code></strong> if the Transmit Holding Register is empty</p>
<p>(Hence we should transmit more data)</p>
</li>
<li>
<p><strong>Binary <code>0100</code></strong> if theres Receive Data available</p>
<p>(Hence we should read the data received)</p>
</li>
</ul>
<p>This is how we handle these conditions in our Interrupt Handler: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L301-L415">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Interrupt Handler for PinePhone Allwinner A64 UART
static int a64_uart_irq_handler(int irq, void *context, void *arg)
{
// Get the UART Device
struct uart_dev_s *dev = (struct uart_dev_s *)arg;
UNUSED(irq);
UNUSED(context);
DEBUGASSERT(dev != NULL &amp;&amp; dev-&gt;priv != NULL);
// Read UART Interrupt Identity Register (UART_IIR)
// Offset: 0x0008
const uint8_t *uart_iir = (const uint8_t *) (A64_UART0_ADDR + 0x08);
// Bits 3 to 0: Interrupt ID
// This indicates the highest priority pending interrupt which can be one of the following types:
// 0000: modem status
// 0001: no interrupt pending
// 0010: THR empty
// 0100: received data available
// 0110: receiver line status
// 0111: busy detect
// 1100: character timeout
// Bit 3 indicates an interrupt can only occur when the FIFOs are enabled and used to distinguish a Character Timeout condition interrupt.
uint8_t int_id = (*uart_iir) &amp; 0b1111;
// 0100: If received data is available...
if (int_id == 0b0100) {
// Receive the data
uart_recvchars(dev);
// 0010: If THR is empty (Transmit Holding Register)...
} else if (int_id == 0b0010) {
// Transmit the data
uart_xmitchars(dev);
}
return OK;
}</code></pre></div>
<p>Lets talk about <strong><code>uart_recvchars</code></strong> and <strong><code>uart_xmitchars</code></strong></p>
<h2 id="uart-transmit"><a class="doc-anchor" href="#uart-transmit">§</a>3.4 UART Transmit</h2>
<p><em>Whats <code>uart_xmitchars</code>?</em></p>
<div class="example-wrap"><pre class="language-c"><code>// 0010: If THR is empty (Transmit Holding Register)...
if (int_id == 0b0010) {
// Transmit the data
uart_xmitchars(dev);</code></pre></div>
<p>If the Transmit Holding Register is empty, our Interrupt Handler calls <strong><code>uart_xmitchars</code></strong> to transmit more data.</p>
<p><strong><code>uart_xmitchars</code></strong> is a NuttX System Function that calls <a href="https://lupyuen.github.io/articles/serial#transmit-uart"><strong><code>a64_uart_send</code></strong></a> to transmit data to UART, while buffering the UART Output Data.</p>
<p><a href="https://lupyuen.github.io/articles/serial#transmit-uart">(Weve seen <strong><code>a64_uart_send</code></strong> earlier)</a></p>
<p><strong><code>uart_xmitchars</code></strong> will also call <a href="https://lupyuen.github.io/articles/serial#wait-to-transmit"><strong><code>a64_uart_txready</code></strong></a> to check if the UART Port is ready to accept more data, before transmitting the data.</p>
<p>Now for the other direction…</p>
<h2 id="uart-receive"><a class="doc-anchor" href="#uart-receive">§</a>3.5 UART Receive</h2>
<p><em>Whats <code>uart_recvchars</code>?</em></p>
<div class="example-wrap"><pre class="language-c"><code>// 0100: If received data is available...
if (int_id == 0b0100) {
// Receive the data
uart_recvchars(dev);</code></pre></div>
<p>If Received Data is available, our Interrupt Handler calls <strong><code>uart_recvchars</code></strong> to read the Received Data.</p>
<p><strong><code>uart_recvchars</code></strong> is a NuttX System Function that calls <a href="https://lupyuen.github.io/articles/serial#receive-uart"><strong><code>a64_uart_receive</code></strong></a> to receive data from UART, while buffering the UART Input Data.</p>
<p><a href="https://lupyuen.github.io/articles/serial#receive-uart">(Weve seen <strong><code>a64_uart_receive</code></strong> earlier)</a></p>
<p><strong><code>uart_recvchars</code></strong> will also call <a href="https://lupyuen.github.io/articles/serial#wait-to-receive"><strong><code>a64_uart_rxavailable</code></strong></a> to check if Received Data is actually available, before reading the data.</p>
<p>And thats how we transmit and receive UART Data with Interrupts!</p>
<h1 id="initialise-uart"><a class="doc-anchor" href="#initialise-uart">§</a>4 Initialise UART</h1>
<p><em>Did we forget something?</em></p>
<p>Rightfully we should initialise the <strong>UART Baud Rate</strong>: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L301-L582">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Setup PinePhone Allwinner A64 UART
static int a64_uart_setup(struct uart_dev_s *dev)
{
// TODO: Set the Baud Rate
return 0;
}</code></pre></div>
<p>PinePhones <strong>U-Boot Bootloader</strong> has kindly set the Baud Rate for us (115.2 kbps), so we skip this for now. More about the bootloader…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/uboot"><strong>“PinePhone boots Apache NuttX RTOS”</strong></a></li>
</ul>
<p>Later when need to set the <strong>UART Baud Rate</strong> for other UART Ports, the steps are explained here…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen/pinephone-nuttx#configure-uart-port"><strong>“Configure UART Port”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinephone-nuttx#test-uart3-port"><strong>“Test UART3 Port”</strong></a></p>
</li>
</ul>
<p><em>What about UART Shutdown?</em></p>
<p>To shutdown a UART Port, we disable the interrupts: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L584-L605">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Shutdown PinePhone Allwinner A64 UART
static void a64_uart_shutdown(struct uart_dev_s *dev)
{
// Disable the Receive and Transmit Interrupts
a64_uart_rxint(dev, false);
a64_uart_txint(dev, false);
}</code></pre></div>
<p>The Shutdown Function will <strong>never be called for UART0</strong> because its always active as the Serial Console.</p>
<p>But for UART1 to UART4, the Shutdown Function will be called when our NuttX App closes the UART.</p>
<p><em>Anything else?</em></p>
<p>One last thing: For NuttX we need to implement a simple <strong>I/O Control Handler <code>ioctl</code></strong>: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L690-L725">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// I/O Control for PinePhone Allwinner A64 UART
static int a64_uart_ioctl(struct file *filep, int cmd, unsigned long arg)
{
int ret = OK;
switch (cmd)
{
case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */
case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */
default:
{
ret = -ENOTTY;
break;
}
}
return ret;
}</code></pre></div>
<p>Were almost done with our PinePhone UART Driver for NuttX!</p>
<h1 id="nuttx-uart-driver"><a class="doc-anchor" href="#nuttx-uart-driver">§</a>5 NuttX UART Driver</h1>
<p><em>How do we create a PinePhone UART Driver for NuttX?</em></p>
<p>Weve implemented all the <strong>UART Operations</strong> for our PinePhone UART Driver…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#initialise-uart"><strong><code>a64_uart_setup</code></strong></a>: Initialise UART Driver</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#initialise-uart"><strong><code>a64_uart_shutdown</code></strong></a>: Shutdown UART Driver</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#attach-interrupt-handler"><strong><code>a64_uart_attach</code></strong></a>: Attach Interrupt Handler</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#attach-interrupt-handler"><strong><code>a64_uart_detach</code></strong></a>: Detach Interrupt Handler</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#initialise-uart"><strong><code>a64_uart_ioctl</code></strong></a>: I/O Control</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#receive-uart"><strong><code>a64_uart_receive</code></strong></a>: Receive Data</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#enable-interrupt"><strong><code>a64_uart_rxint</code></strong></a>: Enable / Disable Receive Interrupt</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#wait-to-receive"><strong><code>a64_uart_rxavailable</code></strong></a>: Is Received Data Available</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#transmit-uart"><strong><code>a64_uart_send</code></strong></a>: Transmit Data</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#enable-interrupt"><strong><code>a64_uart_txint</code></strong></a>: Enable / Disable Transmit Interrupt</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#wait-to-transmit"><strong><code>a64_uart_txready</code></strong></a>: Is UART Ready to Transmit</p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#wait-to-transmit"><strong><code>a64_uart_txempty</code></strong></a>: Is Transmit Buffer Empty</p>
</li>
</ul>
<p>NuttX expects us to wrap the UART Operations into a <strong><code>uart_ops_s</code></strong> Struct like so: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L1002-L1021">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Serial driver UART operations for PinePhone Allwinner A64 UART
static const struct uart_ops_s g_uart_ops =
{
.setup = a64_uart_setup,
.shutdown = a64_uart_shutdown,
.attach = a64_uart_attach,
.detach = a64_uart_detach,
.ioctl = a64_uart_ioctl,
.receive = a64_uart_receive,
.rxint = a64_uart_rxint,
.rxavailable = a64_uart_rxavailable,
#ifdef CONFIG_SERIAL_IFLOWCONTROL
.rxflowcontrol = NULL,
#endif
.send = a64_uart_send,
.txint = a64_uart_txint,
.txready = a64_uart_txready,
.txempty = a64_uart_txempty,
};</code></pre></div>
<p>We should <strong>start our UART Driver</strong> like this: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L1323-L1398">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// UART0 is console and ttyS0
#define CONSOLE_DEV g_uart0port
#define TTYS0_DEV g_uart0port
// Performs the low level UART initialization early in
// debug so that the serial console will be available
// during bootup. This must be called before arm_serialinit.
void a64_earlyserialinit(void)
{
// NOTE: This function assumes that low level hardware configuration
// -- including all clocking and pin configuration -- was performed
// earlier by U-Boot Bootloader.
// Enable the console UART. The other UARTs will be initialized if and
// when they are first opened.
CONSOLE_DEV.isconsole = true;
a64_uart_setup(&amp;CONSOLE_DEV);
// Omitted: Init UART1 to UART4, if required</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L1051-L1069">(<strong><code>g_uart0port</code></strong> contains the UART Operations <strong><code>g_uart_ops</code></strong>)</a></p>
<p>Then we expose UART0 as <strong><code>/dev/console</code></strong> and <strong><code>/dev/ttyS0</code></strong>: <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c#L143-L1501">a64_serial.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Register serial console and serial ports. This assumes
// that imx_earlyserialinit was called previously.
void arm64_serialinit(void)
{
// Register UART0 as /dev/console
int ret = uart_register(&quot;/dev/console&quot;, &amp;CONSOLE_DEV);
if (ret &lt; 0) { _err(&quot;Register /dev/console failed, ret=%d\n&quot;, ret); }
// Register UART0 as /dev/ttyS0
ret = uart_register(&quot;/dev/ttyS0&quot;, &amp;TTYS0_DEV);
if (ret &lt; 0) { _err(&quot;Register /dev/ttyS0 failed, ret=%d\n&quot;, ret); }
// Omitted: Register UART1 to UART4 as /dev/ttyS1 to /dev/ttyS4
// TTY Numbering is always Sequential:
// If UART1 is disabled, then UART2 becomes /dev/ttyS1</code></pre></div>
<p>And were done with our PinePhone UART Driver for NuttX!</p>
<h1 id="uart-in-action"><a class="doc-anchor" href="#uart-in-action">§</a>6 UART In Action</h1>
<p>Lets watch our UART Driver in action!</p>
<p>Follow these steps to <strong>build NuttX</strong> and copy to Jumpdrive microSD…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/uboot#pinephone-boots-nuttx"><strong>“PinePhone Boots NuttX”</strong></a></li>
</ul>
<p>Insert the microSD into PinePhone and power it on. We should see…</p>
<div class="example-wrap"><pre class="language-text"><code>Starting kernel ...
HELLO NUTTX ON PINEPHONE!
- Ready to Boot CPU
- Boot from EL2
- Boot from EL1
- Boot to C runtime for OS Initialize
nx_start: Entry
up_allocate_heap: heap_start=0x0x400c4000, heap_size=0x7f3c000
arm64_gic_initialize: TODO: Init GIC for PinePhone
arm64_gic_initialize: CONFIG_GICD_BASE=0x1c81000
arm64_gic_initialize: CONFIG_GICR_BASE=0x1c82000
arm64_gic_initialize: GIC Version is 2
up_timer_initialize: up_timer_initialize: cp15 timer(s) running at 24.00MHz, cycle 24000
up_timer_initialize: _vector_table=0x400a7000
up_timer_initialize: Before writing: vbar_el1=0x40227000
up_timer_initialize: After writing: vbar_el1=0x400a7000
uart_register: Registering /dev/console
uart_register: Registering /dev/ttyS0
work_start_highpri: Starting high-priority kernel worker thread(s)
nx_start_application: Starting init thread
lib_cxx_initialize: _sinit: 0x400a7000 _einit: 0x400a7000 _stext: 0x40080000 _etext: 0x400a8000
nsh: sysinit: fopen failed: 2
nshn:x _msktfaarttf:s :C PcUo0m:m aBnedg innonti nfgo uInddle L oNouptt
Shell (NSH) NuttX-10.3.0-RC2
nsh&gt; </code></pre></div>
<p><a href="https://github.com/lupyuen/pinephone-nuttx#garbled-console-output">(Yeah the output is slightly garbled, heres the workaround)</a></p>
<p>Now that we handle UART Interrupts, <strong>NuttX Shell</strong> works perfectly OK on PinePhone…</p>
<div class="example-wrap"><pre class="language-text"><code>nsh&gt; uname -a
NuttX 10.3.0-RC2 fc909c6-dirty Sep 1 2022 17:05:44 arm64 pinephone
nsh&gt; help
help usage: help [-v] [&lt;cmd&gt;]
. cd dmesg help mount rmdir true xd
[ cp echo hexdump mv set truncate
? cmp exec kill printf sleep uname
basename dirname exit ls ps source umount
break dd false mkdir pwd test unset
cat df free mkrd rm time usleep
Builtin Apps:
getprime hello nsh ostest sh
nsh&gt; hello
task_spawn: name=hello entry=0x4009b1a0 file_actions=0x400c9580 attr=0x400c9588 argv=0x400c96d0
spawn_execattrs: Setting policy=2 priority=100 for pid=3
Hello, World!!
nsh&gt; ls /dev
/dev:
console
null
ram0
ram2
ttyS0
zero</code></pre></div>
<p><a href="https://youtube.com/shorts/WmRzfCiWV6o?feature=share"><strong>Watch the Demo on YouTube</strong></a></p>
<p><em>What about other UART Ports? (Besides UART0)</em></p>
<p>Were adding support for <strong>other UART Ports</strong>, like UART3 for PinePhones 4G LTE Modem…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/serial#appendix-uart-ports-on-pinephone"><strong>“UART Ports on PinePhone”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinephone-nuttx#configure-uart-port"><strong>“Configure UART Port”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinephone-nuttx#test-uart3-port"><strong>“Test UART3 Port”</strong></a></p>
</li>
</ul>
<p>The changes have been upstreamed to NuttX Mainline…</p>
<ul>
<li><a href="https://github.com/apache/nuttx/pull/9243"><strong>Pull Request: Support multiple UART Ports</strong></a></li>
</ul>
<p><em>How do we enable a UART Port? Like UART3?</em></p>
<p>Head over to the NuttX Build Configuration…</p>
<div class="example-wrap"><pre class="language-bash"><code>make menuconfig</code></pre></div>
<p>Then select…</p>
<ul>
<li>System Type &gt; Allwinner A64 Peripheral Selection &gt; UART3</li>
</ul>
<p>To configure the Baud Rate, Bits, Parity and Stop Bits…</p>
<ul>
<li>Device Drivers &gt; Serial Driver Support &gt; UART3 Configuration</li>
</ul>
<p>(The default Baud Rate / Bits / Parity / Stop Bits work OK with the PinePhone LTE Modem on UART3: 115.2 kbps / 8 Bits / No Parity / 1 Stop Bit)</p>
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>7 Whats Next</h1>
<p>Today we talked about PinePhone UART and how we created the NuttX UART Driver.</p>
<p>Theres plenty to be done for NuttX on PinePhone, please lemme know if you would like to join me 🙏</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://www.reddit.com/r/PINE64official/comments/xafz9o/nuttx_rtos_for_pinephone_uart_driver/"><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/serial.md"><strong>lupyuen.github.io/src/serial.md</strong></a></p>
<h1 id="appendix-uart-ports-on-pinephone"><a class="doc-anchor" href="#appendix-uart-ports-on-pinephone">§</a>8 Appendix: UART Ports on PinePhone</h1>
<p><em>Which Allwinner A64 UART Ports are used in PinePhone?</em></p>
<p>According to the <a href="https://files.pine64.org/doc/PinePhone/PinePhone%20v1.2b%20Released%20Schematic.pdf"><strong>PinePhone Schematic</strong></a>, the following <strong>UART Ports</strong> in Allwinner A64 are connected…</p>
<ul>
<li>
<p><strong>UART0:</strong> Serial Console</p>
<p>Pins <strong>PB8</strong> <em>(TX)</em> and <strong>PB9</strong> <em>(RX)</em></p>
<p>(Assigned as <strong>/dev/ttyS0</strong>)</p>
</li>
<li>
<p><strong>UART1:</strong> Bluetooth Module (Realtek RTL8723CS)</p>
<p>Pins <strong>PG6</strong> <em>(TX)</em>, <strong>PG7</strong> <em>(RX)</em>, <strong>PG8</strong> <em>(RTS)</em> and <strong>PG9</strong> <em>(CTS)</em></p>
<p>(TODO: Assign as <strong>/dev/ttyS1</strong>)</p>
</li>
<li>
<p><strong>UART2:</strong> Unused</p>
<p>Pins <strong>PB0</strong> and <strong>PB1</strong></p>
<p>(Wired to Light Sensor STK3311 and Compass Sensor AK09911)</p>
</li>
<li>
<p><strong>UART3:</strong> 4G LTE Modem (Quectel EG25-G)</p>
<p>Pins <strong>PD0</strong> <em>(TX)</em> and <strong>PD1</strong> <em>(RX)</em></p>
<p>(TODO: Assign as <strong>/dev/ttyS3</strong>)</p>
<p><a href="https://lupyuen.github.io/articles/lte#test-uart-with-nuttx">(More about this)</a></p>
</li>
<li>
<p><strong>UART4:</strong> 4G LTE Modem (Quectel EG25-G)</p>
<p>Pins <strong>PD4</strong> and <strong>PD5</strong></p>
<p>(Wired to RTS and CTS, not really a UART)</p>
<p><a href="https://lupyuen.github.io/articles/lte#test-uart-with-nuttx">(More about this)</a></p>
</li>
</ul>
<p>TODO: Disable UART2 and /dev/ttyS2 so that UART3 maps neatly to /dev/ttyS3. <a href="https://github.com/apache/nuttx/pull/9304#discussion_r1195862416">(See this)</a></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>