EDN首页   博客首页

最新日志

发表于:2007/11/16 21:16:47
标签:无标签

0

卖给了中科院西安光机所了!

大家认为这家单位怎么样啊?

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

评论(2) | 阅读(884)
发表于:2007/11/16 21:00:29
标签:无标签

1

An unspecified Debug Toolbox call failed

把EasyJtag.dll等文件拷到ads安装目录下的Bin目录下,就好
我也遇到这样的事儿,把EasyJtag.dll等文件拷到ads安装目录下的Bin目录下,就好了

系统分类: 嵌入式   |    用户分类: 无分类    |    来源: 无分类

评论(2) | 阅读(940)
发表于:2007/9/12 17:07:28
标签:无标签

0

基于DSP的超声编码激励发射研究

基于DSP的超声编码激励发射研究

蔡欣荣

1.西安交通大学电子与信息工程学院,西安,7100492.西安交通大学生命学院,西安,710049

摘要与传统的脉冲回波成像技术相比,编码激励技术能够提高超声图像的信噪比及降低脉冲峰值功率。文章首先介绍了GOLAY码互补序列对作为超声编码激励的原理和优点,然后采用TITMS320F2812 DSP芯片,把预先编制好的GOLAY码脉冲序列存储在DSP芯片的FLASH中,通过DSPMcBSP多通道缓冲串口把编码脉冲发送给电压放大器,驱动MOS管信号放大后直接加载在超声探头上,回波信号增益采集,最后,对采集到的数据进行脉冲压缩及求和。实验数据表明信号信杂比(SCR)约为31dB,已经满足了医学超声成像的要求。

关键字Golay互补序列对;DSP MOS McBSP  ;超声换能器 

中图分类号 TP274+ .2     

 

Pulse Transmitting In Ultrasound Coded Excitation System Based On DSP

CAI Xinrong1MIAO Xianglin1WANG Xiaosen1LI Peng2, BIAN Zhengzhong2

(.Xi’an Jiaotong University, Xi’an 710049, China2.Xi’an Jiaotong University, Xi’an 710049, China)

Abstract: Compare with traditional pulse echo ultrasound imagingcoded excitation can get high SNR of ultrasound image and low the high pulse peak powerThis paper first introduce the basic theory and excellence of Golay complementary serial pairthen we use tms320f2812 A type of Dsp which is produced by TI(Texas instrument),we put these coded excitation which programmed in advance into the flash of DSPusing McBSPmultichannel buffered serial portextracts these pulse to electric voltage enlargerdrive MOS and get enlarger voltage to probeecho signals gained and collectedat last ,compress the collected signals and add A echo signal and Bs. The results show that the signal to clutter ratio is about 31dB,which already satisfy medical ultrasound system

Keywords Golay complementary serial pair Digital Signal Processing MOS Multichannel buffered serial portUltrasound transducer

引言

传统医学超声成像系统通常采用单一脉冲波[12],为了获取高的信噪比,需要提高发射的脉冲峰值功率,这样就受到安全诊断阈值的限制及超声换能器非线性制约。因此,传统的医学超声系统存在脉冲峰值功率高、信噪比差、穿透力弱等缺陷。

现代超声医学成像系统采用编码激励脉冲序列来替代单一脉冲作为发射信号[3],降低了发射脉冲的峰值。在接收信号时经过相关解码电路,探测到人体深部的微弱回波信号。选择一组二值自相关性好的编码序列(如GOLAY互补序列对),将其作为超声编码激励成像系统的发射编码,来达到提高图像的信噪比和穿透力,且实现动态超声图像的实时处理。

    文章以GOLAY码互补序列对为例研究了基于DSP的超声编码激励发射,实验数据表明信号信杂比SCR31dB,已经满足医学超声成像的要求。

1GOLAY 码互补序列对

11 采用GOLAY互补序列对模型

GOLAY互补序列对定义[4]:一对由两种元素构成的等长有限序列,且在任何给定间隔下,一个序列中的相同元素对的个数等于另一个序列中的相异元素对的个数。数学语言描述如下:设有一对长度相同的有限二相序列 ,其非周期自相关函数分别为:

如果           

就称序列A和序列B互补,或称AB为互补序列。二相互补序列长度为N必须是偶数,且为两个完全平方数之和。

12 GOLAY互补序列对自相关

我们要发射的是128GOLAY码互补序列对,设探头的中心频率为5MHz,则GOLAY码脉宽为100 128位需要 。这里构造GOLAY互补对分别为A、B(各自长度为128比特),他们的自相关后数据图表示分别为:

aA序列码自相关,(bB序列码自相关,(c)两个自相关结果求和之后为。    

    

图1双极Golay互补序列对自相关(a)、(b)及自                                                              相关求和结果(c)波形图

              

由图1可以看出,采用GOLAY码互补序列对作为发射激励码,自相关之后求和,旁瓣已经完全消失了,他们的各自的回波自相关也具有这样的特性,所以,采用GOLAY码互补序列对,其信号穿透力强,图象性噪比高。

 

 

2 系统整体设计

21 采用DSP理论模型

TMS320F2812是美国德州仪器公司(TI公司)推出的最先进也是功能最强大的32位定点DSP芯片。该芯片处理能力强,运算速度高,具有丰富的片内外设,如内部看门狗、CANMCBSPSCIADC、集成Flash等。该处理器芯片主要用于家电产品、工业控制场合等高性能的应用领域。

本文使用TMS320F2812[5]作为发射编码的主控芯片,使用多通道缓冲串口(McBSP[6])来完成连续128位编码的发射,并且具有发射速率可以随时修改,以适应不同频率的超声探头(一般探头有3.5MHz5MHz等),编码通过DSPMDXA口发送给超声脉冲电压放大板,进行电压放大,放大的电压送入探头换能器,进行电声转换,超声在组织内的回波被超声探头换能器接收进行声电转换,之后信号进行放大并验证波形。

22 系统硬件设计

系统时钟采用30MHz,因为TMS320F2812可以工作在150MHz,所以要想全频率工作,PLL需要5倍频率, JP3McBSP多引脚,其中我们用到了MDXA口,其他引脚在该发射编码程序中暂且不用,SPICLKASCITXDASPISTEAMDXA这些引脚还具有跳线设置功能,只有当SCITXDA设置为高电平,系统从FLASH开始执行程序。该芯片内部集成看门狗,方便调试、节省资源。XMP/MC一般拉地,那么该芯片就认为微计算机器状态,无须外扩FLASHRAM

    连续128位的编码激励信号经过DSPMDXA口发出之后,送入SN75372[8]非门进行电压提升,提升的电压可以驱动IRFU420[7]NMOS管,在MOS管源极下拉电阻并在源极处取电压送入探头。当开始产生回波信号时,NMOS此时截止,回波信号送入运算放大器AD8048[8]同向放大,放大倍数为  。为了改善放大部分的波形,在运放输出端加小电容滤波,去处尖锐沿,图3

2 DSP 硬件电路部分

3采用IRFU420  NMOS管进行电压放大

23 软件系统设计

2.3.1程序设计流程描述

DSP编码发射程序是在CCS2.0环境下实现的。整个系统过程包括程序代码的编写,程序调试以及在线把程序下载到片内Flash中。

CCSCode Composer Studio)是美国德州仪器公司(TI)开发的一个完整的DSP集成开发环境。由于TI DSP使用非常广泛,使得CCS也就成为目前使用最为广泛的DSP开发软件之一。整个程序流程图如图4

 

 

 

 

 

 

 

 

 

 

 

 

 

判断选择:奇次发A序列,偶数次发B序列

 

 

 

 

 

 


初始化通用GPIO口,设置GPIO8~GPIO13McBSP口。

清除所有中断,初始化系统中断向量表、向量控制寄存器。

