こんにちは、ENGかぴです。
Seeeduino XIAOのWire(SPI)を使用すると加速度センサー(ADXL345)の加速度やタップ情報などを取得することができます。ADXL345のライブラリを流用しながら加速度センサーの情報を取得する方法をまとめました。
ADXL345モジュール(秋月電子)を使用して加速度を取得しています。ライブラリーは一部ソースコードを追加して使用しています。
Seeeduino XIAOで動作確認したことについてリンクをまとめています。
Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方
加速度センサーのライブラリを使用する
ADXL345の製造元のアナログデバイセズのHPに提供されているライブラリを使用してもよいのですが、今回はSeeeduino用に有志が提供している「Accelerometer ADXL345」を使用します。
ADXL345のライブラリをインストールする
Arduino IDEを使ってライブラリをインストールして追加します。Arduino IDEのスケッチ欄からライブラリをインクルードを選択するとライブラリを管理の項目が表示されるのでクリックしてライブラリマネージャに遷移します。
ライブラリマネージャの検索欄にADXLを入力すると候補としてADXLに関するライブラリが表示されます。「Accelerometer ADXL345」を選択してインストールします。
ADXL345のライブラリを使用
ADXL345のライブラリをインストールするとADXL345のスケッチ例としてADXL345_demo_codeが準備されます。このスケッチ例を引用しながら初期化処理と加速度センサーの情報を取得します。設定する項目について使用しているライブラリについては以下の通りです。
関数名 | 設定するレジスタ | 内容 |
---|---|---|
powerOn() | POWER_CTL(0x2d) | レジスタをクリアした後、AUTO_SLEEPとMeasureのビットをセットして測定モードにする。 |
setActivityTheshold(引数) | THRESH_ACT(0x24) | 引数は符号なしの8ビットを値を指定する。スケールは62.5mg/LSB。アクティブイベントでの加速度信号の絶対値を比較される。 |
setInactivityThreshold(引数) | THRESH_INACT(0x25) | 引数は符号なしの8ビットの値を指定する。スケールは62.5mg/LSB。インアクティブイベントでの加速度信号の絶対値と比較される。 |
setTimeInactivity(引数) | TIME_INACT(0x26) | 引数は符号なしの8ビットの値を指定する。スケールは1sec/LSB。加速度がTHRESH_INACT値を下回ったまま設定した時刻が経過するとインアクティブとなる。 |
setActivityX(引数) setActivityY(引数) setActivityZ(引数) setInactivityX(引数) setInactivityY(引数) setInactivityZ(引数) | ACT_INACT_CTL(0x27) | 引数に1または0を指定する。0を指定するとDCカップリング動作となり1を設定するとACカップリング動作が有効になります。 DCカップリング動作は現在の加速度の大きさをTHRESH_ACT及びTHRESH_INACTと比較して判定する。ACカップリング動作はアクティブ開始時の加速度を基準としてTHRESH_ACT値を上回るとアクティブをトリガする。THRESH_INACTは基準値を下回ったまま時間経過でインアクティブ判定となる。 |
setTapDetectionOnX(引数) setTapDetectionOnY(引数) setTapDetectionOnZ(引数) | TAP_AXES(0x2A) | 引数に1または0を指定する。タップ検出したい軸に対して1を指定するとタップ検出の対象となる。 |
setTapThreshold(引数) | THRESH_TAP(0x1D) | 引数は符号なしの8ビットの値を指定する。タップ検出するための閾値を設定する。最大g値が+16gの時のスケールは62.5mg/LSB。 |
setTapDuration(引数) | DUR(0x21) | 引数は符号なしの8ビットの値を指定する。スケールは625us/LSB。タップイベントの検出時に加速度信号がTHRESH_TAP閾値を超える最大時間を設定する。 |
setDoubleTapLatency(引数) | LATENT(0x22) | 引数は符号なしの8ビットの値を指定する。スケールは1.25ms/LSB。タップイベントが検出されてから時間ウィンドウの開始までの待ち時間を設定する。 |
setDoubleTapWindow(引数) | WINDOW(0x23) | 引数は符号なしの8ビットの値を指定する。スケールは1.25ms/LSB。ダブルタップ検出において待ち時間満了後2番目の有効なタップを検出できる時間幅を設定する。 |
setFreeFallThreshold(引数) | THRESH_FF(0x28) | 引数は符号なしの8ビットの値を指定する。自由落下イベントを判定するためすべての軸の加速度とレジスタ値を比較する。スケールは62.5mg/LSB。推奨値は300~600ms(0x05~0x09)。 |
setFreeFallDuration(引数) | TIME_FF(0x29) | 引数は符号なしの8ビットの値を指定する。スケールは5ms/LSB。全ての軸がTHRESH_FFの値を下回ったまま本レジスタの時間が経過すると自由落下割り込みとなる。推奨値は100~350ms(0x14~0x46)。 |
setInterruptMapping(引数1,引数2) | INT_MAP(0x2F) | 引数1は対応するビット番号、引数2は割り込みの対象とするINT番号。引数1で指定したビット番号に対して0を指定するとINT1の出力、1を指定するとINT2の出力となる。 |
setInterrupt(引数1,引数2) | INT_ENABLE(0x2E) | 引数1は対応するビット番号、引数2は割り込み対象のビットの割り込みを有効の有無。 |
getAcceleration(引数) | DATAX0(0x32)~DATAZ1(0x37) | 引数は計算後の加速度情報を格納したい変数。小数点以下がでるためdouble型でXYZのデータを格納する配列が必要。 |
ADXL345について詳細なデータを把握したい場合はデータシートを下記リンクからダウンロードしてください。
ADXL345のライブラリでは設定項目はビットで管理しているものが多いためバイトで操作したい場合はソースコードを追加する必要があります。
ライブラリの一部を追加する
ADXL345のライブラリはビット指定で作られているためバイトデータを取得したい場合には不便なことがあります。タップ又はアクティブイベントの種別をバイトデータを取得して表示するためにライブラリの一部を追加しています。(ライブラリの更新により下記は不要となる可能性があります)
//ADXL345.hに追加する内容
class ADXL345
{
public:
//既存の関数
byte isTapSourcebyte(); //add
private:
//既存の関数
void setRegisterByte(byte regAdress, byte val); //add
byte getRegisterByte(byte regAdress); //add
};
ADXL345.hの内容を表示するためにはVsCode上で関数にカーソルを合わせて右クリックしたときに表示される定義へ移動および宣言へ移動を選択すると表示できます。次にADXL345.cpp内に関数の処理を追加します。
//ADXL345.cppに追加する内容
//既存の関数
byte ADXL345::isTapSourcebyte(){
return getRegisterByte(ADXL345_ACT_TAP_STATUS);
}
void ADXL345::setRegisterByte(byte regAdress, byte val){
writeTo(regAdress, val);
}
byte ADXL345::getRegisterByte(byte regAdress){
byte val;
readFrom(regAdress, 1, &val);
return val;
}
追加関数のisTapSourcebyte()はpublic指定しているのでADXL345.cpp以外のファイルからでもアクセスできますが、setRegisterbyte()及びgetRegisterByte()はprivate指定しているのでADXL345.cpp内のみ使用できます。
外部のファイルからの読み込みが不要な関数についてはprivate指定することでアクセスできないようにして参照の区分を明確にすることは有効な方法です。
追加関数のisTapSourcebyte()の使用例はACT_TAP_STATUSレジスタをバイトデータで読み出し各ビットをマスクした値と論理積をとることでビットが立っているのを確認しています。
byte intsrc2; //アクティブまたはタップイベントの内容
void loop(){
if( digitalRead(PIN_DI1) ){ //INT1
intsrc2 = adxl.isTapSourcebyte();
if( intsrc2 & ACT_TAP_ACT_X ){
Serial.println("ACT X");
}
/* 追加した関数を使用しない場合
if( adxl.isActivitySourceOnX()){
Serial.println("SINGLE_TAP");
}
*/
}
}
比較のためライブラリをそのまま使用した場合の例も示しています。どちらの考え方でも同じ動作になります。
ライブラリをそのまま使用する場合は関数をコールするたびにレジスタの値を参照することになりますが、isTapSourcebyte()の場合は一度のみのレジスタ参照となります。
レジスタへのアクセスを減らしたい場合は追加関数のisTapSourcebyte()を使用し、特にこだわりが無いのならそのまま使用しても問題ありません。
広告
動作確認
Seeeduino XIAOのDIを2本使ってADXL345のINT1とINT2の割り込みを判定できるようにしています。Wireを使用する場合マイコン内蔵のプルアップ抵抗などがない場合は抵抗を実装する必要があります。ADXL345モジュールにプルアップ抵抗が実装されているため不要です。
Arduino IDEのシリアルモニタにはXYZの軸の加速度の表示するようにしています。DATA_READY割り込みでデータを取得しているのでシリアルモニタにはかなり多くの頻度(10ms毎)で表示されてしまうため、タップ情報などの確認する際は表示部分をコメントアウトして確認しました。
Arduino IDEのシリアルプロッタを使用して加速度の変化が分かるように表示しました。シリアルモニタとシリアルプロッタは同時には使用できないためシリアルプロッタを使用する場合はソースコード全体の#define MONITER_USEをコメントアウトすると表示できます。
シリアルプロッタによる結果を確認すると加速度センサーを動かしているとXYZ軸の値が変化していることが分かります。机において動かさないようにすると加速度の動きが安定していることからうまく検出できていることが分かりました。
ソースコード全体
以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。
#include <Wire.h>
#include <ADXL345.h>
#define INT_DATA_READY_BIT_MASK 0x80
#define INT_SINGLE_TAP_BIT_MASK 0x40
#define INT_DOUBLE_TAP_BIT_MASK 0x20
#define INT_ACTIVITY_BIT_MASK 0x10
#define INT_INACTIVITY_BIT_MASK 0x08
#define INT_FREE_FALL_BIT_MASK 0x04
#define INT_WATERMARK_BIT_MASK 0x02
#define INT_OVERRUNY_BIT_MASK 0x01
#define ACT_TAP_ACT_X 0x40
#define ACT_TAP_ACT_Y 0x20
#define ACT_TAP_ACT_Z 0x10
#define ACT_TAP_TAP_X 0x04
#define ACT_TAP_TAP_Y 0x02
#define ACT_TAP_TAP_Z 0x01
#define PIN_DI1 0
#define PIN_DI2 1
#define MONITER_USE //動作情報を確認する場合(タップ情報など)は有効にする
// application use
ADXL345 adxl;
uint16_t cnt;
uint16_t cnt2;
uint8_t di1;
uint8_t di2;
byte intsrc; //割り込みイベント
byte intsrc2; //アクティブまたはタップイベントの内容
byte int2src;
/*** Local function prototypes */
void InitAdxl345();
void Adxl345Rcv();
void setup(){
Serial.begin(115200);
pinMode(PIN_DI1,INPUT_PULLDOWN);
pinMode(PIN_DI2,INPUT_PULLDOWN);
InitAdxl345();
}
void InitAdxl345(){
byte dmy;
adxl.powerOn();
//set activity/ inactivity thresholds (0-255)
adxl.setActivityThreshold(75); //62.5mg per increment 75
adxl.setInactivityThreshold(75); //62.5mg per increment
adxl.setTimeInactivity(10); // how many seconds of no activity is inactive?
//look of activity movement on this axes - 1 == on; 0 == off
adxl.setActivityX(1);
adxl.setActivityY(1);
adxl.setActivityZ(1);
//look of inactivity movement on this axes - 1 == on; 0 == off
adxl.setInactivityX(1);
adxl.setInactivityY(1);
adxl.setInactivityZ(1);
//look of tap movement on this axes - 1 == on; 0 == off
adxl.setTapDetectionOnX(0);//0
adxl.setTapDetectionOnY(0);//0
adxl.setTapDetectionOnZ(1);//1
//set values for what is a tap, and what is a double tap (0-255)
adxl.setTapThreshold(50); //62.5mg per increment
adxl.setTapDuration(15); //625us per increment
adxl.setDoubleTapLatency(80); //1.25ms per increment
adxl.setDoubleTapWindow(200); //1.25ms per increment
//set values for what is considered freefall (0-255)
adxl.setFreeFallThreshold(7); //(5 - 9) recommended - 62.5mg per increment
adxl.setFreeFallDuration(45); //(20 - 70) recommended - 5ms per increment 45
//setting all interrupts to take place on int pin 1
//I had issues with int pin 2, was unable to reset it
adxl.setInterruptMapping( ADXL345_DATA_READY, ADXL345_INT2_PIN );
adxl.setInterruptMapping( ADXL345_INT_SINGLE_TAP_BIT, ADXL345_INT1_PIN );
adxl.setInterruptMapping( ADXL345_INT_DOUBLE_TAP_BIT, ADXL345_INT1_PIN );
adxl.setInterruptMapping( ADXL345_INT_FREE_FALL_BIT, ADXL345_INT1_PIN );
adxl.setInterruptMapping( ADXL345_INT_ACTIVITY_BIT, ADXL345_INT1_PIN );
adxl.setInterruptMapping( ADXL345_INT_INACTIVITY_BIT, ADXL345_INT1_PIN );
//register interrupt actions - 1 == on; 0 == off
adxl.setInterrupt( ADXL345_DATA_READY, 1);
adxl.setInterrupt( ADXL345_INT_SINGLE_TAP_BIT, 1);
adxl.setInterrupt( ADXL345_INT_DOUBLE_TAP_BIT, 1);
adxl.setInterrupt( ADXL345_INT_FREE_FALL_BIT, 1);
adxl.setInterrupt( ADXL345_INT_ACTIVITY_BIT, 1);
adxl.setInterrupt( ADXL345_INT_INACTIVITY_BIT, 1);
dmy = adxl.getInterruptSource(); //ダミーで割り込みビットをクリア
}
void loop(){
if( digitalRead(PIN_DI1) ){ //INT1
intsrc2 = adxl.isTapSourcebyte();
intsrc = adxl.getInterruptSource();
#ifdef MONITER_USE
if( intsrc & INT_SINGLE_TAP_BIT_MASK){
Serial.println("SINGLE_TAP");
}
if( intsrc & INT_DOUBLE_TAP_BIT_MASK){
Serial.println("DOUBLE_TAP");
}
if( intsrc & INT_ACTIVITY_BIT_MASK){
Serial.println("Activity");
}
if( intsrc & INT_INACTIVITY_BIT_MASK){
Serial.println("inactivity");
}
if( intsrc & INT_FREE_FALL_BIT_MASK){
Serial.println("FREE_FALL");
}
if( intsrc2 & ACT_TAP_ACT_X ){
Serial.println("ACT X");
}
/* 追加した関数を使用しない場合
if( adxl.isActivitySourceOnX()){
Serial.println("SINGLE_TAP");
}
*/
if( intsrc2 & ACT_TAP_ACT_Y ){
Serial.println("ACT Y");
}
if( intsrc2 & ACT_TAP_ACT_Z ){
Serial.println("ACT Z");
}
if( intsrc2 & ACT_TAP_TAP_X ){
Serial.println("TAP X");
}
if( intsrc2 & ACT_TAP_TAP_Y ){
Serial.println("TAP Y");
}
if( intsrc2 & ACT_TAP_TAP_Z ){
Serial.println("TAP Z");
}
#endif
}
if( digitalRead(PIN_DI2) ){ //INT2
int2src = adxl.getInterruptSource();
if( int2src & INT_DATA_READY_BIT_MASK ){
Adxl345Rcv();
}
}
}
void Adxl345Rcv(){
double xyz[3];
double ax,ay,az;
adxl.getAcceleration(xyz); //XYZの加速度の取得
ax = xyz[0];
ay = xyz[1];
az = xyz[2];
#ifndef MONITER_USE
Serial.print("X:");
Serial.print(ax);
Serial.print(",");
Serial.print("Y:");
Serial.print(ay);
Serial.print(",");
Serial.print("Z:");
Serial.print(az);
Serial.println("");
#endif
}
関連リンク
Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。
Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方
Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方
ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方
最後まで、読んでいただきありがとうございました。
割り込みイベントでFREE_FALL(自由落下)を発生させようとするとXYZ軸すべての条件を満たす必要があるため頻繁に発生させることができませんでしたが、20cm程度の高さから落下させると発生することがありました。あまり衝撃を与えるとモジュールが傷つくので動作確認はほどほどにしています。