"Hello World!" with the Longan Nano
Blink with a Wio Lite RISC-V with WiFi->

In a recently arrived package from Seeed Studio there was a Sipeed Longan Nano. I have been eyeing some Sipeed products for a little while, but the Nano was more or less an impulse purchase as explained in a previous post. This is a completely new architecture for me, so this post is about just getting a "Hello World" program running on the device.

Table of Contents

  1. Longan Nano
  2. RISC-V ?
  3. PlatformIO Only
  4. Compiling Blink
  5. Uploading With a Serial Link
  6. Uploading With USB (DFU)
  7. Adding Serial Output
  8. Hello World on the LCD
  9. Downloading the Example Blink Projects
  10. References

Longan Nano Board toc

The Longan Nano is a development board based on the GD32VF103CBT6 micro-controller with a RISC-V 32-bit core by GigaDevice. The board purchased came with a 0.96 inch display and a translucent acrylic case which holds the display securely, allows for the manipulation of the two on board push buttons and provides access to 0.1 inch pin headers along the long edges of the board. The headers are supplied with the board but must be soldered. The little screen is a 160x80 pixel colour liquid-crystal display using IPS (in-plane switching) screen technology. The 108 MHz, 32 bit, GD32VF103CBT6 is no slouch with impressive capabilities.

The following image showing the pin out of the device is from an SVG drawing by Kali Prasad.

While not obvious, there is a mini TF card slot for micro-SD cards on the back side of the board.

RISC-V ? toc

The RISC ISA (love or hate it, you have to live with the alphabet soup of electronic technology; RISC stands for Reduced Instruction Set Computer and ISA for Instruction Set Architecture) uses simplified instructions requiring fewer cycles to execute in opposition to the CISC (Complex Instruction Set Computer) ISA where the richer set of complex instructions can take many cycles to complete. As one would expect, the demarcation between CISC and RISC architectures is not very clear. MIPS, SPARC, PowerPC and ARM are well-known families of processors that implement the RISC architecture, while the venerable Intel 8080 and its current x64 successors are in the CISC camp.

