<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://embeddedgo.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://embeddedgo.github.io/" rel="alternate" type="text/html" /><updated>2025-08-21T20:42:04+00:00</updated><id>https://embeddedgo.github.io/feed.xml</id><title type="html">Embedded Go</title><subtitle>Bare-metal programming with Go.</subtitle><author><name>Embedded Go authors</name></author><entry><title type="html">Embedded Go as a toolchain, Pi Pico 2, Nintendo 64</title><link href="https://embeddedgo.github.io/2025/08/17/news.html" rel="alternate" type="text/html" title="Embedded Go as a toolchain, Pi Pico 2, Nintendo 64" /><published>2025-08-17T00:00:00+00:00</published><updated>2025-08-17T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2025/08/17/news</id><content type="html" xml:base="https://embeddedgo.github.io/2025/08/17/news.html"><![CDATA[<p><img src="/images/gophers_with_tools.jpg" alt="Teensy 4.1 and ESP-01S" /></p>

<!--more-->

<p>It’s been a while since the last post on this blog. This doesn’t mean, however, that nothing has happened with the Embedded Go during this time. We have stable but rather slow progress.</p>

<h4 id="embedded-go-124">Embedded Go 1.24</h4>

<p>The Go 1.24 was merged into Embedded Go with all its language changes. Yes, as I’m writing these words the Go 1.25 has just been released, but Embedded Go is always a little behind.</p>

<h4 id="embedded-go-as-a-toolchain">Embedded Go as a toolchain</h4>

<p>The big thing is the new release model. As Embedded Go has become an installable Go toolchain, we have moved away from binary releases. Now you can easily add Embedded Go to your Go compiler using only two commands:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>go install github.com/embeddedgo/dl/go1.24.5-embedded@latest
go1.24.5-embedded download
</code></pre></div></div>

<p>Then set the GOTOOLCHAIN environment variable to go1.24.5-embedded. The most convenient place to set it is the <a href="https://github.com/embeddedgo/pico/blob/master/devboard/pico2/examples/go.env">go.env</a> file which can be used as a per project configuration. With the configuration in file you have to set <a href="https://pkg.go.dev/cmd/go#hdr-Environment_v">GOENV</a> instead of GOTOOLCHAIN before use the <code class="language-plaintext highlighter-rouge">go</code> command. You can avoid setting GOENV by using <code class="language-plaintext highlighter-rouge">egtool build</code> instead of <code class="language-plaintext highlighter-rouge">go build</code> if you just want to build your project (egtool will simply call <code class="language-plaintext highlighter-rouge">go build</code> with the GOENV set to the path of the found go.env).</p>

<h4 id="new-tool-egtool">New tool: egtool</h4>

<p>We have new tool that makes programming embedded targets much convenient. The <a href="https://github.com/embeddedgo/tools/tree/master/egtool">egtool</a> replaced <a href="https://github.com/embeddedgo/tools/tree/master/emgo">emgo</a>. It’s much cleaner and can do much more.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ go install github.com/embeddedgo/tools/egtool@latest
$ egtool
Usage:
  egtool COMMAND [ARGUMENTS]

Available commands:
  bin       convert an ELF file to a binary image
  build     run `go build` with GOENV set to the found go.env file
  hex       convert an ELF file to the Intel HEX format
  imxmbr    generate the MBR file for the I.MX RT106x microcontrollers
  isrnames  generate IRQ handler linknames according the imported irq package
  load      load the program / memory range stored in a file onto the device
  uf2       convert an ELF file to the UF2 format
</code></pre></div></div>

<p>The most useful egtool command is load which currently supports RPi Pico 2, Teensy 4.x and STM32 microcontrollers connected via USB.</p>

<h4 id="pi-pico-2">Pi Pico 2</h4>

<p>We gained <a href="https://github.com/embeddedgo/pico">support for the wonderfull Raspberry Pi RP2350</a> microcontroller. Its both ARM cores will provide power to your goroutines. The number of supported peripherals is still modest (DMA, GPIO, IOMUX, SPI, UART) but this MCU will definitely be our workhorse due to its availability and features, so expect more in the near future.</p>

<h4 id="nintendo-64">Nintendo 64</h4>

<p><a href="https://github.com/clktmr/">Timur Çelik</a> did a great job adding support for the Nintendo 64 CPU. What surprised me is that the MIPS64 from 1996 is supported by GOARCH=mips64 with very minimal changes.</p>

<p>The new toolchain based release model is also heavily influenced by Timur. He also did a lot to clean up and improve many aspects of Embedded Go.</p>

<p>If you are interested in the N64 homebrew development take a look at the <a href="https://github.com/clktmr/n64">github.com/clktmr/n64</a> repository.</p>

<p>The real N64 consoles are still quite cheap, still quite capable. They are also very easy to work with because they are the last cartridge based systems. There are easily available cartridges for programmes like <a href="https://summercart64.dev/">SummerCart64</a> or <a href="https://github.com/kbeckmann/PicoCart64">PicoCart64</a>. You can also work with an N64 emulator but the real hardware is what the Embedded Go is for.</p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="go" /><category term="golang" /><category term="embeddedgo" /><summary type="html"><![CDATA[]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://embeddedgo.github.io/images/gophers_with_tools.jpg" /><media:content medium="image" url="https://embeddedgo.github.io/images/gophers_with_tools.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Running Go HTTP server on Teensy 4.1</title><link href="https://embeddedgo.github.io/2024/01/01/go_http_server_on_teensy4.html" rel="alternate" type="text/html" title="Running Go HTTP server on Teensy 4.1" /><published>2024-01-01T00:00:00+00:00</published><updated>2024-01-01T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2024/01/01/go_http_server_on_teensy4</id><content type="html" xml:base="https://embeddedgo.github.io/2024/01/01/go_http_server_on_teensy4.html"><![CDATA[<p><img src="/images/go_http_server_on_teensy4/title.jpg" alt="Teensy 4.1 and ESP-01S" /></p>

<!--more-->

<p><em>Updated 2025-08-20: use egtool instead of emgo</em></p>

<p>Since its first release in 2009, the Go language has been closely associated with network programming. Unfortunately, until now the Embedded Go had no networking capabilities, mainly due to the strong dependency of the <a href="https://pkg.go.dev/net">net</a> package on the network capabilities of the underlying operating system. But that has changed.</p>

<h3 id="hardware">Hardware</h3>

<p>As a hardware platform, we use the <a href="https://www.pjrc.com/store/teensy41.html">Teensy 4.1</a> development board. The code presented here should also run on <a href="https://www.pjrc.com/store/teensy40.html">Teensy 4.0</a> without any modifications. The exception, unfortunately, is the titular HTTP server, which does not fit into its smaller Flash.</p>

<p><img src="/images/mcu/devboard/teensy4.jpg" alt="Teensy 4.x" /></p>

<p>As a network interface, we use the <em>ESP-01S</em> module. It is easily available, cheap and sufficient for our needs.</p>

<p><img src="/images/mcu/net/esp01s.jpg" alt="ESP-01S" /></p>

<p>You can use any other ESP8266 or ESP32 based development board provided you flash it with the <a href="https://github.com/espressif/esp-at">ESP-AT</a> firmware.</p>

<p>The ESP-AT firmware gives us what the operating system provides for a typical Go application. Instead of using system calls, our programs will talk to the ESP-AT firmware using an UART interface and a rather weird protocol based on <a href="https://en.wikipedia.org/wiki/Hayes_AT_command_set">Hayes AT commands</a>. All these details will be invisible to us thanks to the <a href="https://pkg.go.dev/github.com/embeddedgo/espat">espat</a> package. Moreover, the <a href="https://pkg.go.dev/github.com/embeddedgo/espat/espnet">espat/espnet</a> package will provide our application the interface compatible with the standard <a href="https://pkg.go.dev/net">net</a> package, so any code that uses <a href="https://pkg.go.dev/net#Conn">net.Conn</a> or <a href="https://pkg.go.dev/net#Listener">net.Listener</a> can work with it.</p>

<h3 id="first-steps-with-teensy-and-go">First steps with Teensy and Go</h3>

<p>If you have the required hardware and want to test the code presented below, you need to install the latest release of Embedded Go. Detailed instruction can be found on the <a href="/getting_started">Getting Started</a> page.</p>

<p>Let’s create a folder, say <code class="language-plaintext highlighter-rouge">teensyApp</code>, go into it and create the following two files:</p>

<p><code class="language-plaintext highlighter-rouge">main.go</code></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"time"</span>

	<span class="s">"github.com/embeddedgo/imxrt/devboard/teensy4/board/leds"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="n">leds</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Toggle</span><span class="p">()</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">go.env</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GOTOOLCHAIN=go1.24.5-embedded
GOOS=noos
GOARCH=thumb
GOARM=7,hardfloat
GOFLAGS=-tags=imxrt1060 '-ldflags=-stripfn=1 -M=0x20200000:992K -F=0x60002000:7928K'
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">main.go</code> file contains a simple Go program that blinks the onboard LED. We’ll use it to test our development environment.</p>

<p>The <code class="language-plaintext highlighter-rouge">go.env</code> file contains the build options for Teensy 4.1. They also work for Teensy 4.0 but to be consistent with the true size of its Flash you should set <code class="language-plaintext highlighter-rouge">-F</code> option to <code class="language-plaintext highlighter-rouge">0x60002000:1976K</code>.</p>

<p>Set the <code class="language-plaintext highlighter-rouge">GOENV</code> environment variable to the path of your <code class="language-plaintext highlighter-rouge">go.env</code> file.</p>

<p>Run the <code class="language-plaintext highlighter-rouge">go mod init</code> and <code class="language-plaintext highlighter-rouge">go mod tidy</code> commands to initialize your new “project”.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ go mod init teensyApp
go: creating new go.mod: module teensyApp
go: to add module requirements and sums:
	go mod tidy
$ go mod tidy
go: downloading github.com/embeddedgo/imxrt v0.0.5
go: finding module for package github.com/embeddedgo/fs/termfs
go: found github.com/embeddedgo/fs/termfs in github.com/embeddedgo/fs v0.1.0
</code></pre></div></div>

<p>These two commands create the <code class="language-plaintext highlighter-rouge">go.mod</code> and <code class="language-plaintext highlighter-rouge">go.sum</code> files in our folder and install dependencies.</p>

<p>Now you can build yout first program.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ go build
</code></pre></div></div>

<p>Go compiler is silent, so there is no any message if everything went well. You should find the result of compilation in the <code class="language-plaintext highlighter-rouge">teensyApp.elf</code> file.</p>

<h4 id="programming-the-board">Programming the board</h4>

<p>Connect your Teensy board to your PC using a USB cable and press the onnboard button (Teensy should start blinking its red LED). Now you can load and run your program.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ egtool load
</code></pre></div></div>

<p>The program should start blinking the yellow LED.</p>

<h3 id="getting-started-with-the-esp-01s-module">Getting started with the ESP-01S module</h3>

<p>Once we have a ready and tested development environment, we can get to the point of this tutorial article.</p>

<p>Let’s connect the Wi-Fi module to the Teensy board, according to the table below.</p>

<table>
  <thead>
    <tr>
      <th>ESP-01S</th>
      <th>Teensy 4.x</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>RX</td>
      <td>14</td>
    </tr>
    <tr>
      <td>TX</td>
      <td>15</td>
    </tr>
    <tr>
      <td>GND</td>
      <td>G</td>
    </tr>
    <tr>
      <td>3V3</td>
      <td>3V</td>
    </tr>
  </tbody>
</table>

<p><img src="/images/go_http_server_on_teensy4/connections.jpg" alt="Connections" /></p>

<p>Because of the limited power capabilities of the Teensy 3V pin (250 mA) and rather long connections the ESP-01S module requires a slight hardware modification.</p>

<p>The simplest way to meet the momentary power demand of ESP8266 is to store the energy in the properly sized decoupling capacitor. A 100 µF low-ESR electrolytic capacitor between GND and 3V3 should be just right.</p>

<p><img src="/images/go_http_server_on_teensy4/capacitor.jpg" alt="Add a capacitor" /></p>

<p>Alternatively you can purchase a breadboard adapter that already includes a proper decoupling capacitor and assemble everything securely on the breadboard.</p>

<p><img src="/images/go_http_server_on_teensy4/adapter.jpg" alt="ESP-01 adapter" /></p>

<h4 id="first-program">First program</h4>

<p>The first program will display a list of the names (SSIDs) of nearby wireless networks.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"fmt"</span>
	<span class="s">"time"</span>

	<span class="s">"github.com/embeddedgo/espat"</span>
	<span class="s">"github.com/embeddedgo/imxrt/hal/lpuart"</span>
	<span class="s">"github.com/embeddedgo/imxrt/hal/lpuart/lpuart2"</span>

	<span class="s">"github.com/embeddedgo/imxrt/devboard/teensy4/board/pins"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">for</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"error:"</span><span class="p">,</span> <span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">())</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="c">// Initialize LPUART2 on pins 14 and 15.</span>
	<span class="n">u</span> <span class="o">:=</span> <span class="n">lpuart2</span><span class="o">.</span><span class="n">Driver</span><span class="p">()</span>
	<span class="n">u</span><span class="o">.</span><span class="n">Setup</span><span class="p">(</span><span class="n">lpuart</span><span class="o">.</span><span class="n">Word8b</span><span class="p">,</span> <span class="m">115200</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">UsePin</span><span class="p">(</span><span class="n">pins</span><span class="o">.</span><span class="n">P14</span><span class="p">,</span> <span class="n">lpuart</span><span class="o">.</span><span class="n">TXD</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">UsePin</span><span class="p">(</span><span class="n">pins</span><span class="o">.</span><span class="n">P15</span><span class="p">,</span> <span class="n">lpuart</span><span class="o">.</span><span class="n">RXD</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">EnableRx</span><span class="p">(</span><span class="m">512</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">EnableTx</span><span class="p">()</span>

	<span class="c">// Give the user time to connect to the USB console.</span>
	<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">5</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="s">"Initializing Wi-Fi module... "</span><span class="p">)</span>
	<span class="n">dev</span> <span class="o">:=</span> <span class="n">espat</span><span class="o">.</span><span class="n">NewDevice</span><span class="p">(</span><span class="s">"esp0"</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">u</span><span class="p">)</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">dev</span><span class="o">.</span><span class="n">Init</span><span class="p">(</span><span class="no">true</span><span class="p">))</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"OK"</span><span class="p">)</span>

	<span class="c">// Print the ESP-AT version information.</span>
	<span class="n">s</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">dev</span><span class="o">.</span><span class="n">CmdStr</span><span class="p">(</span><span class="s">"+GMR"</span><span class="p">)</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>

	<span class="c">// Periodically print information about available Wi-Fi networks.</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Nearby wireless networks:"</span><span class="p">)</span>
		<span class="n">s</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">dev</span><span class="o">.</span><span class="n">CmdStr</span><span class="p">(</span><span class="s">"+CWLAP"</span><span class="p">)</span>
		<span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">5</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The above code first issues the <a href="https://docs.espressif.com/projects/esp-at/en/release-v2.2.0.0_esp8266/AT_Command_Set/Basic_AT_Commands.html#cmd-gmr">AT+GMR</a> command to print on the USB console the information about the ESP-AT firmware. Then, it periodically issues the <a href="https://docs.espressif.com/projects/esp-at/en/release-v2.2.0.0_esp8266/AT_Command_Set/Wi-Fi_AT_Commands.html#cmd-ipsta">AT+CWLAP</a> command to print information about the available WI-FI networks.</p>

<p>Let’s build it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ go build
main.go:7:2: missing go.sum entry for module providing package github.com/embeddedgo/espat (imported by teensyApp); to add:
	go get teensyApp
$ go get teensyApp
go: downloading github.com/embeddedgo/espat v0.2.3
$ go build
</code></pre></div></div>

<p>Now press the button on your Teensy to program it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ egtool load
</code></pre></div></div>

<p>If you quickly enough connect to the Teensy USB console, you should see the following output from this program:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ picocom -q /dev/ttyACM0
Initializing Wi-Fi module... OK
AT version:2.2.0.0(s-b097cdf - ESP8266 - Jun 17 2021 12:58:29)
SDK version:v3.4-22-g967752e2
compile time(6800286):Jul 11 2021 11:09:32
Bin version:2.2.0(ESP8266_1MB)

Nearby wireless networks:
+CWLAP:(3,"IOT",-63,"76:4d:28:21:11:83",1,-1,-1,4,4,7,1)
+CWLAP:(3,"Home",-64,"74:4d:28:21:11:83",1,-1,-1,4,4,7,1)
+CWLAP:(3,"IOT",-78,"2e:c8:1b:11:22:bb",5,-1,-1,4,4,6,0)
+CWLAP:(3,"Home",-78,"2c:c8:1b:11:22:bb",5,-1,-1,4,4,6,0)

Nearby wireless networks:
+CWLAP:(3,"IOT",-65,"76:4d:28:21:11:83",1,-1,-1,4,4,7,1)
+CWLAP:(3,"Home",-67,"74:4d:28:21:11:83",1,-1,-1,4,4,7,1)
+CWLAP:(3,"IOT",-75,"2e:c8:1b:11:22:bb",5,-1,-1,4,4,6,0)
+CWLAP:(3,"Home",-82,"2c:c8:1b:11:22:bb",5,-1,-1,4,4,6,0)
</code></pre></div></div>

<p>As you can see, my Teensy sees two Wi-Fi networks provided by four access points.</p>

<h3 id="usb-console">USB console</h3>

<p>If you connect a programmed Teensy board to your computer, it’s seen as a composite USB device that comprises two CDC-ACM (virtual Serial) devices (ports). The first one is used as the USB console, the second one can be used by your program in any way you want. This default USB configuration is performed when you import the <a href="https://github.com/embeddedgo/imxrt/tree/master/devboard/teensy4/board/system">devboard/teensy4/board/system</a> package or any other package from the <a href="https://github.com/embeddedgo/imxrt/tree/master/devboard/teensy4/board">devboard/teensy4/board</a> directory (in our case it’s the <a href="https://github.com/embeddedgo/imxrt/tree/master/devboard/teensy4/board/pins">pins</a> package).</p>

<p>If you want a custom USB configuration, you cannot import anything from <code class="language-plaintext highlighter-rouge">devboard/teensy4/board</code>. Instead, you have to recreate all the things you need from this directory in your application, especially the <code class="language-plaintext highlighter-rouge">system</code> package.</p>

<p>You can interact with the USB console using a terminal emulator program like <a href="https://www.putty.org/">PuTTY</a> on Windows or <a href="https://github.com/npat-efault/picocom">picocom</a> on Linux or Mac.</p>

<h3 id="connecting-to-the-network">Connecting to the network</h3>

<p>Our next program will ask for the network name (SSID) and password to connect to the Wi-Fi network.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"fmt"</span>
	<span class="s">"time"</span>

	<span class="s">"github.com/embeddedgo/espat"</span>
	<span class="s">"github.com/embeddedgo/imxrt/hal/lpuart"</span>
	<span class="s">"github.com/embeddedgo/imxrt/hal/lpuart/lpuart2"</span>

	<span class="s">"github.com/embeddedgo/imxrt/devboard/teensy4/board/pins"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">for</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"error:"</span><span class="p">,</span> <span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">())</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">u</span> <span class="o">:=</span> <span class="n">lpuart2</span><span class="o">.</span><span class="n">Driver</span><span class="p">()</span>
	<span class="n">u</span><span class="o">.</span><span class="n">Setup</span><span class="p">(</span><span class="n">lpuart</span><span class="o">.</span><span class="n">Word8b</span><span class="p">,</span> <span class="m">115200</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">UsePin</span><span class="p">(</span><span class="n">pins</span><span class="o">.</span><span class="n">P14</span><span class="p">,</span> <span class="n">lpuart</span><span class="o">.</span><span class="n">TXD</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">UsePin</span><span class="p">(</span><span class="n">pins</span><span class="o">.</span><span class="n">P15</span><span class="p">,</span> <span class="n">lpuart</span><span class="o">.</span><span class="n">RXD</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">EnableRx</span><span class="p">(</span><span class="m">512</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">EnableTx</span><span class="p">()</span>

	<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">5</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="s">"Initializing ESP-AT module... "</span><span class="p">)</span>
	<span class="n">dev</span> <span class="o">:=</span> <span class="n">espat</span><span class="o">.</span><span class="n">NewDevice</span><span class="p">(</span><span class="s">"esp0"</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">u</span><span class="p">)</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">dev</span><span class="o">.</span><span class="n">Init</span><span class="p">(</span><span class="no">true</span><span class="p">))</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"OK"</span><span class="p">)</span>

	<span class="k">var</span> <span class="n">ssid</span><span class="p">,</span> <span class="n">passwd</span> <span class="kt">string</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="s">"SSID: "</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Scanln</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ssid</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="s">"Password: "</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Scanln</span><span class="p">(</span><span class="o">&amp;</span><span class="n">passwd</span><span class="p">)</span>

	<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">dev</span><span class="o">.</span><span class="n">Cmd</span><span class="p">(</span><span class="s">"+CWMODE=1"</span><span class="p">)</span> <span class="c">// Wi-Fi station</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
	<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">dev</span><span class="o">.</span><span class="n">Cmd</span><span class="p">(</span><span class="s">"+CWJAP="</span><span class="p">,</span> <span class="n">ssid</span><span class="p">,</span> <span class="n">passwd</span><span class="p">)</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>

	<span class="c">// Priodically print the connection state.</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="n">s</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">dev</span><span class="o">.</span><span class="n">CmdStr</span><span class="p">(</span><span class="s">"+CWSTATE?"</span><span class="p">)</span>
		<span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">5</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>By default, if the connection is successfully established, the SSID and password will be permanently saved by our Wi-Fi module. It’ll try to connect to the network after reboot or reconnect in case of the connection loss.</p>

<p>Interaction with this program may look like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Initializing ESP-AT module... OK
SSID: Home
Password: abc123xyz
+CWSTATE:2,"Home"
+CWSTATE:2,"Home"
+CWSTATE:2,"Home"
</code></pre></div></div>

<p>As the Wi-Fi credentials were saved by the ESP-AT firmware, we won’t set them in the following examples.</p>

<h3 id="tcp-client">TCP client</h3>

<p>It took a while, but we finally got there. Let’s write a simple TCP client program.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"fmt"</span>
	<span class="s">"io"</span>
	<span class="s">"net"</span>
	<span class="s">"os"</span>
	<span class="s">"time"</span>

	<span class="s">"github.com/embeddedgo/espat"</span>
	<span class="s">"github.com/embeddedgo/espat/espnet"</span>
	<span class="s">"github.com/embeddedgo/imxrt/hal/lpuart"</span>
	<span class="s">"github.com/embeddedgo/imxrt/hal/lpuart/lpuart2"</span>

	<span class="s">"github.com/embeddedgo/imxrt/devboard/teensy4/board/pins"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">logErr</span><span class="p">(</span><span class="n">err</span> <span class="kt">error</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
	<span class="k">for</span> <span class="n">err</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="k">return</span> <span class="no">false</span>
	<span class="p">}</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"error:"</span><span class="p">,</span> <span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">())</span>
	<span class="k">return</span> <span class="no">true</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">for</span> <span class="n">logErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">u</span> <span class="o">:=</span> <span class="n">lpuart2</span><span class="o">.</span><span class="n">Driver</span><span class="p">()</span>
	<span class="n">u</span><span class="o">.</span><span class="n">Setup</span><span class="p">(</span><span class="n">lpuart</span><span class="o">.</span><span class="n">Word8b</span><span class="p">,</span> <span class="m">115200</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">UsePin</span><span class="p">(</span><span class="n">pins</span><span class="o">.</span><span class="n">P14</span><span class="p">,</span> <span class="n">lpuart</span><span class="o">.</span><span class="n">TXD</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">UsePin</span><span class="p">(</span><span class="n">pins</span><span class="o">.</span><span class="n">P15</span><span class="p">,</span> <span class="n">lpuart</span><span class="o">.</span><span class="n">RXD</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">EnableRx</span><span class="p">(</span><span class="m">512</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">EnableTx</span><span class="p">()</span>

	<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">5</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="s">"Initializing Wi-Fi module... "</span><span class="p">)</span>
	<span class="n">dev</span> <span class="o">:=</span> <span class="n">espat</span><span class="o">.</span><span class="n">NewDevice</span><span class="p">(</span><span class="s">"esp0"</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">u</span><span class="p">)</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">dev</span><span class="o">.</span><span class="n">Init</span><span class="p">(</span><span class="no">true</span><span class="p">))</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"OK"</span><span class="p">)</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Waiting for an IP address..."</span><span class="p">)</span>
	<span class="k">for</span> <span class="n">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">dev</span><span class="o">.</span><span class="n">Async</span><span class="p">()</span> <span class="p">{</span>
		<span class="n">fatalErr</span><span class="p">(</span><span class="n">msg</span><span class="o">.</span><span class="n">Err</span><span class="p">)</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">msg</span><span class="o">.</span><span class="n">Str</span><span class="p">)</span>
		<span class="k">if</span> <span class="n">msg</span><span class="o">.</span><span class="n">Str</span> <span class="o">==</span> <span class="s">"WIFI GOT IP"</span> <span class="p">{</span>
			<span class="k">break</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="k">var</span> <span class="n">conn</span> <span class="n">net</span><span class="o">.</span><span class="n">Conn</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="k">if</span> <span class="n">conn</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
			<span class="n">conn</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
		<span class="p">}</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">2</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span> <span class="c">// one request every two seconds</span>
		<span class="k">var</span> <span class="n">err</span> <span class="kt">error</span>
		<span class="n">conn</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">espnet</span><span class="o">.</span><span class="n">DialDev</span><span class="p">(</span><span class="n">dev</span><span class="p">,</span> <span class="s">"tcp"</span><span class="p">,</span> <span class="s">"httpbin.org:80"</span><span class="p">)</span>
		<span class="k">if</span> <span class="n">logErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span> <span class="p">{</span>
			<span class="k">continue</span>
		<span class="p">}</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">Sending HTTP request... "</span><span class="p">)</span>
		<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">io</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span>
			<span class="n">conn</span><span class="p">,</span>
			<span class="s">"GET /ip HTTP/1.0</span><span class="se">\r\n</span><span class="s">Host: httpbin.org</span><span class="se">\r\n\r\n</span><span class="s">"</span><span class="p">,</span>
		<span class="p">)</span>
		<span class="k">if</span> <span class="n">logErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span> <span class="p">{</span>
			<span class="k">continue</span>
		<span class="p">}</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="s">"OK</span><span class="se">\n</span><span class="s">Receiving response:</span><span class="se">\n\n</span><span class="s">"</span><span class="p">)</span>
		<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">io</span><span class="o">.</span><span class="n">Copy</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Stdout</span><span class="p">,</span> <span class="n">conn</span><span class="p">)</span>
		<span class="n">logErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If we ignore the Wi-Fi initialization and the waiting for an IP address, the only difference from a typical code of TCP client is the use of the <a href="https://pkg.go.dev/github.com/embeddedgo/espat/espnet#DialDev">espnet.DialDev</a> function instead of <a href="https://pkg.go.dev/net#Dial">net.Dial</a>.</p>

<p>The output may look like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Initializing ESP-AT module... OK
Waiting for an IP address...
WIFI CONNECTED
WIFI GOT IP

Sending HTTP request... OK
Receiving response:

HTTP/1.1 200 OK
Date: Sun, 31 Dec 2023 21:58:14 GMT
Content-Type: application/json
Content-Length: 33
Connection: close
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "origin": "37.109.142.142"
}
</code></pre></div></div>

<h3 id="http-server">HTTP server</h3>

