マイコン用OSをインストールしてみる

マイコン用のOSをいろいろインストールしてみた備忘録です。あとで自分がインストール作業を再現する目的のメモですので、あまり丁寧に手順を書いていません。動作確認はまずLチカやシリアル通信など単純なものから始め、順次追加していきます。

FreeRTOS編

Arduino Nano/Uno

ビルド

Arduino向けFreeRTOSはArduino IDEからインストールできる。

  1. Arduino IDEを起動する。
  2. スケッチ→ライブラリをインクルード→ライブラリを管理
  3. FreeRTOSを検索
  4. “FreeRTOS by Richard Barry”を選択してインストール

これでFreeRTOS上のプログラムが作れる状態になった。試しにサンプルを実行してみる。

  1. ファイル→スケッチ例→FreeRTOS→”Blink_AnalogRead”を選択
  2. ArduinoをPCに接続する。
  3. ツール→シリアルポート→Arduinoを接続しているポートを選択
  4. ツール→ボード→つなげたArduino(ここではArduino Nano)を選択
  5. スケッチ→検証・コンパイル
  6. スケッチ→マイコンボードに書き込む

動作確認

  • ボード上のLEDが2秒間隔(1秒点灯、1秒消灯)で点滅していること
  • シリアルポートに接続して、何らかの数字が出力され続けていること

実際には、LED点滅とアナログデータの読み出しを行う2つのタスクを実行している(見易さのためオリジナルのサンプルからほとんどのコメントを削除した)。

#include <Arduino_FreeRTOS.h>

void TaskBlink( void *pvParameters );
void TaskAnalogRead( void *pvParameters );

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ;
  }

  xTaskCreate(
    TaskBlink
    ,  (const portCHAR *)"Blink"   // A name just for humans
    ,  128  // This stack size can be checked & adjusted by reading the Stack Highwater
    ,  NULL
    ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL );

  xTaskCreate(
    TaskAnalogRead
    ,  (const portCHAR *) "AnalogRead"
    ,  128  // Stack size
    ,  NULL
    ,  1  // Priority
    ,  NULL );
}

void loop()
{
}

void TaskBlink(void *pvParameters)  // This is a task.
{
  (void) pvParameters;

  pinMode(LED_BUILTIN, OUTPUT);
  for (;;) // A Task shall never return or exit.
  {
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
  }
}

void TaskAnalogRead(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  for (;;)
  {
    int sensorValue = analogRead(A0);
    Serial.println(sensorValue);
    vTaskDelay(1);  // one tick delay (15ms) in between reads for stability
  }
}

Raspberry Pi 1

ビルド

FreeRTOS Ported to Raspberry Piの情報を参考にする。以下ではDebian Linux上でFreeRTOSのイメージを作成する手順を示す。

  1. Arm GCC関連のツールチェーンをインストールしておく。
    # apt-get install gcc-arm-none-eabi
  2. FreeRTOSパッケージを入手する。
    $&nbsp;git clone https://github.com/jameswalmsley/RaspberryPi-FreeRTOS
  3. パッケージをビルドする。
    $ cd RaspberryPi-FreeRTOS
    $ make
    (中略)
    *[LIST] [RaspberryPi BSP] kernel.list
    *[IMAGE] [RaspberryPi BSP] kernel.img
    *[SYMS] [RaspberryPi BSP] kernel.syms
    text data bss dec hex filename
    18346 12 5088 23446 5b96 kernel.elf
  4. Raspberry Piのブートローダ(bootcode.binstart.elf)を以下のサイトから入手する。
    https://github.com/raspberrypi/firmware/tree/master/boot
  5. SDカードをFAT32でフォーマットする。
  6. ビルドしてできたkernel.imgとブートローダ(bootcode.bin, start.elf)をSDカードのルートディレクトリに格納する。
  7. SDカードをRaspberry Pi 1に挿して電源を入れる(USBに接続する).

これでビルドしたカーネルが実行される。カーネルにはサンプルでLED点滅タスクが組み込まれている。

動作確認

  • ボード上の”ACT”LEDが0.2秒間隔で点滅する。

実際のサンプルは以下の通り(見易さのためコメントを削除した)。

