Apache NuttX OS on RISC-V BL602 and BL604

📝 24 Nov 2021

Tasty Nutty Treat on PineDio Stack BL604 RISC-V Board

Tasty Nutty Treat on PineDio Stack BL604 RISC-V Board

Among all Embedded Operating Systems, Apache NuttX is truly unique because…

Today we shall build, flash and run NuttX on the PineCone BL602 and PineDio Stack BL604 RISC-V Boards. (Pic above)

(The steps in this article should work on any BL602 or BL604 Board: Pinenut, DT-BL10, MagicHome BL602, …)

We’ll briefly explore the internals of NuttX to understand how it works…

Coding a microcontroller with Linux-like (POSIX) functions might sound odd, but we’ll appreciate the benefits in a while.

(And we might have an interesting way to support Embedded Rust on NuttX!)

Building NuttX

1 Boot NuttX

Follow the steps below to build, flash and run NuttX for BL602 and BL604…

We should see the NuttX Shell on our Serial Terminal…

NuttShell (NSH) NuttX-10.2.0-RC0
nsh>

The default NuttX Firmware includes two Demo Apps

Let’s test the Demo Apps.

Booting NuttX

2 Hello Demo

In the NuttX Shell, enter…

hello

We should see…

Hello, World!!

(Yep this is the plain and simple Hello World app!)

The Source Code looks very familiar: hello_main.c

#include <nuttx/config.h>
#include <stdio.h>

int main(int argc, FAR char *argv[])
{
  printf("Hello, World!!\n");
  return 0;
}

It looks exactly like on Linux! (Almost)

That’s because NuttX is POSIX Compliant. It supports Linux features like stdio, main() and printf().

Let’s run the Timer Demo App.

Hello and Timer Demo Apps

3 Timer Demo

In the NuttX Shell, enter…

timer

We should see some Timeout Messages. (Pic above)

This Demo App accesses the System Timer in an interesting way: timer_main.c

Timer Demo App

  1. /dev/timer0 points to the System Timer

    (Everything is a file… Just like Linux!)

  2. We call open() to access the System Timer

  3. ioctl() to set the Timeout

  4. sigaction() to register the Timeout Handler

open(), ioctl() and sigaction() are common functions called by Linux Apps.

NuttX Apps really look like Linux Apps!

help, ls and gpio commands

4 Configure NuttX

Let’s get adventurous and add NuttX Commands

(See pic above)

Enter this command to configure the NuttX build on our computer…

make menuconfig

Let’s explore the options.

(More about configuring NuttX)

Top Menu

4.1 Enable help and ls

In menuconfig, select “Application Configuration”. (Pic above)

Select “NSH Library”

Application Configuration

Select “Disable Individual Commands”

NSH Library

Uncheck the boxes for “help” and “ls”

Disable Individual Commands

“help” and “ls” are now enabled in NuttX Shell!

4.2 Enable GPIO Driver

Hit “Exit” until the Top Menu appears. (“NuttX/x64_64 Configuration”)

Select “Device Drivers”….

Top Menu

Select “IO Expander / GPIO Support”

Device Drivers

Check the box for “GPIO Driver”

IO Expander / GPIO Support

This enables the GPIO Driver for NuttX.

(If we don’t enable the GPIO Driver, NuttX won’t let us select the GPIO Demo App!)

4.3 Enable GPIO Demo

Hit “Exit” until the Top Menu appears. (“NuttX/x64_64 Configuration”)

Select “Application Configuration”

Top Menu

Select “Examples”

Application Configuration

NuttX reveals the list of Demo Apps

Examples

(Hello and Timer Demo Apps are already selected)

Check the box for “GPIO Driver Example”

GPIO Driver Example

Hit “Save”

Save

Then “OK” to save the NuttX Configuration to “.config”.

(See the NuttX Configuration)

Hit “Exit” until menuconfig quits.

4.4 Rebuild NuttX

Rebuild and copy the NuttX Firmware…

#  Rebuild NuttX
make

#  For Linux: Change $HOME/blflash to the full path of blflash
cp nuttx.bin $HOME/blflash

#  For WSL: Change /mnt/c/blflash to the full path of blflash in Windows
#  /mnt/c/blflash refers to c:\blflash
cp nuttx.bin /mnt/c/blflash

Flash and run the NuttX Firmware with these steps…

We’re ready to test the new commands!

ls and gpio commands

