FC2カウンター FPGAの部屋 AXI4-Lite インターフェースの I2C Master Core を使用して 3 軸加速度度センサーの値を収集する4(NMJ2884U1を使用してスレーブ側の電源をON/OFFする)
fc2ブログ

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

FPGAの部屋

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

AXI4-Lite インターフェースの I2C Master Core を使用して 3 軸加速度度センサーの値を収集する4(NMJ2884U1を使用してスレーブ側の電源をON/OFFする)

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

ストロベリーリナックスさんの”LTC4331 絶縁型I2C延長モジュール(2個セット) ”のスレーブ側に付けるストロベリーリナックスさんの”ADXL355 超低ノイズ3軸加速度センサモジュール”は同じ値をずっと出力して、リセットが効かないことがある。そこで、秋月電子の低飽和型レギュレーター 3.3V500mA NJM2884U1-33 を使用して、スレーブ側の電源を GPIO で ON/OFF できるようにした。
下図にそのブロック図を示す。
i2cm_axi4ls_17_210601.png

ZYBO Z7-20 の Vivado 2020.2 プロジェクトの acc_sensor_pow_202 を示す。
i2cm_axi4ls_18_210601.png

ブロックデザインを示す。
i2cm_axi4ls_19_210601.png

ZYBO Z7-20 のスライドスイッチの SW0 で NJM2884U1-33 の CONTROL 端子を制御する。

Address Map を示す。
i2cm_axi4ls_20_210601.png

制約ファイルを示す。

set_property IOSTANDARD LVCMOS33 [get_ports iic_0_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports iic_0_sda_io]
set_property PACKAGE_PIN V8 [get_ports iic_0_scl_io]
set_property PACKAGE_PIN W8 [get_ports iic_0_sda_io]

set_property PACKAGE_PIN G15 [get_ports {SW0[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW0[0]}]
set_property PACKAGE_PIN V12 [get_ports {POW_CONTROL[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {POW_CONTROL[0]}]


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

ハードウェアをエクスポートして、Vitis を立ち上げた。
Vitis でプラットフォームとアプリケーション・プロジェクトを作成し、アプリケーション・ソフトウェアを作成して、ZYBO Z7-20 で確かめてみたところ、SW0 を ON した 0.5 秒後に 3軸加速度センサーの計測がスタートした。SW0 を OFF すると 3軸加速度センサーの計測が止まった。何度やっても ON/OFF することができた。成功だ。
i2cm_axi4ls_22_210601.png

実際の実験の様子を示す。ピンクの丸で囲った部分が NJM2884U1-33 だ。
i2cm_axi4ls_23_210601.jpg

アプリケーション・ソフトウェア acc_sensor_pow.c を示す。

// acc_sensor_pow.c
// 2021/05/28 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"
#include "xgpio.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 SW0_GPIO            1
#define POW_CONTROL_GPIO    2

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
}

void initialize_code(){
    // 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
}

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

    // GPIOの初期化
    status = XGpio_Initialize(&XGpio_ap, XPAR_AXI_GPIO_0_BASEADDR);
    if (status != XST_SUCCESS) return XST_FAILURE;
    XGpio_SetDataDirection(&XGpio_ap, SW0_GPIO, 1); // input
    XGpio_SetDataDirection(&XGpio_ap, POW_CONTROL_GPIO, 0); // output
    uint8_t sw0_state = XGpio_DiscreteRead(&XGpio_ap, SW0_GPIO);
    XGpio_DiscreteWrite(&XGpio_ap, POW_CONTROL_GPIO, sw0_state);
    if((sw0_state & 1) == 0){
        do {
            sw0_state = XGpio_DiscreteRead(&XGpio_ap, SW0_GPIO);
            XGpio_DiscreteWrite(&XGpio_ap, POW_CONTROL_GPIO, sw0_state);
            usleep(500000); // 500 ms Wait
        }while((sw0_state & 1) == 0);
    }

    initialize_code();

    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

        sw0_state = XGpio_DiscreteRead(&XGpio_ap, SW0_GPIO);
        if((sw0_state & 1) == 0){
            do {
                sw0_state = XGpio_DiscreteRead(&XGpio_ap, SW0_GPIO);
                XGpio_DiscreteWrite(&XGpio_ap, POW_CONTROL_GPIO, sw0_state);
                usleep(500000); // 500 ms Wait
            }while((sw0_state & 1) == 0);
            XGpio_DiscreteWrite(&XGpio_ap, POW_CONTROL_GPIO, 1);
            initialize_code();
        }
    }
}

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

コメント

コメントの投稿


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

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