<p>Logically, the next example should be a simple TCP server. However, this article is getting too long, so we will go straight to the titular HTTP server. You can find the code of simple TCP echo server on <a href="https://github.com/embeddedgo/imxrt/blob/master/devboard/teensy4/examples/espnet/main.go">Github</a>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"fmt"</span>
	<span class="s">"net/http"</span>
	<span class="s">"strings"</span>
	<span class="s">"time"</span>

	<span class="s">"github.com/embeddedgo/espat"</span>
	<span class="s">"github.com/embeddedgo/espat/espnet"</span>
	<span class="s">"github.com/embeddedgo/imxrt/hal/lpuart"</span>
	<span class="s">"github.com/embeddedgo/imxrt/hal/lpuart/lpuart2"</span>

	<span class="s">"github.com/embeddedgo/imxrt/devboard/teensy4/board/pins"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">for</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"error:"</span><span class="p">,</span> <span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">())</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">u</span> <span class="o">:=</span> <span class="n">lpuart2</span><span class="o">.</span><span class="n">Driver</span><span class="p">()</span>
	<span class="n">u</span><span class="o">.</span><span class="n">Setup</span><span class="p">(</span><span class="n">lpuart</span><span class="o">.</span><span class="n">Word8b</span><span class="p">,</span> <span class="m">115200</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">UsePin</span><span class="p">(</span><span class="n">pins</span><span class="o">.</span><span class="n">P14</span><span class="p">,</span> <span class="n">lpuart</span><span class="o">.</span><span class="n">TXD</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">UsePin</span><span class="p">(</span><span class="n">pins</span><span class="o">.</span><span class="n">P15</span><span class="p">,</span> <span class="n">lpuart</span><span class="o">.</span><span class="n">RXD</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">EnableRx</span><span class="p">(</span><span class="m">512</span><span class="p">)</span>
	<span class="n">u</span><span class="o">.</span><span class="n">EnableTx</span><span class="p">()</span>

	<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">5</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="s">"Initializing Wi-Fi module... "</span><span class="p">)</span>
	<span class="n">dev</span> <span class="o">:=</span> <span class="n">espat</span><span class="o">.</span><span class="n">NewDevice</span><span class="p">(</span><span class="s">"esp0"</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">u</span><span class="p">)</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">dev</span><span class="o">.</span><span class="n">Init</span><span class="p">(</span><span class="no">true</span><span class="p">))</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"OK"</span><span class="p">)</span>

	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Waiting for an IP address..."</span><span class="p">)</span>
	<span class="k">for</span> <span class="n">msg</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">dev</span><span class="o">.</span><span class="n">Async</span><span class="p">()</span> <span class="p">{</span>
		<span class="n">fatalErr</span><span class="p">(</span><span class="n">msg</span><span class="o">.</span><span class="n">Err</span><span class="p">)</span>
		<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">msg</span><span class="o">.</span><span class="n">Str</span><span class="p">)</span>
		<span class="k">if</span> <span class="n">msg</span><span class="o">.</span><span class="n">Str</span> <span class="o">==</span> <span class="s">"WIFI GOT IP"</span> <span class="p">{</span>
			<span class="k">break</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="c">// Print IP address.</span>
	<span class="n">txt</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">dev</span><span class="o">.</span><span class="n">CmdStr</span><span class="p">(</span><span class="s">"+CIPSTA?"</span><span class="p">)</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">strings</span><span class="o">.</span><span class="n">ReplaceAll</span><span class="p">(</span><span class="n">txt</span><span class="p">,</span> <span class="s">"+CIPSTA:"</span><span class="p">,</span> <span class="s">""</span><span class="p">))</span>

	<span class="n">ls</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">espnet</span><span class="o">.</span><span class="n">ListenDev</span><span class="p">(</span><span class="n">dev</span><span class="p">,</span> <span class="s">"tcp"</span><span class="p">,</span> <span class="s">":80"</span><span class="p">)</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"Listen on:"</span><span class="p">,</span> <span class="n">ls</span><span class="o">.</span><span class="n">Addr</span><span class="p">())</span>
	<span class="n">fatalErr</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">Serve</span><span class="p">(</span><span class="n">ls</span><span class="p">,</span> <span class="n">http</span><span class="o">.</span><span class="n">HandlerFunc</span><span class="p">(</span><span class="n">handler</span><span class="p">)))</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">handler</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">RemoteAddr</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">RequestURI</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Fprintln</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"Go HTTP server"</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Fprintln</span><span class="p">(</span><span class="n">w</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Fprintln</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"Method:    "</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">Method</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Fprintln</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"URL:       "</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">URL</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Fprintln</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"Proto:     "</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">Proto</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Fprintln</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"Host:      "</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">Host</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Fprintln</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"RemoteAddr:"</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">RemoteAddr</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Fprintln</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"RequestURI:"</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">RequestURI</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Similarly to the TCP client, the only difference from the typical Go program  is the use of the <a href="https://pkg.go.dev/github.com/embeddedgo/espat/espnet#ListenDev">espnet.ListenDev</a> function instead of <a href="https://pkg.go.dev/net#Listen">net.Listen</a>.</p>

<p>If we run this program on Teensy it should print its IP address so we’ll know where to direct our web browser.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Initializing Wi-Fi module... OK
Waiting for an IP address...
WIFI CONNECTED
WIFI GOT IP
ip:"192.168.1.11"
gateway:"192.168.1.1"
netmask:"255.255.255.0"

Listen on: :80
</code></pre></div></div>

<p>If we enter the appropriate URL into our web browser (e.g. http://192.168.1.11/abc) then we should receive the response as below:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Go HTTP server

Method:     GET
URL:        /abc
Proto:      HTTP/1.1
Host:       192.168.1.11
RemoteAddr: 192.168.1.5:46674
RequestURI: /abc
</code></pre></div></div>

<p>and something like this on the USB console:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>192.168.1.5:46674 /abc
192.168.1.5:46674 /favicon.ico
</code></pre></div></div>

<p>That’s all folks.</p>

<p>Happy New Year!</p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="go" /><category term="golang" /><category term="embeddedgo" /><category term="network" /><category term="graphic" /><summary type="html"><![CDATA[]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://embeddedgo.github.io/images/go_http_server_on_teensy4/title.jpg" /><media:content medium="image" url="https://embeddedgo.github.io/images/go_http_server_on_teensy4/title.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">pix, a minimalistic graphic library (part 1)</title><link href="https://embeddedgo.github.io/2022/03/09/pix_a_minimalistic_graphic_library_part1.html" rel="alternate" type="text/html" title="pix, a minimalistic graphic library (part 1)" /><published>2022-03-09T00:00:00+00:00</published><updated>2022-03-09T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2022/03/09/pix_a_minimalistic_graphic_library_part1</id><content type="html" xml:base="https://embeddedgo.github.io/2022/03/09/pix_a_minimalistic_graphic_library_part1.html"><![CDATA[<p><img src="/images/pix_a_minimalistic_graphic_library/pix.jpg" alt="pix displays" /></p>

<!--more-->

<p><a href="https://pkg.go.dev/github.com/embeddedgo/display/pix">Pix</a> occupies the same niche as the <a href="https://learn.adafruit.com/adafruit-gfx-graphics-library">Adafruit GFX library</a>, <a href="https://github.com/olikraus/ucglib/wiki">ucglib</a> or <a href="https://github.com/olikraus/u8g2/wiki">u8g2</a> but may also be used outside the microcontroller world. As it was created much later and written in <a href="https://go.dev/">Go</a> it can be much simpler, concise and clean without sacrificing essential features. The main design principles were simplicity, portability and speed, hence the small number of graphics functions and the lack of features that require significant hardware support to make them work fast. See also the <a href="/2020/03/27/programming_with_eve.html">previous article</a> about the library targeted more capable displays.</p>

<p>Pix provides only 9 drawing functions. Three basic ones:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">Area</span><span class="p">)</span> <span class="n">Draw</span><span class="p">(</span><span class="n">r</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span><span class="p">,</span> <span class="n">src</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">,</span> <span class="n">sp</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">mask</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">,</span> <span class="n">mp</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">op</span> <span class="n">draw</span><span class="o">.</span><span class="n">Op</span><span class="p">)</span>
<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">Area</span><span class="p">)</span> <span class="n">Fill</span><span class="p">(</span><span class="n">r</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span><span class="p">)</span>
<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">Area</span><span class="p">)</span> <span class="n">NewTextWriter</span><span class="p">(</span><span class="n">f</span> <span class="n">font</span><span class="o">.</span><span class="n">Face</span><span class="p">)</span> <span class="o">*</span><span class="n">TextWriter</span>
</code></pre></div></div>

<p>and six implemented using the above <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.Fill"><code class="language-plaintext highlighter-rouge">Fill</code></a> function:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">Area</span><span class="p">)</span> <span class="n">Point</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="kt">int</span><span class="p">)</span>
<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">Area</span><span class="p">)</span> <span class="n">Line</span><span class="p">(</span><span class="n">p0</span><span class="p">,</span> <span class="n">p1</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">)</span>
<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">Area</span><span class="p">)</span> <span class="n">RoundRect</span><span class="p">(</span><span class="n">p0</span><span class="p">,</span> <span class="n">p1</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">ra</span><span class="p">,</span> <span class="n">rb</span> <span class="kt">int</span><span class="p">,</span> <span class="n">fill</span> <span class="kt">bool</span><span class="p">)</span>
<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">Area</span><span class="p">)</span> <span class="n">Quad</span><span class="p">(</span><span class="n">p0</span><span class="p">,</span> <span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="n">p3</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">fill</span> <span class="kt">bool</span><span class="p">)</span>
<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">Area</span><span class="p">)</span> <span class="n">FillQuad</span><span class="p">(</span><span class="n">p0</span><span class="p">,</span> <span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="n">p3</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">)</span>
<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">Area</span><span class="p">)</span> <span class="n">Arc</span><span class="p">(</span><span class="n">p</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">mina</span><span class="p">,</span> <span class="n">minb</span><span class="p">,</span> <span class="n">maxa</span><span class="p">,</span> <span class="n">maxb</span> <span class="kt">int</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th1</span> <span class="kt">int32</span><span class="p">,</span> <span class="n">fill</span> <span class="kt">bool</span><span class="p">)</span>
</code></pre></div></div>

<p>With some additional properties of the <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area"><code class="language-plaintext highlighter-rouge">Area</code></a> type this small set of drawing primitives allows you to easily present various kinds of data in many different ways and even create a simple user interface.</p>

<h3 id="basic-functions">Basic functions</h3>

<p>You can do surprisingly a lot with just three basic functions mentioned above. In fact, you may throw <code class="language-plaintext highlighter-rouge">Fill</code> out of this set reducing it to just two functions. <code class="language-plaintext highlighter-rouge">Fill</code> can be easily implemented using <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.Draw"><code class="language-plaintext highlighter-rouge">Draw</code></a> so it’s not that basic. On the other hand <code class="language-plaintext highlighter-rouge">Fill</code> is simpler to use and often much more efficient than <code class="language-plaintext highlighter-rouge">Draw</code> so let’s let it stay in the basic set.</p>

<p>To show how it all works closer to practice let’s create an example view of a simple music player. To begin with, let’s see what can be done using just these three basic functions.</p>

<h4 id="display">Display</h4>

<p>Eventually everything we draw will go to the display so we need a display.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">disp</span> <span class="o">:=</span> <span class="n">pix</span><span class="o">.</span><span class="n">NewDisplay</span><span class="p">(</span><span class="n">driver</span><span class="p">)</span>
</code></pre></div></div>

<p>But what’s a <em>driver</em>? Driver can by anything that implements the <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Driver"><code class="language-plaintext highlighter-rouge">pix.Driver</code></a> interface:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">Driver</span> <span class="k">interface</span> <span class="p">{</span>
	<span class="n">Draw</span><span class="p">(</span><span class="n">r</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span><span class="p">,</span> <span class="n">src</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">,</span> <span class="n">sp</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">mask</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">,</span> <span class="n">mp</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">op</span> <span class="n">draw</span><span class="o">.</span><span class="n">Op</span><span class="p">)</span>
	<span class="n">Fill</span><span class="p">(</span><span class="n">r</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span><span class="p">)</span>
	<span class="n">SetColor</span><span class="p">(</span><span class="n">c</span> <span class="n">color</span><span class="o">.</span><span class="n">Color</span><span class="p">)</span>
	<span class="n">SetDir</span><span class="p">(</span><span class="n">dir</span> <span class="kt">int</span><span class="p">)</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span>
	<span class="n">Flush</span><span class="p">()</span>
	<span class="n">Err</span><span class="p">(</span><span class="n">clear</span> <span class="kt">bool</span><span class="p">)</span> <span class="kt">error</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Most cheap display controllers provide just such an interface:</p>

<ul>
  <li>
    <p>writing to (and sometimes reading from) a specified rectangular area of the frame memory (<code class="language-plaintext highlighter-rouge">Draw</code>, <code class="language-plaintext highlighter-rouge">Fill</code>),</p>
  </li>
  <li>
    <p>some also let you set the display orientation  (<code class="language-plaintext highlighter-rouge">SetDir</code>).</p>
  </li>
</ul>

<p>The remaining three methods are related to the internal operation of the driver.</p>

<p>However, we won’t be working with any real display for now.  All we need is a driver that simulates the display in a file. Let’s write one. It’s easy.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">Driver</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">Name</span>  <span class="kt">string</span>
	<span class="n">Image</span> <span class="n">draw</span><span class="o">.</span><span class="n">Image</span>
	<span class="n">fill</span>  <span class="n">image</span><span class="o">.</span><span class="n">Uniform</span>
	<span class="n">err</span>   <span class="kt">error</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">d</span> <span class="o">*</span><span class="n">Driver</span><span class="p">)</span> <span class="n">Draw</span><span class="p">(</span><span class="n">r</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span><span class="p">,</span> <span class="n">src</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">,</span> <span class="n">sp</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">mask</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">,</span> <span class="n">mp</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">op</span> <span class="n">draw</span><span class="o">.</span><span class="n">Op</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">draw</span><span class="o">.</span><span class="n">DrawMask</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">Image</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">sp</span><span class="p">,</span> <span class="n">mask</span><span class="p">,</span> <span class="n">mp</span><span class="p">,</span> <span class="n">op</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">d</span> <span class="o">*</span><span class="n">Driver</span><span class="p">)</span> <span class="n">SetColor</span><span class="p">(</span><span class="n">c</span> <span class="n">color</span><span class="o">.</span><span class="n">Color</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">RGBA</span><span class="p">()</span>
	<span class="n">d</span><span class="o">.</span><span class="n">fill</span><span class="o">.</span><span class="n">C</span> <span class="o">=</span> <span class="n">color</span><span class="o">.</span><span class="n">RGBA64</span><span class="p">{</span><span class="kt">uint16</span><span class="p">(</span><span class="n">r</span><span class="p">),</span> <span class="kt">uint16</span><span class="p">(</span><span class="n">g</span><span class="p">),</span> <span class="kt">uint16</span><span class="p">(</span><span class="n">b</span><span class="p">),</span> <span class="kt">uint16</span><span class="p">(</span><span class="n">a</span><span class="p">)}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">d</span> <span class="o">*</span><span class="n">Driver</span><span class="p">)</span> <span class="n">Fill</span><span class="p">(</span><span class="n">r</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">d</span><span class="o">.</span><span class="n">Draw</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">d</span><span class="o">.</span><span class="n">fill</span><span class="p">,</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">{},</span> <span class="no">nil</span><span class="p">,</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">{},</span> <span class="n">draw</span><span class="o">.</span><span class="n">Over</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">d</span> <span class="o">*</span><span class="n">Driver</span><span class="p">)</span> <span class="n">SetDir</span><span class="p">(</span><span class="n">dir</span> <span class="kt">int</span><span class="p">)</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">d</span><span class="o">.</span><span class="n">Image</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">d</span> <span class="o">*</span><span class="n">Driver</span><span class="p">)</span> <span class="n">Flush</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">if</span> <span class="n">d</span><span class="o">.</span><span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="k">return</span>
	<span class="p">}</span>
	<span class="n">f</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Create</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">Name</span><span class="p">)</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="n">d</span><span class="o">.</span><span class="n">err</span> <span class="o">=</span> <span class="n">err</span>
		<span class="k">return</span>
	<span class="p">}</span>
	<span class="n">d</span><span class="o">.</span><span class="n">err</span> <span class="o">=</span> <span class="n">jpeg</span><span class="o">.</span><span class="n">Encode</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">Image</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">jpeg</span><span class="o">.</span><span class="n">Options</span><span class="p">{</span><span class="m">90</span><span class="p">})</span>
	<span class="n">f</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">d</span> <span class="o">*</span><span class="n">Driver</span><span class="p">)</span> <span class="n">Err</span><span class="p">(</span><span class="n">clear</span> <span class="kt">bool</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
	<span class="n">err</span> <span class="o">:=</span> <span class="n">d</span><span class="o">.</span><span class="n">err</span>
	<span class="k">if</span> <span class="n">clear</span> <span class="p">{</span>
		<span class="n">d</span><span class="o">.</span><span class="n">err</span> <span class="o">=</span> <span class="no">nil</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The driver is ready. A few details, however, require clarification.</p>

<p><code class="language-plaintext highlighter-rouge">Driver</code> is almost simplest possible implementation of <code class="language-plaintext highlighter-rouge">pix.Driver</code>. As you can see everything is drawn using the standard <a href="https://pkg.go.dev/image/draw#DrawMask"><code class="language-plaintext highlighter-rouge">draw.DrawMask</code></a> function.</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Image</code> serves as a temporary framebuffer.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Name</code> is the name of the file where the <code class="language-plaintext highlighter-rouge">Flush</code> method will save the content of the temporary framebuffer. This file can be thought of as a target framebuffer whose content is visible to the user.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Draw</code> is just a straightforward wrapper over <code class="language-plaintext highlighter-rouge">draw.DrawMask</code>.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">SetColor</code> sets the color of the uniform image used by <code class="language-plaintext highlighter-rouge">Fill</code>.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Fill</code> simply draws filled rectangles using the <code class="language-plaintext highlighter-rouge">Draw</code> method. There is no any optimization here. Well, maybe avoiding allocating new <a href="https://pkg.go.dev/image#Uniform"><code class="language-plaintext highlighter-rouge">image.Uniform</code></a> every call is some sort of optimization.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">SetDir</code> is intended for display rotations but it’s fine for the driver to completely ignore the changes of display direction and always return the same bounds. By the way, <code class="language-plaintext highlighter-rouge">SetDir</code> is called first before any other <code class="language-plaintext highlighter-rouge">Driver</code> method so can be used for (lazy) initialization.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Flush</code> allows the driver to have internal buffers. In our case the buffer stores the entire display screen before save it to the file but in other cases it can be much smaller or nonexistent.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Err</code> is for signaling errors. For performance reasons there is no any internal error checking in pix. The driver is responsible to check errors as necessary and record the occurrence of the first error. It should avoid any actions that may cause further errors until the recorded error is cleared. The user can check for an error at his convenience using the <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.Err"><code class="language-plaintext highlighter-rouge">Area.Err</code></a> or <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Display.Err"><code class="language-plaintext highlighter-rouge">Display.Err</code></a> functions.</p>
  </li>
</ul>

<p>For the sake of convenience w can also define a display constructor:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">NewDisplay</span><span class="p">(</span><span class="n">name</span> <span class="kt">string</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span> <span class="kt">int</span><span class="p">)</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">Display</span> <span class="p">{</span>
	<span class="n">driver</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">Driver</span><span class="p">{</span>
		<span class="n">Name</span><span class="o">:</span>  <span class="n">name</span><span class="p">,</span>
		<span class="n">Image</span><span class="o">:</span> <span class="n">image</span><span class="o">.</span><span class="n">NewRGBA</span><span class="p">(</span><span class="n">image</span><span class="o">.</span><span class="n">Rect</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">)),</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">pix</span><span class="o">.</span><span class="n">NewDisplay</span><span class="p">(</span><span class="n">driver</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It returns <a href="https://pkg.go.dev/image#RGBA"><code class="language-plaintext highlighter-rouge">image.RGBA</code></a> backed display with given dimensions, associated with the file with the given name.</p>

<h4 id="areadraw">Area.Draw</h4>

<p>Let’s finally draw something.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">playerView</span><span class="p">(</span><span class="n">disp</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">Display</span><span class="p">,</span> <span class="n">artist</span><span class="p">,</span> <span class="n">title</span> <span class="kt">string</span><span class="p">,</span> <span class="n">cover</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">a</span> <span class="o">:=</span> <span class="n">disp</span><span class="o">.</span><span class="n">NewArea</span><span class="p">(</span><span class="n">disp</span><span class="o">.</span><span class="n">Bounds</span><span class="p">())</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Draw</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">(),</span> <span class="n">cover</span><span class="p">,</span> <span class="n">cover</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span><span class="o">.</span><span class="n">Min</span><span class="p">,</span> <span class="no">nil</span><span class="p">,</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">{},</span> <span class="n">draw</span><span class="o">.</span><span class="n">Src</span><span class="p">)</span>
	<span class="n">disp</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">disp</span> <span class="o">:=</span> <span class="n">NewDisplay</span><span class="p">(</span><span class="s">"/tmp/player.jpg"</span><span class="p">,</span> <span class="m">640</span><span class="p">,</span> <span class="m">320</span><span class="p">)</span>

	<span class="n">artist</span> <span class="o">:=</span> <span class="s">"Gophers"</span>
	<span class="n">title</span> <span class="o">:=</span> <span class="s">"Work Hard Play Hard"</span>
	<span class="n">cover</span> <span class="o">:=</span> <span class="n">loadImage</span><span class="p">(</span><span class="s">"testdata/gopherbug.jpg"</span><span class="p">)</span>
	<span class="n">playerView</span><span class="p">(</span><span class="n">disp</span><span class="p">,</span> <span class="n">artist</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">cover</span><span class="p">)</span>

	<span class="n">checkErr</span><span class="p">(</span><span class="n">disp</span><span class="o">.</span><span class="n">Err</span><span class="p">(</span><span class="no">true</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player1.jpg" alt="player1" /></p>

<p>This isn’t exactly what we wanted but we’ll fix it in a moment.</p>

<p>The <code class="language-plaintext highlighter-rouge">playerView</code> function creates a new drawing area that covers the whole display screen. Next, it uses the <code class="language-plaintext highlighter-rouge">Area.Draw</code> function to draw the cover image in the selected area of the display.</p>

<p><code class="language-plaintext highlighter-rouge">Area.Draw</code> works like <code class="language-plaintext highlighter-rouge">draw.DrawMask</code> with <code class="language-plaintext highlighter-rouge">dst</code> set to the whole drawing area. But what’s an <code class="language-plaintext highlighter-rouge">Area</code>?</p>

<p><code class="language-plaintext highlighter-rouge">Area</code> gives you access to the rectangular portion of the display screen. One area, without additional synchronization, can be used by only one goroutine, but multiple goroutines can share a single display using multiple areas. Moreover, one area can span multiple displays.</p>

<p>Area isn’t a window as you’re used to in case of Windows or X11. Everything is drawn directly on the display and <code class="language-plaintext highlighter-rouge">Area</code> only limits the drawing area. Areas may overlap but their intersection will be affected by all of them.</p>

<p><code class="language-plaintext highlighter-rouge">loadImage</code> is a helper function that loads an image from a file:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">loadImage</span><span class="p">(</span><span class="n">name</span> <span class="kt">string</span><span class="p">)</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span> <span class="p">{</span>
	<span class="n">f</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Open</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
	<span class="n">checkErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
	<span class="k">defer</span> <span class="n">f</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
	<span class="n">img</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">image</span><span class="o">.</span><span class="n">Decode</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
	<span class="n">checkErr</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
	<span class="k">return</span> <span class="n">img</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="areafill">Area.Fill</h4>

<p>Let’s organize our screen a little bit. The space for the cover image will be on the left, centered vertically.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">bgColor</span> <span class="o">=</span> <span class="n">color</span><span class="o">.</span><span class="n">Gray</span><span class="p">{</span><span class="m">50</span><span class="p">}</span>

<span class="k">func</span> <span class="n">playerView</span><span class="p">(</span><span class="n">disp</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">Display</span><span class="p">,</span> <span class="n">artist</span><span class="p">,</span> <span class="n">title</span> <span class="kt">string</span><span class="p">,</span> <span class="n">cover</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">r</span> <span class="o">:=</span> <span class="n">disp</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
	<span class="n">a</span> <span class="o">:=</span> <span class="n">disp</span><span class="o">.</span><span class="n">NewArea</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">bgColor</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Fill</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">())</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">()</span>
	<span class="k">const</span> <span class="n">margin</span> <span class="o">=</span> <span class="m">20</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetRect</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Inset</span><span class="p">(</span><span class="n">margin</span><span class="p">))</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetColorRGBA</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">200</span><span class="p">,</span> <span class="m">255</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Fill</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">())</span>
	<span class="n">disp</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the above code the <code class="language-plaintext highlighter-rouge">a</code> area initially covers the whole display. <code class="language-plaintext highlighter-rouge">a.SetColor(bgColor)</code> sets the color for the subsequent <code class="language-plaintext highlighter-rouge">a.Fill(a.Bounds())</code> that fills the whole screen with it.</p>

<p>Next we do some calculations with <code class="language-plaintext highlighter-rouge">r</code> to reduce it to the square filling the left side of the screen. <code class="language-plaintext highlighter-rouge">a.SetRect(r.Inset(margin))</code> reshapes our drawing area to be on the center of <code class="language-plaintext highlighter-rouge">r</code> with 20 point margins. Finally we fill the area with blue color to see if everything went as planned.</p>

<p><img src="/images/pix_a_minimalistic_graphic_library/player2.jpg" alt="player2" /></p>

<p>Let’s draw the cover image in the center of this area.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">playerView</span><span class="p">(</span><span class="n">disp</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">Display</span><span class="p">,</span> <span class="n">artist</span><span class="p">,</span> <span class="n">title</span> <span class="kt">string</span><span class="p">,</span> <span class="n">cover</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">r</span> <span class="o">:=</span> <span class="n">disp</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
	<span class="n">a</span> <span class="o">:=</span> <span class="n">disp</span><span class="o">.</span><span class="n">NewArea</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">bgColor</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Fill</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">())</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">()</span>
	<span class="k">const</span> <span class="n">margin</span> <span class="o">=</span> <span class="m">20</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetRect</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Inset</span><span class="p">(</span><span class="n">margin</span><span class="p">))</span>
	<span class="n">sr</span> <span class="o">:=</span> <span class="n">cover</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
	<span class="n">r</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Min</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Size</span><span class="p">()</span><span class="o">.</span><span class="n">Sub</span><span class="p">(</span><span class="n">sr</span><span class="o">.</span><span class="n">Size</span><span class="p">())</span><span class="o">.</span><span class="n">Div</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Draw</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">cover</span><span class="p">,</span> <span class="n">sr</span><span class="o">.</span><span class="n">Min</span><span class="p">,</span> <span class="no">nil</span><span class="p">,</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">{},</span> <span class="n">draw</span><span class="o">.</span><span class="n">Src</span><span class="p">)</span>
	<span class="n">disp</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player3.jpg" alt="player3" /></p>

<p>The magic <code class="language-plaintext highlighter-rouge">r.Size().Sub(sr.Size()).Div(2)</code> expression calculates the position for the top left corner of the cover image to place it in the center of the area. We use the “simplified” expression instead of <code class="language-plaintext highlighter-rouge">r.Min.Add(r.Size().Sub(sr.Size()).Div(2))</code> because by default the area’s top left corner called <em>origin</em> is (0, 0).</p>

<p><code class="language-plaintext highlighter-rouge">Area</code> has its own internal coordinate system independent of the other areas and the displays it covers. You can change the origin using the <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.SetOrigin"><code class="language-plaintext highlighter-rouge">SetOrigin(p image.Point)</code></a> method but usually you don’t want to do that because (0, 0) is nice and convenient.</p>

<p>Let’s see what happens when we double the size of the image.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cover</span> <span class="o">=</span> <span class="n">images</span><span class="o">.</span><span class="n">Magnify</span><span class="p">(</span><span class="n">cover</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="n">images</span><span class="o">.</span><span class="n">Bilinear</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player4.jpg" alt="player4" /></p>

<p>Everything’s all right. It was supposed to be like that. The area has cropped the magnified image so we see only its central part.</p>

<h4 id="textwriter">TextWriter</h4>

<p>Text is still the main way to communicate and present data. Sometimes the whole graphics library is used just to print text on a small <a href="http://www.lcdwiki.com/0.96inch_SPI_OLED_Module">monochrome OLED display</a>, currently the cheapest way to display multilingual text messages.</p>

<p>Although drawing text in pix is really drawing multiple images on the same baseline, <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#TextWriter"><code class="language-plaintext highlighter-rouge">TextWriter</code></a> is listed as one of the three main drawing primitives. This is because drawing text is a much broader concept than just drawing images. See the definition of <code class="language-plaintext highlighter-rouge">TextWriter</code> below:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">TextWriter</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">Area</span>   <span class="o">*</span><span class="n">Area</span>       <span class="c">// drawing area</span>
	<span class="n">Face</span>   <span class="n">font</span><span class="o">.</span><span class="n">Face</span>   <span class="c">// source of glyphs</span>
	<span class="n">Color</span>  <span class="n">image</span><span class="o">.</span><span class="n">Image</span> <span class="c">// glyph color (foreground image)</span>
	<span class="n">Pos</span>    <span class="n">image</span><span class="o">.</span><span class="n">Point</span> <span class="c">// position of the next glyph</span>
	<span class="n">Offset</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span> <span class="c">// offset from the Pos to the glyph origin</span>
	<span class="n">Wrap</span>   <span class="kt">byte</span>        <span class="c">// wrapping mode</span>
	<span class="n">Break</span>  <span class="kt">byte</span>        <span class="c">// line breaking mode</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">TextWriter</code> combines <em>drawing images</em> with things like <em>font</em>, <em>font face</em>, <em>drawing direction</em>, <em>wrapping mode</em> and <em>line breaking mode</em>. Rendering text is a very broad topic and we don’t want to go over all these details here.</p>

<p><code class="language-plaintext highlighter-rouge">TextWriter</code> uses the <a href="https://pkg.go.dev/github.com/embeddedgo/display/font#Face"><code class="language-plaintext highlighter-rouge">font.Face</code></a> interface to convert individual unicode code points (runes) to their graphical representation (glyphs).</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">Face</span> <span class="k">interface</span> <span class="p">{</span>
	<span class="n">Size</span><span class="p">()</span> <span class="p">(</span><span class="n">height</span><span class="p">,</span> <span class="n">ascent</span> <span class="kt">int</span><span class="p">)</span>
	<span class="n">Advance</span><span class="p">(</span><span class="n">r</span> <span class="kt">rune</span><span class="p">)</span> <span class="kt">int</span>
	<span class="n">Glyph</span><span class="p">(</span><span class="n">r</span> <span class="kt">rune</span><span class="p">)</span> <span class="p">(</span><span class="n">img</span> <span class="n">image</span><span class="o">.</span><span class="n">Image</span><span class="p">,</span> <span class="n">origin</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">advance</span> <span class="kt">int</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This simple approach turns out to be quite flexible and gives satisfactory results in most pix applications.</p>

<p>Glyphs are drawn side by side, always horizontally, from left to right. This doesn’t mean that you cannot draw lines of text in other directions but <code class="language-plaintext highlighter-rouge">TextWriter</code> alone cannot. You have to use the <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.SetMirror"><code class="language-plaintext highlighter-rouge">Area.SetMirror</code></a> method to trick it.</p>

<p>Let’s print the name of the author and the song title on the right-hand side of the cover image.</p>

<p>The code of <code class="language-plaintext highlighter-rouge">playerView</code> function is getting long so from now I’ll only show the changes in this function with one line context.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">a</span><span class="o">.</span><span class="n">Draw</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">cover</span><span class="p">,</span> <span class="n">sr</span><span class="o">.</span><span class="n">Min</span><span class="p">,</span> <span class="no">nil</span><span class="p">,</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">{},</span> <span class="n">draw</span><span class="o">.</span><span class="n">Src</span><span class="p">)</span>
<span class="c">// new code begin</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">disp</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+=</span> <span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">()</span>
<span class="n">a</span><span class="o">.</span><span class="n">SetRect</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Inset</span><span class="p">(</span><span class="n">margin</span><span class="p">))</span>
<span class="n">w</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">NewTextWriter</span><span class="p">(</span><span class="n">titleFont</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">textColor</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">artist</span> <span class="o">+</span> <span class="s">" -- "</span> <span class="o">+</span> <span class="n">title</span><span class="p">)</span>
<span class="c">// new code end</span>
<span class="n">disp</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player5.jpg" alt="player5" /></p>

<p>Hmm, the default line breaking mode doesn’t look very good for our use case. Let’s change it.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">w</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">NewTextWriter</span><span class="p">(</span><span class="n">titleFont</span><span class="p">)</span>
<span class="c">// new code begin</span>
<span class="n">w</span><span class="o">.</span><span class="n">Break</span> <span class="o">=</span> <span class="n">pix</span><span class="o">.</span><span class="n">BreakSpace</span>
<span class="c">// new code end</span>
<span class="n">w</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">textColor</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player6.jpg" alt="player6" /></p>

<p>Much better.</p>

<p>I still have to explain where did <code class="language-plaintext highlighter-rouge">titleFont</code> come from. <code class="language-plaintext highlighter-rouge">titleFont</code> represents a <em>font face</em>, the specific size, style and weight of a font. In our case it’s the 18 point regular <em>Dejavu Sans</em> font. The necessary fonts can be loaded at run time from external source or included at compile time this way:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="s">"github.com/embeddedgo/display/font/subfont/font9/dejavusans18"</span>

<span class="k">var</span> <span class="n">titleFont</span> <span class="o">=</span> <span class="n">dejavusans18</span><span class="o">.</span><span class="n">NewFace</span><span class="p">(</span>
	<span class="n">dejavusans18</span><span class="o">.</span><span class="n">X0020_007e</span><span class="p">,</span> <span class="c">//  !"#$%&amp;'()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJ</span>
	<span class="n">dejavusans18</span><span class="o">.</span><span class="n">X0101_0201</span><span class="p">,</span> <span class="c">// āĂăĄąĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Embedding fonts into program code is a common practice in case of embedded systems. Such systems often have no external storage and a limited size of program memory. Fonts can be a large part of program image hence the concept of <a href="https://pkg.go.dev/github.com/embeddedgo/display/font/subfont">subfont</a> taken straight from <a href="https://9fans.github.io/plan9port/man/man3/subfont.html">Plan 9</a>. It allows you to select only the necessary components of the font. In our case, we included two 18 point subfonts for unicode code points from 0x020 to 0x201.</p>

<p>The above declaration using the <a href="https://pkg.go.dev/github.com/embeddedgo/display/font/subfont/font9/dejavusans18#NewFace"><code class="language-plaintext highlighter-rouge">NewFace</code></a> function is a more convenient counterpart to the declaration below:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">titleFont</span> <span class="o">=</span>  <span class="o">&amp;</span><span class="n">subfont</span><span class="o">.</span><span class="n">Face</span><span class="p">{</span>
	<span class="n">Height</span><span class="o">:</span> <span class="n">dejavusans18</span><span class="o">.</span><span class="n">Height</span><span class="p">,</span>
	<span class="n">Ascent</span><span class="o">:</span> <span class="n">dejavusans18</span><span class="o">.</span><span class="n">Ascent</span><span class="p">,</span>
	<span class="n">Subfonts</span><span class="o">:</span> <span class="p">[]</span><span class="o">*</span><span class="n">subfont</span><span class="o">.</span><span class="n">Subfont</span><span class="p">{</span>
		<span class="n">dejavusans18</span><span class="o">.</span><span class="n">X0020_007e</span><span class="p">,</span>
		<span class="n">dejavusans18</span><span class="o">.</span><span class="n">X0101_0201</span><span class="p">,</span>
	<span class="p">},</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <a href="https://pkg.go.dev/github.com/embeddedgo/display/font/subfont#Face"><code class="language-plaintext highlighter-rouge">subfont.Face</code></a> type implements the <code class="language-plaintext highlighter-rouge">font.Face</code> interface. As you can see it’s defined by its height, ascent (height above the baseline) and the list of subfonts. Missing subfonts can be loaded at runtime if the (not shown) <code class="language-plaintext highlighter-rouge">Loader</code> field is set.</p>

<p>Much more could be written about fonts, subfonts, their storage, loading and rendering, but let’s go back to our music player example.</p>

<p>What will happen if the author/title text will be much longer? With the current size of the title area it can take up the entire half of the screen. Let’s reduce the height of the area  to only two lines of text, and at the same time, let’s halve the spacing between the cover and title areas because currently it has a width of two margins.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">a</span><span class="o">.</span><span class="n">Draw</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">cover</span><span class="p">,</span> <span class="n">sr</span><span class="o">.</span><span class="n">Min</span><span class="p">,</span> <span class="no">nil</span><span class="p">,</span> <span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">{},</span> <span class="n">draw</span><span class="o">.</span><span class="n">Src</span><span class="p">)</span>
<span class="c">// new code begin</span>
<span class="n">height</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">titleFont</span><span class="o">.</span><span class="n">Size</span><span class="p">()</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">disp</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+=</span> <span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">()</span>
<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">-=</span> <span class="n">margin</span>
<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span> <span class="o">+=</span> <span class="n">margin</span>
<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span> <span class="o">+</span> <span class="m">2</span><span class="o">*</span><span class="n">height</span>
<span class="n">a</span><span class="o">.</span><span class="n">SetRect</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
<span class="n">a</span><span class="o">.</span><span class="n">SetColorRGBA</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">200</span><span class="p">,</span> <span class="m">255</span><span class="p">)</span>
<span class="n">a</span><span class="o">.</span><span class="n">Fill</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">())</span> <span class="c">// temporary, to see the area</span>
<span class="c">// new code end</span>
<span class="n">w</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">NewTextWriter</span><span class="p">(</span><span class="n">titleFont</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player7.jpg" alt="player7" /></p>

<p>Now the layout looks fine.</p>

<p>One might want to center the text in the area but <code class="language-plaintext highlighter-rouge">TextWriter</code> won’t help us with that. Centering or right-justify operations are well defined only for the complete line of text. <code class="language-plaintext highlighter-rouge">TextWriter</code> is intended to be used as <a href="https://pkg.go.dev/io#Writer">io.Writer</a> or <a href="https://pkg.go.dev/io#StringWriter">io.StringWriter</a> so it’s inherently stream oriented, not line oriented. In this light even the <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#BreakSpace"><code class="language-plaintext highlighter-rouge">BreakSpace</code></a> mode fills somewhat stretched feature. Long story short, if you want to center the text you have to do it yourself and then <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#StringWidth"><code class="language-plaintext highlighter-rouge">StringWidth</code></a> and <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Width"><code class="language-plaintext highlighter-rouge">Width</code></a> functions will be helpful to you.</p>

<p>In addition to the author and the title of the song our player will also show the song duration and the current play time. The current time may be updated in real time by another (playing) goroutine. We can give it an area to work with and the goroutine may use the function below to periodically update the information.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">drawTimeDuration</span><span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">Area</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">duration</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">h</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">s</span> <span class="o">:=</span> <span class="n">hms</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
	<span class="n">str</span> <span class="o">:=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%02d:%02d:%02d /"</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span>
	<span class="n">width</span> <span class="o">:=</span> <span class="n">pix</span><span class="o">.</span><span class="n">StringWidth</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">titleFont</span><span class="p">)</span>
	<span class="n">height</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">titleFont</span><span class="o">.</span><span class="n">Size</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">bgColor</span><span class="p">)</span>
	<span class="n">r</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Fill</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
	<span class="n">w</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">NewTextWriter</span><span class="p">(</span><span class="n">titleFont</span><span class="p">)</span>
	<span class="n">w</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">textColor</span><span class="p">)</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="n">r</span><span class="o">.</span><span class="n">Dx</span><span class="p">()</span><span class="o">/</span><span class="m">2</span> <span class="o">-</span> <span class="n">width</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span> <span class="o">+</span> <span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">()</span><span class="o">-</span><span class="n">height</span><span class="p">)</span><span class="o">/</span><span class="m">2</span>
	<span class="n">w</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
	<span class="n">h</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">s</span> <span class="o">=</span> <span class="n">hms</span><span class="p">(</span><span class="n">duration</span><span class="p">)</span>
	<span class="n">fmt</span><span class="o">.</span><span class="n">Fprintf</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">" %02d:%02d:%02d"</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p><a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.Flush"><code class="language-plaintext highlighter-rouge">a.Flush()</code></a> at the end of this function calls flush methods of all displays the area covers. Although <a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Display.Flush"><code class="language-plaintext highlighter-rouge">Display.Flush</code></a> is safe for concurrent use it can cause side effects in special cases like double buffering. There is no one-size-fits-all flush approach in case of full-frame buffering with multiple drawing goroutines. The “flush strategy” must be tailored to each specific case.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">w</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">artist</span> <span class="o">+</span> <span class="s">" -- "</span> <span class="o">+</span> <span class="n">title</span><span class="p">)</span>
<span class="c">// new code begin</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">disp</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+=</span> <span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">()</span>
<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">-=</span> <span class="n">margin</span>
<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span><span class="o">+</span><span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span><span class="p">)</span><span class="o">/</span><span class="m">2</span> <span class="o">-</span> <span class="m">10</span>
<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span> <span class="o">+</span> <span class="m">20</span>
<span class="n">drawTimeDuration</span><span class="p">(</span><span class="n">disp</span><span class="o">.</span><span class="n">NewArea</span><span class="p">(</span><span class="n">r</span><span class="p">),</span> <span class="m">58</span><span class="p">,</span> <span class="m">3</span><span class="o">*</span><span class="m">60</span><span class="o">+</span><span class="m">17</span><span class="p">)</span>
<span class="c">// new code end</span>
<span class="n">disp</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player8.jpg" alt="player8" /></p>

<h4 id="buttons">Buttons</h4>

<p>Even though pix doesn’t provide any <em>Button</em> function (after all, it’s not a GUI library) we definitely want some control buttons in our music player. The simplest possible button can be drawn using the function below:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">button</span><span class="p">(</span><span class="n">w</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">TextWriter</span><span class="p">,</span> <span class="n">r</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span><span class="p">,</span> <span class="n">caption</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Area</span><span class="o">.</span><span class="n">Fill</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
	<span class="n">width</span> <span class="o">:=</span> <span class="n">pix</span><span class="o">.</span><span class="n">StringWidth</span><span class="p">(</span><span class="n">caption</span><span class="p">,</span> <span class="n">buttonFont</span><span class="p">)</span>
	<span class="n">height</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">buttonFont</span><span class="o">.</span><span class="n">Size</span><span class="p">()</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Dx</span><span class="p">()</span><span class="o">-</span><span class="n">width</span><span class="p">)</span><span class="o">/</span><span class="m">2</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span> <span class="o">+</span> <span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">()</span><span class="o">-</span><span class="n">height</span><span class="p">)</span><span class="o">/</span><span class="m">2</span>
	<span class="n">w</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">caption</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As in the case of printing play time the control buttons will likely be handled by a separate goroutine. Our program isn’t a real music player so the function below only draws buttons in the provided area.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">drawControls</span><span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">Area</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">ctrlColor</span><span class="p">)</span>
	<span class="n">w</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">NewTextWriter</span><span class="p">(</span><span class="n">buttonFont</span><span class="p">)</span>
	<span class="n">w</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">captionColor</span><span class="p">)</span>
	<span class="n">r</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
	<span class="n">width</span> <span class="o">:=</span> <span class="n">r</span><span class="o">.</span><span class="n">Dx</span><span class="p">()</span> <span class="o">/</span> <span class="m">3</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="n">width</span> <span class="o">-</span> <span class="m">2</span>
	<span class="n">button</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="s">"Rewind"</span><span class="p">)</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="m">2</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="n">width</span> <span class="o">-</span> <span class="m">2</span>
	<span class="n">button</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="s">"Play/Pause"</span><span class="p">)</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="m">2</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="n">width</span>
	<span class="n">button</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="s">"FastForward"</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s draw the buttons at the bottom right of our view.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">drawTimeDuration</span><span class="p">(</span><span class="n">disp</span><span class="o">.</span><span class="n">NewArea</span><span class="p">(</span><span class="n">r</span><span class="p">),</span> <span class="m">58</span><span class="p">,</span> <span class="m">3</span><span class="o">*</span><span class="m">60</span><span class="o">+</span><span class="m">17</span><span class="p">)</span>
<span class="c">// new code begin</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">disp</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+=</span> <span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">()</span>
<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">-=</span> <span class="n">margin</span>
<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span> <span class="o">-=</span> <span class="n">margin</span>
<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span> <span class="o">-</span> <span class="m">30</span>
<span class="n">drawControls</span><span class="p">(</span><span class="n">disp</span><span class="o">.</span><span class="n">NewArea</span><span class="p">(</span><span class="n">r</span><span class="p">))</span>
<span class="c">// new code end</span>
<span class="n">disp</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
</code></pre></div></div>

<p>We can finally see our music player in all its glory.</p>

<p><img src="/images/pix_a_minimalistic_graphic_library/player9.jpg" alt="player9" /></p>

<h3 id="other-drawing-functions">Other drawing functions</h3>

<p>As you can see, it’s possible to create a pretty decent looking user interface by using only three basic drawing primitives. But pix give us six more ones. Let’s take a look at each of them.</p>

<h4 id="areapoint">Area.Point</h4>

<p><a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.Point"><code class="language-plaintext highlighter-rouge">Point</code></a> is like Go <code class="language-plaintext highlighter-rouge">i++</code>. It’s redundant but convenient. You can always replace <code class="language-plaintext highlighter-rouge">Point(x, y)</code> with <code class="language-plaintext highlighter-rouge">Fill(image.Rect(x, y, x+1, y+1))</code> but in applications where <code class="language-plaintext highlighter-rouge">Point</code> is useful, using <code class="language-plaintext highlighter-rouge">Fill</code> doesn’t look very legible.</p>

<h4 id="arealine">Area.Line</h4>

<p><a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.Line"><code class="language-plaintext highlighter-rouge">Line</code></a> simply draws a straight line connecting two points.</p>

<h4 id="arearoundrect">Area.RoundRect</h4>

<p><a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.RoundRect"><code class="language-plaintext highlighter-rouge">RoundRect</code></a> is a swiss army knife function. It can draw rectangles, circles and ellipses or a combination of them, either filled or unfilled. Drawing performance is almost the same as with multiple specialized functions.</p>

<p>We can use <code class="language-plaintext highlighter-rouge">RoundRect</code> to round the corners of the buttons in our example.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">button</span><span class="p">(</span><span class="n">w</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">TextWriter</span><span class="p">,</span> <span class="n">r</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span><span class="p">,</span> <span class="n">caption</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">r</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Inset</span><span class="p">(</span><span class="m">7</span><span class="p">)</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span><span class="o">--</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span><span class="o">--</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Area</span><span class="o">.</span><span class="n">RoundRect</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="p">,</span> <span class="m">7</span><span class="p">,</span> <span class="m">7</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>
	<span class="n">width</span> <span class="o">:=</span> <span class="n">pix</span><span class="o">.</span><span class="n">StringWidth</span><span class="p">(</span><span class="n">caption</span><span class="p">,</span> <span class="n">buttonFont</span><span class="p">)</span>
	<span class="n">height</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">buttonFont</span><span class="o">.</span><span class="n">Size</span><span class="p">()</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Dx</span><span class="p">()</span><span class="o">-</span><span class="n">width</span><span class="p">)</span><span class="o">/</span><span class="m">2</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span> <span class="o">+</span> <span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">()</span><span class="o">-</span><span class="n">height</span><span class="p">)</span><span class="o">/</span><span class="m">2</span>
	<span class="n">w</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">caption</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player10.jpg" alt="player10" /></p>

<h4 id="areaquad">Area.Quad</h4>

<p><a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.Quad"><code class="language-plaintext highlighter-rouge">Quad</code></a> draws a convex quadrilateral or a triangle described by the given vertices, either filled or unfilled. It draws a triangle if two adjacent vertices are identical.</p>

<p>Drawing a quadrilateral is almost as fast as drawing a triangle and much faster than drawing a quadrilateral using two triangles. The quadrilaterals are also more versatile and in many cases more convenient to use than triangles.</p>

<p>Let’s modify the buttons even more and replace the text with graphic symbols.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">gbutton</span><span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">Area</span><span class="p">,</span> <span class="n">r</span> <span class="n">image</span><span class="o">.</span><span class="n">Rectangle</span><span class="p">,</span> <span class="n">caption</span> <span class="p">[][</span><span class="m">4</span><span class="p">]</span><span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">r</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Inset</span><span class="p">(</span><span class="m">7</span><span class="p">)</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span><span class="o">--</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span><span class="o">--</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">ctrlColor</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">RoundRect</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="p">,</span> <span class="m">7</span><span class="p">,</span> <span class="m">7</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>
	<span class="n">p</span> <span class="o">:=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="p">)</span><span class="o">.</span><span class="n">Div</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">captionColor</span><span class="p">)</span>
	<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">q</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">caption</span> <span class="p">{</span>
		<span class="n">a</span><span class="o">.</span><span class="n">Quad</span><span class="p">(</span><span class="n">q</span><span class="p">[</span><span class="m">0</span><span class="p">]</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">p</span><span class="p">),</span> <span class="n">q</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">p</span><span class="p">),</span> <span class="n">q</span><span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">p</span><span class="p">),</span> <span class="n">q</span><span class="p">[</span><span class="m">3</span><span class="p">]</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">p</span><span class="p">),</span> <span class="no">true</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">var</span> <span class="n">rewind</span> <span class="o">=</span> <span class="p">[][</span><span class="m">4</span><span class="p">]</span><span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">{</span>
	<span class="p">{{</span><span class="m">15</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">15</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">1</span><span class="p">,</span> <span class="m">0</span><span class="p">},</span> <span class="p">{</span><span class="m">15</span><span class="p">,</span> <span class="m">7</span><span class="p">}},</span>
	<span class="p">{{</span><span class="o">-</span><span class="m">5</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="o">-</span><span class="m">5</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="o">-</span><span class="m">19</span><span class="p">,</span> <span class="m">0</span><span class="p">},</span> <span class="p">{</span><span class="o">-</span><span class="m">5</span><span class="p">,</span> <span class="m">7</span><span class="p">}},</span>
<span class="p">}</span>
<span class="k">var</span> <span class="n">playPause</span> <span class="o">=</span> <span class="p">[][</span><span class="m">4</span><span class="p">]</span><span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">{</span>
	<span class="p">{{</span><span class="o">-</span><span class="m">15</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="o">-</span><span class="m">15</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="o">-</span><span class="m">1</span><span class="p">,</span> <span class="m">0</span><span class="p">},</span> <span class="p">{</span><span class="o">-</span><span class="m">15</span><span class="p">,</span> <span class="m">7</span><span class="p">}},</span>
	<span class="p">{{</span><span class="m">6</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">9</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">9</span><span class="p">,</span> <span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">6</span><span class="p">,</span> <span class="m">7</span><span class="p">}},</span>
	<span class="p">{{</span><span class="m">14</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">17</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">17</span><span class="p">,</span> <span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">14</span><span class="p">,</span> <span class="m">7</span><span class="p">}},</span>
<span class="p">}</span>
<span class="k">var</span> <span class="n">fastForward</span> <span class="o">=</span> <span class="p">[][</span><span class="m">4</span><span class="p">]</span><span class="n">image</span><span class="o">.</span><span class="n">Point</span><span class="p">{</span>
	<span class="p">{{</span><span class="o">-</span><span class="m">15</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="o">-</span><span class="m">15</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="o">-</span><span class="m">1</span><span class="p">,</span> <span class="m">0</span><span class="p">},</span> <span class="p">{</span><span class="o">-</span><span class="m">15</span><span class="p">,</span> <span class="m">7</span><span class="p">}},</span>
	<span class="p">{{</span><span class="m">5</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">5</span><span class="p">,</span> <span class="o">-</span><span class="m">7</span><span class="p">},</span> <span class="p">{</span><span class="m">19</span><span class="p">,</span> <span class="m">0</span><span class="p">},</span> <span class="p">{</span><span class="m">5</span><span class="p">,</span> <span class="m">7</span><span class="p">}},</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">drawControls</span><span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">Area</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">r</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
	<span class="n">width</span> <span class="o">:=</span> <span class="n">r</span><span class="o">.</span><span class="n">Dx</span><span class="p">()</span> <span class="o">/</span> <span class="m">3</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="n">width</span> <span class="o">-</span> <span class="m">3</span>
	<span class="n">gbutton</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">rewind</span><span class="p">)</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="m">3</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="n">width</span> <span class="o">-</span> <span class="m">3</span>
	<span class="n">gbutton</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">playPause</span><span class="p">)</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="m">3</span>
	<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">X</span> <span class="o">+</span> <span class="n">width</span>
	<span class="n">gbutton</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">fastForward</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player11.jpg" alt="player11" /></p>

<p>The buttons drawn in this way don’t look the best mainly because of the flat appearance and lack of anti-aliasing. Image based buttons would look much better.</p>

<h4 id="areafillquad">Area.FillQuad</h4>

<p><a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.FillQuad"><code class="language-plaintext highlighter-rouge">FillQuad</code></a> draws a filled convex quadrilateral or triangle. The difference to <code class="language-plaintext highlighter-rouge">Quad</code> is that <code class="language-plaintext highlighter-rouge">FillQuad</code> obeys the filling rules so it can be used to draw filled polygons composed of adjacent quadrilaterals and triangles, ensuring that the common edges are drawn once. In this regard, it’s somehow similar to the <code class="language-plaintext highlighter-rouge">Fill</code> function which fills a rectangle defined as inclusive at the top-left and exclusive at the bottom-right.</p>

<h4 id="areaarc">Area.Arc</h4>

<p><a href="https://pkg.go.dev/github.com/embeddedgo/display/pix#Area.FillQuad"><code class="language-plaintext highlighter-rouge">Arc</code></a> is the last function left to describe. It’s most complex one but very useful to present data.</p>

<p>The most obvious use case is a <a href="https://en.wikipedia.org/wiki/Pie_chart">Pie Chart</a>. Let’s create one for 15%, 25%, 60% data.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">disp</span> <span class="o">:=</span> <span class="n">newDisplay</span><span class="p">(</span><span class="m">640</span><span class="p">,</span> <span class="m">320</span><span class="p">)</span>
<span class="n">a</span> <span class="o">:=</span> <span class="n">disp</span><span class="o">.</span><span class="n">NewArea</span><span class="p">(</span><span class="n">disp</span><span class="o">.</span><span class="n">Bounds</span><span class="p">())</span>
<span class="n">p</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Div</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>

<span class="k">var</span> <span class="n">alpha</span> <span class="kt">int64</span>

<span class="n">alpha</span> <span class="o">=</span> <span class="n">math2d</span><span class="o">.</span><span class="n">FullAngle</span> <span class="o">*</span> <span class="m">15</span> <span class="o">/</span> <span class="m">100</span> <span class="c">// angle corresponding to 15%</span>
<span class="n">th0</span> <span class="o">:=</span> <span class="o">-</span><span class="n">math2d</span><span class="o">.</span><span class="n">RightAngle</span> <span class="o">/</span> <span class="m">3</span>       <span class="c">// start angle of the first piece (any)</span>
<span class="n">th1</span> <span class="o">:=</span> <span class="n">th0</span> <span class="o">+</span> <span class="kt">int32</span><span class="p">(</span><span class="n">alpha</span><span class="p">)</span>           <span class="c">// end angle of the first piece</span>
<span class="n">a</span><span class="o">.</span><span class="n">SetColorRGBA</span><span class="p">(</span><span class="m">200</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">200</span><span class="p">)</span>
<span class="n">a</span><span class="o">.</span><span class="n">Arc</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th1</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>

<span class="n">alpha</span> <span class="o">=</span> <span class="n">math2d</span><span class="o">.</span><span class="n">FullAngle</span> <span class="o">*</span> <span class="m">25</span> <span class="o">/</span> <span class="m">100</span> <span class="c">// angle corresponding to 25%</span>
<span class="n">th0</span> <span class="o">=</span> <span class="n">th1</span>                           <span class="c">// start angle of the second piece</span>
<span class="n">th1</span> <span class="o">=</span> <span class="n">th0</span> <span class="o">+</span> <span class="kt">int32</span><span class="p">(</span><span class="n">alpha</span><span class="p">)</span>            <span class="c">// end angle of the second piece</span>
<span class="n">a</span><span class="o">.</span><span class="n">SetColorRGBA</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">200</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">200</span><span class="p">)</span>
<span class="n">a</span><span class="o">.</span><span class="n">Arc</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th1</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>

<span class="n">alpha</span> <span class="o">=</span> <span class="n">math2d</span><span class="o">.</span><span class="n">FullAngle</span> <span class="o">*</span> <span class="m">60</span> <span class="o">/</span> <span class="m">100</span> <span class="c">// angle corresponding to 60%</span>
<span class="n">th0</span> <span class="o">=</span> <span class="n">th1</span>
<span class="n">th1</span> <span class="o">=</span> <span class="n">th0</span> <span class="o">+</span> <span class="kt">int32</span><span class="p">(</span><span class="n">alpha</span><span class="p">)</span>
<span class="n">a</span><span class="o">.</span><span class="n">SetColorRGBA</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">200</span><span class="p">,</span> <span class="m">200</span><span class="p">)</span>
<span class="n">a</span><span class="o">.</span><span class="n">Arc</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th1</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/piechart1.png" alt="piechart1" /></p>

<p><code class="language-plaintext highlighter-rouge">Arc</code> draws an arc starting at angle <code class="language-plaintext highlighter-rouge">th0</code> and ending at, but not including, <code class="language-plaintext highlighter-rouge">th1</code>. Such one-side inclusiveness ensures that the adjacent edges of our pie chart are drawn once. The colors with alpha=200 allow us to confirm this easily.</p>

<p>In <code class="language-plaintext highlighter-rouge">pix</code> and <a href="https://pkg.go.dev/github.com/embeddedgo/display/math2d"><code class="language-plaintext highlighter-rouge">math2d</code></a> packages the angles are of type <code class="language-plaintext highlighter-rouge">int32</code> which can represent angles from <code class="language-plaintext highlighter-rouge">-FullAngle/2</code> to <code class="language-plaintext highlighter-rouge">FullAngle/2 - 1</code> (inclusive). The angle unit is equal to 1/4294967296 of the full angle.</p>

<p>Two well known angles are declared in <code class="language-plaintext highlighter-rouge">math2d</code> this way:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="p">(</span>
	<span class="n">FullAngle</span>        <span class="o">=</span> <span class="m">1</span> <span class="o">&lt;&lt;</span> <span class="m">32</span>       <span class="c">// 2π rad = 360°</span>
	<span class="n">RightAngle</span> <span class="kt">int32</span> <span class="o">=</span> <span class="n">FullAngle</span> <span class="o">/</span> <span class="m">4</span> <span class="c">// π/2 rad = 90°</span>
<span class="p">)</span>
</code></pre></div></div>

<p>As you can see, <code class="language-plaintext highlighter-rouge">FullAngle</code> and even <code class="language-plaintext highlighter-rouge">FullAngle/2</code> doesn’t fit in <code class="language-plaintext highlighter-rouge">int32</code>. As the full range of <code class="language-plaintext highlighter-rouge">int32</code> is used to represent angles from -180° to 180° the calculations on angles aren’t obvious:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">theta</span> <span class="o">:=</span> <span class="n">math2d</span><span class="o">.</span><span class="n">RightAngle</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">theta</span> <span class="o">*</span> <span class="m">4</span><span class="p">)</span> <span class="c">// prints 0</span>
</code></pre></div></div>

<p>The compiler will warn you on problematic constant angles but the correctness of the calculations on the angle variables is up to you. For example, computing the mean of two angles <code class="language-plaintext highlighter-rouge">(th0 + th1) / 2</code> may give unexpected result. If you don’t feel confident you can use <code class="language-plaintext highlighter-rouge">int64</code> for calculations which may help a bit.</p>

<p>Let’s go back to our examples. Sometimes slightly spaced pieces of a pie chart look better. We can move them around 10 points from the center by replacing</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">a</span><span class="o">.</span><span class="n">Arc</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th1</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>
</code></pre></div></div>

<p>with the code below</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">o0</span> <span class="o">:=</span> <span class="n">image</span><span class="o">.</span><span class="n">Pt</span><span class="p">(</span><span class="m">10</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>
<span class="n">o</span> <span class="o">:=</span> <span class="n">math2d</span><span class="o">.</span><span class="n">Rotate</span><span class="p">(</span><span class="n">o0</span><span class="p">,</span> <span class="n">th0</span><span class="o">+</span><span class="kt">int32</span><span class="p">(</span><span class="n">alpha</span><span class="o">/</span><span class="m">2</span><span class="p">))</span>
<span class="n">a</span><span class="o">.</span><span class="n">Arc</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">o</span><span class="p">),</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th1</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>
</code></pre></div></div>
<p><img src="/images/pix_a_minimalistic_graphic_library/piechart2.png" alt="piechart2" /></p>

<p>By replacing</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">a</span><span class="o">.</span><span class="n">Arc</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">o</span><span class="p">),</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th1</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>
</code></pre></div></div>

<p>with</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">a</span><span class="o">.</span><span class="n">Arc</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">o</span><span class="p">),</span> <span class="m">80</span><span class="p">,</span> <span class="m">60</span><span class="p">,</span> <span class="m">200</span><span class="p">,</span> <span class="m">150</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th1</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>
</code></pre></div></div>

<p>we get an interesting looking elliptical pie chart with the center part cut out.</p>

<p><img src="/images/pix_a_minimalistic_graphic_library/piechart3.png" alt="piechart3" /></p>

<p>Let’s make the last change to our music player example. Let’s use the <code class="language-plaintext highlighter-rouge">Arc</code> function to present the current playback position graphically.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">printTime</span><span class="p">(</span><span class="n">w</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">TextWriter</span><span class="p">,</span> <span class="n">t</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">h</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">s</span> <span class="o">:=</span> <span class="n">hms</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
	<span class="n">str</span> <span class="o">:=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%02d:%02d:%02d"</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span>
	<span class="n">width</span> <span class="o">:=</span> <span class="n">pix</span><span class="o">.</span><span class="n">StringWidth</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">titleFont</span><span class="p">)</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">X</span> <span class="o">-=</span> <span class="n">width</span> <span class="o">/</span> <span class="m">2</span>
	<span class="n">w</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">drawTimeDuration</span><span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">pix</span><span class="o">.</span><span class="n">Area</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">duration</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">r</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">bgColor</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Fill</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>

	<span class="n">p</span> <span class="o">:=</span> <span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="p">)</span><span class="o">.</span><span class="n">Div</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>
	<span class="n">rmax</span> <span class="o">:=</span> <span class="n">min</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Dx</span><span class="p">(),</span> <span class="n">r</span><span class="o">.</span><span class="n">Dy</span><span class="p">())</span> <span class="o">/</span> <span class="m">2</span>
	<span class="n">rmin</span> <span class="o">:=</span> <span class="n">rmax</span> <span class="o">-</span> <span class="m">20</span>
	<span class="n">th0</span> <span class="o">:=</span> <span class="n">math2d</span><span class="o">.</span><span class="n">RightAngle</span> <span class="o">+</span> <span class="n">math2d</span><span class="o">.</span><span class="n">RightAngle</span><span class="o">/</span><span class="m">3</span>
	<span class="n">th1</span> <span class="o">:=</span> <span class="n">math2d</span><span class="o">.</span><span class="n">RightAngle</span> <span class="o">-</span> <span class="n">math2d</span><span class="o">.</span><span class="n">RightAngle</span><span class="o">/</span><span class="m">3</span>
	<span class="n">a</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">ctrlColor</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Arc</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">rmin</span><span class="p">,</span> <span class="n">rmin</span><span class="p">,</span> <span class="n">rmax</span><span class="p">,</span> <span class="n">rmax</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th1</span><span class="p">,</span> <span class="no">false</span><span class="p">)</span>
	<span class="k">if</span> <span class="n">t</span> <span class="o">&gt;</span> <span class="m">0</span> <span class="p">{</span>
		<span class="n">alpha</span> <span class="o">:=</span> <span class="kt">int64</span><span class="p">(</span><span class="n">math2d</span><span class="o">.</span><span class="n">FullAngle</span><span class="o">*</span><span class="m">5</span><span class="o">/</span><span class="m">6</span><span class="p">)</span> <span class="o">*</span> <span class="kt">int64</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="o">/</span> <span class="kt">int64</span><span class="p">(</span><span class="n">duration</span><span class="p">)</span>
		<span class="n">a</span><span class="o">.</span><span class="n">Arc</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">rmin</span><span class="p">,</span> <span class="n">rmin</span><span class="p">,</span> <span class="n">rmax</span><span class="p">,</span> <span class="n">rmax</span><span class="p">,</span> <span class="n">th0</span><span class="p">,</span> <span class="n">th0</span><span class="o">+</span><span class="kt">int32</span><span class="p">(</span><span class="n">alpha</span><span class="p">),</span> <span class="no">true</span><span class="p">)</span>
	<span class="p">}</span>

	<span class="n">w</span> <span class="o">:=</span> <span class="n">a</span><span class="o">.</span><span class="n">NewTextWriter</span><span class="p">(</span><span class="n">titleFont</span><span class="p">)</span>
	<span class="n">w</span><span class="o">.</span><span class="n">SetColor</span><span class="p">(</span><span class="n">textColor</span><span class="p">)</span>
	<span class="n">height</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">titleFont</span><span class="o">.</span><span class="n">Size</span><span class="p">()</span>

	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">X</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">Y</span><span class="o">/</span><span class="m">2</span> <span class="o">-</span> <span class="n">height</span><span class="o">/</span><span class="m">2</span>
	<span class="n">printTime</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">t</span><span class="p">)</span>

	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">X</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">X</span>
	<span class="n">w</span><span class="o">.</span><span class="n">Pos</span><span class="o">.</span><span class="n">Y</span> <span class="o">+=</span> <span class="n">p</span><span class="o">.</span><span class="n">Y</span>
	<span class="n">printTime</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">duration</span><span class="p">)</span>

	<span class="c">// play button</span>
	<span class="n">p0</span> <span class="o">:=</span> <span class="n">image</span><span class="o">.</span><span class="n">Pt</span><span class="p">(</span><span class="o">-</span><span class="m">14</span><span class="p">,</span> <span class="o">-</span><span class="m">14</span><span class="p">)</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
	<span class="n">p1</span> <span class="o">:=</span> <span class="n">image</span><span class="o">.</span><span class="n">Pt</span><span class="p">(</span><span class="m">14</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
	<span class="n">p2</span> <span class="o">:=</span> <span class="n">image</span><span class="o">.</span><span class="n">Pt</span><span class="p">(</span><span class="o">-</span><span class="m">14</span><span class="p">,</span> <span class="m">14</span><span class="p">)</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">Quad</span><span class="p">(</span><span class="n">p0</span><span class="p">,</span> <span class="n">p0</span><span class="p">,</span> <span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="no">true</span><span class="p">)</span>

	<span class="n">a</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">playerView</code> function also requires some changes:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">w</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">artist</span> <span class="o">+</span> <span class="s">" -- "</span> <span class="o">+</span> <span class="n">title</span><span class="p">)</span>
<span class="c">// new code begin</span>
<span class="n">r</span><span class="o">.</span><span class="n">Min</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span> <span class="o">+</span> <span class="n">margin</span><span class="o">/</span><span class="m">2</span>
<span class="n">r</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span> <span class="o">=</span> <span class="n">disp</span><span class="o">.</span><span class="n">Bounds</span><span class="p">()</span><span class="o">.</span><span class="n">Max</span><span class="o">.</span><span class="n">Y</span> <span class="o">-</span> <span class="n">margin</span><span class="o">/</span><span class="m">2</span>
<span class="n">drawTimeDuration</span><span class="p">(</span><span class="n">disp</span><span class="o">.</span><span class="n">NewArea</span><span class="p">(</span><span class="n">r</span><span class="p">),</span> <span class="m">58</span><span class="p">,</span> <span class="m">3</span><span class="o">*</span><span class="m">60</span><span class="o">+</span><span class="m">17</span><span class="p">)</span>
<span class="c">// new code end</span>
<span class="n">disp</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="/images/pix_a_minimalistic_graphic_library/player12.jpg" alt="player12" /></p>

<p>We’ve reduced the number of buttons to just one. The function of the <em>FastForward</em> and <em>Rewind</em> buttons can be replaced by the playback position indicator.</p>

<p>This is where we end the first part of the article about the pix library. In the second part, we’ll get our hands dirty with some real hardware.</p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="go" /><category term="golang" /><category term="embeddedgo" /><category term="graphic" /><summary type="html"><![CDATA[]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://embeddedgo.github.io/images/pix_a_minimalistic_graphic_library/pix.jpg" /><media:content medium="image" url="https://embeddedgo.github.io/images/pix_a_minimalistic_graphic_library/pix.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Playing with Go schedulers on a dual-core RISC-V</title><link href="https://embeddedgo.github.io/2020/06/21/playing_with_go_schedulers_on_a_dual-core_risc-v.html" rel="alternate" type="text/html" title="Playing with Go schedulers on a dual-core RISC-V" /><published>2020-06-21T00:00:00+00:00</published><updated>2020-06-21T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2020/06/21/playing_with_go_schedulers_on_a_dual-core_risc-v</id><content type="html" xml:base="https://embeddedgo.github.io/2020/06/21/playing_with_go_schedulers_on_a_dual-core_risc-v.html"><![CDATA[<p><img src="/images/playing_with_go_schedulers_on_a_dual-core_risc-v/dual-core_go.png" alt="Kendryte &amp; Go" /></p>

<!--more-->

<p>You can port the Go runtime to a system that doesn’t implement threads. An example would be the current <a href="https://en.wikipedia.org/wiki/WebAssembly">WebAssembly</a> port.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">newosproc</span><span class="p">(</span><span class="n">mp</span> <span class="o">*</span><span class="n">m</span><span class="p">)</span> <span class="p">{</span>
	<span class="nb">panic</span><span class="p">(</span><span class="s">"newosproc: not implemented"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>But if you want to run a bare metal Go program on multiple cores the thread abstraction is a must, unless you are ready to implement a completely new goroutine scheduler.</p>

<p>The goroutine scheduler uses operating system threads as workhorses to execute its goroutines. The goal is to efficiently run thousands of goroutines using only a few OS threads. Threads are considered expensive. There is also much cheaper to access shared resources by multiple goroutines that run on the same thread. This is further optimized in Go by introducing the concept of a logical processor (called P) that can “execute” only one thread at a time and has local cache of the most commonly used resources. There can be unlimited number of threads sleeping in the system calls but they aren’t assigned to any P.</p>

<p>You can set a number of logical processors using <a href="https://golang.org/pkg/runtime">GOMAXPROCS</a> environment variable or <a href="https://golang.org/pkg/runtime/#GOMAXPROCS">runtime.GOMAXPROCS</a> function. The default GOMAXPROCS for <a href="/2020/05/31/bare_metal_programming_risc-v_in_go.html">Kendryte K210</a> is 2.</p>

<h4 id="tasker">Tasker</h4>

<p>The Embedded Go implements a thread scheduler for GOOS=noos called <a href="https://github.com/embeddedgo/go/blob/embedded/src/runtime/tasker_noos.go">tasker</a>. Tasker was designed from the very beginning as a multi-core scheduler but the first multicore tests and bug fixes were done on K210 while working on <em>noos/riscv64</em> port.</p>

<p>Tasker is tightly coupled to the <a href="https://github.com/golang/go/blob/release-branch.go1.14/src/runtime/proc.go">goroutine scheduler</a>. It doesn’t have it’s own representation of thread. Instead it directly uses the <a href="https://github.com/golang/go/blob/release-branch.go1.14/src/runtime/runtime2.go#L473">m structs</a>. The Go logical processor concept is taken seriously. Any available P is associated with a specific CPU using the following simple formula:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cpuid</span> <span class="o">=</span> <span class="n">P</span><span class="o">.</span><span class="n">id</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">allcpus</span><span class="p">)</span>
</code></pre></div></div>

<p>CPU is the name used by the tasker for any independent hardware thread of execution, a <em>hart</em> in the <a href="https://en.wikipedia.org/wiki/RISC-V">RISC-V</a> nomenclature.</p>

<p>The tasker threads are cheap.</p>

<h4 id="print-hartid">Print hartid</h4>

<p>Let’s start playing with Go schedulers and two cores available in K210. The basic tool we need is a function that returns the current hart id which in case of K210 means the core id.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="n">_</span> <span class="s">"github.com/embeddedgo/kendryte/devboard/maixbit/board/init"</span>

<span class="k">func</span> <span class="n">hartid</span><span class="p">()</span> <span class="kt">int</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="nb">print</span><span class="p">(</span><span class="n">hartid</span><span class="p">())</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As you can see the <em>hartid</em> function has no body. To define it we have to reach for <a href="https://golang.org/doc/asm">Go assembly</a>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#include "textflag.h"

#define CSRR(CSR,RD) WORD $(0x2073 + RD&lt;&lt;7 + CSR&lt;&lt;20)
#define mhartid 0xF14
#define s0 8

// func hartid() int
TEXT ·hartid(SB),NOSPLIT|NOFRAME,$0
	CSRR  (mhartid, s0)
	MOV   S0, ret+0(FP)
	RET
</code></pre></div></div>

<p>The Go assembler doesn’t recognize privileged instructions so we used macros to implement the <em>CSRR</em> instruction.</p>

<p>Let’s use <em>GDB+OpenOCD</em> to load and run the compiled program. I recommend using the <a href="https://github.com/sysprogs/openocd-kendryte/">modified version</a> of the <a href="https://github.com/kendryte/openocd-kendryte">openocd-kendryte</a>. You can use the <a href="https://github.com/embeddedgo/scripts/blob/master/debug-oocd.sh">debug-oocd.sh</a> helper script as shown in the <a href="https://github.com/embeddedgo/kendryte/blob/master/devboard/maixbit/examples/debug-oocd.sh">maixbit example</a>. GDB isn’t required to follow this article. You can use <em>kflash</em> utility instead as described in the <a href="/2020/05/31/bare_metal_programming_risc-v_in_go.html">previous article</a>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Core [0] halted at 0x8000bb4c due to debug interrupt
Core [1] halted at 0x800093ea due to debug interrupt
(gdb) load
Loading section .text, size 0x62230 lma 0x80000000
Loading section .rodata, size 0x2c80f lma 0x80062240
Loading section .typelink, size 0x658 lma 0x8008ec20
Loading section .itablink, size 0x18 lma 0x8008f278
Loading section .gopclntab, size 0x3df15 lma 0x8008f2a0
Loading section .go.buildinfo, size 0x20 lma 0x800cd1b8
Loading section .noptrdata, size 0xf00 lma 0x800cd1d8
Loading section .data, size 0x3f0 lma 0x800ce0d8
Start address 0x80000000, load size 844500
Transfer rate: 64 KB/sec, 14313 bytes/write.
(gdb) c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
runtime.defaultHandler ()
    at /home/michal/embeddedgo/go/src/runtime/tasker_noos_riscv64.s:388
(gdb)
</code></pre></div></div>

<p>As you can see our program has been stopped in <code class="language-plaintext highlighter-rouge">runtime.defaultHandler</code>. This function handles unsupported traps (there are still a lot of them). Let’s see what happened.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(gdb) p $a0/8
$1 = 2
</code></pre></div></div>

<p>The A0 register contains a value of the <em>mcause</em> CSR saved at the trap entry (multiplied by 8). We can’t rely on the current mcause value because the interrupts are enabled. Bu we can check if it’s the same.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(gdb) p $mcause
$2 = 2
</code></pre></div></div>

<p>It seems there was no other traps in the meantime. The <em>mcause</em> CSR contains a code indicating the event that caused the trap. In our case it’s <em>Illegal instruction exception</em>. Let’s see what this illegal instruction is. The <em>mepc</em> register (return address from trap) was saved on the stack.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(gdb) x $sp+24
0x800d4820:     0x80062221
</code></pre></div></div>

<p>As before we can check does it’s the same as the current one.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(gdb) p/x $mepc
$2 = 0x80062220
</code></pre></div></div>

<p>Almost the same (LSBit is used to store <em>fromThread</em> flag).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(gdb) list *0x80062220
0x80062220 is in main.hartid (/home/michal/embeddedgo/kendryte/devboard/maixbit/examples/multicore/asm.s:9).
4       #define mhartid 0xF14
5       #define s0 8
6
7       // func hartid() int
8       TEXT ·hartid(SB),NOSPLIT|NOFRAME,$0
9               CSRR  (mhartid, s0)
10              MOV   S0, ret+0(FP)
11              RET
12
13      // func  loop(n int)
</code></pre></div></div>

<p>All clear. Our program runs in the RISC-V <em>user mode</em>. We have no access to the <em>machine mode</em> CSRs. But there is a way to tackle this problem.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">runtime</span><span class="o">.</span><span class="n">LockOSThread</span><span class="p">()</span>
	<span class="n">rtos</span><span class="o">.</span><span class="n">SetPrivLevel</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="nb">print</span><span class="p">(</span><span class="n">hartid</span><span class="p">())</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <em>rtos.SetPrivLevel</em> function can be used to change the privilege level for the current thread . As it affects the current thread only we must call <em>runtime.LockOSThread</em> first to wire our goroutine to its current thread (no other goroutine will execute in this thread). Now we can run our program.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/playing_with_go_schedulers_on_a_dual-core_risc-v/zeros.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>As you can see our printing thread is locked to hart 0.</p>

<h4 id="multiple-threads">Multiple threads</h4>

<p>Let’s modify the previous code in a way that allows us to easily alter the number of threads.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"embedded/rtos"</span>
	<span class="s">"runtime"</span>

	<span class="n">_</span> <span class="s">"github.com/embeddedgo/kendryte/devboard/maixbit/board/init"</span>
<span class="p">)</span>

<span class="k">type</span> <span class="n">report</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">tid</span><span class="p">,</span> <span class="n">hartid</span> <span class="kt">int</span>
<span class="p">}</span>

<span class="k">var</span> <span class="n">ch</span> <span class="o">=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="n">report</span><span class="p">,</span> <span class="m">3</span><span class="p">)</span>