5 GPIO Demo

Let’s run the new commands: “help”, “ls” and “gpio”.

In the NuttX Shell, enter…

help

(“?” works too)

NuttX says that the “ls” and “gpio” commands are now available…

help usage: help [-v] [<cmd>]
  ?  help  ls  uname
Builtin Apps:
  timer  sh  getprime  hello  nsh  gpio

5.1 NuttX Devices

Remember everything is a file in NuttX?

Let’s list the Hardware Devices in NuttX…

ls /dev

NuttX reveals the devices that we may control…

/dev:
 console
 gpin0
 gpint2
 gpout1
 null
 timer0
 zero

Let’s write to the GPIO Output at /dev/gpout1.

gpio command

5.2 Write to GPIO

Enter this to set the GPIO Output to High…

gpio -o 1 /dev/gpout1

(As explained in the pic above)

The GPIO Output changes from Low to High

Driver: /dev/gpout1
  Output pin:    Value=0
  Writing:       Value=1
  Verify:        Value=1

Can we do this to flip an LED on and off?

Not yet. We haven’t told NuttX which GPIO Pin our LED is connected to!

Let’s learn how.

NuttX Pin Definitions

6 Configure Pins

How do we define the Pin Numbers for GPIO, UART, PWM, I2C, SPI, …?

We define the Pin Numbers in board.h (Pic above)

Note: Some pins on BL602 and BL604 may only be assigned to specific functions.

Refer to the BL602 / BL604 Reference Manual, Section 3.2.8 (GPIO Function) for the Pin Description Table.

How shall we define the GPIO Output Pin for our LED?

On PineCone BL602 the Blue LED is connected on GPIO 11.

We change the Pin Definition for BOARD_GPIO_OUT1 like so: board.h

//  GPIO Output Pin:
//  Changed GPIO_PIN1 to GPIO_PIN11 (Blue LED on PineCone BL602)
//  Changed GPIO_PULLDOWN to GPIO_FLOAT
#define BOARD_GPIO_OUT1 \
  (GPIO_OUTPUT | GPIO_FLOAT | \
    GPIO_FUNC_SWGPIO | GPIO_PIN11)

//  Previously:
//  #define BOARD_GPIO_OUT1 \
//    (GPIO_OUTPUT | GPIO_PULLDOWN | \
//      GPIO_FUNC_SWGPIO | GPIO_PIN1)

Make sure the Pin Number isn’t used by another port!

(FreeRTOS on BL602 uses a Device Tree to assign the pins)

6.1 Rerun NuttX

Rebuild and copy the NuttX Firmware…

#  Rebuild NuttX
make

#  For Linux: Change $HOME/blflash to the full path of blflash
cp nuttx.bin $HOME/blflash

#  For WSL: Change /mnt/c/blflash to the full path of blflash in Windows
#  /mnt/c/blflash refers to c:\blflash
cp nuttx.bin /mnt/c/blflash

Flash and run the NuttX Firmware with these steps…

We’re ready to test the LED!

Testing the LED

7 Test the LED

Let’s flip PineCone BL602’s LED on and off!

The Blue LED is wired to GPIO 11 like so…

At startup, the Blue LED is On (because the default GPIO Output is Low)…

LED On

In the NuttX Shell, enter this to flip GPIO 11 to High

gpio -o 1 /dev/gpout1

NuttX flips GPIO 11 from Low to High

Driver: /dev/gpout1
  Output pin:    Value=0
  Writing:       Value=1
  Verify:        Value=1

Our Blue LED switches Off

LED Off

So far so good!

Enter this to flip GPIO 11 to Low

gpio -o 0 /dev/gpout1

As expected, NuttX flips GPIO 11 from High to Low

Driver: /dev/gpout1
  Output pin:    Value=1
  Writing:       Value=0
  Verify:        Value=0

Our Blue LED switches On

LED On

Congratulations we have successfully tested the BL602 LED with NuttX!

(Got problems with GPIO? See these troubleshooting tips)

(If we’re controlling LEDs, consider using NuttX’s LED Driver)

GPIO Demo App

8 GPIO Driver

Let’s look inside NuttX to understand how the GPIO Driver works.

We start at the “gpio” command: gpio_main.c

From the pic above we see that the “gpio” command calls…

What are GPIOC_READ and GPIOC_WRITE?

GPIOC_READ and GPIOC_WRITE are GPIO Driver Commands defined in the NuttX GPIO Interface…

