EDN首页   博客首页

最新日志

发表于:2007-12-17 15:55:54
标签:无标签

1

高速电路设计中,走线的等长、关键信号的阻抗控制、差分走线的设置

摘要:本文首先简述了高性能ARM9微处理器EP9315集成的外设接口及硬件结构框架,提出了当前高速电路设计中的问题;然后,详细介绍了利用Allegro实现嵌入式系统中SDRAM和IDE总线接口的电路设计;最后以Cirrus Logic公司的CS8952为例,阐述了物理层接口芯片的布线准则及其在Allegro中的实现。
关键词:嵌入式系统; Allegro;等长;差分对;阻抗控制

引 言

随着嵌入式微处理器主频的不断提高,信号的传输处理速度越来越快,当系统时钟频率达到100 MHZ以上,传统的电路设计方法和软件已无法满足高速电路设计的要求。在高速电路设计中,走线的等长、关键信号的阻抗控制、差分走线的设置等越来越重要。笔者所在的武汉华中科技大学与武汉中科院岩土力学所智能仪器室合作,以ARM9微处理器EP9315为核心的嵌入式系统完成工程检测仪的开发。其中在该嵌入式系统硬件电路设计中的SDRAM和IDE等长走线、关键信号的阻抗控制和差分走线是本文的重点,同时以cirrus logic公司的网络物理层接口芯片cs8952为例详细介绍了网络部分的硬件电路设计,为同类高速硬件电路设计提供了一种可借鉴的方法。

2 硬件平台

2.1 主要芯片

本设计采用的嵌入式微处理器是Cirrus Logic公司2004年7月推出的EP93XX系列中的高端产品EP9315。该微处理器是高度集成的片上系统处理器,拥有200兆赫工作频率的 ARM920T内核,它具有ARM920T内核所有的优异性能,其中丰富的集成外设接口包括PCMCIA、接口图形加速器、可接两组设备的EIDE、1/10/100Mbps以太网MAC、3个2.0全速HOST USB、专用SDRAM通道的LCD接口、触摸屏接口、SPI串行外设接口、AC97接口、6通道I2S接口和8*8键盘扫描接口,并且支持4组32位SDRAM的无缝连接等。

主芯片丰富的外设接口大大简化了系统硬件电路,除了网络控制部分配合使用Cirrus Logic公司的100Base-X/10Base-T物理层(PHY)接口芯片CS8952外,其他功能模块无需增加额外的控制芯片。

2.2 系统主体结构

由图2可见,系统以微处理器EP9315为核心,具有完备的外设接口功能,同时控制工程检测仪。IDE/CF卡接口为工程检测数据提供大容量移动存储设备;扩展32M的SDRAM作为外部数据存储空间;3个主动USB接口支持USB键盘鼠标;LCD接口支持STN/TFT液晶和触摸屏,为用户提供友好的交互界面;1/10/100 Mbps以太网为调试操作系统时下载内核及工程检测时远程监控提供途径;面板按键为工程人员野外作业无法使用键盘鼠标时提供人机交互接口。

EP9315在操作系统下主频达到200M,总线频率100M,外设时钟为50M,数据线和地址线的布线密度大,速度高,网络部分对差分线和微带线控制有特殊要求,以往使用Protel设计主要依照经验进行PCB布局布线,显然这种方法无法满足当前的高速电路设计。CADENCE公司作为EDA领域最大的公司之一,其PCB设计工具性能上的优势在高速电路设计中越来越明显,故笔者使用CADENCE公司的PCB设计布局软件Allegro完成高速电路设计。

3设计实现

3.1 SDRAM的布线规则

该嵌入式系统使用64M字节的SDRAM扩展数据存储区,由两片K4S561632组成工作在32位模式下,最高频率可达100M以上,对于SDRAM的数据线、时钟线、片选及其它控制信号需要进行线长匹配,由此提出以下布线要求:

1.          SDRAM时钟信号:时钟信号频率较高,为避免传输线效应,按照工作频率达到或超过75MHz时布线长度应在1000mil以内的原则及为避免与相邻信号产生串扰,走线长度不超过1000mil,线宽10mil,内部间距5mil,外部间距30mil,要求差分布线,精确匹配差分对走线,误差允许在20mil以内。

2. 地址、片选及其它控制信号:线宽5mil,外部间距12mil,内部间距10mil,尽量走成菊花链拓补,可有效控制高次谐波干扰,可比时钟线长,但不能短。

3. SDRAM数据线:线宽5mil,内部间距5mil,外部间距8mil,尽量在同一层布线,数据线与时钟线的线长差控制在50mil内。  

根据布线要求,在Allegro中设置不同的约束:

针对线宽设置3个约束SDRAM_CLK,SDRAM_ADDDR,SDRAM_DATA,设置完约束后将约束添加到对应的net上,使得各个net都具有线宽、线距约束属性,最后为不同的信号组选择合适的约束即可。但是设置的约束在系统CPU内部是无法达到的,因为EP9315为BGA封装,pin间距1.27毫米,显然在CPU内部,线宽线距无法达到上述要求,利用Allegro设置CPU特殊走线区域cpu_area,并加上area属性,在此区域中另设置适合BGA内部走线的约束。

3.2 Xnet在IDE总线等长布线中的应用

3.2.1 系统中的IDE接口设计

     EP9315强大的外设接口能力能够直接驱动IDE硬盘,布线时需要注意IDE总线的等长设置,但是IDE总线这类高速线需要端接匹配,可以防止信号反射和回流。如图3所示其中的排阻起到了端接匹配的作用,但使得整个走线被分为好几个NET,而Allegro中常用的走线长度设置propagation_delay和relative_propagation_delay只能针对同一NET设置,IDE总线信号由EP9315扇出,要求EP9315到IDE接口走线DD*+UBDD*(如图3中NET)等长,误差为+/-20mil,最简单的方法是分别设置DD*等长和UBDD*等长,误差各位+/-10mil,就可以达到要求,但是增加了布线难度,特别当DD*有较大绕线空间,而UBDD*没有足够绕线空间时,这样设置等长不可行,Allegro提供了一种方法,将DD*和UBDD*走线相加再进行等长比对,这就要用到Xnet。

表情

3.2.2 Xnet概念和Xnet等长设置

     通常把连续的几断由无源元件(电阻,电容或电感)连接的NET合称为Xnet,如图4所示。

图3中将DD*和UBDD*设置为同一个Xnet,对属于该Xnet的所有信号等长控制。Xnet等长设置分为以下步骤:

1.设置Xnet

   选择要设置Xnet的器件(图3中为排阻RA1-RA4),创建ESpiceDevice model ,Allegro将自动填入模型名称,电路类型――Resistor, PIN连接顺序:1,8,2,7,3,6,4,5,表示1和8是一个电阻(见图3)。至此,查看排阻两边NET都添加了同一Xnet属性。

2.Xnet的等长设置

(1)建立Xnet的pin pair:在Allegro中打开constraint manager,选择relative_propagation_delay属性,已设置的Xnet自动显示,选择Xnet建立pin pair,Allegro提供整个项目中Xnet关联的起始pin和结束pin ,选择需要等长设置的起始pin和结束pin。

(2)建立等长group:选中所有需要设置等长的pin pair,创建名为R_IDE_DATA的MATCH GROUP,在与relative_propagation_delay对应的工作窗体选择区中出现了刚创建的R_IDE_DATA,其内含建立的pin pair,按照IDE总线走线等长要求设置走线误差10mil以内,一般选择最长走线为基准线(target)。

(3)走线完成后,重新打开constrait manager对实际走线进行分析,Allergo自动显示分析结果,绿色表示走线以基准线为标准,走线误差在10mil以内,红色表示走线误差超过10mil,如果分析结果,大部分走线都为红色,可以适当调整基准线的选择。

此外,Allegro在等长走线时,会实时显示走线长度是否在误差范围内,可以使用蛇型线调整走线长度,这些都极大的确保了布线可靠性。

3.3差分线和阻抗控制在网络布线中的应用

3.2.1 物理层接口芯片CS8952布线准则

CS8952使用CMOS技术,提供一个高性能的100Base-X/10Base-T物理层(PHY)线路接口。它使自适应均衡器达到最优化的抗扰性和抗近端串扰(NEXT)性,可将接收器的应用扩展至超过160米的电缆,它结合了一个标准介质无关端口(MII),可简便地连接微处理器EP9315的介质访问控制器(MAC)。

以下一些PCB布线规则,将使得CS8952工作更加稳定并得到良好的EMC性能:

1.使用多层电路板,至少有一个电源层,一个地层,叠层设置为:top,gnd,vcc,bottom。使用底层pcb走信号线只作为第二选择。把所有的元件都放在顶层。然而,旁路电容优选越靠近芯片越好,最好放置在CS8952下方的底层pcb上。RJ45终端元件和光纤元件可以选择放在底层。

2 . 4.99k的参考电阻应该越靠近RES管脚越好,把电阻另外一端使用一个过孔接到地平面。邻近的VSS(85和87脚)

接在电阻接地端,形成一个屏蔽。

3.对关键信号TX+/-,RX+/-,RX_NRZ+/-控制阻抗,作为微带传输线(差分对100欧,单线60欧),MII信号作为68欧微带传输线。

