天天看點

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)

一、I2C介紹

I2C:Inter-Integrated Circuit bus,雙方向的2-wire bus:SDA-serial data;SCL-serial clock.一般用于兩個裝置間的通信,即master和slave,slave既可以做receiver也可以做transmitter。I2C總線協定規定,任何将資料傳送到總線的作為發送器,任何從總線接收資料的器件作為接收器。

資料傳送由主器件控制:SCL以及起始終止條件均由master産生。

在這裡介紹的主要是兩種時鐘晶片:DS1302和AT24C02:

二、讀寫位元組的實作

DS1302: 

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)
基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)

 RST是複位或者片選信号,因為它起着這兩個功能。

時序分析:

單位元組寫

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)

以上為DS1302一個位元組寫入的時序圖,第一個是位址位元組,第二個是資料位元組,RST必須拉高,否則資料的輸入是無效的,即RST信号控制資料信号輸入的開始和結束,位址位元組和資料位元組的讀取時上升沿有效,而且是從LSB開始讀入。

單位元組讀:

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)

Vivado部分:

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)

用一個AXI GPIO的IP來組合三個信号。

代碼部分:

void WriteClock(u32 address, u32 value)
{
	XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_SCLK_OFF);//0x00
	XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_ON); // SLCK = off 0x01

	// write command byte
	u32 bitval = 0x0;
	int i;
	for(i = 0; i<8; i++)
	{
		bitval = (address >> i) & BITMASK; // sending bit for bit 0x01
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_1, bitval);
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_SCLK_ON);//0x03
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_1, bitval);
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_ON); // SLCK = off0x01
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_1, bitval);
	}

	// write value
	bitval = 0x0;
	for(i = 0; i<8;i++)
	{
		bitval = (value >> i) & BITMASK; // sending bit for bit
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_1, bitval);
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_SCLK_ON);
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_1, bitval);
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_ON); // SLCK = off
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_1, bitval);
	}

	XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_SCLK_OFF);
}

/*
 * This function reads the DS1302 clock value on the address (hex)
 * provided in the parameter.
 * Note: addresses can be used from the header file (seconds, minutes, hours, etc.)
 * One does not need to care about setting the read bit. The function adds it, if the
 * programmer forgot.
 */
u32 ReadClock(u32 address)
{
	address = address | 0x01; // add read bit if not already added
	u32 retval = 0x00;

	XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_SCLK_OFF);
	XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_ON); // SLCK = off

	// write command byte
	u32 bitval = 0x0;
	int i;
	for(i = 0; i<8; i++)
	{
		bitval = (address >> i) & BITMASK; // sending bit for bit
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_1, bitval);
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_SCLK_ON);
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_1, bitval);
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_ON); // SLCK = off
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_1, bitval);
	}

	// read value
	bitval = 0x0;
	XGpio_SetDataDirection(&Gpio_DS1302, CHANNEL_1, 0xff);
	for(i = 0; i<8;i++)
	{
		//reading bit for bit

		bitval = XGpio_DiscreteRead(&Gpio_DS1302, CHANNEL_1);

		// setting the bit value in the return value (hex)
		retval ^= (-bitval ^ retval) & (1 << i);

		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_SCLK_ON);
		XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_ON); // SLCK = off

	}

	XGpio_DiscreteWrite(&Gpio_DS1302, CHANNEL_2, DS1302_RST_SCLK_OFF);
	XGpio_SetDataDirection(&Gpio_DS1302, CHANNEL_1, 0x0);
	return retval;
}
           

讀位元組和寫位元組有一些不同之處,第一個位元組也是先寫位址,然後再讀資料位元組,RST全程拉高,寫位元組的時候SCLK上升沿有效,讀位元組時下降沿有效。

在寫一個位元組的時候I/O一直保持輸出狀态,相反的是在讀位元組的時候I/O先為輸出狀态,然後為輸入狀态,且必須改變時鐘信号的順序。

AT24C02:

起始和終止條件:

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)
void startCondition()
{
	XGpio_DiscreteWrite(&Gpio,SDA_CHANNEL,1);
	delay1();
	delay1();
	XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,1);
	delay1();
	delay1();
	XGpio_DiscreteWrite(&Gpio,SDA_CHANNEL,0);
	delay1();
	delay1();
}
void stopCondition()
{
	XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,0);
	delay1();
	delay1();
	XGpio_DiscreteWrite(&Gpio,SDA_CHANNEL,0);
	delay1();
	delay1();
	XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,1);
	delay1();
	delay1();
	XGpio_DiscreteWrite(&Gpio,SDA_CHANNEL,1);
	delay1();
	delay1();
}
           

