【乌拉喵.教程】IIC总线介绍及FPGA编程

最近将多年来收集到的教学视频、国内外图书、源码等整理整合拿出来,涉及arm、Linux、python、信号完整性、FPFA、DSP、算法、stm32、单片机、制图、电子模块、kali、出版社图书等。资料目前约1.5TB+。详情:

1.5TB+电子工程师资料详细介绍https://b23.tv/7Kq7GMc

视频讲解:

【教程6】IIC总线介绍及FPGA编程-上https://www.bilibili.com/video/BV1YZ4y1D7zr/【教程6】IIC总线介绍及FPGA编程-下https://www.bilibili.com/video/BV1SP4y1J7JS/

IIC总线介绍及FPGA编程

  • OC、OD是啥?
  • EEPROM、FLASH的区别
  • IIC总线规范
  • FPGA的IIC程序

①什么是OC、OD

OC门,又称集电极开路门,Open Collector,还有OD门(Open Drain,漏极开路门,对场效应管而言)。

  

AT24C02手册:

接线方法:

上拉电阻的阻值决定了逻辑电平转换的沿的速度。阻值越大,速度越低功耗越小。反之亦然。

OC OD介绍 - fuluoerde - 博客园

为什么只有OC、OD的接口才可以接到一起呢?

我们来看一个很常用的芯片,74LVC4245,这是一款实现3.3V信号与5V信号互转的芯片。

我们来看它管脚的输出电平:

它的管脚是有输出能力的,所以当把它输出的两个管脚接到一起,比如Pin3(A1)、Pin4(A2),如果一个输出高,一个输出低。那么就相当于Pin3=1=5V,Pin4=0=0V,可见相当于VCCA和GND短接了,所以它不能直接把两个管脚连接到一起。

好在这个芯片有OE控制脚,可以使管脚输出变为三态,也就是门阵中的高阻态Z。但设为高阻态后,这个芯片就失去了电平转换的能力,因为OE是output enable的意思么。那么这个功能干啥用呢?

在总线通讯中是有至关重要的作用的。

②EEPROM和Flash的区别

EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。

这种Rom的特点是可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。

详细结构介绍可以看:百度安全验证

Flash也是电擦除的Rom。但是为了区别于一般的按字节为单位的擦写的EEPROM,Flash擦除时不再以字节为单位,而是以块为单位,一次简化了电路,数据密度更高,降低了成本。Flash 的编程原理都是只能将 1 写为 0,而不能将 0 写为 1。所以在 Flash 编程之前,必须将对应的块擦除,而擦除的过程就是把所有位都写为 1 的过程。

③IIC总线规范(以EEPROM AT24C02为例讲解)

所有的数据读写操作,都必须由发送器,也就是IIC主控制器发起。

IIC总线中,SCL是单向的,是由主控制器产生的,SDA是双向的。

把这个举个例子,IIC总线就好比一条电话线,电话线串起了领导、员工两种人的电话机,领导(IIC控制器)只能有一个,员工(IIC从设备)可以有很多个。电话只能由领导打给员工,员工不能打给领导,代表了IIC总线读写的操作的发起者必须是IIC控制器。当电话拨通了,员工和领导之间可以说话了,这个说话就是双向的了,互相说。

字节写操作:

中文说明:

英文说明:

下面把上面的图详细拆解,说明。

在总线空闲,也就是主IIC控制器和从IIC设备之间没有数据通讯时,SCL和SDA是都为高电平的。

当IIC主控制器要发起读写操作时,要先发起起始。

Ⅰ.起始条件,称为Start,要求在SCL为高电平时,SDA产生下降沿。

II.起始之后,开始发送器件地址,器件地址就是类似的在给总线上的IIC从设备起名字。

还是接着前面的例子,领导用电话线与多个员工通讯,电话又必须得领导来打,那领导怎么知道要和哪个员工通话嘞?那就把每个员工的电话分个电话号码呗,和现在的座机一样。这个电话号码就是IIC的器件地址。

我们看AT24C02的器件地址是啥。

再找一个IIC设备的地址,一个RTC芯片PCF8563的:

首先发现,最后一位都是R/Wn,也就是这个从设备器件地址的最低位Bit0,是作为读写控制位来使用的。设为1为从IIC从设备中读数据,设为0为往IIC从设备写数据。