#include <FreeRTOS.h>
#include <task.h>
#include "Drivers/irq.h"
#include "Drivers/gpio.h"

void task1(void *pParam) {

        int i = 0;
        while(1) {
                i++;
                SetGpio(16, 1);
                vTaskDelay(200);
        }
}

void task2(void *pParam) {

        int i = 0;
        while(1) {
                i++;
                vTaskDelay(100);
                SetGpio(16, 0);
                vTaskDelay(100);
        }
}

void main (void)
{
        SetGpioFunction(16, 1);                 // RDY led

        xTaskCreate(task1, "LED_0", 128, NULL, 0, NULL);
        xTaskCreate(task2, "LED_1", 128, NULL, 0, NULL);

        vTaskStartScheduler();

        while(1) {
                ;
        }
}

Windows 10 IoT Core編

Raspberry Pi 3 Model B

ビルド

Windows 10 IoT CoreのOSイメージを作成するには、Windows 10のPCが必須。またRaspberry Pi 3 Model B+では動かなかった。Raspberry Pi 3 Model Bを用意する。(Raspberry Pi 2は未確認)

  1. Windows 10 IoT DownloadsからRaspberry Pi 2 & 3向けのISOイメージをダウンロードする。
  2. ダウンロードしたISOファイルを右クリックしてマウントする。
  3. Windows_10_Iot_Core_for_RPi.msiをダブルクリックしてインストーラを実行する。
  4. 終わったらISOイメージをアンマウント(右クリックして取り出し)しておく(DVD-ROMイメージが適当なドライブ名にマウントされているはず)
  5. プログラムメニューから「Windows 10 IoT Core Dashboard」を実行する。
  6. 新しいデバイスのセットアップを選択する。
  7. 以下を入力する。
    デバイスの種類: Broadcomm [Raspberry Pi 2 & 3]
    OSビルド: Windows 10 IoT Core (17134)
    ドライブ: SDカードのドライブ
    デバイス名: 適当に (ここでは”rpi3bp”とした。Raspberry Pi 3 Model B plusの略)
    新しい管理者パスワード: 適当に
  8. SDカードをPCに挿す。内容がすべて消去されるので注意。
  9. 「ダウンロードとインストール」を実行するとWindows 10 IoT CoreのダウンロードとSDカードへの書き込みが実行される。
  10. 終わったらSDカードをRaspberry Pi 3 Model Bに挿し、電源を入れる(USBに接続する)。
  11. Windowsマークが表示された後、しばらく画面が黒い状態が続くがそのまま待つ(1分くらい)。
  12. 言語を選んで次へ。(今回は日本語を選択)
  13. ネットワーク接続を選択する。LANケーブルで直結する場合は「直接接続」、Wi-Fiを使う場合は「Wi-Fi」のところに表示されているアクセスポイントを選択する。
  14. Wi-Fiを選んだ場合はセキュリティキーを入力して次へ。
  15. Windows 10 PCで実行しているWindows 10 IoT Core Dashboardで「デバイスは正常に起動されましたか?」に「はい」と答える。

動作確認

  • Windows 10 IoT Core Dashboardの「自分のデバイス」に、Windows 10 IoT Coreをインストールしたデバイスが表示されていることを確認する。

Raspberry Pi公式タッチパネルを繋いでいると表示が上下反転している場合がある。この場合はSDカード内のうルートディレクトリにあるconfig.txtに以下の行を追加する。

lcd_rotate=2

NuttX編

SPRESENCE

SPERESENCEはArduino IDEによるGUIベースの開発環境と、Spresence SDKによるCLIベースの開発環境がある。それぞれインストール方法は以下を参照のこと。Spresence SDKはUbuntu 64bitのみサポート。他のディストリビューション(Debianとか)でも利用できるかは不明。

mbed LPC1768

環境

  • NuttXのビルド: Ubuntu 18.04
  • mbed LPC1768の接続先: Windows 10のUSB

