一天一个设计实例-基于FPGA的数模转换器应用设计
基于FPGA的数模转换器应用设计
1.1.1带 EEPROM 存储器的 12 位MCP4725应用设计
7.4.1.1 MCP4725简介
MCP4725 是低功耗、高精度、单通道的 12 位缓冲电压输出数模转换器 ( Digital-to-Analog Convertor,DAC),具有非易失性存储器( EEPROM)。其片上精密输出放大器使其能够达到轨到轨模拟输出摆幅。
用户可以使用 I2C 接口命令将 DAC 输入和配置数据烧写到非易失性存储器( EEPROM)。非易失性存储器功能使得 DAC 器件在断电期间仍能保持 DAC 输入代码,且 DAC 输出在上电后立即可用。当 DAC 器件用作网络中其他器件的支持器件时,此功能非常有用。
该器件包括用于确保可靠上电的上电复位(Power-On-Reset, POR)电路和用于为 EEPROM 提供编程电压的片上电荷泵。DAC 参考电压由 VDD 直接驱动。在关断模式下,可对输出放大器进行配置,以提供已知的低、中和高电阻输出负载。
MCP4725 具有外部 A0 地址位选择引脚。此 A0 引脚可连接用户应用电路板的 VDD 或 VSS。
MCP4725 具有 2 线型 I2C 兼容串行接口,可用于标准( 100 kHz) 、快速 ( 400 kHz)或高速 (3.4 MHz)模式。
MCP4725 是适用于设计简易且外形小巧的应用的理想DAC 器件,且适合用于要求在断电期间保存 DAC 器件设置的应用。
特性:
· 12 位分辨率
· 片上非易失性存储器 (EEPROM)
· ±0.2 LSB DNL (典型值)
· 外部 A0 地址引脚
· 正常或关断模式
· 快速的稳定时间:6 µs (典型值)
· 外部参考电压 (VDD)
· 轨到轨输出
· 低功耗
· 单电源工作:2.7V 至 5.5V
· I2C™ 接口:- 8 个可用地址- 标准 (100 kbps)、快速 ( 400 kbps)和高速 (3.4 Mbps)模式
· 6 引脚小型 SOT-23 封装
· 扩展级温度范围:-40°C 至 +125°C
应用:
· 设定点或失调微调
· 传感器校准
· 闭环伺服控制
· 低功耗便携式仪表
· PC 外设
· 数据采集系统
器件的功能框图如下图所示:

图7‑19 MCP4725功能框图

图7‑20 MCP4725封装引脚图
表7‑6 MCP4725引脚功能说明
MCP4725 |
名称 |
说明 |
SOT-23 |
||
1 |
VOUT |
模拟输出电压 |
2 |
VSS |
参考地 |
3 |
VDD |
电源电压 |
4 |
SDA |
I2C 串行数据 |
5 |
SCL |
I2C 串行时钟输入 |
6 |
A0 |
I2C 地址位选择引脚 (A0 位)。该引脚可连接到 VSS 或 VDD,或由数字逻辑电平有效驱动。该引脚的逻辑状态决定了 I2C 地址位的 A0 位。 |
1.模拟输出电压 ( VOUT)
VOUT 是 DAC 器件的模拟输出电压。DAC 输出放大器在VSS 至 VDD 的范围内驱动此引脚。
2.电源电压 ( VDD 或 VSS)
VDD 是该器件的电源引脚。VDD 引脚上的电压可用作电源输入以及 DAC 参考输入。VDD 引脚上的电源应尽可能干净,以提供好的 DAC 性能。
该引脚需要一个大约为 0.1 µF 的旁路陶瓷电容接地。还推荐并联一个 10 µF 的钽电容,以进一步削弱应用电路板中的高频噪声。电源电压( VDD)必须保持在 2.7V 至5.5V 的范围内,以进行正常操作。
VSS 为地引脚,是器件的电流返回路径。用户必须通过低阻抗走线将VSS引脚连接至地平面。如果在应用PCB(印刷电路板)中提供了模拟地路径,强烈推荐将 VSS引脚连接到模拟地路径或用电路板上的模拟地平面进行隔离。
3.串行数据引脚 ( SDA)
SDA 是 I2C 接口的串行数据引脚。SDA 引脚用于读写DAC 寄存器和 EEPROM 数据。SDA 引脚是开漏 N 通道驱动器。因此,它需要一个从 VDD 线到 SDA 引脚的上拉电阻。除了在启动和停止条件下以外, SDA 引脚上的数据在时钟信号的高电平期间必须稳定。SDA 引脚的高或低电平状态仅在SCL引脚上的时钟信号为低电平时改变。
4.串行时钟引脚 ( SCL)
SCL 是 I2C 接口的串行时钟引脚。MCP4725 仅用作从器件, SCL 引脚仅接受外部串行时钟。来自主器件的输入数据在 SCL 时钟的上升沿移入 SDA 引脚,而MCP4725 的输出发生在 SCL 时钟的下降沿。SCL 引脚是开漏 N 通道驱动器。因此,它需要一个从 VDD 线到SCL 引脚的上拉电阻。
5.器件地址选择引脚 ( A0)
用户使用该引脚选择 A0 地址位。用户可将此引脚连接到 VSS(逻辑 0)或 VDD(逻辑 1),或由数字逻辑电平(例如 I2C 主器件输出)有效驱动。
地址字节是主器件启动条件后接收到的第一个字节。地址字节的第一部分由 4 位器件代码 (对于 MCP4725,设置为 1100)组成。器件代码后跟三个地址位 (A2、A1 和 A0),这三个地址位的编程方式如下:
· A2 和 A1 位的选择由客户在订货过程中提供。然后,在出厂前编程这些位 (硬连线)
· 如果客户没有提出要求的话,那么 A2 和 A1 编程为 00 (默认)
· A0 位由 A0 引脚的逻辑状态决定。A0 引脚可连接到 VDD 或 VSS,或由数字逻辑电平有效驱动。使用 A0 引脚的优点在于用户能够控制其应用 PCB电路上的 A0 位,而且还可以在同一总线上使用两个一样的 MCP4725 器件。
在器件接收到地址字节时,它将 A0 引脚的逻辑状态与接收到的 A0 地址位进行比较,然后才通过应答位进行响应。在接口通信之前, 需要设置 A0 引脚的逻辑状态。

