卡联系人IccProvider

6572_Telephony 6572_Telephony   1 术语和前言: 4 2 TelePhony: 4 2.1 Telephony框架 4 2.2 Phone介绍 6 2.3 Message 9 2.4 Call 9 2.5 Network States 9 2.6 Data Connection 10 2.7 Simcard 12 2.8 PhoneNotifier 12 2.9 Te 阅读详情

IccProvider概述

读取卡联系人的provider定义于:

/packages/services/Telephony/AndroidManifest.xml

            <provider android:name="IccProvider"
                      android:authorities="icc"
                      android:multiprocess="true"
                      android:exported="true"
                      android:readPermission="android.permission.READ_CONTACTS"
                      android:writePermission="android.permission.WRITE_CONTACTS" />
packages/services/Telephony/src/com/android/phone/IccProvider.java

public class IccProvider extends com.android.internal.telephony.IccProvider {
    public IccProvider() {
        super();
    }
}
Telephony目录下的IccProvider其实是空的,实现全部在framework的同名文件中

frameworks/opt/telephony/src/java/com/android/internal/telephony/IccProvider.java

public class IccProvider extends ContentProvider {

IccProvider读取卡联系人流程

    static {
        URL_MATCHER.addURI("icc", "adn", ADN);
        URL_MATCHER.addURI("icc", "adn/subId/#", ADN_SUB);
        ...
    }
static块中加入了adn的uri,adn/subid/#可以指定读取的sim卡
  public Cursor query(Uri url, String[] projection, String selection,
            String[] selectionArgs, String sort) {
        ...
        switch (URL_MATCHER.match(url)) {
          case ADN:
              return loadFromEf(IccConstants.EF_ADN, mSubscriptionManager.getDefaultSubId());
        ...
  }
query方法,调用loadFromEf
  private MatrixCursor loadFromEf(int efType, int subId) {

        List<AdnRecord> adnRecords = null;
        try {
            IIccPhoneBook iccIpb = getIccPhbService();
            if (iccIpb != null) {
                adnRecords = iccIpb.getAdnRecordsInEfForSubscriber(subId, efType);
            }
        } catch (RemoteException ex) {
            log(ex.toString());
        } catch (SecurityException ex) {
            log(ex.toString());
        }

        if (adnRecords != null) {
            // Load the results
            final int size = adnRecords.size();
            final MatrixCursor cursor = new MatrixCursor(ADDRESS_BOOK_COLUMN_NAMES, size);
            if (DBG) {
                log("adnRecords.size=" + size);
            }
            for (int i = 0; i < size; i++) {
                loadRecord(adnRecords.get(i), cursor, i);
            }
            return cursor;
        }
        ...
  }
首先获取AdnRecord列表然后,然后loadRecord依据这个列表生成cursor返回。生成cursor的函数很简单,不做分析了。
    private IIccPhoneBook getIccPhbService() {
        IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
                ServiceManager.getService("simphonebook"));

        return iccIpb;
    }
读取的服务名称叫做simphonebook,该服务添加的代码在UiccPhoneBookController中:
frameworks/opt/telephony/src/java/com/android/internal/telephony/UiccPhoneBookController.java
    public UiccPhoneBookController(Phone[] phone) {
        if (ServiceManager.getService("simphonebook") == null) {
               ServiceManager.addService("simphonebook", this);
        }
        mPhone = phone;
    }
构造函数中添加了服务,UiccPhoneBookController实例是在phone进程启动就初始化的,phone进程又是常驻的,所以phone的相关服务基本等于是永远可用的。
    public List<AdnRecord> getAdnRecordsInEfForSubscriber(int subId, int efid)
           throws android.os.RemoteException {
        IccPhoneBookInterfaceManagerProxy iccPbkIntMgrProxy =
                             getIccPhoneBookInterfaceManagerProxy(subId);
        if (iccPbkIntMgrProxy != null) {
            return iccPbkIntMgrProxy.getAdnRecordsInEf(efid);
        } 
        ...
    }
然后调用IccPhoneBookInterfaceManagerProxy的getAdnRecordsInEf
  private IccPhoneBookInterfaceManagerProxy
            getIccPhoneBookInterfaceManagerProxy(int subId) {
        ...
        try {
            return ((PhoneProxy)mPhone[(int)phoneId]).getIccPhoneBookInterfaceManagerProxy();
        ...
  }
frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneProxy.java
    public IccPhoneBookInterfaceManagerProxy getIccPhoneBookInterfaceManagerProxy() {
        return mIccPhoneBookInterfaceManagerProxy;
    }
  public PhoneProxy(PhoneBase phone) {
        ...
        mIccPhoneBookInterfaceManagerProxy = new IccPhoneBookInterfaceManagerProxy(
                phone.getIccPhoneBookInterfaceManager());
        ...
  }
IccPhoneBookInterfaceManagerProxy是在PhoneProxy构造函数中初始化的。
frameworks/opt/telephony/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java
   public List<AdnRecord> getAdnRecordsInEf(int efid) {
        return mIccPhoneBookInterfaceManager.getAdnRecordsInEf(efid);
    }
这里的mIccPhoneBookInterfaceManager就是PhoneProxy构造函数传递进去的phone.getIccPhoneBookInterfaceManager()
该对象实际是在Phone的构造函数中初始化的,拿GsmPhone举例
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java
            mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
SimPhoneBookInterfaceManager的基类是IccPhoneBookInterfaceManager
/home/lgy/code/mtk6797/frameworks/opt/telephony/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
    public synchronized List<AdnRecord> getAdnRecordsInEf(int efid) {
        ...
        synchronized (mLock) {
            checkThread();
            AtomicBoolean status = new AtomicBoolean(false);
            Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, status);
            if (mAdnCache != null) {
                mAdnCache.requestLoadAllAdnLike(efid, mAdnCache.extensionEfForEf(efid), response);
                waitForResult(status);
            }
            ...
        }
        return mRecords;
    }
frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
   public void
    requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
        ArrayList<Message> waiters;
        ArrayList<AdnRecord> result;
        if (efid == EF_PBR) {
            result = mUsimPhoneBookManager.loadEfFilesFromUsim();
        } else {
            result = getRecordsIfLoaded(efid); //该方法实际是从缓存读取数据
        }
        logd("requestLoadAllAdnLike result = null ?" + (result == null));

