基于INA226 -16bitADC的 I2C实验

发布时间 2023-09-17 21:29:00作者: MyBooks

模块框图,(按键只用到了一个),注意三态门不在配置模块和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
View Code

 

配置模块代码:

  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
View Code

 

配置的参数文件代码:

 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
View Code

 

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
View Code

 

顶层代码;

  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
View Code

 

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
View Code

 

总结:

1、尽量将功能拆分模块来写,不要想着兼容各种模式,一套搞完,要考虑的跳转条件太多了,废脑子。

2、尽量用modelsim前期仿真,不要等全部写完在仿,太多的信号,看时序也会看晕,尽量完成某个功能可以先仿一段