基于FPGA的I2C读写EEPROM
I2C在芯片的配置中应用还是很多的,比如摄像头、VGA转HDMI转换芯片,之前博主分享过一篇I2C协议的基础学习IIC协议学习笔记,这篇就使用Verilog来实现EEPROM的读写,进行一个简单的I2C实战应用。
EEPROM
我使用的这个芯片是AT24C32,它手册上还有一种AT24C64,其实操作都是一样的,只是内存大小不同,AT24C32是32k(4096×8)AT24C64是64k(9=8192×8),
SCL设置为频率200Khz
SCL clk posedge data输入EEPROM
SCL clk negedge data输出EEPROM
SDA 双向Pin
A2,A1,A0 Device Addr default all 0,只操作一片可悬空引脚。
WP 接地正常读写,WP接Vcc写操作被禁止
字节寻址地址,是由12(AT24C32)或13bit(AT24C64)的地址组成,需要操作16位字地址高3或4位忽略即可。
Device Address 8’hA0写器件地址,8’hA1读器件地址
写字节操作
随机读字节操作
我这个芯片是双字节数据地址,所以在写数据地址时要写两次,先是高字节后是低字节。
开始结束标志
这个I2C总线的时序是一致的。
EEPROM应答
输出应答scl的第九个周期给出,低电平应答。如果主机没有收到应答,需要重新配置。
数据传输时序
sda数据线在scl时钟的下降沿中间变化,可以避免产生误触开始结束标志。
I2C Design
i2c_start为高电平有效,传输完成后会产生一个i2c_done结束标志,表示操作完成。
I2C状态转移图
I2C写操作
(1)产生start位
(2)传送器件地址ID_Address,器件地址的最后一位为数据的传输方向位,R/W,低电平0表示主机往从机写数据(W),1表示主机从从机读数据(R)。这里按照手册给出的操作图,应该是W即低电平。ACK应答,应答是从机发送给主机的应答,这里不用管。
(3)传送写入器件寄存器地址,即数据要写入的位置。同样ACK应答不用管。
(4)传送要写入的数据。ACK应答不用管。
(5)产生stop信号。
I2C读操作
(1)产生start信号
(2)传送器件地址(写ID_Address),这里按照手册给出的操作图,最低位是W即低电平。ACK。
(3)传送字地址(写REG_Address),ACK。
(4)再次产生start信号
(5)再传送一次器件地址,这里根据手册最低位是读R高电平,ACK。
(6)读取一个字节的数据,读数据最后结束前无应答ACK信号。
(7)产生stop信号。
读写操作的写器件地址和写数据地址操作是一样的,状态转移图中读写操作中这两部分复用了,根据读写标志来判断。
其他部分没啥好说的根据时序图写就行了,需要注意的一点是我们应该在sclk的高电平的中间采样数据,在sclk低电平的中间改变数据,当sclk为高电平的时候,sda为出现下降沿为start位, sda出现上升沿为stop位,所以在sclk为高电平的时候sda应该保持稳定不能随意乱动。这就又回到了数据传输有效的条件,只有在sclk为低电平期间,才允许数据变化,在高电平期间,不允许数据变化,否则就会出现起始位或结束位。
EEPROM有个仿真模型,在夏雨闻老师的书里面就有,这个模型默认是200khz的sclk驱动,仿真的时候可以将时间参数改小,我这里也分享出来。
仿真模型代码
1 `timescale 1ns/1ns 2 `define timeslice 1250 3 //`define timeslice 300 4 5 module EEPROM_AT24C64( 6 scl, 7 sda 8 ); 9 input scl; //串行时钟线 10 inout sda; //串行数据线 11 12 reg out_flag; //SDA数据输出的控制信号 13 14 reg[7:0] memory[8191:0]; //数组模拟存储器 15 reg[12:0]address; //地址总线 16 reg[7:0]memory_buf; //数据输入输出寄存器 17 reg[7:0]sda_buf; //SDA数据输出寄存器 18 reg[7:0]shift; //SDA数据输入寄存器 19 reg[7:0]addr_byte_h; //EEPROM存储单元地址高字节寄存器 20 reg[7:0]addr_byte_l; //EEPROM存储单元地址低字节寄存器 21 reg[7:0]ctrl_byte; //控制字寄存器 22 reg[1:0]State; //状态寄存器 23 24 integer i; 25 26 //--------------------------- 27 parameter 28 r7 = 8'b1010_1111, w7 = 8'b1010_1110, //main7 29 r6 = 8'b1010_1101, w6 = 8'b1010_1100, //main6 30 r5 = 8'b1010_1011, w5 = 8'b1010_1010, //main5 31 r4 = 8'b1010_1001, w4 = 8'b1010_1000, //main4 32 r3 = 8'b1010_0111, w3 = 8'b1010_0110, //main3 33 r2 = 8'b1010_0101, w2 = 8'b1010_0100, //main2 34 r1 = 8'b1010_0011, w1 = 8'b1010_0010, //main1 35 r0 = 8'b1010_0001, w0 = 8'b1010_0000; //main0 36 //--------------------------- 37 38 assign sda = (out_flag == 1) ? sda_buf[7] : 1'bz; 39 40 //------------寄存器和存储器初始化--------------- 41 initial 42 begin 43 addr_byte_h = 0; 44 addr_byte_l = 0; 45 ctrl_byte = 0; 46 out_flag = 0; 47 sda_buf = 0; 48 State = 2'b00; 49 memory_buf = 0; 50 address = 0; 51 shift = 0; 52 53 for(i=0;i<=8191;i=i+1) 54 memory[i] = 0; 55 end 56 57 //启动信号 58 always@(negedge sda) 59 begin 60 if(scl == 1) 61 begin 62 State = State + 1; 63 if(State == 2'b11) 64 disable write_to_eeprom; 65 end 66 end 67 68 //主状态机 69 always@(posedge sda) 70 begin 71 if(scl == 1) //停止操作 72 stop_W_R; 73 else 74 begin 75 casex(State) 76 2'b01:begin 77 read_in; 78 if(ctrl_byte == w7 || ctrl_byte == w6 79 || ctrl_byte == w5 || ctrl_byte == w4 80 || ctrl_byte == w3 || ctrl_byte == w2 81 || ctrl_byte == w1 || ctrl_byte == w0) 82 begin 83 State = 2'b10; 84 write_to_eeprom; //写操作 85 end 86 else 87 State = 2'b00; 88 //State = State; 89 end 90 91 2'b11: 92 read_from_eeprom; 93 94 default: 95 State = 2'b00; 96 endcase 97 end 98 end //主状态机结束 99 100 //操作停止 101 task stop_W_R; 102 begin 103 State = 2'b00; 104 addr_byte_h = 0; 105 addr_byte_l = 0; 106 ctrl_byte = 0; 107 out_flag = 0; 108 sda_buf = 0; 109 end 110 endtask 111 112 //读进控制字和存储单元地址 113 task read_in; 114 begin 115 shift_in(ctrl_byte); 116 shift_in(addr_byte_h); 117 shift_in(addr_byte_l); 118 end 119 endtask 120 121 //EEPROM的写操作 122 task write_to_eeprom; 123 begin 124 shift_in(memory_buf); 125 address = {addr_byte_h[4:0], addr_byte_l}; 126 memory[address] = memory_buf; 127 State = 2'b00; 128 end 129 endtask 130 131 //EEPROM的读操作 132 task read_from_eeprom; 133 begin 134 shift_in(ctrl_byte); 135 if(ctrl_byte == r7 || ctrl_byte == w6 136 || ctrl_byte == r5 || ctrl_byte == r4 137 || ctrl_byte == r3 || ctrl_byte == r2 138 || ctrl_byte == r1 || ctrl_byte == r0) 139 begin 140 address = {addr_byte_h[4:0], addr_byte_l}; 141 sda_buf = memory[address]; 142 shift_out; 143 State = 2'b00; 144 end 145 end 146 endtask 147 148 //SDA数据线上的数据存入寄存器,数据在SCL的高电平有效 149 task shift_in; 150 output[7:0]shift; 151 begin 152 @(posedge scl) shift[7] = sda; 153 @(posedge scl) shift[6] = sda; 154 @(posedge scl) shift[5] = sda; 155 @(posedge scl) shift[4] = sda; 156 @(posedge scl) shift[3] = sda; 157 @(posedge scl) shift[2] = sda; 158 @(posedge scl) shift[1] = sda; 159 @(posedge scl) shift[0] = sda; 160 161 @(negedge scl) 162 begin 163 #`timeslice; 164 out_flag = 1; //应答信号输出 165 sda_buf = 0; 166 end 167 168 @(negedge scl) 169 begin 170 #`timeslice; 171 out_flag = 0; 172 end 173 end 174 endtask 175 176 //EEPROM存储器中的数据通过SDA数据线输出,数据在SCL低电平时变化 177 task shift_out; 178 begin 179 out_flag = 1; 180 for(i=6; i>=0; i=i-1) 181 begin 182 @(negedge scl); 183 #`timeslice; 184 sda_buf = sda_buf << 1; 185 end 186 @(negedge scl) #`timeslice sda_buf[7] = 1; //非应答信号输出 187 @(negedge scl) #`timeslice out_flag = 0; 188 end 189 endtask 190 191 endmodule 192 //eeprom.v文件结束
根据仿真模型仿真的话基本不会有什么问题,需要注意的是操作的完成标志。从仿真上看到输入读写都没问题,但是stop标志没产生好,仿真看到读写操作没问题,但实际还是不行的,需要严格按照EEPROM的手册操作时序进行,差一点就不行。
I2C的代码我分享出来,我最后使用拨码开关作为读写使能,数码管显示读出来的输出,最后实现了对指定存储地址读写数据。
I2C设计代码点击阅读原文可以查看。
1 `timescale 1ns/1ps 2 // ********************************************************************************* 3 // Project Name : 4 // Author : NingHeChuan 5 // Email : ninghechuan@foxmail.com 6 // Blogs : http://www.cnblogs.com/ninghechuan/ 7 // File Name : I2C_Ctrl_EEPROM.v 8 // Module Name : 9 // Called By : 10 // Abstract : 11 // 12 // CopyRight(c) 2018, NingHeChuan Studio.. 13 // All Rights Reserved 14 // 15 // ********************************************************************************* 16 // Modification History: 17 // Date By Version Change Description 18 // ----------------------------------------------------------------------- 19 // 2018/8/15 NingHeChuan 1.0 Original 20 // 21 // ********************************************************************************* 22 23 module I2C_Ctrl_EEPROM( 24 input clk, 25 input rst_n, 26 input [31:0] eeprom_config_data, 27 input i2c_start, //1 valid 28 inout i2c_sdat, 29 output i2c_sclk, 30 output i2c_done, 31 output reg [7:0] i2c_rd_data 32 ); 33 34 35 //------------------------------------------------------- 36 parameter I2C_IDLE = 'd0; 37 parameter I2C_START = 'd1; 38 parameter I2C_WR_IDADDR = 'd2; 39 parameter I2C_WR_ACK1 = 'd3; 40 parameter I2C_WR_REGADDR1 = 'd4; 41 parameter I2C_WR_ACK2 = 'd5; 42 parameter I2C_WR_REGADDR2 = 'd6; 43 parameter I2C_WR_ACK3 = 'd7; 44 parameter I2C_WR_DATA = 'd8; 45 parameter I2C_WR_ACK4 = 'd9; 46 parameter I2C_WR_STOP = 'd10; 47 //------------------------------------------------------- 48 parameter I2C_RD_START = 'd11; 49 parameter I2C_RD_IDADDR = 'd12; 50 parameter I2C_RD_ACK = 'd13; 51 parameter I2C_RD_DATA = 'd14; 52 parameter I2C_RD_NPACK = 'd15; 53 parameter I2C_RD_STOP = 'd16; 54 //i2c_sclk freq 55 parameter I2C_FREQ = 250; //50Mhz/200Khz/2 = 125 56 parameter TRANSFER = 1; 57 parameter CAPTURE = 125; 58 //parameter I2C_FREQ = 60; //50Mhz/200Khz/2 = 125 59 //parameter TRANSFER = 1; 60 //parameter CAPTURE = 30; 61 parameter SEND_BIT = 8; 62 63 //------------------------------------------------------- 64 reg [4:0] pre_state; 65 reg [4:0] next_state; 66 // 67 reg i2c_sdat_r; 68 wire bir_en; 69 // 70 wire transfer_en; 71 wire capture_en; 72 reg i2c_sclk_r; 73 reg [7:0] sclk_cnt; 74 // 75 reg [3:0] tran_cnt; 76 // 77 wire [7:0] wr_device_addr = {eeprom_config_data[31:25], 1'b0}; 78 wire [7:0] rd_device_addr = {eeprom_config_data[31:25], 1'b1}; 79 wire wr_rd_flag = eeprom_config_data[24]; 80 wire [7:0] reg_addr1 = eeprom_config_data[23:16]; 81 wire [7:0] reg_addr2 = eeprom_config_data[15:8]; 82 wire [7:0] wr_data = eeprom_config_data[7:0]; 83 // 84 reg wr_ack1; 85 reg wr_ack2; 86 reg wr_ack3; 87 reg wr_ack4; 88 reg rd_ack1; 89 90 //------------------------------------------------------- 91 //i2c_sclk 92 always @(posedge clk or negedge rst_n)begin 93 if(rst_n == 1'b0) 94 sclk_cnt <= 'd1; 95 else if(sclk_cnt == I2C_FREQ - 1'b1) 96 sclk_cnt <= 'd0; 97 else 98 sclk_cnt <= sclk_cnt + 1'b1; 99 end 100 101 always @(posedge clk or negedge rst_n)begin 102 if(rst_n == 1'b0) 103 i2c_sclk_r <= 1'b0; 104 else if(sclk_cnt >= (I2C_FREQ>>2)*1 && sclk_cnt <= (I2C_FREQ>>2)*3) 105 i2c_sclk_r <= 1'b1; 106 else 107 i2c_sclk_r <= 1'b0; 108 end 109 // 110 assign transfer_en = (sclk_cnt == TRANSFER - 1)? 1'b1: 1'b0; 111 assign capture_en = (sclk_cnt == CAPTURE - 1)? 1'b1: 1'b0; 112 113 //------------------------------------------------------- 114 always @(posedge clk or negedge rst_n)begin 115 if(rst_n == 1'b0) 116 tran_cnt <= 'd0; 117 else if(tran_cnt == SEND_BIT && transfer_en == 1'b1) 118 tran_cnt <= 'd0; 119 else if(((next_state == I2C_WR_IDADDR || next_state == I2C_WR_REGADDR1 || 120 next_state ==I2C_WR_REGADDR2 || next_state == I2C_WR_DATA || 121 next_state == I2C_RD_IDADDR) && transfer_en == 1'b1) || 122 (next_state == I2C_RD_DATA && capture_en == 1'b1)) 123 tran_cnt <= tran_cnt + 1'b1; 124 else 125 tran_cnt <= tran_cnt; 126 end 127 128 //------------------------------------------------------- 129 //FSM step1 130 always @(posedge clk or negedge rst_n)begin 131 if(rst_n == 1'b0) 132 pre_state <= I2C_IDLE; 133 else 134 pre_state <= next_state; 135 end 136 137 //FSM step2 138 always @(*)begin 139 next_state = I2C_IDLE; 140 case(pre_state) 141 I2C_IDLE: 142 if(i2c_start == 1'b1 && transfer_en == 1'b1) 143 next_state = I2C_START; 144 else 145 next_state = I2C_IDLE; 146 I2C_START: 147 if(transfer_en == 1'b1) 148 next_state = I2C_WR_IDADDR; 149 else 150 next_state = I2C_START; 151 I2C_WR_IDADDR: 152 if(transfer_en == 1'b1 && tran_cnt == SEND_BIT) 153 next_state = I2C_WR_ACK1; 154 else 155 next_state = I2C_WR_IDADDR; 156 I2C_WR_ACK1: 157 if(transfer_en == 1'b1 && wr_ack1 == 1'b0) 158 next_state = I2C_WR_REGADDR1; 159 else if(transfer_en == 1'b1) 160 next_state = I2C_IDLE; 161 else 162 next_state = I2C_WR_ACK1; 163 I2C_WR_REGADDR1: 164 if(transfer_en == 1'b1 && tran_cnt == SEND_BIT) 165 next_state = I2C_WR_ACK2; 166 else 167 next_state = I2C_WR_REGADDR1; 168 I2C_WR_ACK2: 169 if(transfer_en == 1'b1 && wr_ack2 == 1'b0) 170 next_state = I2C_WR_REGADDR2; 171 else if(transfer_en == 1'b1) 172 next_state = I2C_IDLE; 173 else 174 next_state = I2C_WR_ACK2; 175 I2C_WR_REGADDR2: 176 if(transfer_en == 1'b1 && tran_cnt == SEND_BIT) 177 next_state = I2C_WR_ACK3; 178 else 179 next_state = I2C_WR_REGADDR2; 180 I2C_WR_ACK3: 181 if(transfer_en == 1'b1 && wr_ack3 == 1'b0 && wr_rd_flag == 1'b0) 182 next_state = I2C_WR_DATA; 183 else if(transfer_en == 1'b1 && wr_ack3 == 1'b0 && wr_rd_flag == 1'b1) 184 next_state = I2C_RD_START; 185 else if(transfer_en == 1'b1) 186 next_state = I2C_IDLE; 187 else 188 next_state = I2C_WR_ACK3; 189 I2C_WR_DATA: 190 if(transfer_en == 1'b1 && tran_cnt == SEND_BIT) 191 next_state = I2C_WR_ACK4; 192 else 193 next_state = I2C_WR_DATA; 194 I2C_WR_ACK4: 195 if(transfer_en == 1'b1 && wr_ack4 == 1'b0) 196 next_state = I2C_WR_STOP; 197 else if(transfer_en == 1'b1) 198 next_state = I2C_IDLE; 199 else 200 next_state = I2C_WR_ACK4; 201 I2C_WR_STOP: 202 if(transfer_en == 1'b1) 203 next_state = I2C_IDLE; 204 else 205 next_state = I2C_WR_STOP; 206 I2C_RD_START: 207 if(transfer_en == 1'b1) 208 next_state = I2C_RD_IDADDR; 209 else 210 next_state = I2C_RD_START; 211 I2C_RD_IDADDR: 212 if(transfer_en == 1'b1 && tran_cnt == SEND_BIT) 213 next_state = I2C_RD_ACK; 214 else 215 next_state = I2C_RD_IDADDR; 216 I2C_RD_ACK: 217 if(transfer_en == 1'b1 && rd_ack1 == 1'b0) 218 next_state = I2C_RD_DATA; 219 else if(transfer_en == 1'b1) 220 next_state = I2C_IDLE; 221 else 222 next_state = I2C_RD_ACK; 223 I2C_RD_DATA: 224 if(transfer_en == 1'b1 && tran_cnt == SEND_BIT) 225 next_state = I2C_RD_NPACK; 226 else 227 next_state = I2C_RD_DATA; 228 I2C_RD_NPACK: 229 if(transfer_en == 1'b1) 230 next_state = I2C_RD_STOP; 231 else 232 next_state = I2C_RD_NPACK; 233 I2C_RD_STOP: 234 if(transfer_en == 1'b1) 235 next_state = I2C_IDLE; 236 else 237 next_state = I2C_RD_STOP; 238 default:next_state = I2C_IDLE; 239 endcase 240 end 241 242 //FSM step3 243 always @(posedge clk or negedge rst_n)begin 244 if(rst_n == 1'b0) 245 i2c_sdat_r <= 1'b1; 246 else begin 247 case(next_state) 248 I2C_IDLE: if(capture_en == 1'b1) i2c_sdat_r <= 1'b1; 249 I2C_START: if(capture_en == 1'b1) i2c_sdat_r <= 1'b0; 250 I2C_WR_IDADDR: if(transfer_en == 1'b1) i2c_sdat_r <= wr_device_addr['d7 - tran_cnt]; 251 I2C_WR_REGADDR1:if(transfer_en == 1'b1) i2c_sdat_r <= reg_addr1['d7 - tran_cnt]; 252 I2C_WR_REGADDR2:if(transfer_en == 1'b1) i2c_sdat_r <= reg_addr2['d7 - tran_cnt]; 253 I2C_WR_DATA: if(transfer_en == 1'b1) i2c_sdat_r <= wr_data['d7 - tran_cnt]; 254 I2C_WR_ACK4: if(transfer_en == 1'b1) i2c_sdat_r <= 1'b0; 255 I2C_WR_STOP: if(capture_en == 1'b1) i2c_sdat_r <= 1'b1; 256 I2C_RD_START: if(capture_en == 1'b1) i2c_sdat_r <= 1'b0; 257 I2C_RD_IDADDR: if(transfer_en == 1'b1) i2c_sdat_r <= rd_device_addr['d7 - tran_cnt]; 258 I2C_RD_NPACK: if(transfer_en == 1'b1) i2c_sdat_r <= 1'b0; 259 I2C_RD_STOP: if(capture_en == 1'b1) i2c_sdat_r <= 1'b1; 260 default: i2c_sdat_r <= i2c_sdat_r; 261 endcase 262 end 263 end 264 265 always @(posedge clk or negedge rst_n)begin 266 if(rst_n == 1'b0)begin 267 i2c_rd_data <= 8'b0; 268 wr_ack1 <= 1'b1; 269 wr_ack2 <= 1'b1; 270 wr_ack3 <= 1'b1; 271 wr_ack4 <= 1'b1; 272 rd_ack1 <= 1'b1; 273 end 274 else if(capture_en == 1'b1)begin 275 case(next_state) 276 I2C_WR_ACK1: wr_ack1 <= i2c_sdat; 277 I2C_WR_ACK2: wr_ack2 <= i2c_sdat; 278 I2C_WR_ACK3: wr_ack3 <= i2c_sdat; 279 I2C_WR_ACK4: wr_ack4 <= i2c_sdat; 280 I2C_WR_STOP: begin 281 wr_ack1 <= 1'b1; 282 wr_ack2 <= 1'b1; 283 wr_ack3 <= 1'b1; 284 wr_ack4 <= 1'b1; 285 rd_ack1 <= 1'b1; 286 end 287 I2C_RD_ACK: rd_ack1 <= i2c_sdat; 288 I2C_RD_DATA: i2c_rd_data['d7 - tran_cnt] <= i2c_sdat; 289 I2C_RD_STOP:begin 290 wr_ack1 <= 1'b1; 291 wr_ack2 <= 1'b1; 292 wr_ack3 <= 1'b1; 293 wr_ack4 <= 1'b1; 294 rd_ack1 <= 1'b1; 295 end 296 default:begin 297 i2c_rd_data <= i2c_rd_data; 298 wr_ack1 <= wr_ack1; 299 wr_ack2 <= wr_ack2; 300 wr_ack3 <= wr_ack3; 301 wr_ack4 <= wr_ack4; 302 rd_ack1 <= rd_ack1; 303 end 304 endcase 305 end 306 else begin 307 i2c_rd_data <= i2c_rd_data; 308 wr_ack1 <= wr_ack1; 309 wr_ack2 <= wr_ack2; 310 wr_ack3 <= wr_ack3; 311 wr_ack4 <= wr_ack4; 312 rd_ack1 <= rd_ack1; 313 end 314 end 315 316 //------------------------------------------------------- 317 assign bir_en = (pre_state == I2C_WR_ACK1 || pre_state == I2C_WR_ACK2 || pre_state == I2C_WR_ACK3 || 318 pre_state == I2C_WR_ACK4 || pre_state == I2C_RD_ACK || pre_state == I2C_RD_DATA)? 1'b0: 1'b1; 319 320 assign i2c_sdat = (bir_en == 1'b1)? i2c_sdat_r: 1'bz; 321 322 assign i2c_sclk = i2c_sclk_r; 323 assign i2c_done = (pre_state == I2C_WR_STOP && next_state == I2C_IDLE || 324 pre_state == I2C_RD_STOP && next_state == I2C_IDLE)? 1'b1: 1'b0; 325 326 endmodule
转载请注明出处:NingHeChuan(宁河川)
个人微信订阅号:开源FPGA
如果你想及时收到个人撰写的博文推送,可以扫描左边二维码(或者长按识别二维码)关注个人微信订阅号
知乎ID:NingHeChuan
微博ID:NingHeChuan
原文地址:https://www.cnblogs.com/ninghechuan/p/9534893.html