天天看点

基于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的字节读写)