PX4与Ardupilot的入门基础知识(第一章:架构与启动过程)

目录

摘要

本节主要记录自己学px4的代码架构与Ardupilot代码架构对比文档,欢迎批评指正。



  (1)px4与apm固件的区别与联系
  (2)px4与apm每个文件夹的作用
  (3)px4与apm无人机的启动过程


第一节:px4与apm的区别与联系



这里写图片描述
(1)pixhawk
pixhawk硬件
(2)APM
APM硬件
(3)px4—Firmware
这里写图片描述
(4)apm—Ardupilot
这里写图片描述



第二节:px4与apm每个文件夹的作用



(1)px4代码代码架构
1>整理目录
这里写图片描述


2>每个文件夹的作用
这里写图片描述


(2)apm代码代码架构
1>整理目录
这里写图片描述


2>每个文件夹的作用
这里写图片描述



第三节:px4与apm无人机的启动过程

这里只讲解Nuttx启动到ArduCopter多旋翼入口函数的过程,Bootloader到Nuttx的过程将在:Bootloader启动分析中讲解。
代码逻辑

1.px4固件启动过程




(1)Bootloader启动后,单片机的入口函数:stm32_start.c

扫描二维码关注公众号,回复: 2742240 查看本文章




需要思考的问题:这个启动过程是STM32F427还是STM32F103的入口,或者两个同时都有


//复位后的入口函数:This is the reset entry point.
void __start(void)
{
  const uint32_t *src;
  uint32_t *dest;

#ifdef CONFIG_ARMV7M_STACKCHECK
  /* Set the stack limit before we attempt to call any functions */

  __asm__ volatile ("sub r10, sp, %0" : : "r" (CONFIG_IDLETHREAD_STACKSIZE - 64) : );
#endif

  /* Configure the UART so that we can get debug output as soon as possible */

  stm32_clockconfig(); //初始化时钟
  stm32_fpuconfig();   //配置FPU(浮点运算)
  stm32_lowsetup();    //基本初始化串口,之后可以使用up_lowputc()
  stm32_gpioinit();    //初始化gpio,只是调用stm32_gpioremap()设置重映射
  showprogress('A');   //输出A

  /* Clear .bss.  We'll do this inline (vs. calling memset) just to be
   * certain that there are no issues with the state of global variables.
   */

  for (dest = _START_BSS; dest < _END_BSS; )
    {
      *dest++ = 0;
    }

  showprogress('B');

  /* Move the initialized data section from his temporary holding spot in
   * FLASH into the correct place in SRAM.  The correct place in SRAM is
   * give by _sdata and _edata.  The temporary location is in FLASH at the
   * end of all of the other read-only data (.text, .rodata) at _eronly.
   */

  for (src = _DATA_INIT, dest = _START_DATA; dest < _END_DATA; )
    {
      *dest++ = *src++;
    }

  showprogress('C');

#ifdef CONFIG_ARMV7M_ITMSYSLOG
  /* Perform ARMv7-M ITM SYSLOG initialization */

  itm_syslog_initialize();
#endif

  /* Perform early serial initialization */

#ifdef USE_EARLYSERIALINIT
  up_earlyserialinit(); //初始化串口,之后可以使用up_putc()
#endif
  showprogress('D');

  /* For the case of the separate user-/kernel-space build, perform whatever
   * platform specific initialization of the user memory is required.
   * Normally this just means initializing the user space .data and .bss
   * segments.
   */

#ifdef CONFIG_BUILD_PROTECTED
  stm32_userspace();
  showprogress('E');
#endif

  /* Initialize onboard resources */

  stm32_boardinitialize();  //stm32硬件板层配置初始化,完成,之后可以启动STM32的Nuttx操作系统
  showprogress('F');

  /* Then start NuttX */

  showprogress('\r');
  showprogress('\n');

#ifdef CONFIG_STACK_COLORATION
  /* Set the IDLE stack to the coloration value and jump into os_start() */

  go_os_start((FAR void *)&_ebss, CONFIG_IDLETHREAD_STACKSIZE);
#else
  /* Call os_start() */

  os_start();  //启动Nuttx操作系统

  /* Shoulnd't get here */

  for (; ; ); //进入死循环,这里是不是看到跟一般的FreeRTOS基本一样,就是配置硬件时钟,串口,IO等等,可以启动Nuttx操作系统,之后执行while死循环。
#endif
}



(2)nuttx启动入口函数:void os_start(void)

这里写图片描述



/****************************************************************************
 * Name: os_start
 *
 * Description:
 *   This function is called to initialize the operating system and to spawn
 *   the user initialization thread of execution.  This is the initial entry
 *   point into NuttX
 *
 * Input Parameters:
 *   None
 *
 * Returned value:
 *   Does not return.
 *
 ****************************************************************************/
//这个函数被调用来初始化操作系统并生成执行的用户初始化线程。这是NuttX最初的切入点。
void os_start(void)
{
#ifdef CONFIG_SMP
  int cpu;
#else
# define cpu 0
#endif
  int i;

  sinfo("Entry\n");

  /* Boot up is complete */

  g_os_initstate = OSINIT_BOOT;

  /* Initialize RTOS Data ***************************************************/
  /* Initialize all task lists */

  dq_init(&g_readytorun); //初始化各种状态的任务列表(置为null)
  dq_init(&g_pendingtasks);
  dq_init(&g_waitingforsemaphore);
#ifndef CONFIG_DISABLE_SIGNALS
  dq_init(&g_waitingforsignal);
#endif
#ifndef CONFIG_DISABLE_MQUEUE
  dq_init(&g_waitingformqnotfull);
  dq_init(&g_waitingformqnotempty);
#endif
#ifdef CONFIG_PAGING
  dq_init(&g_waitingforfill);
#endif
  dq_init(&g_inactivetasks);
#if (defined(CONFIG_BUILD_PROTECTED) || defined(CONFIG_BUILD_KERNEL)) && \
     defined(CONFIG_MM_KERNEL_HEAP)
  sq_init(&g_delayed_kfree);
#endif
#ifndef CONFIG_BUILD_KERNEL
  sq_init(&g_delayed_kufree);
#endif

#ifdef CONFIG_SMP
  for (i = 0; i < CONFIG_SMP_NCPUS; i++)
    {
      dq_init(&g_assignedtasks[i]);
    }
#endif

  /* Initialize the logic that determine unique process IDs. */

  g_lastpid = 0;
  for (i = 0; i < CONFIG_MAX_TASKS; i++) //初始化唯一可以确定的元素--进程ID
    {
      g_pidhash[i].tcb = NULL;
      g_pidhash[i].pid = INVALID_PROCESS_ID;
    }

  /* Initialize the IDLE task TCB *******************************************/
//初始化空闲任务TCB
#ifdef CONFIG_SMP
  for (cpu = 0; cpu < CONFIG_SMP_NCPUS; cpu++, g_lastpid++)
#endif
    {
      FAR dq_queue_t *tasklist;
      int hashndx;

      /* Assign the process ID(s) of ZERO to the idle task(s) */

      hashndx                = PIDHASH(g_lastpid);
      g_pidhash[hashndx].tcb = &g_idletcb[cpu].cmn;
      g_pidhash[hashndx].pid = g_lastpid;

      /* Initialize a TCB for this thread of execution.  NOTE:  The default
       * value for most components of the g_idletcb are zero.  The entire
       * structure is set to zero.  Then only the (potentially) non-zero
       * elements are initialized. NOTE:  The idle task is the only task in
       * that has pid == 0 and sched_priority == 0.
       */

      memset((void *)&g_idletcb[cpu], 0, sizeof(struct task_tcb_s));
      g_idletcb[cpu].cmn.pid        = g_lastpid;
      g_idletcb[cpu].cmn.task_state = TSTATE_TASK_RUNNING;

      /* Set the entry point.  This is only for debug purposes.  NOTE: that
       * the start_t entry point is not saved.  That is acceptable, however,
       * becaue it can be used only for restarting a task: The IDLE task
       * cannot be restarted.
       */

#ifdef CONFIG_SMP
      if (cpu > 0)
        {
          g_idletcb[cpu].cmn.start      = os_idle_trampoline;
          g_idletcb[cpu].cmn.entry.main = os_idle_task;
        }
      else
#endif
        {
          g_idletcb[cpu].cmn.start      = (start_t)os_start;
          g_idletcb[cpu].cmn.entry.main = (main_t)os_start;
        }

      /* Set the task flags to indicate that this is a kernel thread and, if
       * configured for SMP, that this task is locked to this CPU.
       */

#ifdef CONFIG_SMP
      g_idletcb[cpu].cmn.flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_NONCANCELABLE |
                                  TCB_FLAG_CPU_LOCKED);
      g_idletcb[cpu].cmn.cpu   = cpu;
#else
      g_idletcb[cpu].cmn.flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_NONCANCELABLE);
#endif

#ifdef CONFIG_SMP
      /* Set the affinity mask to allow the thread to run on all CPUs.  No,
       * this IDLE thread can only run on its assigned CPU.  That is
       * enforced by the TCB_FLAG_CPU_LOCKED which overrides the affinity
       * mask.  This is essential because all tasks inherit the affinity
       * mask from their parent and, ultimately, the parent of all tasks is
       * the IDLE task.
       */

      g_idletcb[cpu].cmn.affinity = SCHED_ALL_CPUS;
#endif

#if CONFIG_TASK_NAME_SIZE > 0
      /* Set the IDLE task name */

#  ifdef CONFIG_SMP
      snprintf(g_idletcb[cpu].cmn.name, CONFIG_TASK_NAME_SIZE, "CPU%d IDLE", cpu);
#  else
      strncpy(g_idletcb[cpu].cmn.name, g_idlename, CONFIG_TASK_NAME_SIZE);
      g_idletcb[cpu].cmn.name[CONFIG_TASK_NAME_SIZE] = '\0';
#  endif
#endif

      /* Configure the task name in the argument list.  The IDLE task does
       * not really have an argument list, but this name is still useful
       * for things like the NSH PS command.
       *
       * In the kernel mode build, the arguments are saved on the task's
       * stack and there is no support that yet.
       */

#if CONFIG_TASK_NAME_SIZE > 0
      g_idleargv[cpu][0]  = g_idletcb[cpu].cmn.name;
#else
      g_idleargv[cpu][0]  = (FAR char *)g_idlename;
#endif /* CONFIG_TASK_NAME_SIZE */
      g_idleargv[cpu][1]  = NULL;
      g_idletcb[cpu].argv = &g_idleargv[cpu][0];

      /* Then add the idle task's TCB to the head of the corrent ready to
       * run list.
       */

#ifdef CONFIG_SMP
      tasklist = TLIST_HEAD(TSTATE_TASK_RUNNING, cpu);
#else
      tasklist = TLIST_HEAD(TSTATE_TASK_RUNNING);
#endif
      dq_addfirst((FAR dq_entry_t *)&g_idletcb[cpu], tasklist);

      /* Initialize the processor-specific portion of the TCB */

      up_initial_state(&g_idletcb[cpu].cmn);
    }

  /* Task lists are initialized */

  g_os_initstate = OSINIT_TASKLISTS;

  /* Initialize RTOS facilities *********************************************/
  /* Initialize the semaphore facility.  This has to be done very early
   * because many subsystems depend upon fully functional semaphores.
   */

  sem_initialize();   //初始化信号量

#if defined(MM_KERNEL_USRHEAP_INIT) || defined(CONFIG_MM_KERNEL_HEAP) || \
    defined(CONFIG_MM_PGALLOC)
  /* Initialize the memory manager */

  {
    FAR void *heap_start;
    size_t heap_size;

#ifdef MM_KERNEL_USRHEAP_INIT
    /* Get the user-mode heap from the platform specific code and configure
     * the user-mode memory allocator.
     */

    up_allocate_heap(&heap_start, &heap_size); //分配用户模式的堆(设置堆的起点和大小)
    kumm_initialize(heap_start, heap_size); //初始化用户模式的堆
#endif

#ifdef CONFIG_MM_KERNEL_HEAP
    /* Get the kernel-mode heap from the platform specific code and configure
     * the kernel-mode memory allocator.
     */

    up_allocate_kheap(&heap_start, &heap_size);
    kmm_initialize(heap_start, heap_size);
#endif

#ifdef CONFIG_MM_PGALLOC
    /* If there is a page allocator in the configuration, then get the page
     * heap information from the platform-specific code and configure the
     * page allocator.
     */
    //如果在配置页面分配器,然后让页面堆信息从特定于平台的代码和配置页面分配器。
    up_allocate_pgheap(&heap_start, &heap_size); //分配用户模式的堆(设置堆的起点和大小)
    mm_pginitialize(heap_start, heap_size);      //初始化内核模式的堆
#endif
  }
#endif

  /* The memory manager is available */

  g_os_initstate = OSINIT_MEMORY;

#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)
  /* Initialize tasking data structures */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (task_initialize != NULL)
#endif
    {
      task_initialize(); //初始化任务数据结构
    }
#endif

  /* Initialize the interrupt handling subsystem (if included) */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (irq_initialize != NULL)
#endif
    {
      irq_initialize(); //初始化中断处理子系统(如果包含的话)
    }

  /* Initialize the watchdog facility (if included in the link) */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (wd_initialize != NULL)
#endif
    {
      wd_initialize(); //初始化看门狗设施(如果包含在链接中)
    }

  /* Initialize the POSIX timer facility (if included in the link) */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (clock_initialize != NULL)
#endif
    {
      clock_initialize(); //初始化POSIX定时器工具(如果包含在链接中)
    }

#ifndef CONFIG_DISABLE_POSIX_TIMERS
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (timer_initialize != NULL)
#endif
    {
      timer_initialize(); //配置POSIX定时器
    }
#endif

#ifndef CONFIG_DISABLE_SIGNALS
  /* Initialize the signal facility (if in link) */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (sig_initialize != NULL)
#endif
    {
      sig_initialize();//初始化信号设施(如果在链路中)
    }
#endif

#ifndef CONFIG_DISABLE_MQUEUE
  /* Initialize the named message queue facility (if in link) */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (mq_initialize != NULL)
#endif
    {
      mq_initialize(); //初始化命名的消息队列设施(如果在链接中)
    } 
#endif

#ifndef CONFIG_DISABLE_PTHREAD
  /* Initialize the thread-specific data facility (if in link) */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (pthread_initialize != NULL)
#endif
    {
      pthread_initialize(); //初始化线程特定的数据,空函数
    }
#endif

#if CONFIG_NFILE_DESCRIPTORS > 0
  /* Initialize the file system (needed to support device drivers) */

  fs_initialize(); //初始化文件系统 
#endif

#ifdef CONFIG_NET
  /* Initialize the networking system.  Network initialization is
   * performed in two steps:  (1) net_setup() initializes static
   * configuration of the network support.  This must be done prior
   * to registering network drivers by up_initialize().  This step
   * cannot require upon any hardware-depending features such as
   * timers or interrupts.
   */

  net_setup(); //初始化网络
#endif

  /* The processor specific details of running the operating system
   * will be handled here.  Such things as setting up interrupt
   * service routines and starting the clock are some of the things
   * that are different for each  processor and hardware platform.
   */

  up_initialize(); //处理器特定的初始化

  /* Hardware resources are available */

  g_os_initstate = OSINIT_HARDWARE;

#ifdef CONFIG_NET
  /* Complete initialization the networking system now that interrupts
   * and timers have been configured by up_initialize().
   */

  net_initialize();
#endif

#ifdef CONFIG_MM_SHM
  /* Initialize shared memory support */

  shm_initialize();
#endif

  /* Initialize the C libraries.  This is done last because the libraries
   * may depend on the above.
   */

  lib_initialize();

  /* IDLE Group Initialization **********************************************/
  /* Announce that the CPU0 IDLE task has started */

  sched_note_start(&g_idletcb[0].cmn);

#ifdef CONFIG_SMP
  /* Initialize the IDLE group for the IDLE task of each CPU */

  for (cpu = 0; cpu < CONFIG_SMP_NCPUS; cpu++)
#endif
    {
#ifdef HAVE_TASK_GROUP
      /* Allocate the IDLE group */

      DEBUGVERIFY(group_allocate(&g_idletcb[cpu], g_idletcb[cpu].cmn.flags));
#endif

#if CONFIG_NFILE_DESCRIPTORS > 0 || CONFIG_NSOCKET_DESCRIPTORS > 0
#ifdef CONFIG_SMP
      if (cpu > 0)
        {
          /* Clone stdout, stderr, stdin from the CPU0 IDLE task. */

          DEBUGVERIFY(group_setuptaskfiles(&g_idletcb[cpu]));
        }
      else
#endif
        {
          /* Create stdout, stderr, stdin on the CPU0 IDLE task.  These
           * will be inherited by all of the threads created by the CPU0
           * IDLE task.
           */

          DEBUGVERIFY(group_setupidlefiles(&g_idletcb[cpu]));
        }
#endif

#ifdef HAVE_TASK_GROUP
      /* Complete initialization of the IDLE group.  Suppress retention
       * of child status in the IDLE group.
       */

      DEBUGVERIFY(group_initialize(&g_idletcb[cpu]));
      g_idletcb[cpu].cmn.group->tg_flags = GROUP_FLAG_NOCLDWAIT;
#endif
    }

  /* Start SYSLOG ***********************************************************/
  /* Late initialization of the system logging device.  Some SYSLOG channel
   * must be initialized late in the initialization sequence because it may
   * depend on having IDLE task file structures setup.
   */

  syslog_initialize(SYSLOG_INIT_LATE);

#ifdef CONFIG_SMP
  /* Start all CPUs *********************************************************/

  /* A few basic sanity checks */

  DEBUGASSERT(this_cpu() == 0 && CONFIG_MAX_TASKS > CONFIG_SMP_NCPUS);

  /* Take the memory manager semaphore on this CPU so that it will not be
   * available on the other CPUs until we have finished initialization.
   */

  DEBUGVERIFY(kmm_trysemaphore());

  /* Then start the other CPUs */

  DEBUGVERIFY(os_smp_start());//然后启动其他cpus

#endif /* CONFIG_SMP */

  /* Bring Up the System ****************************************************/
  /* The OS is fully initialized and we are beginning multi-tasking */

  g_os_initstate = OSINIT_OSREADY;

  /* Create initial tasks and bring-up the system */

  DEBUGVERIFY(os_bringup()); //创建初始线程并提出系统

#ifdef CONFIG_SMP
  /* Let other threads have access to the memory manager */

  kmm_givesemaphore(); //让其他线程访问内存管理器

#endif /* CONFIG_SMP */

  /* The IDLE Loop **********************************************************/
  /* When control is return to this point, the system is idle. */

  sinfo("CPU0: Beginning Idle Loop\n");
  for (; ; )
    {
      /* Perform garbage collection (if it is not being done by the worker
       * thread).  This cleans-up memory de-allocations that were queued
       * because they could not be freed in that execution context (for
       * example, if the memory was freed from an interrupt handler).
       */

#ifndef CONFIG_SCHED_WORKQUEUE
      /* We must have exclusive access to the memory manager to do this
       * BUT the idle task cannot wait on a semaphore.  So we only do
       * the cleanup now if we can get the semaphore -- this should be
       * possible because if the IDLE thread is running, no other task is!
       *
       * WARNING: This logic could have undesirable side-effects if priority
       * inheritance is enabled.  Imaginee the possible issues if the
       * priority of the IDLE thread were to get boosted!  Moral: If you
       * use priority inheritance, then you should also enable the work
       * queue so that is done in a safer context.
       */

      if (sched_have_garbage() && kmm_trysemaphore() == 0)
        {
          sched_garbage_collection();
          kmm_givesemaphore();
        }
#endif

      /* Perform any processor-specific idle state operations */

      up_idle(); //执行任何处理器特定的空闲状态操作
    }
}



(3)创建初始线程任务:os_bringup()
这里写图片描述




int os_bringup(void)
{
  /* Setup up the initial environment for the idle task.  At present, this
   * may consist of only the initial PATH variable.  The PATH variable is
   * (probably) not used by the IDLE task.  However, the environment
   * containing the PATH variable will be inherited by all of the threads
   * created by the IDLE task.
   */

//为空闲任务设置初始环境。目前,这可能只有初始路径变量。路径变量(可能)不被空闲任务使用。然而,包含路径变量,将所有的空闲任务创建线程继承的环境。
#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
  (void)setenv("PATH", CONFIG_PATH_INITIAL, 1);
#endif

  /* Start the page fill worker kernel thread that will resolve page faults.
   * This should always be the first thread started because it may have to
   * resolve page faults in other threads
   */
//启动将解决页面错误的页面填充工作者内核线程。这应该总是第一个线程启动,因为它可能需要解决其他线程中的页面错误。
  os_pgworker();

  /* Start the worker thread that will serve as the device driver "bottom-
   * half" and will perform misc garbage clean-up.
   */
 //启动将充当设备驱动程序“下半部分”的工作线程,并执行MISC垃圾清理
  os_workqueues();

  /* Once the operating system has been initialized, the system must be
   * started by spawning the user initialization thread of execution.  This
   * will be the first user-mode thread.
   */
//一旦操作系统被初始化,系统就必须通过生成执行的用户初始化线程来启动。这将是第一个用户模式线程。
  os_start_application(); //这里是比较重要的函数

  /* We an save a few bytes by discarding the IDLE thread's environment. */

#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
  (void)clearenv();
#endif

  return OK;
}



(4)创建初始线程任务:os_start_application()
这里写图片描述




static inline void os_start_application(void)
{
#ifdef CONFIG_BOARD_INITTHREAD
  int pid;

  /* Do the board/application initialization on a separate thread of
   * execution.
   */
  //在单独的执行线程上执行板/应用程序初始化。
  //任务创建:名字,优先级,堆栈大小,任务函数入口
  pid = kernel_thread("AppBringUp", CONFIG_BOARD_INITTHREAD_PRIORITY,  //内核线程函数,
                      CONFIG_BOARD_INITTHREAD_STACKSIZE,
                      (main_t)os_start_task, (FAR char * const *)NULL);
  ASSERT(pid > 0);

#else
  /* Do the board/application initialization on this thread of execution. */

  os_do_appstart();//在这个执行线程上执行板/应用程序初始化

#endif
}

//分析下这个函数:kernel_thread()

