FPGAの部屋 Wio Terminal
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

Wio Terminal をArduino IDE で使う3(Timer を使用して 3 軸加速度センサーを計測する)

Wio Terminal をArduino IDE で使う2(Windows 10 で 3 軸加速度センサーを使う)”の続き。

前回は、Windows 10 に Arduino IDE をインストールして、3 軸加速度センサーの値を LCD に表示することができた。今回は、とりあえず、データを取得するために 3 軸加速度センサーの値をシリアルモニタに表示した。そこで、ループで 3 軸加速度センサーの値を取得すると時間間隔がばらついてしまうためタイマー割り込みを使用して、実装した。

最初に、ブログに”Wio Terminal: ATSAMD51& Realtek RTL8720DN BLE 5.0 & Wi-Fi 2.4G/5G 開発ボード”のリンクを張って、ブログ記事を書くと 10 ドルのクーポンをくれるというオファーがあった。よって、この部分はステマであることに注意されたい。秋月電子でも Wio Terminal の在庫が復活したので、こちらの方が早く来るとは思う。
ブログ記事は、いつか書こうと思っていたので、ステマというわけではない。

さて、最初に前回のプログラムをもとに、シリアルモニタに表示するプログラムを作成した。今回は、micros() 関数を使用して、加速度の取得間隔を計測する。
ime_sample2.ino を示す。

#include"LIS3DHTR.h"
LIS3DHTR<TwoWire> lis;
unsigned long time1, time2;
float x_values, y_values, z_values;

void setup() {
  Serial.begin(115200);
  lis.begin(Wire1);
 
  if (!lis) {
    Serial.println("ERROR");
    while(1);
  }
  lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ); //Data output rate
  lis.setFullScaleRange(LIS3DHTR_RANGE_2G); //Scale range set to 2g

  time2 = micros();
}
 
void loop() {
  time1 = micros();
  x_values = lis.getAccelerationX();
  y_values = lis.getAccelerationY();
  z_values = lis.getAccelerationZ();

  Serial.print(time1-time2);
  Serial.print(" "); Serial.print(x_values,3);
  Serial.print(" "); Serial.print(y_values,3);
  Serial.print(" "); Serial.print(z_values,3);
  Serial.println();
  delay(96);

  time2 = time1;
}


プログラムの実行結果の一部を示す。

99656 -0.056 -0.720 -0.564
99556 -0.420 -0.888 -0.812
100069 0.484 -0.744 -0.624
100181 -0.056 -0.820 -0.560
99907 -0.012 -0.700 -0.644
100551 -0.152 -0.780 -0.936
99978 0.040 -0.780 -0.684
100634 0.004 -0.792 -0.676
100858 0.008 -0.676 -0.516


最初の列が 3 軸加速度センサーの取得間隔なのだが、やはりばらついている。全部で 137 個データを取得したので、Excel に入れてグラフを書いてみた。
Wio_Terminal_Arduino_11_200710.png

3 軸加速度センサーの取得間隔の最大値が 101258 us で、最小値が 98559 us 、その差が 2699 us ということになった。
これだけばらついてはまずいということで、@ciniml さんの”ciniml/ISRBlink.ino”を引用させていただいて、タイマー割り込みを使用した 3 軸加速度センサーのデータ取得プログラムを作成した。ほとんど”ciniml/ISRBlink.ino”を引用している。 @ciniml さん、ありがとうございました。

タイマー割り込みの 3 軸加速度センサーのデータ取得プログラム、ime_isr_serial2.ino を示す。

//#include "SAMD51_TC.h"

#include <samd.h>

#include <cstdint>
#include <array>
#include"LIS3DHTR.h"

LIS3DHTR<TwoWire> lis;
unsigned long time1, time2;
float x_values, y_values, z_values;

#define GENERIC_CLOCK_GENERATOR_1M 5u

class SAMD51TCInterruptHelper;