初始化McBSP口,包括串口控制寄存器、发送控制寄存器、引脚控制寄存器等。设置32位字发送模式,128位需要4个字发送,发送速率为5MHz

初始化32位比特字模式,启动发送控制寄存器,开始连续发送编码,并匹配时序。

一次发送128位结束,开始发送连续高电平,经过反向之后使得MOS管截止,这期间开始回波采集和运算放大,并且DSP要喂狗,防止程序跑飞。

 

循环发送,奇偶变换

初始化系统控制寄存器,包括看门狗寄存器,系统时钟倍频(30MHz*5倍频),外设时钟(McBSP为低速时钟)。

                                 4 程序设计流程图

 

2.3.2 回波分析与采集

在超声成像系统中,若不考虑超声在介质中的衰减时,信号通道的框图如图6所示[9] 是激励信号, 是发射换能器的传递函数, 是声场中的反射函数, 是接收换能器的传递函数, 是接收电路的电子噪声。 分别是他们的傅立叶变换[9]

 

 

 

 

激励信号

e(t), E(f)

 

发射换能器的

传递函数

p1(t)P1(f)

发射函数

u(t),U(f)

接收换能器的传递函数

p2(t),P2(f)

电子噪声

n(t), N(f)

 

 

 


超声回波 r(t),R(f)

5超声成像中信号通道框图

所以超声回波 可以表示为:

医学超声成像系统的研究中,通常将发射换能器和接收换能器的传递函数视为一致的,即: 为了简化分析,只研究声场中单一散射子的反射,不考虑接收电路电子噪声的影响,即:   。因此,(3-2-12)式简化为:

 

 

 

 

 

 

 

 

 

 

6  128GOLAY码互补序列对的各自回波压力场(a)(b),两者求和压缩脉冲如图(c),压缩脉冲包络(d),其分贝表示的压缩脉冲包络为(e

 

从图6可以看出,采用编码激励超声脉冲发射方式其信杂比为31dB,完全满足了医学超声成像的要求。

 

 

 

3 结  论  

本文采用GOLAY码互补序列对作为发射编码激励脉冲,使用DSP2812实现编码的发射,使用MOS进行电压放大,回波信号因为很小,进行增益补偿放大。整个过程包括DSP板和发射电压放大以及回波信号放大板,软件是在CCS2.0软件下编制和调试,具体的时序调试需要匹配好,达到正确发射,在工程实际中要注意每次编码发射之后的空闲循环时间和帧频、每帧线数搭配好。采集的回波信号在MATLAB中展开,并在MATLAB中进行编码压缩和求和。

参考文献:

[1] D.NahamooACKak Ultrasonic echo imaging with pesdu-random and  pulsed sourcesacomparative study.Ultrasonic Imaging[J]198131-36

[2] 关力勋,雷纪胜.超声诊断仪原理和维护[M] 北京:人民卫生出版社,1982

[3] N.AH.K.Rao.Investigation of pulse compression technique for medical ultrasound:A smulation study. med.biol.Eng.comp.[J].1994,32(2):181-188

[4] MJGolayComplementary series  IRE trans Inform  Theory[J]  1961  Apr  IT-7:82-87

[5] TMS320F2812. Data Sheet. Texas Instruments 2001

[6] TMS320F28x Multichannel Buffered Serial  Port (McBSP)  Peripheral Reference Guide  2002

[7] IRFU420. Data Sheet. International Rectifier

[8] SN75372. Data Sheet.  Texas Instruments  1986

[9]AD8048  DataSheet  Analog device

[9] 彭旗宇.数字化医学超声成像方法的研究(清华大学博士学位论文)[D] 清华大学,2003

 

系统分类: DSP   |    用户分类: 无分类    |    来源: 无分类

评论(1) | 阅读(1013)
发表于:2007/9/12 17:00:29
标签:无标签

0

关于CPSR_cxsf

关于CPSR_cxsf

msr cpsr_cxsf,r1        ;这里的cxsf表示从低到高分别占用的4个8bit的数据域
指令中有时还有出现cpsr_cf, cpsr_all, cpsr_c等,这里:

        c 指  CPSR中的control field ( PSR[7:0])
        f 指  flag field (PSR[31:24])
        x 指  extend field (PSR[15:8])
        s 指  status field ( PSR[23:16])
其中cpsr的位表示为:
31 30 29 28  ---   7   6   -   4     3     2     1     0
N   Z   C   V         I   F       M4  M3  M2  M1  M0

                                       0    0    0    0    0     User26 模式
                                       0    0    0    0    1     FIQ26 模式
                                       0    0    0    1    0     IRQ26 模式
                                       0    0    0    1    1     SVC26 模式
                                       1    0    0    0    0     User 模式
                                       1    0    0    0    1     FIQ 模式
                                       1    0    0    1    0     IRQ 模式
                                       1    0    0    1    1     SVC 模式
                                       1    0    1    1    1     ABT 模式
                                       1    1    0    1    1     UND 模式
 
深入分析:
对于MSR(寄存器到状态寄存器)的指令,
        MSR CPSR,        r0
        MSR CPSR_all,    r0
        MSR CPSR_flg,   r0
都是已经过时的表示方法。
对于MRS(状态寄存器到寄存器)的指令,
        MRS R0,     CPSR        等同于MRS R0, CPSR_cxsf
        MRS R0,     CPSR_all    会有waring
        MRS R0,     CPSR_flg   会有错误
在ADS中使用c,f,x,s表示cpsr的各个部分是推荐的。从指令来说:
        MSR CPSR_f,         r0机器码为0xe128f000
        MSR CPSR_c,         r0机器码为0xe121f000
        MSR CPSR_x,         r0机器码为0xe122f000
        MSR CPSR_s,         r0机器码为0xe124f000
可见机器码中用bit[29:16]4bit表示是f,c,x,s的。所以能够在机器执行的时候,
给予不同的执行结果。为了代码向后兼容性,建议使用f,c,x,s尾缀。

系统分类: ARM   |    用户分类: 无分类    |    来源: 无分类

评论(0) | 阅读(1210)
发表于:2007/3/13 10:58:24
标签:Delphi中的线程类  

2

Delphi中的线程类

Delphi中的线程类--之(1
Delphi
中的线程类--之(1    Raptor(原作)  
  
关键字
     Thread Event CriticalSection Synchronize 
  
Delphi
中的线程类


猛禽
[Mental Studio]

http://mental.mentsu.com

 之一)


Delphi
中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchronize的用法就完了。然而这并不是多线程编程的全部,我写此文的目的在于对此作一个补充。


线程本质上是进程中一段并发运行的代码。一个进程至少有一个线程,即所谓的主线程。同时还可以有多个子线程。当一个进程中用到超过一个线程时,就是所谓的多线程


那么这个所谓的一段代码是如何定义的呢?其实就是一个函数或过程(对Delphi而言)。


如果用Windows API来创建线程的话,是通过一个叫做CreateThreadAPI函数来实现的,它的定义为:


HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes, 
    DWORD dwStackSize, 
    LPTHREAD_START_ROUTINE lpStartAddress, 
    LPVOID lpParameter, 
    DWORD dwCreationFlags, 
    LPDWORD lpThreadId 
   );

其各参数如它们的名称所说,分别是:线程属性(用于在NT下进行线程的安全属性设置,在9X下无效),堆栈大小,起始地址,参数,创建标志(用于设置线程创建时的状态),线程ID,最后返回线程Handle。其中的起始地址就是线程函数的入口,直至线程函数结束,线程也就结束了。

整个线程的执行过程如下图:
此主题相关图片如下:

因为CreateThread参数很多,而且是WindowsAPI,所以在C Runtime Library里提供了一个通用的线程函数(理论上可以在任何支持线程的OS中使用):

unsigned long _beginthread(void (_USERENTRY *__start)(void *), unsigned __stksize, void *__arg);

Delphi
也提供了一个相同功能的类似函数:


function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord; ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord; var ThreadId: LongWord): Integer;

