nRF I2C LSM6DS3TR-C

 I2Cの理解のために、センサから値を読む事をしてみる。

【回路】

nRFの勉強に、正規のDKをつかわず、XAIOで勉強しているのだが、メリットの一つはSeeed Seeed Studio XIAO nRF52840 Senseを購入していると、6軸慣性センサ(IMU)がすでに搭載されている。型番は以下のLSM6DS3TR-Cである事がわかる。

https://wiki.seeedstudio.com/XIAO_BLE/

このセンサはI2Cに接続されているので、I2Cを学ぶのに回路をブレッドボードに作らなくてもすぐに試せる。

でどのピンに接続されているかは、回路図を見てみるとわかる。

https://files.seeedstudio.com/wiki/XIAO-BLE/Seeed-Studio-XIAO-nRF52840-Sense-v1.1.pdf



P0.27にINTERNAL_I2C_SCLのラベルP0.07にINTERNAL_I2C_SDAのラベルがついていてそれぞれ内部でどこに接続されているかはわかる。また、6D_PWRのラベル のついているP1.08のピンがHIHGになると、このICに電源が共有される。また6D_INI1のピンはP0.11に接続されている。

【コードの記載】

過去に作っているTemplateからnRF_logでシリアルに文字列が出力ができるプロジェクトをテンプレートにするため、フォルダーをmyprojectsの下にでもコピーして名前を変更する。

ダブルクリックしてSESで立ち上げる。

I2Cのアドレス

SDO/SA0 ピン(1番ピン)は。デバイスアドレスの下位ビットを変更するために使用されるSDO/SA0ピンが電源電圧に接続されている場合 LSbは'1'(アドレス1101011b)、SDO/SA0ピンがグランドに接続されている場合 LSbは'0'(アドレス1101010b)。

XAIOのSENSEの場合は、SDO/SA0ピンはGNDに接続されているのでアドレス1101010bになる。これは2進数8ビットで表現すると6A。


ヘッダーファイルと変数

まず、#includeしている所に、

//To use I2C

#include "nrf_drv_twi.h"

と6D_PWRのラベル のついているP1.08のピンを電源のONーOFFに使うので、

//To use GPIO

#include "nrf_gpio.h"

とヘッダーファイルを追加する。

/*************************************************/

//user definition

/*************************************************/

static const nrf_drv_twi_t m_twi =NRF_DRV_TWI_INSTANCE(1);

m_twi という名前でインスタンスを定義。

ENABLEになっているかを確認する。

使う定数や変数の定義をする。

// I2C Address of LSM6DS3TR which embedded in XAIO

#define LSM6DS3TR_ADDRESS                0x6A

#define LSM6DS3TR_PWR                    40  // P1.08

uint8_t I2C_sendData;

uint8_t I2C_reciveData;


I2C初期化関数

I2Cの初期設定の関数を作る。

/*************************************************/
//user function
/*************************************************/
void twi_init (void)
{
    const nrf_drv_twi_config_t twi_config = {
       .scl                = 27,
       .sda                = 7,
       .frequency          = NRF_DRV_TWI_FREQ_400K,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
       .clear_bus_init     = false
    };
    nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);

    nrf_drv_twi_enable(&m_twi);
}
scl 、sdaは内部接続されているI2Cのピンの番号にそろえる。

mainの変更

int main(void)
{
    //PIN configuration
    nrf_gpio_cfg_output(LSM6DS3TR_PWR);
    nrf_gpio_pin_set(LSM6DS3TR_PWR); //supply VCC

    //Initilization of NRF_LOG
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();
   
    //Initilization of I2C
    twi_init();
    I2C_sendData=0x0F; //WHO_I_AM

     nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_sendData, 1, false);
     nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_reciveData,1);

     NRF_LOG_INFO("ID=%x\n",I2C_reciveData);
     NRF_LOG_FLUSH();
     nrf_delay_ms(500);
   while (true)
    {
    }
}
mainの中は、
40ピンをHIGH状態にして電源をいれてやる。
NRF_LOGでログを排出する初期化
I2Cの初期化
WHO_AM_Iのレジスタは、0x0Fなので先に、読み先のレジスタを送って、帰ってくる値ををI2C_reciveDataに格納して、表示するという流れ

