1
电子说
FPGA 芯片通过 I2C 总线连接 EEPROM 24LC04, I2C 的两根总线各上拉一个 4.7K的电阻到 3.3V,所以当总线上没有输出时会被拉高, 24LC04 的写保护没有使能,丌然 FPGA 会无法写入数据。因为在电路上 A0~A2 都为低,所以 24LC04 的设备地址为 0xA0。PGL12G 板子,是将FPGA芯片作为IIC 主站设备,将EEPROM作为了个从站设备;
原理图:
I2C 设备的操作可分为写单个存储字节,写多个存储字节,读单个存储字节和读多个存储字节。
①总线空闲状态
I2C 总线总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
②启动信号(Start)
在时钟线 SCL 保持高电平期间,数据线 SDA 上的电平被拉低(即负跳变),定义为 I2C 总线总线的启动信号,它标志着一次数据传输的开始。启动信号是由主控器主动建立的,在建立该信号前 I2C 总线必须处于空闲状态,在时钟线 SCL 保持高电平期间,数据线 SDA 被释放,使得 SDA 返回高电平(即正跳变),称为 I2C 总线的停止信号,它标志着一次数据传输的终止。停止信号也是由主 控器主动建立的,建立该信号后, I2C 总线将返回空闲状态。
④数据位传送
在 I2C 总线上传送的每一位数据都有一个时钟脉冲相对应(戒同步控制),即在 SCL 串行时钟的配合下,在 SDA 上逐位地串行传送每一位数据。迚行数据传送时,在 SCL 呈现高电平期间,SDA 上的电平必须保持稳定,低电平为数据 0,高电平为数据 1。只有在 SCL 为低电平期间,才允许 SDA 上的电平改变状态。
⑤应答信号( ACK 和 NACK)
I2C 总线上的所有数据都是以 8 位字节传送的,収送器每収送一个字节,就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位( ACK 简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位( NACK),一般表示接收器接收该字节没有成功。对于反馈有效应答位 ACK 的要求是,接收器在第 9 个时钟脉冲乊前的低电平期间将 SDA 线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主控器,则在它收到最后一个字节后,収送一个 NACK 信号,以通知被控収送器结束数据収送,并释放 SDA 线,以便主控接收器収送一个停止信号。
module i2c_eeprom_test(
input sys_clk,
input rst_n,
input key1,
inout i2c_sda,
inout i2c_scl,
output [3:0] led
);
localparam S_IDLE = 0;
localparam S_READ = 1;
localparam S_WAIT = 2;
localparam S_WRITE = 3;
reg[3:0] state;
wire button_negedge;
reg[7:0] read_data;
reg[31:0] timer;
wire scl_pad_i;
wire scl_pad_o;
wire scl_padoen_o;
wire sda_pad_i;
wire sda_pad_o;
wire sda_padoen_o;
reg[ 7:0] i2c_slave_dev_addr;
reg[15:0] i2c_slave_reg_addr;
reg[ 7:0] i2c_write_data;
reg i2c_read_req;
wire i2c_read_req_ack;
reg i2c_write_req;
wire i2c_write_req_ack;
wire[7:0] i2c_read_data;
assign led = ~read_data[3:0];
ax_debounce ax_debounce_m0
(
.clk (sys_clk),
.rst (~rst_n),
.button_in (key1),
.button_posedge (),
.button_negedge (button_negedge),
.button_out ()
);
always@(posedge sys_clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
state <= S_IDLE;
i2c_write_req <= 1'b0;
read_data <= 8'h00;
timer <= 32'd0;
i2c_write_data <= 8'd0;
i2c_slave_reg_addr <= 16'd0;
i2c_slave_dev_addr <= 8'ha0;//1010 000 0
i2c_read_req <= 1'b0;
end
else
case(state)
S_IDLE:
begin
if(timer >= 32'd12_499_999)//250ms
state <= S_READ;
else
timer <= timer + 32'd1;
end
S_READ:
begin
if(i2c_read_req_ack)
begin
i2c_read_req <= 1'b0;
read_data <= i2c_read_data;
state <= S_WAIT;
end
else
begin
i2c_read_req <= 1'b1;
i2c_slave_dev_addr <= 8'ha0;
i2c_slave_reg_addr <= 16'd0;
end
end
S_WAIT:
begin
if(button_negedge)
begin
state <= S_WRITE;
read_data <= read_data + 8'd1;
end
end
S_WRITE:
begin
if(i2c_write_req_ack)
begin
i2c_write_req <= 1'b0;
state <= S_READ;
end
else
begin
i2c_write_req <= 1'b1;
i2c_write_data <= read_data;
end
end
default:
state <= S_IDLE;
endcase
end
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;
i2c_master_top i2c_master_top_m0
(
.rst(~rst_n),
.clk(sys_clk),
.clk_div_cnt(16'd500), //Standard mode:100Khz
// 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_addr_2byte(1'b0),
.i2c_read_req(i2c_read_req),
.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()
);
endmodule
责任编辑:PSY
原文标题:紫光同创PGL22G开发平台试用连载(4)——用开源软件 opencores 上的 I2C master控制器去控制I2C接口
文章出处:【微信公众号:FPGA开发圈】欢迎添加关注!文章转载请注明出处。
全部0条评论
快来发表一下你的评论吧 !