<span class="k">func</span> <span class="n">thread</span><span class="p">(</span><span class="n">tid</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">runtime</span><span class="o">.</span><span class="n">LockOSThread</span><span class="p">()</span>
	<span class="n">rtos</span><span class="o">.</span><span class="n">SetPrivLevel</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="n">ch</span> <span class="o">&lt;-</span> <span class="n">report</span><span class="p">{</span><span class="n">tid</span><span class="p">,</span> <span class="n">hartid</span><span class="p">()}</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">var</span> <span class="n">lasthart</span> <span class="p">[</span><span class="m">2</span><span class="p">]</span><span class="kt">int</span>
	<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">lasthart</span> <span class="p">{</span>
		<span class="k">go</span> <span class="n">thread</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="n">runtime</span><span class="o">.</span><span class="n">LockOSThread</span><span class="p">()</span>
	<span class="n">rtos</span><span class="o">.</span><span class="n">SetPrivLevel</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
	<span class="k">for</span> <span class="n">r</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">ch</span> <span class="p">{</span>
		<span class="n">lasthart</span><span class="p">[</span><span class="n">r</span><span class="o">.</span><span class="n">tid</span><span class="p">]</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">hartid</span>
		<span class="nb">print</span><span class="p">(</span><span class="s">" "</span><span class="p">,</span> <span class="n">hartid</span><span class="p">())</span>
		<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">hid</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">lasthart</span> <span class="p">{</span>
			<span class="nb">print</span><span class="p">(</span><span class="s">" "</span><span class="p">,</span> <span class="n">hid</span><span class="p">)</span>
		<span class="p">}</span>
		<span class="nb">println</span><span class="p">()</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">hartid</span><span class="p">()</span> <span class="kt">int</span>
</code></pre></div></div>

<p>Now the main function launches len(lasthart) goroutines and after that prints in a loop the hartid for itself and all other goroutines. Every launched goroutine periodically checks its hartid and sends report to the main goroutine.</p>

<p>Let’s start with main + 2 goroutines.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/playing_with_go_schedulers_on_a_dual-core_risc-v/main+2.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>You can see we have a stable state: the main goroutine runs on hart 1, the reporting goroutines run on hart 0. Let’s add more goroutines.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/playing_with_go_schedulers_on_a_dual-core_risc-v/main+30.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>The beginning looks interesting:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
</code></pre></div></div>

<p>It seems that almost all reporting goroutines start on hart 0 but after a while
they migrate to hart 1 and stay there. <em>Edit. It doesn’t have to be true because the lasthart array is zero initialized. It should be initialized to something
other value, eg: -1.</em></p>

<p>Remember the goroutine scheduler can’t run more than 2 goroutines at the same time. Our reporting goroutines don’t do much. They spend most of their time sleeping on the full channel. It seems reasonable to gather them all on one P and give the other P for busy main thread.</p>

<p>Let’s increase the number of logical processors by adding <code class="language-plaintext highlighter-rouge">runtime.GOMAXPROCS(4)</code> at the beginning of the main function.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/playing_with_go_schedulers_on_a_dual-core_risc-v/main+30_4p.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>It seems the goroutine scheduler cannot reach a stable state. But we can see the hart id only. Can we see also the logical processor id? Yes, we can. Let’s modify the hartid function to return both.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// func hartid() int
TEXT ·hartid(SB),NOSPLIT|NOFRAME,$0
	CSRR  (mhartid, s0)
	MOV   48(g), A0    // g.m
	MOV   160(A0), A0  // m.p
	MOVW  (A0), S1     // p.id
	SLL   $8, S1
	OR    S1, S0
	MOV   S0, ret+0(FP)
	RET
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">print(" ", hartid())</code> call has been changed to <code class="language-plaintext highlighter-rouge">print(" ", hid&gt;&gt;8, hid&amp;0xFF)</code> to show both numbers next to each other.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/playing_with_go_schedulers_on_a_dual-core_risc-v/main+30_4p_ph.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>As you can see the goroutine scheduler keeps the main goroutine on P=0,1 and reporting goroutines on P=2,3. Our simple rule that maps Ps to CPUs causes threads to jumping between K210 cores.</p>

<p>Ending this article let’s get back to two Ps but let’s give our reporting goroutines something to do. As we’ve already got some practice with Go assembly we will use it to write simple busy loop. Thanks to this we’ll be sure the compiler won’t optimize this code.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// func loop(n int)
TEXT ·loop(SB),NOSPLIT|NOFRAME,$0
	MOV  n+0(FP), S0
	BEQ  ZERO, S0, end
	ADD  $-1, S0
	BNE  ZERO, S0, -1(PC)
end:
	RET
</code></pre></div></div>

<p>You can find the full code for this last case on <a href="https://github.com/embeddedgo/kendryte/tree/master/devboard/maixbit/examples/multicore">github</a>. You can play with other things, like the channel length, the loop count, odd GOMAXPROCS values, etc.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/playing_with_go_schedulers_on_a_dual-core_risc-v/main+30loop_ph.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>Workload disturbs the stable state from the second example. We can observe quite long periods when all goroutines run on the same logical processor which may be disturbing. <em>Edit: It doesn’t have to be true. We print the whole line after each report received so it takes some lines to print a global change which affects many/all goroutines</em>.</p>

<h4 id="summary">Summary</h4>

<p>It’s hard to draw any deeper conclusions from these superficial tests. It wasn’t the purpose of this article. We had some fun with Go, RISC-V assembler, debugger and underlying hardware which is what you can expect from bare-metal programming. It seems the goroutine scheduler and tasker both work in harmony with each other. A more strict approach is needed to draw more definitive conclusions that can be used to improve one or the other.</p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="go" /><category term="golang" /><category term="embeddedgo" /><category term="risc-v" /><category term="kendryte" /><category term="k210" /><summary type="html"><![CDATA[]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://embeddedgo.github.io/images/playing_with_go_schedulers_on_a_dual-core_risc-v/dual-core_go.png" /><media:content medium="image" url="https://embeddedgo.github.io/images/playing_with_go_schedulers_on_a_dual-core_risc-v/dual-core_go.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Bare metal RISC-V programming in Go</title><link href="https://embeddedgo.github.io/2020/05/31/bare_metal_programming_risc-v_in_go.html" rel="alternate" type="text/html" title="Bare metal RISC-V programming in Go" /><published>2020-05-31T00:00:00+00:00</published><updated>2020-05-31T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2020/05/31/bare_metal_programming_risc-v_in_go</id><content type="html" xml:base="https://embeddedgo.github.io/2020/05/31/bare_metal_programming_risc-v_in_go.html"><![CDATA[<p><img src="/images/bare_metal_risc-v_programming_in_go/kendryte_go.png" alt="Kendryte &amp; Go" /></p>

<!--more-->

<p>For a couple of weeks I’ve worked on adding support for bare metal RISC-V programming to Go. As the noos/riscv64 port reached the usable level I’ve decided to write something about it.</p>

<p>Until now, the <a href="https://github.com/embeddedgo">Embedded Go</a> have supported only ARM microcontrollers. I wanted to add another architecture as soon as possible to revise the design choices. From very beggining I’ve considered to add support for the very popular WiFi capable <a href="https://www.espressif.com/en/products/socs/esp32/overview">ESP32</a> microcontroller and even finished studying the Xtensa LX6 ISA but in the meantime two things happened:</p>

<ul>
  <li>
    <p>the support for linux/riscv64 <a href="https://golang.org/doc/go1.14#riscv">landed in Go compiler</a>,</p>
  </li>
  <li>
    <p>the Siped <a href="https://maixduino.sipeed.com/en/hardware/board.html">Maix Bit</a> landed in my hands:</p>
  </li>
</ul>

<p><img src="/images/mcu/kendryte/maix_bit.jpg" alt="Sipeed Maix Bit" /></p>

<p>As I’ve already ported Go to a new architecture (ARMv7-M Thumb2 ISA) I know how much work it costs. The ready to use RISC-V port and the access to the real hardware left no choice.</p>

<h4 id="kendryte-k210">Kendryte K210</h4>

<p>If you want to play with real RISC-V hardware you don’t have much choice today. I’m aware of three sources:</p>

<ul>
  <li>
    <p><a href="https://www.sifive.com/">SiFive</a>, the RISC-V pioneer with they rather expensive <a href="https://www.sifive.com/boards">development boards</a>,</p>
  </li>
  <li>
    <p><a href="https://www.gigadevice.com/">GigaDevice</a>, they provide very cheap RISC-V based <a href="https://www.gigadevice.com/products/microcontrollers/gd32/risc-v/">GD32V microcontrollers</a> (development boards are provided by <a href="https://www.seeedstudio.com/SeeedStudio-GD32-RISC-V-Dev-Board-p-4302.html">SeedStudio</a>, <a href="https://longan.sipeed.com/en/">Sipeed</a> and others),</p>
  </li>
  <li>
    <p>and finally <a href="https://kendryte.com/">Kendryte</a> (<a href="https://www.canaan.io/">Canaan</a>) with their <a href="https://s3.cn-north-1.amazonaws.com.cn/dl.kendryte.com/documents/kendryte_datasheet_20181011163248_en.pdf">K210</a> chip and development boards/modules provided by <a href="https://www.sipeed.com/">Sipeed</a>.</p>
  </li>
</ul>

<p>You can buy the Maix Bit board as seen above for about $13 or the whole kit with camera and LCD for about $21.</p>

<p>The heart of the Maix Bit is K210, an AI capable SOC from Kendryte. It’s equipped with two <a href="https://en.wikipedia.org/wiki/RISC-V#ISA_base_and_extensions">RV64G</a> cores and over a dozen peripherals of which the convolutional neural network accelerator (KPU) deserves special attention.</p>

<p><img src="/images/mcu/kendryte/k210.png" alt="Sipeed Maix Bit" /></p>

<p>The good news is the 8 MB of integrated SRAM divided into 6 MB general purpose memory (plenty of space for most Go programs) and 2 MB dedicated to KPU. There is no integrated Flash. The program is loaded from external SPI Flash and runs from RAM.</p>

<h4 id="getting-started-with-maix-bit">Getting started with Maix Bit</h4>

<p>The Maix Bit comes with the preinstaled <a href="http://micropython.org/">MicroPython</a> port called <a href="https://maixpy.sipeed.com/en/">MaixPy</a>. A good starting point will be to blink an onboard LED using MaixPy and next do the same with Go.</p>

<p>Let’s connect the board to the PC and paste the following code to MaixPy.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">sleep</span>
<span class="kn">from</span> <span class="nn">Maix</span> <span class="kn">import</span> <span class="n">GPIO</span>

<span class="n">fm</span><span class="p">.</span><span class="n">register</span><span class="p">(</span><span class="n">board_info</span><span class="p">.</span><span class="n">LED_B</span><span class="p">,</span> <span class="n">fm</span><span class="p">.</span><span class="n">fpioa</span><span class="p">.</span><span class="n">GPIO0</span><span class="p">)</span>
<span class="n">led</span> <span class="o">=</span> <span class="n">GPIO</span><span class="p">(</span><span class="n">GPIO</span><span class="p">.</span><span class="n">GPIO0</span><span class="p">,</span> <span class="n">GPIO</span><span class="p">.</span><span class="n">OUT</span><span class="p">)</span>

<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
	<span class="n">led</span><span class="p">.</span><span class="n">value</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
	<span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
	<span class="n">led</span><span class="p">.</span><span class="n">value</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
	<span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/bare_metal_risc-v_programming_in_go/maixpy.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>As you can see the <code class="language-plaintext highlighter-rouge">board_info</code> assigns wrong pin to the blue LED but otherwise the code is working as expected.</p>

<p>Let’s write a similar program in Go.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"time"</span>
	<span class="s">"github.com/embeddedgo/kendryte/devboard/maixbit/board/leds"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">led</span> <span class="o">:=</span> <span class="n">leds</span><span class="o">.</span><span class="n">Blue</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="n">led</span><span class="o">.</span><span class="n">SetOn</span><span class="p">()</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
		<span class="n">led</span><span class="o">.</span><span class="n">SetOff</span><span class="p">()</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Save it in <em>maix_blinky</em> directory as <em>main.go</em> file and try to compile.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd maix_blinky
$ go mod init maix_blinky
$
$ GOOS=noos GOARCH=riscv64 go build -tags k210 -ldflags '-M 0x80000000:6M'
go: finding module for package github.com/embeddedgo/kendryte/devboard/maixbit/board/leds
go: downloading github.com/embeddedgo/kendryte v0.0.1
go: found github.com/embeddedgo/kendryte/devboard/maixbit/board/leds in github.com/embeddedgo/kendryte v0.0.1
$
$ ls -l
total 1188
-rw-r--r-- 1 michal michal      75 May 31 11:49 go.mod
-rw-r--r-- 1 michal michal     179 May 31 11:49 go.sum
-rw-r--r-- 1 michal michal     222 May 31 11:46 main.go
-rwxr-xr-x 1 michal michal 1200398 May 31 11:52 maix_blinky
</code></pre></div></div>

<p>Compilation succeeded! But to achieve this, you need to add support for noos/riscv64 pair to the vanilla Go compiler. Read <a href="/getting_started">Getting started</a> and <a href="https://github.com/embeddedgo/patch">github.com/embeddedgo/patch readme</a> for more information.</p>

<p>Let’s flash our program to the Maix Bit. You need to install <a href="https://github.com/kendryte/kflash.py">kflash ISP utility</a>. The simplest way is to use <code class="language-plaintext highlighter-rouge">pip3 install kflash</code> command.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ objcopy -O binary maix_blinky maix_blinky.bin
$ kflash -p /dev/ttyUSB0 -b 750000 -B bit_mic maix_blinky.bin
[INFO] COM Port Selected Manually:  /dev/ttyUSB0
[INFO] Default baudrate is 115200 , later it may be changed to the value you set.
[INFO] Trying to Enter the ISP Mode...
[INFO] Greeting Message Detected, Start Downloading ISP
Downloading ISP: |============================================| 100.0% 10kiB/s
[INFO] Booting From 0x80000000
[INFO] Wait For 0.1 second for ISP to Boot
[INFO] Boot to Flashmode Successfully
[INFO] Selected Baudrate:  750000
[INFO] Baudrate changed, greeting with ISP again ...
[INFO] Boot to Flashmode Successfully
[INFO] Selected Flash:  On-Board
[INFO] Initialization flash Successfully
Programming BIN: |============================================| 100.0% 38kiB/s
[INFO] Rebooting...
</code></pre></div></div>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/bare_metal_risc-v_programming_in_go/blinky1.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>The developement of Go on Kendryte K210 takes place on <a href="https://github.com/embeddedgo/kendryte">github.com/embeddedgo/kendryte</a>. You can clone the repository and try the other examples. Any help in developing this project is appreciated.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git clone https://github.com/embeddedgo/kendryte
remote: Enumerating objects: 229, done.
remote: Counting objects: 100% (229/229), done.
remote: Compressing objects: 100% (137/137), done.
remote: Total 229 (delta 48), reused 215 (delta 34), pack-reused 0
Receiving objects: 100% (229/229), 570.65 KiB | 28.00 KiB/s, done.
Resolving deltas: 100% (48/48), done.
$ cd kendryte/devboard/maixbit/examples
$ ls
blinky  build.sh  debug-oocd.sh  goroutines  load-kflash.sh
$ cd blinky
$ ../build.sh
$ ../load-kflash.sh
</code></pre></div></div>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/bare_metal_risc-v_programming_in_go/blinky2.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<h4 id="sumary">Sumary</h4>

<p>The Kendryte K210 is impresive piece of hardware for it’s price. It seems to be an ideal platform to play with Embedded Go.</p>

<p>At the end I will describe briefly the Maix Bit v2 development board.</p>

<p><img src="/images/bare_metal_risc-v_programming_in_go/maix_bit_num.jpg" alt="Maix Bit explained" /></p>

<p>From the left to the right:</p>

<p><strong>1</strong> USB-C connector, connected to the CH552, can be used to power the board, program it and communicate with K210 through UART,</p>

<p><strong>2</strong> BOOT/USER button, pressed during reset starts the bootloader in programming mode, you can read its state in your code,</p>

<p><strong>3</strong> RESET button,</p>

<p><strong>4</strong> power LED,</p>

<p><strong>5</strong> RGB LED,</p>

<p><strong>6</strong> CH552 MCU, presents itself as FTDI FT2232H USB to UART converter, has ability to reset the K210 chip and control its BOOT pin,</p>

<p><strong>7</strong> MEMS microphone,</p>

<p><strong>8</strong> CH552 UART Rx/Tx LEDs,</p>

<p><strong>9</strong> DC/DC step-down PMU (RY1303A), provides all voltages need by K210 (0.9V, 1.8V, 3.3V),</p>

<p><strong>A</strong> Kendryte K210 SOC,</p>

<p><strong>B</strong> 16 MB SPI Flash,</p>

<p><strong>C</strong> camera connector,</p>

<p><strong>D</strong> LCD connector.</p>

<p>There is also a Micro SD card slot on the other side of the board.</p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="go" /><category term="embeddedgo" /><category term="risc-v" /><category term="kendryte" /><category term="k210" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Programming with EVE</title><link href="https://embeddedgo.github.io/2020/03/27/programming_with_eve.html" rel="alternate" type="text/html" title="Programming with EVE" /><published>2020-03-27T00:00:00+00:00</published><updated>2020-03-27T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2020/03/27/programming_with_eve</id><content type="html" xml:base="https://embeddedgo.github.io/2020/03/27/programming_with_eve.html"><![CDATA[<p><img src="/images/gopher/gopher-eve.jpg" alt="Eve &amp; Gopher" /></p>

<!--more-->

<p>There are hundreds of different types of displays targeting the MCU market. From the simplest text only displays based on <a href="https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller">HD44780</a> compatible controllers to the <a href="https://nextion.itead.cc/">Nextion</a> “intelligent” displays that contain their own MCUs and megabytes of RAM.</p>

<p>But there is a series of display controllers that shines among many others. They are product of FTDI, a company well known for their USB to UART converters. Currently there are three generations of these controllers available on the market: FT80x, FT81x and BT81x, all based on the same concept called <a href="https://www.ftdichip.com/EVE.htm">Embedded Video Engine</a>.</p>

<p>The following two videos are from Gameduino 2/3 Kickstarter campaigns. In the first one James Bowman presents the features of <a href="https://excamera.com/sphinx/gameduino2/">Gameduino 2</a> the Arduino display shield based on FT800 chip. In the second one you can see the improvements introduced by <a href="https://excamera.com/sphinx/gameduino3/">Gameduino 3</a> that uses FT810 chip (EVE2).</p>

<video width=640 height=362 controls preload=auto>
	<source src='/videos/programming_with_eve/gameduino2.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<video width=640 height=360 controls preload=auto>
	<source src='/videos/programming_with_eve/gameduino3.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>At the end of this article you can see one demo and one game from the first video. They are translated to Go and can run on any supported MCU.</p>

<h4 id="embedded-video-engine">Embedded Video Engine</h4>

<p>EVE is something like GPU for microcontrollers. It provides drawing interface similar to OpenGL 2D and also handles audio and touch functionality.</p>

<p>Unique is the way EVE produces the displayed image. If you think about typical GPU based design you probably think about a specialized processor that executes commands and draws to the large framebuffer. We’re just before connecting a relatively large 800x480 display to the STM32L476 microcontroller. Such display would need about 1.5 MB of graphics RAM for single 32-bit RGBA framebuffer. The STM32L476 has only 128 KB of RAM so the framebuffer must be handled by display controller.</p>

<p>EVE does it for our MCU but uses only 3200 bytes for its internal framebuffer. How is it possible? It’s possible because EVE renders the display list (scene in the OpenGL nomenclature) line by line during the LCD refresh.</p>

<p>The display list can contain up to 2048 commands and is double-buffered in RAM of size 2 x 8 KB. A microcontroller can write basic commands like points, lines, rectangles, bitmaps, etc. directly to the inactive display list or use Co-processor Engine (CE) for high-level commands like drawing buttons, scroll bars, progress bars, text strings, decompressing JPEG or PNG images, etc. The CE executes high-level commands and in effect writes basic display list commands to the inactive display list buffer or decompress data to the general purpose RAM. When the display list is ready the inactive and active buffers are swapped.</p>

<p>There is 1 MB (256 KB in case of FT80x) of general purpose RAM (RAM_G), where you can store bitmaps, decompress JPEG and PNG images, setup video queues, store audio files, etc. As you can see the FT81x uses about 1050 KB of RAM for a lot of things instead of 1500 KB for only one framebuffer (you need two for double buffering).</p>

<p>The EVE handles also touchscreen and audio. You can assign a tag to any object in the display list. The graphics engine will tell you which object is touched by reporting its tag. It can also assists in tracking touches on graphics objects by reporting the angle or distance. Additionally it can play audio generated by simple built-in MIDI synthesizer or play samples from RAM_G.</p>

<h4 id="the-hotmcu-ft811cb-display">The HOTMCU FT811CB display</h4>

<p>HAOYU Electronics aka HOTMCU is probably the cheapest source of FT8xx based displays. When I was working on the <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve">display/eve</a> package I’ve used two of them: <a href="https://www.hotmcu.com/5-graphical-lcd-touchscreen-480x272-spi-ft800-p-124.html?cPath=6_16">FT800CB-HY50B</a> and <a href="https://www.hotmcu.com/5-graphical-lcd-capacitive-touch-screen-800x480-spi-ft811-p-301.html">FT811CB-HY50HD</a>. The first one uses EVE1 chip (FT800), the second one uses EVE2 chip (FT811). The eve package autodetects EVE version and supports both. You can see the pictures of the FT811CB bellow:</p>

<p><img src="/images/display/eve/ft811cb-hy50hd-front.jpg" alt="WS2812B" /></p>

<p><img src="/images/display/eve/ft811cb-hy50hd-back.jpg" alt="WS2812B" /></p>

<p>Its TFT panel isn’t outstanding, to put it mildly, but good enough for development purposes.</p>

<p>Unfortunately the displays from HOTMCU are designed to work with 5V logic used by most Arduino boards. They probably work well with 5V logic, even with the highest supported 30 MHz SPI clock but in case of 3.3V logic used by most 32-bit microcontrollers they can’t work reliably with such high speeds. You can use them without any modification but you’ve to reduce the SPI clock much (unfortunately I don’t remember how much).</p>

<p>However, there is a better solution to this problem. You can remove the two 74LVC125A level shifters as shown below:</p>

<p><img src="/images/display/eve/ft811cb-mod.jpg" alt="WS2812B" /></p>

<p>As a side effect this modification allows to use 2-bit QSPI mode which doubles the available bandwidth.</p>

<h4 id="programming-with-eve">Programming with EVE</h4>

<p>Programming with Embedded Video Engine mainly consists of creating display lists. But before you create your first display list you’ve to establish a communication channel with your display. The display/eve package uses <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DCI">DCI interface</a> to communicate with EVE. The <a href="https://pkg.go.dev/github.com/embeddedgo/stm32/dci/evedci?tab=doc">stm32/dci/evedci</a> package provides DCI implementation optimized for STM32 microcontrollers.</p>

<p>Let’s connect the FT811CB display to the <a href="https://www.st.com/en/evaluation-tools/nucleo-l476rg.html">Nucleo-L476RG</a> development board.</p>

<p><img src="/images/display/eve/stm32-ft811.jpg" alt="FT811CB and Nucleo-L476RG" /></p>

<p>The FT811CB requires 8 wires for GND, 5V, INT, PDN, CSN, SCK, MISO, MOSI signals. There is also AUDIO signal on the header which you can connect to the line input of your audio amplifier or portable speaker.</p>

<p>Now we’ve to reflect these connections in the code.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"embedded/rtos"</span>

	<span class="s">"github.com/embeddedgo/display/eve"</span>
	<span class="s">"github.com/embeddedgo/stm32/dci/evedci"</span>
	<span class="s">"github.com/embeddedgo/stm32/hal/exti"</span>
	<span class="s">"github.com/embeddedgo/stm32/hal/gpio"</span>
	<span class="s">"github.com/embeddedgo/stm32/hal/irq"</span>
	<span class="s">"github.com/embeddedgo/stm32/hal/spi"</span>
	<span class="s">"github.com/embeddedgo/stm32/hal/spi/spi2"</span>

	<span class="n">_</span> <span class="s">"github.com/embeddedgo/stm32/devboard/nucleo-l476rg/board/init"</span>
<span class="p">)</span>

<span class="k">var</span> <span class="n">eveInt</span> <span class="n">rtos</span><span class="o">.</span><span class="n">Note</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>

	<span class="c">// GPIO pins used to connect to FT811CB, all from port B</span>
	<span class="n">pb</span> <span class="o">:=</span> <span class="n">gpio</span><span class="o">.</span><span class="n">B</span><span class="p">()</span>
	<span class="n">pb</span><span class="o">.</span><span class="n">EnableClock</span><span class="p">(</span><span class="no">true</span><span class="p">)</span>
	<span class="n">intn</span> <span class="o">:=</span> <span class="n">pb</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
	<span class="n">pdn</span> <span class="o">:=</span> <span class="n">pb</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">11</span><span class="p">)</span>
	<span class="n">csn</span> <span class="o">:=</span> <span class="n">pb</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">12</span><span class="p">)</span>
	<span class="n">sck</span> <span class="o">:=</span> <span class="n">pb</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">13</span><span class="p">)</span>
	<span class="n">miso</span> <span class="o">:=</span> <span class="n">pb</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">14</span><span class="p">)</span>
	<span class="n">mosi</span> <span class="o">:=</span> <span class="n">pb</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">15</span><span class="p">)</span>

	<span class="c">// connect intn pin to EXTI for detection of EVE interrupts</span>
	<span class="n">evedci</span><span class="o">.</span><span class="n">UseIntPin</span><span class="p">(</span><span class="n">intn</span><span class="p">)</span>
	<span class="n">irq</span><span class="o">.</span><span class="n">EXTI1</span><span class="o">.</span><span class="n">Enable</span><span class="p">(</span><span class="n">rtos</span><span class="o">.</span><span class="n">IntPrioLowest</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>

	<span class="c">// connect csn, sck, mosi, miso pins to SPI2</span>
	<span class="n">spi2</span><span class="o">.</span><span class="n">UsePinMaster</span><span class="p">(</span><span class="n">spi</span><span class="o">.</span><span class="n">NSS</span><span class="p">,</span> <span class="n">csn</span><span class="p">)</span>
	<span class="n">spi2</span><span class="o">.</span><span class="n">UsePinMaster</span><span class="p">(</span><span class="n">spi</span><span class="o">.</span><span class="n">SCK</span><span class="p">,</span> <span class="n">sck</span><span class="p">)</span>
	<span class="n">spi2</span><span class="o">.</span><span class="n">UsePinMaster</span><span class="p">(</span><span class="n">spi</span><span class="o">.</span><span class="n">MOSI</span><span class="p">,</span> <span class="n">mosi</span><span class="p">)</span>
	<span class="n">spi2</span><span class="o">.</span><span class="n">UsePinMaster</span><span class="p">(</span><span class="n">spi</span><span class="o">.</span><span class="n">MISO</span><span class="p">,</span> <span class="n">miso</span><span class="p">)</span>

	<span class="c">// setup EVE driver</span>
	<span class="n">lcd</span> <span class="o">:=</span> <span class="n">eve</span><span class="o">.</span><span class="n">NewDriver</span><span class="p">(</span><span class="n">evedci</span><span class="o">.</span><span class="n">NewSPI</span><span class="p">(</span><span class="n">spi2</span><span class="o">.</span><span class="n">Driver</span><span class="p">(),</span> <span class="n">pdn</span><span class="p">),</span> <span class="m">256</span><span class="p">)</span>
	<span class="n">lcd</span><span class="o">.</span><span class="n">SetNote</span><span class="p">(</span><span class="o">&amp;</span><span class="n">eveInt</span><span class="p">)</span>

	<span class="c">// initialize display</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">Init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">eve</span><span class="o">.</span><span class="n">Default800x480</span><span class="p">,</span> <span class="no">nil</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="nb">println</span><span class="p">(</span><span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">())</span>
		<span class="k">return</span>
	<span class="p">}</span>
	<span class="n">lcd</span><span class="o">.</span><span class="n">SetBacklight</span><span class="p">(</span><span class="m">96</span><span class="p">)</span>

	<span class="c">// we will create display lists here</span>
<span class="p">}</span>

<span class="c">//go:interrupthandler</span>
<span class="k">func</span> <span class="n">EXTI1_Handler</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">exti</span><span class="o">.</span><span class="n">L1</span><span class="o">.</span><span class="n">ClearPending</span><span class="p">()</span>
	<span class="n">eveInt</span><span class="o">.</span><span class="n">Wakeup</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You can follow this article with other supported boards, e.g. compare <a href="https://github.com/embeddedgo/stm32/blob/master/devboard/f4-discovery/examples/evetest/main.go">sample code for STM32F4-Discovery</a>.</p>

<p>Save the above code and create build.sh script to facilitate future builds.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh

GOTARGET=stm32l4x6
GOMEM=0x20000000:96K,0x10000000:32K
GOTEXT=0x8000000

. $HOME/embeddedgo/scripts/build.sh $@
</code></pre></div></div>

<p>Now we can try to compile our code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ go mod init evetutorial
go: creating new go.mod: module evetutorial
$ chmod a+x build.sh
$ ./build.sh
go: downloading github.com/embeddedgo/stm32 v0.4.4
go: extracting github.com/embeddedgo/stm32 v0.4.4
go: finding github.com/embeddedgo/stm32 v0.4.4
go: downloading github.com/embeddedgo/display v0.1.6
go: extracting github.com/embeddedgo/display v0.1.6
go: finding github.com/embeddedgo/display v0.1.6
</code></pre></div></div>

<p>Success! But this code doesn’t draw anything on the screen yet. Before we start drawing something  we will prepare load.sh script which we will use to write our program to the STM32 Flash:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh

INTERFACE=stlink
TARGET=stm32l4x
TRACECLKIN=80000000

. $HOME/embeddedgo/scripts/load-oocd.sh
</code></pre></div></div>

<h4 id="first-display-list">First display list</h4>

<p>Let’s create our first display list. Add the following code at the end of the main function:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dl</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">DL</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ClearColorRGB</span><span class="p">(</span><span class="m">0x0000AA</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Clear</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">CST</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">PointSize</span><span class="p">(</span><span class="m">60</span> <span class="o">*</span> <span class="m">16</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">POINTS</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2ii</span><span class="p">(</span><span class="m">200</span><span class="p">,</span> <span class="m">100</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="m">500</span><span class="o">*</span><span class="m">16</span><span class="p">,</span> <span class="m">300</span><span class="o">*</span><span class="m">16</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">End</span><span class="p">()</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
<span class="n">dl</span><span class="o">.</span><span class="n">SwapDL</span><span class="p">()</span>
</code></pre></div></div>

<p>Compile it and flash:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./build.sh
chmod a+x load.sh
./load.sh
</code></pre></div></div>

<p>If everything went well you should see the following image on the screen (real screenshot obtained from FT811 using <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.Snapshot2">Snapshot2</a> command, open it in new tab/window to see original size):</p>

<p><img src="/images/programming_with_eve/points.png" alt="Points" width="400px" /></p>

<p>Let’s discuss the above code.</p>

<p>The <code class="language-plaintext highlighter-rouge">dl := lcd.DL(-1)</code> begins the writing transaction to the EVE memory. You start write at address passed to the <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#Driver.DL">DL</a> method. -1 is the special address that means the beginning of display list buffer (RAM_DL). It also means that DL will wait for scheduled swap to avoid overwriting the previously created display list.</p>

<p>The returned display list writer will be used to write display list commands to RAM_DL.</p>

<p>The <code class="language-plaintext highlighter-rouge">ClearColorRGB(0x0000AA)</code> and <code class="language-plaintext highlighter-rouge">Clear(eve.CST)</code> commands instruct EVE to fill its single-line frame buffer with the specified color before rendering the next line. If you omit <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.Clear">Clear</a> command you will obtain the following image on the screen:</p>

<p><img src="/images/programming_with_eve/noclear.png" alt="points" width="400px" /></p>

<p>This image says a lot about how the rendering is done.</p>

<p>Our display list draws two points on the screen. The <code class="language-plaintext highlighter-rouge">PointSize(60 * 16)</code> command specifies radius of the following points in 1/16 pixel precision. In the subsequent display lists we use <code class="language-plaintext highlighter-rouge">f(x)</code> instead of <code class="language-plaintext highlighter-rouge">x * 16</code> where f is defined as:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>func f(x int) int { return x * 16 }
</code></pre></div></div>

<p>This increases the readability of the presented code.</p>

<p>The <code class="language-plaintext highlighter-rouge">Begin(eve.POINTS)</code> tells the graphics engine to start drawing points. The following vertex commands draw points with the given coordinates. There are two kinds of vertex commands.</p>

<p>The first one is <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.Vertex2ii">Vertex2ii</a> which accept four arguments. First two are x ,y coordinates and the next two allow to specify bitmap handle and cell (more about them later). The second one is <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.Vertex2f">Vertex2f</a> which accepts only two arguments which are x, y coordinates in 1/16 pixel precision.</p>

<p>The Vertex2ii x, y coordinates can be in the range 0 to 511 only. It’s a significant disadvantage in the case of 800x480 display. Despite this you can use Vertex2ii to draw on the whole screen (useful in case of bitmaps) thanks to the <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.VertexTranslateX">VertexTranslateX</a> and <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.VertexTranslateY">VertexTranslateY</a> commands.</p>

<p>The <code class="language-plaintext highlighter-rouge">End()</code> command closes the drawing block started by Begin. It can be omitted in most cases.</p>

<p>The graphics engine executes commands until it encounters the <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.Display">Diaplay</a> command so this command ends our display list.</p>

<p>The <code class="language-plaintext highlighter-rouge">SwapDL()</code> isn’t an EVE command. It’s equivalent to the following sequence:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dl</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="n">lcd</span><span class="o">.</span><span class="n">ClearInt</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">IntSwap</span><span class="p">)</span>
<span class="n">lcd</span><span class="o">.</span><span class="n">WriteReg</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">REG_DLSWAP</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DLSWAP_FRAME</span><span class="p">)</span>
</code></pre></div></div>

<p>It closes the write transaction, clears the IntSwap interrupt flag and schedules the display list swap after the current frame is scanned out. SwapDL doesn’t wait for the display list swap to be done so your code can start calculation for the next display frame immediately, while the previous frame is still scanned out and the new one is waiting for swap.</p>

<h4 id="color-and--transparency">Color and  transparency</h4>

<p>You can specify the size, color and transparency before each vertex. They are part of <em>graphics context</em> (more about this later).</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dl</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">DL</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ClearColorRGB</span><span class="p">(</span><span class="m">0x0000AA</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Clear</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">CST</span><span class="p">)</span>

<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">POINTS</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0xFF0000</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">PointSize</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0x00FF00</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorA</span><span class="p">(</span><span class="m">128</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">PointSize</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">500</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">))</span>

<span class="n">dl</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
<span class="n">dl</span><span class="o">.</span><span class="n">SwapDL</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="/images/programming_with_eve/color.png" alt="Color" width="400px" /></p>

<h4 id="other-graphics-primitives">Other graphics primitives</h4>

<p>The Begin command accepts the following graphics primitives: BITMAPS, POINTS, LINES, LINE_STRIP, EDGE_STRIP_R, EDGE_STRIP_L, EDGE_STRIP_A, EDGE_STRIP_B, RECTS. The following display list demonstrates some of them:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">BITMAPS</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2ii</span><span class="p">(</span><span class="m">20</span><span class="p">,</span> <span class="m">20</span><span class="p">,</span> <span class="m">31</span><span class="p">,</span> <span class="sc">'A'</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">29</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="m">25</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">Cell</span><span class="p">(</span><span class="kt">byte</span><span class="p">(</span><span class="sc">'B'</span> <span class="o">+</span> <span class="n">i</span><span class="p">))</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">60</span><span class="o">+</span><span class="m">25</span><span class="o">*</span><span class="n">i</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">20</span><span class="p">))</span>
<span class="p">}</span>

<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">LINES</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">LineWidth</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">1</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">20</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">LineWidth</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">5</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">20</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">150</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">150</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">LineWidth</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">10</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">20</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">))</span>

<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">LINE_STRIP</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">LineWidth</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">1</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">600</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">780</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">LineWidth</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">5</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">600</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">150</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">780</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">150</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">LineWidth</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">10</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">600</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">780</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">))</span>

<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">EDGE_STRIP_B</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0xFFFF00</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">400</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">350</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">500</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">450</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">700</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">320</span><span class="p">))</span>

<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">RECTS</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0xFF0000</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">250</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">550</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0x00FF00</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">LineWidth</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">1</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">150</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">500</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">250</span><span class="p">))</span>
</code></pre></div></div>