RISC-V, pronounced risk-5 and not as in "Hôtel Georges Vé" (see Les Bonbons de Jacques Brel, that's an obscure reference that would mean something only to those that remember French songs of the 1960s), is a relatively recent open standard ISA started at the University of California, Berkeley in 2010. The fact that it is open distinguishes it from the ARM ISA. The GigaDevice introduced its GD32V system in 2019 just as the RISC-V ISA was frozen to allow hardware and software development to proceed. I find it a bit confusing, but it looks like the MCU core, named Bumblebee, was designed by or in cooperation with Nuclei System Technology another Chinese entity. Looking at press releases, it seems that Chinese companies such as GigaDevice are, if not embracing RISC-V, at least hedging their bets by testing the technology because of the absence of royalties whereas ARM is definitely not free. Furthermore, being open source RISC-V cannot be subjected to export controls by the US government which seems to have happened in the case of ARM. However, things are not so simple: ARM "is (partly) Chinese", there are two competing RISC-V consortium in China, the MIPS OPEN Initiative making the MIPS ISA freely available is being rolled out, and ARM is reacting. All this is fascinating, but not germane to my purpose.

PlatformIO Only toc

For the last three or four weeks, I have been using the Visual Studio Code editor for managing the source files for this site, and using the PlatformIO extension for embedded device development. This was either the second or third attempt to use PlatformIO and so far it is working very well and I feel more productive as a consequence. I was therefore quite pleased to see that the Sipeed Longan Nano among the supported boards. Furthermore, the Sipeed Longan Documentation has a Getting Started/Getting to Blinky article in which PlatformIO is used. By the way the introduction to the current post was shamelessly stolen from the Sipeed introduction.

It will be assumed that PlatformIO (PIO for short) is installed and working properly in Visual Studio Code on the desktop, but if not, here are the installation instructions.

I also tried installing the board in the Arduino IDE following the sparse instructions at Sipeed/Longuino. That means that I added the following


in the list of Additional Boards Manager URLs in the IDE Preferences. Then when attempting to install Longuino, I got the following error:

I played around the RISC-V fork of openocd found in the GV32D package installed in PlatformIO and managed to get a different error message when installing Longduino a second time. I gave up at this point and decided to use the Platformio environment which supports the Arduino framework.

Compiling Blink toc

One could install the GD32V platform as instructed in PIO configuration. I just started a new project, not because I make it a habit to not read instructions, but because I did not find the documentation until after creating the first Blink sketch.

  1. If PIO Home is not open, click on the PIO icon on the left edge,
  2. Selection QUICK ACCESS/PIO Home/Open in the PLATFORMIO LIST in the left pane.
  3. Click on + New Project in the PIO Home page.

A Project Wizard window will pop up.

  1. Set the project name. I chose nanoblink.
  2. Select the board. I entered "nano" in the box and then scrolled down the list of boards with "nano" somewhere in their name.
  3. Select Longan Nano.

  1. Change the framework from GigaDevice GD32V SDK to Arduino
  2. Click on the Finish button.

PIO will obtain the platform toolchain and set up the project as a directory with assorted subdirectories all shown in the EXPLORER pane on the left.

  1. Click on the src directory and then on the main.cpp file name in the EXPLORER pane.
  2. The typical bare source template will be displayed in the main.cpp tab. Replace the bare setup() and loop() functions with those in the standard Arduino Blink sketch. Do not remove the #include <Arduino.h> directive at the top of the file; it is necessary in PlatformIO when using the Arduino platform, it is implicit in the Arduino IDE. Note that I shortened the LED on time by an order of magnitude for a reason to be explored below.
  3. Click on the PlatformIO: Build icon. It is the check mark on the tool bar along the bottom of the application window.

The image says it all, the compilation was a success. Now there remained the task of uploading the binary to the Nano.

Uploading With a Serial Link toc

Above is a photograph, borrowed from a Mikrozone Server video showing the RESET and BOOT0 buttons to the left of the USB-C connector and the 8 pin header at the other end. The top 4 pins provide a serial connection that can be used to upload the compiled firmware with a 3.3 volt USB to TTY converter and to power the Nano. The pin out is shown below.

It's best to power off the USB to TTY converter and disconnect the Nano from all devices. Connect the ground of both the converter and the Nano together and connect the 3.3 volt line of the converter to the 3.3 volt pin of the Nano. My USB to TTY converter has a jumper to select either 5 or 3.3 volts and I set it to 3.3 volts. Make sure your converter will output 3.3 volt signals otherwise the Nano will be damaged. Finally, connect the converter Tx signal to the RX0 pin of the Nano and the converter Rx signal to the TX0 pin of the Nano.

When the USB converter is connected to the desktop or portable computer, the factory installed firmware, which flashes different coloured LED will probably start up. The board has to be reset and restarted in download mode. The sequence of button presses to do that is simple and not time critical.

  1. Press and hold down the BOOT0 button.
  2. Press and release the RESET button. The flashing LEDs will go off.
  3. Release the BOOT0 button.

If PlatformIO is running on a Linux system, then it will help to install PlatformIO UDEV rules as instructed in 99-platform-udev.rules in the PIO Troubleshooting FAQ.

I think serial is the default upload protocol, but it will not hurt to confirm it. If PIO cannot find the correct serial port, then it should be specified in the configuration file. It was not necessary to do so on my system. After all this preparation, it is now possible to upload the compiled firmware. Click on the PlatformIO: Upload icon. It is the right pointing arrow on the tool bar along the bottom of the application window. Here is the result.

The upload was successful and a red LED, partly hidden under the LCD display started to blink. However the on and off times showed that the I/O pin controlling the LED is active LOW. In other words, the pin has to be set to 0 volt to turn on the LED. I rewrote the Blink sketch to reflect this and added a for loop to quickly blink the LED before turning it off for a longer delay. By changing the number of short consecutive on-off cycles, it will be easier to ascertain that a new version has been uploaded to the device.

/* blink Turns an LED on and off for multiple short periods and then off for a longer period. This action is continuously repeated. Based on the example Blink.ino sketch in Arduino by Scott Fitzgerald, Arturo Guadalupi and Colby Newman March 29, 2020 Michel Deslierres <https://sigmdel.ca/michel> This example code is in the public domain. */ #include <Arduino.h>        #define CYCLES 2           // 2 yields a heartbeat effect #define SHORT_DELAY  100   // 1/10 second #define LONG_DELAY  1000   // 1 second // the setup function runs once when you press reset or power the board void setup() {   // initialize digital pin LED_BUILTIN as an output.  pinMode(LED_BUILTIN, OUTPUT); } // the loop function runs over and over again forever void loop() {  for (int i=0; i<CYCLES; i++) {    digitalWrite(LED_BUILTIN, LOW);    // turn the red LED on (it is active when the pin is LOW)    delay(SHORT_DELAY);                // wait for a short on period    digitalWrite(LED_BUILTIN, HIGH);   // turn the red LED off (by setting the active LOW pin HIGH)    delay(SHORT_DELAY);                // wait for a short off period  }      digitalWrite(LED_BUILTIN, HIGH);     // turn the LED off  delay(LONG_DELAY);                   // wait for a long off period }

From now, uploading modified versions of this firmware is a two-step procedure: (1) reset the Nano in download mode and (2) press on the upload icon in PIO which will recompile the firmware automatically.

Uploading With USB in Platformio (DFU) toc

It is possible to power the Longan Nano and upload sketches over the USB port just as done with ESP boards such as the nodeMCU or Lolin/Wemos D1 mini. I was keen to try that since it is marginally easier than with a USB-serial converter as shown above.

Before connecting the USB cable, remove the USB-serial cable or at least disconnect the 3.3 volt. This might be wrong, but I assume that it's not a good idea to have two power sources to the device. Then connect the USB-C cable to the desktop and Nano and change the upload protocol from serial to udf in the platformio.ini configuration file.

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:sipeed-longan-nano] platform = gd32v board = sipeed-longan-nano framework = arduino ;upload_protocol = serial ;upload_port = /dev/ttyUSB0 upload_protocol = dfu