int kernel_thread(FAR const char *name, int priority,
                  int stack_size, main_t entry, FAR char *const argv[])
{
  return thread_create(name, TCB_FLAG_TTYPE_KERNEL, priority, stack_size,
                       entry, argv);
}
static int thread_create(FAR const char *name, uint8_t ttype, int priority,
                         int stack_size, main_t entry,
                         FAR char * const argv[])
{
  FAR struct task_tcb_s *tcb;
  pid_t pid;
  int errcode;
  int ret;

  /* Allocate a TCB for the new task. */

  tcb = (FAR struct task_tcb_s *)kmm_zalloc(sizeof(struct task_tcb_s));
  if (!tcb)
    {
      serr("ERROR: Failed to allocate TCB\n");
      errcode = ENOMEM;
      goto errout;
    }

  /* Allocate a new task group with privileges appropriate for the parent
   * thread type.
   */

#ifdef HAVE_TASK_GROUP
  ret = group_allocate(tcb, ttype);
  if (ret < 0)
    {
      errcode = -ret;
      goto errout_with_tcb;
    }
#endif

#if CONFIG_NFILE_DESCRIPTORS > 0 || CONFIG_NSOCKET_DESCRIPTORS > 0
#if 0 /* No... there are side effects */
  /* Associate file descriptors with the new task.  Exclude kernel threads;
   * kernel threads do not have file or socket descriptors.  They must use
   * SYSLOG for output and the low-level psock interfaces for network I/O.
   */

  if (ttype != TCB_FLAG_TTYPE_KERNEL)
#endif
    {
      ret = group_setuptaskfiles(tcb);
      if (ret < OK)
        {
          errcode = -ret;
          goto errout_with_tcb;
        }
    }
#endif

  /* Allocate the stack for the TCB */

  ret = up_create_stack((FAR struct tcb_s *)tcb, stack_size, ttype);
  if (ret < OK)
    {
      errcode = -ret;
      goto errout_with_tcb;
    }

  /* Initialize the task control block */

  ret = task_schedsetup(tcb, priority, task_start, entry, ttype);
  if (ret < OK)
    {
      errcode = -ret;
      goto errout_with_tcb;
    }

  /* Setup to pass parameters to the new task */

  (void)task_argsetup(tcb, name, argv);

  /* Now we have enough in place that we can join the group */

#ifdef HAVE_TASK_GROUP
  ret = group_initialize(tcb);
  if (ret < 0)
    {
      errcode = -ret;
      goto errout_with_tcb;
    }
#endif

  /* Get the assigned pid before we start the task */

  pid = (int)tcb->cmn.pid;

  /* Activate the task */

  ret = task_activate((FAR struct tcb_s *)tcb);
  if (ret < OK)
    {
      errcode = get_errno();

      /* The TCB was added to the active task list by task_schedsetup() */

      dq_rem((FAR dq_entry_t *)tcb, (FAR dq_queue_t *)&g_inactivetasks);
      goto errout_with_tcb;
    }

  return pid;

errout_with_tcb:
  sched_releasetcb((FAR struct tcb_s *)tcb, ttype);

errout:
  set_errno(errcode);
  return ERROR;
}



(5)创建初始线程任务:os_start_task()
这里写图片描述




#ifdef CONFIG_BOARD_INITTHREAD
static int os_start_task(int argc, FAR char **argv)
{
  /* Do the board/application initialization and exit */

  os_do_appstart(); //核心任务,这个将会调用FMU和IO的APP函数入口
  return OK;
}
#endif
#if defined(CONFIG_INIT_ENTRYPOINT)
static inline void os_do_appstart(void)
{
  int pid;

#ifdef CONFIG_BOARD_INITIALIZE
  /* Perform any last-minute, board-specific initialization, if so
   * configured.
   */

  board_initialize();
#endif

  /* Start the application initialization task.  In a flat build, this is
   * entrypoint is given by the definitions, CONFIG_USER_ENTRYPOINT.  In
   * the protected build, however, we must get the address of the
   * entrypoint from the header at the beginning of the user-space blob.
   */

  sinfo("Starting init thread\n");

#ifdef CONFIG_BUILD_PROTECTED
  DEBUGASSERT(USERSPACE->us_entrypoint != NULL);
  pid = task_create("init", SCHED_PRIORITY_DEFAULT,
                    CONFIG_USERMAIN_STACKSIZE, USERSPACE->us_entrypoint,
                    (FAR char * const *)NULL);
#else
  pid = task_create("init", SCHED_PRIORITY_DEFAULT,  //优先级
                    CONFIG_USERMAIN_STACKSIZE,      //堆栈大小
                    (main_t)CONFIG_USER_ENTRYPOINT, //函数入口
                    (FAR char * const *)NULL);
#endif
  ASSERT(pid > 0);
}
#elif defined(CONFIG_INIT_FILEPATH)
static inline void os_do_appstart(void)
{
  int ret;

#ifdef CONFIG_BOARD_INITIALIZE
  /* Perform any last-minute, board-specific initialization, if so
   * configured.
   */

  board_initialize();
#endif

  /* Start the application initialization program from a program in a
   * mounted file system.  Presumably the file system was mounted as part
   * of the board_initialize() operation.
   */

  sinfo("Starting init task: %s\n", CONFIG_USER_INITPATH);

  ret = exec(CONFIG_USER_INITPATH, NULL, CONFIG_INIT_SYMTAB,
             CONFIG_INIT_NEXPORTS);
  ASSERT(ret >= 0);
}



(6)核心接口CONFIG_USER_ENTRYPOINT
这里写图片描述




CONFIG_USER_ENTRYPOINT="nsh_main"

//所在的目录
这里写图片描述

CONFIG_USER_ENTRYPOINT="user_start"

//所在的目录
这里写图片描述



到这里需要分两路去研究,一路是STM32F427的nsh_main()函数,另外一路是user_start()




(7)核心接口nsh_main()函数




int nsh_main(int argc, char *argv[])
#endif
{
  int exitval = 0;
  int ret;

  /* Call all C++ static constructors */

#if defined(CONFIG_EXAMPLES_NSH_CXXINITIALIZE)
  up_cxxinitialize(); //调用C++静态构造函数
#endif

  /* Make sure that we are using our symbol table */

#if defined(CONFIG_LIBC_EXECFUNCS) && defined(CONFIG_EXECFUNCS_SYMTAB)
  exec_setsymtab(CONFIG_EXECFUNCS_SYMTAB, 0); //外部函数缺少定义
#endif

  /* Register the BINFS file system */

#if defined(CONFIG_FS_BINFS) && (CONFIG_BUILTIN)
  ret = builtin_initialize();
  if (ret < 0)
    {
     fprintf(stderr, "ERROR: builtin_initialize failed: %d\n", ret);
     exitval = 1;
   }
#endif

  /* Initialize the NSH library */

  nsh_initialize(); //初始化NSH库

  /* If the Telnet console is selected as a front-end, then start the
   * Telnet daemon UNLESS network initialization is deferred via
   * CONFIG_NSH_NETLOCAL.  In that case, the telnet daemon must be
   * started manually with the telnetd command after the network has
   * been initialized
   */

#if defined(CONFIG_NSH_TELNET) && !defined(CONFIG_NSH_NETLOCAL)
  ret = nsh_telnetstart(ADDR_FAMILY);
  if (ret < 0)
    {
     /* The daemon is NOT running.  Report the error then fail...
      * either with the serial console up or just exiting.
      */

     fprintf(stderr, "ERROR: Failed to start TELNET daemon: %d\n", ret);
     exitval = 1;
   }
#endif

  /* If the serial console front end is selected, then run it on this thread */

#ifdef CONFIG_NSH_CONSOLE
  ret = nsh_consolemain(0, NULL); //重要的函数

  /* nsh_consolemain() should not return.  So if we get here, something
   * is wrong.
   */

#if CONFIG_NFILE_DESCRIPTORS > 0
  fprintf(stderr, "ERROR: nsh_consolemain() returned: %d\n", ret);
#else
  printf("ERROR: nsh_consolemain() returned: %d\n", ret);
#endif

  exitval = 1;
#endif

  return exitval;
}

(1)int nsh_consolemain(int argc, char *argv[])

int nsh_consolemain(int argc, char *argv[])
{
  FAR struct console_stdio_s *pstate = nsh_newconsole();
  int ret;

  DEBUGASSERT(pstate != NULL);

#ifdef CONFIG_NSH_ROMFSETC
  /* Execute the start-up script */

  (void)nsh_initscript(&pstate->cn_vtbl); //执行脚本初始化

#ifndef CONFIG_NSH_DISABLESCRIPT
  /* Reset the option flags */

  pstate->cn_vtbl.np.np_flags = NSH_NP_SET_OPTIONS_INIT;
#endif
#endif

#ifdef CONFIG_NSH_USBDEV_TRACE
  /* Initialize any USB tracing options that were requested */

  usbtrace_enable(TRACE_BITSET);
#endif

  /* Execute the session */

  ret = nsh_session(pstate); //与用户交互的终端命令,我们用还是那个危机连接飞控时,可以与飞控交互的串口命令

  /* Exit upon return */

  nsh_exit(&pstate->cn_vtbl, ret);
  return ret;
}

#endif /* !HAVE_USB_CONSOLE && !HAVE_USB_KEYBOARD */

(2) (void)nsh_initscript(&pstate->cn_vtbl);

int nsh_initscript(FAR struct nsh_vtbl_s *vtbl)
{
  static bool initialized;
  bool already;
  int ret = OK;

  /* Atomic test and set of the initialized flag */

  sched_lock();
  already     = initialized;
  initialized = true;
  sched_unlock();

  /* If we have not already executed the init script, then do so now */

  if (!already)
    {
      ret = nsh_script(vtbl, "init", NSH_INITPATH); //解析脚本
    }

  return ret;
}

(3)ret = nsh_script(vtbl, “init”, NSH_INITPATH); //解析脚本
这里写图片描述


#!nsh
# Un comment and use set +e to ignore and set -e to enable 'exit on error control'
set +e
# Un comment the line below to help debug scripts by printing a trace of the script commands
#set -x
# PX4FMU startup script.
#
# NOTE: environment variable references:
#    If the dollar sign ('$') is followed by a left bracket ('{') then the
#    variable name is terminated with the right bracket character ('}').
#    Otherwise, the variable name goes to the end of the argument.
#
#
# NOTE: COMMENT LINES ARE REMOVED BEFORE STORED IN ROMFS.
#
# UART mapping on FMUv2/3/4:
#
# UART1         /dev/ttyS0      IO debug
# USART2        /dev/ttyS1      TELEM1 (flow control)
# USART3        /dev/ttyS2      TELEM2 (flow control)
# UART4
# UART7                         CONSOLE
# UART8                         SERIAL4
#
#
# UART mapping on FMUv5:
#
# UART1         /dev/ttyS0      GPS
# USART2        /dev/ttyS1      TELEM1 (flow control)
# USART3        /dev/ttyS2      TELEM2 (flow control)
# UART4         /dev/ttyS3      ?
# USART6        /dev/ttyS4      TELEM3 (flow control)
# UART7         /dev/ttyS5      ?
# UART8         /dev/ttyS6      CONSOLE

#
# Mount the procfs.
#
mount -t procfs /proc

#
# Start CDC/ACM serial driver
#
sercon

# print full system version
ver all

#
# Default to auto-start mode.
#
set MODE autostart

set TUNE_ERR ML<<CP4CP4CP4CP4CP4
set LOG_FILE /fs/microsd/bootlog.txt

#
# Try to mount the microSD card.
#
# REBOOTWORK this needs to start after the flight control loop
if mount -t vfat /dev/mmcsd0 /fs/microsd
then
    echo "[i] microSD mounted: /fs/microsd"
    if hardfault_log check
    then
        tone_alarm error
        if hardfault_log commit
        then
            hardfault_log reset
            tone_alarm stop
        fi
    else
        # Start playing the startup tune
        tone_alarm start
    fi
else
    tone_alarm MBAGP
    if mkfatfs /dev/mmcsd0
    then
        if mount -t vfat /dev/mmcsd0 /fs/microsd
        then
            echo "INFO  [init] MicroSD card formatted"
        else
            echo "ERROR [init] Format failed"
            tone_alarm MNBG
            set LOG_FILE /dev/null
        fi
    else
        set LOG_FILE /dev/null
    fi
fi

#
# Look for an init script on the microSD card.
# Disable autostart if the script found.
#
set FRC /fs/microsd/etc/rc.txt
if [ -f $FRC ]
then
    echo "INFO  [init] Executing script: ${FRC}"
    sh $FRC
    set MODE custom
fi
unset FRC

