本章继上节iic通信协议,在理论学习之后,找到一块iic接口的片子——PCF8591,它是一款AD-DA集成芯片。所以本节对iic通信协议不做过多的介绍,重心放在iic的rtl建模,本次通过iic控制PCF8591实现DAC输出功能。
PART1:建模前的准备
包括两部分,一是芯片手册阅读,二是建模思路;
阅读PCF8591芯片手册,获得以下信息:
>PCF8591的指令控制顺序:主机先向其发送地址指令(包括sda方向选择指令);然后是控制字指令;最后是数据指令(dac对应8bit的dac二进制数据,adc对应为sda方向操作控制指令)
>PCF8591具有adc和dac两种功能,选择哪一种功能需要通过指令(2、3指令功能决定)进行确定。
>模块硬件地址应确定,为000(A2A1A0)。
>具体的寄存器配置(指令)就不详细介绍,总结如下:
adc功能配置,先后发送0x90、0x40+channel(取值0、1、2、3,总共有四个通道)、0x91即可;
dac功能配置,先后发送0x90、0x40、user_data(8bit)即可;
>要注意iic通信速率及相关时间的限制,总结如下:
DAC模式输出频率Fmax=11.1KHz;
Fsclk_max=100kHz;(这就对sclk的高低电平持续时间有要求);
开始、结束、数据的建立保持时间(如下表);
【相关参数表格】
【iic时序示意图】
总结一下:
建立时间和保持时间大都与4.7us有关,且Fsclk_max=100K,即Tmin=10us,取5us作为标准,保持时间和建立时间都为5us;
输出信号频率有全范围输出的时间间隔决定(1111_1111à0000_0000),由数据手册知,Tscale=90us,则fout_max=11.1KHz。
以上,就是建模前的准备工作。
PART2:第一次建模(失败)
如下,是第一次建模的仿真波形,整体上sclk和sda的时序都是满足起始和停止条件的,但下载到FPGA中并没有正确的结果。
仿真波形(第一次):
所以究竟是什么原因呢?于是我用signaltap对板子上的信号进行波形抓取,确实,在signaltap中确实抓到了波形,且与仿真的波形基本一致。这时,我一度认为是芯片坏了(寻求一下自我安慰,实际上这个模块是新买的移植没有使用过)。还是不甘心,于是我下老本了,找到一台示波器,我要亲自看看这个数据波形到底是不是真的有(提醒一下大家,signaltap上抓取的波形基本上就是实际用示波器探测到的波形,所以大家不用怀疑signaltap欺骗了你,大家也不用向我这样真的去用示波器看波形)。以下给出示波器波形:
>sda波形:
>sclk波形:
就在这万般无奈之际,闹钟突然闪过一条信息——iic的速度不会非常快。这才意识到,我最开始sclk的频率取得是clk(50MHz)的4分频,也就是接近12.5MHz,而从手册上看到Fsclk_max=100KHz。于是,欣喜地修改了分频系数,本以为可以成功输出目标电压值,然后事实是并没有。不幸,Round1失败。
PART3:建模修改
第二天,并没有花时间去修改代码,而是在思考原因何在?在没有对策之后,我决定重新阅读英文版芯片手册,主要看其通信协议及其注意事项,这才有了本文最开始的图和表,也就引出一个很重要的概念——信号的建立时间和保持时间。
所以,我是走了弯路的,这里希望大家引以为戒。我犯了一个错误,一开始值看了中文版的芯片手册,而里面并没有给出相关的通信协议的注意事项(因为中文版的资料是出自他人之手的加工品,它不可能像原版手册那么全面地介绍了芯片使用的方方面面),所以,大家以后还是尽量阅读英文原版手册(或者至少出问题时请及时去参看原文手册)。
以上是建议,此外,就是建立时间和保持时间,这个概念真的非常重要,是任何一个数字设计者必须熟悉的概念。所以,在进行通信协议建模或者数字设计时,心中时刻要有这两个的概念。此后,我也将继续学习相关知识,可能的话建立相关专题,为大家分享的我的学习经历。
回归主题,意识到问题所在,对照最开始的建模代码,不满足时序要求的最主要的两点:一是start的相关时序;二是stop的相关时序。主要就是时间过短,最终的解决方法是折中取5us左右作为相关时序的持续时间。对症下药之后,一遍出电压,成功驱动。以下给出dac=1100_1111时的电压输出值,V_the=4.0V左右(基准4.95V),实际输出V_act=3.95V。这样的误差在正常范围内。
仿真波形如下:(dac_reg=1111_1111)
SignalTap_ii中的波形:
其中sda线上在第8bit数据输出后悔出现一个小的脉冲,这是与仿真波形不一致的地方,但这并没有影响最终的输出。(因为sclk为1时数据时有效的)
以上波形对照iic时序示意图,起始和停止的建立时间和保持时间均满足极限要求。
最后,给出整个工程的RTL视图:
总结一下:
<1>iic协议是一种半双工通信协议,sda是双向接口。用verilog设计,可以采用以下方式:定义reg变量表示sda的输出方向,若是输入则定义为高组态,输入由外部连接所决定;正常时zuuuooo输出即可。
reg sda_out; //sda输出寄存器
reg io_link; //sda方向寄存器,1表示输出(dac),0表示输入(adc)
assign sda = io_link ? sda_out : 1'bz; //sda作输入时,将其设置为高阻状态,则sda的实际状态由实际线上的状态决定
<2>使用状态机实现,考虑好是单个状态机实现还是使用嵌套,不是说越多就越高大上,功能稳定是关键;
<3>数字设计特别是涉及到时序相关的,心中一定时刻要有建立时间和保持时间的概念。
以上就是运用PCF8951学习iic通信协议的相关总结。
本设计的相关代码链接: