基于FPGA的IIC驱动设计

一、IIC基本概念

    IIC 总线(I2C busInter-IC bus)是一个双向的两线连续总线,提供集成电路(ICs)之间的通信线路。IIC总线是一种串行扩展技术,最早由Philips公司推出,广泛应用于电视,录像机和音频设备,IIC 线的意思是完成集成电路或功能单元之间信息交换的规范或协议Philips 公司推出的 IIC 总线采用一条数据线(SDA),加一条时钟线(SCL)来完成数据的传输及外围器件的扩展。 如图1所示:

图1

二、主机和从机的概念

    主机就是负责整个系统的任务协调与分配,从机一般是通过接收主机的指令从而完成某些特定的任务,主机和从机之间通过总线连接,进行数据通讯。

 

三、传输速率

    IIC 总线数据传输速率在标准模式下可达 100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/sIIC总线上的主设备与从设备之间以字节(8)为单位进行双向的数据传输,IIC协议为半双工协议。 

四、器件地址的概念

   每个 IIC 器件都有一个器件地址,有的器件地址在出厂时地址就设置好了,用户不可以更改(例如 OV7670 器件地址为固定的 0x42),有的确定了几位,剩下几位由硬件确定(比如常见的 IIC 接口的 EEPROM 存储器,留有 3 个控制地 址的引脚,由用户自己在硬件设计时确定)。

 

五、总体时序简介

图2

    在IIC器件开始通信(传输数据)之前,串行时钟线SCL和串行数据线SDA线由于上拉的原 因处于高电平状态,此时IIC总线处于空闲状态。如果主机(此处指FPGA)想开始传输数据,只需在SCL为高电平时将SDA线拉低,产生一个起始信号,从机检测到起始信号后,准备接收数 据,当数据传输完成,主机只需产生一个停止信号,告诉从机数据传输结束,停止信号的产生 是在SCL为高电平时, SDA从低电平跳变到高电平,从机检测到停止信号后,停止接收数据。 IiC 整体时序如图2所示,起始信号之前为空闲状态,起始信号之后到停止信号之前的这一段为数据传 输状态,主机可以向从机写数据, 也可以读取从机输出的数据,数据的传输由双向数据线( SDA) 完成。停止信号产生后,总线再次处于空闲状态。

    由于IIC总线协议启动和停止信号都是在SCL高电平期间发生跳变的(当不发送或接收数据时,SCL一直处于高电平),这就决定了我们其他数据的改变只能发生在SCL低电平期间,在SCL为高电平期间,数据必须保持稳定。即在SCL低电平时改变数据,在SCL高电平时采集数据。

 

六、三态电路设计

    主机(FPGA)给从机发送信号时,如传输启动信号,停止信号,数据位时,sda为输出;从机给主机发送信号,如ACK,读取数据时,sda充当输入,如图3所示;

 图3


七、单字节写时序

图4 单字节写时序

    由时序图可以看出,如果要向EEPROM 24C64中写入1字节,那么必须经过以下步骤。

        (1)发送启动信号;

        (2)发送器件地址(CONTROL BYTE);

        (3)接收并检测EEPROM发送过来的应答信号(ACK);

        (4)发送高字节地址位(ADDRESS HIGH BYTE);

        (5)接收并检测EEPROM发送过来的应答信号(ACK);

        (6)发送低字节地址位(ADDRESS LOW BYTE);

        (7)接收并检测EEPROM发送过来的应答信号(ACK);

        (8)发送8bit有效数据(DATA);

        (9)接收并检测EEPROM发送过来的应答信号(ACK);

        (10)发送停止信号(STOP);

八、单字节读时序

图5 单字节读时序

 

    由时序图可以看出,如果要向EEPROM 24C64中读出1字节,那么必须经过以下步骤。

        (1)发送启动信号;

        (2)发送器件地址1010_0000(CONTROL BYTE);

        (3)接收并检测EEPROM发送过来的应答信号(ACK);

        (4)发送高字节地址位(ADDRESS HIGH BYTE);

        (5)接收并检测EEPROM发送过来的应答信号(ACK);

        (6)发送低字节地址位(ADDRESS LOW BYTE);

        (7)接收并检测EEPROM发送过来的应答信号(ACK);

        (8)发送启动信号;

        (9)发送器件地址1010_0001(CONTROL BYTE);

        (10)接收并检测EEPROM发送过来的应答信号(ACK);

        (11)读取1字节的数据(DATA);

        (12)发送NO_ACK信号;

        (13)发送停止信号(STOP);

 

九、时序设计

   (1)启动信号:在SCL为高电平期间,sda出现从高到低的跳变沿,代表启动信号;

   (2)器件地址:EEPROM 24C64 的写时序时的器件地址为:8'b1010_0000,读时序时的器件地址为:8'b1010_0001;

   (3)高、低位的字地址:由于24C64有64bit的存储空间,所以我们需要13位的地址位宽才能寻址所有的存储空间,由于IIC协议规定只能以字节形式写入,所以必须将13位的地址扩展为16位的地址,分为高8位和低8位,多出来的前三位填充任意数据即可。

   (4)停止信号:在SCL为高电平期间,sda出现从低到高的跳变沿,代表停止信号;

   (5)应答信号:应答信号是由数据接收方发出的,当SCL为高电平期间,如果监测到SDA为低电平,则说明有应答信号;

   (6)非应答信号:非应答信号也是由数据接收方发出的,当SCL为高电平时,如果SDA为高电平,则说明有非应答信号;

九、代码设计(代码还未上板验证)

   功能描述:按键读写EEPROM 24C64

  1 module iic_test(
  2   clk,
  3   rst_n,
  4   
  5   scl,
  6   sda,
  7   
  8   key_rd,
  9   key_wr,
 10   
 11   //data_in,
 12   data_out
 13 );
 14 
 15 input clk;
 16 input rst_n;
 17 
 18 input key_rd;
 19 input key_wr;
 20 //input[7:0] data_in;
 21 
 22 output scl;
 23 output reg[7:0] data_out;
 24 
 25 inout sda;
 26 
 27 wire flag;
 28 reg sda_buffer;
 29 reg scl_r;
 30 
 31 reg[11:0] current_state,next_state;
 32 
 33 parameter W_IDLE              = 12'd0;
 34 parameter W_START            = 12'd1;
 35 parameter W_DEVICE_ADD     = 12'd2;
 36 parameter W_ACK1              = 12'd3;
 37 parameter W_WORD_H_ADD     = 12'd4;
 38 parameter W_ACK2           = 12'd5;
 39 parameter W_WORD_L_ADD     = 12'd6;
 40 parameter W_ACK3           = 12'd7;
 41 parameter W_DATA           = 12'd8;
 42 parameter W_ACK4           = 12'd9;
 43 parameter W_STOP           = 12'd10;
 44 
 45 parameter R_START            = 12'd11;
 46 parameter R_DEVICE_ADD      = 12'd12;
 47 parameter R_ACK1              = 12'd13;
 48 parameter R_DATA             = 12'd14;
 49 parameter R_NO_ACK            = 12'd15;
 50 parameter R_STOP             = 12'd16;
 51 
 52 reg[1:0] en;
 53 always@(posedge clk or negedge rst_n)  // en信号设计
 54   if(!rst_n)
 55     en <= 2'b00;
 56   else begin
 57     if(!key_wr)
 58        en <= 2'b01;
 59      else begin
 60        if(!key_rd)
 61           en <= 2'b10;
 62      end
 63   end
 64 
 65 parameter SYS_CLOCK = 50_000_000;
 66 parameter SCL_CLOCK = 400_000;
 67 localparam SCL_CNT_M = SYS_CLOCK/SCL_CLOCK;
 68 
 69 reg[11:0] cnt;
 70 always@(posedge clk or negedge rst_n) begin
 71   if(!rst_n)
 72     cnt <= 12'd0;
 73   else if(cnt == SCL_CNT_M - 1)
 74     cnt <= 12'd0;
 75   else
 76     cnt <= cnt + 1'b1;
 77 end
 78 
 79 always@(posedge clk or negedge rst_n) begin
 80   if(!rst_n)
 81     scl_r <= 1'b1;
 82   else if(cnt == SCL_CNT_M >> 1)
 83     scl_r <= 1'b0;
 84   else if(cnt == 12'd0)
 85     scl_r <= 1'b1;
 86   else
 87     scl_r <= scl_r;
 88 end
 89 
 90 wire scl_l;
 91 wire scl_h;
 92 assign scl_h = (cnt == SCL_CNT_M >> 2) ? 1'b1 : 1'b0;  // scl 高电平的中间
 93 
 94 assign scl_l = (cnt == (SCL_CNT_M >> 1) + (SCL_CNT_M >> 2)) ? 1'b1 : 1'b0;  // scl低电平的中间
 95 
 96 reg[4:0] count;
 97 reg[7:0] memory;     
 98 always@(posedge clk or negedge rst_n)  // 同步时序,次态寄存器迁移到现态寄存器
 99   if(!rst_n)
100     current_state <= W_IDLE;
101   else if(scl_l)
102     current_state <= next_state;
103      
104 always@(*) begin  // 状态转移条件判断
105   next_state = W_IDLE;
106     case(current_state)
107        W_IDLE:begin
108           if(en != 2'b00)
109             next_state = W_START;
110           else
111             next_state = W_IDLE;
112         end
113         
114         W_START:begin
115           if(scl_l)
116             next_state = W_DEVICE_ADD;
117           else
118             next_state = W_START;
119         end
120         
121         W_DEVICE_ADD:begin
122           if((count == 5'd8) && (scl_l))
123             next_state = W_ACK1;
124           else
125             next_state = W_DEVICE_ADD;
126         end
127         
128         W_ACK1:begin
129           if(scl_l)
130             next_state = W_WORD_H_ADD;
131           else
132             next_state = W_ACK1;
133         end
134         
135         W_WORD_H_ADD:begin
136           if((count == 5'd8) && (scl_l))
137             next_state = W_ACK2;
138           else
139             next_state = W_WORD_H_ADD;
140         end
141         
142         W_ACK2:begin
143           if(scl_l)
144             next_state = W_WORD_L_ADD;
145           else
146             next_state = W_ACK2;
147         end
148         
149         W_WORD_L_ADD:begin
150           if((count == 5'd8) && (scl_l))
151             next_state = W_ACK3;
152           else
153             next_state = W_WORD_L_ADD;
154         end
155         
156         W_ACK3:begin
157           if(scl_l) begin
158             if(en == 2'b01)
159                next_state = W_DATA;
160              else if(en == 2'b10)
161                next_state = R_START;
162           end
163           else
164             next_state = W_ACK3;   
165         end
166         
167         W_DATA:begin
168           if((count == 5'd8) && (scl_l))
169             next_state = W_ACK4;
170           else
171             next_state = W_DATA;
172         end
173         
174         W_ACK4:begin
175           if(scl_l)
176             next_state = W_STOP;
177           else
178             next_state = W_ACK4;
179         end
180         
181         W_STOP:begin
182           if(scl_l)
183             next_state = W_IDLE;
184           else
185             next_state = W_STOP;
186         end
187         
188         R_START:begin
189           if(scl_l)
190             next_state = R_DEVICE_ADD;
191           else
192             next_state = R_START;
193         end
194         
195         R_DEVICE_ADD:begin
196           if((count == 5'd8) && (scl_l))
197             next_state = R_ACK1;
198           else
199             next_state = R_DEVICE_ADD;
200         end
201         
202         R_ACK1:begin
203           if(scl_l)
204             next_state = R_DATA;
205           else
206             next_state = R_ACK1;
207         end
208         
209         R_DATA:begin
210           if((count == 5'd8) && (scl_l))
211             next_state = R_NO_ACK;
212           else
213             next_state = R_DATA;
214         end
215         
216         R_NO_ACK:begin
217           if(scl_l)
218             next_state = R_STOP;
219           else
220             next_state = R_NO_ACK;
221         end
222         
223         R_STOP:begin
224           if(scl_l)
225             next_state = W_IDLE;
226           else
227             next_state = R_STOP;
228         end
229         
230         default:next_state = W_IDLE;
231     endcase
232 end
233 
234 always@(posedge clk or negedge rst_n)  // 状态输出
235   if(!rst_n) begin
236      sda_buffer <= 1'b1;
237      count <= 5'd0;
238      //data_out <= 8'b00000000;
239      memory <= 8'b00000000;
240   end
241   else if(scl_l) begin
242     case(next_state)
243        W_IDLE:begin
244           sda_buffer <= 1'b1;
245         end
246         
247         W_START:begin
248           sda_buffer <= 1'b0;  //起始位
249           count <= 5'd0;
250           memory <= 8'b10100000;  // 写器件地址
251         end
252         
253         W_DEVICE_ADD:begin
254           count <= count + 1'b1;
255           sda_buffer <= memory[3'd7 - count];
256         end
257         
258         W_ACK1:begin
259           count <= 5'd0;
260           memory <= 8'b00000000;  // 高八位字地址
261         end
262         
263         W_WORD_H_ADD:begin
264           count <= count + 1'b1;
265           sda_buffer <= memory[3'd7 - count];
266         end
267         
268         W_ACK2:begin
269           count <= 5'd0;
270           memory <= 8'b01010011;  // 低八位的字地址
271         end
272         
273         W_WORD_L_ADD:begin
274           count <= count + 1'b1;
275           sda_buffer <= memory[3'd7 - count];
276         end
277         
278         W_ACK3:begin
279           count <= 5'd0;
280           memory <= 8'b11110000;  // 写数据
281         end
282        
283        W_DATA:begin
284           count <= count + 1'b1;
285           sda_buffer <= memory[3'd7 - count];
286        end
287         
288         W_ACK4:begin
289           count <= 5'd0;
290         end
291         
292         W_STOP:begin
293           sda_buffer <= 1'b1;
294         end
295         
296         R_START:begin
297           sda_buffer <= 1'b0;
298           memory <= 8'b10100001;  // 读器件地址
299           count <= 5'd0;
300         end
301         
302         R_DEVICE_ADD:begin
303           count <= count + 1'b1;
304           sda_buffer <= memory[3'd7 - count];
305         end
306         
307         R_ACK1:begin
308           count <= 5'd0;
309         end
310         
311         R_DATA:begin
312           count <= count + 1'b1;
313           //data_out <= {data_out[6:0],sda};  //memory[5'd7 - count] <= sda;  // data_out[5'd7 - count] <= sda;    data_out[5'd7 - count] <= sda;
314         end
315         
316         R_NO_ACK:begin
317           sda_buffer <= 1'b1;
318         end
319         
320         R_STOP:begin
321           sda_buffer <= 1'b0;
322         end
323         
324         default:begin
325           count <= 5'd0;
326           sda_buffer <= 1'b1;
327           memory <= 8'b00000000;
328         end
329     endcase 
330   end
331   else begin
332     sda_buffer <= sda_buffer;
333      count <= count;
334   end
335   
336 always@(posedge clk or negedge rst_n) 
337   if(!rst_n)
338     data_out <= 8'b00000000;
339   else if(scl_h) begin
340     case(next_state)
341        R_DATA:data_out[3'd7 - count] <= sda;
342         default:;
343     endcase
344   end
345   else
346     data_out <= data_out;
347   
348 assign scl = ((current_state >= W_DEVICE_ADD && current_state <= W_ACK4) || 
349               (current_state >= R_DEVICE_ADD && current_state <= R_NO_ACK)) ? scl_r : 1'b1;
350   
351 assign flag =((current_state == W_ACK1)||
352              (current_state == W_ACK2) || 
353                  (current_state == W_ACK3) || 
354                  (current_state == W_ACK4) ||
355                  (current_state == R_ACK1) ||
356                  (current_state == R_DATA) 
357                  
358                  ) ? 1'b0 : 1'b1;
359                  
360 assign sda = flag ? sda_buffer : 1'bz;
361   
362 endmodule
View Code

    测试代码

 1 `timescale 1s/1ps
 2 module iic_test_tb;
 3   reg clk;
 4   reg rst_n;
 5   
 6   reg key_rd;  // 低电平有效
 7   reg key_wr;  // 低电平有效
 8   
 9   wire[7:0] data_out;
10   wire scl;
11   wire sda;
12   
13 iic_test u0(
14   .clk(clk),
15   .rst_n(rst_n),
16   
17   .scl(scl),
18   .sda(sda),
19   
20   .key_wr(key_wr),
21   .key_rd(key_rd),
22   .data_out(data_out)
23 );
24 
25 initial
26   clk = 1'b0;
27   always #10 clk = ~clk;
28   
29 initial
30   begin
31     rst_n = 1'b0;
32      key_rd = 1'b1;
33      key_wr = 1'b1;
34      #1000;
35      rst_n = 1'b1;
36      
37      #800;
38      #8000 key_wr = 1'b0;
39      #40000;
40      key_wr = 1'b1;
41      
42      #1000000;
43      key_rd = 0;
44      #40000;
45      key_rd = 1'b1;
46      
47      #1000000;
48      $stop;
49   end
50 endmodule
51 
52      
View Code

    仿真结果

    (1)写时序

   写时序存在的一些问题,在停止信号上没有完全符合在SCL的高电平时,sda由0变为1。解决办法是在停止信号之前,再加一个状态,在这个状态中,令scl处于低电平的中间时,sda_buffer为0,这样在停止信号时,就可以由0变1.

   (2)读时序

 

 读时序也存在一些问题,停止信号出现了和写时序时同样的问题,没有完全符合预期,数据接收部分全为高阻状态。

  注:

    (1)文字叙述方面参考了小梅哥,正点原子,至芯科技的相关文章;

    (2)代码设计参考了小梅哥,CrazyBingo的设计思路;

下面的代码为至芯科技的源码(最初的代码无法仿真成功,下面是我做了小小修改的代码)

 

  1 // Time : 2020.03.19 22:59
  2 // Describe : iic_driver
  3 module iic_driver(
  4   clk,
  5   rst_n,
  6   
  7   iic_scl,
  8   iic_sda,
  9   
 10   key_wr,
 11   key_rd,
 12   
 13   data_in,
 14   data_out
 15 );
 16 
 17 input clk;
 18 input rst_n;
 19 
 20 input key_rd;
 21 input key_wr;
 22 
 23 input[7:0] data_in;
 24 
 25 inout iic_sda;
 26 
 27 output reg iic_scl;
 28 output reg[7:0] data_out;
 29 
 30 reg[7:0] sda_buffer;
 31 reg flag;
 32 
 33 assign iic_sda = (flag) ? sda_buffer : 1'bz;
 34 
 35 reg[7:0] count;  
 36 reg clk_sys;
 37 
 38 always@(posedge clk or negedge rst_n)  // 分频为800KHz的时钟
 39   if(!rst_n) begin
 40     clk_sys <= 1'b0;
 41      count <= 8'd0;
 42   end
 43   else begin
 44     if(count < 31)
 45        count <= count + 1'b1;
 46      else begin
 47        count <= 8'd0;
 48         clk_sys <= ~clk_sys;
 49      end
 50   end
 51 
 52 reg[5:0] state;  // 状态寄存器
 53 
 54 always@(posedge clk_sys or negedge rst_n)  // iic_scl设计
 55   if(!rst_n)
 56     iic_scl <= 1'b1;
 57   else begin
 58     if(state > 0)  // 当总线忙的时候,iic_scl为近400kHz的时钟
 59        iic_scl <= ~iic_scl;
 60      else 
 61        iic_scl <= 1'b1;  // 空闲时为高电平
 62   end
 63   
 64 reg[1:0] en;
 65 always@(posedge clk or negedge rst_n)  // en信号设计
 66   if(!rst_n)
 67     en <= 2'b00;
 68   else begin
 69     if(!key_wr)
 70        en <= 2'b01;
 71      else begin
 72        if(!key_rd)
 73           en <= 2'b10;
 74      end
 75   end
 76   
 77 reg[3:0] cnt; // 发送或接收数据的个数
 78 reg[1:0] temp;  // 读/写使能中间寄存器
 79 reg[7:0] memory;  // 发送或接收数据的中间寄存器
 80 
 81 always@(posedge clk_sys or negedge rst_n)
 82   if(!rst_n) begin
 83      data_out <= 8'd0;
 84      flag <= 1'b1;        //复位时,系统获得总线的控制权,作为输出端口
 85      sda_buffer <= 1'b1;  //向 iic_scl 发送高电平
 86      state <= 0;
 87      temp <= 2'b0;
 88   end
 89   else
 90     case(state)
 91        0:begin
 92           if(iic_scl) begin
 93             if(en != temp) begin  // 有按键按下
 94                sda_buffer <= 1'b0;  // 发送启动信号
 95                 state <= 1;
 96                 temp <= en;
 97                 cnt <= 0;
 98                 memory <= 8'b10100000;
 99              end
100              else
101                state <= 0;   
102           end
103           else
104             state <= 0; 
105         end
106         
107         1:begin
108           if((iic_scl == 0) && (cnt < 8)) begin  // 发送8位控制字,器件地址
109             sda_buffer <= memory[7];
110              cnt <= cnt + 1'b1;
111              memory <= {memory[6:0],memory[7]};
112              state <= 1;
113           end
114           else begin
115             if((iic_scl == 0) && (cnt == 8)) begin
116                cnt <= 0;
117                 flag <= 0;  // 释放总线控制权
118                 state <= 2;
119              end
120              else
121                state <= 1;
122           end
123         end
124         
125         2:begin
126           if(!iic_scl) begin  // 检测应答信号
127             state <= 3;
128              memory <= 8'd0;  // 高字节地址
129           end
130           else
131             state <= 2;
132         end
133         
134         3:begin  // 发送高字节地址
135           if((iic_scl == 0) && (cnt < 8 )) begin
136             flag <= 1'b1;  // 系统获得总线控制权
137              sda_buffer <= memory[7];
138              cnt <= cnt + 1'b1;
139              memory ={memory[6:0],memory[7]};
140              state <= 3;
141           end
142           else begin
143             if((iic_scl == 0) && (cnt == 8)) begin
144                cnt <= 0;
145                 flag <= 0;  // 释放总线控制权
146                 state <= 4;
147              end
148              else
149                state <= 3;
150           end
151         end
152         
153         4:begin
154           if(!iic_scl) begin  // 检测应答信号
155             state <= 5;
156              memory <= 8'b00110101;  // 低字节地址
157           end
158           else begin
159             state <= 4;
160           end
161         end
162         
163         5:begin
164           if((iic_scl == 0) && (cnt < 8)) begin  //发送低字节地址
165             flag <= 1'b1;  // 获得总线控制权
166              sda_buffer <= memory[7];
167              cnt <= cnt + 1'b1;
168              memory = {memory[6:0],memory[7]};
169              state <= 5; 
170           end
171           else begin
172             if((iic_scl == 0) && (cnt == 8)) begin
173                cnt <= 0;
174                 flag <= 0;  // 释放总线控制权
175                 state <= 6;
176              end
177              else 
178                state <= 5;
179           end
180         end
181         
182         6:begin
183           if(!iic_scl) begin  // 检测应答信号
184             if(temp == 2'b01) begin // 判断是否为写信号
185                state <= 7;
186                 memory = data_in[7:0];  // 发送数据
187              end
188              if(temp == 2'b10)  // 判断是否为读信号
189                state <= 11;
190           end
191           else
192             state <= 6;
193         end
194         
195         7:begin
196           if((iic_scl == 0) && (cnt < 8)) begin  // 发送数据
197             flag <= 1'b1;  // 获得总线控制权
198              sda_buffer <= memory[7];
199              cnt <= cnt + 1'b1;
200              memory = {memory[6:0],memory[7]};
201              state <= 7;
202           end
203           else begin
204             if((iic_scl == 0) && (cnt == 8)) begin
205                cnt <= 0;
206                 flag <= 0;  // 释放总线控制权
207                 state <= 8;
208              end
209              else
210                state <= 7;
211           end
212         end
213         
214         8:begin
215           if(!iic_scl)  //检测应答信号
216             state <= 9;
217           else
218             state <= 8;
219         end
220         
221         9:begin
222           if(iic_scl == 0) begin
223             flag <= 1; //获得总线控制权
224              sda_buffer <= 0;  //拉低IIC的数据线,为发送停止信号做准备
225              state <= 10;
226           end
227           else
228             state <= 9;
229         end
230         
231         10:begin
232           if(iic_scl == 1) begin
233             sda_buffer <= 1;  // 发送停止信号
234              state <= 0;
235           end
236           else
237             state <= 10;
238         end
239 //--------------------------------------//        
240         11:begin
241           flag <= 1;  //获得总线控制权
242           sda_buffer <= 1;  // 拉高IIC控制线(为发送启动信号做准备)
243           state <= 12;
244         end
245         
246         12:begin
247           sda_buffer <= 0;  // 发送启动信号
248           state <= 13;
249           memory <= 8'b10100001; 
250         end
251         
252         13:begin
253           if((iic_scl == 0) && (cnt < 8)) begin  // 发送8位控制字
254             flag <= 1;  // 获得总线控制权
255              sda_buffer <= memory[7];
256              cnt <= cnt + 1'b1;
257              memory <= {memory[6:0],memory[7]};
258              state <= 13;
259           end
260           else begin
261             if((iic_scl == 0) && (cnt == 8)) begin
262                cnt <= 0;
263                 flag <= 0;  // 释放总线控制权
264                 state <= 14;
265              end
266              else
267                state <= 13; 
268           end
269         end
270         
271         14:begin
272           if(!iic_scl)  // 检测应答信号
273             state <= 15;
274           else
275             state <= 14;   
276         end
277         
278         15:begin
279           if((iic_scl == 1) && (cnt < 8)) begin  // 接收数据
280             cnt <= cnt + 1'b1;
281              memory <= {memory[6:0],iic_sda};
282           end
283           else begin
284             if((iic_scl == 0) && (cnt == 8)) begin
285                cnt <= 0;
286                 flag <= 1;  // 获得总线控制权
287                 state <= 16;
288                 sda_buffer <= 1;  // 发送应答信号
289              end
290              else
291                state <= 15;   
292           end
293         end
294         
295         16:begin
296           data_out <= memory;  // 发送数据
297           state <= 17;
298         end
299         
300         17:begin
301           if(iic_scl == 0) begin
302             sda_buffer <= 0;  //拉低IIC的数据线,为发送停止信号做准备
303              state <= 18;
304           end
305           else
306             state <= 17;
307         end
308         
309         18:begin  // 发送停止信号
310           if(iic_scl == 1) begin
311             sda_buffer <= 1;
312              state <= 0;
313           end
314           else
315             state <= 18; 
316         end
317         
318         default: state <= 0;
319      endcase
320 
321 endmodule
322 
323   
324   
325   
326   
327   
328   
329   
330   
331   
332   
333   
334   
335   
336   
337   
338   
339   
View Code

测试代码

 1 `timescale 1ns/1ps
 2 module iic_driver_tb;
 3   reg clk;
 4   reg rst_n;
 5   
 6   reg key_rd;
 7   reg key_wr;
 8   
 9   reg[7:0] data_in;
10   
11   wire iic_sda;
12   
13   wire iic_scl;
14   wire[7:0] data_out;
15   
16 iic_driver u0(
17   .clk(clk),
18   .rst_n(rst_n),
19   
20   .iic_scl(iic_scl),
21   .iic_sda(iic_sda),
22   
23   .key_wr(key_wr),
24   .key_rd(key_rd),
25   
26   .data_in(data_in),
27   .data_out(data_out)
28 );
29 
30 initial
31   clk = 0;
32   always #10 clk = ~clk;
33   
34 initial
35   begin
36     rst_n = 1'b0;
37      key_rd = 1'b1;
38      key_wr = 1'b1;
39      data_in = 1'b0;
40      
41      #1000;
42      rst_n = 1'b1;
43      
44      #800;
45      
46      #8000 key_wr = 1'b0;
47      data_in = 8'h23;
48      #4000 key_wr = 1'b1;
49      
50      #1000000;
51      key_rd = 1'b0;
52      #40000;
53      key_rd = 1'b1;
54      
55      #10000000;
56      $stop;
57   end
58 endmodule
View Code

仿真结果:

    (1)写时序

     (2)读时序

 

 改进版:2021/11/29

    针对前文提到的起始位和结束位的控制问题,这里对这些问题进行改进。

源码:

  1 // *********************************************************************************
  2 // Project Name : iic_driver
  3 // Email        : 
  4 // Create Time  : 2021/11/24 16:15
  5 // Module Name  : 
  6 // editor        : Qing
  7 // Version        : Rev1.0.0
  8 // *********************************************************************************
  9 
 10 /*
 11     iic驱动时序中,值得注意的是,在SCL高电平的中间对sda信号线上的数据进行采集,
 12 在SCL低电平的中间对数据进行切换,这样数据采集效果会更好。
 13 
 14 */
 15 
 16 module iic_driver
 17 #(
 18     parameter    DEVICE_ADDR        =    7'b1010_000,
 19     parameter    SYS_CLK_FREQ    =    26'd50000000,
 20     parameter    SCL_FREQ        =    18'd250000
 21 )
 22 (
 23     input                                clk                ,
 24     input                                rst_n            ,
 25     
 26     input                                wr_en            ,
 27     input                                rd_en            ,
 28     
 29     input                                iic_start        ,    //    iic启动信号
 30     input                                addr_num        ,    //    字节地址的个数,为1,则表示两个字节地址,为0则表示一个字节地址
 31     input        [15:0]                    byte_addr        ,    //    字节地址
 32     input        [ 7:0]                    wr_data            ,
 33     
 34     output    reg                            iic_end            ,    //    iic结束信号
 35     output    reg    [ 7:0]                    rd_data            ,
 36     output                                iic_scl            ,
 37     inout                                iic_sda        
 38 );
 39 
 40 //========================================================================\
 41 // =========== Define Parameter and Internal signals =========== 
 42 //========================================================================/
 43 
 44     parameter    CNT_MAX            =     (SYS_CLK_FREQ/SCL_FREQ)    ;    //    200
 45     parameter    CNT_HIGH        =     CNT_MAX >> 1        ;
 46     
 47     parameter     IDLE            =     4'd0                ;
 48     parameter     START_1            =     4'd1                ;
 49     parameter     SEND_D_ADDR        =     4'd2                ;
 50     parameter    ACK_1            =     4'd3                ;
 51     parameter     BYTE_ADDR_H        =     4'd4                ;
 52     parameter    ACK_2            =     4'd5                ;
 53     parameter     BYTE_ADDR_L        =     4'd6                ;
 54     parameter    ACK_3            =     4'd7                ;
 55     parameter    WR_DATA         =     4'd8                ;
 56     parameter    ACK_4            =     4'd9                ;
 57     
 58     parameter    START_2            =     4'd10                ;
 59     parameter    SEND_RD_ADDR    =     4'd11                ;
 60     parameter    ACK_5            =     4'd12                ;
 61     parameter    RD_DATA         =     4'd13                ;    
 62     parameter    N_ACK             =     4'd14                ;
 63     parameter    STOP             =     4'd15                ;
 64     
 65     reg            [3:0]            current_state            ;
 66     reg            [3:0]            next_state                ;
 67     
 68     wire                        scl_h                    ;    //    高电平的中点
 69     wire                        scl_l                    ;    //    低电平的中点
 70     
 71     reg            [7:0]            cnt                     ;
 72     reg                            scl_r                    ;
 73     
 74     wire                        sda_en                    ;
 75     reg                            sda_buffer                ;
 76     
 77     reg            [3:0]            bit_cnt                    ;
 78     reg            [7:0]            memory                    ;
 79 
 80 //=============================================================================
 81 //****************************     Main Code    *******************************
 82 //=============================================================================
 83 
 84 assign    iic_sda        =      (sda_en == 1'b1) ? sda_buffer : 1'hz;        //    三态控制
 85 
 86 always @(posedge clk or negedge rst_n) begin
 87     if(!rst_n)
 88         cnt <= 8'd0;
 89     else if(cnt == CNT_MAX - 1)
 90         cnt <= 8'd0;
 91     else
 92         cnt <= cnt + 1'b1;
 93 end
 94 
 95 always @(posedge clk or negedge rst_n) begin
 96     if(!rst_n)
 97         scl_r <= 1'b0;
 98     else if(cnt == (CNT_MAX >> 2))
 99         scl_r <= 1'b1;
100     else if(cnt == ((CNT_MAX >> 1) + (CNT_MAX >> 2)))
101         scl_r <= 1'b0;
102     else
103         scl_r <= scl_r;
104 end
105 
106 assign    scl_l    =    (cnt == 0) ? 1'b1 : 1'b0; // 0
107 
108 assign    scl_h   =    (cnt == CNT_HIGH) ? 1'b1 : 1'b0;  // 100
109 
110 always @(posedge clk or negedge rst_n) begin    //    次态迁移到现态
111     if(!rst_n)
112         current_state <= IDLE;
113     else
114         current_state <= next_state;
115 end
116 
117 always @(*) begin    // 状态的转移
118     case(current_state)
119         IDLE:begin
120             if(iic_start == 1'b1)
121                 next_state = START_1;
122             else
123                 next_state = current_state;
124         end
125         
126         START_1:begin
127             if(scl_l == 1'b1)
128                 next_state = SEND_D_ADDR;
129             else
130                 next_state = current_state;
131         end
132         
133         SEND_D_ADDR:begin
134             if((bit_cnt == 4'd8) && (scl_l == 1'b1))
135                 next_state = ACK_1;
136             else
137                 next_state = current_state;
138         end
139         
140         ACK_1:begin
141             if(scl_l == 1'b1) begin
142                 if(addr_num == 1'b1)
143                     next_state = BYTE_ADDR_H;
144                 else
145                     next_state = BYTE_ADDR_L;
146             end
147             else
148                 next_state = current_state;
149         end
150         
151         BYTE_ADDR_H:begin
152             if((bit_cnt == 4'd8) && (scl_l == 1'b1))
153                 next_state = ACK_2;
154             else
155                 next_state = current_state;
156         end
157         
158         ACK_2:begin
159             if(scl_l == 1'b1)
160                 next_state = BYTE_ADDR_L;
161             else
162                 next_state = current_state;
163         end
164 
165         BYTE_ADDR_L:begin
166             if((bit_cnt == 4'd8) && (scl_l == 1'b1))
167                 next_state = ACK_3;
168             else
169                 next_state = current_state;
170         end    
171         
172         ACK_3:begin
173             if(scl_l == 1'b1) begin
174                 if(wr_en == 1'b1)
175                     next_state = WR_DATA;
176                 else if(rd_en == 1'b1)
177                     next_state = START_2;
178                 else
179                     next_state = current_state;
180             end
181             else
182                 next_state = current_state;
183         end    
184         
185         WR_DATA:begin
186             if((bit_cnt == 8) && (scl_l == 1'b1))
187                 next_state = ACK_4;
188             else
189                 next_state = current_state;
190         end
191         
192         ACK_4:begin
193             if(scl_l == 1'b1)
194                 next_state = STOP;
195             else
196                 next_state = current_state;
197         end
198         
199         START_2:begin
200             if(scl_l == 1'b1)
201                 next_state = SEND_RD_ADDR;
202             else
203                 next_state = current_state;
204         end
205         
206         SEND_RD_ADDR:begin
207             if((bit_cnt == 8) && (scl_l == 1'b1))
208                 next_state = ACK_5;
209             else
210                 next_state = current_state;
211         end
212         
213         ACK_5:begin
214             if(scl_l == 1'b1)
215                 next_state = RD_DATA;
216             else
217                 next_state = current_state;
218         end
219         
220         RD_DATA:begin
221             if((bit_cnt == 8) && (scl_l == 1'b1))
222                 next_state = N_ACK;
223             else
224                 next_state = current_state;
225         end
226         
227         N_ACK:begin
228             if(scl_l == 1'b1)
229                 next_state = STOP;
230             else
231                 next_state = current_state;
232         end
233         
234         STOP:begin
235             if(scl_l == 1'b1)
236                 next_state = IDLE;
237             else
238                 next_state = current_state;
239         end
240         
241         default:begin
242             next_state = IDLE;
243         end
244     endcase
245 end
246 
247 always @(posedge clk or negedge rst_n) begin    // 状态输出
248     if(!rst_n) begin
249         sda_buffer <= 1'b1;
250         bit_cnt <= 4'd0;
251         memory <= 8'd0;
252     end
253     else begin
254         case(next_state)
255             IDLE:begin
256                 sda_buffer <= 1'b1;
257             end
258             
259             START_1:begin
260                 memory <= {DEVICE_ADDR,1'b0};
261                 bit_cnt <= 4'd0;
262                 if(cnt <= CNT_HIGH)         //    对起始位的严格控制
263                     sda_buffer <= 1'b1;
264                 else 
265                     sda_buffer <= 1'b0;
266             end    
267             
268             SEND_D_ADDR:begin
269                 if(scl_l == 1'b1) begin
270                     bit_cnt <= bit_cnt + 1'b1;
271                     sda_buffer <= memory[7 - bit_cnt];
272                 end
273                 else begin
274                     bit_cnt <= bit_cnt;
275                     sda_buffer <= sda_buffer;
276                 end
277             end
278             
279             ACK_1:begin
280                 bit_cnt <= 4'd0;
281                 memory <= byte_addr[15:8];
282             end
283             
284             BYTE_ADDR_H:begin
285                 if(scl_l == 1'b1) begin
286                     bit_cnt <= bit_cnt + 1'b1;
287                     sda_buffer <= memory[7 - bit_cnt];
288                 end
289                 else begin
290                     bit_cnt <= bit_cnt;
291                     sda_buffer <= sda_buffer;
292                 end                
293             end
294             
295             ACK_2:begin
296                 bit_cnt <= 4'd0;
297                 memory <= byte_addr[7:0];
298             end
299 
300             BYTE_ADDR_L:begin
301                 if(scl_l == 1'b1) begin
302                     bit_cnt <= bit_cnt + 1'b1;
303                     sda_buffer <= memory[7 - bit_cnt];
304                 end
305                 else begin
306                     bit_cnt <= bit_cnt;
307                     sda_buffer <= sda_buffer;
308                 end                
309             end
310             
311             ACK_3:begin
312                 bit_cnt <= 4'd0;
313                 memory <= wr_data;
314             end
315 
316             WR_DATA:begin
317                 if(scl_l == 1'b1) begin
318                     bit_cnt <= bit_cnt + 1'b1;
319                     sda_buffer <= memory[7 - bit_cnt];
320                 end
321                 else begin
322                     bit_cnt <= bit_cnt;
323                     sda_buffer <= sda_buffer;
324                 end                
325             end
326             
327             ACK_4:begin
328                 bit_cnt <= 4'd0;
329                 memory <= 8'd0;
330             end    
331 
332             START_2:begin
333                 memory <= {DEVICE_ADDR,1'b1};
334                 bit_cnt <= 4'd0;
335                 if(cnt <= CNT_HIGH)   // 对起始位的严格控制
336                     sda_buffer <= 1'b1;
337                 else 
338                     sda_buffer <= 1'b0;
339             end    
340             
341             SEND_RD_ADDR:begin
342                 if(scl_l == 1'b1) begin
343                     bit_cnt <= bit_cnt + 1'b1;
344                     sda_buffer <= memory[7 - bit_cnt];
345                 end
346                 else begin
347                     bit_cnt <= bit_cnt;
348                     sda_buffer <= sda_buffer;
349                 end                
350             end    
351             
352             ACK_5:begin
353                 bit_cnt <= 4'd0;
354                 memory <= 8'd0;
355             end
356             
357             RD_DATA:begin
358                 if(scl_l == 1'b1)
359                     bit_cnt <= bit_cnt + 1'b1;
360                 else
361                     bit_cnt <= bit_cnt;
362             end
363             
364             N_ACK:begin
365                 sda_buffer <= 1'b1;
366             end
367             
368             STOP:begin
369                 if(cnt <= CNT_HIGH)   // 对结束位的严格控制
370                     sda_buffer <= 1'b0;
371                 else
372                     sda_buffer <= 1'b1;
373             end
374             
375             default:begin
376                 bit_cnt <= 4'd0;
377                 sda_buffer <= 1'b1;
378                 memory <= 8'd0;
379             end
380         endcase
381     end
382 end
383 
384 always @(posedge clk or negedge rst_n) begin
385     if(!rst_n)
386         rd_data <= 8'd0;
387     else if(scl_h == 1'b1) begin
388         case(next_state)
389             RD_DATA: rd_data[7 - bit_cnt] <= iic_sda;
390             default:;
391         endcase
392     end
393     else
394         rd_data <= rd_data;
395 end
396 
397 
398 assign    iic_scl = ((current_state >= SEND_D_ADDR  && current_state <= N_ACK)) ? scl_r : 1'b1;
399                    
400 assign    sda_en  = ((current_state == ACK_1) || (current_state == ACK_2) ||
401                    (current_state == ACK_3) || (current_state == ACK_4)    ||
402                    (current_state == ACK_5) || (current_state == RD_DATA)) ? 1'b0 : 1'b1; 
403                    
404 always @(posedge clk or negedge rst_n) begin
405     if(!rst_n)
406         iic_end <= 1'b0;
407     else if((current_state == STOP) && (scl_l == 1'b1))
408         iic_end <= 1'b1;
409     else
410         iic_end <= 1'b0;
411 end
412                    
413 endmodule
View Code

testbench:

 1 `timescale 1ns/1ns
 2 
 3 module iic_driver_tb;
 4     reg                                clk                ;
 5     reg                                rst_n            ;
 6     reg                                wr_en            ;
 7     reg                                rd_en            ;
 8     reg                                iic_start        ;
 9     reg                                addr_num        ;    //    字节地址的个数
10     reg        [15:0]                    byte_addr        ;
11     reg        [ 7:0]                    wr_data            ;
12     wire                            iic_end            ;
13     wire    [ 7:0]                    rd_data            ;
14     wire                            iic_scl            ;
15     wire                            iic_sda         ;     
16     
17 iic_driver #(
18     .DEVICE_ADDR  ( 7'b1010000          ),            
19     .SYS_CLK_FREQ ( 26'd50000000     ),
20     .SCL_FREQ      ( 18'd250000       )            
21 )
22 iic_driver_inst(
23     .clk         ( clk                 ),    
24     .rst_n         ( rst_n             ),
25     .wr_en         ( wr_en             ),
26     .rd_en         ( rd_en             ),
27     .iic_start     ( iic_start         ),
28     .addr_num     ( addr_num             ),
29     .byte_addr     ( byte_addr         ),
30     .wr_data     ( wr_data             ),    
31     .iic_end     ( iic_end             ),    
32     .rd_data     ( rd_data             ),    
33     .iic_scl     ( iic_scl             ),    
34     .iic_sda     ( iic_sda           )
35 );
36 
37 
38 
39 initial
40     clk = 1'b0;
41     always #10 clk = ~clk;
42     
43 initial
44     begin
45         #1;
46         rst_n = 1'b0;
47         wr_en = 1'b0;
48         rd_en = 1'b0;
49         iic_start = 1'b0;
50         addr_num = 1'b0;
51         byte_addr = 16'd0;
52         wr_data = 8'd0;
53         #21;
54         rst_n = 1'b1;
55         #21;
56         
57         //wr_en = 1'b1;
58         rd_en = 1'b1;
59         wr_data = 8'b0011_0101;
60         addr_num = 1'b1;
61         byte_addr = 16'b0101_1000_1101_0010;
62         iic_start = 1'b1;
63         #21;
64         iic_start = 1'b0;
65         
66     end
67     
68 endmodule
View Code

写时序仿真:

读时序仿真:

 

 

 

 

 

 

 

posted @ 2020-04-06 10:52  青河  阅读(916)  评论(0编辑  收藏  举报