图7‑21 器件寻址过程说明
7.4.1.2 MCP4725相关术语说明
1.分辨率
分辨率就是划分满量程范围的 DAC 输出状态数。对于12位DAC,分辨率为212或DAC代码范围为0至4095。
2.LSB
最低有效位或两个连续代码之间的理想电压差。

图7‑22 LSB计算公式
3.积分非线性性 ( IntegralNonlinearity, INL)或相对精度
INL 误差是实际代码跳变点及其相应的理想跳变点 (直线)之间的最大偏差。图7‑23显示了 MCP4725 的 INL曲线。使用端点法进行计算。给定输入 DAC 代码处的INL 误差按如下公式计算:


图7‑23 INL 精度
4.微分非线性 ( DifferentialNonlinearity, DNL)
微分非线性误差 (图7‑24)是实际传递函数的代码之间步长大小的测量值。代码间的理想步长大小为 1 LSB。DNL 误差为零意味着每个代码正好是 1 LSB 宽。如果DNL 误差小于 1 LSB,那么 DAC 可保证单调性输出且不会丢失代码。任何两个邻近代码之间的 DNL 误差按如下公式计算:


图7‑24 DNL精度
5.失调误差
失调误差 (图7‑25)是数字输入代码为零时输出电压与零电压之间的偏差。此误差对所有代码的影响是一样大的。在 MCP4725 中,失调误差在出厂时未经微调。但
是,可以通过软件在应用电路中对它进行校准。

图7‑25 失调误差
6.增益误差
增益误差 (见图7‑26)是传递曲线上实际满量程输出电压与理想输出电压之差。增益误差是在消除失调误差(即满量程误差减去失调误差)后计算得到的。
增益误差表示实际传递函数斜率与理想传递函数斜率的匹配程度。增益误差通常用满量程的百分比(% FSR )或 LSB 表示。
在 MCP4725 中,增益误差在出厂时未经校准,且大部分增益误差是由代码范围超过 4000 的输出运算放大器饱和所产生的。对于需要增益误差规格小于 1% 最大值的应用而言,用户可以考虑使用 100 至 4000 之间的DAC代码, 而不是使用整个代码范围(代码0至4095)。代码范围在 100 至 4000 之间的 DAC 输出比满量程范围( 0 至 4095)的线性度更好。可以通过软件在应用中校准增益误差。
7.满量程误差 ( Full-Scale Error,FSE)
满量程误差 (图7‑26)是失调误差与增益误差的总和。它是在所有位均设置为 1 (DAC 输入代码 = FFFh)时的理想 DAC 输出电压和测得的 DAC 输出电压之差。