The “gpio” command works across all NuttX Platforms because it calls the common GPIO Interface.

8.1 GPIO Interface

Below is the implementation of the platform-independent GPIO Interface (ioctl): gpio.c

//  Standard character driver ioctl method
static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
  ...
  //  Handle each GPIO Driver Command...
  switch (cmd)
    {
      //  If we're setting the value of an output GPIO...
      case GPIOC_WRITE:
        ...
        //  Call the Board-Specific GPIO Driver
        ret = dev->gp_ops->go_write(
          dev,       //  GPIO Device
          (bool)arg  //  1 (High) or 0 (Low)
        );

This is a Character Device Driver that handles each GPIO Driver Command (like GPIOC_WRITE).

The driver calls the Board-Specific GPIO Driver to execute the command.

8.2 Board Driver

What’s a Board-Specific Driver?

PineCone BL602 and PineDio Stack BL604 are two Dev Boards based on BL602 / BL604.

Each Dev Board has hardware features that are specific to the board. Like LEDs connected on different GPIO Pins.

NuttX isolates these board differences by calling a Board-Specific Driver.

(We’re actually calling the Board-Specific Driver for BL602 EVB)

Here is our Board-Specific GPIO Driver: bl602_gpio.c

//  Board-Specific GPIO Driver: Set the value of an output GPIO
static int gpout_write(FAR struct gpio_dev_s *dev, bool value)
{
  //  Alias the GPIO Device as bl602xgpio
  FAR struct bl602_gpio_dev_s *bl602xgpio =
    (FAR struct bl602_gpio_dev_s *)dev;
  ...
  //  Call the BL602-Specific GPIO Driver
  bl602_gpiowrite(                  //  Set GPIO Output...
    g_gpiooutputs[bl602xgpio->id],  //  GPIO Pin Set
    value                           //  1 (High) or 0 (Low)
  );

g_gpiooutputs maps the GPIO Device (like “/dev/gpout1”) to a GPIO Pin Set, which contains the GPIO Pin Number.

(Which makes sense, because each board may map the Hardware Devices to different GPIO Pins)

The Board-Specific Driver calls the BL602-Specific GPIO Driver to set the GPIO Output, passing the GPIO Pin Set.

8.3 BL602 Driver

The BL602-Specific GPIO Driver manipulates the BL602 Hardware Registers to perform GPIO Functions.

(The driver is called by the Board-Specific Drivers for all BL602 boards)

Here’s how the BL602-Specific GPIO Driver sets the GPIO Output: bl602_gpio.c

//  BL602-Specific GPIO Driver: Set the value of an output GPIO
void bl602_gpiowrite(gpio_pinset_t pinset, bool value)
{
  //  Extract the GPIO Pin Number from Pin Set
  uint8_t pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;

  //  If we're setting the GPIO to High...
  if (value)
    {
      //  Set the pin's bit in the GPIO Output Register
      modifyreg32(BL602_GPIO_CFGCTL32, 0, (1 << pin));
    }
  else
    {
      //  Clear the pin's bit in the GPIO Output Register
      modifyreg32(BL602_GPIO_CFGCTL32, (1 << pin), 0);
    }
}

(modifyreg32 is defined here)

BL602_GPIO_CFGCTL32 is the Address of the GPIO Output Register: 0x40000188

This code looks similar to GLB_GPIO_Write from BL602 IoT SDK’s Standard Driver.

That’s because NuttX implements its own Hardware Abstraction Layer (HAL) for BL602.

(Which might have quirks different from the BL602 IoT SDK)

(Got problems with the GPIO Driver? See these troubleshooting tips)

Let’s try out a fun freebie for NuttX… BASIC Interpreter!

Enable BASIC Interpreter

9 BASIC Interpreter

One of the best things about NuttX: It comes with many freebies… Like the BASIC Interpreter!

Let’s do some BASIC on BL602 NuttX…

  1. Configure our NuttX Build…

    make menuconfig
  2. Select “Application Configuration → Interpreters”

  3. Check the box for “Basic Interpreter Support”

    (Pic above)

  4. Save the configuration and exit menuconfig

  5. BL602 doesn’t support environment variables and folders, so we need to patch the source files…

    “Disable environment variables and folders”

    (See the modified files: bas_global.c and bas_statement.c)

  6. We’ll use “peek” and “poke” in a while. Patch the source file to enable the commands…

    “Enable peek and poke”

    (See the modified file)

  7. Rebuild, reflash and rerun NuttX

  8. In the NuttX Shell, enter…

    bas
  9. The BASIC Interpreter comes to life!

    bas 2.4
    Copyright 1999-2014 Michael Haardt.
    This is free software with ABSOLUTELY NO WARRANTY.
  10. Go ahead and run a BASIC Program!

    10 print "hello"
    20 sleep 5
    30 goto 10
    list
    run

BASIC Interpreter

(Childhood Memories 🥲)

In the olden days we would “peek” and “poke” to light up pixels on our Apple ][… Let’s do the same for our BL602 LED!

  1. In the BASIC Interpreter, enter this…

    print peek(&h40000188)
    poke &h40000188, &h800

    Remember that 0x40000188 is the Address of the GPIO Output Register.

    Setting (or “poking”) this register to 0x800 will set GPIO 11 to High.

    (Because 0x800 equals 1 << 11)

    Which switches off the Blue LED on PineCone BL602.

  2. Now do this…

    print peek(&h40000188)
    poke &h40000188, &h00

    Setting the GPIO Output Register to 0x00 will set GPIO 11 to Low.

    Which switches on the Blue LED.

Yep it’s indeed possible to blink the LED in BASIC!

(OK this code isn’t so legit… We ought to preserve the existing bits in the register, not overwrite them)

Blinking the LED in BASIC

10 Why NuttX?

Now that we understand NuttX inside out, let’s have a chat…

I’m familiar with Embedded Coding on Arduino / STM32 / nRF52 / BL602. NuttX’s POSIX Interface looks very strange to me: open(), read(), ioctl(), …

Well NuttX’s POSIX Interface might be a good thing for folks who are familiar with Linux and Single-Board Computers.

The NuttX Team has done an incredible job enforcing API Consistency across all kinds of platforms. “Write once run anywhere” might be true on NuttX!

In any case it’s hard to find an Open Source Embedded OS that supports so many platforms.

BL602 Peripherals supported by #NuttX

(Source)

For BL602 and BL604, shall I use NuttX or FreeRTOS (BL602 IoT SDK)?

Remember that the NuttX Team (with Bouffalo Lab) has created their own Hardware Abstraction Layer (HAL) for BL602 / BL604. (See this)

Some features on BL602 / BL604 are not yet supported by NuttX. (Pic above)

But NuttX on BL602 is getting better every day!

(Though SPI with DMA is not yet supported on BL602 NuttX)

POSIX still looks kinda odd to me. Is there something we could do with Rust?

Thanks for asking! Yes we could wrap the POSIX Interface into a Rust Embedded HAL that’s familiar to many Rust coders.

And the Rust Embedded HAL might be portable across all NuttX platforms. Thanks to POSIX Compatibility!

More about this in the next section.

Rust Embedded HAL

(Source)

11 Rust on NuttX

Does Rust provide a standard way to access the Hardware Functions on Microcontrollers?

Yes! The Embedded Rust Community has created a Hardware Abstraction Layer (HAL) that supports all kinds of Microcontrollers…

Take a look at these Hardware Interfaces in Rust Embedded HAL for…

How popular is the Rust Embedded HAL?

According to the official list

(Would be awesome if we could run all these Device Drivers on NuttX!)

So the Rust Embedded HAL is kinda like NuttX’s POSIX Interface?

Conceptually yes! Rust Embedded HAL was created to allow Rust Drivers and Apps to be portable across all Microcontroller Platforms.

Can we port Rust Embedded HAL to NuttX?

We could wrap the NuttX POSIX Interface into a Rust Embedded HAL.

This means that we build a layer of code that translates the Rust Embedded HAL Interface into the NuttX POSIX Interface.

And the Rust Embedded HAL for NuttX might be portable across all NuttX platforms. Thanks to POSIX Compatibility!

(Rust Embedded HAL might also become a friendlier API for NuttX)

Shall I should work on this? Lemme know!

(UPDATE: According to Brennan Ashton, Sony has worked on Rust for NuttX. See this for the details)

12 What’s Next

I’m new to NuttX but I had lots of fun experimenting with it. I hope you’ll enjoy NuttX too!

Here are some topics I might explore in future articles, lemme know if I should do these…

(BL602 IoT SDK / FreeRTOS is revamping right now to the new “hosal” HAL. Terrific time to explore NuttX now!)

Many Thanks to my GitHub Sponsors for supporting my work! This article wouldn’t have been possible without your support.

Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…

lupyuen.github.io/src/nuttx.md

13 Notes

  1. This article is the expanded version of this Twitter Thread

  2. Check out the NuttX developer forum

    NuttX Dev Mail Archive

  3. History of NuttX on BL602, how it all started…

    “BL602 and NuttX”

  4. More about NuttX on BL602

    “How to install NuttX on BL602”

  5. For NuttX on RISC-V ESP32-C3

    “Installing Apache NuttX on Arch Linux for RISC-V and use it with RISC-V based ESP32-C3”

  6. Xiaomi is actively contributing to NuttX…

    “Xiaomi launches a new IoT Software Platform “Xiaomi Vela” based on NuttX OS”

  7. The built-in “timer” Demo App we’ve seen uses Timer Handlers that are not deterministic and may have longer latency.

    Check out the improved “timer_gpout” Demo App, which catches the Timer Signal in real time…

    timer_gpout Demo App

    (Thanks to Sara Monteiro for the tip!)

14 Appendix: Build, Flash and Run NuttX

Below are the steps to build, flash and run NuttX on BL602 and BL604.

14.1 Build NuttX

Let’s build NuttX on Linux (Ubuntu), WSL (Ubuntu) or macOS

(Instructions for other platforms)

(See this for Arch Linux)

  1. Download the RISC-V GCC Toolchain from BL602 IoT SDK…

    git clone https://github.com/lupyuen/bl_iot_sdk
  2. Edit ~/.bashrc (or equivalent) and add…

    #  TODO: Change $HOME/bl_iot_sdk to the full path of bl_iot_sdk
    
    #  For Linux and WSL:
    PATH="$HOME/bl_iot_sdk/toolchain/riscv/Linux/bin:$PATH"
    
    #  For macOS:
    PATH="$HOME/bl_iot_sdk/toolchain/riscv/Darwin/bin:$PATH"
  3. Update the PATH

    . ~/.bashrc
  4. Install the Build Tools

    #  For Linux and WSL:
    sudo apt install \
      bison flex gettext texinfo libncurses5-dev libncursesw5-dev \
      gperf automake libtool pkg-config build-essential gperf genromfs \
      libgmp-dev libmpc-dev libmpfr-dev libisl-dev binutils-dev libelf-dev \
      libexpat-dev gcc-multilib g++-multilib picocom u-boot-tools util-linux \
      kconfig-frontends
    
    #  For macOS:
    brew install automake
    #  Build "kconfig-frontends" because the "brew install" version doesn't work
    pushd /tmp
    git clone https://bitbucket.org/nuttx/tools.git
    cd tools/kconfig-frontends
    patch < ../kconfig-macos.diff -p 1
    ./configure --enable-mconf --disable-shared --enable-static --disable-gconf --disable-qconf --disable-nconf
    #  Needed because "make" requires "aclocal-1.15" and "automake-1.15"
    sudo ln -s /usr/local/bin/aclocal /usr/local/bin/aclocal-1.15
    sudo ln -s /usr/local/bin/automake /usr/local/bin/automake-1.15
    make
    #  Install "kconfig-frontends"
    make install
    popd
  5. Download NuttX…

    mkdir nuttx
    cd nuttx
    git clone https://github.com/apache/incubator-nuttx.git nuttx
    git clone https://github.com/apache/incubator-nuttx-apps apps
  6. Configure NuttX…

    cd nuttx
    ./tools/configure.sh bl602evb:nsh
  7. We should see…

    configuration written to .config

    (See the complete log)

    If we see this instead…

    kconfig-tweak: command not found

    Check whether the kconfig-frontends package has been installed correctly. (See above)

    Then delete the Build Configuration so that configure.sh can proceed…

    make distclean
    ./tools/configure.sh bl602evb:nsh
  8. Build NuttX…

    make
  9. We should see…

    LD: nuttx
    CP: nuttx.hex
    CP: nuttx.bin

    (See the complete log)

  10. Copy the NuttX Firmware to the blflash directory…

    #  For Linux and macOS:
    #  TODO: Change $HOME/blflash to the full path of blflash
    cp nuttx.bin $HOME/blflash
    
    #  For WSL:
    #  TODO: Change /mnt/c/blflash to the full path of blflash in Windows
    #  /mnt/c/blflash refers to c:\blflash
    cp nuttx.bin /mnt/c/blflash

    (We’ll cover blflash in the next section)

    For WSL we need to run blflash under plain old Windows CMD (not WSL) because it needs to access the COM port.

  11. In case of problems, refer to the NuttX Docs

    “BL602 NuttX”

    “Installing NuttX”

Building NuttX

14.2 Flash NuttX

Follow these steps to install blflash

  1. “Install rustup”

  2. “Download and build blflash”

We assume that our Firmware Binary File nuttx.bin has been copied to the blflash folder.

Set BL602 / BL604 to Flashing Mode and restart the board…

For PineDio Stack BL604:

  1. Set the GPIO 8 Jumper to High (Like this)

  2. Press the Reset Button

For PineCone BL602:

  1. Set the PineCone Jumper (IO 8) to the H Position (Like this)

  2. Press the Reset Button

For BL10:

  1. Connect BL10 to the USB port

  2. Press and hold the D8 Button (GPIO 8)

  3. Press and release the EN Button (Reset)

  4. Release the D8 Button

For Pinenut and MagicHome BL602:

  1. Disconnect the board from the USB Port

  2. Connect GPIO 8 to 3.3V

  3. Reconnect the board to the USB port

Enter these commands to flash nuttx.bin to BL602 / BL604 over UART…

# TODO: Change ~/blflash to the full path of blflash
cd ~/blflash

# For Linux:
sudo cargo run flash nuttx.bin \
    --port /dev/ttyUSB0

# For macOS:
cargo run flash nuttx.bin \
    --port /dev/tty.usbserial-1420 \
    --initial-baud-rate 230400 \
    --baud-rate 230400

# For Windows: Change COM5 to the BL602 / BL604 Serial Port
cargo run flash nuttx.bin --port COM5

(See the Output Log)

For WSL: Do this under plain old Windows CMD (not WSL) because blflash needs to access the COM port.

(Flashing WiFi apps to BL602 / BL604? Remember to use bl_rfbin)

(More details on flashing firmware)

Flashing NuttX

14.3 Run NuttX

Set BL602 / BL604 to Normal Mode (Non-Flashing) and restart the board…

For PineDio Stack BL604:

  1. Set the GPIO 8 Jumper to Low (Like this)

  2. Press the Reset Button

For PineCone BL602:

  1. Set the PineCone Jumper (IO 8) to the L Position (Like this)

  2. Press the Reset Button

For BL10:

  1. Press and release the EN Button (Reset)

For Pinenut and MagicHome BL602:

  1. Disconnect the board from the USB Port

  2. Connect GPIO 8 to GND

  3. Reconnect the board to the USB port

After restarting, connect to BL602 / BL604’s UART Port at 2 Mbps like so…

For Linux:

sudo screen /dev/ttyUSB0 2000000

For macOS: Use CoolTerm (See this)

For Windows: Use putty (See this)

Alternatively: Use the Web Serial Terminal (See this)

Press Enter to reveal the NuttX Shell

NuttShell (NSH) NuttX-10.2.0-RC0
nsh>

Congratulations NuttX is now running on BL602 / BL604!

(More details on connecting to BL602 / BL604)

Running NuttX

15 Appendix: Fix GPIO Output

This section describes the GPIO Output glitch that we observed in the BL602 GPIO Driver, and explains how we fixed it.

The fix has been merged into NuttX. (Thank you NuttX Maintainers! 🙏)

Summary of the GPIO Output glitch on BL602…

  1. We have an LED connected to a GPIO Output Pin

  2. Setting the GPIO Output to High and Low doesn’t blink the LED

  3. We discover that the BL602 GPIO Driver doesn’t set the GPIO Output Enable Register

  4. After patching the BL602 GPIO Driver to set the GPIO Output Enable Register, the LED blinks OK

LED On

15.1 Observe the glitch

We observe the GPIO Output Glitch on Pine64 PineCone BL602 Board. (Pic above)

PineCone BL602 has a Blue LED connected on GPIO 11

We configure GPIO 11 as the GPIO Output Pin BOARD_GPIO_OUT1 in board.h

//  GPIO Output Pin:
//  Changed GPIO_PIN1 to GPIO_PIN11 (Blue LED on PineCone BL602)
//  Changed GPIO_PULLDOWN to GPIO_FLOAT
#define BOARD_GPIO_OUT1 \
  (GPIO_OUTPUT | GPIO_FLOAT | \
    GPIO_FUNC_SWGPIO | GPIO_PIN11)

//  Previously:
//  #define BOARD_GPIO_OUT1 \
//    (GPIO_OUTPUT | GPIO_PULLDOWN | \
//      GPIO_FUNC_SWGPIO | GPIO_PIN1)

After building and flashing NuttX to BL602, we run the NuttX GPIO Command to toggle GPIO 11…

nsh> gpio -o 1 /dev/gpout1
Driver: /dev/gpout1
  Output pin:    Value=0
  Writing:       Value=1
  Verify:        Value=1

nsh> gpio -o 0 /dev/gpout1
Driver: /dev/gpout1
  Output pin:    Value=1
  Writing:       Value=0
  Verify:        Value=0

NuttX changes GPIO 11 from Low to High and back to Low.

But the BL602 LED doesn’t blink. Let’s track down the glitch.

Flipping GPIO 11 doesn’t blink the LED

15.2 Trace the glitch

To track down the glitch, we add debug logging to the BL602 GPIO Driver functions bl602_configgpio and bl602_gpiowrite

bl602_gpiowrite writes correctly to the GPIO Output Register

From the log we see that bl602_gpiowrite writes correctly to the GPIO Output Register at 0x40000188 (BL602_GPIO_CFGCTL32)…

bl602_gpiowrite high:
  pin=11
  addr=0x40000188
  clearbits=0x0
  setbits=0x800

At startup, bl602_configgpio configures GPIO 11 (0x40000114) with GPIO Input Disabled..

bl602_configgpio:
  pin=11
  addr=0x40000114
  clearbits=0xffff0000
  setbits=0xb000000

But bl602_configgpio doesn’t enable GPIO Output on GPIO 11.

bl602_configgpio doesn’t enable GPIO Output

According to the BL602 Reference Manual (Section 3.2.9 “GPIO Output”, Page 27), we should update the GPIO Output Enable Register to enable GPIO Output…

(Source)

But the GPIO Output Enable Register is missing from the manual.

We look up BL602 IoT SDK and we discover in the function GLB_GPIO_OUTPUT_Enable that the GPIO Output Enable Register is at 0x40000190 (GLB_GPIO_CFGCTL34)…

GPIO Output Enable Register is at 0x40000190

(Source)

Let’s update the GPIO Output Enable Register in NuttX.

15.3 Fix the glitch

We patch the bl602_configgpio function to update the GPIO Output Enable Register: bl602_gpio.c

// Existing function
int bl602_configgpio(gpio_pinset_t cfgset)
{
  // Existing code
  ...
  modifyreg32(regaddr, mask, cfg);
  
  // Insert this code near the end of the function...
  // Enable GPIO Output if requested
  if (!(cfgset & GPIO_INPUT))
    {
      modifyreg32(            // Modify the register...
        BL602_GPIO_CFGCTL34,  // At address 0x40000190 (GPIO Enable Output)
        0,                    // Don't clear any bits
        (1 << pin)            // Set the bit for the GPIO Pin
      );
    }
  // End of inserted code

  // Existing code
  return OK;
}

Here is the patch…

Let’s test the patch.

Update the GPIO Output Enable Register

15.4 Test the fix

We rebuild and run the patched code.

At startup, the Blue LED is On (because the default GPIO Output is Low)…

LED On

(Remember the LED switches on when GPIO 11 is Low)

At startup the patched bl602_configgpio function correctly updates the GPIO Output Enable Register at 0x40000190

bl602_configgpio enable output:
  pin=11
  addr=0x40000190
  clearbits=0x0
  setbits=0x800

(See complete log)

We run the GPIO Command to set GPIO 11 to High

nsh> gpio -o 1 /dev/gpout1
Driver: /dev/gpout1
  Output pin:    Value=0
  Writing:       Value=1
  Verify:        Value=1

PineCone’s Blue LED on GPIO 11 correctly switches off.

LED Off

We run the GPIO Command to set GPIO 11 to Low

nsh> gpio -o 0 /dev/gpout1
Driver: /dev/gpout1
  Output pin:    Value=1
  Writing:       Value=0
  Verify:        Value=0

And PineCone’s Blue LED on GPIO 11 correctly switches on.

LED On

We have successfully fixed the GPIO Output glitch!

The fix has been merged into NuttX…

PineCone Blue LED blinks correctly

(Source)