if [ $MODE == autostart ]
then

    #
    # Start the ORB (first app to start)
    #
    uorb start

    #
    # Load parameters
    #
    set PARAM_FILE /fs/microsd/params
    if mtd start
    then
        set PARAM_FILE /fs/mtd_params
    fi

    param select $PARAM_FILE
    if param load
    then
    else
        if param reset
        then
        fi
    fi

    #
    # Start system state indicator
    #
    if rgbled start
    then
    else
        if blinkm start
        then
            blinkm systemstate
        fi
    fi

    # FMUv5 may have both PWM I2C RGB LED support
    rgbled_pwm start

    #
    # Set AUTOCNF flag to use it in AUTOSTART scripts
    #
    if param compare SYS_AUTOCONFIG 1
    then
        # Wipe out params except RC* and total flight time
        param reset_nostart RC* LND_FLIGHT_T_*
        set AUTOCNF yes
    else
        set AUTOCNF no

        #
        # Release 1.4.0 transitional support:
        # set to old default if unconfigured.
        # this preserves the previous behaviour
        #
        if param compare BAT_N_CELLS 0
        then
            param set BAT_N_CELLS 3
        fi
    fi

    #
    # Set default values
    #
    set VEHICLE_TYPE none
    set MIXER none
    set MIXER_AUX none
    set OUTPUT_MODE none
    set PWM_OUT none
    set PWM_RATE p:PWM_RATE
    set PWM_DISARMED p:PWM_DISARMED
    set PWM_MIN p:PWM_MIN
    set PWM_MAX p:PWM_MAX
    set PWM_AUX_OUT none
    set PWM_AUX_RATE none
    set PWM_ACHDIS none
    set PWM_AUX_DISARMED p:PWM_AUX_DISARMED
    set PWM_AUX_MIN p:PWM_AUX_MIN
    set PWM_AUX_MAX p:PWM_AUX_MAX
    set FAILSAFE_AUX none
    set MK_MODE none
    set FMU_MODE pwm
    set AUX_MODE pwm
    set FMU_ARGS ""
    set MAVLINK_F default
    set MAVLINK_COMPANION_DEVICE /dev/ttyS2
    set MAV_TYPE none
    set FAILSAFE none
    set USE_IO no
    set LOGGER_BUF 16

    if ver hwcmp PX4FMU_V4
    then
        param set SYS_FMU_TASK 1
    fi

    if ver hwcmp PX4FMU_V4PRO
    then
        param set SYS_FMU_TASK 1
    fi

    if ver hwcmp PX4FMU_V5
    then
        param set SYS_FMU_TASK 1
        set MAVLINK_COMPANION_DEVICE /dev/ttyS3

        set LOGGER_BUF 64
        param set SDLOG_MODE 2
    fi

    if ver hwcmp CRAZYFLIE
    then
        if param compare SYS_AUTOSTART 0
        then
            param set SYS_AUTOSTART 4900
            set AUTOCNF yes
        fi
    fi

    if ver hwcmp AEROFC_V1
    then
        if param compare SYS_AUTOSTART 0
        then
            set AUTOCNF yes
        fi

        # We don't allow changing AUTOSTART as it doesn't work in
        # other configurations
        param set SYS_AUTOSTART 4070
    fi

    if ver hwcmp NXPHLITE_V3
    then
        param set SYS_FMU_TASK 1
        set MAVLINK_COMPANION_DEVICE /dev/ttyS4
    fi

    #
    # Set USE_IO flag
    #
    if param compare SYS_USE_IO 1
    then
        set USE_IO yes
    fi

    if param compare SYS_FMU_TASK 1
    then
        set FMU_ARGS "-t"
    fi

    #
    # Set parameters and env variables for selected AUTOSTART
    #
    if param compare SYS_AUTOSTART 0
    then
    else
        sh /etc/init.d/rc.autostart
    fi
    unset MODE

    #
    # If mount (gimbal) control is enabled and output mode is AUX, set the aux
    # mixer to mount (override the airframe-specific MIXER_AUX setting)
    #
    if param compare MNT_MODE_IN -1
    then
    else
        if param compare MNT_MODE_OUT 0
        then
            set MIXER_AUX mount
        fi
    fi

    #
    # Override parameters from user configuration file
    #
    set FCONFIG /fs/microsd/etc/config.txt
    if [ -f $FCONFIG ]
    then
        echo "Custom: ${FCONFIG}"
        sh $FCONFIG
    fi
    unset FCONFIG

    #
    # If autoconfig parameter was set, reset it and save parameters
    #
    if [ $AUTOCNF == yes ]
    then
        # Disable safety switch by default on Pixracer
        if ver hwcmp PX4FMU_V4
        then
            param set CBRK_IO_SAFETY 22027
        fi
        param set SYS_AUTOCONFIG 0
    fi
    unset AUTOCNF

    set IO_PRESENT no

    if [ $USE_IO == yes ]
    then
        #
        # Check if PX4IO present and update firmware if needed
        #
        if [ -f /etc/extras/px4io-v2.bin ]
        then
            set IO_FILE /etc/extras/px4io-v2.bin

            if px4io checkcrc ${IO_FILE}
            then
                echo "[init] PX4IO CRC OK" >> $LOG_FILE

                set IO_PRESENT yes
            else
                tone_alarm MLL32CP8MB

                if px4io start
                then
                    # try to safe px4 io so motor outputs dont go crazy
                    if px4io safety_on
                    then
                        # success! no-op
                    else
                        # px4io did not respond to the safety command
                        px4io stop
                    fi
                fi

                if px4io forceupdate 14662 ${IO_FILE}
                then
                    usleep 10000
                    if px4io checkcrc ${IO_FILE}
                    then
                        echo "PX4IO CRC OK after updating" >> $LOG_FILE
                        tone_alarm MLL8CDE

                        set IO_PRESENT yes
                    else
                        echo "PX4IO update failed" >> $LOG_FILE
                        tone_alarm ${TUNE_ERR}
                    fi
                else
                    echo "PX4IO update failed" >> $LOG_FILE
                    tone_alarm ${TUNE_ERR}
                fi
            fi
        fi
        unset IO_FILE

        if [ $IO_PRESENT == no ]
        then
            echo "PX4IO not found" >> $LOG_FILE
            tone_alarm ${TUNE_ERR}
        fi
    fi

    #
    # Set default output if not set
    #
    if [ $OUTPUT_MODE == none ]
    then
        if [ $USE_IO == yes ]
        then
            set OUTPUT_MODE io
        else
            set OUTPUT_MODE fmu
        fi
    fi

    if [ $OUTPUT_MODE == io -a $IO_PRESENT != yes ]
    then
        # Need IO for output but it not present, disable output
        set OUTPUT_MODE none
    fi

    if [ $OUTPUT_MODE == tap_esc ]
    then
        set FMU_MODE rcin
    fi

    set DATAMAN_OPT ""
    if ver hwcmp AEROFC_V1
    then
        set DATAMAN_OPT -i
    fi
    if ver hwcmp AEROCORE2
    then
        set DATAMAN_OPT "-f /fs/mtd_dataman"
    fi
    # waypoint storage
    # REBOOTWORK this needs to start in parallel
    if dataman start $DATAMAN_OPT
    then
    fi
    unset DATAMAN_OPT

    #
    # Sensors System (start before Commander so Preflight checks are properly run)
    # commander Needs to be this early for in-air-restarts
    if param compare SYS_HITL 1
    then
        set OUTPUT_MODE hil
        sensors start -h
        commander start --hil
    else
        if ver hwcmp PX4_SAME70XPLAINED_V1
        then
            gps start -d /dev/ttyS2
        else
            gps start
        fi
        sh /etc/init.d/rc.sensors
        commander start
    fi

    send_event start
    load_mon start

    #
    # Check if UAVCAN is enabled, default to it for ESCs
    #
    if param greater UAVCAN_ENABLE 2
    then
        set OUTPUT_MODE uavcan_esc
    fi

    # Sensors on the PWM interface bank
    if param compare SENS_EN_LL40LS 1
    then
        # clear pins 5 and 6
        set FMU_MODE pwm4
        set AUX_MODE pwm4
    fi
    if param greater TRIG_MODE 0
    then
        # We ONLY support trigger on pins 5 and 6 when simultanously using AUX for actuator output
        if param compare TRIG_PINS 56
        then
            # clear pins 5 and 6
            set FMU_MODE pwm4
            set AUX_MODE pwm4
        else
            set FMU_MODE none
            set AUX_MODE none
        fi
        camera_trigger start

        param set CAM_FBACK_MODE 1
        camera_feedback start
    fi

    # If OUTPUT_MODE == none then something is wrong with setup and we shouldn't try to enable output
    if [ $OUTPUT_MODE != none ]
    then
        if [ $OUTPUT_MODE == uavcan_esc ]
        then
            if param compare UAVCAN_ENABLE 0
            then
                echo "OVERRIDING UAVCAN_ENABLE = 3" >> $LOG_FILE
                param set UAVCAN_ENABLE 3
            fi
        fi

        if [ $OUTPUT_MODE == io -o $OUTPUT_MODE == uavcan_esc ]
        then
            if px4io start
            then
                sh /etc/init.d/rc.io
            else
                echo "PX4IO start failed" >> $LOG_FILE
                tone_alarm $TUNE_ERR
            fi
        fi

        if [ $OUTPUT_MODE == fmu ]
        then
            if fmu mode_$FMU_MODE $FMU_ARGS
            then
            else
                echo "FMU start failed" >> $LOG_FILE
                tone_alarm $TUNE_ERR
            fi
        fi

        if [ $OUTPUT_MODE == mkblctrl ]
        then
            set MKBLCTRL_ARG ""
            if [ $MKBLCTRL_MODE == x ]
            then
                set MKBLCTRL_ARG "-mkmode x"
            fi
            if [ $MKBLCTRL_MODE == + ]
            then
                set MKBLCTRL_ARG "-mkmode +"
            fi

            if mkblctrl $MKBLCTRL_ARG
            then
            else
                echo "MK start failed" >> $LOG_FILE
                tone_alarm $TUNE_ERR
            fi
            unset MKBLCTRL_ARG
        fi
        unset MK_MODE

        if [ $OUTPUT_MODE == hil ]
        then
            if pwm_out_sim mode_pwm16
            then
            else
                tone_alarm $TUNE_ERR
            fi
        fi

        #
        # Start IO or FMU for RC PPM input if needed
        #
        if [ $IO_PRESENT == yes ]
        then
            if [ $OUTPUT_MODE != io ]
            then
                if px4io start
                then
                    sh /etc/init.d/rc.io
                else
                    echo "PX4IO start failed" >> $LOG_FILE
                    tone_alarm $TUNE_ERR
                fi
            fi
        else
            if [ $OUTPUT_MODE != fmu ]
            then
                if fmu mode_${FMU_MODE} $FMU_ARGS
                then
                else
                    echo "FMU mode_${FMU_MODE} start failed" >> $LOG_FILE
                    tone_alarm $TUNE_ERR
                fi
            fi
        fi
    fi

    if [ $MAVLINK_F == default ]
    then
        # Normal mode, use baudrate 57600 (default) and data rate 1000 bytes/s
        set MAVLINK_F "-r 1200 -f"

        # Avoid using ttyS1 for MAVLink on FMUv4
        if ver hwcmp PX4FMU_V4
        then
            set MAVLINK_F "-r 1200 -d /dev/ttyS1"
            # Start MAVLink on Wifi (ESP8266 port)
            mavlink start -r 20000 -b 921600 -d /dev/ttyS0
        fi

        if ver hwcmp AEROFC_V1
        then
            set MAVLINK_F "-r 1200 -d /dev/ttyS3"
        fi

        if ver hwcmp CRAZYFLIE
        then
            # Avoid using either of the two available serials
            set MAVLINK_F none
        fi
    fi

    if [ "x$MAVLINK_F" == xnone ]
    then
    else
        mavlink start ${MAVLINK_F}
    fi
    unset MAVLINK_F

    #
    # MAVLink onboard / TELEM2
    #
    # XXX We need a better way for runtime eval of shell variables,
    # but this works for now
    if param compare SYS_COMPANION 10
    then
        frsky_telemetry start -d ${MAVLINK_COMPANION_DEVICE}
    fi
    if param compare SYS_COMPANION 20
    then
        syslink start
        mavlink start -d /dev/bridge0 -b 57600 -m osd -r 40000
    fi
    if param compare SYS_COMPANION 921600
    then
        if ver hwcmp AEROFC_V1
        then
            if protocol_splitter start ${MAVLINK_COMPANION_DEVICE}
            then
                mavlink start -d /dev/mavlink -b 921600 -m onboard -r 5000 -x
                micrortps_client start -d /dev/rtps -b 921600 -l -1 -s 2000
            else
                 mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 921600 -m onboard -r 80000 -x -f
            fi
        else
            mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 921600 -m onboard -r 80000 -x -f
        fi
    fi
    if param compare SYS_COMPANION 57600
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 57600 -m onboard -r 5000 -x -f
    fi
    if param compare SYS_COMPANION 460800
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 460800 -m onboard -r 5000 -x -f
    fi
    if param compare SYS_COMPANION 157600
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 57600 -m osd -r 1000
    fi
    if param compare SYS_COMPANION 257600
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 57600 -m magic -r 5000 -x -f
    fi
    if param compare SYS_COMPANION 319200
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 19200 -r 1000 -f
    fi
    if param compare SYS_COMPANION 338400
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 38400 -r 1000 -f
    fi
    if param compare SYS_COMPANION 357600
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 57600 -r 1000 -f
    fi
    if param compare SYS_COMPANION 3115200
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 115200 -r 1000 -f
    fi
    if param compare SYS_COMPANION 419200
    then
        iridiumsbd start -d /dev/ttyS2
        mavlink start -d /dev/iridium -b 19200 -m iridium -r 10
    fi
    if param compare SYS_COMPANION 1921600
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 921600 -r 20000
    fi
    if param compare SYS_COMPANION 1500000
    then
        mavlink start -d ${MAVLINK_COMPANION_DEVICE} -b 1500000 -m onboard -r 10000 -x -f
    fi

    unset MAVLINK_COMPANION_DEVICE

    #
    # Starting stuff according to UAVCAN_ENABLE value
    #
    if param greater UAVCAN_ENABLE 0
    then
        if uavcan start
        then
            set LOGGER_BUF 6
            uavcan start fw
        else
            tone_alarm ${TUNE_ERR}
        fi
    fi

    if ver hwcmp PX4FMU_V4
    then
        frsky_telemetry start -d /dev/ttyS6
    fi

    if ver hwcmp MINDPX_V2
    then
        frsky_telemetry start -d /dev/ttyS6
    fi

    if ver hwcmp PX4FMU_V2
    then
        # Check for flow sensor - as it is a background task, launch it last
        px4flow start &
    fi

    if ver hwcmp PX4FMU_V4
    then
        # Check for flow sensor - as it is a background task, launch it last
        px4flow start &
    fi

    if ver hwcmp PX4FMU_V4PRO
    then
        # Check for flow sensor - as it is a background task, launch it last
        px4flow start &
    fi

    if ver hwcmp MINDPX_V2
    then
        px4flow start &
    fi

    if ver hwcmp AEROFC_V1
    then
        # don't start mavlink ttyACM0 on aerofc_v1
    else
        # Start MAVLink
        mavlink start -r 800000 -d /dev/ttyACM0 -m config -x
    fi

    #
    # Logging
    #
    if param compare SYS_LOGGER 0
    then
        sdlog2 start -r 100 -a -b 9 -t
    else
        set LOGGER_ARGS ""

        if param compare SDLOG_MODE 1
        then
            set LOGGER_ARGS "-e"
        fi

        if param compare SDLOG_MODE 2
        then
            set LOGGER_ARGS "-f"
        fi

        if ver hwcmp AEROFC_V1
        then
            set LOGGER_ARGS "-m mavlink"
        fi

        logger start -b ${LOGGER_BUF} -t ${LOGGER_ARGS}

        unset LOGGER_BUF
        unset LOGGER_ARGS
    fi

    #
    # Fixed wing setup
    #
    if [ $VEHICLE_TYPE == fw ]
    then
        if [ $MIXER == none ]
        then
            # Set default mixer for fixed wing if not defined
            set MIXER AERT
        fi

        if [ $MAV_TYPE == none ]
        then
            # Use MAV_TYPE = 1 (fixed wing) if not defined
            set MAV_TYPE 1
        fi

        param set MAV_TYPE ${MAV_TYPE}

        # Load mixer and configure outputs
        sh /etc/init.d/rc.interface

        # Start standard fixedwing apps
        sh /etc/init.d/rc.fw_apps
    fi

    #
    # Multicopters setup
    #
    if [ $VEHICLE_TYPE == mc ]
    then
        if [ $MIXER == none ]
        then
            echo "Mixer undefined"
        fi

        if [ $MAV_TYPE == none ]
        then
            # Use mixer to detect vehicle type
            if [ $MIXER == quad_x -o $MIXER == quad_+ ]
            then
                set MAV_TYPE 2
            fi
            if [ $MIXER == quad_w -o $MIXER == quad_dc ]
            then
                set MAV_TYPE 2
            fi
            if [ $MIXER == quad_h ]
            then
                set MAV_TYPE 2
            fi
            if [ $MIXER == tri_y_yaw- -o $MIXER == tri_y_yaw+ ]
            then
                set MAV_TYPE 15
            fi
            if [ $MIXER == hexa_x -o $MIXER == hexa_+ ]
            then
                set MAV_TYPE 13
            fi
            if [ $MIXER == hexa_cox ]
            then
                set MAV_TYPE 13
            fi
            if [ $MIXER == octo_x -o $MIXER == octo_+ ]
            then
                set MAV_TYPE 14
            fi
            if [ $MIXER == octo_cox -o $MIXER == octo_cox_w ]
            then
                set MAV_TYPE 14
            fi
            if [ $MIXER == coax ]
            then
                set MAV_TYPE 3
            fi
        fi

        # Still no MAV_TYPE found
        if [ $MAV_TYPE == none ]
        then
            echo "Unknown MAV_TYPE"
            param set MAV_TYPE 2
        else
            param set MAV_TYPE ${MAV_TYPE}
        fi

        # Load mixer and configure outputs
        sh /etc/init.d/rc.interface

        # Start standard multicopter apps
        sh /etc/init.d/rc.mc_apps
    fi

    #
    # VTOL setup
    #
    if [ $VEHICLE_TYPE == vtol ]
    then
        if [ $MIXER == none ]
        then
            echo "VTOL mixer undefined"
        fi

        if [ $MAV_TYPE == none ]
        then
            # Use mixer to detect vehicle type
            if [ $MIXER == caipirinha_vtol ]
            then
                set MAV_TYPE 19
            fi
            if [ $MIXER == firefly6 ]
            then
                set MAV_TYPE 21
            fi
            if [ $MIXER == quad_x_pusher_vtol ]
            then
                set MAV_TYPE 22
            fi
        fi

        # Still no MAV_TYPE found
        if [ $MAV_TYPE == none ]
        then
            echo "Unknown MAV_TYPE"
            param set MAV_TYPE 19
        else
            param set MAV_TYPE ${MAV_TYPE}
        fi

        # Load mixer and configure outputs
        sh /etc/init.d/rc.interface

        # Start standard vtol apps
        sh /etc/init.d/rc.vtol_apps
    fi

    #
    # UGV setup
    #
    if [ $VEHICLE_TYPE == ugv ]
    then
        if [ $MIXER == none ]
        then
            # Set default mixer for UGV if not defined
            set MIXER ugv_generic
        fi

        if [ $MAV_TYPE == none ]
        then
            # Use MAV_TYPE = 10 (UGV) if not defined
            set MAV_TYPE 10
        fi

        param set MAV_TYPE ${MAV_TYPE}

        # Load mixer and configure outputs
        sh /etc/init.d/rc.interface

        # Start standard UGV apps
        sh /etc/init.d/rc.ugv_apps
    fi

    #
    # For snapdragon, we need a passthrough mode
    # Do not run any mavlink instances since we need the serial port for
    # communication with Snapdragon.
    #
    if [ $VEHICLE_TYPE == passthrough ]
    then
        mavlink stop-all
        commander stop

        # Stop multicopter attitude controller if it is running, the controls come
        # from Snapdragon.
        if mc_att_control stop
        then
        fi

        # Start snapdragon interface on serial port.
        if ver hwcmp PX4FMU_V2
        then
            # On Pixfalcon use the standard telemetry port (Telem 1).
            snapdragon_rc_pwm start -d /dev/ttyS1
            px4io start
        fi

        if ver hwcmp PX4FMU_V4
        then
            # On Pixracer use Telem 2 port (TL2).
            snapdragon_rc_pwm start -d /dev/ttyS2
            fmu mode_pwm4 $FMU_ARGS
        fi

        pwm failsafe -c 1234 -p 900
        pwm disarmed -c 1234 -p 900

        # Arm straightaway.
        pwm arm
        # Use 400 Hz PWM on all channels.
        pwm rate -a -r 400
    fi

    unset MIXER
    unset MAV_TYPE
    unset OUTPUT_MODE

    #
    # Start the navigator
    #
    navigator start

    #
    # Generic setup (autostart ID not found)
    #
    if [ $VEHICLE_TYPE == none ]
    then
        echo "No autostart ID found"
        ekf2 start
    fi

    # Start any custom addons
    set FEXTRAS /fs/microsd/etc/extras.txt
    if [ -f $FEXTRAS ]
    then
        echo "Addons script: ${FEXTRAS}"
        sh $FEXTRAS
    fi
    unset FEXTRAS

    if ver hwcmp CRAZYFLIE
    then
        # CF2 shouldn't have an sd card
    else

        if ver hwcmp AEROCORE2
        then
            # AEROCORE2 shouldn't have an sd card
        else

            # Run no SD alarm
            if [ $LOG_FILE == /dev/null ]
            then
                # Play SOS
                tone_alarm error
            fi

        fi

    fi

    #
    # Check if we should start a thermal calibration
    # TODO move further up and don't start unnecessary services if we are calibrating
    #
    set TEMP_CALIB_ARGS ""
    if param compare SYS_CAL_GYRO 1
    then
        set TEMP_CALIB_ARGS "${TEMP_CALIB_ARGS} -g"
        param set SYS_CAL_GYRO 0
    fi
    if param compare SYS_CAL_ACCEL 1
    then
        set TEMP_CALIB_ARGS "${TEMP_CALIB_ARGS} -a"
        param set SYS_CAL_ACCEL 0
    fi
    if param compare SYS_CAL_BARO 1
    then
        set TEMP_CALIB_ARGS "${TEMP_CALIB_ARGS} -b"
        param set SYS_CAL_BARO 0
    fi
    if [ "x$TEMP_CALIB_ARGS" != "x" ]
    then
        send_event temperature_calibration ${TEMP_CALIB_ARGS}
    fi
    unset TEMP_CALIB_ARGS

    # vmount to control mounts such as gimbals, disabled by default.
    if param compare MNT_MODE_IN -1
    then
    else
        if vmount start
        then
        fi
    fi

