iic通信協(xié)議是什么
IIC協(xié)議是二線制,信號(hào)線包含SDA和SCL,且信號(hào)線是雙向的,開路結(jié)構(gòu),需要通過上拉電阻到VCC,具體的電阻值影響的是信號(hào)反應(yīng)速度和驅(qū)動(dòng)能力。
首先,IIC通信與UART,還有SPI統(tǒng)稱為串行接口通信,不過它們之間還是有區(qū)別的,如UART的負(fù)電平邏輯,還有UART通信不需要時(shí)鐘,只需要特定的波特率即可,SPI與IIC都可以有一個(gè)主機(jī),多個(gè)從機(jī)的情況,不過IIC適用于短距離傳輸,如片間通信,攝像頭的配置等場(chǎng)景。
要搞定IIC首先來(lái)看IIC的硬件接口:
如圖所示,我們知道IIC一個(gè)主機(jī)可以懸掛多個(gè)從機(jī),所以地址線A2,A1,A0 可以實(shí)行片選的功能,那么WP這個(gè)引腳的功能就是當(dāng)WP懸空或者接地的時(shí)候,表示這時(shí)的EEPROM既可以讀,也可以寫,當(dāng)WP接電源時(shí),則只可以讀而不能寫。
SCL與SDL這兩個(gè)引腳,必須上拉,否則驅(qū)動(dòng)能力不夠,無(wú)法進(jìn)行正常的IIC通信。
OK,硬件接口已經(jīng)介紹清楚了,那么我們現(xiàn)在開始來(lái)看協(xié)議了。
首先IIC分為字節(jié)讀寫和頁(yè)面讀寫,首先來(lái)看字節(jié)讀寫的協(xié)議:
如上圖所示,如果我們要向EEPROM中寫入一個(gè)字節(jié)的數(shù)據(jù),得有如下幾個(gè)步驟:
1.開始信號(hào)——在SCLK的高電平器件,拉低SDA的信號(hào)(由1 變?yōu)?)。
2.控制字節(jié)——即器件地址,就是你操作那一塊EEPROM。
3.ACK信號(hào)——由從機(jī)發(fā)出,主機(jī)為接收,所以在此階段,sda_link必須置為0,即為讀取這個(gè)應(yīng)答信號(hào),所以在SCLK的高點(diǎn)平期間。
4.字節(jié)地址——即某一塊EEPROM里面的哪一個(gè)地址。
5.ACK信號(hào)——與上述相同。
6.數(shù)據(jù)信號(hào)——即你往某個(gè)地址里面寫入的8位數(shù)據(jù)。
7.ACK信號(hào)——上述相同。
8.結(jié)束信號(hào)——在SCLK的高電平期間,拉高SDA信號(hào),表示通信結(jié)束。
再來(lái)看讀的時(shí)序:
由上圖可看出讀時(shí)序的前面處理方式與寫相同,不同的時(shí)在第三個(gè)ACK信號(hào)來(lái)了之后,如果是讀,那么會(huì)又有一個(gè)起始信號(hào),緊接著讀器件地址,然后應(yīng)答,再然后讀數(shù)據(jù),再然后在SCLK的低電平期間發(fā)送一個(gè)NO ACK信號(hào),要記住這個(gè)信號(hào)由主機(jī)發(fā)出,然后緊接著一個(gè)結(jié)束信號(hào)。
由上述讀寫時(shí)序我們可知,通信的起始均在SCLK的高電平期間發(fā)生跳變,這就據(jù)定了我們其他信號(hào)跳變均在SCLK的下降沿,SCLK高電平期間數(shù)據(jù)穩(wěn)定,適用于讀(即低電平改變數(shù)據(jù),高電平采集數(shù)據(jù))。
具體過程如下:
首先板子上電來(lái)個(gè)初始化需要來(lái)個(gè)延時(shí),具體多少用計(jì)數(shù)器自己搞定。
代碼如下:
reg [6:0] hadware_initial_delay;
wire hadware_initial_delay_done;
always@(posedge clk or negedge rst_n)
if(!rst_n)
hadware_initial_delay《=7’d0;
else
if(hadware_initial_delay《=7’d49)
hadware_initial_delay《=hadware_initial_delay+1;else
hadware_initial_delay《=hadware_initial_delay;assign hadware_initial_delay_done=(hadware_initial_delay==7’d50)?1’b1:1’b0;OK,我們要知道IIC的速率一般就幾百KH而我們的系統(tǒng)時(shí)鐘為50M,所以需要分頻:
代碼如下:
reg [8:0] sclk_cnt;
always@(posedge clk or negedge rst_n)
if(!rst_n)
sclk_cnt《=9’d0;
else
if(hadware_initial_delay_done)
begin
if(sclk_cnt《9’d499)
sclk_cnt《=sclk_cnt+1;
else
sclk_cnt《=0;
end
assign sclk=(sclk_cnt《=9’d249)?1’b1:1’b0;OK,我們知道SCLK高電平期間采集數(shù)據(jù),低電平期間改變數(shù)據(jù),那么當(dāng)然,這個(gè)“期間”肯定時(shí)時(shí)鐘沿中間最好啦,畢竟更容易滿足建立時(shí)間與保持時(shí)間,很穩(wěn)定的。
具體代碼如下:
wire sclk_posedge_middle=(sclk_cnt==9’d124)?1’b1:1’b0;wire sclk_negedge_middle=(sclk_cnt==9’d374)?1’b1:1’b0;OK,讀寫定義了那么多個(gè)過程,當(dāng)然需要狀態(tài)機(jī)來(lái)搞定啦,定義變量如下:
parameter IDLE = 4’d0 ;
parameter START1 = 4’d1 ;
parameter ADD1 = 4’d2 ;
parameter ACK1 = 4’d3 ;
parameter ADD2 = 4’d4 ;
parameter ACK2 = 4’d5 ;
parameter DATA = 4’d6 ;
parameter ACK3 = 4’d7 ;
parameter STOP1 = 4’d8 ;
parameter START2 = 4’d9 ;
parameter ADD3 = 4’d10;
parameter ACK4 = 4’d11;
parameter DATA_READ = 4’d12;
parameter NO_ACK = 4’d13;
parameter STOP2 = 4’d14;
OK,再來(lái)個(gè)宏定義,假設(shè)寫入是這幾個(gè)地址,這幾個(gè)數(shù)據(jù)。
define DEVICE_READ 8‘b1010_0001
define DEVICE_WRITE 8’b1010_0000
define WRITE_DATA 8’b0001_0001
define BYTE_ADDR 8’b0000_0011
SDA雙向端口,這個(gè)記住,一般這樣搞;
reg sda_link;
reg sda_out_r;
assign sda=sda_link?sda_out_r:1’bz;
當(dāng)作為輸出時(shí),對(duì)吧,使sda_link拉高,作為輸入時(shí),輸入高阻。
各過程如下:
reg [3:0] current_state;
//reg [3:0] next_state;
reg [7:0] db_r;
reg [3:0] num;
reg [7:0] data_out_reg;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
sda_link《=0;
db_r《=0;
num《=0;
current_state《=IDLE;
sda_out_r《=0;
data_out_reg《=8’b0;
end
else
begin
case(current_state)
IDLE:begin
sda_out_r《=1;
sda_link《=1;
if(!sw1_r||!sw2_r)
current_state《=START1;
else
current_state《=IDLE;
end
START1:if(sclk_posedge_middle)
begin
sda_out_r《=0;
db_r《=`DEVICE_WRITE;
current_state《=ADD1;
end
else
current_state《=START1;
ADD1 :
if(sclk_negedge_middle)
begin
if(num==4‘d8)
begin
sda_link《=0;
num《=0;
current_state《=ACK1;
sda_out_r《=1;
end
else
begin
current_state《=ADD1;
sda_out_r《=db_r[7-num];
num《=num+1;
end
end
else
current_state《=ADD1;
ACK1:
if(sclk_posedge_middle)
// begin
// if(!sda)
// begin
begin // */current_state《=ADD2;
db_r《=`BYTE_ADDR;
end
else
current_state《=ACK1;
ADD2:begin
sda_link《=1;
if(sclk_negedge_middle)begin
if(num==4’d8)
begin
sda_link《=0;
current_state《=ACK2;
num《=4‘d0;
sda_out_r《=1;
end
else
begin
num《=num+1;
current_state《=ADD2;
sda_out_r《=db_r[7-num];
end
end
else
current_state《=ADD2;
end
ACK2:
if(sclk_posedge_middle)
////begin
//if(!sda)
begin
begin
if(!sw1_r)
begin
db_r《=`WRITE_DATA;
current_state《=DATA;
end
else
if(!sw2_r)
begin
current_state《=START2;
sda_out_r《=1;
end
end
else
current_state《=ACK2;
DATA: begin
sda_link《=1;
if(sclk_negedge_middle)
begin
if(num==4’d8)
begin
num《=4‘d0;
current_state《=ACK3;
sda_out_r《=1;
sda_link《=0;
end
else
begin
num《=num+1;
current_state《=DATA;
sda_out_r《=db_r[7-num];
end
end
else
current_state《=DATA;
end
ACK3: if(sclk_posedge_middle)
// begin
// if(!sda)
current_state《=STOP1;
// end
STOP1:
begin
sda_link《=1;
sda_out_r《=0;
if(sclk_posedge_middle)
begin
sda_out_r《=1;
if(sw1_r)
// 你要是不等它松開才恢復(fù)初始狀態(tài),那么你一旦恢復(fù)初始狀態(tài)SW1_r就為低電平,他又開始寫了,所以為了避免重復(fù)寫入數(shù)據(jù)。
current_state《=IDLE;
else
current_state《=STOP1;
end
else
current_state《=STOP1;
end
START2:begin
sda_link《=1;
if(sclk_posedge_middle)
begin
sda_out_r《=0;
sda_link《=1;
db_r《=`DEVICE_READ;
current_state《=ADD3 ;
end
end
ADD3: begin
if(sclk_negedge_middle)
begin
if(num==4’d8)
begin
num《=0;
sda_link《=0;
sda_out_r《=1;
current_state《=ACK4;
end
else
begin
num《=num+1;
sda_out_r《=db_r[7-num];
current_state《=ADD3;
end
end
else
current_state《=ADD3;
end
ACK4:
if(sclk_posedge_middle)
// begin
// if(!sda)
current_state《=DATA_READ;
else
current_state《=ACK4;
// end
DATA_READ:
begin
sda_link《=0;
if(sclk_posedge_middle)
begin
if(num==4‘d8)
begin
sda_link《=1;
sda_out_r《=1;
current_state《=NO_ACK;
num《=4’d0;
end
else
begin
num《=num+1;
current_state《=DATA_READ;
data_out_reg[7-num]《=sda;
end
end
end
NO_ACK:
if(sclk_negedge_middle)
begin
sda_out_r《=1;
current_state《=STOP2;
end
else
current_state《=NO_ACK;
STOP2:begin
sda_out_r《=0;
sda_link《=1;
if(sclk_posedge_middle)
begin
sda_out_r《=1;
current_state《=IDLE;
end
else
current_state《=STOP2;
end
default:current_state《=IDLE;
endcase
end
assign data_out=data_out_reg;
endmodule
仿真結(jié)果如下:
評(píng)論