4.差分传输线布线应靠近(线宽间距6-8mil),与其他走线、元件保证2个线宽的距离,TX和RX差分对布线远离彼此,必要时使用pcb的相对面。

3.2.2 网络部分关键信号差分走线和阻抗控制的设置

    网络部分差分线及其阻抗控制以信号TX+/-为例,步骤如下:

1.  在Allegro的assign diff pair菜单中选择建立差分对的信号TX+/-,命名为TX_Pair。

2.  按照对信号TX+/-阻抗控制要求计算差分对线宽、线距,如图5所示,选择走线层面top层,填入差分对阻抗100欧,单线阻抗60欧,得到线宽10.1mil,主要线间距8.1mil。

主要线宽/线间距:10mil/8mil;次要线宽/线间距:10mil/8mi;线最小间距:6mil;两条线无法走到一起时允许的线长:100mil;两条线可允许的误差值:25mil。

4.  分配差分对TX_PAIR到电气约束集,打开差分对DRC模式。

以上实现差分对走线和阻抗控制的方法在涉及到大量差分对的通信系统电路中非常简单实用。

4结语

         笔者利用强大的PCB设计软件Allegro实现了基于EP9315嵌入式系统的硬件电路设计。该板采用6层PCB布线,完全满足高速电路设计中对等长、差分、阻抗控制的要求。基于该电路设计的嵌入式系统与武汉中科院岩土力学所设计的SY5声波工程检测仪实现了良好接口,与由51单片机搭建的原SY5声波仪相比,该系统功耗降低,体积变小,稳定性增强,成本降低,更适合工业控制中的运用。

点击此处查看原文 >>

系统分类: 汽车电子   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(1183)
发表于:2007-12-17 15:52:42
标签:无标签

1

PCB设计技巧百问(合集)

PCB设计技巧百问(合集) 
2007/11/11 23:49

1、如何选择PCB板材?
选择PCB板材必须在满足设计需求和可量产性及成本中间取得平衡点。设计需求包含电气和机构这两部分。通常在设计非常高速的PCB板子(大于GHz的频率)时这材质问题会比较重要。例如,现在常用的FR-4材质,在几个GHz的频率时的介

质损(dielectric loss)会对信号衰减有很大的影响,可能就不合用。就电气而言,要注意介电常数(dielectric constant)和介质损在所设计的频率是否合用。
2、如何避免高频干扰?
避免高频干扰的基本思路是尽量降低高频信号电磁场的干扰,也就是所谓的串扰(Crosstalk)。可用拉大高速信号和模拟信号之间的距离,或加ground guard/shunt traces在模拟信号旁边。还要注意数字地对模拟地的噪声干扰。

3、在高速设计中,如何解决信号的完整性问题?
信号完整性基本上是阻抗匹配的问题。而影响阻抗匹配的因素有信号源的架构和输出阻抗(output impedance),走线的特性阻抗,负载端的特性,走线的拓朴(topology)架构等。解决的方式是靠端接(termination)与调整走线的拓朴。

4、差分布线方式是如何实现的?
差分对的布线有两点要注意,一是两条线的长度要尽量一样长,另一是两线的间距(此间距由差分阻抗决定)要一直保持不变,也就是要保持平行。平行的方式有两种,一为两条线走在同一走线层(side-by-side),一为两条线走在上下相

邻两层(over-under)。一般以前者side-by-side实现的方式较多。

5、对于只有一个输出端的时钟信号线,如何实现差分布线?
要用差分布线一定是信号源和接收端也都是差分信号才有意义。所以对只有一个输出端的时钟信号是无法使用差分布线的。

6、接收端差分线对之间可否加一匹配电阻?
接收端差分线对间的匹配电阻通常会加, 其值应等于差分阻抗的值。这样信号品质会好些。

7、为何差分对的布线要靠近且平行?
对差分对的布线方式应该要适当的靠近且平行。所谓适当的靠近是因为这间距会影响到差分阻抗(differential impedance)的值, 此值是设计差分对的重要参数。需要平行也是因为要保持差分阻抗的一致性。若两线忽远忽近, 差分阻抗

就会不一致, 就会影响信号完整性(signal integrity)及时间延迟(timing delay)。

8、如何处理实际布线中的一些理论冲突的问题
1. 基本上, 将模/数地分割隔离是对的。 要注意的是信号走线尽量不要跨过有分割的地方(moat), 还有不要让电源和信号的回流电流路径(returning current path)变太大。

2. 晶振是模拟的正反馈振荡电路, 要有稳定的振荡信号, 必须满足loop gain与phase的规范, 而这模拟信号的振荡规范很容易受到干扰, 即使加ground guard traces可能也无法完全隔离干扰。 而且离的太远, 地平面上的噪声也会影响

正反馈振荡电路。 所以, 一定要将晶振和芯片的距离进可能靠近。

3. 确实高速布线与EMI的要求有很多冲突。 但基本原则是因EMI所加的电阻电容或ferrite bead, 不能造成信号的一些电气特性不符合规范。 所以, 最好先用安排走线和PCB叠层的技巧来解决或减少EMI的问题, 如高速信号走内层。 最

后才用电阻电容或ferrite bead的方式, 以降低对信号的伤害。

9、如何解决高速信号的手工布线和自动布线之间的矛盾?
现在较强的布线软件的自动布线器大部分都有设定约束条件来控制绕线方式及过孔数目。 各家EDA公司的绕线引擎能力和约束条件的设定项目有时相差甚远。 例如, 是否有足够的约束条件控制蛇行线(serpentine)蜿蜒的方式, 能否控制

差分对的走线间距等。 这会影响到自动布线出来的走线方式是否能符合设计者的想法。 另外, 手动调整布线的难易也与绕线引擎的能力有绝对的关系。 例如, 走线的推挤能力, 过孔的推挤能力, 甚至走线对敷铜的推挤能力等等。 所

以, 选择一个绕线引擎能力强的布线器, 才是解决之道。

10、关于test coupon。
test coupon是用来以TDR (Time Domain Reflectometer) 测量所生产的PCB板的特性阻抗是否满足设计需求。 一般要控制的阻抗有单根线和差分对两种情况。 所以, test coupon上的走线线宽和线距(有差分对时)要与所要控制的线一

样。 最重要的是测量时接地点的位置。 为了减少接地引线(ground lead)的电感值, TDR探棒(probe)接地的地方通常非常接近量信号的地方(probe tip), 所以, test coupon上量测信号的点跟接地点的距离和方式要符合所用的探棒

。详情参考如下链接1. http://developer.intel.com/design/chipsets/applnots/pcd_pres399.pdf2. http://www.Polarinstruments.com/index.html (点选Application notes)

11、在高速PCB设计中,信号层的空白区域可以敷铜,而多个信号层的敷铜在接地和接电源上应如何分配?
一般在空白区域的敷铜绝大部分情况是接地。 只是在高速信号线旁敷铜时要注意敷铜与信号线的距离, 因为所敷的铜会降低一点走线的特性阻抗。 也要注意不要影响到它层的特性阻抗, 例如在dual stripline的结构时。

12、是否可以把电源平面上面的信号线使用微带线模型计算特性阻抗?电源和地平面之间的信号是否可以使用带状线模型计算?
是的, 在计算特性阻抗时电源平面跟地平面都必须视为参考平面。 例如四层板: 顶层-电源层-地层-底层, 这时顶层走线特性阻抗的模型是以电源平面为参考平面的微带线模型。

13、在高密度印制板上通过软件自动产生测试点一般情况下能满足大批量生产的测试要求吗?
一般软件自动产生测试点是否满足测试需求必须看对加测试点的规范是否符合测试机具的要求。另外,如果走线太密且加测试点的规范比较严,则有可能没办法自动对每段线都加上测试点,当然,需要手动补齐所要测试的地方。

14、添加测试点会不会影响高速信号的质量?
至于会不会影响信号质量就要看加测试点的方式和信号到底多快而定。基本上外加的测试点(不用线上既有的穿孔(via or DIP pin)当测试点)可能加在线上或是从线上拉一小段线出来。前者相当于是加上一个很小的电容在线上,后者则

是多了一段分支。这两个情况都会对高速信号多多少少会有点影响,影响的程度就跟信号的频率速度和信号缘变化率(edge rate)有关。影响大小可透过仿真得知。原则上测试点越小越好(当然还要满足测试机具的要求)分支越短越好。

15、若干PCB组成系统,各板之间的地线应如何连接?
各个PCB板子相互连接之间的信号或电源在动作时,例如A板子有电源或信号送到B板子,一定会有等量的电流从地层流回到A板子 (此为Kirchoff current law)。这地层上的电流会找阻抗最小的地方流回去。所以,在各个不管是电源或信

号相互连接的接口处,分配给地层的管脚数不能太少,以降低阻抗,这样可以降低地层上的噪声。另外,也可以分析整个电流环路,尤其是电流较大的部分,调整地层或地线的接法,来控制电流的走法(例如,在某处制造低阻抗,让大部

分的电流从这个地方走),降低对其它较敏感信号的影响。