ビルド

  1. ARM GCC(arm-none-eabi-gcc)のツールチェインをUbuntu上にインストールしておく。
  2. NuttXのソースを入手する。2個目のapps.gitは最初はいらないかもしれないがとりあえず。
    $ git clone https://bitbucket.org/nuttx/nuttx.git
    $ git clone https://bitbucket.org/nuttx/apps.git
  3. コンフィグ、ビルドを実行
    $ cd nuttx
    $ tools/configure.sh mbed/nsh
    $ make menuconfig
    $ make CROSSDEV=arm-none-eabi-
  4. mbed LPC1768をWindows PCにUSBで接続する。このときFドライブにマウントされたとする。
  5. mbedのシリアルドライバがインストールされていなければインストールしておく(Windows用→https://os.mbed.com/handbook/Windows-serial-configuration)
  6. カレントディレクトリにできたnuttx.binをFドライブの直下にコピーする。転送には今回sftpを用いた。
  7. mbed LPC1768のリセットボタンを押す。

動作確認

  • mbed LPC1768のシリアルポートにターミナルソフトで接続し、NSH (Nutt shell)のコマンドプロンプトが出ることを確認
NuttXシェル on mbed LPC1768

ESP32-DevKitC / M5Stack

Ubuntu上でビルド環境を構築する。構築は~/workspaceディレクトリ配下で行う。

環境

ビルドはVMware ESXi上のUbuntu 18.04を用いる。ESP32-DevKitCを接続するのはESXiとは異なるWindows 10 PCとする。

準備

  1. ESP-IDFを入手する。ESP-IDFが必要とするPythonパッケージもrequirements.txtに従い入れておく。
    $ mkdir ~/workspace/esp-idf
    $ cd ~/workspace/esp-idf
    $ git clone --recursive https://github.com/espressif/esp-idf.git
    $ export IDF_PATH=~/workspace/esp-idf/esp-idf
    $ python -m pip install --user -r ~/workspace/esp-idf/esp-idf/requirements.txt
    
  2. 前提パッケージとESP32用のツールチェインをインストールする。手元の環境で不足していたパッケージを1個ずつaptの引数に追加しているため、環境によってはすでに入っていたりこれでも足りなかったりするかもしれない。
    $ sudo apt install texinfo python-pip help2man gawk libtool-bin
    $ sudo pip install esptool
    $ mkdir ~/workspace/esp32
    $ cd ~/workspace/esp32
    $ git clone -b xtensa-1.22.x https://github.com/espressif/crosstool-NG.git
    $ cd crosstool-NG
    $ ./ct-ng xtensa-esp32-elf
    $ ./ct-ng build
    $ chmod -R u+w builds/xtensa-esp32-elf
    $ export PATH=~/workspace/esp32/crosstool-NG/builds/xtensa-esp32-elf/bin:$PATH

ビルド

  1. NuttXのソースを入手する。2個のapps.gitは最初はいらないかもしれないがとりあえず。
    $ mkdir ~/workspace/nuttx
    $ cd ~/workspace/nuttx
    $ git clone https://bitbucket.org/nuttx/nuttx.git
    $ git clone https://bitbucket.org/nuttx/apps.git
  2. NuttXをビルドする。
    $ cd nuttx
    $ make distclean
    $ ./tools/configure.sh esp32-core/nsh
    $ make
  3. ESP32用のブートローダとパーティションテーブルを作成する。それだけソースからコンパイルしても良いが、手抜きのためESP-IDFに入っているサンプルプログラムで作られるものを流用することにした。make menuconfigのときに、”USE RTC watchdog in start code”をオフにしておかないと、NuttXがウォッチドッグタイマーによる要因で無限リセットがかかる現象が発生しているので、そうした。それで正しいかは不明。
    $ cd ~/workspace/esp-idf/esp-idf/examples/get-started/hello_world
    $ make menuconfig
    $ make
    $ cd ~/workspace/nuttx/nuttx/
    $ cp ~/workspace/esp-idf/esp-idf/examples/get-started/hello_world/build/bootloader/bootloader.bin .
    $ cp ~/workspace/esp-idf/esp-idf/examples/get-started/hello_world/build/partitions_singleapp.bin .
    

    WDTをオフにする。
  4. ESP32-DevKitCをWindows PCに接続する。
  5. Ubuntuのリモートコンソールを開く。
  6. 右上のUSBアイコンを右クリックし、Windows PC上のUSBデバイスをゲストOSのUbuntuに接続する。
  7. できたカーネルイメージをESP32に転送する。
    $ esptool.py --chip esp32 elf2image --flash_mode dio --flash_size 4MB -o ./nuttx.bin nuttx
    $ esptool.py --chip esp32 --port /dev/ttyUSB0 write_flash 0x1000 bootloader.bin 0x8000 partitions_singleapp.bin 0x10000 nuttx.bin

    動作確認

  • シリアルでESP32-DevKitCに接続して、NSH (Nutt shell)が動作していること。
nsh on ESP32-DevKitC

同じバイナリをM5Stackにコピーしてみたところ同じように動作した。

TOPPERS編

GR-PEACH × TOPPER/ASP3

基本的に公式サイトの情報を参考にすればよい。
GR-PEACH特設 TOPPERS/ASPでマルチタスク!

ビルド

既定でビルドされるサンプルは”blinky”だが、以下のビルド手順ではTOPPERSスケジューラによるマルチタスク処理を試せる”multitask_arduino”をビルドするものとする。

  1. がじぇっとるねさすのwebコンパイラを起動する。→http://gadget.renesas.com/ja/
    (2018年9月23日現在) Chromeからだとwebコンパイラを起動できない。Edgeならできた。
  2. テンプレートから「GR-PEACH_TOPPERS_ASP_Vxxx.zip」を選択し、適当な名前のプロジェクト名を入力したうえで「プロジェクト作成」ボタンを押す。
    2018年9月23日現在、GR-PEACH_TOPPERS_ASP_V1.03.zipが選択可能。
  3. プロジェクトツリーにあるbuild_targetを開く(ダブルクリックする)。
  4. “./examples/blinky”となっている行を”./examples/multitask_arduino”に修正して保存する。
  5. 画面右側の「ビルド実行」を押す。
  6. GR-PEACHをPCのUSBに接続する。これによりPC上のドライブとしてマウントされる(Fドライブとする)。
  7. 左側のプロジェクトツリーからexamples/multitask_arduino/multitask_arduino.binを右クリックし、GR-PEACHがマウントされたドライブ(Fドライブ)に保存する。

動作確認

  • GR-PEACH上のリセットボタンを押し、ボード上の赤、緑、青、ユーザーLEDがそれぞれ定めれた時間ごとに点滅を繰り返す。

実際のサンプルコードは以下。loop()、loop1()、loop2()の3つのループが並列処理される。

#include <kernel.h>
#include <t_syslog.h>
#include <t_stdlib.h>
#include "syssvc/serial.h"
#include "syssvc/syslog.h"
#include "kernel_cfg.h"

#include "arduino_app.h"
#include "arduino_main.h"

/* GR-PEACH Sketch Template V1.00 */
#include <Arduino.h>

#define INTERVAL       100
#define INTERVAL_RED   100
#define INTERVAL_BLUE  150
#define INTERVAL_GREEN 200

void setup()
{
    pinMode(PIN_LED_RED   , OUTPUT);
    pinMode(PIN_LED_GREEN , OUTPUT);
    pinMode(PIN_LED_BLUE  , OUTPUT);
    pinMode(PIN_LED_USER  , OUTPUT);
	pinMode(PIN_SW        , INPUT);

	while(digitalRead(PIN_SW) == 0){
		digitalWrite(PIN_LED_USER, 1);
        delay(INTERVAL);
        digitalWrite(PIN_LED_USER, 0);
        delay(INTERVAL);
	}	
}

void loop()
{
	digitalWrite(PIN_LED_RED, 1);
	delay(INTERVAL_RED);
	digitalWrite(PIN_LED_RED, 0);
	delay(INTERVAL_RED);
}

void loop1(){
    digitalWrite(PIN_LED_GREEN, 1);
    delay(INTERVAL_GREEN);
    digitalWrite(PIN_LED_GREEN, 0);
	delay(INTERVAL_GREEN);
}

void loop2(){
    digitalWrite(PIN_LED_BLUE, 1);
    delay(INTERVAL_BLUE);
	digitalWrite(PIN_LED_BLUE, 0);
	delay(INTERVAL_BLUE);
}

/*
 *  Cyclic Handler
 * 
 *  This handler is called every 10 [ms] as specified in
 *  multitask_arduino.cfg.
 */
void cyclic_handler(intptr_t exinf) {
	irot_rdq(LOOP_PRI); /* change the running loop. */
}

Azure Secure OS編

MT3620 Development Kit

以下に示す手順は私が行ったものであり、他の環境でうまくいくかは検証していないことに注意。Azureのテナントやアカウントを作成していくが、それぞれのステップで何をしているのか、言葉や内容を事前に理解しておくのが望ましい。

Getting started with Azure Sphereのページを参考にする。

前提

  • Windows 10 Aniversary Update以降のPC
  • Visual Studio 2017 Enterprise, Professional, Commumity version 15.7以降

インストール

  1. Azure Sphere SDK for Visual Studio Previewをダウンロードする。
  2. Azure Portalにログインする。
  3. Azure ADテナント(Azure Active Directory, Azure AD, AADとも呼ぶ)を作成する。→このショートカットから作成可能。初期ドメイン名で指定した名称をDOMAINとすると、作成したAzure ADテナントのドメイン名は「DOMAIN.onmicrosoft.com」となる。
    組織名: 作成するディレクトリの名称  (今回は”asamomiji”とした)
    初期ドメイン名: このディレクトリのドメイン名  (今回は”asamomiji”とした)
  4. 左のペインから”Azure Active Directory”を選んだあと、その右のペインで”ユーザー”を選ぶ。
  5. 新しいユーザーを作成する。作成時に表示されるパスワードを控えておく。
    名前: 自分の名前
    ユーザー名: 名前@ディレクトリ名.onmicrosoft.com
    ディレクトリロール: 全体管理者
  6. MT3620をWindows 10のPCにUSBで接続する。
  7. デバイスマネージャの「ポート(COMとLPT)」でUSBシリアルポートができていることを確認する(手元の環境ではCOM13, COM15, COM16の3つのポートができていた)。
  8. ダウンロードしたVS_Tools_Preview_for_Azure_Sphere.exeを実行してAzure Sphere SDKをインストールする。
  9. 次のステップからMT3620をAzure Sphereテナントに割り当てる作業を行う。ここで重要な注意事項がある。
    • Azure SphereテナントへのMT3620の割当はやり直しができない
    • デバイスを他人や他の組織に売り渡す場合でも不可能 (一度テナントに割り当てられたら他者への譲渡はできない)
    • 一度テナントに割り当てると、そのデバイスは永久にそのテナントに括り付けられる
  10. Azure Active Directoryの概念を正しく理解することを強く推奨する。ステップ3のAzure ADテナントと、これから作るAzure Sphereテナントは異なる。
  11. 上記注意事項を理解したら次に進む。ステップ12~ステップ14はまだやり直し可能。
  12. Azure Sphere Developer Command Promptを開く。Azure Sphere SDKをインストールしたWindows 10 PCのスタートメニュー→Azure Sphereフォルダにある。
  13. コマンドプロンプトからazsphereコマンドでAzure Sphereにサインインする。アカウントの確認が求められたらステップ5で作成したユーザ名を選択する。パスワードの変更を求められたら適切なパスワードを設定する。
    >azsphere login
    warn: No Azure Sphere tenants found under the selected AAD user. Use 'azsphere login' to change AAD user, or create a new Azure Sphere tenant using 'azsphere tenant create'.
    Successfully logged in with the selected AAD user. This authentication will be used for subsequent commands.
    Command completed successfully in 00:01:57.6290316.
    
  14. 上記のようにAzure Sphereテナントが存在しないというエラー(No Azure Sphere tenants found under the selected AAD user)が出たら、Azure Sphereテナントを作る必要がある。
    Azure Sphere Developer Command Promptで以下のコマンドを実行する。–nameの後ろにAzure Sphereテナント名を指定する。以下の例ではテナント名を’iot’とした。アカウントの確認が求められたらステップ5で作成したユーザ名を選択する。

    >azsphere tenant create --name iot
    Created a new Azure Sphere tenant:
     --> Tenant Name: iot
     --> Tenant ID:   xxxx
    Selected Azure Sphere tenant 'iot' as the default.
    You may now wish to claim the attached device into this tenant using 'azsphere device claim'.
    Command completed successfully in 00:00:18.3797519.
  15. デバイスをAzure Sphereテナントに登録する。このステップは不可逆でありやり直しがきかない。

    >azsphere device claim
    Claiming device.
    Successfully claimed device ID 'xxxx' into tenant 'iot' with ID 'xxxx'.
    Command completed successfully in 00:00:02.8853005.
  16. MT3620にWi-Fiを設定する。
    >azsphere device wifi add --ssid <SSID> --key <アクセスキー>
    Add network succeeded:
    ID                  : 0
    SSID                : <SSID>
    Configuration state : enabled
    Connection state    : unknown
    Security state      : psk
    
    Command completed successfully in 00:00:01.5804944.
  17. Wi-Fiの接続状態を確認する。
    >azsphere device wifi show-status
    SSID                : <SSID>
    Configuration state : enabled
    Connection state    : connected
    Security state      : psk
    Frequency           : 2422
    Mode                : station
    Key management      : WPA2-PSK
    WPA State           : COMPLETED
    IP Address          : 192.168.1.134
    MAC Address         : 2c:f7:f1:XX:XX:XX
    
    Command completed successfully in 00:00:00.7497091.
  18. MT3620がWi-Fiに接続すると、Azure Sphere OSと現在のアプリケーションの更新データがあるかをチェックし、もしあれば自動的にダウンロードされる(over-the-airアップデート/OTAアップデート)。OTAアップデートの状態は以下のコマンドで確認できる。
    >azsphere device show-ota-status
    The Azure Sphere Security Service is targeting this device with OS version TP4.2.1
    Your device has the expected version of the Azure Sphere OS: TP4.2.1.
    Command completed successfully in 00:00:03.1851583.

    ここまででMT3620のセットアップは完了した。

テスト用アプリケーションの作成

以下のページを参考にする。
Quickstart: Build the Blink sample application

  1. MT3620は初期状態で「ロック」されており、アプリケーションを書き込めないようになっている。まずMT3620をアンロックする。
    >azsphere device prep-debug
    Getting device capability configuration for application development.
    Downloading device capability configuration for device ID 'XXXX'.
    Successfully downloaded device capability configuration.
    Successfully wrote device capability configuration file 'C:\....\AppData\Local\Temp\tmpA688.tmp'.
    Setting device group ID 'XXXX' for device with ID 'XXXX'.
    Successfully disabled over-the-air updates.
    Enabling application development capability on attached device.
    Applying device capability configuration to device.
    Successfully applied device capability configuration to device.
    The device is rebooting.
    Installing debugging server to device.
    Deploying 'C:\Program Files (x86)\Microsoft Azure Sphere SDK\DebugTools\gdbserver.imagepackage' to the attached device.
    Image package 'C:\Program Files (x86)\Microsoft Azure Sphere SDK\DebugTools\gdbserver.imagepackage' has been deployed to the attached device.
    Application development capability enabled.
    Successfully set up device 'XXXX' for application development, and disabled over-the-air updates.
    Command completed successfully in 00:00:38.3733280.
  2. Visual Studio 2017を起動する。
  3. 「ファイル >新規作成>プロジェクト」を選ぶ。
  4. テンプレートとして「インストール済み>Visual C++>テスト>Azure Sphere」から、「Blink Sample for MT3620 RDB (Azure Sphere)」を選ぶ。
  5. 名前と場所を指定する。
  6. main.cの以下の行にブレークポイントを設定する(行を探してF9を押す)。
    if (newButtonState == GPIO_Value_Low) {
  7. F5を押してビルドを開始する。ビルドが終わるとプログラムが自動的にMT3620に書かれる。
  8. この時点でMT3620上のLED1が点滅する。
  9. MT3620上のボタンAを押すと、Visual Studio上でステップ6で設定したブレークポイントで一時停止する。
  10. 「デバッグ>ウインドウ>自動変数」を選ぶと、ブレークポイント時点での自動変数の値を確認できる。
  11. プログラムの実行を「続行」すると再度ブレークポイントで停止する。ステップ10とはbuttonState変数とnewButtonState変数の値が変わっていることが分かる。
  12. テストを終わるためにShift-F5を押す。

コメントを残す

メールアドレスが公開されることはありません。