« 2016年8月 | トップページ | 2016年10月 »

2016年9月の記事

ZYBOのPSでI2Cを動かしてみた

ZYBOのPS(ARM Core部分)のI2Cを動かしてみました。本当は、OV7670カメラモジュールを使って画像の取り込みをやってみたかったのですが、Amazonで買ったOV7670モジュールがどうも不良品のようで、SCCB(I2Cのサブセットのカメラモジュール制御プロトコル)を使ってカメラモジュールとどうしても通信ができず、その過程で分かったIC2の使い方を書いています。

カメラモージュールの実験は、代替え品をaitendoさんに注文したので、商品が届いたら出直しです。Amazonのもaitendoさんのもカメラモジュール自体は同じものを使っていると思いますが、回路構成が若干異なり、aitendoの製品の方が使いやすと思います。理由は、Amazonで販売しているモジュールはパワーオンリセットやI2C信号線のプルアップがないためです。ペリフェラル側にプルアップがあると、ブレッドボードなどを経由してプルアップの配線をする必要がないので構成がスッキリします。

プルアップはFPGA内蔵のプルアップ機能を使う手もありますが、外付け抵抗を使ったプルアップの方が安定して動くと思います。ちなみに、今回の実験で使ったTMP102温度センサーは外付けプルアップ抵抗が必要で、FPGAのプルアップではクロックを10KHzに落としても正常に動作しませんでした。FPGAの内蔵プルアップはweak pull-upなので高速動作には使えないというようなフォーラムの書き込みがありました。

Update:FPGA内蔵のプルアップで動作しなかったのは、200Ωの保護抵抗が直列に入っているStandard Pmod (JE)にI2Cセンサーをつないだ時の場合でした。試しに保護抵抗が入っていない、Hi-Speed Pmod (JD)にFPGA内蔵のプルアップでつないでみたたらクロック100KHzでも動作しました。条件がよければFPGA内蔵のプルアップでも動作しますが、10KΩ程度の外部抵抗を使った方がより安定していると思われます。

ZYNQ PSのI2Cを使う方法

ZYBOでZYNQ PSのI2Cを使う方法は、以下の2通りのやり方があります:

  • PL(FPGAブロック)を介して、ZYBOのPmodコネクタ(JB〜JE)に接続する
  • PS(CPUブロック)直結のPmod MIO(JF)に接続する

ここでは、それぞれの場合について試してみます。開発環境は執筆時点で最新の、Vivado 2016.2を使用しています。

PL(FPGAブロック)を介して、ZYBOのPmodコネクタ(JB〜JE)に接続する方法

Vivadoで新規プロジェクトを作成して、IP Integrator(IPI)で新規のデザインを作成します(今回はsystemというデザイン名にしています)。図のようにZYNQ 7 Processing Sysemのみをインスタンス化してクロックの接続を手動で行います。

Block Design

ZYNQ 7 Processing Systemのアイコンを右クリックして、”Customize Block..”メニューを開きます。

Customize Block

MIO Configurationをクリックして、IO Peripheralsのプルダウンを開き、I2C0にチェックを入れます。また、IOに「EMIO」を指定します。EMIOを指定することによって、CPUコアのI2C信号がFPGAのPL部分を通って外部に接続できるようになります。

I2C EMIO

ブロックデザインに戻って、ZYNQ 7のIIC_0を右クリックして”Make External”メニューを選択します。この操作によって、I2Cの信号をFPGAから外部に出力できるようになります。外部出力を作る方法には、”Create Interface Port.."などオプション指定ができる方法もありますが、今回はMake Externalで問題ありませんでした。

I2C Make External

次に、Block DesignのSource画面に移って、デザイン名(今回の場合はsystem)を右クリックし、”Create HDL Wrapper..”を選択します。

CreateHDL Wrapper

以下のダイアログボックスが表示されるので、”Let Vivado manage wrapper and auto-update”を選択してOKをクリック。

Let Vivado Manage Wrapper

Flow Navigatorから「Run Implementation」を実行。Implementationが終了すると以下のダイアログボックスが表示されるので、”Open Implemented Design”を選択。

Implementation Complete