图7‑26 增益误差和满量程误差
8.增益误差漂移
增益误差漂移是由于环境温度的变化而引起的增益误差变化。增益误差漂移通常用 ppm/oC 表示。
9.失调误差漂移
失调误差漂移是由于环境温度的变化而引起的失调误差变化。失调误差漂移通常用 ppm/oC 表示。
10.稳定时间
稳定时间指在规定的精度范围内, DAC 输出从代码跳变开始到其新的输出值稳定所需的时间延迟。在MCP4725 中,稳定时间是从 DAC 代码从 400h 变为C00h 开始直到 DAC 输出到达其最终值(在 0.5 LSB 范围内)的时间延迟的测量值。
11.主要代码跳变毛刺
主要代码跳变毛刺是 DAC 寄存器中的代码改变状态时注入到 DAC 模拟输出的脉冲能量。它通常是指以 nV-S为单位的毛刺区,且在数字代码在主要跳变 (例如:011...111 至 100... 000,或 100... 000 至 011 ... 111) 内发生 1 LSB 的变化时测得。
12.数字馈通
数字馈通指由于器件的数字输入引脚耦合到输出导致模拟输出中出现的毛刺。以 nV-S 为单位来指定其值,在数字输入引脚上发生满量程变化 (例如:000... 000至 111... 111,或 111... 111 至 000... 000)时测得。数字馈通在 DAC 未写入寄存器时测得。
7.4.1.3 MCP4725输出电压
到 MCP4725 器件的输入代码为无符号的二进制值。输出电压范围为 0V 至 VDD。公式(6.4)给出了输出电压:

64(.)
MCP4725 的 LSB 大小 (示例):
表7‑7 MCP4725 的 LSB 大小
满量程范围( VDD) |
LSB大小 |
条件 |
3.0V |
0.73 mV |
3V / 4096 |
5.0V |
1.22 mV |
5V / 4096 |
7.4.1.4 MCP4725配置及使用
1.非易失性 EEPROM 存储器
MCP4725 器件有一个 14 位宽的 EEPROM 存储器,用来存储配置位( 2 位)和 DAC 输入数据 ( 12 位)。使用 I2C 接口命令可读取和重写这些位。该器件有一个片上电荷泵电路,可在不使用外部编程电压的情况下写EEPROM 存储器位。
在器件接收到 EEPROM 写命令 ( C2 = 0、 C1 = 1 和C0 = 1)时启动 EEPROM 写操作。然后将配置和写数据位传送到EEPROM存储器模块中。状态位RDY/BSY在 EEPROM 写操作期间保持低电平,在写操作完成时变为高电平。在 RDY/BSY 位为低电平时 ( EEPROM写操作期间),忽略所有针对 EEPROM 或 DAC 寄存器的新写命令。表7‑8给出了 EEPROM 位和出厂默认设置。表7‑9给出了 MCP4725 的 DAC 输入寄存器位。
表7‑8 EEPROM 存储器和出厂默认设置 (总位数:14 位)
位名称 |
PD1 |
PD0 |
D11 |
D10 |
D9 |
D8 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
位功能 |
关断选择(2 位) |
DAC 输入数据 ( 12 位) |
||||||||||||
出厂默认值 |
0 |
0 (1) |
1 (2) |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
注 1:见表 5-2,以了解详细信息。
2:位 D11 = 1 (而所有其他位为 0)使器件输出 0.5 * VDD 的电压 ( = 中等量程输出)。
表7‑9 DAC 寄存器
位名称 |
C2 |
C1 |
C0 |
RDY/BSY |
POR |
PD1 |
PD0 |
D11 |
D10 |
D9 |
D8 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
位功能 |
命令类型 |
(1) |
关断选择 |
数据 ( 12 位) |
注 1:写 EEPROM 状态指示位 (0:EEPROM 写操作未完成, 1:EEPROM 写操作完成。)
当器件连接到 I2C 总线时,器件作为从器件工作。使用I2C 接口命令,主器件 ( MCU)可以读 / 写 DAC 输入寄存器或 EEPROM。MCP4725 器件地址包含 4 个固定位 (1100 = 器件代码)和 3 个地址位 ( A2、 A1 和A0)。A2 和 A1 位是在出厂前硬连线的,而 A0 位由 A0引脚的逻辑状态决定。A0 引脚可连接到 VDD 或 VSS,或由数字逻辑电平有效驱动。
2.写命令
写命令用于将配置位和 DAC 输入代码装载到 DAC 寄存器,或写入器件的 EEPROM。通过使用 3 个写命令类型位 (C2、 C1 和 C0)定义写命令类型。
快速模式的写命令 ( C2 = 0, C1 = 0,C0 = X, X = 忽略)
快速写命令用于更新 DAC 寄存器。器件 EEPROM 中的数据不受此命令影响。此命令可更新关断模式选择位( PD1 和 PD0)和 DAC 寄存器中 12 个 DAC 输入代码位。图7‑27给出了 MCP4725 器件快速写命令的示例。

