一、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:
RST是複位或者片選信号,因為它起着這兩個功能。
時序分析:
單位元組寫
以上為DS1302一個位元組寫入的時序圖,第一個是位址位元組,第二個是資料位元組,RST必須拉高,否則資料的輸入是無效的,即RST信号控制資料信号輸入的開始和結束,位址位元組和資料位元組的讀取時上升沿有效,而且是從LSB開始讀入。
單位元組讀:
Vivado部分:
用一個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:
起始和終止條件:
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位後,等待或者發送一個應答信号:
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。
Setup Debug:
Export Hardware,啟動SDK,Open Hardware Manager:
選擇open target的auto connect連接配接到我們的闆子上,然後program device打開檢視時序的界面,根據之前的設計,設定觸發的起始條件:
最初的狀态是Idle,當點選運作時狀态變為 Waiting for Trigger,當SDK部分運作或調試啟動時,狀态變為Full,觸發開始記錄時序:
最終的軟體運作時序為: