FC2カウンター FPGAの部屋 Xilinx 社の AXI IIC IP で ADXL355 を使用する
fc2ブログ

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

FPGAの部屋

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

Xilinx 社の AXI IIC IP で ADXL355 を使用する

Xilinx 社の AXI IIC IP を使用して、I2C インターフェースで、ストロベリーリナックスの ADXL355 超低ノイズ3軸加速度センサモジュール を ZYBO Z7-20 ボードに接続した。その際に AXI IIC IP の使い方に相当時間がかかってしまったのでブログに書いておく。

ADXL355 の日本語マニュアルはここにある

I2C のマニュアルや参考URL について紹介しておく。
UM10204 I2C バス仕様およびユーザーマニュアル Rev. 5.0J — 2012 年 10 月 9 日 (日本語翻訳 11 月 2 日)
第5回 Arduino入門 I2C通信編” Read のプロトコルが参考になった。

AXI IIC については、今まで CMOS カメラの設定用に使ってきたが、Write しか使っていなかった。Read を本格的に使うのは今回が初めてだった。
AXI IIC について当ブログでマニュアルをまとめた記事を以下に示す。
AXI IIC のお勉強1
AXI IIC のお勉強2” 結局、この Read 方法でやれば良かったのだが、プログラムにバグがあったため動作しなかった。

Vivado 2020.1 の acc_sensor プロジェクトを示す。
acc_sensor_1_201016.png

ブロックデザインを示す。ZYNQ の PS に AXI IIC が接続されているデザインだ。
acc_sensor_2_201016.png

Address Editor を示す。
acc_sensor_3_201016.png

制約ファイルの acc_sensor.xdc を示す。

set_property PACKAGE_PIN V8 [get_ports IIC_0_scl_io]
set_property PACKAGE_PIN W8 [get_ports IIC_0_sda_io]
set_property IOSTANDARD LVCMOS33 [get_ports IIC_0_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports IIC_0_sda_io]


Vitis を示す。
acc_sensor_4_201016.png

アプリケーション・ソフトウェアの acc_sensor_test.c を示す。これで動作している。

// acc_sensor_test.c
// 2020/10/12 by marsee
//

#include <stdio.h>
#include <stdint.h>
#include "xparameters.h"

#define TX_FIFO_EMPTY   0x80
#define RX_FIFO_EMPTY   0x40
#define BB              0x04

void acc_sensor_init(volatile uint32_t *axi_iic_ad){
    axi_iic_ad[72] = 0x0F; // RX_FIFI_PIRQ
    axi_iic_ad[64] = 0x2; // Control Register (100h) reset tx fifo
    axi_iic_ad[64] = 0x1; // Control Register (100h) enable i2c
}

void idle_check(volatile uint32_t *axi_iic_ad){
    int32_t status_reg;
    int32_t check_bit;

    do{
        status_reg = axi_iic_ad[65]; // Status Register (104h))
        check_bit = status_reg & (TX_FIFO_EMPTY | RX_FIFO_EMPTY | BB);
    }while(check_bit != (TX_FIFO_EMPTY | RX_FIFO_EMPTY)) ;
}

void acc_sensor_write(volatile uint32_t *axi_iic_ad, uint32_t device_addr, uint32_t write_addr, uint32_t write_data){
    idle_check(axi_iic_ad);
    axi_iic_ad[66] = 0x100 | (device_addr & 0xfe); // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    axi_iic_ad[66] = write_addr & 0xff;           // address
    axi_iic_ad[66] = 0x200 | (write_data & 0xff);      // data
}

uint32_t acc_sensor_read(volatile uint32_t *axi_iic_ad, uint32_t device_addr, uint32_t read_addr){
    int32_t status_reg, rx_fifo_empty;

    idle_check(axi_iic_ad);
    axi_iic_ad[66] = 0x100 | (device_addr & 0xfe); // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    axi_iic_ad[66] = read_addr & 0xff;  // address byte
    axi_iic_ad[66] = 0x100 | device_addr & 0xff; // Slave IIC Read Address, address is 0x108, i2c_tx_fifo, with repeat start
    axi_iic_ad[66] = 0x201;      // 1 byte data, NACK condition

    do{
        status_reg = axi_iic_ad[65];
        rx_fifo_empty = status_reg & RX_FIFO_EMPTY;
    }while(rx_fifo_empty); // Wait untill not RX_FIFO_EMPTY(Status Register (104h))

    int32_t read_data = axi_iic_ad[67] & 0xff; // Read Receive FIFO (10Ch)
    return(read_data);
}

int main(){
    volatile uint32_t *axi_iic_ad;
    int32_t dataX, dataY, dataZ;
    int32_t read_data, data_ready;

    axi_iic_ad = XPAR_AXI_IIC_0_BASEADDR;

    acc_sensor_init(axi_iic_ad);

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

    int32_t temp = acc_sensor_read(axi_iic_ad, 0x3b, 0x00);

    do{
        read_data = acc_sensor_read(axi_iic_ad, 0x3b, 0x04);
        data_ready = read_data & 0x01;
    }while(data_ready != 0x01);

    dataX = acc_sensor_read(axi_iic_ad, 0x3b, 0x08) << 12; // XDATA3
    dataX |= (acc_sensor_read(axi_iic_ad, 0x3b, 0x09) << 4); // XDATA2
    dataX |= ((acc_sensor_read(axi_iic_ad, 0x3b, 0x0a) & 0xf0) >> 4); // XDATA1
    if(dataX & 0x80000) // Is the 19th bit 1?
        dataX |= 0xfff00000; // sign extension

    dataY = acc_sensor_read(axi_iic_ad, 0x3b, 0x0b) << 12; // YDATA3
    dataY |= (acc_sensor_read(axi_iic_ad, 0x3b, 0x0c) << 4); // YDATA2
    dataY |= ((acc_sensor_read(axi_iic_ad, 0x3b, 0x0d) & 0xf0) >> 4); // YDATA1
    if(dataY & 0x80000) // Is the 19th bit 1?
        dataY |= 0xfff00000; // sign extension

    dataZ = acc_sensor_read(axi_iic_ad, 0x3b, 0x0e) << 12; // ZDATA3
    dataZ |= (acc_sensor_read(axi_iic_ad, 0x3b, 0x0f) << 4); // ZDATA2
    dataZ |= ((acc_sensor_read(axi_iic_ad, 0x3b, 0x10) & 0xf0) >> 4); // ZDATA1
    if(dataZ & 0x80000) // Is the 19th bit 1?
        dataZ |= 0xfff00000; // sign extension

    printf("datax = %x, dataY = %x, dataZ = %x\n", (int)dataX, (int)dataY, (int)dataZ);

    return(0);
}


アプリケーション・ソフトウェアを動作させた結果は Tera Term に表示している。
acc_sensor_5_201016.png

ZYBO Z7-20 で ADXL355 を動作させている様子を示す。
acc_sensor_6_201016.jpg

acc_sensor_write(axi_iic_ad, 0x3a, 0x2d, 0x00); のオシロスコープの波形を示す。
acc_sensor_7_201016.jpg

Write は問題なかった。

Read のプロトコルについてはよく分かっていなかった。
ADXL355 の日本語マニュアルの 29 ページの図 68. I2C のタイミング図 - 1 バイト読出しを引用する。
acc_sensor_9_201016.png

これによると、Write のデバイス・アドレス、レジスタ・アドレスを送ってからRepeat Start し、Read のデバイス・アドレスを送って、データをスレーブ(ADXL355)からもらうということのようだ。

第5回 Arduino入門 I2C通信編” の 7 枚目のスライドの”5.タイミングパラメータ”の下の 2 つの図を引用する。
acc_sensor_10_201016.png

これよると、I2C Master が Read データの NACK を送った Read データで転送が終了するということのようだ。

これらを踏まえて、 int32_t temp = acc_sensor_read(axi_iic_ad, 0x3b, 0x00); コードの実行によるオシロスコープの波形を見てみよう。
acc_sensor_8_201016.jpg

Write デバイス・アドレス(0x3a)、レジスタ・アドレス(0x00)、 Repeat Start、 Read デバイス・アドレス(0x3b)、 Read データ(0xad)、 NACK が見える。

ADXL355 のレジスタ・アドレス 0 番地を Read した時のデータは 0xAD なので、合っている。
  1. 2020年10月17日 05:12 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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