class SAMD51TC
{
public:
    /**
     * @brief Create a new instance of SAM51TC for the TCx peripheral where x == tcUnit.
     * @param [in] tcUnit index of TC peripheral.
     */
    SAMD51TC(std::uint_least8_t tcUnit) : tcUnit(tcUnit), regs(nullptr), isrCallback(nullptr), microseconds(0)
    {
        switch(tcUnit)
        {
            case 0: {this->regs = &TC0->COUNT16; this->irqn = TC0_IRQn; break;}
            case 1: {this->regs = &TC1->COUNT16; this->irqn = TC1_IRQn; break;}
            case 2: {this->regs = &TC2->COUNT16; this->irqn = TC2_IRQn; break;}
            case 3: {this->regs = &TC3->COUNT16; this->irqn = TC3_IRQn; break;}
            case 4: {this->regs = &TC4->COUNT16; this->irqn = TC4_IRQn; break;}
            case 5: {this->regs = &TC5->COUNT16; this->irqn = TC5_IRQn; break;}
            case 6: {this->regs = &TC6->COUNT16; this->irqn = TC6_IRQn; break;}
            case 7: {this->regs = &TC7->COUNT16; this->irqn = TC7_IRQn; break;}
        }
    }

    void initialize(std::uint32_t microseconds = 1000000)
    {
        this->configureClock();

        this->regs->CTRLA.bit.SWRST = 1;
        while(this->regs->SYNCBUSY.bit.SWRST);

        this->setPeriod(microseconds);
    }
    void setPeriod(std::uint32_t microseconds)
    {
        this->microseconds = microseconds;
    }

    void __attribute__((noinline)) start()
    {
        this->regs->CTRLA.bit.ENABLE = 0;
        while(this->regs->SYNCBUSY.bit.ENABLE);

        this->regs->COUNT.reg = 0;
        while(this->regs->SYNCBUSY.bit.COUNT);

        const std::array<uint_fast8_t, 8> prescaler_shifts = { 0, 1, 2, 3, 4, 6, 8, 10 };
        for( std::uint_fast8_t index = 0; index < prescaler_shifts.size(); index++ ) {
            const auto compare_value = this->microseconds >> prescaler_shifts[index];
            if( compare_value <= 65535 ) {
                this->regs->CTRLA.bit.PRESCALER = index;
                this->regs->CC[0].reg = compare_value;
                break;
            }
        }
        while(this->regs->SYNCBUSY.bit.CC0);

        this->regs->WAVE.bit.WAVEGEN = TC_WAVE_WAVEGEN_MFRQ_Val;

        this->regs->INTENSET.bit.MC0 = 1;
        NVIC_EnableIRQ(this->irqn);

        this->regs->CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val;
        this->regs->CTRLA.bit.ENABLE = 1;
        while(this->regs->SYNCBUSY.bit.ENABLE);

        this->regs->CTRLBSET.bit.CMD = TC_CTRLBCLR_CMD_RETRIGGER_Val;
        while(this->regs->SYNCBUSY.bit.CTRLB);
    }
    void stop()
    {
        this->regs->CTRLA.bit.ENABLE = 0;
        while(this->regs->SYNCBUSY.bit.ENABLE);

        this->regs->INTENCLR.bit.MC0 = 1;
        NVIC_DisableIRQ(this->irqn);
        NVIC_ClearPendingIRQ(this->irqn);
    }

    void restart()
    {
        this->regs->CTRLBSET.bit.CMD = TC_CTRLBCLR_CMD_RETRIGGER_Val;
        while(this->regs->SYNCBUSY.bit.CTRLB);
    }

    void attachInterrupt(void (*isrCallback)())
    {
        this->isrCallback = isrCallback;
        this->start();
    }
    void detachInterrupt()
    {
        this->stop();
        this->isrCallback = nullptr;
    }

private:
    std::uint_least8_t tcUnit;
    TcCount16* regs;
    IRQn_Type irqn;
    std::uint32_t microseconds;
    void (*isrCallback)();

