FC2カウンター FPGAの部屋 ”L3GD20H使用3軸ジャイロセンサーモジュールキット”を使用する2(ジャイロセンサーを動作させる)
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

”L3GD20H使用3軸ジャイロセンサーモジュールキット”を使用する2(ジャイロセンサーを動作させる)

”L3GD20H使用3軸ジャイロセンサーモジュールキット”を使用する1(ジャイロセンサーの仕様を検討)”の続き。

秋月電子の”L3GD20H使用3軸ジャイロセンサーモジュールキット”を ZYBO Z7-20 に接続して使用したいということで、前回は、”L3GD20H PDFデータシート”をざっと読んで仕様をまとめてみた。今回は、I2C IP の i2cm_axi4ls IP を使用して、”L3GD20H使用3軸ジャイロセンサーモジュールキット”を動作させることができた。

最初に Vivado 2022.2 で ZYBO Z7-20 用の gyro_test プロジェクトを作成した。
gyro_sensor_5_230407.png

i2cm_axi4ls IP を使用して gyro_bd ブロック・デザインを作成した。
gyro_sensor_6_230407.png

processing_system7_0 の FCLK_CLK0 は 100 MHz に設定した。
gyro_sensor_9_230407.png

Address Editor 画面を示す。
gyro_sensor_7_230407.png

論理合成、インプリメンテーション、ビットストリームの生成を行った。
Project Summary を示す。
gyro_sensor_8_230407.png

ハードウェアをエクスポートして、gyro_bd_wrapper.xsa ファイルを生成した。

Vitis 2022.2 を起動して、gyro_test アプリケーション・プロジェクトを作成した。
gyro_test.c を作成して、ビルドを行った。
gyro_sensor_10_230407.png

gyro_test.c を示す。

// gyro_test.c
// 2023/04/05 by marsee
//

#include <stdio.h>
#include <stdint.h>
#include "xil_io.h"
#include "xparameters.h"
#include <unistd.h>
#include "xtime_l.h"

#define I2CM_PRER_LO    XPAR_I2CM_AXI4LS_0_BASEADDR
#define I2CM_PRER_HI    (XPAR_I2CM_AXI4LS_0_BASEADDR+0x4)
#define I2CM_CTR        (XPAR_I2CM_AXI4LS_0_BASEADDR+0x8)
#define I2CM_RXR        (XPAR_I2CM_AXI4LS_0_BASEADDR+0xc)
#define I2CM_TXR        (XPAR_I2CM_AXI4LS_0_BASEADDR+0xc)
#define I2CM_CR         (XPAR_I2CM_AXI4LS_0_BASEADDR+0x10)
#define I2CM_SR         (XPAR_I2CM_AXI4LS_0_BASEADDR+0x10)

#define CR_STA      0x80    // generate (repeated) start condition
#define CR_STO      0x40    // generate stop condition
#define CR_RD       0x20    // read from slave
#define CR_WR       0x10    // write from slave
#define CR_NACK     0x08    //  when a receiver, sent ACK (ACK = ‘0’) or NACK (ACK = ‘1’)
#define CR_IACK     0x01    // Interrupt acknowledge. When set, clears a pending interrupt.

#define SR_RxACK    0x80    // Received acknowledge from slave.This flag represents acknowledge from  the addressed slave.
                            // ‘1’ = No acknowledge received , ‘0’ = Acknowledge received
#define SR_Busy     0x40    // I2C bus busy
                            // ‘1’ after START signal detected , ‘0’ after STOP signal detected
#define SR_AL       0x20    // Arbitration lost. This bit is set when the core lost arbitration. Arbitration is lost when:
                            // * a STOP signal is detected, but non requested , * The master drives SDA high, but SDA is low.
#define SR_TIP      0x02    // Transfer in progress.
                            // ‘1’ when transferring data , ‘0’ when transfer complete
#define SR_IF       0x01    //  Interrupt Flag. This bit is set when an interrupt is pending, which
                            //  will cause a processor interrupt request if the IEN bit is set.
                            //  The Interrupt Flag is set when:
                            // *  one byte transfer has been completed , *  arbitration is lost