6Aとかえってくる。

これは初期値で0b01101010でちゃんと返事をしている事が確認できる。

角速度センサー

まずは動かしてみる。FIFO経由でデータ取得ではなく直接レジスタから読み取る。
赤い矢印のルートでデータを取り出す。

変数定義
/*************************************************/
//user difinishon
/*************************************************/
static const nrf_drv_twi_t m_twi =NRF_DRV_TWI_INSTANCE(1);

// I2C Address of LSM6DS3TR which embedded in XAIO
#define LSM6DS3TR_ADDRESS                0x6A
#define LSM6DS3TR_PWR                     40  // P1.08

uint8_t I2C_TX_buffer[2];
uint8_t I2C_RX_buffer;

uint16_t XH,XL,YH,YL,ZH,ZL;
int16_t X_rot,Y_rot,Z_rot;//<--signed integer type

最後の角速度は正負の値が入るので"u"の無いintで定義。


書き込み関数の定義

レジスタに命令を送る際は、行先のレジスタ、次いで書き込む内容を順に送ってやる。

簡単に取り扱えるように関数を定義した。

void LSM6DS3TR_Write(uint8_t reg, uint8_t data) {

uint8_t dt[2];

dt[0] = reg;

dt[1] = data;

nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &dt[0], 2, false);

}

コントロールレジスタの記載

      LSM6DS3TR_Write(0x11,0b01011000);

これをさっきIDを表示させた後に追記する。(mainの中、whileの外の上)

0x11のレジスタに、0b01011000を書き込む事になる。


最初の4ビットで動作の設定をする。

最初の4ビット、初期値は0000になっており、Powerdown、つまり動かない。

なんHzの応答をさせるか?といいう事になるが、あまり早くとっても仕方がい(I2Cの転送が400kHzしかないので、16ビットを送るにはさらに割り算して・・となると)と考えて、normalとあるし、0101の208kHzの設定にしている。(特に理由はなく最適化して最終的には決める)

次がの2ビットがフルスケールをどの値にするか?”10”の1000dpsを選択した。

125dpsと選択する時に1にする。1000dpsを選択しているのでゼロ、

最後のビットはゼロで固定。最後の2ビットは00。これで8ビット分。


while の中を以下にする。

   while (true)

    {

    I2C_TX_buffer[0]=0x22;

          nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

          nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  XL=I2C_RX_buffer;


  I2C_TX_buffer[0]=0x23;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  XH=I2C_RX_buffer;


  I2C_TX_buffer[0]=0x24;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  YL=I2C_RX_buffer;


  I2C_TX_buffer[0]=0x25;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  YH=I2C_RX_buffer;

  I2C_TX_buffer[0]=0x26;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  ZL=I2C_RX_buffer;


  I2C_TX_buffer[0]=0x27;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  ZH=I2C_RX_buffer;


  X_rot= (XH<<8)|XL;

  Y_rot= (YH<<8)|YL;

  Z_rot= (ZH<<8)|ZL;


    NRF_LOG_INFO("Xg:%d, Yg:%d, Zg:%d",X_rot,Y_rot,Z_rot);

    //NRF_LOG_INFO("X:" NRF_LOG_FLOAT_MARKER "", NRF_LOG_FLOAT(X_rot));


    NRF_LOG_FLUSH();

    //nrf_delay_ms(500);

    }


レジストリの値を読む時は、読みたいレジストリのアドレスをまず送り、返ってくる値を読み取る。


例えば、x軸の角速度。0x22と0x23のレジストリから値を読む。

それぞれ、下位ビット8ビット分、上位ビット8ビット分なので、

  X_rot= (XH<<8)|XL;