<p><img src="/images/programming_with_eve/primitives.png" alt="Primitives" width="400px" /></p>

<p>Although the number of available primitives is small, however along with the other display list commands such as bitmap transformations, blending, scissors and stencils it gives you immense possibilities for creating images.</p>

<h4 id="bitmaps">Bitmaps</h4>

<p>As you can see in the example above, the built-in fonts are bitmaps saved in ROM. But we can load other bitmaps to the RAM_G and display them just like fonts.</p>

<p>You can find an example of L1 bitmap in the <a href="https://github.com/embeddedgo/display/blob/master/eve/examples/evetest/gophermask.go">display/eve/examples/evetest/gophermask.go</a> file. It’s stored in the long Go string. In case of lack of external storage like SD card it’s perfectly fine to store bitmaps this way. There are other things like assets with display list commands which you would prefer to have in uint32 arrays. But there is no way in the current Go specification to leave the immutable data other than strings in the Flash. See the <a href="https://github.com/golang/go/issues/37303">immutable data proposal</a> for more information about this topic.</p>

<p>Let’s copy the <a href="https://github.com/embeddedgo/display/blob/master/eve/examples/evetest/gophermask.go">display/eve/examples/evetest/gophermask.go</a> file to our project directory and change its package name to <em>main</em>. Now we can load gopherMask bitmap string to the RAM_G and display it on the screen.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dl</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">DL</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">gopherMask</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>

<span class="n">dl</span> <span class="o">=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">DL</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapSource</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapLayout</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">L1</span><span class="p">,</span> <span class="m">216</span><span class="o">/</span><span class="m">8</span><span class="p">,</span> <span class="m">251</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapSize</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">211</span><span class="p">,</span> <span class="m">251</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
<span class="n">dl</span><span class="o">.</span><span class="n">SwapDL</span><span class="p">()</span>

<span class="n">dl</span> <span class="o">=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">DL</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ClearColorRGB</span><span class="p">(</span><span class="m">0x0000AA</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Clear</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">CST</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">BITMAPS</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
<span class="n">dl</span><span class="o">.</span><span class="n">SwapDL</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="/images/programming_with_eve/bitmap.png" alt="Bitmap" width="400px" /></p>

<p>The code above consists of three write transactions. The first one transfers our bitmap to the RAM_G. The second one creates the display list that tells the graphics engine about our bitmap and assigns handle to it. The third transaction creates the display list that draws our bitmap on the screen.</p>

<p>The last two transactions can be merged into one:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dl</span> <span class="o">=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">DL</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapSource</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapLayout</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">L1</span><span class="p">,</span> <span class="m">216</span><span class="o">/</span><span class="m">8</span><span class="p">,</span> <span class="m">251</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapSize</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">211</span><span class="p">,</span> <span class="m">251</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ClearColorRGB</span><span class="p">(</span><span class="m">0x0000AA</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Clear</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">CST</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">BITMAPS</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
<span class="n">dl</span><span class="o">.</span><span class="n">SwapDL</span><span class="p">()</span>
</code></pre></div></div>

<p>but the previous example shows that the bitmap defined by one display list can be used by another one. This is an example of the <em>global graphics state</em>. Every display list has also its local state called <em>graphics context</em>. The graphics context contains attributes like color, line width, point size, bitmap handle, bitmap cell, bitmap transform coefficients, etc. You can save and restore the current graphics context on the context stack using <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.SaveContext">SaveContext</a> and <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.RestoreContext">RestoreContext</a> commands.</p>

<p>You select the bitmap to draw using handle assigned to it. There are 15 handle numbers from 0 to 14 available for programmer. The handle 15 is used as scratch bitmap handle by co-processor engine and handles from 16 to 31 are used for built-in fonts. One handle can represent multiple bitmaps with the same layout located in the memory one after another. The cell parameter is used to select one of them.</p>

<p>Our bitmap is an example of the L1 format where every pixel can be only opaque or transparent. But we can draw it in different colors:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">BITMAPS</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0xFFFF00</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">250</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0x00FFFF</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0xFF0000</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorA</span><span class="p">(</span><span class="m">128</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">350</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
</code></pre></div></div>

<p><img src="/images/programming_with_eve/colorbitmaps.png" alt="Color itmaps" width="400px" /></p>

<p>You can scale, rotate and translate bitmaps by changing coefficients of transform matrix. Below we draw the first bitmap scaled down and the other two rotated 45 degrees.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">s</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="m">256</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">Sin</span><span class="p">(</span><span class="n">math</span><span class="o">.</span><span class="n">Pi</span><span class="o">/</span><span class="m">4</span><span class="p">))</span>
<span class="n">c</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="m">256</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">Cos</span><span class="p">(</span><span class="n">math</span><span class="o">.</span><span class="n">Pi</span><span class="o">/</span><span class="m">4</span><span class="p">))</span>

<span class="n">dl</span> <span class="o">=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">DL</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ClearColorRGB</span><span class="p">(</span><span class="m">0x0000AA</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Clear</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">CST</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">BITMAPS</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0xFFFF00</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapTransformA</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">2</span><span class="o">*</span><span class="m">256</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapTransformE</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">2</span><span class="o">*</span><span class="m">256</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapTransformA</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapTransformB</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="o">-</span><span class="n">s</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapTransformD</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">BitmapTransformE</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0x00FFFF</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0xFF0000</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">ColorA</span><span class="p">(</span><span class="m">128</span><span class="p">)</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">400</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
<span class="n">dl</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
<span class="n">dl</span><span class="o">.</span><span class="n">SwapDL</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="/images/programming_with_eve/transform.png" alt="Bitmap transformations" width="400px" /></p>

<p>The scaling and rotation invariant is the upper left corner of the bitmap. If you want to rotate bitmap around the center of the original image you need to translate it before rotation and translate it back after rotation. This all can be expressed by one transform matrix but the formula for coefficients will be more complicated. Fortunately, EVE has a graphics co-processor that facilitates many things including rotating and scaling bitmaps.</p>

<h4 id="graphics-co-processor">Graphics co-processor</h4>

<p>So far we’ve been writing display lists directly to RAM_DL. But we can also send the display list commands to the co-processor engine. It will write the received commands to RAM_DL anyway. So what’s the profit from using it for this? There is no any profit if you send DL commands only but it gives you ability to mix co-processor commands with DL commands which makes your job easier.</p>

<p>Let’s write the last example one more time but now using co-processor engine and doing scaling and rotation relative to the center of the bitmaps.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dx</span> <span class="o">:=</span> <span class="m">211</span> <span class="o">&lt;&lt;</span> <span class="m">16</span> <span class="o">/</span> <span class="m">2</span>
<span class="n">dy</span> <span class="o">:=</span> <span class="m">251</span> <span class="o">&lt;&lt;</span> <span class="m">16</span> <span class="o">/</span> <span class="m">2</span>
<span class="n">scale</span> <span class="o">:=</span> <span class="m">1</span> <span class="o">&lt;&lt;</span> <span class="m">16</span> <span class="o">/</span> <span class="m">2</span>
<span class="n">angle</span> <span class="o">:=</span> <span class="m">0x10000</span> <span class="o">/</span> <span class="m">8</span>

<span class="n">ce</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">CE</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">DLStart</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">ClearColorRGB</span><span class="p">(</span><span class="m">0x0000AA</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Clear</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">CST</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">BITMAPS</span><span class="p">)</span>

<span class="n">ce</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0xFFFF00</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">LoadIdentity</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Translate</span><span class="p">(</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Scale</span><span class="p">(</span><span class="n">scale</span><span class="p">,</span> <span class="n">scale</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Translate</span><span class="p">(</span><span class="o">-</span><span class="n">dx</span><span class="p">,</span> <span class="o">-</span><span class="n">dy</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">SetMatrix</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>

<span class="n">ce</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0x00FFFF</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Translate</span><span class="p">(</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Rotate</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Translate</span><span class="p">(</span><span class="o">-</span><span class="n">dx</span><span class="p">,</span> <span class="o">-</span><span class="n">dy</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">SetMatrix</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">300</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>

<span class="n">ce</span><span class="o">.</span><span class="n">ColorRGB</span><span class="p">(</span><span class="m">0xFF0000</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">ColorA</span><span class="p">(</span><span class="m">128</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Translate</span><span class="p">(</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Rotate</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Translate</span><span class="p">(</span><span class="o">-</span><span class="n">dx</span><span class="p">,</span> <span class="o">-</span><span class="n">dy</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">SetMatrix</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">400</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>

<span class="n">ce</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Swap</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="/images/programming_with_eve/cetransform.png" alt="CE transformations" width="400px" /></p>

<p>The actual transformation is a composition of all previous transformations. Use <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.LoadIdentity">LoadIdentity</a> command to start from the identity matrix.</p>

<p><a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.Translate">Translate</a> and <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.Rotate">Rotate</a> commands affect the internal co-processor state only. You’ve to use <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.SetMatrix">SetMatrix</a> command to write appropriate BitmapTransformA-F commands to RAM_DL.</p>

<h4 id="jpeg-images">JPEG images</h4>

<p>The co-processor engine has the ability to decompress JPEG images. We will use it in the example below.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">w</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">W</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">gopherMask</span><span class="p">)</span>
<span class="n">addr</span> <span class="o">:=</span> <span class="n">w</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>

<span class="n">ce</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">CE</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">DLStart</span><span class="p">()</span>

<span class="n">ce</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapSource</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapLayout</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">L1</span><span class="p">,</span> <span class="m">216</span><span class="o">/</span><span class="m">8</span><span class="p">,</span> <span class="m">251</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapSize</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">211</span><span class="p">,</span> <span class="m">251</span><span class="p">)</span>

<span class="n">ce</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">LoadImage</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">OPT_RGB565</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">gopher</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Align</span><span class="p">(</span><span class="m">4</span><span class="p">)</span>

<span class="n">ce</span><span class="o">.</span><span class="n">Gradient</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0x001155</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">480</span><span class="p">,</span> <span class="m">0x772200</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">BITMAPS</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">121</span><span class="p">))</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">400</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>

<span class="n">ce</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Swap</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="/images/programming_with_eve/loadjpeg.png" alt="Load JPEG image" width="400px" /></p>

<p>The gopher string contains JPEG image. You can find it in the <a href="https://github.com/embeddedgo/display/blob/master/eve/examples/evetest/gopher.go">display/eve/examples/evetest/gopher.go</a> file. The <code class="language-plaintext highlighter-rouge">LoadImage(addr, eve.OPT_RGB565)</code> co-processor command decompress it into RAM_DL just above the gopherMask bitmap. It also generates appropriate BitmapSource, BitmapLayout and BitmapSize display list commands. The JPEG string is sent to the co-processor engine immediately after <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.LoadImage">LoadImage</a> command. CE commands must be 4-byte aligned so we use <code class="language-plaintext highlighter-rouge">Align(4)</code> to ensures this for the next command.</p>

<p>The <code class="language-plaintext highlighter-rouge">Gradient(0, 0, 0x001155, 0, 480, 0x772200)</code> co-processor command draws vertical gradient on the whole screen. In this case we don’t need the Clear command.</p>

<h4 id="alpha-compositing">Alpha compositing</h4>

<p>The EVE renders display list into one-line RGBA color buffer. The alpha channel isn’t visible on the screen but can be used in many ways while drawing. One use case is to use one bitmap to simulate an alpha channel in the another bitmap that doesn’t have it.</p>

<p>Let’s modify the next to last paragraph of the previous example as shown below.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ce</span><span class="o">.</span><span class="n">Gradient</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0x001155</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">480</span><span class="p">,</span> <span class="m">0x772200</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">BITMAPS</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">ColorMask</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">A</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Clear</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">C</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">231</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">121</span><span class="p">))</span>
<span class="n">ce</span><span class="o">.</span><span class="n">ColorMask</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">RGBA</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BlendFunc</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">DST_ALPHA</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">ONE_MINUS_DST_ALPHA</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">200</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="m">100</span><span class="p">))</span>
</code></pre></div></div>

<p><img src="/images/programming_with_eve/alphajpeg.png" alt="Add alpha to JPEG" width="400px" /></p>

<p>The <code class="language-plaintext highlighter-rouge">ColorMask(eve.A)</code> disables drawing on the red, green and blue channels so the <code class="language-plaintext highlighter-rouge">Clear(eve.C)</code> apply only to the alpha channel same as <code class="language-plaintext highlighter-rouge">Vertex2f(f(231), f(121))</code> which draws gopherMask bitmap to the alpha channel only. After that the <code class="language-plaintext highlighter-rouge">ColorMask(eve.RGBA)</code> command restores the default state.</p>

<p>The <code class="language-plaintext highlighter-rouge">Vertex2f(f(200), f(100))</code> draws the gopher bitmap using the blending factors set by the <code class="language-plaintext highlighter-rouge">BlendFunc(eve.DST_ALPHA, eve.ONE_MINUS_DST_ALPHA)</code> command. As a result, the gopherMask drawn in the alpha channel determines what is visible from the gopher bitmap.</p>

<p>EVE calculates the pixel color using the following formula:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">screenColor</span> <span class="o">=</span> <span class="n">srcFactor</span><span class="o">*</span><span class="n">drawColor</span> <span class="o">+</span> <span class="n">dstFactor</span><span class="o">*</span><span class="n">screenColor</span>
</code></pre></div></div>