        // Have we already loaded this efid?
        if (result != null) {   //如果缓存已有数据,return
            if (response != null) {
                AsyncResult.forMessage(response).result = result;
                response.sendToTarget();
            }

            return;
        }

        // Have we already *started* loading this efid?

        waiters = mAdnLikeWaiters.get(efid);

        if (waiters != null) { //正在读取中,把回调消息加入等待队列中,return
            waiters.add(response);
            return;
        }

        waiters = new ArrayList<Message>();
        waiters.add(response);

        mAdnLikeWaiters.put(efid, waiters);
        ...

        new AdnRecordLoader(mFh).loadAllFromEF(efid, extensionEf,
            obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0));  //正真读取
    }
流程分析已经写在注释中,usim是另一条分支(本流程不做解析),继续看loadAllFromEF
frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
   public void
    loadAllFromEF(int ef, int extensionEF,
                Message response) {
          ...
          mFh.mCi.queryPhbStorageInfo(
               type,
               obtainMessage(EVENT_PHB_QUERY_STAUTS));
          ...
    }
调用ril的queryPhbStorageInfo向modem发送请求,读取结果会在handleMessage中处理
                case EVENT_PHB_QUERY_STAUTS:
                    /*
                     * response.obj.result[0] is number of current used entries
                     * response.obj.result[1] is number of total entries in the
                     * storage
                     */

                    ar = (AsyncResult) (msg.obj);
                    int[] info = (int[]) (ar.result);

                    if (ar.exception != null) {
                        throw new RuntimeException("PHB Query Info Error",
                                ar.exception);
                    }

                    type = getPhbStorageType(mEf);
                    readInfo = new int[3];
                    readInfo[0] = 1; // current_index;
                    readInfo[1] = info[0]; // # of remaining entries
                    readInfo[2] = info[1]; // # of total entries

                    mAdns = new ArrayList<AdnRecord>(readInfo[2]);
                    for (int i = 0; i < readInfo[2]; i++) {
                        // fillin empty entries to mAdns
                        adn = new AdnRecord(mEf, i + 1, "", "");
                        mAdns.add(i, adn);
                    }

                    readEntryFromModem(type, readInfo);
                    mPendingExtLoads = 1;
                    break;