16、能介绍一些国外关于高速PCB设计的技术书籍和资料吗?
现在高速数字电路的应用有通信网路和计算机等相关领域。在通信网路方面,PCB板的工作频率已达GHz上下,迭层数就我所知有到40层之多。计算机相关应用也因为芯片的进步,无论是一般的PC或服务器(Server),板子上的最高工作频

率也已经达到400MHz (如Rambus) 以上。因应这高速高密度走线需求,盲埋孔(blind/buried vias)、mircrovias及build-up制程工艺的需求也渐渐越来越多。 这些设计需求都有厂商可大量生产。 以下提供几本不错的技术书籍:

1.Howard W. Johnson,“High-Speed Digital Design – A Handbook of Black Magic”;

2.Stephen H. Hall,“High-Speed Digital System Design”;

3.Brian Yang,“Digital Signal Integrity”;

4.Dooglas Brook,“Integrity Issues and printed Circuit Board Design”。

17、两个常被参考的特性阻抗公式:
a.微带线(microstrip) Z={87/[sqrt(Er+1.41)]}ln[5.98H/(0.8W+T)] 其中,W为线宽,T为走线的铜皮厚度,H为走线到参考平面的距离,Er是PCB板材质的介电常数(dielectric constant)。此公式必须在0.1<(W/H)<2.0及1<(Er)<15的情

况才能应用。

b.带状线(stripline) Z=[60/sqrt(Er)]ln{4H/[0.67π(T+0.8W)]} 其中,H为两参考平面的距离,并且走线位于两参考平面的中间。此公式必须在W/H<0.35及T/H<0.25的情况才能应用。

18、差分信号线中间可否加地线?
差分信号中间一般是不能加地线。因为差分信号的应用原理最重要的一点便是利用差分信号间相互耦合(coupling)所带来的好处,如flux cancellation,抗噪声(noise immunity)能力等。若在中间加地线,便会破坏耦合效应。

19、刚柔板设计是否需要专用设计软件与规范?国内何处可以承接该类电路板加工?
可以用一般设计PCB的软件来设计柔性电路板(Flexible Printed Circuit)。一样用Gerber格式给FPC厂商生产。由于制造的工艺和一般PCB不同,各个厂商会依据他们的制造能力会对最小线宽、最小线距、最小孔径(via)有其限制。除此

之外,可在柔性电路板的转折处铺些铜皮加以补强。至于生产的厂商可上网“FPC”当关键词查询应该可以找到。

20、适当选择PCB与外壳接地的点的原则是什么?
选择PCB与外壳接地点选择的原则是利用chassis ground提供低阻抗的路径给回流电流(returning current)及控制此回流电流的路径。例如,通常在高频器件或时钟产生器附近可以借固定用的螺丝将PCB的地层与chassis ground做连接,

以尽量缩小整个电流回路面积,也就减少电磁辐射。

21、电路板DEBUG应从那几个方面着手?
就数字电路而言,首先先依序确定三件事情: 1. 确认所有电源值的大小均达到设计所需。有些多重电源的系统可能会要求某些电源之间起来的顺序与快慢有某种规范。 2. 确认所有时钟信号频率都工作正常且信号边缘上没有非单调

(non-monotonic)的问题。3. 确认reset信号是否达到规范要求。 这些都正常的话,芯片应该要发出第一个周期(cycle)的信号。接下来依照系统运作原理与bus protocol来debug。
22、在电路板尺寸固定的情况下,如果设计中需要容纳更多的功能,就往往需要提高PCB的走线密度,但是这样有可能导致走线的相互干扰增强,同时走线过细也使阻抗无法降低,请专家介绍在高速(>100MHz)高密度PCB设计中的技巧?
在设计高速高密度PCB时,串扰(crosstalk interference)确实是要特别注意的,因为它对时序(timing)与信号完整性(signal integrity)有很大的影响。以下提供几个注意的地方:

1.控制走线特性阻抗的连续与匹配。

2.走线间距的大小。一般常看到的间距为两倍线宽。可以透过仿真来知道走线间距对时序及信号完整性的影响,找出可容忍的最小间距。不同芯片信号的结果可能不同。

3.选择适当的端接方式。

4.避免上下相邻两层的走线方向相同,甚至有走线正好上下重迭在一起,因为这种串扰比同层相邻走线的情形还大。

5.利用盲埋孔(blind/buried via)来增加走线面积。但是PCB板的制作成本会增加。 在实际执行时确实很难达到完全平行与等长,不过还是要尽量做到。

除此以外,可以预留差分端接和共模端接,以缓和对时序与信号完整性的影响。

23、模拟电源处的滤波经常是用LC电路。但是为什么有时LC比RC滤波效果差?
LC与RC滤波效果的比较必须考虑所要滤掉的频带与电感值的选择是否恰当。 因为电感的感抗(reactance)大小与电感值和频率有关。如果电源的噪声频率较低,而电感值又不够大,这时滤波效果可能不如RC。但是,使用RC滤波要付出的

代价是电阻本身会耗能,效率较差,且要注意所选电阻能承受的功率。

24、滤波时选用电感,电容值的方法是什么?
电感值的选用除了考虑所想滤掉的噪声频率外,还要考虑瞬时电流的反应能力。如果LC的输出端会有机会需要瞬间输出大电流,则电感值太大会阻碍此大电流流经此电感的速度,增加纹波噪声(ripple noise)。 电容值则和所能容忍的纹

波噪声规范值的大小有关。纹波噪声值要求越小,电容值会较大。而电容的ESR/ESL也会有影响。 另外,如果这LC是放在开关式电源(switching regulation power)的输出端时,还要注意此LC所产生的极点零点(pole/zero)对负反馈控制

(negative feedback control)回路稳定度的影响。

25、如何尽可能的达到EMC要求,又不致造成太大的成本压力?
PCB板上会因EMC而增加的成本通常是因增加地层数目以增强屏蔽效应及增加了ferrite bead、choke等抑制高频谐波器件的缘故。除此之外,通常还是需搭配其它机构上的屏蔽结构才能使整个系统通过EMC的要求。以下仅就PCB板的设计技

巧提供几个降低电路产生的电磁辐射效应。

1、尽可能选用信号斜率(slew rate)较慢的器件,以降低信号所产生的高频成分。

2、注意高频器件摆放的位置,不要太靠近对外的连接器。

3、注意高速信号的阻抗匹配,走线层及其回流电流路径(return current path), 以减少高频的反射与辐射。

4、在各器件的电源管脚放置足够与适当的去耦合电容以缓和电源层和地层上的噪声。特别注意电容的频率响应与温度的特性是否符合设计所需。

5、对外的连接器附近的地可与地层做适当分割,并将连接器的地就近接到chassis ground。

6、可适当运用ground guard/shunt traces在一些特别高速的信号旁。但要注意guard/shunt traces对走线特性阻抗的影响。

7、电源层比地层内缩20H,H为电源层与地层之间的距离。

26、当一块PCB板中有多个数/模功能块时,常规做法是要将数/模地分开,原因何在?
将数/模地分开的原因是因为数字电路在高低电位切换时会在电源和地产生噪声,噪声的大小跟信号的速度及电流大小有关。如果地平面上不分割且由数字区域电路所产生的噪声较大而模拟区域的电路又非常接近,则即使数模信号不交叉

, 模拟的信号依然会被地噪声干扰。也就是说数模地不分割的方式只能在模拟电路区域距产生大噪声的数字电路区域较远时使用。

27、另一种作法是在确保数/模分开布局,且数/模信号走线相互不交叉的情况下,整个PCB板地不做分割,数/模地都连到这个地平面上。道理何在?
数模信号走线不能交叉的要求是因为速度稍快的数字信号其返回电流路径(return current path)会尽量沿着走线的下方附近的地流回数字信号的源头,若数模信号走线交叉,则返回电流所产生的噪声便会出现在模拟电路区域内。

28、在高速PCB设计原理图设计时,如何考虑阻抗匹配问题?
在设计高速PCB电路时,阻抗匹配是设计的要素之一。而阻抗值跟走线方式有绝对的关系, 例如是走在表面层(microstrip)或内层(stripline/double stripline),与参考层(电源层或地层)的距离,走线宽度,PCB材质等均会影响走线的

特性阻抗值。也就是说要在布线后才能确定阻抗值。一般仿真软件会因线路模型或所使用的数学算法的限制而无法考虑到一些阻抗不连续的布线情况,这时候在原理图上只能预留一些 terminators(端接),如串联电阻等,来缓和走线阻

抗不连续的效应。真正根本解决问题的方法还是布线时尽量注意避免阻抗不连续的发生。

29、哪里能提供比较准确的IBIS模型库?
IBIS模型的准确性直接影响到仿真的结果。基本上IBIS可看成是实际芯片I/O buffer等效电路的电气特性资料,一般可由SPICE模型转换而得 (亦可采用测量, 但限制较多),而SPICE的资料与芯片制造有绝对的关系,所以同样一个器件

不同芯片厂商提供,其SPICE的资料是不同的,进而转换后的IBIS模型内之资料也会随之而异。也就是说,如果用了A厂商的器件,只有他们有能力提供他们器件准确模型资料,因为没有其它人会比他们更清楚他们的器件是由何种工艺做

出来的。如果厂商所提供的IBIS不准确, 只能不断要求该厂商改进才是根本解决之道。