其次,还能发现PCF8563的器件地址是固定的为“1010001x”,AT24C02的器件地址高四位是固定的1010,但是bit3~bit1却是A2~A0,不是一个确定值,那么这个A2~A0是什么?是芯片的硬件管脚:

同时,我们又发现了一件事情,就是PCF8563的器件地址,和AT24C02的器件地址是会冲突的。

器件

器件地址bit7~bit4

Bit3~bit1

Bit0

PCF8563

1010

001

R/Wn

AT24C02

1010

由A2~A0管脚决定,范围:

000~111

R/Wn

所以也就是说,如果IIC总线既挂PCF8563,又挂AT24C02时,A2~A0的管脚配置就不能配成001,不然一个IIC总线上就会出现两个地址为“1010 001x”的设备,也就是电话线上有两个同一个电话号码的电话机,就乱了。

III.ACK应答位

Ack简单讲就是打电话中,听电话的人,在听完说话人的话之后“嗯”了那么一下。不要去机械的记忆谁给谁去ack,去按照打电话的情形去理解这件事。领导给员工说完,员工要“嗯”一下,员工给领导说完,领导也要“嗯”一下。领导给员工,就是IIC主控去写从设备,员工给领导就是,IIC主控去读从设备,从设备返回数据给IIC主控。

这里我们在说IIC控制器的写操作,所以ack应该是从设备返回给控制器的。

在scl采样中,SDA保持低电平,认为有ack。

IV字地址(寄存器地址)

IIC总线上的器件地址发送了,选通了IIC从设备,下面要操作器件内部寄存器了。

这里就是把要操作的寄存器地址告诉IIC从设备。

操作方式就与发送器件地址类似了,但是这个地址长度是会变的,因为8bit长可以访问28=256个地址,如果比256个寄存器还要多,那就访问不全了。所以这里长度是变的。

V.数据

数据和写器件地址,字地址一样,操作方式一致,所以这里把SCL、SDA的位传输补充一下。

也就是我们可以认为,在SCL高时,SDA上的电平值就是我们要传输的数据值。SCL下降沿后,SDA的电平才能发生变化。这个对于FPGA来说,比较靠谱的方式就是在SCL高电平时的中间位置采样SDA(FPGA作为IIC主控时),在SCL的下降沿产生后使SDA发生变化(FPGA作为IIC从设备时)。

VIStop

与start正好相反。要求在SCL为高电平时,SDA产生上升沿。

连续写:

字节读:

其实这个时序前面(标黄)的部分,就是往IIC从设备里的读数据地址寄存器里写入了要读取数据的地址。后面当进行读时,IIC从设备就根据这个写入的地址,去取自己ROM里的数据,再返回给IIC控制器。

连续读:

这个连续读啥时候结束呢?当IIC控制器不再应答,也就是不给IIC从设备ACK之后,连续读就结束了。

④FPGA的IIC程序

首先说,这里FPGA的位置相当于是IIC控制器,也就是主设备。这个FPGA的IIC模块是“lb2iic_module.vhd”,也就是LocalBus to iic。所以得知道啥是LocalBus。

不太清楚的请看我之前的讲解。

视频:【教程3】LocalBus总线及FPGA总线编程_哔哩哔哩_bilibili

文档:【乌拉喵.教程】LocalBus总线介绍及FPGA总线编程_乌拉大喵喵-CSDN博客

先跳过localbus这一部分,先看看IIC这里的主要思路。

首先是时钟,既然是IIC控制器,那么SCL就得主设备来产生了。同时,我们需要产生start、stop信号。所以在门阵内部逻辑中,我们要有一个比SCL时钟快的时钟,并且最好是SCL的2倍,4倍,8倍这个样子的。

模块需要输入两个时钟进来:

之后,根据IIC的时序,有start、写器件地址、写寄存器地址、数据、ack、noack、stop等操作时序,分别写了对应的状态机。这样就把每一种操作时序都变成了积木形式,根据总的大时序,按顺序调用状态机即可。

这里先不展开每个状态机是怎么写的,我们先看一个字节写操作的控制状态机怎么写:

也就是说,主流程状态机,只是控制调用了各个子状态机,把他们有序的组织起来了,至于每个时序中应该怎么做,那是每个子状态机的事情。

这里不展开所有状态机,讲了start和写器件地址基本包含了主要的操作过程,其他的就大家自己再去看程序了。