//#define DEBUG

void idle_check(){
    while(Xil_In32(I2CM_SR) & SR_TIP); // TIP bit is clear
}
void acc_sensor_write(uint8_t dev_addr, uint8_t waddr, uint8_t wdata){
    dev_addr &= 0xfe;
    idle_check();

    Xil_Out32(I2CM_TXR, (u32)dev_addr);
    Xil_Out32(I2CM_CR, (u32)(CR_STA|CR_WR));
    idle_check();

    Xil_Out32(I2CM_TXR, (u32)waddr);
    Xil_Out32(I2CM_CR, (u32)CR_WR);
    idle_check();

    Xil_Out32(I2CM_TXR, (u32)wdata);
    Xil_Out32(I2CM_CR, (u32)(CR_STO|CR_WR));
    idle_check();
}
uint8_t acc_sensor_read1(uint8_t dev_addr, uint8_t raddr){
    const uint8_t devw_addr = dev_addr & 0xfe;

    dev_addr |= 0x01;
    idle_check();

    Xil_Out32(I2CM_TXR, (u32)devw_addr);
    Xil_Out32(I2CM_CR, (u32)(CR_STA|CR_WR));
    idle_check();

    Xil_Out32(I2CM_TXR, (u32)raddr);
    Xil_Out32(I2CM_CR, (u32)CR_WR);
    idle_check();

    Xil_Out32(I2CM_TXR, (u32)dev_addr);
    Xil_Out32(I2CM_CR, (u32)(CR_STA|CR_WR));
    idle_check();

    Xil_Out32(I2CM_CR, (u32)(CR_STO|CR_RD|CR_NACK));
    idle_check();

    uint8_t rdata8 = (uint8_t)(Xil_In32(I2CM_RXR) & 0xff);
    return(rdata8);
}
void acc_sensor_read2(uint8_t dev_addr, uint8_t raddr, int32_t *rdata){
    uint8_t rdata8a[2];
    const uint8_t devw_addr = dev_addr & 0xfe;

    dev_addr |= 0x01;
    idle_check();

    Xil_Out32(I2CM_TXR, (u32)devw_addr);
    Xil_Out32(I2CM_CR, (u32)(CR_STA|CR_WR));
    idle_check();

    Xil_Out32(I2CM_TXR, (u32)raddr);
    Xil_Out32(I2CM_CR, (u32)CR_WR);
    idle_check();

    Xil_Out32(I2CM_TXR, (u32)dev_addr);
    Xil_Out32(I2CM_CR, (u32)(CR_STA|CR_WR));
    idle_check();

    Xil_Out32(I2CM_CR, (u32)CR_RD);
    idle_check();
    rdata8a[0] = (uint8_t)(Xil_In32(I2CM_RXR) & 0xff);

    Xil_Out32(I2CM_CR, (u32)(CR_STO|CR_RD|CR_NACK));
    idle_check();
    rdata8a[1] = (uint8_t)(Xil_In32(I2CM_RXR) & 0xff);

    *rdata = (int32_t)rdata8a[0] + (((int32_t)rdata8a[1])<<8);
    /*if(*rdata & 0x8000) // Is the 16th bit 1?
        *rdata |= 0xffff0000; // sign extension */
}

void initialize_code(){
    // I2C I2C operating frequency setting 396KHz/100MHz: I2CM_PRER_LO=0x2c
    Xil_Out32(I2CM_PRER_LO, (u32)0x2c);
    Xil_Out32(I2CM_PRER_HI, (u32)0x0);

    Xil_Out32(I2CM_CTR, 0x80); // enable core

    acc_sensor_write(0xd4, 0x20, 0xef);
    // CTRL1(20h) : PD = 8:Normal Mode, Zen = 4 : Z axis enable,
    // Yen = 2 : Y axis enable, Xen = 1 : X axis enable
    // DR = 0xC0 : 11 - 800 Hz, BW = 10 - LPF1 Cut off 211 Hz, LPF2 Cut off -

    acc_sensor_write(0xd4, 0x23, 0x00); // CTRL4(23h) 245 dps
    //acc_sensor_write(0xd4, 0x23, 0x20); // CTRL4(23h) 2000 dps
    //acc_sensor_write(0xd4, 0x39, 0x00); // LOW_ODR(39h) Low_ODR = 0
}

