【Cソースコード修正】DE0-Nanoで、NiosとI2CのIPを使ってEEPROMにアクセスする

FPGA

前回、QuartusのPlatform Designerで、NiosとI2CのIP実装してDE0-NanoのEEPROMにアクセスしてみました。

EEPROMへのリード・ライトはできたのものの、リードの動作が不安定でusleep()で待ち時間を多く入れていたので、今回はちょっとソースコードを修正してみました。

ちなみに、ハードウェアは前回から変更はありません。

スポンサーリンク

修正版のソースコード

機能説明

やることは前回と同様に、EEPROMへのリードとライトです。

今回は、書き込み先のアドレスとデータを変更した、Test1とTest2を用意しました。

Test1

  • 書き込み先アドレス:0x00
  • 書き込みデータ : 0x55

Test2

  • 書き込み先アドレス : 0x01
  • 書き込みデータ : 0xAA

と、内容はシンプルです。

書き込み先アドレスから、書き込んだデータが読み込めればOKとします。

Cソースコード

では、修正したソースコードです。

#include "sys/alt_stdio.h"
#include "system.h"
#include <stdio.h>
#include <unistd.h>
#include <altera_avalon_i2c.h>
#include <altera_avalon_i2c_regs.h>
#include "sys/alt_irq.h"


void Check_TxRdy()
{
	alt_u32 data_u32 = 0;

	//Wait until TxReady = 1
	while(1){
		usleep(50);
		data_u32 = IORD_ALT_AVALON_I2C_ISR(I2C_0_BASE);
		if(data_u32 & 1){
			break;
		}
	}
}

void Check_RxRdy()
{
	alt_u32 data_u32 = 0;

	//Wait until RxReady = 1
	while(1){
		usleep(50);
		data_u32 = IORD_ALT_AVALON_I2C_ISR(I2C_0_BASE);
		if(data_u32 & 2){
			break;
		}
	}
}




void Write_EEPROM(alt_u32 addr, alt_u32 data)
{

	//Write device address
		//bit9 : STA  -- 1: Requests a repeated START condition to be generated before current byte transfer
		//bit8 : STO  -- 0:
		//bit7:1      -- I2c write address = 0xA0
		//bit0 : RW_D -- 0: Specifies I2C write transfer request

	alt_printf("Tx = 0x2A0 \n");
	Check_TxRdy();
	IOWR_ALT_AVALON_I2C_TFR_CMD(I2C_0_BASE, 0x2a0);


	//Write EEPROM address
		//bit9 : STA  -- 0:
		//bit8 : STO  -- 0:
		//bit7:1      -- EEPROM address
		//bit0 : RW_D -- 0: Specifies I2C write transfer request

	alt_printf("Tx = %x \n", addr);
	Check_TxRdy();
	IOWR_ALT_AVALON_I2C_TFR_CMD(I2C_0_BASE, addr);


	//Write EEPROM data
		//bit9 : STA  -- 0:
		//bit8 : STO  -- 1: Requests a STOP condition to be generated after current byte transfer
		//bit7:1      -- EEPROM data
		//bit0 : RW_D -- 0: Specifies I2C write transfer request

	alt_printf("Tx = %x \n", data);
	Check_TxRdy();
	IOWR_ALT_AVALON_I2C_TFR_CMD(I2C_0_BASE, 0x100 | data);

}


