PICを使って、加速度、角速度をBMX055から読み出すプログラムを作ってみた
2023/05/28


秋葉原の秋月電子で、「BMX055使用9軸センサーモジュール」というものがあった。
このBMX055というのはナント!!、あの有名なBOSCH社のICで、それを使ったモジュールなのである。
特に最近はドローンなどに使われているものだそうで、なかなか興味深いものなのである。

そのモジュールがせいぜい1680円で手に入るということなので、早速秋葉原に出向いて買ってきた。
さて、どうなるのであろうか?

photo1735
ネット上には「Arduino」とか「RaspberryPi」と組み合わせて使ったものが出ており、割と簡単そうなことが書いてある。
そうかそうかと、気軽にPICでも読み込めるかと思ったら、そう簡単なものではなかった。

今回は加速度と角速度の読み込みをやってみた。
磁気データも取り込めるのだが、どうもどう使ったらよいか判らなかったので、磁気データは取り込まなかった。

ただ、やり方はアドレスとデータの長さが違うだけなので、必要となればすぐに出来るとは思うのである。



photo1736
まずは、ジャンパーピンの4と5をハンダで導通させ、I2Cの信号線をプルアップする。

私が作る回路には、LCDがもう付いており、I2Cで通信しているので、本来はプルアップは不要なのだが、テストでLCDを外したりするので、一応やっておく。

もう一つはJ6ピンで、これは電源と、信号のレベルを両方共5Vにする設定にする。

PICは相変わらずPIC18F4550で5V動作である。



I2C通信のプログラムは、LCDのテストで使用したものをそのまま使用している。
ただ、LCDのときはデータの書き込みのみだったのが、今回はセンサーからのデータを受け取るルーチンが追加になる。
その部分のI2Cのヘッダー

#include  // include processor files - each processor file is guarded.  

void I2C_Master_Init(void);
void I2C_Master_Wait(void);
void I2C_Master_Wait2(void);
void I2C_Master_Start(void);
void I2C_Master_ReStart(void);
void I2C_Master_Stop(void);
void I2C_Master_Write(unsigned d);
unsigned char I2C_Master_Read(unsigned char a);

#define _XTAL_FREQ 48000000     // 使用するPIC等により動作周波数値を設定する
                                // 水晶は20MHzでPLLで48MHz動作


I2C通信のデータの受け取りルーチンを追加

unsigned char I2C_Master_Read(unsigned char ack)
{
    unsigned char temp;
    
    I2C_Master_Wait();
    RCEN=1;                         //Enable reception in Master device 
    I2C_Master_Wait();
    temp = SSPBUF;                  //Read data from SSP1BUF
    I2C_Master_Wait();
    ACKDT = ack;                    //Acknowledge bit     1:NACK   0:ACK
    ACKEN = 1;                      //Acknowledge sequence

    return temp;                    //return received data
}


BMX055のI2C通信は、デフォルトのアドレスが、加速度は0x19、ジャイロが0x69なのであるが、なかなかうまく動作してくれなかった。
しかたなく、BMX055のマニュアルをポンコツ頭で解読していくと、どうもLCDとは様子が違う。

BMX055のマニュアルを見ると、I2Cのアドレスは7bitを使用し、0bit目は、readとwriteのフラグとして使用していると出ている。
ただしPICのI2C通信は8bitのアドレスなので、1bitずらしたアドレスになる。

まずは読み出したいアドレスを書き込んで、次から読み込みを複数バイト実行し、アクノリッジを立てるまで、マルチバイトの読み込みが可能だそうである。

このあたりは、私がなにぶんポンコツな英語力なので、何処まで正確なのか不明なのであるが、なんとなく動いているようなので良しとしている。

BMX055処理のヘッダーファイル


#include  // include processor files - each processor file is guarded.  

