基于INA226 -16bitADC的 I2C实验
模块框图,(按键只用到了一个),注意三态门不在配置模块和I2C接口中,这样好处配置模块和I2C接口内不存在双向信号,也不存在高阻“z”的赋值, 三态门放在顶层文件中
I2C读写时序,注意数据位宽是8bit,而INA266 数据位宽是16,需要改下,大同小异
设计三个计数器:
1、计数器cnt0 用于产生scl的100Khz
2、计数器cnt1用于对每个状态内scl的个数,比如在start 、ACK、stop状态时只需一个scl,即 x=1; 而在发送地址、数据时需要8个scl, 即 x=8
3、计数器cnt2,分别对每各状态进行标记,有的状态也要重复执行的,就可以利用cnt2 、wr_vld 和 vr_vld三个条件来区分,cnt2走了两套方式:
(1)、一套是在写时,cnt2 计数0~9 (即y=10),注意前提条件是在写状态,即 w_vld =1;
(2)、一套是在读时,cnt2 计数0~12 (即y=13),注意前提条件是在读状态,即 r_vld =1;
写时的状态机跳转如下图
读时的状态机跳转如下图
按键检测代码:
1 module key_module( 2 clk , 3 rst_n , 4 key_in , 5 key_vld 6 ); 7 parameter DATA_W = 20 ; 8 parameter TIME_20MS = 500_000 ; 9 10 input clk ; 11 input rst_n ; 12 input key_in ; 13 output key_vld ; 14 reg key_vld ; 15 reg [DATA_W-1:0] cnt ; 16 wire add_cnt ; 17 wire end_cnt ; 18 reg flag ; 19 reg key_in_ff1 ; 20 reg key_in_ff0 ; 21 22 //检测20ms内持续为低电平,就被认为有按键被按下 23 always @(posedge clk or negedge rst_n)begin 24 if(rst_n==1'b0)begin 25 cnt <= 20'b0; 26 end 27 else if(add_cnt)begin 28 if(end_cnt) 29 cnt <= 20'b0; 30 else 31 cnt <= cnt + 1'b1; 32 end 33 else begin 34 cnt <= 0; 35 end 36 end 37 38 assign add_cnt = flag==1'b0 && (key_in_ff1 != 1'b1); //检测到有抖动,也就是检测有高的脉冲,计数器停止,cnt清0,便于下次重新计数 39 assign end_cnt = add_cnt && cnt == TIME_20MS - 1; 40 41 always @(posedge clk or negedge rst_n)begin 42 if(rst_n==1'b0)begin 43 flag <= 1'b0; 44 end 45 else if(end_cnt)begin //能够完整的计数到20ms结束, 说明已经检测到了按键有被按下 46 flag <= 1'b1; 47 end 48 else if(key_in_ff1 == 1'b1)begin //按键释放后,flag 置0 ,方便下次检测 49 flag <= 1'b0; 50 end 51 end 52 53 54 //打两拍,消除亚稳态 55 always @(posedge clk)begin 56 if(rst_n==1'b0)begin 57 key_in_ff0 <= 0; 58 key_in_ff1 <= 0; 59 end 60 else begin 61 key_in_ff0 <= key_in ; 62 key_in_ff1 <= key_in_ff0; 63 end 64 end 65 66 always @(posedge clk or negedge rst_n)begin 67 if(rst_n==1'b0)begin 68 key_vld <= 0; 69 end 70 else if(end_cnt)begin 71 key_vld <= ~key_in_ff1; 72 end 73 else begin 74 key_vld <= 0; 75 end 76 end 77 endmodule
配置模块代码:
1 module ina226_config( 2 clk , 3 rst_n , 4 config_en , //配置使能,一次只能配置一次,要么写一次,要么读一次 5 6 w_en , //产生写使能 7 r_en , //产生读使能 8 w_data , //待写入的数据,要写2个字节 9 wr_add , //寄存器地址,位宽8bit 10 r_data , //存放读到的数据,要读2个字节 11 r_data_vld , //收到数据有效信号,即表示数据已经收完整了 12 rdy //下游忙标志,空闲时为1,忙时为0 13 ); 14 15 input clk ; 16 input rst_n ; 17 input config_en ; 18 19 output w_en ; 20 output r_en ; 21 output [16-1:0] w_data ; 22 output [ 8-1:0] wr_add ; 23 input [16-1:0] r_data ; 24 25 input r_data_vld ; 26 input rdy ; 27 28 29 reg w_en ; 30 reg r_en ; 31 reg [16-1:0] w_data ; 32 reg [ 8-1:0] wr_add ; 33 34 reg [26-1:0] data ; 35 reg [ 4-1:0] ina226_reg_cnt ; 36 wire add_ina226_reg_cnt ; 37 wire end_ina226_reg_cnt ; 38 39 `include "ina226_para.v" 40 41 always @(posedge clk or negedge rst_n)begin 42 if(!rst_n)begin 43 ina226_reg_cnt <= 0; 44 end 45 else if(add_ina226_reg_cnt)begin 46 if(end_ina226_reg_cnt)begin 47 ina226_reg_cnt <= 0; //不用每次都配置,所以不需要从0开始 48 end 49 else begin 50 ina226_reg_cnt <= ina226_reg_cnt + 1; 51 end 52 end 53 end 54 55 assign add_ina226_reg_cnt = config_en; //直接启动计数一次 56 assign end_ina226_reg_cnt = add_ina226_reg_cnt && ina226_reg_cnt == 15 -1; 57 58 59 //产生写使能 60 always @(posedge clk or negedge rst_n)begin 61 if(!rst_n)begin 62 w_en <= 0; 63 end 64 else if(config_en && data[24] == 1'b1 && rdy)begin 65 w_en <= 1; 66 end 67 else begin 68 w_en <= 0; 69 end 70 end 71 72 //产生读使能 73 always @(posedge clk or negedge rst_n)begin 74 if(!rst_n)begin 75 r_en <= 0; 76 end 77 else if(config_en && data[25] == 1'b1 && rdy)begin 78 r_en <= 1; 79 end 80 else begin 81 r_en <= 0; 82 end 83 end 84 85 //要写的数据锁存 86 always @(posedge clk or negedge rst_n)begin 87 if(!rst_n)begin 88 w_data <= 0; 89 end 90 else if(config_en)begin 91 w_data <= data[15:0]; 92 end 93 end 94 95 //要读、写的寄存器地址锁存 96 always @(posedge clk or negedge rst_n)begin 97 if(!rst_n)begin 98 wr_add <= 0; 99 end 100 else if(config_en)begin 101 wr_add <= data[23:16]; 102 end 103 end 104 105 endmodule
配置的参数文件代码:
1 //bit[2:0]:Mode_110 - Bus Voltage, Continuous 2 //bit[5:3]:shunt转换时间_000 - 140us 3 //bit[8:6]:bus V转换时间 _000- 140us 4 //bit[11:9] averaging mode_010 - 16次 5 //bit[14:12]:100 - 保留 6 //bit[15] - reset bit , 1: reset; 0:normal 7 8 parameter AVG_POINT = 2; //bit[2:0] //0-avg1 ; 1-avg4; 2-avg16; 3-avg64; 4-avg128; 5-avg256; 6-avg512; 7-avg1024 9 parameter SHUNT_CONV_TIME = 1; //bit[5:3] //0-140us; 1-204us; 2-332us; 3-588us; 4-1.1ms; 5-2.116ms; 6-4.156ms; 7-8.244ms 10 parameter BUS_CONV_TIME = 1; //bit[8:6] //0-140us; 1-204us; 2-332us; 3-588us; 4-1.1ms; 5-2.116ms; 6-4.156ms; 7-8.244ms 11 parameter ADC_MODE = 6; //bit[5:3] //1-shut_by_trig; 2-bus_by_trig; 3-shut_and_bus_by_trig; 4-off; 5-shut_continue; 6-bus_continue; 7-shut_and_bus_continue 12 13 14 //寄存器地址 15 parameter INA226_REG_ID = 8'hff; 16 parameter INA226_REG_CONFIG = 8'h00; 17 parameter INA226_REG_SHUNT_VOLTAGE = 8'h01; //获取分流电压 18 parameter INA226_REG_VBUS_VOLTAGE = 8'h02; //获取VBUS电压 19 parameter INA226_REG_CALIBRAION = 8'h05; //获取、设置校准值 20 21 22 always@(*)begin 23 case(ina226_reg_cnt) 24 0 : data = {2'b00,INA226_REG_CONFIG,16'h4406}; //00 无操作 25 1 : data = {2'b01,INA226_REG_CONFIG,16'h4406}; //01 写, 配置工作模式 26 2 : data = {2'b10,INA226_REG_CONFIG,16'h0000}; 27 /* 28 3 : data = {2'b10,INA226_REG_VBUS_VOLTAGE,16'h0000}; 29 4 : data = {2'b10,INA226_REG_CONFIG,16'h0000}; 30 5 : data = {2'b10,INA226_REG_VBUS_VOLTAGE,16'h0000}; 31 6 : data = {2'b10,INA226_REG_CONFIG,16'h0000}; 32 7 : data = {2'b10,INA226_REG_VBUS_VOLTAGE,16'h0000}; 33 8 : data = {2'b10,INA226_REG_CONFIG,16'h0000}; 34 9 : data = {2'b10,INA226_REG_VBUS_VOLTAGE,16'h0000}; 35 10 : data = {2'b10,INA226_REG_CONFIG,16'h0000}; 36 11 : data = {2'b10,INA226_REG_VBUS_VOLTAGE,16'h0000}; 37 12 : data = {2'b10,INA226_REG_CONFIG,16'h0000}; 38 13 : data = {2'b10,INA226_REG_VBUS_VOLTAGE,16'h0000}; 39 14 : data = {2'b10,INA226_REG_CONFIG,16'h0000}; 40 */ 41 default : data = {2'b10,INA226_REG_VBUS_VOLTAGE,16'h0000}; //默认获取电压值 42 endcase 43 end
I2C接口代码:
1 module ina226_I2c( 2 clk , 3 rst_n , 4 w_en , 5 w_data , 6 wr_add , 7 r_en , 8 r_data , 9 r_data_vld , 10 rdy , //空闲位1,忙为0 11 scl , 12 sda_link , 13 sda_out , 14 sda_in 15 ); 16 17 parameter SCL_100K = 9'd500 ; //scl周期时间10_000ns,即100K 18 parameter SLAVE_ADDRESS = 8'h80 ; 19 20 parameter IDLE = 0 ; 21 parameter START = 1 ; 22 parameter SLAVE_ADD = 2 ; 23 parameter ACK = 3 ; 24 parameter REG_ADD = 4 ; 25 parameter REG_DAT_H = 5 ; 26 parameter REG_DAT_L = 6 ; 27 parameter REC_DAT_H = 7 ; 28 parameter REC_DAT_L = 8 ; 29 parameter NOACK = 9 ; 30 parameter STOP = 10 ; 31 32 input clk ; 33 input rst_n ; 34 input w_en ; 35 input [16-1:0] w_data ; 36 input [ 8-1:0] wr_add ; 37 input r_en ; 38 output [16-1:0] r_data ; 39 output r_data_vld ; 40 output rdy ; 41 output scl ; 42 output sda_link ; 43 output sda_out ; 44 input sda_in ; 45 46 reg w_vld ; 47 reg r_vld ; 48 reg scl ; 49 reg sda_link ; 50 reg sda_out ; 51 52 wire IDLE2START ; 53 wire START2SLAVE_ADD ; 54 wire SLAVE_ADD2ACK ; 55 wire ACK2REG_ADD ; 56 wire ACK2REG_DAT_H ; 57 wire ACK2REG_DAT_L ; 58 wire ACK2REC_DAT_H ; 59 wire ACK2REC_DAT_L ; 60 wire ACK2START ; 61 wire ACK2STOP ; 62 wire REG_ADD2ACK ; 63 wire REG_DAT_H2ACK ; 64 wire REG_DAT_L2ACK ; 65 wire REC_DAT_H2ACK ; 66 wire REC_DAT_L2NOACK ; 67 wire NOACK2STOP ; 68 wire STOP2IDLE ; 69 70 reg [ 4-1:0] state_c ; 71 reg [ 4-1:0] state_n ; 72 73 reg [ 9-1:0] cnt0 ; 74 wire add_cnt0 ; 75 wire end_cnt0 ; 76 77 reg [ 4-1:0] cnt1 ; 78 wire add_cnt1 ; 79 wire end_cnt1 ; 80 81 reg [ 4-1:0] cnt2 ; 82 wire add_cnt2 ; 83 wire end_cnt2 ; 84 85 reg [ 4-1:0] x ; 86 reg [ 4-1:0] y ; 87 88 wire SCL_H2L ; 89 wire SCL_L2H ; 90 wire SCL_H_MIDDLE ; 91 wire [ 8-1:0] slave_address_w ; 92 wire [ 8-1:0] slave_address_r ; 93 reg [16-1:0] w_data_temp ; 94 reg [ 8-1:0] wr_add_temp ; 95 reg [16-1:0] r_data_temp ; 96 reg [16-1:0] r_data ; 97 reg r_data_vld ; 98 reg rdy ; 99 100 101 `ifdef ILA_INA226_I2C 102 ila_ina226_i2c u_ina226_i2c ( 103 .clk(clk), // input wire clk 104 105 106 .probe0(w_en), 107 .probe1(w_vld), 108 .probe2(r_en), 109 .probe3(r_vld), 110 .probe4(w_data), 111 .probe5(wr_add), 112 .probe6(r_data_vld), 113 .probe7(r_data), 114 .probe8(sda_link), 115 .probe9(sda_out), 116 .probe10(sda_in), 117 .probe11(scl), 118 .probe12(rdy), 119 .probe13(cnt2), 120 .probe14(state_c) 121 ); 122 `endif 123 124 125 assign slave_address_w = SLAVE_ADDRESS; 126 assign slave_address_r = SLAVE_ADDRESS | 8'h01; 127 128 //收到写使能,拉高w_vld 129 always@(posedge clk or negedge rst_n)begin 130 if(!rst_n)begin 131 w_vld <= 0; 132 end 133 else if(w_en)begin 134 w_vld <= 1; 135 end 136 else if(state_c == STOP && end_cnt0)begin 137 w_vld <= 0; 138 end 139 end 140 141 //收到读使能,拉高r_vld 142 always@(posedge clk or negedge rst_n)begin 143 if(!rst_n)begin 144 r_vld <= 0; 145 end 146 else if(r_en)begin 147 r_vld <= 1; 148 end 149 else if(state_c == STOP && end_cnt0)begin 150 r_vld <= 0; 151 end 152 end 153 154 155 //计时100Khz的时钟 156 always@(posedge clk or negedge rst_n)begin 157 if(!rst_n)begin 158 cnt0 <= 0; 159 end 160 else if(add_cnt0)begin 161 if(end_cnt0)begin 162 cnt0 <= 0; 163 end 164 else begin 165 cnt0 <= cnt0 + 1; 166 end 167 end 168 end 169 170 assign add_cnt0 = w_vld || r_vld; 171 assign end_cnt0 = add_cnt0 && cnt0 == SCL_100K - 1; 172 173 174 //每个状态下的scl周期个数 175 always@(posedge clk or negedge rst_n)begin 176 if(!rst_n)begin 177 cnt1 <= 0; 178 end 179 else if(add_cnt1)begin 180 if(end_cnt1)begin 181 cnt1 <= 0; 182 end 183 else begin 184 cnt1 <= cnt1 + 1; 185 end 186 end 187 end 188 189 assign add_cnt1 = end_cnt0; 190 assign end_cnt1 = add_cnt1 && cnt1 == x - 1; 191 192 always@(*)begin 193 if(state_c == START || state_c == ACK || state_c == NOACK || state_c == STOP)begin 194 x = 1; 195 end 196 else begin 197 x = 8; 198 end 199 end 200 201 202 //产生scl时钟周期 _--_ 203 always @(posedge clk or negedge rst_n)begin 204 if(!rst_n)begin 205 scl <= 1; 206 end 207 else if(add_cnt0 && cnt0 == ((SCL_100K>>1)+ (SCL_100K>>2)) -1 && state_c != STOP)begin //四分之三处拉低 208 scl <= 0; 209 end 210 else if(add_cnt0 && cnt0 == (SCL_100K>>2) -1)begin //四分之一处拉高 211 scl <= 1; 212 end 213 end 214 215 assign SCL_H2L = add_cnt0 && cnt0 == ((SCL_100K>>1)+ (SCL_100K>>2)) -1; //下降沿 216 assign SCL_L2H = add_cnt0 && cnt0 == (SCL_100K>>2) -1; //上升沿 217 assign SCL_H_MIDDLE = add_cnt0 && cnt0 == (SCL_100K>>1) -1; //高电平中间 218 219 //第一段 220 always @(posedge clk or negedge rst_n)begin 221 if(!rst_n)begin 222 state_c <= IDLE; 223 end 224 else begin 225 state_c <= state_n; 226 end 227 end 228 229 //第二段,设计状态跳转 230 always @(*)begin 231 case(state_c) 232 233 IDLE:begin 234 if(IDLE2START)begin 235 state_n = START; 236 end 237 else begin 238 state_n = state_c; 239 end 240 end 241 242 START:begin 243 if(START2SLAVE_ADD)begin 244 state_n = SLAVE_ADD; 245 end 246 else begin 247 state_n = state_c; 248 end 249 end 250 251 SLAVE_ADD:begin 252 if(SLAVE_ADD2ACK)begin 253 state_n = ACK; 254 end 255 else begin 256 state_n = state_c; 257 end 258 end 259 260 ACK:begin 261 if(ACK2REG_ADD)begin 262 state_n = REG_ADD; 263 end 264 else if(ACK2REG_DAT_H)begin 265 state_n = REG_DAT_H; 266 end 267 else if(ACK2REG_DAT_L)begin 268 state_n = REG_DAT_L; 269 end 270 else if(ACK2REC_DAT_H)begin 271 state_n = REC_DAT_H; 272 end 273 else if(ACK2REC_DAT_L)begin 274 state_n = REC_DAT_L; 275 end 276 else if(ACK2START)begin 277 state_n = START; 278 end 279 else if(ACK2STOP)begin 280 state_n = STOP; 281 end 282 else begin 283 state_n = state_c; 284 end 285 end 286 287 REG_ADD:begin 288 if(REG_ADD2ACK)begin 289 state_n = ACK; 290 end 291 else begin 292 state_n = state_c; 293 end 294 end 295 296 REG_DAT_H:begin 297 if(REG_DAT_H2ACK)begin 298 state_n = ACK; 299 end 300 else begin 301 state_n = state_c; 302 end 303 end 304 305 REG_DAT_L:begin 306 if(REG_DAT_L2ACK)begin 307 state_n = ACK; 308 end 309 else begin 310 state_n = state_c; 311 end 312 end 313 314 REC_DAT_H:begin 315 if(REC_DAT_H2ACK)begin 316 state_n = ACK; 317 end 318 else begin 319 state_n = state_c; 320 end 321 end 322 323 REC_DAT_L:begin 324 if(REC_DAT_L2NOACK)begin 325 state_n = NOACK; 326 end 327 else begin 328 state_n = state_c; 329 end 330 end 331 332 NOACK:begin 333 if(NOACK2STOP)begin 334 state_n = STOP; 335 end 336 else begin 337 state_n = state_c; 338 end 339 end 340 341 STOP:begin 342 if(STOP2IDLE)begin 343 state_n = IDLE; 344 end 345 else begin 346 state_n = state_c; 347 end 348 end 349 350 default:begin 351 state_n = IDLE; 352 end 353 354 endcase 355 end 356 357 //第三段,设计条件转移 358 assign IDLE2START = state_c == IDLE && (w_vld || r_vld); 359 assign START2SLAVE_ADD = state_c == START && end_cnt1 && ((w_vld==1 && cnt2==0) || (r_vld==1 && cnt2==0) || (r_vld==1 && cnt2==5)); 360 assign SLAVE_ADD2ACK = state_c == SLAVE_ADD && end_cnt1 && ((w_vld==1 && cnt2==1) || (r_vld==1 && cnt2==1) || (r_vld==1 && cnt2==6)); 361 assign ACK2REG_ADD = state_c == ACK && end_cnt1 && ((w_vld==1 && cnt2==2) || (r_vld==1 && cnt2==2)); 362 assign ACK2REG_DAT_H = state_c == ACK && end_cnt1 && (w_vld==1 && cnt2==4); 363 assign ACK2REG_DAT_L = state_c == ACK && end_cnt1 && (w_vld==1 && cnt2==6); 364 assign ACK2REC_DAT_H = state_c == ACK && end_cnt1 && (r_vld==1 && cnt2==7); 365 assign ACK2REC_DAT_L = state_c == ACK && end_cnt1 && (r_vld==1 && cnt2==9); 366 assign ACK2START = state_c == ACK && end_cnt1 && (r_vld==1 && cnt2==4); 367 assign ACK2STOP = state_c == ACK && end_cnt1 && (w_vld==1 && cnt2==8); 368 assign REG_ADD2ACK = state_c == REG_ADD && end_cnt1 && ((w_vld==1 && cnt2==3) || (r_vld==1 && cnt2==3)); 369 assign REG_DAT_H2ACK = state_c == REG_DAT_H && end_cnt1 && (w_vld==1 && cnt2==5); //写高字节 370 assign REG_DAT_L2ACK = state_c == REG_DAT_L && end_cnt1 && (w_vld==1 && cnt2==7); //写低字节 371 assign REC_DAT_H2ACK = state_c == REC_DAT_H && end_cnt1 && (r_vld==1 && cnt2==8); 372 assign REC_DAT_L2NOACK = state_c == REC_DAT_L && end_cnt1 && (r_vld==1 && cnt2==10); 373 assign NOACK2STOP = state_c == NOACK && end_cnt1 && (r_vld==1 && cnt2==11); 374 assign STOP2IDLE = state_c == STOP && end_cnt1 ; 375 376 //对状态进行标记 377 always@(posedge clk or negedge rst_n)begin 378 if(!rst_n)begin 379 cnt2 <= 0; 380 end 381 else if(add_cnt2)begin 382 if(end_cnt2)begin 383 cnt2 <= 0; 384 end 385 else begin 386 cnt2 <= cnt2 + 1; 387 end 388 end 389 end 390 391 assign add_cnt2 = end_cnt1; 392 assign end_cnt2 = add_cnt2 && cnt2 == y - 1; 393 394 always @(*)begin 395 if(w_vld)begin 396 y = 10; 397 end 398 else begin 399 y = 13; 400 end 401 end 402 403 //sda_link 方向控制 404 always @(posedge clk or negedge rst_n)begin 405 if(!rst_n)begin 406 sda_link <= 1; 407 end 408 else if(w_vld && state_c==ACK && add_cnt0 && cnt0>=0 && cnt0<((SCL_100K>>1)+ (SCL_100K>>2)))begin 409 sda_link <= 0; //关闭三态输出 410 end 411 else if(r_vld && state_c==ACK && add_cnt0 && cnt0>=0 && cnt0<((SCL_100K>>1)+ (SCL_100K>>2)) && cnt2!=9 || state_c == REC_DAT_H || state_c == REC_DAT_L)begin 412 sda_link <= 0; //关闭三态输出 413 end 414 else begin 415 sda_link <= 1; //对外写数据 416 end 417 end 418 419 always @(posedge clk or negedge rst_n)begin 420 if(!rst_n)begin 421 sda_out <= 1; 422 end 423 else if(state_c == START && SCL_H_MIDDLE)begin //在高电平中间拉低sda 424 sda_out <= 0; //产生start信号 425 end 426 else if(state_c == ACK && end_cnt1 && (r_vld==1 && cnt2==4))begin 427 sda_out <= 1; //拉高SDA,准备再次产生start信号 428 end 429 else if(state_c == SLAVE_ADD && ((w_vld==1 && cnt2==1) || (r_vld==1 && cnt2==1)))begin 430 sda_out <= slave_address_w[7-cnt1]; //写设备地址,写操作 431 end 432 else if(state_c == REG_ADD && ((w_vld==1 && cnt2==3) || (r_vld==1 && cnt2==3)))begin 433 sda_out <= wr_add_temp[7-cnt1]; //写寄存器地址 434 end 435 else if(state_c == SLAVE_ADD && (r_vld==1 && cnt2==6))begin 436 sda_out <= slave_address_r[7-cnt1]; //写设备地址,读操作 437 end 438 else if(state_c == REG_DAT_H && (w_vld==1 && cnt2==5))begin 439 sda_out <= w_data_temp[15-cnt1]; //写高字节 440 end 441 else if(state_c == REG_DAT_L && (w_vld==1 && cnt2==7))begin 442 sda_out <= w_data_temp[7-cnt1]; //写低字节 443 end 444 else if(state_c == ACK && end_cnt0 && (w_vld==1 && cnt2==8))begin 445 sda_out <= 0; //在产生stop之前,先将sda信号拉低 446 end 447 else if(state_c == ACK && (r_vld==1 && cnt2==9))begin 448 sda_out <= 0; //在读数据之间的ACK,需主机发送0,回应器件设备 449 end 450 else if(state_c == NOACK)begin 451 sda_out <= 0; //在产生stop之前,先将sda信号拉低 452 end 453 else if(state_c == STOP && SCL_H_MIDDLE)begin 454 sda_out <= 1; 455 end 456 end 457 458 //先将数据锁存 459 always @(posedge clk or negedge rst_n)begin 460 if(!rst_n)begin 461 w_data_temp <= 0; 462 end 463 else if(w_en)begin 464 w_data_temp <= w_data; 465 end 466 end 467 468 //先将地址锁存 469 always @(posedge clk or negedge rst_n)begin 470 if(!rst_n)begin 471 wr_add_temp <= 0; 472 end 473 else if(w_en ||r_en)begin 474 wr_add_temp <= wr_add; 475 end 476 end 477 478 479 //读数据 480 always @(posedge clk or negedge rst_n)begin 481 if(!rst_n)begin 482 r_data_temp <= 0; 483 end 484 else if(state_c == REC_DAT_H && SCL_H_MIDDLE)begin 485 r_data_temp[15-cnt1] <= sda_in; 486 end 487 else if(state_c == REC_DAT_L && SCL_H_MIDDLE)begin 488 r_data_temp[7-cnt1] <= sda_in; 489 end 490 end 491 492 //保存数据,和r_data_vld保持同步 493 always @(posedge clk or negedge rst_n)begin 494 if(!rst_n)begin 495 r_data <= 0; 496 end 497 else if(state_c == REC_DAT_L && end_cnt1 && wr_add == 8'h02)begin 498 r_data <= r_data_temp * 125 / 100; //转换电压,单位mv 499 end 500 else if(state_c == REC_DAT_L && end_cnt1)begin 501 r_data <= r_data_temp; 502 end 503 end 504 505 //接收完两个字节后,需产生一个有效标志位 506 always @(posedge clk or negedge rst_n)begin 507 if(!rst_n)begin 508 r_data_vld <= 0; 509 end 510 else if(state_c == REC_DAT_L && end_cnt1)begin 511 r_data_vld <= 1; 512 end 513 else begin 514 r_data_vld <= 0; 515 end 516 end 517 518 //产生rdy 519 //只要看到写或读标志信号就立刻产生rdy,所以加上 w_en 和 r_en 520 always @(*)begin 521 if(w_vld || r_vld || w_en || r_en)begin 522 rdy = 0; 523 end 524 else begin 525 rdy = 1; 526 end 527 end 528 529 endmodule
顶层代码;
1 `timescale 1ns / 1ps 2 ////////////////////////////////////////////////////////////////////////////////// 3 // Company: 4 // Engineer: 5 // 6 // Create Date: 2023/09/10 11:40:45 7 // Design Name: 8 // Module Name: dac_pwm_ldo 9 // Project Name: 10 // Target Devices: 11 // Tool Versions: 12 // Description: 13 // 14 // Dependencies: 15 // 16 // Revision: 17 // Revision 0.01 - File Created 18 // Additional Comments: 19 // 20 ////////////////////////////////////////////////////////////////////////////////// 21 22 23 module dac_pwm_ldo( 24 sys_clk_p , 25 sys_clk_n , 26 sys_rst_n , 27 led_out , 28 scl , 29 sda , 30 key_in , 31 pwm 32 ); 33 34 parameter T_20us = 10'd1000 ; //pwm 50K 35 parameter PWM_DUTY= 9'd600 ; 36 parameter T_200ms = 14'd10_000 ; 37 38 input sys_clk_p ; 39 input sys_clk_n ; 40 input sys_rst_n ; 41 output [3-1 : 0] led_out ; 42 inout sda ; 43 output scl ; 44 input key_in ; 45 output pwm ; 46 47 reg pwm ; 48 reg [ 3-1:0] led_out ; 49 50 reg [10-1:0] cnt0 ; 51 wire add_cnt0 ; 52 wire end_cnt0 ; 53 54 reg [14-1:0] cnt1 ; 55 wire add_cnt1 ; 56 wire end_cnt1 ; 57 wire cnt0_mid_flag ; 58 59 wire clk_100m ; 60 wire locked ; 61 62 wire sda_in ; 63 wire key_vld ; 64 65 wire w_en ; 66 wire r_en ; 67 wire [16-1:0] w_data ; 68 wire [ 8-1:0] wr_add ; 69 wire [16-1:0] r_data ; 70 wire r_data_vld ; 71 wire rdy ; 72 wire sda_link ; 73 wire sda_out ; 74 wire scl ; 75 76 assign sda = sda_link ? sda_out : 1'bz ; 77 assign sda_in = sda; 78 79 clk_wiz_0 instance_name 80 ( 81 // Clock out ports 82 .clk_50m(clk_50m), // output clk_50m 83 .clk_100m(clk_100m), // output clk_100m 84 // Status and control signals 85 .resetn(sys_rst_n), // input resetn 86 .locked(locked), // output locked 87 // Clock in ports 88 .clk_in1_p(sys_clk_p), // input clk_in1_p 89 .clk_in1_n(sys_clk_n)); // input clk_in1_n 90 91 `ifdef ILA_LED_PWM_LDO 92 ila_led_pwd_ldo your_instance_name ( 93 .clk(clk_100m), // input wire clk 94 95 96 .probe0(key_vld), 97 .probe1(w_en), 98 .probe2(r_en), 99 .probe3(rdy), 100 .probe4(w_data), 101 .probe5(wr_add), 102 .probe6(r_data_vld), 103 .probe7(r_data), 104 .probe8(sda_link), 105 .probe9(sda_out), 106 .probe10(sda_in), 107 .probe11(sda), 108 .probe12(scl), 109 .probe13(pwm) 110 ); 111 `endif 112 113 114 key_module u0( 115 .clk (clk_50m ) , 116 .rst_n (sys_rst_n ) , 117 .key_in (key_in ) , 118 .key_vld (key_vld ) 119 ); 120 121 ina226_config u1( 122 .clk (clk_50m ) , 123 .rst_n (sys_rst_n ) , 124 .config_en (key_vld ) , //配置使能,一次只能配置一次,要么写一次,要么读一次 125 126 .w_en (w_en ) , //产生写使能 127 .r_en (r_en ) , //产生读使能 128 .w_data (w_data ) , //待写入的数据,要写2个字节 129 .wr_add (wr_add ) , //寄存器地址,位宽8bit 130 .r_data (r_data ) , //存放读到的数据,要读2个字节 131 .r_data_vld (r_data_vld ) , //收到数据有效信号,即表示数据已经收完整了 132 .rdy (rdy ) //下游忙标志,空闲时为1,忙时为0 133 ); 134 135 ina226_I2c u2( 136 .clk (clk_50m ) , 137 .rst_n (sys_rst_n ) , 138 .w_en (w_en ) , 139 .w_data (w_data ) , 140 .wr_add (wr_add ) , 141 .r_en (r_en ) , 142 .r_data (r_data ) , 143 .r_data_vld (r_data_vld ) , 144 .rdy (rdy ) , //空闲位1,忙为0 145 .scl (scl ) , 146 .sda_link (sda_link ) , 147 .sda_out (sda_out ) , 148 .sda_in (sda_in ) 149 ); 150 151 152 always@(posedge clk_50m or negedge sys_rst_n)begin 153 if(!sys_rst_n)begin 154 cnt0 <= 0; 155 end 156 else if(add_cnt0)begin 157 if(end_cnt0)begin 158 cnt0 <= 0; 159 end 160 else begin 161 cnt0 <= cnt0 + 1'b1; 162 end 163 end 164 end 165 166 assign add_cnt0 = 1; 167 assign end_cnt0 = add_cnt0 && cnt0 == T_20us - 1; 168 169 170 always@(posedge clk_50m or negedge sys_rst_n)begin 171 if(!sys_rst_n)begin 172 cnt1 <= 0; 173 end 174 else if(add_cnt1)begin 175 if(end_cnt1)begin 176 cnt1 <= 0; 177 end 178 else begin 179 cnt1 <= cnt1 + 1'b1; 180 end 181 end 182 end 183 184 assign add_cnt1 = end_cnt0; 185 assign end_cnt1 = add_cnt1 && cnt1 == T_200ms - 1; 186 187 188 assign cnt0_mid_flag = add_cnt0 && cnt0 == PWM_DUTY-1; //淇敼PWM_DUTY锛屽彲璋冩暣鍗犵┖姣? 189 always@(posedge clk_50m or negedge sys_rst_n)begin 190 if(!sys_rst_n)begin 191 pwm <= 1'b0; 192 end 193 else if(add_cnt0 && end_cnt0)begin 194 pwm <= 1'b1; 195 end 196 else if(cnt0_mid_flag)begin 197 pwm <= 1'b0; 198 end 199 end 200 201 202 //led flash 203 always@(posedge clk_50m or negedge sys_rst_n)begin 204 if(!sys_rst_n)begin 205 led_out <= 3'b001; 206 end 207 else if(end_cnt1)begin 208 led_out <= {led_out[1:0], led_out[2]}; 209 end 210 end 211 212 213 endmodule
ILA抓取到的波形
写时序:
读
读ADC检测到的电压 0.91V
读ADC检测到的电压1.821V
I2C时序先用modelsim仿真,直接上vivado 太废时间了,时间都浪费在软件是编译、综合、布局布线、生成bit文件上了,
测试文件:
1 `timescale 1ns/1ns 2 3 module sim(); 4 5 parameter CYCLE_CLK = 20; 6 7 reg clk ; 8 reg rst_n ; 9 10 reg w_en ; 11 reg [16-1:0] w_data ; 12 reg [ 8-1:0] wr_add ; 13 reg r_en ; 14 wire [16-1:0] r_data ; 15 wire r_data_vld ; 16 wire rdy ; 17 wire scl ; 18 wire sda_link ; 19 wire sda_out ; 20 wire sda_in ; 21 22 initial begin 23 clk = 0; 24 forever begin 25 #(CYCLE_CLK/2); 26 clk = ~clk; 27 end 28 end 29 30 31 initial begin 32 rst_n = 1; 33 #2; 34 rst_n = 0; 35 #(3*CYCLE_CLK); 36 rst_n = 1; 37 end 38 39 40 initial begin 41 #1; 42 w_en = 0; 43 r_en = 0; 44 w_data = 0; 45 wr_add = 0; 46 47 #(50*CYCLE_CLK); 48 w_en = 1; 49 r_en = 0; 50 w_data = 16'ha4AA; 51 wr_add = 8'h55; 52 #(1*CYCLE_CLK); 53 w_en = 0; 54 r_en = 0; 55 w_data = 0; 56 wr_add = 0; 57 58 #(20000*CYCLE_CLK); 59 w_en = 0; 60 r_en = 1; 61 w_data = 0; 62 wr_add = 8'h4a; 63 #(1*CYCLE_CLK); 64 w_en = 0; 65 r_en = 0; 66 w_data = 0; 67 wr_add = 0; 68 69 end 70 71 ina226_I2c U1( 72 .clk (clk ) , 73 .rst_n (rst_n ) , 74 .w_en (w_en ) , 75 .w_data (w_data ) , 76 .wr_add (wr_add ) , 77 .r_en (r_en ) , 78 .r_data (r_data ) , 79 .r_data_vld (r_data_vld ) , 80 .rdy (rdy ) , 81 .scl (scl ) , 82 .sda_link (sda_link ) , 83 .sda_out (sda_out ) , 84 .sda_in (sda_in ) 85 ); 86 87 endmodule
总结:
1、尽量将功能拆分模块来写,不要想着兼容各种模式,一套搞完,要考虑的跳转条件太多了,废脑子。
2、尽量用modelsim前期仿真,不要等全部写完在仿,太多的信号,看时序也会看晕,尽量完成某个功能可以先仿一段