30、在高速PCB设计时,设计者应该从那些方面去考虑EMC、EMI的规则呢?
一般EMI/EMC设计时需要同时考虑辐射(radiated)与传导(conducted)两个方面. 前者归属于频率较高的部分(>30MHz)后者则是较低频的部分(<30MHz). 所以不能只注意高频而忽略低频的部分.一个好的EMI/EMC设计必须一开始布局时就要

考虑到器件的位置, PCB迭层的安排, 重要联机的走法, 器件的选择等, 如果这些没有事前有较佳的安排, 事后解决则会事倍功半, 增加成本. 例如时钟产生器的位置尽量不要靠近对外的连接器, 高速信号尽量走内层并注意特性阻抗匹配

与参考层的连续以减少反射, 器件所推的信号之斜率(slew rate)尽量小以减低高频成分, 选择去耦合(decoupling/bypass)电容时注意其频率响应是否符合需求以降低电源层噪声. 另外, 注意高频信号电流之回流路径使其回路面积尽量

小(也就是回路阻抗loop impedance尽量小)以减少辐射. 还可以用分割地层的方式以控制高频噪声的范围. 最后, 适当的选择PCB与外壳的接地点(chassis ground)。

31、如何选择EDA工具?
目前的pcb设计软件中,热分析都不是强项,所以并不建议选用,其它的功能1.3.4可以选择PADS或Cadence性能价格比都不错。 PLD的设计的初学者可以采用PLD芯片厂家提供的集成环境,在做到百万门以上的设计时可以选用单点工具。

32、请推荐一种适合于高速信号处理和传输的EDA软件。
常规的电路设计,INNOVEDA 的 PADS 就非常不错,且有配合用的仿真软件,而这类设计往往占据了70%的应用场合。在做高速电路设计,模拟和数字混合电路,采用Cadence的解决方案应该属于性能价格比较好的软件,当然Mentor的性能

还是非常不错的,特别是它的设计流程管理方面应该是最为优秀的。(大唐电信技术专家 王升)

33、对PCB板各层含义的解释
Topoverlay ----顶层器件名称, 也叫 top silkscreen 或者 top component legend, 比如 R1 C5, IC10.bottomoverlay----同理multilayer-----如果你设计一个4层板,你放置一个 free pad or via, 定义它作为multilay 那么它的

pad就会自动出现在4个层 上,如果你只定义它是top layer, 那么它的pad就会只出现在顶层上。
34、2G以上高频PCB设计,走线,排版,应重点注意哪些方面?
2G以上高频PCB属于射频电路设计,不在高速数字电路设计讨论范围内。而射频电路的布局(layout)和布线(routing)应该和原理图一起考虑的,因为布局布线都会造成分布效应。而且,射频电路设计一些无源器件是通过参数化定义,

特殊形状铜箔实现,因此要求EDA工具能够提供参数化器件,能够编辑特殊形状铜箔。Mentor公司的boardstation中有专门的RF设计模块,能够满足这些要求。而且,一般射频设计要求有专门射频电路分析工具,业界最著名的是agilent

的eesoft,和Mentor的工具有很好的接口。

35、2G以上高频PCB设计,微带的设计应遵循哪些规则?
射频微带线设计,需要用三维场分析工具提取传输线参数。所有的规则应该在这个场提取工具中规定。

36、对于全数字信号的PCB,板上有一个80MHz的钟源。除了采用丝网(接地)外,为了保证有足够的驱动能力,还应该采用什么样的电路进行保护?
确保时钟的驱动能力,不应该通过保护实现,一般采用时钟驱动芯片。一般担心时钟驱动能力,是因为多个时钟负载造成。采用时钟驱动芯片,将一个时钟信号变成几个,采用点到点的连接。选择驱动芯片,除了保证与负载基本匹配,

信号沿满足要求(一般时钟为沿有效信号),在计算系统时序时,要算上时钟在驱动芯片内时延。

37、如果用单独的时钟信号板,一般采用什么样的接口,来保证时钟信号的传输受到的影响小?
时钟信号越短,传输线效应越小。采用单独的时钟信号板,会增加信号布线长度。而且单板的接地供电也是问题。如果要长距离传输,建议采用差分信号。LVDS信号可以满足驱动能力要求,不过您的时钟不是太快,没有必要。

38、27M,SDRAM时钟线(80M-90M),这些时钟线二三次谐波刚好在VHF波段,从接收端高频窜入后干扰很大。除了缩短线长以外,还有那些好办法?
如果是三次谐波大,二次谐波小,可能因为信号占空比为50%,因为这种情况下,信号没有偶次谐波。这时需要修改一下信号占空比。此外,对于如果是单向的时钟信号,一般采用源端串联匹配。这样可以抑制二次反射,但不会影响时钟

沿速率。源端匹配值,可以采用下图公式得到。

39、什么是走线的拓扑架构?
Topology,有的也叫routing order.对于多端口连接的网络的布线次序。

40、怎样调整走线的拓扑架构来提高信号的完整性?
这种网络信号方向比较复杂,因为对单向,双向信号,不同电平种类信号,拓朴影响都不一样,很难说哪种拓朴对信号质量有利。而且作前仿真时,采用何种拓朴对工程师要求很高,要求对电路原理,信号类型,甚至布线难度等都要了

解。

41、怎样通过安排迭层来减少EMI问题?
首先,EMI要从系统考虑,单凭PCB无法解决问题。层叠对EMI来讲,我认为主要是提供信号最短回流路径,减小耦合面积,抑制差模干扰。另外地层与电源层紧耦合,适当比电源层外延,对抑制共模干扰有好处。

42、为何要铺铜?
一般铺铜有几个方面原因。1,EMC.对于大面积的地或电源铺铜,会起到屏蔽作用,有些特殊地,如PGND起到防护作用。2,PCB工艺要求。一般为了保证电镀效果,或者层压不变形,对于布线较少的PCB板层铺铜。3,信号完整性要求

,给高频数字信号一个完整的回流路径,并减少直流网络的布线。当然还有散热,特殊器件安装要求铺铜等等原因。

43、在一个系统中,包含了dsp和pld,请问布线时要注意哪些问题呢?
看你的信号速率和布线长度的比值。如果信号在传输线上的时延和信号变化沿时间可比的话,就要考虑信号完整性问题。另外对于多个DSP,时钟,数据信号走线拓普也会影响信号质量和时序,需要关注。

44、除protel工具布线外,还有其他好的工具吗?
至于工具,除了PROTEL,还有很多布线工具,如MENTOR的WG2000,EN2000系列和powerpcb,Cadence的allegro,zuken的cadstar,cr5000等,各有所长。

45、什么是“信号回流路径”?
信号回流路径,即return current。高速数字信号在传输时,信号的流向是从驱动器沿PCB传输线到负载,再由负载沿着地或电源通过最短路径返回驱动器端。这个在地或电源上的返回信号就称信号回流路径。Dr.Johson在他的书中解释,

高频信号传输,实际上是对传输线与直流层之间包夹的介质电容充电的过程。SI分析的就是这个围场的电磁特性,以及他们之间的耦合。

46、如何对接插件进行SI分析?
在IBIS3.2规范中,有关于接插件模型的描述。一般使用EBD模型。如果是特殊板,如背板,需要SPICE模型。也可以使用多板仿真软件(HYPERLYNX或IS_multiboard),建立多板系统时,输入接插件的分布参数,一般从接插件手册中得到

。当然这种方式会不够精确,但只要在可接受范围内即可。

47、请问端接的方式有哪些?
端接(terminal),也称匹配。一般按照匹配位置分有源端匹配和终端匹配。其中源端匹配一般为电阻串联匹配,终端匹配一般为并联匹配,方式比较多,有电阻上拉,电阻下拉,戴维南匹配,AC匹配,肖特基二极管匹配。

48、采用端接(匹配)的方式是由什么因素决定的?
匹配采用方式一般由BUFFER特性,拓普情况,电平种类和判决方式来决定,也要考虑信号占空比,系统功耗等。

49、采用端接(匹配)的方式有什么规则?
数字电路最关键的是时序问题,加匹配的目的是改善信号质量,在判决时刻得到可以确定的信号。对于电平有效信号,在保证建立、保持时间的前提下,信号质量稳定;对延有效信号,在保证信号延单调性前提下,信号变化延速度满足

要求。Mentor ICX产品教材中有关于匹配的一些资料。另外《High Speed Digital design a hand book of blackmagic》有一章专门对terminal的讲述,从电磁波原理上讲述匹配对信号完整性的作用,可供参考。

50、能否利用器件的IBIS模型对器件的逻辑功能进行仿真?如果不能,那么如何进行电路的板级和系统级仿真?
IBIS模型是行为级模型,不能用于功能仿真。功能仿真,需要用SPICE模型,或者其他结构级模型。

51、在数字和模拟并存的系统中,有2种处理方法,一个是数字地和模拟地分开,比如在地层,数字地是独立地一块,模拟地独立一块,单点用铜皮或FB磁珠连接,而电源不分开;另一种是模拟电源和数字电源分开用FB连接,而地是统一

地地。请问李先生,这两种方法效果是否一样?


应该说从原理上讲是一样的。因为电源和地对高频信号是等效的。

