赞
踩
1.1:
I2C全称:Inter-Integrated Circuit,是一种同步、半双工的通信总线。
同步:发送接收端要严格同步,一般有同步时钟线。
半双工:I2C只有一条数据线,所以master发数据与收数据不能同时进行。
图中是2个master+2个slave的示意,同一时刻只有一个master与一个slave通信。若想实现这个效果:
1.多个master-slave 时钟、数据线连在一起,需要实现信号的“线与”逻辑(所以SDA、SCL 被设计为漏极开路结构,外加上拉电阻实现“线与”)。
2.需要实现 “时钟同步”和“总线仲裁”,引脚在输出信号的同时还能对引脚上的电平进行检测,检测是否与刚才输出一致,为 “时钟同步”和“总线仲裁”提供硬件基础。
3.I2C在读写时需要带上设备地址,这样不使用多的信号线就可指定特定的slave(而SPI通信需要多的片选线)。
1.2:
写过程:
Master发起START
Master发送I2C addr(7bit)和W(写)操作0(1bit),等待ACK
Slave发送ACK
Master发送reg addr(8bit),等待ACK
Slave发送ACK
Master发送data(8bit),即要写入寄存器中的数据,等待ACK
Slave发送ACK
第6步和第7步可以重复多次,即顺序写多个寄存器
Master发起STOP结束传输
读过程:
Master发起START
Master发送I2C addr(7bit)和r(读)操作1(1bit),等待ACK
Slave发送ACK
Slave发送data(8bit),即寄存器里的值
Master发送ACK
第7步和第8步可以重复多次,即顺序读多个寄存器
当master接收完想要的数据后,由Master发送NACK,告知slave停止发送数据
Master发送STOP结束传输
1.3:
1.总线空闲状态:
SDA和SCL同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
2.总线START:
SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
3.总线STOP:
SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
4.总线Restart:
SCL为高电平时,SDA由高电平向低电平跳变,本质上也是START信号,用在完整I2C读过程中的读阶段,在首次发送停止信号之前,master通过发送Restart信号,可以转换与当前slave的通信模式(从写模式到读模式),或是切换到与另一个slave通信。
5.数据阶段:
在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。进行数据传送时,在SCL呈现高电平期间,SDA上的电平必须保持稳定。只有在SCL为低电平期间,才允许SDA上的电平改变状态。简单的说就是,数据在SCL下降会被采样,所以SDA需要在SCL为高电平时保持稳定。
6. ACK与NACK信号:
IIC总线上的所有数据都是以8位字节传送的,发送器每发送一个字节,就在第9个时钟脉冲期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
这段话再说细一点:
在写阶段,master写了一字节数据,在第9个时钟脉冲期间释放数据线,由slave反馈应答信号,ACK(低)表示数据成功接收,NACK(高)表示该字节没有接收成功;
在读阶段,master向slave收数据,slave写了一字节数据,在第9个时钟脉冲期间释放数据线,由master反馈应答信号,ACK(低)表示数据成功接收,NACK(高)表示该字节没有接收成功。
还有一种特殊情况:当master决定不再接收数据时,应向slave发送NACK信号,高速slave不再发送。
以下情况会导致出现NACK位:
1.接收器没有发送机响应的地址,接收端没有任何ACK发送给发送器
2.由于接收器正在忙碌处理实时程序导致接无法接收或者发送
传输过程中,接收机器别不了发送机的数据或命令
3.接收器无法接收
4.master接收完成读取数据后,要发送NACK结束告知slave。
当master接收到slave的NACK信号后,可以STOP这次传输,也可以重新START。
所以:NACK并不只是表示字节没有成功接收,也可以表示master告诉slave不再需要发送数据。
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2023/02/23 16:16:18 // Design Name: // Module Name: i2c_dri // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module i2c_dri #( parameter SLAVE_ADDR = 7'b1010000, parameter CLK_FREQ = 26'd50_000_000, parameter I2C_FREQ = 18'd250_000 ) ( input clk , input rst_n , input i2c_exec , input i2c_rh_wl , input bit_ctrl , input [15:0] i2c_addr , input [7:0] i2c_data_w , output reg [7:0] i2c_data_r , output reg i2c_done , output reg i2c_ack , output reg scl , inout sda , output reg dri_clk ); localparam st_idle = 8'b0000_0001; //空闲状态 localparam st_sladdr = 8'b0000_0010; //发送器件地址 localparam st_addr16 = 8'b0000_0100; //发送16位地址 localparam st_addr8 = 8'b0000_1000; //发送8位地址 localparam st_data_wr = 8'b0001_0000; //写数据 localparam st_addr_rd = 8'b0010_0000; //读地址 localparam st_data_rd = 8'b0100_0000; //读数据 localparam st_stop = 8'b1000_0000; //结束i2c操作 reg sda_dir ; reg sda_out ; reg st_done ;//状态结束 reg wr_flag ;//写标志 reg [6:0] cnt ; reg [7:0] cur_state ; reg [7:0] next_state ; reg [15:0] addr_t ; reg [7:0] data_r ; reg [7:0] data_wr_t ; reg [9:0] clk_cnt ; wire sda_in ; wire [8:0] clk_divide ; assign sda = sda_dir ? sda_out : 1'bz; assign sda_in = sda; assign clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2; //I2C的SCL四倍频 always@(posedge clk or negedge rst_n)begin if(!rst_n)begin dri_clk <= 1'b0; clk_cnt <= 10'd0; end else if(clk_cnt == clk_divide[8:1] - 1'd1)begin clk_cnt <= 10'd0; dri_clk <= ~dri_clk; end else clk_cnt <= clk_cnt + 1'd1; end //状态机状态转移 always@(posedge dri_clk or negedge rst_n)begin if(!rst_n) cur_state <= st_idle; else cur_state <= next_state; end //状态机状态转移条件 always@(*)begin next_state = st_idle; case(cur_state) st_idle: begin if(i2c_exec == 1'b1) next_state = st_sladdr; else next_state = st_idle; end st_sladdr:begin if(st_done)begin if(bit_ctrl) next_state = st_addr16; else next_state = st_addr8; end else next_state = st_sladdr; end st_addr16:begin if(st_done)begin next_state = st_addr8; end else begin next_state = st_addr16; end end st_addr8:begin if(st_done)begin if(wr_flag == 1'b0) next_state = st_data_wr; else next_state = st_addr_rd; end else begin next_state = st_addr8; end end st_data_wr:begin if(st_done) next_state = st_stop; else next_state = st_data_wr; end st_addr_rd:begin if(st_done)begin next_state = st_data_rd; end else begin next_state = st_addr_rd; end end st_data_rd:begin if(st_done) next_state = st_stop; else next_state = st_data_rd; end st_stop:begin if(st_done) next_state = st_idle; else next_state = st_stop; end default:next_state = st_idle; endcase end //时序电路描述状态输出 always@(posedge dri_clk or negedge rst_n)begin if(!rst_n)begin scl <=1'b1; sda_out <=1'b1; sda_dir <=1'b1; i2c_done <=1'b0; i2c_ack <=1'b0; cnt <=1'b0; st_done <=1'b0; data_r <=1'b0; i2c_data_r <=1'b0; wr_flag <=1'b0; addr_t <=1'b0; data_wr_t <=1'b0; end else begin st_done <=1'b0; cnt<= cnt+1'b1; case(cur_state) st_idle:begin scl <=1'b1; sda_out <=1'b1; sda_dir <=1'b1; i2c_done<=1'b0; cnt <=1'b0; if(i2c_exec)begin wr_flag <=i2c_rh_wl; addr_t <=i2c_addr; data_wr_t <=i2c_data_w; i2c_ack <=1'b0; end end st_sladdr:begin case(cnt) 7'd1 : sda_out <= 1'b0; 7'd3 : scl <= 1'b0; 7'd4 : sda_out <= SLAVE_ADDR[6]; 7'd5 : scl <=1'b1; 7'd7 : scl <=1'b0; 7'd8 : sda_out <= SLAVE_ADDR[5]; 7'd9 : scl <= 1'b1; 7'd11: scl <= 1'b0; 7'd12: sda_out <= SLAVE_ADDR[4]; 7'd13: scl <= 1'b1; 7'd15: scl <= 1'b0; 7'd16: sda_out <= SLAVE_ADDR[3]; 7'd17: scl <= 1'b1; 7'd19: scl <= 1'b0; 7'd20: sda_out <= SLAVE_ADDR[2]; 7'd21: scl <= 1'b1; 7'd23: scl <= 1'b0; 7'd24: sda_out <= SLAVE_ADDR[1]; 7'd25: scl <= 1'b1; 7'd27: scl <= 1'b0; 7'd28: sda_out <= SLAVE_ADDR[0]; 7'd29: scl <= 1'b1; 7'd31: scl <= 1'b0; 7'd32: sda_out <= 1'b0; //0:写 7'd33: scl <= 1'b1; 7'd35: scl <= 1'b0; 7'd36: begin sda_dir <= 1'b0; sda_out <= 1'b1; end 7'd37: scl <= 1'b1; 7'd38: begin st_done <= 1'b1; if(sda_in==1'b1) i2c_ack <= 1'b1; end 7'd39:begin scl <= 1'b0; cnt <= 1'b0; end default : ; endcase end st_addr16:begin case(cnt) 7'd0:begin sda_dir <= 1'b1; sda_out <= addr_t[15]; end 7'd1: scl <= 1'b1; 7'd3: scl <= 1'b0; 7'd4: sda_out <= addr_t[14]; 7'd5: scl <= 1'b1; 7'd7: scl <= 1'b0; 7'd8: sda_out <= addr_t[13]; 7'd9: scl <= 1'b1; 7'd11:scl <= 1'b0; 7'd12:sda_out <= addr_t[12]; 7'd13:scl <= 1'b1; 7'd15:scl <= 1'b0; 7'd16:sda_out <= addr_t[11]; 7'd17:scl <= 1'b1; 7'd19:scl <= 1'b0; 7'd20:sda_out <= addr_t[10]; 7'd21:scl <= 1'b1; 7'd23:scl <= 1'b0; 7'd24:sda_out <= addr_t[9]; 7'd25:scl <= 1'b1; 7'd27:scl <= 1'b0; 7'd28:sda_out <= addr_t[8]; 7'd29:scl <= 1'b1; 7'd31:scl <= 1'b0; 7'd32:begin sda_dir <= 1'b0; sda_out <= 1'b1; end 7'd34:begin st_done <= 1'b1; if(sda_in == 1'b1) i2c_ack <= 1'b1; end 7'd35:begin scl <= 1'b0; cnt <= 1'b0; end default: ; endcase end st_addr8:begin case(cnt) 7'd0:begin sda_dir <= 1'b1; sda_out <= addr_t[7]; end 7'd1: scl <= 1'b1; 7'd3: scl <= 1'b0; 7'd4: sda_out <= addr_t[6]; 7'd5: scl <= 1'b1; 7'd7: scl <= 1'b0; 7'd8: sda_out <= addr_t[5]; 7'd9: scl <= 1'b1; 7'd11:scl <= 1'b0; 7'd12:sda_out <= addr_t[4]; 7'd13:scl <= 1'b1; 7'd15:scl <= 1'b0; 7'd16:sda_out <= addr_t[3]; 7'd17:scl <= 1'b1; 7'd19:scl <= 1'b0; 7'd20:sda_out <= addr_t[2]; 7'd21:scl <= 1'b1; 7'd23:scl <= 1'b0; 7'd24:sda_out <= addr_t[1]; 7'd25:scl <= 1'b1; 7'd27:scl <= 1'b0; 7'd28:sda_out <= addr_t[0]; 7'd29:scl <= 1'b1; 7'd31:scl <= 1'b0; 7'd32:begin sda_dir <= 1'b0; sda_out <= 1'b1; end 7'd34:begin st_done <= 1'b1; if(sda_in == 1'b1) i2c_ack <= 1'b1; end 7'd35:begin scl <= 1'b0; cnt <= 1'b0; end default: ; endcase end st_data_wr:begin case(cnt) 7'd0:begin sda_out <= data_wr_t[7]; sda_dir <= 1'b1; end 7'd1: scl <= 1'b1; 7'd3: scl <= 1'b0; 7'd4: sda_out <= data_wr_t[6]; 7'd5 : scl <= 1'b1; 7'd7 : scl <= 1'b0; 7'd8 : sda_out <= data_wr_t[5]; 7'd9 : scl <= 1'b1; 7'd11: scl <= 1'b0; 7'd12: sda_out <= data_wr_t[4]; 7'd13: scl <= 1'b1; 7'd15: scl <= 1'b0; 7'd16: sda_out <= data_wr_t[3]; 7'd17: scl <= 1'b1; 7'd19: scl <= 1'b0; 7'd20: sda_out <= data_wr_t[2]; 7'd21: scl <= 1'b1; 7'd23: scl <= 1'b0; 7'd24: sda_out <= data_wr_t[1]; 7'd25: scl <= 1'b1; 7'd27: scl <= 1'b0; 7'd28: sda_out <= data_wr_t[0]; 7'd29: scl <= 1'b1; 7'd31: scl <= 1'b0; 7'd32: begin sda_dir <= 1'b0; sda_out <= 1'b1; end 7'd33: scl <= 1'b1; 7'd34: begin //从机应答 st_done <= 1'b1; if(sda_in == 1'b1) //高电平表示未应答 i2c_ack <= 1'b1; //拉高应答标志位 end 7'd35: begin scl <= 1'b0; cnt <= 1'b0; end default : ; endcase end st_addr_rd: begin case(cnt) 7'd0 : begin sda_dir <= 1'b1; sda_out <= 1'b1; end 7'd1 : scl <= 1'b1; 7'd2 : sda_out <= 1'b0; 7'd3 : scl <= 1'b0; 7'd4 : sda_out <= SLAVE_ADDR[6]; 7'd5 : scl <= 1'b1; 7'd7 : scl <= 1'b0; 7'd8 : sda_out <= SLAVE_ADDR[5]; 7'd9 : scl <= 1'b1; 7'd11: scl <= 1'b0; 7'd12: sda_out <= SLAVE_ADDR[4]; 7'd13: scl <= 1'b1; 7'd15: scl <= 1'b0; 7'd16: sda_out <= SLAVE_ADDR[3]; 7'd17: scl <= 1'b1; 7'd19: scl <= 1'b0; 7'd20: sda_out <= SLAVE_ADDR[2]; 7'd21: scl <= 1'b1; 7'd23: scl <= 1'b0; 7'd24: sda_out <= SLAVE_ADDR[1]; 7'd25: scl <= 1'b1; 7'd27: scl <= 1'b0; 7'd28: sda_out <= SLAVE_ADDR[0]; 7'd29: scl <= 1'b1; 7'd31: scl <= 1'b0; 7'd32: sda_out <= 1'b1; 7'd33: scl <= 1'b1; 7'd35: scl <= 1'b0; 7'd36: begin sda_dir <= 1'b0; sda_out <= 1'b1; end 7'd37: scl <=1'b1; 7'd38: begin st_done <= 1'b1; if(sda_in == 1'b1) i2c_ack <= 1'b1; end 7'd39: begin scl <= 1'b0; cnt <= 1'b0; end default : ; endcase end st_data_rd:begin case(cnt) 7'd0: sda_dir <= 1'b0; 7'd1: begin data_r[7] <= sda_in; scl <= 1'b1; end 7'd3: scl <= 1'b0; 7'd5: begin data_r[6] <= sda_in; scl <= 1'b1; end 7'd7: scl <= 1'b0; 7'd9: begin data_r[5] <= sda_in; scl <= 1'b1; end 7'd11: scl <= 1'b0; 7'd13: begin data_r[4] <= sda_in; scl <= 1'b1 ; end 7'd15: scl <= 1'b0; 7'd17: begin data_r[3] <= sda_in; scl <= 1'b1 ; end 7'd19: scl <= 1'b0; 7'd21: begin data_r[2] <= sda_in; scl <= 1'b1 ; end 7'd23: scl <= 1'b0; 7'd25: begin data_r[1] <= sda_in; scl <= 1'b1 ; end 7'd27: scl <= 1'b0; 7'd29: begin data_r[0] <= sda_in; scl <= 1'b1 ; end 7'd31: scl <= 1'b0; 7'd32: begin sda_dir <= 1'b1; sda_out <= 1'b1; end 7'd33: scl <= 1'b1; 7'd34: st_done <= 1'b1; 7'd35:begin scl <= 1'b0; cnt <= 1'b0; i2c_data_r <= data_r; end default : ; endcase end st_stop:begin case(cnt) 7'd0:begin sda_dir <= 1'b1; sda_out <= 1'b0; end 7'd1: scl <= 1'b1; 7'd3: sda_out <= 1'b1; 7'd15: st_done <= 1'b1; 7'd16:begin cnt <=1'b0; i2c_done<=1'b1; end default : ; endcase end endcase end end endmodule
分频系数就是scl一个周期有几个clk,dri_clk=4*scl,所以分频系数再除以四(等价于右移两位)。
- //I2C的SCL四倍频
- always@(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- dri_clk <= 1'b0;
- clk_cnt <= 10'd0;
- end
- else if(clk_cnt == clk_divide[8:1] - 1'd1)begin
- clk_cnt <= 10'd0;
- dri_clk <= ~dri_clk;
- end
- else
- clk_cnt <= clk_cnt + 1'd1;
- end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。