<p>where drawColor is the pixel color of the object being drawn.</p>

<p>The <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.BlendFunc">BlendFunc</a> command changes srcFactor and dstFactor. The initial state is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>srcFactor = drawColor.Aplha     // eve.SRC_ALPHA constant
dstFactor = 1 - drawColor.Aplha // eve.ONE_MINUS_SRC_ALPHA constant
</code></pre></div></div>

<p>which means that the source alpha channel determines the transparency of the drawn object.</p>

<p>There are six possible factor constants:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ZERO                 // 0
ONE                  // 1
SRC_ALPHA            // drawColor.Aplha
DST_ALPHA            // screenColor.Alpha
ONE_MINUS_SRC_ALPHA  // 1 - drawColor.Aplha
ONE_MINUS_DST_ALPHA  // 1 - screenColor.Alpha
</code></pre></div></div>

<h4 id="touchscreen">Touchscreen</h4>

<p>The FT811CB display supports up to five touches to be read. The following code demonstrates this ability.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">w</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">W</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">gopherMask</span><span class="p">)</span>
<span class="n">addr</span> <span class="o">:=</span> <span class="n">w</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>

<span class="n">ce</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">CE</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">DLStart</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapSource</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapLayout</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">L1</span><span class="p">,</span> <span class="m">216</span><span class="o">/</span><span class="m">8</span><span class="p">,</span> <span class="m">251</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapSize</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">211</span><span class="p">,</span> <span class="m">251</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">LoadImage</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">OPT_RGB565</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">gopher</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Align</span><span class="p">(</span><span class="m">4</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>

<span class="n">ramdl</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">MemMap</span><span class="p">()</span><span class="o">.</span><span class="n">RAM_DL</span>
<span class="n">drawGopher</span> <span class="o">:=</span> <span class="n">ramdl</span><span class="o">.</span><span class="n">Size</span> <span class="o">-</span> <span class="m">16</span><span class="o">*</span><span class="m">4</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="m">2</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
	<span class="n">lcd</span><span class="o">.</span><span class="n">WaitInt</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">IntSwap</span><span class="p">)</span>
	<span class="n">dl</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">DL</span><span class="p">(</span><span class="n">ramdl</span><span class="o">.</span><span class="n">Start</span> <span class="o">+</span> <span class="n">drawGopher</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">SaveContext</span><span class="p">()</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">Begin</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">BITMAPS</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">28</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="o">-</span><span class="m">60</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="o">-</span><span class="m">55</span><span class="p">))</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">ColorMask</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">A</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">Clear</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">C</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">Cell</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="o">-</span><span class="m">70</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="o">-</span><span class="m">251</span><span class="p">))</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">ColorMask</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">RGBA</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">BlendFunc</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">DST_ALPHA</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">ONE_MINUS_DST_ALPHA</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">BitmapHandle</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">Vertex2f</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="o">-</span><span class="m">70</span><span class="o">-</span><span class="m">31</span><span class="p">),</span> <span class="n">f</span><span class="p">(</span><span class="o">-</span><span class="m">251</span><span class="o">-</span><span class="m">21</span><span class="p">))</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">End</span><span class="p">()</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">RestoreContext</span><span class="p">()</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">Return</span><span class="p">()</span>
	<span class="n">dl</span><span class="o">.</span><span class="n">SwapDL</span><span class="p">()</span>
<span class="p">}</span>

<span class="n">lcd</span><span class="o">.</span><span class="n">WriteReg</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">REG_CTOUCH_EXTENDED</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span> <span class="c">// multi-touch detection mode</span>

<span class="k">for</span> <span class="p">{</span>
	<span class="n">touches</span> <span class="o">:=</span> <span class="p">[</span><span class="m">5</span><span class="p">]</span><span class="kt">uint32</span><span class="p">{</span>
		<span class="n">lcd</span><span class="o">.</span><span class="n">ReadReg</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">REG_CTOUCH_TOUCH0_XY</span><span class="p">),</span>
		<span class="n">lcd</span><span class="o">.</span><span class="n">ReadReg</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">REG_CTOUCH_TOUCH1_XY</span><span class="p">),</span>
		<span class="n">lcd</span><span class="o">.</span><span class="n">ReadReg</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">REG_CTOUCH_TOUCH2_XY</span><span class="p">),</span>
		<span class="n">lcd</span><span class="o">.</span><span class="n">ReadReg</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">REG_CTOUCH_TOUCH3_XY</span><span class="p">),</span>
		<span class="n">lcd</span><span class="o">.</span><span class="n">ReadReg</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">REG_CTOUCH_TOUCH4_X</span><span class="p">)</span><span class="o">&lt;&lt;</span><span class="m">16</span> <span class="o">|</span>
			<span class="n">lcd</span><span class="o">.</span><span class="n">ReadReg</span><span class="p">(</span><span class="n">eve</span><span class="o">.</span><span class="n">REG_CTOUCH_TOUCH4_Y</span><span class="p">)</span><span class="o">&amp;</span><span class="m">0xFFFF</span><span class="p">,</span>
	<span class="p">}</span>
	<span class="n">ce</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">CE</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">DLStart</span><span class="p">()</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Gradient</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0x001155</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">480</span><span class="p">,</span> <span class="m">0x772200</span><span class="p">)</span>
	<span class="k">for</span> <span class="n">n</span><span class="p">,</span> <span class="n">touch</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">touches</span> <span class="p">{</span>
		<span class="k">if</span> <span class="n">touch</span> <span class="o">!=</span> <span class="m">0x80008000</span> <span class="p">{</span>
			<span class="n">ce</span><span class="o">.</span><span class="n">Cell</span><span class="p">(</span><span class="sc">'1'</span> <span class="o">+</span> <span class="n">n</span><span class="p">)</span>
			<span class="n">ce</span><span class="o">.</span><span class="n">VertexTranslateX</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="kt">int</span><span class="p">(</span><span class="n">touch</span> <span class="o">&gt;&gt;</span> <span class="m">16</span><span class="p">)))</span>
			<span class="n">ce</span><span class="o">.</span><span class="n">VertexTranslateY</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="kt">int</span><span class="p">(</span><span class="n">touch</span> <span class="o">&amp;</span> <span class="m">0xFFFF</span><span class="p">)))</span>
			<span class="n">ce</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="n">drawGopher</span><span class="p">)</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Swap</span><span class="p">()</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/programming_with_eve/touchscreen.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>EVE supports something like procedure/function calling. The above code uses this feature. It writes the gopher drawing procedure to the end of both display list buffers only once during initialization. Then the <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.Call">Call</a> command is used to call it.</p>

<p>The procedure starts with the <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.SaveContext">SaveContext</a> command and ends with the <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.RestoreContext">RestoreContext</a> followed by <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.Return">Return</a> command. You cannot pass any arguments to the procedure but you can influence the operation of the procedure by modifying the graphics context. The above code modifies graphics context using the VertexTranslateX/Y and Cell commands to draw gophers in different places on the screen together with the touch number.</p>

<h4 id="touch-tag">Touch tag</h4>

<p>You can specify a touch tag for graphics objects in the display list. You can think of it as the fourth component of RGBAT color. Graphics engine can report the tag value of the point on the screen which is being touched just like it can determine its color.</p>

<p>Let’s replace the second for loop in the previous example with the following code.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">{</span>
	<span class="n">tag</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">TouchTag</span><span class="p">()</span>

	<span class="n">ce</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">CE</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">DLStart</span><span class="p">()</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Gradient</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0x001155</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">480</span><span class="p">,</span> <span class="m">0x772200</span><span class="p">)</span>

	<span class="n">ce</span><span class="o">.</span><span class="n">TextString</span><span class="p">(</span><span class="m">400</span><span class="p">,</span> <span class="m">30</span><span class="p">,</span> <span class="m">31</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">OPT_RIGHTX</span><span class="p">,</span> <span class="s">"Tag:"</span><span class="p">)</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Number</span><span class="p">(</span><span class="m">410</span><span class="p">,</span> <span class="m">30</span><span class="p">,</span> <span class="m">31</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="n">tag</span><span class="p">)</span>

	<span class="n">ce</span><span class="o">.</span><span class="n">Tag</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Cell</span><span class="p">(</span><span class="sc">'1'</span><span class="p">)</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">VertexTranslateX</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">180</span><span class="p">))</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">VertexTranslateY</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">400</span><span class="p">))</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="n">drawGopher</span><span class="p">)</span>

	<span class="n">ce</span><span class="o">.</span><span class="n">Tag</span><span class="p">(</span><span class="m">2</span><span class="p">)</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Cell</span><span class="p">(</span><span class="sc">'2'</span><span class="p">)</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">VertexTranslateX</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">550</span><span class="p">))</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">VertexTranslateY</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="m">400</span><span class="p">))</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Call</span><span class="p">(</span><span class="n">drawGopher</span><span class="p">)</span>

	<span class="n">ce</span><span class="o">.</span><span class="n">Display</span><span class="p">()</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Swap</span><span class="p">()</span>
	<span class="n">ce</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/programming_with_eve/touchtags.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>As you can see in the video above the <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.SaveContext">TouchTag</a> function returns 0 if there is no touch. The initial tag is 255 just like the initial color is white and the initial alpha is 255. Therefore the touch on the background gradient reports 255.</p>

<p>You can set the tag for empty screen using <code class="language-plaintext highlighter-rouge">Clear(eve.CST)</code> (C means color, S - stencil, T - tag). There is also <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#DL.ClearTag">ClearTag</a> command which does for tag bufer what the ClearColorRGB does for color buffer.</p>

<h4 id="widgets">Widgets</h4>

