I2C在芯片的配置中應用仍是不少的,好比攝像頭、VGA轉HDMI轉換芯片,以前博主分享過一篇I2C協議的基礎學習IIC協議學習筆記,這篇就使用Verilog來實現EEPROM的讀寫,進行一個簡單的I2C實戰應用。html
我使用的這個芯片是AT24C32,它手冊上還有一種AT24C64,其實操做都是同樣的,只是內存大小不一樣,AT24C32是32k(4096x8)AT24C64是64k(9=8192x8),數組
SCL設置爲頻率200Khz微信
SCL clk posedge data輸入EEPROMpost
SCL clk negedge data輸出EEPROM學習
SDA 雙向Pinurl
A2,A1,A0 Device Addr default all 0,只操做一片可懸空引腳。spa
WP 接地正常讀寫,WP接Vcc寫操做被禁止設計
字節尋址地址,是由12(AT24C32)或13bit(AT24C64)的地址組成,須要操做16位字地址高3或4位忽略便可。3d
Device Address 8’hA0寫器件地址,8’hA1讀器件地址code
寫字節操做
隨機讀字節操做
我這個芯片是雙字節數據地址,因此在寫數據地址時要寫兩次,先是高字節後是低字節。
開始結束標誌
這個I2C總線的時序是一致的。
EEPROM應答
輸出應答scl的第九個週期給出,低電平應答。若是主機沒有收到應答,須要從新配置。
數據傳輸時序
sda數據線在scl時鐘的降低沿中間變化,能夠避免產生誤觸開始結束標誌。
i2c_start爲高電平有效,傳輸完成後會產生一個i2c_done結束標誌,表示操做完成。
(1)產生start位
(2)傳送器件地址ID_Address,器件地址的最後一位爲數據的傳輸方向位,R/W,低電平0表示主機往從機寫數據(W),1表示主機從從機讀數據(R)。這裏按照手冊給出的操做圖,應該是W即低電平。ACK應答,應答是從機發送給主機的應答,這裏不用管。
(3)傳送寫入器件寄存器地址,即數據要寫入的位置。一樣ACK應答不用管。
(4)傳送要寫入的數據。ACK應答不用管。
(5)產生stop信號。
(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