# End of autostart
fi

# There is no further script processing, so we can free some RAM
# XXX potentially unset all script variables.
unset TUNE_ERR

# Boot is complete, inform MAVLink app(s) that the system is now fully up and running
mavlink boot_complete

int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd,
               FAR const char *path)
{
  FAR char *fullpath;
  FAR FILE *savestream;
  FAR char *buffer;
  FAR char *pret;
  int ret = ERROR;

  /* The path to the script may be relative to the current working directory */

  fullpath = nsh_getfullpath(vtbl, path);
  if (!fullpath)
    {
      return ERROR;
    }

  /* Get a reference to the common input buffer */

  buffer = nsh_linebuffer(vtbl);
  if (buffer)
    {
      /* Save the parent stream in case of nested script processing */

      savestream = vtbl->np.np_stream;

      /* Open the file containing the script */

      vtbl->np.np_stream = fopen(fullpath, "r");
      if (!vtbl->np.np_stream)
        {
          nsh_output(vtbl, g_fmtcmdfailed, cmd, "fopen", NSH_ERRNO);

          /* Free the allocated path */

          nsh_freefullpath(fullpath);

          /* Restore the parent script stream */

          vtbl->np.np_stream = savestream;
          return ERROR;
        }

      /* Loop, processing each command line in the script file (or
       * until an error occurs)
       */

      do
        {
          /* Flush any output generated by the previous line */

          fflush(stdout);

#ifndef CONFIG_NSH_DISABLE_LOOPS
          /* Get the current file position.  This is used to control
           * looping.  If a loop begins in the next line, then this file
           * offset will be needed to locate the top of the loop in the
           * script file.  Note that ftell will return -1 on failure.
           */

          vtbl->np.np_foffs = ftell(vtbl->np.np_stream);
          vtbl->np.np_loffs = 0;

          if (vtbl->np.np_foffs < 0)
            {
              nsh_output(vtbl, g_fmtcmdfailed, "loop", "ftell", NSH_ERRNO);
            }
#endif

          /* Now read the next line from the script file */

          pret = fgets(buffer, CONFIG_NSH_LINELEN, vtbl->np.np_stream);
          if (pret)
            {
              /* Parse process the command.  NOTE:  this is recursive...
               * we got to cmd_sh via a call to nsh_parse.  So some
               * considerable amount of stack may be used.
               */

              if ((vtbl->np.np_flags & NSH_PFLAG_SILENT) == 0)
                {
                  nsh_output(vtbl,"%s", buffer);
                }

              ret = nsh_parse(vtbl, buffer);
            }
        }
      while (pret && (ret == OK || (vtbl->np.np_flags & NSH_PFLAG_IGNORE)));

      /* Close the script file */

      fclose(vtbl->np.np_stream);

      /* Restore the parent script stream */

      vtbl->np.np_stream = savestream;
    }

  /* Free the allocated path */

  nsh_freefullpath(fullpath);
  return ret;
}



