1 IIC传输协议及其代码的实现-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

IIC传输协议及其代码的实现

HJ18656750788 来源:Cascatrix 2023-01-08 09:16 次阅读

01

IIC基础知识

集成电路总线(Inter-Intergrated Circuit),通常称作IICBUS,简称为IIC,是一种采用多主从结构的串行通信总线。IIC由PHILIPS公司于1980年推出,利用该总线可实现多主机系统所需的裁决和高低速设备同步等功能。

IIC串行总线一般有两根信号线,分别是时钟线SCL和数据线SDA,所有IIC总线各设备的串行数据SDA接到总线SDA上,时钟线SLC接到总线SCL上。

双向串行数据SDA:输出电路用于向总线发送数据,输入电路用于接收总线上的数据;

双向时钟数据SCL:输出电路用于向总线发送时钟信号,输入电路用于检测总线时钟电平以决定下次时钟脉冲电平时间。

各设备与总线相连的输出端采用高阻态以避免总线信号的混乱,通常采用漏极开路输出或集电极开路输出。总线空闲时,各设备都是开漏输出,通常可采用上拉电阻使总线保持高电平,而任一设备输出低电平都将拉低相应的总线信号。

57191204-8e9c-11ed-bfe3-dac502259ad0.png

IIC总线上的设备可分为主设备和从设备两种:主设备有权利主动发起/结束一次通信;从设备只能被动响应。

所有连接在IIC总线上的设备都有各自唯一的地址(原地址位宽为7bits,改进后采用10bits位宽),每个设备都可以作为主设备或从设备,但是同一时刻只能有一个主设备控制。

如果总线上有多个设备同时启用总线,IIC通过检测和仲裁机制解决传输冲突。IIC允许连接的设备传输速率不同,多台设备之间时钟信号的同步过程称为同步化。

02

IIC传输协议

2.1 IIC协议模式

IIC协议为半双工模式,同一条数据线上完成读/写操作,不同操作时的数据帧格式可分为写操作、读操作、读写操作。

2.1.1 写操作数据帧格式

主机向从机写数据的数据帧格式如下:

573bf314-8e9c-11ed-bfe3-dac502259ad0.png

白色为主机发送数据,灰色为从机发送数据。

1. 主机发送开始信号S;

2. 主机发送地址数据ADDR;

3. 主机发送写信号0;

4. 从机响应主机写信号ACK;

5. 主机发送数据信息DATA;

6. 从机响应主机发送数据ACK;

7. 循环5 6步骤可完成主机向从机连续写数据过程;

8. 主机发送结束信号P.

2.1.2 读操作数据帧格式

主机从从机读数据的数据帧格式如下:

5757227e-8e9c-11ed-bfe3-dac502259ad0.png

白色为主机发送数据,灰色为从机发送数据。

1. 主机发送开始信号S;

2. 主机发送地址数据ADDR;

3. 主机发送读信号1;

4. 从机响应主机读信号ACK;

5. 从机发送数据信息DATA;

6. 主机响应从机发送数据ACK;

7. 循环5 6步骤可完成主机向从机连续读数据过程;

8. 主机发送结束信号P.

2.1.3 读写同时操作数据帧格式

主机先向从机写数据后从从机读数据的数据帧格式如下:

577905c4-8e9c-11ed-bfe3-dac502259ad0.png

白色为主机发送数据,灰色为从机发送数据。

主机可以在完成写操作后不发送结束信号P,直接进行读操作。该过程主机不可变,而从机可以通过发送不同地址选择不同的从机。

2.2 IIC写时序

IIC协议写时序可分为单字节写时序和连续写时序:

2.2.1 单字节写时序

单字节地址写时序过程:

579d0316-8e9c-11ed-bfe3-dac502259ad0.png

1. 主机发送开始信号S;

2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;

3. 从机响应主机控制字节ACK;

4. 主机发送单字节寄存器地址信息WORD ADDRESS;

5. 从机响应主机发送寄存器地址ACK;

6. 主机发送单字节数据信息DATA;

7. 从机响应主机发送数据信息ACK;

8. 主机发送结束信号P.

双字节地址写时序过程:

57d0eb86-8e9c-11ed-bfe3-dac502259ad0.png

1. 主机发送开始信号S;

2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;

3. 从机响应主机控制字节ACK;

4. 主机发送寄存器地址信息高字节ADDRESS HIGH BYTE;

5. 从机响应主机发送寄存器地址高字节ACK;

6. 主机发送寄存器地址信息低字节ADDRESS LOW BYTE;

7. 从机响应主机发送寄存器地址低字节ACK;

8. 主机发送单字节数据信息DATA;

9. 从机响应主机发送数据信息ACK;

10. 主机发送结束信号P.

2.2.2 连续写时序

单字节地址写时序过程:

57f8c480-8e9c-11ed-bfe3-dac502259ad0.png

1. 主机发送开始信号S;

2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;

3. 从机响应主机控制字节ACK;

4. 主机发送单字节寄存器地址信息WORD ADDRESS;

5. 从机响应主机发送寄存器地址ACK;

6. 主机发送单字节数据信息DATA;

7. 从机响应主机发送数据信息ACK;

8. 循环6 7步骤可以连续向从机写数据DATA(D+n);

9. 主机发送结束信号P.

双字节地址写时序过程:

581c8b5e-8e9c-11ed-bfe3-dac502259ad0.png

1. 主机发送开始信号S;

2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;

3. 从机响应主机控制字节ACK;

4. 主机发送寄存器地址信息高字节ADDRESS HIGH BYTE;

5. 从机响应主机发送寄存器地址高字节ACK;

6. 主机发送寄存器地址信息低字节ADDRESS LOW BYTE;

7. 从机响应主机发送寄存器地址低字节ACK;

8. 主机发送单字节数据信息DATA;

9. 从机响应主机发送数据信息ACK;

10. 循环8 9步骤可以连续向从机写数据DATA(D+n);

11. 主机发送结束信号P.

2.3 IIC读时序

IIC协议读时序可分为单字节读时序和连续读时序:

2.3.1 单字节读时序

单字节地址读时序过程:

5838e736-8e9c-11ed-bfe3-dac502259ad0.png

1. 主机发送开始信号S;

2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;

3. 从机响应主机控制字节ACK;

4. 主机发送单字节寄存器地址信息WORD ADDRESS;

5. 从机响应主机发送寄存器地址ACK;

6. 主机发送开始信号S;

7. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为1表示主机从从机读数据;

8. 从机响应主机控制字节ACK;

9. 从机发送单字节数据信息DATA;

10. 主机响应从机发送数据信息NACK(非应答位: 接收器是主机时,在接收到最后一个字节后发送NACK已通知被控发送从机结束数据发送,并释放SDA数据线以便主机发送停止信号P);

11. 主机发送结束信号P.

双字节地址读时序过程:

586baa7c-8e9c-11ed-bfe3-dac502259ad0.png

1. 主机发送开始信号S;

2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;

3. 从机响应主机控制字节ACK;

4. 主机发送寄存器地址信息高字节ADDRESS HIGH BYTE;

5. 从机响应主机发送寄存器地址高字节ACK;

6. 主机发送寄存器地址信息低字节ADDRESS LOW BYTE;

7. 从机响应主机发送寄存器地址低字节ACK;

8. 主机发送开始信号S;

9. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为1表示主机从从机读数据;

10. 从机响应主机控制字节ACK;

11. 从机发送单字节数据信息DATA;

12. 主机响应从机发送数据信息NACK(非应答位: 接收器是主机时,在接收到最后一个字节后发送NACK已通知被控发送从机结束数据发送,并释放SDA数据线以便主机发送停止信号P);

13. 主机发送结束信号P.

2.3.2 连续读时序

单字节地址读时序过程:

588c590c-8e9c-11ed-bfe3-dac502259ad0.png

1. 主机发送开始信号S;

2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;

3. 从机响应主机控制字节ACK;

4. 主机发送单字节寄存器地址信息WORD ADDRESS;

5. 从机响应主机发送寄存器地址ACK;

6. 主机发送开始信号S;

7. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为1表示主机从从机读数据;

8. 从机响应主机控制字节ACK;

9. 从机发送单字节数据信息DATA;

10. 主机响应从机发送数据信息ACK;

11. 循环9 10步骤可完成主机向从机连续读数据过程,读取最后一个字节数据时主机应响应NACK(非应答位: 接收器是主机时,在接收到最后一个字节后发送NACK已通知被控发送从机结束数据发送,并释放SDA数据线以便主机发送停止信号P);

12. 主机发送结束信号P.

双字节地址读时序过程:

58aee86e-8e9c-11ed-bfe3-dac502259ad0.png

1. 主机发送开始信号S;

2. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为0表示主机向从机写数据;

3. 从机响应主机控制字节ACK;

4. 主机发送寄存器地址信息高字节ADDRESS HIGH BYTE;

5. 从机响应主机发送寄存器地址高字节ACK;

6. 主机发送寄存器地址信息低字节ADDRESS LOW BYTE;

7. 从机响应主机发送寄存器地址低字节ACK;

8. 主机发送开始信号S;

9. 主机发送控制字节CONTROL BYTE(7bits设备地址dev addr和1bit读写信号),最低位为1表示主机从从机读数据;

10. 从机响应主机控制字节ACK;

11. 从机发送单字节数据信息DATA;

12.主机响应从机发送数据信息ACK;

13. 循环11 12步骤可完成主机向从机连续读数据过程,读取最后一个字节数据时主机应响应NACK(非应答位: 接收器是主机时,在接收到最后一个字节后发送NACK已通知被控发送从机结束数据发送,并释放SDA数据线以便主机发送停止信号P);

14. 主机发送结束信号P.

03

IIC代码实现

3.1 IIC目标实现功能

设计一个IIC模块,具体要求如下:

设计一个IIC协议提供给主设备,通过查找表实现对从设备寄存器进行配置。按查找表顺序lut_index依次对设备地址为lut_dev_addr的从设备寄存器进行配置,为lut_reg_addr配置寄存器数据lut_reg_data,同时将传输异常和传输结束信号引出以对传输过程进行监控。模块的定义如下:

module i2c(

input rst,//复位信号

input clk, //时钟信号

input[15:0] clk_div_cnt, //时钟计数器

input i2c_addr_2byte, //双字节地址

output reg[9:0] lut_index, //查找表顺序号

input[7:0] lut_dev_addr, //从设备地址

input[15:0] lut_reg_addr, //寄存器地址

input[7:0] lut_reg_data, //寄存器数据

output reg error, //传输异常信号

output done, //传输结束信号

inout i2c_scl, //IIC时钟信号

inout i2c_sda //IIC数据信号

);

3.2 Verilog代码

1. 顶层模块 (i2c):

module i2c(

input rst,

input clk,

input[15:0] clk_div_cnt,

input i2c_addr_2byte,

output reg[9:0] lut_index,

input[7:0] lut_dev_addr,

input[15:0] lut_reg_addr,

input[7:0] lut_reg_data,

output reg error,

output done,

inout i2c_scl,

inout i2c_sda

);

wire scl_pad_i;

wire scl_pad_o;

wire scl_padoen_o;

wire sda_pad_i;

wire sda_pad_o;

wire sda_padoen_o;

assign sda_pad_i = i2c_sda;

assign i2c_sda = ~sda_padoen_o ? sda_pad_o : 1'bz;

assign scl_pad_i = i2c_scl;

assign i2c_scl = ~scl_padoen_o ? scl_pad_o : 1'bz;

reg i2c_read_req;

wire i2c_read_req_ack;

reg i2c_write_req;

wire i2c_write_req_ack;

wire[7:0] i2c_slave_dev_addr;

wire[15:0] i2c_slave_reg_addr;

wire[7:0] i2c_write_data;

wire[7:0] i2c_read_data;

wire err;

reg[2:0] state;

localparam S_IDLE = 0;

localparam S_WR_I2C_CHECK = 1;

