はじめに
Arduinoは、シンプルさ・使いやすさ・オープン性で知られる人気のオープンソースハードウェアプラットフォームです。豊富なライブラリ関数やサンプルコードが用意されており、プログラミング未経験者でも扱いやすいのが特徴です。また、活発なコミュニティがあり、様々なプロジェクトチュートリアルやドキュメント、サポートも充実してい ます。
Milk-V DuoシリーズはArduino開発に対応しています。Arduino IDEを直接利用でき、簡単な設定後すぐに使い始めることができます。
DuoシリーズのCPUはbig-littleコア設計を採用しており、Arduinoファームウェアはリトルコア上で動作し、ビッグコアはArduino IDEとの通信を担当します。Arduinoファームウェアを受信し、リトルコアにロードして実行します。同時に、ビッグコアのLinuxシステムも通常通り動作します。
また、Duoシリーズボードはビジュアルプログラミングソフト「VISUINO」にも対応しています。詳細はVISUINOの章をご参照ください。
1. 開発環境のセットアップ
Arduino IDEのインストール
Arduino IDEはWindows、Linux、macOSの3つのOSに対応しています。ご利用のOSに合わせてArduino公式サイトからインストーラーをダウンロードしてください。現在の最新版は2.3.2で、最新版の利用を推奨します。
Arduino IDEにDuoを追加
Arduino IDEを開き、ファイルメニューの環境設定を選択し、設定タブの追加のボードマネージャのURLに以下のDuo設定ファイルアドレスを追加します。
https://github.com/kubuds/sophgo-arduino/releases/download/v0.2.5/package_sg200x_index.json

他の開発ボードのURLを既に設定している場合はカンマで区切るか、アドレスバー右側のアイコンをクリックしてウィンドウを表示し、案内に従って追加してください。
設定後、ツールメニューのボードからボードマネージャを開き、SG200Xで検索し、インストールをクリックします。

これでArduino IDE上のDuo開発環境がインストールされ、コードの作成・テストが可能です。
オンボードLED点滅テスト
現在、DuoのSDカードシステムにはArduino対応ファームウェアの書き込みが必要です。Latest Releaseからarduinoで始まるファームウェアをダウンロードしてください。
最新のArduinoファームウェアバージョンはDuo-V1.1.2です。
DuoSは現時点でArduino版ファームウェアを提供していません。Buildroot SDKをクローンし、arduinoブランチに切り替えてコンパイルしてください。
前章のBoot the Duoを参考にシステムをインストールします。
USBケーブルでDuoをPCに接続すると自動で電源が入ります。
Duoのデフォルトファームウェア(ビッグコアLinux)は起動スクリプトでオンボードLEDを点滅させます。リトルコアArduinoでLEDを制御するには、ビッグコアLinuxのLED点滅スクリプトを無効化します。Duoのターミナルで以下を実行してください。
mv /mnt/system/blink.sh /mnt/system/blink.sh_backup && sync
LED点滅スクリプトをリネームします。再起動後、LEDは点滅しなくなります。
reboot
この時、PCの「デバイスマネージャー」の「ポート」に新しいシリアルデバイスが表示されます。

Arduino IDEのメイン画面でボードを選択をクリックし、他のボードとポートを選択...をクリックします。

「duo」で検索し、Duoの場合はDuo Dev Module、Duo256Mの場合はDuo256 Dev Moduleを選択し、ポートも選択してOKをクリックします。

Arduino IDEのファイルメニューからExamples > 01.Basics > Blinkテストプログラムを開きます。このプログラムはArduinoデバイスのオンボードLEDを点滅させます。Duoでも動作します。アップロードにはpyserialのインストールが必要な場合があります。Uploadボタンをクリックしてテストしてください。

Duoボード上のLEDが1秒間隔で点滅するのが確認できます。
コードのコンパイル・ダウンロード前に、PCにpython環境がインストールされ、環境変数が正しく設定されていることを確認してください。python環境がないとコンパイル・ダウンロードに失敗します。
Duoへのファームウェアダウンロードができない場合、まずpyserialがインストールされているか確認してください。未インストールの場合はpip install pyserialでインストールできます。
pyserialインストール後もアップロードできない場合は、serialがインスト ールされていないか確認してください。pyserialとserialを同時にインストールするとダウンロードに失敗する場合があります。pip uninstall serialでserialをアンインストールしてください。
2. Duo Arduinoピンリソース
Duo
Duo256M
DuoS
Header J3(RJ45ポート側)のGPIOは3.3Vロジックレベルです。
*GND*: ピン9はV1.1ハードウェアではLowレベルGPIO、V1.2以降ではGNDです。
注意: CSIカメラコネクタJ2のI2CはI2C2です。J2でCSIカメラを使う場合、J3ピンヘッダのI2C2は利用できません。
Header J4(USB-Aポート側)のGPIOは1.8Vロジックレベルです。
DuoSではSPI、I2C1/2、ADCは一時的に利用できません。今後のソフトウェアアップデートをお待ちください。
3. コード例
GPIO使用例
このプログラムはDuo物理ピン20で1秒間隔でHigh/Lowを出力し、外部LEDで現象を観察します。
接続方法:LEDのマイナスをDuoのGND(例:ピン18)、プラスを1K抵抗経由でピン20に接続します。

テストコード:
#define TEST_PIN 20 //0,1,2,14,15,19,20,21,22,24,25,26,27
void setup() {
pinMode(TEST_PIN, OUTPUT);
}
void loop() {
digitalWrite(TEST_PIN, HIGH);
delay(1000);
digitalWrite(TEST_PIN, LOW);
delay(1000);
}
- LED未接続の場合は、マルチメータやオシロスコープでピンの状態 変化を観察できます。
- TEST_PINを0に設定するとDuoオンボードLEDのテストができます。
UART使用例
UARTシリアルポート
UARTシリアルポートはデフォルトで物理ピン6/7のUART3を使用します。Arduinoプログラムのデバッグ時はこのシリアルポートで情報を出力できます。
DuoSボードの場合、UART3はデフォルトで50/48ピンにマッピングされています。これらは1.8Vレベルのため使いにくい場合があります。UART2の利用を推奨します。
接続方法:PCはUSB-TTLシリアルケーブルを使用。ロジックレベルは3.3V、ボーレートは115200。シリアルケーブルのRXをDuoのPIN 6 UART3_TX、TXをPIN 7 UART3_RX、GNDをDuoのGND(例:ピン3)に接続します。

テストコード:
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.printf("hello world\r\n");
delay(1000);
}
実行するとPCのシリアルツールに1秒ごとに"hello world"が表示されます。
また、デフォルトシリアルポートはDuoのUART3なので、プログラム内でSerial3も利用できます。
void setup() {
Serial3.begin(115200);
}
void loop() {
Serial3.printf("hello world\r\n");
delay(1000);
}
I2C使用例
DuoとDuo256MのI2Cインターフェースリソースは異なるため、前述のピンリソース図を参照してください。
I2C0からI2C1へデータ送信(Duo)
ハードウェア接続:I2C0とI2C1のSDA/SCLをそれぞれ接続し、UART例の方法でPCにシリアル接続して出力 を確認します。

DuoのWire関数はデフォルトでI2C0にマッピングされています(Wire=Wire0)。
テストコード:
#include <Wire.h>
void receive(int a) {
Serial.printf("receive %d bytes\n\r", a);
while(a--) {
Serial.printf("%d \n\r", Wire1.read());
}
}
void setup() {
Serial.begin(115200);
Wire1.begin(0x50);
Wire1.onReceive(receive);
Wire.begin();
Serial.printf("test slave\n\r");
Wire1.print();
}
byte val = 0;
void loop() {
Wire.beginTransmission(0x50);
Serial.printf("send %d \n\r", ++val);
Wire.write(val);
Wire.endTransmission();
Wire1.onService();
delay(1000);
}
テスト結果:
test slave
Wire1: 1
[iic_dump_register]: ===dump start
IC_CON = 0x22
IC_TAR = 0x55
IC_SAR = 0x50
IC_SS_SCL_HCNT = 0x1ab
IC_SS_SCL_LCNT = 0x1f3
IC_ENABLE = 0x1
IC_STATUS = 0x6
IC_INTR_MASK = 0x224
IC_INTR_STAT = 0
IC_RAW_INTR_STAT = 0x10
[iic_dump_register]: ===dump end
send 1
receive 1 bytes
1
send 2
receive 1 bytes
2
send 3
receive 1 bytes
3
send 4
receive 1 bytes
4
I2C1からI2C2へデータ送信(Duo256M)
Duo256MにはI2C0はありません。
ハードウェア接続:I2C1とI2C2のSDA/SCLをそれぞれ接続し、UART例の方法でPCにシリアル接続して出力を確認します。

Duo256MのWire関数はデフォルトでI2C1にマッピングされています(Wire=Wire1)。
テストコード:
#include <Wire.h>
void receive(int a) {
Serial.printf("receive %d bytes\n\r", a);
while(a--) {
Serial.printf("%d \n\r", Wire2.read());
}
}
void setup() {
Serial.begin(115200);
Wire2.begin(0x50);
Wire2.onReceive(receive);
Wire.begin();
Serial.printf("test slave\n\r");
Wire2.print();
}
byte val = 0;
void loop() {
Wire.beginTransmission(0x50);
Serial.printf("send %d \n\r", ++val);
Wire.write(val);
Wire.endTransmission();
Wire2.onService();
delay(1000);
}