質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.53%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

2回答

1873閲覧

ADXL345加速度センサの読み出しに時間がかかる。

masay0

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

1クリップ

投稿2021/09/13 06:03

前提・実現したいこと

 ADXL345加速度センサICで3軸方向の加速度を定期的に計測し、CPUのメモリに保存したい。

発生している問題・エラーメッセージ

 FFT処理を行うために、1回に取るデータは256組または512組としたいが、ADXL345から連続的に読み出すデータの組数がある程度以上になると、読み出しにかかる時間が異常に長くなる。
下記ソースコード中のloop()関数中の

for (i = 0; i < 128; i++) {    adxl345_get_axi(accData[i]);  // 加速度データ3軸分を読み出す }

 forループ回数が128以下の場合、読み出しにかかる時間は、約120μs×ループ回数程度ですが、回数を150回程度にすると、約1ms×ループ回数程度に増えます。回数を256回にすると、forループから帰ってこなくなります。

該当のソースコード

 ソースコード(下記)

/* Name: ADXL345 Reading Sample for Wio LTE JP.ino Created: 2021/09/09 9:33:14 Author: */ #include <Wire.h> #include <WioLTEforArduino.h> WioLTE Wio; #define ADXL345_I2C_ADRESS 0x53 // I2C address of ADXL345 #define ADXL345_DATAX0 0x32 // Output data register #define ADXL345_SCALE_FACTOR 1 // LSB = 4m[G] #define ADXL345_DEVID 0x00 // Device ID register #define ADXL345_DEV_ID 0xe5 // Device's ID #define ADXL345_POWER_CTL 0x2d // Power control register #define ADXL345_INT_SOURCE 0x30 // Interrupt source register, bit 7 is data-ready #define ADXL345_BW_RATE 0x2c // Bandwidth register // The setup() function runs once each time the micro-controller starts void setup() { SerialUSB.begin(115200); delay(2000); SerialUSB.println("Read ADXL345"); SerialUSB.println("Initilize Wio LTE JP"); Wio.Init(); SerialUSB.println("Setup I2C"); Wire.begin(); Wire.setClock(1000000); // I2Cのクロックを1MHzに設定 SerialUSB.println("Initilize ADXL345"); //ArduinoとADXL345のI2C通信確認 uint8_t ucData; adxl345_get_data(ADXL345_DEVID, &ucData, 1); //デバイスID取得 if (ucData != ADXL345_DEV_ID) { SerialUSB.println(F("I2C connection fail.")); } //ADXL345の帯域を3.2kHzに設定 adxl345_set_data(ADXL345_BW_RATE, 0x0f); //ADXL345を測定モードへ変更 adxl345_set_data(ADXL345_POWER_CTL, 0x08); delay(100); SerialUSB.println("ADXL345 Initialized, Start measuring."); } // Add the main program code into the continuous loop() function void loop() { int i; unsigned long time1,time2,time3; float accData[512][3]; time1 = micros(); // 読み出し開始時刻(μs) for (i = 0; i < 128; i++) { adxl345_get_axi(accData[i]);  // 加速度データ3軸分を読み出す } time2 = micros(); // 3軸×512データ 読み出し完了時刻 SerialUSB.print("Read time:"); SerialUSB.print(time2 - time1); // ADXL345の読出しにかかった時間を表示 // 読出し保存したデータの先頭を確認のために表示 for(i=0;i<2; i++){ SerialUSB.print(" X:"); SerialUSB.print(accData[i][0]); SerialUSB.print(" Y:"); SerialUSB.print(accData[i][1]); SerialUSB.print(" Z:"); SerialUSB.print(accData[i][2]); SerialUSB.println(); delay(100); } delay(10000); // 10秒間待ち } /*ADXL345のレジスタデータ書き込み関数*/ void adxl345_set_data(uint8_t ucReg, uint8_t ucData) { Wire.beginTransmission(ADXL345_I2C_ADRESS); Wire.write(ucReg); Wire.write(ucData); Wire.endTransmission(true); } /*ADXL345のレジスタデータ読み込み関数*/ void adxl345_get_data(uint8_t ucReg, uint8_t* ucData, uint8_t ucSize) { uint8_t cnt = 0; Wire.beginTransmission(ADXL345_I2C_ADRESS); Wire.write(ucReg); Wire.endTransmission(false); Wire.requestFrom(ADXL345_I2C_ADRESS, ucSize, true); while (Wire.available()) { ucData[cnt] = Wire.read(); cnt++; } } /*ADXL345 X,Y,Zデータ取得関数*/ void adxl345_get_axi(float* pfData) { uint8_t ucBuf[6]; int16_t acc[3]; adxl345_get_data(ADXL345_DATAX0, ucBuf, 6); acc[0] = ((int16_t)ucBuf[1] << 8) | ucBuf[0]; acc[1] = ((int16_t)ucBuf[3] << 8) | ucBuf[2]; acc[2] = ((int16_t)ucBuf[5] << 8) | ucBuf[4]; pfData[0] = acc[0] * ADXL345_SCALE_FACTOR; pfData[1] = acc[1] * ADXL345_SCALE_FACTOR; pfData[2] = acc[2] * ADXL345_SCALE_FACTOR; }

試したこと

 1回に読み出すデータの組数を変化させて、一組のデータを読むのに必要な時間を計測した。
組数が少ないうちは、一組当たり120μs程度で安定しているが、組数を増やしていくと、一組当たりの読み出し時間が急激に長くなる。

補足情報(FW/ツールのバージョンなど)

 使用している開発環境: Aruduino IDE (Ver. 1.8) (C言語)
使用しているボード:  Seeed社 Wio LTE JP Version
加速度センサ:     ACCEL ADXL345 (I2C接続、SwitchScience通販で購入)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2021/09/13 06:12

Wire.setClock(1000000); // I2Cのクロックを1MHzに設定 データシートより抜粋 : > 標準(100 kHz)と高速(400 kHz)のデータ転送モードに対応します。
masay0

2021/09/13 06:20

ありがとうございます。 クロックを下げて試してみます。 CPUと外部デバイスの組み合わせによっては、クロックを上げても動作するとのことだったので、試験的に上げたり下げたりして試したのが、そのまま残っていました。
masay0

2021/09/14 04:56

有難うございました。 クロックを下げてみました。伝送速度は遅くなりましたが、結果は変わらずでした。
ozwk

2021/09/14 05:18

adxl345_get_axiの最初でも最後でもdelay(10)ぐらいを入れてみて (当然遅くはなると思いますが)読み出し数ごとの挙動を確認してみてください
guest

回答2

0

Wire.setClock(1000000); // I2Cのクロックを1MHzに設定
ADXL345のI2CのSCLは400kHzとのことなので、データが正常に取得できない恐れがあります。
SPIモードで使用する場合はCLKは5MHzまで使用可能なので、高速動作が必要な場合はSPIモードを使うのがよいと思います。

その他の方法としては、2つの関数を統合して無駄を省くくらいしかないかもしれません。

/*ADXL345のレジスタデータ読み込み関数*/ void adxl345_get_data(uint8_t ucReg, uint8_t* ucData, uint8_t ucSize) { uint8_t cnt = 0; Wire.beginTransmission(ADXL345_I2C_ADRESS); Wire.write(ucReg); Wire.endTransmission(false); Wire.requestFrom(ADXL345_I2C_ADRESS, ucSize, true); while (Wire.available()) { ucData[cnt] = Wire.read(); cnt++; } } /*ADXL345 X,Y,Zデータ取得関数*/ void adxl345_get_axi(float* pfData) { uint8_t ucBuf[6]; int16_t acc[3]; adxl345_get_data(ADXL345_DATAX0, ucBuf, 6); acc[0] = ((int16_t)ucBuf[1] << 8) | ucBuf[0]; acc[1] = ((int16_t)ucBuf[3] << 8) | ucBuf[2]; acc[2] = ((int16_t)ucBuf[5] << 8) | ucBuf[4]; pfData[0] = acc[0] * ADXL345_SCALE_FACTOR; pfData[1] = acc[1] * ADXL345_SCALE_FACTOR; pfData[2] = acc[2] * ADXL345_SCALE_FACTOR;

例えば、ADXL345_SCALE_FACTORは現時点では1なので下3行を削除するなど。
もしくは、実行可能か定かではないですが、

Wire.beginTransmission(ADXL345_I2C_ADRESS); Wire.write(ucReg); Wire.endTransmission(false);

は一回のみ行い、

Wire.requestFrom(ADXL345_I2C_ADRESS, ucSize, true); while (Wire.available()) { ucData[cnt] = Wire.read(); cnt++; }

を繰り返し行う等。

投稿2021/09/13 06:32

YOshim

総合スコア1085

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masay0

2021/09/14 04:55

SPI接続で使える加速度センサを検討します。 有難うございました。
guest

0

adxl345_get_axi(accData[i]);

これ、なんかおかしくないですか?
データを読み出す、という引数ではないですね


ああ、コードを見るとこれであってますね

単純にI2Cでの読み出しですんで回数重ねて時間が変わったり戻ってこなくなるような性質のものではないですね。

float accData[512][3];

ローカル変数でスタック消費しますんで、ここで12KByte消費してるってのがなんかやばい気がします
一度これをグローバル変数としてみては。

投稿2021/09/13 06:08

編集2021/09/13 06:20
y_waiwai

総合スコア87693

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masay0

2021/09/13 06:16

ありがとうございます。 渡している配列が2次元なので、この形でポインタ(アドレス)が引き渡されると理解していました。 1次元の配列にして試してみます。
y_waiwai

2021/09/13 06:21

いえ、それはこれであってます。私の勘違いでした。 それよりも、このバッファをグローバル変数としてみてください
masay0

2021/09/13 06:29

ありがとうございます。 グローバル変数にしてみます。 確認結果は、また報告します。 このサイトを使うのが初めてなので、少しもたつくかと思いますがご容赦ください。
masay0

2021/09/14 04:54

配列をグローバル変数にして試してみました。結果は変わらずでした。 もう少し試行錯誤してみます。 有難うございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.53%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問