最近将多年来收集到的教学视频、国内外图书、源码等整理整合拿出来,涉及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从设备时)。

VI.Stop

与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;

波形:

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

  1. 郭天祥51单片机教程--- IIC总线

    郭天祥51单片机教程- IIC总线 #include <reg52.h> #define uchar unsigned charsbit SDA = P2^0; //数据总线 sbit S ...

  2. 【乌拉喵.教程】不同负载下继电器的保护电路设计(解决继电器触点粘接的问题)

    继电器触点"粘接"总结         袁坤 20170110 视频讲解: https://www.bilibili.com/video/BV1Zb4y1t7Ht/https:// ...

  3. fpga驱动oled iic显示代码_【接口时序】6、IIC总线的原理与Verilog实现

    欢迎FPGA工程师加入官方微信技术群 点击蓝字关注我们FPGA之家-中国最好最大的FPGA纯工程师社群 一. 软件平台与硬件平台 软件平台: 1.操作系统:Windows-8.1 2.开发套件:ISE ...

  4. 【转】IIC总线的FPGA实现

    转自:http://www.cnblogs.com/sunev/archive/2012/05/17/2506731.html 一.摘要 DE2_TV中,有关于寄存器的配置的部分,采用的方法是通过II ...

  5. (3)zynq FPGA AXI4_Stream总线介绍

    1.1 zynq FPGA AXI4_Stream总线介绍 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)zynq FPGA AXI4_Stream总线介绍: 5)结束 ...

  6. (2)zynq FPGA AXI_Lite总线介绍

    1.1 zynq FPGA AXI_Lite总线介绍 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)zynq FPGA AXI_Lite总线介绍: 5)结束语. 1.1 ...

  7. FPGA IIC总线协议简介

    1.1 FPGA IIC总线协议简介 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA IIC总线协议简介: 5)结束语. 1.1.2 本节引言 "不积 ...

  8. FPGA实现IIC协议(一)----初识IIC总线

    写在前面 IIC协议系列博文: FPGA实现IIC协议(一)----初识IIC总线 FPGA实现IIC协议(二)----IIC总线的FPGA实现(单次读写驱动) 1.什么是IIC协议 IIC通讯协议( ...

  9. IIC串行通信总线介绍

    学习记录: 近日学习了IIC集成电路总线,记录一下 学习清单: IIC的介绍 IIC的起始信号.终止信号 IIC的数据的发送 IIC的应答信号 主机发送数据流程.主机接收数据流程 学习内容: 1.II ...

最新文章

  1. WR:微生物污染源解析中宿主特异性标记物在中国的表现特征
  2. 从json到抽取关键词
  3. STM32开发 -- 可调直流稳压电源
  4. 如何使用ABP进行软件开发之基础概览
  5. JavaFX鼠标滚动放大缩小图片
  6. mysql limti_mysql优化
  7. Intel Sandy Bridge/Ivy Bridge架构/微架构/流水线 (14) - 读存操作写存操作
  8. 边缘计算应用场景_从6大应用场景,看边缘计算落地生根
  9. Matlab科研绘图颜色补充(特别篇5)—176种美国传统颜色
  10. 【解决】微信播报,连接蓝牙后延迟播报
  11. 机载激光雷达原理与应用科普(四)
  12. 【android】Android Studio创建模拟手机虚拟机
  13. [生存志] 第55节 吴公子札巡访中原
  14. win、linux环境下JAVA+GDAL部署,jar包启动,相关描述,问题解决
  15. 有序列表和无序列表的不同类型
  16. 用PYTHON判断一个数是不是素数
  17. c语言编写消防车声音程序教程,51单片机蜂鸣器模拟救护车消防车等各种报警喇叭声音的学习源代码...
  18. 爱值得 写给深深爱过的人
  19. 最新ThinkPHP福娃源码交易网站源码+功能很强大
  20. 华为鸿蒙山海,华为,求求你放过《山海经》吧!

热门文章

  1. Javascript 从ES5到ES7 - 14 类型运算
  2. 24小时切换简易时钟-51单片机
  3. runtime-compiler 与 runtime-only区别
  4. 洛谷——P3906 Geodetic集合
  5. 开有geodetic engineering的世界著名高校(持续更新)
  6. SQL Server 进阶 01 数据库的设计
  7. 利用Python计算两个地理位置之间的中点
  8. 测试壁纸相机软件叫什么,如何检测照片的拍摄角度,以及如何像查看桌面应用程序一样自动旋转以显示网站?...
  9. 学技术总是很慢,你得培养这些个技巧!
  10. ERP : 投入控制