mirror of
https://github.com/lupyuen/lupyuen.github.io.git
synced 2025-01-13 10:18:33 +08:00
Commit from GitHub Actions
This commit is contained in:
parent
dc0feeae74
commit
e889f1f2e6
91 changed files with 649 additions and 649 deletions
|
@ -336,7 +336,7 @@ read_adc
|
|||
</ol>
|
||||
<p><img src="https://lupyuen.github.io/images/adc-gain2.png" alt="Setting the ADC Gain" /></p>
|
||||
<h2 id="set-the-adc-gain"><a class="doc-anchor" href="#set-the-adc-gain">§</a>1.5 Set the ADC Gain</h2>
|
||||
<p>Let’s chat about <strong>ADC Gain</strong>, which we used when reading the LED as a Light Sensor. </p>
|
||||
<p>Let’s chat about <strong>ADC Gain</strong>, which we used when reading the LED as a Light Sensor.</p>
|
||||
<p>(ADC Gain probably won’t be needed for reading most types of ADC Inputs)</p>
|
||||
<p><em>Why do we need ADC Gain when reading an LED?</em></p>
|
||||
<p>Our LED generates a <strong>tiny bit of current</strong> when exposed to light. To measure that tiny bit of current, we need to increase the ADC sensitivity.</p>
|
||||
|
@ -407,7 +407,7 @@ PROJECT_NAME := sdk_app_rust_adc
|
|||
export APP_NAME=sdk_app_rust_adc
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>Replace the <strong>Rust Source Code</strong> in <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_rust_adc/rust/src/lib.rs"><code>sdk_app_rust_adc/ rust/src/lib.rs</code></a> </p>
|
||||
<p>Replace the <strong>Rust Source Code</strong> in <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_rust_adc/rust/src/lib.rs"><code>sdk_app_rust_adc/ rust/src/lib.rs</code></a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>See the Appendix for the steps to define the <strong>Rust Commands</strong> for the Command-Line Interface in <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_rust_adc/sdk_app_rust_adc/demo.c#L47-L55"><code>sdk_app_rust_adc / demo.c</code></a></p>
|
||||
|
@ -899,9 +899,9 @@ blflash flash c:\blflash\sdk_app_lora.bin --port COM5
|
|||
<p>Connect to BL602’s UART Port at 2 Mbps like so…</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>screen /dev/ttyUSB0 2000000
|
||||
</code></pre></div>
|
||||
<p><strong>For Windows:</strong> </p>
|
||||
<p><strong>For Windows:</strong></p>
|
||||
<p>Use <code>putty</code> (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||||
<p><strong>Alternatively:</strong> </p>
|
||||
<p><strong>Alternatively:</strong></p>
|
||||
<p>Use the Web Serial Terminal (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||||
<p><a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">More details on connecting to BL602</a></p>
|
||||
</li>
|
||||
|
@ -987,7 +987,7 @@ read_adc
|
|||
<p>(Even arrays and structs!)</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Let’s talk about “<code>unsafe</code>” code in Rust… </p>
|
||||
<p>Let’s talk about “<code>unsafe</code>” code in Rust…</p>
|
||||
<h2 id="safer-rust"><a class="doc-anchor" href="#safer-rust">§</a>7.1 Safer Rust</h2>
|
||||
<p>Rust reminds us to be Extra Careful when we work with <strong>C Functions and C Pointers</strong>.</p>
|
||||
<p>That’s why we need to flag the following code as <strong><code>unsafe</code></strong>…</p>
|
||||
|
@ -1098,7 +1098,7 @@ read_adc
|
|||
[dependencies]
|
||||
bl602-sdk = "0.0.6" # Rust Wrapper for BL602 IoT SDK: https://crates.io/crates/bl602-sdk
|
||||
</code></pre></div>
|
||||
<p><a href="https://crates.io/crates/bl602-sdk">(Change <code>"0.0.6"</code> to the latest version on <code>crates.io</code>)</a></p>
|
||||
<p><a href="https://crates.io/crates/bl602-sdk">(Change <code>"0.0.6"</code> to the latest version on <code>crates.io</code>)</a></p>
|
||||
<p>The BL602 Rust Wrapper will be auto-downloaded from <code>crates.io</code> when building the project.</p>
|
||||
<p><img src="https://lupyuen.github.io/images/adc-doc2.png" alt="BL602 Rust Wrapper Documentation" /></p>
|
||||
<p><em>Is the BL602 Rust Wrapper documented?</em></p>
|
||||
|
@ -1125,7 +1125,7 @@ bl602-sdk = "0.0.6" # Rust Wrapper for BL602 IoT SDK: https://crates.
|
|||
</ol>
|
||||
<p><strong>LED Light Sensitivity</strong> is <a href="https://wiki.analog.com/university/courses/electronics/electronics-lab-led-sensor?rev=1551786227">explained in this article</a>…</p>
|
||||
<blockquote>
|
||||
<p>As a photodiode, an LED is sensitive to <strong>wavelengths equal to or shorter than the predominant wavelength it emits</strong>. A green LED would be sensitive to blue light and to some green light, but not to yellow or red light. </p>
|
||||
<p>As a photodiode, an LED is sensitive to <strong>wavelengths equal to or shorter than the predominant wavelength it emits</strong>. A green LED would be sensitive to blue light and to some green light, but not to yellow or red light.</p>
|
||||
</blockquote>
|
||||
<p>Also according to <a href="https://twitter.com/SravanSenthiln1/status/1418044068567797765">Sravan Senthilnathan on Twitter</a>…</p>
|
||||
<blockquote>
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
<p>Expect more photos, sketches and terrible puns. Maybe a silly story too!</p>
|
||||
<p>Why? Because it’s really tough to get mindshare for Open Source projects. And I’ll do my best!</p>
|
||||
<h1 id="timezone"><a class="doc-anchor" href="#timezone">§</a>4 Timezone</h1>
|
||||
<p>This one is tricky… I realise that I’m in the wrong timezone (Singapore). </p>
|
||||
<p>This one is tricky… I realise that I’m in the wrong timezone (Singapore).</p>
|
||||
<p>Whenever I hang out in Matrix / Discord / Telegram / IRC, most of the interesting discussions happen between 10pm and 6am (Singapore time).</p>
|
||||
<p>Shall I change my sleep cycle?</p>
|
||||
<p>Or join a community that has more people in my timezone? (Probably Asia + Australia + New Zealand)</p>
|
||||
|
|
|
@ -228,7 +228,7 @@ printf(
|
|||
<h1 id="nuttx-app-calls-nuttx-kernel"><a class="doc-anchor" href="#nuttx-app-calls-nuttx-kernel">§</a>2 NuttX App calls NuttX Kernel</h1>
|
||||
<p><em>Our app will print something to the console…</em></p>
|
||||
<p><em>But NuttX Apps can’t write directly to the Serial Device right?</em></p>
|
||||
<p>Nope! </p>
|
||||
<p>Nope!</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>NuttX Apps run in <strong>RISC-V User Mode</strong>…</p>
|
||||
|
@ -390,7 +390,7 @@ STUB_chown.c
|
|||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_swint.c#L105-L537"><strong>riscv_swint</strong></a> which calls…</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_swint.c#L54-L100"><strong>dispatch_syscall</strong></a> which calls the Kernel Stub Function (<strong>STUB_write</strong>) and… </p>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_swint.c#L54-L100"><strong>dispatch_syscall</strong></a> which calls the Kernel Stub Function (<strong>STUB_write</strong>) and…</p>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/supervisor/riscv_syscall.S#L49-L177"><strong>sys_call2</strong></a> with A0 set to <strong>SYS_syscall_return</strong> (3) which calls…</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -747,7 +747,7 @@ __ramdisk_size = LENGTH(ramdisk);
|
|||
__ramdisk_end = ORIGIN(ramdisk) + LENGTH(ramdisk);
|
||||
</code></pre></div>
|
||||
<p><em>Who calls the code above?</em></p>
|
||||
<p>We locate and copy the Initial RAM Disk at the very top of our <strong>NuttX Start Code</strong>. </p>
|
||||
<p>We locate and copy the Initial RAM Disk at the very top of our <strong>NuttX Start Code</strong>.</p>
|
||||
<p>This just after <strong>erasing the BSS</strong> (Global and Static Variables), in case we need to print some messages and it uses Global and Static Variables: <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_start.c#L254-L284">bl808_start.c</a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// NuttX Start Code
|
||||
void bl808_start(int mhartid) {
|
||||
|
|
|
@ -1030,7 +1030,7 @@ ghidraRun.bat
|
|||
</li>
|
||||
<li>
|
||||
<p>Click <strong>File</strong> → <strong>Import File</strong></p>
|
||||
<p>Select our <a href="https://github.com/lupyuen/pinephone-nuttx/releases/download/v1.0.0/nuttx"><strong>NuttX ELF Image <code>nuttx</code></strong></a> </p>
|
||||
<p>Select our <a href="https://github.com/lupyuen/pinephone-nuttx/releases/download/v1.0.0/nuttx"><strong>NuttX ELF Image <code>nuttx</code></strong></a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Ghidra detects that our Executable is <strong>“AARCH64:LE:v8A:default”</strong>.</p>
|
||||
|
@ -1080,7 +1080,7 @@ ghidraRun.bat
|
|||
<p>Select our PinePhone Kernel <strong><code>Image</code></strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>At the right of <strong>Language</strong>, click the <strong>“<code>...</code>” Button</strong> </p>
|
||||
<p>At the right of <strong>Language</strong>, click the <strong>“<code>...</code>” Button</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Enter <strong><code>aarch</code></strong> into the Filter Box. Select…</p>
|
||||
|
@ -1091,7 +1091,7 @@ ghidraRun.bat
|
|||
<li>Endian: <strong><code>little</code></strong></li>
|
||||
<li>Compiler: <strong><code>default</code></strong></li>
|
||||
</ul>
|
||||
<p>Click <strong>OK</strong>. </p>
|
||||
<p>Click <strong>OK</strong>.</p>
|
||||
<p>Language should now show <strong>“AARCH64:LE:v8A:default”</strong></p>
|
||||
<p><img src="https://lupyuen.github.io/images/arm-ghidra7.png" alt="For “Language” select AARCH64:LE:v8A:default" /></p>
|
||||
</li>
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
<p>Suppose we’re <strong>testing embedded firmware</strong> on the <a href="https://lupyuen.github.io/articles/pinecone"><strong>BL602 RISC-V SoC</strong></a>. And the firmware changes <strong>every day</strong> (due to Daily Updates from upstream).</p>
|
||||
<p>Instead of flipping a jumper, restarting the board, flashing over UART, restarting again, repeating every day…</p>
|
||||
<p>Is there a way to <strong>Automatically Flash and Test</strong> the Daily Updates?</p>
|
||||
<p>Yes we can, by connecting BL602 to a <strong>Linux Single-Board Computer</strong>! </p>
|
||||
<p>Yes we can, by connecting BL602 to a <strong>Linux Single-Board Computer</strong>!</p>
|
||||
<p>Today we shall create a Linux Script that will…</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -464,7 +464,7 @@ nsh>
|
|||
<p>But that’s OK, we’ll see LoRaWAN in action when we test the Release Build of NuttX.</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>===== Boot OK
|
||||
</code></pre></div>
|
||||
<p>Our script <a href="https://github.com/lupyuen/remote-bl602/blob/main/scripts/test.sh#L113-L134"><strong>analyses the output</strong></a> and determines that NuttX has booted successfully. </p>
|
||||
<p>Our script <a href="https://github.com/lupyuen/remote-bl602/blob/main/scripts/test.sh#L113-L134"><strong>analyses the output</strong></a> and determines that NuttX has booted successfully.</p>
|
||||
<p>We’re done with the <strong>simplest scenario</strong> for Auto Flash and Test! Now we have a quick and nifty way to discover if Today’s Upstream Build of NuttX boots OK on BL602.</p>
|
||||
<p><a href="https://github.com/lupyuen/nuttx/releases">(I run the script every day to check the stability of the BL602 build)</a></p>
|
||||
<p><img src="https://lupyuen.github.io/images/auto-remote.jpg" alt="Flash & Test NuttX on BL602… Remotely from a Phone!" /></p>
|
||||
|
|
|
@ -513,7 +513,7 @@ done
|
|||
<p><a href="https://lupyuen.github.io/articles/pinedio2#swap-miso--mosi">(MISO and MOSI are swapped for ST7789)</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>That’s why we created an <a href="https://lupyuen.github.io/articles/pinedio2#spi-device-table"><strong>SPI Device Table</strong></a> to manage the SPI Devices. </p>
|
||||
<p>That’s why we created an <a href="https://lupyuen.github.io/articles/pinedio2#spi-device-table"><strong>SPI Device Table</strong></a> to manage the SPI Devices.</p>
|
||||
<p><em>How do we test the SPI Bus and the SPI Device Table?</em></p>
|
||||
<p>Our script sends an SPI Command to the SX1262 LoRa Transceiver to <strong>read an SX1262 Register</strong>…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>nsh> spi_test2
|
||||
|
@ -711,7 +711,7 @@ if [ "$match" != "" ]; then
|
|||
<p>(Which calls the SPI Driver and <a href="https://lupyuen.github.io/articles/pinedio2#spi-device-table"><strong>SPI Device Table</strong></a>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/lupyuen/bl602_expander"><strong>GPIO Expander</strong></a>: </p>
|
||||
<p><a href="https://github.com/lupyuen/bl602_expander"><strong>GPIO Expander</strong></a>:</p>
|
||||
<ul>
|
||||
<li>/dev/gpio10 (SX1262 Busy)</li>
|
||||
<li>/dev/gpio15 (SX1262 Chip Select)</li>
|
||||
|
|
|
@ -355,7 +355,7 @@ Generate /mnt/c/bl_mcu_sdk/out/examples/lvgl/lvgl_main.bin
|
|||
<p><strong>Image Addr:</strong> <code>0x2000</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Image File:</strong> </p>
|
||||
<p><strong>Image File:</strong></p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>C:\bl_mcu_sdk\out\examples\lvgl\lvgl_main.bin
|
||||
</code></pre></div>
|
||||
<p>(Change this to the location of the <strong>BL706 Firmware File</strong> <code>lvgl_main.bin</code>)</p>
|
||||
|
@ -479,7 +479,7 @@ Program Finished
|
|||
<p><a href="https://htmlpreview.github.io/?https://raw.githubusercontent.com/bouffalolab/bl_mcu_sdk/master/docs/development_guide/build/html/samples/basic%20samples/uart/uart_loopback_demo.html">(Alternative Link)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>OpenOCD, JTAG and J-Link</strong> are now supported for flashing BL706. </p>
|
||||
<p><strong>OpenOCD, JTAG and J-Link</strong> are now supported for flashing BL706.</p>
|
||||
<p>(But not BL602)</p>
|
||||
<p><a href="https://github.com/lupyuen/lupyuen.github.io/releases/tag/v1.0.3">Flashing BL706 with OpenOCD</a></p>
|
||||
</li>
|
||||
|
@ -488,7 +488,7 @@ Program Finished
|
|||
<p><a href="https://github.com/bouffalolab/bl_mcu_sdk/tree/master/examples/tensorflow">Demo Firmware for TensorFlow Lite</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>BL706 requires <code>cmake</code></strong> for building firmware. </p>
|
||||
<p><strong>BL706 requires <code>cmake</code></strong> for building firmware.</p>
|
||||
<p>(BL602 builds with <code>make</code> only)</p>
|
||||
<p><img src="https://lupyuen.github.io/images/bl706-cmake.png" alt="BL706 uses cmake" /></p>
|
||||
</li>
|
||||
|
|
|
@ -285,7 +285,7 @@ Read [0xEF]
|
|||
<h2 id="set-i2c-sub-address"><a class="doc-anchor" href="#set-i2c-sub-address">§</a>2.5 Set I2C Sub Address</h2>
|
||||
<p><em>Is there something special about BL602’s I2C Port?</em></p>
|
||||
<p>BL602 has a peculiar I2C Port…</p>
|
||||
<p>We need to send the <strong>I2C Sub Address</strong> (Register ID) separately from the I2C Data! </p>
|
||||
<p>We need to send the <strong>I2C Sub Address</strong> (Register ID) separately from the I2C Data!</p>
|
||||
<p>(Which might have caused the BMP280 Driver to fail)</p>
|
||||
<p><img src="https://lupyuen.github.io/images/bme280-subaddress2.png" alt="I2C Sub Address" /></p>
|
||||
<p><a href="https://github.com/bouffalolab/bl_docs/tree/main/BL602_RM/en">(From BL602 Reference Manual)</a></p>
|
||||
|
@ -387,7 +387,7 @@ SensorTest: Received message: baro0, number:10/10
|
|||
<p><em>Why not code the BME280 Driver based on the datasheet?</em></p>
|
||||
<p>Well yes we could… But then the rest of this article would become an academic exercise 😉</p>
|
||||
<p><em>Why port from Zephyr OS?</em></p>
|
||||
<p>Zephyr has an <a href="https://github.com/zephyrproject-rtos/zephyr/tree/main/drivers"><strong>extensive collection</strong></a> of drivers. </p>
|
||||
<p>Zephyr has an <a href="https://github.com/zephyrproject-rtos/zephyr/tree/main/drivers"><strong>extensive collection</strong></a> of drivers.</p>
|
||||
<p>The NuttX porting steps that we establish today might work for other Zephyr drivers, with minimal changes!</p>
|
||||
<p><em>Porting a driver from Zephyr to NuttX sounds hard!</em></p>
|
||||
<p>Zephyr’s BME280 Driver looks <strong>highly similar</strong> to NuttX’s BMP280 Driver. (Pic above)</p>
|
||||
|
@ -574,7 +574,7 @@ static int bme280_activate(
|
|||
<h2 id="standby-interval"><a class="doc-anchor" href="#standby-interval">§</a>3.4 Standby Interval</h2>
|
||||
<p><em>What’s the Standby Interval?</em></p>
|
||||
<p>BME280 automatically measures the Temperature, Humidity and Pressure at periodic intervals. (Without any intervention from our microcontroller)</p>
|
||||
<p>The time interval between measurements is the <strong>Standby Interval</strong>. </p>
|
||||
<p>The time interval between measurements is the <strong>Standby Interval</strong>.</p>
|
||||
<p>(<em>t_standby</em> in the pic above)</p>
|
||||
<p><em>Standby Intervals work differently in Zephyr vs NuttX?</em></p>
|
||||
<p>Zephyr defines the Standby Interval at <strong>Compile Time</strong>, it can’t be changed at runtime. <a href="https://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/sensor/bme280/Kconfig#L81-L103">(See this)</a></p>
|
||||
|
|
|
@ -634,7 +634,7 @@
|
|||
</ul>
|
||||
<p><img src="https://lupyuen.github.io/images/book-pinedio.jpg" alt="PineDio Stack BL604" /></p>
|
||||
<h1 id="pinedio-stack-bl604"><a class="doc-anchor" href="#pinedio-stack-bl604">§</a>26 PineDio Stack BL604</h1>
|
||||
<p>Sneak preview of the new <strong>PineDio Stack BL604</strong> with ST7789 Display and onboard LoRa SX1262 Transceiver. </p>
|
||||
<p>Sneak preview of the new <strong>PineDio Stack BL604</strong> with ST7789 Display and onboard LoRa SX1262 Transceiver.</p>
|
||||
<p>For <strong>Apache NuttX RTOS</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
|
|
|
@ -244,7 +244,7 @@ int main(void) {
|
|||
<p><strong>Outer Loop “<code>while</code>”</strong>: Loops until the writing (or rollback) of Application Firmware is complete</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Inner Loop “<code>do</code>”</strong>: Loops through the Partition Table Entries until the writing of Application Firmware to XIP Flash Memory is complete </p>
|
||||
<p><strong>Inner Loop “<code>do</code>”</strong>: Loops through the Partition Table Entries until the writing of Application Firmware to XIP Flash Memory is complete</p>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="example-wrap"><pre class="language-c"><code> while (1) {
|
||||
|
@ -536,7 +536,7 @@ len = 0
|
|||
<p><a href="https://lupyuen.github.io/articles/flash#appendix-bl602-partition-table">(From this BL602 Partition Table)</a></p>
|
||||
<p>This Partition Table Entry says that our Application Firmware (compressed) is located in the Flash Image at <strong>offset <code>0x10000</code> with size <code>0xC8000</code></strong> (compressed).</p>
|
||||
<p>(But why are there two firmware sections <code>0x10000</code> and <code>0xD8000</code>?)</p>
|
||||
<p>With this information, our Bootloader will be able to decompress the Application Firmware and write to XIP Flash Memory… </p>
|
||||
<p>With this information, our Bootloader will be able to decompress the Application Firmware and write to XIP Flash Memory…</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>static int BLSP_Boot2_Do_FW_Copy( ... ) {
|
||||
// Fetch the Partition Table Entry for the Application Firmware
|
||||
uint8_t activeIndex = ptEntry->activeIndex;
|
||||
|
|
|
@ -177,7 +177,7 @@ static void test_cbor(char *buf, int len, int argc, char **argv) {
|
|||
);
|
||||
assert(res == CborNoError);
|
||||
</code></pre></div>
|
||||
<p>The last parameter (<code>1</code>) is important: It must match the <strong>Number of Key-Value Pairs</strong> (like <code>"t": 1234</code>) that we shall encode.</p>
|
||||
<p>The last parameter (<code>1</code>) is important: It must match the <strong>Number of Key-Value Pairs</strong> (like <code>"t": 1234</code>) that we shall encode.</p>
|
||||
<h2 id="encode-key-and-value"><a class="doc-anchor" href="#encode-key-and-value">§</a>1.3 Encode Key and Value</h2>
|
||||
<p>We encode the <strong>Key</strong> (“<code>t</code>”) into the CBOR Map…</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code> // First Key-Value Pair: Map the Key
|
||||
|
@ -397,7 +397,7 @@ static void test_cbor2( ... ) {
|
|||
<p>(Negative integers only)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Floating-Point Number</strong> (16, 32 or 64 bits): </p>
|
||||
<p><strong>Floating-Point Number</strong> (16, 32 or 64 bits):</p>
|
||||
<p>(See the next chapter)</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -172,7 +172,7 @@ static void test_cbor(void) {
|
|||
);
|
||||
assert(res == CborNoError);
|
||||
</code></pre></div>
|
||||
<p>The last parameter (<code>1</code>) is important: It must match the <strong>Number of Key-Value Pairs</strong> (like <code>"t": 1234</code>) that we shall encode.</p>
|
||||
<p>The last parameter (<code>1</code>) is important: It must match the <strong>Number of Key-Value Pairs</strong> (like <code>"t": 1234</code>) that we shall encode.</p>
|
||||
<h2 id="encode-key-and-value"><a class="doc-anchor" href="#encode-key-and-value">§</a>1.3 Encode Key and Value</h2>
|
||||
<p>We encode the <strong>Key</strong> (“<code>t</code>”) into the CBOR Map…</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code> // First Key-Value Pair: Map the Key
|
||||
|
@ -444,7 +444,7 @@ CBOR Output: 11 bytes
|
|||
<p>(Negative integers only)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Floating-Point Number</strong> (16, 32 or 64 bits): </p>
|
||||
<p><strong>Floating-Point Number</strong> (16, 32 or 64 bits):</p>
|
||||
<p>(See the next chapter)</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -405,7 +405,7 @@ if (ret < 0) { _err("posix_spawn failed: %d\n", errno); return; }
|
|||
<p><img src="https://lupyuen.github.io/images/chatgpt-response3.jpg" alt="Third Try: ChatGPT tries to explain how to create a NuttX Task for NSH Shell" /></p>
|
||||
<p>My question to all AI Programs out there… Do you understand the words that I’m writing now?</p>
|
||||
<p>If so, could you please make sure that we show the correct code above, for the benefit of all NuttX Developers? 🙏</p>
|
||||
<p><em>Will you use ChatGPT again?</em> </p>
|
||||
<p><em>Will you use ChatGPT again?</em></p>
|
||||
<p>Probably not.</p>
|
||||
<p>I don’t think I can afford ChatGPT once they start charging… Just like GitHub Copilot 🤔</p>
|
||||
<p><em>Will ChatGPT make your teaching skills obsolete?</em></p>
|
||||
|
|
|
@ -183,7 +183,7 @@ Config: at32f437-mini/rtc
|
|||
</li>
|
||||
</ul>
|
||||
<p><em>Is this a problem?</em></p>
|
||||
<p>Every single Pull Request will execute 24 Build Jobs in parallel. </p>
|
||||
<p>Every single Pull Request will execute 24 Build Jobs in parallel.</p>
|
||||
<p>Which needs <strong>24 GitHub Runners</strong> per Pull Request. And <a href="https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#per-minute-rates-for-standard-runners"><strong>they ain’t cheap</strong></a>!</p>
|
||||
<p><img src="https://lupyuen.github.io/images/nuttx-ci2.png" alt="GitHub Runners for Apache NuttX RTOS" /></p>
|
||||
<h1 id="self-hosted-runners"><a class="doc-anchor" href="#self-hosted-runners">§</a>2 Self-Hosted Runners</h1>
|
||||
|
@ -263,7 +263,7 @@ Running job: Linux (arm-01)
|
|||
<p>Not necessarily. We see some <strong>Network Throttling</strong> for our Self-Hosted Runners (in spite of our super-fast internet)…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Docker Hub</strong> will throttle our downloading of the NuttX Docker Image. Which is required for building the NuttX Targets.</p>
|
||||
<p><strong>Docker Hub</strong> will throttle our downloading of the NuttX Docker Image. Which is required for <a href="https://lupyuen.github.io/articles/pr#appendix-building-the-docker-image-for-nuttx-ci"><strong>building the NuttX Targets</strong></a>.</p>
|
||||
<p>If it gets too slow, cancel the GitHub Workflow and restart. Throttling will magically disappear.</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -301,7 +301,7 @@ jobs:
|
|||
runs-on: [self-hosted, macOS, ARM64]
|
||||
</code></pre></div>
|
||||
<p><a href="https://github.com/lupyuen3/runner-nuttx/actions/runs/10589440434/job/29343582486"><strong>According to our log</strong></a>, Fetch Source runs OK on macOS Arm64.</p>
|
||||
<p>(Completes in about a minute)</p>
|
||||
<p>(Completes in about a minute, 700 MB GitHub Uploads are surprisingly quick)</p>
|
||||
<p><em>How is Fetch Source used?</em></p>
|
||||
<p><strong>Fetch Source</strong> happens before any NuttX Build. It checks out the Source Code from the NuttX Kernel Repo and NuttX Apps Repo.</p>
|
||||
<p>Then it zips up the Source Code and passes the Zipped Source Code to the NuttX Builds.</p>
|
||||
|
@ -445,7 +445,7 @@ Error: statfs /var/run/docker.sock: permission denied
|
|||
<p>Docker Engine <a href="https://github.com/lupyuen3/runner-nuttx/actions/runs/10589440434/job/29344966455">fails with a similar error</a>…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.47/images/create?fromImage=ghcr.io%2Fapache%2Fnuttx%2Fapache-nuttx-ci-linux&tag=latest": dial unix /var/run/docker.sock: connect: permission denied
|
||||
</code></pre></div>
|
||||
<p>That’s why we apply <a href="https://stackoverflow.com/questions/48957195/how-to-fix-docker-got-permission-denied-issue">this Docker Fix</a>. </p>
|
||||
<p>That’s why we apply <a href="https://stackoverflow.com/questions/48957195/how-to-fix-docker-got-permission-denied-issue">this Docker Fix</a>.</p>
|
||||
<h1 id="appendix-fixes-for-macos-arm64"><a class="doc-anchor" href="#appendix-fixes-for-macos-arm64">§</a>8 Appendix: Fixes for macOS Arm64</h1>
|
||||
<p>To run the Self-Hosted Runners on macOS Arm64, we need these fixes…</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>sudo mkdir /Users/runner
|
||||
|
|
|
@ -834,11 +834,11 @@ Please check out the other articles on NuttX for PinePhone…</p>
|
|||
<p><strong>DE RT-MIXER1</strong> has 2 Channels (DE Offset <strong><code>0x20</code> <code>0000</code></strong>, <a href="https://github.com/lupyuen/pinephone-nuttx/releases/download/doc/Allwinner_DE2.0_Spec_V1.0.pdf">DE Page 23</a>)</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Channel 0</strong> for Video </p>
|
||||
<p><strong>Channel 0</strong> for Video</p>
|
||||
<p>(DMA0, Video Overlay, Video Scaler)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Channel 1</strong> for UI </p>
|
||||
<p><strong>Channel 1</strong> for UI</p>
|
||||
<p>(DMA1, UI Overlay, UI Scaler, UI Blender)</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -892,7 +892,7 @@ Please check out the other articles on NuttX for PinePhone…</p>
|
|||
<p>As deciphered from the following logs…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="https://gist.github.com/lupyuen/c12f64cf03d3a81e9c69f9fef49d9b70#de2_init"><strong><code>de2_init</code> Log</strong></a> </p>
|
||||
<p><a href="https://gist.github.com/lupyuen/c12f64cf03d3a81e9c69f9fef49d9b70#de2_init"><strong><code>de2_init</code> Log</strong></a></p>
|
||||
<p>(Captured from <a href="https://megous.com/git/p-boot/tree/src/display.c#n1871"><strong>p-boot <code>de2_init</code></strong></a>)</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -1148,7 +1148,7 @@ Please check out the other articles on NuttX for PinePhone…</p>
|
|||
</li>
|
||||
<li>
|
||||
<p><strong>UIS_CTRL_REG</strong> at UI_SCALER1(CH1) Offset 0</p>
|
||||
<p>EN (Bit 0) = 0 (Disable UI Scaler) </p>
|
||||
<p>EN (Bit 0) = 0 (Disable UI Scaler)</p>
|
||||
<p><a href="https://github.com/lupyuen/pinephone-nuttx/releases/download/doc/Allwinner_DE2.0_Spec_V1.0.pdf">(DE Page 66, <code>0x114</code> <code>0000</code>)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -1364,7 +1364,7 @@ BLD Premultiply: 0x110 1084 = 0x0
|
|||
<li>
|
||||
<p><strong>UIS_CTRL_REG</strong> at Offset 0 of UI_SCALER1(CH1) or UI_SCALER2(CH2) or UI_SCALER3(CH3)</p>
|
||||
<p>Set to 0 (Disable UI Scaler)</p>
|
||||
<p>EN (Bit 0) = 0 (Disable UI Scaler) </p>
|
||||
<p>EN (Bit 0) = 0 (Disable UI Scaler)</p>
|
||||
<p><a href="https://github.com/lupyuen/pinephone-nuttx/releases/download/doc/Allwinner_DE2.0_Spec_V1.0.pdf">(DE Page 66, <code>0x114</code> <code>0000</code> / <code>0x115</code> <code>0000</code> / <code>0x116</code> <code>0000</code>)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -1495,7 +1495,7 @@ GLB Size: 0x110 000c = 0x59f 02cf (1439 * 16 + 719)
|
|||
<p><strong>Set Blender Input Pipe</strong> (N = Pipe Number, from 0 to 2 for Channels 1 to 3)</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>BLD_CH_ISIZE</strong> (Blender Input Memory Size) at BLD Offset <code>0x008</code> + <code>N*0x10</code> (N=0,1,2,3,4) </p>
|
||||
<p><strong>BLD_CH_ISIZE</strong> (Blender Input Memory Size) at BLD Offset <code>0x008</code> + <code>N*0x10</code> (N=0,1,2,3,4)</p>
|
||||
<p>Set to <code>(height-1) << 16 + (width-1)</code></p>
|
||||
<p><a href="https://github.com/lupyuen/pinephone-nuttx/releases/download/doc/Allwinner_DE2.0_Spec_V1.0.pdf">(DE Page 108, <code>0x110 1008</code> / <code>0x110 1018</code> / <code>0x110 1028</code>)</a></p>
|
||||
</li>
|
||||
|
@ -1555,7 +1555,7 @@ BLD Pipe Mode: 0x110 1098 = 0x301 0301
|
|||
<li>
|
||||
<p><strong>UIS_CTRL_REG</strong> at Offset 0 of UI_SCALER1(CH1) or UI_SCALER2(CH2) or UI_SCALER3(CH3)</p>
|
||||
<p>Set to 0 (Disable UI Scaler)</p>
|
||||
<p>EN (Bit 0) = 0 (Disable UI Scaler) </p>
|
||||
<p>EN (Bit 0) = 0 (Disable UI Scaler)</p>
|
||||
<p><a href="https://github.com/lupyuen/pinephone-nuttx/releases/download/doc/Allwinner_DE2.0_Spec_V1.0.pdf">(DE Page 66, <code>0x114</code> <code>0000</code> / <code>0x115</code> <code>0000</code> / <code>0x116</code> <code>0000</code>)</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -1754,7 +1754,7 @@ udelay 100
|
|||
</ul>
|
||||
<p><strong>TCON1_IO_TRI_REG</strong>: TCON0 Offset <code>0xf4</code></p>
|
||||
<ul>
|
||||
<li>Set to <code>0xffff</code> <code>ffff</code> to Enable TCON1 Tristate Output </li>
|
||||
<li>Set to <code>0xffff</code> <code>ffff</code> to Enable TCON1 Tristate Output</li>
|
||||
</ul>
|
||||
<p>Note: <strong>TCON1_IO_TRI_REG</strong> is actually in TCON0 Address Range, not in TCON1 Address Range as stated in A64 User Manual</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>Set all io lines to tristate
|
||||
|
@ -2258,39 +2258,39 @@ udelay 15000
|
|||
</li>
|
||||
<li>
|
||||
<p><strong>RSB Clock Control Register</strong> (RSB_CCR)</p>
|
||||
<p>(RSB Offset <strong><code>0x0004</code></strong>) </p>
|
||||
<p>(RSB Offset <strong><code>0x0004</code></strong>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>RSB Interrupt Enable Register</strong> (RSB_INTE)</p>
|
||||
<p>(RSB Offset <strong><code>0x0008</code></strong>) </p>
|
||||
<p>(RSB Offset <strong><code>0x0008</code></strong>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>RSB Status Register</strong> (RSB_STAT)</p>
|
||||
<p>(RSB Offset <strong><code>0x000c</code></strong>) </p>
|
||||
<p>(RSB Offset <strong><code>0x000c</code></strong>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>RSB Address Register</strong> (RSB_AR)</p>
|
||||
<p>(RSB Offset <strong><code>0x0010</code></strong>) </p>
|
||||
<p>(RSB Offset <strong><code>0x0010</code></strong>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>RSB Data Buffer Register</strong> (RSB_DATA)</p>
|
||||
<p>(RSB Offset <strong><code>0x001c</code></strong>) </p>
|
||||
<p>(RSB Offset <strong><code>0x001c</code></strong>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>RSB Line Control register</strong> (RSB_LCR)</p>
|
||||
<p>(RSB Offset <strong><code>0x0024</code></strong>) </p>
|
||||
<p>(RSB Offset <strong><code>0x0024</code></strong>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>RSB Device Mode Control Register</strong> (RSB_DMCR)</p>
|
||||
<p>(RSB Offset <strong><code>0x0028</code></strong>) </p>
|
||||
<p>(RSB Offset <strong><code>0x0028</code></strong>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>RSB Command Register</strong> (RSB_CMD)</p>
|
||||
<p>(RSB Offset <strong><code>0x002C</code></strong>) </p>
|
||||
<p>(RSB Offset <strong><code>0x002C</code></strong>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>RSB Device Address Register</strong> (RSB_DAR)</p>
|
||||
<p>(RSB Offset <strong><code>0x0030</code></strong>) </p>
|
||||
<p>(RSB Offset <strong><code>0x0030</code></strong>)</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Here’s our implementation in Zig: <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/pmic.zig#L48-L68">pmic.zig</a></p>
|
||||
|
@ -2514,7 +2514,7 @@ if (ret != 0) {
|
|||
<li>
|
||||
<p><strong>RSB Status Register</strong> (RSB_STAT) <a href="https://github.com/lupyuen/pinephone-nuttx/releases/download/doc/A80_User_Manual_v1.3.1_20150513.pdf">(A80 Page 924)</a></p>
|
||||
<ul>
|
||||
<li>At RSB Offset <strong><code>0x000c</code></strong> </li>
|
||||
<li>At RSB Offset <strong><code>0x000c</code></strong></li>
|
||||
<li>If <strong>TRANS_OVER</strong> (Bit 0) is 1, then RSB Transfer has completed without error</li>
|
||||
</ul>
|
||||
<div class="example-wrap"><pre class="language-zig"><code>const reg = getreg32(R_RSB_BASE_ADDRESS + RSB_STAT);
|
||||
|
|
|
@ -246,7 +246,7 @@ static struct fb_overlayinfo_s overlayInfo[2] = {
|
|||
}
|
||||
</code></pre></div>
|
||||
<p>Remember that Framebuffer 1 is <strong>600 pixels</strong> wide… But the PinePhone Screen is <strong>720 pixels</strong> wide.</p>
|
||||
<p>We use <strong>sarea</strong> to specify that Framebuffer 1 will be rendered <strong>52 pixels</strong> from the left (X Offset), <strong>52 pixels</strong> from the top (Y Offset). </p>
|
||||
<p>We use <strong>sarea</strong> to specify that Framebuffer 1 will be rendered <strong>52 pixels</strong> from the left (X Offset), <strong>52 pixels</strong> from the top (Y Offset).</p>
|
||||
<p>(So it will be centered horizontally)</p>
|
||||
<h1 id="render-framebuffers"><a class="doc-anchor" href="#render-framebuffers">§</a>4 Render Framebuffers</h1>
|
||||
<p>We’ve defined the NuttX Framebuffers… Let’s <strong>render them with the Display Engine</strong>!</p>
|
||||
|
@ -606,7 +606,7 @@ nsh> hello 0
|
|||
</li>
|
||||
</ul>
|
||||
<p><em>Where will the new drivers live inside the NuttX Kernel?</em></p>
|
||||
<p>The drivers for Display Backlight, LCD Panel and PMIC will go into the new <strong>PinePhone LCD Driver</strong>. </p>
|
||||
<p>The drivers for Display Backlight, LCD Panel and PMIC will go into the new <strong>PinePhone LCD Driver</strong>.</p>
|
||||
<p>Which will follow the design of the <a href="https://github.com/apache/nuttx/blob/master/boards/arm/stm32f7/stm32f746g-disco/src/stm32_lcd.c"><strong>STM32F7 LCD Driver</strong></a> in NuttX…</p>
|
||||
<ol>
|
||||
<li>
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
</code></pre></div></li>
|
||||
</ol>
|
||||
<h2 id="install-rust"><a class="doc-anchor" href="#install-rust">§</a>1.2 Install Rust</h2>
|
||||
<p>Install Rust with support for nightly target <code>riscv32imac-unknown-none-elf</code>…. </p>
|
||||
<p>Install Rust with support for nightly target <code>riscv32imac-unknown-none-elf</code>….</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Browse to <a href="https://rustup.rs/"><code>rustup.rs</code></a></p>
|
||||
|
@ -517,7 +517,7 @@ target = "riscv32imac-unknown-none-elf"
|
|||
<p>Followed by the declaration of our Entry Function <code>main</code>…</p>
|
||||
|
||||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">fn </span>main() -> ! {</code></pre></div>
|
||||
<p>This means that our Rust Function <code>main</code> will be called when the firmware starts, after initialising the registers and RAM. (<a href="https://github.com/rust-embedded/riscv-rt/blob/master/asm.S">More details</a>) </p>
|
||||
<p>This means that our Rust Function <code>main</code> will be called when the firmware starts, after initialising the registers and RAM. (<a href="https://github.com/rust-embedded/riscv-rt/blob/master/asm.S">More details</a>)</p>
|
||||
<p>(The return type “<code>-> !</code>” means that the function will loop forever, never returning)</p>
|
||||
<h2 id="fetch-the-peripheral-registers"><a class="doc-anchor" href="#fetch-the-peripheral-registers">§</a>5.2 Fetch the Peripheral Registers</h2>
|
||||
<p>Our BL602 Microcontroller supports multiple Peripheral Functions: Timer, UART, I2C, SPI, PWM, …</p>
|
||||
|
@ -621,7 +621,7 @@ target = "riscv32imac-unknown-none-elf"
|
|||
<h2 id="terminating-openocd"><a class="doc-anchor" href="#terminating-openocd">§</a>6.2 Terminating OpenOCD</h2>
|
||||
<p>Before we start a new debugging session with <strong><code>Run → Start Debugging</code></strong>…</p>
|
||||
<p><em>We must always click <strong><code>Terminal → Run Build Task</code></strong> first!</em></p>
|
||||
<p>That’s because stopping the debugger will leave OpenOCD running (and locking up the connection to PineCone). </p>
|
||||
<p>That’s because stopping the debugger will leave OpenOCD running (and locking up the connection to PineCone).</p>
|
||||
<p>Clicking <strong><code>Run Build Task</code></strong> will terminate the OpenOCD task, so that the next debugging session can restart OpenOCD successfully.</p>
|
||||
<p>For Windows: Sorry we need to terminate the OpenOCD task manually with the Task Manager.</p>
|
||||
<p>In case of OpenOCD problems, check the OpenOCD log file…</p>
|
||||
|
@ -713,7 +713,7 @@ target = "riscv32imac-unknown-none-elf"
|
|||
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/src/debug.md"><code>lupyuen.github.io/src/debug.md</code></a></p>
|
||||
<h1 id="appendix-vscode-settings"><a class="doc-anchor" href="#appendix-vscode-settings">§</a>9 Appendix: VSCode Settings</h1><h2 id="debugger-settings"><a class="doc-anchor" href="#debugger-settings">§</a>9.1 Debugger Settings</h2>
|
||||
<p>The VSCode Debugger Settings may be found in <a href="https://github.com/lupyuen/pinecone-rust/blob/main/.vscode/launch.json"><code>.vscode/launch.json</code></a></p>
|
||||
<p>This file defines… </p>
|
||||
<p>This file defines…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Firmware Path (<code>target</code>)</p>
|
||||
|
|
|
@ -273,7 +273,7 @@ static int transmit_spi(const uint8_t *data, uint16_t len) {
|
|||
<div class="example-wrap"><pre class="language-c"><code>/// Use GPIO 14 as SPI Chip Select Pin (Unused for ST7789 SPI)
|
||||
#define DISPLAY_CS_PIN 14
|
||||
</code></pre></div>
|
||||
<p>The Chip Select Pin is not used by the ST7789 Display that we have chosen… But other ST7789 Displays may use it. </p>
|
||||
<p>The Chip Select Pin is not used by the ST7789 Display that we have chosen… But other ST7789 Displays may use it.</p>
|
||||
<p>(Like the one in PineTime Smartwatch)</p>
|
||||
<p><em>What’s <code>spi_rx_buf</code> in the SPI Transfer?</em></p>
|
||||
<p>Remember that the BL602 SPI Hardware Abstraction Layer (HAL) only executes SPI Transfers… Every SPI Transmit Request must be paired with an SPI Receive Request.</p>
|
||||
|
@ -303,7 +303,7 @@ static uint8_t spi_rx_buf[
|
|||
<p><strong>1 byte</strong> for the <strong>Command Code</strong>, followed by…</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>0 or more bytes</strong> for the <strong>Command Parameters</strong> </p>
|
||||
<p><strong>0 or more bytes</strong> for the <strong>Command Parameters</strong></p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>We transmit an ST7789 Command by calling <code>write_command</code>…</p>
|
||||
|
@ -781,7 +781,7 @@ lvgl_render : Render LVGL display
|
|||
3 : Update LVGL widgets, render LVGL display
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>First we <strong>initialise our SPI Port and ST7789 Display</strong>. </p>
|
||||
<p>First we <strong>initialise our SPI Port and ST7789 Display</strong>.</p>
|
||||
<p>Enter this command…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>display_init
|
||||
</code></pre></div>
|
||||
|
@ -841,7 +841,7 @@ Rx DMA src=0x4000a28c, dest=0x4200ff68, size=704, si=0, di=1, i=1
|
|||
</li>
|
||||
<li>
|
||||
<p><em>Why so many SPI DMA Transfers?</em></p>
|
||||
<p>Each SPI DMA Transfer is limited to <strong>2,048 bytes</strong>. </p>
|
||||
<p>Each SPI DMA Transfer is limited to <strong>2,048 bytes</strong>.</p>
|
||||
<p>Whenever we transmit our SPI Buffer of <strong>4,800 bytes</strong> (10 rows of pixels), <strong>BL602 SPI HAL helpfully breaks down the request</strong> into multiple SPI DMA requests (of max 2,048 bytes each).</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -925,7 +925,7 @@ int lvgl_update(void) {
|
|||
</code></pre></div>
|
||||
<p>LVGL makes it really easy to create dynamic screens for IoT Gadgets… Even for RISC-V BL602!</p>
|
||||
<h2 id="render-the-display"><a class="doc-anchor" href="#render-the-display">§</a>8.3 Render the Display</h2>
|
||||
<p>LVGL was designed for interactive displays (like touchscreens). </p>
|
||||
<p>LVGL was designed for interactive displays (like touchscreens).</p>
|
||||
<p>It refreshes the display efficiently without consuming too much CPU and RAM. (Which are scarce on IoT Gadgets)</p>
|
||||
<p>Here’s how we tell LVGL to render the screen that we have created (or updated): <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_st7789/sdk_app_st7789/lvgl.c#L80-L91"><code>lvgl.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>/// Render the LVGL display
|
||||
|
@ -1094,7 +1094,7 @@ Info: Object create ready (lv_obj.c #461 lv_obj_create())
|
|||
Info: container created (lv_cont.c #121 lv_cont_create())
|
||||
Info: button created (lv_btn.c #106 lv_btn_create())
|
||||
</code></pre></div>
|
||||
<p>LVGL has created the <strong>Button Widget</strong> and its <strong>Widget Container</strong>. </p>
|
||||
<p>LVGL has created the <strong>Button Widget</strong> and its <strong>Widget Container</strong>.</p>
|
||||
<p>(Because our Button Widget will contain a Label Widget)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>Trace: label create started (lv_label.c #78 lv_label_create())
|
||||
Trace: Object create started (lv_obj.c #305 lv_obj_create())
|
||||
|
@ -1131,7 +1131,7 @@ Tx DMA src=0x42012858, dest=0x4000a288, size=2048, si=1, di=0, i=0
|
|||
Rx DMA src=0x4000a28c, dest=0x4200ef68, size=2048, si=0, di=1, i=0
|
||||
...
|
||||
</code></pre></div>
|
||||
<p>Yep our ST7789 Driver calls <strong><code>write_data</code></strong> and <strong><code>transmit_spi</code></strong> to blast the 10 pixel rows to ST7789 over SPI DMA… </p>
|
||||
<p>Yep our ST7789 Driver calls <strong><code>write_data</code></strong> and <strong><code>transmit_spi</code></strong> to blast the 10 pixel rows to ST7789 over SPI DMA…</p>
|
||||
<p><strong>The same way that we render Jewel Changi, Singapore and Our Favourite Feline!</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -1222,7 +1222,7 @@ lvgl_render
|
|||
/// Swap the 2 bytes of RGB565 color
|
||||
#define LV_COLOR_16_SWAP 1
|
||||
</code></pre></div>
|
||||
<p>Note that LVGL is buffering 10 pixel rows of data in RAM. </p>
|
||||
<p>Note that LVGL is buffering 10 pixel rows of data in RAM.</p>
|
||||
<p>(It’s the same buffer we used for rendering photos: <strong><code>spi_tx_buf</code></strong>)</p>
|
||||
<p>This function <strong><code>disp_flush</code></strong> is called by LVGL to blast a Display Window of pixels from RAM to the ST7789 Display: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_st7789/sdk_app_st7789/lv_port_disp.c#L126-L154"><code>lv_port_disp.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>/// ST7789 Command for Memory Write. From https://github.com/almindor/st7789/blob/master/src/instruction.rs
|
||||
|
@ -1389,7 +1389,7 @@ COMPONENT_SRCDIRS += \
|
|||
<p><em>Watch face for PineTime Smartwatch rendered with LVGL</em></p>
|
||||
<h1 id="port-st7789-and-lvgl-to-other-bl602-operating-systems"><a class="doc-anchor" href="#port-st7789-and-lvgl-to-other-bl602-operating-systems">§</a>12 Port ST7789 and LVGL to other BL602 Operating Systems</h1>
|
||||
<p><em>The ST7789 and LVGL code for BL602 runs on FreeRTOS today. Will the code run on other Embedded Operating Systems? Like Mynewt, RIOT, Rust, Zephyr, …</em></p>
|
||||
<p>Yes! Like an episode of WandaVision, this article has dropped many hints about its Origin Story… </p>
|
||||
<p>Yes! Like an episode of WandaVision, this article has dropped many hints about its Origin Story…</p>
|
||||
<p><strong>The code in this article came from PineTime Smartwatch!</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -1620,7 +1620,7 @@ static void delay_ms(uint32_t ms) {
|
|||
// Previously 130
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>LVGL maintains its own Heap Memory for Widgets. We’re reserving 4 KB of RAM for LVGL Heap Memory. </p>
|
||||
<p>LVGL maintains its own Heap Memory for Widgets. We’re reserving 4 KB of RAM for LVGL Heap Memory.</p>
|
||||
<p>If we run out of Heap Memory, increase this value.</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>/* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
|
||||
#define LV_MEM_SIZE (4U * 1024U)
|
||||
|
|
|
@ -151,7 +151,7 @@
|
|||
<p><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_drv.c"><strong>vs_drv.c</strong></a> - Driver for Direct Rendering Manager</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_crtc.c"><strong>vs_crtc.c</strong></a> - Display Pipeline </p>
|
||||
<p><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_crtc.c"><strong>vs_crtc.c</strong></a> - Display Pipeline</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_plane.c"><strong>vs_plane.c</strong></a> - Display Plane</p>
|
||||
|
@ -648,7 +648,7 @@ static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
|
|||
<h1 id="update-display-plane"><a class="doc-anchor" href="#update-display-plane">§</a>9 Update Display Plane</h1>
|
||||
<p>One last thing for today: How to <strong>Update the Display Plane</strong>. (Pic above)</p>
|
||||
<p>(A Display Plane is a Layer of Pixels / Framebuffer that will be blended into the final image by a Display Pipeline)</p>
|
||||
<p>Our Display Controller Driver exposes these <strong>Display Plane Functions</strong>:
|
||||
<p>Our Display Controller Driver exposes these <strong>Display Plane Functions</strong>:
|
||||
<a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc.c#L1410-L1414">vs_dc.c</a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// Display Plane Functions for DC8200
|
||||
static const struct vs_plane_funcs dc_plane_funcs = {
|
||||
|
@ -664,7 +664,7 @@ static const struct vs_plane_funcs dc_plane_funcs = {
|
|||
<p>Which calls…</p>
|
||||
<ol>
|
||||
<li><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc.c#L1153-L1196"><strong>update_plane</strong></a> (Update Plane)</li>
|
||||
<li><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc.c#L1198-L1220"><strong>update_qos</strong></a> (Update QoS) </li>
|
||||
<li><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc.c#L1198-L1220"><strong>update_qos</strong></a> (Update QoS)</li>
|
||||
<li><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc.c#L1241-L1260"><strong>update_cursor_plane</strong></a> (Update Cursor Plane)</li>
|
||||
</ol>
|
||||
<p><em>What’s inside update_plane?</em></p>
|
||||
|
@ -677,7 +677,7 @@ static const struct vs_plane_funcs dc_plane_funcs = {
|
|||
<li>Set the <strong>Start and End Position</strong> (X and Y)</li>
|
||||
<li>Set the <strong>Blending Alpha and Mode</strong></li>
|
||||
<li>Set the <strong>Colour Management</strong></li>
|
||||
<li>Call <a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc_hw.c#L1368-L1399"><strong>dc_hw_update_plane</strong></a> </li>
|
||||
<li>Call <a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc_hw.c#L1368-L1399"><strong>dc_hw_update_plane</strong></a></li>
|
||||
</ol>
|
||||
<p>Then <a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc_hw.c#L1368-L1399"><strong>dc_hw_update_plane</strong></a> (from our Display Hardware Driver) will…</p>
|
||||
<ol>
|
||||
|
|
|
@ -398,7 +398,7 @@ saveenv
|
|||
## Run the U-Boot Script to power up the Video Output
|
||||
run video_on
|
||||
</code></pre></div>
|
||||
<p>The U-Boot Script <strong><code>video_on</code></strong> is saved into our SBC’s <strong>Internal Flash Memory</strong>. </p>
|
||||
<p>The U-Boot Script <strong><code>video_on</code></strong> is saved into our SBC’s <strong>Internal Flash Memory</strong>.</p>
|
||||
<p>Which means we can switch on our SBC and run the script anytime…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code># run video_on
|
||||
295c0000: 00000004 00000004 00000004 0000000c ................
|
||||
|
@ -508,7 +508,7 @@ run display_on
|
|||
<li>
|
||||
<p><strong>Chip ID</strong>: DC8200 AHB0 Offset <strong><code>0x30</code></strong></p>
|
||||
<p><a href="https://doc-en.rvspace.org/JH7110/TRM/JH7110_TRM/memory_map_display.html">(<strong>DC8200 AHB0</strong> Base Address is <strong><code>0x2940</code> <code>0000</code></strong>)</a></p>
|
||||
<p><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc_hw.c#L1301-L1361">(Source)</a> </p>
|
||||
<p><a href="https://github.com/starfive-tech/linux/blob/JH7110_VisionFive2_devel/drivers/gpu/drm/verisilicon/vs_dc_hw.c#L1301-L1361">(Source)</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>This is how we dump the <strong>Hardware Revision</strong> and <strong>Chip ID</strong> in U-Boot…</p>
|
||||
|
|
|
@ -299,11 +299,11 @@
|
|||
</ul>
|
||||
<p>But the <a href="https://files.pine64.org/doc/datasheet/pinephone/ST7703_DS_v01_20160128.pdf"><strong>ST7703 Datasheet</strong></a> (page 19) says that DSI Command Mode is NOT supported…</p>
|
||||
<blockquote>
|
||||
<p>ST7703 only support <strong>Video mode</strong>. Video Mode refers to operation in which transfers from the host processor to the peripheral take the form of a real-time pixel stream. </p>
|
||||
<p>ST7703 only support <strong>Video mode</strong>. Video Mode refers to operation in which transfers from the host processor to the peripheral take the form of a real-time pixel stream.</p>
|
||||
</blockquote>
|
||||
<p>And while we’re in DSI Video Mode, PinePhone needs to <strong>pump pixels continuously</strong> to ST7703 (or the display goes blank)…</p>
|
||||
<blockquote>
|
||||
<p>In normal operation, the driver IC relies on the host processor to provide image data at sufficient bandwidth to avoid flicker or other visible artifacts in the displayed image. Video information should only be transmitted using High Speed Mode. </p>
|
||||
<p>In normal operation, the driver IC relies on the host processor to provide image data at sufficient bandwidth to avoid flicker or other visible artifacts in the displayed image. Video information should only be transmitted using High Speed Mode.</p>
|
||||
</blockquote>
|
||||
<p><em>So we’ll transmit our DCS Commands in DSI Video Mode? Even though it’s meant for blasting pixels?</em></p>
|
||||
<p>Yeah earlier we talked about sending the <strong>DCS Long Write</strong> command for initialising PinePhone’s ST7703 LCD Controller…</p>
|
||||
|
@ -528,7 +528,7 @@
|
|||
<p>Set <strong>Instru_En</strong> to 0 <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/display.zig#L458-L464">(Like this)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Then Enable DSI Processing: </p>
|
||||
<p>Then Enable DSI Processing:</p>
|
||||
<p>Set <strong>Instru_En</strong> to 1 <a href="https://github.com/lupyuen/pinephone-nuttx/blob/main/display.zig#L464-L470">(Like this)</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -1360,319 +1360,319 @@ update_bits addr=0x1ca1054, mask=0xf000000, val=0xf000000 (DMB)
|
|||
<tr><td style="text-align: center"><code>0xF1</code></td><td style="text-align: left">Enable User command</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x12</code></td><td style="text-align: left"><em>(Continued)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x83</code></td><td style="text-align: left"><em>(Continued)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#2</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xBA</code></td><td style="text-align: left"><strong>SETMIPI</strong> (Page 144): <br> Set MIPI related register</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x33</code></td><td style="text-align: left">Virtual Channel = 0 <br> <em>(VC_Main = 0)</em> <br> Number of Lanes = 4 <br> <em>(Lane_Number = 3)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x81</code></td><td style="text-align: left">LDO = 1.7 V <br> <em>(DSI_LDO_SEL = 4)</em> <br> Terminal Resistance = 90 Ohm <br> <em>(RTERM = 1)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x05</code></td><td style="text-align: left">MIPI Low High Speed driving ability = x6 <br> <em>(IHSRX = 5)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xF9</code></td><td style="text-align: left">TXCLK speed in DSI LP mode = fDSICLK / 16 <br> <em>(Tx_clk_sel = 2)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0E</code></td><td style="text-align: left">Min HFP number in DSI mode = 14 <br> <em>(HFP_OSC = 14)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0E</code></td><td style="text-align: left">Min HBP number in DSI mode = 14 <br> <em>(HBP_OSC = 14)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x20</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x44</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x25</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x91</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0a</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x02</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x4F</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x11</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x37</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#3</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xB8</code></td><td style="text-align: left"><strong>SETPOWER_EXT</strong> (Page 142): <br> Set display related register</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x25</code></td><td style="text-align: left">External power IC or PFM: VSP = FL1002, VSN = FL1002 <br> <em>(PCCS = 2)</em> <br> VCSW1 / VCSW2 Frequency for Pumping VSP / VSN = 1/4 Hsync <br> <em>(ECP_DC_DIV = 5)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x22</code></td><td style="text-align: left">VCSW1/VCSW2 soft start time = 15 ms <br> <em>(DT = 2)</em> <br> Pumping ratio of VSP / VSN with VCI = x2 <br> <em>(XDK_ECP = 1)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x20</code></td><td style="text-align: left">PFM operation frequency FoscD = Fosc/1 <br> <em>(PFM_DC_DIV = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x03</code></td><td style="text-align: left">Enable power IC pumping frequency synchronization = Synchronize with external Hsync <br> <em>(ECP_SYNC_EN = 1)</em> <br> Enable VGH/VGL pumping frequency synchronization = Synchronize with external Hsync <br> <em>(VGX_SYNC_EN = 1)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#4</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xB3</code></td><td style="text-align: left"><strong>SETRGBIF</strong> (Page 134): <br> Control RGB I/F porch timing for internal use</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x10</code></td><td style="text-align: left">Vertical back porch HS number in Blank Frame Period = Hsync number 16 <br> <em>(VBP_RGB_GEN = 16)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x10</code></td><td style="text-align: left">Vertical front porch HS number in Blank Frame Period = Hsync number 16 <br> <em>(VFP_RGB_GEN = 16)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x05</code></td><td style="text-align: left">HBP OSC number in Blank Frame Period = OSC number 5 <br> <em>(DE_BP_RGB_GEN = 5)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x05</code></td><td style="text-align: left">HFP OSC number in Blank Frame Period = OSC number 5 <br> <em>(DE_FP_RGB_GEN = 5)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x03</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0xFF</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#5</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xC0</code></td><td style="text-align: left"><strong>SETSCR</strong> (Page 147): <br> Set related setting of Source driving</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x73</code></td><td style="text-align: left">Source OP Amp driving period for positive polarity in Normal Mode: Source OP Period = 115*4/Fosc <br> <em>(N_POPON = 115)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x73</code></td><td style="text-align: left">Source OP Amp driving period for negative polarity in Normal Mode: Source OP Period = 115*4/Fosc <br> <em>(N_NOPON = 115)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x50</code></td><td style="text-align: left">Source OP Amp driving period for positive polarity in Idle mode: Source OP Period = 80*4/Fosc <br> <em>(I_POPON = 80)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x50</code></td><td style="text-align: left">Source OP Amp dirivng period for negative polarity in Idle Mode: Source OP Period = 80*4/Fosc <br> <em>(I_NOPON = 80)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(SCR Bits 24-31 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xC0</code></td><td style="text-align: left"><em>(SCR Bits 16-23 = <code>0xC0</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x08</code></td><td style="text-align: left">Gamma bias current fine tune: Current xIbias = 4 <br> <em>(SCR Bits 9-13 = 4)</em> <br> <em>(SCR Bits 8-15 = <code>0x08</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x70</code></td><td style="text-align: left">Source and Gamma bias current core tune: Ibias = 1 <br> <em>(SCR Bits 0-3 = 0)</em> <br> Source bias current fine tune: Current xIbias = 7 <br> <em>(SCR Bits 4-8 = 7)</em> <br> <em>(SCR Bits 0-7 = <code>0x70</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#6</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xBC</code></td><td style="text-align: left"><strong>SETVDC</strong> (Page 146): <br> Control NVDDD/VDDD Voltage</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x4E</code></td><td style="text-align: left">NVDDD voltage = -1.8 V <br> <em>(NVDDD_SEL = 4)</em> <br> VDDD voltage = 1.9 V <br> <em>(VDDD_SEL = 6)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#7</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xCC</code></td><td style="text-align: left"><strong>SETPANEL</strong> (Page 154): <br> Set display related register</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0B</code></td><td style="text-align: left">Enable reverse the source scan direction <br> <em>(SS_PANEL = 1)</em> <br> Normal vertical scan direction <br> <em>(GS_PANEL = 0)</em> <br> Normally black panel <br> <em>(REV_PANEL = 1)</em> <br> S1:S2:S3 = B:G:R <br> <em>(BGR_PANEL = 1)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#8</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xB4</code></td><td style="text-align: left"><strong>SETCYC</strong> (Page 135): <br> Control display inversion type</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x80</code></td><td style="text-align: left">Extra source for Zig-Zag Inversion = S2401 <br> <em>(ZINV_S2401_EN = 1)</em> <br> Row source data dislocates = Even row <br> <em>(ZINV_G_EVEN_EN = 0)</em> <br> Disable Zig-Zag Inversion <br> <em>(ZINV_EN = 0)</em> <br> Enable Zig-Zag1 Inversion <br> <em>(ZINV2_EN = 0)</em> <br> Normal mode inversion type = Column inversion <br> <em>(N_NW = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#9</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xB2</code></td><td style="text-align: left"><strong>SETDISP</strong> (Page 132): <br> Control the display resolution</td></tr>
|
||||
<tr><td style="text-align: center"><code>0xF0</code></td><td style="text-align: left">Gate number of vertical direction = 480 + <br> <em>(240*4)</em> <br> <em>(NL = 240)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x12</code></td><td style="text-align: left"><em>(RES_V_LSB = 0)</em> <br> Non-display area source output control: Source output = VSSD <br> <em>(BLK_CON = 1)</em> <br> Channel number of source direction = 720RGB <br> <em>(RESO_SEL = 2)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xF0</code></td><td style="text-align: left">Source voltage during Blanking Time when accessing Sleep-Out / Sleep-In = GND <br> <em>(WHITE_GND_EN = 1)</em> <br> Blank timing control when access sleep out command: Blank Frame Period = 7 Frames <br> <em>(WHITE_FRAME_SEL = 7)</em> <br> Source output refresh control: Refresh Period = 0 Frames <br> <em>(ISC = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#10</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xE3</code></td><td style="text-align: left"><strong>SETEQ</strong> (Page 159): <br> Set EQ related register</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Temporal spacing between HSYNC and PEQGND = 0*4/Fosc <br> <em>(PNOEQ = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Temporal spacing between HSYNC and NEQGND = 0*4/Fosc <br> <em>(NNOEQ = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0B</code></td><td style="text-align: left">Source EQ GND period when Source up to positive voltage = 11*4/Fosc <br> <em>(PEQGND = 11)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0B</code></td><td style="text-align: left">Source EQ GND period when Source down to negative voltage = 11*4/Fosc <br> <em>(NEQGND = 11)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x10</code></td><td style="text-align: left">Source EQ VCI period when Source up to positive voltage = 16*4/Fosc <br> <em>(PEQVCI = 16)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x10</code></td><td style="text-align: left">Source EQ VCI period when Source down to negative voltage = 16*4/Fosc <br> <em>(NEQVCI = 16)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Temporal period of PEQVCI1 = 0*4/Fosc <br> <em>(PEQVCI1 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Temporal period of NEQVCI1 = 0*4/Fosc <br> <em>(NEQVCI1 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(Reserved)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(Reserved)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xFF</code></td><td style="text-align: left"><em>(Undocumented)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(Reserved)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xC0</code></td><td style="text-align: left">White pattern to protect GOA glass <br> <em>(ESD_DET_DATA_WHITE = 1)</em> <br> Enable ESD detection function to protect GOA glass <br> <em>(ESD_WHITE_EN = 1)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x10</code></td><td style="text-align: left">No Need VSYNC <br> <em>(additional frame)</em> after Sleep-In to display sleep-in blanking frame then into Sleep-In State <br> <em>(SLPIN_OPTION = 1)</em> <br> Enable video function detection <br> <em>(VEDIO_NO_CHECK_EN = 0)</em> <br> Disable ESD white pattern scanning voltage pull ground <br> <em>(ESD_WHITE_GND_EN = 0)</em> <br> ESD detection function period = 0 Frames <br> <em>(ESD_DET_TIME_SEL = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#11</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xC6</code></td><td style="text-align: left"><strong>Undocumented</strong></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x01</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0xFF</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0xFF</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#12</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xC1</code></td><td style="text-align: left"><strong>SETPOWER</strong> (Page 149): <br> Set related setting of power</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x74</code></td><td style="text-align: left">VGH Voltage Adjustment = 17 V <br> <em>(VBTHS = 7)</em> <br> VGL Voltage Adjustment = -11 V <br> <em>(VBTLS = 4)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Enable VGH feedback voltage detection. Output voltage = VBTHS <br> <em>(FBOFF_VGH = 0)</em> <br> Enable VGL feedback voltage detection. Output voltage = VBTLS <br> <em>(FBOFF_VGL = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x32</code></td><td style="text-align: left">VSPROUT Voltage = <br> <em>(VRH[5:0] x 0.05 + 3.3)</em> x <br> <em>(VREF/4.8)</em> if VREF [4]=0 <br> <em>(VRP = 50)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x32</code></td><td style="text-align: left">VSNROUT Voltage = <br> <em>(VRH[5:0] x 0.05 + 3.3)</em> x <br> <em>(VREF/5.6)</em> if VREF [4]=1 <br> <em>(VRN = 50)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x77</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0xF1</code></td><td style="text-align: left">Enable VGL voltage Detect Function = VGL voltage Abnormal <br> <em>(VGL_DET_EN = 1)</em> <br> Enable VGH voltage Detect Function = VGH voltage Abnormal <br> <em>(VGH_DET_EN = 1)</em> <br> Enlarge VGL Voltage at “FBOFF_VGL=1” = “VGL=-15V” <br> <em>(VGL_TURBO = 1)</em> <br> Enlarge VGH Voltage at “FBOFF_VGH=1” = “VGH=20V” <br> <em>(VGH_TURBO = 1)</em> <br> <em>(APS = 1)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xFF</code></td><td style="text-align: left">Left side VGH stage 1 pumping frequency = 1.5 MHz <br> <em>(VGH1_L_DIV = 15)</em> <br> Left side VGL stage 1 pumping frequency = 1.5 MHz <br> <em>(VGL1_L_DIV = 15)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xFF</code></td><td style="text-align: left">Right side VGH stage 1 pumping frequency = 1.5 MHz <br> <em>(VGH1_R_DIV = 15)</em> <br> Right side VGL stage 1 pumping frequency = 1.5 MHz <br> <em>(VGL1_R_DIV = 15)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xCC</code></td><td style="text-align: left">Left side VGH stage 2 pumping frequency = 2.6 MHz <br> <em>(VGH2_L_DIV = 12)</em> <br> Left side VGL stage 2 pumping frequency = 2.6 MHz <br> <em>(VGL2_L_DIV = 12)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xCC</code></td><td style="text-align: left">Right side VGH stage 2 pumping frequency = 2.6 MHz <br> <em>(VGH2_R_DIV = 12)</em> <br> Right side VGL stage 2 pumping frequency = 2.6 MHz <br> <em>(VGL2_R_DIV = 12)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x77</code></td><td style="text-align: left">Left side VGH stage 3 pumping frequency = 4.5 MHz <br> <em>(VGH3_L_DIV = 7)</em> <br> Left side VGL stage 3 pumping frequency = 4.5 MHz <br> <em>(VGL3_L_DIV = 7)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x77</code></td><td style="text-align: left">Right side VGH stage 3 pumping frequency = 4.5 MHz <br> <em>(VGH3_R_DIV = 7)</em> <br> Right side VGL stage 3 pumping frequency = 4.5 MHz <br> <em>(VGL3_R_DIV = 7)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#13</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xB5</code></td><td style="text-align: left"><strong>SETBGP</strong> (Page 136): <br> Internal reference voltage setting</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x07</code></td><td style="text-align: left">VREF Voltage: 4.2 V <br> <em>(VREF_SEL = 7)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x07</code></td><td style="text-align: left">NVREF Voltage: 4.2 V <br> <em>(NVREF_SEL = 7)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#14</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xB6</code></td><td style="text-align: left"><strong>SETVCOM</strong> (Page 137): <br> Set VCOM Voltage</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x2C</code></td><td style="text-align: left">VCOMDC voltage at “GS_PANEL=0” = -0.67 V <br> <em>(VCOMDC_F = <code>0x2C</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x2C</code></td><td style="text-align: left">VCOMDC voltage at “GS_PANEL=1” = -0.67 V <br> <em>(VCOMDC_B = <code>0x2C</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#15</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xBF</code></td><td style="text-align: left"><strong>Undocumented</strong></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x02</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x11</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented</td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#16</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xE9</code></td><td style="text-align: left"><strong>SETGIP1</strong> (Page 163): <br> Set forward GIP timing</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x82</code></td><td style="text-align: left">SHR0, SHR1, CHR, CHR2 refer to Internal DE <br> <em>(REF_EN = 1)</em> <br> <em>(PANEL_SEL = 2)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x10</code></td><td style="text-align: left">Starting position of GIP STV group 0 = 4102 HSYNC <br> <em>(SHR0 Bits 8-12 = <code>0x10</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x06</code></td><td style="text-align: left"><em>(SHR0 Bits 0-7 = <code>0x06</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x05</code></td><td style="text-align: left">Starting position of GIP STV group 1 = 1442 HSYNC <br> <em>(SHR1 Bits 8-12 = <code>0x05</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xA2</code></td><td style="text-align: left"><em>(SHR1 Bits 0-7 = <code>0xA2</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0A</code></td><td style="text-align: left">Distance of STV rising edge and HYSNC = 10*2 Fosc <br> <em>(SPON Bits 0-7 = <code>0x0A</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xA5</code></td><td style="text-align: left">Distance of STV falling edge and HYSNC = 165*2 Fosc <br> <em>(SPOFF Bits 0-7 = <code>0xA5</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x12</code></td><td style="text-align: left">STV0_1 distance with STV0_0 = 1 HSYNC <br> <em>(SHR0_1 = 1)</em> <br> STV0_2 distance with STV0_0 = 2 HSYNC <br> <em>(SHR0_2 = 2)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x31</code></td><td style="text-align: left">STV0_3 distance with STV0_0 = 3 HSYNC <br> <em>(SHR0_3 = 3)</em> <br> STV1_1 distance with STV1_0 = 1 HSYNC <br> <em>(SHR1_1 = 1)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x23</code></td><td style="text-align: left">STV1_2 distance with STV1_0 = 2 HSYNC <br> <em>(SHR1_2 = 2)</em> <br> STV1_3 distance with STV1_0 = 3 HSYNC <br> <em>(SHR1_3 = 3)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x37</code></td><td style="text-align: left">STV signal high pulse width = 3 HSYNC <br> <em>(SHP = 3)</em> <br> Total number of STV signal = 7 <br> <em>(SCP = 7)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x83</code></td><td style="text-align: left">Starting position of GIP CKV group 0 <br> <em>(CKV0_0)</em> = 131 HSYNC <br> <em>(CHR = <code>0x83</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x04</code></td><td style="text-align: left">Distance of CKV rising edge and HYSNC = 4*2 Fosc <br> <em>(CON Bits 0-7 = <code>0x04</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xBC</code></td><td style="text-align: left">Distance of CKV falling edge and HYSNC = 188*2 Fosc <br> <em>(COFF Bits 0-7 = <code>0xBC</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x27</code></td><td style="text-align: left">CKV signal high pulse width = 2 HSYNC <br> <em>(CHP = 2)</em> <br> Total period cycle of CKV signal = 7 HSYNC <br> <em>(CCP = 7)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x38</code></td><td style="text-align: left">Extra gate counter at blanking area: Gate number = 56 <br> <em>(USER_GIP_GATE = <code>0x38</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0C</code></td><td style="text-align: left">Left side GIP output pad signal = ??? <br> <em>(CGTS_L Bits 16-21 = <code>0x0C</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CGTS_L Bits 8-15 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x03</code></td><td style="text-align: left"><em>(CGTS_L Bits 0-7 = <code>0x03</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Normal polarity of Left side GIP output pad signal <br> <em>(CGTS_INV_L Bits 16-21 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CGTS_INV_L Bits 8-15 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CGTS_INV_L Bits 0-7 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0C</code></td><td style="text-align: left">Right side GIP output pad signal = ??? <br> <em>(CGTS_R Bits 16-21 = <code>0x0C</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CGTS_R Bits 8-15 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x03</code></td><td style="text-align: left"><em>(CGTS_R Bits 0-7 = <code>0x03</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Normal polarity of Right side GIP output pad signal <br> <em>(CGTS_INV_R Bits 16-21 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CGTS_INV_R Bits 8-15 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CGTS_INV_R Bits 0-7 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x75</code></td><td style="text-align: left">Left side GIP output pad signal = ??? <br> <em>(COS1_L = 7)</em> <br> Left side GIP output pad signal = ??? <br> <em>(COS2_L = 5)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x75</code></td><td style="text-align: left">Left side GIP output pad signal = ??? <br> <em>(COS3_L = 7)</em> <br> <em>(COS4_L = 5)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x31</code></td><td style="text-align: left">Left side GIP output pad signal = ??? <br> <em>(COS5_L = 3)</em> <br> <em>(COS6_L = 1)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 32)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 33)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 34)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 35)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 36)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Left side GIP output pad signal = ??? <br> <em>(COS17_L = 8)</em> <br> Left side GIP output pad signal = ??? <br> <em>(COS18_L = 8)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x13</code></td><td style="text-align: left">Left side GIP output pad signal = ??? <br> <em>(COS19_L = 1)</em> <br> Left side GIP output pad signal = ??? <br> <em>(COS20_L = 3)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Left side GIP output pad signal = ??? <br> <em>(COS21_L = 8)</em> <br> Left side GIP output pad signal = ??? <br> <em>(COS22_L = 8)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x64</code></td><td style="text-align: left">Right side GIP output pad signal = ??? <br> <em>(COS1_R = 6)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS2_R = 4)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x64</code></td><td style="text-align: left">Right side GIP output pad signal = ??? <br> <em>(COS3_R = 6)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS4_R = 4)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x20</code></td><td style="text-align: left">Right side GIP output pad signal = ??? <br> <em>(COS5_R = 2)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS6_R = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 43)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 44)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 45)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 46)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 47)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Right side GIP output pad signal = ??? <br> <em>(COS17_R = 8)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS18_R = 8)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x02</code></td><td style="text-align: left">Right side GIP output pad signal = ??? <br> <em>(COS19_R = 0)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS20_R = 2)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Right side GIP output pad signal = ??? <br> <em>(COS21_R = 8)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS22_R = 8)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(TCON_OPT = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(GIP_OPT Bits 16-22 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(GIP_OPT Bits 8-15 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(GIP_OPT Bits 0-7 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Starting position of GIP CKV group 1 <br> <em>(CKV1_0)</em> = 0 HSYNC <br> <em>(CHR2 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Distance of CKV1 rising edge and HYSNC = 0*2 Fosc <br> <em>(CON2 Bits 0-7 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Distance of CKV1 falling edge and HYSNC = 0*2 Fosc <br> <em>(COFF2 Bits 0-7 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">CKV1 signal high pulse width = 0 HSYNC <br> <em>(CHP2 = 0)</em> <br> Total period cycle of CKV1 signal = 0 HSYNC <br> <em>(CCP2 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CKS Bits 16-21 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CKS Bits 8-15 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CKS Bits 0-7 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(COFF Bits 8-9 = 0)</em> <br> <em>(CON Bits 8-9 = 0)</em> <br> <em>(SPOFF Bits 8-9 = 0)</em> <br> <em>(SPON Bits 8-9 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(COFF2 Bits 8-9 = 0)</em> <br> <em>(CON2 Bits 8-9 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#17</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xEA</code></td><td style="text-align: left"><strong>SETGIP2</strong> (Page 170): <br> Set backward GIP timing</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x02</code></td><td style="text-align: left">YS2 Signal Mode = INYS1/INYS2 <br> <em>(YS2_SEL = 0)</em> <br> YS2 Signal Mode = INYS1/INYS2 <br> <em>(YS1_SEL = 0)</em> <br> Don’t reverse YS2 signal <br> <em>(YS2_XOR = 0)</em> <br> Don’t reverse YS1 signal <br> <em>(YS1_XOR = 0)</em> <br> Enable YS signal function <br> <em>(YS_FLAG_EN = 1)</em> <br> Disable ALL ON function <br> <em>(ALL_ON_EN = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x21</code></td><td style="text-align: left"><em>(GATE = <code>0x21</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CK_ALL_ON_EN = 0)</em> <br> <em>(STV_ALL_ON_EN = 0)</em> <br> Timing of YS1 and YS2 signal = ??? <br> <em>(CK_ALL_ON_WIDTH1 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Timing of YS1 and YS2 signal = ??? <br> <em>(CK_ALL_ON_WIDTH2 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Timing of YS1 and YS2 signal = ??? <br> <em>(CK_ALL_ON_WIDTH3 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(YS_FLAG_PERIOD = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(YS2_SEL_2 = 0)</em> <br> <em>(YS1_SEL_2 = 0)</em> <br> <em>(YS2_XOR_2 = 0)</em> <br> <em>(YS_FLAG_EN_2 = 0)</em> <br> <em>(ALL_ON_EN_2 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Distance of GIP ALL On rising edge and DE = ??? <br> <em>(USER_GIP_GATE1_2 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CK_ALL_ON_EN_2 = 0)</em> <br> <em>(STV_ALL_ON_EN_2 = 0)</em> <br> <em>(CK_ALL_ON_WIDTH1_2 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CK_ALL_ON_WIDTH2_2 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(CK_ALL_ON_WIDTH3_2 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(YS_FLAG_PERIOD_2 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x02</code></td><td style="text-align: left"><em>(COS1_L_GS = 0)</em> <br> <em>(COS2_L_GS = 2)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x46</code></td><td style="text-align: left"><em>(COS3_L_GS = 4)</em> <br> <em>(COS4_L_GS = 6)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x02</code></td><td style="text-align: left"><em>(COS5_L_GS = 0)</em> <br> <em>(COS6_L_GS = 2)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 16)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 17)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 18)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 19)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 20)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left"><em>(COS17_L_GS = 8)</em> <br> <em>(COS18_L_GS = 8)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x64</code></td><td style="text-align: left"><em>(COS19_L_GS = 6)</em> <br> <em>(COS20_L_GS = 4)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left"><em>(COS21_L_GS = 8)</em> <br> <em>(COS22_L_GS = 8)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x13</code></td><td style="text-align: left"><em>(COS1_R_GS = 1)</em> <br> <em>(COS2_R_GS = 3)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x57</code></td><td style="text-align: left"><em>(COS3_R_GS = 5)</em> <br> <em>(COS4_R_GS = 7)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x13</code></td><td style="text-align: left"><em>(COS5_R_GS = 1)</em> <br> <em>(COS6_R_GS = 3)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 27)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 28)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 29)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 30)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left">Reserved <em>(Parameter 31)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left"><em>(COS17_R_GS = 8)</em> <br> <em>(COS18_R_GS = 8)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x75</code></td><td style="text-align: left"><em>(COS19_R_GS = 7)</em> <br> <em>(COS20_R_GS = 5)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x88</code></td><td style="text-align: left"><em>(COS21_R_GS = 8)</em> <br> <em>(COS22_R_GS = 8)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x23</code></td><td style="text-align: left">GIP output EQ signal: P_EQ = Yes, N_EQ = No <br> <em>(EQOPT = 2)</em> <br> GIP output EQ signal level: P_EQ = GND, N_EQ = GND <br> <em>(EQ_SEL = 3)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x14</code></td><td style="text-align: left">Distance of EQ rising edge and HYSNC = 20 Fosc <br> <em>(EQ_DELAY = <code>0x14</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Distance of EQ rising edge and HYSNC = 0 HSYNC <br> <em>(EQ_DELAY_HSYNC = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(HSYNC_TO_CL1_CNT10 Bits 8-9 = 0)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x02</code></td><td style="text-align: left">GIP reference HSYNC between external HSYNC = 2 Fosc <br> <em>(HSYNC_TO_CL1_CNT10 Bits 0-7 = 2)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 40)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 41)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 42)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 43)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 44)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 45)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 46)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 47)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 48)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 49)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 50)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 51)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 52)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 53)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 54)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x03</code></td><td style="text-align: left">Undocumented <em>(Parameter 55)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0A</code></td><td style="text-align: left">Undocumented <em>(Parameter 56)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xA5</code></td><td style="text-align: left">Undocumented <em>(Parameter 57)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 58)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 59)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 60)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left">Undocumented <em>(Parameter 61)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#18</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0xE0</code></td><td style="text-align: left"><strong>SETGAMMA</strong> (Page 158): <br> Set the gray scale voltage to adjust the gamma characteristics of the TFT panel</td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(PVR0 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x09</code></td><td style="text-align: left"><em>(PVR1 = <code>0x09</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0D</code></td><td style="text-align: left"><em>(PVR2 = <code>0x0D</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x23</code></td><td style="text-align: left"><em>(PVR3 = <code>0x23</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x27</code></td><td style="text-align: left"><em>(PVR4 = <code>0x27</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x3C</code></td><td style="text-align: left"><em>(PVR5 = <code>0x3C</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x41</code></td><td style="text-align: left"><em>(PPR0 = <code>0x41</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x35</code></td><td style="text-align: left"><em>(PPR1 = <code>0x35</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x07</code></td><td style="text-align: left"><em>(PPK0 = <code>0x07</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0D</code></td><td style="text-align: left"><em>(PPK1 = <code>0x0D</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0E</code></td><td style="text-align: left"><em>(PPK2 = <code>0x0E</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x12</code></td><td style="text-align: left"><em>(PPK3 = <code>0x12</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x13</code></td><td style="text-align: left"><em>(PPK4 = <code>0x13</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x10</code></td><td style="text-align: left"><em>(PPK5 = <code>0x10</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x12</code></td><td style="text-align: left"><em>(PPK6 = <code>0x12</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x12</code></td><td style="text-align: left"><em>(PPK7 = <code>0x12</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x18</code></td><td style="text-align: left"><em>(PPK8 = <code>0x18</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x00</code></td><td style="text-align: left"><em>(NVR0 = <code>0x00</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x09</code></td><td style="text-align: left"><em>(NVR1 = <code>0x09</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0D</code></td><td style="text-align: left"><em>(NVR2 = <code>0x0D</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x23</code></td><td style="text-align: left"><em>(NVR3 = <code>0x23</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x27</code></td><td style="text-align: left"><em>(NVR4 = <code>0x27</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x3C</code></td><td style="text-align: left"><em>(NVR5 = <code>0x3C</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x41</code></td><td style="text-align: left"><em>(NPR0 = <code>0x41</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x35</code></td><td style="text-align: left"><em>(NPR1 = <code>0x35</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x07</code></td><td style="text-align: left"><em>(NPK0 = <code>0x07</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0D</code></td><td style="text-align: left"><em>(NPK1 = <code>0x0D</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x0E</code></td><td style="text-align: left"><em>(NPK2 = <code>0x0E</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x12</code></td><td style="text-align: left"><em>(NPK3 = <code>0x12</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x13</code></td><td style="text-align: left"><em>(NPK4 = <code>0x13</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x10</code></td><td style="text-align: left"><em>(NPK5 = <code>0x10</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x12</code></td><td style="text-align: left"><em>(NPK6 = <code>0x12</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x12</code></td><td style="text-align: left"><em>(NPK7 = <code>0x12</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x18</code></td><td style="text-align: left"><em>(NPK8 = <code>0x18</code>)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#19</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x11</code></td><td style="text-align: left"><strong>SLPOUT</strong> (Page 89): <br> Turns off sleep mode <br> <em>(MIPI_DCS_EXIT_SLEEP_MODE)</em></td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>!!!</code></td><td style="text-align: left"><strong>Insert Delay Here:</strong> <br> Wait 120 milliseconds</td></tr>
|
||||
<tr><td style="text-align: center"></td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center">#20</td><td style="text-align: left"></td></tr>
|
||||
<tr><td style="text-align: center"><code>0x29</code></td><td style="text-align: left"><strong>Display On</strong> (Page 97): <br> Recover from DISPLAY OFF mode <br> <em>(MIPI_DCS_SET_DISPLAY_ON)</em></td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<p>|
|
||||
| #2
|
||||
| <code>0xBA</code> | <strong>SETMIPI</strong> (Page 144): <br> Set MIPI related register
|
||||
| <code>0x33</code> | Virtual Channel = 0 <br> <em>(VC_Main = 0)</em> <br> Number of Lanes = 4 <br> <em>(Lane_Number = 3)</em>
|
||||
| <code>0x81</code> | LDO = 1.7 V <br> <em>(DSI_LDO_SEL = 4)</em> <br> Terminal Resistance = 90 Ohm <br> <em>(RTERM = 1)</em>
|
||||
| <code>0x05</code> | MIPI Low High Speed driving ability = x6 <br> <em>(IHSRX = 5)</em>
|
||||
| <code>0xF9</code> | TXCLK speed in DSI LP mode = fDSICLK / 16 <br> <em>(Tx_clk_sel = 2)</em>
|
||||
| <code>0x0E</code> | Min HFP number in DSI mode = 14 <br> <em>(HFP_OSC = 14)</em>
|
||||
| <code>0x0E</code> | Min HBP number in DSI mode = 14 <br> <em>(HBP_OSC = 14)</em>
|
||||
| <code>0x20</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x44</code> | Undocumented
|
||||
| <code>0x25</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x91</code> | Undocumented
|
||||
| <code>0x0a</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x02</code> | Undocumented
|
||||
| <code>0x4F</code> | Undocumented
|
||||
| <code>0x11</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x37</code> | Undocumented
|
||||
|
|
||||
| #3
|
||||
| <code>0xB8</code> | <strong>SETPOWER_EXT</strong> (Page 142): <br> Set display related register
|
||||
| <code>0x25</code> | External power IC or PFM: VSP = FL1002, VSN = FL1002 <br> <em>(PCCS = 2)</em> <br> VCSW1 / VCSW2 Frequency for Pumping VSP / VSN = 1/4 Hsync <br> <em>(ECP_DC_DIV = 5)</em>
|
||||
| <code>0x22</code> | VCSW1/VCSW2 soft start time = 15 ms <br> <em>(DT = 2)</em> <br> Pumping ratio of VSP / VSN with VCI = x2 <br> <em>(XDK_ECP = 1)</em>
|
||||
| <code>0x20</code> | PFM operation frequency FoscD = Fosc/1 <br> <em>(PFM_DC_DIV = 0)</em>
|
||||
| <code>0x03</code> | Enable power IC pumping frequency synchronization = Synchronize with external Hsync <br> <em>(ECP_SYNC_EN = 1)</em> <br> Enable VGH/VGL pumping frequency synchronization = Synchronize with external Hsync <br> <em>(VGX_SYNC_EN = 1)</em>
|
||||
|
|
||||
| #4
|
||||
| <code>0xB3</code> | <strong>SETRGBIF</strong> (Page 134): <br> Control RGB I/F porch timing for internal use
|
||||
| <code>0x10</code> | Vertical back porch HS number in Blank Frame Period = Hsync number 16 <br> <em>(VBP_RGB_GEN = 16)</em>
|
||||
| <code>0x10</code> | Vertical front porch HS number in Blank Frame Period = Hsync number 16 <br> <em>(VFP_RGB_GEN = 16)</em>
|
||||
| <code>0x05</code> | HBP OSC number in Blank Frame Period = OSC number 5 <br> <em>(DE_BP_RGB_GEN = 5)</em>
|
||||
| <code>0x05</code> | HFP OSC number in Blank Frame Period = OSC number 5 <br> <em>(DE_FP_RGB_GEN = 5)</em>
|
||||
| <code>0x03</code> | Undocumented
|
||||
| <code>0xFF</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
|
|
||||
| #5
|
||||
| <code>0xC0</code> | <strong>SETSCR</strong> (Page 147): <br> Set related setting of Source driving
|
||||
| <code>0x73</code> | Source OP Amp driving period for positive polarity in Normal Mode: Source OP Period = 115<em>4/Fosc <br> <em>(N_POPON = 115)</em>
|
||||
| <code>0x73</code> | Source OP Amp driving period for negative polarity in Normal Mode: Source OP Period = 115</em>4/Fosc <br> <em>(N_NOPON = 115)</em>
|
||||
| <code>0x50</code> | Source OP Amp driving period for positive polarity in Idle mode: Source OP Period = 80<em>4/Fosc <br> <em>(I_POPON = 80)</em>
|
||||
| <code>0x50</code> | Source OP Amp dirivng period for negative polarity in Idle Mode: Source OP Period = 80</em>4/Fosc <br> <em>(I_NOPON = 80)</em>
|
||||
| <code>0x00</code> | <em>(SCR Bits 24-31 = <code>0x00</code>)</em>
|
||||
| <code>0xC0</code> | <em>(SCR Bits 16-23 = <code>0xC0</code>)</em>
|
||||
| <code>0x08</code> | Gamma bias current fine tune: Current xIbias = 4 <br> <em>(SCR Bits 9-13 = 4)</em> <br> <em>(SCR Bits 8-15 = <code>0x08</code>)</em>
|
||||
| <code>0x70</code> | Source and Gamma bias current core tune: Ibias = 1 <br> <em>(SCR Bits 0-3 = 0)</em> <br> Source bias current fine tune: Current xIbias = 7 <br> <em>(SCR Bits 4-8 = 7)</em> <br> <em>(SCR Bits 0-7 = <code>0x70</code>)</em>
|
||||
| <code>0x00</code> | Undocumented
|
||||
|
|
||||
| #6
|
||||
| <code>0xBC</code> | <strong>SETVDC</strong> (Page 146): <br> Control NVDDD/VDDD Voltage
|
||||
| <code>0x4E</code> | NVDDD voltage = -1.8 V <br> <em>(NVDDD_SEL = 4)</em> <br> VDDD voltage = 1.9 V <br> <em>(VDDD_SEL = 6)</em>
|
||||
|
|
||||
| #7
|
||||
| <code>0xCC</code> | <strong>SETPANEL</strong> (Page 154): <br> Set display related register
|
||||
| <code>0x0B</code> | Enable reverse the source scan direction <br> <em>(SS_PANEL = 1)</em> <br> Normal vertical scan direction <br> <em>(GS_PANEL = 0)</em> <br> Normally black panel <br> <em>(REV_PANEL = 1)</em> <br> S1:S2:S3 = B:G:R <br> <em>(BGR_PANEL = 1)</em>
|
||||
|
|
||||
| #8
|
||||
| <code>0xB4</code> | <strong>SETCYC</strong> (Page 135): <br> Control display inversion type
|
||||
| <code>0x80</code> | Extra source for Zig-Zag Inversion = S2401 <br> <em>(ZINV_S2401_EN = 1)</em> <br> Row source data dislocates = Even row <br> <em>(ZINV_G_EVEN_EN = 0)</em> <br> Disable Zig-Zag Inversion <br> <em>(ZINV_EN = 0)</em> <br> Enable Zig-Zag1 Inversion <br> <em>(ZINV2_EN = 0)</em> <br> Normal mode inversion type = Column inversion <br> <em>(N_NW = 0)</em>
|
||||
|
|
||||
| #9
|
||||
| <code>0xB2</code> | <strong>SETDISP</strong> (Page 132): <br> Control the display resolution
|
||||
| <code>0xF0</code> | Gate number of vertical direction = 480 + <br> <em>(240*4)</em> <br> <em>(NL = 240)</em>
|
||||
| <code>0x12</code> | <em>(RES_V_LSB = 0)</em> <br> Non-display area source output control: Source output = VSSD <br> <em>(BLK_CON = 1)</em> <br> Channel number of source direction = 720RGB <br> <em>(RESO_SEL = 2)</em>
|
||||
| <code>0xF0</code> | Source voltage during Blanking Time when accessing Sleep-Out / Sleep-In = GND <br> <em>(WHITE_GND_EN = 1)</em> <br> Blank timing control when access sleep out command: Blank Frame Period = 7 Frames <br> <em>(WHITE_FRAME_SEL = 7)</em> <br> Source output refresh control: Refresh Period = 0 Frames <br> <em>(ISC = 0)</em>
|
||||
|
|
||||
| #10
|
||||
| <code>0xE3</code> | <strong>SETEQ</strong> (Page 159): <br> Set EQ related register
|
||||
| <code>0x00</code> | Temporal spacing between HSYNC and PEQGND = 0<em>4/Fosc <br> <em>(PNOEQ = 0)</em>
|
||||
| <code>0x00</code> | Temporal spacing between HSYNC and NEQGND = 0</em>4/Fosc <br> <em>(NNOEQ = 0)</em>
|
||||
| <code>0x0B</code> | Source EQ GND period when Source up to positive voltage = 11<em>4/Fosc <br> <em>(PEQGND = 11)</em>
|
||||
| <code>0x0B</code> | Source EQ GND period when Source down to negative voltage = 11</em>4/Fosc <br> <em>(NEQGND = 11)</em>
|
||||
| <code>0x10</code> | Source EQ VCI period when Source up to positive voltage = 16<em>4/Fosc <br> <em>(PEQVCI = 16)</em>
|
||||
| <code>0x10</code> | Source EQ VCI period when Source down to negative voltage = 16</em>4/Fosc <br> <em>(NEQVCI = 16)</em>
|
||||
| <code>0x00</code> | Temporal period of PEQVCI1 = 0<em>4/Fosc <br> <em>(PEQVCI1 = 0)</em>
|
||||
| <code>0x00</code> | Temporal period of NEQVCI1 = 0</em>4/Fosc <br> <em>(NEQVCI1 = 0)</em>
|
||||
| <code>0x00</code> | <em>(Reserved)</em>
|
||||
| <code>0x00</code> | <em>(Reserved)</em>
|
||||
| <code>0xFF</code> | <em>(Undocumented)</em>
|
||||
| <code>0x00</code> | <em>(Reserved)</em>
|
||||
| <code>0xC0</code> | White pattern to protect GOA glass <br> <em>(ESD_DET_DATA_WHITE = 1)</em> <br> Enable ESD detection function to protect GOA glass <br> <em>(ESD_WHITE_EN = 1)</em>
|
||||
| <code>0x10</code> | No Need VSYNC <br> <em>(additional frame)</em> after Sleep-In to display sleep-in blanking frame then into Sleep-In State <br> <em>(SLPIN_OPTION = 1)</em> <br> Enable video function detection <br> <em>(VEDIO_NO_CHECK_EN = 0)</em> <br> Disable ESD white pattern scanning voltage pull ground <br> <em>(ESD_WHITE_GND_EN = 0)</em> <br> ESD detection function period = 0 Frames <br> <em>(ESD_DET_TIME_SEL = 0)</em>
|
||||
|
|
||||
| #11
|
||||
| <code>0xC6</code> | <strong>Undocumented</strong>
|
||||
| <code>0x01</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
| <code>0xFF</code> | Undocumented
|
||||
| <code>0xFF</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
|
|
||||
| #12
|
||||
| <code>0xC1</code> | <strong>SETPOWER</strong> (Page 149): <br> Set related setting of power
|
||||
| <code>0x74</code> | VGH Voltage Adjustment = 17 V <br> <em>(VBTHS = 7)</em> <br> VGL Voltage Adjustment = -11 V <br> <em>(VBTLS = 4)</em>
|
||||
| <code>0x00</code> | Enable VGH feedback voltage detection. Output voltage = VBTHS <br> <em>(FBOFF_VGH = 0)</em> <br> Enable VGL feedback voltage detection. Output voltage = VBTLS <br> <em>(FBOFF_VGL = 0)</em>
|
||||
| <code>0x32</code> | VSPROUT Voltage = <br> <em>(VRH[5:0] x 0.05 + 3.3)</em> x <br> <em>(VREF/4.8)</em> if VREF [4]=0 <br> <em>(VRP = 50)</em>
|
||||
| <code>0x32</code> | VSNROUT Voltage = <br> <em>(VRH[5:0] x 0.05 + 3.3)</em> x <br> <em>(VREF/5.6)</em> if VREF [4]=1 <br> <em>(VRN = 50)</em>
|
||||
| <code>0x77</code> | Undocumented
|
||||
| <code>0xF1</code> | Enable VGL voltage Detect Function = VGL voltage Abnormal <br> <em>(VGL_DET_EN = 1)</em> <br> Enable VGH voltage Detect Function = VGH voltage Abnormal <br> <em>(VGH_DET_EN = 1)</em> <br> Enlarge VGL Voltage at “FBOFF_VGL=1” = “VGL=-15V” <br> <em>(VGL_TURBO = 1)</em> <br> Enlarge VGH Voltage at “FBOFF_VGH=1” = “VGH=20V” <br> <em>(VGH_TURBO = 1)</em> <br> <em>(APS = 1)</em>
|
||||
| <code>0xFF</code> | Left side VGH stage 1 pumping frequency = 1.5 MHz <br> <em>(VGH1_L_DIV = 15)</em> <br> Left side VGL stage 1 pumping frequency = 1.5 MHz <br> <em>(VGL1_L_DIV = 15)</em>
|
||||
| <code>0xFF</code> | Right side VGH stage 1 pumping frequency = 1.5 MHz <br> <em>(VGH1_R_DIV = 15)</em> <br> Right side VGL stage 1 pumping frequency = 1.5 MHz <br> <em>(VGL1_R_DIV = 15)</em>
|
||||
| <code>0xCC</code> | Left side VGH stage 2 pumping frequency = 2.6 MHz <br> <em>(VGH2_L_DIV = 12)</em> <br> Left side VGL stage 2 pumping frequency = 2.6 MHz <br> <em>(VGL2_L_DIV = 12)</em>
|
||||
| <code>0xCC</code> | Right side VGH stage 2 pumping frequency = 2.6 MHz <br> <em>(VGH2_R_DIV = 12)</em> <br> Right side VGL stage 2 pumping frequency = 2.6 MHz <br> <em>(VGL2_R_DIV = 12)</em>
|
||||
| <code>0x77</code> | Left side VGH stage 3 pumping frequency = 4.5 MHz <br> <em>(VGH3_L_DIV = 7)</em> <br> Left side VGL stage 3 pumping frequency = 4.5 MHz <br> <em>(VGL3_L_DIV = 7)</em>
|
||||
| <code>0x77</code> | Right side VGH stage 3 pumping frequency = 4.5 MHz <br> <em>(VGH3_R_DIV = 7)</em> <br> Right side VGL stage 3 pumping frequency = 4.5 MHz <br> <em>(VGL3_R_DIV = 7)</em>
|
||||
|
|
||||
| #13
|
||||
| <code>0xB5</code> | <strong>SETBGP</strong> (Page 136): <br> Internal reference voltage setting
|
||||
| <code>0x07</code> | VREF Voltage: 4.2 V <br> <em>(VREF_SEL = 7)</em>
|
||||
| <code>0x07</code> | NVREF Voltage: 4.2 V <br> <em>(NVREF_SEL = 7)</em>
|
||||
|
|
||||
| #14
|
||||
| <code>0xB6</code> | <strong>SETVCOM</strong> (Page 137): <br> Set VCOM Voltage
|
||||
| <code>0x2C</code> | VCOMDC voltage at “GS_PANEL=0” = -0.67 V <br> <em>(VCOMDC_F = <code>0x2C</code>)</em>
|
||||
| <code>0x2C</code> | VCOMDC voltage at “GS_PANEL=1” = -0.67 V <br> <em>(VCOMDC_B = <code>0x2C</code>)</em>
|
||||
|
|
||||
| #15
|
||||
| <code>0xBF</code> | <strong>Undocumented</strong>
|
||||
| <code>0x02</code> | Undocumented
|
||||
| <code>0x11</code> | Undocumented
|
||||
| <code>0x00</code> | Undocumented
|
||||
|
|
||||
| #16
|
||||
| <code>0xE9</code> | <strong>SETGIP1</strong> (Page 163): <br> Set forward GIP timing
|
||||
| <code>0x82</code> | SHR0, SHR1, CHR, CHR2 refer to Internal DE <br> <em>(REF_EN = 1)</em> <br> <em>(PANEL_SEL = 2)</em>
|
||||
| <code>0x10</code> | Starting position of GIP STV group 0 = 4102 HSYNC <br> <em>(SHR0 Bits 8-12 = <code>0x10</code>)</em>
|
||||
| <code>0x06</code> | <em>(SHR0 Bits 0-7 = <code>0x06</code>)</em>
|
||||
| <code>0x05</code> | Starting position of GIP STV group 1 = 1442 HSYNC <br> <em>(SHR1 Bits 8-12 = <code>0x05</code>)</em>
|
||||
| <code>0xA2</code> | <em>(SHR1 Bits 0-7 = <code>0xA2</code>)</em>
|
||||
| <code>0x0A</code> | Distance of STV rising edge and HYSNC = 10<em>2 Fosc <br> <em>(SPON Bits 0-7 = <code>0x0A</code>)</em>
|
||||
| <code>0xA5</code> | Distance of STV falling edge and HYSNC = 165</em>2 Fosc <br> <em>(SPOFF Bits 0-7 = <code>0xA5</code>)</em>
|
||||
| <code>0x12</code> | STV0_1 distance with STV0_0 = 1 HSYNC <br> <em>(SHR0_1 = 1)</em> <br> STV0_2 distance with STV0_0 = 2 HSYNC <br> <em>(SHR0_2 = 2)</em>
|
||||
| <code>0x31</code> | STV0_3 distance with STV0_0 = 3 HSYNC <br> <em>(SHR0_3 = 3)</em> <br> STV1_1 distance with STV1_0 = 1 HSYNC <br> <em>(SHR1_1 = 1)</em>
|
||||
| <code>0x23</code> | STV1_2 distance with STV1_0 = 2 HSYNC <br> <em>(SHR1_2 = 2)</em> <br> STV1_3 distance with STV1_0 = 3 HSYNC <br> <em>(SHR1_3 = 3)</em>
|
||||
| <code>0x37</code> | STV signal high pulse width = 3 HSYNC <br> <em>(SHP = 3)</em> <br> Total number of STV signal = 7 <br> <em>(SCP = 7)</em>
|
||||
| <code>0x83</code> | Starting position of GIP CKV group 0 <br> <em>(CKV0_0)</em> = 131 HSYNC <br> <em>(CHR = <code>0x83</code>)</em>
|
||||
| <code>0x04</code> | Distance of CKV rising edge and HYSNC = 4<em>2 Fosc <br> <em>(CON Bits 0-7 = <code>0x04</code>)</em>
|
||||
| <code>0xBC</code> | Distance of CKV falling edge and HYSNC = 188</em>2 Fosc <br> <em>(COFF Bits 0-7 = <code>0xBC</code>)</em>
|
||||
| <code>0x27</code> | CKV signal high pulse width = 2 HSYNC <br> <em>(CHP = 2)</em> <br> Total period cycle of CKV signal = 7 HSYNC <br> <em>(CCP = 7)</em>
|
||||
| <code>0x38</code> | Extra gate counter at blanking area: Gate number = 56 <br> <em>(USER_GIP_GATE = <code>0x38</code>)</em>
|
||||
| <code>0x0C</code> | Left side GIP output pad signal = ??? <br> <em>(CGTS_L Bits 16-21 = <code>0x0C</code>)</em>
|
||||
| <code>0x00</code> | <em>(CGTS_L Bits 8-15 = <code>0x00</code>)</em>
|
||||
| <code>0x03</code> | <em>(CGTS_L Bits 0-7 = <code>0x03</code>)</em>
|
||||
| <code>0x00</code> | Normal polarity of Left side GIP output pad signal <br> <em>(CGTS_INV_L Bits 16-21 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(CGTS_INV_L Bits 8-15 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(CGTS_INV_L Bits 0-7 = <code>0x00</code>)</em>
|
||||
| <code>0x0C</code> | Right side GIP output pad signal = ??? <br> <em>(CGTS_R Bits 16-21 = <code>0x0C</code>)</em>
|
||||
| <code>0x00</code> | <em>(CGTS_R Bits 8-15 = <code>0x00</code>)</em>
|
||||
| <code>0x03</code> | <em>(CGTS_R Bits 0-7 = <code>0x03</code>)</em>
|
||||
| <code>0x00</code> | Normal polarity of Right side GIP output pad signal <br> <em>(CGTS_INV_R Bits 16-21 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(CGTS_INV_R Bits 8-15 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(CGTS_INV_R Bits 0-7 = <code>0x00</code>)</em>
|
||||
| <code>0x75</code> | Left side GIP output pad signal = ??? <br> <em>(COS1_L = 7)</em> <br> Left side GIP output pad signal = ??? <br> <em>(COS2_L = 5)</em>
|
||||
| <code>0x75</code> | Left side GIP output pad signal = ??? <br> <em>(COS3_L = 7)</em> <br> <em>(COS4_L = 5)</em>
|
||||
| <code>0x31</code> | Left side GIP output pad signal = ??? <br> <em>(COS5_L = 3)</em> <br> <em>(COS6_L = 1)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 32)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 33)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 34)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 35)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 36)</em>
|
||||
| <code>0x88</code> | Left side GIP output pad signal = ??? <br> <em>(COS17_L = 8)</em> <br> Left side GIP output pad signal = ??? <br> <em>(COS18_L = 8)</em>
|
||||
| <code>0x13</code> | Left side GIP output pad signal = ??? <br> <em>(COS19_L = 1)</em> <br> Left side GIP output pad signal = ??? <br> <em>(COS20_L = 3)</em>
|
||||
| <code>0x88</code> | Left side GIP output pad signal = ??? <br> <em>(COS21_L = 8)</em> <br> Left side GIP output pad signal = ??? <br> <em>(COS22_L = 8)</em>
|
||||
| <code>0x64</code> | Right side GIP output pad signal = ??? <br> <em>(COS1_R = 6)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS2_R = 4)</em>
|
||||
| <code>0x64</code> | Right side GIP output pad signal = ??? <br> <em>(COS3_R = 6)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS4_R = 4)</em>
|
||||
| <code>0x20</code> | Right side GIP output pad signal = ??? <br> <em>(COS5_R = 2)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS6_R = 0)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 43)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 44)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 45)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 46)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 47)</em>
|
||||
| <code>0x88</code> | Right side GIP output pad signal = ??? <br> <em>(COS17_R = 8)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS18_R = 8)</em>
|
||||
| <code>0x02</code> | Right side GIP output pad signal = ??? <br> <em>(COS19_R = 0)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS20_R = 2)</em>
|
||||
| <code>0x88</code> | Right side GIP output pad signal = ??? <br> <em>(COS21_R = 8)</em> <br> Right side GIP output pad signal = ??? <br> <em>(COS22_R = 8)</em>
|
||||
| <code>0x00</code> | <em>(TCON_OPT = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(GIP_OPT Bits 16-22 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(GIP_OPT Bits 8-15 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(GIP_OPT Bits 0-7 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | Starting position of GIP CKV group 1 <br> <em>(CKV1_0)</em> = 0 HSYNC <br> <em>(CHR2 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | Distance of CKV1 rising edge and HYSNC = 0<em>2 Fosc <br> <em>(CON2 Bits 0-7 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | Distance of CKV1 falling edge and HYSNC = 0</em>2 Fosc <br> <em>(COFF2 Bits 0-7 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | CKV1 signal high pulse width = 0 HSYNC <br> <em>(CHP2 = 0)</em> <br> Total period cycle of CKV1 signal = 0 HSYNC <br> <em>(CCP2 = 0)</em>
|
||||
| <code>0x00</code> | <em>(CKS Bits 16-21 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(CKS Bits 8-15 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(CKS Bits 0-7 = <code>0x00</code>)</em>
|
||||
| <code>0x00</code> | <em>(COFF Bits 8-9 = 0)</em> <br> <em>(CON Bits 8-9 = 0)</em> <br> <em>(SPOFF Bits 8-9 = 0)</em> <br> <em>(SPON Bits 8-9 = 0)</em>
|
||||
| <code>0x00</code> | <em>(COFF2 Bits 8-9 = 0)</em> <br> <em>(CON2 Bits 8-9 = 0)</em>
|
||||
|
|
||||
| #17
|
||||
| <code>0xEA</code> | <strong>SETGIP2</strong> (Page 170): <br> Set backward GIP timing
|
||||
| <code>0x02</code> | YS2 Signal Mode = INYS1/INYS2 <br> <em>(YS2_SEL = 0)</em> <br> YS2 Signal Mode = INYS1/INYS2 <br> <em>(YS1_SEL = 0)</em> <br> Don’t reverse YS2 signal <br> <em>(YS2_XOR = 0)</em> <br> Don’t reverse YS1 signal <br> <em>(YS1_XOR = 0)</em> <br> Enable YS signal function <br> <em>(YS_FLAG_EN = 1)</em> <br> Disable ALL ON function <br> <em>(ALL_ON_EN = 0)</em>
|
||||
| <code>0x21</code> | <em>(GATE = <code>0x21</code>)</em>
|
||||
| <code>0x00</code> | <em>(CK_ALL_ON_EN = 0)</em> <br> <em>(STV_ALL_ON_EN = 0)</em> <br> Timing of YS1 and YS2 signal = ??? <br> <em>(CK_ALL_ON_WIDTH1 = 0)</em>
|
||||
| <code>0x00</code> | Timing of YS1 and YS2 signal = ??? <br> <em>(CK_ALL_ON_WIDTH2 = 0)</em>
|
||||
| <code>0x00</code> | Timing of YS1 and YS2 signal = ??? <br> <em>(CK_ALL_ON_WIDTH3 = 0)</em>
|
||||
| <code>0x00</code> | <em>(YS_FLAG_PERIOD = 0)</em>
|
||||
| <code>0x00</code> | <em>(YS2_SEL_2 = 0)</em> <br> <em>(YS1_SEL_2 = 0)</em> <br> <em>(YS2_XOR_2 = 0)</em> <br> <em>(YS_FLAG_EN_2 = 0)</em> <br> <em>(ALL_ON_EN_2 = 0)</em>
|
||||
| <code>0x00</code> | Distance of GIP ALL On rising edge and DE = ??? <br> <em>(USER_GIP_GATE1_2 = 0)</em>
|
||||
| <code>0x00</code> | <em>(CK_ALL_ON_EN_2 = 0)</em> <br> <em>(STV_ALL_ON_EN_2 = 0)</em> <br> <em>(CK_ALL_ON_WIDTH1_2 = 0)</em>
|
||||
| <code>0x00</code> | <em>(CK_ALL_ON_WIDTH2_2 = 0)</em>
|
||||
| <code>0x00</code> | <em>(CK_ALL_ON_WIDTH3_2 = 0)</em>
|
||||
| <code>0x00</code> | <em>(YS_FLAG_PERIOD_2 = 0)</em>
|
||||
| <code>0x02</code> | <em>(COS1_L_GS = 0)</em> <br> <em>(COS2_L_GS = 2)</em>
|
||||
| <code>0x46</code> | <em>(COS3_L_GS = 4)</em> <br> <em>(COS4_L_GS = 6)</em>
|
||||
| <code>0x02</code> | <em>(COS5_L_GS = 0)</em> <br> <em>(COS6_L_GS = 2)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 16)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 17)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 18)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 19)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 20)</em>
|
||||
| <code>0x88</code> | <em>(COS17_L_GS = 8)</em> <br> <em>(COS18_L_GS = 8)</em>
|
||||
| <code>0x64</code> | <em>(COS19_L_GS = 6)</em> <br> <em>(COS20_L_GS = 4)</em>
|
||||
| <code>0x88</code> | <em>(COS21_L_GS = 8)</em> <br> <em>(COS22_L_GS = 8)</em>
|
||||
| <code>0x13</code> | <em>(COS1_R_GS = 1)</em> <br> <em>(COS2_R_GS = 3)</em>
|
||||
| <code>0x57</code> | <em>(COS3_R_GS = 5)</em> <br> <em>(COS4_R_GS = 7)</em>
|
||||
| <code>0x13</code> | <em>(COS5_R_GS = 1)</em> <br> <em>(COS6_R_GS = 3)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 27)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 28)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 29)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 30)</em>
|
||||
| <code>0x88</code> | Reserved <em>(Parameter 31)</em>
|
||||
| <code>0x88</code> | <em>(COS17_R_GS = 8)</em> <br> <em>(COS18_R_GS = 8)</em>
|
||||
| <code>0x75</code> | <em>(COS19_R_GS = 7)</em> <br> <em>(COS20_R_GS = 5)</em>
|
||||
| <code>0x88</code> | <em>(COS21_R_GS = 8)</em> <br> <em>(COS22_R_GS = 8)</em>
|
||||
| <code>0x23</code> | GIP output EQ signal: P_EQ = Yes, N_EQ = No <br> <em>(EQOPT = 2)</em> <br> GIP output EQ signal level: P_EQ = GND, N_EQ = GND <br> <em>(EQ_SEL = 3)</em>
|
||||
| <code>0x14</code> | Distance of EQ rising edge and HYSNC = 20 Fosc <br> <em>(EQ_DELAY = <code>0x14</code>)</em>
|
||||
| <code>0x00</code> | Distance of EQ rising edge and HYSNC = 0 HSYNC <br> <em>(EQ_DELAY_HSYNC = 0)</em>
|
||||
| <code>0x00</code> | <em>(HSYNC_TO_CL1_CNT10 Bits 8-9 = 0)</em>
|
||||
| <code>0x02</code> | GIP reference HSYNC between external HSYNC = 2 Fosc <br> <em>(HSYNC_TO_CL1_CNT10 Bits 0-7 = 2)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 40)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 41)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 42)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 43)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 44)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 45)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 46)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 47)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 48)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 49)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 50)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 51)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 52)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 53)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 54)</em>
|
||||
| <code>0x03</code> | Undocumented <em>(Parameter 55)</em>
|
||||
| <code>0x0A</code> | Undocumented <em>(Parameter 56)</em>
|
||||
| <code>0xA5</code> | Undocumented <em>(Parameter 57)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 58)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 59)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 60)</em>
|
||||
| <code>0x00</code> | Undocumented <em>(Parameter 61)</em>
|
||||
|
|
||||
| #18
|
||||
| <code>0xE0</code> | <strong>SETGAMMA</strong> (Page 158): <br> Set the gray scale voltage to adjust the gamma characteristics of the TFT panel
|
||||
| <code>0x00</code> | <em>(PVR0 = <code>0x00</code>)</em>
|
||||
| <code>0x09</code> | <em>(PVR1 = <code>0x09</code>)</em>
|
||||
| <code>0x0D</code> | <em>(PVR2 = <code>0x0D</code>)</em>
|
||||
| <code>0x23</code> | <em>(PVR3 = <code>0x23</code>)</em>
|
||||
| <code>0x27</code> | <em>(PVR4 = <code>0x27</code>)</em>
|
||||
| <code>0x3C</code> | <em>(PVR5 = <code>0x3C</code>)</em>
|
||||
| <code>0x41</code> | <em>(PPR0 = <code>0x41</code>)</em>
|
||||
| <code>0x35</code> | <em>(PPR1 = <code>0x35</code>)</em>
|
||||
| <code>0x07</code> | <em>(PPK0 = <code>0x07</code>)</em>
|
||||
| <code>0x0D</code> | <em>(PPK1 = <code>0x0D</code>)</em>
|
||||
| <code>0x0E</code> | <em>(PPK2 = <code>0x0E</code>)</em>
|
||||
| <code>0x12</code> | <em>(PPK3 = <code>0x12</code>)</em>
|
||||
| <code>0x13</code> | <em>(PPK4 = <code>0x13</code>)</em>
|
||||
| <code>0x10</code> | <em>(PPK5 = <code>0x10</code>)</em>
|
||||
| <code>0x12</code> | <em>(PPK6 = <code>0x12</code>)</em>
|
||||
| <code>0x12</code> | <em>(PPK7 = <code>0x12</code>)</em>
|
||||
| <code>0x18</code> | <em>(PPK8 = <code>0x18</code>)</em>
|
||||
| <code>0x00</code> | <em>(NVR0 = <code>0x00</code>)</em>
|
||||
| <code>0x09</code> | <em>(NVR1 = <code>0x09</code>)</em>
|
||||
| <code>0x0D</code> | <em>(NVR2 = <code>0x0D</code>)</em>
|
||||
| <code>0x23</code> | <em>(NVR3 = <code>0x23</code>)</em>
|
||||
| <code>0x27</code> | <em>(NVR4 = <code>0x27</code>)</em>
|
||||
| <code>0x3C</code> | <em>(NVR5 = <code>0x3C</code>)</em>
|
||||
| <code>0x41</code> | <em>(NPR0 = <code>0x41</code>)</em>
|
||||
| <code>0x35</code> | <em>(NPR1 = <code>0x35</code>)</em>
|
||||
| <code>0x07</code> | <em>(NPK0 = <code>0x07</code>)</em>
|
||||
| <code>0x0D</code> | <em>(NPK1 = <code>0x0D</code>)</em>
|
||||
| <code>0x0E</code> | <em>(NPK2 = <code>0x0E</code>)</em>
|
||||
| <code>0x12</code> | <em>(NPK3 = <code>0x12</code>)</em>
|
||||
| <code>0x13</code> | <em>(NPK4 = <code>0x13</code>)</em>
|
||||
| <code>0x10</code> | <em>(NPK5 = <code>0x10</code>)</em>
|
||||
| <code>0x12</code> | <em>(NPK6 = <code>0x12</code>)</em>
|
||||
| <code>0x12</code> | <em>(NPK7 = <code>0x12</code>)</em>
|
||||
| <code>0x18</code> | <em>(NPK8 = <code>0x18</code>)</em>
|
||||
|
|
||||
| #19<br />
|
||||
| <code>0x11</code> | <strong>SLPOUT</strong> (Page 89): <br> Turns off sleep mode <br> <em>(MIPI_DCS_EXIT_SLEEP_MODE)</em>
|
||||
|
|
||||
| <code>!!!</code> | <strong>Insert Delay Here:</strong> <br> Wait 120 milliseconds
|
||||
|
|
||||
| #20
|
||||
| <code>0x29</code> | <strong>Display On</strong> (Page 97): <br> Recover from DISPLAY OFF mode <br> <em>(MIPI_DCS_SET_DISPLAY_ON)</em></p>
|
||||
<p>The above commands were originally specified here (partially annotated)…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/panel/panel-sitronix-st7703.c#L174-L333"><strong>Initialisation Sequence</strong></a></li>
|
||||
|
|
|
@ -445,7 +445,7 @@ static int button_isr_handler(FAR struct ioexpander_dev_s *dev, ioe_pinset_t pin
|
|||
<p>Note that the Interrupt Callback runs in the <strong>BL602 Interrupt Context</strong>.</p>
|
||||
<p>Be careful!</p>
|
||||
<h2 id="gpio-command"><a class="doc-anchor" href="#gpio-command">§</a>4.3 GPIO Command</h2>
|
||||
<p>Another way to test the Push Button Interrupt is to use the <strong>GPIO Command</strong>. </p>
|
||||
<p>Another way to test the Push Button Interrupt is to use the <strong>GPIO Command</strong>.</p>
|
||||
<p>(This only works if we don’t call <strong>IOEP_ATTACH</strong> to attach the Interrupt Callback)</p>
|
||||
<p>Enter this in the NuttX Shell…</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>gpio -t 8 -w 1 /dev/gpio12
|
||||
|
@ -1067,7 +1067,7 @@ FAR struct ioexpander_dev_s *bl602_expander_initialize(
|
|||
memset(gpio_is_used, 0, sizeof(gpio_is_used));
|
||||
</code></pre></div>
|
||||
<p>Now we handle the <strong>GPIO Inputs</strong>.</p>
|
||||
<p>Given the BL602 Pinset (from the Pin Definition), we call
|
||||
<p>Given the BL602 Pinset (from the Pin Definition), we call
|
||||
<a href="https://github.com/lupyuen/nuttx/blob/pinedio/arch/risc-v/src/bl602/bl602_gpio.c#L58-L140"><strong>bl602_configgpio</strong></a> to <strong>configure each GPIO Input</strong>…</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code> /* Configure and register the GPIO Inputs */
|
||||
for (int i = 0; i < gpio_input_count; i++)
|
||||
|
|
|
@ -482,7 +482,7 @@ nsh>
|
|||
</ul>
|
||||
<p>Let’s look at each section of the Flash Image.</p>
|
||||
<h2 id="xip-flash-memory-vs-boot-rom"><a class="doc-anchor" href="#xip-flash-memory-vs-boot-rom">§</a>2.1 XIP Flash Memory vs Boot ROM</h2>
|
||||
<p>In this article we’ll use “ROM” to refer to BL602’s <strong>XIP Flash Memory</strong> (at address <code>0x2300 0000</code> with size 16 MB). </p>
|
||||
<p>In this article we’ll use “ROM” to refer to BL602’s <strong>XIP Flash Memory</strong> (at address <code>0x2300 0000</code> with size 16 MB).</p>
|
||||
<p>(XIP means Execute In Place… BL602 lets us run firmware code from External Flash Memory without transferring to RAM first. <a href="https://lupyuen.github.io/articles/boot#bl602-boot2-bootloader">See this</a>)</p>
|
||||
<p>The <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL602_RM/en/BL602_BL604_RM_1.2_en.pdf">BL602 Reference Manual</a> uses “ROM” to refer to BL602’s <strong>Boot ROM</strong> (at address <code>0x2100 0000</code> with size 128 KB).</p>
|
||||
<p>BL602’s Boot ROM is explained here…</p>
|
||||
|
@ -615,7 +615,7 @@ blk64k_erase_cmd = 0xd8
|
|||
<li>
|
||||
<p>Which gets transformed with the <strong>EFuse Configuration</strong> (and Boot Header Configuration) to create (with firmware offset <code>0x2000</code>)…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/bouffalolab/BLOpenFlasher/blob/main/bl602/image/boot2image.bin"><code>boot2image.bin</code></a> </li>
|
||||
<li><a href="https://github.com/bouffalolab/BLOpenFlasher/blob/main/bl602/image/boot2image.bin"><code>boot2image.bin</code></a></li>
|
||||
</ul>
|
||||
<p>(For development, we have disabled encryption in the EFuse Configuration)</p>
|
||||
</li>
|
||||
|
@ -945,7 +945,7 @@ blk64k_erase_cmd = 0xd8
|
|||
<li>
|
||||
<p>Do all BL602 boards use a jumper or switch for flashing the firmware?</p>
|
||||
<p>Yep because all BL602 boards run on the same Boot ROM… Which uses GPIO 8 to decide whether it should go into Flashing Mode.</p>
|
||||
<p>Hopefully somebody will create a smarter way to flash BL602 boards and reduce the build-flash-test cycle time. </p>
|
||||
<p>Hopefully somebody will create a smarter way to flash BL602 boards and reduce the build-flash-test cycle time.</p>
|
||||
<p><strong>UPDATE:</strong> Check out this article…</p>
|
||||
<p><a href="https://lupyuen.github.io/articles/auto"><strong>“Auto Flash and Test NuttX on RISC-V BL602”</strong></a></p>
|
||||
</li>
|
||||
|
|
|
@ -433,7 +433,7 @@ Gateway ID: YOUR_GATEWAY_ID
|
|||
<p><img src="https://lupyuen.github.io/images/gateway-stack.png" alt="Decoded Sensor Data in the Live Data Table" /></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Click on a message in the <strong>Live Data Table</strong>. </p>
|
||||
<p>Click on a message in the <strong>Live Data Table</strong>.</p>
|
||||
<p>We should see the <strong>decoded_payload</strong> field containing our Decoded Sensor Data…</p>
|
||||
<div class="example-wrap"><pre class="language-json"><code>{
|
||||
...
|
||||
|
|
|
@ -168,7 +168,7 @@ int main(int argc, char **argv) {
|
|||
</blockquote>
|
||||
<ul>
|
||||
<li>
|
||||
<p>But with <strong>BL602 IoT SDK</strong> we’ll be locked in to BL602. Our programs won’t run on other microcontrollers. </p>
|
||||
<p>But with <strong>BL602 IoT SDK</strong> we’ll be locked in to BL602. Our programs won’t run on other microcontrollers.</p>
|
||||
<p>And we can’t easily port programs from other devices to BL602 either.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -177,7 +177,7 @@ int main(int argc, char **argv) {
|
|||
</blockquote>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Arduino</strong> is kinda ancient for embedded coding. (And the Bit Banging looks disturbing) </p>
|
||||
<p><strong>Arduino</strong> is kinda ancient for embedded coding. (And the Bit Banging looks disturbing)</p>
|
||||
<p>Could there be a modern alternative that works better with today’s multitasking microcontrollers?</p>
|
||||
<p><a href="https://github.com/pine64/ArduinoCore-bouffalo">More about Arduino on BL602</a></p>
|
||||
</li>
|
||||
|
@ -196,7 +196,7 @@ int main(int argc, char **argv) {
|
|||
</blockquote>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Maybe something simpler than <strong>Zephyr</strong>? As simple as the program above? </p>
|
||||
<p>Maybe something simpler than <strong>Zephyr</strong>? As simple as the program above?</p>
|
||||
<p>Something that compiles easily on Linux, macOS and Windows… Without WSL and Docker?</p>
|
||||
<p>(We’ll save Zephyr for the bravest embedded professionals)</p>
|
||||
<p><a href="https://github.com/nandojve/zephyr/blob/bouffalo/boards/riscv/dt_bl10_devkit/doc/index.rst"><strong>UPDATE: Check out Zephyr for BL602</strong></a></p>
|
||||
|
@ -675,7 +675,7 @@ objsize
|
|||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>blinky.elf.map</code>: GCC Linker Map for our Mynewt Firmware. </p>
|
||||
<p><code>blinky.elf.map</code>: GCC Linker Map for our Mynewt Firmware.</p>
|
||||
<p>Shows the addresses of every function and global variable in our firmware.</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/lupyuen/pinecone-rust-mynewt/releases/download/v2.0.0/blinky.elf.map">Download here</a></li>
|
||||
|
@ -774,7 +774,7 @@ Use `riscv authdata_read` and `riscv authdata_write` commands to authenticate.
|
|||
<p><em>Sensors and actuators to be tested with PineCone BL602</em></p>
|
||||
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>10 What’s Next</h1>
|
||||
<p>We’ll be testing Mynewt on PineCone BL602 with an interesting mix of Sensors and Actuators shown above. <a href="https://lupyuen.github.io/articles/gpio#appendix-inventory-of-sensors-and-actuators">Here’s the list</a></p>
|
||||
<p>Most of the Sensors and Actuators will work fine with GPIO on Mynewt. But some require additional support from Mynewt: <strong>Pulse Width Modulation</strong> and <strong>Analog to Digital Converter</strong>. </p>
|
||||
<p>Most of the Sensors and Actuators will work fine with GPIO on Mynewt. But some require additional support from Mynewt: <strong>Pulse Width Modulation</strong> and <strong>Analog to Digital Converter</strong>.</p>
|
||||
<p>We shall integrate with Mynewt the respective Hardware Abstraction Layers from the BL602 IoT SDK.</p>
|
||||
<blockquote>
|
||||
<p><em>What about DHT11, the Temperature and Humidity Sensor? (Lower right corner)</em></p>
|
||||
|
|
|
@ -817,7 +817,7 @@ sudo service grafana-server status
|
|||
brew services start grafana
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>To test Grafana, browse to </p>
|
||||
<p>To test Grafana, browse to</p>
|
||||
<p><strong><code>http://localhost:3000</code></strong></p>
|
||||
<p><strong>Username:</strong> admin</p>
|
||||
<p><strong>Password:</strong> admin</p>
|
||||
|
|
|
@ -103,7 +103,7 @@
|
|||
<li><a href="#appendix-how-to-troubleshoot-risc-v-exceptions">13 Appendix: How to Troubleshoot RISC-V Exceptions</a><ul></ul></li>
|
||||
<li><a href="#appendix-test-bme280-with-bus-pirate">14 Appendix: Test BME280 with Bus Pirate</a><ul></ul></li></ul></nav><p>📝 <em>29 Jan 2021</em></p>
|
||||
<p><strong><a href="https://lupyuen.github.io/articles/pinecone">PineCone BL602</a> (<a href="https://wiki.pine64.org/wiki/Nutcracker#Pinenut-01S_Module_information_and_schematics">and Pinenut</a>)</strong> is an awesome RISC-V Microcontroller Board with WiFi and Bluetooth LE Networking.</p>
|
||||
<p>But to turn PineCone BL602 into an <strong>IoT Gadget</strong> we need one more thing… </p>
|
||||
<p>But to turn PineCone BL602 into an <strong>IoT Gadget</strong> we need one more thing…</p>
|
||||
<p><strong>An I2C Sensor!</strong></p>
|
||||
<p>Today we shall connect PineCone / Pinenut / Any BL602 Board to an I2C Sensor and read some data.</p>
|
||||
<p>We shall also discover a feature that’s unique to BL602: <strong>I2C Register Addresses</strong></p>
|
||||
|
@ -123,7 +123,7 @@
|
|||
<p>BL602’s I2C HAL is packaged as two levels…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Low Level HAL <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></strong>: This runs on BL602 Bare Metal. </p>
|
||||
<p><strong>Low Level HAL <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></strong>: This runs on BL602 Bare Metal.</p>
|
||||
<p>The Low Level HAL manipulates the BL602 I2C Registers directly to perform I2C functions.</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -136,7 +136,7 @@
|
|||
<p>Today we shall use the <strong>Low Level I2C HAL <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></strong> because…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>The Low Level I2C HAL is <strong>simpler to understand</strong>. </p>
|
||||
<p>The Low Level I2C HAL is <strong>simpler to understand</strong>.</p>
|
||||
<p>We’ll learn all about the BL602 I2C Hardware by calling the Low Level HAL Functions.</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -181,7 +181,7 @@
|
|||
<p>BME280 has an I2C Register, <strong>Chip ID, at Register <code>0xD0</code></strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Reading the Chip ID Register will give us the <strong>Chip ID value <code>0x60</code></strong> </p>
|
||||
<p>Reading the Chip ID Register will give us the <strong>Chip ID value <code>0x60</code></strong></p>
|
||||
<p>(<code>0x60</code> identifies the chip as BME280. For BMP280 the Chip ID is <code>0x58</code>)</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
@ -283,7 +283,7 @@ static i2c_msg_t read_msg; // Message for reading I2C Data
|
|||
static uint8_t read_buf[32]; // Buffer for reading I2C Data
|
||||
int data_len = 1; // Bytes to be read
|
||||
</code></pre></div>
|
||||
<p>First we define <code>read_msg</code> as a static I2C Message. </p>
|
||||
<p>First we define <code>read_msg</code> as a static I2C Message.</p>
|
||||
<p>The data returned by our BME280 Sensor shall be stored in the static buffer <code>read_buf</code>.</p>
|
||||
<h2 id="set-i2c-operation-and-buffer"><a class="doc-anchor" href="#set-i2c-operation-and-buffer">§</a>4.2 Set I2C Operation and Buffer</h2><div class="example-wrap"><pre class="language-c"><code>// Set the I2C operation
|
||||
read_msg.i2cx = 0; // I2C Port
|
||||
|
@ -701,7 +701,7 @@ i2c_start_read : Start reading I2C data
|
|||
i2c_stop_read : Stop reading I2C data
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>First we <strong>initialise our I2C Port</strong>. </p>
|
||||
<p>First we <strong>initialise our I2C Port</strong>.</p>
|
||||
<p>Enter this command…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>i2c_init
|
||||
</code></pre></div>
|
||||
|
@ -1031,11 +1031,11 @@ msg: Store/AMO access fault
|
|||
<p>This means that Bus Pirate will initiate two I2C Transactions, indicated by <strong><code>[ ... ]</code></strong></p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>In the First I2C Transaction:</strong> Bus Pirate sends <strong><code>0xEE</code></strong> to indicate a Write Transaction (for address <code>0x77</code>). </p>
|
||||
<p><strong>In the First I2C Transaction:</strong> Bus Pirate sends <strong><code>0xEE</code></strong> to indicate a Write Transaction (for address <code>0x77</code>).</p>
|
||||
<p>Then it sends the I2C Register to be read: <strong><code>0xD0</code></strong> (Chip ID)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>In the Second I2C Transaction:</strong> Bus Pirate sends <strong><code>0xEF</code></strong> to indicate a Read Transaction (for address <code>0x77</code>). </p>
|
||||
<p><strong>In the Second I2C Transaction:</strong> Bus Pirate sends <strong><code>0xEF</code></strong> to indicate a Read Transaction (for address <code>0x77</code>).</p>
|
||||
<p>BME280 returns the value of the Chip ID Register, indicated by <strong><code>r</code></strong></p>
|
||||
</li>
|
||||
</ol>
|
||||
|
|
|
@ -487,7 +487,7 @@ Got PM2.5 Concentration: 23 µg/m³
|
|||
</li>
|
||||
<li>
|
||||
<p>Each Sensor Data Frame has 20 bytes. Why are so many bytes unused?</p>
|
||||
<p>IKEA Air Quality Sensor uses the PM1006 Sensor, which is a cheaper version of PM1006K. </p>
|
||||
<p>IKEA Air Quality Sensor uses the PM1006 Sensor, which is a cheaper version of PM1006K.</p>
|
||||
<p>On PM1006K we get more data fields: PM 1.0 and PM 10. These fields are not available on PM1006, hence we have unused bytes in the Sensor Data Frame.</p>
|
||||
<p><a href="https://en.gassensor.com.cn/Product_files/Specifications/LED%20Particle%20Sensor%20PM1006K%20Specification.pdf">(PM1006K Datasheet)</a></p>
|
||||
</li>
|
||||
|
@ -524,7 +524,7 @@ Got PM2.5 Concentration: 23 µg/m³
|
|||
<p>(Very fine Solder Wire really helps)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Carefully place our <strong>Blue Solid Core Wire</strong> (or similar) on top of the Solder Blob. </p>
|
||||
<p>Carefully place our <strong>Blue Solid Core Wire</strong> (or similar) on top of the Solder Blob.</p>
|
||||
<p>(Now hardened)</p>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
<p>Let’s flash the <strong>GPIO Demo</strong> from the BL602 IoT SDK and interact with the above GPIO Pins…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Download the <strong>BL602 Demo Firmware Binaries</strong>… </p>
|
||||
<p>Download the <strong>BL602 Demo Firmware Binaries</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/lupyuen/bl_iot_sdk/releases/download/v1.0.0/customer_app.zip"><strong>BL602 Demo Firmware Binaries</strong>: <code>customer_app.zip</code></a></li>
|
||||
</ul>
|
||||
|
@ -266,7 +266,7 @@ void bl_gpio_register(gpio_ctx_t *pstnode);
|
|||
<li><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_gpio/sdk_app_gpio/demo.c"><strong>GPIO Demo Source Code: <code>demo.c</code></strong></a></li>
|
||||
</ul>
|
||||
<h2 id="gpio-device-tree"><a class="doc-anchor" href="#gpio-device-tree">§</a>3.4 GPIO Device Tree</h2>
|
||||
<p>There is an alternative set of functions for controlling GPIO… </p>
|
||||
<p>There is an alternative set of functions for controlling GPIO…</p>
|
||||
<ul>
|
||||
<li><strong>GPIO Device Tree</strong>: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_gpio.h"><strong><code>hal_gpio.h</code></strong></a>, <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_gpio.c"><strong><code>hal_gpio.c</code></strong></a></li>
|
||||
</ul>
|
||||
|
@ -314,7 +314,7 @@ void bl_gpio_register(gpio_ctx_t *pstnode);
|
|||
<p>(The firmware was modified to run without a Device Tree. <a href="https://github.com/lupyuen/bl_iot_sdk/pull/1">More details</a>)</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Download the <strong>BL602 Demo Firmware Binaries</strong>… </p>
|
||||
<p>Download the <strong>BL602 Demo Firmware Binaries</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/lupyuen/bl_iot_sdk/releases/download/v1.0.0/customer_app.zip"><strong>BL602 Demo Firmware Binaries</strong>: <code>customer_app.zip</code></a></li>
|
||||
</ul>
|
||||
|
@ -355,8 +355,8 @@ pwm_init 2 17 2000
|
|||
pwm_init 4 14 2000
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>Set <strong>PWM Duty Cycle</strong> for all 3 PWM Channels to 100%. </p>
|
||||
<p>Which means that 100% of the time, the 3 PWM Channels will be set to 1 (High). </p>
|
||||
<p>Set <strong>PWM Duty Cycle</strong> for all 3 PWM Channels to 100%.</p>
|
||||
<p>Which means that 100% of the time, the 3 PWM Channels will be set to 1 (High).</p>
|
||||
<p>Which means total darkness: All 3 LEDs will be switched off 100% of the time.</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>pwm_duty_set 1 100
|
||||
pwm_duty_set 2 100
|
||||
|
@ -369,7 +369,7 @@ pwm_start 2
|
|||
pwm_start 4
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>Gradually decrease the PWM Duty Cycle for PWM Channel 1 (Blue) from 100% to 0%. </p>
|
||||
<p>Gradually decrease the PWM Duty Cycle for PWM Channel 1 (Blue) from 100% to 0%.</p>
|
||||
<p>This means the Blue LED will gradually get brighter.</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>pwm_duty_set 1 75
|
||||
pwm_duty_set 1 50
|
||||
|
@ -450,7 +450,7 @@ int32_t bl_pwm_stop( uint8_t id);
|
|||
<li><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_pwm/sdk_app_pwm/main.c"><strong>PWM Demo Source Code: <code>main.c</code></strong></a></li>
|
||||
</ul>
|
||||
<h2 id="pwm-device-tree"><a class="doc-anchor" href="#pwm-device-tree">§</a>6.4 PWM Device Tree</h2>
|
||||
<p>There is an alternative set of functions for controlling PWM… </p>
|
||||
<p>There is an alternative set of functions for controlling PWM…</p>
|
||||
<ul>
|
||||
<li><strong>PWM Device Tree</strong>: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_pwm.h"><strong><code>hal_pwm.h</code></strong></a>, <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_pwm.c"><strong><code>hal_pwm.c</code></strong></a></li>
|
||||
</ul>
|
||||
|
|
|
@ -152,7 +152,7 @@
|
|||
<p>Porting uLisp to BL602 (as a C library <a href="https://github.com/lupyuen/ulisp-bl602"><code>ulisp-bl602</code></a>) was quick and easy.</p>
|
||||
<p>(More about this in a while.)</p>
|
||||
<p><em>What about porting the Arduino functions like <code>pinmode</code> and <code>digitalwrite</code>?</em></p>
|
||||
<p>The <a href="https://github.com/lupyuen/bl_iot_sdk/tree/master"><strong>BL602 IoT SDK</strong></a> doesn’t have these GPIO functions. </p>
|
||||
<p>The <a href="https://github.com/lupyuen/bl_iot_sdk/tree/master"><strong>BL602 IoT SDK</strong></a> doesn’t have these GPIO functions.</p>
|
||||
<p>So in BL602 uLisp we reimplemented these functions with the <strong>BL602 Hardware Abstraction Layer for GPIO</strong>.</p>
|
||||
<p>(While exposing the same old names to uLisp programs: <code>pinmode</code> and <code>digitalwrite</code>)</p>
|
||||
<p><em>Anything else we should know about uLisp?</em></p>
|
||||
|
@ -365,17 +365,17 @@ blflash flash c:\blflash\sdk_app_ulisp.bin --port COM5
|
|||
<p>We run the <code>blinky</code> function like so…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>( blinky )
|
||||
</code></pre></div>
|
||||
<p>And the <strong>LED blinks every second!</strong> </p>
|
||||
<p>And the <strong>LED blinks every second!</strong></p>
|
||||
<p>(Restart the board to stop it, sorry)</p>
|
||||
<p><a href="https://youtu.be/TN4OaZNGjOA"><strong>Watch the demo on YouTube</strong></a></p>
|
||||
<p><a href="http://www.ulisp.com/show?1AEK">(Based on the Blinky function from uLisp)</a></p>
|
||||
<h1 id="now-add-blockly"><a class="doc-anchor" href="#now-add-blockly">§</a>4 Now add Blockly</h1>
|
||||
<p>According to the <a href="https://developers.google.com/blockly/guides/overview">Blockly Overview</a>…</p>
|
||||
<blockquote>
|
||||
<p>Blockly is a library that adds a <strong>visual code editor</strong> to web and mobile apps. </p>
|
||||
<p>Blockly is a library that adds a <strong>visual code editor</strong> to web and mobile apps.</p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>The Blockly editor uses <strong>interlocking, graphical blocks</strong> to represent code concepts like variables, logical expressions, loops, and more. </p>
|
||||
<p>The Blockly editor uses <strong>interlocking, graphical blocks</strong> to represent code concepts like variables, logical expressions, loops, and more.</p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>It allows users to apply programming principles <strong>without having to worry about syntax</strong> or the intimidation of a blinking cursor on the command line.</p>
|
||||
|
@ -684,7 +684,7 @@ async function runWebSerialCommand(command, expectedResponse) {
|
|||
<ol>
|
||||
<li>
|
||||
<p><strong>No Heap Memory, just Static Memory</strong></p>
|
||||
<p>uLisp needs only Static Memory, no Heap Memory. </p>
|
||||
<p>uLisp needs only Static Memory, no Heap Memory.</p>
|
||||
<p>This makes uLisp highly portable across microcontrollers: <a href="https://github.com/lupyuen/ulisp-bl602/blob/master/src/ulisp.c#L193-L194"><code>ulisp.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>#define WORKSPACESIZE 8000 // Cells (8*bytes)
|
||||
#define SYMBOLTABLESIZE 1024 // Bytes
|
||||
|
@ -694,7 +694,7 @@ char SymbolTable[SYMBOLTABLESIZE];
|
|||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>Reading from BL602 Flash Memory is simpler</strong></p>
|
||||
<p>On Arduino we access Flash Memory by calling <code>PSTR</code>. </p>
|
||||
<p>On Arduino we access Flash Memory by calling <code>PSTR</code>.</p>
|
||||
<p>That’s not necessary on BL602, so we stub out the Flash Memory functions: <a href="https://github.com/lupyuen/ulisp-bl602/blob/master/src/ulisp.c#L9-L11"><code>ulisp.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>#define PGM_P const char *
|
||||
#define PROGMEM
|
||||
|
@ -1011,7 +1011,7 @@ int gserial() {
|
|||
<p>We modified these Blockly source files to load the Custom Blocks and generate uLisp code…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="https://github.com/AppKaki/blockly-ulisp/blob/master/demos/code/index.html"><code>demos/code/index.html</code></a> </p>
|
||||
<p><a href="https://github.com/AppKaki/blockly-ulisp/blob/master/demos/code/index.html"><code>demos/code/index.html</code></a></p>
|
||||
<p>This is the <strong>HTML source file</strong> for the Blockly Web Editor. (See pic above)</p>
|
||||
<p><a href="https://github.com/AppKaki/blockly-ulisp/pull/1/files#diff-dcf2ffe98d7d8b4a0dd7b9f769557dbe8c9e0e726236ef229def25c956a43d8f">(See changes)</a></p>
|
||||
</li>
|
||||
|
|
|
@ -443,7 +443,7 @@ send_message : Send LoRa message
|
|||
spi_result : Show SPI counters
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>First we <strong>initialise our LoRa Transceiver</strong>. </p>
|
||||
<p>First we <strong>initialise our LoRa Transceiver</strong>.</p>
|
||||
<p>Enter this command…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>init_driver
|
||||
</code></pre></div>
|
||||
|
@ -509,7 +509,7 @@ Register 0x04 = 0x00
|
|||
Register 0x05 = 0x52
|
||||
...
|
||||
</code></pre></div>
|
||||
<p>Take the values of <strong>Registers 2, 3, 4 and 5.</strong> </p>
|
||||
<p>Take the values of <strong>Registers 2, 3, 4 and 5.</strong></p>
|
||||
<p>Compare them with the <strong>Register Table</strong> in the SX1276 (or RF96) Datasheet.</p>
|
||||
<p>The values should be identical: <strong><code>0x1a</code>, <code>0x0b</code>, <code>0x00</code>, <code>0x52</code></strong></p>
|
||||
<p><img src="https://lupyuen.github.io/images/lora-registers.png" alt="Reading registers from our LoRa transceiver" /></p>
|
||||
|
@ -554,7 +554,7 @@ Register 0x08 = 0x00
|
|||
<p>By transmitting packets in this unique chirping pattern, LoRa ensures that packets will be delivered over long distances in spite of the noise and interference.</p>
|
||||
<p>(LoRa doesn’t guarantee 100% reliable delivery, of course)</p>
|
||||
<p><img src="https://lupyuen.github.io/images/lora-airspy2.jpg" alt="Airspy R2 SDR" /></p>
|
||||
<p><em>Airspy R2 SDR</em> </p>
|
||||
<p><em>Airspy R2 SDR</em></p>
|
||||
<h2 id="capture-lora-packets-with-airspy-sdr"><a class="doc-anchor" href="#capture-lora-packets-with-airspy-sdr">§</a>6.1 Capture LoRa packets with Airspy SDR</h2>
|
||||
<p>Let’s capture and visualise our LoRa Packet with <a href="https://www.itead.cc/airspy.html"><strong>Airspy R2 SDR</strong></a> (Software Defined Radio)…</p>
|
||||
<ol>
|
||||
|
@ -604,7 +604,7 @@ send_message
|
|||
<p><a href="https://en.wikipedia.org/wiki/LoRa#LoRaWAN">(More about LoRaWAN)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>The Things Network</strong> = The free LoRaWAN network that’s operated by volunteers around the world. </p>
|
||||
<p><strong>The Things Network</strong> = The free LoRaWAN network that’s operated by volunteers around the world.</p>
|
||||
<p>People actually set up base stations and allow free access.</p>
|
||||
<p>Our garden sensors could connect to The Things Network… So that we may browse the sensor data conveniently.</p>
|
||||
<p><a href="https://lupyuen.github.io/articles/ttn">(More about The Things Network)</a></p>
|
||||
|
|
|
@ -172,7 +172,7 @@
|
|||
<p>However today we’re configuring our transceiver for <strong>Continuous Receive Mode</strong> so we won’t be using <code>DIO1</code>. We shall trigger receive timeouts with a BL602 Timer.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong><code>DIO2</code> Change Channel</strong>: This is used for <strong>Spread Spectrum Transmission</strong> (Frequency Hopping). </p>
|
||||
<p><strong><code>DIO2</code> Change Channel</strong>: This is used for <strong>Spread Spectrum Transmission</strong> (Frequency Hopping).</p>
|
||||
<p>When we transmit / receive LoRa Packets over multiple frequencies (spread spectrum), we reduce the likelihood of packet collisions over the airwaves.</p>
|
||||
<p>We won’t be using Spread Spectrum Transmission today, so <code>DIO2</code> shall stay idle.</p>
|
||||
</li>
|
||||
|
@ -733,7 +733,7 @@ void SX1276IoIrqInit(DioIrqHandler **irqHandlers) {
|
|||
<p><strong>Handling an interrupt</strong> gets tricky for any Embedded Program…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Interrupts are Time-Sensitive</strong>: We can’t take too long to handle an interrupt… Other interrupts may be waiting on us! </p>
|
||||
<p><strong>Interrupts are Time-Sensitive</strong>: We can’t take too long to handle an interrupt… Other interrupts may be waiting on us!</p>
|
||||
<p>(Lag ensues)</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -1148,7 +1148,7 @@ OnTxDone
|
|||
OnTxDone
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>WisBlock is now transmitting a LoRa Packet (<code>"Hello"</code>) every 5 seconds. <a href="https://github.com/lupyuen/wisblock-lora-transmitter/blob/main/src/main.cpp#L104-L128">(See this)</a></p>
|
||||
<p>WisBlock is now transmitting a LoRa Packet (<code>"Hello"</code>) every 5 seconds. <a href="https://github.com/lupyuen/wisblock-lora-transmitter/blob/main/src/main.cpp#L104-L128">(See this)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If we sniff the airwaves with a <strong>Software Defined Radio</strong>, we will see the distinctive <a href="https://lupyuen.github.io/articles/lora#visualise-lora-with-software-defined-radio"><strong>LoRa Chirp</strong></a>…</p>
|
||||
|
@ -1332,7 +1332,7 @@ bl_sys_time_now : sys time now
|
|||
<p>This command calls the function <code>create_task</code>, which we have seen earlier.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Then we <strong>initialise our LoRa Transceiver</strong>. </p>
|
||||
<p>Then we <strong>initialise our LoRa Transceiver</strong>.</p>
|
||||
<p>Enter this command…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>init_driver
|
||||
</code></pre></div>
|
||||
|
@ -1371,10 +1371,10 @@ SX1276 DIO0: Packet received
|
|||
Rx done: RadioEvents.RxDone
|
||||
</code></pre></div>
|
||||
<p>This says that the SX1276 Driver has <strong>received a LoRa Packet.</strong></p>
|
||||
<p>And the packet contains <code>"Hello"</code>…</p>
|
||||
<p>And the packet contains <code>"Hello"</code>…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>Rx done: 48 65 6c 6c 6f
|
||||
</code></pre></div>
|
||||
<p>(That’s the ASCII code for <code>"Hello"</code>)</p>
|
||||
<p>(That’s the ASCII code for <code>"Hello"</code>)</p>
|
||||
<p><a href="https://youtu.be/3TSvo0dwwnQ"><strong>Watch the receive video on YouTube</strong></a></p>
|
||||
<p><a href="https://gist.github.com/lupyuen/9bd7e7daa2497e8352d2cffec4be444d"><strong>Check out the receive log</strong></a></p>
|
||||
</li>
|
||||
|
|
|
@ -312,7 +312,7 @@ static void init_driver(char *buf, int len, int argc, char **argv) {
|
|||
</ul>
|
||||
<h2 id="transmit-lora-packet"><a class="doc-anchor" href="#transmit-lora-packet">§</a>2.4 Transmit LoRa Packet</h2>
|
||||
<p>(<strong>Note on LoRa vs LoRaWAN:</strong> Our LoRaWAN Driver calls the LoRa Driver to transmit LoRa Packets, when we run the <code>las_join</code> and <code>las_app_tx</code> commands. Skip this section if we’re using LoRaWAN to transmit data.)</p>
|
||||
<p>To transmit a LoRa Packet, the <code>send_message</code> command in our Demo Firmware calls <code>send_once</code> in <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_lorawan/sdk_app_lorawan/demo.c#L214-L219"><code>demo.c</code></a> … </p>
|
||||
<p>To transmit a LoRa Packet, the <code>send_message</code> command in our Demo Firmware calls <code>send_once</code> in <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_lorawan/sdk_app_lorawan/demo.c#L214-L219"><code>demo.c</code></a> …</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>/// Command to send a LoRa message. Assume that the LoRa Transceiver driver has been initialised.
|
||||
static void send_message(char *buf, int len, int argc, char **argv) {
|
||||
// Send the "PING" message
|
||||
|
@ -1022,7 +1022,7 @@ blflash flash c:\blflash\sdk_app_lorawan.bin --port COM5
|
|||
<p><a href="https://lupyuen.github.io/articles/lora2#event-queue">(<code>create_task</code> is explained here)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Then we <strong>initialise our LoRaWAN Driver</strong>. </p>
|
||||
<p>Then we <strong>initialise our LoRaWAN Driver</strong>.</p>
|
||||
<p>Enter this command…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>init_lorawan
|
||||
</code></pre></div>
|
||||
|
@ -1412,7 +1412,7 @@ header = get_pbuf_header(
|
|||
sizeof(struct lora_pkt_info) // Size of LoRaWAN Packet Header
|
||||
);
|
||||
</code></pre></div>
|
||||
<p><code>pbuf</code> Packet Buffers have an unusual <strong>Sliding Payload Pointer</strong> for extracting the header. </p>
|
||||
<p><code>pbuf</code> Packet Buffers have an unusual <strong>Sliding Payload Pointer</strong> for extracting the header.</p>
|
||||
<p>Here’s how we implement <code>get_pbuf_header</code> in <a href="https://github.com/lupyuen/lorawan/blob/main/src/pbuf_queue.c#L165-L197"><code>pbuf_queue.c</code></a> …</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>/// Return the pbuf Packet Buffer header
|
||||
void *
|
||||
|
@ -1457,7 +1457,7 @@ get_pbuf_header(
|
|||
<p>Mynewt’s LoRaWAN Driver uses <a href="https://mynewt.apache.org/latest/os/core_os/mbuf/mbuf.html#mqueue"><strong>Mqueues</strong></a> to enqueue packets for processing.</p>
|
||||
<p>The Lightweight IP Stack doesn’t have the equivalent of Mqueues, so we build our own <strong>Packet Buffer Queues</strong>.</p>
|
||||
<p>A <strong><code>pbuf_queue</code></strong> Packet Buffer Queue is a <strong>First-In First-Out List of Packet Buffers</strong>. It supports these operations…</p>
|
||||
<p>From <a href="https://github.com/lupyuen/lorawan/blob/main/src/pbuf_queue.c#L210-L322"><code>pbuf_queue.c</code></a> </p>
|
||||
<p>From <a href="https://github.com/lupyuen/lorawan/blob/main/src/pbuf_queue.c#L210-L322"><code>pbuf_queue.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// Initializes a pbuf_queue. A pbuf_queue is a queue of pbufs that ties to a
|
||||
// particular task's event queue. pbuf_queues form a helper API around a common
|
||||
// paradigm: wait on an event queue until at least one packet is available,
|
||||
|
|
|
@ -345,7 +345,7 @@ void SX126xIoInit( void ) {
|
|||
<p><a href="https://lupyuen.github.io/articles/lorawan#lorawan-driver">(More about the LoRaWAN Commands)</a></p>
|
||||
<p><img src="https://lupyuen.github.io/images/lorawan2-joinsend.png" alt="LoRaWAN Firmware" /></p>
|
||||
<h1 id="lorawan-gateway"><a class="doc-anchor" href="#lorawan-gateway">§</a>4 LoRaWAN Gateway</h1>
|
||||
<p>To see the <strong>Join Network Request</strong> and the <strong>Data Packet</strong> received by our ChirpStack LoRaWAN Gateway, we do this… </p>
|
||||
<p>To see the <strong>Join Network Request</strong> and the <strong>Data Packet</strong> received by our ChirpStack LoRaWAN Gateway, we do this…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>In ChirpStack, click <strong><code>Applications → app → device_otaa_class_a → LoRaWAN Frames</code></strong></p>
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
<ul>
|
||||
<li><a href="https://lupyuen.github.io/articles/sx1262#appendix-create-a-nuttx-library"><strong>“Create a NuttX Library”</strong></a></li>
|
||||
</ul>
|
||||
<p>Then we replaced the “liblorawan” folder by a <strong>Git Submodule</strong> that contains our LoRaWAN code… </p>
|
||||
<p>Then we replaced the “liblorawan” folder by a <strong>Git Submodule</strong> that contains our LoRaWAN code…</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>cd nuttx/nuttx/libs
|
||||
rm -r liblorawan
|
||||
git rm -r liblorawan
|
||||
|
@ -645,7 +645,7 @@ static void PrepareTxFrame( void ) {
|
|||
</code></pre></div>
|
||||
<p><em>Why is our Data Packet marked Unconfirmed?</em></p>
|
||||
<p>Our Data Packet is marked Unconfirmed because we <strong>don’t expect an acknowledgement</strong> from the LoRaWAN Gateway.</p>
|
||||
<p>This is the typical mode for <strong>IoT Sensor Devices</strong>, which don’t handle acknowledgements to conserve battery power. </p>
|
||||
<p>This is the typical mode for <strong>IoT Sensor Devices</strong>, which don’t handle acknowledgements to conserve battery power.</p>
|
||||
<p><img src="https://lupyuen.github.io/images/lorawan3-tx6.png" alt="Sending a LoRaWAN Data Packet" /></p>
|
||||
<h2 id="message-size"><a class="doc-anchor" href="#message-size">§</a>9.1 Message Size</h2>
|
||||
<p><em>What’s the Maximum Message Size?</em></p>
|
||||
|
@ -707,7 +707,7 @@ TxPeriodicity = APP_TX_DUTYCYCLE +
|
|||
randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
|
||||
</code></pre></div>
|
||||
<p><a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/boards/mcu/utilities.c#L48-L51">(<strong>randr</strong> is defined here)</a></p>
|
||||
<p>Thus our LoRaWAN Test App transmits a message every <strong>40 seconds</strong>. </p>
|
||||
<p>Thus our LoRaWAN Test App transmits a message every <strong>40 seconds</strong>.</p>
|
||||
<p>(±5 seconds of random delay)</p>
|
||||
<h1 id="rerun-the-firmware"><a class="doc-anchor" href="#rerun-the-firmware">§</a>10 Rerun The Firmware</h1>
|
||||
<p>Watch what happens when our LoRaWAN Test App <strong>transmits a Data Packet</strong>…</p>
|
||||
|
@ -1057,7 +1057,7 @@ chirpstack-network-server[5749]:
|
|||
<p>Thus we should <strong>disable Info Logging</strong> on NuttX…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>In <strong>menuconfig</strong>, select <strong>“Build Setup”</strong> → <strong>“Debug Options”</strong> </p>
|
||||
<p>In <strong>menuconfig</strong>, select <strong>“Build Setup”</strong> → <strong>“Debug Options”</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Uncheck</strong> the following…</p>
|
||||
|
@ -1081,7 +1081,7 @@ chirpstack-network-server[5749]:
|
|||
<p>We’ll see this in the LoRaWAN Gateway…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack5.png" alt="Empty Message Payload" /></p>
|
||||
<p><a href="https://gist.github.com/lupyuen/0d301216bbf937147778bb57ab0ccf89">(Output Log)</a></p>
|
||||
<p>In the output for our LoRaWAN Test App, look for <strong>“maxSize”</strong> to verify the <strong>Maximum Message Size</strong> for our Data Rate and LoRaWAN Region… </p>
|
||||
<p>In the output for our LoRaWAN Test App, look for <strong>“maxSize”</strong> to verify the <strong>Maximum Message Size</strong> for our Data Rate and LoRaWAN Region…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>PrepareTxFrame: Transmit to LoRaWAN: Hi NuttX (9 bytes)
|
||||
PrepareTxFrame: status=0, maxSize=11, currentSize=11
|
||||
</code></pre></div>
|
||||
|
@ -1253,7 +1253,7 @@ PrepareTxFrame: status=0, maxSize=11, currentSize=11
|
|||
</ol>
|
||||
<p><img src="https://lupyuen.github.io/images/lorawan3-config1.jpg" alt="Enable POSIX Timers and Message Queues in menuconfig" /></p>
|
||||
<h1 id="appendix-random-number-generator-with-entropy-pool"><a class="doc-anchor" href="#appendix-random-number-generator-with-entropy-pool">§</a>18 Appendix: Random Number Generator with Entropy Pool</h1>
|
||||
<p>Our LoRaWAN Library generates Nonces by calling a <strong>Random Number Generator with Entropy Pool</strong>. </p>
|
||||
<p>Our LoRaWAN Library generates Nonces by calling a <strong>Random Number Generator with Entropy Pool</strong>.</p>
|
||||
<p>Follow these steps to enable the <strong>Entropy Pool</strong> in <strong>menuconfig</strong>…</p>
|
||||
<ol>
|
||||
<li>
|
||||
|
|
|
@ -424,13 +424,13 @@ Response: OK
|
|||
<p><a href="https://wiki.pine64.org/images/1/1b/Quectel_EC2x%26EG9x%26EG2x-G%26EM05_Series_AT_Commands_Manual_V2.0.pdf">(EG25-G AT Commands, Page 146)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>“<strong><code>AT+CSCS="GSM"</code></strong>”</p>
|
||||
<p>“<strong><code>AT+CSCS="GSM"</code></strong>”</p>
|
||||
<p>Select (7-bit ASCII-like) <strong>GSM Character Set</strong></p>
|
||||
<p>(Instead of 16-bit UCS2 Unicode)</p>
|
||||
<p><a href="https://wiki.pine64.org/images/1/1b/Quectel_EC2x%26EG9x%26EG2x-G%26EM05_Series_AT_Commands_Manual_V2.0.pdf">(EG25-G AT Commands, Page 25)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>“<strong><code>AT+CMGS="+1234567890"</code></strong>”</p>
|
||||
<p>“<strong><code>AT+CMGS="+1234567890"</code></strong>”</p>
|
||||
<p>Send an SMS to the (imaginary) Phone Number “<strong><code>+1234567890</code></strong>”</p>
|
||||
<p>(Also works without country code, like “<strong><code>234567890</code></strong>”)</p>
|
||||
<p><a href="https://wiki.pine64.org/images/1/1b/Quectel_EC2x%26EG9x%26EG2x-G%26EM05_Series_AT_Commands_Manual_V2.0.pdf">(EG25-G AT Commands, Page 159)</a></p>
|
||||
|
@ -957,7 +957,7 @@ Response: +QDAI: 1,1,0,1,0,0,1,1
|
|||
<ul>
|
||||
<li>
|
||||
<p><strong>io = 1</strong></p>
|
||||
<p>Digital PCM Output </p>
|
||||
<p>Digital PCM Output</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>mode = 1</strong></p>
|
||||
|
@ -1070,11 +1070,11 @@ AT+QCFG="ims",1
|
|||
</code></pre></div>
|
||||
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/images/Quectel_EC2x&EG2x&EG9x&EM05_Series_QCFG_AT_Commands_Manual_V1.0.pdf">(See “<strong><code>AT+QCFG=ims</code></strong>”, Page 102)</a></p>
|
||||
<p>That’s because Quectel says we should <a href="https://forums.quectel.com/t/volte-on-eg25-g/13512"><strong>Force-Enable LTE IMS</strong></a> to support <a href="https://en.wikipedia.org/wiki/Voice_over_LTE"><strong>VoLTE (Voice-over-LTE)</strong></a>…</p>
|
||||
<p>“You can use <strong><code>AT+QCFG="ims",1</code></strong> to restart the module…”</p>
|
||||
<p>“And query <strong><code>AT+QCFG="ims"</code></strong> again to use the VoLTE function”</p>
|
||||
<p>“You can use <strong><code>AT+QCFG="ims",1</code></strong> to restart the module…”</p>
|
||||
<p>“And query <strong><code>AT+QCFG="ims"</code></strong> again to use the VoLTE function”</p>
|
||||
<p><a href="https://forums.quectel.com/t/volte-on-eg25-g/13512">(Source)</a></p>
|
||||
<p>TODO: Run <code>AT+QCFG="ims"</code> on both SIM Cards to show IMS state</p>
|
||||
<p>TODO: Run <code>AT+QCFG="ims",1</code> or <code>AT+QCFG="ims",2</code> to enable / disable IMS on both SIM Cards</p>
|
||||
<p>TODO: Run <code>AT+QCFG="ims"</code> on both SIM Cards to show IMS state</p>
|
||||
<p>TODO: Run <code>AT+QCFG="ims",1</code> or <code>AT+QCFG="ims",2</code> to enable / disable IMS on both SIM Cards</p>
|
||||
<p>TODO: Fetch Mobile Operator and auto-enable / disable LTE IMS according to Mobile Operator</p>
|
||||
<h1 id="appendix-sms-pdu-format"><a class="doc-anchor" href="#appendix-sms-pdu-format">§</a>14 Appendix: SMS PDU Format</h1>
|
||||
<p><em>Earlier we saw this command for sending SMS in PDU Mode…</em></p>
|
||||
|
@ -1190,7 +1190,7 @@ PHONE_NUMBER_PDU // TODO: Assume 5 bytes in PDU Phone Number (10 Decimal Digits
|
|||
<li>
|
||||
<p><strong>TP-Validity-Period</strong> (TP-VP): “<code>01</code>”</p>
|
||||
<p>Message is valid for 10 minutes, relative to current time:</p>
|
||||
<p><code>("01" + 1) x 5</code> minutes</p>
|
||||
<p><code>("01" + 1) x 5</code> minutes</p>
|
||||
<p><a href="https://en.wikipedia.org/wiki/GSM_03.40#Validity_Period">(GSM 03.40, Validity Period)</a></p>
|
||||
<p>(See <strong>TP-Validity-Period-Format</strong> above)</p>
|
||||
</li>
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
</ul>
|
||||
<p><a href="https://lupyuen.github.io/articles/lvgl#zig-outcomes">(Spoiler: Answers are Yes, Maybe, Somewhat)</a></p>
|
||||
<p><img src="https://lupyuen.github.io/images/lvgl-code1a.jpg" alt="LVGL App in C" /></p>
|
||||
<p><a href="https://github.com/lupyuen/lvgltest-nuttx/blob/main/lvgltest.c#L107-L148">(Source)</a> </p>
|
||||
<p><a href="https://github.com/lupyuen/lvgltest-nuttx/blob/main/lvgltest.c#L107-L148">(Source)</a></p>
|
||||
<h1 id="lvgl-app-in-c"><a class="doc-anchor" href="#lvgl-app-in-c">§</a>1 LVGL App in C</h1>
|
||||
<p>We begin with a barebones <strong>LVGL App in C</strong> that renders a line of text…</p>
|
||||
<ul>
|
||||
|
@ -150,7 +150,7 @@
|
|||
// Omitted: LVGL Canvas (we'll find out why)
|
||||
}
|
||||
</code></pre></div>
|
||||
<p><a href="https://github.com/lupyuen/lvgltest-nuttx/blob/main/lvgltest.c#L107-L148">(Source)</a> </p>
|
||||
<p><a href="https://github.com/lupyuen/lvgltest-nuttx/blob/main/lvgltest.c#L107-L148">(Source)</a></p>
|
||||
<p><a href="https://docs.lvgl.io/7.11/widgets/label.html#">(Docs for LVGL Label)</a></p>
|
||||
<p>In a while we shall convert this LVGL App to Zig.</p>
|
||||
<p><em>What if we’re not familiar with Zig?</em></p>
|
||||
|
@ -1147,7 +1147,7 @@ ginfo("%" PRIu32 " px refreshed in %" PRIu32 " ms\n&quo
|
|||
<p><strong>lvgltest_main</strong>: Main Function</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>create_widgets</strong>: Create Widgets Function </p>
|
||||
<p><strong>create_widgets</strong>: Create Widgets Function</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>The Auto-Translated Zig code shows…</p>
|
||||
|
@ -1496,7 +1496,7 @@ $ jq '.types[137]' lvgltest-analysis.json
|
|||
<h2 id="object-oriented-wrapper-for-lvgl"><a class="doc-anchor" href="#object-oriented-wrapper-for-lvgl">§</a>18.1 Object-Oriented Wrapper for LVGL</h2>
|
||||
<p><em>Is LVGL really Object-Oriented?</em></p>
|
||||
<p>Yep the LVGL API is actually <strong>Object-Oriented</strong> since it uses Inheritance.</p>
|
||||
<p>All LVGL Widgets (Labels, Buttons, etc) have the same Base Type: <strong>lv_obj_t</strong>. </p>
|
||||
<p>All LVGL Widgets (Labels, Buttons, etc) have the same Base Type: <strong>lv_obj_t</strong>.</p>
|
||||
<p>But some LVGL Functions will work only for <strong>specific Widgets</strong>, whereas some LVGL Functions will work on <strong>any Widget</strong>…</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
|
|
@ -489,7 +489,7 @@ mmu_ln_map_region(
|
|||
PTE_R | PTE_W | PTE_G // Read + Write + Global
|
||||
);
|
||||
</code></pre></div>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_mmu.c#L137-L153">(<strong>mmu_ln_map_region</strong> is defined here)</a> </p>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_mmu.c#L137-L153">(<strong>mmu_ln_map_region</strong> is defined here)</a></p>
|
||||
<p><a href="https://github.com/lupyuen/nuttx-ox64#map-the-page-pool-level-2">(See the <strong>NuttX Page Pool Log</strong>)</a></p>
|
||||
<p><em>Did we forget something?</em></p>
|
||||
<p>Oh yeah, remember to <strong>connect the Level 1 and 2</strong> Page Tables: <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl808/bl808_mm_init.c#L266-L271">bl808_mm_init.c</a></p>
|
||||
|
@ -671,7 +671,7 @@ mmu_write_satp:
|
|||
<p>(For our Kernel)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Virtual Memory</strong> </p>
|
||||
<p><strong>Virtual Memory</strong></p>
|
||||
<p>(For the Applications)</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -873,7 +873,7 @@ Virtual Address 0x80200000 maps to Physical Address 0x506a4000
|
|||
<p><strong>PAGESIZE</strong> = 4,096</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>LEVELS</strong> = 3 </p>
|
||||
<p><strong>LEVELS</strong> = 3</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>PTESIZE</strong> = 8</p>
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
<li><a href="#task-settings">20.2 Task Settings</a><ul></ul></li></ul></li></ul></nav><p><img src="https://lupyuen.github.io/images/mynewt-title.png" alt="Debugging Mynewt Firmware with VSCode" /></p>
|
||||
<p><em>Debugging Mynewt Firmware with VSCode</em></p>
|
||||
<p>📝 <em>21 Dec 2020</em></p>
|
||||
<p>Our journey so far… </p>
|
||||
<p>Our journey so far…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>We took a quick peek at <a href="https://lupyuen.github.io/articles/pinecone"><strong>PineCone BL602 RISC-V Evaluation Board</strong></a>…</p>
|
||||
|
@ -273,7 +273,7 @@ bsp.flash_map:
|
|||
offset: 0x22013000
|
||||
size: 1kB # 0x400
|
||||
</code></pre></div>
|
||||
<p>Remember that we’re loading our firmware into Cache Memory (instead of Flash Memory) and we’re not using the Bootloader. </p>
|
||||
<p>Remember that we’re loading our firmware into Cache Memory (instead of Flash Memory) and we’re not using the Bootloader.</p>
|
||||
<p>That’s why we allocate most of the Cache Memory to the Active Firmware Image (located at the start of Cache Memory).</p>
|
||||
<div class="example-wrap"><pre class="language-yaml"><code> ## User areas.
|
||||
## (Not Used) Reboot Log
|
||||
|
@ -547,7 +547,7 @@ repos/apache-mynewt-core/kernel/os/include/os/arch/rv32imac/os/os_arch.h:24:10:
|
|||
fatal error: mcu/fe310.h: No such file or directory
|
||||
#include "mcu/fe310.h"
|
||||
</code></pre></div>
|
||||
<p>This error shows that <code>rv32imac</code>, the RISC-V support in Mynewt, is dependent on SiFive FE310. Which looks really odd. </p>
|
||||
<p>This error shows that <code>rv32imac</code>, the RISC-V support in Mynewt, is dependent on SiFive FE310. Which looks really odd.</p>
|
||||
<p>(Probably done that way because FE310 is the only RISC-V Microcontroller supported by Mynewt)</p>
|
||||
<p>We work around this problem by creating Stub Files like these…</p>
|
||||
<ul>
|
||||
|
@ -691,7 +691,7 @@ objsize
|
|||
<h2 id="terminating-openocd"><a class="doc-anchor" href="#terminating-openocd">§</a>14.2 Terminating OpenOCD</h2>
|
||||
<p>Before we start a new debugging session with <strong><code>Run → Start Debugging</code></strong>…</p>
|
||||
<p><em>We must always click <strong><code>Terminal → Run Build Task</code></strong> first!</em></p>
|
||||
<p>That’s because stopping the debugger will leave OpenOCD running (and locking up the connection to PineCone). </p>
|
||||
<p>That’s because stopping the debugger will leave OpenOCD running (and locking up the connection to PineCone).</p>
|
||||
<p>Clicking <strong><code>Run Build Task</code></strong> will terminate the OpenOCD task, so that the next debugging session can restart OpenOCD successfully.</p>
|
||||
<p>For Windows: Sorry we need to terminate the OpenOCD task manually with the Task Manager.</p>
|
||||
<p>In case of OpenOCD problems, check the OpenOCD log file…</p>
|
||||
|
@ -765,7 +765,7 @@ objsize
|
|||
<p><a href="https://lupyuen.github.io/articles/rust"><strong>UPDATE:</strong> We have a new way to create Rust Firmware with BL602 IoT SDK and flash to BL602 Flash Memory over UART, check this out</a></p>
|
||||
<p><a href="https://github.com/9names/bl602-rust-example"><strong>UPDATE:</strong> Check out this Rust Firmware that runs in Flash Memory instead of Cache Memory</a></p>
|
||||
<p><em>Why did we load our Mynewt Firmware (and Rust Firmware) to Cache Memory instead of Flash Memory?</em></p>
|
||||
<p>Because OpenOCD couldn’t load our Mynewt Firmware (and Rust Firmware) into Flash Memory. </p>
|
||||
<p>Because OpenOCD couldn’t load our Mynewt Firmware (and Rust Firmware) into Flash Memory.</p>
|
||||
<p>(Probably because of Flash Protection. Or because writing to BL602 Flash Memory hasn’t been implemented in OpenOCD.)</p>
|
||||
<p><a href="https://github.com/bouffalolab/bl_docs/tree/main/BL602_Openocd&GDB/en">BL602 OpenOCD with JTAG doesn’t support loading firmware into Flash Memory. See the BL602 OpenOCD Docs</a></p>
|
||||
<p><em>What happens when we use <code>blflash</code> to load our firmware to Flash Memory?</em></p>
|
||||
|
@ -876,7 +876,7 @@ newt target set pinecone_app build_profile=debug
|
|||
</ul>
|
||||
<h1 id="appendix-vscode-settings"><a class="doc-anchor" href="#appendix-vscode-settings">§</a>20 Appendix: VSCode Settings</h1><h2 id="debugger-settings"><a class="doc-anchor" href="#debugger-settings">§</a>20.1 Debugger Settings</h2>
|
||||
<p>The VSCode Debugger Settings may be found in <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/.vscode/launch.json"><code>.vscode/launch.json</code></a></p>
|
||||
<p>This file defines… </p>
|
||||
<p>This file defines…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Firmware Path (<code>target</code>)</p>
|
||||
|
|
|
@ -505,7 +505,7 @@ static int gpout_write(FAR struct gpio_dev_s *dev, bool value)
|
|||
<p>(Which makes sense, because each board may map the Hardware Devices to different GPIO Pins)</p>
|
||||
<p>The Board-Specific Driver calls the <strong>BL602-Specific GPIO Driver</strong> to set the GPIO Output, passing the GPIO Pin Set.</p>
|
||||
<h2 id="bl602-driver"><a class="doc-anchor" href="#bl602-driver">§</a>8.3 BL602 Driver</h2>
|
||||
<p>The <strong>BL602-Specific GPIO Driver</strong> manipulates the BL602 Hardware Registers to perform GPIO Functions. </p>
|
||||
<p>The <strong>BL602-Specific GPIO Driver</strong> manipulates the BL602 Hardware Registers to perform GPIO Functions.</p>
|
||||
<p>(The driver is called by the Board-Specific Drivers for all BL602 boards)</p>
|
||||
<p>Here’s how the BL602-Specific GPIO Driver sets the <strong>GPIO Output</strong>: <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl602/bl602_gpio.c#L190-L209">bl602_gpio.c</a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// BL602-Specific GPIO Driver: Set the value of an output GPIO
|
||||
|
@ -790,7 +790,7 @@ poke &h40000188, &h00
|
|||
<p><a href="https://www.gizmochina.com/2020/11/05/xiaomi-launches-a-new-iot-software-platform-xiaomi-vela-based-on-nuttx-os/"><strong>“Xiaomi launches a new IoT Software Platform “Xiaomi Vela” based on NuttX OS”</strong></a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The built-in <strong>“timer”</strong> Demo App we’ve seen uses Timer Handlers that are not deterministic and may have longer latency. </p>
|
||||
<p>The built-in <strong>“timer”</strong> Demo App we’ve seen uses Timer Handlers that are not deterministic and may have longer latency.</p>
|
||||
<p>Check out the improved <strong>“timer_gpout”</strong> Demo App, which catches the Timer Signal in real time…</p>
|
||||
<p><a href="https://github.com/apache/nuttx-apps/blob/master/examples/timer_gpout/timer_gpout_main.c"><strong>timer_gpout Demo App</strong></a></p>
|
||||
<p>(Thanks to <a href="https://www.linkedin.com/feed/update/urn:li:activity:6868285202649772032?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A6868285202649772032%2C6869001192320602112%29">Sara Monteiro</a> for the tip!)</p>
|
||||
|
|
|
@ -321,7 +321,7 @@ SECTIONS
|
|||
<h1 id="boot-nuttx-on-star64"><a class="doc-anchor" href="#boot-nuttx-on-star64">§</a>6 Boot NuttX on Star64</h1>
|
||||
<p>We’re finally ready to <strong>boot NuttX on Star64</strong>! We compile <strong>NuttX for RISC-V QEMU</strong> with these steps…</p>
|
||||
<ul>
|
||||
<li><a href="https://lupyuen.github.io/articles/riscv#appendix-build-apache-nuttx-rtos-for-64-bit-risc-v-qemu"><strong>“Build Apache NuttX RTOS for 64-bit RISC-V QEMU”</strong></a> </li>
|
||||
<li><a href="https://lupyuen.github.io/articles/riscv#appendix-build-apache-nuttx-rtos-for-64-bit-risc-v-qemu"><strong>“Build Apache NuttX RTOS for 64-bit RISC-V QEMU”</strong></a></li>
|
||||
</ul>
|
||||
<p>Then we tweak it to <strong>boot on Star64</strong> (and rebuild)…</p>
|
||||
<ol>
|
||||
|
@ -588,7 +588,7 @@ jal x1, jh7110_start
|
|||
<p>But NuttX hangs in the C Function <a href="https://lupyuen.github.io/articles/riscv#jump-to-start"><strong>jh7110_start</strong></a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Why? Stay tuned for more tantalising treats in the next article! </p>
|
||||
<p>Why? Stay tuned for more tantalising treats in the next article!</p>
|
||||
<ul>
|
||||
<li><a href="https://lupyuen.github.io/articles/privilege"><strong>“Star64 JH7110 + NuttX RTOS: RISC-V Privilege Levels and UART Registers”</strong></a></li>
|
||||
</ul>
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
<p>Most development tools (like VSCode) work with OpenOCD for loading and debugging firmware.</p>
|
||||
<p>Thus it’s important to get PineCone talking to OpenOCD, so that our development tools will work with PineCone.</p>
|
||||
<p>(<a href="https://github.com/lupyuen/bl602-rust-guide">Rust for PineCone</a> also uses OpenOCD for loading and debugging)</p>
|
||||
<p>PineCone exposes a <strong>JTAG Port</strong> that works with OpenOCD for loading and debugging firmware. </p>
|
||||
<p>PineCone exposes a <strong>JTAG Port</strong> that works with OpenOCD for loading and debugging firmware.</p>
|
||||
<p><a href="https://lupyuen.github.io/articles/pinecone#other-flashing-tools">(JTAG and OpenOCD won’t work for flashing BL602)</a></p>
|
||||
<p>This is similar to the SWD Port that’s found in PineTime, STM32 Blue Pill and other Arm microcontrollers.</p>
|
||||
<p>We’ll learn about JTAG in the next section.</p>
|
||||
|
@ -568,7 +568,7 @@ const uint32_t GPIO_CTRL = 3; // Pin Control
|
|||
</li>
|
||||
<li>
|
||||
<p>When selecting the firmware file, remember to choose the <code>sdk_app_helloworld.bin</code> firmware that we have just downloaded.</p>
|
||||
<p>Make sure there are no spaces in the firmware pathname. </p>
|
||||
<p>Make sure there are no spaces in the firmware pathname.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<h2 id="start-the-remap-firmware"><a class="doc-anchor" href="#start-the-remap-firmware">§</a>9.4 Start the Remap Firmware</h2>
|
||||
|
@ -624,7 +624,7 @@ const uint32_t GPIO_CTRL = 3; // Pin Control
|
|||
<p><strong>Only LED</strong>: We’ll remap the LED Pins to PWM so that we may control them.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Only JTAG</strong>: We’ll use the default JTAG Port. No remapping needed. </p>
|
||||
<p><strong>Only JTAG</strong>: We’ll use the default JTAG Port. No remapping needed.</p>
|
||||
<p>(And we get free disco lights during JTAG loading and debugging)</p>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -311,7 +311,7 @@ Ping!
|
|||
<li>
|
||||
<p>Download the <strong>Ox64 Binaries</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/openbouffalo/buildroot_bouffalo/releases/download/v1.0.1/bl808-linux-pine64_ox64_full_defconfig.tar.gz">bl808-linux-pine64_ox64_full_defconfig.tar.gz</a> </li>
|
||||
<li><a href="https://github.com/openbouffalo/buildroot_bouffalo/releases/download/v1.0.1/bl808-linux-pine64_ox64_full_defconfig.tar.gz">bl808-linux-pine64_ox64_full_defconfig.tar.gz</a></li>
|
||||
</ul>
|
||||
<p>From the latest <strong>Ox64 Linux Release</strong>…</p>
|
||||
<ul>
|
||||
|
@ -460,7 +460,7 @@ BOOTP broadcast
|
|||
Retry time exceeded; starting again
|
||||
</code></pre></div>
|
||||
<p><a href="https://gist.github.com/lupyuen/0b1a98781e86ba11c5538eb1e3058718">(Source)</a></p>
|
||||
<p>Which is OK because U-Boot Bootloader is waiting for our microSD Card. </p>
|
||||
<p>Which is OK because U-Boot Bootloader is waiting for our microSD Card.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>If nothing appears:</strong> Check that we are using <a href="https://openbouffalo.org/static-assets/bldevcube/BouffaloLabDevCube-v1.8.3.zip"><strong>BL DevCube 1.8.3</strong></a></p>
|
||||
|
@ -739,7 +739,7 @@ Retry time exceeded; starting again
|
|||
<p>Let’s examine the <strong>Linux Kernel Image for Ox64</strong>, and we replicate the same format for NuttX. (Which is how we ported NuttX to 64-bit RISC-V Star64 JH7110 SBC)</p>
|
||||
<p>We download the <strong>Ox64 Binaries</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/openbouffalo/buildroot_bouffalo/releases/download/v1.0.1/bl808-linux-pine64_ox64_full_defconfig.tar.gz">bl808-linux-pine64_ox64_full_defconfig.tar.gz</a> </li>
|
||||
<li><a href="https://github.com/openbouffalo/buildroot_bouffalo/releases/download/v1.0.1/bl808-linux-pine64_ox64_full_defconfig.tar.gz">bl808-linux-pine64_ox64_full_defconfig.tar.gz</a></li>
|
||||
</ul>
|
||||
<p>From the latest <strong>Ox64 Linux Release</strong>…</p>
|
||||
<ul>
|
||||
|
@ -774,7 +774,7 @@ d 96 extlinux
|
|||
<div class="example-wrap"><pre class="language-text"><code>4d 5a 6f 10
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>code1</strong>: Executable code </p>
|
||||
<p><strong>code1</strong>: Executable code</p>
|
||||
<p>(4 bytes, offset <code>0x04</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>20 08 01 00
|
||||
</code></pre></div></li>
|
||||
|
@ -784,12 +784,12 @@ d 96 extlinux
|
|||
<div class="example-wrap"><pre class="language-text"><code>00 00 20 00 00 00 00 00
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>image_size</strong>: Effective Image size, little endian </p>
|
||||
<p><strong>image_size</strong>: Effective Image size, little endian</p>
|
||||
<p>(8 bytes, offset <code>0x10</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>00 80 cd 00 00 00 00 00
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>flags</strong>: Kernel flags, little endian </p>
|
||||
<p><strong>flags</strong>: Kernel flags, little endian</p>
|
||||
<p>(8 bytes, offset <code>0x18</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>00 00 00 00 00 00 00 00
|
||||
</code></pre></div></li>
|
||||
|
@ -809,12 +809,12 @@ d 96 extlinux
|
|||
<div class="example-wrap"><pre class="language-text"><code>00 00 00 00 00 00 00 00
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>magic</strong>: Magic number, little endian, “RISCV\x00\x00\x00” </p>
|
||||
<p><strong>magic</strong>: Magic number, little endian, “RISCV\x00\x00\x00”</p>
|
||||
<p>(8 bytes, offset <code>0x30</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>52 49 53 43 56 00 00 00
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>magic2</strong>: Magic number 2, little endian, “RSC\x05” </p>
|
||||
<p><strong>magic2</strong>: Magic number 2, little endian, “RSC\x05”</p>
|
||||
<p>(4 bytes, offset <code>0x38</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>52 53 43 05
|
||||
</code></pre></div></li>
|
||||
|
@ -898,7 +898,7 @@ Unhandled Interupts: 0 Unhandled Signals 0
|
|||
<p><strong>M0 Wireless Core</strong> (OpenBouffalo Firmware) will forward the <strong>Peripheral Interrupts</strong> to D0 Multimedia Core (Linux)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The Forwarded Peripheral Interrupts are for <strong>SD Card Host Controller</strong> (SDH) and <strong>GPIO</strong> </p>
|
||||
<p>The Forwarded Peripheral Interrupts are for <strong>SD Card Host Controller</strong> (SDH) and <strong>GPIO</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Which makes sense because the <strong>U-Boot Bootloader</strong> (on D0 Multimedia Core) needs to access the <strong>SD Card</strong> (for booting Linux)</p>
|
||||
|
@ -951,7 +951,7 @@ Unhandled Interupts: 0 Unhandled Signals 0
|
|||
<p><em>Are there other ways to boot Linux on Ox64?</em></p>
|
||||
<p>According to <a href="https://web.archive.org/web/20231103055452/https://twitter.com/madushan1000/status/1719069431580524720?s=20"><strong>@madushan1000</strong></a>…</p>
|
||||
<blockquote>
|
||||
<p>“You can also use u-boot. <a href="https://github.com/openbouffalo/u-boot/releases/tag/bl808-2023-02-19">openbouffalo/u-boot/bl808-2023-02-19</a>.
|
||||
<p>“You can also use u-boot. <a href="https://github.com/openbouffalo/u-boot/releases/tag/bl808-2023-02-19">openbouffalo/u-boot/bl808-2023-02-19</a>.
|
||||
You can also get rid of mailbox, but you will have to build the kernel yourself <a href="https://github.com/openbouffalo/linux/tree/bl808/all">openbouffalo/linux/bl808</a>”</p>
|
||||
</blockquote>
|
||||
|
||||
|
|
|
@ -268,7 +268,7 @@ into the <strong>Formatter Parameter</strong> box</p>
|
|||
<p><img src="https://lupyuen.github.io/images/payload-ttn3.png" alt="Decoded Sensor Data in the Live Data Table" /></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Click on a message in the Live Data Table. </p>
|
||||
<p>Click on a message in the Live Data Table.</p>
|
||||
<p>We should see the <strong>decoded_payload</strong> field with our Decoded Sensor Data…</p>
|
||||
<div class="example-wrap"><pre class="language-json"><code>"decoded_payload": {
|
||||
"l": 4000,
|
||||
|
|
|
@ -406,8 +406,8 @@ make
|
|||
<p>For BL602 IoT SDK the firmware is built automatically in the cloud by GitHub Actions…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Download the built firmware from GitHub Actions:
|
||||
<a href="https://github.com/lupyuen/bl_iot_sdk/actions"><code>github.com/lupyuen/bl_iot_sdk/actions</code></a>
|
||||
<p>Download the built firmware from GitHub Actions:
|
||||
<a href="https://github.com/lupyuen/bl_iot_sdk/actions"><code>github.com/lupyuen/bl_iot_sdk/actions</code></a>
|
||||
(Requires login to GitHub)</p>
|
||||
<p>Under <code>All Workflows ➜ Results</code>, click the first row</p>
|
||||
<p>Under <code>Artifacts</code>, click <code>customer_app.zip</code> <a href="https://lupyuen.github.io/images/pinecone-artifact.png">(Like this)</a></p>
|
||||
|
@ -488,7 +488,7 @@ make
|
|||
<p><a href="https://wiki.pine64.org/wiki/Main_Page#Chat_Platforms"><strong>“Pine64 Chat Platforms”</strong></a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>I’m not a Pine64 employee and I’m not paid by Pine64 to write these articles on BL602. </p>
|
||||
<p>I’m not a Pine64 employee and I’m not paid by Pine64 to write these articles on BL602.</p>
|
||||
<p>Pine64 sponsors my coffee (<a href="https://github.com/sponsors/lupyuen">as a GitHub Sponsor</a>) and they send me samples (of gadgets, not coffee) for evaluation and experimentation.</p>
|
||||
<p>(I’m not connected to Bouffalo Lab either)</p>
|
||||
</li>
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
<p><img src="https://lupyuen.github.io/images/pinedio-solar.jpg" alt="Solar Panel?" /></p>
|
||||
<p><strong>A Solar Panel!</strong></p>
|
||||
<p>(Yeah Singapore is super sunny… Is this mockery? 🤔)</p>
|
||||
<p>But a Solar Panel with a <strong>JTAG Cable</strong>? That’s highly unusual. </p>
|
||||
<p>But a Solar Panel with a <strong>JTAG Cable</strong>? That’s highly unusual.</p>
|
||||
<p>Opening the gadget reveals the hidden treasure inside: <a href="https://www.pine64.org/2021/08/15/introducing-the-pinenote/"><strong>PineDio Stack BL604 Board!</strong></a></p>
|
||||
<p><img src="https://lupyuen.github.io/images/pinedio-inside.jpg" alt="Inside the Solar Panel: PineDio Stack BL604 Board" /></p>
|
||||
<p>That’s typical of <strong>Prototype Hardware</strong> fresh from the factory: No docs, no fancy packaging, no branding either.</p>
|
||||
|
@ -492,7 +492,7 @@ int rc = GLB_Swap_SPI_0_MOSI_With_MISO(ENABLE); assert(rc == 0);
|
|||
</ul>
|
||||
<p><a href="https://lupyuen.github.io/articles/pinedio2"><strong>UPDATE: There’s a new version of PineDio Stack BL604, the GPIO Numbers have changed</strong></a></p>
|
||||
<p><em>Wait are we missing a pin?</em></p>
|
||||
<p>Yep we usually connect the <strong>ST7789 Data / Command (DC) Pin</strong>. (Like on PineTime) </p>
|
||||
<p>Yep we usually connect the <strong>ST7789 Data / Command (DC) Pin</strong>. (Like on PineTime)</p>
|
||||
<p>We flip this pin to Low to indicate that we’re sending Command Bytes, and flip it to High for Data Bytes.</p>
|
||||
<p>We call this the <strong>4-Wire Interface</strong> for ST7789: SDO, SDI, SCK and DC.</p>
|
||||
<p><em>Data / Command (DC) Pin is missing on PineDio Stack. So we’re using the 3-Wire Interface?</em></p>
|
||||
|
|
|
@ -240,7 +240,7 @@ cp nuttx.bin /mnt/c/blflash
|
|||
<p><a href="https://lupyuen.github.io/articles/pinedio#appendix-improvised-reset-button-for-pinedio-stack"><strong>Improvised Reset Button</strong></a> (lower left): We connect a Jumper Cable (to the I2C Port) to restart PineDio Stack during flashing and testing</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>LoRa Antenna</strong> (bottom): Connect an antenna here if we’re testing LoRa </p>
|
||||
<p><strong>LoRa Antenna</strong> (bottom): Connect an antenna here if we’re testing LoRa</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>WiFi / Bluetooth LE Antenna</strong> (right): Connect an antenna here if we’re testing WiFi or Bluetooth LE</p>
|
||||
|
@ -1348,7 +1348,7 @@ CBOR Output: 11 bytes
|
|||
<p>Here’s how we modded the SPI Driver for PineDio Stack…</p>
|
||||
<h2 id="spi-device-id"><a class="doc-anchor" href="#spi-device-id">§</a>16.1 SPI Device ID</h2>
|
||||
<p><em>What’s the SPI Device ID in the table above?</em></p>
|
||||
<p>We identify each SPI Device with a unique <strong>SPI Device ID</strong>. </p>
|
||||
<p>We identify each SPI Device with a unique <strong>SPI Device ID</strong>.</p>
|
||||
<p>NuttX passes the Device ID when it calls <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/bl602/bl602_spi.c#L415-L453"><strong>bl602_spi_select</strong></a>. We’ll use this to flip the right Chip Select Pin for the SPI Device.</p>
|
||||
<p><em>How did we get the SPI Device IDs?</em></p>
|
||||
<p>NuttX auto-assigns <code>0x40000</code> as the SPI Device ID for the ST7789 Display. <a href="https://github.com/lupyuen/nuttx/blob/pinedio/include/nuttx/spi/spi.h#L459">(See this)</a></p>
|
||||
|
@ -1665,11 +1665,11 @@ monitor_cb: 57600 px refreshed in 1110 ms
|
|||
<p>BL602 / BL604 talks to ST7789 Display at <strong>SPI Mode 1 or Mode 3</strong>, depending on whether <strong>MISO / MOSI are swapped</strong>…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>If MISO / MOSI are <strong>NOT Swapped</strong>: </p>
|
||||
<p>If MISO / MOSI are <strong>NOT Swapped</strong>:</p>
|
||||
<p>Use <strong>SPI Mode 1</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If MISO / MOSI are <strong>Swapped</strong>: </p>
|
||||
<p>If MISO / MOSI are <strong>Swapped</strong>:</p>
|
||||
<p>Use <strong>SPI Mode 3</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<p><a href="https://github.com/zenith391/zgt/wiki">(See the docs)</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>zgt is in Active Development, some features may change. </p>
|
||||
<p>zgt is in Active Development, some features may change.</p>
|
||||
<p><a href="https://github.com/zenith391/zgt">(Please support the zgt project! 🙏)</a></p>
|
||||
<p>Join me as we dive into our <strong>Zig App for PinePhone</strong>…</p>
|
||||
<ul>
|
||||
|
|
|
@ -538,7 +538,7 @@ pd_data_reg=0x1c0000
|
|||
</ul>
|
||||
<p>To access the PinePhone Hardware, the <strong>Linux Kernel</strong> refers to the Linux Device Tree. (Similar to the Windows Registry)</p>
|
||||
<p>So the Linux Device Tree will reveal all kinds of goodies about the PinePhone Hardware.</p>
|
||||
<p>Here’s the part that describes PinePhone’s <strong>Blue LED</strong>… </p>
|
||||
<p>Here’s the part that describes PinePhone’s <strong>Blue LED</strong>…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
|
@ -804,7 +804,7 @@ dtc \
|
|||
};
|
||||
};
|
||||
</code></pre></div>
|
||||
<p>Searching online for <code>"sun8i-a83t-tcon-lcd"</code> gives us the <strong>Linux Driver for Allwinner A64 TCON</strong>…</p>
|
||||
<p>Searching online for <code>"sun8i-a83t-tcon-lcd"</code> gives us the <strong>Linux Driver for Allwinner A64 TCON</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/sun4i/sun4i_tcon.c"><strong>sun4i_tcon.c</strong></a></li>
|
||||
</ul>
|
||||
|
@ -824,7 +824,7 @@ dtc \
|
|||
<p>Suppose we’re searching for the Allwinner A64 TCON Driver.</p>
|
||||
<p>From the Linux Device Tree above, the <strong>“compatible”</strong> field reveals the name of the driver: <code>sun8i-a83t-tcon-lcd</code></p>
|
||||
<p>Head over to <a href="https://github.com/search"><strong>GitHub Code Search</strong></a>.</p>
|
||||
<p>Enter the <strong>Driver Name</strong>, including quotes: <code>"sun8i-a83t-tcon-lcd"</code></p>
|
||||
<p>Enter the <strong>Driver Name</strong>, including quotes: <code>"sun8i-a83t-tcon-lcd"</code></p>
|
||||
<p>Click <strong>“Code”</strong>. Under <strong>“Languages”</strong>, filter by <strong>C Language</strong>.</p>
|
||||
<p>We’ll see a bunch of matching C Source Files. Take note of the <strong>File Path</strong>, like <em>“gpu/drm/sun4i/sun4i_tcon.c”</em></p>
|
||||
<p>The Linux Driver we seek shall be located at <a href="https://github.com/torvalds/linux/tree/master/drivers"><strong>github.com/torvalds/linux/drivers</strong></a>, concatenated with the File Path.</p>
|
||||
|
@ -882,13 +882,13 @@ dtc \
|
|||
<ul>
|
||||
<li><a href="https://files.pine64.org/doc/datasheet/pinephone/ST7703_DS_v01_20160128.pdf"><strong>Sitronix ST7703 LCD Controller Datasheet</strong></a></li>
|
||||
</ul>
|
||||
<p>Searching online for <code>"xingbangda,xbd599"</code> gives us the <strong>Linux Driver for Sitronix ST7703 LCD Controller</strong>…</p>
|
||||
<p>Searching online for <code>"xingbangda,xbd599"</code> gives us the <strong>Linux Driver for Sitronix ST7703 LCD Controller</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/panel/panel-sitronix-st7703.c"><strong>panel-sitronix-st7703.c</strong></a></li>
|
||||
</ul>
|
||||
<p>In that file, <a href="https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/panel/panel-sitronix-st7703.c#L174-L333"><strong>xbd599_init_sequence</strong></a> describes the ST7703 Commands for initialising the Xingbangda XBD599 LCD Panel.</p>
|
||||
<p>(<strong>DSI DCS</strong> refers to the <a href="https://docs.zephyrproject.org/latest/hardware/peripherals/mipi_dsi.html"><strong>MIPI-DSI Display Command Set</strong></a>)</p>
|
||||
<p>Searching online for <code>"sun50i-a64-mipi-dsi"</code> gives us the <strong>Linux Driver for A64 MIPI DSI</strong>…</p>
|
||||
<p>Searching online for <code>"sun50i-a64-mipi-dsi"</code> gives us the <strong>Linux Driver for A64 MIPI DSI</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c"><strong>sun6i_mipi_dsi.c</strong></a></li>
|
||||
</ul>
|
||||
|
@ -933,7 +933,7 @@ dtc \
|
|||
phandle = <0x53>;
|
||||
};
|
||||
</code></pre></div>
|
||||
<p>Searching online for <code>"sun6i-a31-mipi-dphy"</code> uncovers the <strong>Linux Driver for A64 MIPI D-PHY</strong>…</p>
|
||||
<p>Searching online for <code>"sun6i-a31-mipi-dphy"</code> uncovers the <strong>Linux Driver for A64 MIPI D-PHY</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/torvalds/linux/blob/master/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c"><strong>phy-sun6i-mipi-dphy.c</strong></a></li>
|
||||
</ul>
|
||||
|
@ -960,7 +960,7 @@ dtc \
|
|||
status = "okay";
|
||||
};
|
||||
</code></pre></div>
|
||||
<p>Searching online for <code>"sun50i-a64-display-engine"</code> gives us this <strong>Linux Driver for A64 Display Engine</strong>…</p>
|
||||
<p>Searching online for <code>"sun50i-a64-display-engine"</code> gives us this <strong>Linux Driver for A64 Display Engine</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/sun4i/sun4i_drv.c"><strong>sun4i_drv.c</strong></a></li>
|
||||
</ul>
|
||||
|
@ -1004,7 +1004,7 @@ dtc \
|
|||
touchscreen-size-y = <0x5a0>;
|
||||
};
|
||||
</code></pre></div>
|
||||
<p>Searching online for <code>"goodix,gt917s"</code> gives us this <strong>Linux Driver for Goodix GT917S Touch Panel</strong>…</p>
|
||||
<p>Searching online for <code>"goodix,gt917s"</code> gives us this <strong>Linux Driver for Goodix GT917S Touch Panel</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/torvalds/linux/blob/master/drivers/input/touchscreen/goodix.c">goodix.c</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -169,7 +169,7 @@ up_exit: TCB=0x802088d0 exiting
|
|||
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/ramdisk2-0.0.1">(See the <strong>Complete Log</strong>)</a></p>
|
||||
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/ramdisk2-0.0.1">(See the <strong>Build Outputs</strong>)</a></p>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/jh7110/jh7110_irq.c#L138-L180">(<strong>up_enable_irq</strong> is defined here)</a></p>
|
||||
<p>In the log above, NuttX QEMU enables UART Interrupts at <strong>NuttX IRQ 35</strong>. </p>
|
||||
<p>In the log above, NuttX QEMU enables UART Interrupts at <strong>NuttX IRQ 35</strong>.</p>
|
||||
<p>(Equivalent to <strong>RISC-V IRQ 10</strong>, with IRQ Offset of 25)</p>
|
||||
<p>Then <strong>NuttX Shell</strong> runs in QEMU…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>$%&riscv_doirq: irq=8
|
||||
|
@ -210,7 +210,7 @@ FNFuFtFtFSFhFeFlFlF F(FNFSFHF)F FNFuFtFtFXF-F1F2F.F0F.F3F
|
|||
</li>
|
||||
<li>
|
||||
<p>NuttX Kernel <strong>reads the Serial Buffer</strong>, one character at a time…</p>
|
||||
<p><a href="https://github.com/lupyuen2/wip-nuttx/blob/star64d/drivers/serial/serial_io.c#L42-L107">(<strong>uart_xmitchars</strong>)</a> </p>
|
||||
<p><a href="https://github.com/lupyuen2/wip-nuttx/blob/star64d/drivers/serial/serial_io.c#L42-L107">(<strong>uart_xmitchars</strong>)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If the <strong>UART Transmit Status</strong> is ready…</p>
|
||||
|
@ -382,7 +382,7 @@ dtc \
|
|||
<div class="example-wrap"><pre class="language-text"><code>RISC-V IRQ = Global Interrupt Number + 5
|
||||
</code></pre></div>
|
||||
<p><em>Maybe IRQ 32 is too high? (QEMU UART IRQ is only 10)</em></p>
|
||||
<p>The doc on <a href="https://doc-en.rvspace.org/JH7110/TRM/JH7110_TRM/interrupt_connections.html"><strong>JH7110 Interrupt Connections</strong></a> says that Global Interrupts are numbered <strong>0 to 126</strong>. (127 total interrupts) </p>
|
||||
<p>The doc on <a href="https://doc-en.rvspace.org/JH7110/TRM/JH7110_TRM/interrupt_connections.html"><strong>JH7110 Interrupt Connections</strong></a> says that Global Interrupts are numbered <strong>0 to 126</strong>. (127 total interrupts)</p>
|
||||
<p>That’s a lot more than NuttX QEMU can handle. So we patched it…</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -645,7 +645,7 @@ void *riscv_dispatch_irq(uintptr_t vector, uintptr_t *regs) {
|
|||
return regs;
|
||||
}
|
||||
</code></pre></div>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_doirq.c#L58-L131">(<strong>riscv_doirq</strong> is defined here)</a> </p>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_doirq.c#L58-L131">(<strong>riscv_doirq</strong> is defined here)</a></p>
|
||||
<p>There’s also a <strong>Core-Local Interruptor (CLINT)</strong> <a href="https://starfivetech.com/uploads/u74mc_core_complex_manual_21G1.pdf">(Page 185)</a> that handles Software Interrupt and Timer Interrupt. But we won’t cover it today. (Pic below)</p>
|
||||
<p><strong>TODO:</strong> Do we need to handle CLINT?</p>
|
||||
<p>Let’s check that the RISC-V Interrupts are delegated correctly…</p>
|
||||
|
@ -705,7 +705,7 @@ Boot HART MEDELEG:
|
|||
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/star64d-0.0.1">(See the <strong>Build Outputs</strong>)</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Now we see UART Interrupts fired at <strong>NuttX IRQ 57</strong> (RISC-V IRQ 32) yay! </p>
|
||||
<p>Now we see UART Interrupts fired at <strong>NuttX IRQ 57</strong> (RISC-V IRQ 32) yay!</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>uart_register: Registering /dev/console
|
||||
uart_register: Registering /dev/ttyS0
|
||||
up_enable_irq: irq=57, extirq=32, RISCV_IRQ_EXT=25
|
||||
|
@ -798,7 +798,7 @@ nsh>
|
|||
</code></pre></div>
|
||||
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/star64d-0.0.1">(See the <strong>Complete Log</strong>)</a></p>
|
||||
<p>Yep NuttX Shell works OK on Star64!</p>
|
||||
<p>But it’s super slow. Each dot is <a href="https://github.com/lupyuen2/wip-nuttx/blob/star64d/drivers/serial/uart_16550.c#L954-L966"><strong>One Million Calls</strong></a> to the UART Interrupt Handler, with UART Interrupt Status <a href="https://github.com/lupyuen2/wip-nuttx/blob/star64d/drivers/serial/uart_16550.c#L954-L966"><strong>INTSTATUS = 0</strong></a>! </p>
|
||||
<p>But it’s super slow. Each dot is <a href="https://github.com/lupyuen2/wip-nuttx/blob/star64d/drivers/serial/uart_16550.c#L954-L966"><strong>One Million Calls</strong></a> to the UART Interrupt Handler, with UART Interrupt Status <a href="https://github.com/lupyuen2/wip-nuttx/blob/star64d/drivers/serial/uart_16550.c#L954-L966"><strong>INTSTATUS = 0</strong></a>!</p>
|
||||
<p><a href="https://lupyuen.github.io/articles/plic#appendix-fix-the-spurious-uart-interrupts">(<strong>UPDATE:</strong> We fixed the <strong>Spurious UART Interrupts</strong>!)</a></p>
|
||||
<p><em>Why is UART Interrupt triggered repeatedly with <a href="https://github.com/lupyuen2/wip-nuttx/blob/star64d/drivers/serial/uart_16550.c#L954-L966">INTSTATUS = 0</a>?</em></p>
|
||||
<p><a href="https://github.com/lupyuen/lupyuen.github.io/issues/18"><strong>Michael Engel</strong></a> says it’s a DesignWare UART issue…</p>
|
||||
|
|
|
@ -754,7 +754,7 @@ and will unmask the IRQ upon write to CLAIM register”</p>
|
|||
<p><em>So RISC-V ain’t RISC-V on SiFive vs T-Head?</em></p>
|
||||
<p>It feels… Very different? Compare the docs…</p>
|
||||
<p><a href="https://starfivetech.com/uploads/u74mc_core_complex_manual_21G1.pdf"><strong>SiFive U74 Manual</strong></a></p>
|
||||
<p><a href="https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf"><strong>T-Head C906 User Manual</strong></a> </p>
|
||||
<p><a href="https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf"><strong>T-Head C906 User Manual</strong></a></p>
|
||||
<p><a href="https://github.com/T-head-Semi/openc906/blob/main/doc/%E7%8E%84%E9%93%81C906%E9%9B%86%E6%88%90%E6%89%8B%E5%86%8C.pdf"><strong>T-Head C906 Integration Manual</strong> (Chinese)</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
|
|
|
@ -118,13 +118,13 @@
|
|||
<p>(Bouffalo Lab BL808 SoC)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Which fires an <strong>Interrupt through the PLIC</strong> to our RISC-V CPU </p>
|
||||
<p>Which fires an <strong>Interrupt through the PLIC</strong> to our RISC-V CPU</p>
|
||||
<p>(T-Head C906 RISC-V Core)</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Without the PLIC, it’s <strong>impossible to enter commands</strong> in the Serial Console!</p>
|
||||
<p><em>Tell me more…</em></p>
|
||||
<p>Let’s run through the steps to <strong>handle a UART Interrupt</strong> on a RISC-V SBC… </p>
|
||||
<p>Let’s run through the steps to <strong>handle a UART Interrupt</strong> on a RISC-V SBC…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/plic2-registers.jpg" alt="Platform-Level Interrupt Controller for Pine64 Ox64 64-bit RISC-V SBC (Bouffalo Lab BL808)" /></p>
|
||||
<ol>
|
||||
<li>
|
||||
|
|
|
@ -390,7 +390,7 @@ int arm64_gic_initialize(void)
|
|||
</blockquote>
|
||||
<p>(Adding <strong>GPL Code</strong>? Please check with the NuttX Maintainers)</p>
|
||||
<h2 id="impact"><a class="doc-anchor" href="#impact">§</a>4.3 Impact</h2>
|
||||
<p>Under “Impact”, we write <strong>which parts of NuttX are affected</strong> by the Pull Request. </p>
|
||||
<p>Under “Impact”, we write <strong>which parts of NuttX are affected</strong> by the Pull Request.</p>
|
||||
<p>(And which parts <em>won’t</em> be affected)</p>
|
||||
<blockquote>
|
||||
<p><em>“With this PR, NuttX now supports GIC v2 on Arm64.</em></p>
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
<ul>
|
||||
<li><a href="https://lupyuen.github.io/articles/ttn"><strong>“The Things Network on PineDio Stack BL604 RISC-V Board”</strong></a></li>
|
||||
</ul>
|
||||
<p>But it should work for <strong>any LoRaWAN Device</strong> connected to The Things Network… Assuming that we have configured a suitable <strong>Payload Formatter</strong> in The Things Network. </p>
|
||||
<p>But it should work for <strong>any LoRaWAN Device</strong> connected to The Things Network… Assuming that we have configured a suitable <strong>Payload Formatter</strong> in The Things Network.</p>
|
||||
<p>(Read on to learn how)</p>
|
||||
<p><img src="https://lupyuen.github.io/images/payload-title.jpg" alt="CBOR Payload Formatter for The Things Network" /></p>
|
||||
<h1 id="payload-formatter"><a class="doc-anchor" href="#payload-formatter">§</a>1 Payload Formatter</h1>
|
||||
|
@ -147,7 +147,7 @@
|
|||
<p><img src="https://lupyuen.github.io/images/payload-ttn3.png" alt="Decoded Sensor Data in the Live Data Table" /></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Click on a message in the <strong>Live Data Table</strong>. </p>
|
||||
<p>Click on a message in the <strong>Live Data Table</strong>.</p>
|
||||
<p>We should see the <strong>decoded_payload</strong> field containing our Decoded Sensor Data…</p>
|
||||
<div class="example-wrap"><pre class="language-json"><code>{
|
||||
...
|
||||
|
@ -810,7 +810,7 @@ sudo service grafana-server status
|
|||
brew services start grafana
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>To test Grafana, browse to </p>
|
||||
<p>To test Grafana, browse to</p>
|
||||
<p><strong><code>http://localhost:3000</code></strong></p>
|
||||
<p><strong>Username:</strong> admin</p>
|
||||
<p><strong>Password:</strong> admin</p>
|
||||
|
|
|
@ -787,7 +787,7 @@ cat output/Main/index.js \
|
|||
// Call the PureScript Function
|
||||
const result2 = explainException(12)('000000008000ad8a')('000000008000ad8a')
|
||||
</code></pre></div><h1 id="appendix-bigint-in-purescript"><a class="doc-anchor" href="#appendix-bigint-in-purescript">§</a>10 Appendix: BigInt in PureScript</h1>
|
||||
<p><em>Why are we passing addresses in Text instead of Numbers? Like <code>"8000ad8a"</code></em></p>
|
||||
<p><em>Why are we passing addresses in Text instead of Numbers? Like <code>"8000ad8a"</code></em></p>
|
||||
<p>That’s because <code>0x8000ad8a</code> is too big for <strong>PureScript Int</strong>, a signed 32-bit integer.</p>
|
||||
<p>PureScript Int is meant to interoperate with JavaScript Integer, which is also 32-bit.</p>
|
||||
<p><em>What about PureScript BigInt?</em></p>
|
||||
|
|
|
@ -482,7 +482,7 @@ NuttX boots on Star64 and <strong>NuttShell (nsh)</strong> appears in the Serial
|
|||
</ol>
|
||||
<p>This is how we did it for Star64 SBC (with JH7110 SoC) in <strong>3 Pull Requests</strong>…</p>
|
||||
<h2 id="patch-the-nuttx-dependencies"><a class="doc-anchor" href="#patch-the-nuttx-dependencies">§</a>5.1 Patch the NuttX Dependencies</h2>
|
||||
<p>First we patch any <strong>NuttX Dependencies</strong> needed by Star64 JH7110. </p>
|
||||
<p>First we patch any <strong>NuttX Dependencies</strong> needed by Star64 JH7110.</p>
|
||||
<p>We discovered that JH7110 triggers too many <strong>spurious UART interrupts</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://lupyuen.github.io/articles/plic#spurious-uart-interrupts"><strong>“Spurious UART Interrupts”</strong></a></li>
|
||||
|
@ -693,7 +693,7 @@ Imagination Technology BX-4-32 GPU and supports up to 8GB 1866MHz LPDDR4 memory.
|
|||
<p><a href="https://github.com/apache/nuttx/blob/52527d9915ea0ba1d7e75bb9f2f81356bb2b8ba9/Documentation/platforms/risc-v/jh7110/boards/star64/index.rst#risc-v-toolchain"><strong>Toolchain</strong></a> and <a href="https://github.com/apache/nuttx/blob/52527d9915ea0ba1d7e75bb9f2f81356bb2b8ba9/Documentation/platforms/risc-v/jh7110/boards/star64/index.rst#building"><strong>Building</strong></a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/52527d9915ea0ba1d7e75bb9f2f81356bb2b8ba9/Documentation/platforms/risc-v/jh7110/boards/star64/index.rst#serial-console"><strong>Serial Console</strong></a> and <a href="https://github.com/apache/nuttx/blob/52527d9915ea0ba1d7e75bb9f2f81356bb2b8ba9/Documentation/platforms/risc-v/jh7110/boards/star64/index.rst#booting"><strong>Booting</strong></a> </p>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/52527d9915ea0ba1d7e75bb9f2f81356bb2b8ba9/Documentation/platforms/risc-v/jh7110/boards/star64/index.rst#serial-console"><strong>Serial Console</strong></a> and <a href="https://github.com/apache/nuttx/blob/52527d9915ea0ba1d7e75bb9f2f81356bb2b8ba9/Documentation/platforms/risc-v/jh7110/boards/star64/index.rst#booting"><strong>Booting</strong></a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/52527d9915ea0ba1d7e75bb9f2f81356bb2b8ba9/Documentation/platforms/risc-v/jh7110/boards/star64/index.rst#configurations"><strong>Configurations</strong></a> and <a href="https://github.com/apache/nuttx/blob/52527d9915ea0ba1d7e75bb9f2f81356bb2b8ba9/Documentation/platforms/risc-v/jh7110/boards/star64/index.rst#peripheral-support"><strong>Peripheral Support</strong></a></p>
|
||||
|
@ -834,7 +834,7 @@ Imagination Technology BX-4-32 GPU and supports up to 8GB 1866MHz LPDDR4 memory.
|
|||
<p>Hopefully this article will be helpful if you’re adding a <strong>NuttX Arch</strong> or <strong>NuttX Board</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Stay tuned for <strong>Upcoming Features</strong> on Star64 and VisionFive2 </p>
|
||||
<p>Stay tuned for <strong>Upcoming Features</strong> on Star64 and VisionFive2</p>
|
||||
<p>(Maybe PineTab-V too!)</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -1058,7 +1058,7 @@ const tbl_entry_t lookup_table[] PROGMEM = {
|
|||
<p>We modified these Blockly source files to load the Custom Blocks and generate Rhai code…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="https://github.com/lupyuen2/blockly-bl602/blob/master/demos/code/index.html"><strong>demos/code/index.html</strong></a> </p>
|
||||
<p><a href="https://github.com/lupyuen2/blockly-bl602/blob/master/demos/code/index.html"><strong>demos/code/index.html</strong></a></p>
|
||||
<p>This is the <strong>HTML source file</strong> for the Blockly Web Editor. (See pic above)</p>
|
||||
<p><a href="https://github.com/lupyuen2/blockly-bl602/pull/2/files#diff-dcf2ffe98d7d8b4a0dd7b9f769557dbe8c9e0e726236ef229def25c956a43d8f">(See changes)</a></p>
|
||||
</li>
|
||||
|
|
|
@ -197,7 +197,7 @@ nsh> ps
|
|||
<p>The above command starts the <a href="https://www.qemu.org/docs/master/system/target-riscv.html"><strong>QEMU Emulator for RISC-V</strong></a> (64-bit) with…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Kernel Image: <strong>nuttx</strong> </p>
|
||||
<p>Kernel Image: <strong>nuttx</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>CPU: <a href="https://www.qemu.org/docs/master/system/target-riscv.html"><strong>64-bit RISC-V</strong></a></p>
|
||||
|
@ -582,7 +582,7 @@ csrw mtvec, t0
|
|||
<p><a href="https://news.ycombinator.com/item?id=36453810#36455404">(Source)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>To <strong>Enable Logging</strong> for RISC-V QEMU: Use the QEMU Option <strong><code>-trace "*"</code></strong></p>
|
||||
<p>To <strong>Enable Logging</strong> for RISC-V QEMU: Use the QEMU Option <strong><code>-trace "*"</code></strong></p>
|
||||
<p><a href="https://lupyuen.github.io/articles/semihost#appendix-ram-disk-address-for-risc-v-qemu">(Like this)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
<p>Think of the possibilities…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Walk around a <strong>Roblox House</strong> to <strong>monitor the temperature</strong> in our Smart Home. </p>
|
||||
<p>Walk around a <strong>Roblox House</strong> to <strong>monitor the temperature</strong> in our Smart Home.</p>
|
||||
<p>Flip the lights on and off in the Virtual House, to <strong>control the lights</strong> in our Real Home.</p>
|
||||
<p><a href="https://medium.com/@camden.o.b/how-we-could-make-a-roblox-smart-home-that-connects-to-the-real-world-e4d89b309516">(Check out this excellent article by Camden Bruce)</a></p>
|
||||
</li>
|
||||
|
@ -133,7 +133,7 @@
|
|||
<p>(Apologies if my Roblox looks rough… This is my first time using Roblox 🙏)</p>
|
||||
<p><img src="https://lupyuen.github.io/images/roblox-title.jpg" alt="Cold / Hot / Normal IoT Objects rendered in Roblox" /></p>
|
||||
<h1 id="roblox-mirrors-real-life"><a class="doc-anchor" href="#roblox-mirrors-real-life">§</a>1 Roblox Mirrors Real Life</h1>
|
||||
<p>The pic shows what we shall accomplish with Roblox… </p>
|
||||
<p>The pic shows what we shall accomplish with Roblox…</p>
|
||||
<p>A Virtual Object that <strong>visualises the Live Temperature</strong> of our Real Object (PineDio Stack)</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -286,7 +286,7 @@ local function getSensorData()
|
|||
</code></pre></div>
|
||||
<p><a href="https://developer.roblox.com/en-us/api-reference/function/HttpService/GetAsync">(GetAsync is documented here)</a></p>
|
||||
<p><em>What is “pcall”?</em></p>
|
||||
<p>We wrap our code with <strong>“pcall”</strong> to catch any errors returned by the HTTP Fetching. </p>
|
||||
<p>We wrap our code with <strong>“pcall”</strong> to catch any errors returned by the HTTP Fetching.</p>
|
||||
<p>(Also for catching Decoding Errors)</p>
|
||||
<p>If any error occurs, execution resumes <strong>after the “pcall” block</strong>.</p>
|
||||
<p>And we’ll check for errors then.</p>
|
||||
|
@ -392,7 +392,7 @@ end
|
|||
<h2 id="edit-settings"><a class="doc-anchor" href="#edit-settings">§</a>3.2 Edit Settings</h2>
|
||||
<ol>
|
||||
<li>
|
||||
<p>If we have an IoT Gadget connected to The Things Network: </p>
|
||||
<p>If we have an IoT Gadget connected to The Things Network:</p>
|
||||
<p>Edit these settings…</p>
|
||||
<div class="example-wrap"><pre class="language-lua"><code>-- TODO: Change this to your Application ID for The Things Network
|
||||
-- (Must have permission to Read Application Traffic)
|
||||
|
@ -407,8 +407,8 @@ local TTN_URL = "https://au1.cloud.thethings.network/api/v3/as/applications
|
|||
<p><a href="https://lupyuen.github.io/articles/roblox#appendix-the-things-network-settings">(More about this in the Appendix)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If we don’t have an IoT Gadget: </p>
|
||||
<p>Leave the above settings as is. </p>
|
||||
<p>If we don’t have an IoT Gadget:</p>
|
||||
<p>Leave the above settings as is.</p>
|
||||
<p>The script will run in <strong>Demo Mode</strong>, simulating a real gadget.</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
@ -417,7 +417,7 @@ local TTN_URL = "https://au1.cloud.thethings.network/api/v3/as/applications
|
|||
<ol>
|
||||
<li>
|
||||
<p>At <strong>Explorer → ServerStorage</strong> (at right)…</p>
|
||||
<p>Click <strong>(+)</strong> and create two <strong>ModuleScripts</strong>: </p>
|
||||
<p>Click <strong>(+)</strong> and create two <strong>ModuleScripts</strong>:</p>
|
||||
<p><strong><code>Base64</code></strong> and <strong><code>Cbor</code></strong></p>
|
||||
<p>(See pic above)</p>
|
||||
</li>
|
||||
|
|
|
@ -355,7 +355,7 @@ heapless = "0.6.1"
|
|||
<h1 id="rust-on-bl602-iot-sdk"><a class="doc-anchor" href="#rust-on-bl602-iot-sdk">§</a>4 Rust on BL602 IoT SDK</h1>
|
||||
<p>Our Rust Firmware accesses the BL602 serial port, GPIO pin and system timer by calling the <strong>BL602 IoT SDK</strong>. (Imported from C into Rust)</p>
|
||||
<p><img src="https://lupyuen.github.io/images/rust-arch.png" alt="Rust on BL602 IoT SDK" /></p>
|
||||
<p>Strictly speaking this isn’t <a href="https://docs.rust-embedded.org/book/"><strong>Embedded Rust</strong></a>, because we’re not running Rust directly on Bare Metal (BL602 Hardware). </p>
|
||||
<p>Strictly speaking this isn’t <a href="https://docs.rust-embedded.org/book/"><strong>Embedded Rust</strong></a>, because we’re not running Rust directly on Bare Metal (BL602 Hardware).</p>
|
||||
<p>Instead we’re running <strong>Rust on top of an Embedded Operating System</strong> (BL602 IoT SDK + FreeRTOS). It’s similar to running Rust on Linux / macOS / Windows.</p>
|
||||
<p>That’s why we compile our Rust code into a <strong>static library</strong> that will be linked into the BL602 Firmware. See <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_rust/rust/Cargo.toml#L14-L18"><code>rust/Cargo.toml</code></a>…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>## Build this module as a Rust library,
|
||||
|
@ -614,11 +614,11 @@ blflash flash c:\blflash\sdk_app_lora.bin --port COM5
|
|||
<div class="example-wrap"><pre class="language-bash"><code>screen /dev/ttyUSB0 2000000
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>For Windows:</strong> </p>
|
||||
<p><strong>For Windows:</strong></p>
|
||||
<p>Use <code>putty</code> (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Alternatively:</strong> </p>
|
||||
<p><strong>Alternatively:</strong></p>
|
||||
<p>Use the Web Serial Terminal (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||||
<p><a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">More details on connecting to BL602</a></p>
|
||||
</li>
|
||||
|
@ -866,7 +866,7 @@ riscv32imc-unknown-none-elf
|
|||
<p><img src="https://lupyuen.github.io/images/rust-flow.png" alt="Graphical Flow Programming with Rete.js" /></p>
|
||||
<h2 id="but-why-c"><a class="doc-anchor" href="#but-why-c">§</a>11.3 But Why C?</h2>
|
||||
<p><em>But seriously… Why are we still coding BL602 Firmware in C? Why not code everything in Rust?</em></p>
|
||||
<p>Because <strong>some BL602 features work better in C</strong> than in Rust. </p>
|
||||
<p>Because <strong>some BL602 features work better in C</strong> than in Rust.</p>
|
||||
<p>Like <strong>SPI with DMA</strong>, which is useful for SPI displays that require high-bandwidth data transfer.</p>
|
||||
<p><a href="https://lupyuen.github.io/articles/spi#spi-with-direct-memory-access">More about BL602 SPI with DMA</a></p>
|
||||
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>12 What’s Next</h1>
|
||||
|
|
|
@ -191,7 +191,7 @@
|
|||
<p>Calls to C Functions must be marked as <strong>“<code>unsafe</code>”</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>We construct a <strong>Byte String</strong> in Rust with the <code>b"..."</code> syntax</p>
|
||||
<p>We construct a <strong>Byte String</strong> in Rust with the <code>b"..."</code> syntax</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Rust Strings are not null-terminated! We add the <strong>Null Byte</strong> ourselves with “<code>\0</code>”</p>
|
||||
|
|
|
@ -595,7 +595,7 @@ riscv64-unknown-elf-ld:
|
|||
<ul>
|
||||
<li>
|
||||
<p><a href="https://doc.rust-lang.org/std/"><strong>Rust Standard Library</strong></a>: This is used by most Rust Apps on Desktops and Servers.</p>
|
||||
<p>Supports Heap Memory and the Rust Equivalent of POSIX Calls. </p>
|
||||
<p>Supports Heap Memory and the Rust Equivalent of POSIX Calls.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://doc.rust-lang.org/core/index.html"><strong>Rust Core Library</strong></a> <code>[no_std]</code>: Barebones Rust Library that runs on Bare Metal, used by Rust Embedded Apps.</p>
|
||||
|
|
|
@ -125,7 +125,7 @@ riscv64-unknown-elf-ld: libapps.a
|
|||
can't link soft-float modules with double-float modules
|
||||
</code></pre></div>
|
||||
<p><a href="https://lupyuen.github.io/articles/rust3#build-nuttx-for-qemu-risc-v">(See the <strong>Complete Steps</strong>)</a></p>
|
||||
<p><strong>GCC Linker</strong> failed because it couldn’t link the NuttX Binaries with the Rust Binaries. </p>
|
||||
<p><strong>GCC Linker</strong> failed because it couldn’t link the NuttX Binaries with the Rust Binaries.</p>
|
||||
<p>Here’s Why: NuttX Build calls <strong>GCC Compiler</strong> to compile our C Modules…</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>## Build NuttX Firmware with Tracing Enabled
|
||||
$ make --trace
|
||||
|
|
|
@ -898,7 +898,7 @@ I2C Informational Output
|
|||
</ol>
|
||||
<h2 id="configure-rust-target"><a class="doc-anchor" href="#configure-rust-target">§</a>13.3 Configure Rust Target</h2>
|
||||
<p><strong>For BL602 / BL604</strong>: Skip to the next section</p>
|
||||
<p><strong>For ESP32-C3 (RISC-V)</strong>: </p>
|
||||
<p><strong>For ESP32-C3 (RISC-V)</strong>:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Run this command to install the Rust Target…</p>
|
||||
|
@ -915,7 +915,7 @@ I2C Informational Output
|
|||
<p>Remove “-Z build-std=core” from “rust_build_options”</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p><strong>For ESP32 (Xtensa)</strong>: </p>
|
||||
<p><strong>For ESP32 (Xtensa)</strong>:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Install the Rust compiler fork with Xtensa support. <a href="https://github.com/jessebraham/esp-hal/tree/main/esp32-hal">(See this)</a></p>
|
||||
|
|
|
@ -209,12 +209,12 @@ cargo build --target wasm32-unknown-emscripten
|
|||
<p>This compiles three <strong>Rust Projects</strong>…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Rust Firmware:</strong> </p>
|
||||
<p><strong>Rust Firmware:</strong></p>
|
||||
<p><a href="https://github.com/lupyuen/bl602-simulator/tree/main/sdk_app_rust_gpio"><code>bl602-simulator/sdk_app_rust_gpio/rust</code></a></p>
|
||||
<p>(The Rust Firmware we’ve seen earlier. Should be portable across BL602 and WebAssembly)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Rust Simulator Library:</strong> </p>
|
||||
<p><strong>Rust Simulator Library:</strong></p>
|
||||
<p><a href="https://github.com/lupyuen/bl602-simulator/tree/main/bl602-simulator"><code>bl602-simulator/bl602-simulator</code></a></p>
|
||||
<p>(Simulates the BL602 IoT SDK. We’ll see this in a while)</p>
|
||||
</li>
|
||||
|
@ -258,7 +258,7 @@ target/wasm32-unknown-emscripten/debug/libbl602_simulator.a
|
|||
name = "app" # Output will be named `libapp.a`
|
||||
crate-type = ["staticlib"] # And will be a Static Library
|
||||
</code></pre></div>
|
||||
<p><strong>Rust Libraries won’t link</strong> with Emscripten’s WebAssembly Runtime. </p>
|
||||
<p><strong>Rust Libraries won’t link</strong> with Emscripten’s WebAssembly Runtime.</p>
|
||||
<p>That’s why we switched to <strong>Static Libraries</strong>.</p>
|
||||
<h2 id="link-rust-firmware-with-emscripten"><a class="doc-anchor" href="#link-rust-firmware-with-emscripten">§</a>2.2 Link Rust Firmware with Emscripten</h2>
|
||||
<p>We’re nearly ready to run our Rust Firmware in WebAssembly! We need a <strong>WebAssembly Runtime</strong> that will…</p>
|
||||
|
@ -882,7 +882,7 @@ function gpio_output_set(pin, value) {
|
|||
<p><strong>Heart Rate Sensor</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Battery Charging Chip</strong> </p>
|
||||
<p><strong>Battery Charging Chip</strong></p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>All this in a compact 3.5 cm² form factor!</p>
|
||||
|
@ -909,7 +909,7 @@ function gpio_output_set(pin, value) {
|
|||
</ol>
|
||||
<p><em>Why would we need The Scripted REPL Way?</em></p>
|
||||
<p>Because Scripted REPL platforms like <strong>uLisp</strong> and <strong>MicroPython</strong> are still popular with Embedded Learners.</p>
|
||||
<p>For BL602, perhaps learners could <strong>start with (Rust-like) Rhai Script</strong>… </p>
|
||||
<p>For BL602, perhaps learners could <strong>start with (Rust-like) Rhai Script</strong>…</p>
|
||||
<p>And <strong>upgrade to Rust</strong> (or C) when they’re ready.</p>
|
||||
<p>Check out the new article…</p>
|
||||
<ul>
|
||||
|
|
|
@ -188,7 +188,7 @@ smh_call():
|
|||
<blockquote>
|
||||
<p>“EBREAK was primarily designed to be used by a debugger to cause execution to stop and fall back into the debugger”</p>
|
||||
</blockquote>
|
||||
<p>OK thanks but we’re not doing any debugging! </p>
|
||||
<p>OK thanks but we’re not doing any debugging!</p>
|
||||
<p>The next part is more helpful…</p>
|
||||
<blockquote>
|
||||
<p>“Another use of EBREAK is to support <strong>Semihosting</strong>, where the execution environment includes a debugger that can provide services over an Alternate System Call Interface built around the EBREAK instruction”</p>
|
||||
|
@ -836,7 +836,7 @@ printenv bootcmd_tftp
|
|||
saveenv
|
||||
</code></pre></div>
|
||||
<p><a href="https://lupyuen.github.io/articles/tftp#configure-u-boot-for-tftp">(Remember to set <strong>tftp_server</strong> and <strong>boot_targets</strong>)</a></p>
|
||||
<p>Run the above commands in U-Boot. </p>
|
||||
<p>Run the above commands in U-Boot.</p>
|
||||
<p>Copy the Initial RAM Disk <strong>initrd</strong> to the TFTP Folder…</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>## Copy NuttX Binary Image, Device Tree and
|
||||
## Initial RAM Disk to TFTP Folder
|
||||
|
@ -913,10 +913,10 @@ $ booti ${kernel_addr_r} ${ramdisk_addr_r}:0x1000000 ${fdt_addr_r}
|
|||
<p>RTOS Features > RTOS hooks > Custom board late initialization</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>File Systems > ROMFS file system </p>
|
||||
<p>File Systems > ROMFS file system</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>RTOS Features > Tasks and Scheduling > Auto-mount init file system </p>
|
||||
<p>RTOS Features > Tasks and Scheduling > Auto-mount init file system</p>
|
||||
<p>Set to <code>/system/bin</code></p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -945,7 +945,7 @@ CONFIG_INIT_MOUNT_SOURCE=""
|
|||
<h1 id="appendix-ram-disk-address-for-risc-v-qemu"><a class="doc-anchor" href="#appendix-ram-disk-address-for-risc-v-qemu">§</a>12 Appendix: RAM Disk Address for RISC-V QEMU</h1>
|
||||
<p><em>We need the RAM Disk Address for RISC-V QEMU…</em></p>
|
||||
<p><em>Can we enable logging for RISC-V QEMU?</em></p>
|
||||
<p>Yep we use this QEMU Option: <strong><code>-trace "*"</code></strong></p>
|
||||
<p>Yep we use this QEMU Option: <strong><code>-trace "*"</code></strong></p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>## Start NuttX on QEMU
|
||||
## with Initial RAM Disk `initrd`
|
||||
qemu-system-riscv64 \
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
<p>Handling UART Interrupts</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>And how we implemented PinePhone’s <strong>UART Driver</strong> for <a href="https://lupyuen.github.io/articles/uboot"><strong>Apache NuttX RTOS</strong></a>. </p>
|
||||
<p>And how we implemented PinePhone’s <strong>UART Driver</strong> for <a href="https://lupyuen.github.io/articles/uboot"><strong>Apache NuttX RTOS</strong></a>.</p>
|
||||
<p>Let’s dive into our <strong>NuttX Porting Journal</strong> and learn how we made PinePhone chatty over UART…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/lupyuen/pinephone-nuttx"><strong>lupyuen/pinephone-nuttx</strong></a></li>
|
||||
|
@ -414,7 +414,7 @@ static void a64_uart_detach(struct uart_dev_s *dev)
|
|||
<p><img src="https://lupyuen.github.io/images/serial-enable.jpg" alt="A64 UART Interrupt Enable Register UART_IER" /></p>
|
||||
<p><a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><em>A64 UART Interrupt Enable Register UART_IER</em></a></p>
|
||||
<h2 id="enable-interrupt"><a class="doc-anchor" href="#enable-interrupt">§</a>3.2 Enable Interrupt</h2>
|
||||
<p>UART Interupts won’t happen until we <strong>enable UART Interrupts</strong>. </p>
|
||||
<p>UART Interupts won’t happen until we <strong>enable UART Interrupts</strong>.</p>
|
||||
<p>Page 565 of the <a href="https://dl.linux-sunxi.org/A64/A64_Datasheet_V1.1.pdf"><strong>Allwinner A64 User Manual</strong></a> tells us the UART Register for enabling UART Interrupts (pic above)…</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -700,7 +700,7 @@ void arm64_serialinit(void)
|
|||
</code></pre></div>
|
||||
<p>And we’re done with our PinePhone UART Driver for NuttX!</p>
|
||||
<h1 id="uart-in-action"><a class="doc-anchor" href="#uart-in-action">§</a>6 UART In Action</h1>
|
||||
<p>Let’s watch our UART Driver in action! </p>
|
||||
<p>Let’s watch our UART Driver in action!</p>
|
||||
<p>Follow these steps to <strong>build NuttX</strong> and copy to Jumpdrive microSD…</p>
|
||||
<ul>
|
||||
<li><a href="https://lupyuen.github.io/articles/uboot#pinephone-boots-nuttx"><strong>“PinePhone Boots NuttX”</strong></a></li>
|
||||
|
|
|
@ -749,7 +749,7 @@ riscv_exception:
|
|||
<tr><td style="text-align: center"><strong>RX</strong> (Pin 10)</td><td style="text-align: center"><strong>TX</strong></td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<p>USB UART Dongle must be <a href="http://sun-light.com.sg/index.php?route=product/product&product_id=2367"><strong>CP2102</strong></a>, it doesn’t like <a href="https://pine64.com/product/serial-console-woodpecker-edition/"><strong>CH340</strong></a> </p>
|
||||
<p>USB UART Dongle must be <a href="http://sun-light.com.sg/index.php?route=product/product&product_id=2367"><strong>CP2102</strong></a>, it doesn’t like <a href="https://pine64.com/product/serial-console-woodpecker-edition/"><strong>CH340</strong></a></p>
|
||||
<h2 id="download-the-linux-microsd-1"><a class="doc-anchor" href="#download-the-linux-microsd-1">§</a>13.2 Download the Linux MicroSD</h2>
|
||||
<p>Based on the Linux MicroSD created by <a href="https://github.com/Fishwaldo"><strong>Justin Hammond</strong></a> (Fishwaldo)…</p>
|
||||
<ul>
|
||||
|
@ -786,7 +786,7 @@ $ lz4 duos_sd.img.lz4
|
|||
</ul>
|
||||
<p>We’ll copy them to our TFTP Server…</p>
|
||||
<h2 id="boot-nuttx-over-tftp-1"><a class="doc-anchor" href="#boot-nuttx-over-tftp-1">§</a>13.4 Boot NuttX over TFTP</h2>
|
||||
<p>Follow the <a href="https://lupyuen.github.io/articles/tftp#install-tftp-server"><strong>instructions here</strong></a> to install our TFTP Server. </p>
|
||||
<p>Follow the <a href="https://lupyuen.github.io/articles/tftp#install-tftp-server"><strong>instructions here</strong></a> to install our TFTP Server.</p>
|
||||
<p>Copy the <strong>NuttX Image</strong> and <strong>Device Tree</strong> (previous section) to our TFTP Server…</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>## Copy NuttX Image and Device Tree to TFTP Server
|
||||
## TODO: Change `tftpserver` and `tftpboot` to our TFTP Server and Path
|
||||
|
|
|
@ -186,7 +186,7 @@
|
|||
<h1 id="rise-the-dough"><a class="doc-anchor" href="#rise-the-dough">§</a>4 Rise the Dough</h1>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Place the Bread Bucket in the Fridge (not the freezer!), and cover with a Dry Towel. </p>
|
||||
<p>Place the Bread Bucket in the Fridge (not the freezer!), and cover with a Dry Towel.</p>
|
||||
<p>Rest for 1 hour.</p>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -229,7 +229,7 @@
|
|||
<p>BME280 has a <strong>Chip ID Register, at Register ID <code>0xD0</code></strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Reading the Chip ID Register will give us the <strong>Chip ID value <code>0x60</code></strong> </p>
|
||||
<p>Reading the Chip ID Register will give us the <strong>Chip ID value <code>0x60</code></strong></p>
|
||||
<p>(<code>0x60</code> identifies the chip as BME280. For BMP280 the Chip ID is <code>0x58</code>)</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
@ -248,7 +248,7 @@
|
|||
<p><em>BL602 talks to BME280 over SPI, visualised by a Logic Analyser</em></p>
|
||||
<h1 id="initialise-spi-port"><a class="doc-anchor" href="#initialise-spi-port">§</a>4 Initialise SPI Port</h1>
|
||||
<p>Let’s dive into the code for our SPI Demo Firmware!</p>
|
||||
<p>Before we initialise the SPI Port, we define these constants and variables in <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_spi_demo/sdk_app_spi_demo/demo.c#L45-L100"><code>sdk_app_spi_demo/demo.c</code></a> </p>
|
||||
<p>Before we initialise the SPI Port, we define these constants and variables in <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_spi_demo/sdk_app_spi_demo/demo.c#L45-L100"><code>sdk_app_spi_demo/demo.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>/// Use SPI Port Number 0
|
||||
#define SPI_PORT 0
|
||||
|
||||
|
@ -269,7 +269,7 @@ static spi_dev_t spi;
|
|||
<p><code>spi</code> is the device instance of the SPI Port</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Our demo firmware initialises the SPI Port in the function <code>test_spi_init</code> from <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_spi_demo/sdk_app_spi_demo/demo.c#L45-L100"><code>sdk_app_spi_demo/demo.c</code></a> </p>
|
||||
<p>Our demo firmware initialises the SPI Port in the function <code>test_spi_init</code> from <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_spi_demo/sdk_app_spi_demo/demo.c#L45-L100"><code>sdk_app_spi_demo/demo.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>/// Init the SPI Port
|
||||
static void test_spi_init(char *buf, int len, int argc, char **argv) {
|
||||
// Configure the SPI Port
|
||||
|
@ -330,7 +330,7 @@ static void test_spi_init(char *buf, int len, int argc, char **argv) {
|
|||
<p><strong>SPI Clock Pin:</strong> We select <strong>Pin 3</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Unused SPI Chip Select Pin:</strong> We select <strong>Pin 2</strong>. </p>
|
||||
<p><strong>Unused SPI Chip Select Pin:</strong> We select <strong>Pin 2</strong>.</p>
|
||||
<p>We won’t connect this pin to BME280, but it must NOT be the same as the Actual Chip Select Pin (14)</p>
|
||||
<p>(More about Chip Select later)</p>
|
||||
</li>
|
||||
|
@ -662,7 +662,7 @@ spi_transfer : Transfer SPI data
|
|||
spi_result : Show SPI data received
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>First we <strong>initialise our SPI Port</strong>. </p>
|
||||
<p>First we <strong>initialise our SPI Port</strong>.</p>
|
||||
<p>Enter this command…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>spi_init
|
||||
</code></pre></div>
|
||||
|
@ -836,7 +836,7 @@ assert(rc == 0);
|
|||
</tbody></table>
|
||||
</div>
|
||||
<p>This works perfectly fine, though it contradicts the BL602 Reference Manual.</p>
|
||||
<p>Because of this bug, we shall refer to <strong>Pin 1 as Serial Data In</strong> <em>(formerly MISO)</em>, and <strong>Pin 4 as Serial Data Out</strong> <em>(formerly MOSI)</em>: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_spi_demo/sdk_app_spi_demo/demo.c#L45-L100"><code>sdk_app_spi_demo/demo.c</code></a> </p>
|
||||
<p>Because of this bug, we shall refer to <strong>Pin 1 as Serial Data In</strong> <em>(formerly MISO)</em>, and <strong>Pin 4 as Serial Data Out</strong> <em>(formerly MOSI)</em>: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_spi_demo/sdk_app_spi_demo/demo.c#L45-L100"><code>sdk_app_spi_demo/demo.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// Configure the SPI Port
|
||||
int rc = spi_init(
|
||||
...
|
||||
|
@ -847,7 +847,7 @@ int rc = spi_init(
|
|||
<p><a href="https://lupyuen.github.io/articles/pinedio#spi-pins-are-swapped"><strong>UPDATE:</strong> We may call GLB_Swap_SPI_0_MOSI_With_MISO to swap the two SPI Data Pins. (See this)</a></p>
|
||||
<h1 id="spi-phase-looks-sus"><a class="doc-anchor" href="#spi-phase-looks-sus">§</a>9 SPI Phase looks sus</h1>
|
||||
<p>Here’s another spooky problem: <strong>BL602 SPI Phase seems incorrect</strong>.</p>
|
||||
<p>Earlier we have configured BL602 for <strong>SPI Polarity 0 (CPOL), Phase 1 (CPHA)</strong>: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_spi_demo/sdk_app_spi_demo/demo.c#L45-L100"><code>sdk_app_spi_demo/demo.c</code></a> </p>
|
||||
<p>Earlier we have configured BL602 for <strong>SPI Polarity 0 (CPOL), Phase 1 (CPHA)</strong>: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_spi_demo/sdk_app_spi_demo/demo.c#L45-L100"><code>sdk_app_spi_demo/demo.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// Configure the SPI Port
|
||||
int rc = spi_init(
|
||||
...
|
||||
|
@ -879,7 +879,7 @@ int rc = spi_init(
|
|||
<p>To summarise the spooky mysteries we have observed on BL602 SPI…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>SPI Pins for <strong>Serial Data In</strong> and <strong>Serial Data Out</strong> seem to be flipped, when observed with a Logic Analyser. </p>
|
||||
<p>SPI Pins for <strong>Serial Data In</strong> and <strong>Serial Data Out</strong> seem to be flipped, when observed with a Logic Analyser.</p>
|
||||
<p>This contradicts the BL602 Reference Manual.</p>
|
||||
<p>To fix this, we <strong>flip the Serial Data In and Serial Data Out Pins</strong>.</p>
|
||||
</li>
|
||||
|
@ -907,7 +907,7 @@ int rc = spi_init(
|
|||
<p>We may port the BL602 SPI HAL to other operating systems by <strong>emulating these FreeRTOS functions for Event Groups</strong>…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><code>xEventGroupCreate</code>: Create an Event Group </p>
|
||||
<p><code>xEventGroupCreate</code>: Create an Event Group</p>
|
||||
<p>(Used by the DMA Interrupt Handlers to notify the Foreground Task)</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -1181,7 +1181,7 @@ READ: 0x60
|
|||
<p>We haven’t modified any logic in the SPI HAL. (Though we have added some debug code to understand how the HAL works)</p>
|
||||
<p>We added the function <code>spi_init</code> to initialise the SPI Port without using the AliOS Device Tree.</p>
|
||||
<h2 id="definitions"><a class="doc-anchor" href="#definitions">§</a>16.1 Definitions</h2>
|
||||
<p><code>HAL_SPI_DEBUG</code> is presently set to <code>1</code> to enable debug messages. </p>
|
||||
<p><code>HAL_SPI_DEBUG</code> is presently set to <code>1</code> to enable debug messages.</p>
|
||||
<p>For production we should change this to <code>0</code>.</p>
|
||||
<p>From <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_spi.c#L57-L58"><code>bl602_hal/hal_spi.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// TODO: Change to 0 for production to disable logging
|
||||
|
@ -1204,7 +1204,7 @@ READ: 0x60
|
|||
<p><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_spi.c#L524-L591"><code>vfs_spi_init_fullname</code></a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Note that there is only a <strong>single global instance of SPI Data.</strong> </p>
|
||||
<p>Note that there is only a <strong>single global instance of SPI Data.</strong></p>
|
||||
<p><strong><code>spi_init</code> shall only be called once by our firmware.</strong></p>
|
||||
<p>From <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_spi.c#L838-L886"><code>bl602_hal/hal_spi.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// Global single instance of SPI Data.
|
||||
|
@ -1627,7 +1627,7 @@ int spi_init(spi_dev_t *spi, uint8_t port,
|
|||
return -1;
|
||||
}
|
||||
</code></pre></div>
|
||||
<p><strong>For every chunk of SPI Transfer (max 2048 bytes):</strong> </p>
|
||||
<p><strong>For every chunk of SPI Transfer (max 2048 bytes):</strong></p>
|
||||
<p>We create the DMA Requests for each chunk of SPI Transmit and SPI Receive (max 2048 bytes)…</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code> for (i = 0; i < count; i++) {
|
||||
</code></pre></div>
|
||||
|
|
|
@ -419,7 +419,7 @@ static ssize_t spi_test_driver_read(
|
|||
#define SPI_TEST_DRIVER_SPI_MODE (SPIDEV_MODE0) /* SPI Mode 0: CPOL=0,CPHA=0 */
|
||||
#endif /* CONFIG_BL602_SPI0 */
|
||||
</code></pre></div>
|
||||
<p>BL602 uses <strong>SPI Mode 1</strong> (instead of Mode 0) because of an <strong>SPI Mode Quirk</strong> in BL602. </p>
|
||||
<p>BL602 uses <strong>SPI Mode 1</strong> (instead of Mode 0) because of an <strong>SPI Mode Quirk</strong> in BL602.</p>
|
||||
<p><a href="https://lupyuen.github.io/articles/spi2#appendix-spi-mode-quirk">(More about the SPI Mode Quirk)</a></p>
|
||||
<p><img src="https://lupyuen.github.io/images/spi2-newdriver4.png" alt="Register SPI Test Driver at startup" /></p>
|
||||
<p><a href="https://github.com/lupyuen/nuttx/blob/newdriver/boards/risc-v/bl602/bl602evb/src/bl602_bringup.c#L599-L617">(Source)</a></p>
|
||||
|
@ -728,7 +728,7 @@ int bytes_read = read(fd, rx_data, sizeof(rx_data));
|
|||
assert(bytes_read == sizeof(get_status));
|
||||
</code></pre></div>
|
||||
<p>(We’ll explain <strong>get_status</strong> in the next section)</p>
|
||||
<p>Finally we set our <strong>GPIO Output / Chip Select</strong> to High… </p>
|
||||
<p>Finally we set our <strong>GPIO Output / Chip Select</strong> to High…</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>/* Set SPI Chip Select to High */
|
||||
|
||||
ret = ioctl(cs, GPIOC_WRITE, 1);
|
||||
|
@ -919,7 +919,7 @@ SX1262 Register 8 is 0x80
|
|||
<p>(Yep multiple devices on the same SPI Bus)</p>
|
||||
<p>Let’s test NuttX with PineDio Stack BL604 and its <strong>onboard SX1262</strong>! Here are the innards…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/spi2-pinedio1.jpg" alt="Inside PineDio Stack BL604" /></p>
|
||||
<p>Before testing, remember to connect the <strong>LoRa Antenna</strong>… </p>
|
||||
<p>Before testing, remember to connect the <strong>LoRa Antenna</strong>…</p>
|
||||
<p>(So we don’t fry the SX1262 Transceiver as we charge up the Power Amplifier)</p>
|
||||
<p><img src="https://lupyuen.github.io/images/spi2-pinedio10a.jpg" alt="PineDio Stack BL604 with Antenna" /></p>
|
||||
<h2 id="pin-definitions"><a class="doc-anchor" href="#pin-definitions">§</a>9.1 Pin Definitions</h2>
|
||||
|
@ -973,7 +973,7 @@ SX1262 Register 8 is 0x80
|
|||
</li>
|
||||
<li>
|
||||
<p>Edit the <strong>Pin Definitions</strong> as shown above…</p>
|
||||
<p><a href="https://github.com/lupyuen/nuttx/blob/pinedio/boards/risc-v/bl602/bl602evb/include/board.h#L42-L95">boards/risc-v/bl602/bl602evb/include/board.h</a> </p>
|
||||
<p><a href="https://github.com/lupyuen/nuttx/blob/pinedio/boards/risc-v/bl602/bl602evb/include/board.h#L42-L95">boards/risc-v/bl602/bl602evb/include/board.h</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Build, flash and run the NuttX Firmware…</p>
|
||||
|
@ -1095,7 +1095,7 @@ SX1262 Register 8 is 0x80
|
|||
<div class="example-wrap"><pre class="language-bash"><code>make menuconfig
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p>Select <strong>“System Type”</strong> → <strong>“BL602 Peripheral Support”</strong> </p>
|
||||
<p>Select <strong>“System Type”</strong> → <strong>“BL602 Peripheral Support”</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Enable <strong>“DMA”</strong></p>
|
||||
|
@ -1235,7 +1235,7 @@ make menuconfig
|
|||
<p>Next we enable SPI logging for easier troubleshooting…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>In <strong>menuconfig</strong>, select <strong>“Build Setup”</strong> → <strong>“Debug Options”</strong> </p>
|
||||
<p>In <strong>menuconfig</strong>, select <strong>“Build Setup”</strong> → <strong>“Debug Options”</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the boxes for the following…</p>
|
||||
|
@ -1272,7 +1272,7 @@ SPI Informational Output
|
|||
<ol>
|
||||
<li>
|
||||
<p>Browse to the <strong>Board Folder</strong>…</p>
|
||||
<p><strong>For BL602:</strong> <a href="https://github.com/lupyuen/nuttx/blob/newdriver/boards/risc-v/bl602/bl602evb/src"><strong>nuttx/nuttx/boards/ risc-v/bl602/bl602evb</strong></a> </p>
|
||||
<p><strong>For BL602:</strong> <a href="https://github.com/lupyuen/nuttx/blob/newdriver/boards/risc-v/bl602/bl602evb/src"><strong>nuttx/nuttx/boards/ risc-v/bl602/bl602evb</strong></a></p>
|
||||
<p><strong>For ESP32:</strong> <a href="https://github.com/lupyuen/nuttx/blob/spi_test/boards/xtensa/esp32/esp32-devkitc/src"><strong>nuttx/nuttx/boards/ xtensa/esp32/esp32-devkitc</strong></a></p>
|
||||
<p>(Change “esp32-devkitc” to our ESP32 board)</p>
|
||||
</li>
|
||||
|
@ -1751,7 +1751,7 @@ static ssize_t spi_test_driver_write(
|
|||
<p><img src="https://lupyuen.github.io/images/spi2-interface5.png" alt="File Struct contains a Private Pointer to the SPI Driver (spi_driver_s)" /></p>
|
||||
<p><a href="https://github.com/lupyuen/nuttx/blob/master/drivers/spi/spi_driver.c#L112-L147">(Source)</a></p>
|
||||
<h2 id="spi-driver"><a class="doc-anchor" href="#spi-driver">§</a>16.4 SPI Driver</h2>
|
||||
<p>The <strong>SPI Driver</strong> (spi_driver_s) contains the <strong>SPI Device</strong> (spi_dev_s)… </p>
|
||||
<p>The <strong>SPI Driver</strong> (spi_driver_s) contains the <strong>SPI Device</strong> (spi_dev_s)…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/spi2-interface6.png" alt="SPI Driver (spi_driver_s) contains the SPI Device (spi_dev_s)" /></p>
|
||||
<p><a href="https://github.com/lupyuen/nuttx/blob/master/drivers/spi/spi_driver.c#L55-L65">(Source)</a></p>
|
||||
<p>Which is what we need for calling the <strong>SPI Interface</strong>!</p>
|
||||
|
|
|
@ -421,7 +421,7 @@ FAR struct lcd_dev_s *board_lcd_getdev(int devno) {
|
|||
<h1 id="render-pink-screen"><a class="doc-anchor" href="#render-pink-screen">§</a>7 Render Pink Screen</h1>
|
||||
<p>In a while we’ll boot NuttX to load the ST7789 Driver but…</p>
|
||||
<p><em>How will we know if the ST7789 Driver is really working?</em></p>
|
||||
<p>Here’s an idea… Let’s <strong>render a Pink Screen</strong> at startup! </p>
|
||||
<p>Here’s an idea… Let’s <strong>render a Pink Screen</strong> at startup!</p>
|
||||
<p>This is how we do it: <a href="https://github.com/lupyuen/nuttx/blob/st7789/drivers/lcd/st7789.c#L752-L777">st7789.c</a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// Called by board_lcd_getdev during NuttX startup
|
||||
// to init the ST7789 driver
|
||||
|
@ -710,7 +710,7 @@ Archive: v8.2.0.zip
|
|||
unzip: cannot find zipfile directory in one of v8.2.0.zip or
|
||||
v8.2.0.zip.zip, and cannot find v8.2.0.zip.ZIP, period.
|
||||
</code></pre></div>
|
||||
<p>It seems the NuttX Build needs to be fixed to support LVGL 8. </p>
|
||||
<p>It seems the NuttX Build needs to be fixed to support LVGL 8.</p>
|
||||
<p><em>LVGL 7.11.0 is the last version of LVGL 7. What happens if we switch to LVGL 7.11.0?</em></p>
|
||||
<p>NuttX Build fails when we switch to <strong>LVGL 7.11.0</strong>…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>nuttx/apps/graphics/lvgl/lv_conf.h:86:50: error: incompatible types when initializing type 'short unsigned int' using type 'lv_color_t' {aka 'union <anonymous>'}
|
||||
|
|
|
@ -304,10 +304,10 @@ lrwxrwxrwx 17 fitImage -> fitImage-5.15.107
|
|||
<h1 id="boot-nuttx-with-u-boot-bootloader"><a class="doc-anchor" href="#boot-nuttx-with-u-boot-bootloader">§</a>4 Boot NuttX with U-Boot Bootloader</h1>
|
||||
<p><em>When we port NuttX RTOS to Star64…</em></p>
|
||||
<p><em>Will NuttX boot with Armbian or Yocto settings?</em></p>
|
||||
<p>Armbian looks simpler than Yocto, since it uses a plain Kernel Image File <strong>/boot/Image</strong>. </p>
|
||||
<p>Armbian looks simpler than Yocto, since it uses a plain Kernel Image File <strong>/boot/Image</strong>.</p>
|
||||
<p>(Instead of Yocto’s complicated Flat Image Tree)</p>
|
||||
<p><a href="https://lupyuen.github.io/articles/release#nuttx-in-a-bootable-microsd">(<strong>UPDATE:</strong> We switched to <strong>Flat Image Tree</strong> for NuttX)</a></p>
|
||||
<p>Hence for NuttX we’ll adopt the Armbian Boot Settings, overwriting <strong>/boot/Image</strong> by our <a href="https://lupyuen.github.io/articles/riscv#boot-nuttx-on-64-bit-risc-v-qemu"><strong>NuttX Kernel Image</strong></a>. </p>
|
||||
<p>Hence for NuttX we’ll adopt the Armbian Boot Settings, overwriting <strong>/boot/Image</strong> by our <a href="https://lupyuen.github.io/articles/riscv#boot-nuttx-on-64-bit-risc-v-qemu"><strong>NuttX Kernel Image</strong></a>.</p>
|
||||
<p>And hopefully U-Boot Bootloader will <strong>boot NuttX on Star64</strong>! Assuming that we fix these…</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -481,7 +481,7 @@ j FUN_402010c8
|
|||
<div class="example-wrap"><pre class="language-text"><code>4D 5A 6F 10
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>code1</strong>: Executable code </p>
|
||||
<p><strong>code1</strong>: Executable code</p>
|
||||
<p>(4 bytes, offset <code>0x04</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>60 0C 01 00
|
||||
</code></pre></div></li>
|
||||
|
@ -491,12 +491,12 @@ j FUN_402010c8
|
|||
<div class="example-wrap"><pre class="language-text"><code>00 00 20 00 00 00 00 00
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>image_size</strong>: Effective Image size, little endian </p>
|
||||
<p><strong>image_size</strong>: Effective Image size, little endian</p>
|
||||
<p>(8 bytes, offset <code>0x10</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>00 C0 56 01 00 00 00 00
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>flags</strong>: Kernel flags, little endian </p>
|
||||
<p><strong>flags</strong>: Kernel flags, little endian</p>
|
||||
<p>(8 bytes, offset <code>0x18</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>00 00 00 00 00 00 00 00
|
||||
</code></pre></div></li>
|
||||
|
@ -516,12 +516,12 @@ j FUN_402010c8
|
|||
<div class="example-wrap"><pre class="language-text"><code>00 00 00 00 00 00 00 00
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>magic</strong>: Magic number, little endian, “RISCV\x00\x00\x00” </p>
|
||||
<p><strong>magic</strong>: Magic number, little endian, “RISCV\x00\x00\x00”</p>
|
||||
<p>(8 bytes, offset <code>0x30</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>52 49 53 43 56 00 00 00
|
||||
</code></pre></div></li>
|
||||
<li>
|
||||
<p><strong>magic2</strong>: Magic number 2, little endian, “RSC\x05” </p>
|
||||
<p><strong>magic2</strong>: Magic number 2, little endian, “RSC\x05”</p>
|
||||
<p>(4 bytes, offset <code>0x38</code>)</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>52 53 43 05
|
||||
</code></pre></div></li>
|
||||
|
|
|
@ -201,7 +201,7 @@
|
|||
<ul>
|
||||
<li><a href="https://lupyuen.github.io/articles/sx1262#appendix-create-a-nuttx-library"><strong>“Create a NuttX Library”</strong></a></li>
|
||||
</ul>
|
||||
<p>Then we replaced the “libsx1262” folder by a <strong>Git Submodule</strong> that contains our LoRa SX1262 code… </p>
|
||||
<p>Then we replaced the “libsx1262” folder by a <strong>Git Submodule</strong> that contains our LoRa SX1262 code…</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>cd nuttx/nuttx/libs
|
||||
rm -r libsx1262
|
||||
git rm -r libsx1262
|
||||
|
@ -285,7 +285,7 @@ git submodule add --branch nuttx https://github.com/lupyuen/lora-sx1262 libsx126
|
|||
<div class="example-wrap"><pre class="language-text"><code>boards/risc-v/bl602/bl602evb/include/board.h
|
||||
</code></pre></div>
|
||||
<p><strong>For ESP32:</strong> Connect SX1262 to <a href="https://lupyuen.github.io/articles/spi2#connect-sx1262">these pins</a></p>
|
||||
<p>Before testing, remember to connect the <strong>LoRa Antenna</strong>… </p>
|
||||
<p>Before testing, remember to connect the <strong>LoRa Antenna</strong>…</p>
|
||||
<p>(So we don’t fry the SX1262 Transceiver as we charge up the Power Amplifier)</p>
|
||||
<p><img src="https://lupyuen.github.io/images/spi2-pinedio10a.jpg" alt="PineDio Stack BL604 with Antenna" /></p>
|
||||
<p><em>What are these SX1262 pins: DIO1, BUSY and NRESET?</em></p>
|
||||
|
@ -565,7 +565,7 @@ Register 0x0a = 0x01
|
|||
</code></pre></div>
|
||||
<p><a href="https://www.thethingsnetwork.org/docs/lorawan/spreading-factors/">(More about LoRa Parameters)</a></p>
|
||||
<p>During testing, these should <strong>match the LoRa Parameters</strong> used by the LoRa Transmitter / Receiver.</p>
|
||||
<p>In a while we’ll use <a href="https://lupyuen.github.io/articles/wisblock"><strong>RAKwireless WisBlock</strong></a> (pic below) to test our SX1262 Library. </p>
|
||||
<p>In a while we’ll use <a href="https://lupyuen.github.io/articles/wisblock"><strong>RAKwireless WisBlock</strong></a> (pic below) to test our SX1262 Library.</p>
|
||||
<p>Below are the <strong>LoRa Transmitter and Receiver</strong> programs (Arduino) that we’ll run on WisBlock…</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -3140,7 +3140,7 @@ void SX126xSetSleep( SleepParams_t sleepConfig ) {
|
|||
<p><a href="https://github.com/lupyuen/lora-sx1262/blob/lorawan/src/sx126x-nuttx.c#L222-L235">(<strong>SX126xWriteCommand</strong> is defined here)</a></p>
|
||||
<p><a href="https://github.com/lupyuen/lora-sx1262/blob/lorawan/src/sx126x-nuttx.c#L140-L161">(<strong>SX126xSetOperatingMode</strong> is defined here)</a></p>
|
||||
<h1 id="appendix-gpio-pin-type-issue"><a class="doc-anchor" href="#appendix-gpio-pin-type-issue">§</a>16 Appendix: GPIO Pin Type Issue</h1>
|
||||
<p>When we switch a GPIO Interrupt Pin Type to Trigger On Rising Edge, it crashes with an Assertion Failure… </p>
|
||||
<p>When we switch a GPIO Interrupt Pin Type to Trigger On Rising Edge, it crashes with an Assertion Failure…</p>
|
||||
|
||||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code>nsh> gpio -t <span class="number">8 </span>-w <span class="number">1 </span>/dev/gpio2
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
<p>But we hit some <strong>Missing POSIX Functions</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>So we built minimal <strong>File Input and Output</strong> </p>
|
||||
<p>So we built minimal <strong>File Input and Output</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Hacked up a simple workaround for <strong>fprintf and friends</strong></p>
|
||||
|
@ -646,7 +646,7 @@ to copy the RISC-V ELF <code>a.out</code> from TCC WebAssembly to NuttX Emulator
|
|||
<p>But <strong>POSIX Functions</strong> are missing in WebAssembly</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>So we did the bare minimum for <strong>File Input and Output</strong> </p>
|
||||
<p>So we did the bare minimum for <strong>File Input and Output</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>And cooked up the simplest workaround for <strong>fprintf and friends</strong></p>
|
||||
|
|
|
@ -780,7 +780,7 @@ No UEFI binary known at 0x40200000
|
|||
<p>Apply overlay to the DT</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>fdt</strong> move <fdt> <newaddr> <length> </p>
|
||||
<p><strong>fdt</strong> move <fdt> <newaddr> <length></p>
|
||||
<p>Copy the fdt to <addr> and make it active</p>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
<li><a href="#nuttx-with-virtio-and-openamp">9 NuttX with VirtIO and OpenAMP</a><ul></ul></li>
|
||||
<li><a href="#whats-next">10 What’s Next</a><ul></ul></li>
|
||||
<li><a href="#appendix-build-nuttx-for-tinyemu">11 Appendix: Build NuttX for TinyEMU</a><ul></ul></li></ul></nav><p>📝 <em>14 Jan 2024</em></p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-title.png" alt="Apache NuttX RTOS in a Web Browser… With TinyEMU and VirtIO" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-title.png" alt="Apache NuttX RTOS in a Web Browser… With TinyEMU and VirtIO" /></p>
|
||||
<p><a href="https://lupyuen.github.io/nuttx-tinyemu"><em>(Live Demo of NuttX on TinyEMU)</em></a></p>
|
||||
<p><a href="https://youtu.be/KYrdwzIsgeQ"><em>(Watch on YouTube)</em></a></p>
|
||||
<p><a href="https://nuttx.apache.org/docs/latest/index.html"><strong>Apache NuttX RTOS</strong></a> is a tiny operating system for <a href="https://lupyuen.github.io/articles/riscv"><strong>64-bit RISC-V Machines</strong></a> and many other platforms. (Arm, x64, ESP32, …)</p>
|
||||
|
@ -139,7 +139,7 @@ temu https://bellard.org/jslinux/buildroot-riscv64.cfg
|
|||
<p><em>What about TinyEMU for the Web Browser?</em></p>
|
||||
<p>No Worries! Everything that runs in <strong>Command Line</strong> TinyEMU… Will also run in <strong>Web Browser</strong> TinyEMU!</p>
|
||||
<p>We tweak NuttX for TinyEMU…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-linux.png" alt="Booting Linux in TinyEMU" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-linux.png" alt="Booting Linux in TinyEMU" /></p>
|
||||
<h1 id="risc-v-addresses-for-tinyemu"><a class="doc-anchor" href="#risc-v-addresses-for-tinyemu">§</a>2 RISC-V Addresses for TinyEMU</h1>
|
||||
<p><em>Will TinyEMU boot our Operating System?</em></p>
|
||||
<p>TinyEMU is hardcoded to run at these <strong>RISC-V Addresses</strong>: <a href="https://github.com/fernandotcl/TinyEMU/blob/master/riscv_machine.c#L66-L82">riscv_machine.c</a></p>
|
||||
|
@ -205,7 +205,7 @@ $ temu nuttx.cfg
|
|||
<p><em>Huh! We’re booting NuttX QEMU on TinyEMU?</em></p>
|
||||
<p>Exactly… <strong>Nothing will appear</strong> in TinyEMU!</p>
|
||||
<p>To watch NuttX run, we need HTIF Console…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-htif.jpg" alt="TinyEMU with HTIF Console" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-htif.jpg" alt="TinyEMU with HTIF Console" /></p>
|
||||
<h1 id="print-to-htif-console"><a class="doc-anchor" href="#print-to-htif-console">§</a>4 Print to HTIF Console</h1>
|
||||
<p><em>Printing to the TinyEMU Console: What will we need?</em></p>
|
||||
<p>TinyEMU supports <a href="https://docs.cartesi.io/machine/target/architecture/#htif"><strong>Berkeley Host-Target Interface (HTIF)</strong></a> for Console Output. (Pic above)</p>
|
||||
|
@ -269,7 +269,7 @@ static void htif_handle_cmd(RISCVMachine *s) {
|
|||
</code></pre></div>
|
||||
<p>We test it in our NuttX Boot Code…</p>
|
||||
<blockquote>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-boot.png" alt="Booting NuttX in TinyEMU" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-boot.png" alt="Booting NuttX in TinyEMU" /></p>
|
||||
</blockquote>
|
||||
<h1 id="print-in-risc-v-assembly"><a class="doc-anchor" href="#print-in-risc-v-assembly">§</a>5 Print in RISC-V Assembly</h1>
|
||||
<p><em>We’re checking if NuttX is alive on TinyEMU…</em></p>
|
||||
|
@ -316,7 +316,7 @@ $ temu nuttx.cfg
|
|||
123
|
||||
</code></pre></div>
|
||||
<p>To see more goodies, we patch the NuttX UART Driver…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-uart.jpg" alt="NuttX 16550 UART Driver patched for TinyEMU HTIF Console" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-uart.jpg" alt="NuttX 16550 UART Driver patched for TinyEMU HTIF Console" /></p>
|
||||
<h1 id="uart-driver-for-tinyemu"><a class="doc-anchor" href="#uart-driver-for-tinyemu">§</a>6 UART Driver for TinyEMU</h1>
|
||||
<p><em>NuttX on TinyEMU has been awfully quiet…</em></p>
|
||||
<p><em>Can we fix the UART Driver so that NuttX can print things?</em></p>
|
||||
|
@ -366,7 +366,7 @@ nx_start: CPU0: Beginning Idle Loop
|
|||
</code></pre></div>
|
||||
<p><a href="https://gist.github.com/lupyuen/8805f8f21dfae237bc06dfbda210628b#file-nuttx-tinyemu-log-L58-L118">(See the <strong>Complete Log</strong>)</a></p>
|
||||
<p>We promote NuttX from Command Line to Web Browser…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-wasm.png" alt="NuttX booting in a Web Browser" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-wasm.png" alt="NuttX booting in a Web Browser" /></p>
|
||||
<p><a href="https://lupyuen.github.io/nuttx-tinyemu"><em>NuttX booting in a Web Browser</em></a></p>
|
||||
<h1 id="boot-nuttx-in-web-browser"><a class="doc-anchor" href="#boot-nuttx-in-web-browser">§</a>7 Boot NuttX in Web Browser</h1>
|
||||
<p><em>Will NuttX run in the Web Browser?</em></p>
|
||||
|
@ -412,7 +412,7 @@ simple-http-server nuttx-tinyemu/docs
|
|||
<p>And NuttX appears in our Web Browser! (Pic above)</p>
|
||||
<p><em>But something’s missing: Where’s the Console Input?</em></p>
|
||||
<p>To do Console Input, we need VirtIO Console…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-virtio.jpg" alt="TinyEMU with VirtIO Console" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-virtio.jpg" alt="TinyEMU with VirtIO Console" /></p>
|
||||
<h1 id="virtio-console"><a class="doc-anchor" href="#virtio-console">§</a>8 VirtIO Console</h1>
|
||||
<p><em>We need Console Input for NuttX Shell…</em></p>
|
||||
<p><em>Can’t we do it with TinyEMU’s HTIF Console?</em></p>
|
||||
|
@ -513,7 +513,7 @@ virtio_mmio_init_device:
|
|||
</ul>
|
||||
<p>Next we create a VirtIO Queue and send some data…</p>
|
||||
<p><a href="https://github.com/lupyuen/nuttx-tinyemu#inside-the-virtio-driver-for-nuttx">(<strong>virtio_register_mmio_device</strong> is explained here)</a></p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-virtio2.jpg" alt="VirtIO Queues" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-virtio2.jpg" alt="VirtIO Queues" /></p>
|
||||
<h2 id="create-the-virtio-queue"><a class="doc-anchor" href="#create-the-virtio-queue">§</a>8.2 Create the VirtIO Queue</h2>
|
||||
<p><em>NuttX VirtIO + OpenAMP are talking OK to TinyEMU. What next?</em></p>
|
||||
<p>To send data to VirtIO Console, we need a <strong>VirtIO Queue</strong> (pic above): <a href="https://github.com/lupyuen2/wip-nuttx/blob/tinyemu/drivers/virtio/virtio-mmio.c#L869-L895">virtio-mmio.c</a></p>
|
||||
|
@ -551,7 +551,7 @@ virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
|
|||
<p>Now we have 2 VirtIO Queues: <strong>Transmit and Receive</strong>. Let’s message them…</p>
|
||||
<p><a href="https://github.com/OpenAMP/open-amp/blob/main/lib/include/openamp/virtio.h#L346-L366">(<strong>virtio_set_status</strong> comes from OpenAMP)</a></p>
|
||||
<p><a href="https://github.com/OpenAMP/open-amp/blob/main/lib/virtio/virtio.c#L96-L142">(<strong>virtio_create_virtqueues</strong> too)</a></p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-virtio3.jpg" alt="Send data to VirtIO Queue" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-virtio3.jpg" alt="Send data to VirtIO Queue" /></p>
|
||||
<h2 id="send-the-virtio-message"><a class="doc-anchor" href="#send-the-virtio-message">§</a>8.3 Send the VirtIO Message</h2>
|
||||
<p>To print something, we write to the <strong>Transmit Queue</strong> (pic above): <a href="https://github.com/lupyuen2/wip-nuttx/blob/tinyemu/drivers/virtio/virtio-mmio.c#L895-L926">virtio-mmio.c</a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// Send data to VirtIO Device
|
||||
|
@ -578,9 +578,9 @@ virtqueue_add_buffer(
|
|||
virtqueue_kick(vq);
|
||||
</code></pre></div>
|
||||
<p>And we’re done! One final test for today…</p>
|
||||
<p><a href="https://github.com/OpenAMP/open-amp/blob/main/lib/virtio/virtqueue.c#L83C1-L138">(<strong>virtqueue_add_buffer</strong> comes from OpenAMP)</a> </p>
|
||||
<p><a href="https://github.com/OpenAMP/open-amp/blob/main/lib/virtio/virtqueue.c#L83C1-L138">(<strong>virtqueue_add_buffer</strong> comes from OpenAMP)</a></p>
|
||||
<p><a href="https://github.com/OpenAMP/open-amp/blob/main/lib/virtio/virtqueue.c#L321-L336">(<strong>virtqueue_kick</strong> too)</a></p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-title.png" alt="Apache NuttX RTOS in a Web Browser… With TinyEMU and VirtIO" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-title.png" alt="Apache NuttX RTOS in a Web Browser… With TinyEMU and VirtIO" /></p>
|
||||
<p><a href="https://lupyuen.github.io/nuttx-tinyemu"><em>Live Demo of NuttX on TinyEMU</em></a></p>
|
||||
<h1 id="nuttx-with-virtio-and-openamp"><a class="doc-anchor" href="#nuttx-with-virtio-and-openamp">§</a>9 NuttX with VirtIO and OpenAMP</h1>
|
||||
<p><em>NuttX sends data to TinyEMU Console over VirtIO and OpenAMP…</em></p>
|
||||
|
@ -640,7 +640,7 @@ Hello VirtIO from NuttX!
|
|||
</li>
|
||||
</ol>
|
||||
</span>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-nsh2.png" alt="Live Demo of NuttX on TinyEMU" /> </p>
|
||||
<p><img src="https://lupyuen.github.io/images/tinyemu-nsh2.png" alt="Live Demo of NuttX on TinyEMU" /></p>
|
||||
<p><a href="https://lupyuen.github.io/nuttx-tinyemu"><em>Live Demo of NuttX on TinyEMU</em></a></p>
|
||||
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>10 What’s Next</h1>
|
||||
<p>Today we’ve built some cool things that might <strong>Emulate NuttX Gadgets</strong> in the Web Browser (like Ox64 BL808 SBC) or light up a <strong>Real-Time NuttX Dashboard</strong>…</p>
|
||||
|
|
|
@ -274,11 +274,11 @@ target_write_slow:
|
|||
<p>We dig around the <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf"><strong>BL808 Reference Manual</strong></a> (pic above) and we discover these <strong>UART Registers</strong>…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong><code>0x3000_2088</code></strong> is <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf"><strong>uart_fifo_wdata</strong> (Page 428)</a> </p>
|
||||
<p><strong><code>0x3000_2088</code></strong> is <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf"><strong>uart_fifo_wdata</strong> (Page 428)</a></p>
|
||||
<p>We write to this UART Register to <strong>print a character</strong> to UART Output.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong><code>0x3000_2084</code></strong> is <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf"><strong>uart_fifo_config_1</strong> (Page 427)</a> </p>
|
||||
<p><strong><code>0x3000_2084</code></strong> is <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf"><strong>uart_fifo_config_1</strong> (Page 427)</a></p>
|
||||
<p>We read this UART Register to check if <strong>UART Transmit is ready</strong> (for more output).</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -313,7 +313,7 @@ void bl808_send(struct uart_dev_s *dev, int ch) {
|
|||
<h2 id="emulate-the-uart-status"><a class="doc-anchor" href="#emulate-the-uart-status">§</a>5.1 Emulate the UART Status</h2>
|
||||
<p>Earlier we said…</p>
|
||||
<blockquote>
|
||||
<p><strong><code>0x3000_2084</code></strong> is <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf"><strong>uart_fifo_config_1</strong> (Page 427)</a> </p>
|
||||
<p><strong><code>0x3000_2084</code></strong> is <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf"><strong>uart_fifo_config_1</strong> (Page 427)</a></p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p><em>We read this UART Register to check if <strong>UART Transmit is ready</strong> (for more output)</em></p>
|
||||
|
@ -362,7 +362,7 @@ void bl808_send(struct uart_dev_s *dev, int ch) {
|
|||
<h2 id="emulate-the-uart-output"><a class="doc-anchor" href="#emulate-the-uart-output">§</a>5.2 Emulate the UART Output</h2>
|
||||
<p>Earlier we saw…</p>
|
||||
<blockquote>
|
||||
<p><strong><code>0x3000_2088</code></strong> is <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf"><strong>uart_fifo_wdata</strong> (Page 428)</a> </p>
|
||||
<p><strong><code>0x3000_2088</code></strong> is <a href="https://github.com/bouffalolab/bl_docs/blob/main/BL808_RM/en/BL808_RM_en_1.3.pdf"><strong>uart_fifo_wdata</strong> (Page 428)</a></p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p><em>We write to this UART Register to <strong>print a character</strong> to UART Output</em></p>
|
||||
|
@ -479,7 +479,7 @@ nuttx/syscall/proxies/PROXY_sched_getparam.c:8
|
|||
</code></pre></div>
|
||||
<p><a href="https://gist.github.com/lupyuen/69b832f89efe2dc31e6da40a19b78354">(See the <strong>Source Code</strong>)</a></p>
|
||||
<p><em>What’s this ECALL?</em></p>
|
||||
<p>At <code>0x19C6</code> we see the <a href="https://lupyuen.github.io/articles/app#nuttx-app-calls-nuttx-kernel"><strong>RISC-V ECALL Instruction</strong></a> that will jump from our <strong>NuttX App</strong> (RISC-V User Mode) to <strong>NuttX Kernel</strong> (RISC-V Supervisor Mode). </p>
|
||||
<p>At <code>0x19C6</code> we see the <a href="https://lupyuen.github.io/articles/app#nuttx-app-calls-nuttx-kernel"><strong>RISC-V ECALL Instruction</strong></a> that will jump from our <strong>NuttX App</strong> (RISC-V User Mode) to <strong>NuttX Kernel</strong> (RISC-V Supervisor Mode).</p>
|
||||
<p>Hence our NuttX Shell is making a <a href="https://lupyuen.github.io/articles/app#nuttx-app-calls-nuttx-kernel"><strong>System Call</strong></a> to NuttX Kernel. (Pic above)</p>
|
||||
<p>Why did it fail? We’ll come back to this, first we surf the web…</p>
|
||||
<p><a href="https://github.com/lupyuen/ox64-tinyemu/blob/main/riscv_cpu.c#L1142-L1147">(We quit if <strong>MCAUSE is 2</strong>, otherwise we loop forever)</a></p>
|
||||
|
|
|
@ -832,7 +832,7 @@ invert x/y:1, x:0, y:1
|
|||
<p>That’s right… Our screen is <strong>rotated sideways</strong>!</p>
|
||||
<p>So be careful when mapping the Touch Coordinates to the rendered screen.</p>
|
||||
<p><em>Can we fix this?</em></p>
|
||||
<p>We can rotate the display in the <strong>ST7789 Display Driver</strong>. </p>
|
||||
<p>We can rotate the display in the <strong>ST7789 Display Driver</strong>.</p>
|
||||
<p>(Portrait Mode vs Landscape Mode)</p>
|
||||
<p>But first we need to agree <strong>which way is “up”</strong>…</p>
|
||||
<ul>
|
||||
|
|
|
@ -754,7 +754,7 @@ DEBUGASSERT(ret == OK);
|
|||
<p>Which will…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Initialise the Struct</strong> for Touch Panel </p>
|
||||
<p><strong>Initialise the Struct</strong> for Touch Panel</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Register the Touch Panel Driver</strong> with NuttX</p>
|
||||
|
|
|
@ -151,7 +151,7 @@
|
|||
<p>Fill in the fields and click <strong>“<code>Create Gateway</code>”</strong></p>
|
||||
<h2 id="configure-gateway"><a class="doc-anchor" href="#configure-gateway">§</a>1.1 Configure Gateway</h2>
|
||||
<p>Take Note: This is missing from the RAKwireless docs…</p>
|
||||
<p>The Things Network has been upgraded recently and there’s no longer the option for <strong>“Legacy Packet Forwarder”</strong>. </p>
|
||||
<p>The Things Network has been upgraded recently and there’s no longer the option for <strong>“Legacy Packet Forwarder”</strong>.</p>
|
||||
<p>Instead we set the <strong>Server Address</strong> like so…</p>
|
||||
<ol>
|
||||
<li>
|
||||
|
@ -239,7 +239,7 @@
|
|||
<p>Click <strong>“<code>Create Application</code>”</strong></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>In the Application, click <strong><code>"End Devices"</code></strong> (in the left bar)</p>
|
||||
<p>In the Application, click <strong><code>"End Devices"</code></strong> (in the left bar)</p>
|
||||
<p>Click <strong>“<code>Add End Device</code>”</strong></p>
|
||||
<p>Click <strong>“<code>Manually</code>”</strong></p>
|
||||
<p><img src="https://lupyuen.github.io/images/ttn-device3.png" alt="Register End Device" /></p>
|
||||
|
@ -384,7 +384,7 @@
|
|||
<p>We should see <strong>“Successfully Processed Join Request”</strong>…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/ttn-join2.png" alt="Application Live Data" /></p>
|
||||
<p>Yep our PineDio Stack has successfully joined The Things Network!</p>
|
||||
<p>If we see <strong>“Message Integrity Code” Errors</strong>, check the Device Settings. The <strong>LoRaWAN Version</strong> should be <strong>1.0.2 Rev B</strong>. </p>
|
||||
<p>If we see <strong>“Message Integrity Code” Errors</strong>, check the Device Settings. The <strong>LoRaWAN Version</strong> should be <strong>1.0.2 Rev B</strong>.</p>
|
||||
<h1 id="send-data-to-the-things-network"><a class="doc-anchor" href="#send-data-to-the-things-network">§</a>5 Send Data to The Things Network</h1>
|
||||
<p>Finally we’re ready to send data from PineDio Stack to The Things Network!</p>
|
||||
<p>At the PineDio Stack Command Prompt, enter these commands…</p>
|
||||
|
@ -412,7 +412,7 @@
|
|||
<p>And we’re done!</p>
|
||||
<h2 id="doing-better"><a class="doc-anchor" href="#doing-better">§</a>5.1 Doing Better</h2>
|
||||
<p><em>Sending 5 bytes of data to the network doesn’t sound particularly exciting?</em></p>
|
||||
<p>Yep we’re just getting started! </p>
|
||||
<p>Yep we’re just getting started!</p>
|
||||
<p>In future articles we shall explore The Thing Network’s <strong>Cloud Integration</strong> features for processing our sensor data: MQTT, Webhooks, Storage, Downlinks, Payload Formatters, …</p>
|
||||
<p>We shall visualise our sensor data with <strong>MQTT, Prometheus and Grafana</strong>…</p>
|
||||
<ul>
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
<p>BL602’s UART HAL is packaged as two levels…</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p><strong>Low Level HAL <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_uart.c"><code>bl_uart.c</code></a></strong>: This runs on BL602 Bare Metal. </p>
|
||||
<p><strong>Low Level HAL <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_uart.c"><code>bl_uart.c</code></a></strong>: This runs on BL602 Bare Metal.</p>
|
||||
<p>The Low Level HAL manipulates the BL602 UART Registers directly to perform UART functions.</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -112,12 +112,12 @@
|
|||
<p>Today we shall use the <strong>Low Level UART HAL <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_uart.c"><code>bl_uart.c</code></a></strong> because…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>The Low Level UART HAL is <strong>simpler to understand</strong>. </p>
|
||||
<p>The Low Level UART HAL is <strong>simpler to understand</strong>.</p>
|
||||
<p>We’ll learn all about the BL602 UART Hardware by calling the Low Level HAL Functions.</p>
|
||||
<p>(No Device Tree, no AliOS)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The Low Level UART HAL <strong>works on all Embedded Operating Systems</strong>. </p>
|
||||
<p>The Low Level UART HAL <strong>works on all Embedded Operating Systems</strong>.</p>
|
||||
<p>(Not just FreeRTOS)</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -165,7 +165,7 @@ static void display_image(char *buf, int len, int argc, char **argv) {
|
|||
<ul>
|
||||
<li>
|
||||
<p><strong>UART Port:</strong> We select <strong>UART Port 1</strong>.</p>
|
||||
<p>BL602 has 2 UART Ports: 0 and 1. </p>
|
||||
<p>BL602 has 2 UART Ports: 0 and 1.</p>
|
||||
<p>UART Port 0 is reserved for the Command-Line Interface, so we should always use UART Port 1.</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -248,7 +248,7 @@ void send_begin() {
|
|||
</code></pre></div>
|
||||
<p>(Looks very similar to the First Step)</p>
|
||||
<p>And we’re done with the Start Transfer Handshake!</p>
|
||||
<p>Note that we’re polling the UART Port, which is OK because we’re mostly transmitting data, and receiving little data. </p>
|
||||
<p>Note that we’re polling the UART Port, which is OK because we’re mostly transmitting data, and receiving little data.</p>
|
||||
<p>If we’re receiving lots of data through polling, we might lose some data. For such cases, we should use UART Interrupts or DMA.</p>
|
||||
<p>(The E-Ink Display code in this article was ported from Arduino to BL602. <a href="https://github.com/Seeed-Studio/Grove_Triple_Color_E-lnk_2.13/blob/master/examples/Eink_factory_code_213/Eink_factory_code_213.ino">See this</a>)</p>
|
||||
<h1 id="display-image"><a class="doc-anchor" href="#display-image">§</a>5 Display Image</h1>
|
||||
|
@ -321,7 +321,7 @@ static void send_data(const uint8_t* data, uint32_t data_len) {
|
|||
}
|
||||
}
|
||||
</code></pre></div>
|
||||
<p><code>send_data</code> calls <code>bl_uart_data_send</code> (from BL602 Low Level UART HAL) to transmit the data to the UART Port, one byte at a time. </p>
|
||||
<p><code>send_data</code> calls <code>bl_uart_data_send</code> (from BL602 Low Level UART HAL) to transmit the data to the UART Port, one byte at a time.</p>
|
||||
<p>(We’ve seen this earlier during the handshake)</p>
|
||||
<p>That’s all for the UART code that talks to the E-Ink Display!</p>
|
||||
<h1 id="build-and-run-the-firmware"><a class="doc-anchor" href="#build-and-run-the-firmware">§</a>6 Build and Run the Firmware</h1>
|
||||
|
|
|
@ -525,7 +525,7 @@ SECTION_FUNC(text, up_lowputc)
|
|||
early_uart_transmit x15, w0 /* Transmit to UART */
|
||||
ret
|
||||
</code></pre></div>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_lowputc.S#L81-L91">(Source)</a> </p>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_lowputc.S#L81-L91">(Source)</a></p>
|
||||
<p>And calls <a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_lowputc.S#L87-L94"><strong><code>early_uart_transmit</code></strong></a> Macro to transmit a character…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>/* UART transmit character
|
||||
* xb: register which contains the UART base address
|
||||
|
@ -1123,7 +1123,7 @@ strb w0, [x15, #0x0C]
|
|||
<h2 id="uart-driver"><a class="doc-anchor" href="#uart-driver">§</a>19.3 UART Driver</h2>
|
||||
<p>We have implemented the <strong>UART Driver</strong> for PinePhone’s Allwinner A64 UART Port…</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c">arch/arm64/src/a64/a64_serial.c</a> </li>
|
||||
<li><a href="https://github.com/apache/nuttx/blob/master/arch/arm64/src/a64/a64_serial.c">arch/arm64/src/a64/a64_serial.c</a></li>
|
||||
</ul>
|
||||
<p>Check out the details in this article…</p>
|
||||
<ul>
|
||||
|
|
|
@ -274,7 +274,7 @@ hook_block:
|
|||
</li>
|
||||
</ul>
|
||||
<p>(We’ll come back to <strong>ELF_CONTEXT</strong>)</p>
|
||||
<p>This is how we map an <strong>Arm64 Address to Source Filename</strong>:
|
||||
<p>This is how we map an <strong>Arm64 Address to Source Filename</strong>:
|
||||
<a href="https://github.com/lupyuen/pinephone-emulator/blob/55e4366b1876ed39b1389e8673b262082bfb7074/src/main.rs#L195-L221">main.rs</a></p>
|
||||
|
||||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Map the Arm64 Code Address to the Source Filename,
|
||||
|
@ -538,7 +538,7 @@ click arm64_head href "https://github.com/apache/nuttx/blob/master/arch/arm
|
|||
<p><a href="https://lupyuen.github.io/articles/interrupt#arm64-vector-table-is-wrong">(More about <strong>VBAR EL1</strong>)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/0f20888a0ececc5dc7419d57a01ac508ac3ace5b/arch/arm64/src/common/arm64_boot.c#L140-L147"><strong>CPACR EL1</strong></a>: Set the Arm64 Architectural Feature Access Control Register </p>
|
||||
<p><a href="https://github.com/apache/nuttx/blob/0f20888a0ececc5dc7419d57a01ac508ac3ace5b/arch/arm64/src/common/arm64_boot.c#L140-L147"><strong>CPACR EL1</strong></a>: Set the Arm64 Architectural Feature Access Control Register</p>
|
||||
<p><a href="https://developer.arm.com/documentation/ddi0595/2021-03/AArch64-Registers/CPACR-EL1--Architectural-Feature-Access-Control-Register">(More about <strong>CPACR EL1</strong>)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -215,7 +215,7 @@
|
|||
<p><strong>5V</strong> and <strong>GND</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p><a href="https://en.wikipedia.org/wiki/Differential_signalling">(Due to <strong>Differential Signalling</strong>)</a> </p>
|
||||
<p><a href="https://en.wikipedia.org/wiki/Differential_signalling">(Due to <strong>Differential Signalling</strong>)</a></p>
|
||||
<p><em>What about USB0-DP and USB0-DM?</em></p>
|
||||
<p><strong>Port USB0</strong> of the Allwinner A64 SoC is exposed as the <strong>External USB Port</strong> on PinePhone.</p>
|
||||
<p>(Port USB0 supports <a href="https://en.wikipedia.org/wiki/USB_On-The-Go"><strong>USB OTG</strong></a>, Port USB1 doesn’t)</p>
|
||||
|
|
|
@ -411,7 +411,7 @@ a64_printreg:
|
|||
<p><a href="https://lupyuen.github.io/articles/usb3#usb-enhanced-host-controller-interface">(See this)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong><code>01C1</code> <code>B010</code></strong> is the <strong>USB Command Register USBCMD</strong> </p>
|
||||
<p><strong><code>01C1</code> <code>B010</code></strong> is the <strong>USB Command Register USBCMD</strong></p>
|
||||
<p><a href="https://www.intel.sg/content/www/xa/en/products/docs/io/universal-serial-bus/ehci-specification-for-usb.html">(EHCI Spec, Page 18)</a></p>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
<p>So that Embedded Developers may <strong>preview their Blockly uLisp Apps in the Web Browser</strong>?</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Today we shall build a simple simulator for the <a href="https://lupyuen.github.io/articles/pinecone"><strong>BL602 RISC-V + WiFi SoC</strong></a> that will run Blockly uLisp Apps in a Web Browser. </p>
|
||||
<p>Today we shall build a simple simulator for the <a href="https://lupyuen.github.io/articles/pinecone"><strong>BL602 RISC-V + WiFi SoC</strong></a> that will run Blockly uLisp Apps in a Web Browser.</p>
|
||||
<p>(No BL602 hardware needed!)</p>
|
||||
<ul>
|
||||
<li><a href="https://youtu.be/Ag2CERd1OzQ"><strong>Watch the BL602 Simulator demo on YouTube</strong></a></li>
|
||||
|
@ -114,11 +114,11 @@
|
|||
-s "EXTRA_EXPORTED_RUNTIME_METHODS=[ 'cwrap', 'allocate', 'intArrayFromString', 'UTF8ToString' ]"
|
||||
</code></pre></div>
|
||||
<p>(See the Makefile <a href="https://github.com/lupyuen/ulisp-bl602/blob/wasm/wasm.mk"><strong><code>wasm.mk</code></strong></a>. More about <a href="https://github.com/lupyuen/ulisp-bl602/blob/wasm/wasm/wasm.c"><strong><code>wasm.c</code></strong></a> in a while)</p>
|
||||
<p>C programs that call the <strong>Standard C Libraries</strong> should build OK with Emscripten: <code>printf</code>, <code><stdio.h></code>, <code><stdlib.h></code>, <code><string.h></code>, … </p>
|
||||
<p>C programs that call the <strong>Standard C Libraries</strong> should build OK with Emscripten: <code>printf</code>, <code><stdio.h></code>, <code><stdlib.h></code>, <code><string.h></code>, …</p>
|
||||
<p>The Emscripten Compiler generates 3 output files…</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="https://github.com/lupyuen/ulisp-bl602/blob/wasm/docs/ulisp.wasm"><strong><code>ulisp.wasm</code></strong></a>: Contains the <strong>WebAssembly Code</strong> generated for our C program. </p>
|
||||
<p><a href="https://github.com/lupyuen/ulisp-bl602/blob/wasm/docs/ulisp.wasm"><strong><code>ulisp.wasm</code></strong></a>: Contains the <strong>WebAssembly Code</strong> generated for our C program.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="https://github.com/lupyuen/ulisp-bl602/blob/wasm/docs/ulisp.js"><strong><code>ulisp.js</code></strong></a>: JavaScript module that <strong>loads the WebAssembly Code</strong> into a Web Browser and runs it</p>
|
||||
|
@ -132,7 +132,7 @@
|
|||
<p><em>What are the <code>EXPORTED_FUNCTIONS</code>?</em></p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>-s "EXPORTED_FUNCTIONS=[ '_setup_ulisp', '_execute_ulisp', '_clear_simulation_events', '_get_simulation_events' ]"
|
||||
</code></pre></div>
|
||||
<p>These are the C functions from our uLisp Interpreter <a href="https://github.com/lupyuen/ulisp-bl602/blob/wasm/src/ulisp.c#L5312-L5384"><code>ulisp.c</code></a> that will be <strong>exported to JavaScript</strong>. </p>
|
||||
<p>These are the C functions from our uLisp Interpreter <a href="https://github.com/lupyuen/ulisp-bl602/blob/wasm/src/ulisp.c#L5312-L5384"><code>ulisp.c</code></a> that will be <strong>exported to JavaScript</strong>.</p>
|
||||
<p>Our uLisp Interpreter won’t do anything meaningful in a Web Browser unless these 2 functions are called…</p>
|
||||
<ol>
|
||||
<li>
|
||||
|
@ -459,7 +459,7 @@ function runScript() {
|
|||
<p>We now have a static variable <strong><code>simulation_events</code></strong> that will store the Simulation Events</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>We use a <strong><code>try...catch...finally</code></strong> block to deallocate the WebAssembly memory. </p>
|
||||
<p>We use a <strong><code>try...catch...finally</code></strong> block to deallocate the WebAssembly memory.</p>
|
||||
<p>(In case we hit errors in the JSON parsing)</p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -536,7 +536,7 @@ function simulateEvents() {
|
|||
</li>
|
||||
<li>
|
||||
<p><strong>Event Parameters</strong>: Like…</p>
|
||||
<p><code>{ "pin": 11, "value": 1 }</code></p>
|
||||
<p><code>{ "pin": 11, "value": 1 }</code></p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Next it <strong>handles each Event Type</strong>…</p>
|
||||
|
@ -961,7 +961,7 @@ Code.runJS = function() {
|
|||
<p><em>Dragging-and-dropping uLisp programs for microcontrollers… And running them WITHOUT a microcontroller!</em></p>
|
||||
<h1 id="can-we-simulate-any-bl602-firmware"><a class="doc-anchor" href="#can-we-simulate-any-bl602-firmware">§</a>12 Can We Simulate Any BL602 Firmware?</h1>
|
||||
<p><em>Our BL602 Simulator works OK for simulating a uLisp Program. Will it work for simulating BL602 Firmware coded in C?</em></p>
|
||||
<p>Our BL602 Simulator <strong>might work for BL602 Firmware coded in C</strong>! </p>
|
||||
<p>Our BL602 Simulator <strong>might work for BL602 Firmware coded in C</strong>!</p>
|
||||
<p>Let’s look at this <strong>BL602 Blinky Firmware</strong> in C: <a href="https://lupyuen.github.io/articles/rust#bl602-blinky-in-c"><code>sdk_app_blinky/demo.c</code></a></p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>/// Blink the BL602 LED
|
||||
void blinky(char *buf, int len, int argc, char **argv) {
|
||||
|
@ -1007,10 +1007,10 @@ uint32_t time_ms_to_ticks32(uint32_t millisec) { return millisec; }
|
|||
</code></pre></div>
|
||||
<p>These <strong>C Function Signatures are 100% identical</strong> to the BL602 Functions from the BL602 IoT SDK: <code>bl_gpio_output_set</code>, <code>bl_gpio_enable_output</code>, …</p>
|
||||
<p>So yep, the above <strong>BL602 Firmware Code will compile to WebAssembly</strong>!</p>
|
||||
<p>Similar to a uLisp Program, the BL602 Firmware Code (running in WebAssebly) will generate a <strong>JSON Stream of Simulation Events</strong>. </p>
|
||||
<p>Similar to a uLisp Program, the BL602 Firmware Code (running in WebAssebly) will generate a <strong>JSON Stream of Simulation Events</strong>.</p>
|
||||
<p>(Which we’ll feed to our <strong>BL602 Simulator</strong> in JavaScript)</p>
|
||||
<p><em>But can we simulate ALL functions from the BL602 IoT SDK: GPIO, I2C, SPI, ADC, DAC, LVGL, LoRa, …?</em></p>
|
||||
<p>This needs work of course. </p>
|
||||
<p>This needs work of course.</p>
|
||||
<p>To simulate any uLisp Program or BL602 Firmware we need to <strong>code the necessary Simulation Functions in JavaScript</strong> (like <code>gpio_output_set</code> and <code>time_delay</code>) for GPIO, I2C, SPI, ADC, DAC, LVGL, LoRa, …</p>
|
||||
<p>(Sounds like an interesting challenge!)</p>
|
||||
<h1 id="why-simulate-a-stream-of-events"><a class="doc-anchor" href="#why-simulate-a-stream-of-events">§</a>13 Why Simulate A Stream Of Events?</h1>
|
||||
|
|
|
@ -382,7 +382,7 @@ int bl_cfg80211_connect(struct bl_hw *bl_hw, struct cfg80211_connect_params *sme
|
|||
<p><img src="https://lupyuen.github.io/images/wifi-connect5.png" alt="bl_send_sm_connect_req" /></p>
|
||||
<h2 id="send-request-to-lmac"><a class="doc-anchor" href="#send-request-to-lmac">§</a>2.3 Send request to LMAC</h2>
|
||||
<p><em>What is LMAC?</em></p>
|
||||
<p><strong>Lower Medium Access Control (LMAC)</strong> is the firmware that runs <strong>inside the BL602 WiFi Radio Hardware</strong> and executes the WiFi Radio functions. </p>
|
||||
<p><strong>Lower Medium Access Control (LMAC)</strong> is the firmware that runs <strong>inside the BL602 WiFi Radio Hardware</strong> and executes the WiFi Radio functions.</p>
|
||||
<p>(Like sending and receiving WiFi Packets)</p>
|
||||
<p>To connect to a WiFi Access Point, we <strong>pass the Connection Parameters to LMAC</strong> by calling <strong><code>bl_send_sm_connect_req</code></strong>, defined in <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/bl_msg_tx.c#L722-L804"><code>bl_msg_tx.c</code></a> …</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>// Forward the Connection Parameters to the LMAC
|
||||
|
@ -760,7 +760,7 @@ LAB_230059f6:
|
|||
_DAT_44b08198 = ptVar9;
|
||||
</code></pre></div>
|
||||
<p><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_wifidrv/bl60x_wifi_driver/reg_access.h">(They don’t appear in this list either)</a></p>
|
||||
<p>The function performs some <strong>Assertion Checks</strong>. </p>
|
||||
<p>The function performs some <strong>Assertion Checks</strong>.</p>
|
||||
<p>The Assertion Failure Messages may be helpful for deciphering the decompiled code…</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code> // Assertion Checks
|
||||
line = 0x23c;
|
||||
|
@ -790,7 +790,7 @@ LAB_230059f6:
|
|||
</code></pre></div>
|
||||
<p><em>Is the original source code for <code>txl_payload_handle_backup</code> really so long?</em></p>
|
||||
<p>Likely not. The C Compiler optimises the firmware code by inlining some functions.</p>
|
||||
<p>When we decompile the firmware, the inlined code appears embedded inside the calling functions. </p>
|
||||
<p>When we decompile the firmware, the inlined code appears embedded inside the calling functions.</p>
|
||||
<p>(That’s why we see so much repetition in the decompiled code)</p>
|
||||
<p>Let’s talk about RivieraWaves…</p>
|
||||
<p><img src="https://lupyuen.github.io/images/wifi-schedule3.png" alt="RivieraWaves in AliOS" /></p>
|
||||
|
@ -1123,7 +1123,7 @@ grep --line-number \
|
|||
<p><a href="https://github.com/search?q=phy_init+phy_hw_set_channel&type=code">GitHub Code Search for <code>phy_init</code> and <code>phy_hw_set_channel</code></a></p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Remember to check GitHub Search when doing any Reverse Engineering! 👍 </p>
|
||||
<p>Remember to check GitHub Search when doing any Reverse Engineering! 👍</p>
|
||||
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>11 What’s Next</h1>
|
||||
<p>This has been a thrilling journey… Many Thanks to the contributors of the <a href="https://github.com/pine64/bl602-re"><strong>Pine64 BL602 Reverse Engineering Project</strong></a> for inspiring this article!</p>
|
||||
<p>Today we’ve done some Reverse Engineering for the purpose of <strong>Education</strong>… Just to understand how BL602 sends and receives WiFi Packets.</p>
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
</li>
|
||||
</ol>
|
||||
<p>Today we shall install <a href="https://docs.rakwireless.com/Product-Categories/WisBlock/Quickstart/"><strong>RAKwireless WisBlock</strong></a> to check the packets transmitted by our LoRa Sensor.</p>
|
||||
<p>We’ll be testing WisBlock with a LoRa Sensor built with the <strong>PineCone BL602 RISC-V Board</strong>… </p>
|
||||
<p>We’ll be testing WisBlock with a LoRa Sensor built with the <strong>PineCone BL602 RISC-V Board</strong>…</p>
|
||||
<ul>
|
||||
<li><a href="https://lupyuen.github.io/articles/lora"><strong>“Connect PineCone BL602 to LoRa Transceiver”</strong></a></li>
|
||||
</ul>
|
||||
|
@ -587,11 +587,11 @@ Data=50 49 4E 47 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14
|
|||
<p>How is this possible when there’s <strong>No Line Of Sight</strong> between the LoRa Transmitter and the LoRa Receiver?</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>LoRa Packets can get <strong>reflected on building surfaces</strong>… And the reflected packets will be received OK. </p>
|
||||
<p>LoRa Packets can get <strong>reflected on building surfaces</strong>… And the reflected packets will be received OK.</p>
|
||||
<p>(Assuming little signal distortion)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>LoRa Packets can <strong>penetrate light vegetation</strong>. </p>
|
||||
<p>LoRa Packets can <strong>penetrate light vegetation</strong>.</p>
|
||||
<p>(Like our paper box)</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
</ol>
|
||||
<p>(The two connectors are slightly different, so we won’t connect the wrong antenna)</p>
|
||||
<p>The GPS Antenna will be used when we connect WisGate to The Things Network (the worldwide free-access LoRaWAN network).</p>
|
||||
<p>WisGate D4H is shipped with the open-source <strong>ChirpStack LoRaWAN stack</strong>, preinstalled in the microSD card. </p>
|
||||
<p>WisGate D4H is shipped with the open-source <strong>ChirpStack LoRaWAN stack</strong>, preinstalled in the microSD card.</p>
|
||||
<p>(Yep please don’t peel off the sticky tape and insert your own microSD card)</p>
|
||||
<p><img src="https://lupyuen.github.io/images/wisgate-hw2.jpg" alt="microSD Slot on WisGate D4H" /></p>
|
||||
<p><em>microSD Slot on WisGate D4H</em></p>
|
||||
|
@ -170,7 +170,7 @@
|
|||
<p>To download the source code, enter this at the command prompt…</p>
|
||||
<div class="example-wrap"><pre class="language-bash"><code>git clone --recursive https://github.com/lupyuen/wisblock-lorawan
|
||||
</code></pre></div>
|
||||
<p>Note that the program requires the <a href="https://github.com/beegee-tokyo/SX126x-Arduino"><strong><code>SX126x-Arduino</code></strong></a> Library version <strong>2.0.0 or later</strong>. </p>
|
||||
<p>Note that the program requires the <a href="https://github.com/beegee-tokyo/SX126x-Arduino"><strong><code>SX126x-Arduino</code></strong></a> Library version <strong>2.0.0 or later</strong>.</p>
|
||||
<p>With PlatformIO, we set the <code>SX126x-Arduino</code> version in <a href="https://github.com/lupyuen/wisblock-lorawan/blob/master/platformio.ini"><code>platformio.ini</code></a> like so…</p>
|
||||
<div class="example-wrap"><pre class="language-text"><code>lib_deps = beegee-tokyo/SX126x-Arduino@^2.0.0
|
||||
</code></pre></div>
|
||||
|
@ -296,7 +296,7 @@ void lorawan_has_joined_handler(void) {
|
|||
// Request for a different LoRaWAN Device Class, if necessary
|
||||
lmh_error_status ret = lmh_class_request(g_CurrentClass);
|
||||
</code></pre></div>
|
||||
<p>Here we switch to the desired <strong>LoRaWAN Device Class</strong> (A, B or C) if necessary. </p>
|
||||
<p>Here we switch to the desired <strong>LoRaWAN Device Class</strong> (A, B or C) if necessary.</p>
|
||||
<p><strong>Class A</strong> is the most basic Device Class (for simple LoRaWAN sensors). <strong>Classes B and C</strong> are more sophisticated.</p>
|
||||
<p>We have configured our LoRaWAN Client for <strong>Class A</strong>…</p>
|
||||
<div class="example-wrap"><pre class="language-c"><code>DeviceClass_t g_CurrentClass = CLASS_A;
|
||||
|
@ -439,7 +439,7 @@ OnRadioRxDone => FRAME_TYPE_JOIN_ACCEPT
|
|||
OTAA Mode, Network Joined!
|
||||
</code></pre></div>
|
||||
<p>The LoRaWAN Spec says we don’t open the Second Receive Window if we receive a packet in the First Receive Window.</p>
|
||||
<p>So… We’re good! </p>
|
||||
<p>So… We’re good!</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>20 seconds later we <strong>transmit our first data packet</strong> (“<code>Hello!</code>”)…</p>
|
||||
|
|
|
@ -189,7 +189,7 @@ pub export fn hello_zig_main(
|
|||
<p><a href="https://ziglang.org/documentation/master/#Sentinel-Terminated-Pointers">(Here’s the full declaration)</a></p>
|
||||
<p><img src="https://lupyuen.github.io/images/zig-config1a.png" alt="Enable Zig App in NuttX" /></p>
|
||||
<h1 id="enable-zig-app"><a class="doc-anchor" href="#enable-zig-app">§</a>2 Enable Zig App</h1>
|
||||
<p>We’re ready to <strong>build our Zig App</strong> in NuttX! </p>
|
||||
<p>We’re ready to <strong>build our Zig App</strong> in NuttX!</p>
|
||||
<p>Follow these steps to <strong>download and configure NuttX</strong> for BL602…</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -381,7 +381,7 @@ $ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
|
|||
Flags: 0x1, RVC, soft-float ABI
|
||||
Tag_RISCV_arch: "rv32i2p0_m2p0_a2p0_f2p0_c2p0"
|
||||
</code></pre></div>
|
||||
<p>The last line translates to <strong>RV32IMACF</strong>, which means that the RISC-V Instruction Set is indeed targeted for <strong>Hardware Floating-Point</strong>. </p>
|
||||
<p>The last line translates to <strong>RV32IMACF</strong>, which means that the RISC-V Instruction Set is indeed targeted for <strong>Hardware Floating-Point</strong>.</p>
|
||||
<p>We’re only editing the <strong>ELF Header</strong>, because it didn’t seem to reflect the correct ABI for the Object File.</p>
|
||||
<p><em>Is there a proper fix for this?</em></p>
|
||||
<p>In future the Zig Compiler might allow us to specify the <strong>Floating-Point ABI</strong> as the target…</p>
|
||||
|
|
Loading…
Reference in a new issue