画面下に表示される「IO Ports」タブを開くとIIC_0ポートのピンアサイン画面が表示されます。ここに信号を接続したいFPGAのピン番号を入力します。

IO Port

ZYBOのReference Manualを参照して、今回は信号をPmod JE(Standard Pmod)のJE1とJE2に接続します。それぞれに対応するFPGAのピン番号V12とW16を入力、出力電圧をLVCMOS33(3.3V)に指定します。

Assign PL Pin

CTL-Sキーを押すと、Constraintsを保存するダイアログボックスが表示されるのでOKをクリック。

Save Constraints

ファイル名を指定して保存すると、SourcesにConstraints(制約)ファイルが追加されています。

Constraints Added

続けて、Flow Navigatorから「Generate Bitstream」を実行。実行が完了すると以下のダイアログボックスが表示されるので、”Open Implementation Design”を指定してOKをクリック。

Genarate BitStream

File Menu → Export → Export Hardware..を選択。Include bitstreamをチェックしてOKをクリック。

Expot Hardware

File Menu → Launch SDKを選択。SDKが立ち上がります。

SDK Launched

SDKのFile Menu → New → Application Projectを選択。New Projectの設定画面にプロジェクト名(今回はI2C Test)を入力し、Nextをクリック。

New Project

Hello World Templateを選択します(このテンプレートにはprint文を使ってUARTにデバッグ情報を出力するために必要なファイルが含まれているため)。

Select Hello World

プロジェクトが生成されたら、helloworld.cをリネーム。今回は、i2c_test.cにしています(これは好みですが)。

Rename helloworld c

テンプレートが自動生成したソースを全部削除して、以下のコードを入力。 

 
#include "platform.h"
#include "xparameters.h"
#include "sleep.h"
#include "xiicps.h"
#include "stdio.h"


// I2C parameters
#define IIC_SCLK_RATE		100000	// clock 100KHz
#define TMP102_ADDRESS		0x48	// 7bit address
#define IIC_DEVICE_ID		XPAR_XIICPS_0_DEVICE_ID

XIicPs Iic;

int Init()
{
	int Status;
	XIicPs_Config *Config;	/**< configuration information for the device */

	Config = XIicPs_LookupConfig(IIC_DEVICE_ID);
	if(Config == NULL){
		printf("Error: XIicPs_LookupConfig()\n");
		return XST_FAILURE;
	}

	Status = XIicPs_CfgInitialize(&Iic, Config, Config->BaseAddress);
	if(Status != XST_SUCCESS){
		printf("Error: XIicPs_CfgInitialize()\n");
		return XST_FAILURE;
	}

	Status = XIicPs_SelfTest(&Iic);
	if(Status != XST_SUCCESS){
		printf("Error: XIicPs_SelfTest()\n");
		return XST_FAILURE;
	}

	XIicPs_SetSClk(&Iic, IIC_SCLK_RATE);
	printf("I2C configuration done.\n");

	return XST_SUCCESS;
}

int i2c_write(XIicPs *Iic, u8 command, u16 i2c_adder)
{
	int Status;
	u8 buffer[4];
	buffer[0] = command;

	Status = XIicPs_MasterSendPolled(Iic, buffer, 1, i2c_adder);

	if(Status != XST_SUCCESS){
		return XST_FAILURE;
	}

	// Wait until bus is idle to start another transfer.
	while(XIicPs_BusIsBusy(Iic)){
		/* NOP */
	}

	return XST_SUCCESS;
}


int i2c_read(XIicPs *Iic, u8* buff, u32 len, u16 i2c_adder)
{
	int Status;

	Status = XIicPs_MasterRecvPolled(Iic, buff, len, i2c_adder);

	if (Status == XST_SUCCESS)
		return XST_SUCCESS;
	else
		return -1;
}


int main()
{
	init_platform();
	Init();

	u8    buff[4];
	u16   rawdata;
	float temp;

	while(1) {
		i2c_write(&Iic, 0, TMP102_ADDRESS);
		i2c_read(&Iic, buff, 2, TMP102_ADDRESS);

		rawdata = ((int8_t)buff[0] << 4) | ((u8)buff[1] >> 4);
		temp = (float) ((float)rawdata * 0.0625);
		printf("Tmep: %2.1f\n", temp);
		usleep(1000*1000);		// sleep 1sec (1000 x 1000us)
	}

	cleanup_platform();
	return 0;
}