#define BMX055_ACC_ADDR      0x32  // x19<<1 加速度センサのアドレス
#define BMX055_ACC_DATA      0x02  // 加速度センサのデータアドレス
#define BMX055_GYRO_ADDR     0xD2  // 0x69<<1 ジャイロセンサのアドレス
#define BMX055_GYRO_DATA     0x02  // ジャイロセンサのデータアドレス
//#define BMX055_MAG_ADDR      0x26  // 0x13<<1磁気センサのアドレス

void BMX055_init(void);
void BMX055_GET_ACC(int acc_data[4]);
void BMX055_GET_GYRO(int gyro_data[3]);


BMX055の初期設定は、基本的に各センサの初期設定である。

加速度の読み込みは、3軸と、温度データもついでに読み込める。
温度データは23度を0として+/-を0.5度刻みで計測したデータだそうである。

加速度のデータは、分解能が12bitで+/-2gで設定しているので、範囲は4gである。
したがって分解能は、4g/4096で、bitあたり0.0009765gということになる。

ジャイロのデータは角速度で、分解能は16bitで、+/-125度という設定をしているので、範囲は250度である。
したがって分解能は、250度/65536で、bitあたり0.003815度/sということになる。

BMX055読み込みプログラムは以下のようなものになる。

#include "I2C.h"
#include "BMX055.h"

void BMX055_init()
{
    //-----------------------------------------
    I2C_Master_Start();
    I2C_Master_Write(BMX055_ACC_ADDR);      // 加速度センサ
    I2C_Master_Write(0x0F);                 // Select PMU_Range register
    I2C_Master_Write(0x03);                 // Range = +/- 2g
    I2C_Master_Stop();
    __delay_ms(100) ;        // ms
    //-----------------------------------------
    I2C_Master_Start();
    I2C_Master_Write(BMX055_ACC_ADDR);      // 加速度センサ
    I2C_Master_Write(0x10);                 // Select PMU_BW register
    I2C_Master_Write(0x08);                 // Bandwidth = 7.81 Hz
    I2C_Master_Stop();
    __delay_ms(100) ;        // ms
    //-----------------------------------------
    I2C_Master_Start();
    I2C_Master_Write(BMX055_ACC_ADDR);      // 加速度センサ
    I2C_Master_Write(0x11);                 // Select PMU_LPW register
    I2C_Master_Write(0x00);                 // Normal mode, Sleep duration = 0.5ms
    I2C_Master_Stop();
    __delay_ms(100) ;        // ms
    //-----------------------------------------
    I2C_Master_Start();
    I2C_Master_Write(BMX055_GYRO_ADDR);     // ジャイロセンサ
    I2C_Master_Write(0x0F);                 // Select Range register
    I2C_Master_Write(0x04);                 // Full scale = +/- 125 degree/s
    I2C_Master_Stop();
    __delay_ms(100) ;        // ms
    //-----------------------------------------
    I2C_Master_Start();
    I2C_Master_Write(BMX055_GYRO_ADDR);     // ジャイロセンサ
    I2C_Master_Write(0x10);                 // Select Bandwidth register
    I2C_Master_Write(0x07);                 // ODR = 100 Hz
    I2C_Master_Stop();
    __delay_ms(100) ;        // ms
    //-----------------------------------------
    I2C_Master_Start();
    I2C_Master_Write(BMX055_GYRO_ADDR);     // ジャイロセンサ
    I2C_Master_Write(0x11);                 // Select LPM1 register
    I2C_Master_Write(0x00);                 // Normal mode, Sleep duration = 2ms
    I2C_Master_Stop();
    __delay_ms(100) ;        // ms
    //-----------------------------------------
}