获取到了卡联系人总数目,先用空值初始化mAdn列表,然后调用readEntryFromModem正真的读取数据
    private void readEntryFromModem(int type, int[] readInfo) {
        ...
        mFh.mCi.ReadPhbEntry(type, readInfo[0], eIndex,
                obtainMessage(EVENT_PHB_LOAD_ALL_DONE, readInfo));
    }
消息处理:
          case EVENT_PHB_LOAD_ALL_DONE:
                    ar = (AsyncResult) (msg.obj);
                    readInfo = (int[]) (ar.userObj);
                    entries = (PhbEntry[]) (ar.result);
                    ...

                    for (int i = 0; i < entries.length; i++) {
                        adn = getAdnRecordFromPhbEntry(entries[i]);
                        if (adn != null) {
                            mAdns.set(adn.mRecordNumber - 1, adn);
                            readInfo[1]--;
                            Rlog.d(LOG_TAG, "Read entries: " + adn);

                        }
                    }
                    ...
for循环中向mAdns添加数据。AdnRecordLoader会向AdnRecordCache发消息,EVENT_LOAD_ALL_ADN_LIKE_DONE消息处理:
            case EVENT_LOAD_ALL_ADN_LIKE_DONE:
                ...

                if (ar.exception == null) {
                    mAdnLikeFiles.put(efid, (ArrayList<AdnRecord>) ar.result);
                } else {
                    Rlog.d(LOG_TAG, "EVENT_LOAD_ALL_ADN_LIKE_DONE exception", ar.exception);
                }
                notifyWaiters(waiters, ar);
                ...
                break;
一路向上传递消息,这里的ar其实就包含了联系人数据列表ArrayList<AdnRecord>

回到IccPhoneBookInterfaceManager.java

                case EVENT_LOAD_DONE:
                    ar = (AsyncResult)msg.obj;
                    ...
                       mRecords = (List<AdnRecord>) ar.result;
                    ...
整个流程走完了。可以看出名称叫做IccProvider,其实没有建立任何数据库。第一次的查询是通过发送ril请求读取sim卡得到数据,后续用缓存返回数据。

Contacts读取Sim卡联系人的流程

分析的以mtk的代码为例,高通的代码和mtk差异很大,且不在Contacts目录下。是在vendor目录下,单独作为一个app。
/home/lgy/code/mtk6797/packages/apps/Contacts/AndroidManifest.xml
        <service android:name="com.mediatek.contacts.simservice.SIMProcessorService"/>
        <receiver android:name="com.mediatek.contacts.simcontact.BootCmpReceiver">
           <intent-filter>
                <action android:name="android.intent.action.PHB_STATE_CHANGED" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.USER_SWITCHED_FOR_MULTIUSER_APP"/>
                <action android:name="com.android.contacts.REFRESH_SIM_CONTACT"/>
           </intent-filter>
        </receiver>
packages/apps/Contacts/src/com/mediatek/contacts/simcontact/BootCmpReceiver.java
public void onReceive(Context context, Intent intent) {
    ...
    if (action.equals(TelephonyIntents.ACTION_PHB_STATE_CHANGED)) {
                processPhoneBookChanged(context, intent);
            }
    ...
}
收到TelephonyIntents.ACTION_PHB_STATE_CHANGED广播后,该广播表示卡联系人可用不可用,调用processPhoneBookChanged
 private void processPhoneBookChanged(Context context, Intent intent) {
        ...
        if (phbReady && subId > 0) {
            startSimService(context, subId, SIMServiceUtils.SERVICE_WORK_IMPORT);
        } else if (subId > 0 && !phbReady) {
            startSimService(context, subId, SIMServiceUtils.SERVICE_WORK_REMOVE);
        }
    }
广播处理有两个分支,一个是删除卡联系人,一个是导入卡联系人
packages/apps/Contacts/src/com/mediatek/contacts/simservice/SIMProcessorService.java
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "[onCreate]...");
        mProcessorManager = new SIMProcessorManager(this, mListener);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        processIntent(intent);
    }
 private void processIntent(Intent intent) {
        ...
        mProcessorManager.handleProcessor(getApplicationContext(), subId, workType, intent);
    }
