LIS2DW12を使ったSpresense用3軸加速度センサーボードを作ってみた 

 Spresense用の加速度センサーボードとして既に6軸、9軸のボードは開発していましたが、
今回3軸の加速度センサーボードを作りました。
使ったICはSTMicroelectronics社の[LIS2DW12](https://www.st.com/ja/mems-and-sensors/lis2dw12.html)です。

**特徴**
・超低電力消費:パワーダウンモードで50nA、アクティブ低消費電力モードで1µA以下
・非常に低いノイズ: 低消費電力モードで最低1.3mgまでのRMS
・複数の帯域幅がある複数の動作モード
・Android静止検出、動き検出
・供給電圧、1.62V～3.6V
・独立IO供給
・±2g/±4g/±8g/±16gフル・スケール
・高速I2C/SPIデジタル出力インターフェイス
・オンデマンドで単一のデータ変換
・16ビットのデータ出力
・組み込み温度センサ
・セルフテスト
・32レベルFIFO
・高耐衝撃性: 1000g
・ECOPACK、RoHS、「Green」準拠

基板は弊社のBMI270使用の6軸加速度センサーと同じサイズとなっています。
(BMI270をLIS2DW12に乗せ換えただけです)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/4218ee85-f6c6-4ca3-c5ed-ee0d21ff26df.png)

コチラが回路図です。
![LIS2DW12TR_Schematic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/944f822e-7078-562b-404e-bd7f85135c68.png)