ファイルをセーブすると自動的にビルドが実行されます。次に、ツールバーのProgram FPGAボタンをクリックしてFPGAのコンフィグデーター(Bitstream)をJTAGインタフェース経由で転送します(ZYBOのJP5ジャンパーピンをJTAGに設定しておくこと)。

Program FPGA

続いて、デバッガーを起動してPS(ARM CPUコア)のプログラムを転送しますが、ちょっとコツがあります。私の環境では、Debgug Configurationをいきなり作って起動しようとするとエラーが出てプログラムの起動に失敗することが多いです。そのため、Project ExplorerのI2C_Testプロジェクトを右クリックし、Debug As → Launc on Hardware (GDB)を選択してまずプログラムを起動します。無事プログラムが起動するとmainの最初の行でプログラムがブレークします。

Debug Started

この段階ではDebug Configurationをしていないため、"STDIO not connected”の警告がConsoleに出力され、print文の実行結果は表示されません。ここで一旦、ツルーバーのTerminateボタンを押してプログラムを終了します。デバッグPerspectiveから一旦C/C++ Perspectiveに戻ってProject Explorer → I2C_Testプロジェクトを右クリック → Debug As → Debug Configurations..を選択。STDIO ConnectionにCOMポート番号と通信速度(115200)を設定してDebugをクリック。ツールバーのResumeボタンをクリックするとプログラムが動き出します。

Debug Configurations

TMP102から読み取った温度がConsole画面に表示されています。

Program Run

今回使っているI2CドラバーライブラリのサンプルやドキュメントはSDKのインストールフォルダーの中(C:\Xilinx\SDK\2016.2\data\embeddedsw\XilinxProcessorIPLib\drivers)にあるので、コードの中身の説明は割愛します。

以上が、I2Cの信号をFPGA(PL)経由で取り出す方法です。FPGA内の配線を見ると、I2CのSDAなどI/Oの信号はinとoutの2本の独立した信号としてCPUから出ており、Tri-state Bufferを介してI/Oの信号として外部ピンに接続されています。なんだが回りくどいことをやってるんですね。

SDA iobuf

このIOBUFはVivadoのIPをWrapperの中でインスタンス化して接続していることが以下のHDLコードから分かります。

Wrapper IOBUF

PS(CPUブロック)直結のPmod MIO(JF)に接続する方法

SDKを終了し、VivadoのBlock Designを開き、IIC_0ポートを削除します。

DeleteI 2C Port

Customize Block..を開き、I2C0の接続先を「MIO 10..11」に変更します。

Assign I2C MIO

OKをクリックし、Run Implementationを実行します。実行後のI/O Portsタブを見ると、I2Cの信号が消えていることが分かります。これはI2Cの信号がFPGAブロック(PL)を経由しなくなったことを意味します。

No I2C In PL

Generate Bitstreamを実行し、終了後、Export Hardwareを実行して再度SDKを立ち上げます。SDKのProject ExplorerからI2C_Test_bspを右クリックして、Re-generate BSP Sourcesを実行。さらに、Project Menu → Clean..を実行してビルド環境をクリーンアップします。

Clean Build

センサーのI2CのピンをJFコネクタのMIO-10 (JF2: SCL)、MIO-11 (JF3: SDA)に繋ぎ変えてデバックを実行するとプログラムが起動します。今回はCPUから直接I2Cに出力しています。デバッグでプログラムの起動に失敗する場合は、Debgug Configurationに入って既存のデバッグエントリを一旦削除してから、最初の手順ででデバッグを際実行する(先ずはDebug Configを作らずにDebug Asから起動する)とうまくいくと思います。

Delete Debug Target

ということで、PL経由・PS直結のどちらでもI2Cが動くことが確認できました。

参考情報

« 2016年8月 | トップページ | 2016年10月 »

2017年2月
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28        
無料ブログはココログ