这个S_sda_0i1o_s和S_sda_s是干啥的呢?

这里就要先明确一件事情,就是模块的port中,不能出现inout型的信号。

所有类似于SDA这样的双向信号都要在模块的port中拆开,拆成一个in、一个out、外加一个方向控制端。

也就是这样的:

这个是为啥呢?我们看quartus中的双向信号实现的示例程序:

看下这样写出来的RTL是什么:

 引用:FPGA 双向口的使用及Verilog实现 - 知乎

下面再看下字节写,字节写里包含了ack的判断:

那么,这个S_sda_s和S_sda_w、S_sda_0i1o_s和S_sda_0i1o_w都是在自己状态机里控制的,这个怎么传到最终总的流程控制上呢?

结合下面的程序,就能实现把每个子状态机的控制传到上层模块了。

看下RTL:

之后我们通读一遍程序代码lb2iic_module.vhd:

建立最顶层,树形结构是这样的:

 

顶层程序的写法:

单字节写

测试程序:

         process(I_rst, S_clk_40M)

         begin

                  if I_rst = '0' then

                          S_cs_n <= '1';

                          S_wr_n <= '1';

                          S_rd_n <= '1';

                          ST_wr <= M_idle;

                  elsif S_clk_40M'event and S_clk_40M = '1' then

                          S_busy_bufs(0) <= S_busy;

                          S_busy_bufs(1) <= S_busy_bufs(0);

                          --单字节写

                          case ST_wr is

                                   when M_idle =>

                                            if I_key_start = '0' then

                                                     ST_wr <= M_wr;   

                                            end if;

                                   when M_wr =>

                                            S_cs_n <= '0';

                                            S_wr_n <= '0';

                                            S_addr <= x"15";

                                            S_data_in <= x"5a";

                                            S_byte_num <= 1;

                                            if S_abyte = '1' then

                                                     S_cs_n <= '1';

                                                     S_wr_n <= '1';                                                 

                                            end if;

                                   when others =>

                                            ST_wr <= M_idle;

                          end case;

                  end if;

         end process;

连续写:

测试程序:

         process(I_rst, S_clk_40M)

         begin

                  if I_rst = '0' then

                          S_cs_n <= '1';

                          S_wr_n <= '1';

                          S_rd_n <= '1';

                          ST_wr <= M_idle;

                  elsif S_clk_40M'event and S_clk_40M = '1' then

                          S_busy_bufs(0) <= S_busy;

                          S_busy_bufs(1) <= S_busy_bufs(0);

                          --连续写

                          case ST_wr is

                                   when M_idle =>

                                            if I_key_start = '0' then

                                                     S_cs_n <= '0';

                                                     S_wr_n <= '0';

                                                     S_addr <= x"15";

                                                     S_data_in <= x"5a";

                                                     S_byte_num <= 8;

                                                     ST_wr <= M_wr;   

                                            end if;

                                   when M_wr =>

                                            if S_abyte = '1' then

                                                     S_data_in <= S_data_in + '1';                                              

                                            end if;

                                            if S_busy_bufs(1) = '1' and S_busy_bufs(0) = '0' then

                                                     S_cs_n <= '1';

                                                     S_wr_n <= '1';                                                 

                                            end if;

                                   when others =>

                                            ST_wr <= M_idle;

                          end case;

                  end if;

         end process;

单字节读

测试程序:

         process(I_rst, S_clk_40M)

         begin

                  if I_rst = '0' then

                          S_cs_n <= '1';

                          S_wr_n <= '1';

                          S_rd_n <= '1';

                          ST_wr <= M_idle;

                  elsif S_clk_40M'event and S_clk_40M = '1' then

                          S_busy_bufs(0) <= S_busy;

                          S_busy_bufs(1) <= S_busy_bufs(0);

                          --单字节读

                          case ST_wr is

                                   when M_idle =>

                                            if I_key_start = '0' then

                                                     S_cs_n <= '0';

                                                     S_rd_n <= '0';

                                                     S_addr <= x"15";

                                                     S_byte_num <= 1;

                                                     ST_wr <= M_wr;   

                                            end if;

                                   when M_wr =>

                                            if S_busy_bufs(1) = '1' and S_busy_bufs(0) = '0' then

                                                     S_cs_n <= '1';

                                                     S_rd_n <= '1';      

                                                     S_data_reg <= S_data_out;                               

                                            end if;

                                   when others =>

                                            ST_wr <= M_idle;

                          end case;

                  end if;

         end process;