注 1:A2 和 A1 位是在出厂前通过硬连线方式编程的,而 A0 位由 A0 引脚的逻辑状态决定。
注 2:器件在第三个字节的 ACK 脉冲信号的下降沿更新 VOUT。
图7‑27 使用快速模式写命令写 DAC 寄存器:(C2,C1) = (0,0)
DAC 输入寄存器的写命令 ( C2 = 0,C1 = 1, C0 = 0)
在 MCP4725 中,此命令执行与上一节 “快速模式的写命令( C2 = 0, C1 = 0, C0 = X, X = 忽略) ” 中快速模式命令相同的功能。图7‑28给出了 MCP4725 的写命令协议。
如图7‑28所示,第三和第四个字节中的 D11 - D0 位是DAC 输入数据。第四个字节中的最后 4 位 ( X、 X、 X和 X)忽略。
在接收到最后一个字节(第四个字节)后,器件执行主器件写命令。主器件可发送一个停止位来终止当前序列,或依次发送一个重复启动位和一个地址字节。如果器件在第四个字节后连续接收到三个数据字节,那么它将使用最后三个输入数据字节来更新第二个至第四个数据字节。
寄存器的内容在第四个字节接收完成后更新。如果在完成第四个字节之前 I2C 与主器件的通信结束,那么器件忽略所有未全部接收到的数据字节。

注 1:在 EEPROM 写操作期间, RDY/BSY 位保持“低电平”。EEPROM 写模式期间,包括重复的字节在内的任何新的写命令都将被忽略。
在 EEPROM 写操作完成后, RDY/BSY 位置为 “高电平”。
图7‑28 DAC 输入寄存器和 EEPROM 的写命令
DAC 输入寄存器和 EEPROM 的写命
令 ( C2 = 0, C1 = 1, C0 = 1)当器件接收到此命令时,它 (a) 将配置和数据位装载到DAC 寄存器, 且 (b) 写 EEPROM。当器件写 EEPROM时, RDY/BSY 位变为低电平,且保持低电平直到EEPROM 写操作完成为止。可通过读命令对 RDY/BSY位的状态进行监视。图7‑28给出了此写命令协议的详细信息,而图7‑29给出了读命令的详细信息。
表7‑11 给出了写命令类型及其功能。MCP4725 有 3 种写命令类型。
快速模式的写命令 ( C2 = 0, C1 = 0,C0 = X, X = 忽略)
快速写命令用于更新 DAC 寄存器。器件 EEPROM 中的数据不受此命令影响。此命令可更新关断模式选择位( PD1 和 PD0)和 DAC 寄存器中 12 个 DAC 输入代码位。图7‑27给出了 MCP4725 器件快速写命令的示例。

注 1:A2 和 A1 位是在出厂前通过硬连线方式编程的,而 A0 位由 A0 引脚的逻辑状态决定。
注 2:器件在第三个字节的 ACK 脉冲信号的下降沿更新 VOUT。
图7‑27 使用快速模式写命令写 DAC 寄存器:(C2,C1) = (0,0)
DAC 输入寄存器的写命令 ( C2 = 0,C1 = 1, C0 = 0)
在 MCP4725 中,此命令执行与上一节 “快速模式的写命令( C2 = 0, C1 = 0, C0 = X, X = 忽略) ” 中快速模式命令相同的功能。图7‑28给出了 MCP4725 的写命令协议。
如图7‑28所示,第三和第四个字节中的 D11 - D0 位是DAC 输入数据。第四个字节中的最后 4 位 ( X、 X、 X和 X)忽略。
在接收到最后一个字节(第四个字节)后,器件执行主器件写命令。主器件可发送一个停止位来终止当前序列,或依次发送一个重复启动位和一个地址字节。如果器件在第四个字节后连续接收到三个数据字节,那么它将使用最后三个输入数据字节来更新第二个至第四个数据字节。
寄存器的内容在第四个字节接收完成后更新。如果在完成第四个字节之前 I2C 与主器件的通信结束,那么器件忽略所有未全部接收到的数据字节。