//
// 加速度を取得
void BMX055_GET_ACC(int acc_data[4]) {
    unsigned char data[7];
    
    LATAbits.LA1 = 1;
    
    I2C_Master_Start();
    I2C_Master_Write(BMX055_ACC_ADDR);      // 加速度センサ
    I2C_Master_Write(BMX055_ACC_DATA);      // Select data register
    I2C_Master_Stop();
    
    I2C_Master_Start();
    I2C_Master_Write(BMX055_ACC_ADDR+1);      // 加速度センサ読み込み
    for (int i = 0; i < 6; i++) {
        data[i] = I2C_Master_Read(0);  // Request 1 byte of data
        // Read 7 bytes of data
        // xAccl lsb,msb, yAccl lsb,msb, zAccl lsb,msb temp
    }
    data[6] = I2C_Master_Read(1);  // Request 1 byte of data
    I2C_Master_Stop();
    //data[0] = 0xFF;
    //data[1] = 0xFF;
    //data[6] = 0xFF;
  
    acc_data[0] = (data[0] >> 4) + (data[1] << 4);
    acc_data[1] = (data[2] >> 4) + (data[3] << 4);
    acc_data[2] = (data[4] >> 4) + (data[5] << 4);
    acc_data[3] = data[6];
    if(acc_data[0] > 2047) acc_data[0] = acc_data[0] - 4096;
    if(acc_data[1] > 2047) acc_data[1] = acc_data[1] - 4096;
    if(acc_data[2] > 2047) acc_data[2] = acc_data[2] - 4096;
    if(acc_data[3] > 127) acc_data[3] = acc_data[3] - 256;
    acc_data[3] = acc_data[3] + 46;     // 23*2 0.5度単位
            
    LATAbits.LA1 = 0;
}

//
// 角速度を取得
void BMX055_GET_GYRO(int gyro_data[3]) {
    unsigned char data[6];
    
    LATAbits.LA1 = 1;
    
    I2C_Master_Start();
    I2C_Master_Write(BMX055_GYRO_ADDR);      // ジャイロセンサ
    I2C_Master_Write(BMX055_GYRO_DATA);      // Select data register
    I2C_Master_Stop();
    
    I2C_Master_Start();
    I2C_Master_Write(BMX055_GYRO_ADDR+1);      // 角速度センサ読み込み
    for (int i = 0; i < 5; i++) {
        data[i] = I2C_Master_Read(0);  // Request 1 byte of data
        // Read 6 bytes of data
        // xAccl lsb,msb, yAccl lsb,msb, zAccl lsb,msb temp
    }
    data[5] = I2C_Master_Read(1);  // Request 1 byte of data
    I2C_Master_Stop();
    //data[0] = 0xFF;
    //data[1] = 0xFF;
  
    gyro_data[0] = data[0] + (data[1] << 8);
    gyro_data[1] = data[2] + (data[3] << 8);
    gyro_data[2] = data[4] + (data[5] << 8);
    if(gyro_data[0] > 32767) gyro_data[0] = gyro_data[0] - 65536;
    if(gyro_data[1] > 32767) gyro_data[1] = gyro_data[1] - 65536;
    if(gyro_data[2] > 32767) gyro_data[2] = gyro_data[2] - 65536;
            
    LATAbits.LA1 = 0;
}


メインのプログラム。

今回は、BMX055から、加速度とジャイロのデータを取り出し、加速度のデータと温度データをLCDに表示している。


/*
 * File:   MAIN.c
 * Author: rsy88
 *
 * Created on 2023/05/05, 14:31
 */

#include 
#include 
#include "I2C.h"
#include "LCD.h"
#include "BMX055.h"

// コンフィギュレーション pic18f4550  c18
#pragma config PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2
#pragma config FOSC = HSPLL_HS, FCMEN = OFF, IESO = OFF
#pragma config PWRT = ON, BOR = OFF, BORV = 0
#pragma config VREGEN = ON, WDT = OFF, WDTPS = 1
#pragma config CCP2MX = OFF, PBADEN = OFF, LPT1OSC = OFF
#pragma config MCLRE = OFF, STVREN = OFF, LVP = OFF
#pragma config ICPRT = OFF, XINST = OFF, DEBUG = OFF
#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF
#pragma config CPB = OFF, CPD = OFF
#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF
#pragma config WRTC = OFF, WRTB = OFF, WRTD = OFF
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF, EBTRB = OFF

