【CVPR 2023】BiFormer: 具有双层路由注意力的ViT

★★★ 本文源自AlStudio社区精品项目,【点击此处】查看更多精品内容 >>>

摘要

        作为视觉Transformer的核心组成部分,注意力是捕捉远程依赖性的有力工具。 然而,这种能力是有代价的:当计算跨所有空间位置的成对Token交互时,它会带来巨大的计算负担和沉重的内存占用。 一系列的工作试图通过在注意力中引入手工的和内容不可知的稀疏性来缓解这一问题,例如将注意力操作限制在局部窗口、轴向条纹或扩张窗口内。 相对于这些方法,我们提出了一种新的动态稀疏注意力通过双层路由,使计算更灵活地分配且内容感知。 具体来说,对于查询,首先在粗区域级别过滤掉不相关的键值对,然后在剩余候选区域(即路由区域)的并集中应用细粒度的Token到Token关注。 我们提供了一个简单而有效的双层路由注意力的实现,它利用稀疏性来节省计算和内存,同时只涉及GPU友好的稠密矩阵乘法。 在此基础上,提出了一种新的通用视觉Transformer——Biformer。 由于Biformer以查询自适应的方式关注少量相关令牌,而不会分散注意力到其他无关令牌,因此它具有良好的性能和较高的计算效率,特别是在密集预测任务中。 在图像分类、目标检测和语义分割等多个计算机视觉任务中的实验结果验证了我们设计的有效性。

1. BiFormer

        众所周知,Transformer相比于CNNs的一大核心优势便是借助自注意力机制的优势捕捉长距离上下文依赖。正所谓物极必反,如图1(a)所示,在原始的 Transformer 架构设计中,这种结构虽然在一定程度上带来了性能上的提升,但却会引起两个老生常态的问题:

  1. 内存占用大
  2. 计算代价高

        因此,有许多研究也在致力于做一些这方面的优化工作,包括但不仅限于将注意力操作限制在:

  1. inside local windows, e.g., Swin transformer and Crossformer(如图1(b))
  2. axial stripes, e.g., Cswin transformer(如图1(c))
  3. dilated windows, e.g., Maxvit and Crossformer(如图1(d))
  4. Deformable windows, e.g., DAT(如图1(e))

        总的来说,作者认为以上这些方法大都是通过将手工制作和与内容无关的稀疏性引入到注意力机制来试图缓解这个问题。因此,本文通过双层路由(Bi-level Routing)提出了一种新颖的动态稀疏注意力(Dynamic Sparse Attention ),以实现更灵活的计算分配和内容感知,使其具备动态的查询感知稀疏性,如图1(f)所示。

1.1 Bi-Level Routing Attention (BRA)

        本文探索了一种动态的、查询感知的稀疏注意力机制,其关键思想是在粗糙区域级别过滤掉大部分不相关的键值对,以便只保留一小部分路由区域(。其次,作者在这些路由区域的联合中应用细粒度的token-to-token注意力。BRA主要包括如下三个部分:

  1. Region partition and input projection。首先将特征图划分为 S × S S \times S S×S 个不重叠区域并进行线性映射:

Q = X r W q , K = X r W k , V = X r W v \mathbf{Q}=\mathbf{X}^{r} \mathbf{W}^{q}, \quad \mathbf{K}=\mathbf{X}^{r} \mathbf{W}^{k}, \quad \mathbf{V}=\mathbf{X}^{r} \mathbf{W}^{v} Q=XrWq,K=XrWk,V=XrWv

  1. Region-to-region routing with directed graph。然后在粗粒度Token上计算注意力权重,然后仅取其中的Topk区域作为相关区域参与细粒度的运算。

A r = Q r ( K r ) T I r =  topkIndex  ( A r ) \begin{array}{c} \mathbf{A}^{r} = \mathbf{Q}^{r}\left(\mathbf{K}^{r}\right)^{T}\\ \mathbf{I}^{r} = \text { topkIndex }\left(\mathbf{A}^{r}\right) \end{array} Ar=Qr(Kr)TIr= topkIndex (Ar)

  1. Token-to-token attention。最后将每个Token最相关的Topk粗粒度区域作为键和值参与最终的运算。为了增强局部性,在值上还是用了一个深度卷积。

K g = gather ⁡ ( K , I r ) , V g = gather ⁡ ( V , I r ) O =  Attention  ( Q , K g , V g ) + L C E ( V ) \begin{array}{c} \mathbf{K}^{g}=\operatorname{gather}\left(\mathbf{K}, \mathbf{I}^{r}\right), \quad \mathbf{V}^{g}=\operatorname{gather}\left(\mathbf{V}, \mathbf{I}^{r}\right)\\ \mathbf{O}=\text { Attention }\left(\mathbf{Q}, \mathbf{K}^{g}, \mathbf{V}^{g}\right)+\mathrm{LCE}(\mathbf{V}) \end{array} Kg=gather(K,Ir),Vg=gather(V,Ir)O= Attention (Q,Kg,Vg)+LCE(V)

1.2 BiFormer

        基于BRA模块,本文构建了一种新颖的通用视觉Transformer——BiFormer。如上图所示,其遵循大多数的vision transformer架构设计,也是采用四级金字塔结构,即下采样32倍。具体来说,BiFormer在第一阶段使用重叠块嵌入,在第二到第四阶段使用Merge模块来降低输入空间分辨率,同时增加通道数,然后是采用连续的BiFormer块做特征变换。需要注意的是,在每个块的开始均是使用 3 × 3 3 \times 3 3×3 的深度卷积来隐式编码相对位置信息。随后依次应用BRA模块和扩展率为e的2层多层感知机(Multi-Layer Perceptron, MLP)模块,分别用于交叉位置关系建模和每个位置嵌入。

2. 代码复现

2.1 下载并导入所需的库

!pip install einops-0.3.0-py3-none-any.whl
!pip install paddlex
%matplotlib inline
import paddle
import paddle.fluid as fluid
import numpy as np
import matplotlib.pyplot as plt
from paddle.vision.datasets import Cifar10
from paddle.vision.transforms import Transpose
from paddle.io import Dataset, DataLoader
from paddle import nn
import paddle.nn.functional as F
import paddle.vision.transforms as transforms
import os
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
import paddlex
import itertools
from einops import rearrange

2.2 创建数据集

train_tfm = transforms.Compose([
    transforms.RandomResizedCrop(224, scale=(0.6, 1.0)),
    transforms.ColorJitter(brightness=0.2,contrast=0.2, saturation=0.2),
    transforms.RandomHorizontalFlip(0.5),
    transforms.RandomRotation(20),
    paddlex.transforms.MixupImage(),
    transforms.ToTensor(),
    transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
])

test_tfm = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
])
paddle.vision.set_image_backend('cv2')
# 使用Cifar10数据集
train_dataset = Cifar10(data_file='data/data152754/cifar-10-python.tar.gz', mode='train', transform = train_tfm)
val_dataset = Cifar10(data_file='data/data152754/cifar-10-python.tar.gz', mode='test',transform = test_tfm)
print("train_dataset: %d" % len(train_dataset))
print("val_dataset: %d" % len(val_dataset))
train_dataset: 50000
val_dataset: 10000
batch_size=32
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, drop_last=True, num_workers=4)
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, drop_last=False, num_workers=4)

2.3 模型的创建

2.3.1 标签平滑
class LabelSmoothingCrossEntropy(nn.Layer):
    def __init__(self, smoothing=0.1):
        super().__init__()
        self.smoothing = smoothing

    def forward(self, pred, target):

        confidence = 1. - self.smoothing
        log_probs = F.log_softmax(pred, axis=-1)
        idx = paddle.stack([paddle.arange(log_probs.shape[0]), target], axis=1)
        nll_loss = paddle.gather_nd(-log_probs, index=idx)
        smooth_loss = paddle.mean(-log_probs, axis=-1)
        loss = confidence * nll_loss + self.smoothing * smooth_loss

        return loss.mean()
2.3.2 DropPath
def drop_path(x, drop_prob=0.0, training=False):
    """
    Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
    the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper...
    See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ...
    """
    if drop_prob == 0.0 or not training:
        return x
    keep_prob = paddle.to_tensor(1 - drop_prob)
    shape = (paddle.shape(x)[0],) + (1,) * (x.ndim - 1)
    random_tensor = keep_prob + paddle.rand(shape, dtype=x.dtype)
    random_tensor = paddle.floor(random_tensor)  # binarize
    output = x.divide(keep_prob) * random_tensor
    return output


class DropPath(nn.Layer):
    def __init__(self, drop_prob=None):
        super(DropPath, self).__init__()
        self.drop_prob = drop_prob

    def forward(self, x):
        return drop_path(x, self.drop_prob, self.training)
2.3.3 BiFormer模型的创建
class TopkRouting(nn.Layer):
    """
    differentiable topk routing with scaling
    Args:
        qk_dim: int, feature dimension of query and key
        topk: int, the 'topk'
        qk_scale: int or None, temperature (multiply) of softmax activation
        with_param: bool, wether inorporate learnable params in routing unit
    """
    def __init__(self, qk_dim, topk=4, qk_scale=None):
        super().__init__()
        self.topk = topk
        self.qk_dim = qk_dim
        self.scale = qk_scale or qk_dim ** -0.5

    def forward(self, query, key):
        """
        Args:
            q, k: (n, p^2, c) tensor
        Return:
            topk_index: (n, p^2, topk) tensor
        """
        query_hat, key_hat = query.detach(), key.detach()
        attn_logit = (query_hat * self.scale) @ key_hat.transpose([0, 2, 1]) # (n, p^2, p^2)
        topk_attn_logit, topk_index = paddle.topk(attn_logit, k=self.topk, axis=-1) # (n, p^2, k), (n, p^2, k)

        return topk_index
class KVGather(nn.Layer):
    def __init__(self):
        super().__init__()

    def forward(self, r_idx, kv):
        """
        r_idx: (b, p^2, topk) tensor
        r_weight: (b, p^2, topk) tensor
        kv: (b, p^2, w^2, c_kq+c_v)

        Return:
            (n, p^2, topk, w^2, c_kq+c_v) tensor
        """
        # select kv according to routing index
        b, p2, w2, c_kv = kv.shape
        topk = r_idx.shape[-1]

        topk_kv = paddle.take_along_axis(
            kv.reshape((b, 1, p2, w2, c_kv)).expand((-1, p2, -1, -1, -1)), # (n, p^2, p^2, w^2, c_kv) without mem cpy
            r_idx.reshape((b, p2, topk, 1, 1)).expand((-1, -1, -1, w2, c_kv)), # (n, p^2, k, w^2, c_kv)
            axis=2
        )

        return topk_kv
class QKVLinear(nn.Layer):
    def __init__(self, dim, qk_dim, bias=True):
        super().__init__()
        self.dim = dim
        self.qk_dim = qk_dim
        self.qkv = nn.Linear(dim, qk_dim + qk_dim + dim, bias_attr=bias)

    def forward(self, x):
        q, kv = paddle.split(self.qkv(x), [self.qk_dim, self.qk_dim+self.dim], axis=-1)
        return q, kv