DFU (Device Firmware Upgrade) is a mechanism for upgrading the firmware of USB devices. As far as I know, dfu-util is the only way to support this mechanism on a desktop running Linux. But then, what do I know? I had to look up DFU yesterday!. In any case, PIO installs and uses this utility in the GD32V package to upload and download firmware with the dfu protocol.

michel@hp:~$ ls -l .platformio/packages/tool-gd32vflash/dfu* total 256 -rwxr-xr-x 1 michel michel 47736 sep 11 2019 dfu-prefix -rwxr-xr-x 1 michel michel 47720 sep 11 2019 dfu-suffix -rwxr-xr-x 1 michel michel 163680 sep 11 2019 dfu-util

Restart the Nano in download mode.

  1. Press and hold down the BOOT0 button.
  2. Press and release the RESET button to reset the Nano.
  3. Release the BOOT0 button.

Before uploading the firmware with this protocol, it is not a bad idea to check that the Nano now shows up as an anonymous USB device with ID 28e9:0189 and to check that there is a UDEV rule for the device. You can even check that the installed dfu utility recognizes the Nano.

michel@hp:~$ lsusb ... Bus 003 Device 071: ID 28e9:0189 ... michel@hp:~$ sudo cat /etc/udev/rules.d/99-platformio-udev.rules | grep 28e9 ATTRS{idVendor}=="28e9", ATTRS{idProduct}=="0189", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" michel@hp:~$ .platformio/packages/tool-gd32vflash/dfu-util -l dfu-util 0.9 ... Found DFU: [28e9:0189] ver=0100, devnum=71, cfg=1, intf=0, path="3-10", alt=0, name="@Internal Flash /0x08000000/128*001Kg", serial="3CBJ"

Change the LED flashing parameters to something quite different to those currently on the Nano, say

#define CYCLES 6 // was 2 #define SHORT_DELAY 50 // was 10 #define LONG_DELAY 3000 // was 1000

