精华内容
参与话题
问答
  • Activity启动流程总结

    2019-11-07 20:03:06
    学习插件化首先要清楚Activity启动流程。此篇文章对Activity整体的启动流程做一个总结。SDK版本基于25,项目的插件化适配也只做了API 25的。 通常使用startActivity来启动一个Activity @Override public void start...

    学习插件化首先要清楚Activity启动流程。此篇文章对Activity整体的启动流程做一个总结。SDK版本基于25,项目的插件化适配也只做了API 25的。

    通常使用startActivity来启动一个Activity

    @Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }
    

    继续跟进

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            startActivityForResult(intent, -1);
        }
    } 
    
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
        startActivityForResult(intent, requestCode, null);
    } 
    

    最终来到了startActivityForResult

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
                @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                mStartedActivity = true;
            }
            cancelInputsAndStartExitTransition(options);
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    } 
    

    其中有个很关键的调用

    mInstrumentation.execStartActivity()
    

    可以看到启动交给了Instrumentation类,这个类也是插件化的一个hook点。

    跟进execStartActivity方法

    public ActivityResult execStartActivity(
                Context who, IBinder contextThread, IBinder token, Activity target,
                Intent intent, int requestCode, Bundle options) {
        ...
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
        ...
    } 
    

    对于这个方法的几个参数:

    • who:启动Activity的上下文
    • contextThread:启动Activity的上下文线程
    • token:正在启动Activity的标识
    • target:启动该Activity的Activity(回调Activity)

    下面调用了checkStartActivityResult方法

    这个方法是干什么的呢

    public static void checkStartActivityResult(int res, Object intent) {
        if (res >= ActivityManager.START_SUCCESS) {
            return;
        }
        switch (res) {
            case ActivityManager.START_INTENT_NOT_RESOLVED:
            case ActivityManager.START_CLASS_NOT_FOUND:
                if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
                    throw new ActivityNotFoundException(
                            "Unable to find explicit activity class "
                            + ((Intent)intent).getComponent().toShortString()
                            + "; have you declared this activity in your AndroidManifest.xml?");
                throw new ActivityNotFoundException(
                        "No Activity found to handle " + intent);
            case ActivityManager.START_PERMISSION_DENIED:
                throw new SecurityException("Not allowed to start activity "
                        + intent);
            case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
                throw new AndroidRuntimeException(
                        "FORWARD_RESULT_FLAG used while also requesting a result");
            case ActivityManager.START_NOT_ACTIVITY:
                throw new IllegalArgumentException(
                        "PendingIntent is not an activity");
            case ActivityManager.START_NOT_VOICE_COMPATIBLE:
                throw new SecurityException(
                        "Starting under voice control not allowed for: " + intent);
            case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:
                throw new IllegalStateException(
                        "Session calling startVoiceActivity does not match active session");
            case ActivityManager.START_VOICE_HIDDEN_SESSION:
                throw new IllegalStateException(
                        "Cannot start voice activity on a hidden session");
            case ActivityManager.START_CANCELED:
                throw new AndroidRuntimeException("Activity could not be started for "
                        + intent);
            default:
                throw new AndroidRuntimeException("Unknown error code "
                        + res + " when starting " + intent);
        }
    } 
    

    很明显,就是根据得到的返回码去做一些合法性的判断。

    这个是插件化需要欺骗过的方法。

    继续看我们的execStartActivity,它调用了ActivityManagerNative.getDefault()。

    static public IActivityManager getDefault() {
        return gDefault.get();
    }
    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            ...
            IActivityManager am = asInterface(b);
            ...
            return am;
        }
    };
    static public IActivityManager asInterface(IBinder obj) {
        ...
        return new ActivityManagerProxy(obj);
    } 
    

    它通过gDefault得到了一个IActivityManager实例ActivityManagerProxy。gDefault是Singleton类型,这里可以理解为ActivityManagerProxy是一个单例。

    所以Activity的启动又交给了ActivityManagerProxy。

    看一下他的startActivity方法。

    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
                String resolvedType, IBinder resultTo, String resultWho, int requestCode,
                int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        ...
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        ...
        reply.recycle();
        data.recycle();
        return result;
    } 
    

    这里如果熟悉Binder的话就知道transact方法。

    transact的作用是通过Binder远程调用服务端的方法。

    对于参数简单说一下,详细情况可我看我的AIDL和Binder的博客。

    transact的参数

    • 第一个参数相当于一个code——表明要调用的方法
    • 第二个参数是我们要传递的参数
    • 第三个是返回值

    mRemote.transact()实际上就是调用了AMS的startActivity方法。

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,resulUserHandle.getCallingUserId());
    } 
    

    关于他的参数

    • caller——就是我们的whoThread
    • callingPackage——包名
    • intent——意图
    • resolvedType——注册文件注册时的MIME类型
    • resultTo——token
    • resultWho——target的id
    • requestCode——请求码
    • startFlags——启动标志
    • profilerInfo——默认null
    • options——。。。

    AMS中的startActivity方法直接调用了startActivityAsUser方法。

    @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
        enforceNotIsolatedCaller("startActivity");
        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),userId, false, ALLOW_FULL_ONLY, "startActivity", null);
        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,profilerInfo, null, null, bOptions, false, userId, null, null);
    } 
    

    其中enforceNotlsolatedCaller方法是检查是否属于被隔离对象

    mUserController.handleIncomingUser检查操作权限然后返回一个用户id

    startActivityAsUser方法大概就是检测下权限。

    然后返回由mActivityStarter 调用的startActivityMayWait方法。

    ActivityStarter:可以说关于Activity启动的所有信息都在这了,然后根据信息把Activity具体分配到哪个任务栈

    继续跟进:

    final int startActivityMayWait(IApplicationThread caller, int callingUid,
                String callingPackage, Intent intent, String resolvedType,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                IBinder resultTo, String resultWho, int requestCode, int startFlags,
                ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult, Configuration config,
                Bundle bOptions, boolean ignoreTargetSecurity, int userId,
                IActivityContainer iContainer, TaskRecord inTask) {
        ...
        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
        ...
        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
        ...
        final ProcessRecord heavy = mService.mHeavyWeightProcess;
        if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
                || !heavy.processName.equals(aInfo.processName))) {
            ...
        }
        ...
        int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
                aInfo, rInfo, voiceSession, voiceInteractor,
                resultTo, resultWho, requestCode, callingPid,
                callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                options, ignoreTargetSecurity, componentSpecified, outRecord, container,
                inTask);
        ...
    } 
    

    mSupervisor(ActivityStackSupervisor)是用来管理ActivityStack的。

    resolveIntent和resolveActivity方法用来确定此次启动activity的信息。

    关于heavy(ResolveInfo)涉及到重量级进程(SystemServer, MediaServer,ServiceManager),如果当前系统中已经存在的重量级进程且不是即将要启动的这个,那么就要给Intent赋值。

    startActivityLocked方法

    final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
                String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
                String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
                ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
                ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
                TaskRecord inTask) {
        ...
        ProcessRecord callerApp = null;
        ...
        ActivityRecord sourceRecord = null;
        ActivityRecord resultRecord = null;
        ...
        ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
                requestCode, componentSpecified, voiceSession != null, mSupervisor, container,
                options, sourceRecord);
        ...
        err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true, options, inTask);
        ...
        return err;
    }
    

    callerApp、sourceRecord、resultRecord都为r服务,让记录着整个方法的各种结果,然后带着r调用了startActivityUnchecked方法

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
        voiceInteractor);
        computeLaunchingTaskFlags();
        ...
        mIntent.setFlags(mLaunchFlags);
        mReusedActivity = getReusableIntentActivity();
        ...
        boolean newTask = false;
        ...
         if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
            && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            newTask = true;
            ...
        }
        ...
        mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
        ...
        mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                            mOptions);
        ...
    } 
    

    先看setInitialState方法。

    private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
                boolean doResume, int startFlags, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
        reset();
        mStartActivity = r;
        mIntent = r.intent;
        mOptions = options;
        mCallingUid = r.launchedFromUid;
        mSourceRecord = sourceRecord;
        mVoiceSession = voiceSession;
        ...
        mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
        mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;
        mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;
        mLaunchFlags = adjustLaunchFlagsToDocumentMode(
                r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
         ...
    } 
    

    这里面初始了比较重要的mStartActivity,mIntent,mCallingUid,
    mSourceRecord,mLaunchFlags。

    mLaunchFlags用来记录我们Activity的启动方式,省略的部分都是根据启动方式来初始化一堆变量或进行操作。

    下面的方法computeLaunchingTaskFlags还是用来初始化启动标志位的。

    getReusableIntentActivity方法中,我们需要去找一个可复用的ActivityRecord,那么只有启动模式为SingleInstance才能真正的复用,因为整个系统就只有一个实例。

    下来看后面的方法startActivityLocked

    final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,
                ActivityOptions options) {
        ...
        mWindowManager.setAppVisibility(r.appToken, true);
        ...
    } 
    

    mWindowManager.setAppVisibility(r.appToken, true);这句话表示这个Activity已经具备了显示的条件。

    接着会调用ActivityStackSupervisor的resumeFocusedStackTopActivityLocked方法,接着调ActivityStack的resumeTopActivityUncheckedLocked方法,再调自己的resumeTopActivityInnerLocked方法

    然后,然后,如果Activity已存在需要可见的状态,那么会调用IApplicationThread的scheduleResumeActivity方法

    反之调用ActivityStackSupervisor的startSpecificActivityLocked的方法,那么继续会调realStartActivityLocked的方法

    终于调用ApplicationThread的scheduleLaunchActivity方法了

    @Override
    public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
            ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
            CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
            int procState, Bundle state, PersistableBundle persistentState,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
        updateProcessState(procState, false);
        ActivityClientRecord r = new ActivityClientRecord();
        r.token = token;
        r.ident = ident;
        r.intent = intent;
        r.referrer = referrer;
        r.voiceInteractor = voiceInteractor;
        r.activityInfo = info;
        r.compatInfo = compatInfo;
        r.state = state;
        r.persistentState = persistentState;
        r.pendingResults = pendingResults;
        r.pendingIntents = pendingNewIntents;
        r.startsNotResumed = notResumed;
        r.isForward = isForward;
        r.profilerInfo = profilerInfo;
        r.overrideConfig = overrideConfig;
        updatePendingConfiguration(curConfig);
        sendMessage(H.LAUNCH_ACTIVITY, r);
    } 
    

    通过Handler发送message。

    这个handler是H

    private class H extends Handler {
        ...
        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                ...
        }
    } 
    

    接收到消息后,他调用了ActivityThread的handleLaunchActivity方法。

    WindowManagerGlobal.initialize();//初始化WMS
    Activity a = performLaunchActivity(r, customIntent);//创建并启动
    
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;//取出组件信息,并一顿操作
        ...
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
             //开始创建Activity实例,通过类加载器创建,看参数就知道了
             ...
             Application app = r.packageInfo.makeApplication(false, mInstrumentation);
             //获取Application
             ...
             activity.attach(appContext, this, getInstrumentation(), r.token,
                            r.ident, app, r.intent, r.activityInfo, title, r.parent,
                            r.embeddedID, r.lastNonConfigurationInstances, config,
                            r.referrer, r.voiceInteractor, window);
             //与window建立关联
             ...
             mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
             //callActivityOnCreate->activity.performCreate->onCreate,之后就很熟悉了
             ...
        }
    } 
    

    在这里,通过mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    创建了Activity实例。

    这实际上也是插件化的第二个Hook点。即hook newActivity方法。

    public Application makeApplication(boolean forceDefaultAppClass,
                Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }
        //如果mApplication不为空则直接返回,这也是为什么Application为单例
        ...
        Application app = null;
        ...
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
        //创建Application,跟Activity一样,都是用类加载器创建
        ...
        mApplication = app;//保存下来
        ...
        instrumentation.callApplicationOnCreate(app);
        //callApplicationOnCreate->onCreate
        ...
    } 
    

    在这里插入图片描述

    展开全文
  • 4、stm32F103入门学习--点亮LED(寄存器操作)

    千次阅读 多人点赞 2019-09-01 12:54:33
    写在最前面 通过点亮LED入门,采用两种方式,本节采用寄存器操作,下一节采用库函数操作,寄存器操作主要帮助大家理解stm32底层是怎么实现的,在此基础上再采用库函数操作。 ...

    写在最前面

    通过点亮LED入门,采用两种方式,本节采用寄存器操作,下一节采用库函数操作,寄存器操作主要帮助大家理解stm32底层是怎么实现的,在此基础上再采用库函数操作,虽然以后基本以库函数操作为主,但是理解寄存器操作也非常重要!

    启动文件添加

    在上一小节中有这么一张图,左侧栏除了“main.c”,还有“startup_stm32f10x_hd.s”。以后随着学习的深入大家会知道很多工作Keil软件给我们完成了,我们只是在进行逻辑编程。那么记住在一段程序前需要一段启动程序,以后讲解这一段启动程序。
    在这里插入图片描述
    在官方提供的固件库中可以按以下路径找寻启动文件。

    F:\STM32F103官方固件库\【固件库】STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm

    在下图中可以看到很多启动文件,后缀名都为.s,s是start的意思。怎么选择合适的启动文件呢?

    在这里插入图片描述
    给大家推荐下学习的资料。首先官方资料,非常详细,基本所有教程都是以此为基础。等会儿会用到“ST选型手册.pdf”。

    在这里插入图片描述
    野火的教程也不错,截取其中的一个图,可以看到根据FLASH的大小选择启动文件。
    在这里插入图片描述查看ST选型手册.pdf,截图如下,看到我的单片机flash为64k,所以选取“startup_stm32f10x_md.s”。
    在这里插入图片描述
    至于上节为什么选用““startup_stm32f10x_hd.s””也可以,有可能说明选稍大的问题不大。但是尽量根据标准去选。

    在没有启动文件的情况下编译可以看到,会出现错误,所以启动文件必不可少!举个例子,比如老师在课堂上课,看起来很简单铃声响了去指定教室上课就行。其实学校做了大量工作包括课程设定、教室安排等工作。所以我们能在Keil写程序是因为Keil前期后期做了大量工作。
    在这里插入图片描述
    将startup_stm32f10x_md.s放置到main.c同文件夹中。
    在这里插入图片描述
    打开Keil软件,跟添加main.c方式一样,双击“Source Group 1”,出现如下对话框,并没有看到启动文件,这是因为显示的文件类型都是.c。点击箭头所指。
    在这里插入图片描述
    选择“All files”,此时可以看到启动文件。添加即可。

    在这里插入图片描述
    再次编译,编译正常。

    在这里插入图片描述
    关于程序部分,一部分是熟知的main函数,另一部分是SystemInit函数。关于main函数大家都可以接受,SystemInit函数不可或缺,在以后讲启动文件再具体介绍。现在先默认写着,里面是空函数。

    相关寄存器配置介绍

    不管是库函数操作还是HAL操作(最近很流行),本质上都是对寄存器的操作。
    看下配套开发版的原理图。根据原理图可知PC13为低电平可以点亮LED。那么怎么使PC13为低电平呢?
    是否跟51单片机一样在相应的位写0就可以了?并不是,举个例子,比如烧饭,以前大锅柴火烧,烧熟了差不多。现在烧饭基本用电饭煲,不仅可以烧熟,还能根据自己的口味选择还能定时等等。所以说,stm32配置引脚更加复杂,但是更加强大。不仅要配置低电平还要配置是输入还是输出,如果是输出的话还要配置输出速度。接下里具体给大家介绍。
    根据原理图可知

    在提供的“STM32F10x系列编程手册(中文)”手册中看到GPIO寄存器7种类型。
    在这里插入图片描述
    首先看下8.2.1端口配置低寄存器,通过该寄存器具体介绍下。
    先解释下(GPIOx_CRL)(x=A…E)的意思。stm32f103引脚有多有少,多则144个,少则48个(我用的板子)。所以会把这些引脚分成组,A、B、C、D…每组最多16个引脚,注意,是最多,并不是一定要有16个引脚!
    图1下图可以看到,48引脚的单片机只分成了A、B、C、D(PA、PB、PC、PD)组。GPIO英语的全称是General-purpose input/output,翻译过来就是通用的IO口。GPIOA_CRL的意思是控制A口的CRL寄存器。
    在这里插入图片描述
    再回到上面的CTRL寄存器图,“偏移地址0x00”。那么偏移是相对于谁偏移的,应该有个基准!先给大家举个例子,要盖一幢商务大楼,外面盖完了如果里面是空的,是不行的,所以要盖一层层,一层层盖了也不行,在每一层隔处一个个房间,这样一幢商务大楼才能使用。我们单片机内存也是,整一大块是不能用的,也要隔成一个个房间才能使用,每个房间相当于寄存器,要么有人(相当于为1),要么没人(相当于为0)。所以要查下GPIOC安排在几楼!

    在官方提供的手册中(P28)可以看到GPIOC的地址范围是0x40011000~0x400113FF。所以它的起始地址是0x40011000,也就是基地址!偏移地址0x00后还是0x40011000。
    在这里插入图片描述再回到CTRL寄存器图,先不讲复位值,先看下面的图。一共有32个位,0~31。也就是CTRL寄存器有32位,这也是为什么叫做stm32的原因,以前的51单片机是8位单片机也就是一次性最多能处理8个位。
    看红色框部分(0位3位),CNF0和MODE0。再看4位7位,CNF1和MODE1。说明GPIOC中的第0位需要CNF0和MODE0这4各位来控制,GPIOC中的第1位需要CNF1和MODE1这4各位来控制。具体可以控制哪些呢,在往下看。

    在这里插入图片描述以MODE0为例,占了两个位,每个为可以表示0或1,所以可以表示4种情况!一般情况下是输出模式,在下表中的MODEy[1:0]就可以看到①,有输出和输入模式,假设选择“01”表示输出模式,最大速度为10MHZ,再去看CNFy[1:0],根据输入输出的不同有不同的选择,那么我们刚才选的是输出模式,所以看②,选择“00”,具体不展开什么叫通用推完输出模式,有机会再讲。
    在这里插入图片描述所以假设GPIOC第0位要输出低电平的话首先要配置这4位,0b00 01。但是我们的引脚是GPIOC13,而
    CTRL寄存器图中共32位,4个位一组,只能配置GPIOC0~GPIOC7。所以看CTRH寄存器。
    在这里插入图片描述
    GPIOC_CRH控制的是GPIOC的第8位~第15位。大家应该看到了GPIOC_CRL和GPIOC_CRH基本一样,一个控制低8位,一个控制高8位。注意下GPIOC_CRH的偏移地址是0x04。
    复位值的意思是也就是刚上电时的值,或者按复位键后的值,把0x4444 4444展成2进制,就可以知道,每个引脚的初始状态(0100)为浮空输入模式。

    在这里插入图片描述通过以下代码实现通用推挽输出模式,不懂可以留言。

    GPIOC_CRH &= ~(0x0F<<(4*5));
    GPIOC_CRH |= (1<<(4*5));
    

    再看8.2.3和8.2.4,一个用于配置输入,一个用于配置输出。那么我们看输出。

    在这里插入图片描述
    因为每个GPIO组只有16个引脚,所以ODR寄存器只用了低16位,高16位保留。
    在这里插入图片描述

    GPIOC_ODR &= ~(1<<13);//配置输出低电平
    GPIOC_ODR |= (1<<13);//配置输出高电平
    

    我们去银行取钱或者存钱都是在窗口排队,可能一个窗口10来号人,就像我们以前学的51单片机。所有的时间基准都是以唯一的晶振为主。但是现在银行推出了VIP服务,也就是你的存款足够多,他会对你进行一对一服务!我们的stm32就是VIP服务,每个引脚使用都需要开启相应的时钟功能!,一定要记住我们是VIP。

    在6.3.7小节中可以看到,位4表示开启相应的GPIOC时钟,它的偏移地址是0x18,再找下它的基地址。

    在这里插入图片描述
    根据上图知道我们要找的是RCC_APB2ENR时钟,从下图可以看到时钟控制的起始地址是0x40021000。那么RCC_APB2ENR的基地址是0x4002 1000,加上偏移地址后是0x4002 1018。

    RCC_APB2ENR |=(1<<4);//打开GPIOC时钟
    

    在这里插入图片描述

    综上,可以写出main函数的伪代码

    int main(void)
    {
    	RCC_APB2 |=(1<<4);//打开时钟
    	GPIOC_CRH &= ~(0x0F<<(4*5));//配置输出模式
        GPIOC_CRH |= (1<<20);
        GPIOC_ODR&=~(1<<13);//配置输出低电平
    }
    

    转换成可实用的代码

    int main(void)
    {
    	*(unsigned int*)0x4002 1018 |=(1<<4);//打开时钟
    	*(unsigned int*)0x40011004 &= ~(0x0F<<(4*5));//配置输出模式
        *(unsigned int*)0x40011004 |= (1<<20);
        *(unsigned int*)0x4001 100C&=~(1<<13)//配置输出低电平
    }
    

    编写完整程序烧录到单片机,看到LED正常点亮。在程序中我们也能看到,一个函数里不一定需要头文件,以前写51程序的时候第一句就是#include<reg52.h>,已形成固定模式,如果了解单片机本质的话就知道为什么不用写了。这节是从最最底层、完全暴力的方式点亮LED,可读性比较差。
    在这里插入图片描述
    在下下小节中我们把程序进一步完善,因为最终趋向于库函数操作,看看怎么从寄存器操作演变成库函数操作。在下一节中,主要讲C语言知识,因为这节有关一些C语言知识没有展开,以后碰到c语言知识点的话也在下一节补充。

    展开全文
  • 开机启动

    2017-06-07 09:03:00
    using System; using System.Windows.Forms; using Microsoft.Win32; namespace RegistryUtil { static class Program { /// <summary> /// 应用程序的主入口...
    using System;  
    using System.Windows.Forms;  
    using Microsoft.Win32;  
    namespace RegistryUtil {  
           static class Program {  
                  /// <summary>  
                  /// 应用程序的主入口点。  
                  /// </summary>  
                  [STAThread]  
                  static void Main() {  
                         Application.EnableVisualStyles();  
                         Application.SetCompatibleTextRenderingDefault(false);  
                         SetAutoBootStatu(true);  
                  }  
       
                  /// <summary>  
                  /// 在注册表中添加、删除开机自启动键值  
                  /// </summary>  
                  public static int SetAutoBootStatu(bool isAutoBoot) {  
                         try {  
                                string execPath = Application.ExecutablePath;  
                                RegistryKey rk = Registry.LocalMachine;  
                                RegistryKey rk2 = rk.CreateSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run");  
                                if (isAutoBoot) {  
                                       rk2.SetValue("MyExec", execPath);  
                                       Console.WriteLine(string.Format("[注册表操作]添加注册表键值:path = {0}, key = {1}, value = {2} 成功", rk2.Name, "TuniuAutoboot", execPath));  
                                } else {  
                                       rk2.DeleteValue("MyExec", false);  
                                       Console.WriteLine(string.Format("[注册表操作]删除注册表键值:path = {0}, key = {1} 成功", rk2.Name, "TuniuAutoboot"));  
                                }  
                                rk2.Close();  
                                rk.Close();  
                                return 0;  
                         } catch (Exception ex) {  
                                Console.WriteLine(string.Format("[注册表操作]向注册表写开机启动信息失败, Exception: {0}", ex.Message));  
                                return -1;  
                         }  
                  }  
           }  
    }
    

      需要注意的是:
    Windows中微软的注册表信息是分32位和64位的:
    32位:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft
    64位:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft

    以下代码

    RegistryKey rk = Registry.LocalMachine;  
    RegistryKey rk2 = rk.CreateSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run");  
    rk2.SetValue("MyExec", execPath);  

    在32位机器上执行,那么没有问题,变量会创建在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run下。但是如果在64位机器上执行,会自动创建在
    HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run

    private void checkBox1_CheckedChanged(object sender, EventArgs e)
            {
                if (checkBox1.Checked) //设置开机自启动  
                {
                    MessageBox.Show ("设置开机自启动,需要修改注册表","提示");  
                    string path = Application.ExecutablePath;
                    RegistryKey rk = Registry.LocalMachine;
                    RegistryKey rk2 = rk.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run");
                    rk2.SetValue("JcShutdown", path);
                    rk2.Close();
                    rk.Close();
                }
                else //取消开机自启动  
                {
                    MessageBox.Show ("取消开机自启动,需要修改注册表","提示");  
                    string path = Application.ExecutablePath;
                    RegistryKey rk = Registry.LocalMachine;
                    RegistryKey rk2 = rk.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run");
                    rk2.DeleteValue("JcShutdown", false);
                    rk2.Close();
                    rk.Close();
                }  
            }
    

      

    转载于:https://www.cnblogs.com/xiangxiong/p/6955231.html

    展开全文
  • 实例启动

    2019-03-05 10:09:00
    SQL SERVER启动步骤 SQL SERVER服务启动方法 Services.msc中启动 SQL SERVER Configuration Manager中启动 SSMS中启动 cmd 中net start启动 failover集群切换,节点上的服务启动 cmd中“sqlserver....

    SQL SERVER启动步骤

     

     

    SQL SERVER服务启动方法

    • Services.msc中启动
    • SQL SERVER Configuration Manager中启动
    • SSMS中启动
    • cmd 中net start启动
    • failover集群切换,节点上的服务启动
    • cmd中“sqlserver.exe -c -s INSTANCENAME”

      前五种都是以后台服务启动,信息写入日志文件。最后一种,信息写在cmd中,窗口关闭,SQL SERVER也关闭。

     

     

     从注册表读取SQL SERVER启动信息

    (1)Audit  Level:设置SQL SERVER是否记录用户登陆信息

        Login Mode:设置SQL SERVER登陆类型是只接受windows账户,还是混合登陆(windows与SQL SERVER账号都可)

     (2)启动参数

    -d:master数据库路径

    -l:master日志路径

    -e:错误日志路径

    (3)网络配置信息 

    SQL SERVER会侦听哪几个网络协议,以及每个网络协议的配置。

    检测硬件,配置内存与CPU

    SQL SERVER在读取注册表数据后,创建出errorlog后(日志文件只保存7个,实例每次启动都新建一个errorlog文件,并将原先的errorlog改为errorlog.1,1改为2,以此类推,errorlog,6会被删除),启动信息就可在errorlog中查看。

    系统数据库启动 

    启动顺序 master,msdb,mssqlsystemresource,model和tempdb

    准备网络连接

    SQL SERVER 将Master启动后,就到注册表读取网络配置。

    常开启三个协议:

    Shared Memory:LPC(Local Procedure Call)本地连接实例

    Named Pipe:通过Pipe的名字与实例交互。

    TCP/IP:默认通过1433端口连接。

    转载于:https://www.cnblogs.com/JinweiChang/p/10474989.html

    展开全文
  • 一、启动 Redis启动有直接启动和通过初始化脚本启动两种方式。 1.直接启动,适用于开发环境。 直接运行redis-server即可启动Redis: $ redis-server 2.通过初始化脚本启动,适用于生产环境 在Linux系统中...
  • http服务启动不了

    千次阅读 2018-10-15 22:14:50
    转载自,@Harlon先生,解决了一下午终于解决了。感谢一波大佬。 先放在这里。 windows server 2008开启IIS服务...2.找到World Wide Web Publishing Service服务,右键启动启动成功就可以了,如果还是出现1068错...
  • SpringBoot项目运行jar包启动

    万次阅读 多人点赞 2018-09-14 15:20:43
    人工智能,零基础入门!... SpringBoot项目在开发中,方便快捷,有一点原因就是SpringBoot项目可以打jar包运行:把jar包直接...一、我们所熟悉的是在开发环境下,直接用开发工具来运行那个启动类,然后就能启动这个项...
  • docker服务启动,重启,关闭命令

    万次阅读 多人点赞 2018-05-26 18:16:50
    最近刚学习dockerdocker启动命令,docker重启命令,docker关闭命令启动 systemctl start docker守护进程重启 sudo systemctl daemon-reload重启docker服务 systemctl restart docker重启docker服务 sudo service ...
  • 有时候在开发过程中,如果是手动搭的tomcat服务器,比如文件域名配置服务器,电脑重启后经常要去运行这个tomcat的start脚本手动启动这个tomcat,所以有时候就很烦,然后找了一个办法摆脱这种烦恼:就是把tomcat配置...
  • SpringBoot项目启动图标展示个性化

    万次阅读 2018-09-14 11:59:02
    SpringBoot启动时在控制台会有一个图标展示,默认的是一个单词“spring” 默认启动图标但是,我想要不一样,有个性化的图标,下面是步骤: 【1】在项目的resource目录下新建一个文件banner.txt,SpringBoot项目...
  • 自己的linux每次开机都要启动tomcat,网上好多都是用shell脚本来实现tomcat开机自启动,后来看到一种方法,直接修改系统文件来实现,已经实践过,方法有效。然后实验总结出,其实其他的比如redis和nginx也是可以这样...
  • 启动mysql

    万次阅读 2019-04-17 10:59:06
    1.dos窗口启动mysql: 1.Windows + R 输入 cmd 启动 dos 窗口; 输入: net start mysql 启动 mysql 服务 net stop mysql 关闭 mysql 服务 2.输入: mysql -hlocalhost -uroot -proot 进入mysql数据库,其中-h表示...
  • 如何在IDEA启动多个Spring Boot工程实例

    万次阅读 多人点赞 2017-07-30 22:27:01
    在我讲解的案例中,经常一个工程启动多个实例,分别占用不同的端口,有很多读者百思不得其解,在博客上留言,给我发邮件,加我微信询问。所以有必要在博客上记录下,方便读者。step 1在IDEA上点击Application右边的...
  • docker更新容器使其自启动

    万次阅读 2020-06-17 13:36:16
    使用docker update命令进行更新即可 docker update --restart=always #容器id或别名 docker update --配置=值 这种格式 官网地址:https://docs.docker.com/engine/reference/commandline/update/
  • 传统的装系统的方法是用光盘当启动盘来装系统,所以在电脑配置的时候要考虑光驱。但是随着网络的发展,科技的进步,U盘装系统逐步取代了光盘装系统。U盘装系统首先是要找启动工具将普通U盘制作成启动盘,然后再装...
  • Redis启动、配置 及 常用命令

    万次阅读 2014-06-12 14:14:35
    启动 Redis 服务 src/redis-server或者src/redis-server redis.conf 注:src/redis-server 会不加载redis.conf配置文件,提示:Warning: no config file specified, using the default config. 标题 而:src/...
  • openoffice启动和自动启动设置

    万次阅读 2018-07-28 21:04:25
    openoffice 在 Windows 和 linux 环境下启动和自动启动 windows环境下安装启动openoffice 安装、启动 下载openoffice的 Windows 版本并安装,默认路径为「C:\Program Files (x86)\OpenOffice 4」。 启动...
  • 在windows下制作mac os x的启动安装U盘

    万次阅读 2018-08-09 16:11:23
    在windows下制作mac os x的启动安装U盘 制作教程:在windows下制作mac os x的启动安装U盘
  • docker启动zookeeper服务器

    万次阅读 2020-05-06 15:17:27
    网易云镜像地址,找一个合适的zookeeper镜像下载下来,并且参考下面的启动zookeeper的命令启动容器 下载镜像 docker pull zookeeper 启动容器 这里我直接使用host网卡 docker run -it -d --restart=always --...
  • App 冷启动与热启动启动白屏优化

    万次阅读 2018-08-26 15:19:41
    博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载。... 介绍一下 app 冷启动和热启动方式来实现 app 秒开的效果。那么,先来看看什么叫冷启动和热启动。 冷启动:指 app 被后台杀死后,在这个状态...
  • 微PE制作U盘启动盘教程

    万次阅读 多人点赞 2019-06-12 21:53:23
    微PE可以安装到当前系统下,开机通过启动菜单可以进入;也可以将PE安装到U盘、移动硬盘,当Windows系统无法进入时,可以通过U盘或移动硬盘启动进入PE;此外,它还可以将PE生成ISO格式的文件,方便的进行光盘刻录。 ...
  • 监听kafka是否启动

    万次阅读 2019-12-30 17:18:27
    #!/bin/sh . /etc/profile . ~/.bash_profile date=`date` cd /usr/local/kafka echo "$date star to monitor kafka" >> /usr/local/kafka/monitor.log pid=`jps | grep 'Kafka'` ... ...
  • 项目启动tomcat失败的几种可能原因和解决方法

    万次阅读 多人点赞 2018-05-22 21:13:36
    总结一下tomcat启动问题,也给自己做个笔记 , 逐渐补充完善。1、java配置路径有问题,请配置好jdk路径,具体参考java路径的配置吧。2、项目未添加tomcat驱动,(一般提示The superclass "javax.servlet....
  • dubbo没有自己原生的配置中心,采用zookeeper作为配置中心(除了zookeeper,在官网文档中还有其他配置中心,官方推荐使用zookeeper) 下载zookeeper ... 解压缩zookeeper的压缩包 拷贝一份 conf/zoo_sample.cfg ...
  • 基于项目实例解析ng启动加载过程前言 在AngularJS项目开发过程中,自己将遇到的问题进行了整理。回过头来总结一下angular的启动过程。 下面以实际项目为例进行简要讲解。1.载入ng库2.等待,直到DOM树构造完毕。3....
  • 只有namenode没有启动起来,导致整个集群不可用? 原因是进行了第二次格式化namenode;不止一次执行了hdfs namenode -format。导致唯一识别标识集群id变化了。所以启动不起来namenode 解决方法: 删除对应文件...
  • win10装了一些杂七杂八的软件之后 一直出现这样的蓝屏 百度了好多还是没有解决 大概记录一下这个糟心的过程   ...因为工作需要所以一直不敢重装 ...过程1:(有人说重启就可以了,但是我的不行,所以用了点骚操作...
  • Tomcat启动一闪而过就消失的原因和解决方法

    万次阅读 多人点赞 2018-07-04 11:47:19
    Tomcat启动一闪而过怎么办?这成为了许多刚接触tomcat开发环境工作者的一个大问题,许多用户朋友都不知道是因为什么问题导致出现这种情况,下面就一起来了解一下吧。原因一: (1)首先要搞明白启动不起来的原因。...
  • Windows设置自己的程序开机自动启动

    万次阅读 多人点赞 2018-12-20 17:49:26
    Windows系统想要快速设置开机自动启动某个程序,可以使用以下几种方法设置: 第一种:设置启动项 1.找到启动文件夹,我的是C:\Users\ThinkPad\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup,...
  • 在Linux中利用Service命令添加系统服务及开机自启动

    万次阅读 多人点赞 2017-12-13 15:44:08
    近期由于做嵌入式项目需要,要求将编写的程序在板载系统开机时自启动。这里做个笔记,备忘。 1 概念 通过查资料发现linux启动服务是用SERVICE +COMMAND。这里的command命令本身也是一个脚本。比如说:service ...

空空如也

1 2 3 4 5 ... 20
收藏数 4,824,093
精华内容 1,929,637
关键字:

启动