*模拟地和数字地单点接地*
只要是地,最终都要接到一起,然后入大地。如果不接在一起就是"浮地",存在压差,容易积累电荷,造成静电。地是参考0电位,所有电压都是参考地得出的,地的标准要一致,故各种地应短接在一起。人们认为大地能够吸收所有电荷,始终维持稳定,是最终的地参考点。虽然有些板子没有接大地,但发电厂是接大地的,板子上的电源最终还是会返回发电厂入地。如果把模拟地和数字地大面积直接相连,会导致互相干扰。不短接又不妥,理由如上有四种方法解决此问题:1、用磁珠连接;2、用电容连接;3、用电感连接;4、用0欧姆电阻连接。
磁珠的等效电路相当于带阻限波器,只对某个频点的噪声有显著抑制作用,使用时需要预先估计噪点频率,以便选用适当型号。对于频率不确定或无法预知的情况,磁珠不合。
电容隔直通交,造成浮地。
电感体积大,杂散参数多,不稳定。
0欧电阻相当于很窄的电流通路,能够有效地限制环路电流,使噪声得到抑制。电阻在所有频带上都有衰减作用(0欧电阻也有阻抗),这点比磁珠强。
*跨接时用于电流回路*
当分割电地平面后,造成信号最短回流路径断裂,此时,信号回路不得不绕道,形成很大的环路面积,电场和磁场的影响就变强了,容易干扰/被干扰。在分割区上跨接0欧电阻,可以提供较短的回流路径,减小干扰。
*配置电路*
一般,产品上不要出现跳线和拨码开关。有时用户会乱动设置,易引起误会,为了减少维护费用,应用0欧电阻代替跳线等焊在板子上。
空置跳线在高频时相当于天线,用贴片电阻效果好。
*其他用途*
布线时跨线
调试/测试用
临时取代其他贴片器件
作为温度补偿器件
更多时候是出于EMC对策的需要。另外,0欧姆电阻比过孔的寄生电感小,而且过孔还会影响地平面(因为要挖孔)。
以前单点接地先要分开走线,然后再改原理图连接AGND、GND,再改走线连接,用楼主的方法,真是太简单了!
posted @ 2009-06-18 21:58 陈广强 阅读(100) 评论(0)
编辑
这部分介绍下ADS下如何生成可以运行的ROM镜像文件,我们知道当程序下载到flash中运行的时候,对于RW、ZI数据就存在着两个环境,一个load环境,一个是exec环境,有时候由于速度的需要RO数据也要重新加载,那么对RO数据也是有两个环境。
编译器产生ROM镜像文件时候,这三块数据的存放依次为RO、RW、ZI,并且地址空间时连续的。但是到了运行的时候,RW数据必须被拷贝到SDRAM(SRAM)中以支持读写,这就是我们所谓的运行环境。那么就要有一段代码去完成这个任务,在本章中我们介绍如何生成这段代码。
玩过2410的朋友都知道2410初始化代码中有一段搬运RW和ZI初始化的代码,没错,它确实能够在一定程度上完成上面所说的任务,只要我们在生成二进制可执行代码的时候在编译器链接项的地方填写正确的RO&RW地址,(比如RO = 0, RW = 0x30000000), 那么将程序下到 NOR flash的零地址并从nor flash启动,启动代码会将RW&ZI数据弄到0x30000000,程序就能跑起来了。
但是各位有没有想过,
怎么把RO代码弄到SDRAM中(有时候这是必须的,比方后面我将提到用nor flash的bootloader烧写nor flash)?如果直接设RO=0x30000000,那么这段代码下载到0地址肯定跑不起来,除非是ROPI,这个要求就高了。这里我们有必要从介绍ADS中规定的C语言入口开始,ADS中从初始化汇编代码跳到main函数有两种方式,main和__main:
1,在__main入口的模式下,汇编代码的指令为 b __main, 编译器在跳转到main之前还要作一系列的工作,这其中就包括对运行环境的初始化,在
中提到: copies nonroot(RO&RW) execution regions from load addr to exec addr, and Zeros ZI region. 借助编译器,我们就可以定义更为复杂的运行环境,这里要用到scatter文件(.scf),比如我们要的目标运行环境是:将启动代码以外的所有代码都 拷贝到SDRAM的初始地址中运行,比且把RW段设在0x30800000,那么对应的scf文件如下:
FLASH 0x0 0x200000
{
EXEC1 0x0 0x200000
{
2410init.o(Init, +First)
__main.o(+RO) ; copy code
* (Region$$Table) ; RO/RW addresses to copy
* (ZISection$$Table) ; ZI addresses to zero
}
EXEC2 0x30000000 0x00800000
{
*(+RO)
}
SDRAM 0x30800000 0x00800000
{
*(+RW,+ZI)
}
}
;Sections named Region$$Table and ZISection$$Table which contain the addresses of the code/data to be copied.
当然,在这种模式下,有些入口函数必须自己重定义,比如__user_initial_stackheap,具体参见ADS文档。
2, main入口模式即简单的跳转,这里j即使不用“main”这个名字也无所谓。那么编译器不会作任何的初始化,所有运行环境的建立都要靠我们自己,这就是大家看到的那段搬运代码存在的理由。但是它实现一些简单的运行环境是可取的,如果用scf定义的复杂环境,虽然我相信是可以做到的,但是可能会比较麻烦。我还没深究。
另外,这里提一下semihost,因为我们在看ADS的东西的时候经常出现这个词,我也一直受其困扰。这里我简单说一下自己的见解,semihost 仅仅是一种调试手段,它的机理就是利用MULTI_IDE等工具捕捉目标环境运行过程中产生的值为0x123456的SWI中断,然后向上位机的ADS 软件发送对应的调试信息。对于我们最后的应用代码来说,都是nonsemihost类型的。如果我们在调试中使用semihost,那么只要在最后重定义ADS中的一些使用到的库函数(比如fputc),代码就可以从semihost向nonsemihost的类型转变。不过到目前为止,我还没体会到semihost的威力。
2410启动代码分析
这一章主要对目前广泛流行的2410启动代码进行分析:S3C2410的初始化代码主要涉及到对系统主要模块的配置、运行环境的建立、系统时钟、MMU等模块的配置,下面按执行顺序依次都各个部分进行分析:
程序入口:(ResetHandler)
在程序一开始,首先进行的一些操作主要保证初始化程序能够顺利的运行, 因此主要包括关闭WDT、中断,配置锁相环等。
配置memory接口
memory接口是数据访问正确的基本保障,此处主要配置SFR寄存器中0x48000000开始的memory接口寄存器组, 确保每个bank的位宽、访问类型(waitable)以及时序参数正确。如果没有特别的要求,一般来说时序参数使用默认值即可。
初始化堆栈
ARM有6种运行模式,必须为每一种模式提供独立的堆栈空间,在堆栈设置之前是不能进行C函数的调用的。ARM的堆栈模式是从高地址递减的,我的所有代码统一将堆栈的首地址设在0x33ff8000处,往低依次为FIQ、IRQ、Abort、Undef、SVC,其中SVC和User模式不予区分。堆栈大小一般可在头文件或者当前文件中修改。
运行空间的初始化
这段代码主要完成两个功能,一是将RW数据搬运到RW空间(我们生成ROM镜像时,RW数据是跟在RO数据之后的),二是初始化ZI数据段。当然,这段代码存在的前提是代码的运行环境只是标准的两段式:一段RO空间和一段RW空间;并且在C程序入口时没有调用编译器的链接库(__main)。后者已经提供相应的功能,并且支持更加复杂的运行环境定义(使用SCF文件)。
__rt_lib_init
在ADS1.2的环境中,如果在C入口没有调用编译器的链接库(__main),那么在C程序一开始要调用该函数以初始化运行时的函数库,以保证对ADS提供的某些库函数能够正常调用。从这个函数开始,我们已经在C语言环境下了。
MMU初始化
2410的MMU支持1级&2级地址映射,在我们目前大部分应用中均采用1级section模式的地址映射,一个section的大小为1M,也就是说从逻辑地址到物理地址的转变是这样的一个过程:
一个32位的地址,高12位决定了该地址在页表中的index,这个index的内容决定了该逻辑section对应的物理section; 低20位决定了该地址在section中的偏移(index)。
因此从0x0~0xffffffff的地址空间总共可以分成0x1000(4K)个section,页表中每项的大小为32个bit,因此页表的大小为0x4000(16K)。在我的代码中所有程序的页表统一存放在地址0x33ff8000。
每个页表项的内容如下:
bit: 31 20 19 12 11 10 9 8 5 4 3 2 1 0
content: Section对应的物理地址 NULL AP 0 Domain 1 C B 1 0
最低两位(10)是section分页的标识。
AP:Access Permission,区分只读、读写、SVC&其它模式。
Domain:每个section都属于某个Domain,一共有16个Domain,每个Domain的属性由CP15的R3寄存器控制。 在我得所有程序中,都只包含两个Domain,一个是SFR地址以下(包括SFR)的空间,可访问; 另一个是SFR以上的空间,不可访问。
C、B:这两位决定了该section的cache&write buffer属性,这与该段的用途(RO or RW)有密切关系。不同的用途要做不同的设置。
C B 具体含义
0 0 无cache,无写缓冲,任何对memory的读写都反映到ASB总线上。 对 memory 的操作过程中CPU需要等待。
0 1 无cache,有写缓冲,读操作直接反映到ASB总线上。写操作CPU将数据写入到写缓冲后继续运行,由写缓冲进行ASB操作。
1 0 有cache,写通模式(write-through (WT)),读操作首先考虑cache hit;写操作时直接将数据写入写缓冲,如果同时出现cache hit,那么也更新cache。
1 1 有cache,写回模式(write-back (WB)),读操作首先考虑cache hit;写操作也首先考虑cache,如果hit,则只修改cache,并将cache对应半行的dirty比特置位;如果miss,则写入写缓冲,触发ASB总线操作。
在我的程序中内存空间的分配统一采用了文末的MEMORY图。虽然MMU只是使用了逻辑地址到物理地址的linear transfer(值不改变),但是由于MMU能够引入cache&write buffer,因此系统性能有很大的提高!
配置时钟比、重新设置PLL
2410内部有三个时钟:FCLK、HCLK、PCLK,分别供CPU、AHB总线和APB总线使用,为了降低功耗,一般都选择周期比为1:2:4的合理配置。 同时将PLL配置为运行环境时钟,一般都达到最高202M。
(注:FCLK is used by ARM920T.
HCLK is used for AHB bus, which is used by the ARM920T, the memory controller, the interrupt controller, the LCD controller, the DMA and USB host block.
PCLK is used for APB bus, which is used by the peripherals such as WDT, IIS, I2C, PWM timer, MMC interface,
ADC, UART, GPIO, RTC and SPI)
IO初始化
将IO口配置为对应的功能选项,同时一般会点亮相应的LED灯。
中断初始化
2410的内存空间没有remap的机制,应该中断入口时钟位于零地址。因此中断服务机制可以描述如下:
首先,不管使用那种启动方式,必须确保一下代码段位于内存的0x0地址:
b ResetHandler
b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b . ;reserved
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt
除ResetHandler外,其余各项都是由如下的宏定义的一段代码:
HandlerFIQ HANDLER HandleFIQ
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 ; decrement sp(to store jump address)
stmfd sp!,{r0} ; PUSH the work register to stack
ldr r0,=$HandleLabel ; load the address of HandleXXX to r0
ldr r0,[r0] ; load the contents
str r0,[sp,#4] ; store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ; POP the work register and pc(jump to ISR)
MEND
这段代码的含义是通过堆栈将中断向量表中的内容赋给PC指针(如HandleFIQ是存放着FIQ服务程序入口地址的地址),自然程序就跳到相应的入口地址。
可见,中断向量表存放的是各个中断服务程序的入口地址,它是用来被加载的,而并不是可执行代码。为了统一,所有示例程序都将中断向量表放在0x33ffff00开始的地址,并根据入口地址依次排列。
需要注意的是如果各种模式的服务程序用C语言定义,那么类型必须用__irq定义,以保证能够正确返回。
初始化串口
串口统一选用UART0,模式采用115200、1bit STOP、No Parity。
最后跳转到我们自己的应用程序!
附:我得程序所使用的地址空间结构以及MMU中C、B的设置:
Blank Area: RW_FAULT 0x5b000000 ~ 0xffffffff
Sram & SFR: NCNB 0x40000000 ~ 0x4affffff
Blank Area: RW_FAULT 0x34000000 ~ 0x3fffffff
Int_Vec, Stack, MTT: CNB 0x33f00000 ~ 0x33ffffff
SDRAM Download: NCNB 0x31000000 ~ 0x33efffff
SDRAM Exec RW: CB 0x30800000 ~ 0x30ffffff
SDRAM Exec R CNB 0x30000000 ~ 0x307fffff
Bank5, FPGA: NCNB 0x28000000 ~ 0x2fffffff
Bank4, FPGA: NCNB 0x20000000 ~ 0x27ffffff
Bank3, Bottom NIC: NCNB 0x18000000 ~ 0x1fffffff
Bank2, Bottom Flash: CNB 0x10000000 ~ 0x17ffffff
Bank1, Bottom Sram: CNB 0x08000000 ~ 0x0fffffff
Bank0, Flash or Sram: CNB 0x00000000 ~ 0x07ffffff
Nor Flash Bootloader
这是我着手写的第一个程序,我的想*是让这个程序同时支持通过串口对Nand 和 Nor FLASH的烧写,如果不进行任何烧写,那么就跳到Nor Flash的第二个section启动应用程序,这样一来,即使脱离JTGA,我也可以使用串口进行盲调。
由于有现成的初始化文件和flash烧写的示例程序,开发起来还比较快。当然也遇到了一些问题,一开始连flash的device ID都读不出来,后来发现我指针没有定义成volatile类型,flash的操作时序被编译器优化了;再者,在对Nor Flash进行操作时,bank0在MMU中的类型一定要设为NCNB,这样比较保险。
遇到最大的问题就是下面的了,一开始我用jtag把程序下载到0x30000000的地方运行,对Nor Flash的烧写完全正常,但是当把程序下载到Nor Flash中启动运行后,再对Nor Flash的section 2进行烧写时,就出现了问题。所幸没多久我就意识到了问题,将程序放在Nor Flash中运行,同时有对同一片flash进行操作,那么操作时序势必会被CPU的指令读取时序所破坏,因此程序必须搬运到SDRAM中运行。
但是启动地址有必须是零地址,所以我采用了前文提到的scatter文件的方法,将非必要的代码全部搬到sdram中运行,scf文件格式就是前文中的那个。当然采用了__main的入口,调用了ADS的链接库,让它帮忙建立程序的运行环境。
至此,Nor Flash Bootloader可以顺畅无忧的实现其功能了。
Nand Flash Bootloader
因为Nor flash bootloader已经实现了对Nand Flash的烧写,因此在Nand Flash Bootloader中实现flash烧写并不是我的目的,况且,S3C2410运行在NAND BOOT模式下的时候,4K的SRAM位于0地址,上电时刻Nand Flash中block 0的前8个page的数据自动加载到SRAM后开始运行,Nor flash这个时候是不可见的。 因此,我做Nand Flash Bootloader的目的简单而又直接,就是把block1开始的若干个block数据加载到sdram首地址,然后PC跳到那里运行应用程序就可以了。比方说我把编译好的ucos-ii代码放在block1,那么ucos-ii就可以跑起来了。
因此制作Nand Flash一个最重要的问题就是真个程序必须小于4K。应用程序应该是一个完整的应用代码,只是在编译时RO的起始地址应该定位成0x30000000,如果直接用JTAG将其下载到对应地址,程序照样能够跑起来(当然零地址要有中断向量入口程序)。这里我偷懒了一下,将应用程序的中断向量表地址和Nand Flash Bootloader设得完全相同,那样应用程序就可以借用bootloader的中断跳转程序以实现中断的正确跳转,当然应用程序也有自己相应的跳转代码,但是这段代码位于SDRAM起始地址,是不会被执行的;至于堆栈,应用程序在自己得初始化代码中可以重新设置堆栈。
在Nand Flash的硬件方面,我开发板使用的是K9F5608(32M),相对于K9S208(64M),后者的地址需要写四次才能全部送出,而前者只要三次就够了,2410的引脚中专门有nCON控制地址送出的次数。因此当硬件在这两者之间变化时,既要注意外部电路图的接法,又要注意软件代码的正确性。
RTL8019调试心得
一开始接触8019真的是让我头晕,首先我没有一点网络基础,另外,8019的datasheet称不上最烂也算是极品了。当初作PCB的时候选用8019主要是因为价格便宜以及lbbbb做过,能够提供源代码&技术支持。最后能搞定,我觉得还是很有成就感的。
8109AS的运行模式包括跳线模式、非跳线模式和PnP模式,PnP模式是在电脑上使用的即插即用模式,因此这里我们可以不予考虑。8109AS的IO寄存器符合NE2000标准,分为4个page,其中page3是8109AS自己定义的寄存器。所谓跳线模式,是指8019AS I/O寄存器page3中的大部分配置寄存器(CONFIGn)的值是在上电复位时刻确定的,来源是在RESET上升沿时捕捉到的一些外部引脚的电平值。在非跳线模式下,这些寄存器值的配置由外部EEPROM 93C46完成。配置寄存器在运行过程中大部分值时不能改变的。
目前驱动程序目前只实现了最基本的收发功能。片内16K的SRAM划分如下:40~46:发送缓冲区1;46~4c:发送缓冲区2;4c~80:接收缓冲区。
另外我在调试中发现片内的SRAM是不可按地址读的,虽然我在原理图上也象CS8900A那样连了mem_wr&mem_rd,但是似乎不能访问,希望哪位高人能够给我一个明确的回答。
起初作硬件了时候我加了93C46,想使用非跳线模式,JP脚就悬空在那里。后来93C46买不到,就一直空着,虽然8019的初始化没有出问题,但是对这种不洋不土的模式,我还是心有余悸,因此将JP脚接到了5V电源,板上唯一的飞线就是这么来
现在还有一个郁闷的问题就是linux 2.4.18是不支持8019的。天下的2410开发板都采用8900a也就是这个道理。所以,我还要完成驱动!!!
posted @ 2009-05-31 16:11 陈广强 阅读(249) 评论(0)
编辑
S3C2440 CPU默认的工作主频为12MHz或16.9344MHz,这里使用最多的是12M。使用PLL电路可以产生更高的主频供CPU及外围器件使用。
S3C2440有两个PLL:MPLL和UPLL,UPLL专用与USB设备。MPLL用于CPU及其他外围器件。
通过MPLL会产生三个部分的时钟频率:FCLK、HCLK、PLCK。FCLK用于CPU核,HCLK用于AHB总线的设备(比如SDRAM),PCLK用于APB总线的设备(比如UART)。从时钟结构图中可以查看到使用不同时钟频率的硬件。

(注:这里要注意从图中看出,Uart使用的是PCLK,后面Uart实验会用到)
下面介绍MPLL的启动流程:
下图展示了上电后MPLL启动的过程:
请跟随FCLK的图像了解启动过程:
1、上电几毫秒后,晶振输出稳定,FCLK=晶振频率,nRESET信号恢复高电平后,CPU开始执行指令。
2、我们可以在程序开头启动MPLL,在设置MPLL的几个寄存器后,需要等待一段时间(Lock Time),MPLL的输出才稳定。在这段时间(Lock Time)内,FCLK停振,CPU停止工作。Lock Time的长短由寄存器LOCKTIME设定。
3、Lock Time之后,MPLL输出正常,CPU工作在新的FCLK下。
设置S3c2440的时钟频率就是设置MPLL的几个寄存器:
1、LOCKTIME:设为0x00ffffff
前面说过,MPLL启动后需要等待一段时间(Lock Time),使得其输出稳定。位[23:12]用于UPLL,位[11:0]用于MPLL。使用确省值0x00ffffff即可。
2、CLKDIVN:用来设置FCLK:HCLK:PCLK的比例关系,默认为1:1:1
这里值设为0x03,即FCLK:HCLK:PCLK=1:2:4
CLKDIVN不同的设置及对应的时钟比例关系如下图:

3、MPLLCON:设为(0x5c << 12)|(0x04 << 4)|(0x00),即0x5c0040
对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV。有如下计算公式:
MPLL(FCLK) = (m * Fin)/(p * 2^s)
其中: m = MDIV + 8, p = PDIV + 2
Fin 即默认输入的时钟频率12MHz。MPLLCON设为0x5c0040,可以计算出FCLK=200MHz,再由CLKDIVN的设置可知:HCLK=100MHz,PCLK=50MHz。
到这里我们应该彻底弄清楚了程序中经常出现的几个CLK:
Fin,MPLL,UPLL,FCLK,HCLK,PCLK.
Fin指CPU外围接的晶振本身的频率,通常为12MHz。
MPLL和UPLL分别指的是用于供整机系统的PLL和专用于USB的UPLL。
FCLK = MPLL = (m * Fin)/(p + 2^s);
HCLK,PCLK受CLKDIVN寄存器的影响,即当FCLK确定后,CLKDIVN决定了HCLK和PCLK,具体如上表。
这里UART使用PCLK,要想串口打印信息正常显示,这里是关键!
通常我们将如上时钟初始化的过程写成clock_init函数供其他函数调用,代码如下:
void clock_init(void)
{
rLOCKTIME = 0xFFFFFF;
rCLKDIVN = 0x3;
rMPLLCON = 0x5c0040;
}
实际CLKCON寄存器的设置也很关键,它控制着HCLK、PCLK是否送到AC97、Camera、LCD、UART0等模块,如果对应的模块时钟使能关掉,对对应模块的寄存器不能读写,例如我们项目中曾在romInit.s中把LCDCON的初始值设置为0x0043d00,带来的问题是以后无法写入LCD模块所对应的寄存器。
posted @ 2009-05-27 11:44 陈广强 阅读(318) 评论(0)
编辑
1、在一个大的电容上还并联一个小电容的原因
因为大电容由于容量大,所以体积一般也比较大,且通常使用多层卷绕的方式制作(动手拆过铝电解电容应该会很有体会,没拆过的也可以拿几种不同的电容拆来看看,不过要注意安全,别弄伤手),这就导致了大电容的分布电感比较大(也叫等效串联电感,英文简称ESL)。大家知道,电感对高频信号的阻抗是很大的,所以,大电容的高频性能不好。而一些小容量电容则刚刚相反,由于容量小,因此体积可以做得很小(缩短了引线,就减小了ESL,因为一段导线也可以看成是一个电感的),而且常使用平板电容的结构,这样小容量电容就有很小的ESL,这样它就具有了很好的高频性能,但由于容量小的缘故,对低频信号的阻抗大。所以,如果我们为了让低频、高频信号都可以很好的通过,就采用一个大电容再并上一个小电容的方式。常使用的小电容为0.1uF的瓷片电容,当频率更高时,还可并联更小的电容,例如几pF,几百pF的。而在数字电路中,一般要给每个芯片的电源引脚上并联一个0.1uF的电容到地(这电容叫做去耦电容,当然也可以理解为电源滤波电容。它越靠近芯片的位置越好),因为在这些地方的信号主要是高频信号,使用较小的电容滤波就可以了。
2、用多个电阻串联获得一个期望电阻为何会减小寄生电容?是什么原理?
每个电阻也有寄生电容,多个电阻串联在一起也相当于多个电容串联在一起 故根据1/C=1/C1+1/C2+1/C3····得出总的寄生电容减小
3、上下拉电阻总结
上拉电阻:
1、当TTL电路驱动COMS电路时,如果TTL电路输出的高电平低于COMS电路的最低高电平(一般为3.5V),这时就需要在TTL的输出端接上拉电阻,以提高输出高电平的值。
2、OC门电路必须加上拉电阻,才能使用。
3、为加大输出引脚的驱动能力,有的单片机管脚上也常使用上拉电阻。
4、在COMS芯片上,为了防止静电造成损坏,不用的管脚不能悬空,一般接上拉电阻产生降低输入阻抗,提供泄荷通路。
5、芯片的管脚加上拉电阻来提高输出电平,从而提高芯片输入信号的噪声容限增强抗干扰能力。
6、提高总线的抗电磁干扰能力。管脚悬空就比较容易接受外界的电磁干扰。
7、长线传输中电阻不匹配容易引起反射波干扰,加上下拉电阻是电阻匹配,有效的抑制反射波干扰。
上拉电阻阻值的选择原则包括:
1、从节约功耗及芯片的灌电流能力考虑应当足够大;电阻大,电流小。
2、从确保足够的驱动电流考虑应当足够小;电阻小,电流大。
3、对于高速电路,过大的上拉电阻可能边沿变平缓。综合考虑
以上三点,通常在1k到10k之间选取。对下拉电阻也有类似道理
对上拉电阻和下拉电阻的选择应结合开关管特性和下级电路的输入特性进行设定,主要需要考虑以下几个因素:
1. 驱动能力与功耗的平衡。以上拉电阻为例,一般地说,上拉电阻越小,驱动能力越强,但功耗越大,设计是应注意两者之间的均衡。
2. 下级电路的驱动需求。同样以上拉电阻为例,当输出高电平时,开关管断开,上拉电阻应适当选择以能够向下级电路提供足够的电流。
3. 高低电平的设定。不同电路的高低电平的门槛电平会有不同,电阻应适当设定以确保能输出正确的电平。以上拉电阻为例,当输出低电平时,开关管导通,上拉电阻和开关管导通电阻分压值应确保在零电平门槛之下。
4. 频率特性。以上拉电阻为例,上拉电阻和开关管漏源级之间的电容和下级电路之间的输入电容会形成RC延迟,电阻越大,延迟越大。上拉电阻的设定应考虑电路在这方面的需求。
下拉电阻的设定的原则和上拉电阻是一样的。
OC门输出高电平时是一个高阻态,其上拉电流要由上拉电阻来提供,设输入端每端口不大于100uA,设输出口驱动电流约500uA,标准工作电压是5V,输入口的高低电平门限为0.8V(低于此值为低电平);2V(高电平门限值)。
选上拉电阻时:
500uA x 8.4K= 4.2即选大于8.4K时输出端能下拉至0.8V以下,此为最小阻值,再小就拉不下来了。如果输出口驱动电流较大,则阻值可减小,保证下拉时能低于0.8V即可。
当输出高电平时,忽略管子的漏电流,两输入口需200uA
200uA x15K="3V"即上拉电阻压降为3V,输出口可达到2V,此阻值为最大阻值,再大就拉不到2V了。选10K可用。COMS门的可参考74HC系列
设计时管子的漏电流不可忽略,IO口实际电流在不同电平下也是不同的,上述仅仅是原理,一句话概括为:输出高电平时要喂饱后面的输入口,输出低电平不要把输出口喂撑了(否则多余的电流喂给了级联的输入口,高于低电平门限值就不可靠了)
在数字电路中不用的输入脚都要接固定电平,通过1k电阻接高电平或接地。
1. 电阻作用:
l 接电组就是为了防止输入端悬空
l 减弱外部电流对芯片产生的干扰
l 保护cmos内的保护二极管,一般电流不大于10mA
l 上拉和下拉、限流
l 1. 改变电平的电位,常用在TTL-CMOS匹配
2. 在引脚悬空时有确定的状态
3.增加高电平输出时的驱动能力。
4、为OC门提供电流
l 那要看输出口驱动的是什么器件,如果该器件需要高电压的话,而输出口的输出电压又不够,就需要加上拉电阻。
l 如果有上拉电阻那它的端口在默认值为高电平你要控制它必须用低电平才能控制如三态门电路三极管的集电极,或二极管正极去控制把上拉电阻的电流拉下来成为低电平。反之,
l 尤其用在接口电路中,为了得到确定的电平,一般采用这种方法,以保证正确的电路状态,以免发生意外,比如,在电机控制中,逆变桥上下桥臂不能直通,如果它们都用同一个单片机来驱动,必须设置初始状态.防止直通!
2、定义:
l 上拉就是将不确定的信号通过一个电阻嵌位在高电平!电阻同时起限流作用!下拉同理!
l 上拉是对器件注入电流,下拉是输出电流
l 弱强只是上拉电阻的阻值不同,没有什么严格区分
l 对于非集电极(或漏极)开路输出型电路(如普通门电路)提升电流和电压的能力是有限的,上拉电阻的功能主要是为集电极开路输出型电路输出电流通道。
3、为什么要使用拉电阻:
l 一般作单键触发使用时,如果IC本身没有内接电阻,为了使单键维持在不被触发的状态或是触发后回到原状态,必须在IC外部另接一电阻。
l 数字电路有三种状态:高电平、低电平、和高阻状态,有些应用场合不希望出现高阻状态,可以通过上拉电阻或下拉电阻的方式使处于稳定状态,具体视设计要求而定!
l 一般说的是I/O端口,有的可以设置,有的不可以设置,有的是内置,有的是需要外接,I/O端口的输出类似与一个三极管的C,当C接通过一个电阻和电源连接在一起的时候,该电阻成为上C拉电阻,也就是说,如果该端口正常时为高电平,C通过一个电阻和地连接在一起的时候,该电阻称为下拉电阻,使该端口平时为低电平,作用吗:
比如:当一个接有上拉电阻的端口设为输如状态时,他的常态就为高电平,用于检测低电平的输入。
l 上拉电阻是用来解决总线驱动能力不足时提供电流的。一般说法是拉电流,下拉电阻是用来吸收电流的,也就是你同学说的灌电流
---------------------------------------------------------------------------------
有可商讨的地方。
1 、长线传输中电阻不匹配容易引起反射波干扰,加上下拉电阻是电阻匹配,有效的抑制反射波干扰。
电阻串联才是实现阻抗匹配的好方法。通常线阻的数量级都在几十ohm,如果加上下拉的话,功耗太大。
电阻串联和拉电阻都是阻抗匹配的方法,只是使用范围不同,依电路工作频率而定
21、当TTL电路驱动COMS电路时,如果TTL电路输出的高电平低于COMS电路的最低高电平(一般为3.5V),这时就需要在TTL的输出端接上拉电阻,以提高输出高电平的值。
不建议采用这种方法。缺点有2。1 TTL输出地电平时,功耗大。2TTL 输出高电平时,上拉电源可能会有电流灌到TTL电路的电源,影响系统稳定性。
3 3、对于高速电路,过大的上拉电阻可能边沿变平缓。
应该不会。做输入时,上拉电阻又不吸收电流。做输出时,驱动电流为 电路输出电流+上拉通道输出电流。 电阻的容性特征很小,可忽略。
4 2. 下级电路的驱动需求。同样以上拉电阻为例,当输出高电平时,开关管断开,上拉电阻应适当选择以能够向下级电路提供足够的电流。
当输出高电平时,开关管怎么回关断呢? CMOS电路的输出级基本上是推拉时。输出地电平时,下面的MOSFET关断,上面的导通。高电平时反过来。该条只适合OC电路。
posted @ 2009-05-26 09:43 陈广强 阅读(168) 评论(0)
编辑
引用(reference)是c++的初学者比较容易迷惑的概念。下面我们比较详细地讨论引用。
一、引用的概念
引用引入了对象的一个同义词。定义引用的表示方法与定义指针相似,只是用&代替了*。
例如: Point pt1(10,10);
Point &pt2=pt1; 定义了pt2为pt1的引用。通过这样的定义,pt1和pt2表示同一对象。
需要特别强调的是引用并不产生对象的副本,仅仅是对象的同义词。因此,当下面的语句执行后:
pt1.offset(2,2);
pt1和pt2都具有(12,12)的值。
引用必须在定义时马上被初始化,因为它必须是某个东西的同义词。你不能先定义一个引用后才
初始化它。例如下面语句是非法的:
Point &pt3;
pt3=pt1;
那么既然引用只是某个东西的同义词,它有什么用途呢?
下面讨论引用的两个主要用途:作为函数参数以及从函数中返回左值。
二、引用参数
1、传递可变参数
传统的c中,函数在调用时参数是通过值来传递的,这就是说函数的参数不具备返回值的能力。
所以在传统的c中,如果需要函数的参数具有返回值的能力,往往是通过指针来实现的。比如,实现
两整数变量值交换的c程序如下:
void swapint(int *a,int *b)
{
int temp;
temp=*a;
a=*b;
*b=temp;
}
使用引用机制后,以上程序的c++版本为:
void swapint(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}
调用该函数的c++方法为:swapint(x,y); c++自动把x,y的地址作为参数传递给swapint函数。
2、给函数传递大型对象
当大型对象被传递给函数时,使用引用参数可使参数传递效率得到提高,因为引用并不产生对象的
副本,也就是参数传递时,对象无须复制。下面的例子定义了一个有限整数集合的类:
const maxCard=100;
Class Set
{
int elems[maxCard]; // 集和中的元素,maxCard 表示集合中元素个数的最大值。
int card; // 集合中元素的个数。
public:
Set () {card=0;} //构造函数
friend Set operator * (Set ,Set ) ; //重载运算符号*,用于计算集合的交集 用对象作为传值参数
// friend Set operator * (Set & ,Set & ) 重载运算符号*,用于计算集合的交集 用对象的引用作为传值参数
...
}
先考虑集合交集的实现
Set operator *( Set Set1,Set Set2)
{
Set res;
for(int i=0;i<Set1.card;++i)
for(int j=0;j>Set2.card;++j)
if(Set1.elems[i]==Set2.elems[j])
{
res.elems[res.card++]=Set1.elems[i];
break;
}
return res;
}
由于重载运算符不能对指针单独操作,我们必须把运算数声明为 Set 类型而不是 Set * 。
每次使用*做交集运算时,整个集合都被复制,这样效率很低。我们可以用引用来避免这种情况。
Set operator *( Set &Set1,Set &Set2)
{ Set res;
for(int i=0;i<Set1.card;++i)
for(int j=0;j>Set2.card;++j)
if(Set1.elems[i]==Set2.elems[j])
{
res.elems[res.card++]=Set1.elems[i];
break;
}
return res;
}
三、引用返回值
如果一个函数返回了引用,那么该函数的调用也可以被赋值。这里有一函数,它拥有两个引用参数并返回一个双精度数的引用:
double &max(double &d1,double &d2)
{
return d1>d2?d1:d2;
}
由于max()函数返回一个对双精度数的引用,那么我们就可以用max() 来对其中较大的双精度数加1:
max(x,y)+=1.0;
C++引用与指针的比较
引用是C++中的概念,初学者容易把引用和指针混淆一起。
一下程序中,n是m的一个引用(reference),m是被引用物(referent)。
int m;
int &n = m;
n相当于m的别名(绰号),对n的任何操作就是对m的操作。
所以n既不是m的拷贝,也不是指向m的指针,其实n就是m它自己。
引用的规则:
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
以下示例程序中,k被初始化为i的引用。
语句k = j并不能将k修改成为j的引用,只是把k的值改变成为6。
由于k是i的引用,所以i的值也变成了6。
int i = 5;
int j = 6;
int &k = i;
k = j; // k和i的值都变成了6;
引用的主要功能是传递函数的参数和返回值。
C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。
以下是"值传递"的示例程序。
由于Func1函数体内的x是外部变量n的一份拷贝,改变x的值不会影响n, 所以n的值仍然是0。
void Func1(int x)
{
x = x + 10;
}
...
int n = 0;
Func1(n);
cout << "n = " << n << endl; // n = 0
以下是"指针传递"的示例程序。
由于Func2函数体内的x是指向外部变量n的指针,改变该指针的内容将导致n的值改变,所以n的值成为10。
void Func2(int *x)
{
(* x) = (* x) + 10;
}
...
int n = 0;
Func2(&n);
cout << "n = " << n << endl; // n = 10
以下是"引用传递"的示例程序。
由于Func3函数体内的x是外部变量n的引用,x和n是同一个东西,改变x等于改变n,所以n的值成为10。
void Func3(int &x)
{
x = x + 10;
}
...
int n = 0;
Func3(n);
cout << "n = " << n << endl; // n = 10
对比上述三个示例程序,会发现"引用传递"的性质象"指针传递",而书写方式象"值传递"。
实际上"引用"可以做的任何事情"指针"也都能够做,为什么还要"引用"这东西?
答案是"用适当的工具做恰如其分的工作"。
指针能够毫无约束地操作内存中的任何东西,尽管指针功能强大,但是非常危险。
如果的确只需要借用一下某个对象的"别名",那么就用"引用",而不要用"指针",以免发生意外。
posted @ 2009-05-09 14:33 陈广强 阅读(280) 评论(0)
编辑
posted @ 2009-05-03 00:51 陈广强 阅读(183) 评论(0)
编辑
posted @ 2009-05-03 00:50 陈广强 阅读(85) 评论(0)
编辑
posted @ 2009-05-03 00:49 陈广强 阅读(124) 评论(0)
编辑
posted @ 2009-05-03 00:48 陈广强 阅读(73) 评论(0)
编辑
posted @ 2009-05-02 17:41 陈广强 阅读(155) 评论(1)
编辑