so that it will be quite obvious if the firmware upload worked or not. Try to upload the modified binary with a click on the PlatformIO: Upload icon on the tool bar along the bottom of the application window. If it worked, then congratulations. Chances are that the sizes and dates of the dfu-* files in the GD32V package are different from those shown above and PIO has been updated. By all means skip the rest of this section.

I followed the instructions by dr-br to downoad and compile the latest version of dfu-util package.

michel@hp:~$ git clone https://git.code.sf.net/p/dfu-util/dfu-util ... michel@hp:~$ cd dfu-utils michel@hp:~/dfu-util$ ./autogen.sh autoreconf: Entering directory `.' ... autoreconf: Leaving directory `.' michel@hp:~/dfu-util$ ./configure --prefix=$HOME/.local checking for a BSD-compatible install... /usr/bin/install -c ... ... config.status: creating doc/Makefile config.status: creating config.h config.status: executing depfiles commands michel@hp:~/dfu-util$ make -j install Making install in src ... make[1] : on quitte le répertoire « /home/michel/dfu-util » michel@hp:~/dfu-util$ cd ~

If the configure command given had been ./configure --prefix=$HOME as suggested by dr-br, the utilities would have ended up in a ~/bin which is not in the search path on my system. I checked that the utility was installed in my .local/bin directory and then uploaded the firmware with it.

michel@hp:~$ ls -l .local/bin/dfu-* -rwxr-xr-x 1 michel michel 47944 mar 31 17:52 ../.local/bin/dfu-prefix -rwxr-xr-x 1 michel michel 47936 mar 31 17:52 ../.local/bin/dfu-suffix different sizes and dates! -rwxr-xr-x 1 michel michel 175680 mar 31 17:52 ../.local/bin/dfu-util michel@hp:~$ which dfu-util /home/michel/.local/bin/dfu-util michel@hp:~$ dfu-util -d 28e9:0189 -a 0 --dfuse-address 0x08000000:leave -D Documents/PlatformIO/Projects/nanoblink/.pio/build/sipeed-longan-nano/firmware.bin ... Opening DFU capable USB device... ID 28e9:0189 Run-time device DFU version 011a Claiming USB DFU Interface... ... Found GD32VF103, which reports a bad page size and count for its internal memory. Fixed layout based on part number: page size 1024, count 128. Downloading to address = 0x08000000, size = 8268 Download [=========================] 100% 8268 bytes Download done. File downloaded successfully dfu-util: Error during download get_status

Despite that ominous bad page size message and the final reported error the firmware did get onto the Nano. I copied the three dfu-* files in /.local/bin/ on top of the files in ~/.platformio/packages/tool-gd32vflash/, changed the LED blinking frequency again and uploaded.

Download done. File downloaded successfully dfu-util: Error during download get_status *** [upload] Error 74 ============================ [FAILED] Took 2.03 seconds ============================ The terminal process terminated with exit code: 1

PIO gave an even scarier FAILED message, but the new firmware was uploaded to the Nano.

Adding Serial Output toc

What is a sketch without some serial output? It is not too difficult to do, but the usual Serial class used in Arduino is not available. Instead, USART0 will be initialized explicitly and then the _put_char() function used by printf will be modified to send the output to the UART. The source for this is a Sipeed documentation page entitled UART. The document is not available in English, so thank you Google translate.

