FC2カウンター FPGAの部屋 AXI4-Lite インターフェースの I2C Master Core を使用して 3 軸加速度度センサーの値を収集する2
fc2ブログ

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

FPGAの部屋

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

AXI4-Lite インターフェースの I2C Master Core を使用して 3 軸加速度度センサーの値を収集する2

AXI4-Lite インターフェースの I2C Master Core を使用して 3 軸加速度度センサーの値を収集する1”の続き。

AXI4-Lite インターフェースの I2C Master Core を IP にする”で作成した AXI4-Lite インターフェースの I2C Master Core IP を使用して、ストロベリーリナックスの ADXL355 超低ノイズ3軸加速度センサモジュールのデータを I2C で収集してみようということで、前回は、Vivado 2020.2 でプロジェクトを作成し、i2cm_axi4ls IP を使ってブロックデザインを作成し、論理合成、インプリメンテーション、ビットストリームを生成して成功した。そして、ハードウェアをエクスポートした。今回は、Vitis 2020.2 を立ち上げて、プラットフォームとアプリケーション・ソフトウェアを作成して、ZYBO Z7-20 とストロベリーリナックスの ADXL355 超低ノイズ3軸加速度センサモジュールで試してみよう。

Vivado 2020.2 の Tools メニューから Launch Vitis を選択して、Vitis 2020.2 を立ち上げた。
acc_sensor_202 ディレクトリに下に、vitis_work ディレクトリを作成して、Workspace とした。
acc_bd_wrapper プラットフォームと acc_sensor アプリケーション・プロジェクトを作成した。
acc_sensor.c アプリケーション・ソフトウェアを作成して、ビルドしたところ、成功し、acc_sensor.elf が生成された。
i2cm_axi4ls_6_210426.png

ZYBO Z7-20 とストロベリーリナックスの ADXL355 超低ノイズ3軸加速度センサモジュールを接続した。
SCL と SDA のプルアップ抵抗はそれぞれ 1 kΩにしてある。
i2cm_axi4ls_7_210426.jpg

アプリケーション・ソフトウェアを Run すると、3軸加速度センサーのセンサー・データを取得することができた。成功だ。うまく行ってよかった。。。
i2cm_axi4ls_11_210426.png

I2C の波形を示す。
i2cm_axi4ls_8_210426.jpg

拡大する。
i2cm_axi4ls_9_210426.jpg

クロックの周期を測定した。 2.32 us だった。周波数にすると 431 kHz だった。
i2cm_axi4ls_10_210426.jpg

acc_sensor.c を貼っておく。

// acc_sensor.c
// 2021/04/19 by marsee
// コード中のコメント部分は”OpenCores.org の I2C controller core ”のマニュアルから引用した
// https://opencores.org/projects/i2c

#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’ f 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’ f when transferring data , ‘0’f 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

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_read3(uint8_t dev_addr, uint8_t raddr, int32_t *rdata){
    uint8_t rdata8a[3];
    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_RD);
    idle_check();
    rdata8a[1] = (uint8_t)(Xil_In32(I2CM_RXR) & 0xff);

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

    *rdata = (((int32_t)rdata8a[0])<<12) + (((int32_t)rdata8a[1])<<4) + (((int32_t)(rdata8a[2] & 0xf0))>>4);
    if(*rdata & 0x80000) // Is the 19th bit 1?
        *rdata |= 0xfff00000; // sign extension
}

int main(){
    uint8_t read_data, read_rdy;
    int32_t dataX, dataY, dataZ;
    XTime cur_time;

    // I2C I2C operating frequency setting 433KHz/100MHz, 415kHz/96MHz
    Xil_Out32(I2CM_PRER_LO, (u32)0x29);
    Xil_Out32(I2CM_PRER_HI, (u32)0x0);

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

    acc_sensor_write(0x3a, 0x2c, 0x83); // I2C speed is Hi speed, +-8g
    acc_sensor_write(0x3a, 0x1e, 0x00); // OFFSET_X_H
    acc_sensor_write(0x3a, 0x1f, 0x00); // OFFSET_X_L
    acc_sensor_write(0x3a, 0x20, 0x00); // OFFSET_Y_H
    acc_sensor_write(0x3a, 0x21, 0x00); // OFFSET_Y_L
    acc_sensor_write(0x3a, 0x22, 0x00); // OFFSET_Z_H
    acc_sensor_write(0x3a, 0x23, 0x00); // OFFSET_Z_L

    acc_sensor_write(0x3a, 0x2d, 0x00); // stanby clear

    while(1){
        do{
            read_data = acc_sensor_read1(0x3b, 0x04);
            read_rdy = read_data & 0x01;
        }while(read_rdy != 0x01);

        acc_sensor_read3(0x3b, 0x08, &dataX);
        acc_sensor_read3(0x3b, 0x0b, &dataY);
        acc_sensor_read3(0x3b, 0x0e, &dataZ);

        XTime_GetTime(&cur_time);
        printf("%lf,%x,%x,%x\n", (double)((long long int)cur_time)/333333.3435, (int)dataX, (int)dataY, (int)dataZ);

        usleep(2434); // for 400 kHz
    }
}

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

コメント

コメントの投稿


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

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