int main(){
    int32_t dataX, dataY, dataZ;
    int32_t dataXL, dataYL, dataZL;
    int32_t dataXH, dataYH, dataZH;
    uint8_t read_data, read_rdy;
    XTime time;
    double time0, timeb;
    double past_time;

    initialize_code();

    XTime_GetTime(&time);
    timeb = (double)((long long int)time)/333333.3435; // ms

    for(int i=0; i<10000; i++){
        do {
            read_data = acc_sensor_read1(0xd5, 0x27); // STATUS(27h) read
            read_rdy = read_data & 0x08; // XYXDA(X, Y, Z -axis new data available) enable?
        }while(read_rdy != 0x08);

        dataXL = acc_sensor_read1(0xd5, 0x28);
        dataXH = acc_sensor_read1(0xd5, 0x29);
        dataX = (dataXH<<8)+dataXL;

        dataYL = acc_sensor_read1(0xd5, 0x2a);
        dataYH = acc_sensor_read1(0xd5, 0x2b);
        dataY = (dataYH<<8)+dataYL;

        dataZL = acc_sensor_read1(0xd5, 0x2c);
        dataZH = acc_sensor_read1(0xd5, 0x2d);
        dataZ = (dataZH<<8)+dataZL;

        XTime_GetTime(&time);
        time0 = (double)((long long int)time)/333333.3435; // ms
        past_time = time0 - timeb;
        timeb = time0;

        printf("X = %x, Y = %x, Z = %x, %lf\n", (int)dataX, (int)dataY, (int)dataZ, past_time);
        usleep(2623);
        //usleep(100000);
    }
    return(0);
}


まずは、”L3GD20H PDFデータシート”の I2C インターフェースの最大動作周波数が 400 KHz だったので、I2C のクロックを 431 KHz から 396 KHz に落とした。

サブルーチンは基本的に加速度センサー用を使用した。

I2C は連続アドレスを連続的に読んでくることができる。gyro_test.c で言うと acc_sensor_read2() 関数がそれにあたるが、これを L3GD20H に使用すると、最初のアドレスのデータを 2 回読んでしまうというバグ?(仕様なのかも?)があった。よって、acc_sensor_read1() 関数を使用して、2 回データを読んでいる。

gyro_test.elf を動作させた結果を示す。

X = 11e, Y = fe13, Z = 219, 3.338460
X = e1, Y = fe70, Z = 241, 3.338502
X = ee, Y = fe20, Z = 213, 3.339000
X = df, Y = fe8d, Z = 245, 3.339000
X = c1, Y = fe62, Z = 11a, 3.338499
X = be, Y = fe7e, Z = 237, 3.339591
X = f3, Y = fe92, Z = 1d8, 3.338910
X = 15, Y = fe55, Z = 248, 3.339000
X = f5, Y = fe5c, Z = 1e9, 3.338598
X = d1, Y = fe72, Z = 248, 3.338502
X = e0, Y = fe7f, Z = 246, 3.338460
X = da, Y = fe40, Z = 268, 3.338100
X = fa, Y = fe5a, Z = 1f1, 3.338550
X = e7, Y = fe72, Z = 237, 3.339000
X = ea, Y = fe6a, Z = 213, 3.338508
X = ce, Y = fe4a, Z = 291, 3.338901
X = e2, Y = fe59, Z = 1dc, 3.338511
X = bd, Y = fea9, Z = 222, 3.340089
X = bd, Y = fe2b, Z = 1e8, 3.338901
X = d0, Y = fe81, Z = 245, 3.338010
X = f5, Y = fe68, Z = 250, 3.339048
X = c8, Y = fe31, Z = 24b, 3.339000
X = d5, Y = fe8b, Z = 21c, 3.338502

  1. 2023年04月07日 04:30 |
  2. FPGAを使用したシステム
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/5900-4dfb283a
この記事にトラックバックする(FC2ブログユーザー)