#include <Arduino.h>        #define CYCLES         2  // 2 yields a heartbeat effect #define SHORT_DELAY  100  // 1/10 second #define LONG_DELAY  1000  // 1 second static void init_uart0(void) {  // enable GPIO clock  rcu_periph_clock_enable(RCU_GPIOA);  // enable USART0 clock  rcu_periph_clock_enable(RCU_USART0);    // configure USART0  usart_deinit(USART0);  usart_baudrate_set(USART0, 115200U);  usart_word_length_set(USART0, USART_WL_8BIT);  usart_stop_bit_set(USART0, USART_STB_1BIT);  usart_parity_config(USART0, USART_PM_NONE);  usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);  usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);  usart_receive_config(USART0, USART_RECEIVE_ENABLE);  usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);  usart_enable(USART0); } // retarget the C library printf function to USART0 extern "C" int _put_char(int ch) // used by printf {    usart_data_transmit(USART0, (uint8_t) ch );    while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET){    }    return ch; } // the setup function runs once when you press reset or power the board void setup() {  // initialize digital pin LED_BUILTIN as an output.  pinMode(LED_BUILTIN, OUTPUT);  init_uart0();  printf("\nSipeed Nano Blink\n");  printf("=================\n");  printf("Platform: Arduino\n");  printf("LED_BUILTIN: %d\n", LED_BUILTIN);  printf("\n"); } // the loop function runs over and over again forever void loop() {  for (int i=0; i<CYCLES; i++) {    digitalWrite(LED_BUILTIN, LOW);    // turn the red LED on (it is active when the pin is LOW)    printf("ON ");              // update terminal    delay(SHORT_DELAY);                // wait for a short on period    digitalWrite(LED_BUILTIN, HIGH);   // turn the red LED off (by setting the active LOW pin HIGH)    delay(SHORT_DELAY);                // wait for a short off period  }      digitalWrite(LED_BUILTIN, HIGH);     // turn the LED off  printf("OFF\n");               // update terminal  delay(LONG_DELAY);                   // wait for a long off period }

To see the output it will be necessary to connect a USB to TTY converter to the Nano. This is described above. It remains possible to upload the firmware with the udf protocol, but in that case do not connect the 3.3 volt line of the USB-TTY converter to the Nano header. Set the baud for the communication software (miniterm) to 115200 to match the rate set for the USART in the firmware. This is done in the platformio.ini configuration file.

[env:sipeed-longan-nano] platform = gd32v board = sipeed-longan-nano framework = arduino monitor_speed = 115200 ;upload_port = /dev/ttyUSB0 ;upload_protocol = serial upload_protocol = dfu

The output will be sent to the serial port just as soon as the firmware is uploaded. If the terminal is not visible then click on the PlatformIO: Serial Monitor icon, which looks like a 2-prong plug facing up on the bottom tool bar. To view the beginning of the output, press on the RESET button on the Nano.

... ON ON OFF ON ON OFF ON ON OFF Pressed Nano reset button Sipeed Nano Blink ================= Platform: Arduino LED_BUILTIN: 45 ON ON OFF ON ON OFF ON ON OFF ON ON OFF ...

I could have printed "Hello World" and that would have fulfilled the initial boast of this post. Of course, it would be better to do it on the LCD.

Hello World on the LCD toc

It only makes sense to send any text output to the liquid crystal display supplied with the Nano. Based on the work of others, notably the micros example in longan-nano-experiments by Stephan Mühlstrasser, here is what I came up with.

#include <Arduino.h>        extern "C" { #include "lcd/lcd.h" } #define CYCLES         4  // 2 yields a heartbeat effect #define SHORT_DELAY  100  // 1/10 second #define LONG_DELAY  1000  // 1 second // the setup function runs once when you press reset or power the board void setup() {  char buf[64];  // initialize digital pin LED_BUILTIN as an output.  pinMode(LED_BUILTIN, OUTPUT);    // initialize LCD and clear the display to black  Lcd_Init();  LCD_Clear(BLACK);  // write "Hello World!" at the top of the LCD, blue on white  BACK_COLOR = WHITE;  sprintf(buf, "Hello World!");  LCD_ShowString(0, 0, (u8 const *) buf, BLUE);  BACK_COLOR = BLACK;  sprintf(buf, "Count:");  LCD_ShowString(0, 16, (u8 const *) buf, GBLUE); } int count = 0; // the loop function runs over and over again forever void loop() {  char buf[64];    sprintf(buf, "%d", count);  LCD_ShowString(7*8, 16, (u8 const *) buf, YELLOW);  count++;  sprintf(buf, "                   ");  LCD_ShowString(0, 32, (u8 const *) buf, BLACK);  delay(SHORT_DELAY);  sprintf(buf, "ON");  int x = 0;  for (int i=0; i<CYCLES; i++) {    digitalWrite(LED_BUILTIN, LOW);    // turn the red LED on (it is active when the pin is LOW)    LCD_ShowString(x, 32, (u8 const *) buf, GREEN);  //print "ON" on the LCD    x = x + 3*8;                       // move cursor 3 chars to the right    delay(SHORT_DELAY);                // wait for a short on period    digitalWrite(LED_BUILTIN, HIGH);   // turn the red LED off (by setting the active LOW pin HIGH)    delay(SHORT_DELAY);                // wait for a short off period  }      digitalWrite(LED_BUILTIN, HIGH);     // turn the LED off  sprintf(buf, "OFF");  LCD_ShowString(x, 32, (u8 const *) buf, RED);  //print "OFF" on the LCD  delay(LONG_DELAY);                   // wait for a long off period }