<p>You can use co-processor engine to draw some predefined widgets. The code below presents most of them.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ce</span> <span class="o">:=</span> <span class="n">lcd</span><span class="o">.</span><span class="n">CE</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">DLStart</span><span class="p">()</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Gradient</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0x001155</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">480</span><span class="p">,</span> <span class="m">0x772200</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">TextString</span><span class="p">(</span><span class="n">ce</span><span class="o">.</span><span class="n">Width</span><span class="p">()</span><span class="o">/</span><span class="m">2</span><span class="p">,</span> <span class="m">30</span><span class="p">,</span> <span class="m">31</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">OPT_CENTER</span><span class="p">,</span> <span class="s">"Widgets:"</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">ButtonString</span><span class="p">(</span><span class="m">20</span><span class="p">,</span> <span class="m">70</span><span class="p">,</span> <span class="m">120</span><span class="p">,</span> <span class="m">60</span><span class="p">,</span> <span class="m">28</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="s">"Button"</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Clock</span><span class="p">(</span><span class="m">100</span><span class="p">,</span> <span class="m">280</span><span class="p">,</span> <span class="m">80</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">13</span><span class="p">,</span> <span class="m">25</span><span class="p">,</span> <span class="m">46</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Gauge</span><span class="p">(</span><span class="m">290</span><span class="p">,</span> <span class="m">280</span><span class="p">,</span> <span class="m">80</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">6</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">43</span><span class="p">,</span> <span class="m">100</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Progress</span><span class="p">(</span><span class="m">540</span><span class="p">,</span> <span class="m">70</span><span class="p">,</span> <span class="m">200</span><span class="p">,</span> <span class="m">20</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">43</span><span class="p">,</span> <span class="m">100</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Scrollbar</span><span class="p">(</span><span class="m">540</span><span class="p">,</span> <span class="m">120</span><span class="p">,</span> <span class="m">200</span><span class="p">,</span> <span class="m">20</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">43</span><span class="p">,</span> <span class="m">33</span><span class="p">,</span> <span class="m">100</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Slider</span><span class="p">(</span><span class="m">540</span><span class="p">,</span> <span class="m">170</span><span class="p">,</span> <span class="m">200</span><span class="p">,</span> <span class="m">20</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">43</span><span class="p">,</span> <span class="m">100</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Dial</span><span class="p">(</span><span class="m">640</span><span class="p">,</span> <span class="m">300</span><span class="p">,</span> <span class="m">60</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">30000</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">KeysString</span><span class="p">(</span><span class="m">20</span><span class="p">,</span> <span class="m">420</span><span class="p">,</span> <span class="m">400</span><span class="p">,</span> <span class="m">40</span><span class="p">,</span> <span class="m">30</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="s">"ABCDEFGHIJK"</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">ToggleString</span><span class="p">(</span><span class="m">600</span><span class="p">,</span> <span class="m">420</span><span class="p">,</span> <span class="m">70</span><span class="p">,</span> <span class="m">29</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="no">true</span><span class="p">,</span> <span class="s">"no</span><span class="se">\xFF</span><span class="s">yes"</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Spinner</span><span class="p">(</span><span class="m">400</span><span class="p">,</span> <span class="m">200</span><span class="p">,</span> <span class="n">eve</span><span class="o">.</span><span class="n">DEFAULT</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>
<span class="n">ce</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="/images/programming_with_eve/widgets.png" alt="Widgets" width="400px" /></p>

<p>Note the lack of Display and Swap commands. The <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.Spinner">Spinner</a> command ends our display list. The co-processor engine overlays the spinner on the current display list and continuously animates until it receives <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.Stop">Stop</a> command. There are other commands that work this way: <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.Sketch">Sketch</a>, <a href="https://pkg.go.dev/github.com/embeddedgo/display/eve?tab=doc#CE.Screensaver">Screensaver</a>.</p>

<h4 id="more-about-programming-with-eve">More about programming with EVE</h4>

<p>This article does not exhaust the topic. You should definitely read the <a href="https://excamera.com/files/gd2book_v0.pdf">Gameduino 2 book</a> which gives you a lot of practical knowledge. Its worth reading before the official programming guide.</p>

<p>The <a href="https://brtchip.com/wp-content/uploads/Support/Documentation/Programming_Guides/ICs/EVE/BRT_AN_033_BT81X_Series_Programming_Guide.pdf">BT81X Series Programming Guide</a> is an official guide which you must have on hand when you programming with EVE. It gives you all details about EVE features. You don’t have to read it from cover to cover in particular not before the Gameduino book.</p>

<h4 id="bonus">Bonus</h4>

<p>The display/eve package contains some examples including two ones translated from Gameduino C++ to Go.</p>

<p>The <a href="https://github.com/embeddedgo/display/blob/master/eve/examples/gameduino/sprites">first one</a> animates 2001 sprites on the screen. See the <a href="https://excamera.com/sphinx/gameduino2/samples/sprites/">original description</a>.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/programming_with_eve/sprites.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>The <a href="https://github.com/embeddedgo/display/tree/master/eve/examples/gameduino/nightstrike">second one</a> is the NightStrike game (<a href="https://excamera.com/sphinx/gameduino2/samples/nightstrike/">original description</a>).</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/programming_with_eve/nightstrike.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>They are written for 480x272 display.</p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="go" /><category term="mcu" /><category term="embeddedgo" /><category term="eve" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Getting started with Bluetooth LE</title><link href="https://embeddedgo.github.io/2020/02/11/getting_started_with_bluetooth_le.html" rel="alternate" type="text/html" title="Getting started with Bluetooth LE" /><published>2020-02-11T00:00:00+00:00</published><updated>2020-02-11T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2020/02/11/getting_started_with_bluetooth_le</id><content type="html" xml:base="https://embeddedgo.github.io/2020/02/11/getting_started_with_bluetooth_le.html"><![CDATA[<p><img src="/images/gopher/gopher-bluetooth.jpg" alt="Gopher Blue Tooth" /></p>

<!--more-->

<p><a href="https://en.wikipedia.org/wiki/Bluetooth_Low_Energy">Bluetooth LE</a> is increasingly popular wireless communication standard. It’s not only a power efficient version of classic Bluetooth (originally designed as personal area network) but has also capabilities (mesh profile, IPv6 support) to handle other use cases typically called <a href="https://en.wikipedia.org/wiki/Internet_of_things">IoT</a>.</p>

<p>Its newest incarnation (BLE 5) features 2 Mb/s bitrate, long-range mode, improved broadcast capability and better coexistence efficiency with other Bluetooth and WiFi traffic.</p>

<p>The Nordic Semiconductor nRF5 MCU family is the main player in this field from the very beginning. You can choose from legacy Cortex-M0 based nRF51 series, through mainline Cortex-M4 based nRF52 series up to the newest two-core Cortex-M33 nRF53 series.</p>

<p>We will focus our attention on <a href="https://www.nordicsemi.com/Products/Low-power-short-range-wireless/nRF52840">nRF52840</a> chip because of its wide availability and relatively big memory which makes it an ideal target for the memory hungry Go compiler. The possibilities for optimizing the binary size generated by the current Go compiler are enormous but still not on the priority list.</p>

<p>There are many nRF52840 based modules and development boards available on the market. The one in the picture above, called <a href="https://www.nordicsemi.com/Software-and-tools/Development-Kits/nRF52840-Dongle">nRF52840 Dongle</a> (or PCA10059) is in my opinion the best one. It’s a development board but small enough to be used in real projects. It is easily programmable thanks to the preprogrammed bootloader and has all the necessary things: LEDs, buttons, ESD protected USB connector, antenna, plenty of GPIO pins and RTC crystal. If you want to buy any other nRF5 module/devboard pay attention that it includes the 32768 Hz resonator. It’s important for low power operation and generally makes things easier. This dongle is widely available and relatively cheap.</p>

<h4 id="radio">Radio</h4>

<p>The one thing that distinguishes the nRF5 family from the other ARM based microcontrollers is its multiprotocol radio. It can handle Bluetooth LE, <a href="https://en.wikipedia.org/wiki/ANT_(network)">ANT</a> and other <a href="https://en.wikipedia.org/wiki/IEEE_802.15.4">IEEE 802.15.4</a> based protocols like <a href="https://en.wikipedia.org/wiki/Thread_(network_protocol)">Thread</a>, <a href="https://en.wikipedia.org/wiki/Zigbee">Zigbee</a>. It also supports proprietary protocols like the one used by popular nRF24L01 modules.</p>

<p>The integrated radio peripheral with its -95 dBm Rx sensitivity and 8 dBm Tx power is impressive piece of RF technology. At the same time it’s rather simple from the programmer point of view. It doesn’t handle much more itself than sending and receiving packets, however the packet layout is very customizable.</p>

<p>You can customize the packet header and CRC sum, select the bit endianness for payload and some header fields but the other protocol things like timing and encryption must be handled externally by software with a help of other hardware like AES ECB peripheral, timers and PPI. The PPI is my favorite thing. It allows you to setup multiple peripherals to communicate without CPU intervention. It’s simple concept but gives you the power similar to Unix pipelines.</p>

<p>As you can see you can do much with builtin radio and other peripherals but it requires a lot of work to implement something like Bluetooth LE stack on top of them. I know this because <a href="https://github.com/ziutek/emgo/blob/master/egpath/src/nrf5/blec">I tried to do it</a> with moderate success.</p>

<p>Nordic claim that they sell you a Bluetooth LE enabled product but their chip alone doesn’t understand Bluetooth at all. The solution for this “paradox” is a firmware which you can download from Nordic website for free. They called it <a href="https://cm.nordicsemi.com//Software-and-tools/Software/Bluetooth-Software">SoftDevice</a>.</p>

<h4 id="softdevice">SoftDevice</h4>

<p>The SoftDevice isn’t a typical firmware because it runs on the main CPU and shares with your application also SRAM, Flash and other resources like timers and PPI. In this respect it looks more like shared library or rather like an operating system because your code communicate with it using the system calls. On the other hand, it actually resembles some kind of device because it generates hardware interrupts to communicate events to the application. It’s all clever enough that if you have a code that runs on bare metal MCU it still can run with SoftDevice installed. You just have to move it higher in memory which only requires re-linking.</p>

<p>Every SoftDevice version has slightly different memory requirements. The APP_CODE_BASE and the minimum APP_RAM_BASE (see figure below) is specified in the SoftDevice release notes.</p>

<p><img src="/images/mcu/nrf5/softdevice/memory_region_designation.svg" alt="SoftDevice memory" /></p>

<p>The actual APP_RAM_BASE depends on the number of SoftDevice features enabled and is also checked at runtime. The next figure shows the organization of Flash memory in detail, including also the bootloader present on our nRF52840 Dongle.</p>

<p><img src="/images/mcu/nrf5/softdevice/master_boot_record.svg" alt="SoftDevice memory" /></p>

<p>But let’s leave the theory for now and show it all in practice.</p>

<h4 id="programming-the-nrf52840-dongle">Programming the nRF52840 Dongle</h4>

<p>There is a <a href="https://devzone.nordicsemi.com/nordic/short-range-guides/b/getting-started/posts/nrf52840-dongle-programming-tutorial">great tutorial</a> on this topic that is definitely worth reading. Here I’ll describe the programming considering the specifics of the Embedded Go.</p>

<p>I assume you’ve read the <a href="/getting_started">Getting started</a> article and you have a ready to use Go compiler with support for noos/thumb target. So let’s clone the embeddedgo/nrf5 repository:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd $HOME/embeddedgo
git clone https://github.com/embeddedgo/nrf5.git
</code></pre></div></div>

<p>and start with a simple <a href="https://github.com/embeddedgo/nrf5/blob/master/devboard/pca10059/examples/blinky/main.go">blinky</a> program:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"time"</span>

	<span class="s">"github.com/embeddedgo/nrf5/devboard/pca10059/board/buttons"</span>
	<span class="s">"github.com/embeddedgo/nrf5/devboard/pca10059/board/leds"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">delay</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">if</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Read</span><span class="p">()</span> <span class="o">!=</span> <span class="m">0</span> <span class="p">{</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">/</span> <span class="m">8</span><span class="p">)</span>
	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="o">/</span> <span class="m">2</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="n">leds</span><span class="o">.</span><span class="n">Blue</span><span class="o">.</span><span class="n">SetOff</span><span class="p">()</span>
		<span class="n">leds</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">SetOn</span><span class="p">()</span>
		<span class="n">delay</span><span class="p">()</span>

		<span class="n">leds</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">SetOff</span><span class="p">()</span>
		<span class="n">leds</span><span class="o">.</span><span class="n">Red</span><span class="o">.</span><span class="n">SetOn</span><span class="p">()</span>
		<span class="n">delay</span><span class="p">()</span>

		<span class="n">leds</span><span class="o">.</span><span class="n">Red</span><span class="o">.</span><span class="n">SetOff</span><span class="p">()</span>
		<span class="n">leds</span><span class="o">.</span><span class="n">Green</span><span class="o">.</span><span class="n">SetOn</span><span class="p">()</span>
		<span class="n">delay</span><span class="p">()</span>

		<span class="n">leds</span><span class="o">.</span><span class="n">Green</span><span class="o">.</span><span class="n">SetOff</span><span class="p">()</span>
		<span class="n">leds</span><span class="o">.</span><span class="n">Blue</span><span class="o">.</span><span class="n">SetOn</span><span class="p">()</span>
		<span class="n">delay</span><span class="p">()</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This program blinks all LEDs on the board at a speed dependent on the button state. It definitely doesn’t use Bluetooth so it can run without any SoftDevice installed. But we cannot use the bare metal memory settings:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GOTEXT=0x00000000
GOMEM=0x20000000:256K
</code></pre></div></div>

<p>because of the MBR (see the figure above) which occupies the first 4 KB of Flash. It also requires 8 bytes of RAM so the memory settings should look like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GOTEXT=0x00001000
GOMEM=0x20000008:262136
</code></pre></div></div>

<p>The <a href="https://github.com/embeddedgo/nrf5/blob/master/devboard/pca10059/examples/build-mbr.sh">build-mbr.sh</a> script contains the correct settings for this case. Let’s use it to compile our application:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd nrf5/devboard/pca10059/examples/blinky
../build-mbr.sh
</code></pre></div></div>

<p>As a result, we’ve received not only the usual blinky.elf file but also the blinky.hex file required by the DFU programming tools.</p>

<p>Now we can flash our blinky to the dongle using its bootloader. We will start with <a href="https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Connect-for-desktop">nRF Connect for Desktop</a>. In my case it’s a Linux version called nrfconnect-3.3.0-x86_64.AppImage. Download it from Nordic website and run this way:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chmod a+rx nrfconnect-3.3.0-x86_64.AppImage
./nrfconnect-3.3.0-x86_64.AppImage
</code></pre></div></div>

<p>If you encounter the following error:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[11280:0208/141211.615969:FATAL:setuid_sandbox_host.cc(157)] The SUID sandbox
helper binary was found, but is not configured correctly. Rather than run
without sandboxing I'm aborting now. [...]
</code></pre></div></div>
<p>add <code class="language-plaintext highlighter-rouge">--no-sandbox</code> options to run it without sandboxing or try to find the right solution and share it with us.</p>

<p><img src="/images/mcu/nrf5/nrfconnect.jpg" alt="nRF Connect" /></p>

<p>Install the <em>Programmer</em> application and open it.</p>

<p><img src="/images/mcu/nrf5/programmer.jpg" alt="nRF Connect" /></p>

<p>Insert the dongle into a free USB port and press the Reset button (the board has two buttons: the User button facing up and the Reset button facing to the side). The dongle should start blinking slow the red LED.</p>

<p>Click the <em>Select device</em> menu. You should see your dongle here. If you don’t see it check your dmesg log. It should look like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo dmesg |tail
[23830.797956] usb 1-1.6.4: new full-speed USB device number 21 using ehci-pci
[23830.909661] usb 1-1.6.4: New USB device found, idVendor=1915, idProduct=521f, bcdDevice= 1.00
[23830.909665] usb 1-1.6.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[23830.909676] usb 1-1.6.4: Product: Open DFU Bootloader
[23830.909677] usb 1-1.6.4: Manufacturer: Nordic Semiconductor
[23830.909678] usb 1-1.6.4: SerialNumber: C20AFA9EF7E4
[23830.910046] cdc_acm 1-1.6.4:1.0: ttyACM0: USB ACM device
</code></pre></div></div>

<p>You can also check the rights to the /dev/ttyACM0:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 166, 0 lut  8 14:55 /dev/ttyACM0
</code></pre></div></div>

<p>In this case you need to be in the dialout group or add rights to everyone but it’s a temporary solution.</p>

<p>If you selected your dongle from drop-down menu then its current Flash content should appear on the left side.</p>

<p><img src="/images/mcu/nrf5/programmer-connected.jpg" alt="nRF Connect" /></p>

<p>Now we can program our blinky application. Add blinky.hex to the <em>File memory layout</em> using <em>Add HEX file</em> button and click <em>Write</em>. After programming the dongle should start blinking.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/getting_started_with_bluetooth_le/blinky-nrf52-dongle.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>Press Reset button to check does the bootloader still work. If it is you can see a new Flash content, without a SoftDevice.</p>

<p><img src="/images/mcu/nrf5/programmer-blinky.jpg" alt="nRF Connect" /></p>

<p>You can see how huge is the binary generated by current Go compiler. But don’t worry. This is a base size because of builtin thread scheduler, goroutine scheduler, memory allocator, garbage collector and some other useful things as well as the symbol table of all that stuff. It doesn’t grow much as you add more and more of your code.</p>

<p>So now you know how to program the nRF52840 Dongle using the nRF Connect programmer. It has its advantages from an educational point of view but it’s not very convenient in everyday use. Fortunately, there is a more useful tool called <a href="https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrfutil%2FUG%2Fnrfutil%2Fnrfutil_intro.html">nrfutil</a>, but we will deal with it later. For now let’s get to the main topic of this article and start to do something with Bluetooth LE.</p>

<h4 id="bluetooth-blinky">Bluetooth blinky</h4>

<p>Let’s compile and programm a simple <a href="https://github.com/embeddedgo/nrf5/blob/master/devboard/pca10059/examples/ble_blinky/main.go">BLE blinky application</a>. It’s based on the original Nordic’s <a href="https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v14.0.0%2Fble_sdk_app_blinky.html">ble_app_blinky</a> but uses the SoftDevice API directly (the original one uses nRF5 SDK that provides an event based abstraction layer over the SoftDevice API).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd $HOME/embeddedgo/nrf5/devboard/pca10059/examples/ble_blinky
../build-s140-7.sh
</code></pre></div></div>

<p>The <a href="https://github.com/embeddedgo/nrf5/blob/master/devboard/pca10059/examples/build-s140-7.sh">build-s140-7.sh</a> script sets GOTEXT and GOMEM variables as required by S140 SoftDevice version 7. You can download the S140 SoftDevice from <a href="https://cm.nordicsemi.com/Software-and-tools/Software/S140/Download">Nordic website</a> but for convenience it is also included in the embeddedgo/nrf5 repository:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ls -o $HOME/embeddedgo/nrf5/softdevice/nordic/s140/hex
-rw-r--r-- 1 michal   1952 sty 16 23:04 s140_nrf52_7.0.1_licence-agreement.txt
-rw-r--r-- 1 michal 438258 sty 16 23:04 s140_nrf52_7.0.1_softdevice.hex
</code></pre></div></div>

<p>Let’s come back to the nRF Connect programmer, clear the previous file and add the ble_blinky.hex and s140_nrf52_7.0.1_softdevice.hex files. The file memory layout should look like on the image bellow.</p>

<p><img src="/images/mcu/nrf5/programmer-ble.jpg" alt="nRF Connect" /></p>

<p>Click <em>Write</em> to start programming. If all went well the dongle will start blinking green LED which means it advertising the <em>Nordic LED Button</em> service. To test it you can use <a href="https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp">Android</a> or <a href="https://apps.apple.com/pl/app/nrf-connect/id1054362403">iOS</a> version of <em>nRF Connect</em>. There is also a <a href="https://play.google.com/store/apps/details?id=no.nordicsemi.android.nrfblinky">nRF Blinky</a> application for Android with its source code available on <a href="https://github.com/NordicSemiconductor/Android-nRF-Blinky">github</a>. The video bellow shows both applications in action.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/getting_started_with_bluetooth_le/ble_blinky-nrf52-dongle.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>As you can see you can start to play with Bluetooth LE in Go even today. For now
only the raw SoftDevice API is available. A more convenient interface that
resembles net or net/http package should be created over it. The SoftDevice
supports multiple connections, central and peripheral roles at the same time so a Go programmer expects familiar and easy to use interface that allow to handle multiple connections using multiple goroutines.</p>

<h4 id="using-nrfutil">Using nrfutil</h4>

<p>There is a command line programming tool that is better suited for everyday use. It’s a Python application and you can install it using pip:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install nrfutil
</code></pre></div></div>

<p>Its usage is described in the <a href="https://devzone.nordicsemi.com/nordic/short-range-guides/b/getting-started/posts/nrf52840-dongle-programming-tutorial">nRF52840 Dongle Programming Tutorial</a>.</p>

<p>In case of our ble_blinky example you can use it this way:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ../load-nrfutil-s140-7.sh

Zip created at ble_blinky.zip
  [####################################]  100%
Device programmed.
</code></pre></div></div>

<h4 id="openocd">OpenOCD</h4>

<p>You can use OpenOCD to debug and program the nRF52840 Dongle without affecting MBR, SoftDevice and bootloader. The build script generates required bootloader settings that are programmed with the main binary.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ../load-oocd.sh
Open On-Chip Debugger 0.10.0+dev-00362-g78a44055-dirty (2018-03-22-10:14)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
debug_level: 0
adapter speed: 1000 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00000a80 msp: 0x20000400
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00000a80 msp: 0x20000400
** Programming Started **
auto erase enabled
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000001e msp: 0x20000400
wrote 618496 bytes from file ble_blinky.hex in 28.774637s (20.991 KiB/s)
** Programming Finished **
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00000a80 msp: 0x20000400
** Programming Started **
auto erase enabled
wrote 8192 bytes from file ble_blinky-settings.hex in 0.757974s (10.554 KiB/s)
** Programming Finished **
Setup BLE stack:
- enable softdevice: OK
- configure connection count: OK
- configure role count: OK
- enable BLE stack: OK
- softdevice RAM: required=8960, reserved=10240
- configure device name: OK
- configure connection parameters: OK
- add LED-Button service base UUID: OK
- add LED-Button service: OK
- add Button characteristic: OK
- add LED characteristic: OK
- create advertising set: OK
- start advertising: OK
Handling BLE stack events...
</code></pre></div></div>

<p>The load-oocd.sh script expects the ST-LINK programmer. If you use an other programming hardware you have to edit this script and set the INTERFACE variable to the right OpenOCD configuration.</p>

<p>Be careful to don’t remove HEX files leaving ELF file in place. If you run load-oocd.sh with only ELF file in the current directory the OpenOCD will program it to the beginning of Flash overwriting the MBR. If it happens to you use OpenOCD to flash the bootloader back this way:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openocd -f interface/stlink.cfg -f target/nrf52.cfg  -c "init; reset init; program $HOME/embeddedgo/nrf5/devboard/pca10059/doc/bootloader/bootloader.hex; exit"
</code></pre></div></div>

<p>Replace the stlink.cfg with the right configuration file for your programmer.</p>

<p>I’ve developed very useful adapter board to easy program, test and debug nRF52 dongles without soldering them. It’s so useful and simple to make that I think it’s worth sharing this idea:</p>

<p><img src="/images/mcu/nrf5/adapter-board.jpg" alt="Adapter board" /></p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="mcu" /><category term="go" /><category term="embeddedgo" /><category term="bluetooth" /><category term="ble" /><category term="nrf52" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Playing with linear accelerometer</title><link href="https://embeddedgo.github.io/2019/12/08/playing_with_linear_accelerometer.html" rel="alternate" type="text/html" title="Playing with linear accelerometer" /><published>2019-12-08T00:00:00+00:00</published><updated>2019-12-08T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2019/12/08/playing_with_linear_accelerometer</id><content type="html" xml:base="https://embeddedgo.github.io/2019/12/08/playing_with_linear_accelerometer.html"><![CDATA[<p><img src="/images/gopher/gopher-accel.jpg" alt="Gopher IRQ" /></p>

<!--more-->

<p>Finally, the <a href="https://github.com/embeddedgo/stm32">STM32 library</a> gained its <a href="https://github.com/embeddedgo/stm32/tree/master/hal/spi">first driver</a> to the real I/O peripheral: Serial Peripheral Interface (SPI). It’s a port of the <a href="https://github.com/ziutek/emgo/tree/master/egpath/src/stm32/hal/spi">Emgo driver</a> with some improvements based on real-life experiences.</p>

<p>Although I have the STM32F4-Discovery board for many years, I’ve never used the onboard accelerometer (the chip marked red on the image above). The new SPI driver and this article gives me a great opportunity to play with it.</p>

<h4 id="spi-driver">SPI driver</h4>

<p>You can use the SPI driver (and any other Embedded Go driver in the future) in two ways.</p>

<p>The first more general way is to use the <a href="https://pkg.go.dev/github.com/embeddedgo/stm32/hal/spi">spi</a> package. In this case you have to handle many things in your code:</p>

<ol>
  <li>
    <p>Select the instance of SPI peripheral.</p>
  </li>
  <li>
    <p>Select the I/O pins you want to use as SCK, MOSI, MISO, NSS signals.</p>
  </li>
  <li>
    <p>Determine the right <em>alternate function</em> to configure these pins.</p>
  </li>
  <li>
    <p>Determine the DMA streams/channels/requests that can be used for this peripheral, select one set and use it with the driver.</p>
  </li>
  <li>
    <p>Enable appropriate interrupts in the NVIC and implement couple of interrupt handlers as small wrappers over the <code class="language-plaintext highlighter-rouge">spi.Driver.*ISR</code> methods.</p>
  </li>
</ol>

<p>The second way is to use the <a href="https://pkg.go.dev/github.com/embeddedgo/stm32/hal/spi/spi1">spi<em>n</em></a> package which contains the ready to use
driver for SPI<em>n</em> peripheral. It does 3, 4, 5 for you. You can go this way as
long as you don’t encounter a conflict in the use of the same DMA stream/channel by two different peripherals.</p>

<h4 id="the-onboard-accelerometer">The onboard accelerometer</h4>

<p>Even if we use the the spi<em>n</em> package we need to deal with items 1 and 2 so let’s see how the onboard accelerometer chip is connected to the microcontroller.</p>

<p><img src="/images/mcu/devboard/lis3dsh.jpg" alt="LIS3DSH" /></p>

<p>The figure above was cut from the <a href="https://github.com/embeddedgo/stm32/blob/master/devboard/f4-discovery/doc/user_manual.pdf">F4-Discovery documentation</a>. It gives us all information needed to setup the SPI driver:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Allocate GPIO pins</span>

<span class="n">pa</span> <span class="o">:=</span> <span class="n">gpio</span><span class="o">.</span><span class="n">A</span><span class="p">()</span>
<span class="n">pa</span><span class="o">.</span><span class="n">EnableClock</span><span class="p">(</span><span class="no">true</span><span class="p">)</span>
<span class="n">sck</span> <span class="o">:=</span> <span class="n">pa</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">5</span><span class="p">)</span>
<span class="n">miso</span> <span class="o">:=</span> <span class="n">pa</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">6</span><span class="p">)</span>
<span class="n">mosi</span> <span class="o">:=</span> <span class="n">pa</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">7</span><span class="p">)</span>

<span class="n">pe</span> <span class="o">:=</span> <span class="n">gpio</span><span class="o">.</span><span class="n">E</span><span class="p">()</span>
<span class="n">pe</span><span class="o">.</span><span class="n">EnableClock</span><span class="p">(</span><span class="no">true</span><span class="p">)</span>
<span class="n">cs</span> <span class="o">:=</span> <span class="n">pe</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">3</span><span class="p">)</span>

<span class="c">// Configure SPI pins</span>

<span class="n">spi1</span><span class="o">.</span><span class="n">UsePinMaster</span><span class="p">(</span><span class="n">spi</span><span class="o">.</span><span class="n">SCK</span><span class="p">,</span> <span class="n">sck</span><span class="p">)</span>
<span class="n">spi1</span><span class="o">.</span><span class="n">UsePinMaster</span><span class="p">(</span><span class="n">spi</span><span class="o">.</span><span class="n">MOSI</span><span class="p">,</span> <span class="n">mosi</span><span class="p">)</span>
<span class="n">spi1</span><span class="o">.</span><span class="n">UsePinMaster</span><span class="p">(</span><span class="n">spi</span><span class="o">.</span><span class="n">MISO</span><span class="p">,</span> <span class="n">miso</span><span class="p">)</span>

<span class="n">cs</span><span class="o">.</span><span class="n">Set</span><span class="p">()</span> <span class="c">// CS active state is low</span>
<span class="n">cs</span><span class="o">.</span><span class="n">Setup</span><span class="p">(</span><span class="o">&amp;</span><span class="n">gpio</span><span class="o">.</span><span class="n">Config</span><span class="p">{</span><span class="n">Mode</span><span class="o">:</span> <span class="n">gpio</span><span class="o">.</span><span class="n">Out</span><span class="p">})</span>
</code></pre></div></div>

<p>As you can see we first allocate the pins and next configure them. You definitely should maintain the pin and peripheral allocation in one place in your source code. This gives you a full picture of the pins and peripherals used by your project.</p>

<p>The code above requires the following packages:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">"github.com/embeddedgo/stm32/hal/gpio"</span>
<span class="s">"github.com/embeddedgo/stm32/hal/spi/spi1"</span>
</code></pre></div></div>

<p>The driver configuration is finished but you can’t use it yet because the
<a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface">SPI</a> protocol itself
has some additional parameters and the default values don’t match the ones
described in the <a href="https://github.com/embeddedgo/stm32/blob/master/devboard/f4-discovery/doc/accel_ds.pdf">LIS3DSH datasheet</a>:</p>

<p><img src="/images/mcu/devboard/lis3dsh_spi1.jpg" alt="LIS3DSH" /></p>

<p>Table 5 gives us the maximum SPI clock frequency: 10 MHz.</p>

<p><img src="/images/mcu/devboard/lis3dsh_spi2.jpg" alt="LIS3DSH" /></p>

<p>The Figure 3 allows us to determine CPOL and CPHA parameters. As you can see the idle state of SPC (SPI clock) is high so CPOL=1. The SDI value is probed at the rising edge of SPC so CPHA=1.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Configure and enable SPI</span>

<span class="n">d</span> <span class="o">:=</span> <span class="n">spi1</span><span class="o">.</span><span class="n">Driver</span><span class="p">()</span>
<span class="n">d</span><span class="o">.</span><span class="n">Setup</span><span class="p">(</span><span class="n">spi</span><span class="o">.</span><span class="n">Master</span><span class="o">|</span><span class="n">spi</span><span class="o">.</span><span class="n">CPOL1</span><span class="o">|</span><span class="n">spi</span><span class="o">.</span><span class="n">CPHA1</span><span class="o">|</span><span class="n">spi</span><span class="o">.</span><span class="n">SoftSS</span><span class="o">|</span><span class="n">spi</span><span class="o">.</span><span class="n">ISSHigh</span><span class="p">,</span> <span class="m">10e6</span><span class="p">)</span>
<span class="n">d</span><span class="o">.</span><span class="n">SetWordSize</span><span class="p">(</span><span class="m">8</span><span class="p">)</span>
<span class="n">d</span><span class="o">.</span><span class="n">Enable</span><span class="p">()</span>
</code></pre></div></div>

<p>The SPI configuration consist of:</p>

<ul>
  <li>
    <p>spi.Master sets the SPI1 in master mode,</p>
  </li>
  <li>
    <p>CPOL1|spi.CPHA1 sets the clock polarity and phase,</p>
  </li>
  <li>
    <p>spi.SoftSS|spi.ISSHigh disables hardware control on <em>slave select</em> signal because the onboard chip isn’t connected to any pin that can be used as SPI1 NSS,</p>
  </li>
  <li>
    <p>10e6 sets the maximum clock frequency to 10 MHz.</p>
  </li>
</ul>

<p>We also selected the SPI word size and enabled the peripheral.</p>

<h4 id="first-conversation-with-the-lis3dsh">First conversation with the LIS3DSH</h4>

<p>Now we have everything configured. It’s time to talk with our accelerometer. The <a href="https://github.com/embeddedgo/stm32/blob/master/devboard/f4-discovery/doc/accel_an.pdf">application note</a> will help us with the first steps. In the chapter 2 it describes the startup sequence and gives us a simple algorithm to read acceleration data:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Startup sequence

0. Wait 10 ms from power on for the end of boot procedure.

1. Write CTRL_REG4 = 67h // X, Y, Z enabled, ODR = 100 Hz
2. Write CTRL_REG3 = C8h // DRY active high on INT1 pin

Reading acceleration data using the status register

1.  Read STATUS
2.  If STATUS(3) = 0, then go to 1
3.  If STATUS(7) = 1, then some data have been overwritten
4.  Read OUT_X_L
5.  Read OUT_X_H
6.  Read OUT_Y_L
7.  Read OUT_Y_H
8.  Read OUT_Z_L
9.  Read OUT_Z_H
10. Data processing
11. Go to 1
</code></pre></div></div>

<p>Let’s write it in Go:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="p">(</span>
	<span class="n">CTRL_REG4</span> <span class="o">=</span> <span class="m">0x20</span>
	<span class="n">CTRL_REG3</span> <span class="o">=</span> <span class="m">0x23</span>
	<span class="n">STATUS</span>    <span class="o">=</span> <span class="m">0x27</span>
	<span class="n">OUT_X_L</span>   <span class="o">=</span> <span class="m">0x28</span>
	<span class="n">OUT_X_H</span>   <span class="o">=</span> <span class="m">0x29</span>
	<span class="n">OUT_Y_L</span>   <span class="o">=</span> <span class="m">0x2A</span>
	<span class="n">OUT_Y_H</span>   <span class="o">=</span> <span class="m">0x2B</span>
	<span class="n">OUT_Z_L</span>   <span class="o">=</span> <span class="m">0x2C</span>
	<span class="n">OUT_Z_H</span>   <span class="o">=</span> <span class="m">0x2D</span>
<span class="p">)</span>

<span class="n">write</span> <span class="o">:=</span> <span class="k">func</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">val</span> <span class="kt">uint8</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">cs</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
	<span class="n">d</span><span class="o">.</span><span class="n">WriteReadByte</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>
	<span class="n">d</span><span class="o">.</span><span class="n">WriteReadByte</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
	<span class="n">cs</span><span class="o">.</span><span class="n">Set</span><span class="p">()</span>
<span class="p">}</span>

<span class="n">read</span> <span class="o">:=</span> <span class="k">func</span><span class="p">(</span><span class="n">addr</span> <span class="kt">uint8</span><span class="p">)</span> <span class="kt">byte</span> <span class="p">{</span>
	<span class="n">cs</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
	<span class="n">d</span><span class="o">.</span><span class="n">WriteReadByte</span><span class="p">(</span><span class="m">1</span><span class="o">&lt;&lt;</span><span class="m">7</span> <span class="o">|</span> <span class="n">addr</span><span class="p">)</span>
	<span class="n">val</span> <span class="o">:=</span> <span class="n">d</span><span class="o">.</span><span class="n">WriteReadByte</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
	<span class="n">cs</span><span class="o">.</span><span class="n">Set</span><span class="p">()</span>
	<span class="k">return</span> <span class="n">val</span>
<span class="p">}</span>

<span class="c">// Startup sequence</span>

<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">10</span><span class="o">*</span><span class="n">time</span><span class="o">.</span><span class="n">Millisecond</span> <span class="o">-</span> <span class="n">rtos</span><span class="o">.</span><span class="n">Nanotime</span><span class="p">())</span>
<span class="n">write</span><span class="p">(</span><span class="n">CTRL_REG4</span><span class="p">,</span> <span class="m">0x67</span><span class="p">)</span>
<span class="n">write</span><span class="p">(</span><span class="n">CTRL_REG3</span><span class="p">,</span> <span class="m">0xC8</span><span class="p">)</span>

<span class="c">// Reading acceleration data using the status register</span>

<span class="k">for</span> <span class="p">{</span>
	<span class="n">status</span> <span class="o">:=</span> <span class="n">read</span><span class="p">(</span><span class="n">STATUS</span><span class="p">)</span>
	<span class="k">if</span> <span class="n">status</span><span class="o">&gt;&gt;</span><span class="m">3</span><span class="o">&amp;</span><span class="m">1</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
		<span class="k">continue</span>
	<span class="p">}</span>
	<span class="n">datalost</span> <span class="o">:=</span> <span class="n">status</span><span class="o">&gt;&gt;</span><span class="m">7</span><span class="o">&amp;</span><span class="m">1</span> <span class="o">!=</span> <span class="m">0</span>

	<span class="n">xl</span> <span class="o">:=</span> <span class="n">read</span><span class="p">(</span><span class="n">OUT_X_L</span><span class="p">)</span>
	<span class="n">xh</span> <span class="o">:=</span> <span class="n">read</span><span class="p">(</span><span class="n">OUT_X_H</span><span class="p">)</span>
	<span class="n">yl</span> <span class="o">:=</span> <span class="n">read</span><span class="p">(</span><span class="n">OUT_Y_L</span><span class="p">)</span>
	<span class="n">yh</span> <span class="o">:=</span> <span class="n">read</span><span class="p">(</span><span class="n">OUT_Y_H</span><span class="p">)</span>
	<span class="n">zl</span> <span class="o">:=</span> <span class="n">read</span><span class="p">(</span><span class="n">OUT_Z_L</span><span class="p">)</span>
	<span class="n">zh</span> <span class="o">:=</span> <span class="n">read</span><span class="p">(</span><span class="n">OUT_Z_H</span><span class="p">)</span>

	<span class="n">x</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int16</span><span class="p">(</span><span class="n">xl</span><span class="p">)</span><span class="o">|</span><span class="kt">int16</span><span class="p">(</span><span class="n">xh</span><span class="p">)</span><span class="o">&lt;&lt;</span><span class="m">8</span><span class="p">)</span> <span class="o">*</span> <span class="m">2000</span> <span class="o">/</span> <span class="m">32768</span>
	<span class="n">y</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int16</span><span class="p">(</span><span class="n">yl</span><span class="p">)</span><span class="o">|</span><span class="kt">int16</span><span class="p">(</span><span class="n">yh</span><span class="p">)</span><span class="o">&lt;&lt;</span><span class="m">8</span><span class="p">)</span> <span class="o">*</span> <span class="m">2000</span> <span class="o">/</span> <span class="m">32768</span>
	<span class="n">z</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int16</span><span class="p">(</span><span class="n">zl</span><span class="p">)</span><span class="o">|</span><span class="kt">int16</span><span class="p">(</span><span class="n">zh</span><span class="p">)</span><span class="o">&lt;&lt;</span><span class="m">8</span><span class="p">)</span> <span class="o">*</span> <span class="m">2000</span> <span class="o">/</span> <span class="m">32768</span>

	<span class="nb">println</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">datalost</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The video below shows our program in action:</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/playing_with_linear_accelerometer/video1.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>You can find the complete source code <a href="/code/playing_with_linear_accelerometer/listing1.html">here</a>.</p>

<p>This code works but contains a subtle bug that doesn’t have a chance to reveal in case of relatively slow MCU. The following three sequences:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cs</span><span class="o">.</span><span class="n">Set</span><span class="p">()</span>
<span class="n">cs</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>

<span class="n">cs</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
<span class="n">d</span><span class="o">.</span><span class="n">WriteReadByte</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>

<span class="n">d</span><span class="o">.</span><span class="n">WriteReadByte</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">cs</span><span class="o">.</span><span class="n">Set</span><span class="p">()</span>
</code></pre></div></div>

<p>have undefined duration. The LIS3DSH datasheet specifies:</p>

<ul>
  <li>
    <p>CS setup time: 6 ns,</p>
  </li>
  <li>
    <p>CS hold time: 8 ns.</p>
  </li>
</ul>

<p>Such small time intervals are unreachable for our 168 MHz microcontroller but a decent code should handle this in some way.</p>

<h4 id="improvements">Improvements</h4>

<p>Tracking rows of numbers on the screen isn’t easy. Let’s replace <code class="language-plaintext highlighter-rouge">println(x, y, z, datalost)</code> with something more readable:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">show</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">gauge</span><span class="p">(</span><span class="s">"x"</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span>
	<span class="n">gauge</span><span class="p">(</span><span class="s">"y"</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
	<span class="n">gauge</span><span class="p">(</span><span class="s">"z"</span><span class="p">,</span> <span class="n">z</span><span class="p">)</span>
	<span class="nb">print</span><span class="p">(</span><span class="s">"+-----------------------------------------+</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
	<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">100</span><span class="o">*</span><span class="n">time</span><span class="o">.</span><span class="n">Millisecond</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">gauge</span><span class="p">(</span><span class="n">name</span> <span class="kt">string</span><span class="p">,</span> <span class="n">v</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">const</span> <span class="n">bar</span> <span class="o">=</span> <span class="s">"--------------------"</span>
	<span class="k">const</span> <span class="n">spc</span> <span class="o">=</span> <span class="s">"                    "</span>
	<span class="n">v</span> <span class="o">/=</span> <span class="m">100</span>
	<span class="nb">print</span><span class="p">(</span><span class="s">"|"</span><span class="p">)</span>
	<span class="k">switch</span> <span class="p">{</span>
	<span class="k">case</span> <span class="n">v</span> <span class="o">&gt;</span> <span class="m">0</span><span class="o">:</span>
		<span class="k">if</span> <span class="n">v</span> <span class="o">&gt;</span> <span class="m">20</span> <span class="p">{</span>
			<span class="n">v</span> <span class="o">=</span> <span class="m">20</span>
		<span class="p">}</span>
		<span class="nb">print</span><span class="p">(</span><span class="n">spc</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bar</span><span class="p">[</span><span class="o">:</span><span class="n">v</span><span class="p">],</span> <span class="n">spc</span><span class="p">[</span><span class="n">v</span><span class="o">:</span><span class="p">])</span>
	<span class="k">case</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="m">0</span><span class="o">:</span>
		<span class="k">if</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="o">-</span><span class="m">20</span> <span class="p">{</span>
			<span class="n">v</span> <span class="o">=</span> <span class="o">-</span><span class="m">20</span>
		<span class="p">}</span>
		<span class="nb">print</span><span class="p">(</span><span class="n">spc</span><span class="p">[</span><span class="o">:</span><span class="m">20</span><span class="o">+</span><span class="n">v</span><span class="p">],</span> <span class="n">bar</span><span class="p">[</span><span class="m">20</span><span class="o">+</span><span class="n">v</span><span class="o">:</span><span class="p">],</span> <span class="n">name</span><span class="p">,</span> <span class="n">spc</span><span class="p">)</span>
	<span class="k">default</span><span class="o">:</span> <span class="c">// v == 0:</span>
		<span class="nb">print</span><span class="p">(</span><span class="n">spc</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">spc</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="nb">print</span><span class="p">(</span><span class="s">"|</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>

</code></pre></div></div>

<p>Now it’s easier to see what’s going on:</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/playing_with_linear_accelerometer/video2.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>Our current code requires twelve ReadWriteByte calls to read x, y, z. Every call is a new SPI transaction that internally requires setting interrupts and sleeping on the note. This in turn implies four context switches (two for interrupt handler and two for <em>notesleep</em> system call). But we can read x, y, z using only one SPI transaction:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">LIS3DSH</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">d</span>  <span class="o">*</span><span class="n">spi</span><span class="o">.</span><span class="n">Driver</span>
	<span class="n">cs</span> <span class="n">gpio</span><span class="o">.</span><span class="n">Pin</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">LIS3DSH</span><span class="p">)</span> <span class="n">ReadXYZ</span><span class="p">()</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">buf</span> <span class="o">:=</span> <span class="p">[</span><span class="m">1</span> <span class="o">+</span> <span class="m">6</span><span class="p">]</span><span class="kt">byte</span><span class="p">{</span><span class="m">1</span><span class="o">&lt;&lt;</span><span class="m">7</span> <span class="o">|</span> <span class="n">OUT_X_L</span><span class="p">}</span>
	<span class="n">a</span><span class="o">.</span><span class="n">cs</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">d</span><span class="o">.</span><span class="n">WriteRead</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="o">:</span><span class="m">1</span><span class="p">],</span> <span class="n">buf</span><span class="p">[</span><span class="o">:</span><span class="p">])</span>
	<span class="n">a</span><span class="o">.</span><span class="n">cs</span><span class="o">.</span><span class="n">Set</span><span class="p">()</span>
	<span class="n">x</span> <span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">1</span><span class="p">])</span><span class="o">|</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">2</span><span class="p">])</span><span class="o">&lt;&lt;</span><span class="m">8</span><span class="p">)</span> <span class="o">*</span> <span class="m">2000</span> <span class="o">/</span> <span class="m">32768</span>
	<span class="n">y</span> <span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">3</span><span class="p">])</span><span class="o">|</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">4</span><span class="p">])</span><span class="o">&lt;&lt;</span><span class="m">8</span><span class="p">)</span> <span class="o">*</span> <span class="m">2000</span> <span class="o">/</span> <span class="m">32768</span>
	<span class="n">z</span> <span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">5</span><span class="p">])</span><span class="o">|</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">6</span><span class="p">])</span><span class="o">&lt;&lt;</span><span class="m">8</span><span class="p">)</span> <span class="o">*</span> <span class="m">2000</span> <span class="o">/</span> <span class="m">32768</span>
	<span class="k">return</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You have to replace the for loop with this code:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">a</span> <span class="o">:=</span> <span class="n">LIS3DSH</span><span class="p">{</span><span class="n">d</span><span class="p">,</span> <span class="n">cs</span><span class="p">}</span>
<span class="k">for</span> <span class="p">{</span>
	<span class="n">status</span> <span class="o">:=</span> <span class="n">read</span><span class="p">(</span><span class="n">STATUS</span><span class="p">)</span>
	<span class="k">if</span> <span class="n">status</span><span class="o">&gt;&gt;</span><span class="m">3</span><span class="o">&amp;</span><span class="m">1</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
		<span class="k">continue</span>
	<span class="p">}</span>
	<span class="n">show</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="n">ReadXYZ</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The next improvement will be getting rid of the busy polling the STATUS register. We have <em>data ready</em> signal on the INT1 pin which is connected to the PE0. Let’s use it:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Allocate GPIO pins</span>

<span class="c">// ...</span>
<span class="n">dr</span> <span class="o">:=</span> <span class="n">pe</span><span class="o">.</span><span class="n">Pin</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>

<span class="c">// Configure EXTI</span>

<span class="n">dr</span><span class="o">.</span><span class="n">Setup</span><span class="p">(</span><span class="o">&amp;</span><span class="n">gpio</span><span class="o">.</span><span class="n">Config</span><span class="p">{</span><span class="n">Mode</span><span class="o">:</span> <span class="n">gpio</span><span class="o">.</span><span class="n">In</span><span class="p">})</span>
<span class="n">dri</span> <span class="o">:=</span> <span class="n">exti</span><span class="o">.</span><span class="n">Lines</span><span class="p">(</span><span class="m">1</span> <span class="o">&lt;&lt;</span> <span class="n">dr</span><span class="o">.</span><span class="n">Index</span><span class="p">())</span>
<span class="n">dri</span><span class="o">.</span><span class="n">Connect</span><span class="p">(</span><span class="n">dr</span><span class="o">.</span><span class="n">Port</span><span class="p">())</span>
<span class="n">dri</span><span class="o">.</span><span class="n">EnableRiseTrig</span><span class="p">()</span>
<span class="n">irq</span><span class="o">.</span><span class="n">EXTI0</span><span class="o">.</span><span class="n">Enable</span><span class="p">(</span><span class="n">rtos</span><span class="o">.</span><span class="n">IntPrioLow</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>
</code></pre></div></div>

<p>The code below is a very simple driver for the LIS3DSH accelerometer:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">LIS3DSH</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">d</span>   <span class="o">*</span><span class="n">spi</span><span class="o">.</span><span class="n">Driver</span>
	<span class="n">cs</span>  <span class="n">gpio</span><span class="o">.</span><span class="n">Pin</span>
	<span class="n">dri</span> <span class="n">exti</span><span class="o">.</span><span class="n">Lines</span>
	<span class="n">drn</span> <span class="n">rtos</span><span class="o">.</span><span class="n">Note</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">NewLIS3DSH</span><span class="p">(</span><span class="n">d</span> <span class="o">*</span><span class="n">spi</span><span class="o">.</span><span class="n">Driver</span><span class="p">,</span> <span class="n">cs</span> <span class="n">gpio</span><span class="o">.</span><span class="n">Pin</span><span class="p">,</span> <span class="n">dri</span> <span class="n">exti</span><span class="o">.</span><span class="n">Lines</span><span class="p">)</span> <span class="o">*</span><span class="n">LIS3DSH</span> <span class="p">{</span>
	<span class="k">return</span> <span class="o">&amp;</span><span class="n">LIS3DSH</span><span class="p">{</span><span class="n">d</span><span class="o">:</span> <span class="n">d</span><span class="p">,</span> <span class="n">cs</span><span class="o">:</span> <span class="n">cs</span><span class="p">,</span> <span class="n">dri</span><span class="o">:</span> <span class="n">dri</span><span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">LIS3DSH</span><span class="p">)</span> <span class="n">Init</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">10</span><span class="o">*</span><span class="n">time</span><span class="o">.</span><span class="n">Millisecond</span> <span class="o">-</span> <span class="n">rtos</span><span class="o">.</span><span class="n">Nanotime</span><span class="p">())</span>
	<span class="n">a</span><span class="o">.</span><span class="n">cs</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">d</span><span class="o">.</span><span class="n">WriteRead</span><span class="p">([]</span><span class="kt">byte</span><span class="p">{</span><span class="n">CTRL_REG4</span><span class="p">,</span> <span class="m">0x67</span><span class="p">},</span> <span class="no">nil</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">cs</span><span class="o">.</span><span class="n">Set</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">cs</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">d</span><span class="o">.</span><span class="n">WriteRead</span><span class="p">([]</span><span class="kt">byte</span><span class="p">{</span><span class="n">CTRL_REG3</span><span class="p">,</span> <span class="m">0xC8</span><span class="p">},</span> <span class="no">nil</span><span class="p">)</span>
	<span class="n">a</span><span class="o">.</span><span class="n">cs</span><span class="o">.</span><span class="n">Set</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">LIS3DSH</span><span class="p">)</span> <span class="n">ReadXYZ</span><span class="p">()</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">a</span><span class="o">.</span><span class="n">drn</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">dri</span><span class="o">.</span><span class="n">EnableIRQ</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">drn</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
	<span class="n">buf</span> <span class="o">:=</span> <span class="p">[</span><span class="m">1</span> <span class="o">+</span> <span class="m">6</span><span class="p">]</span><span class="kt">byte</span><span class="p">{</span><span class="m">1</span><span class="o">&lt;&lt;</span><span class="m">7</span> <span class="o">|</span> <span class="n">OUT_X_L</span><span class="p">}</span>
	<span class="n">a</span><span class="o">.</span><span class="n">cs</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">d</span><span class="o">.</span><span class="n">WriteRead</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="o">:</span><span class="m">1</span><span class="p">],</span> <span class="n">buf</span><span class="p">[</span><span class="o">:</span><span class="p">])</span>
	<span class="n">a</span><span class="o">.</span><span class="n">cs</span><span class="o">.</span><span class="n">Set</span><span class="p">()</span>
	<span class="n">x</span> <span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">1</span><span class="p">])</span><span class="o">|</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">2</span><span class="p">])</span><span class="o">&lt;&lt;</span><span class="m">8</span><span class="p">)</span> <span class="o">*</span> <span class="m">2000</span> <span class="o">/</span> <span class="m">32768</span>
	<span class="n">y</span> <span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">3</span><span class="p">])</span><span class="o">|</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">4</span><span class="p">])</span><span class="o">&lt;&lt;</span><span class="m">8</span><span class="p">)</span> <span class="o">*</span> <span class="m">2000</span> <span class="o">/</span> <span class="m">32768</span>
	<span class="n">z</span> <span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">5</span><span class="p">])</span><span class="o">|</span><span class="kt">int16</span><span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="m">6</span><span class="p">])</span><span class="o">&lt;&lt;</span><span class="m">8</span><span class="p">)</span> <span class="o">*</span> <span class="m">2000</span> <span class="o">/</span> <span class="m">32768</span>
	<span class="k">return</span>
<span class="p">}</span>

<span class="k">func</span> <span class="p">(</span><span class="n">a</span> <span class="o">*</span><span class="n">LIS3DSH</span><span class="p">)</span> <span class="n">ISR</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">a</span><span class="o">.</span><span class="n">dri</span><span class="o">.</span><span class="n">DisableIRQ</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">dri</span><span class="o">.</span><span class="n">ClearPending</span><span class="p">()</span>
	<span class="n">a</span><span class="o">.</span><span class="n">drn</span><span class="o">.</span><span class="n">Wakeup</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The Init method performs the initialization sequence but uses only two SPI transactions instead of four.</p>