区分模拟和数字部分的目的是为了抗干扰,主要是数字电路对模拟电路的干扰。但是,分割可能造成信号回流路径不完整,影响数字信号的信号质量,影响系统EMC质量。因此,无论分割哪个平面,要看这样作,信号回流路径是否被增大

,回流信号对正常工作信号干扰有多大。

现在也有一些混合设计,不分电源和地,在布局时,按照数字部分、模拟部分分开布局布线,避免出现跨区信号。

52、安规问题:FCC、EMC的具体含义是什么?

FCC: federal communication commission 美国通信委员会

EMC: electro megnetic compatibility 电磁兼容

FCC是个标准组织,EMC是一个标准。标准颁布都有相应的原因,标准和测试方法。

53、何谓差分布线? 差分信号,有些也称差动信号,用两根完全一样,极性相反的信号传输一路数据,依靠两根信号电平差进行判决。为了保证两根信号完全一致,在布线时要保持并行,线宽、线间距保持不变。

54、PCB仿真软件有哪些? 仿真的种类很多,高速数字电路信号完整性分析仿真分析(SI)常用软件有icx,signalvision,hyperlynx,XTK,speectraquest等。有些也用Hspice。

55、PCB仿真软件是如何进行LAYOUT仿真的? 高速数字电路中,为了提高信号质量,降低布线难度,一般采用多层板,分配专门的电源层,地层。

56、在布局、布线中如何处理才能保证50M以上信号的稳定性

高速数字信号布线,关键是减小传输线对信号质量的影响。因此,100M以上的高速信号布局时要求信号走线尽量短。

数字电路中,高速信号是用信号上升延时间来界定的。而且,不同种类的信号(如TTL,GTL,LVTTL),确保信号质量的方法不一样。
57、室外单元的射频部分,中频部分,乃至对室外单元进行监控的低频电路部分往往采用部署在同一PCB上,请问对这样的PCB在材质上有何要求?如何防止射频,中频乃至低频电路互相之间的干扰?

混合电路设计是一个很大的问题。很难有一个完美的解决方案。

一般射频电路在系统中都作为一个独立的单板进行布局布线,甚至会有专门的屏蔽腔体。而且射频电路一般为单面或双面板,电路较为简单,所有这些都是为了减少对射频电路分布参数的影响,提高射频系统的一致性。相对于一般的FR4

材质,射频电路板倾向与采用高Q值的基材,这种材料的介电常数比较小,传输线分布电容较小,阻抗高,信号传输时延小。

在混合电路设计中,虽然射频,数字电路做在同一块PCB上,但一般都分成射频电路区和数字电路区,分别布局布线。之间用接地过孔带和屏蔽盒屏蔽。

58、对于射频部分,中频部分和低频电路部分部署在同一PCB上,mentor有什么解决方案?

Mentor的板级系统设计软件,除了基本的电路设计功能外,还有专门的RF设计模块。在RF原理图设计模块中,提供参数化的器件模型,并且提供和EESOFT等射频电路分析仿真工具的双向接口;在RF LAYOUT模块中,提供专门用于射频电路

布局布线的图案编辑功能,也有和EESOFT等射频电路分析仿真工具的双向接口,对于分析仿真后的结果可以反标回原理图和PCB。同时,利用Mentor软件的设计管理功能,可以方便的实现设计复用,设计派生,和协同设计。大大加速混合

电路设计进程。

手机板是典型的混合电路设计,很多大型手机设计制造商都利用Mentor加安杰伦的eesoft作为设计平台。

59、mentor的产品结构如何?

61、Mentor的PCB设计软件对差分线队的处理又如何?
Mentor软件在定义好差分对属性后,两根差分对可以一起走线,严格保证差分对线宽,间距和长度差,遇到障碍可以自动分开,在换层时可以选择过孔方式。

62、在一块12层PCb板上,有三个电源层2.2v,3.3v,5v,将三个电源各作在一层,地线该如何处理?
一般说来,三个电源分别做在三层,对信号质量比较好。因为不大可能出现信号跨平面层分割现象。跨分割是影响信号质量很关键的一个因素,而仿真软件一般都忽略了它。对于电源层和地层,对高频信号来说都是等效的。在实际中,

除了考虑信号质量外,电源平面耦合(利用相邻地平面降低电源平面交流阻抗),层叠对称,都是需要考虑的因素。

63、PCB在出厂时如何检查是否达到了设计工艺要求?
很多PCB厂家在PCB加工完成出厂前,都要经过加电的网络通断测试,以确保所有联线正确。同时,越来越多的厂家也采用x光测试,检查蚀刻或层压时的一些故障。对于贴片加工后的成品板,一般采用ICT测试检查,这需要在PCB设计时添

加ICT测试点。如果出现问题,也可以通过一种特殊的X光检查设备排除是否加工原因造成故障。

64、“机构的防护”是不是机壳的防护?
是的。机壳要尽量严密,少用或不用导电材料,尽可能接地。

65、在芯片选择的时候是否也需要考虑芯片本身的esd问题?
不论是双层板还是多层板,都应尽量增大地的面积。在选择芯片时要考虑芯片本身的ESD特性,这些在芯片说明中一般都有提到,而且即使不同厂家的同一种芯片性能也会有所不同。设计时多加注意,考虑的全面一点,做出电路板的性能

也会得到一定的保证。但ESD的问题仍然可能出现,因此机构的防护对ESD的防护也是相当重要的。

66、在做pcb板的时候,为了减小干扰,地线是否应该构成闭和形式?
在做PCB板的时候,一般来讲都要减小回路面积,以便减少干扰,布地线的时候,也不 应布成闭合形式,而是布成树枝状较好,还有就是要尽可能增大地的面积。

67、如果仿真器用一个电源,pcb板用一个电源,这两个电源的地是否应该连在一起?
如果可以采用分离电源当然较好,因为如此电源间不易产生干扰,但大部分设备是有具体要求的。既然仿真器和PCB板用的是两个电源,按我的想法是不该将其共地的。

68、一个电路由几块pcb板构成,他们是否应该共地?
一个电路由几块PCB构成,多半是要求共地的,因为在一个电路中用几个电源毕竟是不太实际的。但如果你有具体的条件,可以用不同电源当然干扰会小些。

69、设计一个手持产品,带LCD,外壳为金属。测试ESD时,无法通过ICE-1000-4-2的测试,CONTACT只能通过1100V,AIR可以通过6000V。ESD耦合测试时,水平只能可以通过3000V,垂直可以通过4000V测试。CPU主频为33MHZ。有什么方法

可以通过ESD测试?
手持产品又是金属外壳,ESD的问题一定比较明显,LCD也恐怕会出现较多的不良现象。如果没办法改变现有的金属材质,则建议在机构内部加上防电材料,加强PCB的地,同时想办法让LCD接地。当然,如何操作要看具体情况。

70、设计一个含有DSP,PLD的系统,该从那些方面考虑ESD?
就一般的系统来讲,主要应考虑人体直接接触的部分,在电路上以及机构上进行适当的保护。至于ESD会对系统造成多大的影响,那还要依不同情况而定。干燥的环境下,ESD现象会比较严重,较敏感精细的系统,ESD的影响也会相对明显

。虽然大的系统有时ESD影响并不明显,但设计时还是要多加注意,尽量防患于未然。

点击此处查看原文 >>

系统分类: 汽车电子   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(502)
发表于:2007-11-27 17:34:50
标签:无标签

1

阅读理解:关于SPI驱动