连续读

测试程序:

         process(I_rst, S_clk_40M)

         begin

                  if I_rst = '0' then

                          S_cs_n <= '1';

                          S_wr_n <= '1';

                          S_rd_n <= '1';

                          ST_wr <= M_idle;

                  elsif S_clk_40M'event and S_clk_40M = '1' then

                          S_busy_bufs(0) <= S_busy;

                          S_busy_bufs(1) <= S_busy_bufs(0);

                          --连续读

                          case ST_wr is

                                   when M_idle =>

                                            if I_key_start = '0' then

                                                     S_cs_n <= '0';

                                                     S_rd_n <= '0';

                                                     S_addr <= x"15";

                                                     S_byte_num <= 8;

                                                     ST_wr <= M_wr;   

                                            end if;

                                   when M_wr =>

                                            if S_abyte = '1' then

                                                     S_data_reg <= S_data_out;                                                 

                                            end if;

                                            if S_busy_bufs(1) = '1' and S_busy_bufs(0) = '0' then

                                                     S_cs_n <= '1';

                                                     S_rd_n <= '1';                                

                                            end if;

                                   when others =>

                                            ST_wr <= M_idle;

                          end case;

                  end if;

         end process;

波形:

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: FPGA(现场可编程门阵列)Localbus是一种在FPGA芯片内部实现的总线结构,用于连接芯片内部不同的功能模块。Localbus可以使不同的模块之间进行数据传输和通信,实现内部组件的有效协同工作。 FPGA Localbus的主要特点如下: 1. 高速传输:Localbus内部通信速度较快,能够满足对高速数据传输的要求。这样可以确保模块之间的快速数据交换,提高系统的整体工作效率。 2. 灵活性:FPGA Localbus可以根据需要自定义总线结构和协议,使得不同类型的模块可以灵活地连接和通信。这为设计者提供了较大的自由度,可以根据具体的应用需求进行定制化设计。 3. 可扩展性:Localbus可以支持多种类型的功能模块,如存储器、处理器、输入输出接口等。这使得FPGA芯片可以应用于不同的领域,提供更多的可扩展性和适应性。 4. 低成本:使用FPGA Localbus可以减少硬件成本,因为FPGA芯片内部的连接更加简单直接,无需额外的物理连接线路。同时,通过对总线功能的灵活配置,可以减少对外部外设的依赖,从而降低整体设计的成本。 总之,FPGA Localbus是一种在FPGA芯片内部实现的高速、灵活和可扩展的总线结构,可以实现芯片内部不同功能模块之间的数据传输和通信。它的使用可以提高系统的性能和效率,降低设计成本,为FPGA芯片的应用提供更多的灵活性和可定制性。 ### 回答2: FPGA LocalBus,即现场可编程逻辑门阵列局部总线,是一种用于连接FPGA内部模块的总线架构。它提供了一种高效的数据传输机制,用于连接FPGA的硬核和可编程逻辑资源。 FPGA LocalBus的设计目的是通过简化FPGA内部模块之间的通信,提供低延迟、高带宽和高效能的数据传输。与外部总线不同,FPGA LocalBus被设计成在FPGA芯片内部运行,以最大程度地利用FPGA的并行处理能力。 FPGA LocalBus通常采用并行通信的方式,通过多个数据线同时传输数据。这样可以在较短的时间内传输大量数据,提高总线的带宽。局部总线是一种点对点的连接方式,只有单个发送器和接收器之间存在通信,避免了总线抢占和冲突问题。 在FPGA设计中,各种IP核、片上RAM、DSP等硬核资源常常需要通过局部总线进行通信。FPGA LocalBus提供了一种共享资源的方式,允许多个模块同时访问同一资源。因此,FPGA LocalBus的设计需要考虑总线的并发性和协议的冲突解决机制,以保证通信的准确性和稳定性。 总之,FPGA LocalBus是一种用于连接FPGA内部模块的高效数据传输总线。它通过并行通信和点对点连接的方式,实现了低延迟、高带宽和高效能的数据传输。在FPGA设计中,FPGA LocalBus被广泛地应用于各种IP核、硬核资源之间的通信,并具有重要的作用和意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值