    void configureClock()
    {
        switch(this->tcUnit) {
            case 0: {MCLK->APBAMASK.bit.TC0_ = 1; GCLK->PCHCTRL[TC0_GCLK_ID].reg = GCLK_PCHCTRL_GEN(GENERIC_CLOCK_GENERATOR_1M) | GCLK_PCHCTRL_CHEN;  break;}
            case 1: {MCLK->APBAMASK.bit.TC1_ = 1; GCLK->PCHCTRL[TC1_GCLK_ID].reg = GCLK_PCHCTRL_GEN(GENERIC_CLOCK_GENERATOR_1M) | GCLK_PCHCTRL_CHEN;  break;}
            case 2: {MCLK->APBBMASK.bit.TC2_ = 1; GCLK->PCHCTRL[TC2_GCLK_ID].reg = GCLK_PCHCTRL_GEN(GENERIC_CLOCK_GENERATOR_1M) | GCLK_PCHCTRL_CHEN;  break;}
            case 3: {MCLK->APBBMASK.bit.TC3_ = 1; GCLK->PCHCTRL[TC3_GCLK_ID].reg = GCLK_PCHCTRL_GEN(GENERIC_CLOCK_GENERATOR_1M) | GCLK_PCHCTRL_CHEN;  break;}
            case 4: {MCLK->APBCMASK.bit.TC4_ = 1; GCLK->PCHCTRL[TC4_GCLK_ID].reg = GCLK_PCHCTRL_GEN(GENERIC_CLOCK_GENERATOR_1M) | GCLK_PCHCTRL_CHEN;  break;}
            case 5: {MCLK->APBCMASK.bit.TC5_ = 1; GCLK->PCHCTRL[TC5_GCLK_ID].reg = GCLK_PCHCTRL_GEN(GENERIC_CLOCK_GENERATOR_1M) | GCLK_PCHCTRL_CHEN;  break;}
            case 6: {MCLK->APBDMASK.bit.TC6_ = 1; GCLK->PCHCTRL[TC6_GCLK_ID].reg = GCLK_PCHCTRL_GEN(GENERIC_CLOCK_GENERATOR_1M) | GCLK_PCHCTRL_CHEN;  break;}
            case 7: {MCLK->APBDMASK.bit.TC7_ = 1; GCLK->PCHCTRL[TC7_GCLK_ID].reg = GCLK_PCHCTRL_GEN(GENERIC_CLOCK_GENERATOR_1M) | GCLK_PCHCTRL_CHEN;  break;}
        }
    }
public:
    void processIsr()
    {
        if( this->regs->INTFLAG.bit.MC0 ) {
            this->regs->INTFLAG.bit.MC0 = 1;
            if( this->isrCallback != nullptr ) {
                this->isrCallback();
            }
        }
    }
};

SAMD51TC TimerTC3(3);
void TC3_Handler() { TimerTC3.processIsr(); }

bool isLEDOn = false;
char time = 0;

void setup() 
{
    TimerTC3.initialize(100000); // microseconds
    TimerTC3.attachInterrupt(timerIsr);

    Serial.begin(115200);
    lis.begin(Wire1);
   
    if (!lis) {
      Serial.println("ERROR");
      while(1);
    }
    lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ); //Data output rate
    lis.setFullScaleRange(LIS3DHTR_RANGE_2G); //Scale range set to 2g

    time2 = micros();
}
 
void loop()
{
    /*
    time ++;
    if(time == 10)TimerTc3.detachInterrupt();
    else if(time == 20)
    {
       TimerTc3.attachInterrupt(timerIsr);
       time = 0;
    }
    delay(1000);
    */
}

void timerIsr()
{    
    time1 = micros();
    x_values = lis.getAccelerationX();
    y_values = lis.getAccelerationY();
    z_values = lis.getAccelerationZ();

    Serial.print(time1-time2);
    Serial.print(" "); Serial.print(x_values,3);
    Serial.print(" "); Serial.print(y_values,3);
    Serial.print(" "); Serial.print(z_values,3);
    Serial.println();

    time2 = time1;
}


プログラムの実行結果の一部を示す。

97002 -0.104 -0.276 -1.088
98002 -0.044 -0.260 -0.856
97002 -0.100 -0.212 -0.932
97002 -0.036 -0.004 -1.020
97002 -0.164 -0.192 -0.980
97002 -0.104 -0.160 -0.928
97002 -0.228 0.000 -1.732
97002 -0.080 -0.212 -1.044
97002 -0.060 -0.184 -1.000
97002 -0.052 -0.096 -0.944


最初の列が 3 軸加速度センサーの取得間隔なのだが、大体一定しているが、たまに違う値がある。全部で 105 個データを取得したので、Excel に入れてグラフを書いてみた。
Wio_Terminal_Arduino_12_200710.png

