Introduction
wiringX is a library that allows developers to control the GPIO of various platforms with generic and uniform functions. By using wiringX, the same code will run on all platforms supported by wiringX, natively.
This article will be divided into the following four parts to introduce how to develop applications on Duo using wiringX:
- Configuration of the application compilation environment based on wiringX
- Basic usage code demonstration
- Introduction to some demos and projects implemented using wiringX
- wiringX APIs
If you are already familiar with the usage of wiringX, you can directly refer to our sample code: duo-examples.
Please note that many pin functions of the Duo series are multiplexed. When using wiringX to control the functions of each pin, it is important to confirm the current state of the pin to ensure it matches the desired functionality. If it doesn't, you can use the duo-pinmux command to switch it to the desired function.
Please refer to the detailed usage instructions for more information:pinmux.
Duo series wiringX pin numbers
Duo/Duo256M
The wiringX pin numbers of Duo and Duo256M are consistent with the pin name numbers. However, the blue LED control pin is not available on the 40-pin physical pinout, and its wiringX pin number is 25.

| wiringX | PIN NAME | Pin# | Pin# | PIN NAME | wiringX |
|---|---|---|---|---|---|
| 0 | GP0 | 1 | 40 | VBUS | |
| 1 | GP1 | 2 | 39 | VSYS | |
| GND | 3 | 38 | GND | ||
| 2 | GP2 | 4 | 37 | 3V3_EN | |
| 3 | GP3 | 5 | 36 | 3V3(OUT) | |
| 4 | GP4 | 6 | 35 | ||
| 5 | GP5 | 7 | 34 | ||
| GND | 8 | 33 | GND | ||
| 6 | GP6 | 9 | 32 | GP27 | 27 |
| 7 | GP7 | 10 | 31 | GP26 | 26 |
| 8 | GP8 | 11 | 30 | RUN | |
| 9 | GP9 | 12 | 29 | GP22 | 22 |
| GND | 13 | 28 | GND | ||
| 10 | GP10 | 14 | 27 | GP21 | 21 |
| 11 | GP11 | 15 | 26 | GP20 | 20 |
| 12 | GP12 | 16 | 25 | GP19 | 19 |
| 13 | GP13 | 17 | 24 | GP18 | 18 |
| GND | 18 | 23 | GND | ||
| 14 | GP14 | 19 | 22 | GP17 | 17 |
| 15 | GP15 | 20 | 21 | GP16 | 16 |
| 25 | GP25 | LED |
DuoS
The wiringX pin numbers of DuoS are consistent with the physical pin numbers. The blue LED control pin is not on the 40PIN physical pins, and its wiringX number is 0.

Header J3
GPIO on Header J3 use 3.3V logic levels.
| wiringX | PIN NAME | PIN# | PIN# | PIN NAME | wiringX |
|---|---|---|---|---|---|
| 3V3 | 1 | 2 | VSYS(5V) | ||
| 3 | B20 | 3 | 4 | VSYS(5V) | |
| 5 | B21 | 5 | 6 | GND | |
| 7 | B18 | 7 | 8 | A16 | 8 |
| GND* | 9 | 10 | A17 | 10 | |
| 11 | B11 | 11 | 12 | B19 | 12 |
| 13 | B12 | 13 | 14 | GND | |
| 15 | B22 | 15 | 16 | A20 | 16 |
| 3V3 | 17 | 18 | A19 | 18 | |
| 19 | B13 | 19 | 20 | GND | |
| 21 | B14 | 21 | 22 | A18 | 22 |
| 23 | B15 | 23 | 24 | B16 | 24 |
| GND | 25 | 26 | A28 | 26 |
GND*: Pin 9 is a low-level GPIO in the V1.1 version of the hardware, and is GND in the V1.2 version and later.
Header J4
GPIO on Header J4 use 1.8V logic levels.
Most of the pins on this header have dedicated functions, such as MIPI DSI signals, Touch Screen signals, and audio signals. If there is no special requirement, it is not recommended to use the pins on this header as GPIO.
| wiringX | PIN NAME | PIN# | PIN# | PIN NAME | wiringX |
|---|---|---|---|---|---|
| VSYS(5V) | 52 | 51 | AUDIO_OUT_R | ||
| 50 | B1 | 50 | 49 | AUDIO_OUT_L | |
| 48 | B2 | 48 | 47 | AUDIO_IN_R | |
| 46 | B3 | 46 | 45 | AUDIO_IN_L | |
| 44 | E2 | 44 | 43 | 3V3 | |
| 42 | E1 | 42 | 41 | C18 | 41 |
| 40 | E0 | 40 | 39 | C19 | 39 |
| GND | 38 | 37 | GND | ||
| 36 | C20 | 36 | 35 | C16 | 35 |
| 34 | C21 | 34 | 33 | C17 | 33 |
| GND | 32 | 31 | GND | ||
| 30 | C14 | 30 | 29 | C12 | 29 |
| 28 | C15 | 28 | 27 | C13 | 27 |
Development Environment Setup
Build environment on Ubuntu22.04
You can also use Ubuntu installed in a virtual machine, Ubuntu installed via WSL on Windows, or Ubuntu-based systems using Docker.
-
Install the tools that compile dependencies
sudo apt-get install wget git make -
Get example source code
git clone https://github.com/milkv-duo/duo-examples.git -
Prepare compilation environment
cd duo-examples
source envsetup.shThe first time you source it, the required compilation toolchain will be automatically downloaded. The downloaded directory is named
host-tools. The next time the compilation environment is loaded, the directory will be detected. If it already exists, it will not be downloaded again.In the source process, you need to enter the required compilation target as prompted:
Select Product:
1. Duo (CV1800B)
2. Duo256M (SG2002) or DuoS (SG2000)If the target board is Duo, select
1, if the target board is Duo256M or DuoS, select2. Since Duo256M and DuoS support both RISCV and ARM architectures, you need to continue to select as prompted:Select Arch:
1. ARM64
2. RISCV64
Which would you like:If the test program needs to be run on a ARM system, select
1, if it is an RISCV system, select2.In the same terminal, you only need to source it once.
-
Compile testing
Take hello-world as an example, enter the hello-world directory and execute make:
cd hello-world
makeAfter the compilation is successful, send the generated
helloworldexecutable program to the Duo device through the network port or the USB network. For example, the USB-NCM method supported by the default firmware, Duo’s IP is 192.168.42.1, the user name isroot, and the password ismilkv.scp -O helloworld [email protected]:/root/After sending successfully, run
./helloworldin the terminal logged in via ssh or serial port, and it will printHello, World![root@milkv]~# ./helloworld
Hello, World!At this point, our compilation and development environment is ready for use.
How to create your own project
You can simply copy existing examples and make necessary modifications. For instance, if you need to manipulate a GPIO, you can refer to the blink example. LED blinking is achieved by controlling the GPIO's voltage level. The current SDK utilizes the WiringX library for GPIO operations, which has been adapted specifically for Duo. You can find the platform initialization and GPIO control methods in the blink.c code for reference.
- Create your own project directory called
my-project. - Copy the
blink.candMakefilefiles from theblinkexample to themy-projectdirectory. - Rename
blink.cto your desired name, such asgpio_test.c. - Modify the
Makefileby changingTARGET=blinktoTARGET=gpio_test. - Modify
gpio_test.cto implement your own code logic. - Execute the
makecommand to compile. - Send the generated
gpio_testexecutable program to Duo for execution.
Note:
- Creating a new project directory is not mandatory to be placed within the
duo-examplesdirectory. You can choose any location based on your preference. Before executing themakecompilation command, it is sufficient to load the compilation environment from theduo-examplesdirectory (source /PATH/TO/duo-examples/envsetup.sh). - Within the terminal where the compilation environment (envsetup.sh) is loaded, avoid compiling Makefile projects for other platforms such as ARM or X86. If you need to compile projects for other platforms, open a new terminal.
Code example
GPIO Usage Example
Here is an example of working with GPIO. It will toggle pin 20 on Duo every 1 second, pulling it high and then low. The physical pin number for pin 20 is 15 in the WiringX numbering system.
#include <stdio.h>
#include <unistd.h>
#include <wiringx.h>
int main() {
int DUO_GPIO = 15;
// Duo: milkv_duo
// Duo256M: milkv_duo256m
// DuoS: milkv_duos
if(wiringXSetup("milkv_duo", NULL) == -1) {
wiringXGC();
return -1;
}
if(wiringXValidGPIO(DUO_GPIO) != 0) {
printf("Invalid GPIO %d\n", DUO_GPIO);
}
pinMode(DUO_GPIO, PINMODE_OUTPUT);
while(1) {
printf("Duo GPIO (wiringX) %d: High\n", DUO_GPIO);
digitalWrite(DUO_GPIO, HIGH);
sleep(1);
printf("Duo GPIO (wiringX) %d: Low\n", DUO_GPIO);
digitalWrite(DUO_GPIO, LOW);
sleep(1);
}
return 0;
}
After compiling and running it on Duo, you can use a multimeter or an oscilloscope to measure the state of pin 20 and verify if it matches the expected behavior.
You can also use the onboard LED pin to verify the behavior. By observing the on/off state of the LED, you can intuitively determine if the program is executing correctly. The WiringX pin number for the LED is 25. Simply modify the code mentioned above by replacing pin 15 with pin 25. However, please note that the default firmware has a script to control LED blinking on startup, which needs to be disabled. You can refer to the example explanation for blink below for instructions on how to disable it.