localparam S_WR_I2C = 2;

localparam S_WR_I2C_DONE = 3;

assign done = (state == S_WR_I2C_DONE);

assign i2c_slave_dev_addr = lut_dev_addr;

assign i2c_slave_reg_addr = lut_reg_addr;

assign i2c_write_data = lut_reg_data;

//cascatrix carson

always@(posedge clk or posedge rst)

begin

if(rst)

begin

state <= S_IDLE;

error <= 1'b0;

lut_index <= 8'd0;

end

else

case(state)

S_IDLE:

begin

state <= S_WR_I2C_CHECK;

error <= 1'b0;

lut_index <= 8'd0;

end

S_WR_I2C_CHECK:

begin

if(i2c_slave_dev_addr != 8'hff)

begin

i2c_write_req <= 1'b1;

state <= S_WR_I2C;

end

else

begin

state <= S_WR_I2C_DONE;

end

end

S_WR_I2C:

begin

if(i2c_write_req_ack)

begin

error <= err ? 1'b1 : error; 

lut_index <= lut_index + 8'd1;

i2c_write_req <= 1'b0;

state <= S_WR_I2C_CHECK;

end

end

S_WR_I2C_DONE:

begin

state <= S_WR_I2C_DONE;

end

default:

state <= S_IDLE;

endcase

end

i2c_ctrl i2c_ctrl

(

.rst(rst),

.clk(clk),

.clk_div_cnt(clk_div_cnt),

// I2C signals

// i2c clock line

.scl_pad_i(scl_pad_i), // SCL-line input

.scl_pad_o(scl_pad_o), // SCL-line output (always 1'b0)

.scl_padoen_o(scl_padoen_o), // SCL-line output enable (active low)

// i2c data line

.sda_pad_i(sda_pad_i), // SDA-line input

.sda_pad_o(sda_pad_o), // SDA-line output (always 1'b0)

.sda_padoen_o(sda_padoen_o), // SDA-line output enable (active low)

.i2c_read_req(i2c_read_req),

.i2c_addr_2byte(i2c_addr_2byte),

.i2c_read_req_ack(i2c_read_req_ack),

.i2c_write_req(i2c_write_req),

.i2c_write_req_ack(i2c_write_req_ack),

.i2c_slave_dev_addr(i2c_slave_dev_addr),

.i2c_slave_reg_addr(i2c_slave_reg_addr),

.i2c_write_data(i2c_write_data),

.i2c_read_data(i2c_read_data),

.error(err)

);

endmodule

2. 组帧模块 (i2c_ctrl):

module i2c_ctrl

(

input rst,

input clk,

input[15:0] clk_div_cnt,

// I2C signals

// i2c clock line

input scl_pad_i, //SCL-line input

output scl_pad_o, //SCL-line output (always 1'b0)

output scl_padoen_o, //SCL-line output enable (active low)

// i2c data line

input sda_pad_i, //SDA-line input

output sda_pad_o, //SDA-line output (always 1'b0)

output sda_padoen_o, //SDA-line output enable (active low)

input i2c_addr_2byte, //register address 16bit or 8bit

input i2c_read_req, //Read register request

output i2c_read_req_ack, //Read register request response

input i2c_write_req, //Write register request

output i2c_write_req_ack, //Write register request response

input[7:0] i2c_slave_dev_addr, //I2c device address

input[15:0] i2c_slave_reg_addr, //I2c register address

input[7:0] i2c_write_data, //I2c write register data

output reg[7:0] i2c_read_data,//I2c read register data

output reg error //The error indication, generally there is no response

);

//State machine definition cascatrix carson

localparam S_IDLE= 0;

localparam S_WR_DEV_ADDR=1;

localparam S_WR_REG_ADDR=2;

localparam S_WR_DATA=3;

localparam S_WR_ACK=4;

localparam S_WR_ERR_NACK=5;

localparam S_RD_DEV_ADDR0=6;

localparam S_RD_REG_ADDR=7;

localparam S_RD_DEV_ADDR1=8;

localparam S_RD_DATA=9;

localparam S_RD_STOP=10;

localparam S_WR_STOP=11;

localparam S_WAIT=12;

localparam S_WR_REG_ADDR1=13;

localparam S_RD_REG_ADDR1=14;

localparam S_RD_ACK=15;

reg start;

reg stop;

reg read;

reg write;

reg ack_in;

reg[7:0] txr;

wire[7:0] rxr;

wire i2c_busy;

wire i2c_al;

wire done;

wire irxack;

reg[3:0] state, next_state;

assign i2c_read_req_ack = (state == S_RD_ACK);

assign i2c_write_req_ack = (state == S_WR_ACK);

always@(posedge clk or posedge rst)

begin

if(rst)

state <= S_IDLE;

else

state <= next_state;    

end

always@(*)

begin

case(state)

S_IDLE:

//Waiting for read and write requests

if(i2c_write_req)

next_state <= S_WR_DEV_ADDR;

else if(i2c_read_req)

next_state <= S_RD_DEV_ADDR0;

else

next_state <= S_IDLE;

//Write I2C device address

S_WR_DEV_ADDR:

if(done && irxack)

next_state <= S_WR_ERR_NACK;

else if(done)

next_state <= S_WR_REG_ADDR;

else

next_state <= S_WR_DEV_ADDR;

//Write the address of the I2C register

S_WR_REG_ADDR:

if(done)

//If it is the 8bit register address, it enters the write data state

next_state<=i2c_addr_2byte? S_WR_REG_AD DR1  : S_WR_DATA;

else

next_state <= S_WR_REG_ADDR;

S_WR_REG_ADDR1:

if(done)

next_state <= S_WR_DATA;

else

next_state <= S_WR_REG_ADDR1; 

//Write data

S_WR_DATA:

if(done)

next_state <= S_WR_STOP;

else

next_state <= S_WR_DATA;

S_WR_ERR_NACK:

next_state <= S_WR_STOP;

S_RD_ACK,S_WR_ACK:

next_state <= S_WAIT;

S_WAIT:

next_state <= S_IDLE;

S_RD_DEV_ADDR0:

if(done && irxack)

next_state <= S_WR_ERR_NACK;

else if(done)

next_state <= S_RD_REG_ADDR;

else

next_state <= S_RD_DEV_ADDR0;

S_RD_REG_ADDR:

if(done)

next_state<=i2c_addr_2byte?S_RD_REG_ADDR1 : S_RD_DEV_ADDR1;

else

next_state <= S_RD_REG_ADDR;

S_RD_REG_ADDR1:

if(done)

next_state <= S_RD_DEV_ADDR1;

else

next_state <= S_RD_REG_ADDR1;               

S_RD_DEV_ADDR1:

if(done)

next_state <= S_RD_DATA;

else

next_state <= S_RD_DEV_ADDR1;   

S_RD_DATA:

if(done)

next_state <= S_RD_STOP;

else

next_state <= S_RD_DATA;

S_RD_STOP:

if(done)

next_state <= S_RD_ACK;

else

next_state <= S_RD_STOP;

S_WR_STOP:

if(done)

next_state <= S_WR_ACK;

else

next_state <= S_WR_STOP;                

default:

next_state <= S_IDLE;

endcase

end

always@(posedge clk or posedge rst)

begin

if(rst)

error <= 1'b0;

else if(state == S_IDLE)

error <= 1'b0;

else if(state == S_WR_ERR_NACK)

error <= 1'b1;

end

always@(posedge clk or posedge rst)

begin

if(rst)

start <= 1'b0;

else if(done)

start <= 1'b0;

else if(state == S_WR_DEV_ADDR || state == S_RD_DEV_ADDR0 || state == S_RD_DEV_ADDR1)

start <= 1'b1;

end

always@(posedge clk or posedge rst)

begin

if(rst)

stop <= 1'b0;

else if(done)

stop <= 1'b0;

else if(state == S_WR_STOP || state == S_RD_STOP)

stop <= 1'b1;

end

always@(posedge clk or posedge rst)

begin

if(rst)

ack_in <= 1'b0;

else

ack_in <= 1'b1;

end

always@(posedge clk or posedge rst)

begin

if(rst)

write <= 1'b0;

else if(done)

write <= 1'b0;

else if(state == S_WR_DEV_ADDR || state == S_WR_REG_ADDR || state == S_WR_REG_ADDR1|| state == S_WR_DATA || state == S_RD_DEV_ADDR0 || state == S_RD_DEV_ADDR1 || state == S_RD_REG_ADDR || state == S_RD_REG_ADDR1)

write <= 1'b1;

end

always@(posedge clk or posedge rst)

begin

if(rst)

read <= 1'b0;

else if(done)

read <= 1'b0;

else if(state == S_RD_DATA)

read <= 1'b1;

end

always@(posedge clk or posedge rst)

begin

if(rst)

i2c_read_data <= 8'h00;

else if(state == S_RD_DATA && done)

i2c_read_data <= rxr;

end

always@(posedge clk or posedge rst)

begin

if(rst)

txr <= 8'd0;

else

case(state)

S_WR_DEV_ADDR,S_RD_DEV_ADDR0:

txr <= {i2c_slave_dev_addr[7:1],1'b0};

S_RD_DEV_ADDR1:

txr <= {i2c_slave_dev_addr[7:1],1'b1};

S_WR_REG_ADDR,S_RD_REG_ADDR:

txr<=(i2c_addr_2byte==1'b1)?i2c_slave_reg_addr[15 :8] : i2c_slave_reg_addr[7:0];

S_WR_REG_ADDR1,S_RD_REG_ADDR1:

txr <= i2c_slave_reg_addr[7:0];             

S_WR_DATA:

txr <= i2c_write_data;

default:

txr <= 8'hff;

endcase

end

i2c_byte_ctrl byte_controller (

.clk ( clk ),

.rst ( rst ),

.nReset ( 1'b1 ),

.ena ( 1'b1 ),

.clk_cnt ( clk_div_cnt ),

.start ( start ),

.stop ( stop ),

.read ( read ),

.write ( write ),

.ack_in ( ack_in ),

.din ( txr ),

.cmd_ack ( done ),

.ack_out ( irxack ),

.dout ( rxr ),

.i2c_busy ( i2c_busy ),

.i2c_al ( i2c_al ),

.scl_i ( scl_pad_i ),

.scl_o ( scl_pad_o ),

.scl_oen ( scl_padoen_o ),

.sda_i ( sda_pad_i ),

.sda_o ( sda_pad_o ),

.sda_oen ( sda_padoen_o )

);

endmodule

3. 字节控制模块(i2c_byte_ctrl):

`define I2C_CMD_NOP 4'b0000

`define I2C_CMD_START 4'b0001

`define I2C_CMD_STOP 4'b0010

`define I2C_CMD_WRITE 4'b0100

`define I2C_CMD_READ 4'b1000

module i2c_byte_ctrl (

input clk, // master clock

input rst, // synchronous active high reset

input nReset, // asynchronous active low reset

input ena, // core enable signal

input [15:0] clk_cnt, // 4x SCL

// control inputs

input start,

input stop,

input read,

input write,

input ack_in,

input [7:0] din,

// status outputs

output reg cmd_ack,

output regack_out,

output i2c_busy,

output i2c_al,

output [7:0] dout,

// I2C signals

input scl_i,

output scl_o,

output scl_oen,

input sda_i,

output sda_o,

output sda_oen

);

//

// Variable declarations cascatrix carson

//

// statemachine

parameter [4:0] ST_IDLE = 5'b0_0000;

parameter [4:0] ST_START = 5'b0_0001;

parameter [4:0] ST_READ = 5'b0_0010;

parameter [4:0] ST_WRITE = 5'b0_0100;

parameter [4:0] ST_ACK = 5'b0_1000;

parameter [4:0] ST_STOP = 5'b1_0000;

// signals for bit_controller

reg [3:0] core_cmd;

reg core_txd;

wire core_ack, core_rxd;

// signals for shift register

reg [7:0] sr; //8bit shift register

reg shift, ld;

// signals for state machine

wire go;

reg [2:0] dcnt;

wire cnt_done;

// bit_controller

i2c_bit_ctrl bit_controller (

.clk ( clk ),

.rst ( rst ),

.nReset ( nReset ),

.ena ( ena ),

.clk_cnt ( clk_cnt ),

.cmd ( core_cmd ),

.cmd_ack ( core_ack ),

.busy ( i2c_busy ),

.al ( i2c_al ),

.din ( core_txd ),

.dout ( core_rxd ),

.scl_i ( scl_i ),

.scl_o ( scl_o ),

.scl_oen ( scl_oen ),

.sda_i ( sda_i ),

.sda_o ( sda_o ),

.sda_oen ( sda_oen )

);

// generate go-signal

assign go = (read | write | stop) & ~cmd_ack;

// assign dout output to shift-register

assign dout = sr;

// generate shift register

always @(posedge clk or negedge nReset)

if (!nReset)

sr <= #1 8'h0;

else if (rst)

sr <= #1 8'h0;

else if (ld)

sr <= #1 din;

else if (shift)

sr <= #1 {sr[6:0], core_rxd};

// generate counter

always @(posedge clk or negedge nReset)

if (!nReset)

dcnt <= #1 3'h0;

else if (rst)

dcnt <= #1 3'h0;

else if (ld)

dcnt <= #1 3'h7;

else if (shift)

dcnt <= #1 dcnt - 3'h1;

assign cnt_done = ~(|dcnt);

//

// state machine

//

reg [4:0] c_state; // synopsys enum_state

always @(posedge clk or negedge nReset)

if (!nReset)

begin

core_cmd <= #1 `I2C_CMD_NOP;

core_txd <= #1 1'b0;

shift <= #1 1'b0;

ld <= #1 1'b0;

cmd_ack <= #1 1'b0;

c_state <= #1 ST_IDLE;

ack_out <= #1 1'b0;

end

else if (rst | i2c_al)

begin

core_cmd <= #1 `I2C_CMD_NOP;

core_txd <= #1 1'b0;

shift <= #1 1'b0;

ld <= #1 1'b0;

cmd_ack <= #1 1'b0;

c_state <= #1 ST_IDLE;

ack_out <= #1 1'b0;

end

else

begin

// initially reset all signals

core_txd <= #1 sr[7];

shift <= #1 1'b0;

ld <= #1 1'b0;

cmd_ack <= #1 1'b0;

case (c_state) // synopsys full_case parallel_case

ST_IDLE:

if (go)

begin

if (start)

begin

c_state <= #1 ST_START;

core_cmd <= #1 `I2C_CMD_START;

end

else if (read)

begin

c_state <= #1 ST_READ;

core_cmd <= #1 `I2C_CMD_READ;

end

else if (write)

begin

c_state <= #1 ST_WRITE;

core_cmd <= #1 `I2C_CMD_WRITE;

end

else // stop

begin

c_state <= #1 ST_STOP;

core_cmd <= #1 `I2C_CMD_STOP;

end

ld <= #1 1'b1;

end

ST_START:

if (core_ack)

begin

if (read)

begin

c_state <= #1 ST_READ;

core_cmd <= #1 `I2C_CMD_READ;

end

else

begin

c_state <= #1 ST_WRITE;

core_cmd <= #1 `I2C_CMD_WRITE;

end

ld <= #1 1'b1;

end

ST_WRITE:

if (core_ack)

if (cnt_done)

begin

c_state <= #1 ST_ACK;

core_cmd <= #1 `I2C_CMD_READ;

end

else

begin

// stay in same state

c_state <= #1 ST_WRITE;       

// write next bit

core_cmd <= #1 `I2C_CMD_WRITE; 

shift <= #1 1'b1;

end

ST_READ:

if (core_ack)

begin

if (cnt_done)

begin

c_state <= #1 ST_ACK;

core_cmd <= #1 `I2C_CMD_WRITE;

end

else

begin

// stay in same state

c_state <= #1 ST_READ;       

// read next bit

core_cmd <= #1 `I2C_CMD_READ;

end

shift <= #1 1'b1;

core_txd <= #1 ack_in;

end

ST_ACK:

if (core_ack)

begin

if (stop)

begin

c_state <= #1 ST_STOP;

core_cmd <= #1 `I2C_CMD_STOP;

end

else

begin

c_state <= #1 ST_IDLE;

core_cmd <= #1 `I2C_CMD_NOP;

// generate command acknowledge signal

cmd_ack <= #1 1'b1;

end

// assign ack_out output to bit_controller_rxd (contains last received bit)

ack_out <= #1 core_rxd;

core_txd <= #1 1'b1;

end

else

core_txd <= #1 ack_in;

ST_STOP:

if (core_ack)

begin

c_state <= #1 ST_IDLE;

core_cmd <= #1 `I2C_CMD_NOP;

// generate command acknowledge signal

cmd_ack <= #1 1'b1;

end

endcase

end

endmodule

4. bit控制模块(i2c_bit_ctrl):

`define I2C_CMD_NOP 4'b0000

`define I2C_CMD_START 4'b0001

`define I2C_CMD_STOP 4'b0010

`define I2C_CMD_WRITE 4'b0100

`define I2C_CMD_READ 4'b1000

module i2c_bit_ctrl (

input clk, // system clock

input rst, // synchronous active high reset

input nReset, // asynchronous active low reset

input ena, // core enable signal

input [15:0] clk_cnt, // clock prescale value

input [ 3:0] cmd, // command (from byte controller)

output reg cmd_ack, // command complete acknowledge

output reg busy, // i2c bus busy

output reg al, // i2c bus arbitration lost

input din,

output reg dout,

input scl_i, // i2c clock line input

output scl_o, // i2c clock line output

output reg scl_oen, // i2c clock line output enable (active low)

input sda_i, // i2c data line input

output sda_o, // i2c data line output

output reg sda_oen // i2c data line output enable (active low)

);

//

// variable declarations

//

reg [ 1:0] cSCL, cSDA; // capture SCL and SDA

reg [ 2:0] fSCL, fSDA; // SCL and SDA filter inputs

reg sSCL, sSDA; // filtered and synchronized SCL and SDA inputs

reg dSCL, dSDA; // delayed versions of sSCL and sSDA

reg dscl_oen; // delayed scl_oen

reg sda_chk; // check SDA output (Multi-master arbitration)

reg clk_en; // clock generation signals

reg slave_wait; // slave inserts wait states

reg [15:0] cnt; // clock divider counter (synthesis)

reg [13:0] filter_cnt; // clock divider for filter

// state machine variable

reg [17:0] c_state; // synopsys enum_state

//

// module body

//

// whenever the slave is not ready it can delay the cycle by pulling SCL low

// delay scl_oen

always @(posedge clk)

dscl_oen <= #1 scl_oen;

// slave_wait is asserted when master wants to drive SCL high, but the slave pulls it low

// slave_wait remains asserted until the slave releases SCL

always @(posedge clk or negedge nReset)

if (!nReset) slave_wait <= 1'b0;

else slave_wait <= (scl_oen & ~dscl_oen & ~sSCL) | (slave_wait & ~sSCL);

// master drives SCL high, but another master pulls it low

// master start counting down its low cycle now (clock synchronization)

wire scl_sync = dSCL & ~sSCL & scl_oen;

// generate clk enable signal

always @(posedge clk or negedge nReset)

if (~nReset)

begin

cnt <= #1 16'h0;

clk_en <= #1 1'b1;

end

else if (rst || ~|cnt || !ena || scl_sync)

begin

cnt <= #1 clk_cnt;

clk_en <= #1 1'b1;

end

else if (slave_wait)

begin

cnt <= #1 cnt;

clk_en <= #1 1'b0;    

end

else

begin

cnt <= #1 cnt - 16'h1;

clk_en <= #1 1'b0;

end

// generate bus status controller

// capture SDA and SCL

// reduce metastability risk

always @(posedge clk or negedge nReset)

if (!nReset)

begin

cSCL <= #1 2'b00;

cSDA <= #1 2'b00;

end

else if (rst)

begin

cSCL <= #1 2'b00;

cSDA <= #1 2'b00;

end

else

begin

cSCL <= {cSCL[0],scl_i};

cSDA <= {cSDA[0],sda_i};

end

// filter SCL and SDA signals; (attempt to) remove glitches

always @(posedge clk or negedge nReset)

if (!nReset ) filter_cnt <= 14'h0;

else if (rst || !ena ) filter_cnt <= 14'h0;

else if (~|filter_cnt) filter_cnt <= clk_cnt >> 2; //16x I2C bus frequency

else filter_cnt <= filter_cnt -1;

always @(posedge clk or negedge nReset)

if (!nReset)

begin

fSCL <= 3'b111;

fSDA <= 3'b111;

end

else if (rst)

begin

fSCL <= 3'b111;

fSDA <= 3'b111;

end

else if (~|filter_cnt)

begin

fSCL <= {fSCL[1:0],cSCL[1]};

fSDA <= {fSDA[1:0],cSDA[1]};

end

// generate filtered SCL and SDA signals

always @(posedge clk or negedge nReset)

if (~nReset)

begin

sSCL <= #1 1'b1;

sSDA <= #1 1'b1;

dSCL <= #1 1'b1;

dSDA <= #1 1'b1;

end

else if (rst)

begin

sSCL <= #1 1'b1;

sSDA <= #1 1'b1;

dSCL <= #1 1'b1;

dSDA <= #1 1'b1;

end

else

begin

sSCL <= #1 &fSCL[2:1] | &fSCL[1:0] | (fSCL[2] & fSCL[0]);

sSDA <= #1 &fSDA[2:1] | &fSDA[1:0] | (fSDA[2] & fSDA[0]);

dSCL <= #1 sSCL;

dSDA <= #1 sSDA;

end

// detect start condition => detect falling edge on SDA while SCL is high

// detect stop condition => detect rising edge on SDA while SCL is high

reg sta_condition;

reg sto_condition;

always @(posedge clk or negedge nReset)

if (~nReset)

begin

sta_condition <= #1 1'b0;

sto_condition <= #1 1'b0;

end

else if (rst)

begin

sta_condition <= #1 1'b0;

sto_condition <= #1 1'b0;

end

else

begin

sta_condition <= #1 ~sSDA &  dSDA & sSCL;

sto_condition <= #1  sSDA & ~dSDA & sSCL;

end

// generate i2c bus busy signal

always @(posedge clk or negedge nReset)

if (!nReset) busy <= #1 1'b0;

else if (rst ) busy <= #1 1'b0;

else busy <= #1 (sta_condition | busy) & ~sto_condition;

// generate arbitration lost signal cascatrix carson

// aribitration lost when:

// 1) master drives SDA high, but the i2c bus is low

// 2) stop detected while not requested

reg cmd_stop;

always @(posedge clk or negedge nReset)

if (~nReset)

cmd_stop <= #1 1'b0;

else if (rst)

cmd_stop <= #1 1'b0;

else if (clk_en)

cmd_stop <= #1 cmd == `I2C_CMD_STOP;

always @(posedge clk or negedge nReset)

if (~nReset)

al <= #1 1'b0;

else if (rst)

al <= #1 1'b0;

else

al <= #1 (sda_chk & ~sSDA & sda_oen) | (|c_state & sto_condition & ~cmd_stop);

// generate dout signal (store SDA on rising edge of SCL) cascatrix carson

always @(posedge clk)

if (sSCL & ~dSCL) dout <= #1 sSDA;

// generate statemachine cascatrix carson

// nxt_state decoder

parameter [17:0] idle = 18'b0_0000_0000_0000_0000;

parameter [17:0] start_a = 18'b0_0000_0000_0000_0001;

parameter [17:0] start_b = 18'b0_0000_0000_0000_0010;

parameter [17:0] start_c = 18'b0_0000_0000_0000_0100;

parameter [17:0] start_d = 18'b0_0000_0000_0000_1000;

parameter [17:0] start_e = 18'b0_0000_0000_0001_0000;

parameter [17:0] stop_a = 18'b0_0000_0000_0010_0000;

parameter [17:0] stop_b = 18'b0_0000_0000_0100_0000;

parameter [17:0] stop_c = 18'b0_0000_0000_1000_0000;

parameter [17:0] stop_d = 18'b0_0000_0001_0000_0000;

parameter [17:0] rd_a = 18'b0_0000_0010_0000_0000;

parameter [17:0] rd_b = 18'b0_0000_0100_0000_0000;

parameter [17:0] rd_c = 18'b0_0000_1000_0000_0000;

parameter [17:0] rd_d = 18'b0_0001_0000_0000_0000;

parameter [17:0] wr_a = 18'b0_0010_0000_0000_0000;

parameter [17:0] wr_b = 18'b0_0100_0000_0000_0000;

parameter [17:0] wr_c = 18'b0_1000_0000_0000_0000;

parameter [17:0] wr_d = 18'b1_0000_0000_0000_0000;

always @(posedge clk or negedge nReset)

if (!nReset)

begin

c_state <= #1 idle;

cmd_ack <= #1 1'b0;

scl_oen <= #1 1'b1;

sda_oen <= #1 1'b1;

sda_chk <= #1 1'b0;

end

else if (rst | al)

begin

c_state <= #1 idle;

cmd_ack <= #1 1'b0;

scl_oen <= #1 1'b1;

sda_oen <= #1 1'b1;

sda_chk <= #1 1'b0;

end

else

begin

// default no command acknowledge + assert cmd_ack only 1clk cycle

cmd_ack <= #1 1'b0; 

if (clk_en)// synopsys full_case parallel_case

case (c_state)

// idle state

idle:

begin// synopsys full_case parallel_case

case (cmd)

`I2C_CMD_START: c_state <= #1 start_a;

`I2C_CMD_STOP: c_state <= #1 stop_a;

`I2C_CMD_WRITE: c_state <= #1 wr_a;

`I2C_CMD_READ: c_state <= #1 rd_a;

default: c_state <= #1 idle;

endcase

// keep SCL in same state

scl_oen <= #1 scl_oen; 

// keep SDA in same state

sda_oen <= #1 sda_oen;

// don't check SDA output

sda_chk <= #1 1'b0;    

end

// start

start_a:

begin

c_state <= #1 start_b;

// keep SCL in same state

scl_oen <= #1 scl_oen; 

sda_oen <= #1 1'b1;    // set SDA high

// don't check SDA output

sda_chk <= #1 1'b0;    

end

start_b:

begin

c_state <= #1 start_c;

scl_oen <= #1 1'b1; // set SCL high

sda_oen <= #1 1'b1; // keep SDA high

// don't check SDA output

sda_chk <= #1 1'b0; 

end

start_c:

begin

c_state <= #1 start_d;

scl_oen <= #1 1'b1; // keep SCL high

sda_oen <= #1 1'b0; // set SDA low

// don't check SDA output

sda_chk <= #1 1'b0; 

end

start_d:

begin

c_state <= #1 start_e;

scl_oen <= #1 1'b1; // keep SCL high

sda_oen <= #1 1'b0; // keep SDA low

// don't check SDA output

sda_chk <= #1 1'b0; 

end

start_e:

begin

c_state <= #1 idle;

cmd_ack <= #1 1'b1;

scl_oen <= #1 1'b0; // set SCL low

sda_oen <= #1 1'b0; // keep SDA low

// don't check SDA output

sda_chk <= #1 1'b0; 

end

// stop

stop_a:

begin

c_state <= #1 stop_b;

scl_oen <= #1 1'b0; // keep SCL low

sda_oen <= #1 1'b0; // set SDA low

// don't check SDA output

sda_chk <= #1 1'b0; 

end

stop_b:

begin

c_state <= #1 stop_c;

scl_oen <= #1 1'b1; // set SCL high

sda_oen <= #1 1'b0; // keep SDA low

// don't check SDA output

sda_chk <= #1 1'b0; 

end

stop_c:

begin

c_state <= #1 stop_d;

scl_oen <= #1 1'b1; // keep SCL high

sda_oen <= #1 1'b0; // keep SDA low

// don't check SDA output

sda_chk <= #1 1'b0; 

end

stop_d:

begin

c_state <= #1 idle;

cmd_ack <= #1 1'b1;

scl_oen <= #1 1'b1; // keep SCL high

sda_oen <= #1 1'b1; // set SDA high

// don't check SDA output

sda_chk <= #1 1'b0; 

end

// read

rd_a:

begin

c_state <= #1 rd_b;

scl_oen <= #1 1'b0; // keep SCL low

sda_oen <= #1 1'b1; // tri-state SDA

// don't check SDA output

sda_chk <= #1 1'b0; 

end

rd_b:

begin

c_state <= #1 rd_c;

scl_oen <= #1 1'b1; // set SCL high

sda_oen <= #1 1'b1; // keep SDA tri-stated

// don't check SDA output

sda_chk <= #1 1'b0; 

end

rd_c:

begin

c_state <= #1 rd_d;

scl_oen <= #1 1'b1; // keep SCL high

sda_oen <= #1 1'b1; // keep SDA tri-stated

// don't check SDA output

sda_chk <= #1 1'b0;

end

rd_d:

begin

c_state <= #1 idle;

cmd_ack <= #1 1'b1;

scl_oen <= #1 1'b0; // set SCL low

sda_oen <= #1 1'b1; // keep SDA tri-stated

// don't check SDA output

sda_chk <= #1 1'b0; 

end

// write

wr_a:

begin

c_state <= #1 wr_b;

scl_oen <= #1 1'b0; // keep SCL low

sda_oen <= #1 din;  // set SDA

// don't check SDA output (SCL low)

sda_chk <= #1 1'b0; 

end

wr_b:

begin

c_state <= #1 wr_c;

scl_oen <= #1 1'b1; // set SCL high

sda_oen <= #1 din;  // keep SDA

// don't check SDA output yet

sda_chk <= #1 1'b0; 

// allow some time for SDA and SCL to settle

end

wr_c:

begin

c_state <= #1 wr_d;

scl_oen <= #1 1'b1; // keep SCL high

sda_oen <= #1 din;

sda_chk <= #1 1'b1; // check SDA output

end

wr_d:

begin

c_state <= #1 idle;

cmd_ack <= #1 1'b1;

scl_oen <= #1 1'b0; // set SCL low

sda_oen <= #1 din;

sda_chk <= #1 1'b0; // don't check SDA output (SCL low)

end

endcase

end

// assign scl and sda output (always gnd)

assign scl_o = 1'b0;

assign sda_o = 1'b0;

endmodule

04

IIC的优缺点

4.1 IIC协议优点

1. 通信只需要两条信号线;

2. 多主设备结构下,总线系统无需额外的逻辑与线路;

3. 应答机制完善,通信传输稳定。

4.2 IIC协议缺点

1. 硬件结构复杂;

2. 支持传输距离较短;

3. 半双工速率慢于全双工,SPI全双工一般可实现10Mbps以上的传输速率,IIC最高速度仅能达到3.4Mbps。








审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 上拉电阻
    +关注

    关注

    5

    文章

    359

    浏览量

    30607
  • IIC总线
    +关注

    关注

    1

    文章

    66

    浏览量

    20297
  • SDA
    SDA
    +关注

    关注

    0

    文章

    124

    浏览量

    28127
  • SCL
    SCL
    +关注

    关注

    1

    文章

    239

    浏览量

    17057

原文标题:常用串行总线(三)——IIC协议(Verilog实现)

文章出处:【微信号:Carlinx FPGA,微信公众号:Carlinx FPGA】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    FPGA实现IIC协议的设计

    今天给大家带来的是IIC通信,IIC协议应用非常广泛,例如与MPU6050进行通信,配置OV5640摄像头、驱动OLED屏幕等等,都需要使用到IIC
    的头像 发表于 03-04 10:49 1246次阅读
    FPGA<b class='flag-5'>实现</b><b class='flag-5'>IIC</b><b class='flag-5'>协议</b>的设计

    用verilog实现IIC

    我想用verilog实现一个控制AT24C08读写的IIC协议的状态机,现在出现了一个很奇怪的问题,我的状态机在功能仿真时可以实现读写,但下到芯片里却发现不成功。特此求教大家,望大家不
    发表于 01-29 11:18

    IIc 协议及VHDL代码实现

    位在前。3.2.2位时序和实现3.2.2.1IIC的通信协议:主机和从机在 iic总线上通信的时候, 它们之间有用类似 “ hello” ( 起始信号)和 “ Goodbye”(结束信
    发表于 04-07 13:12

    spi,uart,iic协议之间的对比

    spi,uart,iic协议之间的对比:spi和uart的区别,spi结构上可以实现一主多从进行通信,依靠时钟进行传输数据的同步传输模式。S
    发表于 08-19 08:41

    IIC协议总线特点简介

    IIC协议简介I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯
    发表于 08-20 06:12

    STM32的IIC协议简介

    、地址及数据方向4.5、响应(五)STM32的IIC特性及架构5.1、STM32的IIC外设简介5.2、STM32的IIC架构剖析5.3、通信过程IIC
    发表于 01-05 06:13

    IIC协议原理是什么

    连接微控制器以及其外围设备,IIC也被成为I2C,其实两者是完全相同的,只是名词不一样而已。它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。IIC特点:①数据线SDA:数据线用来
    发表于 01-06 06:23

    IIC协议的相关资料推荐

    STM32 IIC实验讲解,从入门到放弃。文章目录STM32 IIC实验讲解,从入门到放弃。前言一、IICIIC是什么?IIC协议二、代码
    发表于 01-17 08:12

    IIC协议软件模拟方法

    关于iic协议和对AT24C16进行读写数据的代码解读认识IIC协议IIC
    发表于 02-09 07:00

    介绍IIC通信协议以及代码开发的注意事项

    FPGA IIC接口通信本文介绍IIC通信协议以及代码开发的注意事项,跑通了IIC协议,那么后续
    发表于 02-16 07:24

    如何使用代码实现IIC协议

    1. 综述  由上篇博客可知道IIC协议如何用代码实现,本篇博客就不涉及协议内容,只讲解如何使用。  本次的实验传感为:DS3231(时钟模
    发表于 02-21 06:36

    IIC通信以及AT24C02使用

    文章目录1.什么是通信协议①什么是IIC协议IIC协议原理讲解③IIC
    发表于 11-30 20:51 15次下载
    <b class='flag-5'>IIC</b>通信以及AT24C02使用

    关于iic协议和对AT24C02进行读写数据的理解和代码解读

    关于iic协议和对AT24C16进行读写数据的代码解读认识IIC协议IIC
    发表于 12-05 16:36 8次下载
    关于<b class='flag-5'>iic</b><b class='flag-5'>协议</b>和对AT24C02进行读写数据的理解和<b class='flag-5'>代码</b>解读

    3行代码实现单片机IIc通信

    文章目录前言一、实现功能二、接线图三、完整代码四、代码运行效果前言shineblink core 开发板(简称Core)的库函数支持IIc通信功能,所以只需要调用两三个API,即可
    发表于 12-20 19:19 1次下载
    3行<b class='flag-5'>代码</b><b class='flag-5'>实现</b>单片机<b class='flag-5'>IIc</b>通信

    详解物联网常用协议IIC和RS485通信协议

    科技常用的两种通信协议——IIC和RS485。IIC通信协议是一种半双工通信协议,双总线串行,主要用在主机和从机对于数据量较少且
    的头像 发表于 03-02 17:12 1451次阅读
    详解物联网常用<b class='flag-5'>协议</b>:<b class='flag-5'>IIC</b>和RS485通信<b class='flag-5'>协议</b>