(8)核心接口user_start()函数




//所在的目录
这里写图片描述


可以看出IO只是FMU的一个核心模块。所以FMU与IO怎么启动的过程是我们需要核心研究的




int user_start(int argc, char *argv[])
{
    /* configure the first 8 PWM outputs (i.e. all of them) */
    up_pwm_servo_init(0xff); //配置8路PWM输出

#if defined(CONFIG_HAVE_CXX) && defined(CONFIG_HAVE_CXXINITIALIZE)

    /* run C++ ctors before we go any further */

    up_cxxinitialize(); //运行C++库

#   if defined(CONFIG_EXAMPLES_NSH_CXXINITIALIZE)
#       error CONFIG_EXAMPLES_NSH_CXXINITIALIZE Must not be defined! Use CONFIG_HAVE_CXX and CONFIG_HAVE_CXXINITIALIZE.
#   endif

#else
#  error platform is dependent on c++ both CONFIG_HAVE_CXX and CONFIG_HAVE_CXXINITIALIZE must be defined.
#endif

    /* reset all to zero */
    memset(&system_state, 0, sizeof(system_state));//复位系统存储

    /* configure the high-resolution time/callout interface */
    hrt_init(); //配置高速时钟

    /* calculate our fw CRC so FMU can decide if we need to update */
    calculate_fw_crc(); //计算我用的crc时钟,判断fmu是否更新

    /*
     * Poll at 1ms intervals for received bytes that have not triggered
     * a DMA event.对于未触发的接收字节,以1ms间隔进行轮询
     */
#ifdef CONFIG_ARCH_DMA
    hrt_call_every(&serial_dma_call, 1000, 1000, (hrt_callout)stm32_serial_dma_poll, NULL);
#endif

    /* print some startup info */
    syslog(LOG_INFO, "\nPX4IO: starting\n");

    /* default all the LEDs to off while we start */
    LED_AMBER(false); //关闭所有的灯
    LED_BLUE(false);
    LED_SAFETY(false);
#ifdef GPIO_LED4
    LED_RING(false);
#endif

    /* turn on servo power (if supported) */
#ifdef POWER_SERVO
    POWER_SERVO(true); //打开servo电源
#endif

    /* turn off S.Bus out (if supported) */
#ifdef ENABLE_SBUS_OUT
    ENABLE_SBUS_OUT(false); //关闭SBUS
#endif

    /* start the safety switch handler */
    safety_init(); //启动安全开关处理器

    /* initialise the control inputs */
    controls_init(); //初始化控制输入

    /* set up the ADC */
    adc_init(); //初始化ADC

    /* start the FMU interface */
    interface_init();//串口初始化

    /* add a performance counter for mixing */
    perf_counter_t mixer_perf = perf_alloc(PC_ELAPSED, "mix"); //添加用于混合的性能计数器

    /* add a performance counter for controls */
    perf_counter_t controls_perf = perf_alloc(PC_ELAPSED, "controls");//为控件添加性能计数器

    /* and one for measuring the loop rate */
    perf_counter_t loop_perf = perf_alloc(PC_INTERVAL, "loop"); //一种测量环路速率的方法

    struct mallinfo minfo = mallinfo();
    r_page_status[PX4IO_P_STATUS_FREEMEM] = minfo.mxordblk;
    syslog(LOG_INFO, "MEM: free %u, largest %u\n", minfo.mxordblk, minfo.fordblks);

    /* initialize PWM limit lib */
    pwm_limit_init(&pwm_limit); //初始化PWM限制

    /*
     *    P O L I C E    L I G H T S
     *
     * Not enough memory, lock down.
     *
     * We might need to allocate mixers later, and this will
     * ensure that a developer doing a change will notice
     * that he just burned the remaining RAM with static
     * allocations. We don't want him to be able to
     * get past that point. This needs to be clearly
     * documented in the dev guide.
     *
     */
    if (minfo.mxordblk < 600) 
    {

        syslog(LOG_ERR, "ERR: not enough MEM");
        bool phase = false;

        while (true) 
        {

            if (phase) 
            {
                LED_AMBER(true);
                LED_BLUE(false);

            } else 
            {
                LED_AMBER(false);
                LED_BLUE(true);
            }

            up_udelay(250000);

            phase = !phase;
        }
    }

    /* Start the failsafe led init */
    failsafe_led_init();//启动故障安全LED初始化

    /*
     * Run everything in a tight loop.把每件事都搞得一团糟
     */

    uint64_t last_debug_time = 0;
    uint64_t last_heartbeat_time = 0;
    uint64_t last_loop_time = 0;

    for (;;) 
    {
        dt = (hrt_absolute_time() - last_loop_time) / 1000000.0f;
        last_loop_time = hrt_absolute_time();

        if (dt < 0.0001f) 
        {
            dt = 0.0001f;

        } else if (dt > 0.02f) 
        {
            dt = 0.02f;
        }

        /* track the rate at which the loop is running */
        perf_count(loop_perf);//计算循环运行时间

        /* kick the mixer */
        perf_begin(mixer_perf);
        mixer_tick();
        perf_end(mixer_perf);

        /* kick the control inputs */
        perf_begin(controls_perf);
        controls_tick();
        perf_end(controls_perf);

        /* some boards such as Pixhawk 2.1 made
           the unfortunate choice to combine the blue led channel with
           the IMU heater. We need a software hack to fix the hardware hack
           by allowing to disable the LED / heater.
         */
        if (r_page_setup[PX4IO_P_SETUP_THERMAL] == PX4IO_THERMAL_IGNORE) {
            /*
              blink blue LED at 4Hz in normal operation. When in
              override blink 4x faster so the user can clearly see
              that override is happening. This helps when
              pre-flight testing the override system
             */
            uint32_t heartbeat_period_us = 250 * 1000UL;

            if (r_status_flags & PX4IO_P_STATUS_FLAGS_OVERRIDE) {
                heartbeat_period_us /= 4;
            }

            if ((hrt_absolute_time() - last_heartbeat_time) > heartbeat_period_us) {
                last_heartbeat_time = hrt_absolute_time();
                heartbeat_blink();
            }

        } else if (r_page_setup[PX4IO_P_SETUP_THERMAL] < PX4IO_THERMAL_FULL) {
            /* switch resistive heater off */
            LED_BLUE(false);

        } else {
            /* switch resistive heater hard on */
            LED_BLUE(true);
        }

        update_mem_usage();

        ring_blink();

        check_reboot();

        /* check for debug activity (default: none) */
        show_debug_messages();

        /* post debug state at ~1Hz - this is via an auxiliary serial port
         * DEFAULTS TO OFF!
         */
        if (hrt_absolute_time() - last_debug_time > (1000 * 1000)) {

            isr_debug(1, "d:%u s=0x%x a=0x%x f=0x%x m=%u",
                  (unsigned)r_page_setup[PX4IO_P_SETUP_SET_DEBUG],
                  (unsigned)r_status_flags,
                  (unsigned)r_setup_arming,
                  (unsigned)r_setup_features,
                  (unsigned)mallinfo().mxordblk);
            last_debug_time = hrt_absolute_time();
        }
    }
}

可以看出px4所有的任务都是以任务函数执行,都在那个rcs里面,然而apm的不是这样
PX4的运行过程就先说到这里,后面会继续研究每个函数的作用,这里只是大致梳理一下过程。与APM形成比较




2.APM固件启动过程




(1)void __start(void)


void __start(void)
{
  const uint32_t *src;
  uint32_t *dest;

#ifdef CONFIG_ARMV7M_STACKCHECK
  /* Set the stack limit before we attempt to call any functions */

  __asm__ volatile ("sub r10, sp, %0" : : "r" (CONFIG_IDLETHREAD_STACKSIZE - 64) : );
#endif

  /* Configure the uart so that we can get debug output as soon as possible */

  stm32_clockconfig();//初始化时钟
  stm32_fpuconfig();  //配置FMU
  stm32_lowsetup();   //初始化基本串口
  stm32_gpioinit();   //配置IO
  showprogress('A');

  /* Clear .bss.  We'll do this inline (vs. calling memset) just to be
   * certain that there are no issues with the state of global variables.
   */

  for (dest = &_sbss; dest < &_ebss; )
    {
      *dest++ = 0;
    }

  showprogress('B');

  /* Move the intialized data section from his temporary holding spot in
   * FLASH into the correct place in SRAM.  The correct place in SRAM is
   * give by _sdata and _edata.  The temporary location is in FLASH at the
   * end of all of the other read-only data (.text, .rodata) at _eronly.
   */

  for (src = &_eronly, dest = &_sdata; dest < &_edata; )
    {
      *dest++ = *src++;
    }

  showprogress('C');

  /* Perform early serial initialization */

#ifdef USE_EARLYSERIALINIT
  up_earlyserialinit();
#endif
  showprogress('D');

  /* For the case of the separate user-/kernel-space build, perform whatever
   * platform specific initialization of the user memory is required.
   * Normally this just means initializing the user space .data and .bss
   * segments.
   */

#ifdef CONFIG_NUTTX_KERNEL
  stm32_userspace();
  showprogress('E');
#endif

  /* Initialize onboard resources */

  stm32_boardinitialize(); //板层初始化
  showprogress('F');

  /* Then start NuttX */

  showprogress('\r');
  showprogress('\n');
  os_start();   //启动Nuttx

  /* Shoulnd't get here */

  for(;;);
}

(2)os_start(); //启动Nuttx


****************************************************************************/

void os_start(void)
{
  int i;

  slldbg("Entry\n");

  /* Initialize RTOS Data ***************************************************/
  /* Initialize all task lists */

  dq_init(&g_readytorun); /
  dq_init(&g_pendingtasks);
  dq_init(&g_waitingforsemaphore);
#ifndef CONFIG_DISABLE_SIGNALS
  dq_init(&g_waitingforsignal);
#endif
#ifndef CONFIG_DISABLE_MQUEUE
  dq_init(&g_waitingformqnotfull);
  dq_init(&g_waitingformqnotempty);
#endif
#ifdef CONFIG_PAGING
  dq_init(&g_waitingforfill);
#endif
  dq_init(&g_inactivetasks);
  sq_init(&g_delayed_kufree);
#if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_MM_KERNEL_HEAP)
  sq_init(&g_delayed_kfree);
#endif

  /* Initialize the logic that determine unique process IDs. */

  g_lastpid = 0;
  for (i = 0; i < CONFIG_MAX_TASKS; i++)
    {
      g_pidhash[i].tcb = NULL;
      g_pidhash[i].pid = INVALID_PROCESS_ID;
    }

  /* Assign the process ID of ZERO to the idle task */

  g_pidhash[ PIDHASH(0)].tcb = &g_idletcb.cmn;
  g_pidhash[ PIDHASH(0)].pid = 0;

  /* Initialize the IDLE task TCB *******************************************/
  /* Initialize a TCB for this thread of execution.  NOTE:  The default
   * value for most components of the g_idletcb are zero.  The entire
   * structure is set to zero.  Then only the (potentially) non-zero
   * elements are initialized. NOTE:  The idle task is the only task in
   * that has pid == 0 and sched_priority == 0.
   */

  bzero((void*)&g_idletcb, sizeof(struct task_tcb_s));
  g_idletcb.cmn.task_state = TSTATE_TASK_RUNNING;
  g_idletcb.cmn.entry.main = (main_t)os_start;

  /* Set the IDLE task name */

#if CONFIG_TASK_NAME_SIZE > 0
  strncpy(g_idletcb.cmn.name, g_idlename, CONFIG_TASK_NAME_SIZE-1);
#endif /* CONFIG_TASK_NAME_SIZE */

  /* Configure the task name in the argument list.  The IDLE task does
   * not really have an argument list, but this name is still useful
   * for things like the NSH PS command.
   *
   * In the kernel mode build, the arguments are saved on the task's stack
   * and there is no support that yet.
   */

#if defined(CONFIG_CUSTOM_STACK) || !defined(CONFIG_NUTTX_KERNEL)
#if CONFIG_TASK_NAME_SIZE > 0
  g_idletcb.argv[0] = g_idletcb.cmn.name;
#else
  g_idletcb.argv[0] = (char*)g_idlename;
#endif /* CONFIG_TASK_NAME_SIZE */
#endif /* CONFIG_CUSTOM_STACK || !CONFIG_NUTTX_KERNEL */

  /* Then add the idle task's TCB to the head of the ready to run list */

  dq_addfirst((FAR dq_entry_t*)&g_idletcb, (FAR dq_queue_t*)&g_readytorun);

  /* Initialize the processor-specific portion of the TCB */

  up_initial_state(&g_idletcb.cmn);

  /* Initialize RTOS facilities *********************************************/
  /* Initialize the semaphore facility(if in link).  This has to be done
   * very early because many subsystems depend upon fully functional
   * semaphores.
   */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (sem_initialize != NULL)
#endif
    {
      sem_initialize();
    }

  /* Initialize the memory manager */

  {
    FAR void *heap_start;
    size_t heap_size;

    /* Get the user-mode heap from the platform specific code and configure
     * the user-mode memory allocator.
     */

    up_allocate_heap(&heap_start, &heap_size);
    kumm_initialize(heap_start, heap_size);

#if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_MM_KERNEL_HEAP)
    /* Get the kernel-mode heap from the platform specific code and configure
     * the kernel-mode memory allocator.
     */

    up_allocate_kheap(&heap_start, &heap_size);
    kmm_initialize(heap_start, heap_size);
#endif
  }

  /* Initialize tasking data structures */

#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (task_initialize != NULL)
#endif
    {
      task_initialize();
    }
#endif

  /* Initialize the interrupt handling subsystem (if included) */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (irq_initialize != NULL)
#endif
    {
      irq_initialize();
    }

  /* Initialize the watchdog facility (if included in the link) */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (wd_initialize != NULL)
#endif
    {
      wd_initialize();
    }

  /* Initialize the POSIX timer facility (if included in the link) */

#ifndef CONFIG_DISABLE_CLOCK
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (clock_initialize != NULL)
#endif
    {
      clock_initialize();
    }
#endif

#ifndef CONFIG_DISABLE_POSIX_TIMERS
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (timer_initialize != NULL)
#endif
    {
      timer_initialize();
    }
#endif

  /* Initialize the signal facility (if in link) */

#ifndef CONFIG_DISABLE_SIGNALS
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (sig_initialize != NULL)
#endif
    {
      sig_initialize();
    }
#endif

  /* Initialize the named message queue facility (if in link) */

#ifndef CONFIG_DISABLE_MQUEUE
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (mq_initialize != NULL)
#endif
    {
      mq_initialize();
    }
#endif

  /* Initialize the thread-specific data facility (if in link) */

#ifndef CONFIG_DISABLE_PTHREAD
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (pthread_initialize != NULL)
#endif
    {
      pthread_initialize();
    }
#endif

  /* Initialize the file system (needed to support device drivers) */

#if CONFIG_NFILE_DESCRIPTORS > 0
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (fs_initialize != NULL)
#endif
    {
      fs_initialize();
    }
#endif

  /* Initialize the network system */

#ifdef CONFIG_NET
#if 0
  if (net_initialize != NULL)
#endif
    {
      net_initialize();
    }
#endif

  /* The processor specific details of running the operating system
   * will be handled here.  Such things as setting up interrupt
   * service routines and starting the clock are some of the things
   * that are different for each  processor and hardware platform.
   */

  up_initialize();

  /* Initialize the C libraries (if included in the link).  This
   * is done last because the libraries may depend on the above.
   */

#ifdef CONFIG_HAVE_WEAKFUNCTIONS
  if (lib_initialize != NULL)
#endif
    {
      lib_initialize();
    }

  /* IDLE Group Initialization **********************************************/
  /* Allocate the IDLE group and suppress child status. */

#ifdef HAVE_TASK_GROUP
  DEBUGVERIFY(group_allocate(&g_idletcb));
#endif

  /* Create stdout, stderr, stdin on the IDLE task.  These will be
   * inherited by all of the threads created by the IDLE task.
   */

  DEBUGVERIFY(group_setupidlefiles(&g_idletcb));

  /* Complete initialization of the IDLE group.  Suppress retention
   * of child status in the IDLE group.
   */

#ifdef HAVE_TASK_GROUP
  DEBUGVERIFY(group_initialize(&g_idletcb));
  g_idletcb.cmn.group->tg_flags = GROUP_FLAG_NOCLDWAIT;
#endif

  /* Bring Up the System ****************************************************/
  /* Create initial tasks and bring-up the system */

  DEBUGVERIFY(os_bringup());

  /* The IDLE Loop **********************************************************/
  /* When control is return to this point, the system is idle. */

  sdbg("Beginning Idle Loop\n");
  for (;;)
    {
      /* Perform garbage collection (if it is not being done by the worker
       * thread).  This cleans-up memory de-allocations that were queued
       * because they could not be freed in that execution context (for
       * example, if the memory was freed from an interrupt handler).
       */

#ifndef CONFIG_SCHED_WORKQUEUE
      /* We must have exclusive access to the memory manager to do this
       * BUT the idle task cannot wait on a semaphore.  So we only do
       * the cleanup now if we can get the semaphore -- this should be
       * possible because if the IDLE thread is running, no other task is!
       */

      if (kmm_trysemaphore() == 0)
        {
          sched_garbagecollection();
          kmm_givesemaphore();
        }
#endif

      /* Perform any processor-specific idle state operations */

      up_idle();
    }
}

这里写图片描述
这里写图片描述


(3)os_bringup()函数


int os_bringup(void)
{
  int taskid;

  /* Setup up the initial environment for the idle task.  At present, this
   * may consist of only the initial PATH variable.  The PATH variable is
   * (probably) not used by the IDLE task.  However, the environment
   * containing the PATH variable will be inherited by all of the threads
   * created by the IDLE task.
   */

#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
  (void)setenv("PATH", CONFIG_PATH_INITIAL, 1);
#endif

  /* Start the page fill worker kernel thread that will resolve page faults.
   * This should always be the first thread started because it may have to
   * resolve page faults in other threads
   */

#ifdef CONFIG_PAGING
  svdbg("Starting paging thread\n");

  g_pgworker = KERNEL_THREAD("pgfill", CONFIG_PAGING_DEFPRIO,
                             CONFIG_PAGING_STACKSIZE,
                             (main_t)pg_worker, (FAR char * const *)NULL);
  DEBUGASSERT(g_pgworker > 0);
#endif

  /* Start the worker thread that will serve as the device driver "bottom-
   * half" and will perform misc garbage clean-up.
   */

#ifdef CONFIG_SCHED_WORKQUEUE
#ifdef CONFIG_SCHED_HPWORK

#ifdef CONFIG_SCHED_LPWORK
  svdbg("Starting high-priority kernel worker thread\n");
#else
  svdbg("Starting kernel worker thread\n");
#endif

  g_work[HPWORK].pid = KERNEL_THREAD(HPWORKNAME, CONFIG_SCHED_WORKPRIORITY,
                                     CONFIG_SCHED_WORKSTACKSIZE,
                                     (main_t)work_hpthread, (FAR char * const *)NULL);
  DEBUGASSERT(g_work[HPWORK].pid > 0);

  /* Start a lower priority worker thread for other, non-critical continuation
   * tasks
   */

#ifdef CONFIG_SCHED_LPWORK

  svdbg("Starting low-priority kernel worker thread\n");

  g_work[LPWORK].pid = KERNEL_THREAD(LPWORKNAME, CONFIG_SCHED_LPWORKPRIORITY,
                                     CONFIG_SCHED_LPWORKSTACKSIZE,
                                     (main_t)work_lpthread, (FAR char * const *)NULL);
  DEBUGASSERT(g_work[LPWORK].pid > 0);

#endif /* CONFIG_SCHED_LPWORK */
#endif /* CONFIG_SCHED_HPWORK */

#if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_SCHED_USRWORK)
  /* Start the user-space work queue */

  DEBUGASSERT(USERSPACE->work_usrstart != NULL);
  taskid = USERSPACE->work_usrstart();
  DEBUGASSERT(taskid > 0);
#endif

#endif /* CONFIG_SCHED_WORKQUEUE */

  /* Once the operating system has been initialized, the system must be
   * started by spawning the user init thread of execution.  This is the
   * first user-mode thead.
   */

  svdbg("Starting init thread\n");

  /* Perform any last-minute, board-specific initialization, if so
   * configured.
   */

#ifdef CONFIG_BOARD_INITIALIZE
  board_initialize();
#endif

  /* Start the default application.  In a flat build, this is entrypoint
   * is given by the definitions, CONFIG_USER_ENTRYPOINT.  In the kernel
   * build, however, we must get the address of the entrypoint from the
   * header at the beginning of the user-space blob.
   */

#ifdef CONFIG_NUTTX_KERNEL
  DEBUGASSERT(USERSPACE->us_entrypoint != NULL);
  taskid = TASK_CREATE("init", SCHED_PRIORITY_DEFAULT,
                       CONFIG_USERMAIN_STACKSIZE, USERSPACE->us_entrypoint, //启动入口us_entrypoint = (main_t)CONFIG_USER_ENTRYPOINT,
                       (FAR char * const *)NULL);
#else
  taskid = TASK_CREATE("init", SCHED_PRIORITY_DEFAULT,
                       CONFIG_USERMAIN_STACKSIZE,
                       (main_t)CONFIG_USER_ENTRYPOINT, //启动入口
                       (FAR char * const *)NULL);
#endif
  ASSERT(taskid > 0);

  /* We an save a few bytes by discarding the IDLE thread's environment. */

#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)
  (void)clearenv();
#endif

  return OK;
}

这里写图片描述


CONFIG_USER_ENTRYPOINT=”nsh_main”
CONFIG_USER_ENTRYPOINT=”user_start”


这里写图片描述


(4)nsh_mian()函数