关于IIC驱动,本论坛已经有深入的讨论研究了,例如:
http://www.hhcn.com/cgi-bin/topic.cgi?forum=1&topic=148&show=0
因为IIC在嵌入式系统开发中实在是太重要了,在我们的教材第三、四章也有介绍。
另外,这里也有讨论:
http://www.hhcn.com/cgi-bin/topic.cgi?forum=5&topic=498&show=0
SPI的驱动一般要比IIC的简单很多,因为没有复杂的时序。
S3C2410的SPI总线支持主从模式,支持轮询、中断(
#define IRQ_SPI0        22  /* SPI interrupt */)和DMA三种方式(SMOD):
Polling, Interrupt, and DMA transfer mode

其中采用polling模式的SPI传输的代码尤其简单。例如HHARM9-EDU通过SPI接MCP2510跟ADS7846都是polling模式,下面就是一个典型的通过SPI从AD芯片读取数据的例子:
(其实其中的时序是AD芯片的要求,SPI收、发数据就是一句话(SPRDAT0/SPTDAT0寄存器的读写)而已,非常的简单,要比IIC简单得多)。
   spi_tx_data(0xD0);
   x_upper = rSPRDAT0;//dummy data
   spi_tx_data(0x00);
   x_upper =  rSPRDAT0;
   spi_tx_data(0x90);
   x_lower = rSPRDAT0;
   spi_tx_data(0x00);
   y_upper = rSPRDAT0;
   spi_tx_data(0x00);
   y_lower = rSPRDAT0;

如果是使用DMA方式的SPI传输,代码也很简单,以接收数据read为例:
spi_rd
   /*set SPCON0 to configure properly the SPI module,
      DMA mode ,SCK enable,  master mode , active high , format A,
      Tx auto garbage data mode
   */
   rSPCON0 =  0x5a;

   /*DMA is configured properly*/
   /*1.Base address of source data to transfer*/
   rDISRC1 = 0x59000014;

   /*2.LOC=1:the source is in the peripheral bus*/
   /*INC=0:the address is increased by its data size*/
   rDISRCC1 = 0x00000002;

   /*3.Base address of destination for the transfer, to SDRAM*/
   rDIDST1 = dbuf;

   /*4.LOC=0:the source is in the system bus*/
   /*INC=0:the address is increased by its data size*/
   rDISTC1 = 0x00000000;

   /*5.DMD_HS=1:Handshake mode is selected;*/
   /*SYNC=1:DREQ and DACK are synchronized to HCLK*/
   /*INT=1:interrupt request is generated when all the transfer is done*/
   /*TSZ=0:a unit transfer is performed*/
   /*SERVMODE=0:Single service mode*/
   /*HWSRCSEL=011:SPI request DMA;SWHW_SEL=1:select the DMA source from hardware*/
   /*RELOAD=1;DSZ=00:Byte to be transferred;TC=length */
   rDCON1 = DCON1_DMD_HS|DCON1_SYNC|DCON1_INT|DCON1_TSZ|
       DCON1_SERVMODE|DCON1_HWSRCSEL|DCON1_SWHW_SEL|RELOAD|
       DCON1_DSZ|length;

   /*6.STOP=1;ON_OFF=1;SW_TRIG=0  start/enable DMA*/
   rDMASKTRIG1 = 0x006;

若采用SPI的中断时,这你的驱动代码就要稍微复杂一些了,因为引入中断方式就意味着优化驱动的通信性能的可能性,例如用tasklet方式在中断处理函数中释放CPU,或者用interruptible_sleep_on/wake_up_interruptible来实现阻塞方式的接收。


做IIC/SPI这些总线的驱动时,你必须清楚
1、这些总线的驱动其实就只要提供两个函数即可,即收(read)和发(write)
2、要分清哪些是IIC/SPI通信的时序要求,哪些是所接的器件的时序要求,例如IIC接的X1227/24LC04/UDA1380/IP175等。SPI其实没有任何时序,非常简单,如果用POLLING方式,驱动代码不超过100行。例如接CAN总线控制器MCP2510,参见参见教材《ARM9嵌入式Linux系统构建与应用》的P137页,收发就是对SPRDAT0/SPTDAT0的简单读取和赋值而已;而我们看SPI接ADS7846这个AD接四线触摸屏的时候,它的驱动就写得比较复杂,但它的复杂性在于结合了触摸屏的实际应用特性在里面,例如它的read读取坐标就不能像CAN那样死等,而必须是如果没有按下等操作就必须返回的noblock方式,这样才不会造成上层GUI+触摸屏应用程序的反应迟钝和死锁,而来了数据也不能在中断ISR里面完成,而要用tasklet并配合一个timer――digi_sam_callback来尽量让CPU不会停留在内核里面的数据读取上,而实际真正的SPI读取数据还是那个touch_pan_read_dev里面的那几句AD芯片的时序,详细分析参见上面教材的P232~P238.
/HHARM9-EDU/experiments/EXP19/driver/2410spi.c
这个驱动里面就是当年尝试用SPI的DMA+中断方式写的SPI驱动,但最后没有仔细调试,他们偷懒就放弃还是用POLLING模式了。
这个代码里面:
request_irq(INT_DMA1(/*#define IRQ_DMA1        18  /* DMA channel 1 interrupt */*/,...),
有数据来了产生的中断,因为中断肯定是用来通知接收数据的。

关于SPI与DMA
S3C2410支持4路DMA,但不能支持外部总线的DMA,只能是内部集成的peripherals跟local bus(SDRAM)之间的DMA,BF则支持EBIU的DMA。
The S3C2410X supports four-channel DMA controller that is located between the system bus and the peripheral bus.
S3C2410的内部peripherals的DMA是用户通过软件设置从这4路DMA中选择的(当然不是完全任意选择的,是有限范围的选择,看下面图表Table 8-1),而BlackFin则是每个peripheral自己绑定专用的,例如SPI拥有自己绑定的DMA,例如设置DMA时都不需要设置发送时的SRC地址和接收时的目标地址DES,而只要设置Start Address Register
(DMAx_START_ADDR/MDMA_yy_START_ADDR)
即可。

core bus/peripheral bus(CPU内部集成的接口模块)/EBIU
DMA需要了解BUS
Ø16bit的MMR Port  --- PAB
Ø64bit的P Port --- EBIU
Ø32bit的D Port --- DMA


BF的SPI拥有自己的DMA通道,并且有16 bit 4-word的FIFO(到底多少个字节啊?),DMA传送就是从SPISPI_RDBR寄存器到FIFO再到SDRAM。 SPI使用DMA传送的说明参见BF53X manual P462页:Master Mode DMA Operation
带DMA的SPI传送驱动代码是(据说新升级的uClinux for blackfin已经彻底升级了驱动的框架,但我们只要理解SPI的收发操作即可,与驱动的大框架无关):
uClinux-dist/linux-2.6.x/drivers/char/adsp-spidma.c
这个驱动的框架清晰,代码量不大(1000行代码,一多半是注释和说明)
主体代码就是下面的操作函数
static struct file_operations spi_fops = {
   owner:      THIS_MODULE,
   read:       spi_read,
   write:      spi_write,
   ioctl:      spi_ioctl,
   open:       spi_open,
   release:    spi_release,
   fasync:     spi_fasync,
};
另外就是DMA传送完毕后的中断处理函数。
下面逐一看来:
入口点的初始化函数:spidma_init里面没什么,就是注册:
register_chrdev(SPI_MAJOR, SPI_DEVNAME, &spi_fops);
但下面两句看不懂???
 *pPORT_MUX |= PFS4E;  //???
 *pPORTF_FER |= 0x7c40;
__builtin_bfin_ssync();  //????这个专用函数做什么的?

spi_open函数里面:
先是disable SPI:
void spidma_reg_reset(spi_device_t *pdev)
{
   unsigned short sdata = 0;

   /* Ctrl register */
   sdata = BIT_CTL_OPENDRAIN | BIT_CTL_PHASE | BIT_CTL_TIMOD_DMA_RX;
   set_spi_reg(SPI_CTL, sdata); /* Disable SPI, open drain */
   set_spi_reg(SPI_FLG, 0xff00); /* Disable pin, out 3 state*/
   set_spi_reg(SPI_BAUD, SPI_DEFAULT_BARD); /* 设置默认的波特率Default clock. */
   set_spi_reg(SPI_STAT, 0xffff); /* Clear all status bits.*/
}
然后注册中断,这里有个封装了request_irq的函数set_dma_callback。
request_dma(CH_SPI, "BF533_SPI_DMA");
set_dma_callback(CH_SPI, (void*) spidma_irq,filp->private_data); //表示DMA接收数据完毕DMA_DONE

BF SPI在使用DMA进行收发数据的时候,DMA的设置都是在收、发函数里面设置的,因为方向要变化。
static ssize_t spi_read (struct file *filp, char *buf, size_t count, loff_t *pos)
{
   unsigned short regdata;
   int ierr;
   spi_device_t *pdev = filp->private_data;
   pdev->done=0;
   pdev->tmode=RECEIVE;

   /* Invalidate allocated memory in Data Cache */
   // TODO: remove this line as soon GFP_DMA memory allocation is in place
   blackfin_dcache_invalidate_range(buf, buf+(count)*2);

   // configure spi port for DMA TIMOD RX
   get_spi_reg(SPI_CTL,&regdata);
   set_spi_reg(SPI_CTL, regdata | BIT_CTL_TIMOD_DMA_RX); //P440 10-10

       pdev->dma_config |= ( WNR | RESTART | DI_EN );//P375 9-13
       set_dma_config(CH_SPI, pdev->dma_config);
       set_dma_start_addr(CH_SPI, buf);//P374 9-12 直接传到用户态传递来的buf里去
       set_dma_x_count(CH_SPI, (count));//P379 9-17

       if(pdev->length == CFG_SPI_WORDSIZE16)
           set_dma_x_modify(CH_SPI, 2); //P379 9-17
       else
           set_dma_x_modify(CH_SPI, 1);

       __builtin_bfin_ssync();
       enable_dma(CH_SPI);   //开始DMA传送接收数据。

   // enable spi
   get_spi_reg(SPI_CTL,&regdata);
   set_spi_reg(SPI_CTL,regdata | BIT_CTL_ENABLE); //P440 10-10
//下面就等待DMA传送完毕,这里看起来是死循环,但其中通过wait_event_interruptible释放CPU
       /* Wait for data available */
       if(1)
       {
           if(pdev->nonblock) //如果是非阻塞方式
               return -EAGAIN;
           else //阻塞方式的处理
           {
               DPRINTK("SPI wait_event_interruptible\n");
               ierr = wait_event_interruptible(*(pdev->rx_avail),pdev->done);//在2.4上面是用down或者interruptible_sleep_on的
               
if(ierr)
               {
                   /* waiting is broken by a signal */
                   printk("SPI wait_event_interruptible ierr\n");
                   return ierr;
               }
           }
       }

   DPRINTK("SPI wait_event_interruptible done\n");
//等到中断了,下面就可以返回收到的数据了。
return count;
}


//下面是DMA中断的处理函数,是通过set_dma_callback设置的,收发都要用到这个中断
static irqreturn_t spidma_irq(int irq, void *dev_id, struct pt_regs *regs)
{
   unsigned short regdata;
   spi_device_t *pdev = (spi_device_t*)dev_id;

   DPRINTK("spidma_irq: \n");

   /* Acknowledge DMA Interrupt*/
   clear_dma_irqstat(CH_SPI);//进入中断首先要清中断pending bit P391 9-29

   pdev->done = 1; // Found

   /* Give a signal to user program. */
   if(pdev->fasyc)
       kill_fasync(&(pdev->fasyc), SIGIO, POLLIN);
   DPRINTK("spidma_irq: wake_up_interruptible pdev->done=%d\n",pdev->done);
   /* wake up read/write block. */
   wake_up_interruptible(pdev->rx_avail);

if( pdev->tmode==TRANSMIT )
 { /* Make sure TX buffer is empty before disabling SPI */
   while(*pSPI_STAT & TXS);
   while(*pSPI_STAT & TXS);
 } else
   {
   while(*pSPI_STAT & RXS);
   while(*pSPI_STAT & RXS);
 };

   /* 收发数据完毕后要disable spi */
   get_spi_reg(SPI_CTL,&regdata);
   set_spi_reg(SPI_CTL, regdata & ~BIT_CTL_ENABLE);
   DPRINTK("spidma_irq: return \n");
   return IRQ_HANDLED;
注:
关于DMA的函数,封装在
uClinux-dist/linux-2.6.x/arch/blackfin/kernel/simple_dma.c
文件里面,例如set_dma_start_addr/set_dma_x_count等,就类似mizi的armlinux for s3c2410一样,封装了GPIO的操作函数如set_gpio_bit等,封装这种函数的形式方便于阅读和维护,而如果裸写这些寄存器,例如
*(volatile  unsigned long *)rDmaIrqState|=0xfffffffe;
这样的代码就不易维护和阅读。
另外一个代码比较多的就是spi_ioctl,主要完成例如调整波特率等杂项,都是细节了,与我们收发无关了。
从上可见,SPI驱动本身收发数据是非常简单的,只要打开DMA,就开始哗哗的传送了,设置好要接收的count和buffer,数据就自动填进来,收发完了产生一个中断,这样就可以作为结束条件,把收到的数据返回应用层了。就这么简单啦。

点击此处查看原文 >>

系统分类: ARM   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(1404)
发表于:2007-11-27 17:33:11
标签:无标签

1

printk 实现分析

一直都不清楚是怎么被定位到串口的,所以也非常想搞明白,因为以后可能把标准输入输出还原到键盘和显示器上去,所以决心自己再读一读源码了。
不过内核用的打印函数printk完全是和stdin或stdout无关的,因为一开始到start_kernel函数刚开始进入内核就可以用printk 函数了,而建立stdin和stdout是在init函数中实现的。有个问题,在我这里的代码中,建立stdin和stdout如下
 if (open("/dev/null", O_RDWR, 0) < 0)
  printk("Warning: unable to open an initial console.\n");
 (void) dup(0);
 (void) dup(0);
问题在于它打开的是/dev/null,而一般pc机上的linux打开的都是/dev/console,而且我把这几行代码删除也没有问题,所以我猜想这里建立stdin和stdout并没什么用,肯定在shell中建立了定位到串口的stdin和stdout。所以接下来还需要看看busybox的代码吧。
在这里还是主要分析一下printk实现的原理。

static spinlock_t logbuf_lock = SPIN_LOCK_UNLOCKED; //定义logbuf_lock,并初始化为unlock状态
static char log_buf[LOG_BUF_LEN];  //保存日志数据的缓冲区
#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])
static DECLARE_MUTEX(console_sem); //定义全局互斥信号量console_sem并初始化为1

asmlinkage int printk(const char *fmt, ...)
{
 va_list args;
 unsigned long flags;
 int printed_len;
 char *p;
 static char printk_buf[1024];
 static int log_level_unknown = 1;

 if (oops_in_progress)  // default : oops_in_progress = 0
 { //oops_in_progress指示进程发生错误,只有在panic()函数中才等于1
  //所以一般情况下下两句都不运行
  /* If a crash is occurring, make sure we can't deadlock */
  spin_lock_init(&logbuf_lock); //初始化logbuf_lock
  /* And make sure that we print immediately */
  init_MUTEX(&console_sem); //初始化console_sem为互斥的信号量,初值为1
 }

 /* This stops the holder of console_sem just where we want him */
 spin_lock_irqsave(&logbuf_lock, flags);
 //一般spin_lock在单cpu中无效的,所以spin_lock_irqsave真正的作用是关中断 和保存状态寄存器。
 /* Emit the output into the temporary buffer */
 va_start(args, fmt);
 printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
 //先把数据格式化到printk_buf中去
 va_end(args);

 /*
  * Copy the output into log_buf.  If the caller didn't provide
  * appropriate log level tags, we insert them here
  */
 //emit_log_char 把字符存入log_buf中等待被发送,具体的参见下面的分析
 for (p = printk_buf; *p; p++) {
  if (log_level_unknown) {
   if (p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>') {
    emit_log_char('<');
    emit_log_char(default_message_loglevel + '0');
    emit_log_char('>');
   }
 //如果没有提供<1>类似的日志级别,则在此加上<4>
 //我这里的default_message_loglevel=4
   log_level_unknown = 0;
  }
  emit_log_char(*p);
  if (*p == '\n') //每一行前面都需要加<4>之类的日志级别
   log_level_unknown = 1;
 }

 if (!arch_consoles_callable()) // unexecute
 {
  /* 控制台是否可调用,一般下面的不会被执行
   * On some architectures, the consoles are not usable
   * on secondary CPUs early in the boot process.
   */
  spin_unlock_irqrestore(&logbuf_lock, flags);
  goto out;
 }
 if (!down_trylock(&console_sem)) //lock ok
 {
  /* down_trylock获取信号量,lock则返回0,否则立即返回非0值
   * We own the drivers.  We can drop the spinlock and let
   * release_console_sem() print the text
   */
  spin_unlock_irqrestore(&logbuf_lock, flags);
  console_may_schedule = 0;
  release_console_sem(); //在这个函数中把数据发送到串口并释放console_sem
 } else {
  /*
   * Someone else owns the drivers.  We drop the spinlock, which
   * allows the semaphore holder to proceed and to call the
   * console drivers with the output which we just produced.
   */
  spin_unlock_irqrestore(&logbuf_lock, flags);
 }
out:
 return printed_len;
}

static unsigned long log_start;  /* Index into log_buf: next char to be read by syslog() */
static unsigned long con_start;  /* Index into log_buf: next char to be sent to consoles */
static unsigned long log_end;  /* Index into log_buf: most-recently-written-char + 1 */
static unsigned long logged_chars; /* Number of chars produced since last read+clear operation */
static void emit_log_char(char c) 
{
 LOG_BUF(log_end) = c;  //把字符c存到log_buf缓冲区中,缓冲区满了就会覆盖开始的数据
 log_end++;   //
 if (log_end - log_start > LOG_BUF_LEN) //log_start指示syslog读取的开始
  log_start = log_end - LOG_BUF_LEN;//缓冲区满了会把开始的指针向前推
 if (log_end - con_start > LOG_BUF_LEN) //con_start指示控制台读取的开始
  con_start = log_end - LOG_BUF_LEN;
 if (logged_chars < LOG_BUF_LEN)
  logged_chars++;
}

void release_console_sem(void)
{
 unsigned long flags;
 unsigned long _con_start, _log_end;
 unsigned long must_wake_klogd = 0;

 for ( ; ; ) {
  spin_lock_irqsave(&logbuf_lock, flags);//关中断和保存flag
  must_wake_klogd |= log_start - log_end; //唤醒klogd标志
  if (con_start == log_end)
   break;   /* Nothing to print */
  _con_start = con_start;
  _log_end = log_end;
  con_start = log_end;  /* Flush , con_start向前移用了,可见缓冲区是循环使用的 */
  spin_unlock_irqrestore(&logbuf_lock, flags);
  call_console_drivers(_con_start, _log_end);//在这个函数中发送数据,见下面的分析
 }
 console_may_schedule = 0; //指示数据发送时是否能进行任务调度,在使用串口控制台时没用
 up(&console_sem); //释放信号量
 spin_unlock_irqrestore(&logbuf_lock, flags);
 if (must_wake_klogd && !oops_in_progress)
  wake_up_interruptible(&log_wait);
}


static void call_console_drivers(unsigned long start, unsigned long end)
{
 unsigned long cur_index, start_print;
 static int msg_level = -1;

 if (((long)(start - end)) > 0)
  BUG();

 cur_index = start;
 start_print = start;
 while (cur_index != end) {
  if ( msg_level < 0 &&
   ((end - cur_index) > 2) &&
   LOG_BUF(cur_index + 0) == '<' &&
   LOG_BUF(cur_index + 1) >= '0' &&
   LOG_BUF(cur_index + 1) <= '7' &&
   LOG_BUF(cur_index + 2) == '>')
  {
   msg_level = LOG_BUF(cur_index + 1) - '0';
   cur_index += 3;
   start_print = cur_index;
  } //去除每行开头的类似<4>的日志级别,把它赋给msg_level
  while (cur_index != end) {
   char c = LOG_BUF(cur_index);
   cur_index++;
   if (c == '\n') {
    if (msg_level < 0) {
     /*
      * printk() has already given us loglevel tags in
      * the buffer.  This code is here in case the
      * log buffer has wrapped right round and scribbled
      * on those tags
      */
     msg_level = default_message_loglevel;
    }
    _call_console_drivers(start_print, cur_index, msg_level);
    //发送一行数据
    msg_level = -1;
    start_print = cur_index;
    break;
   }
  }
 }
 _call_console_drivers(start_print, end, msg_level); //发送剩余的数据
}


struct console *console_drivers; //全局的console类型的结构体

static void _call_console_drivers(unsigned long start, unsigned long end, int msg_log_level)
{
 //如果msg_log_level < console_loglevel 并且 console_drivers存在 并且 start != end
 if (msg_log_level < console_loglevel && console_drivers && start != end) {
  if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {
   /* wrapped write */
   //缓冲区循环使用就会出现(start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)
   //的清况,于是就分两部分来发送.
   //__call_console_drivers才是真正的发送函数
   __call_console_drivers(start & LOG_BUF_MASK, LOG_BUF_LEN);
   __call_console_drivers(0, end & LOG_BUF_MASK);
  } else {
   __call_console_drivers(start, end);
  }
 }
}

static void __call_console_drivers(unsigned long start, unsigned long end)
{
 struct console *con;

 for (con = console_drivers; con; con = con->next) {
  if ((con->flags & CON_ENABLED) && con->write)
   con->write(con, &LOG_BUF(start), end - start);
 } //调用console_drivers->write来把数据发送出去
}

接下来理解一下console_drivers这个结构体指针
struct console
{
 char name[8];
 void (*write)(struct console *, const char *, unsigned);
 int (*read)(struct console *, const char *, unsigned);
 kdev_t (*device)(struct console *);
 int (*wait_key)(struct console *);
 void (*unblank)(void);
 int (*setup)(struct console *, char *);
 short flags;
 short index;
 int cflag;
 struct  console *next;
};

而开始console_drivers这个指针是NULL的,在什么时候被赋值的呢,原本以为应该在用printk以前就被初始化了,其实不然,它是在start_kernel函数中调用的console_init()函数中被初始化的.最好的办法还是看看代码.
void __init console_init(void)
{
 /* Setup the default TTY line discipline. */
 memset(ldiscs, 0, sizeof(ldiscs));
 (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);

 /*
  * Set up the standard termios.  Individual tty drivers may
  * deviate from this; this is used as a template.
  */
 memset(&tty_std_termios, 0, sizeof(struct termios));
 memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
 tty_std_termios.c_iflag = ICRNL | IXON;
 tty_std_termios.c_oflag = OPOST | ONLCR;
#ifdef CONFIG_MIZI //CONFIG_MIZI=1
 tty_std_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL;
#else
 tty_std_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL;
#endif
 tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
  ECHOCTL | ECHOKE | IEXTEN;

 /*
  * set up the console device so that later boot sequences can
  * inform about problems etc..
  */
#ifdef CONFIG_VT //如果不是串口控制台就定义这个虚拟控制台
 con_init(); //于是输入是键盘输出是显示器
#endif
#ifdef CONFIG_AU1000_SERIAL_CONSOLE
 au1000_serial_console_init();
#endif
#ifdef CONFIG_SERIAL_CONSOLE
#if (defined(CONFIG_8xx) || defined(CONFIG_8260))
 console_8xx_init();
#elif defined(CONFIG_MAC_SERIAL) && defined(CONFIG_SERIAL)
 if (_machine == _MACH_Pmac)
   mac_scc_console_init();
 else
  serial_console_init();
#elif defined(CONFIG_MAC_SERIAL)
  mac_scc_console_init();
#elif defined(CONFIG_PARISC)
 pdc_console_init();
#elif defined(CONFIG_SERIAL)
 serial_console_init();
#endif /* CONFIG_8xx */
#ifdef CONFIG_SGI_SERIAL
 sgi_serial_console_init();
#endif
#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC)
 vme_scc_console_init();
#endif
#if defined(CONFIG_SERIAL167)
 serial167_console_init();
#endif
#if defined(CONFIG_SH_SCI)
 sci_console_init();
#endif
#endif
#ifdef CONFIG_TN3270_CONSOLE
 tub3270_con_init();
#endif
#ifdef CONFIG_TN3215
 con3215_init();
#endif
#ifdef CONFIG_HWC
        hwc_console_init();
#endif
#ifdef CONFIG_STDIO_CONSOLE
 stdio_console_init();
#endif
#ifdef CONFIG_SERIAL_CORE_CONSOLE   //  CONFIG_SERIAL_CORE_CONSOLE=1
 uart_console_init(); //这里唯一一个被运行的函数
#endif
#ifdef CONFIG_ARC_CONSOLE
 arc_console_init();
#endif
#ifdef CONFIG_SERIAL_TX3912_CONSOLE
 tx3912_console_init();
#endif
}

void __init uart_console_init(void)
{
#ifdef CONFIG_SERIAL_AMBA_CONSOLE
 ambauart_console_init();
#endif
#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE
 anakin_console_init();
#endif
#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
 clps711xuart_console_init();
#endif
#ifdef CONFIG_SERIAL_21285_CONSOLE
 rs285_console_init();
#endif
#ifdef CONFIG_SERIAL_SA1100_CONSOLE
 sa1100_rs_console_init();
#endif
#ifdef CONFIG_SERIAL_8250_CONSOLE
 serial8250_console_init();
#endif
#ifdef CONFIG_SERIAL_UART00_CONSOLE
 uart00_console_init();
#endif
#ifdef CONFIG_SERIAL_S3C2400_CONSOLE
 s3c2400_console_init();
#endif
#ifdef CONFIG_SERIAL_S3C2410_CONSOLE
 s3c2410_console_init();  //这个函数被运行
#endif
}

void __init s3c2410_console_init(void)
{
 register_console(&s3c2410_cons); //调用注册控制台的函数
}

static struct console s3c2410_cons = {
 name:  "ttyS",
 write:  s3c2410_console_write,
 device:  s3c2410_console_device,
 wait_key: s3c2410_console_wait_key,
 setup:  s3c2410_console_setup,
 flags:  CON_PRINTBUFFER,
 index:  -1,
}; //这个就是console_drivers所指向的结构了

void register_console(struct console * console)
{ //该函数就是把console_drivers这个全局指针指向console结构体了,而且在注册完后
 //会把存在缓冲区中的都发送出去,所以在注册console以前调用的printk并不发送数据
 //而只是把数据存到缓冲区里,注册了以后才能被马上发送.多个控制台被注册的话就会
 //形成一个链表结构,都能发送数据.
 int     i;
 unsigned long flags;

 /*
  * See if we want to use this console driver. If we
  * didn't select a console we take the first one
  * that registers here.
  */
 if (preferred_console < 0) {
  if (console->index < 0)
   console->index = 0;
  if (console->setup == NULL ||
      console->setup(console, NULL) == 0) {
   console->flags |= CON_ENABLED | CON_CONSDEV;
   preferred_console = 0;
  }
 }

 /*
  * See if this console matches one we selected on
  * the command line.
  */
 for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) {
  if (strcmp(console_cmdline[i].name, console->name) != 0)
   continue;
  if (console->index >= 0 &&
      console->index != console_cmdline[i].index)
   continue;
  if (console->index < 0)
   console->index = console_cmdline[i].index;
  if (console->setup &&
      console->setup(console, console_cmdline[i].options) != 0)
   break;
  console->flags |= CON_ENABLED;
  console->index = console_cmdline[i].index;
  if (i == preferred_console)
   console->flags |= CON_CONSDEV;
  break;
 }

 if (!(console->flags & CON_ENABLED))
  return;

 /*
  * Put this console in the list - keep the
  * preferred driver at the head of the list.
  */
 acquire_console_sem();
 if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
  console->next = console_drivers;
  console_drivers = console;
 } else {
  console->next = console_drivers->next;
  console_drivers->next = console;
 }
 if (console->flags & CON_PRINTBUFFER) {
  /*
   * release_cosole_sem() will print out the buffered messages for us.
   */
  spin_lock_irqsave(&logbuf_lock, flags);
  con_start = log_start;
  spin_unlock_irqrestore(&logbuf_lock, flags);
 }
 release_console_sem();
}

点击此处查看原文 >>

系统分类: ARM   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(1362)
发表于:2007-11-27 16:09:19
标签:无标签

1

linux2.6的IIC驱动编写

目录:

1.四种模式的IIC驱动编写介绍

2.一个完整的IIC驱动(从器件接收模式,并且是裸写驱动)


1.1 开启从器件接收模式的示例

R_IICCON = 0xE2; // 使能ACK,使能中断

R_IICADD = 0xAA; // 从器件地址

R_IICSTAT = 0x10; // 设置从器件接收模式

进入中断处理,读收数据:

unit8_t ch = R_IICDS & 0xff; // 读取数据寄存器