として、上位ビット側の8ビットシフトさせて論理和をとり16ビットにしてやる。

Y軸、Z軸に関しても同様の処理をしてやる。


無事に動いていれば、出力が得られるはず・・・


ArduinoIDEのシリアルプロッタを使って表示させている。

Z軸はブレッドボードにに刺した状態で


図の方向にのみ回転させて素早くもとに戻した時の角速度を示している。正になるのは右ネジの法則の方向が正の値を返してくる。


Y方向についてもできるだけY方向のみを回して元に戻した時の波形。

同様にx方向のみ


加速度センサー

コントロールレジスタの記載

      LSM6DS3TR_Write(0x10,0b01011000);

これをさっき角速度の設定を行った所の後に追記する。

0x10のレジスタに、0b01011000を書き込む事になる。マニュアルを見ると

最初の4ビットが0000が初期値で、Powerdownしている状態。

これもnormalの208Hzに設定する場合は、0101を選択する。(ここいらどれがよいか億わからん。最適化の範疇で、電力消費を見ながら目的が達成できるか?とかで選択していく事になるんだろう)

次2ビットが、フルレンジでどれだけの加速度を測定するか?まずは00にしてみる。

残り2ビット。LPF_BW_SELは、デジタルバンド幅のセレクションと書いてある。

CTRL8_XL (17h)で設定するフィルタを有効なら1、無効なら0といった所だろう。0にしておいて、後で違いを見る。

最後は、1.67kHz以上の時に有効なもので、バンド幅を決めるらしい。ここもゼロ。

よって0b01010000

読み取りは角速度と同様にして、0x28から順にXL、XH・・・

実際に読み取りのコードを進める。変数に、

int16_t X_acc,Y_acc,Z_acc;//<--signed integer type

を足す。

whileの中の先ほどの角速度のX_rot,Y_rot,Z_rotの計算の後に以下を足して、NRF_LOGで出力できるようにしてやる。




 I2C_TX_buffer[0]=0x28;

          nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

          nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  XL=I2C_RX_buffer;


  I2C_TX_buffer[0]=0x29;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  XH=I2C_RX_buffer;


  I2C_TX_buffer[0]=0x2a;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  YL=I2C_RX_buffer;


  I2C_TX_buffer[0]=0x2b;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  YH=I2C_RX_buffer;

  I2C_TX_buffer[0]=0x2c;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  ZL=I2C_RX_buffer;


  I2C_TX_buffer[0]=0x2d;

  nrf_drv_twi_tx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_TX_buffer[0], 1, false);

  nrf_drv_twi_rx(&m_twi, LSM6DS3TR_ADDRESS, &I2C_RX_buffer,1);

  ZH=I2C_RX_buffer;


  X_acc= (XH<<8)|XL;

  Y_acc= (YH<<8)|YL;

  Z_acc= (ZH<<8)|ZL;


    NRF_LOG_INFO("Xg:%d, Yg:%d, Zg:%d, Xa:%d, Ya:%d, Za:%d",X_rot,Y_rot,Z_rot,X_acc,Y_acc,Z_acc);

    //NRF_LOG_INFO("X:" NRF_LOG_FLOAT_MARKER "", NRF_LOG_FLOAT(X_rot));


    NRF_LOG_FLUSH();

    //nrf_delay_ms(500);

上手く出力されていると上のようになる。Z軸は、重力が働いているので正の値17000ぐらいの値を返してくる。


手でもっているので揺れるが、基板を180度ひっくり返してやるとZだけがマイナスの-17000付近の値を返してくる。

無論、基板を垂直に立てると違う軸の値が大きくなるのが確認できるはず。

実際の何Gに換算する事も可能だがほとんどのアプリケーションではあまり重要ではないのでこのままとしてある。





コメント

このブログの人気の投稿

Attiny85とAQM0802A(LCD)のI2C接続

ILI9341 240X320 Arduino

Attiny85 FuseRest