注 1:在 EEPROM 写操作期间, RDY/BSY 位保持“低电平”。EEPROM 写模式期间,包括重复的字节在内的任何新的写命令都将被忽略。
在 EEPROM 写操作完成后, RDY/BSY 位置为 “高电平”。
图7‑28 DAC 输入寄存器和 EEPROM 的写命令
DAC 输入寄存器和 EEPROM 的写命
令 ( C2 = 0, C1 = 1, C0 = 1)当器件接收到此命令时,它 (a) 将配置和数据位装载到DAC 寄存器, 且 (b) 写 EEPROM。当器件写 EEPROM时, RDY/BSY 位变为低电平,且保持低电平直到EEPROM 写操作完成为止。可通过读命令对 RDY/BSY位的状态进行监视。图7‑28给出了此写命令协议的详细信息,而图7‑29给出了读命令的详细信息。
表7‑11中的四个“保留”命令留作以后使用。MCP4725忽略 “保留”命令。图7‑27和图7‑28中给出了写命令协议示例。
表7‑10给出了输入数据代码的编码方式。数据的最高有效位始终最先转换,格式采用单极性二进制编码。
表7‑10 输入数据编码
输入代码 |
标称输出电压 ( V) |
111111111111 ( FFFh) |
VDD - 1 LSB |
111111111110 ( FFEh) |
VDD - 2 LSB |
000000000010 ( 002h) |
2 LSB |
000000000001 ( 001h) |
1 LSB |
000000000000 ( 000h) |
0 |
快速模式的写命令 ( C2 = 0, C1 = 0,C0 = X, X = 忽略)
快速写命令用于更新 DAC 寄存器。器件 EEPROM 中的数据不受此命令影响。此命令可更新关断模式选择位( PD1 和 PD0)和 DAC 寄存器中 12 个 DAC 输入代码位。图7‑27给出了 MCP4725 器件快速写命令的示例。

注 1:A2 和 A1 位是在出厂前通过硬连线方式编程的,而 A0 位由 A0 引脚的逻辑状态决定。
注 2:器件在第三个字节的 ACK 脉冲信号的下降沿更新 VOUT。
图7‑27 使用快速模式写命令写 DAC 寄存器:(C2,C1) = (0,0)
DAC 输入寄存器的写命令 ( C2 = 0,C1 = 1, C0 = 0)
在 MCP4725 中,此命令执行与上一节 “快速模式的写命令( C2 = 0, C1 = 0, C0 = X, X = 忽略) ” 中快速模式命令相同的功能。图7‑28给出了 MCP4725 的写命令协议。
如图7‑28所示,第三和第四个字节中的 D11 - D0 位是DAC 输入数据。第四个字节中的最后 4 位 ( X、 X、 X和 X)忽略。
在接收到最后一个字节(第四个字节)后,器件执行主器件写命令。主器件可发送一个停止位来终止当前序列,或依次发送一个重复启动位和一个地址字节。如果器件在第四个字节后连续接收到三个数据字节,那么它将使用最后三个输入数据字节来更新第二个至第四个数据字节。
寄存器的内容在第四个字节接收完成后更新。如果在完成第四个字节之前 I2C 与主器件的通信结束,那么器件忽略所有未全部接收到的数据字节。

注 1:在 EEPROM 写操作期间, RDY/BSY 位保持“低电平”。EEPROM 写模式期间,包括重复的字节在内的任何新的写命令都将被忽略。
在 EEPROM 写操作完成后, RDY/BSY 位置为 “高电平”。
图7‑28 DAC 输入寄存器和 EEPROM 的写命令
DAC 输入寄存器和 EEPROM 的写命
令 ( C2 = 0, C1 = 1, C0 = 1)当器件接收到此命令时,它 (a) 将配置和数据位装载到DAC 寄存器, 且 (b) 写 EEPROM。当器件写 EEPROM时, RDY/BSY 位变为低电平,且保持低电平直到EEPROM 写操作完成为止。可通过读命令对 RDY/BSY位的状态进行监视。图7‑28给出了此写命令协议的详细信息,而图7‑29给出了读命令的详细信息。
表7‑11 快速模式写命令
C2 |
C1 |
C0 |
命令名 |
功能 |
0 |
0 |
X |
快速模式 |
此命令用于更改 DAC 寄存器。EEPROM 不受影响 |
0 |
0 |
X |
“ |
“ |
0 |
1 |
0 |
写 DAC 寄存器 |
将配置位和数据代码装载到 DAC 寄存器 |
0 |
1 |
1 |
写 DAC 寄存器和EEPROM |
(a) 将配置位和数据代码装载到 DAC 寄存器并(b) 写 EEPROM |
1 |
0 |
0 |
保留 |
留作以后使用 |
1 |
0 |
1 |
保留 |
留作以后使用 |
1 |
1 |
0 |
保留 |
留作以后使用 |
1 |
1 |
1 |
保留 |
留作以后使用 |
注 1:X = 忽略。快速模式未使用 C0 位。
2:MCP4725 忽略 “保留”命令。
3.读命令
如果 R/W 位设置为逻辑 “高电平”,那么器件在 SDA引脚上输出 DAC 寄存器 和 EEPROM 数据。图7‑29给出了读寄存器和 EEPROM 数据的示例。图7‑29中的第二个字节指示器件操作的当前状态。RDY/BSY 位指示EEPROM 写状态。RDY/BSY 位在 EEPROM 写操作期间保持低电平,在写操作完成后变为高电平。