alt_u32 Read_EEPROM(alt_u32 addr)
{

	alt_u32 rdata=0;

	//Write device address
		//bit9 : STA  -- 1: Requests a repeated START condition to be generated before current byte transfer
		//bit8 : STO  -- 0:
		//bit7:1      -- I2c write address = 0xA0
		//bit0 : RW_D -- 0: Specifies I2C write transfer request
	alt_printf("Tx = 0x2A0 \n");
	Check_TxRdy();
	IOWR_ALT_AVALON_I2C_TFR_CMD(I2C_0_BASE, 0x2a0);


	//Write EEPROM address
		//bit9 : STA  -- 0:
		//bit8 : STO  -- 0:
		//bit7:1      -- EEPROM address
		//bit0 : RW_D -- 0: Specifies I2C write transfer request
	alt_printf("Tx = %x \n", addr);
	Check_TxRdy();
	IOWR_ALT_AVALON_I2C_TFR_CMD(I2C_0_BASE, addr);



	//I2c read address
		//bit9 : STA  -- 1: Requests a repeated START condition to be generated before current byte transfer
		//bit8 : STO  -- 0:
		//bit7:1      -- I2c write address = 0xA1
		//bit0 : RW_D -- 0: Specifies I2C read transfer request
	alt_printf("Tx = 0x2A1 \n");
	Check_TxRdy();
	IOWR_ALT_AVALON_I2C_TFR_CMD(I2C_0_BASE, 0x2a1);



	//Data read
		//bit9 : STA  -- 0:
		//bit8 : STO  -- 1: Requests a STOP condition to be generated after current byte transfer
		//bit7:1      -- read data
		//bit0 : RW_D -- 0: Specifies I2C read transfer request
	alt_printf("Tx = 0x100 \n");
	Check_TxRdy();
	IOWR_ALT_AVALON_I2C_TFR_CMD(I2C_0_BASE, 0x100);


	//Read EEPROM data
	Check_RxRdy();										//Wait until RxRDY = 1
	rdata = IORD_ALT_AVALON_I2C_RX_DATA(I2C_0_BASE);	// Read Receive Data FIFO


	return rdata;

}

void i2c_irq_handler()
{
	alt_u32 data_u32=0;

	//----------------------
	//Read Interrupt Status Register
	//----------------------
	data_u32 = IORD_ALT_AVALON_I2C_ISR(I2C_0_BASE);
	alt_printf("ISR = %x \n", data_u32);

	//Receive overrun
	if( (data_u32 & 0x10) == 0x10){
		IOWR_ALT_AVALON_I2C_ISR(I2C_0_BASE, 0x10);	//Flag clear
		alt_printf("Receive overrun \n");
	}

	//Arbitration lost detected
	if( (data_u32 & 0x08) == 0x08){
		IOWR_ALT_AVALON_I2C_ISR(I2C_0_BASE, 0x08);	//Flag clear
		alt_printf("Arbitration lost detected \n");
	}

	//No acknowledgement detected
	if( (data_u32 & 0x04) == 0x04){
		IOWR_ALT_AVALON_I2C_ISR(I2C_0_BASE, 0x04);	//Flag clear
		alt_printf("No acknowledgement detected \n");
	}

}


int main()
{
	ALT_AVALON_I2C_DEV_t *i2c_dev; //pointer to instance structure
	alt_u32 data_u32;
	alt_u32 rdata;
	int context_i2c;



	alt_printf("Hello EEPROM !!\n");

	//get a pointer to the avalon i2c instance
	i2c_dev = alt_avalon_i2c_open("/dev/i2c_0");

	if (NULL == i2c_dev)
	{
		printf("Error: Cannot find /dev/i2c_0\n");
		return 1;
	}


	//----------------------
	//IRQ
	//----------------------
	alt_ic_isr_register(I2C_0_IRQ_INTERRUPT_CONTROLLER_ID, I2C_0_IRQ, i2c_irq_handler, &context_i2c, 0x0);
	alt_ic_irq_enable(I2C_0_IRQ_INTERRUPT_CONTROLLER_ID, I2C_0_IRQ);

	//----------------------
	//Control Register
	//----------------------
		//bit0 : EN        -- 0: Core is disabled
	data_u32 = 0x00;
	IOWR_ALT_AVALON_I2C_CTRL(I2C_0_BASE,data_u32);


	//----------------------
	//Interrupt Status Enable Register Register
	//----------------------
		//bit4 : 1 = Enable interrupt for RX_OVER condition
		//bit3 : 1 = Enable interrupt for ARBLOST_DET condition
		//bit2 : 1 = Enable interrupt for NACK_DET condition
		//bit1 : 0 = Enable interrupt for RX_READY condition
		//bit0 : 0 = Enable interrupt for TX_READY condition
	IOWR_ALT_AVALON_I2C_ISER(I2C_0_BASE, 0x1C);


	//----------------------
	//Control Register
	//----------------------
		//bit1 : Bus speed -- 0: Standard mode (up to 100 kbits/s)
		//bit0 : EN        -- 1: Core is enabled
	data_u32 = 0x01;
	IOWR_ALT_AVALON_I2C_CTRL(I2C_0_BASE,data_u32);


	//----------------------
	// Test1
	//----------------------
	alt_printf("--- Write Test1 !! ---\n");
	Write_EEPROM(0, 0x55);

	usleep(1000);

	alt_printf("--- Read Test1 !! ---\n");
	rdata = Read_EEPROM(0);
	alt_printf("Read data = %x \n", rdata);
	alt_printf(" \n");



	//----------------------
	// Test2
	//----------------------
	alt_printf("--- Write Test2 !! ---\n");
	Write_EEPROM(1, 0xAA);

	usleep(1000);

	alt_printf("--- Read Test2 !! ---\n");
	rdata = Read_EEPROM(1);
	alt_printf("Read data = %x \n", rdata);


	return (0);
}