3 軸加速度センサーの取得間隔の最大値が 99002 us で、最小値が 96002 us 、その差が 3000 us ということになった。安定はしているが 3000 us ばらついている。
  1. 2020年07月10日 04:27 |
  2. Wio Terminal
  3. | トラックバック:0
  4. | コメント:0

Wio Terminal をArduino IDE で使う2(Windows 10 で 3 軸加速度センサーを使う)

Wio Terminal をArduino IDE で使う1”の続き。

前回は、Ubuntu 18.04 で Arduino IDE をインストールしたが、いろいろなサンプル・スケッチが動作しなかった。それではWindows 10 はどうだろう?ということで、前回を参照して、Windows 10 に Arduino IDE をインストールして、Wio Terminal 用のボードマネージャを設定した。今回は、3 軸加速度センサーのライブラリをインストールして、液晶画面に 3 軸加速度センサーの値を表示した。

まずは、Windows 10 用の Arduino IDE をインストーラーでインストールした。
Download the Arduino IDE ページから Winodows 用のインストーラーをダウンロードして、インストールした。

Arduino IDE のファイルメニュー -> 環境設定を選択して、開いた環境設定ダイアログの”追加のボードマネージャのURL :”に

https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json

を入力した。
Wio_Terminal_Arduino_1_200609.png

ツールメニュー -> ボード -> ボードマネージャを選択してダイアログを開いた。
Seeed SAMD Boards があるので”インストール”ボタンをクリックした。(既にインストール済み)
Wio_Terminal_Arduino_2_200609.png

Ubuntu 18.04 では動作しなかったファイルメニュー -> スケッチ例 -> Seeed_Arduino_LCD -> Generic -> drawXBitmap も動作した。
Wio_Terminal_Arduino_3_200609.png

Wio_Terminal_Arduino_4_200609.jpg

次に、3 軸加速度センサーのライブラリを”Installing the 3-Axis Digital Accelerometer(LIS3DHTR) Library For Wio Terminal”を参考にしてインストールする。

GitHub の Seeed-Studio/Seeed_Arduino_LIS3DHTR を ZIP でダウンロードする。

Seeed_Arduino_LIS3DHTR-master.zip がダウンロードできた。
Arduino IDE のスケッチメニュー -> ライブラリをインクルード -> .ZIP 形式のライブラリをインストール... を選択する。
ダイアログで Seeed_Arduino_LIS3DHTR-master.zip を指定する。
Wio_Terminal_Arduino_5_200609.png

スケッチメニュー -> ライブラリをインクルードに Grove 3-Axis Digital Accelerometer ±2g to 16g (LIS3DHTR) が追加された。
Wio_Terminal_Arduino_6_200609.png

Getting Started with IMU を見て、3 軸加速度センサーの Example Code を試してみた。
Wio_Terminal_Arduino_7_200609.png

シリアルモニタを表示して、ソフトウェアを起動した。
Wio_Terminal_Arduino_8_200609.png

加速度が取れている。

これだけだと、上に流れていくのが早すぎてよく分からないので、LCD の固定位置に表示することにした。
Getting Started with IMU の 3 軸加速度センサーの Example Code や Using different Fonts などを参考にして、ime_lcd_disp1.ino を作った。ime_lcd_disp1.ino を貼っておく。

#include"LIS3DHTR.h"
LIS3DHTR<TwoWire> lis;

#include"TFT_eSPI.h"
#include <SPI.h>
TFT_eSPI tft;

void setup() {
  lis.begin(Wire1);
 
  if (!lis) {
    Serial.println("ERROR");
    while(1);
  }
  lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ); //Data output rate
  lis.setFullScaleRange(LIS3DHTR_RANGE_2G); //Scale range set to 2g

  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(TFT_BLACK); //Black background
}

void loop() {
  float x_values, y_values, z_values;
  x_values = lis.getAccelerationX();
  y_values = lis.getAccelerationY();
  z_values = lis.getAccelerationZ();
  
  char x_str[100], y_str[100], z_str[100];
  dtostrf(x_values, 6, 2, x_str);
  dtostrf(y_values, 6, 2, y_str);
  dtostrf(z_values, 6, 2, z_str);

  tft.drawString(x_str, 70, 60, 6);
  tft.drawString(y_str, 70,110, 6);
  tft.drawString(z_str, 70,160, 6);

  delay(100);
}