class BiLevelRoutingAttention(nn.Layer):
    def __init__(self, dim, num_heads=8, n_win=7,  qk_dim=None, qk_scale=None, topk=4, side_dwconv=3):
        super().__init__()
        self.dim = dim
        self.num_heads = num_heads
        self.n_win = n_win
        self.qk_dim = qk_dim or dim
        assert self.qk_dim % num_heads == 0 and self.dim % num_heads==0, 'qk_dim and dim must be divisible by num_heads!'
        self.qk_scale = qk_scale or self.qk_dim ** -0.5

        ################side_dwconv (i.e. LCE in ShuntedTransformer)###########
        self.lepe = nn.Conv2D(dim, dim, kernel_size=side_dwconv, stride=1, padding=side_dwconv//2, groups=dim)

        self.topk = topk

        # 寻找Topk区域
        self.router = TopkRouting(qk_dim=self.qk_dim,
                                  qk_scale=self.qk_scale,
                                  topk=self.topk)
        self.kv_gather = KVGather()

        self.qkv = QKVLinear(self.dim, self.qk_dim)
        self.wo = nn.Linear(dim, dim)

        self.attn_act = nn.Softmax(axis=-1)

    def forward(self, x):
        """
        Input:
            x: NHWC tensor

        Return:
            NHWC tensor
        """

        N, H, W, C = x.shape

        x = rearrange(x, "b (j h) (i w) c -> b (j i) h w c", j=self.n_win, i=self.n_win)

        q, kv = self.qkv(x)

        q_pix = rearrange(q, 'b p2 h w c -> b p2 (h w) c')
        kv_pix = rearrange(kv, 'b p2 h w c -> b p2 (h w) c')

        q_win, k_win = q.mean([2, 3]), kv[..., 0:self.qk_dim].mean([2, 3]) # window-wise qk, (b, p^2, c_qk), (b, p^2, c_qk)

        ################side_dwconv (i.e. LCE in ShuntedTransformer)###########
        lepe = self.lepe(rearrange(kv[..., self.qk_dim:], 'b (j i) h w c -> b c (j h) (i w)', j=self.n_win, i=self.n_win))
        lepe = rearrange(lepe, 'b c h w -> b h w c')

        r_idx = self.router(q_win, k_win)
        kv_pix_sel = self.kv_gather(r_idx=r_idx, kv=kv_pix) #(n, p^2, topk, h_kv*w_kv, c_qk+c_v)
        k_pix_sel, v_pix_sel = paddle.split(kv_pix_sel, [self.qk_dim, self.dim], axis=-1)

        # MHSA
        k_pix_sel = rearrange(k_pix_sel, 'b p2 k w2 (mh c) -> (b p2) mh c (k w2)', mh=self.num_heads)
        v_pix_sel = rearrange(v_pix_sel, 'b p2 k w2 (mh c) -> (b p2) mh (k w2) c', mh=self.num_heads)
        q_pix = rearrange(q_pix, 'b p2 w2 (mh c) -> (b p2) mh w2 c', mh=self.num_heads)

        attn = q_pix @ k_pix_sel * self.qk_scale
        attn = self.attn_act(attn)
        out = attn @ v_pix_sel

        out = rearrange(out, '(b j i) mh (h w) c -> b (j h) (i w) (mh c)',
                        j=self.n_win, i=self.n_win, h=H // self.n_win, w=W // self.n_win)

        out = out + lepe

        out = self.wo(out)

        return out
model = BiLevelRoutingAttention(64, num_heads=2)
paddle.summary(model, (1, 56, 56, 64))
W0318 21:57:45.335470   399 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 11.2
W0318 21:57:45.340256   399 gpu_resources.cc:91] device: 0, cuDNN Version: 8.2.


----------------------------------------------------------------------------------------------------
 Layer (type)          Input Shape                      Output Shape                   Param #    
====================================================================================================
   Linear-1        [[1, 49, 8, 8, 64]]               [1, 49, 8, 8, 192]                12,480     
  QKVLinear-1      [[1, 49, 8, 8, 64]]     [[1, 49, 8, 8, 64], [1, 49, 8, 8, 128]]        0       
   Conv2D-1         [[1, 64, 56, 56]]                  [1, 64, 56, 56]                   640      
 TopkRouting-1  [[1, 49, 64], [1, 49, 64]]               [1, 49, 4]                       0       
  KVGather-1                []                       [1, 49, 4, 64, 128]                  0       
   Softmax-1        [[49, 2, 64, 256]]                [49, 2, 64, 256]                    0       
   Linear-2         [[1, 56, 56, 64]]                  [1, 56, 56, 64]                  4,160     
====================================================================================================
Total params: 17,280
Trainable params: 17,280
Non-trainable params: 0
----------------------------------------------------------------------------------------------------
Input size (MB): 0.77
Forward/backward pass size (MB): 36.75
Params size (MB): 0.07
Estimated Total Size (MB): 37.58
----------------------------------------------------------------------------------------------------






{'total_params': 17280, 'trainable_params': 17280}
class AttentionLePE(nn.Layer):
    """
    vanilla attention
    """
    def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0., side_dwconv=5):
        super().__init__()
        self.num_heads = num_heads
        head_dim = dim // num_heads
        # NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights
        self.scale = qk_scale or head_dim ** -0.5

        self.qkv = nn.Linear(dim, dim * 3, bias_attr=qkv_bias)
        self.attn_drop = nn.Dropout(attn_drop)
        self.proj = nn.Linear(dim, dim)
        self.proj_drop = nn.Dropout(proj_drop)
        self.lepe = nn.Conv2D(dim, dim, kernel_size=side_dwconv, stride=1, padding=side_dwconv//2, groups=dim)
        self.softmax = nn.Softmax(axis=-1)

    def forward(self, x):
        """
        args:
            x: NHWC tensor
        return:
            NHWC tensor
        """
        _, H, W, _ = x.shape
        x = rearrange(x, 'n h w c -> n (h w) c')

        #######################################
        B, N, C = x.shape
        qkv = self.qkv(x).reshape((B, N, 3, self.num_heads, C // self.num_heads)).transpose([2, 0, 3, 1, 4])
        q, k, v = qkv[0], qkv[1], qkv[2]

        lepe = self.lepe(rearrange(x, 'n (h w) c -> n c h w', h=H, w=W))
        lepe = rearrange(lepe, 'n c h w -> n (h w) c')

        attn = (q @ k.transpose([0, 1, 3, 2])) * self.scale
        attn = self.softmax(attn)
        attn = self.attn_drop(attn)

        x = (attn @ v).transpose([0, 2, 1, 3]).reshape((B, N, C))
        x = x + lepe

        x = self.proj(x)
        x = self.proj_drop(x)
        #######################################

        x = rearrange(x, 'n (h w) c -> n h w c', h=H, w=W)
        return x
class Block(nn.Layer):
    def __init__(self, dim, drop_path=0., layer_scale_init_value=-1,
                       num_heads=8, n_win=7, qk_dim=None, qk_scale=None,
                       topk=4, mlp_ratio=3, side_dwconv=5, before_attn_dwconv=3):
        super().__init__()
        qk_dim = qk_dim or dim

        # modules
        self.pos_embed = nn.Conv2D(dim, dim,  kernel_size=before_attn_dwconv, padding=1, groups=dim)

        self.norm1 = nn.LayerNorm(dim, epsilon=1e-6) # important to avoid attention collapsing
        if topk > 0:
            self.attn = BiLevelRoutingAttention(dim=dim, num_heads=num_heads, n_win=n_win, qk_dim=qk_dim,
                                        qk_scale=qk_scale, topk=topk, side_dwconv=side_dwconv)
        else:
            self.attn = AttentionLePE(dim=dim, side_dwconv=side_dwconv)

        self.norm2 = nn.LayerNorm(dim, epsilon=1e-6)

        self.mlp = nn.Sequential(nn.Linear(dim, int(mlp_ratio * dim)),
                                 nn.GELU(),
                                 nn.Linear(int(mlp_ratio * dim), dim))

        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()

        if layer_scale_init_value > 0:
            self.use_layer_scale = True
            self.gamma1 = self.create_parameter([dim], default_initializer=nn.initializer.Constant(layer_scale_init_value))
            self.gamma2 = self.create_parameter([dim], default_initializer=nn.initializer.Constant(layer_scale_init_value))
        else:
            self.use_layer_scale = False

    def forward(self, x):
        """
        x: NCHW tensor
        """
        # conv pos embedding
        x = x + self.pos_embed(x)
        # permute to NHWC tensor for attention & mlp
        x = x.transpose([0, 2, 3, 1])

        # attention & mlp
        if self.use_layer_scale:
            x = x + self.drop_path(self.gamma1 * self.attn(self.norm1(x)))
            x = x + self.drop_path(self.gamma2 * self.mlp(self.norm2(x)))
        else:
            x = x + self.drop_path(self.attn(self.norm1(x)))
            x = x + self.drop_path(self.mlp(self.norm2(x)))

        # permute back
        x = x.transpose([0, 3, 1, 2])
        return x
class BiFormer(nn.Layer):
    def __init__(self, depth=[3, 4, 8, 3], in_chans=3, num_classes=1000, embed_dim=[64, 128, 320, 512],
                 head_dim=64, qk_scale=None,
                 drop_path_rate=0., drop_rate=0.,
                 n_win=7,
                 topks=[8, 8, -1, -1],
                 side_dwconv=5,
                 layer_scale_init_value=-1,
                 qk_dims=[None, None, None, None],
                 pe_stages=[0],
                 before_attn_dwconv=3,
                 mlp_ratios=[3, 3, 3, 3]):
        super().__init__()
        self.num_classes = num_classes
        self.num_features = self.embed_dim = embed_dim  # num_features for consistency with other models

        ############ downsample layers (patch embeddings) ######################
        self.downsample_layers = nn.LayerList()
        # NOTE: uniformer uses two 3*3 conv, while in many other transformers this is one 7*7 conv
        stem = nn.Sequential(
            nn.Conv2D(in_chans, embed_dim[0] // 2, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)),
            nn.BatchNorm2D(embed_dim[0] // 2),
            nn.GELU(),
            nn.Conv2D(embed_dim[0] // 2, embed_dim[0], kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)),
            nn.BatchNorm2D(embed_dim[0]),
        )

        self.downsample_layers.append(stem)

        for i in range(3):
            downsample_layer = nn.Sequential(
                nn.Conv2D(embed_dim[i], embed_dim[i+1], kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)),
                nn.BatchNorm2D(embed_dim[i+1])
            )
            self.downsample_layers.append(downsample_layer)
        ##########################################################################

        self.stages = nn.LayerList() # 4 feature resolution stages, each consisting of multiple residual blocks
        nheads= [dim // head_dim for dim in qk_dims]
        dp_rates=[x.item() for x in paddle.linspace(0, drop_path_rate, sum(depth))]
        cur = 0
        for i in range(4):
            stage = nn.Sequential(
                *[Block(dim=embed_dim[i], drop_path=dp_rates[cur + j],
                        layer_scale_init_value=layer_scale_init_value,
                        topk=topks[i],
                        num_heads=nheads[i],
                        n_win=n_win,
                        qk_dim=qk_dims[i],
                        qk_scale=qk_scale,
                        mlp_ratio=mlp_ratios[i],
                        side_dwconv=side_dwconv,
                        before_attn_dwconv=before_attn_dwconv) for j in range(depth[i])],
            )
            self.stages.append(stage)
            cur += depth[i]

        ##########################################################################
        self.norm = nn.BatchNorm2D(embed_dim[-1])

        # Classifier head
        self.head = nn.Linear(embed_dim[-1], num_classes) if num_classes > 0 else nn.Identity()
        self.apply(self._init_weights)

    def _init_weights(self, m):
        tn = nn.initializer.TruncatedNormal(std=.02)
        zeros = nn.initializer.Constant(0.)
        ones = nn.initializer.Constant(1.)
        if isinstance(m, nn.Linear):
            tn(m.weight)
            if isinstance(m, nn.Linear) and m.bias is not None:
                zeros(m.bias)
        elif isinstance(m, (nn.LayerNorm)):
            zeros(m.bias)
            ones(m.weight)

    def get_classifier(self):
        return self.head

    def reset_classifier(self, num_classes, global_pool=''):
        self.num_classes = num_classes
        self.head = nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity()

    def forward_features(self, x):
        for i in range(4):
            x = self.downsample_layers[i](x)
            x = self.stages[i](x)
        x = self.norm(x)
        return x

    def forward(self, x):
        x = self.forward_features(x)
        x = x.flatten(2).mean(-1)
        x = self.head(x)
        return x
2.3.4 模型的参数
num_classes = 10

def biformer_tiny():
    model = BiFormer(
        depth=[2, 2, 8, 2],
        num_classes=num_classes,
        embed_dim=[64, 128, 256, 512], mlp_ratios=[3, 3, 3, 3],
        n_win=7,
        topks=[1, 4, 16, -2],
        side_dwconv=5,
        before_attn_dwconv=3,
        layer_scale_init_value=-1,
        qk_dims=[64, 128, 256, 512],
        head_dim=32)

    return model

def biformer_small():
    model = BiFormer(
        depth=[4, 4, 18, 4],
        num_classes=num_classes,
        embed_dim=[64, 128, 256, 512], mlp_ratios=[3, 3, 3, 3],
        n_win=7,
        topks=[1, 4, 16, -2],
        side_dwconv=5,
        before_attn_dwconv=3,
        layer_scale_init_value=-1,
        qk_dims=[64, 128, 256, 512],
        head_dim=32)

    return model

def biformer_base():
    model = BiFormer(
        depth=[4, 4, 18, 4],
        num_classes=num_classes,
        embed_dim=[96, 192, 384, 768], mlp_ratios=[3, 3, 3, 3],
        n_win=7,
        topks=[1, 4, 16, -2],
        side_dwconv=5,
        before_attn_dwconv=3,
        layer_scale_init_value=-1,
        qk_dims=[96, 192, 384, 768],
        head_dim=32)

    return model
model = biformer_tiny()
paddle.summary(model, (1, 3, 224, 224))

model = biformer_small()
paddle.summary(model, (1, 3, 224, 224))

model = biformer_base()
paddle.summary(model, (1, 3, 224, 224))

2.4 训练

learning_rate = 0.0001
n_epochs = 50
paddle.seed(42)
np.random.seed(42)
work_path = 'work/model'

# BiFormer-T
model = biformer_tiny()

criterion = LabelSmoothingCrossEntropy()

scheduler = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=learning_rate, T_max=50000 // batch_size * n_epochs, verbose=False)
optimizer = paddle.optimizer.Adam(parameters=model.parameters(), learning_rate=scheduler, weight_decay=1e-5)

gate = 0.0
threshold = 0.0
best_acc = 0.0
val_acc = 0.0
loss_record = {'train': {'loss': [], 'iter': []}, 'val': {'loss': [], 'iter': []}}   # for recording loss
acc_record = {'train': {'acc': [], 'iter': []}, 'val': {'acc': [], 'iter': []}}      # for recording accuracy

loss_iter = 0
acc_iter = 0

for epoch in range(n_epochs):
    # ---------- Training ----------
    model.train()
    train_num = 0.0
    train_loss = 0.0

    val_num = 0.0
    val_loss = 0.0
    accuracy_manager = paddle.metric.Accuracy()
    val_accuracy_manager = paddle.metric.Accuracy()
    print("#===epoch: {}, lr={:.10f}===#".format(epoch, optimizer.get_lr()))
    for batch_id, data in enumerate(train_loader):
        x_data, y_data = data
        labels = paddle.unsqueeze(y_data, axis=1)

        logits = model(x_data)

        loss = criterion(logits, y_data)

        acc = paddle.metric.accuracy(logits, labels)
        accuracy_manager.update(acc)
        if batch_id % 10 == 0:
            loss_record['train']['loss'].append(loss.numpy())
            loss_record['train']['iter'].append(loss_iter)
            loss_iter += 1

        loss.backward()

        optimizer.step()
        scheduler.step()
        optimizer.clear_grad()

        train_loss += loss
        train_num += len(y_data)

    total_train_loss = (train_loss / train_num) * batch_size
    train_acc = accuracy_manager.accumulate()
    acc_record['train']['acc'].append(train_acc)
    acc_record['train']['iter'].append(acc_iter)
    acc_iter += 1
    # Print the information.
    print("#===epoch: {}, train loss is: {}, train acc is: {:2.2f}%===#".format(epoch, total_train_loss.numpy(), train_acc*100))

    # ---------- Validation ----------
    model.eval()

    for batch_id, data in enumerate(val_loader):

        x_data, y_data = data
        labels = paddle.unsqueeze(y_data, axis=1)
        with paddle.no_grad():
          logits = model(x_data)

        loss = criterion(logits, y_data)

        acc = paddle.metric.accuracy(logits, labels)
        val_accuracy_manager.update(acc)

        val_loss += loss
        val_num += len(y_data)

    total_val_loss = (val_loss / val_num) * batch_size
    loss_record['val']['loss'].append(total_val_loss.numpy())
    loss_record['val']['iter'].append(loss_iter)
    val_acc = val_accuracy_manager.accumulate()
    acc_record['val']['acc'].append(val_acc)
    acc_record['val']['iter'].append(acc_iter)

    print("#===epoch: {}, val loss is: {}, val acc is: {:2.2f}%===#".format(epoch, total_val_loss.numpy(), val_acc*100))

    # ===================save====================
    if val_acc > best_acc:
        best_acc = val_acc
        paddle.save(model.state_dict(), os.path.join(work_path, 'best_model.pdparams'))
        paddle.save(optimizer.state_dict(), os.path.join(work_path, 'best_optimizer.pdopt'))

print(best_acc)
paddle.save(model.state_dict(), os.path.join(work_path, 'final_model.pdparams'))
paddle.save(optimizer.state_dict(), os.path.join(work_path, 'final_optimizer.pdopt'))

2.5 结果分析

def plot_learning_curve(record, title='loss', ylabel='CE Loss'):
    ''' Plot learning curve of your CNN '''
    maxtrain = max(map(float, record['train'][title]))
    maxval = max(map(float, record['val'][title]))
    ymax = max(maxtrain, maxval) * 1.1
    mintrain = min(map(float, record['train'][title]))
    minval = min(map(float, record['val'][title]))
    ymin = min(mintrain, minval) * 0.9

    total_steps = len(record['train'][title])
    x_1 = list(map(int, record['train']['iter']))
    x_2 = list(map(int, record['val']['iter']))
    figure(figsize=(10, 6))
    plt.plot(x_1, record['train'][title], c='tab:red', label='train')
    plt.plot(x_2, record['val'][title], c='tab:cyan', label='val')
    plt.ylim(ymin, ymax)
    plt.xlabel('Training steps')
    plt.ylabel(ylabel)
    plt.title('Learning curve of {}'.format(title))
    plt.legend()
    plt.show()
plot_learning_curve(loss_record, title='loss', ylabel='CE Loss')

在这里插入图片描述

plot_learning_curve(acc_record, title='acc', ylabel='Accuracy')

在这里插入图片描述

import time
work_path = 'work/model'
model = biformer_tiny()
model_state_dict = paddle.load(os.path.join(work_path, 'best_model.pdparams'))
model.set_state_dict(model_state_dict)
model.eval()
aa = time.time()
for batch_id, data in enumerate(val_loader):

    x_data, y_data = data
    labels = paddle.unsqueeze(y_data, axis=1)
    with paddle.no_grad():
        logits = model(x_data)
bb = time.time()
print("Throughout:{}".format(int(len(val_dataset)//(bb - aa))))
Throughout:214
def get_cifar10_labels(labels):
    """返回CIFAR10数据集的文本标签。"""
    text_labels = [
        'airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog',
        'horse', 'ship', 'truck']
    return [text_labels[int(i)] for i in labels]
def show_images(imgs, num_rows, num_cols, pred=None, gt=None, scale=1.5):
    """Plot a list of images."""
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if paddle.is_tensor(img):
            ax.imshow(img.numpy())
        else:
            ax.imshow(img)
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)
        if pred or gt:
            ax.set_title("pt: " + pred[i] + "\ngt: " + gt[i])
    return axes
work_path = 'work/model'
X, y = next(iter(DataLoader(val_dataset, batch_size=18)))
model = biformer_tiny()
model_state_dict = paddle.load(os.path.join(work_path, 'best_model.pdparams'))
model.set_state_dict(model_state_dict)
model.eval()
logits = model(X)
y_pred = paddle.argmax(logits, -1)
X = paddle.transpose(X, [0, 2, 3, 1])
axes = show_images(X.reshape((18, 224, 224, 3)), 1, 18, pred=get_cifar10_labels(y_pred), gt=get_cifar10_labels(y))
plt.show()
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

在这里插入图片描述

!pip install interpretdl
import interpretdl as it
work_path = 'work/model'
model = biformer_tiny()
model_state_dict = paddle.load(os.path.join(work_path, 'best_model.pdparams'))
model.set_state_dict(model_state_dict)
X, y = next(iter(DataLoader(val_dataset, batch_size=18)))
lime = it.LIMECVInterpreter(model)
lime_weights = lime.interpret(X.numpy()[3], interpret_class=y.numpy()[3], batch_size=100, num_samples=10000, visual=True)
100%|██████████| 10000/10000 [01:45<00:00, 95.06it/s]

██████| 10000/10000 [01:45<00:00, 95.06it/s]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cqDscsCZ-1687745839073)(main_files/main_53_1.png)]

总结

        BiFormer为Token的稀疏化提供了一个很好的思路,在粗粒度上计算关键区域,对关键区域在细粒度上进行注意力交互,这可以减少冗余和无关Token对查询的影响。但是在实际中,BiFormer仍难以解决Attention内存占用大的问题。

参考文献

  1. 论文:BiFormer: Vision Transformer with Bi-Level Routing Attention(CVPR 2023)
  2. 代码:rayleizhu/BiFormer
  3. CVPR’2023 即插即用系列! | BiFormer: 基于动态稀疏注意力构建高效金字塔网络架构

此文章为搬运
原项目链接

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验一 程控交换原理实验系统及控制单元实验 一、 实验目的 1、熟悉该程控交换原理实验系统的电路组成与主要部件的作用。 2、体会程控交换原理实验系统进行电话通信时的工作过程。 3、了解CPU中央集中控制处理器电路组成及工作过程。 二、 预习要求 预习《程控交换原理》与《MCS-51单片计算机原理与应用》中的有关内容。 三、 实验仪器仪表 1、主机实验箱 一台 2、三用表 一台 3、电话单机 四台 四、 实验系统电路组成 (一)电路组成 图1-1是该实验系统的原理框图 图1-1 实验系统的原理框图 图1—2是该实验系统的方框图,其电路的组成及主要作用如下: 1、用户模块电路 主要完成BORSCHT七种功能,它由下列电路组成: A、 用户线接口电路 B、 二\四线变换器 C、 PCM编译码电路 用户线接口电路 二/ 四线变换器 二/四线变换器 用户线接口电路 用户1 PCM CODEC电路 PCM CODEC电路 用户3 用户线接口电路 二/ 四线变换器 二/ 四线变换器 用户线接口电路 用户2 PCM CODEC电路 PCM CODEC电路 用户4 时钟信号电路 控制、检测电路 输出显示电路 二次稳压电路 多种信号音电路 CPU中央处理器 键盘输入电路 直流电源 图1-2 实验系统方框图 2、交换网络系统 主要完成空分交换与时隙交换两大功能,它由下列电路组成: A、空分交换网络系统 B、时隙交换网络系统 3、多种信号音电路 主要完成各种信号音的产生与发送,它由下列电路组成: A、450Hz拨号音电路 B、忙音发生电路 C、回铃音发生电路 D、25Hz振铃信号电路 4、CPU中央集中控制处理器电路 主要完成对系统电路的各种控制,信号检测,号码识别,键盘输入信息,输出显示信息等各种功能。 5、系统工作电源 主要完成系统所需要的各种电源,本实验系统中有+5V,-5V,+12V,-12V,-48V等5组电源,由下列电路组成: A、内置工作电源:+5V,+12V,-12V,-48V B、稳压电源: -8V,-5V 控制部分就是由CPU中央处理系统、输入电路(键盘)、输出电路(数码管)、双音多频DTMF检测电路、用户环路状态检测电路、自动交换网络驱动电路与交换网络转换电路、扩展电路、信号音控制电路等电路组成。 下面简要说明各部分电路的作用与要求: 1、键盘输入电路: 主要把实验过程中的一些功能通过键盘设置到系统中。 2、显示电路:  显示主叫与被叫电路的电话号码,同时显示通话时间。 3、输入输出扩展电路: 显示电路与键盘输入电路主要通过该电路进行工作。主要芯片是D8155A,SN74LS240,MC1413。 4、双音多频DTMF接收检测电路: 把MT8870DC输出的DTMF四位二进制信号,接收存贮后再送给CPU中央集中控制处理系统。 5、用户状态检测电路: 主要识别主、被叫用户的摘挂机状态,送给CPU进行处理。 6、自动交换网络驱动电路: 主要实现电话交换通信时,CPU发出命令信息,由此电路实现驱动自动交换网络系统,其核心集成电路为SN74LS374,D8255A,GD74LS373等芯片。 7、信号音控制电路: 它完全按照CPU发出的指令进行操作,使各种信号音按照系统程序进行工作。 8、振铃控制电路: 它也是按照CPU发出的指令进行工作,具体如下: (A)不振铃时,要求振铃支路与供电系统分开。 (B)振铃时,铃流送向话机,并且供电系统通过振铃支路向用户馈电,用户状态检测电路同时能检测用户的忙闲工作状态。 (C)当振铃时,用户一摘机就要求迅速断开振铃支路。 (D)振铃时要求有1秒钟振、4秒钟停的通断比。 以上是CPU中央集中控制处理系统的主要工作过程,要全面具体实现上述工作过程,则要有软件支持,该软件程序流程图见图1—4。 图1-3 键盘功能框图 对图1-3所示的键盘功能作如下介绍: “时间”: 该键可设置系统的延时时间。如久不拔号、久不应答、位间不拔号的延时,缺省值为10秒,可选择的时间值有10秒、30秒、1分钟。按一次该键则显示下一个时间值,三个值循环显示,当按下“确认”键时,就选定当前显示值供系统使用,按“复位”键则清除该次时间的设定。 “会议电话”: 该键为召开电话会议的按键。电话会议设置用户1为主叫方,其他三路为被叫方,只能由主叫方主持召开会议,向其他三路发出呼叫。电路完全接通或者接通两路后,主叫方能和任一被叫方互相通话。除“复位”键外,其他键均推失去功能。会议结束后,可按“复位”键重启系统。 “中继”: 该键为局内交换切向中继交换的功能按键,按下此键,再按“确认”键进行确认,则工作模式由局内交换切换为中继交换,显示器循环显示“d”,此时方可通过中继拨打“长途”电话。按“复位”键重启系统,进入正常局内交换模式。 “确认”: 该键完成对其他功能键的确认,防止误按键,在键盘中除“复位”键外,其他功能键都必须加“确认”键才能完成所定义的功能。 “复位”: 该键为重启系统按键。在任何时候或者系统出现不正常状态时都可按下此键重启系统(有用户通话时,会中断通话),所有设置均为默认值。 图1-5是显示电路工作示意说明图。 主叫号码显示 计时显示 被叫号码显示 图1-5 显示电路 开 始 NO 有用户呼叫吗? 呼叫••••••••••••••••••••••••••••••••••••••••••• YES 去 话 接 续 向主叫送拨号音 NO 第一位号码来了吗? 拨号开始•••••••••••••••••••••••••••••••• YES 停送拨号音,收存号码 内 部 处 理 拨号完毕•••••••••••••••••••••••••••••••• 被叫闲吗? NO YES 来 话 接 续 向主叫送忙音 向被叫送铃流,向主叫送回铃音 被叫应答否? NO 主叫挂机否? 应答•••••••••••••••••••••••••••••••••••• YES 停送铃流,回铃音,接通电路 YES 话终挂机否? 挂机•••••••••••••••••••••••••••••••••••••• YES 拆线(释放复原) 结 束 图1-4 程序工作流程示意图 五、实验内容 1、测量实验系统电路板中的TP91~TP95各测量点电压值,并记录。 2、从总体上初步熟悉两部电话单机用空分交换方式进行通话。 3、初步建立程控交换原理系统及电话通信的概念。 4、观察并记录一个正常呼叫的全过程。 5、观察并记录一个不正常呼叫的状态。 图1-6 呼叫识别电路框图 五、 实验步骤 1、接上交流电源线。 2、将K11~K14,K21~K24,K31~K34,K41~K44接2,3脚;K70~K75接2,3脚;K60~K63接2,3脚。 3、先打开“交流开关”,指示发光二极管亮后,再分别按下直流输出开关J8,J9。此时实验箱上的五组电源已供电,各自发光二极管亮。 4、按 “复位”键进行一次上电复位,此时,CPU已对系统进行初始化处理,数码管循环显示“P” ,即可进行实验。 5、将三用表拔至直流电压档,然后测量TP91,TP92,TP93,TP94,TP95的电压是否正常:TP91为-12V,TP92为-48V,TP93为+5V,TP94为+12V,TP95为-5V。(-48V允许误差±10%,其它为±5%) 6、将四个用户接上电话单机。 7、正常呼叫全过程的观察与记录。(现以用户1为主叫,用户4为被叫进行实验) A、 主叫摘机,听到拨号音,数码管显示主叫电话号码“68” 。 B、 主叫拨首位被叫号码“8”,主叫拨号音停,主叫继续拨完被叫号码“9”。 C、 被叫振铃,主叫听到回铃音。 D、 被叫摘机,被叫振铃停,主叫回铃音停,双方通话。数码管显示主叫号码和被叫号码,并开始通话计时。 E、 挂机,任意一方先挂机(如主叫先挂机),另一方(被叫)听到忙音,计时暂停,双方都挂机后,数码管循环显示“P” 。 8、不正常呼叫的自动处理 A、 主叫摘机后在规定的系统时间内不拨号,主叫听到忙音。(系统时间可以设置,在系统复位后按“时间”可循环显示“10”,“30”,“100”,分别表示10秒,30秒,1分钟,选定一个时间,按“确定”即系统时间被设置,在复位状态时,系统时间默认为10秒。) B、 拨完第一位号码后在规定的系统时间内没有拨第二位号码时,主叫听到忙音。 C、 号码拨错时(如主叫拨“56” ),主叫听到忙音。 D、 被叫振铃后在规定的系统时间内不摘机,被叫振铃音停,主叫听到忙音。 六、 实验注意事项 对实验系统加电一定要严格遵循先打开系统工作电源的“交流开关”,然后再打开直流输出开关J8,J9。实验结束后,先分别关直流输出开关J8,J9。最后再关“交流开关”,以避免实验电路的器件损坏。 七、 实验报告要求 1、画出实验系统电路的方框图,并作简要叙述。 2、对正常呼叫全过程进行记录。 实验二 用户线接口电路及二\四线变换实验 一、实验目的 1、全面了解用户线接口电路功能(BORST)的作用及其实现方法。 2、通过对MH88612C电路的学习与实验,进一步加深对BORST功能的理解。 3、了解二\四线变换电路的工作原理。 二、预习要求 认真预习程控交换原理中有关用户线接口电路等章节。 三、实验仪器仪表 1、主机实验箱 一台 2、电话单机 二台 3、20MHz示波器 一台 4、三用表 一台 四、电路工作过程 在现代电话通信设备与程控交换机中,由于交换网络不能通过铃流、馈电等电流,因而将过去在公用设备(如绳路)实现的一些用户功能放到“用户电路”来完成。 用户电路也可称为用户线接口电路(Subscriber Line Interface Circuit—SLIC)。任何交换机都具有用户线接口电路。 模拟用户线接口电路在实现上的最大压力是应能承受馈电、铃流和外界干扰等高压大电流的冲击,过去都是采用晶体管、变压器(或混合线圈)、继电器等分立元件构成,随着微电子技术的发展,近十年来在国际上陆续开发多种模拟SLIC,它们或是采用半导体集成工艺或是采用薄膜、厚膜混合工艺,并已实用化。在实际中,基于实现和应用上的考虑,通常将BORSCHT功能中过压保护由外接元器件完成,编解码器部分另单成一体,集成为编解码器(CODEC),其余功能由所谓集成模拟SLIC完成。 在布控交换机中,向用户馈电,向用户振铃等功能都是在绳路中实现的,馈电电压一般是-60V,用户的馈电电流一般是20mA~30 mA,铃流是25HZ, 90V左右,而在程控交换机中,由于交换网络处理的是数字信息,无法向用户馈电、振铃等,所以向用户馈电、振铃等任务就由用户线接口电路来承担完成,再加上其它一些要求,程控交换机中的用户线接口电路一般要具有B(馈电)、O(过压保护)、R(振铃)、S(监视)、C(编译码)、H(混合)、T(测试)七项功能。 模拟用户线接口电路的功能可以归纳为BORSCHT七种功能,具体含义是: (1)馈电(B-Battery feeling)向用户话机送直流电流。通常要求馈电电压为—48伏,环路电流不小于18mA。 (2)过压保护(O-Overvoltage protection)防止过压过流冲击和损坏电路、设备。 (3)振铃控制(R-Ringing Control)向用户话机馈送铃流,通常为25HZ/90Vrms正弦波。 (4)监视(S-Supervision)监视用户线的状态,检测话机摘机、挂机与拨号脉冲等信号以送往控制网络和交换网络。 (5)编解码与滤波(C-CODEC/Filter)在数字交换中,它完成模拟话音与数字码间的转换。通常采用PCM编码器(Coder)与解码器(Decoder)来完成,统称为CODEC。相应的防混叠与平滑低通滤波器占有话路(300HZ~3400HZ)带宽,编码速率为64kb/s。 (6)混合(H-Hyhird)完成二线与四线的转换功能,即实现模拟二线双向信号与PCM发送,接收数字四线单向信号之间的连接。过去这种功能由混合线圈实现,现在改为集成电路,因此称为“混合电路”。 (7)测试(T-Test)对用户电路进行测试。 模拟用户线接口功能见图2—1。 铃流发生器 馈电电源 发送码流 过 振 低通 编 a 压 测 铃 馈 混 码 模 拟 保 试 继 电 合 平衡 器 用 (编码信号) 户 护 开 电 电 电 网络 解 线 b 电 关 器 路 路 码 路 低通 器 接收码流 测试 振铃控台 用户线 总线 制信号弹 状态信号 图2-1 模拟用户线接口功能框 (一)用户线接口电路 在本实验系统中,用户线接口电路选用的是MITEL公司的MH88612C。MH88612C是2/4线厚膜混合用户线接口电路。它包含向用户话机恒流馈电、向被叫用户话机馈送铃流、用户摘机后自行截除铃流,摘挂机的检测及音频或脉冲信号的识别,用户线是否有话机的识别,语音信号的2/4线混合转换,外接振铃继电器驱动输出。MH88612C用户电路的双向传输衰耗均为-1dB,供电电源+5V和-5V。其各项性能指标符合邮电部制定的有关标准。 (1)该电路的基本特性 1、向用户馈送铃流 2、向用户恒流馈电 3、过压过流保护 4、被叫用户摘机自截铃 5、摘挂机检测和LED显示 6、音频或脉冲拨号检测 7、振铃继电器驱动输出 8、语音信号的2/4线转换 9、能识别是否有话机 10、无需偶合变压器 11、体积小及低功耗 12、极少量外围器件 13、厚膜混合型工艺 14、封装形式为20引线单列直插 图2-2是它的管脚排列图 (2)MH88612C引出端功能的说明 0脚:IC Internal Connection:空端。 1脚:TF Tip Feed: 连接外接二极管作为保护电路连到-48V和地。。 2脚:IC Internal Connection:空端。 3脚:VR Voice Receive(input): 四线语音信号的接收端。 4脚:VRef Voltage Reference:设置向用户电话线送恒流馈电的参考电压,恒流通过VRef调节;也可接地,一般为21mA环流。 5脚:VEE 负供电电源,通常为-5V DC。 6脚:GNDA 供电电源和馈电电源的地端,模拟接地。 7脚:GS Gain setting(input):低电平时直接接收附加增益为-0.5 dB, 此增益除编解码增益设置之外的,高电平时为0dB。 8脚:VX Voice Transmit(output):四线语音信号的发送端。 9脚:TIP 连接用户电话的“TIP”线。 10脚:RING 连接用户电话的“RING”线。 11脚:RF Ring Feed:外部连接至振铃继电器。 12脚:VDD 正供电电源,通常为+5V DC。 13脚:RC Relay Control(input)振铃继电器控制输入端,高电平有效 14脚:RD 振铃继电器驱动输出端,外接振铃继电器线圈至地端,内部有一线圈感应箝位二极管。 15脚:RV Ring Feed Voltage:用户线铃流源输入端,外部连接至振铃继电器。 16脚:VRLY 振铃继电器正供电电源,能常为+5V DC。 17脚:IC Internal Connection:空端。 18脚:VBat 用户线馈电电压,通常为-48V DC 19脚:CAP 连接外部电容作为振铃滤波控制连电阻到地。 20脚:SHK 摘挂机状态检测及脉冲号码输出端,摘机时输出高电平。 (3)用户线接口电路主要功能 图2-3是MH88612C内部电路方框图,其主要功能说明如下: TF VR TIP RING VX RF RV VRLY RC VRef RD CAP SHK 图2-3 MH88612C内部电路方框图 1、向用户话机供电,MH88612C可对用户话机提供恒流馈电,馈电电流由VBAT以及VDD供给。恒定的电流为25 mA。当环路电阻为2KΩ时,馈电电流为18 mA,具体如下: A、 供电电源VBat采用-48V; B、 在静态情况下(不振铃、不呼叫),-48V电源通过继电器静合接点至话机; C、 在振铃时,-48V电源通过振铃支路经继电器动合接点至话机; D、 用户挂机时,话机叉簧下压馈电回路断开,回路无电流流过; E、 用户摘机后,话机叉簧上升,接通馈电回路(在振铃时接通振铃支路)回路。 2、MH88612C内部具有过压保护的功能,可以抵抗保护TIP- -RING端口间的瞬时高压,如结合外部的热敏与压敏电阻保护电路,则可保护250V左右高压。 3、振铃电路可由外部的振铃继电器和用户电路内部的继电器驱动电路以及铃流电源向用户馈送铃流:当继电器控制端(RC端)输入高电平,继电器驱动输出端(RD端)输出高电平,继电器接通,此时铃流源通过与振铃继电器连接的15端(RV端)经TIP––RING端口向被叫用户馈送铃流。当控制端(RC端)输入低电平或被叫用户摘机都可截除铃流。用户电路内部提供一振铃继电器感应电压抑制箝位二极管。 4、监视用户线的状态变化即检测摘挂机信号,具体如下: A、用户挂机时,用户状态检测输出端输出低电平,以向CPU中央集中控制系统表示用户“闲”; B、用户摘机时,用户状态检测输出端输出高电平,以向CPU中央集中控制系统表示“忙”; 5、在TIP––RING端口间传输的语音信号为对地平衡的双向语音信号,在四线VR端与VX端传输的信号为收发分开的不平衡语音信号。MH88612C可以进行TIP––RING端口与四线VR端和VX端间语音信号的双向传输和2/4线混合转换。 6、MH88612C可以提供用户线短路保护:TIP线与RING线间,TIP线与地间,RING线与地间的长时间的短路对器件都不会损坏。 7、MH88612C提供的双向语音信号的传输衰耗均为-dB。该传输衰耗可以通过MH88612C用户电路的内部调整,也可通过外部电路调整; 8、MH88612C的四线端口可供语音信号编译码器或交换矩阵使用。 由图1-1可知,本实验系统共有四个用户线接口电路,电路的组成与工作过程均一样,因此只对其中的一路进行分析。 图2-4是用户1用户线接口电路的原理图: 图2-4 用户线接口电路电原理图 为了简单和经济起见,反映用户状态的信号一般都是直流信号,当用户摘机时,用户环路闭合,有用户线上有直流电流流过。主叫摘机表示呼叫信号,被叫摘机,则表示应答信号,当用户挂机时,用户环路断开,用户线上的直流电流也断开,因此交换机可以通过检测用户线上直流电流的有无来区分用户状态。 当用户摘机时,发光二极管D10亮表示用户已处于摘机状态,TP13由低电平变成高电平,此状态送到CPU进行检测该路是否摘机,当检测到该路有摘机时,CPU命令拨号音及控制电路送出f=450HZ,U=3V的波形。 此时,在TP12上能检测到如图2—5所示波形 TP12 0 2VP-P t f = 400~450Hz 图2-5 450Hz拨号音波形 当用户听到450HZ拨号音信号时,用户开始拨电话号码,双音多频号码检测电路检测到号码时通知CPU进行处理,CPU命令450HZ拨号音发生器停止送拨号音,用户继续拨完号码,CPU检测主叫所要被叫用户的号码后,立即向被叫用户送振铃信号,提醒被叫用户接听电话,同时向主叫用户送回铃音信号,以表示线路能够接通,当被叫用户摘机时,CPU接通双方线路,通信过程建立。一旦接通链路,CPU即开始计时,当任一方先挂机,CPU检测到后,立即向另一方送忙音,以示催促挂机,至此,主、被叫用户一次通信过程结束。 通过上述简单分析,不难得出各测量点的波形。 TP11:通信时有发送话音波形;拨号时有瞬间DTMF波形;不通信时则此点无波形。 TP12:通信时有接收话音波形:摘机后拨号前有450HZ拨号音信号;不通信时则此点无波形。 TP13:摘挂机状态检测测量点 挂机:TP13=低电平。 摘机:TP13=高电平。 TP14:振铃控制信号输入,高电平有效。即工作时为高电平,常态为低电平。 由于4个用户线接口电路的测量点相同,故对其它三个用户线接口电路的测量点就不一一叙述,波形均相同,即: TP11=TP21=TP31=TP41 TP12=TP22=TP32=TP42 TP13=TP23=TP33=TP43 TP14=TP24=TP34=TP44 (二)二\四线变换电路 在该实验系统中,二\四线变换由用户线接口电路中的语音单元电路实现,图2-6为电路的功能框图,该电路完成二线–––单端之间信号转换,在MH88612C内部电路中已经完成了该变换。 T TR R 图2-6 二/四线变换功能框图 二\四线变换的作用就是把用户线接口电路中的语音模拟信号(TR)通过该电路的转换分成去话(T)与来话(R),对该电话的要求是: 1、将二线电路转换成四线电路; 2、信号由四线收端到四线发端要有尽可能大的衰减,衰减越大越好; 3、信号由二线端到四线发端和由四线收端到二线端的衰减应尽可能小,越小越好; 4、应保持各传输端的阻抗匹配; 以便于PCM编译码电路形成发送与接收的数字信号。 五、实验内容 1、参考有关程控交换原理教材中的用户线接口电路等单节,对照该实验系统中的电路,了解其电路的组成与工作过程。 2、通过主叫、被叫的摘、挂机操作,了解B、R、S等功能的具体作用。 六、实验步骤 1. 接上交流电源线。 2. 将K11~K14,K21~K24,K31~K34,K41~K44接2,3脚;K70~K75接2,3脚;K60~K63接2,3脚。 3. 先打开“交流开关”,指示发光二极管亮后,再分别按下直流输出开关J8,J9,此时实验箱上的五组电源已供电,各自发光二极管亮。 4. 按“复位”键进行一次上电复位,此时,CPU已对系统进行初始化处理,显示电路循环显示“P”,即可进行实验。 5. 用户1,用户3接上电话单机。 6. 用户电话单机的直流供电(B)的观测。(现以用户1为例) 1) 用户1的电话处于挂机状态,用三用表的直流档测量TP1A,TP1B对地的电压,TP1A为-48V,TP1B为0V,它们之间电压差为48V。 2) 用户1的电话处于摘机状态,用三用表的直流档测量TP1A,TP1B对地的电压,TP1A为-10V左右(此时的电压与电话的内阻抗有关,所以每部电话的测量值不一定相同),TP1B为-3.7V左右。 以上给出的电压值只是作为参考。 7. 观察二/四线变换的作用。 1) 用正常的呼叫方式,使用户1、用户3处于通话状态。 2) 当用户1对着电话讲话时(或按电话上的任意键),用示波器观察TP11上的波形,为语音信号(或双音多频信号),不讲话时无信号。 3) 当用户1听到用户3讲话时(或用户3按电话上任意键),用示波器观察TP12上的波形,为语音信号(或双音多频信号),对方不讲话时无信号。 4) 用示波器观察TP1A。不管是用户1讲话还是用户3讲话(或按电话上的任意键)此测试点都有语音波形(或双音多频信号)。 8. 摘、挂机状态检测的观测。 1) 当用户1的电话摘机时,用示波器测量TP13为高电平(4V左右)。 2) 当用户1的电话挂机时,用示波器测量TP13为低电平(0V左右)。 9. 被叫话机振铃(R)的观测。 1) 用户1处于挂机状态,用户3呼叫用户1,即用户3拨打“68”,使用户1振铃。 2)当用户1的电话振铃时,用示波器观察TP14,振铃时TP14为高电平(3V左右);不振铃时TP14为低电平(0V左右)。 七、实验注意事项 当实验过程中出现不正常现象时,请按一下“复位”键,以使系统重新启动。 八、实验报告要求 1、画出本次实验电路方框图,并能说出其工作过程。 2、画出各测量点在各种情况下的波形图。 实验三 程控交换PCM编译码器实验 一、实验目的 1、掌握PCM编译码器在程控交换机中的作用。 2、熟悉单片PCM编译码集成电路TP3067的使用方法。 二、预习要求 1、查阅有关TP3067的使用说明及其应用电路。 2、认真预习程控交换原理中有关这方面的内容。 三、实验仪器仪表 1、主机实验箱 一台 2、电话单机 二台 3、20MHz示波器 一台 4、音频信号源 一台 四、实验电路工作过程 1、PCM编译码器的简单介绍 模拟信号经过编译码器时,在编码电路中,它要经过取样、量化、编码,如图3—1(a)所示。到底在什么时候被取样,在什么时序输出PCM码则由A→D控制来决定。同样PCM码被接收到译码电路后经过译码低通、放大。最后输出模拟信号到话机,把这两部分集成在一个芯片上就是一个单路编译码器,它只能为一个用户服务,即在同一时刻只能为一个用户进行A\D及D\A变换。 编码器把模拟信号变换成数字信号的规律一般有二种,一种是μ律十五折线变换法,它一般用在PCM24路系统中,另一种是A律十三折线非线性变换法,它一般应用于PCM30\32路系统中,这是一种比较常用的变换法,模拟信号经取样后就进行A律十三折变换,最后变成8位PCM码头,在单路编译码器中,经变换后的PCM码是在一个时隙中被发送出去,这个时序号是由A→D控制电路来决定的,而在其它时隙时编码器是没有输出的,即对一个单路编译码器来说,它在一个PCM帧里只在一个由它自己的A→D控制电路决定的时隙里输出8位PCM码,同样在一个PCM帧里,它的译码电路也只能在一个由它自己的D—A控制电路决定的时序里,从外部接收8位PCM码。 其实电路编译码器的发送时序和接收时序还是可由外部电路来控制的,编译码器的发送时序由A→D控制电路来控制,而A→D控制电路还是受外部控制电路的控制,同样在译码电路中D→A控制电路也受外部控制电路的控制,这样,我们只要向A→D控制电路或D→A控制电路发某种命令即可控制单路编译码器的发送时序和接收时序号,从而也可以达到总线交换的目的,但各种单路编译码器对其发送时序和接收时序的控制方式都有所不同。象有些编译器就有二种方式,一种是编程法,即给它内部的控制电路输进一个控制字,令其在某某时隙干什么工作,另一种是直接控制,这时它有两个控制端,我们定义为FSX和FSr,要求FSX和FSr是周期性的,并且它的周期和PCM的周期要相同,都为125μS,这样,每来一个FSX,其中codec就输出一个PCM码,每来一个FSr,其codec就从外部输入一个PCM码。 图3-1(b)是PCM的译码电路方框图,它的工作过程同图3-1(a)的工作过程完全相反,因此这里就不再讨论了。 (a)A→D电路 (b)D→A电路 图3—1 A\D及D\A电路框图 2.在本实验系统的PCM编译码电路中,器件为美国国家半导体公司的TP3067。图3-2是它的管脚排列图。 图3-2 TP3067管脚排列图 其引脚符号说明 符号 功能 VP0+ 接收功率放大器的非倒相输出 GNDA 模拟地,所有信号均以该引脚为参考点 VP0- 接收功率放大器的倒相输出 VPI 接收功率放大器的倒相输入 VFRO 接收滤波器的模拟输出 VCC 正电源引脚,VCC=+5V±5% FSR 接收帧同步脉冲,它启动BCLKR,于是PCM数据移入DR,FSR为8KHz脉冲序列。 DR 接收帧数据输入,PCM数据随着FSR前沿移入DR BCLKR\CLKSESL 在FSR的前沿后把数据移入DR的位时钟,其频率可从64KHz至2.48MHz。另一方面它也可能是一个逻辑输入,以此为在同步模式中的主时钟选择频率1.536MHz\1.544MHz或2.048MHz,BCLKR用在发送和接收两个方向(见表3-1) MCLKR\PDN 接收主时钟,其频率可以为1.536MHz、1.544MHz或2.148MHz,它允许与MCLKX异步,但为了获得最佳性能应当与MCLKX同步,当MCLKR连续联在低电位时,CLKX被选用为所有内部定时,当MCLKR连续工作在高电位时,器件就处于掉电模式。 MCLKX 发送主时钟,其频率可以是1.536MHZ,1.544MHZ或2.048MHz,它允许与MCLKR异步,同步工作能实现最佳性能。 BCLKX 把PCM数据从DX上移出的位时钟,其频率可 64kHz变至2.048MHz,但必须与MCLKX同步。 DX 由FSX启动的三态PCM数据输出 FSX 发送帧同步脉冲输入,它启动BCLKX并使DX上PCM数据移出DX上。 ANLB 模拟环回路控制输入,在正常工作时必须置为逻辑“0”当拉到逻辑“1”时,发送滤波器和发送前置放大器输出的连接线被断开,开而改为和接收功率放大器的VP0+输出连接。 GSX 发送输入放大器的模拟输出。用来在外部调节增益。 VFXI- 发送输入放大器的倒相输入。 VFXI+ 发送输入放大器的非倒相输入。 VBB 负电源引脚,VBB= -5V±5%。 3、PCM编译码电路的工作时钟 由上述电路分析可知,PCM编译码电路所需的工作时钟为2.048MHZ,FSR、FSX帧同步信号为8KHZ窄脉冲。它们的时序关系如图3-3 TP2048 0 TPTS0~ TPTS7 0 图3—3 PCM编译码工作钟各测量点波形图 图3-4 PCM编解码电原理图 五、实验内容 PCM编译码(C)的功能实验 六、实验步骤 1. 接上交流电源线。 2. 将K11~K14,K21~K24,K31~K34,K41~K44接2,3脚;K70~K74接2,3脚,K75接1,2脚;K60~K63接2,3脚;KTS7接2,3脚;K51、K52接2、3脚。 3. 先打开“交流开关”,指示发光二极管亮后,再分别按下直流输出开关J8,J9,此时实验箱上的五组电源已供电,各自发光二极管亮。 4. 按“复位”键进行一次上电复位,此时,CPU已对系统进行初始化处理,显示电路循环显示“P”,即可进行实验。 5. 将一外加音频信号正弦波(VP-P为1.5伏,频率为1KHZ左右)接入至TPIN输入端(在实验箱上面中部)。 6. 用示波器逐点观察TPIN、TPDT、TPDTMF各测量点波形。 7. 慢慢增加外加音频信号的幅值,并用示波器观察TPDTMF的波形的变化。 说明:图3-5是PCM编译码输入输出波形图。有一点需注意,PCM编译码电路中,在没有外加信号输入时,PCM编码电路还是有输出的,此时该芯片对输入随机噪声进行编译码,一旦有信号输入,它会立即对输入信号进行编码。 TPIN 0 t TPTS6 t 125uS TPDT 0 t TPDTMF 0 t 图3-5 PCM编译码电路输入、输出波形图 七、实验注意事项 1、在进行PCM实验时,对TP3067芯片要特别小心谨慎操作,+5V、-5V电源必须同时加入,以保证该芯片有接地回路,否则,该芯片特别容易损坏。 2、观测各测量点波形时,示波器探头不能乱碰到其它测量点。 八、实验报告要求 1、画出各测量点的波形,注明在何种状态下测试到的波形。 2、当外加信号源的幅值到达一定值时,TPDTMF外的波形就会失真,这是为什么,分析其原因。 3、写出对实验电路的改进措施,有何体会? 实验四 多种信号音及铃流信号发生器实验 一、实验目的 1、了解电话通信中常用的几种信号和铃流信号的电路组成与产生方法。 2、熟悉这些音信号在传送过程中的技术要求和实现方法。 二、预习要求 预习有关拨号音,忙音,回铃音,铃流等有关内容。 三、实验仪器仪表 1、主机实验箱 一台 2、电话机 二台 3、20MHz示波器 一台 四、电路工作过程 我们知道,在用户话机与电信局的交换机之间的线路上,要沿两个方向传递语言信息。但是,为了接通一个电话,除了上述情况外,还必须沿两个方向传送所需的控制信号。比如,当用户想要通话时,必须首先向程控机提供一个信号,能让交换机识别并使之准备好有关设备,此外,还要把指明呼叫的目的地的信号(被叫)发往交换机。当用户想要结束通话时,也必须向电信局交换机提供一个信号,以释放通话期间所使用的设备。除了用户要向交换机传送信号之外,还需要传送相反方向的信号,如交换机要向用户传送关于交换机设备状况,以及被叫用户状态的信号。 由此可见,一个完整的电话通信系统,除了交换系统和传输系统外,还应有信号系统。 下面是本实验系统的传送信号流程,见图4-1所示。 用户向电信局交换机发送的信号有用户状态信号和号码信号。交换机向用户发送的信号有各种可闻信号与振铃信号(铃流)两种方式。 a、各种可闻信号:一般采用频率为450Hz的交流信号,例如: 拨号音:(Dial tone)连续发送的信号。 回铃音:(Ringing tone)1秒送,4秒断的5秒断续信号,与振铃一致。 忙音:(busy tone)0.35秒送,0.35秒断的0.7秒断续信号。 b、振铃信号(铃流):一般采用频率为25Hz,幅度为75V±15V的交流电压,以1秒送,4秒断的5秒断续方式发送。 在呼叫建立过程中,交换机应向主叫用户发送各种信号音,以使用户能了解连续进展情况和下一步应采取的操作。 用户线 用户线 主叫用户 被叫用户 摘机 拨号音信号 回铃音信号 振铃信号 话音信号 通信建立 忙音信号 挂机(先挂方) 挂机信号 挂机 (用户线信号) 图4-1 本实验系统传送信号流程图 (一)拨号音及产生电路 主叫用户摘机,CPU检测到该用户有摘机状态后,立即送出的音信号,表示可以拨号,当CPU中央处理单元收到第一个拨号脉冲后,应立即给予切断该信号,拨号音用连续的信号音。在本实验系统中,频率为400Hz~450Hz之间,幅度在1.5V~3.5 V之间,图4-2(a)是该电路的框图,图4-2(b)是该原理图。 (a) 450HZ方框图 (b) 450HZ电原理图 图4-2 450Hz拨号音电路图 (二)回铃音及控制电路 回音信号由CPU中央处理单元控制送出,通知主叫用户正在对被叫用户振铃,回铃音信号所用频率也同拨号音频率,继续周期为1秒通,4秒断,与振铃一致。 各国所用的断续周期不同,如日本为1秒断2秒续,重复周期为3秒。美国和加拿大为2秒续,4秒断,重复周期为6秒。我国采用4秒断,1秒续的5秒周期信号。因此在本实验系统中采用大约4秒断,1秒续的重复周期为5秒信号,见图4-3所示。 (a) 方框图 (b) 电原理图 图4-3 回铃音控制产生电路框图及原理图 (三)忙音及控制电路 忙音表示用户处于忙状态,此时用户应挂机等一会再重新呼叫。 在本实验系统中采用大约0.35秒断,0.35秒续的400Hz~450Hz的信号,见图4-4所示。 (a) 方框图 (b) 电原理图 图4-4 忙音控制产生电路框图及电原理图 (四)铃流信号发生器电路 铃流信号的作用是交换机向被叫用户发出,作为呼入信号,一般采用低频电流,如频率有16.6Hz、25Hz、33.3Hz等几种。 它的断续周期同回铃音信号相同,因此,在本实验系统中采用大约4秒断、1秒通的断续信号。图4-5是它的原理方框图,电原理图4-6所示。 图4-5 25HZ铃流发生器框图 图4-6 25Hz铃流发生器电原理图 上述四种信号在本实验系统中均有具体电路实现,然而在程控交换机中,信号音还不止上述几种,在此作一简单介绍,不作实验要求。 图4-7中各测量点的波形 (1)450Hz拨号音电路,其测量点为TP60; (2)回铃音控制电路,其测量点为TP61; (3)忙音控制电路,其测量点为TP62; (4)25Hz铃流号发生器电路,其测量点为TP63; (5)铃流信号输出的变压电路,其测量点为TP64; TP60 +1.5V O t -1.5V f=400~450Hz TP61 4.2V O t 1s 4s TP62 4.2V 0 t 0.35s 0.35s TP63 4.2V 0 t TP64 +50~60V 0 t -50~60V 图4-7 各测量点的波形图 (六)音信号的数字方式产生 众所周知,在数字程控交换机中直接进行交换的是PCM数字信息,在这样的情况下如何使用户接收到信号音(如拨号音,回铃音,忙音等)是一个重要的问题。因为模拟电路产生的信号音是不能通过PCM交换系统的,这就是要求设计一个数字型信号音发生器,使之能向交换网络输出这样一些PCM数字信息,这些数字信息经过非线性译码后能成为一个我们所需的模拟信号音。 1、传统方式产生数字音信号 电路见图4-8所示,可知,这是一种常见的PCM编码方式,400Hz~450Hz的正弦信号由硬件电路实现,再经过PCM编码器电路后,就可输出音信号的PCM数字码流了,经过数字交换网络后,再进行D/A变换还原成正弦信号送往用户电路即可。 图4—8 传统方式产生音信号电原理图 2、用数字电路产生音信号 图4-9是大约450Hz正弦波信号一个周期取样示意图,图4-10是数字电路产生音信号的原理框图。 0 t1 t2 t3 t4 A B C D 图4-9 450Hz正弦波信号取样示意图 图4—10 数字型信号音产生电路原理框图 由此可见,我们只要对正弦信号在理论上以每隔125μs取样一次,并将取样所得的正弦信号幅度按照A律十三折线非线性编码的规律进行计算,变成二进制编码,然后把这些二进制码存贮在EEPROM中,只要每隔125μs对它读出一次即可得到PCM数字信息码流。(注意:TP3067编码输出时,偶数位取反,例如+2.5V的电压编码输入应为 1111 1111,而TP3067输出为 1010 1010。) 五、实验内容 1、用三用表或示波器测量拨号音,忙音、回铃音及铃流信号的各测量点电压或波形,即测量点TP60、TP61、TP62、TP63、TP64。 六、实验步骤 1. 接上交流电源线。 2. 将K11~K14,K21~K24,K31~K34,K41~K44接2,3脚;K70~K75接2,3脚;K60~K63接2,3脚。 3. 先打开“交流开关”,指示发光二极管亮后,再分别按下直流输出开关J8、J9,此时实验箱上的五组电源已供电,各自发光二极管亮。 4. 按“复位”键进行一次上电复位,此时,CPU已对系统进行初始化处理,显示电路循环显示“P”,即可进行实验。 5. 用示波器测量TP60、TP61、TP62、TP63、TP64各点波形。(观察TP61、TP62时示波器应设置为直流档) TP60 TP61 TP62 TP63 TP64 6. 用户1、用户3接上电话单机,用户1呼叫用户3,在呼叫过程中观察TP12的波形。(示波器设为直流档) 1) 用双踪示波器观察TP12的波形和TP60的波形,用户1摘机后听到拨号音时。即TP12与TP60的波形一样为450HZ的三角波信号。 2) 用户1拨完被叫电话号码“88” 后听到回铃音时,用双踪示波器观察TP12的波形和TP61的波形。即当TP61为高电平时(用户1听到回铃音),TP12有450HZ的三角波信号;当TP61为低电平时,TP12无波形。 3) 用户3振铃时,用双踪示波器观察TP3A的波形和TP64的波形。即当用户3振铃时,TP3A与TP64的波形一样;不振铃时,TP3A无波形。 4) 用户3摘机通话后,用户3先挂机,此时用户1听到忙音,用双踪示波器观察TP12的波形和TP62的波形。即当TP62为高电平时(用户1听到忙音),TP12有450HZ的三角波信号;当TP62为低电平时,TP12无波形。 七、实验注意事项 1、此项实验必须要由两人合作完成。 2、在测量25Hz的铃流信号发生器输出的波形时,一定要注意三用表的量程和示波器的电压量程档,以防止损坏仪器和其它电子器件。 八、实验报告要求 1、认真画出实验过程各测量点波形,并进行分析。 2、画出电路组成框图。 3、在实验过程中遇到的其它情况作出记录,并进行分析。 实验五 双音多频DTMF接收实验 一、实验目的 1、了解电话号码双音多频信号在程控交换系统中的发送和接收方法。 2、熟悉该电路的组成及工作过程。 二、预习要求 1、认真预习有关双音多频等相关内容。 三、实验仪器仪表 1、主机实验箱 一台 2、电话单机 两台 3、20MHz示波器 一台 四、实验电路工作过程 (一)双音多频拨号简单介绍 在电话单机中,有两种拨号方式,即脉冲拨号和双音多频拨号。 双音多频拨号方式中的双音多频是指用两个特定的单音频信号的组合来代表数字或功能,两个单音频的频率不同,所代表的数字和功能也不同,在双音多频电话机中有16个按键,其中有10个数字键0~9,6个功能键*、#、A、B、C、D,按照组合的原理,它必须有8种不同的单音频信号,由于采用的频率有8种,故又称之为多频,又因以8种频率中任意抽出2种进行组合,又称其为8中取2的编码方式。 根据CCITT的建议,国际上采用697Hz、770Hz、852Hz、941Hz、1209Hz、1336Hz、1477Hz和1633Hz,把这8种频率分成两个群,即高频群和低频群,从高频群和低频群中任意各抽出一种频率进行组合,共有16种不同组合,代表16种不同数字或功能,见表5-1。 表5-1 1209 1336 1447 1633 697 1 2 3 A 770 4 5 6 B 852 7 8 9 C 941 * 0 # D 表中*、# 键作特殊功能用(如闭音、重发)等,A、B、C、D留作它用,例如拨数字号码“8”,则发双音多频信号频率为fH=1336Hz、fL=852Hz。 双音多频,简写DTMF(DTMF=Dual Tone Multirequency) fH (C1~C4) (R1~R4) fL 图5-1 一个典型的DTMF发送电路原理框图 DTMF发送器的原理与构成如图5-1所示,它主要包括: (1)晶体振荡器––––外接晶体(通常采用3.579545MHz)与片内电路构成振荡器,经分频产生参考信号。 (2)键控可变时钟产生电路–––––它是一种可控分频比的分频器,通常由n级移位寄存器与键控反馈逻辑单元组成。 (3)正弦波产生电路–––––它由正弦波编码器与D/A变换器构成,通常,可变速时钟信号先经5位移位寄存器,产生一组5位移位代码,再由可编程逻辑阵列(PLA)将其转换成二进制代码,加到D/A变换器形成台阶型正弦波。显然台阶的宽度等于时钟频率的倒数,这样形成的正弦波信号频率必然对应时钟的速率和按键的号码。 (4)混合电路–––––将键盘所对应产生的行、列正弦波信号(即低、高群fL、fH)相加、混合成双音信号输出。 (5)附加功能单元,如有时含有单音抑制,输出控制(禁止)、双键同按无输出等控制电路。 DTMF发送器按输入控制方式可分为键盘行列控制和BCD接口控制两种。它们的控制部分真值表分别示于表5-2、表5-3。 表5-2键盘控制接口功能真值表 输入 行 列 R1 R2 R3 R4 C1 C2 C3 C4 发送 fL(HZ) 697 770 852 941 频率 fH(HZ) 1209 1336 1477 1633 表5-3 BCD码控制接口功能真值表 BCD 码 输 入 发 送 频 率 R1 R2 R3 R4 fL(HZ) fH(HZ) 0 0 0 0 941 1336 0 0 0 1 697 1209 0 0 1 0 697 1336 0 0 1 1 697 1477 0 1 0 0 770 1209 0 1 0 1 770 1336 0 1 1 0 770 1477 0 1 1 1 852 1209 1 0 0 0 852 1336 1 0 0 1 852 1477 (二)双音多频接收电路 图5-2 典型DTMF接收器原理框图 DTMF接收器包括DTMF分组滤波器和DTMF译码器,其基本原理如图5-2所示。DTMF接收器先经高、低群带通滤器进行fL/fH区分,然后过零检测、比较,得到相应于DTMF的两路fL、fH信号输出。该两路信号经译码、锁存、缓冲,恢复成对应于16种DTMF信号音的4比特二进制码(D1~D4)。 图5-3 MT8870芯片及管脚排列图 在本实验系统电路中,DTMF接收器采用的是MT8870芯片。 图5-3是该芯片的管脚排列图。 1、该电路的基本特性 (1)提供DTMF信号分离滤波和译码功能,输出相应16种DTMF频率组合的4位并行二进制码。 (2)可外接3.579545MHz晶体,与内含振荡器产生基准频率信号。 (3)具有抑制拨号音和模拟信号输入增益可调的能力。 (4)二进制码为三态输出。 (5)提供基准电压(VDD\2)输出。 (6)电源 +5V (7)功耗 15mw (8)工艺 CMOS (9)封装 18引线双列直插 2、管脚简要说明 引出端符号说明 IN+,IN- 运放同、反相输入端,模拟信号或DTMF信号从此端输入。 FB 运放输出端,外接反馈电阻可调节输入放大器的增益。 VREF 基准电压输出。 IC 内部连接端,应接地。 OSC1,OSC0 振荡器输入、输出端,两端外接3.579545MHz晶体。 EN 数据输出允许端,若为高电平输入,即允许D01~D04输出, 若为低电平输入,则禁止D01~D04输出。 D01~D04 数据输出,它是相应于16种DTMF信号(高,低单音组合) 的4位二进制并行码,为三态缓冲输出。 CI\GT 控制输入,若此输入电压高于门限值VTSt,则电路将接收 DTMF单音对,并锁存相应码字于输出,若输入电压低于VTSt,则电路不接收新的单音对。 EC0 初始控制输出,若电路检测出一可识别的单音对,则此端即变为高电平,若无输入信号或连续失真,则EC0返回低电平。 CID 延迟控制输出,当一有效单音对被接收,CI超过VTSt,输出锁存器被更新,则CID为高电平,若CI低于VTSt,则CID返至低电平。 VDD 接正电源,通常接+5V。 VSS 接负电源,通常接地。 3、电路的基本工作原理 它完成典型DTMF接收器的主要功能:输入信号的高,低频组带通滤波、限幅、频率检测与确认、译码、锁存与缓冲输出及振荡,监测等,具体说来,就是DTMF信号从芯片的输入端输入,经过输入运放和拨号音抑制滤波器进行滤波后,分两路分别进入高,低频组滤波器以分离检测出高、低频组信号。 如果高,低频组信号同时被检测出来,便在EC0输出高电平作为有效检测DTMF信号的标志;如果DTMF信号消失,则EC0即返至低电平,与此同时,EC0通过外接R向C充电,得到CI,GT。(通常此两端相短接)积分波形,如图5-4所示,若经tGTP延时后,CI,GT。电压高于门限值VTst时,产生内部标志,这样,该电路在出现EC0标志时,将证实后的两单音送往译码器,变成4比特码字并送到输出锁存器,而CI标志出现时,则该码字送到三态输出端D01——D04,另外,CI信号经形成和延时,从CID端输出,提供一选通脉冲,表明该码字已被接收和输出已被更新,如若积分电压降到门限VTst以下,使CID也回到低电平。 图5-4是它的工作时序波形图 图5-4 MT8870的时序图 图5-7 DTMF信号测电路原理框图 其中,双音多频信号测试点为TPDTMF,数据输出允许端EN的测量点为TPSTD,它经反相器反向后得到。数据输出则可以通过发光二极管D103~D100显示出来,它代表的数是8421码。 五、实验内容 1、用示波器观察并测量发送DTMF信号的波形,在用户线接口电路的输入端进行测量,即在用户1用户线接口电路的测量点TP1A与TP1B进行测量。 2、用示波器观察并测量DTMF信号接收的波形TPDTMF,以及在MT8870电路输出端TPSTD。 其中,TPDTMF为双音多频信号的测量点 TPSTD为数据输出允许端EN的反相测量点,识别到双音多频信号时为低,否则就为高。 六、实验步骤 1. 接上交流电源线。 2. 将K11~K14,K21~K24,K31~K34,K41~K44接2,3脚;K71~K75接2,3脚;K61~K63接2,3脚,K70、K60接1、2脚。 3. 先打开“交流开关”,指示发光二极管亮后,再分别按下直流输出开关J8、J9,此时实验箱上的五组电源已供电,各自发光二极管亮。 4. 按“复位”键进行一次上电复位,此时,CPU已对系统进行初始化处理,显示电路循环显示“P”,即可进行实验。 5. 用户1、用户3接上电话单机。 6. 用户1摘机,开始拨打号码,即按电话单机上的任意键,用示波器的直流档对以下测量点进行观察并记录波形: 1) TPDTMF:当有键按下时有双音多频信号,无键按下时无信号。 2) TPSDT:当有键按下时该点是低电平,无键按下时该点为高电平。 3) TP11:当有键按下时有双音多频信号,无键按下时无信号。 7. 按不同的键时,其双音多频信号的波形不一样,要仔细观察。 8. 在按键过程中观察发光二极管D103~D100与所按键值的关系:(显示二极管是在该按键抬起的瞬间发生改变的) D103~D100对应的是8421码,如接下的键值为5时,对应的码字为0101,发光二极管D102,D100发光。在按键的过程中观察所按键值与发光二极管是否满足上述对应关系。 七、注意事项 1、使主机实验箱加电处于正常工作状态,并严格遵循操作规程。 2、在测量观察上述各测量点波形时,两位同学一定要配合好,即一位同学按照正常拨打电话的顺序进行操作,另一位同学要找到相应的测量点和有关电路单元,小心慎重操作,仔细体会实验过程中的各种实验现象。 3、在测量TP1A时,示波器接头的另一接地线接到TP1B上。 八、实验报告要求 1、画出DTMF接收电路的电原理图,并能简要分析工作过程。 2、画出在接收DTMF过程中各有关测量点在有、无信号状态的波形,并能作简要的分析与说明。 实验六 空分交换网络原理 系统实验 一、实验目的 1、掌握程控交换的基本原理与实现方法。 2、通过对MT8816芯片的实验,熟悉空分交换网络的工作过程。 二、预习要求 认真预习《程控交换原理》教材中的相关内容。 三、实验仪器仪表 1、主机实验箱 一台 2、电话单机 二~四台 3、20MHz示波器 一台 四、实验电路工作过程 (一)原理说明 其实,我们在实验一中已经对实验系统中的交换网络有了一些了解,下面我们则比较详细分析它的工作过程。它是由两大部分组成,即话路部分和控制部分,话路部分包括交换网络,用户电路出中继电路,入中继电路,收号器,音信号发生器以及信号设备等;控制部分则是一台电子计算机,它包括中央处理器,存储器和输入、输出设备。 在我们本实验系统中,交换网络的方框图见图6-1所示。 图6-1 实验系统的交换网络结构方框图 (二)电子接线器简介 早先的程控空分交换机的网络,采用的接线器是机械的,也就是说它由机械接点组成的。然后由这些机械接线器组成交换网络。这些机械接线器包括小型纵横接线器、螺簧接线器、剩簧接线器、笛簧接线器……五花八门,品种繁多。由于目前已不采用,所以不在这里介绍。当前的空分交换机采用的是电子接线器。这是从MOS型超大规模接线器。目前,生产电子接线器的电子化成为可能。电子接线器就是MOS型的空分接线器。目前,生产电子接线器的厂家很多,型号也各有不同,如Mitel公司的MT8804,MT8812,MT8816等,MOTOROLA公司的142100,145100等,SGS公司的M089,M099,M093等。这些电子接线器在我国生产和引进的空分用户交换机中均能见到。 下面将重点分析MT8816芯片的工作过程。 (1)MT8816基本特性 由图6-2可见,该芯片是8×16模拟开关阵列,它内含7–––128线地址译码器,控制锁存器和8×16交叉点开关阵列,其电路的基本特性为: 1、提供8×16模拟开关阵列功能 2、导通电阻(VDD=12V) 45Ω 3、导通电阻偏差(VDD=12V) 5Ω 4、模拟信号最大幅度 12VPP 5、开关带宽 45MHZ 6、非线性失真 0.01% 7、电源 4.5~13.2V 8、工艺 CMOS 9、封装 双列直插式 (a) MT8816管脚排列图 VCC VEE VSS COL0 COL7 (b) MT8816功能方框图 图6-2 MT8816功能方框图 (2)MT8816管脚说明 下面将对该管脚功能作一简要说明 COL0~COL7 列输入\输出,开关阵列8路列输入或输出。 ROW0~ROW15 行输入\输出,开关阵列16路行输入或输出。 ACOL0~ACOL2 列地址码输入,对开关阵列进行列寻址。 AROW0~AROW3 行地址码输入,对开关阵行进行行寻址。 ST 选通脉冲输入,高电平有效,使地址码与数据得以控制相应开关的通、断。在ST上升沿前,地址必须进入稳定态,在ST下降沿处,数据也应该是稳定的。 DI 数据输入,若DI为低电平,不管CS处于什么电平,均将全部开关置于截止状态。 RESET 复位信号输入,若为高电平,不管CS处于什么电平,均将全部开关置于截止状态。 CS 片选信号输入,高电平有效。 VDD 正电源,电压范围为4.5~13.2V。 VEE 负电源。 VSS 数字地。 (3)MT8816工作原理 下面我们将对MT8816型电子接线器作一介绍,使大家了解电子接线器的结构原理。其它型号的电子接线器也大同小异。 MT8816是CMOS大规模集成电路芯片。这是一片8×16模拟交换矩阵,如图6-3所示 COL7 COL6 COL5 COL4 COL3 COL2 COL1 COL0 图6-3 MT8816交换矩阵示意图 图中有8条COL线(L0—L7)和16条ROW线(J1~J15),形成一个模拟交换矩阵。它们可以通过任意一个交叉点接通。芯片有保持电路,因此可以保持任一叉接点处于接通状态,直至来复信号为止。CPU可以通过地址线ACOL2 ~ACOL0和数据线AROW3~AROW0进行控制和选择需要接通的交叉点号。ACOL2 ~ACOL0管COL7 ~COL0中的一条线。ACOL7 ~ACOL0编成二进制码,经过译码以后就可以接通交叉点相应的COLi;数据线AROW3~AROW0管ROW15~ROW0中的一条。AROW3~AROW0是不编码的,某一条AROW7线为“1”,控制相应ROWi的以接通有关的交叉点。例如要接通L1和J8之间的交叉点。这时一方面向ACOL0 ~ACOL2。送001,另一方面向AROW3送“1”。当送出地址启动门ST时,就可以将相应交叉点接通了,图中还有一个端子叫“CS”片选端。当CS为“1”时,全部交叉点就打开了。 电子接线器速度快,驱动要求低,并能自己保持。因此使用起来十分方便。 其它型号的芯片其基本原理也大致相同。区别只是容量不一样。 电子接线器的优点是体积小,价格便宜,它的缺点是导通电阻较机械接点大(一般几十欧姆到一百欧姆),并且串音衰耗也较机电的接线器小,因此电子接线器组成的交换网络和由机械接点组成的交换网络也有所区别。 五、实验内容 利用空分自动交换网络进行两部电话单机通话,对工作过程作记录。 六、实验步骤 1. 接上交流电源线。 2. 将K11~K14,K21~K24,K31~K34,K41~K44接2,3脚;K70~K75接2,3脚;K60~K63接2,3脚。 3. 先打开“交流开关”,指示发光二极管亮后,再分别按下直流输出开关J8、J9,此时实验箱上的五组电源已供电,各自发光二极管亮。 4. 按“复位”键进行一次上电复位,此时,CPU已对系统进行初始化处理,显示电路循环显示“P”,即可进行实验。 5. 将四个用户接上电话单机。 6. 首先用户1呼叫用户3,并进行通话,然后用户2呼叫用户4通话。 7. 用双踪示波器观察 1) 当用户1说话时 (或按电话上的任意键),TP11(用户1的去话)、TP32(用户3的来话)有语音波形(或双音多频信号),且波形一致,只是TP11的幅值比TP32的幅值大;不说话时无波形。 2) 当用户3说话时(或按电话上的任意键),TP31(用户3的去话)、TP12(用户1的来话)有语音波形(或双音多频信号),且波形一致,只是TP31的幅值比TP12的幅值大;不说话时无波形。 3) 当用户2说话时(或按电话上的任意键),TP21(用户2的去话)、TP42(用户4的来话)有语音波形(或双音多频信号),且波形一致,只是TP21的幅值比TP42的幅值大;不说话时无波形。 4) 当用户4说话时(或按电话上的任意键),TP41(用户4的去话)、TP22(用户2的来话)有语音波形(或双音多频信号),且波形一致,只是TP41的幅值比TP22的幅值大;不说话时无波形。 七、实验报告要求 1、画出本实验系统自动交换网络的电路框图,并分析工作过程。 实验七 程控交换原理编程调试实验 一、实验目的 1、了解CPU的工作原理及各种控制过程。 2、体会程控交换原理实验系统进行电话通信时的控制过程。 二、预习要求 1、熟练使用8051系列单片机仿真器。 2、预习《MCS-51单片机原理与应用》。 三、实验设备 1、主机实验箱 一台 2、电话单机 四台 3、PC机 一台 4、MCS-51系统单片机仿真器 一套 四、实验编程 本实验分为七个单元实验,每个实验单元完成对一个单元电路的控制或一种系统设置。图7-1为本实验总体框图。 图7-1 实验总体框图 在本次实验中,我们通过实际编程调试,实现程控交换机中CPU对话路设备的控制,进一步加深对程控交换网络工作原理的认识。在实验四中我们已经了解到实验系统中已由硬件产生了各种信号音,在电话拨打和接续过程中,CPU自动将各种信号音按照电话接续规则接入电话机,使我们能自如地拨打电话,各种信号音都是通过可由计算机控制的开关接入电话线路的,CPU根据电话接续规则,打开或关闭各种信号音的接入开关,使我们能从拨打电话的过程中听到各种信号音。 注意,系统定义:用户1系统定义为第1路; 用户2系统定义为第2路; 用户3系统定义为第3路; 用户4系统定义为第4路; 下面我们按图7-1将实验系统通过MCS-51单片机仿真器连接到计算机,打开单片机仿真调试软件,编辑、修改、编译源程序,下载执行CPU控制指令,
1 甲醇测试仪总体方案设计 1.1 甲醇浓度检测仪设计要求分析 设计的甲醇浓度测试仪应具有如下特点: (1)数据采集系统以单片机为控制核心,外围电路带有LED显示以及键盘响应电路, 无需要其他计算机,用户就可以与之进行交互工作,完成数据的采集、存储、计算、分 析等过程。 (2)系统具有低功耗、小型化、高性价比等特点。 (3)从便携式的角度出发,系统成功使用了数码管显示器以及小键盘。由单片机系 统控制键盘和LED显示来实现人机交互操作,界面友好。 (4)软件设计简单易懂。 1.2 甲醇浓度检测仪设计方案 设计时,考虑甲醇浓度是由传感器把非电量转换为电量,传感器输出的是0- 5伏的电压值且电压值稳定,外部干扰小等。因此,可以直接把传感器输出电压值经过A /D转换器转换得到数据送入单片机进行处理。此外,还需接人LED显示,4*4键盘,报警 电路等。 其总体框图如图2.1所示。 图1.1 基本工作原理图 2 硬件设计 2.1 传感器的选择 本系统采用气敏传感器直接测量的是空气中的甲醇浓度。传感器只能采对甲醇气体敏 感,对其他气体不敏感,故选用MQ3型气敏传感器,这是考虑到周围空气中的气体成分可 能影响传感器测量的准确性。MQ3型气敏传感器由微型Al2O3,陶瓷管和测量电极、SnO2 敏感层和加热器构成的敏感元件固定在塑料或不锈钢的腔体内,加热器为气敏元件的工 作提供了必要的工作条件。MQ3型气敏传感器有很高的灵敏度、良好的选择性、长期的使 用寿命和可靠的稳定性。传感器的标准回路有加热回路和信号输出回路两部分组成,它 可以准确反映传感器表面电阻值的变化。传感器的表面电阻RS的变化,是通过与其串联 的负载电阻RL上的有效电压信号VRL输出面获得的。负载电阻RL可调为0.5- 200K。加热电压Uh为5v。上述这些参数使得传感器输出电压为0- 5V。MQ3型气敏传感器的结构和外形、标准回路、传感器阻值变化率与酒精浓度、外界温 度的关系图如图3- 3所示。一般在测量前需将传感器预热5分钟,这个样子是为了使测量的精度达到最高, 误差最小,需要找到合适的温度。 图2.1 MQ3 结构和外形 图2.2 MQ3 结构图 2.2 A/D转换电路 在单片机应用系统中,被测量对象的有关变化量,如温度、压力、流量、速度等非电 物理量,须经传感器转换成连续变化的模拟电信号(电压或电流),这些模拟电信号必 须转换成数字量后才能在单片机中用软件进行处理。实现模拟量转换成数字量的器件称 为A/D转换器(ADC)。 A/D转换器大致分有三类:一是双积分A/D转换器,优点是精度高,抗干扰性好,价 格便宜,但速度慢;二是逐次逼近型A/D转换器,精度、速度、价格适中;三是 - A/D转换器。 该设计中选用的是ADC0809属第二类,是8位A/D转换器。0809具有8路模拟信号输入端 口,地址线(23- 25脚)可决定那一路模拟信号进行A/D转换。22脚为地址锁存控制,当输入为高电平时, 对地址信号进行锁存。6脚为测试控制,当输入一个2μs的高电平脉冲时,就开始A/D转换 。7引脚为A/D转换结束标志,当A/D转换结束时,7脚输出高电平。9脚为A/D转换数据输 出允许端,当OE脚为高电平时,A/D转换数据输出。10脚为0809的时钟输入端。 2.2.1 ADC0809的引脚及功能 逐次比较型A/D转换器在精度、速度、和价格上都适中,是最常用的A/D转换器件。芯 片采用的是ADC0809,以下介绍ADC0809的引脚及功能。芯片如图3.4所示。 图2.3 ADC0809的引脚 ADC0809是一种逐次比较式8路模拟输入、8位数字量输出的A/D转换器。由图可见, ADC0809共有28个引脚,采用双列直插式封装。主要引脚功能如下: IN0-IN7是8路模拟信号输入端。 D0-D7是8位数字量输入端。 A、B、C与ALE控制8路模拟通道的切换,A、B、C分别与3根地址线或数据线相连,3位编 码对应8个通道地址端口。 需要注意的是:ADC0809虽然有8路模拟通道可以同时输入8路模拟信号,但每个瞬间 只能换1路,共用一个A/D转换器进行转换,各路之间的切换由软件改变C、A、B引脚上的 代码来实现。地址锁存与译码电路完成对 A、B、C 3个地址位进行锁存和译码,其译码输出用于通道选择,其转换结果通过三态输出锁存器 存放、输出,因此可以直接与系统数据总线相连,图2.4为通道选择表。 图2.4 通道选择表 OE、START、CLK为控制信号端,OE为输出允许端,START为启动信号输入端,CLK为时钟 信号输入端。 VR(+)和VR(-)为参考电压输入端。 2.2.2 ADC0809的结构及转换原理 ADC0809的结构框图如图3- 6。ADC0809采用逐次比较的方法完成A/D转换的,由
1 甲醇测试仪总体方案设计 1.1 甲醇浓度检测仪设计要求分析 设计的甲醇浓度测试仪应具有如下特点: (1)数据采集系统以单片机为控制核心,外围电路带有LED显示以及键盘响应电路, 无需要其他计算机,用户就可以与之进行交互工作,完成数据的采集、存储、计算、分 析等过程。 (2)系统具有低功耗、小型化、高性价比等特点。 (3)从便携式的角度出发,系统成功使用了数码管显示器以及小键盘。由单片机系 统控制键盘和LED显示来实现人机交互操作,界面友好。 (4)软件设计简单易懂。 1.2 甲醇浓度检测仪设计方案 设计时,考虑甲醇浓度是由传感器把非电量转换为电量,传感器输出的是0- 5伏的电压值且电压值稳定,外部干扰小等。因此,可以直接把传感器输出电压值经过A /D转换器转换得到数据送入单片机进行处理。此外,还需接人LED显示,4*4键盘,报警 电路等。 其总体框图如图2.1所示。 图1.1 基本工作原理图 2 硬件设计 2.1 传感器的选择 本系统采用气敏传感器直接测量的是空气中的甲醇浓度。传感器只能采对甲醇气体敏 感,对其他气体不敏感,故选用MQ3型气敏传感器,这是考虑到周围空气中的气体成分可 能影响传感器测量的准确性。MQ3型气敏传感器由微型Al2O3,陶瓷管和测量电极、SnO2 敏感层和加热器构成的敏感元件固定在塑料或不锈钢的腔体内,加热器为气敏元件的工 作提供了必要的工作条件。MQ3型气敏传感器有很高的灵敏度、良好的选择性、长期的使 用寿命和可靠的稳定性。传感器的标准回路有加热回路和信号输出回路两部分组成,它 可以准确反映传感器表面电阻值的变化。传感器的表面电阻RS的变化,是通过与其串联 的负载电阻RL上的有效电压信号VRL输出面获得的。负载电阻RL可调为0.5- 200K。加热电压Uh为5v。上述这些参数使得传感器输出电压为0- 5V。MQ3型气敏传感器的结构和外形、标准回路、传感器阻值变化率与酒精浓度、外界温 度的关系图如图3- 3所示。一般在测量前需将传感器预热5分钟,这个样子是为了使测量的精度达到最高, 误差最小,需要找到合适的温度。 图2.1 MQ3 结构和外形 图2.2 MQ3 结构图 2.2 A/D转换电路 在单片机应用系统中,被测量对象的有关变化量,如温度、压力、流量、速度等非电 物理量,须经传感器转换成连续变化的模拟电信号(电压或电流),这些模拟电信号必 须转换成数字量后才能在单片机中用软件进行处理。实现模拟量转换成数字量的器件称 为A/D转换器(ADC)。 A/D转换器大致分有三类:一是双积分A/D转换器,优点是精度高,抗干扰性好,价 格便宜,但速度慢;二是逐次逼近型A/D转换器,精度、速度、价格适中;三是 - A/D转换器。 该设计中选用的是ADC0809属第二类,是8位A/D转换器。0809具有8路模拟信号输入端 口,地址线(23- 25脚)可决定那一路模拟信号进行A/D转换。22脚为地址锁存控制,当输入为高电平时, 对地址信号进行锁存。6脚为测试控制,当输入一个2μs的高电平脉冲时,就开始A/D转换 。7引脚为A/D转换结束标志,当A/D转换结束时,7脚输出高电平。9脚为A/D转换数据输 出允许端,当OE脚为高电平时,A/D转换数据输出。10脚为0809的时钟输入端。 2.2.1 ADC0809的引脚及功能 逐次比较型A/D转换器在精度、速度、和价格上都适中,是最常用的A/D转换器件。芯 片采用的是ADC0809,以下介绍ADC0809的引脚及功能。芯片如图3.4所示。 图2.3 ADC0809的引脚 ADC0809是一种逐次比较式8路模拟输入、8位数字量输出的A/D转换器。由图可见, ADC0809共有28个引脚,采用双列直插式封装。主要引脚功能如下: IN0-IN7是8路模拟信号输入端。 D0-D7是8位数字量输入端。 A、B、C与ALE控制8路模拟通道的切换,A、B、C分别与3根地址线或数据线相连,3位编 码对应8个通道地址端口。 需要注意的是:ADC0809虽然有8路模拟通道可以同时输入8路模拟信号,但每个瞬间 只能换1路,共用一个A/D转换器进行转换,各路之间的切换由软件改变C、A、B引脚上的 代码来实现。地址锁存与译码电路完成对 A、B、C 3个地址位进行锁存和译码,其译码输出用于通道选择,其转换结果通过三态输出锁存器 存放、输出,因此可以直接与系统数据总线相连,图2.4为通道选择表。 图2.4 通道选择表 OE、START、CLK为控制信号端,OE为输出允许端,START为启动信号输入端,CLK为时钟 信号输入端。 VR(+)和VR(-)为参考电压输入端。 2.2.2 ADC0809的结构及转换原理 ADC0809的结构框图如图3- 6。ADC0809采用逐次比较的方法完成A/D转换的,由

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值