这三个函数的功能是基本相同的,它们都是将线程函数中的代码放到一个独立的线程中执行。线程函数与一般函数的最大不同在于,线程函数一启动,这三个线程启动函数就返回了,主线程继续向下执行,而线程函数在一个独立的线程中执行,它要执行多久,什么时候返回,主线程是不管也不知道的。


正常情况下,线程函数返回后,线程就终止了。但也有其它方式:


Windows API


VOID ExitThread( DWORD dwExitCode );
C Runtime Library


void _endthread(void);

 

Delphi Runtime Library

procedure EndThread(ExitCode: Integer);

为了记录一些必要的线程数据(状态/属性等),OS会为线程创建一个内部Object,如在Windows中那个Handle便是这个内部ObjectHandle,所以在线程结束的时候还应该释放这个Object


 

虽然说用APIRTL(Runtime Library)已经可以很方便地进行多线程编程了,但是还是需要进行较多的细节处理,为此DelphiClasses单元中对线程作了一个较好的封装,这就是VCL的线程类:
TThread

使用这个类也很简单,大多数的Delphi书籍都有说,基本用法是:先从TThread派生一个自己的线程类(因为TThread是一个抽象类,不能生成实例),然后是Override抽象方法:Execute(这就是线程函数,也就是在线程中执行的代码部分),如果需要用到可视VCL对象,还需要通过Synchronize过程进行。关于之方面的具体细节,这里不再赘述,请参考相关书籍。


本文接下来要讨论的是TThread类是如何对线程进行封装的,也就是深入研究一下TThread类的实现。因为只是真正地了解了它,才更好地使用它。


下面是DELPHI7TThread类的声明(本文只讨论在Windows平台下的实现,所以去掉了所有有关Linux平台部分的代码):


  TThread = class
  private
    FHandle: THandle;
    FThreadID: THandle;
    FCreateSuspended: Boolean;
    FTerminated: Boolean;
    FSuspended: Boolean;
    FFreeOnTerminate: Boolean;
    FFinished: Boolean;
    FReturnValue: Integer;
    FOnTerminate: TNotifyEvent;
    FSynchronize: TSynchronizeRecord;
    FFatalException: TObject;
    procedure CallOnTerminate;
    class procedure Synchronize(ASyncRec: PSynchronizeRecord); overload;
    function GetPriority: TThreadPriority;
    procedure SetPriority(Value: TThreadPriority);
    procedure SetSuspended(Value: Boolean);

  protected
    procedure CheckThreadError(ErrCode: Integer); overload;
    procedure CheckThreadError(Success: Boolean); overload;
   procedure DoTerminate; virtual;
    procedure Execute; virtual; abstract;
    procedure Synchronize(Method: TThreadMethod); overload;
    property ReturnValue: Integer read FReturnValue write FReturnValue;
    property Terminated: Boolean read FTerminated;


  public
    constructor Create(CreateSuspended: Boolean);
    destructor Destroy; override;
    procedure AfterConstruction; override;
    procedure Resume;
    procedure Suspend;
    procedure Terminate;
    function WaitFor: LongWord;
    class procedure Synchronize(AThread: TThread; AMethod: TThreadMethod); overload;
    class procedure StaticSynchronize(AThread: TThread; AMethod: TThreadMethod);
    property FatalException: TObject read FFatalException;
    property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
    property Handle: THandle read FHandle;
    property Priority: TThreadPriority read GetPriority write SetPriority;
    property Suspended: Boolean read FSuspended write SetSuspended;
    property ThreadID: THandle read FThreadID;
    property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
  end;

TThread
类在DelphiRTL里算是比较简单的类,类成员也不多,类属性都很简单明白,本文将只对几个比较重要的类成员方法和唯一的事件:OnTerminate作详细分析。

(待续)

 

 

Delphi中的线程类--之(2
 Delphi
中的线程类--之(2    Raptor(原作)  
  
关键字
     Thread Event CriticalSection Synchronize 
  
Delphi
中的线程类


猛禽
[Mental Studio]

http://mental.mentsu.com

之二


首先就是构造函数:


constructor TThread.Create(CreateSuspended: Boolean);
begin
  inherited Create;
  AddThread;
  FSuspended := CreateSuspended;
  FCreateSuspended := CreateSuspended;
  FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);
  if FHandle = 0 then
    raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]);
end;

虽然这个构造函数没有多少代码,但却可以算是最重要的一个成员,因为线程就是在这里被创建的。


在通过Inherited调用TObject.Create后,第一句就是调用一个过程:AddThread,其源码如下:


procedure AddThread;
begin
  InterlockedIncrement(ThreadCount);
end;

同样有一个对应的RemoveThread

procedure RemoveThread;
begin
  InterlockedDecrement(ThreadCount);
end;

它们的功能很简单,就是通过增减一个全局变量来统计进程中的线程数。只是这里用于增减变量的并不是常用的Inc/Dec过程,而是用了InterlockedIncrement/InterlockedDecrement这一对过程,它们实现的功能完全一样,都是对变量加一或减一。但它们有一个最大的区别,那就是InterlockedIncrement/InterlockedDecrement是线程安全的。即它们在多线程下能保证执行结果正确,而Inc/Dec不能。或者按操作系统理论中的术语来说,这是一对原语操作。


以加一为例来说明二者实现细节上的不同:


一般来说,对内存数据加一的操作分解以后有三个步骤:


1
  从内存中读出数据

2
  数据加一
3
  存入内存