The firmware blinks the on-board red LED and echoes the state of the LED to the LCD. It also displays and updates a loop counter. And, of course, the first line on the LCD says "Hello World!".

As can be seen, all the details of the SPI interface with the LCD is taken care of in a library called lcd appropriately enough. My knowledge of Mandarin, whether simplified or otherwise, is nil, but with the help of Google, I added English comments in lcd.c. Someone probably did that already, however, it's not a bad way to familiarize oneself with some code.

Downloading the Example Blink Projects toc

The complete PIO projects can be downloaded with the following links.

Just extract the content of the archive somewhere in the PlatformIO projects directory. Here is one way to do it in Linux.

michel@hp:~$ wget https://sigmdel.ca/michel/ha/gd32v/dnld/nanoblink.zip ... 2020-04-01 13:19:10 (770 KB/s) - «nanoblink.zip» enregistré [4982/4982] michel@hp:~$ mkdir -p Documents/PlatformIO/Projects/longan_nano/blink1 michel@hp:~$ unzip nanoblink.zip -d Documents/PlatformIO/Projects/longan_nano/blink1 Archive: nanoblink.zip inflating: Documents/PlatformIO/Projects/longan_nano/blink1/platformio.ini ... michel@hp:~$ ls Documents/PlatformIO/Projects/longan_nano/blink1 include lib platformio.ini src michel@hp:~$ rm nanoblink.zip

Note that I decided to put the nanoblink example project in a subdirectory, called longan_nano, of the PlatformIO project directory. The -p option of the mkdir command creates all needed directories along the specified path. In other words, it created directory longan_nano before creating blink1 inside of it.

To start using a downloaded project

  1. Click on the Open Project button in the PIO Home page
  2. Click on the Projects folder in the Places section of the Favorites pane

  3. Navigate to the desired project directory such as ~Documents/PlatformIO/Projects/longan_nano/blink1
  4. Click on the Open "blink1" button.

The project can now be compiled. Adjust upload protocol in the platformio.ini configuration file before trying to upload new firmware to the Nano.

References toc

The Sipeed Longan Nano has not been available for a long time. Some sites that mimic an unwrapping video merely repeating the product release announcement. However, there is something to learn in just about every site containing practical example code for the Nano. Here is a non-exhaustive list of sites that should be looked at.

longan-nano-experiments by Stephan Mühlstrasser (smuehlst)
Already cited above, this is a good source of projects many of which are built on the Arduino framework as done here.
Tests, Getting familiar with the Sipeed Longan Nano board by Andreas Müller (MuellerA)
A more extensive list of projects built on the gd32vf103-sdk framework.
Longan Nano (GD32VF103) by Kevin Sangeelee
This post serves as a brain, link, and file dump – an disorganised reference for anything [Kevin needs] to keep regarding the Nano
GD32, Program examples w/ RISC-V GD32VF103CBT6 by Claus Kühnel (ckuehnel)
Includes a Drystone benchmark. See also Dr Kühnel's blog for a comparative table with other board.
Hands On with RISC-V, Part 1 – An Introduction and Hands On with RISC-V, Part 2 – Serial Communication by Dale Wheat
An introduction to the RISC-V computer instruction set architecture using a Longang Nanoe and then describing how to use one of the on-board serial ports for both device programming and communication with the host PC.

Do a search of "longan-nano" and "sipeed nano" on GitHub for projects with a more focused topic.

Of course there are "official sites" that should be consulted.

Blink with a Wio Lite RISC-V with WiFi->