基于FPGA的IIC设计

一、IIC基本概念

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

图1

二、主机和从机的概念

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

三、传输速率

IIC 总线数据传输速率在标准模式下可达 100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。IIC总线上的主设备与从设备之间以字节(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

测试代码

 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      

仿真结果

(1)写时序

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

(2)读时序

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

还有一种IIC驱动的写法,和调用仿真模型的来测试iic驱动程序,留待更新。

注:

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

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

原文地址:https://www.cnblogs.com/571328401-/p/12639976.html

时间: 2024-10-18 00:12:04

基于FPGA的IIC设计的相关文章

基于FPGA的数字频率计(设计全过程)

小序: 前几天看全国大学生电子设计竞赛历年试题,无意间发现了一道题目<简易数字频率计>,跟之前我用verilog写的一个频率计差不多,于是想尝试做一下,题目具体要求如下图中所示,我所用的开发板为 xilinx spartan 3ES, 最后结果还算理想,在要求的范围内的频率测量精度比较高,但是由于板子的限制没能实现脉冲宽度测量等功能,但是其原理比较简单,下文中将会给出设计过程,结果展示图片以及整个工程代码的下载链接. 数字频率计总体设计采用分块的思想,自底向上设计方向,三个子模块包括频率测量模

基于FPGA的VGA显示设计(二)

上一篇:基于FPGA的VGA显示设计(一)     参照 CrazyBingo 的 基于FPGA的VGA可移植模块终极设计代码  的工程代码风格,模块化处理了上一篇的代码,并增加了一点其它图形. 顶层模块: 1 /**************************************************** 2 * Module Name : VGA_color_all.v 3 * Author : yllinux 博客:http://www.cnblogs.com/yllinux/

基于FPGA的VGA可移植模块终极设计【转】

本文转载自:http://www.cnblogs.com/lueguo/p/3373643.html 略过天涯 基于FPGA的VGA可移植模块终极设计 一.VGA的诱惑 首先,VGA的驱动,这事,一般的单片机是办不到的:由于FPGA的速度,以及并行的优势,加上可现场配置的优势,VGA的配置,只有俺们FPGA可以胜任,也只有FPGA可以随心所欲地配置(当然ARM也可以,应用比较高吧). 初学者就是喜欢看炫的效果,往往会忍不住想玩.尤其玩FPGA的,没玩VGA就感到跟单片机没啥提升,因此VGA的驱动

基于FPGA的高速图像处理平台设计_爱学术

[摘要]针对传统图像处理平台采集.处理速度慢,显示分辨率低,不方便携带等问题,设计了一个基于FPGA的高速图像处理平台,包含高速图像的采集.处理.及显示.图像采集通过Camera Link接口采集图像,图像处理部分利用乒乓操作对图像数据进行高速存储,FPGA完成图像预处理,DSP进行运算复杂的图像处理,图像显示部分输出标准的视频信号进行DVI显示.实验结果表明,平台完成了对图像的高速处理,体积小,处理数据量大,可以替代传统图像处理平台,并应用到多种图像处理领域. [作者] 陈序  卿粼波  何小

基于FPGA的音频信号的FIR滤波(Matlab+Modelsim验证)

1 设计内容 本设计是基于FPGA的音频信号FIR低通滤波,根据要求,采用Matlab对WAV音频文件进行读取和添加噪声信号.FFT分析.FIR滤波处理,并分析滤波的效果.通过Matlab的分析验证滤波效果后,将叠加噪声信号的音频信号输出到txt文件里.然后使用Matlab语言编写滤波器模块和测试模块,通过Modelsim软件读取txt文件的数据,将数据送入滤波模块,最后将滤波的结果输出到txt文件里,最后用Matlab将处理的结果从txt文件读出.显示.FFT分析用Verilog设计的FIR滤

基于FPGA的飞机的小游戏

基于FPGA的飞机的小游戏 实验原理 ????该实验主要分为4个模块,采用至上而下的设计方法进行设计.由50M的晶振电路提供时钟源,VGA显示控制模块.图形显示控制模块.移动模块的时钟为25M,由时钟分频电路产生获得.时钟分频模块采用PLL进行设计,由50M时钟进行2分频获得25M时钟. ????移动模块,控制我方飞机和敌方飞机.子弹的移动,移动的速度可以通过时钟的频率进行控制,操作我方飞机的移动和子弹的发射由外部按键进行控制,控制的方式有发射子弹.左移.右移. ????图像显示控制模块,用于控

基于FPGA的图像开发平台 其他摄像头附件说明(OV5642 OV9655)

基于FPGA的图像开发平台 其他摄像头附件说明 FPGA_VIP_V101 编者 奇迹再现 个人博客 http://www.cnblogs.com/ccjt/ 联系邮箱 [email protected] 淘宝网址 http://ccjt.taobao.com 修订记录 见下页 版权归奇迹再现所有,抄袭请注明出处, 参考文献:CrazyBingo原创相关文档.请尊重原创. 前言: 本系统方案理论适合DVP绝大部分摄像头测试,调试及开发,针对其他摄像头,因为寄存器参数不同,需要进行相应移植. 目前

基于FPGA的Sobel边缘检测的实现

前面我们实现了使用PC端上位机串口发送图像数据到VGA显示,通过MATLAB处理的图像数据直接是灰度图像,后面我们在此基础上修改,从而实现,基于FPGA的动态图片的Sobel边缘检测.中值滤波.Canny算子边缘检测.腐蚀和膨胀等.那么这篇文章我们将来实现基于FPGA的Sobel边缘检测. 图像边缘:简言之,边缘就是图像灰度值突变的地方,亦即图像在该部分的像素值变化速度非常之快,这就好比在坐标轴上一条曲线有刚开始的平滑突然来个大转弯,在变化出的导数非常大. Sobel算子主要用作边缘检测,在技术

基于FPGA的均值滤波算法实现

我们为了实现动态图像的滤波算法,用串口发送图像数据到FPGA开发板,经FPGA进行图像处理算法后,动态显示到VGA显示屏上,前面我们把硬件平台已经搭建完成了,后面我们将利用这个硬件基础平台上来实现基于FPGA的一系列图像处理基础算法. 椒盐噪声(salt & pepper noise)是数字图像的一个常见噪声,所谓椒盐,椒就是黑,盐就是白,椒盐噪声就是在图像上随机出现黑色白色的像素.椒盐噪声是一种因为信号脉冲强度引起的噪声,产生清楚该噪声的算法也比较简单. 均值滤波的方法将数据存储成3x3的矩阵