int nsh_main(int argc, char *argv[])
{
  int exitval = 0;
  int ret;

  /* Call all C++ static constructors */

#if defined(CONFIG_HAVE_CXX) && defined(CONFIG_HAVE_CXXINITIALIZE)
  up_cxxinitialize();
#endif

  /* Make sure that we are using our symbol take */

#if defined(CONFIG_LIBC_EXECFUNCS) && defined(CONFIG_EXECFUNCS_SYMTAB)
  exec_setsymtab(CONFIG_EXECFUNCS_SYMTAB, 0);
#endif

  /* Register the BINFS file system */

#if defined(CONFIG_FS_BINFS) && (CONFIG_BUILTIN)
  ret = builtin_initialize();
  if (ret < 0)
    {
     fprintf(stderr, "ERROR: builtin_initialize failed: %d\n", ret);
     exitval = 1;
   }
#endif

  /* Initialize the NSH library */

  nsh_initialize();

  /* If the Telnet console is selected as a front-end, then start the
   * Telnet daemon.
   */

#ifdef CONFIG_NSH_TELNET
  ret = nsh_telnetstart();
  if (ret < 0)
    {
     /* The daemon is NOT running.  Report the the error then fail...
      * either with the serial console up or just exiting.
      */

     fprintf(stderr, "ERROR: Failed to start TELNET daemon: %d\n", ret);
     exitval = 1;
   }
#endif

  /* If the serial console front end is selected, then run it on this thread */

#ifdef CONFIG_NSH_CONSOLE
  ret = nsh_consolemain(0, NULL); //核心文件

  /* nsh_consolemain() should not return.  So if we get here, something
   * is wrong.
   */

  fprintf(stderr, "ERROR: nsh_consolemain() returned: %d\n", ret);
  exitval = 1;
#endif

  return exitval;
}

这里写图片描述


int nsh_consolemain(int argc, char *argv[])
{
  FAR struct console_stdio_s *pstate = nsh_newconsole();
  int ret;

  DEBUGASSERT(pstate);

  /* Execute the start-up script */

#ifdef CONFIG_NSH_ROMFSETC
  (void)nsh_initscript(&pstate->cn_vtbl);
#endif

  /* Initialize any USB tracing options that were requested */

#ifdef CONFIG_NSH_USBDEV_TRACE
  usbtrace_enable(TRACE_BITSET);
#endif

  /* Execute the session */

  ret = nsh_session(pstate);

  /* Exit upon return */

  nsh_exit(&pstate->cn_vtbl, ret);
  return ret;
}

到这里我们只关心这个函数nsh_consolemain()-——》nsh_initscript()——》nsh_script()


int nsh_initscript(FAR struct nsh_vtbl_s *vtbl)
{
  static bool initialized;
  bool already;
  int ret = OK;

  /* Atomic test and set of the initialized flag */

  sched_lock();
  already     = initialized;
  initialized = true;
  sched_unlock();

  /* If we have not already executed the init script, then do so now */

  if (!already)
    {
      ret = nsh_script(vtbl, "init", NSH_INITPATH);//解析脚本函数
    }

  return ret;
}

这里写图片描述
到这里我们就要看rcS文件

#!nsh
#
# PX4FMU startup script.
#
# This script is responsible for:
#
# - mounting the microSD card (if present)
# - running the user startup script from the microSD card (if present)
# - detecting the configuration of the system and picking a suitable
#   startup script to continue with
#
# Note: DO NOT add configuration-specific commands to this script;
#       add them to the per-configuration scripts instead.
#

set USB autoconnect
set NSH_ERROR_UART1 /dev/ttyACM0
set NSH_ERROR_UART2 /dev/ttyS0

#
# Try to mount the microSD card.
#
echo "[init] looking for microSD..."
if mount -t vfat /dev/mmcsd0 /fs/microsd
then
    echo "[init] card mounted at /fs/microsd"
    set HAVE_MICROSD 1
    # Start playing the startup tune
    if [ -f /etc/tones/startup ]
    then
        tone_alarm /etc/tones/startup
    else
        tone_alarm 1
    fi
else
    set HAVE_MICROSD 0
    tone_alarm MNBGG
fi

# Look for an additional init script that allows changing the default
# behavior. Settings may be overriden in the following order:
#
# board-specific file
# etc/rc on microSD card
# etc/rc.txt on microSD card
if [ -f /etc/init.d/rc.board ]
then
    echo "[init] reading /etc/init.d/rc.board"
    sh /etc/init.d/rc.board
fi
if [ -f /fs/microsd/etc/rc ]
then
    echo "[init] reading /fs/microsd/etc/rc"
    sh /fs/microsd/etc/rc
fi
# Also consider rc.txt files
if [ -f /fs/microsd/etc/rc.txt ]
then
    echo "[init] reading /fs/microsd/etc/rc.txt"
    sh /fs/microsd/etc/rc.txt
fi

#
# Check for USB host
#
if [ $USB != autoconnect ]
then
    echo "[init] not connecting USB"
else
    if sercon
    then
        echo "[init] USB interface connected"
    else
        echo "[init] No USB connected"
    fi
fi

if [ $HAVE_MICROSD == 0 ]
then
    if usb_connected
    then
        echo "Opening USB nsh"
    else
        echo "booting with no microSD"
        set HAVE_MICROSD 1
    fi
fi

# if this is an APM build then there will be a rc.APM script
# from an EXTERNAL_SCRIPTS build option
if [ -f /etc/init.d/rc.APM -a $HAVE_MICROSD == 1 -a ! -f /fs/microsd/APM/nostart ]
then
    echo Running rc.APM
    # if APM startup is successful then nsh will exit
    sh /etc/init.d/rc.APM
else
    nshterm /dev/ttyACM0 &
fi

继续执行rc.APM

#!nsh

# APM startup script for NuttX on PX4

# To disable APM startup add a /fs/microsd/APM/nostart file

# check for an old file called APM, caused by 
# a bug in an earlier firmware release
if [ -f /fs/microsd/APM ]
then
    echo "APM file found - renaming"
    mv /fs/microsd/APM /fs/microsd/APM.old
fi

if [ -f /fs/microsd/APM/nostart ]
then
    echo "APM/nostart found - skipping APM startup"
    sh /etc/init.d/rc.error
fi

# mount binfs so we can find the built-in apps
if [ -f /bin/reboot ]
then
    echo "binfs already mounted"
else
    echo "Mounting binfs"
    if mount -t binfs /dev/null /bin
    then
            echo "binfs mounted OK"
    else
            sh /etc/init.d/rc.error
    fi
fi

set sketch NONE
if rm /fs/microsd/APM/boot.log
then
    echo "removed old boot.log"
fi
set logfile /fs/microsd/APM/BOOT.LOG

if [ ! -f /bin/ArduPilot ]
then
    echo "/bin/ardupilot not found"
    sh /etc/init.d/rc.error
fi

if mkdir /fs/microsd/APM > /dev/null
then
    echo "Created APM directory"
fi

if uorb start
then
    echo "uorb started OK"
else
    sh /etc/init.d/rc.error
fi

echo Starting ArduPilot
if ArduPilot start
then
    echo ArduPilot started OK
else
    sh /etc/init.d/rc.error
fi

echo "rc.APM finished"

到这里我们会调用:
if ArduPilot start
then
echo ArduPilot started OK
这里写图片描述
这里写图片描述
到这里可以看出apm和px4的启动还是有很大的差别
(5)user_start()函数

int user_start(int argc, char *argv[])
{
    // detect hw version
    hw_detect();

    /* configure the first 8 PWM outputs (i.e. all of them) */
    up_pwm_servo_init(0xff);

    /* run C++ ctors before we go any further */
    up_cxxinitialize();

    /* reset all to zero */
    memset(&system_state, 0, sizeof(system_state));

    /* configure the high-resolution time/callout interface */
    hrt_init();

    /* calculate our fw CRC so FMU can decide if we need to update */
    calculate_fw_crc();

    /*
     * Poll at 1ms intervals for received bytes that have not triggered
     * a DMA event.
     */
#ifdef CONFIG_ARCH_DMA
    hrt_call_every(&serial_dma_call, 1000, 1000, (hrt_callout)stm32_serial_dma_poll, NULL);
#endif

    /* print some startup info */
    lowsyslog("\nPX4IO: starting\n");

    /* default all the LEDs to off while we start */
    LED_AMBER(false);
    LED_BLUE(false);
    LED_SAFETY(false);
#ifdef GPIO_LED4
    LED_RING(false);
#endif

    /* turn on servo power (if supported) */
#ifdef POWER_SERVO
    POWER_SERVO(true);
#endif

    /* turn off S.Bus out (if supported) */
#ifdef ENABLE_SBUS_OUT
    ENABLE_SBUS_OUT(false);
#endif

    /* start the safety switch handler */
    safety_init();

    /* initialise the control inputs */
    controls_init();

    /* set up the ADC */
    adc_init();

    /* start the FMU interface */
    interface_init();

    /* add a performance counter for mixing */
    perf_counter_t mixer_perf = perf_alloc(PC_ELAPSED, "mix");

    /* add a performance counter for controls */
    perf_counter_t controls_perf = perf_alloc(PC_ELAPSED, "controls");

    /* and one for measuring the loop rate */
    perf_counter_t loop_perf = perf_alloc(PC_INTERVAL, "loop");

    struct mallinfo minfo = mallinfo();
    lowsyslog("MEM: free %u, largest %u\n", minfo.mxordblk, minfo.fordblks);

    /* initialize PWM limit lib */
    pwm_limit_init(&pwm_limit);

    /*
     *    P O L I C E    L I G H T S
     *
     * Not enough memory, lock down.
     *
     * We might need to allocate mixers later, and this will
     * ensure that a developer doing a change will notice
     * that he just burned the remaining RAM with static
     * allocations. We don't want him to be able to
     * get past that point. This needs to be clearly
     * documented in the dev guide.
     *
     */
    if (minfo.mxordblk < 600) {

        lowsyslog("ERR: not enough MEM");
        bool phase = false;

        while (true) {

            if (phase) {
                LED_AMBER(true);
                LED_BLUE(false);

            } else {
                LED_AMBER(false);
                if (r_page_config[PX4IO_P_CONFIG_HARDWARE_VERSION] != 3) {
                        LED_BLUE(true);
                }
            }

            up_udelay(250000);

            phase = !phase;
        }
    }

    /* Start the failsafe led init */
    failsafe_led_init();

    /*
     * Run everything in a tight loop.
     */

    uint64_t last_debug_time = 0;

    for (;;) {

        /* track the rate at which the loop is running */
        perf_count(loop_perf);

        /* kick the mixer */
        perf_begin(mixer_perf);

        if (!(r_setup_features & PX4IO_P_SETUP_FEATURES_ONESHOT)) {
            /*
              when in oneshot we don't trigger output
              until we get new data from the FMU
             */
            mixer_tick();
        }

        perf_end(mixer_perf);

        /* kick the control inputs */
        perf_begin(controls_perf);
        controls_tick();
        perf_end(controls_perf);

        check_reboot();

        /* check for debug activity (default: none) */
        show_debug_messages();

        /* post debug state at ~1Hz - this is via an auxiliary serial port
         * DEFAULTS TO OFF!
         */
        if (hrt_absolute_time() - last_debug_time > (1000 * 1000)) {

            isr_debug(1, "d:%u s=0x%x a=0x%x f=0x%x m=%u",
                  (unsigned)r_page_setup[PX4IO_P_SETUP_SET_DEBUG],
                  (unsigned)r_status_flags,
                  (unsigned)r_setup_arming,
                  (unsigned)r_setup_features,
                  (unsigned)mallinfo().mxordblk);
            last_debug_time = hrt_absolute_time();
        }

        if (r_page_setup[PX4IO_P_SETUP_HEATER_DUTY_CYCLE] <= PX4IO_HEATER_MAX) {
            control_IMU_heater(r_page_setup[PX4IO_P_SETUP_HEATER_DUTY_CYCLE]);

        } else if (r_page_config[PX4IO_P_CONFIG_HARDWARE_VERSION] == 3) {
                // pixhawk2 with no target temperature
                control_IMU_heater(0);
        } else {
                control_heartbeat_LED();
        }
    }
}

这里写图片描述

猜你喜欢

转载自blog.csdn.net/lixiaoweimashixiao/article/details/81545640