Terasic DE0-NANO開発ボード 【P0082】

新品価格
¥14,520から
(2020/10/12 23:25時点)

関数説明

Check_TxRdy()とCheck_RxRdy()

前回の失敗点ですが、送信バッファと受信バッファの状態をチェックしていませんでした。

以下は、インテルが公開しているEmbedded Peripherals IP User Guideからの抜粋です。

バッファが状態を”bit1 : RX_READY“と”bit0 : TX_READY“でチェックしないといけなかったのですが、前回は完全に無視していました。

ということで、今回は”Check_TxRdy()“と”Check_RxRdy()“関数でチェックします。

割込みは使わず、ポーリングでチェックしています。bitが1に変わらなければ無限ループに入ってしまって危険ですが、実験なので。

これらの関数でRX_READYとTX_READYをチェックし、RX_READY=1になったらレジスタからデータを読み込み、TX_READY=1になったらコマンド・データをレジスタにデータを書き込みます。

void Write_EEPROM(alt_u32 addr, alt_u32 data)

EEPROMの指定アドレスに指定データを書き込む関数です。

第一引数は書き込み先のEEPROMのアドレスで、第二引数は書き込みデータです。

  1. TX_READY=1まで待ち
  2. 0x2A0を送信バッファにセット(スタートビット(bit9)とデバイスアドレス0xA0)
  3. TX_READY=1まで待ち
  4. addrを送信バッファにセット(EEPROMアドレスaddr)
  5. TX_READY=1まで待ち
  6. dataを送信バッファにセット(ストップビット(bit8)とデータdata)

alt_u32 Read_EEPROM(alt_u32 addr)

EEPROMの指定アドレスから、データを読み込む関数で、データは呼び出し元の関数に戻します。

第一引数は読み込むEEPROMのアドレスです。

EEPROM 24LC02Bのデータシートを元に、”RANDOM READ”を行っています。

  1. TX_READY=1まで待ち
  2. 0x2A0を送信バッファにセット(スタートビット(bit9)とデバイスアドレス0xA0)
  3. TX_READY=1まで待ち
  4. addrを送信バッファにセット(EEPROMアドレスaddr)
  5. TX_READY=1まで待ち
  6. 0x2A1を送信バッファにセット(スタートビット(bit9)とデバイスアドレス0xA1)
  7. TX_READY=1まで待ち
  8. 0x00を送信バッファにセット(ストップビット(bit8)と0x00)
  9. RX_READY=1まで待ち
  10. 受信バッファからデータ読み込み
  11. 呼び出し元の関数に読み込んだデータをreturn

void i2c_irq_handler()

割込みの関数を用意しましたが、この関数が呼ばれることはありませんでした。

ちなみに、TX_READYとRX_READYは割込みに使えますが、ポーリングでビットを見るようにしたため、割込みには使いませんでした。

実行結果

コンソールウインドウに表示させた結果です。

Test1では、EEPROMアドレス0に、データ0x55がリード・ライトできてそうです。

Test2では、EEPROMアドレス1に、データ0xAAがリード・ライトできてそうです。

検証

terasIC社のDE0_Nano_ControlPanelというツールで確認してみます。

このツールで見てもリード・ライトに成功していそうです。

まとめ

前回より、まともなソースコードになったんじゃないかと思います。

しかし、I2CのIPにはまだまだ謎の機能のレジスタがあります。

例えば、SCL_LOW、SCL_HIGH、SCL_HOLDなど。

オシロスコープがあれば、もっといろいろ検証ができそうですが、持っていないので、レジスタ値を変えると、波形がどう変わるか謎です。。。

今のところ、「動いたから大丈夫でしょう」というレベルの設計です。

タイトルとURLをコピーしました