一路调用到handleProcessor,注意mProcessorManager初始化的时候传入了接口的实现,这样mProcessorManager就可以通知SIMProcessorService工作开始或者完毕
private SIMProcessorManager.ProcessorManagerListener mListener 
packages/apps/Contacts/src/com/mediatek/contacts/simservice/SIMProcessorManager.java
    public void handleProcessor(Context context, int subId, int workType, Intent intent) {
        Log.i(TAG, "[handleProcessor] subId=" + subId + ",time=" + System.currentTimeMillis());
        SIMProcessorBase processor = createProcessor(context, subId, workType, intent);
        if (processor != null && mListener != null) {
            Log.d(TAG, "[handleProcessor]Add processor [subId=" + subId + "] to threadPool.");
            mListener.addProcessor(/* 1000 + slotId * 300 */0, processor);
        }
    }
    private SIMProcessorBase createProcessor(Context context, int subId, int workType,
            Intent intent, ProcessorCompleteListener listener) {
        ...
        if (workType == SIMServiceUtils.SERVICE_WORK_IMPORT) {
            processor = new SIMImportProcessor(context, subId, intent, listener);
        ...
    }
createProcessor生成了processor,然后调用mListener的方法,这个就是SIMProcessorService中实现的,addProcessor开始导入联系人的工作:
     @Override
        public void addProcessor(long scheduleTime, ProcessorBase processor) {
            if (processor != null) {
                try {
                    mExecutorService.execute(processor);
                } catch (RejectedExecutionException e) {
                    Log.e(TAG, "[addProcessor] RejectedExecutionException: " + e.toString());
                }
            }
        }
processor是继承自ProcessorBase。
packages/apps/ContactsCommon/src/com/android/contacts/common/vcard/ProcessorBase.java
public abstract class ProcessorBase implements RunnableFuture<Object> {
ProcessorBase实现了RunnableFuture,所以它可以放到线程池区运行。
packages/apps/Contacts/src/com/mediatek/contacts/simservice/SIMProcessorBase.java
   public void run() {
        try {
            doWork();
        } finally {
            mDone = true;
            if (mListener != null && !mCanceled) {
                mListener.onProcessorCompleted(mIntent);
            }
        }
    }
线程池是调用run方法开启工作的,run函数中调用doWork完成工作,用mListener接口通知SIMProcessorManager工作完毕
packages/apps/Contacts/src/com/mediatek/contacts/simservice/SIMImportProcessor.java
    @Override
    public void doWork() {
        ...
        SIMServiceUtils.deleteSimContact(mContext, mSubId);
        ...

        int simType = SimCardUtils.getSimTypeBySubId(mSubId);
        final Uri iccUri = SubInfoUtils.getIccProviderUri(mSubId);
        Cursor simCursor = querySimContact(mContext, mSubId, simType, iccUri);
        Log.i(TAG, "[dowork]simType = " + simType + ",simType =" + simType + ",mSubId = " + mSubId);
        importAllSimContacts(mContext, mSubId, simCursor, simType);
        if (simCursor != null) {
            simCursor.close();
        }
    }
首先删除所有数据库中的卡联系人,然后查询卡联系人,获取卡联系人数据后导入到ContactsProvider中。
 private Cursor querySimContact(Context context, int subId, int simType, Uri iccUri) {
        ...
        cursor = context.getContentResolver().query(iccUri, COLUMN_NAMES, null, null, null);
        ...
        return cursor;
    }
通过IccProvider查询卡联系人
   private void importAllSimContacts(Context context, final Cursor cursor,
            final ContentResolver resolver, int subId, int simType, HashSet<Long> insertSimIdSet,
            boolean importSdnContacts) {
       ...
       while (cursor.moveToNext()) {
                ...
                i = actuallyImportOneSimContact(context, cursor, resolver, subId, simType,
                        indexInSim, importSdnContacts, operationList, i, account, isUsim,
                        accountSubId, countryCode);
                ...
                if (i > MAX_OP_COUNT_IN_ONE_BATCH) {
                    ...
                    resolver.applyBatch(ContactsContract.AUTHORITY, operationList);
                    ...
                }
       }
       ...
    }
基本流程是依据cursor利用actuallyImportOneSimContact生成数据库插入的operationlist,然后在每大于90个operation就批量操作一次,循环上诉流程直到处理完毕。
doWork结束后会回调接口ProcessorCompleteListener,然后关闭线程池和关闭service,这个流程简单不再做分析。


SIM --- 联系人查询过程分析 6.3 联系人操作 在前面已经论述过, SIM中的联系人是保存在单独的数据库中,其对应的Provider为IccProvider,在packages\services\Telephony 路径下,也就是phone进程中。并且,增删改查直接看jar包中的IccProvider类就可以了, IccProvider路径如下, frameworks/opt/telephony/src/java/co 阅读详情

相关推荐

SAP FI 提示没有为会计年度0 定义版本2025 GP626

SAP 错误 GP626 是一个常见的系统消息,通常表示某个会计年度的版本(如版本 0)未被定义或未激活。:在执行某些事务(如创建采购订单、过账凭证或维护成本中心)时,系统会检查相关版本是否已定义。的“Settings for Each Fiscal Year”界面中,选择一个已激活的会计年度。的“Settings for Each Fiscal Year”界面中,找到未激活的会计年度。:在控制范围(Controlling Area)中,未为指定的会计年度定义版本 0。如果没有,添加或复制现有年度的设置。

GAVIN 2433

adn进程JAVA_ADN加载流程

ADN加载流程:IccProvider.javaIccProvider定义在/packages/services/Telephony/AndroidManifest.xmlandroid:authorities="icc"android:multiprocess="true"android:exported="true"android:readPermission="android.permiss...

weixin_31908827的博客 2513

从 ABAP 视角看 JSON :7.40 版本带来的那些改变

摘要:ABAP 对 JSON 的原生支持 随着企业系统与外部 API 的交互增多,ABAP 从 NetWeaver 7.40 开始原生支持 JSON 格式。通过复用现有 XML 基础设施,ABAP 团队引入 JSON-XML 概念,使用 sXML Library 和 IF_SXML=>CO_XT_JSON 常量处理 JSON 数据。开发人员可通过 CALL TRANSFORMATION 语句实现 ABAP 数据与 JSON 的双向转换,大幅简化了与外部系统的数据交互。这种标准化的 JSON 处理方式提

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 151

Android SIM联系人操作总结

--- by Ruiming.Lv 在Android中,对SIM中的联系人进行操作,需要通过系统提供的Content Provider进行,该Provider就是Telphony中的IccProvider。但是,由于SIM存储的一些特性,在操作上,与ContactsProvider有很多不一样。 1       IccProvider工程相关 IccProvider是Telep

maetelibom的博客 5186

Android知识梳理:System Service

Android系统中有很多内置的软件,例如,当手机接到来电时,会显示对方的电话号。也可以根据周围的环境将手机设置成震动或静音。如果想把这些功能加到自己的软件中应该怎么办呢?答案就是“系统服务”。在Android系统中提供了很多这种服务,通过这些服务,就可以像Android系统的内置软件一样随心所欲地控制Android系统了。系统服务(System Services)并非Android开发应用程序...

勤奋的大叔 500

Android9.0 sim读取联系人

转载: https://blog.csdn.net/firedancer0089/article/details/60762199 https://blog.csdn.net/mafei19870124/article/details/73521995 第一部分:读取联系人流程 static { URL_MATCHER.addURI("icc", "adn", ADN)...

yaoming168的专栏 3323

android读取sim联系人

SIM的provider是IccProvider。既然是provider,就和操作Contact的provider类似的,只是有一些微小差别。 IccProvider的Uri是content://icc/and,可以通过这个Uri来操作SIM。当然,使用时需要转换为android的Uri对象。android中对SIM操作的支持很简单,具体的查询、插入、更新和删除操作如下面的代码段

lksodit_yiyi的专栏 1万+

android操作sim联系人信息

SIM的provider是IccProvider。既然是provider,就和操作Contact的provider类似的,只是有一些微小差别。 IccProvider的Uri是content://icc/and,可以通过这个Uri来操作SIM。当然,使用时需要转换为android的Uri对象。android中对SIM操作的支持很简单,具体的查询、插入、更新和删除操作如下面的代码段

7020

Android4.X 应用层读取SIM联系人的过程

SIM里可以存储部分联系人数据 ,USIM也可以存储部分联系人数据,只不过USIM存储的联系人的数量更多,以及支持更多的联系人数据字段。   在Android 的应用层 是通过IccProvider 来读取SIM里的联系人的,主要有三个步骤   1.IccProvider 根据 EF_ID ,调用 IccPhoneBookInterfaceManager 的getAdnRecordsI

lsdmx2016的总结 6094

android负责sim的应用,android读取sim联系人

SIM的provider是IccProvider。既然是provider,就和操作Contact的provider类似的,只是有一些微小差别。IccProvider的Uri是content://icc/adn,可以通过这个Uri来操作SIM。当然,使用时需要转换为android的Uri对象。android中对SIM操作的支持很简单,具体的查询、插入、更新和删除操作如下面的代码段一、查询联系人查...

weixin_35733495的博客 531

如何获取sim联系人信息

public static final Uri ICC_CONTENT_URI = Uri.parse("content://icc/adn"); public static String _ID = BaseColumns._ID; public static String NAME = "name"; public static String NUMBER = "number"; Cursor cursor = null; try { cursor = context.getContentRes

wangjicong_215的博客 781

sim操作联系人

SIM的provider是IccProvider。既然是provider,就和操作Contact的provider类似的,只是有一些微小差别。 IccProvider的Uri是content://icc/adn,可以通过这个Uri来操作SIM。当然,使用时需要转换为android的Uri对象。android中对SIM操作的支持很简单,具体的查询、插入、更新和删除操作如下面的代码段 一、

安卓学习乐园 949

linux系统读sim信息,Android 1.6 中读写sim

Android 1.6 的文档中未提到怎么样操作SIM,经过google和自己的一些尝试,已经可以成功操作SIM了,记录下来,以便翻阅。android 1.6中,SIM的provider是IccProvider。既然是provider,就和操作Contact的provider类似的,只是有一些微小差别。IccProvider的Uri是content://icc/and,可以通过这个Uri来操作...

weixin_30739835的博客 351

UICC读取联系人

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34167819的博客 252

android 现场写SIM,android 1.6 中读写sim

android 1.6 的文档中未提到怎么样操作SIM,经过google和自己的一些尝试,已经可以成功操作SIM了,记录下来,以便翻阅。android 1.6中,SIM的provider是IccProvider。既然是provider,就和操作Contact的provider类似的,只是有一些微小差别。IccProvider的Uri是content://icc/and,可以通过这个Uri来操作...

weixin_35949857的博客 794

读取SIM联系人流程

本文主要讲USIM插入手机后读取中contacts信息的流程。先介绍几个类的作用: IccProvider:对外暴露的接口,我们通过它来查询SIM中的contacts信息 IccPhoneBookInterfaceManager: PhoneBook操作的服务 IccPhoneBookInterfaceManagerProxy:对外的PhoneBook操作服务, 上面类的代理. IccReco

Yigit_dev的博客 9710

查询SIM联系人——源码流程简介

查询SIM联系人的流程,涉及AIDL跨进程通信与代理模式

Mupceet 1933

模电大作业3 multisim14.0工程文件

用一片通用 4 运放芯片 LM324 组成电路,实现下述功能。使用低频信号源产生 , 的正弦波信号,加至加法器的输入端, 加法器的另一输入端加入由自制三角波产生器输出的uO1 要求 ,允许T1有±5%的误差。要求加法器的输出电压u u u i2 i1 O1 =10  , i2 u 经滤波器滤除uO1的频率分量,选出 信号为 O2 u ,要求得到峰峰值不小于 9V 的正弦信号,u f t i1 0  0.1sin 2 V    0f  500Hz1 T  0.5ms0f用示波器观察无明显失真。该信号再经比较器后在 1kΩ负载上得到峰峰值为 2V的输出电压 O3 u

上一篇: android SubscriptionInfo更新流程
下一篇: System.currentTimeMillis()和SystemClock.elapsedRealtime()
李光宇
博客等级 码龄17年 69粉丝 101原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值