<p>The ReadXYZ method now waits on the note for DRDY interrupt before read x, y, z.</p>

<p>The ISR method implements the handler for EXTI interrupt:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">//go:interrupthandler</span>
<span class="k">func</span> <span class="n">EXTI0_Handler</span><span class="p">()</span> <span class="p">{</span> <span class="n">accel</span><span class="o">.</span><span class="n">ISR</span><span class="p">()</span> <span class="p">}</span>
</code></pre></div></div>

<p>You can read more about this topic in the <a href="/2019/11/29/interrupt_handling_in_go.html">previous article</a>.</p>

<p>You can use LIS3DSH type this way:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Reading acceleration data using DRDY interrupt</span>

<span class="n">accel</span> <span class="o">=</span> <span class="n">NewLIS3DSH</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">cs</span><span class="p">,</span> <span class="n">dri</span><span class="p">)</span>
<span class="n">accel</span><span class="o">.</span><span class="n">Init</span><span class="p">()</span>
<span class="k">for</span> <span class="p">{</span>
	<span class="n">show</span><span class="p">(</span><span class="n">accel</span><span class="o">.</span><span class="n">ReadXYZ</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The complete code is <a href="/code/playing_with_linear_accelerometer/listing2.html">here</a>.</p>

<p>Another improvement of our simple program could be using the onboard LEDs
instead of debug messages. We could transform our discovery board into some kind
of electronic spirit level. It would be nice to use PWM to control LED
brightness and this opens up a fascinating world of the STM32 timers. However, this article is already long enough, so we will leave PWM and timers for another one.</p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="mcu" /><category term="go" /><category term="embeddedgo" /><category term="accelerometer" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Interrupt handling in Go</title><link href="https://embeddedgo.github.io/2019/11/29/interrupt_handling_in_go.html" rel="alternate" type="text/html" title="Interrupt handling in Go" /><published>2019-11-29T00:00:00+00:00</published><updated>2019-11-29T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2019/11/29/interrupt_handling_in_go</id><content type="html" xml:base="https://embeddedgo.github.io/2019/11/29/interrupt_handling_in_go.html"><![CDATA[<p><img src="/images/gopher/gopher-irq.jpg" alt="Gopher IRQ" /></p>

<!--more-->

<p>If you want to write a driver for built-in MCU peripheral you will usually encounter two things:</p>

<ul>
  <li>
    <p>interrupt handling (definitely),</p>
  </li>
  <li>
    <p>using DMA (not necessarily).</p>
  </li>
</ul>

<p>In this article I will explain the idiomatic way of interrupt handling when it comes to the bare-metal programming in Go.</p>

<h4 id="why-we-need-interrupts">Why we need interrupts?</h4>

<p>There is probably no more basic way of communicating with a microcontroller than using a push button. We can definitely handle this simple external peripheral without using interrupts:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">{</span>
	<span class="k">if</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Read</span><span class="p">()</span> <span class="o">!=</span> <span class="m">0</span> <span class="p">{</span>
		<span class="n">buttonPressed</span><span class="p">()</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Assuming we have a debouncing handled by some external circuit and we are interested in button state (pressed) this polling code should work fine. If we want to respond for a change in the button state (released → pressed) the code may look like this:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">{</span>
	<span class="k">for</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Read</span><span class="p">()</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
	<span class="p">}</span>
	<span class="n">buttonPressed</span><span class="p">()</span>
	<span class="k">for</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Read</span><span class="p">()</span> <span class="o">!=</span> <span class="m">0</span> <span class="p">{</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The problem with our sample code is clear – the power consumption. We can significantly improve this system at the expense of response time:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">{</span>
	<span class="k">for</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Read</span><span class="p">()</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">50</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Millisecond</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="n">buttonPressed</span><span class="p">()</span>
	<span class="k">for</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Read</span><span class="p">()</span> <span class="o">!=</span> <span class="m">0</span> <span class="p">{</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">50</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Millisecond</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>A side effect of this change is some kind of software debouncing, imperfect but better than nothing. A good debouncing algorithm should filter false button presses caused, for example, by mechanical vibrations or electromagnetic interferences.</p>

<p>However, in most cases a fast response is essential and interrupts give us both low power consumption and fast response.</p>

<h4 id="external-interrupt-controller">External interrupt controller</h4>

<p>Virtually every microcontroller has an ability to use IO pins as source of external interrupts. Usually, there is some built-in subsystem consisting of a certain number of edge detectors that can be configured to detect changes on digital inputs.</p>

<p>The figure below shows the structure of an external interrupt controller (EXTI) you can found in every STM32 microcontroller:</p>

<p><img src="/images/mcu/stm32/exti.jpg" alt="EXTI" /></p>

<p>An input line can be connected to one of 23 independent edge detectors. The number of detectors varies but only 16 of them can be connected to GPIO pins. Each interrupt line can be enabled/disabled using the <em>interrupt mask register</em>. Generated interrupts are maintained in active state by the <em>pending request register</em> until you deassert them by clearing the corresponding bits.</p>

<h4 id="push-button-as-interrupt-source">Push button as interrupt source</h4>

<p>So we want to replace our polling code with an interrupt based one. Here you are:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"embedded/rtos"</span>

	<span class="s">"github.com/embeddedgo/stm32/hal/exti"</span>
	<span class="s">"github.com/embeddedgo/stm32/hal/irq"</span>

	<span class="s">"github.com/embeddedgo/stm32/devboard/nucleo-l476rg/board/buttons"</span>
	<span class="s">"github.com/embeddedgo/stm32/devboard/nucleo-l476rg/board/leds"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="c">// Get the pin associated with the User button. It will be the</span>
	<span class="c">// PA0 in case of F4-Discovery or PC13 in case of Nucleo boards.</span>
	<span class="n">pin</span> <span class="o">:=</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Pin</span><span class="p">()</span>

	<span class="c">// The EXTI line directly corresponds to the pin number (0 for PA0,</span>
	<span class="c">// 13 for PC13). You can't use PAn, PBn, PCn, ... at the same time</span>
	<span class="c">// as external interrupt sources. This is an EXTI limitation.</span>
	<span class="n">line</span> <span class="o">:=</span> <span class="n">exti</span><span class="o">.</span><span class="n">Lines</span><span class="p">(</span><span class="m">1</span> <span class="o">&lt;&lt;</span> <span class="n">pin</span><span class="o">.</span><span class="n">Index</span><span class="p">())</span>

	<span class="c">// Connect the EXTI line to the GPIO port.</span>
	<span class="n">line</span><span class="o">.</span><span class="n">Connect</span><span class="p">(</span><span class="n">pin</span><span class="o">.</span><span class="n">Port</span><span class="p">())</span>

	<span class="c">// Enable rising edge detection (the button active state is high).</span>
	<span class="n">line</span><span class="o">.</span><span class="n">EnableRiseTrig</span><span class="p">()</span>

	<span class="c">// Enable this line as interrupt source.</span>
	<span class="n">line</span><span class="o">.</span><span class="n">EnableIRQ</span><span class="p">()</span>

	<span class="c">// Enable interrupt source in the Cortex-M NVIC. The EXTI15_10 is an</span>
	<span class="c">// IRQ number that corresponds to the EXTI line numbers from 10 to 15</span>
	<span class="c">// (Nucleo board). In case of F4-Discovery it will be EXTI0.</span>
	<span class="n">irq</span><span class="o">.</span><span class="n">EXTI15_10</span><span class="o">.</span><span class="n">Enable</span><span class="p">(</span><span class="n">rtos</span><span class="o">.</span><span class="n">IntPrioLow</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>

	<span class="k">for</span> <span class="p">{</span>
		<span class="nb">println</span><span class="p">(</span><span class="s">"i am alive"</span><span class="p">)</span>
		<span class="n">rtos</span><span class="o">.</span><span class="n">Nanosleep</span><span class="p">(</span><span class="m">1e9</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">toggle</span><span class="p">(</span><span class="n">led</span> <span class="n">leds</span><span class="o">.</span><span class="n">LED</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">led</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="n">led</span><span class="o">.</span><span class="n">Get</span><span class="p">()</span> <span class="o">+</span> <span class="m">1</span><span class="p">)</span>
<span class="p">}</span>

<span class="c">//go:interrupthandler</span>
<span class="k">func</span> <span class="n">EXTI15_10_Handler</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">toggle</span><span class="p">(</span><span class="n">leds</span><span class="o">.</span><span class="n">User</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If you want to follow the subsequent steps you have to save this code in the <code class="language-plaintext highlighter-rouge">$HOME/irqtest/main.go</code> file.</p>

<p>I think the comments in the code clearly describe each step of EXTI configuration. Additional explanation may be needed for <code class="language-plaintext highlighter-rouge">irq.EXTI15_10.Enable(rtos.IntPrioLow, 0)</code> statement. This call enables an interrupt source in the Cortex-M interrupt controller called NVIC. In our case one IRQ number in NVIC is shared by six EXTI lines.</p>

<p>The <code class="language-plaintext highlighter-rouge">EXTI15_10_Handler</code> function will be called on every EXTI15_10 interrupt. Because only the line 13 is enabled we don’t need to check which line caused the interrupt.</p>

<p>The <code class="language-plaintext highlighter-rouge">buttonPressed()</code> call from the polling examples was replaced by <code class="language-plaintext highlighter-rouge">toggle(leds.User)</code> so we can see how this code behaves on real hardware.</p>

<p>Let’s write <code class="language-plaintext highlighter-rouge">build.sh</code> script to facilitate subsequent builds of our sample program:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh

GOTARGET=stm32l4x6
GOMEM=0x20000000:96K,0x10000000:32K
GOTEXT=0x8000000
IRQNAMES=$HOME/embeddedgo/stm32/hal/irq

. $HOME/embeddedgo/scripts/build.sh $@
</code></pre></div></div>

<p>If you want to use F4-Discovery or other supported STM32 MCU/board you have to modify GOTARGET and GOMEM variables. Now we can use this script to build our sample program:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd $HOME/irqtest
$ go mod init irqtest
$ chmod a+x build.sh
$ ./build.sh
go: finding github.com/embeddedgo/stm32 v0.2.0
go: downloading github.com/embeddedgo/stm32 v0.2.0
go: extracting github.com/embeddedgo/stm32 v0.2.0
$ ls
build.sh  go.mod  go.sum  irqtest.elf  main.go
</code></pre></div></div>

<p>We will use <a href="http://openocd.org/">OpenOCD</a> to load <code class="language-plaintext highlighter-rouge">irqtest.elf</code> to the Nucleo board. Let’s write <code class="language-plaintext highlighter-rouge">load.sh</code> script:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh

INTERFACE=stlink
TARGET=stm32l4x
TRACECLKIN=80000000

. $HOME/embeddedgo/scripts/load-oocd.sh
</code></pre></div></div>

<p>and use it:</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/irq1.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>This is definitely not what we wanted. The problem is that the interrupt is active until we clear it in the pending request register. Let’s clear it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>func EXTI15_10_Handler() {
	p := exti.Pending() &amp; (exti.L15&lt;&lt;1 - exti.L10)
	p.ClearPending()
	toggle(leds.User)
}
</code></pre></div></div>

<p>As this IRQ is shared by six EXTI lines we read six pending bits and clear them all if set.</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/irq2.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<p>This version works much better but it’s slightly unreliable. Even though the Nucleo’s button is equipped with a simple RC debouncing circuit it still generates spurious open/close transitions when pressed. We need some kind of debouncing algorithm.</p>

<h4 id="the-idiomatic-way-of-interrupt-handling">The idiomatic way of interrupt handling</h4>

<p>The idiomatic way of interrupt handling in Go is to divide the handler into two parts:</p>

<ul>
  <li>
    <p>the first one that works in handler mode,</p>
  </li>
  <li>
    <p>the second one that works in thread mode.</p>
  </li>
</ul>

<p>This is similar concept to the Linux software and hardware interrupts. The real interrupt handler does things that can’t be done in thread mode or that require hard-realtime service. Then it passes control to the thread mode and the further work is done by goroutines.</p>

<p>The noos port introduces <a href="https://github.com/embeddedgo/go/blob/embedded/src/embedded/rtos/note.go">embedded/rtos.Note</a> type that allows the interrupt handlers to communicate with goroutines. This isn’t a new thing because the <a href="https://github.com/embeddedgo/go/blob/221885ad6c9764bd435d97fab6f337147dca62fe/src/runtime/runtime2.go#L180">runtime.note</a> exists in the Go runtime for a long time. The noos port exposes it in the rtos package and introduces a way for interrupt handlers to wakeup gorutines sleeping on notes.</p>

<p>A typical control flow is shown in the diagram below:</p>

<p><img src="/images/alg/inthandl.png" alt="Interrutp handling" /></p>

<p>A goroutine prepares data for the next transaction and then clears the note. The <code class="language-plaintext highlighter-rouge">Note.Clear</code> method has memory barrier semantic. It ensures the prepared data will be visible consistent for the interrupt handler even if it runs on the other core.</p>

<p>The goroutine starts the transaction according to the hardware protocol implemented by peripheral, enables interrupts and then waits for the end of transaction using <code class="language-plaintext highlighter-rouge">Note.Sleep</code> method.</p>

<p>The interrupt handler usually disables its interrupt source to ensure that it will not be called again until the goroutine will be ready for next transaction. This scheme can be used if the interrupt is a part of some kind of transaction processing protocol. Fortunately, the vast majority of peripheral devices operate on the basis of a transactional model. If the subsequent interrupt is allowed to occur before the previous one has been serviced many peripherals implement pending state to avoid losing it.</p>

<p>Then the handler handles all hard-realtime things, reads and writes shared data, and if everything is done it wakes the goroutine up.</p>

<p>To show it all in practice let’s rewrite the previous code in the more idiomatic way:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"embedded/rtos"</span>

	<span class="s">"github.com/embeddedgo/stm32/hal/exti"</span>
	<span class="s">"github.com/embeddedgo/stm32/hal/irq"</span>

	<span class="s">"github.com/embeddedgo/stm32/devboard/nucleo-l476rg/board/buttons"</span>
	<span class="s">"github.com/embeddedgo/stm32/devboard/nucleo-l476rg/board/leds"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">pin</span> <span class="o">:=</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Pin</span><span class="p">()</span>
	<span class="n">line</span> <span class="o">:=</span> <span class="n">exti</span><span class="o">.</span><span class="n">Lines</span><span class="p">(</span><span class="m">1</span> <span class="o">&lt;&lt;</span> <span class="n">pin</span><span class="o">.</span><span class="n">Index</span><span class="p">())</span>
	<span class="n">line</span><span class="o">.</span><span class="n">Connect</span><span class="p">(</span><span class="n">pin</span><span class="o">.</span><span class="n">Port</span><span class="p">())</span>
	<span class="n">line</span><span class="o">.</span><span class="n">EnableRiseTrig</span><span class="p">()</span>
	<span class="n">irq</span><span class="o">.</span><span class="n">EXTI15_10</span><span class="o">.</span><span class="n">Enable</span><span class="p">(</span><span class="n">rtos</span><span class="o">.</span><span class="n">IntPrioLow</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>

	<span class="k">go</span> <span class="n">buttonLED</span><span class="p">()</span>

	<span class="k">for</span> <span class="p">{</span>
		<span class="nb">println</span><span class="p">(</span><span class="s">"i am alive"</span><span class="p">)</span>
		<span class="n">rtos</span><span class="o">.</span><span class="n">Nanosleep</span><span class="p">(</span><span class="m">1e9</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">buttonLED</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="n">waitBtn</span><span class="p">()</span>
		<span class="n">toggle</span><span class="p">(</span><span class="n">leds</span><span class="o">.</span><span class="n">User</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">toggle</span><span class="p">(</span><span class="n">led</span> <span class="n">leds</span><span class="o">.</span><span class="n">LED</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">led</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="n">led</span><span class="o">.</span><span class="n">Get</span><span class="p">()</span> <span class="o">+</span> <span class="m">1</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">var</span> <span class="n">note</span> <span class="n">rtos</span><span class="o">.</span><span class="n">Note</span>

<span class="k">func</span> <span class="n">waitBtn</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">note</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
	<span class="n">pin</span> <span class="o">:=</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Pin</span><span class="p">()</span>
	<span class="n">exti</span><span class="o">.</span><span class="n">Lines</span><span class="p">(</span><span class="m">1</span> <span class="o">&lt;&lt;</span> <span class="n">pin</span><span class="o">.</span><span class="n">Index</span><span class="p">())</span><span class="o">.</span><span class="n">EnableIRQ</span><span class="p">()</span>
	<span class="n">note</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
<span class="p">}</span>

<span class="c">//go:interrupthandler</span>
<span class="k">func</span> <span class="n">EXTI15_10_Handler</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">p</span> <span class="o">:=</span> <span class="n">exti</span><span class="o">.</span><span class="n">Pending</span><span class="p">()</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">exti</span><span class="o">.</span><span class="n">L15</span><span class="o">&lt;&lt;</span><span class="m">1</span> <span class="o">-</span> <span class="n">exti</span><span class="o">.</span><span class="n">L10</span><span class="p">)</span>
	<span class="n">p</span><span class="o">.</span><span class="n">DisableIRQ</span><span class="p">()</span>
	<span class="n">p</span><span class="o">.</span><span class="n">ClearPending</span><span class="p">()</span>
	<span class="n">note</span><span class="o">.</span><span class="n">Wakeup</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We introduced a new goroutine to handle the user button and LED. It uses the <code class="language-plaintext highlighter-rouge">waitBtn</code> function to wait for the button to be pressed. The waitBtn function clears the note, enables interrupts and falls asleep. Note that the interrupt enable call has been moved here from the main function.</p>

<p>As we have seen before the button is allowed to generate spurious interrupts so the handler disables generation of interrupts and then clears pending state. <del>This is required because waking the note twice before clearing it is treated by runtime as fatal error</del> (this has changed: now there is allowed to call Note.Wakeup multiple times before clearing the note).</p>

<h4 id="button-debouncing-done-right">Button debouncing done right</h4>

<p>There are many debouncing algorithms but I will present the best one ;-) which is simple and perfectly uses the features of rtos.Note type:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
	<span class="s">"time"</span>
	<span class="s">"embedded/rtos"</span>

	<span class="s">"github.com/embeddedgo/stm32/hal/exti"</span>
	<span class="s">"github.com/embeddedgo/stm32/hal/irq"</span>

	<span class="s">"github.com/embeddedgo/stm32/devboard/nucleo-l476rg/board/buttons"</span>
	<span class="s">"github.com/embeddedgo/stm32/devboard/nucleo-l476rg/board/leds"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">pin</span> <span class="o">:=</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Pin</span><span class="p">()</span>
	<span class="n">line</span> <span class="o">:=</span> <span class="n">exti</span><span class="o">.</span><span class="n">Lines</span><span class="p">(</span><span class="m">1</span> <span class="o">&lt;&lt;</span> <span class="n">pin</span><span class="o">.</span><span class="n">Index</span><span class="p">())</span>
	<span class="n">line</span><span class="o">.</span><span class="n">Connect</span><span class="p">(</span><span class="n">pin</span><span class="o">.</span><span class="n">Port</span><span class="p">())</span>
	<span class="n">line</span><span class="o">.</span><span class="n">EnableRiseTrig</span><span class="p">()</span>
	<span class="n">line</span><span class="o">.</span><span class="n">EnableFallTrig</span><span class="p">()</span>
	<span class="n">irq</span><span class="o">.</span><span class="n">EXTI15_10</span><span class="o">.</span><span class="n">Enable</span><span class="p">(</span><span class="n">rtos</span><span class="o">.</span><span class="n">IntPrioLow</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>

	<span class="k">go</span> <span class="n">buttonLED</span><span class="p">()</span>

	<span class="k">for</span> <span class="p">{</span>
		<span class="nb">println</span><span class="p">(</span><span class="s">"i am alive"</span><span class="p">)</span>
		<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">buttonLED</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="n">waitBtn</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
		<span class="n">toggle</span><span class="p">(</span><span class="n">leds</span><span class="o">.</span><span class="n">User</span><span class="p">)</span>
		<span class="n">waitBtn</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="k">func</span> <span class="n">toggle</span><span class="p">(</span><span class="n">led</span> <span class="n">leds</span><span class="o">.</span><span class="n">LED</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">led</span><span class="o">.</span><span class="n">Set</span><span class="p">(</span><span class="n">led</span><span class="o">.</span><span class="n">Get</span><span class="p">()</span> <span class="o">+</span> <span class="m">1</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">var</span> <span class="n">note</span> <span class="n">rtos</span><span class="o">.</span><span class="n">Note</span>

<span class="k">func</span> <span class="n">waitBtn</span><span class="p">(</span><span class="n">state</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">line</span> <span class="o">:=</span> <span class="n">exti</span><span class="o">.</span><span class="n">Lines</span><span class="p">(</span><span class="m">1</span> <span class="o">&lt;&lt;</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Pin</span><span class="p">()</span><span class="o">.</span><span class="n">Index</span><span class="p">())</span>
	<span class="k">for</span> <span class="p">{</span>
		<span class="n">note</span><span class="o">.</span><span class="n">Clear</span><span class="p">()</span>
		<span class="n">line</span><span class="o">.</span><span class="n">EnableIRQ</span><span class="p">()</span>
		<span class="n">wait</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Duration</span><span class="p">(</span><span class="o">-</span><span class="m">1</span><span class="p">)</span>
		<span class="k">if</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Read</span><span class="p">()</span> <span class="o">==</span> <span class="n">state</span> <span class="p">{</span>
			<span class="n">wait</span> <span class="o">=</span> <span class="m">50</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Millisecond</span> <span class="c">// we want 50 ms of stable state</span>
		<span class="p">}</span>
		<span class="k">if</span> <span class="o">!</span><span class="n">note</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="n">wait</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">line</span><span class="o">.</span><span class="n">DisableIRQ</span><span class="p">()</span>
			<span class="k">return</span>
		<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="c">//go:interrupthandler</span>
<span class="k">func</span> <span class="n">EXTI15_10_Handler</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">p</span> <span class="o">:=</span> <span class="n">exti</span><span class="o">.</span><span class="n">Pending</span><span class="p">()</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">exti</span><span class="o">.</span><span class="n">L15</span><span class="o">&lt;&lt;</span><span class="m">1</span> <span class="o">-</span> <span class="n">exti</span><span class="o">.</span><span class="n">L10</span><span class="p">)</span>
	<span class="n">p</span><span class="o">.</span><span class="n">DisableIRQ</span><span class="p">()</span>
	<span class="n">p</span><span class="o">.</span><span class="n">ClearPending</span><span class="p">()</span>
	<span class="k">if</span> <span class="n">pin</span> <span class="o">:=</span> <span class="n">buttons</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">Pin</span><span class="p">();</span> <span class="n">p</span><span class="o">&gt;&gt;</span><span class="n">pin</span><span class="o">.</span><span class="n">Index</span><span class="p">()</span><span class="o">&amp;</span><span class="m">1</span> <span class="o">!=</span> <span class="m">0</span> <span class="p">{</span>
		<span class="n">note</span><span class="o">.</span><span class="n">Wakeup</span><span class="p">()</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What has been changed? The edge detector now detects both rising and falling edges of the input signal. The waitBtn function gained one parameter which is the button state it should waiting for.</p>

<p>The waitBtn function no longer waits for the first signal from the handler. Now it waits in a loop for the desired stable state. The state is considered stable if it is maintained for at least 50 ms. To determine this it simply uses Note.Sleep method with the timeout set to 50e6 ns.</p>

<p>The buttonLED function now waits for the button to be pressed then toggles the LED and next waits for the button to be released as in polling algorithm from begginningo of this article. Now the button works perfectly:</p>

<video width=640 height=480 controls preload=auto>
	<source src='/videos/irq3.mp4' type='video/mp4'>
	Sorry, your browser doesn't support embedded videos.
</video>
<p></p>

<h4 id="interrupts-and-linker">Interrupts and linker</h4>

<p>In the previous article I wrote that the linker is responsible for creating the interrupt vector table. A more inquisitive reader may ask how does the linker know the interrupt number associated with the EXTI15_10_Handler function? The linker doesn’t know interrupt names but the build.sh script knows them if you specified IRQNAMES variable. It generates a <code class="language-plaintext highlighter-rouge">zisrnames.go</code> file that contains the mapping:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// DO NOT EDIT THIS FILE. GENERATED BY build.sh.</span>

<span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="n">_</span> <span class="s">"unsafe"</span>

<span class="c">//go:linkname WWDG_Handler IRQ0_Handler</span>
<span class="c">//go:linkname PVD_PVM_Handler IRQ1_Handler</span>
<span class="c">//go:linkname TAMP_STAMP_Handler IRQ2_Handler</span>
<span class="c">//go:linkname RTC_WKUP_Handler IRQ3_Handler</span>
<span class="c">//go:linkname FLASH_Handler IRQ4_Handler</span>
<span class="c">//go:linkname RCC_Handler IRQ5_Handler</span>
<span class="c">//go:linkname EXTI0_Handler IRQ6_Handler</span>
<span class="c">//go:linkname EXTI1_Handler IRQ7_Handler</span>
<span class="c">//go:linkname EXTI2_Handler IRQ8_Handler</span>
<span class="c">//go:linkname EXTI3_Handler IRQ9_Handler</span>
<span class="c">//go:linkname EXTI4_Handler IRQ10_Handler</span>
<span class="c">//go:linkname DMA1_CH1_Handler IRQ11_Handler</span>
<span class="c">//go:linkname DMA1_CH2_Handler IRQ12_Handler</span>

<span class="o">...</span>

<span class="c">//go:linkname DMA2D_Handler IRQ90_Handler</span>
</code></pre></div></div>

<p>You can see this file if compilation fails.</p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="mcu" /><category term="go" /><category term="embeddedgo" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Porting Go to microcontrollers (part 2)</title><link href="https://embeddedgo.github.io/2019/11/24/porting_go_to_microcontrollers_part2.html" rel="alternate" type="text/html" title="Porting Go to microcontrollers (part 2)" /><published>2019-11-24T00:00:00+00:00</published><updated>2019-11-24T00:00:00+00:00</updated><id>https://embeddedgo.github.io/2019/11/24/porting_go_to_microcontrollers_part2</id><content type="html" xml:base="https://embeddedgo.github.io/2019/11/24/porting_go_to_microcontrollers_part2.html"><![CDATA[<p><img src="/images/gopher/gopher-noos.jpg" alt="Gopher and noos lollipop" /></p>

<!--more-->

<p>In the <a href="/2019/11/19/porting_go_to_microcontrollers_part1.html">previous part</a> of this article I briefly described the process of porting Go language to a new architecture with little focus of my particular case: linux/thumb and noos/thumb ports. The description concerned tools related to the code generation: assembler, compiler, linker, disassembler. In this part I will deal with the second part of the Go language: the runtime. This part will be also less general because porting the runtime to a bare metal system differs much from porting it to any operating system.</p>

<p>My work on the noos/thumb port took place in two stages:</p>

<ol>
  <li>
    <p>linux/arm → linux/thumb</p>
  </li>
  <li>
    <p>linux/thumb → noos/thumb</p>
  </li>
</ol>

<p>The first stage touched the runtime slightly, mainly because of the Thumb bit in a function call address.</p>

<p>The second stage was mostly runtime because in case of GOOS=noos we don’t have any operationg system but the runtime work is mostly based on the cooperation with the OS.</p>

<p>An operating system provides the Go runtime the following things:</p>

<ul>
  <li>
    <p>memory,</p>
  </li>
  <li>
    <p>threads,</p>
  </li>
  <li>
    <p>synchronization,</p>
  </li>
  <li>
    <p>time.</p>
  </li>
</ul>

<p>In case of the noos target the runtime has to provide it all by itself.</p>

<h4 id="memory">Memory</h4>

<p>The noos port introduces a simple memory allocator that implements the interface expected by runtime. It works on memory blocks described at link time in -M option.
The noos allocator reserves some memory space for persistent allocation need by runtime and gives the whole remaining part to the Go memory allocator as the initial heap arena.</p>

<p>The Go allocator and garbage collector remained almost unmodified. Their rich
set of configuration parameters allowed to adapt them to the system with &lt;1MB
RAM. It is really a good piece of code that turned out to be scalable form
fraction of a megabyte to many gigabytes. The set of parameters I have chosen is
definitely not the optimal one, the allocator and GC not tested much, but the
current effect is promising in particular when it comes to memory fragmentation
which is a headache in case of embedded systems.</p>

<p>There is a <a href="https://github.com/embeddedgo/stm32/blob/master/devboard/f4-discovery/examples/gctest/main.go">test
program</a>
which I used to force the allocator and GC to do some work. It contains two
gorutines that communicate over a channnel. The first one vigorously allocates
random blocks of memory and sends them to the second one. The second one simply receives the blocks and discards them. This simple program can work for days on the border of memory capacity (tested with GOMAXPROCS set to 1 and 2).</p>

<h4 id="threads">Threads</h4>

<p>I had couple of ideas on how to address the problem of threads needed by the Go scheduler. The one was to don’t implement them at all and schedule all gorutines on one thread. However, it turned out that the design of the whole runtime is strongly dependent on the concept of the OS thread. It uses them heavily for so many things that the necessary changes would require enormous work with a vague effect.</p>

<p>The second idea was to implement simple thread scheduler, well separated from the runtime, which does everything a decent RTOS does. I have some experience in this topic so the first implementation was created fairly quickly and it turned out immediately how many things are redundant.</p>

<p>Eventually I implemented a thread scheduler that is tightly coupled to the Go scheduler. It provides a syscall interface required by Go scheduler but works directly on M structs and uses Go scheduler decisions to schedule M’s on available cores (P’s). If you don’t know what these G, M, P mean read the <a href="https://github.com/embeddedgo/go/blob/embedded/src/runtime/HACKING.md">description</a>.</p>

<h4 id="synchronization">Synchronization</h4>

<p>The thread scheduler provides futex like synchronization primitive. It allows to implement all runtime synchronization mechanisms but also allows to handle communication between interrupt handlers and goroutines (see <a href="https://github.com/embeddedgo/go/blob/embedded/src/embedded/rtos/note.go">rtos.Note</a> type) which is essential for an efficient bare-metal programming.</p>

<h4 id="time">Time</h4>

<p>The Go runtime needs to measure time for some of its algorithms. The thread scheduler also requires time for its own needs. But none of them has a built-in time source. The user application is responsible for provide the time source for both. It does this at startup using the <a href="https://github.com/embeddedgo/go/blob/embedded/src/embedded/rtos/systim.go">rtos.SetSystemTimer</a> function.</p>

<h4 id="summary">Summary</h4>

<p>I’m aware that the description above is very cursory. If you would like to learn more, I encourage you to analyze the code and to ask questions on the <a href="https://groups.google.com/forum/#!forum/embeddedgo">embeddedgo discussion group</a>.</p>

<p><em>Michał Derkacz</em></p>]]></content><author><name>Embedded Go authors</name></author><category term="mcu" /><category term="go" /><category term="embeddedgo" /><summary type="html"><![CDATA[]]></summary></entry></feed>