FC2カウンター FPGAの部屋 2020年07月10日
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