注 1:第 2 - 6 个字节在第 6 个字节后重复。
注 2:X 位忽略。
图7‑29 读命令和输出数据格式
7.4.1.5 I2C™ 串行时序规范
详见之前I2C时序分析。
7.4.1.6 MCP4725应用设计
时序主要参考IIC时序,在此不再赘述,主要分析下功能操作。
器件的设置见上几节分析,下面分析调用过程:
写器件地址(8’HC0、8’HC2)器件代吗:1100;地址位A2,A1,A0为 0 , 0 , X;-> 0(启动位)110 0(器件代码) 001(A2、A1、A0)
写命令字(写EEPROM或者DAC寄存器或者两者)
(0x40) // Writes data to the DAC
(0x60) // Writes data to the DAC and the EEPROM (persisting the assigned value after reset)
写DAC高位地址(D11、D10、D9、D8、D7、D6、D5、D4)0到4096
写DAC地位地址(D3、D2、D1、D0、0、0、0、0)
表7‑12 输入数据编码
输入代码 |
标称输出电压 ( V) |
111111111111 ( FFFh) |
VDD - 1 LSB |
111111111110 ( FFEh) |
VDD - 2 LSB |
000000000010 ( 002h) |
2 LSB |
000000000001 ( 001h) |
1 LSB |
000000000000 ( 000h) |
0 |
根据以上分析,建立如下基础模块的结构图

图7‑30 MCP4725基础模块(iic_mcp4725_demo.v)的建模图
图4‑29是 PCF8563基础模块的建模图,内容包含命令控制模块与IIC IP模块,功能函数模块负责最基础的读写操作,命令控制模块则负责功能调度,准备访问字节等任务。换之,函数功能模块的右边是驱动硬件资源的顶层信号。
下面分析下iic.v模块

