赞
踩
I2C(Inter-Integrated Circuit)是一种串行通信协议,也被称为IIC(Inter-IC)。它由飞利浦(Philips)公司开发,并广泛用于连接各种集成电路(IC)之间的通信。 iic属于半双工同步传输类型总线,即允许数据在发送和接收设备之间进行双向传输,但每次只能进行一种方向的数据传输(要么发送,要么接收),而不能同时发送和接收数据。
I2C协议有多个主设备(master)和从设备(slave)。主设备跟从设备之间使用两条线进行通信,一条是串行数据线(SDA,Serial Data Line),另一条是串行时钟线(SCL,erial Clock Line)。
IIC(Inter-Integrated Circuit)协议的时钟频率是可以根据具体应用和设备支持的情况进行调节的。传统的标准模式下,IIC总线的时钟频率通常为100 kHz。此外,还有三种快速模式:快速模式(Fast Mode)和高速模式(High-Speed Mode)和超快速模式(Ultra-fast mode)。在快速模式下,时钟频率可以达到400 kHz,高速模式下,时钟频率可以达到3.4MHZ。在超快速模块下,时钟频率可以达到5MHZ。
(1)sda信号初始状态为高电平,当sda拉低一个周期,表示传输传输
(2)开始后,sda写入一个字节的数据(串行),其中S6-S0为从设备的地址,用来选中从设备,WR为读写选择,当为高电平时,进行写操作
(3)随后从设备回应ack信号,此处标为ack1,本文设置传输的地址为16位,分为两次传输,从设备分别发出ack2跟ack3。
(4)D7-D0为传输的一字节数据
下图是sda信号各个bit表示:
此时的data由从设备给出
iic分为IIDLE START,SLAVE,WR ,ACK1 ,REG1 ,ACK2 ,REG2 ,ACK3 ,DATA ,ACK4 ,STOP
状态图如下:
代码如下:
- module iic(
-
- input sysclk ,
- input rst_n ,
- input i2c_en ,
- input [6:0] slave_addr ,
- input [15:0]register_addr,
- input [7:0] write_data ,
- input start ,
- input slave_ack ,
- input wr ,//wr=1 write,wr=0 read
- output reg [7:0] read_data ,
- output reg i2c_clk ,
- inout i2c_sda
-
- );
-
- parameter IDLE =12'b000000000001;
- parameter START=12'b000000000010;
- parameter SLAVE=12'b000000000100;
- parameter WR =12'b000000001000;
- parameter ACK1 =12'b000000010000;
- parameter REG1 =12'b000000100000;
- parameter ACK2 =12'b000001000000;
- parameter REG2 =12'b000010000000;
- parameter ACK3 =12'b000100000000;
- parameter DATA =12'b001000000000;
- parameter ACK4 =12'b010000000000;
- parameter STOP =12'b100000000000;
-
- reg [12:0]state ;
-
- //clk_cnt
-
- reg[4:0] clk_cnt;
- always @(posedge sysclk or negedge rst_n) begin
- if(rst_n==1'b0)
- clk_cnt<=5'd0;
- else if(clk_cnt==5'd19)
- clk_cnt<=5'd0;
- else
- clk_cnt<=clk_cnt+1'b1;
- end
- //i2c_clk
- always @(posedge sysclk or negedge rst_n) begin
- if(rst_n==1'b0)
- i2c_clk<=1'b0;
- else if(clk_cnt<5'd19&&clk_cnt>5'd9)
- i2c_clk<=1'b0;
- else if(clk_cnt<5'd9)
- i2c_clk<=1'b1;
- else
- i2c_clk<=i2c_clk;
- end
-
- //cnt_slave
- reg[2:0]cnt_slave;
- always @(posedge i2c_clk or negedge rst_n) begin
- if(rst_n==1'b0)
- cnt_slave<=3'd0;
- else if(state==SLAVE)
- cnt_slave<=cnt_slave+1'b1;
- else if(cnt_slave==3'd6)
- cnt_slave<=3'd0;
- else
- cnt_slave<=cnt_slave;
- end
- //cnt_reg1;
- reg[2:0]cnt_reg1;
- always @(posedge i2c_clk or negedge rst_n) begin
- if(rst_n==1'b0)
- cnt_reg1<=3'd0;
- else if(state==REG1)
- cnt_reg1<=cnt_reg1+1'b1;
- else if(cnt_reg1==3'd7)
- cnt_reg1<=3'd0;
- else
- cnt_reg1<=cnt_reg1;
- end
-
-
-
- //cnt_data
- reg[2:0]cnt_data;
- always @(posedge i2c_clk or negedge rst_n) begin
- if(rst_n==1'b0)
- cnt_data<=3'd0;
- else if(state==DATA)
- cnt_data<=cnt_data+1'b1;
- else if(cnt_data==3'd7)
- cnt_data<=3'd0;
- else
- cnt_data<=cnt_data;
- end
- /*//cnt_reg0
- reg[3:0]cnt_reg;
- always @(posedge i2c_clk or negedge rst_n) begin
- if(rst_n==1'b0)'
- cnt_reg<=34'd0;
- else if(state==REG1||state==REG2)
- cnt_reg<=cnt_reg+1'b1;
- else if(cnt_reg==4'd15)
- cnt_reg<=4'd0;
- else
- cnt_reg<=cnt_reg;
- end//*/
- //cnt_reg2;
- reg[2:0]cnt_reg2;
- always @(posedge i2c_clk or negedge rst_n) begin
- if(rst_n==1'b0)
- cnt_reg2<=3'd0;
- else if(state==REG2)
- cnt_reg2<=cnt_reg2+1'b1;
- else if(cnt_reg2==3'd7)
- cnt_reg2<=3'd0;
- else
- cnt_reg2<=cnt_reg2;
- end
-
- //state
-
- always @(posedge i2c_clk or negedge rst_n)
- if(rst_n==1'b0) begin
- state <=IDLE;
- end
- else begin
- case(state)
- IDLE:begin
- if(i2c_en==1'b1) begin
- state <=START;
- end
- else begin
- state <=IDLE;
- end
- end
-
- START:begin
- if(start==1'b1)
- state <=SLAVE;
- else
- state <=START;
- end
- SLAVE:begin
- if(cnt_slave==3'd6)
- state <=WR;
- else
- state <=SLAVE;
- end
- WR:begin
- state <=ACK1;
- end
-
- ACK1:begin
- if(slave_ack==1'b0)
- state<=REG1;
- else
- state<=ACK1;
- end
- REG1:begin
- if(cnt_reg1==3'd7) begin
- state<=ACK2;
- end
- else begin
- state<=REG1;
- end
- end
-
- ACK2:begin
-
- if (slave_ack==1'b0)
- state<=REG2;
- else
- state<=ACK2;
- end
- REG2:begin
- if(cnt_reg2==3'd7) begin
- state<=ACK3;
- end
- else begin
- state<=REG2;
- end
- end
-
- ACK3:begin
- if(slave_ack==1'b0)
- state <=DATA;
- else
- state <=ACK3;
- end
- DATA:begin
- if(cnt_data==3'd7) begin
- state<=ACK4;
- end
- else begin
- state<=DATA;
- end
- end
-
- ACK4: begin
- if(slave_ack==1'b0)
- state <=STOP;
- else
- state<=ACK4;
- end
- STOP:begin
- state<=IDLE;
- end
- default :begin
- state<=IDLE;
- end
- endcase
- end
- reg i2c_sda_out;
- wire out_en;
- assign out_en=((state==ACK1)||(state==ACK2)||(state==ACK3)||(state==ACK4)||(state==DATA&&wr==1'b0))?0:1;
-
- assign i2c_sda = (out_en)?i2c_sda_out:1'bz;
- //reg_data;
- reg[7:0]reg_data;
- always @(posedge sysclk or negedge rst_n) begin
- if(rst_n==1'b0)
- reg_data<=8'd0;
- else if(state==START&&wr==1'b1)
- reg_data<=write_data;
- else
- reg_data<=reg_data;
- end
-
-
-
- //i2c_sda_out
- always @(posedge sysclk or negedge rst_n) begin
- if(rst_n==1'b0)
- i2c_sda_out<=1'b1 ;
- else if(state==IDLE)
- i2c_sda_out<=1'b1 ;
- else if(state==START)
- i2c_sda_out<=1'b0 ;
- else if(state==SLAVE)
- i2c_sda_out<=slave_addr[6-cnt_slave] ;
- else if(state==WR&&wr==1'b1)
- i2c_sda_out<=wr ;
- else if(state==ACK1)
- i2c_sda_out<=slave_ack ;
- else if(state==REG1)
- i2c_sda_out<=register_addr[15-cnt_reg1];
- else if(state==ACK2)
- i2c_sda_out<=slave_ack ;
- else if(state==REG2)
- i2c_sda_out<=register_addr[7-cnt_reg2] ;
- else if(state==ACK3)
- i2c_sda_out<=slave_ack ;
- else if(state==DATA&&wr==1'b1)
- i2c_sda_out<=reg_data[7-cnt_data] ;
- else if(state==ACK4)
- i2c_sda_out<=slave_ack ;
- else if(state==STOP)
- i2c_sda_out<=1'b1 ;
- else
- i2c_sda_out<=i2c_sda_out ;
- end
- reg [7:0] shift_reg;
- always @(posedge sysclk or negedge rst_n) begin
- if(rst_n==1'b0)
- shift_reg<=8'd0;
- else if(state==DATA&&wr==1'b0)
- shift_reg<={i2c_sda,shift_reg[7:1]} ;
- else
- shift_reg<=shift_reg;
- end
-
-
-
- reg [2:0] read_data_cnt;
-
- always @(posedge sysclk or negedge rst_n) begin
- if(rst_n==1'b0)
- read_data_cnt<=3'd0;
- else if(state==DATA&&wr==1'b0)
- read_data_cnt<=read_data_cnt+1'b1;
- else if(read_data_cnt==3'd7)
- read_data_cnt<=3'd0;
- else
- read_data_cnt<=read_data_cnt;
- end
-
- always @(posedge sysclk or negedge rst_n) begin
- if(rst_n==1'b0)
- read_data<=8'd0;
- else if(state==DATA&&wr==1'b0&&read_data_cnt==3'd7)
- read_data<=shift_reg;
- else
- read_data<=reg_data;
- end
-
- endmodule
tb:
- module ii_tb();
- reg sysclk ;
- reg rst_n ;
- reg i2c_en ;
- reg [6:0] slave_addr ;
- reg [15:0]register_addr;
- reg [7:0] write_data ;
- reg start ;
- reg slave_ack ;
- reg wr ;
- wire[7:0] read_data ;
- wire i2c_clk ;
- wire i2c_sda ;
-
- iic iic_u(
- .sysclk (sysclk ),
- .rst_n (rst_n ),
- .i2c_en (i2c_en ),
- .slave_addr (slave_addr ),
- .register_addr(register_addr),
- .write_data (write_data ),
- .start (start ),
- .slave_ack (slave_ack ),
- .wr (wr ),
- .read_data (read_data ),
- .i2c_clk (i2c_clk ),
- .i2c_sda (i2c_sda )
- );
- initial begin
- sysclk =1'b0;
- rst_n =1'b0;
- i2c_en =1'b0;
- slave_addr =7'd0;
- register_addr=16'd0;
- write_data =8'd0;
- start =1'b0;
- slave_ack =1'b1;
- wr =1'b0;
- #10;
- rst_n =1'b1;
- #20;
- slave_addr =7'd25;
- i2c_en =1'b1;
- register_addr=16'd59;
- write_data =8'd32;
- start =1'b1;
- slave_ack =1'b0;
- wr =1'b1;
- end
- always #20 sysclk=!sysclk;
- endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。