在I2C在完成相关的读写操作后,就要向设备发送STOP操作,STOP作为一个独立模块,是因为在I2c总线发生错误时,也需要发送STOP,不仅仅是正常的I2c读写结束时。STOP 信号是在SCL信号为高电平期间,SDA信号由低变高。这个部分就是控制器对设备的STOP操作。在I2C PHY层 Verilog程序结构化设计 文章中的op_cmd[1:0] = 2’b11 来实现。如图1:

图1
1.I2C读数据部分控制器程序设计
-
接口要求:
- 复位信号,系统时钟,系统时钟分频(由于I2C不同速度匹配)
- input rst,
- input sys_clk,
- input clk_en, clk_en_half,
- 命令信号
- input [1:0] op_cmd, // 2’b11 means controller send STOP
- input op_cmd_en, // op_cmd enable , sys_clk single pulse
- I2C 总线的内部信号:由于inout信号一般只在端口使用,因此在FPGA的内部逻辑(内部模块)将会把inout(双向口)变换成input, output类型进行传递
- input sda_i,
- output sda_o,
- output sda_t,
- input scl_i,
- output scl_o,
- output scl_t,
- 握手信号:op_cmd_done 操作完成、从设备应答。当op_cmd_done产生,从设备应答后(dev_ack)同时送给上层模块。op_cmd_done 保持一个sys_clk 时钟宽度。
- output op_cmd_done, // operation finished
- 复位信号,系统时钟,系统时钟分频(由于I2C不同速度匹配)
-
主体程序设计
该部分程序实现的主体内容如下:初始化,在IDLE状态下检测启动请求(op_cmd = 2’b11),在SCL为高电平期间等待半个时钟周期再将SDA由低变高。在STOP之后,总线处于idle状态,即控制器和设备都不去控制SDA,SCL, SDA,SCL 都是处于高阻状态。
程序的流程图如下:

图2
-
程序代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
`timescale 1ns / 1ps module i2c_stop( input sys_clk, input [1:0] op_cmd, input op_cmd_en, output reg op_cmd_done, input clk_en, input clk_en_half, output reg sda_t, output sda_o, input sda_i, output reg scl_t, output scl_o, input scl_i, input rst ); /* op_cmd[1:0] : 00 send start + address + wr/rd (data_in) 01 send data 10 receive data (data_out) 11 send stop flag */ assign scl_o = 1'b0; assign sda_o = 1'b0; reg [7:0] i2c_st; reg [2:0] sft_count; reg [7:0] i2c_wr_data; //将需要写的数据存入本地寄存器中 always@(posedge sys_clk or posedge rst) if(rst) begin op_cmd_done <= 0; scl_t <= 1'b0; sda_t <= 1'b1; sft_count <= 7; i2c_wr_data <= 0; i2c_st <= 0; end else case(i2c_st) 0: begin sft_count <= 7; op_cmd_done <= 1'b0; scl_t <= scl_i; sda_t <= sda_i; if({op_cmd_en,op_cmd[1:0]} == 3'b111) i2c_st <= 1; // stop flag end 1: //stop flag begin sda_t <= 1'b0; if(clk_en) begin scl_t <= 1'b1; i2c_st <= 2; end end 2: begin if(clk_en) begin sda_t <= 1'b1; i2c_st <= 3; end end 3: begin if(clk_en) begin sda_t <= 1'b1; scl_t <= 1'b1; op_cmd_done <= 1'b1; i2c_st <= 0; end end default:i2c_st <= 0; endcase endmodule |
- iic_phy 模块代码
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
`timescale 1ns / 1ps module iic_phy( input sys_clk, input [1:0] op_cmd, input op_cmd_en, output reg op_cmd_done, output reg dev_ack, input master_ack, input [7:0] data_in, output [7:0] data_out, input clk_en, input clk_en_half, output reg sda_t, output sda_o, input sda_i, output reg scl_t, output scl_o, input scl_i, input rst ); /* op_cmd[1:0] : 00 send start + address + wr/rd (data_in) 01 send data 10 receive data (data_out) 11 send stop flag */ wire dev_ack_addr; wire dev_ack_wr; always @(*) begin case(op_cmd) 2'b00: dev_ack = dev_ack_addr; 2'b01: dev_ack = dev_ack_wr; default: ; endcase end wire op_cmd_done_addr; wire op_cmd_done_wr; wire op_cmd_done_read; wire op_cmd_done_stop; always @(*) begin case(op_cmd) 2'b00: op_cmd_done = op_cmd_done_addr; 2'b01: op_cmd_done = op_cmd_done_wr; 2'b10: op_cmd_done = op_cmd_done_read; 2'b11: op_cmd_done = op_cmd_done_stop; endcase end wire sda_t_addr; wire sda_t_wr; wire sda_t_read; wire sda_t_stop; wire scl_t_addr; wire scl_t_wr; wire scl_t_read; wire scl_t_stop; always @(*) begin case(op_cmd) 2'b00: begin sda_t = sda_t_addr; scl_t = scl_t_addr; end 2'b01: begin sda_t = sda_t_wr; scl_t = scl_t_wr; end 2'b10: begin sda_t = sda_t_read; scl_t = scl_t_read; end 2'b11: begin sda_t = sda_t_stop; scl_t = scl_t_stop; end endcase end i2c_addr i2c_addr_inst ( .sys_clk (sys_clk), .op_cmd (op_cmd), .op_cmd_en (op_cmd_en), .op_cmd_done (op_cmd_done_addr), .dev_ack (dev_ack_addr), .data_in (data_in), .clk_en (clk_en), .clk_en_half (clk_en_half), .sda_t (sda_t_addr), .sda_i (sda_i), .scl_t (scl_t_addr), .scl_i (scl_i), .rst (rst) ); i2c_write i2c_write_inst ( .sys_clk (sys_clk), .op_cmd (op_cmd), .op_cmd_en (op_cmd_en), .op_cmd_done (op_cmd_done_wr), .dev_ack (dev_ack_wr), .data_in (data_in), .clk_en (clk_en), .clk_en_half (clk_en_half), .sda_t (sda_t_wr), .sda_i (sda_i), .scl_t (scl_t_wr), .scl_i (scl_i), .rst (rst) ); i2c_read i2c_read_inst ( .sys_clk (sys_clk), .op_cmd (op_cmd), .op_cmd_en (op_cmd_en), .op_cmd_done (op_cmd_done_read), .master_ack (master_ack), .data_out (data_out), .clk_en (clk_en), .clk_en_half (clk_en_half), .sda_t (sda_t_read), .sda_i (sda_i), .scl_t (scl_t_read), .scl_i (scl_i), .rst (rst) ); i2c_stop i2c_stop_inst ( .sys_clk (sys_clk), .op_cmd (op_cmd), .op_cmd_en (op_cmd_en), .op_cmd_done (op_cmd_done_stop), .clk_en (clk_en), .clk_en_half (clk_en_half), .sda_t (sda_t_stop), .sda_i (sda_i), .scl_t (scl_t_stop), .scl_i (scl_i), .rst (rst) ); assign sda_o = 1'b0; assign scl_o = 1'b0; endmodule |
- 将设计的模块例化,并组装到 iic_engine的模块中,这样就可以使用SCl_en模块,充分利用以设计的成果,逐步组装成程序代码及接口如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
`timescale 1ns / 1ps module iic_engine # ( parameter DIV_ADDR = 32'hFFFF_0001, parameter DEV_ADDR = 7'b101_0000, parameter CLK_F = 100_000_000, parameter SPEED = 100_000 ) ( input sys_clk, inout SDA, inout SCL, input [31:0] div_addr, input [31:0] div_in, input div_en, input div_rd, output [31:0] div_dout, input [1:0] op_cmd, input op_cmd_en, output op_cmd_done, output dev_ack, input master_ack, input [7:0] data_in, output [7:0] data_out, output i2c_error, input rst ); wire sda_i; wire sda_o; wire sda_t; wire scl_o; wire scl_t; wire scl_i; assign SDA = sda_t ? 1'bz: sda_o; assign sda_i = SDA; assign SCL = scl_t ? 1'bz: scl_o; assign scl_i = SCL; wire clk_en; wire clk_en_half; //=========================== i2c clock baudtick scl_en # ( .DIV_ADDR ( DIV_ADDR ), .CLK_F ( CLK_F ), .SPEED ( SPEED ) ) scl_en_inst ( .sys_clk ( sys_clk ), .div_addr ( div_addr ), .div_in ( div_in ), .div_en ( div_en ), .clk_en ( clk_en ), .div_rd ( div_rd ), .div_dout ( div_dout ), .clk_en_half ( clk_en_half ), .rst ( rst ) ); //===============================iic phy iic_phy iic_phy_inst( .sys_clk (sys_clk), .op_cmd ( op_cmd ), .op_cmd_en ( op_cmd_en ), .op_cmd_done ( op_cmd_done ), .dev_ack ( dev_ack ), .master_ack ( master_ack ), .data_in ( data_in ), .data_out ( data_out ), .clk_en ( clk_en ), .clk_en_half ( clk_en_half ), .sda_t ( sda_t ), .sda_o ( sda_o ), .sda_i ( sda_i ), .scl_t ( scl_t ), .scl_o ( scl_o ), .scl_i ( scl_i ), .rst ( rst ) ); endmodule |
-
仿真程序设计:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
`timescale 1ns / 1ps module tb_dev_stop(); parameter PERIOD = 10 ; parameter FREQUANCY = 100000000; //100M parameter SPEED = 100000; // 100K parameter SPEED1 = 400000; //400K parameter DEV_ADDR = 7'b101_0001; reg inclk; always #( PERIOD / 2 ) inclk = ~inclk; //133M wire sys_clk = inclk; reg rst; // reg [ 31: 0 ] div_in; //for baudrate register wire [ 31: 0 ] div_addr = 32'hFFFF_0001; reg div_en = 0; reg [1:0] op_cmd = 0; reg op_cmd_en = 0; wire op_cmd_done; wire dev_ack; reg master_ack = 0; reg [7:0] data_in = 0; wire [7:0] data_out; reg i2c_error = 0; initial begin rst = 1'b1; inclk = 0; #50 rst = 1'b0; end reg [7:0] dev_data = 0; reg dev_data_valid = 0; reg [3:0] st = 0; reg [31:0] cnt = 0; always@( posedge sys_clk or posedge rst ) if ( rst ) begin master_ack <= 0; i2c_error <= 0; cnt <= 0; st <= 0; op_cmd_en <= 1'b0; op_cmd <= 0; end else begin case ( st ) 0: begin master_ack <= 0; dev_data <= 0; dev_data_valid <= 0; i2c_error <= 0; op_cmd <= 2'b00; op_cmd_en <= 0; if(cnt == 5000) begin cnt <= 0; st <= 1; end else cnt <= cnt + 1; end 1: begin data_in <= {DEV_ADDR, 1'b0}; op_cmd <= 2'b00; // send device_addr + write op_cmd_en <= 1; st <= 2; end 2: begin op_cmd_en <= 0; if(op_cmd_done) begin if(dev_ack == 1) begin i2c_error <= 1; st <= 13; end else begin data_in <= 8'h00; // write memory address 8'h00 op_cmd <= 2'b01; op_cmd_en <= 1; cnt <= 0; st <= 3; end end end 3: begin op_cmd_en <= 0; if(op_cmd_done) begin if(dev_ack == 1) begin i2c_error <= 1; st <= 13; end else begin data_in <= data_in + 1; // wirte data to device op_cmd <= 2'b01; op_cmd_en <= 1; cnt <= cnt + 1; if(cnt == 7) st <= 4; end end end 4: begin cnt <= 0; op_cmd_en <= 0; if(op_cmd_done) begin if(dev_ack == 1) begin i2c_error <= 1; st <= 13; end else begin op_cmd <= 2'b11; //send STOP command op_cmd_en <= 1; st <= 5; end end end 5: begin cnt <= 0; op_cmd_en <= 0; if(op_cmd_done) st <= 6; end 6: // delay 5ms according to EEPROM spec begin if(cnt == 5_000_00) begin cnt <= 0; st <= 7; end else cnt <= cnt + 1; end 7: begin cnt <= 0; op_cmd_en <= 0; data_in <= {DEV_ADDR, 1'b0}; op_cmd <= 2'b00; // send START + dev_addr + write op_cmd_en <= 1; st <= 8; end 8: begin op_cmd_en <= 0; if(op_cmd_done) begin if(dev_ack == 1) begin i2c_error <= 1; st <= 13; end else begin data_in <= 8'h00; op_cmd <= 2'b01; // send memory address 8'h00 op_cmd_en <= 1; st <= 9; end end end 9: begin op_cmd_en <= 0; if(op_cmd_done) begin if(dev_ack == 1) begin i2c_error <= 1; st <= 13; end else begin data_in <= {DEV_ADDR, 1'b1}; op_cmd <= 2'b00; // send RESTART + dev_addr + read op_cmd_en <= 1; st <= 10; end end end 10: begin op_cmd_en <= 0; if(op_cmd_done) begin if(dev_ack == 1) begin i2c_error <= 1; st <= 13; end else begin op_cmd <= 2'b10; // send read op_cmd_en <= 1; master_ack <= 0; cnt <= cnt + 1; st <= 11; end end end 11: begin op_cmd_en <= 0; dev_data_valid <= 0; if(op_cmd_done) begin dev_data <= data_out; dev_data_valid <= 1; op_cmd <= 2'b10; //send read op_cmd_en <= 1; cnt <= cnt + 1; if(cnt == 7) begin master_ack <= 1; // send NACK for the last data st <= 12; end end end 12: begin dev_data_valid <= 0; cnt <= 0; op_cmd_en <= 0; if(op_cmd_done) begin dev_data <= data_out; dev_data_valid <= 1; st <= 13; end end 13: // STOP command begin dev_data_valid <= 0; op_cmd <= 2'b11; // send STOP command op_cmd_en <= 1; st <= 14; end 14: begin cnt <= 0; op_cmd_en <= 0; if(op_cmd_done) begin st <= 15; end end 15: begin if(cnt == 5000) begin st <= 15; $stop; end else cnt <= cnt + 1; end default: st <= 0; endcase end wire SDA; wire SCL; iic_engine #( .CLK_F ( FREQUANCY ) , //default with 100Mhz .SPEED ( SPEED ) //default speed 100k ) iic_engine_inst ( .sys_clk ( sys_clk ), .div_addr ( div_addr ), .div_in ( div_in ), .div_en ( div_en ), .op_cmd ( op_cmd ), .op_cmd_en ( op_cmd_en ), .op_cmd_done( op_cmd_done ), .dev_ack ( dev_ack ), .master_ack ( master_ack ), .data_in ( data_in ), .data_out ( data_out ), .SDA ( SDA ), .SCL ( SCL ), .rst ( rst ) ); PULLUP SDA_inst ( .O(SDA) ); PULLUP SCL_inst ( .O(SCL) ); AT24C02D # (.SLAVE_ADDRESS(DEV_ADDR)) AT24C02D_inst ( .SDA (SDA), .SCL (SCL), .WP (0) ); endmodule |
-
仿真结果
仿真波形如图3

图3
从图3可以看出:
1)start + dev_addr +wr。设备回ack
2)send memory addr, 设备会ack
3)restart + dev_addr +rd, 设备回ack
4)receive data, 控制器回ack
5) 。。。。
6)receive the last data ,控制器回nack
7 )send STOP command
在等待设备回ack期间,如果设备回nack, I2c总线发生错误(设备无应答)。这时,控制器需要停止总线(发送STOP),将SDA,SCL 线变为高阻。之后其他的I2c 命令才能正常执行。