图7‑31 MCP4725 IIC ip模块(iic.v)的结构图
相关的介绍详见代码说明。
代码7‑7 MCP4725 IIC ip模块(iic.v)
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-09-08 19:55:15 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-09-14 01:26:13 7.//# Description: 8.//# @Modification History: 2019-09-14 00:50:55 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-09-14 00:50:55 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module iic_mcp4725 17.( 18. input CLOCK, RESET, 19. output SCL, 20. inout SDA, 21. input [1:0]iCall, 22. output oDone, 23. input [7:0]iAddr, 24. input [7:0]iData, 25. output [7:0]oData 26.); 27. parameter FCLK = 10'd125, FHALF = 10'd62, FQUARTER = 10'd31; //(1/400E+3)/(1/50E+6) 28. parameter THIGH = 10'd30, TLOW = 10'd65, TR = 10'd15, TF = 10'd15; 29. parameter THD_STA = 10'd30, TSU_STA = 10'd30, TSU_STO = 10'd30; 30. parameter WRFUNC1 = 5'd8; 31. parameter WRFUNC2 = 5'd9, RDFUNC = 5'd19; 32. 33. reg [4:0]i; 34. reg [4:0]Go; 35. reg [9:0]C1; 36. reg [7:0]D1; 37. reg rSCL,rSDA; 38. reg isAck, isDone, isQ; 39. 40. always @ ( posedge CLOCK or negedge RESET ) 41. if( !RESET ) 42. begin 43. { i,Go } <= { 5'd0,5'd0 }; 44. C1 <= 10'd0; 45. D1 <= 8'd0; 46. { rSCL,rSDA,isAck,isDone,isQ } <= 5'b11101; 47. end 48. else if( iCall[1] ) 49. case( i ) 50. 51. 0: // Start 52. begin 53. isQ = 1; 54. rSCL <= 1'b1; 55. 56. if( C1 == 0 ) rSDA <= 1'b1; 57. else if( C1 == (TR + THIGH) ) rSDA <= 1'b0; 58. 59. if( C1 == (FCLK) -1) begin C1 <= 10'd0; i <= i + 1'b1; end 60. else C1 <= C1 + 1'b1; 61. end 62. 63. 1: // Write Device Addr //8'hC0(A0 -- GND if A0 -- VDD then 8'hC2) 64. begin D1 <= {4'b1100, 3'b000, 1'b0}; i <= 5'd8; Go <= i + 1'b1; end 65. 66. 2: // Wirte Word Addr 67. begin D1 <= 8'b0100_0000; i <= WRFUNC1; Go <= i + 1'b1; end 68. 69. 3: // Write Data 70. begin D1 <= iAddr; i <= WRFUNC1; Go <= i + 1'b1; end 71. 72. 4: // Write Data 73. begin D1 <= iData; i <= WRFUNC1; Go <= i + 1'b1; end 74. /*************************/ 75. 76. 5: // Stop 77. begin 78. isQ = 1'b1; 79. 80. if( C1 == 0 ) rSCL <= 1'b0; 81. else if( C1 == FQUARTER ) rSCL <= 1'b1; 82. 83. if( C1 == 0 ) rSDA <= 1'b0; 84. else if( C1 == (FQUARTER + TR + TSU_STO ) ) rSDA <= 1'b1; 85. 86. if( C1 == (FQUARTER + FCLK) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 87. else C1 <= C1 + 1'b1; 88. end 89. 90. 6: 91. begin isDone <= 1'b1; i <= i + 1'b1; end 92. 93. 7: 94. begin isDone <= 1'b0; i <= 5'd0; end 95. 96. /*******************************/ //function 97. 98. 8,9,10,11,12,13,14,15: 99. begin 100. isQ = 1'b1; 101. rSDA <= D1[15-i];// 102. 103. if( C1 == 0 ) rSCL <= 1'b0; 104. else if( C1 == (TF + TLOW) ) rSCL <= 1'b1; 105. 106. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 107. else C1 <= C1 + 1'b1; 108. end 109. 110. 16: // waiting for acknowledge 111. begin 112. isQ = 1'b0; 113. if( C1 == FHALF ) isAck <= SDA; 114. 115. if( C1 == 0 ) rSCL <= 1'b0; 116. else if( C1 == FHALF ) rSCL <= 1'b1; 117. 118. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 119. else C1 <= C1 + 1'b1; 120. end 121. 122. 17: 123. if( isAck != 0 ) i <= 5'd0; 124. else i <= Go; 125. 126. /*******************************/ // end function 127. 128. endcase 129. 130. else if( iCall[0] ) 131. case( i ) 132. 133. 0: // Start 134. begin 135. isQ = 1; 136. rSCL <= 1'b1; 137. 138. if( C1 == 0 ) rSDA <= 1'b1; 139. else if( C1 == (TR + THIGH) ) rSDA <= 1'b0; 140. 141. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 142. else C1 <= C1 + 1'b1; 143. end 144. 145. 1: // Write Device Addr 146. begin D1 <= {4'b1010, 3'b000, 1'b0}; i <= 5'd9; Go <= i + 1'b1; end 147. 148. 2: // Wirte Word Addr 149. begin D1 <= iAddr; i <= WRFUNC2; Go <= i + 1'b1; end 150. 151. 3: // Start again 152. begin 153. isQ = 1'b1; 154. 155. if( C1 == 0 ) rSCL <= 1'b0; 156. else if( C1 == FQUARTER ) rSCL <= 1'b1; 157. else if( C1 == (FQUARTER + TR + TSU_STA + THD_STA + TF) ) rSCL <= 1'b0; 158. 159. if( C1 == 0 ) rSDA <= 1'b0; 160. else if( C1 == FQUARTER ) rSDA <= 1'b1; 161. else if( C1 == ( FQUARTER + TR + THIGH) ) rSDA <= 1'b0; 162. 163. if( C1 == (FQUARTER + FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 164. else C1 <= C1 + 1'b1; 165. end 166. 167. 4: // Write Device Addr ( Read ) 168. begin D1 <= {4'b1010, 3'b000, 1'b1}; i <= 5'd9; Go <= i + 1'b1; end 169. 170. 5: // Read Data 171. begin D1 <= 8'd0; i <= RDFUNC; Go <= i + 1'b1; end 172. 173. 6: // Stop 174. begin 175. isQ = 1'b1; 176. 177. if( C1 == 0 ) rSCL <= 1'b0; 178. else if( C1 == FQUARTER ) rSCL <= 1'b1; 179. 180. if( C1 == 0 ) rSDA <= 1'b0; 181. else if( C1 == (FQUARTER + TR + TSU_STO) ) rSDA <= 1'b1; 182. 183. if( C1 == (FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 184. else C1 <= C1 + 1'b1; 185. end 186. 187. 7: 188. begin isDone <= 1'b1; i <= i + 1'b1; end 189. 190. 8: 191. begin isDone <= 1'b0; i <= 5'd0; end 192. 193. /*******************************/ //function 194. 195. 9,10,11,12,13,14,15,16: 196. begin 197. isQ = 1'b1; 198. 199. rSDA <= D1[16-i]; 200. 201. if( C1 == 0 ) rSCL <= 1'b0; 202. else if( C1 == (TF + TLOW) ) rSCL <= 1'b1; 203. 204. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 205. else C1 <= C1 + 1'b1; 206. end 207. 208. 17: // waiting for acknowledge 209. begin 210. isQ = 1'b0; 211. 212. if( C1 == FHALF ) isAck <= SDA; 213. 214. if( C1 == 0 ) rSCL <= 1'b0; 215. else if( C1 == FHALF ) rSCL <= 1'b1; 216. 217. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 218. else C1 <= C1 + 1'b1; 219. end 220. 221. 18: 222. if( isAck != 0 ) i <= 5'd0; 223. else i <= Go; 224. 225. /*****************************/ 226. 227. 19,20,21,22,23,24,25,26: // Read 228. begin 229. isQ = 1'b0; 230. if( C1 == FHALF ) D1[26-i] <= SDA; 231. 232. if( C1 == 0 ) rSCL <= 1'b0; 233. else if( C1 == FHALF ) rSCL <= 1'b1; 234. 235. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 236. else C1 <= C1 + 1'b1; 237. end 238. 239. 27: // no acknowledge 240. begin 241. isQ = 1'b1; 242. //if( C1 == 100 ) isAck <= SDA; 243. 244. if( C1 == 0 ) rSCL <= 1'b0; 245. else if( C1 == FHALF ) rSCL <= 1'b1; 246. 247. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= Go; end 248. else C1 <= C1 + 1'b1; 249. end 250. 251. /*************************************/ // end fucntion 252. 253. endcase 254. 255. /***************************************/ 256. 257. assign SCL = rSCL; 258. assign SDA = isQ ? rSDA : 1'bz; 259. assign oDone = isDone; 260. assign oData = D1; 261. 262. /***************************************/ 263. 264.endmodule |
注意以上IIC模块使用的时候,要修改写操作地址 /读操作地址,即64、67行代码需要修改。
接下来就是调用该模块后进行赋值设置输出电压大小,具体如下:
代码 7‑8 MCP7425 功能函数模块(iic_mcp4725_demo.v)
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-09-08 19:55:15 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-09-14 01:00:31 7.//# Description: 8.//# @Modification History: 2019-09-14 01:00:31 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-09-14 01:00:31 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module iic_mcp4725_demo 17.( 18. input CLOCK, RESET, 19. output SCL, 20. inout SDA, 21. output [7:0]DIG, 22. output [5:0]SEL 23.); 24. wire [7:0]DataU1; 25. wire DoneU1; 26. 27. iic_mcp4725 U1 28. ( 29. .CLOCK( CLOCK ), 30. .RESET( RESET ), 31. .SCL( SCL ), // > top 32. .SDA( SDA ), // <> top 33. .iCall( isCall ), // < core 34. .oDone( DoneU1 ), // > core 35. .iAddr( D1 ), // < core 36. .iData( D2 ), // < core 37. .oData( DataU1 ) // > core 38. ); 39. 40. 41. /***************************/ 42. 43. reg [3:0]i; 44. reg [7:0]D1,D2; 45. reg [23:0]D3; 46. reg [1:0]isCall; 47. 48. always @ ( posedge CLOCK or negedge RESET ) // core 49. if( !RESET ) 50. begin 51. i <= 4'd0; 52. { D1,D2 } <= { 8'd0,8'd0 }; 53. D3 <= 24'd0; 54. isCall <= 2'b00; 55. end 56. else 57. case( i ) 58. 59. 0: 60. if( DoneU1 ) begin isCall <= 2'b00; i <= i + 1'b1; end 61. else begin isCall <= 2'b10; D1 <= 8'hFF; D2 <= 8'hF0; end 62. 63. 64. 1: 65. i <= i; 66. 67. endcase 68. 69.endmodule |
主要注意61行的赋值,目前赋值FFF0理论上输出电压为VDD,具体可以利用万用表测量VOUT对GND电压即可,本设计VDD为5V,实际测量输出电压VDD±LSB。