在資料傳送8位後,等待或者發送一個應答信号:

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)
char response()
{
	char receive_ack;
	XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,0);//初始SCL
	XGpio_DiscreteWrite(&Gpio,SDA_CHANNEL,1);//将SDA拉高
	XGpio_SetDataDirection(&Gpio, SDA_CHANNEL,1);//SDA放棄對總線的控制權
	delay1();
	delay1();
	XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,1);//SCL拉高
	delay1();
	receive_ack=XGpio_DiscreteRead(&Gpio,SDA_CHANNEL);//擷取應答信号
	delay1();
	XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,0);
	delay1();
	XGpio_SetDataDirection(&Gpio, SDA_CHANNEL,0);//SDA重新擷取控制權
	delay1();
	return receive_ack;
	}
           

此時要在SDA線上置1,放棄對總線的控制權,進而接受應答信号,接收信号完後,又将其方向設為0,重新擷取控制權

單位元組寫和頁寫實作代碼:

void write_add(char address,char value)
{
		int isAck;
		startCondition();
		write_byte(0xa0);//器件位址
		isAck=response();
		if(isAck==0)//确認則繼續寫入資料
		{
			write_byte(address);//寫位址
			isAck=response();
		}
		if(isAck==0)
		{
			write_byte(value);//寫入的資料
			isAck=response();
		}

stopCondition();
}

void write_byte(char value)
{
	char bitval=0x0;
			int i;
			XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,0);
			delay1();
			delay1();
			for(i=0;i<8;i++)
			{
				bitval=value>>(7-i);
				XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,0);//SCL拉低準備寫資料
				delay1();
				XGpio_DiscreteWrite(&Gpio,SDA_CHANNEL,bitval);//将資料送入SDA
				delay1();
				XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,1);//SCL拉高資料寫完畢
				delay1();
				delay1();
				XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,0);//SCL拉低準備寫資料
				
			}
			XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,0);
			delay1();
			XGpio_DiscreteWrite(&Gpio,SDA_CHANNEL,1);
			delay1();
}
int write_Page(char page,char *value)
{
	int isAck,i;
	startCondition();
	write_byte(0xa0);
	isAck=response();
	if(isAck==0)//确認則繼續寫入資料
			{
				write_byte(16*page);//寫位址
				isAck=response();
			}
	for(i=0;i<16;i++)
		{
			if (isAck == 0)
			{
			write_byte(value);
			isAck=response();
			}
			else
				break;
		}
	stopCondition();
	return XST_SUCCESS;
}
           

在寫操作時,第一步寫入器件位址,第二步寫入寫位址,第三步才是要寫入的資料。

讀操作實作代碼:

char read_byte()
{
	char value=0x0;
	char temple=0x0;
	int i,j;
	XGpio_DiscreteWrite(&Gpio,SCL_CHANNEL,0);
	delay1();
	XGpio_DiscreteWrite(&Gpio,SDA_CHANNEL,1);
	delay1();
	for(j=0;j<8;j++)
	{
		XGpio_DiscreteWrite(&Gpio, SCL_CHANNEL, 1);
				delay1();
				delay1();
	temple=XGpio_DiscreteRead(&Gpio, SDA_CHANNEL);
	value=(value<<1)|(temple&0x01);
	XGpio_DiscreteWrite(&Gpio, SCL_CHANNEL, 0);
					delay1();
					delay1();
	}
	return value;
}
char read_add(char address)
{
	int isAck;
	char data;
	startCondition();
	write_byte(0xa0);
	isAck=response();
	if(isAck==0)
	{
		write_byte(address);
		isAck=response();
		startCondition();
	}
	if(isAck==0)
	{
	write_byte(0xa1);
	isAck=response();
	}
	if(isAck==0)
	{
		data=read_byte();
	}
	stopCondition();
	return data;
	}
           

讀操作和寫操作有很大的差別就是,首先要寫入進行讀操作的從器件位址,然後寫入要讀的位址。這之後需要重新開始一次起始條件,再開始進行讀位元組。

三、HardwareDebug檢視軟體時序 

Open Synthesized Design打開綜合後的設計,然後打開debug的schematic或者Netlist設定要檢視的引腳,即mark debug。 

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)
基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)
基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)

Setup Debug:

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)

 Export Hardware,啟動SDK,Open Hardware Manager:

選擇open target的auto connect連接配接到我們的闆子上,然後program device打開檢視時序的界面,根據之前的設計,設定觸發的起始條件:

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)
基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)

最初的狀态是Idle,當點選運作時狀态變為 Waiting for Trigger,當SDK部分運作或調試啟動時,狀态變為Full,觸發開始記錄時序:

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)
基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)

 最終的軟體運作時序為:

基于ZYNQ的嵌入式學習筆記五(DS1302和AT24C02的位元組讀寫)