void main(void) {
    
    unsigned char x;
    unsigned char y;
    unsigned char chr;
    unsigned char z;

    
    int data1 = 12345;
    int data2 = 19876;
    int temp_i;
    int temp_j;
    char pdata[16];
    
    int acc_data[4];    // x,y,z + temp
    int gyro_data[3];    // x,y,z
    
    TRISA = 0b00000000;			//PORTA=0-7 output
    TRISD = 0b00000000;			//PORTD=0-7 output
    TRISB = 0b11111111;			//PORTB=0-7 input
    LATD = 0;

    I2C_Master_Init();
    __delay_ms(5) ;        // 5ms
    lcd_init();
    LATAbits.LA0 = 1;
    BMX055_init();
    LATAbits.LA0 = 0;
    
    while(1){
        LATAbits.LA0 = 1;
        __delay_ms(100) ;        // ms

        BMX055_GET_ACC(acc_data);
        BMX055_GET_GYRO(gyro_data);

        z = acc_data[0];
        z = gyro_data[0];
        LATD = z;

        lcd_cls();
        
        sprintf(pdata,"X=%d Y=%d",acc_data[0],acc_data[1]);
        //sprintf(pdata,"X=%d Y=%d",gyro_data[0],gyro_data[1]);
        lcd_loc(0,0);
        lcd_str(pdata);
        
        temp_i = acc_data[3] >> 1;
        temp_j = (acc_data[3] - (temp_i << 1)) * 5;
        sprintf(pdata,"Z=%d T=%d.%d",acc_data[2],temp_i,temp_j);
        //sprintf(pdata,"Z=%d T=%d.%d",gyro_data[2],temp_i,temp_j);
        lcd_loc(0,1);
        lcd_str(pdata);
       
        LATAbits.LA0 = 0;
        __delay_ms(100) ;        // ms
   }
    
    return;
}

photo1737
今回実験した装置

オシロスコープに表示されているのはRA0とRA1の波形で、無限ループの先頭でLATAbits.LA0 = 1;でRA0がオンになり
BMX055_GET_ACC(acc_data);とBMX055_GET_GYRO(gyro_data);の中でRA1がオンになり、抜けるときにオフになる。
その後にLCDの表示を行ってからRA0がオフになるのを繰り返している。


photo1740
表示されているLCDの値は、Xが27、Yが-53、Zが1001で、気温が23.5度となっている。
値はかなりふらついており、ノイズが乗っているのか?、感度が良すぎるのか?、このあたりは考慮が必要なようである。

Zの値が1001なので、0.0009765を乗算すると0.9775gということで、ほぼ1gの重力加速度がZ軸にかかっていると考えられる。


センサーを乗せている台は、どうしても若干傾いているので、薄い本などをちょっとかませて水平を試す。

なかなか微妙で難しいのだが、なんとかX軸とY軸が一桁以内になるようにしてみる。
ところが、そのときのZ軸の値が1001〜1002ということで、あまり変わらないのである。

本来は、X軸とY軸が0になった状態で、全ての重力加速度がZ軸のみにかかり、Zの値が最大になるはずなのである。
このへんの誤差とか、どういうものなのであろうか?
と思っていたら、時間とともにだんだんと値が変化してくるのである。

2時間くらい(昼食の時間)ほっといたら、X,Yの値が-3と-9くらいになり、Zの値が1003〜1004になっているのである。

確かに、センサーのようなアナログの物体は、時間や温度によってそうとう値がずれるのである。
昔、アマチュア無線の勉強をしていたときに、水晶振動子でさえも温度でドリフトするので、恒温槽に入れた方が良いなどと習ったのである。


HOME