现在假设在一个两个线程的应用中用Inc进行加一操作可能出现的一种情况:
1
  线程A从内存中读出数据(假设为3
2
  线程B从内存中读出数据(也是3
3
  线程A对数据加一(现在是4
4
  线程B对数据加一(现在也是4
5
  线程A将数据存入内存(现在内存中的数据是4
6
  线程B也将数据存入内存(现在内存中的数据还是4,但两个线程都对它加了一,应该是5才对,所以这里出现了错误的结果)

而用InterlockIncrement过程则没有这个问题,因为所谓原语是一种不可中断的操作,即操作系统能保证在一个原语执行完毕前不会进行线程切换。所以在上面那个例子中,只有当线程A执行完将数据存入内存后,线程B才可以开始从中取数并进行加一操作,这样就保证了即使是在多线程情况下,结果也一定会是正确的。


前面那个例子也说明一种线程访问冲突的情况,这也就是为什么线程之间需要同步Synchronize),关于这个,在后面说到同步时还会再详细讨论。


说到同步,有一个题外话:加拿大滑铁卢大学的教授李明曾就Synchronize一词在线程同步中被译作同步提出过异议,个人认为他说的其实很有道理。在中文中同步的意思是同时发生,而线程同步目的就是避免这种同时发生的事情。而在英文中,Synchronize的意思有两个:一个是传统意义上的同步(To occur at the same time),另一个是协调一致To operate in unison)。在线程同步中的Synchronize一词应该是指后面一种意思,即保证多个线程在访问同一数据时,保持协调一致,避免出错。不过像这样译得不准的词在IT业还有很多,既然已经是约定俗成了,本文也将继续沿用,只是在这里说明一下,因为软件开发是一项细致的工作,该弄清楚的,绝不能含糊。


扯远了,回到TThread的构造函数上,接下来最重要就是这句了:


FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);

这里就用到了前面说到的Delphi RTL函数BeginThread,它有很多参数,关键的是第三、四两个参数。第三个参数就是前面说到的线程函数,即在线程中执行的代码部分。第四个参数则是传递给线程函数的参数,在这里就是创建的线程对象(即Self)。其它的参数中,第五个是用于设置线程在创建后即挂起,不立即执行(启动线程的工作是在AfterConstruction中根据CreateSuspended标志来决定的),第六个是返回线程ID


现在来看TThread的核心:线程函数ThreadProc。有意思的是这个线程类的核心却不是线程的成员,而是一个全局函数(因为BeginThread过程的参数约定只能用全局函数)。下面是它的代码:


function ThreadProc(Thread: TThread): Integer;
var
  FreeThread: Boolean;
begin
  try
    if not Thread.Terminated then
    try
      Thread.Execute;
    except
      Thread.FFatalException := AcquireExceptionObject;
    end;
  finally
    FreeThread := Thread.FFreeOnTerminate;
    Result := Thread.FReturnValue;
    Thread.DoTerminate;
    Thread.FFinished := True;
    SignalSyncEvent;
    if FreeThread then Thread.Free;
    EndThread(Result);
  end;
end;

虽然也没有多少代码,但却是整个TThread中最重要的部分,因为这段代码是真正在线程中执行的代码。下面对代码作逐行说明:


首先判断线程类的Terminated标志,如果未被标志为终止,则调用线程类的Execute方法执行线程代码,因为TThread是抽象类,Execute方法是抽象方法,所以本质上是执行派生类中的Execute代码。


所以说,Execute就是线程类中的线程函数,所有在Execute中的代码都需要当作线程代码来考虑,如防止访问冲突等。


如果Execute发生异常,则通过AcquireExceptionObject取得异常对象,并存入线程类的FFatalException成员中。


最后是线程结束前做的一些收尾工作。局部变量FreeThread记录了线程类的FreeOnTerminated属性的设置,然后将线程返回值设置为线程类的返回值属性的值。然后执行线程类的DoTerminate方法。


DoTerminate
方法的代码如下:

procedure TThread.DoTerminate;
begin
  if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end;


很简单,就是通过Synchronize来调用CallOnTerminate方法,而CallOnTerminate方法的代码如下,就是简单地调用OnTerminate事件:

procedure TThread.CallOnTerminate;
begin
  if Assigned(FOnTerminate) then FOnTerminate(Self);
end;

因为OnTerminate事件是在Synchronize中执行的,所以本质上它并不是线程代码,而是主线程代码(具体见后面对Synchronize的分析)。


执行完OnTerminate后,将线程类的FFinished标志设置为True


接下来执行SignalSyncEvent过程,其代码如下:


procedure SignalSyncEvent;
begin
  SetEvent(SyncEvent);
end;

也很简单,就是设置一下一个全局EventSyncEvent,关于Event的使用,本文将在后文详述,而SyncEvent的用途将在WaitFor过程中说明。


然后根据FreeThread中保存的FreeOnTerminate设置决定是否释放线程类,在线程类释放时,还有一些些操作,详见接下来的析构函数实现。


最后调用EndThread结束线程,返回线程返回值。


至此,线程完全结束。


 
(待续)


 

Delphi中的线程类--之(3
Delphi
中的线程类--之(3    Raptor(原作)  
  
关键字
     Thread Event CriticalSection Synchronize 
  
Delphi
中的线程类


猛禽[Mental Studio]

http://mental.mentsu.com

之三


说完构造函数,再来看析构函数:


destructor TThread.Destroy;
begin
  if (FThreadID <> 0) and not FFinished then
  begin
    Terminate;
    if FCreateSuspended then
      Resume;
    WaitFor;
  end;

  if FHandle <> 0 then CloseHandle(FHandle);
  inherited Destroy;
  FFatalException.Free;
  RemoveThread;
end;

在线程对象被释放前,首先要检查线程是否还在执行中,如果线程还在执行中(线程ID不为0,并且线程结束标志未设置),则调用Terminate过程结束线程。Terminate过程只是简单地设置线程类的Terminated标志,如下面的代码:


procedure TThread.Terminate;
begin
  FTerminated := True;
end;

所以线程仍然必须继续执行到正常结束后才行,而不是立即终止线程,这一点要注意。

在这里说一点题外话:很多人都问过我,如何才能立即终止线程(当然是指用TThread创建的线程)。结果当然是不行!终止线程的唯一办法就是让Execute方法执行完毕,所以一般来说,要让你的线程能够尽快终止,必须在Execute方法中在较短的时间内不断地检查Terminated标志,以便能及时地退出。这是设计线程代码的一个很重要的原则!


当然如果你一定要能立即退出线程,那么TThread类不是一个好的选择,因为如果用API强制终止线程的话,最终会导致TThread线程对象不能被正确释放,在对象析构时出现Access Violation。这种情况你只能用APIRTL函数来创建线程。


如果线程处于启动挂起状态,则将线程转入运行状态,然后调用WaitFor进行等待,其功能就是等待到线程结束后才继续向下执行。关于WaitFor的实现,将放到后面说明。


线程结束后,关闭线程Handle(正常线程创建的情况下Handle都是存在的),释放操作系统创建的线程对象。


然后调用TObject.Destroy释放本对象,并释放已经捕获的异常对象,最后调用RemoveThread减小进程的线程数。


其它关于Suspend/Resume及线程优先级设置等方面,不是本文的重点,不再赘述。下面要讨论的是本文的另两个重点:SynchronizeWaitFor


但是在介绍这两个函数之前,需要先介绍另外两个线程同步技术:事件和临界区。


事件(Event)与Delphi中的事件有所不同。从本质上说,Event其实相当于一个全局的布尔变量。它有两个赋值操作:SetReset,相当于把它设置为TrueFalse。而检查它的值是通过WaitFor操作进行。对应在Windows平台上,是三个API函数:SetEventResetEventWaitForSingleObject(实现WaitFor功能的API还有几个,这是最简单的一个)。

这三个都是原语,所以Event可以实现一般布尔变量不能实现的在多线程中的应用。SetReset的功能前面已经说过了,现在来说一下WaitFor的功能:


WaitFor
的功能是检查Event的状态是否是Set状态(相当于True),如果是则立即返回,如果不是,则等待它变为Set状态,在等待期间,调用WaitFor的线程处于挂起状态。另外WaitFor有一个参数用于超时设置,如果此参数为0,则不等待,立即返回Event的状态,如果是INFINITE则无限等待,直到Set状态发生,若是一个有限的数值,则等待相应的毫秒数后返回Event的状态。


EventReset状态向Set状态转换时,唤醒其它由于WaitFor这个Event而挂起的线程,这就是它为什么叫Event的原因。所谓事件就是指状态的转换。通过Event可以在线程间传递这种状态转换信息。


当然用一个受保护(见下面的临界区介绍)的布尔变量也能实现类似的功能,只要用一个循环检查此布尔值的代码来代替WaitFor即可。从功能上说完全没有问题,但实际使用中就会发现,这样的等待会占用大量的CPU资源,降低系统性能,影响到别的线程的执行速度,所以是不经济的,有的时候甚至可能会有问题。所以不建议这样用。


(待续)


Delphi中的线程类--之(4
Delphi
中的线程类--之(4    Raptor(原作)  
  
关键字
     Thread Event CriticalSection Synchronize 
  
Delphi
中的线程类


猛禽
[Mental Studio]

http://mental.mentsu.com

之四


临界区(CriticalSection)则是一项共享数据访问保护的技术。它其实也是相当于一个全局的布尔变量。但对它的操作有所不同,它只有两个操作:EnterLeave,同样可以把它的两个状态当作TrueFalse,分别表示现在是否处于临界区中。这两个操作也是原语,所以它可以用于在多线程应用中保护共享数据,防止访问冲突。


用临界区保护共享数据的方法很简单:在每次要访问共享数据之前调用Enter设置进入临界区标志,然后再操作数据,最后调用Leave离开临界区。它的保护原理是这样的:当一个线程进入临界区后,如果此时另一个线程也要访问这个数据,则它会在调用Enter时,发现已经有线程进入临界区,然后此线程就会被挂起,等待当前在临界区的线程调用Leave离开临界区,当另一个线程完成操作,调用Leave离开后,此线程就会被唤醒,并设置临界区标志,开始操作数据,这样就防止了访问冲突。


以前面那个InterlockedIncrement为例,我们用CriticalSectionWindows API)来实现它:


Var
  InterlockedCrit : TRTLCriticalSection;

Procedure InterlockedIncrement( var aValue : Integer );
Begin
  EnterCriticalSection( InterlockedCrit );
  Inc( aValue );
  LeaveCriticalSection( InterlockedCrit );
End;

现在再来看前面那个例子:


1.         
线程A进入临界区(假设数据为3

2.         
线程B进入临界区,因为A已经在临界区中,所以B被挂起
3.         
线程A对数据加一(现在是4
4.         
线程A离开临界区,唤醒线程B(现在内存中的数据是4
5.         
线程B被唤醒,对数据加一(现在就是5了)
6.         
线程B离开临界区,现在的数据就是正确的了。

临界区就是这样保护共享数据的访问。


关于临界区的使用,有一点要注意:即数据访问时的异常情况处理。因为如果在数据操作时发生异常,将导致Leave操作没有被执行,结果将使本应被唤醒的线程未被唤醒,可能造成程序的没有响应。所以一般来说,如下面这样使用临界区才是正确的做法:


EnterCriticalSection

Try

   //  
操作临界区数据


Finally
  LeaveCriticalSection
End;

 

最后要说明的是,EventCriticalSection都是操作系统资源,使用前都需要创建,使用完后也同样需要释放。如TThread类用到的一个全局EventSyncEvent和全局CriticalSectionTheadLock,都是在InitThreadSynchronizationDoneThreadSynchronization中进行创建和释放的,而它们则是在Classes单元的InitializationFinalization中被调用的。


由于在TThread中都是用API来操作EventCriticalSection的,所以前面都是以API为例,其实Delphi已经提供了对它们的封装,在SyncObjs单元中,分别是TEvent类和TCriticalSection类。用法也与前面用API的方法相差无几。因为TEvent的构造函数参数过多,为了简单起见,Delphi还提供了一个用默认参数初始化的Event类:TSimpleEvent


顺便再介绍一下另一个用于线程同步的类:TMultiReadExclusiveWriteSynchronizer,它是在SysUtils单元中定义的。据我所知,这是Delphi RTL中定义的最长的一个类名,还好它有一个短的别名:TMREWSync。至于它的用处,我想光看名字就可以知道了,我也就不多说了。


有了前面对EventCriticalSection的准备知识,可以正式开始讨论SynchronizeWaitFor了。


我们知道,Synchronize是通过将部分代码放到主线程中执行来实现线程同步的,因为在一个进程中,只有一个主线程。先来看看Synchronize的实现:


procedure TThread.Synchronize(Method: TThreadMethod);
begin
  FSynchronize.FThread := Self;
  FSynchronize.FSynchronizeException := nil;
  FSynchronize.FMethod := Method;
  Synchronize(@FSynchronize);
end;

其中FSynchronize是一个记录类型:


  PSynchronizeRecord = ^TSynchronizeRecord;

  TSynchronizeRecord = record
    FThread: TObject;
    FMethod: TThreadMethod;
    FSynchronizeException: TObject;
  end;

用于进行线程和主线程之间进行数据交换,包括传入线程类对象,同步方法及发生的异常。


Synchronize中调用了它的一个重载版本,而且这个重载版本比较特别,它是一个类方法。所谓类方法,是一种特殊的类成员方法,它的调用并不需要创建类实例,而是像构造函数那样,通过类名调用。之所以会用类方法来实现它,是因为为了可以在线程对象没有创建时也能调用它。不过实际中是用它的另一个重载版本(也是类方法)和另一个类方法StaticSynchronize。下面是这个Synchronize的代码:


class procedure TThread.Synchronize(ASyncRec: PSynchronizeRecord);
var
  SyncProc: TSyncProc;
begin

  if GetCurrentThreadID = MainThreadID then
    ASyncRec.FMethod
  else
  begin
    SyncProc.Signal := CreateEvent(nil, True, False, nil);
    try
      EnterCriticalSection(ThreadLock);
      try
        if SyncList = nil then
          SyncList := TList.Create;
        SyncProc.SyncRec := ASyncRec;
        SyncList.Add(@SyncProc);
        SignalSyncEvent;
        if Assigned(WakeMainThread) then
          WakeMainThread(SyncProc.SyncRec.FThread);
        LeaveCriticalSection(ThreadLock);
        try
          WaitForSingleObject(SyncProc.Signal, INFINITE);
        finally
          EnterCriticalSection(ThreadLock);
        end;
      finally
        LeaveCriticalSection(ThreadLock);
      end;
    finally
      CloseHandle(SyncProc.Signal);
    end;
    if Assigned(ASyncRec.FSynchronizeException) then raise ASyncRec.FSynchronizeException;
  end;
end;


这段代码略多一些,不过也不算太复杂。

首先是判断当前线程是否是主线程,如果是,则简单地执行同步方法后返回。


如果不是主线程,则准备开始同步过程。


通过局部变量SyncProc记录线程交换数据(参数)和一个Event Handle,其记录结构如下:


  TSyncProc = record
    SyncRec: PSynchronizeRecord;
    Signal: THandle;
  end;

然后创建一个Event,接着进入临界区(通过全局变量ThreadLock进行,因为同时只能有一个线程进入Synchronize状态,所以可以用全局变量记录),然后就是把这个记录数据存入SyncList这个列表中(如果这个列表不存在的话,则创建它)。可见ThreadLock这个临界区就是为了保护对SyncList的访问,这一点在后面介绍CheckSynchronize时会再次看到。


再接下就是调用SignalSyncEvent,其代码在前面介绍TThread的构造函数时已经介绍过了,它的功能就是简单地将SyncEvent作一个Set的操作。关于这个SyncEvent的用途,将在后面介绍WaitFor时再详述。


接下来就是最主要的部分了:调用WakeMainThread事件进行同步操作。WakeMainThread是一个TNotifyEvent类型的全局事件。这里之所以要用事件进行处理,是因为Synchronize方法本质上是通过消息,将需要同步的过程放到主线程中执行,如果在一些没有消息循环的应用中(如ConsoleDLL)是无法使用的,所以要使用这个事件进行处理。


而响应这个事件的是Application对象,下面两个方法分别用于设置和清空WakeMainThread事件的响应(来自Forms单元):


procedure TApplication.HookSynchronizeWakeup;
begin
  Classes.WakeMainThread := WakeMainThread;
end;

procedure TApplication.UnhookSynchronizeWakeup;
begin
  Classes.WakeMainThread := nil;
end;

上面两个方法分别是在TApplication类的构造函数和析构函数中被调用。


这就是在Application对象中WakeMainThread事件响应的代码,消息就是在这里被发出的,它利用了一个空消息来实现:


procedure TApplication.WakeMainThread(Sender: TObject);
begin
  PostMessage(Handle, WM_NULL, 0, 0);
end;

而这个消息的响应也是在Application对象中,见下面的代码(删除无关的部分):


procedure TApplication.WndProc(var Message: TMessage);



begin
  try


    with Message do
      case Msg of

        WM_NULL:
          CheckSynchronize;

  except
    HandleException(Self);
  end;
end;

其中的CheckSynchronize也是定义在Classes单元中的,由于它比较复杂,暂时不详细说明,只要知道它是具体处理Synchronize功能的部分就好,现在继续分析Synchronize的代码。


在执行完WakeMainThread事件后,就退出临界区,然后调用WaitForSingleObject开始等待在进入临界区前创建的那个Event。这个Event的功能是等待这个同步方法的执行结束,关于这点,在后面分析CheckSynchronize时会再说明。


注意在WaitForSingleObject之后又重新进入临界区,但没有做任何事就退出了,似乎没有意义,但这是必须的!


因为临界区的EnterLeave必须严格的一一对应。那么是否可以改成这样呢:


        if Assigned(WakeMainThread) then
          WakeMainThread(SyncProc.SyncRec.FThread);
        WaitForSingleObject(SyncProc.Signal, INFINITE);
      finally
        LeaveCriticalSection(ThreadLock);
      end;

上面的代码和原来的代码最大的区别在于把WaitForSingleObject也纳入临界区的限制中了。看上去没什么影响,还使代码大大简化了,但真的可以吗?


事实上是不行!


因为我们知道,在Enter临界区后,如果别的线程要再进入,则会被挂起。而WaitFor方法则会挂起当前线程,直到等待别的线程SetEvent后才会被唤醒。如果改成上面那样的代码的话,如果那个SetEvent的线程也需要进入临界区的话,死锁(Deadlock)就发生了(关于死锁的理论,请自行参考操作系统原理方面的资料)。


死锁是线程同步中最需要注意的方面之一!


最后释放开始时创建的Event,如果被同步的方法返回异常的话,还会在这里再次抛出异常。


(待续)


Delphi中的线程类--之(5,大结局)
Delphi
中的线程类--之(5,大结局)    Raptor(原作)  
  
关键字
     Thread Event CriticalSection Synchronize 
  


Delphi
中的线程类


猛禽
[Mental Studio]

http://mental.mentsu.com

之五(大结局)


回到前面CheckSynchronize,见下面的代码:


function CheckSynchronize(Timeout: Integer = 0): Boolean;
var
  SyncProc: PSyncProc;
  LocalSyncList: TList;
begin
  if GetCurrentThreadID <> MainThreadID then
    raise EThread.CreateResFmt(@SCheckSynchronizeError, [GetCurrentThreadID]);
  if Timeout > 0 then
    WaitForSyncEvent(Timeout)
  else
    ResetSyncEvent;
  LocalSyncList := nil;
  EnterCriticalSection(ThreadLock);
  try
    Integer(LocalSyncList) := InterlockedExchange(Integer(SyncList), Integer(LocalSyncList));
    try
      Result := (LocalSyncList <> nil) and (LocalSyncList.Count > 0);
      if Result then
      begin
        while LocalSyncList.Count > 0 do
        begin
          SyncProc := LocalSyncList[0];
          LocalSyncList.Delete(0);
          LeaveCriticalSection(ThreadLock);
          try
            try
              SyncProc.SyncRec.FMethod;
            except
              SyncProc.SyncRec.FSynchronizeException := AcquireExceptionObject;
            end;
          finally
            EnterCriticalSection(ThreadLock);
          end;
          SetEvent(SyncProc.signal);
        end;
      end;
    finally
      LocalSyncList.Free;
    end;
  finally
    LeaveCriticalSection(ThreadLock);
  end;
end;

首先,这个方法必须在主线程中被调用(如前面通过消息传递到主线程),否则就抛出异常。


接下来调用ResetSyncEvent(它与前面SetSyncEvent对应的,之所以不考虑WaitForSyncEvent的情况,是因为只有在Linux版下才会调用带参数的CheckSynchronizeWindows版下都是调用默认参数0CheckSynchronize)。


现在可以看出SyncList的用途了:它是用于记录所有未被执行的同步方法的。因为主线程只有一个,而子线程可能有很多个,当多个子线程同时调用同步方法时,主线程可能一时无法处理,所以需要一个列表来记录它们。


在这里用一个局部变量LocalSyncList来交换SyncList,这里用的也是一个原语:InterlockedExchange。同样,这里也是用临界区将对SyncList的访问保护起来。


只要LocalSyncList不为空,则通过一个循环来依次处理累积的所有同步方法调用。最后把处理完的LocalSyncList释放掉,退出临界区。


再来看对同步方法的处理:首先是从列表中移出(取出并从列表中删除)第一个同步方法调用数据。然后退出临界区(原因当然也是为了防止死锁)。


接着就是真正的调用同步方法了。


如果同步方法中出现异常,将被捕获后存入同步方法数据记录中。


重新进入临界区后,调用SetEvent通知调用线程,同步方法执行完成了(详见前面Synchronize中的WaitForSingleObject调用)。


至此,整个Synchronize的实现介绍完成。


最后来说一下WaitFor,它的功能就是等待线程执行结束。其代码如下:


function TThread.WaitFor: LongWord;
var
  H: array[0..1] of THandle;
  WaitResult: Cardinal;
  Msg: TMsg;
begin
  H[0] := FHandle;
  if GetCurrentThreadID = MainThreadID then
  begin
    WaitResult := 0;
    H[1] := SyncEvent;
    repeat
      { This prevents a potential deadlock if the background thread
        does a SendMessage to the foreground thread }
      if WaitResult = WAIT_OBJECT_0 + 2 then
        PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
      WaitResult := MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);
      CheckThreadError(WaitResult <> WAIT_FAILED);
      if WaitResult = WAIT_OBJECT_0 + 1 then
        CheckSynchronize;
    until WaitResult = WAIT_OBJECT_0;
  end else WaitForSingleObject(H[0], INFINITE);
  CheckThreadError(GetExitCodeThread(H[0], Result));
end;

如果不是在主线程中执行WaitFor的话,很简单,只要调用WaitForSingleObject等待此线程的HandleSignaled状态即可。


如果是在主线程中执行WaitFor则比较麻烦。首先要在Handle数组中增加一个SyncEvent,然后循环等待,直到线程结束(即MsgWaitForMultipleObjects返回WAIT_OBJECT_0,详见MSDN中关于此API的说明)。


在循环等待中作如下处理:如果有消息发生,则通过PeekMessage取出此消息(但并不把它从消息循环中移除),然后调用MsgWaitForMultipleObjects来等待线程HandleSyncEvent出现Signaled状态,同时监听消息(QS_SENDMESSAGE参数,详见MSDN中关于此API的说明)。可以把此API当作一个可以同时等待多个HandleWaitForSingleObject。如果是SyncEventSetEvent(返回WAIT_OBJECT_0 + 1),则调用CheckSynchronize处理同步方法。


为什么在主线程中调用WaitFor必须用MsgWaitForMultipleObjects,而不能用WaitForSingleObject等待线程结束呢?因为防止死锁。由于在线程函数Execute中可能调用Synchronize处理同步方法,而同步方法是在主线程中执行的,如果用WaitForSingleObject等待的话,则主线程在这里被挂起,同步方法无法执行,导致线程也被挂起,于是发生死锁。


而改用WaitForMultipleObjects则没有这个问题。首先,它的第三个参数为False,表示只要线程HandleSyncEvent中只要有一个Signaled即可使主线程被唤醒,至于加上QS_SENDMESSAGE是因为Synchronize是通过消息传到主线程来的,所以还要防止消息被阻塞。这样,当线程中调用Synchronize时,主线程就会被唤醒并处理同步调用,在调用完成后继续进入挂起等待状态,直到线程结束。


至此,对线程类TThread的分析可以告一个段落了,对前面的分析作一个总结:


1
  线程类的线程必须按正常的方式结束,即Execute执行结束,所以在其中的代码中必须在适当的地方加入足够多的对Terminated标志的判断,并及时退出。如果必须要立即退出,则不能使用线程类,而要改用APIRTL函数。

2
  对可视VCL的访问要放在Synchronize中,通过消息传递到主线程中,由主线程处理。
3
  线程共享数据的访问应该用临界区进行保护(当然用Synchronize也行)。
4
  线程通信可以采用Event进行(当然也可以用Suspend/Resume)。
5
  当在多线程应用中使用多种线程同步方式时,一定要小心防止出现死锁。
6
  等待线程结束要用WaitFor方法。
 

系统分类: 软件开发   |    用户分类: 无分类    |    来源: 转贴

评论(2) | 阅读(925)
发表于:2007/3/10 11:21:12
标签:碰到不能删除DLL文件时的解决办法  

0

碰到不能删除DLL文件时的解决办法

碰到不能删除DLL文件时的解决办法

有时候老听网友说某某文件删不掉啊。。之类的。而且有很多都是dll文件。虽然解决这个问题的方法有很多种。而且也可以把他删除,但是网友们有没有想过是为什么删不掉呢??这是因为你运行的某个程序正在调用这个dll文件。正在使用的文件是当然不可能给你删除的。那么,到底是哪个程序在调用这个dll文件呢。我教大家一个方法可以把那个程序很容易的找出来。。 

在运行里输入cmd进入命令提示符。 
然后输入命令tasklist /m>c:\123.txt 
回车。。是不是没有任何反应?? 
不要急。到C盘下面去找一找,是不是有了一个123.txt?(当然。你可以自己设定文件的输出路径,名字,甚至后缀。但要是文本文件哦。。) 
打开他。里面就是目前运行的各个程序正在调用的dll文件。 
把不能删除的dll文件的名字记下来。然后到记事本里去编辑-查找。输入对应的dll文件。是不是找出来了?? 
找出来了后问题就好办多了。打开任务管理器。把对应的那个程序给关了。。就可以顺利删除了。。那就不必进安全模式,进DOS那么麻烦了。。。 
    当然。有些应用程序是以服务形式运行的。那么你就有可能查到的是svhost.exe但是。里面有很多个哦。。这个也好办。仍然打开命令提示符。输入tasklist /svc,当然,你也可以把他输出为文本文件,如tasklist /svc>C:\234.txt。看到了吗?每个svchost.exe后面是不是对应有一个ID呢?有了ID一对照也可以知道是哪个服务了。。如果是可关的。就关了他。。不过记住。。系统进程可别乱关哦。 

系统分类: 资源共享   |    用户分类: 无分类    |    来源: 转贴

评论(0) | 阅读(1540)
发表于:2007/1/25 14:34:10
标签:joyjs  本名  蔡欣荣  

5

晶体管电路中开源、开漏、开集、开发电路总结和应用

晶体管电路中开源、开漏、开集、开发电路总结和应用

蔡欣荣

(西安交通大学计算机系统结构研究所,西安 710049)

摘要:文章针对在常见晶体管电路设计中,所遇到的开源、开漏、开集、开发电路这些概念名词理解不清而导致电路搭建错误的问题,进行概念总结并说明各自电路外接上拉电阻和下拉电阻的原理,给出具体实际的电路加以说明。并在工程实际应用中,提出了对于MOS管和双极性晶体管电路在上拉电阻和下拉电阻方面应该注意的规则。

关键词: 双极性晶体管 MOS 开漏 开集 上拉电阻 下拉电阻

 

对于常见在晶体管电路分析和设计中,经常会遇到开源(Open Source)、开漏(Open Drain)、开集(Open Collector)、开发(Open Emitter)这些概念名词,有时候对这些概念名词的理解不到位,从而导致电路设计不满足要求,引发电路不能正常工作、电磁干扰噪音很大等问题。

开漏电路中的“漏”指的是场效应管的漏极,那么开源电路中的“源”指的是场效应管的源极。同理,开集电路和开发电路中的“集”和“发”分别指的是三极管的集电极和发射极。

文章对这四种电路分别加以说明,对于开漏、开集电路需要上拉电阻和上拉电压,他们的大小要满足电路的应用需求和限定在晶体管参数以内,对于开源、开发电路也有相应的要求。本文对这些要求都做了详实的说明,以实际的电路加以分析。

1 开源、开漏、开集、开发电路要求

开漏电路和开源电路一般是以绝缘栅型场效应管的漏极、源极为输出的电路。开漏电路一般用法是在漏极外部的电路添加上拉电阻,而开源电路一般在外部电路添加一个下拉电阻。完整的开漏电路应该由开漏器件和开漏上拉电阻组成。同理,完整的开源电路应该由开源器件和开源下拉电阻组成。如图1和2是NMOS管的上拉电路和下拉电路。

  

1  NMOS管外接上拉电阻                          图2 NMOS管外接下拉电阻

开集电路和开发电路一般是以三极管的集电极、发射极为输出的电路。开集电路一般用法是在集电极外部的电路添加上拉电阻,而开发电路一般在外部电路添加一个下拉电阻。完整的开集电路应该由三极管工艺器件和开集上拉电阻组成,同理,完整的开发电路应该由三极管工艺器件和开发下拉电阻组成。如图3和图4。

  

3  NPN三极管外接上拉电阻                          图4 NPN三极管外接下拉电阻

通过上面的图示,只有加上外部拉电阻和电压,才能使输出有确定的值。上拉电阻又称为输入电阻,主要是为了要求为低电平脉冲触发而用的,如一般的复位电路。下拉电阻又称为输出电阻,主要是为了一般下级输入为高电平触发的电路,如中断请求、同步电路。

2 开漏、开源、开集、开发四种电路特点

⑴开源、开漏电路都要外接电阻。利用外接电阻可以驱动下级输入的能力,增强电路的驱动能力。只要给芯片适当的工作电压,外部的输出电平在一定范围内可以由外部上拉电压来决定。内部只需要很小的栅极驱动电流,就可以驱动MOS管的导通,实现小电平控制高电平输出。当内部MOS管导通时,电流是从外部的VCC流入上拉电阻经MOS管到地或者先经过MOS管然后灌入下拉电阻到地。

⑵开源电路必须外接下拉电阻,开漏电路必须外接上拉电阻。否则不能产生所需要的电平,不接电阻一般为高阻态,且会产生干扰。当开漏电路不接上拉电阻时只能输出低电平。

⑶上拉电阻或下拉电阻决定了转换的速度,电阻越大,转换速度越慢,因为漏源之间的电容和上拉电阻或下拉电阻形成RC的充放电时间,但是相应的功耗就会降低。其功耗功率为 。所以电阻越大,功耗越低。

⑷开集电路和开漏电路原理类似,在一般工程应用中我们可以用开集电路来替代开漏电路。

3 开漏、开源、开集、开发四种电路应用

3.1 搭建“或非”逻辑和“或”电路逻辑

多个开漏输出可以并联到一条输出线上,从而产生“或非逻辑”的功能,如图5所示。当只要其中的一个线路输入为高电平,整个输出就为低电平。这在PC机总线使用权判断上很有作用,当有一个逻辑在使用总线,其输出就为低电平。据此想法,我们将多个开源输出并联到一条输出线上,从而产生“或逻辑”的功能,如图6所示。当其中的一个线路的输出为高电平,整个输出就为高电平。这也可以用在某种判断逻辑上。

  

5 MOS管多个输入的“或非”输出                    图6 MOS管多个输入的“或”输出

同理,可以通过晶体管来形成开集“或非”电路(如图7所示)或者开发“”电路。开集、开发电路用的是晶体三极管,开关速度很快,满足一些特殊的高速应用要求。但是三极管电路功耗比MOS管要大,且其驱动电路能力没有MOS强。

   

  7 TTL管多个输入的“或非”输出          8 CMOS电路实现电平转换

3.2 CMOS电路实现电平转换

如图8所示,对于输入3.3 电压,通过CMOS反相器,那么Q1 N-MOS将会导通,Q2关闭,Q1输出的是低电平,Q3不会导通的,所以输出为5 高电平。如果输入的是0 电压,那么输入负载P-MOS管将导通,输出高电压驱动Q3,从而最后Q3上拉输出为低电平。但是上拉电阻的选择能够使得当输出为低电平时,低电平应该低于下级输入电路要求的低电平门槛值。

3.3 CMOS工艺看门狗芯片外部上拉和下拉电阻

CAT24C021[1]CATALYST公司带有2K字节E2PROM看门狗芯片,该芯片采用的是CMOS工艺,其RESET引脚是开源输出,/RESET引脚是开漏输出。RESET引脚是高电平触发,由于开源输出应接下拉电阻,/RESET引脚是低电平触发,是开漏输出引脚应接上拉电阻。如图9所示电路接法。

  

9 CAT24C021外部接线部分图                 图10  TTL管驱动MOS管进行大电压输出

3.4 TTL管驱动MOS管进行大电压输出

如图10所示,该图通常是小电流驱动大电压输出,满足一些特殊的应用场合。TTL管采用2N3904[2]MOS管采用IRF840[3]2N3904最大基极输入电流为100 ,集电极到发射极之间最大电压为40 集电极和发射极之间的最大电流为 。IRF840栅源之间最大正向电压可以加到20 ,同时栅极电阻很高,在正向20 以内其最大栅极电流为100 ,所以功耗很低,漏源最大正向导通电流 。下面我们来分析一下上面的电路。TTL管采用集电极上拉电阻,MOS管采用开漏上拉电阻。我们实现是TMS320F2812DSP串口MDXA输出[4],其输出高电平是3.3 ,低电平一般很小,认为其为0 。输入为方波脉冲。

DSP输入脉冲为低电平时,假设为0 电平,则TTL管截止,TTL管上拉输出为12 ,驱动MOS管,MOS管导通, ,由于栅极输入电流很小,基本上12 经R1上拉电阻和栅极电阻进入地,电流很小。一般情况下,我们只需要考虑栅极输入电压就可以了。12 可以使MOS管导通,MOS管上拉输出为0

DSP输入脉冲为高电平时,TTL管导通,其基极和发射极压降为0.7 ,认为Vb=0.7 ,则  ,此时流过R1电流为 ,设 为流入栅极的电流, ,由于栅极电阻很高,认为流入栅极的电流很小忽略不计,所以 ,由管子的放大倍数设 ,那么 ,因 ,所以这时晶体管饱和,晶体管上拉输出约为0 ,MOS管截止,其上拉输出为50 。对于TTL管 ,所以管子不会击穿。

4 开漏、开源、开集、开发四种电路拉电阻的要求

⑴对于选择晶体管外接上拉电阻时,注意管子的导通电流,上拉电阻不能太小,太小那么集电极发射极之间或者源极、漏极电流就会变大,导致管子过热击穿。选择时注意晶体管的各项参数。

⑵ 驱动能力和功耗之间的平衡选择。 无论对于MOS管电路,还是TTL电路,如果开漏或者开集输出作为下一级的输入,上拉电阻或下拉电阻一定要保证为下一级提供足够的驱动电流,但是电流值也不能太大,应该满足一定的范围,电流越小功耗越低,但同时也要注意驱动能力和功耗的恰当选择。

⑶在频率特性方面,如开漏电路上拉电阻为例,上拉电阻和管漏源级之间的电容和下级电路之间的输入电容会形成 延迟,电阻越大,那么延迟也就越大。上拉电阻的设定应考虑电路在这方面的需求。下拉电阻也应该有类似的考虑。

⑷有些引脚必须接拉电阻,一是为了提高驱动能力,提供适合的电平,提供电流的泄荷通道; 二是加拉电阻可以提高信号输入的噪声容限,以提高抗电磁干扰能力。

 

参考文献:

[1] CAT24C021. Data Sheet. Catalyst Semiconductor, 1998

[2] 2N3904. Data Sheet.  Philips  Semiconductor, 1999 April
[3] IRF840. Data Sheet. International Rectifier
[4] TMS320F2812. Data Sheet.
Texas Instruments, 2001 April
 

 


 

Summary And Application Of OSODOCOE In Transistor Circuit

 

AbstractThe article solves the problems which always appear in traditional transistor circuit ,these problems always arise from being no completely comprehend the open-sourceopen-drainopen-collectoropen-emitter terms in transistor circuit. In this article we summarize and state why some circuit should be placed a pull-up or pull-down resistance, also we give some examples to explain this. Combining with engineering actually application, we should pay attention to some design rules in MOS and BJT circuit.

Key words BJT   MOS   Open-drain   Open-collector   Pull-up resistance   Pull-down resistance

 

稿号:200610217132

 

 



[1] 作者简介:蔡欣荣,男(1982~)西安交通大学计算机系,从事计算机系统结构和网络多媒体应用。

系统分类: 模拟技术   |    用户分类: 无分类    |    来源: 原创

评论(5) | 阅读(2810)
发表于:2007/1/25 14:31:43
标签:数据库  

1

关于enterpriselibraryconfigration

在enterprise library configration 2.0中,在应用程序中的App.config 建立好之后,用enterprise library configration 2.0打开刚才的App.config,修改简要信息,如果是SQL数据库,只需要修改System.data.sqlclient,然后保存.

在.net2005中自动修改 后,在connectionString="Data Source="orcl";Persist Security Info="False";User ID="system";Password=oracle;Unicode=True;修改一下,就可以了。

 

此方法比较简便。

 

系统分类: 软件开发   |    用户分类: 无分类    |    来源: 原创

评论(0) | 阅读(662)
发表于:2007/1/25 14:24:58
标签:数据库  

1

关于PersistsecurityInfo?

在.NET2005开发数据库时经常出现的关键字

Persist security Info="TRUE"  OR FALSE?

 

persist security info property specifies whether the data source can persist senstive authentication such as password!

如果将该关键字设置为 true 或 yes,将允许在打开连接后,从连接中获得涉及安全性的信息(包括用户标识和密码)。如果在建立连接时必须提供用户标识和密码,最安全的方法是在使用信息打开连接后丢弃这些信息,在 Persist Security Info 设置为 false 或 no 时会发生这种情况。当您向不可信的源提供打开的连接,或将连接信息永久保存到磁盘时,这点尤其重要。如果将 Persist Security Info 保持为 false,可帮助确保不可信的源无法访问连接中涉及安全性的信息,并帮助确保任何涉及安全性的信息都不会随连接字符串信息在磁盘上持久化。

系统分类: 软件开发   |    用户分类: 无分类    |    来源: 原创

评论(0) | 阅读(713)
发表于:2007/1/12 21:54:55
标签:IIS安装程序无法复制文件的问题  

2

IIS安装程序无法复制文件的问题

今天在安装IIS时,总是提示无法复制staxmem.dll,导致无法安装IIS,但在C:\WINDOWS\ServicePackFiles\i386下,有这个文件,不可理解。不过,经过多次尝试终于找到解决办法,步骤是:

(1) 开始 > 运行  > 输入 CMD >再输入以下命令:

esentutl /p %windir%/security/database/secedit.sdb

(2) 接着屏幕上会输出如下信息:

Microsoft(R) Windows(R) Database Utilities

Version 5.2

Copyright (C) Microsoft Corporation. All Rights Reserved.



Initiating INTEGRITY mode...

Database: L:\WINDOWS\security\database\secedit.sdb

Temp. Database: TEMPINTEG2680.EDB



Checking database integrity.



Scanning Status (% complete)

0 10 20 30 40 50 60 70 80 90 100

|---|---|---|---|---|---|---|---|---|---|

...................................................

Integrity check successful.

Operation completed successfully in 0.841 seconds.
(3)插入安装盘再试一下,问题解决了。


系统分类: 软件开发   |    用户分类: 无分类    |    来源: 转贴

评论(2) | 阅读(1559)
23Next >Total , Page /