精华内容
参与话题
问答
  • 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语言知识点的话也在下一节补充。

    展开全文
  • 集群启动停止方式1[更方便] 启动 执行bin目录下的 sh start-hbase.sh ,你会发现在当前机器上启动了一个Master,在所有的机器上启动了RegionServer [root@zjj101 bin]# sh start-hbase.sh starting master, logging to...

    集群启动停止方式1[更方便]

    启动

    执行bin目录下的 sh start-hbase.sh ,你会发现在当前机器上启动了一个Master,在所有的机器上启动了RegionServer

    [root@zjj101 bin]# sh start-hbase.sh
    starting master, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-master-zjj101.out
    zjj102: starting regionserver, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-regionserver-zjj102.out
    zjj103: starting regionserver, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-regionserver-zjj103.out
    zjj101: starting regionserver, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-regionserver-zjj101.out
    
    

    对应的停止服务:

    帮你把master 和regionserver都停止掉

    [root@zjj101 bin]# sh stop-hbase.sh
    stopping hbase.....................
    

    集群启动停止方式2

    • 启动单个master

    注意,如果使用 hbase-daemons.sh 启动Master的话,你会发现集群中多台机器都启动了Master,如果你只需要启动一个Master的话,就不用要hbase-daemons.sh , 用 hbase-daemon.sh

    # 启动多个Master
    [root@zjj101 bin]#  sh hbase-daemons.sh start master
    zjj103: starting master, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-master-zjj103.out
    zjj101: starting master, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-master-zjj101.out
    zjj102: starting master, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-master-zjj102.out
    
    # 停止多个Master
    [root@zjj101 bin]# sh hbase-daemons.sh  stop master
    zjj101: stopping master.
    zjj103: stopping master.
    zjj102: stopping master.
    # 启动一个Master
    [root@zjj101 bin]#  sh hbase-daemon.sh start master
    starting master, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-master-zjj101.out
    
    • 启动多个regionserver
    [root@zjj101 bin]#  sh hbase-daemons.sh start regionserver
    zjj101: starting regionserver, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-regionserver-zjj101.out
    zjj102: starting regionserver, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-regionserver-zjj102.out
    zjj103: starting regionserver, logging to /root/soft/hbase-1.3.1/bin/../logs/hbase-root-regionserver-zjj103.out
    
    
    • 群停止脚本

    帮你把master 和regionserver都停止掉

    [root@zjj101 bin]# sh stop-hbase.sh
    stopping hbase.....................
    [root@zjj101 bin]#
    
    
    展开全文
  • 注册服务&开机自启动

    2019-05-27 15:15:00
    2019独角兽企业重金招聘Python工程师标准>>> ...

     

    列出所有服务
    
    [root@localhost ~]# systemctl list-unit-files
    
    [root@localhost ~]# systemctl status mysqld
    
    [root@localhost ~]# systemctl stop mysqld
    
    [root@localhost ~]# systemctl disable  mysqld
    
    
    
    

     

    rpm 安装的mysql 注册的服务 在

    /usr/lib/systemd/system/mysqld.service

     

    Centos7开机启动自己的脚本

    在百度上可以找到好几种Linux开机启动各种服务的方法,在这里我写的是自己喜欢的方式.
    博主是一个不怎么记事的人,有些配置在系统的目录下,配置了一次后就忘了,再也不想去系统的目录下找各种奇奇怪怪的目录和名字.就比如说这个开机启动,在配置完了后的某一天,想要在加一个启动的服务,然而那时已经忘了以前是在哪个目录下配置的了,一个大写的懵逼,所以就自己新建一个脚本放在自己能找到的目录,只用在系统的目录下配置一次,以后就在自己新建的脚本里面写启动服务的命令就好了

    1. 自己新建一个脚本,如centnet-service.sh

    经过后面的几个步骤后,这个脚本在开机的时候会执行,在这个脚本里面可以写你开机的时候想执行的命令,如启动tomcat,oracle等服务

    2. 在脚本中输入启动服务的命令,如(开机启动tomcat):

    #!/bin/bash
    export JDK_HOME=/home/java/jdk1.8.0_91
    export JAVA_HOME=/home/java/jdk1.8.0_91
    /home/tomcat/apache-tomcat-8.0.36/bin/startup.sh
    

    3. 执行如下命令,将该脚本标记为可执行文件(添加可执行的权限)

    chmod +x /home/centnet/centnet-service.sh 
    

    4. 执行如下命令将/etc/rc.d/rc.local文标记为可执行文件

    在centos7中,/etc/rc.d/rc.local文件的权限被降低了,开机的时候执行在自己的脚本是不能起动一些服务的,执行下面的命令可以文件标记为可执行的文件

    chmod +x /etc/rc.d/rc.local
    

    5. 打开/etc/rc.d/rc.local文件,在最后面添加如下脚本

    /home/centnet/centnet-service.sh
    

    这样,centnet-service.sh这个脚本在开机的时候就会被执行了,以后再这里面写启动服务的命令就可以了

     

    注册sentinel 服务

    nohup java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=10.217.17.79:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar >/home/daxing/sentinel/logs/sentinel-dashboard.log 2>&1 &
    
    #由于项目里有配置日志输出,所有不需要配置日志
    nohup java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=10.217.17.79:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar >/dev/null 2>&1 &

     

    CentOS 7 elasticsearch service 开机自启

    vi /etc/security/limits.conf
    

     

    soft nofile 65536
    hard nofile 131072
    soft nproc 2048
    hard nproc 4096
    

     

    在/etc/systemd/system目录下创建elasticsearch.service文件

    [Unit]
    Description=elasticsearch
    [Service]
    User=elasticsearch
    LimitNOFILE=100000
    LimitNPROC=100000
    ExecStart=/usr/local/elasticsearch/bin/elasticsearch
    [Install]
    WantedBy=multi-user.target
    



    设置开机自启

    systemctl enable elasticsearch
     

     

     

    转载于:https://my.oschina.net/zhongwenhao/blog/3054536

    展开全文
  • 开启

    2019-12-02 23:07:02
    文章目录**开启python量化之路**pycharm 使用教程(一)安装和首次使用pycharm 使用教程(二)设置字体大小pycharm 使用教程(三)Hello world!pycharm 使用教程(四)显示行号pycharm 使用教程(五)断点调试...
  • 一、启动 Redis启动有直接启动和通过初始化脚本启动两种方式。 1.直接启动,适用于开发环境。 直接运行redis-server即可启动Redis: $ redis-server 2.通过初始化脚本启动,适用于生产环境 在Linux系统中...
  • Alibaba Spring Cloud已经成为国内主流的新一代微服务架构技术,而Nacos是Alibaba Spring Cloud中的重要成员,它能提供服务注册与发现,配置中心的功能,而且还能配置动态...启动: 下载后解压,进入解压后的bin目录,
  • 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 ...
  • 传统的装系统的方法是用光盘当启动盘来装系统,所以在电脑配置的时候要考虑光驱。但是随着网络的发展,科技的进步,U盘装系统逐步取代了光盘装系统。U盘装系统首先是要找启动工具将普通U盘制作成启动盘,然后再装...
  • Zookeeper官网连接地址: ...再次双击zkServer.cmd命令,发现启动后页面出现这样的错误 按照报错的信息提示,是Zookeeper启动的时候需要依赖你的JAVA_HOME,但是Zookeeper启动的时候没有找到JAVA_HO
  • 启动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表示...
  • 在windows下制作mac os x的启动安装U盘

    万次阅读 2018-08-09 16:11:23
    在windows下制作mac os x的启动安装U盘 制作教程:在windows下制作mac os x的启动安装U盘
  • openoffice启动和自动启动设置

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

    千次阅读 2019-06-27 21:53:32
    这次开启zookeeper的集群 首先克隆虚拟机 一般右键就可以克隆,可是实际操作发现mac地址很容易重复,而且没有办法控制介质存放在哪里,很容易导致c盘爆满,这里采取另外一种方式 点击管理 --> 虚拟介质管理 ...
  • App 冷启动与热启动启动白屏优化

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

    万次阅读 2017-05-27 16:05:09
    主要提供一种解决启动zKServer.cmd闪退的解决思路,遇到类似的问题,都可以采用以下的思路解决。
  • 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/...
  • docker启动zookeeper服务器

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

空空如也

1 2 3 4 5 ... 20
收藏数 4,742,481
精华内容 1,896,992
关键字:

启动