[サンプルプログラム](https://github.com/kriswiner/LIS2DW12)はこちらを使いました。
(ライブラリ化されたサンプルは見つかりませんでした。)
LIS2DW12_DataReady_Ladybug.inoをテストするのに不要な部分を削除したソースコードです。
```LIS2DW12_DataReady_Ladybug.ino
/* 9/18/21 Copyright Tlera Corporation
 *  
 *  Created by Kris Winer   
 *  
 *  The LIS2DW12 is an inexpensive (~$1), three-axis, medium-resolution (12- or 14-bit), ultra-low power 
 *  (<1 uA low power mode) accelerometer in a tiny 2 mm x 2 mm LGA12 package with a 192-byte FIFO, 
 *  two multifunction interrupts and widely configurable sample rate (1.6 - 1600 Hz), full range (2 - 16 g), 
 *  low power modes, and interrupt detection behaviors. This accelerometer is nice choice for motion-based 
 *  wake/sleep, tap detection, step counting, and simple orientation estimation.
 *  
 *  Library may be used freely and without limit with attribution.
 *  
 */
#include "LIS2DW12.h"

// Ladybug pin assignments
#define myLed       13 // red

const char        *build_date = __DATE__;   // 11 characters MMM DD YYYY
const char        *build_time = __TIME__;   //  8 characters HH:MM:SS

#define I2C_BUS    Wire               // Define the I2C bus (Wire instance) you wish to use

I2Cdev             i2c_0(&I2C_BUS);   // Instantiate the I2Cdev object and point to the desired I2C bus

bool SerialDebug = true;

uint8_t seconds, minutes, hours, day, month, year;
uint8_t Seconds, Minutes, Hours, Day, Month, Year;
 
volatile bool alarmFlag = false;

// battery voltage monitor definitions
float VDDA, VBAT, VBUS, Temperature;

//LIS2DW12 definitions
#define LIS2DW12_intPin1   8    // interrupt1 pin definitions, wake-up from STANDBY pin
#define LIS2DW12_intPin2   9    // interrupt2 pin definitions, data ready or sleep interrupt

// Specify sensor parameters //
LPMODE   lpMode = LIS2DW12_LP_MODE_2;      // choices are low power modes 1, 2, 3, or 4
MODE     mode   = LIS2DW12_MODE_LOW_POWER; // choices are low power, high performance, and one shot modes
//ODR    odr    = LIS2DW12_ODR_12_5_1_6HZ; //  1.6 Hz in lpMode, max is 200 Hz in LpMode
ODR      odr    = LIS2DW12_ODR_25Hz;       // choices are 12.5, 25, 50, 100, 200, 400, 800, 1600 Hz, 1.6 Hz in lpMode, and POWERDOWN
FS       fs     = LIS2DW12_FS_2G;          // choices are 2, 4, 8, or 16 g
BW_FILT  bw     = LIS2DW12_BW_FILT_ODR4;   // choices are ODR divided by 2, 4, 10, or 20
bool lowNoise = false;                     // low noise or lowest power
FIFOMODE fifoMode = BYPASS;                // capture 32 samples of data before wakeup event, about 2 secs at 25 Hz

float aRes = 0;         // Sensor data scale in mg/LSB
int16_t accelCount[3];  // Stores the 16-bit signed accelerometer sensor output
int16_t tempCount;      // temperature raw count output
float   temperature;    // Stores the real internal chip temperature in degrees Celsius
float ax, ay, az;       // variables to hold latest sensor data values 
float offset[3];        // holds accel bias offsets
float stress[3];        // holds results of the self test
uint8_t status = 0, wakeSource = 0, FIFOstatus = 0, numFIFOSamples = 0;

// Logic flags to keep track of device states
volatile bool LIS2DW12_dataReady_flag = false;
volatile bool LIS2DW12_sleep_flag = false;
volatile bool InMotion = false;

LIS2DW12 LIS2DW12(&i2c_0); // instantiate LIS2DW12 class


void setup()
{
  /* Enable USB UART */
  Serial.begin(115200);
  delay(1000);
  Serial.println("Serial enabled!");

  // Test the rgb led, active LOW
  pinMode(myLed, OUTPUT);
  digitalWrite(myLed, LOW);   // toggle red led on
  delay(1000);                 // wait 1 second

  /* initialize two wire bus */
  I2C_BUS.begin();                // Set master mode, default on SDA/SCL for STM32L4
  I2C_BUS.setClock(400000);       // I2C frequency at 400 kHz
  delay(1000);

  Serial.println("Scan for I2C devices:");
  i2c_0.I2Cscan();                // should detect LIS2DW12 at 0x14 and BME280 at 0x77
  delay(1000);

  Serial.println(" "); 

  // Read the LIS2DW12 Chip ID register, this is a good test of communication
  Serial.println("LIS2DW12 accelerometer...");
  byte LIS2DW12_ID = LIS2DW12.getChipID();  // Read CHIP_ID register for LIS2DW12
  Serial.print("LIS2DW12 "); Serial.print("I AM "); Serial.print(LIS2DW12_ID, HEX); Serial.print(" I should be "); Serial.println(0x44, HEX);
  Serial.println(" ");
  delay(1000); 

  if(LIS2DW12_ID == 0x44) // check if all I2C sensors with WHO_AM_I have acknowledged
  {
   Serial.println("LIS2DW12 is online..."); Serial.println(" ");
   
   LIS2DW12.reset();                                                // software reset before initialization
   delay(100);      

   LIS2DW12.selfTest(stress);                                       // perform sensor self test
   Serial.print("x-axis self test = "); Serial.print(stress[0], 1); Serial.println("mg, should be between 70 and 1500 mg");
   Serial.print("y-axis self test = "); Serial.print(stress[1], 1); Serial.println("mg, should be between 70 and 1500 mg");
   Serial.print("z-axis self test = "); Serial.print(stress[2], 1); Serial.println("mg, should be between 70 and 1500 mg");
   delay(1000);                                                     // give some time to read the screen

   LIS2DW12.reset();                                                // software reset before initialization
   delay(100);                                                     

   aRes = 0.000244f * (1 << fs);                                    // scale resolutions per LSB for the sensor at 14-bit data 

   Serial.println("hold flat and motionless for bias calibration");
   delay(5000);
   LIS2DW12.Compensation(fs, odr, mode, lpMode, bw, lowNoise, offset); // quickly estimate offset bias in normal mode
   Serial.print("x-axis offset = "); Serial.print(offset[0]*1000.0f, 1); Serial.println(" mg");
   Serial.print("y-axis offset = "); Serial.print(offset[1]*1000.0f, 1); Serial.println(" mg");
   Serial.print("z-axis offset = "); Serial.print(offset[2]*1000.0f, 1); Serial.println(" mg");

   LIS2DW12.init(fs, odr, mode, lpMode, bw, lowNoise);               // Initialize sensor in desired mode for application                     
   LIS2DW12.configureFIFO(fifoMode, 0x1F); // 32 levels of data
   delay(1000); // let sensor settle
   }
  else 
  {
   if(LIS2DW12_ID != 0x44) Serial.println(" LIS2DW12 not functioning!");
  }
  LIS2DW12.getStatus(); // read status of interrupts to clear
}/* end of setup */


void loop()
{
   status = LIS2DW12.getStatus(); // read status of interrupts to clear

   if (status & 0x01) 
   { // if this is a data ready interrupt, read accel data
     LIS2DW12.readAccelData(accelCount); // get 12-bit signed accel data

     // Now we'll calculate the accleration value into actual g's
     ax = (float)accelCount[0]*aRes - offset[0];  // get actual g value, this depends on scale being set
     ay = (float)accelCount[1]*aRes - offset[1];   
     az = (float)accelCount[2]*aRes - offset[2]; 
   }
  
    Serial.print("ax = ");  Serial.print((int)1000*ax);  
    Serial.print(" ay = "); Serial.print((int)1000*ay); 
    Serial.print(" az = "); Serial.print((int)1000*az); Serial.println(" mg");
    Serial.println(" ");

    tempCount = LIS2DW12.readTempData();  // Read the accel chip temperature adc values
    temperature =  ((float) tempCount) + 25.0f; // 8-bit accel chip temperature in degrees Centigrade
    // Print temperature in degrees Centigrade      
    Serial.print("Accel temperature is ");  Serial.print(temperature, 1);  Serial.println(" degrees C"); // Print T values to tenths of s degree C        
  
    digitalWrite(myLed, HIGH); delay(1);  digitalWrite(myLed, LOW); // toggle red led on

    delay(1000);
 }  /* end of loop*/
```
このプログラムを走らせた結果です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/46ac1422-127b-e53a-8d8e-e1ba514da736.png)

細かいチェックは行っていませんがとりあえずは動いています。

参考までにいま流行りの **ChatGPT** に
**ArduinoでLIS2DW12を使うプログラム**と問いかけてみたら
このコードが返ってきましたが、**SparkFunLIS2DW12.h** は公開されていないので
**SparkFunLIS2DH12.h**と混同しているみたいです。(残念)

```
#include <Wire.h>
#include <SparkFunLIS2DW12.h>

LIS2DW12 myIMU; // LIS2DW12オブジェクトの作成

void setup() {
  Wire.begin();
  Serial.begin(9600);
  // センサーの初期化
  if (myIMU.begin(Wire, 0x19) == false) {
    Serial.println("LIS2DW12 is not connected.");
    while (1);
  }
  // センサーの設定
  myIMU.setOutputDataRate(LIS2DW12::ODR_100Hz);
  myIMU.setFullScale(LIS2DW12::FS_2g);
  myIMU.enableHPF(true);
}

void loop() {
  // 加速度データの取得
  float ax, ay, az;
  myIMU.getAcceleration(ax, ay, az);
  // シリアルモニターに表示
  Serial.print("Acceleration: ");
  Serial.print(ax, 2);
  Serial.print(", ");
  Serial.print(ay, 2);
  Serial.print(", ");
  Serial.println(az, 2);
  delay(10);
}
```
**LIS2DW12**と**LIS2DH12**は別のICなので**LIS2DH12**用のサンプルプログラムは**LIS2DW12**では走りませんでした。
時間があれば移植を試みてみます。

これで3軸、6軸、9軸の加速度センサーボードが揃いました。
(3軸ボードは販売中、6軸と9軸のボードは2023年2月20日から販売予定です。)

**※2023年3月9日**
GitHubで公開されていたLIS2DW12ライブラリがうまく走らなかったのですが
TomonobuHayakawa様が動くようにしてGitHubにアップしてくださいました。
[LIS2DW12-Arduino-Lib](https://github.com/TomonobuHayakawa/LIS2DW12-Arduino-Lib)