Wio_Terminal_Arduino_9_200609.png

一番上が x 軸、その下が Y 軸、一番下が Z 軸だ。
Wio_Terminal_Arduino_10_200609.jpg
  1. 2020年06月09日 05:01 |
  2. Wio Terminal
  3. | トラックバック:0
  4. | コメント:0

Wio Terminal をArduino IDE で使う1

秋月電子に頼んでおいた Wio Terminal が届きました。
Wio_Terminal_1_200523.jpg

Wio_Terminal_2_200523.jpg

USB-C ケーブルを Ubuntu 18.04 LTS のパソコンにつないで電源を入れると、ゲームが起動しました。
Wio_Terminal_3_200523.jpg

スタートボタンを押すとゲームが始まってすぐにゲームオーバーになってしまった。。。
Wio_Terminal_4_200523.jpg

プログラムしたいので、”Wio Terminalをはじめよう”を見ながら、Linux 64 bit 版の Arduino をダウンロードした。

ダウンロードした arduino-1.8.12-linux64.tar.xz を解凍して ~/arduino-1.8.12-linux64 ディレクトリに解凍した。
Ubuntu 18.04にArduino IDEをインストールして、プログラムをArduinoに書き込む”を見ながら Ubuntu 18.04 LTS に Arduino をインストールした。

~/arduino-1.8.12-linux64/arduino-1.8.12 ディレクトリに行って
./install_sh
だと
sudo ./install_sh
でインストールできた。
Arduino_1_200522.png

デスクトップに arduino-arduinoide.desktop ができた。
Arduino_2_200522.png

arduino-arduinoide.desktop をダブルクリックして起動すると、信用できないアプリケーションのランチャー・ダイアログが表示された。
Arduino_3_200522.png

信頼して起動ボタンをクリックして起動する。

Arduino IDE が起動した。
Arduino_4_200522.png

以降、設定は”Wio Terminalをはじめよう”を見ながら設定を行う。

Arduino IDE のファイルメニュー -> 環境設定を選択して、開いた環境設定ダイアログの”追加のボードマネージャのURL :”に

https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json

を入力して、OKボタンをクリックした。
Arduino_5_200522.png

ツールメニュー -> ボード -> ボードマネージャを選択してダイアログを開いた。
Seeed SAMD Boards があるので”インストール”ボタンをクリックした。
Arduino_6_200522.png

インストールが始まった。
Arduino_7_200522.png

インストールが成功した。
Arduino_8_200522.png

ツールメニュー -> ボード -> Seeeduino Wio Terminal を選択した。
Arduino_9_200522.png

Wio Terminal の電源をON してツールメニュー -> シリアルポート -> /dev/ttyACM0 (Seeeduino Wio Terminal) を選択した。
Arduino_10_200522.png

ファイルメニュー -> スケッチ例 -> 0.1Basics -> Blink を選択して、Blink スケッチを開いた。
Arduino_11_200522.png

”マイコンボードに書き込む”ボタンをクリックしたが、書き込むことができなかった。権限が無いということで、sudo を入れて起動することにした。
ショートカットは /home/masaaki/arduino-1.8.12-linux64/arduino-1.8.12/arduino なので、
sudo /home/masaaki/arduino-1.8.12-linux64/arduino-1.8.12/arduino
でどうすると root なので、もう一度設定をやり直した。

今度は Blink を起動すると書き込めた。
Arduino_12_200522.png

Wio Terminal の窓の奥で青いLED がブリンクしているのが見えた。

現在のツールメニューを示す。
Arduino_13_200522.png

なお、いろいろとやってみたのだが、変なのをやってみちゃうとシリアルポートが見えなくなってしまった。そういう時は、パワースイッチの反対側が戻るリセットスイッチなので、2回(自分の間隔ではかなり素早く)リセットすると、見えるようになった。

そして、”マイコンボードに書き込む”ボタンをクリックして書き込みしているのに 2 回に 1 回くらいは書き込みが途中で終わってしまうことがある。
Arduino_14_200522.png
  1. 2020年05月23日 21:55 |
  2. Wio Terminal
  3. | トラックバック:0
  4. | コメント:2