加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

PX4源代码分析,Nuttx系统启动(转)

(2017-04-01 11:04:56)
标签:

px4

pixhawk

nuttx

启动

分类: Pixhawk、PX4无人机

写在前面

       最近弄PX4飞控,使用了Nuttx实时操作系统。想必跑裸机而言,使用了操作系统的复杂度是不一样的。至少来说在初期复杂度增加的可不止一点点。

       然而在初期,最痛苦的还是Nuttx的资料不够,简直是稀少。所以也就只能一点点地啃源码以期望能够理清Nuttx的系统流程与基本轮廓。毕竟跑系统不比跑裸机,自己用到的资源一目了然。引入了C++之后有些东西就更加显得不是那么的清晰了。

       其实到这个文档写完《1.2 命令》的时候我才发现我反了一个很严重的错误:我一直都在以裸奔的方式在分析PX4,这给我带来了极大的麻烦,使得我一开始就在代码中找寻应用启动的过程。其实Nuttx只是启动了系统而已,对于应用,那是在脚本中启动的。

1. 走流程

       Nuttx系统我并没有完全搞透,只是从系统的第一条代码到我们的飞控启动的基本轮廓理清了而已,各中细节还得深入分析。

       那么我们先来讲解系统启动后的第一条代码即入口。

1.1 入口

       PX4使用的是STM32,入口自然就在stm32_start.c中:

void __start(void)

{

  const uint32_t *src;

  uint32_t *dest;

 

#ifdef CONFIG_ARMV7M_STACKCHECK

  

 

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

#endif

 

  

 

  stm32_clockconfig();

  stm32_fpuconfig();

  stm32_lowsetup();

  stm32_gpioinit();

  showprogress('A');

 

  

 

  for (dest = &_sbss; dest < &_ebss; )

    {

      *dest++ = 0;

    }

 

  showprogress('B');

 

  

 

  for (src = &_eronly, dest = &_sdata; dest < &_edata; )

    {

      *dest++ = *src++;

    }

 

  showprogress('C');

 

  

 

#ifdef USE_EARLYSERIALINIT

  up_earlyserialinit();

#endif

  showprogress('D');

 

  

 

#ifdef CONFIG_NUTTX_KERNEL

  stm32_userspace();

  showprogress('E');

#endif

 

  

 

  stm32_boardinitialize();

  showprogress('F');

 

  

 

  showprogress('\r');

  showprogress('\n');

  os_start();

 

  

 

  for(;;);

}

我们可以先通过注释大概了解这里都做了哪些事情。在这之前还有一个程序在跑:bootloader。这个不是我们今天讨论的话题。在最后我们看到调用了一个os_start()函数也就是接下来系统将启动,对应的源文件是os_start.c:

void os_start(void)

{

  int i;

 

  slldbg("Entry\n");

 

  

  

 

  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

 

  

 

  g_lastpid = 0;

  for (i = 0; i < CONFIG_MAX_TASKS; i++)

    {

      g_pidhash[i].tcb = NULL;

      g_pidhash[i].pid = INVALID_PROCESS_ID;

    }

 

  

 

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

  g_pidhash[ PIDHASH(0)].pid = 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;

 

  

 

#if CONFIG_TASK_NAME_SIZE > 0

  strncpy(g_idletcb.cmn.name, g_idlename, CONFIG_TASK_NAME_SIZE-1);

#endif 

 

  

 

#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 

#endif 

 

  

 

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

 

  

 

  up_initial_state(&g_idletcb.cmn);

 

  

  

 

#ifdef CONFIG_HAVE_WEAKFUNCTIONS

  if (sem_initialize != NULL)

#endif

    {

      sem_initialize();

    }

 

  

 

  {

    FAR void *heap_start;

    size_t heap_size;

 

    

 

    up_allocate_heap(&heap_start, &heap_size);

    kumm_initialize(heap_start, heap_size);

 

#if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_MM_KERNEL_HEAP)

    

 

    up_allocate_kheap(&heap_start, &heap_size);

    kmm_initialize(heap_start, heap_size);

#endif

  }

 

  

 

#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)

#ifdef CONFIG_HAVE_WEAKFUNCTIONS

  if (task_initialize != NULL)

#endif

    {

      task_initialize();

    }

#endif

 

  

 

#ifdef CONFIG_HAVE_WEAKFUNCTIONS

  if (irq_initialize != NULL)

#endif

    {

      irq_initialize();

    }

 

  

 

#ifdef CONFIG_HAVE_WEAKFUNCTIONS

  if (wd_initialize != NULL)

#endif

    {

      wd_initialize();

    }

 

  

 

#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

 

  

 

#ifndef CONFIG_DISABLE_SIGNALS

#ifdef CONFIG_HAVE_WEAKFUNCTIONS

  if (sig_initialize != NULL)

#endif

    {

      sig_initialize();

    }

#endif

 

  

 

#ifndef CONFIG_DISABLE_MQUEUE

#ifdef CONFIG_HAVE_WEAKFUNCTIONS

  if (mq_initialize != NULL)

#endif

    {

      mq_initialize();

    }

#endif

 

  

 

#ifndef CONFIG_DISABLE_PTHREAD

#ifdef CONFIG_HAVE_WEAKFUNCTIONS

  if (pthread_initialize != NULL)

#endif

    {

      pthread_initialize();

    }

#endif

 

  

 

#if CONFIG_NFILE_DESCRIPTORS > 0

#ifdef CONFIG_HAVE_WEAKFUNCTIONS

  if (fs_initialize != NULL)

#endif

    {

      fs_initialize();

    }

#endif

 

  

 

#ifdef CONFIG_NET

#if 0

  if (net_initialize != NULL)

#endif

    {

      net_initialize();

    }

#endif

 

  

 

  up_initialize();

 

  

 

#ifdef CONFIG_HAVE_WEAKFUNCTIONS

  if (lib_initialize != NULL)

#endif

    {

      lib_initialize();

    }

 

  

  

 

#ifdef HAVE_TASK_GROUP

  DEBUGVERIFY(group_allocate(&g_idletcb));

#endif

 

  

 

  DEBUGVERIFY(group_setupidlefiles(&g_idletcb));

 

  

 

#ifdef HAVE_TASK_GROUP

  DEBUGVERIFY(group_initialize(&g_idletcb));

  g_idletcb.cmn.group->tg_flags = GROUP_FLAG_NOCLDWAIT;

#endif

 

  

  

 

  DEBUGVERIFY(os_bringup());

 

  

  

 

  sdbg("Beginning Idle Loop\n");

  for (;;)

    {

      

 

#ifndef CONFIG_SCHED_WORKQUEUE

      

 

      if (kmm_trysemaphore() == 0)

        {

          sched_garbagecollection();

          kmm_givesemaphore();

        }

#endif

 

      

 

      up_idle();

    }

}

我们看到这里初始化了各种资源,最后进入了一个空闲任务。而在空闲任务的前面我们看到对其他任务也进行了初始化,这是在os_bringup.c文件中:

int os_bringup(void)

{

  int taskid;

 

  

 

#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)

  (void)setenv("PATH", CONFIG_PATH_INITIAL, 1);

#endif

 

  

 

#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

 

  

 

#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);

 

  

 

#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 

#endif 

 

#if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_SCHED_USRWORK)

  

 

  DEBUGASSERT(USERSPACE->work_usrstart != NULL);

  taskid = USERSPACE->work_usrstart();

  DEBUGASSERT(taskid > 0);

#endif

 

#endif 

 

  

 

  svdbg("Starting init thread\n");

 

  

 

#ifdef CONFIG_BOARD_INITIALIZE

  board_initialize();

#endif

 

  

 

#ifdef CONFIG_NUTTX_KERNEL

  DEBUGASSERT(USERSPACE->us_entrypoint != NULL);

  taskid = TASK_CREATE("init", SCHED_PRIORITY_DEFAULT,

                       CONFIG_USERMAIN_STACKSIZE, USERSPACE->us_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);

 

  

 

#if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL)

  (void)clearenv();

#endif

 

  return OK;

}

这里跟我们关系最大的就是init进程。在Linux中也有init进程,是所有应用程序进程的祖先进程,进程ID为1。这里其实类似。Init进程启动了所有应用程序进程。不过这里需要注意的是CONFIG_USER_ENTRYPOINT是在配置文件中指定的,相当于一个宏:

CONFIG_USER_ENTRYPOINT="nsh_main"

int nsh_main(int argc, char *argv[])

{

  int exitval = 0;

  int ret;

 

  

 

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

  up_cxxinitialize();

#endif

 

  

 

#if defined(CONFIG_LIBC_EXECFUNCS) && defined(CONFIG_EXECFUNCS_SYMTAB)

  exec_setsymtab(CONFIG_EXECFUNCS_SYMTAB, 0);

#endif

 

  

 

#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

 

  

 

  nsh_initialize();

 

  

 

#ifdef CONFIG_NSH_TELNET

  ret = nsh_telnetstart();

  if (ret < 0)

    {

     

 

     fprintf(stderr, "ERROR: Failed to start TELNET daemon: %d\n", ret);

     exitval = 1;

   }

#endif

 

  

 

#ifdef CONFIG_NSH_CONSOLE

  ret = nsh_consolemain(0, NULL);

 

  

 

  fprintf(stderr, "ERROR: nsh_consolemain() returned: %d\n", ret);

  exitval = 1;

#endif

 

  return exitval;

}

这里就相当于Linux中启动了shell,然后去执行初始化脚本。

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

{

  FAR struct console_stdio_s *pstate = nsh_newconsole();

  int ret;

 

  DEBUGASSERT(pstate);

 

  

 

#ifdef CONFIG_NSH_ROMFSETC

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

#endif

 

  

 

#ifdef CONFIG_NSH_USBDEV_TRACE

  usbtrace_enable(TRACE_BITSET);

#endif

 

  

 

  ret = nsh_session(pstate);

 

  

 

  nsh_exit(&pstate->cn_vtbl, ret);

  return ret;

}

在这里实际上nsh_initscript和nsh_session都会去执行命令,但nsh_session从代码上看更像是Linux的命令行参数解析, nsh_initscript回去执行/etc/init.d/rcS脚本。

int nsh_initscript(FAR struct nsh_vtbl_s *vtbl)

{

  static bool initialized;

  bool already;

  int ret = OK;

 

  

 

  sched_lock();

  already     = initialized;

  initialized = true;

  sched_unlock();

 

  

 

  if (!already)

    {

      ret = nsh_script(vtbl, "init", NSH_INITPATH);

    }

 

  return ret;

}

这里我们需要对NSH_INITPATH进行展开:

ifndef CONFIG_NSH_ROMFSMOUNTPT

   define CONFIG_NSH_ROMFSMOUNTPT "/etc"

endif

 

ifndef CONFIG_NSH_INITSCRIPT

   define CONFIG_NSH_INITSCRIPT "init.d/rcS"

endif

 

undef NSH_INITPATH

define NSH_INITPATH CONFIG_NSH_ROMFSMOUNTPT "/"CONFIG_NSH_INITSCRIPT

所以到了这里我们需要把目光转向启动脚本。后面我们所需要的很多东西,包括PX4飞控主线程都是在脚本中意命令的方式启动的。所以说如果抛开脚本,单纯看源码是看不出PX4飞控主线程是如何启动的,因为这些个东西根本就不在源码中。

1.2 命令

       说到脚本,我们当然知道脚本中最多的是命令。在Nuttx中命令跟Linux还是有很大的区别的。

       看到这里,可能有的人会想Nuttx到底跟Linux有什么关系。其实Nuttx只是跟Linux某些地方相似而已。而我多次提到Linux只是在分析Nuttx的时候以Linux为坐标,仅此而已。有了坐标,我就不至于完全迷失了方向。

       通过前面的分析,我们很容易想到接下来的代码分析将从nsh_script开始。

int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd,

               FAR const char *path)

{

  char *fullpath;

  FILE *stream;

  char *buffer;

  char *pret;

  int ret = ERROR;

 

  

 

  fullpath = nsh_getfullpath(vtbl, path);

  if (!fullpath)

    {

      return ERROR;

    }

 

  

 

  buffer = nsh_linebuffer(vtbl);

  if (buffer)

    {

      

 

      stream = fopen(fullpath, "r");

      if (!stream)

        {

          nsh_output(vtbl, g_fmtcmdfailed, cmd, "fopen", NSH_ERRNO);

          nsh_freefullpath(fullpath);

          return ERROR;

        }

 

      

 

      do

        {

          

 

          fflush(stdout);

          pret = fgets(buffer, CONFIG_NSH_LINELEN, stream);

          if (pret)

            {

              

 

              ret = nsh_parse(vtbl, buffer);

            }

        }

      while (pret && ret == OK);

      fclose(stream);

    }

 

  nsh_freefullpath(fullpath);

  return ret;

}

这段代码并不难理解,就是打开了我们的脚本文件,然后逐行读取并解析执行。那显然nsh_parse就是解析这些脚本的关键。

int nsh_parse1(FAR struct nsh_vtbl_s *vtbl, char *cmdline)

{

  

 

#if defined(CONFIG_NSH_BUILTIN_APPS) && (!defined(CONFIG_NSH_FILE_APPS) || !defined(CONFIG_FS_BINFS))

  ret = nsh_builtin(vtbl, argv[0], argv, redirfile, oflags);

#endif

    {

      

 

      ret = nsh_execute(vtbl, argc, argv);

    }

  return nsh_saveresult(vtbl, true);

}

需要说明的是这个函数是删减过的,就是说只留下了我最想要看到的部分。nsh_builtin和nsh_execute分别对应两个数组:g_builtins和g_cmdmap。什么区别呢?在我看来这两组命令是可以合并到一起的,但如果真的这样做了会有一个问题,用户扩展不方便。于是对其进行区分。像ls这样的命令不需要用户实现及处理,就作为系统命令在g_cmdmap数组中,而作为用户的应用程序如ArduPilot完全有用户决定,便作为用户命令在g_builtins数组中。对于系统命令我们常用的基本都已经实现,通过特定的宏进行选择编译。而用户命令我们只需按照相应规则编写一个入口函数然并设定堆栈大小然后放到g_builtins数组中即可。

       Nuttx本身提供了一个g_builtins数组,但PX4没有使用。PX4使用了一些技巧,利用Makefile在编译的时候自动生成g_builtins数组。这样做好处是我们修改源码的时候可以不关心这个数组,但坏处也显而易见:增加了我们刚接触是阅读源码的壁垒。

       那么关于命令部分我们先讲解到这里。下面我们将稍微看下脚本。

1.3 启动脚本

       前面我们说过Nuttx的启动脚本是/etc/init.d/rcS,跟Linux是一样的。这样做也有一个好处,就是如果你对Linux比较熟悉即便你完全不看源码只要看下根文件系统你就能够找到启动脚本并进行分析。

       启动脚本并不少,我们不逐条分析。

set MODE autostart

set USB autoconnect

if rgbled start

then

        set HAVE_RGBLED 1

        rgbled rgb 16 16 16

else

        set HAVE_RGBLED 0

fi

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

    tone_alarm start

else

    echo "[init] no microSD card found"

        set HAVE_MICROSD 0

    tone_alarm 2

        if [ $HAVE_RGBLED == 1 ]

        then

                rgbled rgb 16 0 0

        fi

fi

if [ -f /fs/microsd/etc/rc ]

then

    echo "[init] reading /fs/microsd/etc/rc"

    sh /fs/microsd/etc/rc

fi

if [ -f /fs/microsd/etc/rc.txt ]

then

    echo "[init] reading /fs/microsd/etc/rc.txt"

    sh /fs/microsd/etc/rc.txt

fi

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 [ -f /etc/init.d/rc.APM -a $HAVE_MICROSD == 1 -a ! -f /fs/microsd/APM/nostart]

then

    echo Running rc.APM

    sh /etc/init.d/rc.APM

else

        nshterm /dev/ttyACM0 &

fi

我们看到先是设置了环境变量,然后打开了RGB灯,就是PX4飞控上的那个三色高亮LED,设置了这几个等如何闪烁。然后挂载SD卡并执行SD卡中的脚本,接下来自动连接USB,最后是执行rc.APM脚本。所以PX4要用的东西基本上都在rc.APM脚本中启动。

set deviceA /dev/ttyACM0

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

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 [ -f /bin/lsm303d ]

then

     echo "Detected FMUv2 board"

     set BOARD FMUv2

else

     echo "Detected FMUv1 board"

     set BOARD FMUv1

fi

if [ $BOARD == FMUv1 ]

then

   set deviceC /dev/ttyS2

   if [ -f /fs/microsd/APM/AUXPWM.en ]

   then

      set deviceD /dev/null

   else

      set deviceD /dev/ttyS1

   fi

else

   set deviceC /dev/ttyS1

   set deviceD /dev/ttyS2

fi

if uorb start

then

    echo "uorb started OK"

else

    sh /etc/init.d/rc.error

fi

if [ -f /fs/microsd/APM/mkblctrl ]

then

    echo "Setting up mkblctrl driver"

    echo "Setting up mkblctrl driver" >> $logfile

    mkblctrl -d /dev/pwm_output

fi

if [ -f /fs/microsd/APM/mkblctrl_+ ]

then

    echo "Setting up mkblctrl driver +"

    echo "Setting up mkblctrl driver +" >> $logfile

    mkblctrl -mkmode + -d /dev/pwm_output

fi

if [ -f /fs/microsd/APM/mkblctrl_x ]

then

    echo "Setting up mkblctrl driver x"

    echo "Setting up mkblctrl driver x" >> $logfile

    mkblctrl -mkmode x -d /dev/pwm_output

fi

set HAVE_PX4IO false

if px4io start norc

then

    set HAVE_PX4IO true

else

    echo Loading /etc/px4io/px4io.bin

    tone_alarm MBABGP

    if px4io update /etc/px4io/px4io.bin

    then

    echo "upgraded PX4IO firmware OK"

        tone_alarm MSPAA

    else

    echo "Failed to upgrade PX4IO firmware"

        tone_alarm MNGGG

    fi

    sleep 1

    if px4io start norc

    then

        set HAVE_PX4IO true

    tone_alarm start

    fi

fi

if [ $HAVE_PX4IO == true ]

then

    echo "PX4IO board OK"

    if px4io checkcrc /etc/px4io/px4io.bin

    then

        echo "PX4IO CRC OK"

    else

        echo "PX4IO CRC failure"

        echo "PX4IO CRC failure" >> $logfile   

        tone_alarm MBABGP

        if px4io forceupdate 14662 /etc/px4io/px4io.bin

        then

               sleep 1

               if px4io start norc

               then

                  echo "PX4IO restart OK"

                  echo "PX4IO restart OK" >> $logfile   

                  tone_alarm MSPAA

               else

                  echo "PX4IO restart failed"

                  echo "PX4IO restart failed" >> $logfile   

                  tone_alarm MNGGG

                  sh /etc/init.d/rc.error

               fi

        else

               echo "PX4IO update failed"

               echo "PX4IO update failed" >> $logfile   

               tone_alarm MNGGG

        fi

    fi

else

    echo "No PX4IO board found"

    echo "No PX4IO board found" >> $logfile

    if [ $BOARD == FMUv2 ]

    then

       sh /etc/init.d/rc.error

    fi

fi

if [ $BOARD == FMUv1 -a $deviceD == /dev/ttyS1 ]

then

        echo "Setting FMU mode_serial"

        fmu mode_serial

else

        echo "Setting FMU mode_pwm"

        fmu mode_pwm

fi

echo "Starting APM sensors"

if ms5611 start

then

    echo "ms5611 started OK"

else

    sh /etc/init.d/rc.error

fi

if adc start

then

    echo "adc started OK"

else

    sh /etc/init.d/rc.error

fi

if [ $BOARD == FMUv1 ]

then

    echo "Starting FMUv1 sensors"

    if hmc5883 start

    then

        echo "hmc5883 started OK"

        if hmc5883 calibrate

        then

          echo "hmc5883 calibrate OK"

        else

          echo "hmc5883 calibrate failed"

          echo "hmc5883 calibrate failed" >> $logfile

          tone_alarm MSBBB

        fi

    else

        echo "hmc5883 start failed"

        echo "hmc5883 start failed" >> $logfile

        sh /etc/init.d/rc.error

    fi

    if mpu6000 start

    then

       echo "mpu6000  started OK"

    else

       sh /etc/init.d/rc.error

    fi

    if l3gd20 start

    then

       echo "l3gd20 started OK"

    else

       echo "No l3gd20"

       echo "No l3gd20" >> $logfile

    fi

else

    echo "Starting FMUv2 sensors"

    if hmc5883 -C -X start

    then

        echo "Have external hmc5883"

    else

        echo "No external hmc5883"

    fi

    if hmc5883 -C -I -R 4 start

    then

        echo "Have internal hmc5883"

    else

        echo "No internal hmc5883"

    fi

    if mpu6000 -X -R 4 start

    then

       echo "Found MPU6000 external"

       set HAVE_FMUV3 true

    else

       echo "No MPU6000 external"

       set HAVE_FMUV3 false

    fi

    if [ $HAVE_FMUV3 == true ]

    then

        if mpu6000 -R 14 start

        then

               echo "Found MPU6000 internal"

        else

               echo "No MPU6000"

               echo "No MPU6000" >> $logfile

               sh /etc/init.d/rc.error

        fi

        if l3gd20 -X -R 4 start

        then

               echo "l3gd20 external started OK"

        else

               echo "No l3gd20"

               sh /etc/init.d/rc.error

        fi

        if lsm303d -X -R 6 start

        then

               echo "lsm303d external started OK"

        else

               echo "No lsm303d"

               sh /etc/init.d/rc.error

        fi

    else

        if mpu6000 start

        then

               echo "Found MPU6000"

        else

               echo "No MPU6000"

               echo "No MPU6000" >> $logfile

        fi

        if l3gd20 start

        then

               echo "l3gd20 started OK"

        else

               sh /etc/init.d/rc.error

        fi

        if lsm303d start

        then

               echo "lsm303d started OK"

        else

               sh /etc/init.d/rc.error

        fi

    fi

fi

if ets_airspeed start

then

    echo "Found ETS airspeed sensor"

fi

if meas_airspeed start

then

    echo "Found MEAS airspeed sensor"

fi

if ll40ls start

then

    echo "Found ll40ls sensor"

fi

if mb12xx start

then

    echo "Found mb12xx sensor"

fi

echo "Trying PX4IO board"

if mtd start /fs/mtd

then

    echo "started mtd driver OK"

else

    echo "failed to start mtd driver"

    echo "failed to start mtd driver" >> $logfile

    sh /etc/init.d/rc.error          

fi

if mtd readtest /fs/mtd

then

    echo "mtd readtest OK"

else

    echo "failed to read mtd"

    echo "failed to read mtd" >> $logfile

    sh /etc/init.d/rc.error          

fi

if [ $BOARD == FMUv2 ]

then

  if mtd rwtest /fs/mtd

  then

    echo "mtd rwtest OK"

  else

    echo "failed to test mtd"

    echo "failed to test mtd" >> $logfile

    sh /etc/init.d/rc.error          

  fi

fi

echo Starting ArduPilot $deviceA $deviceC $deviceD

if ArduPilot -d $deviceA -d2 $deviceC -d3 $deviceD start

then

    echo ArduPilot started OK

else

    sh /etc/init.d/rc.error

fi

echo "rc.APM finished"

这个脚本比较长。第一句应该是对设备进行重定向,然后对上一次的日志进行备份并创建日志。然后通过lsm303d判断PX4的版本,是V1还是V2。换句话说V1跟V2最大的区别是V2有lsm303d传感器,这是一个地磁传感器,而且内置加计。从这里我们也可以看出相比V1,V2在设计上采用了内置地磁的设计。然后根据硬件版本分别初始化输入输出设备。

       后面那个uorb没搞懂是什么。后面的mkblctrl感觉上像是机型配置相关的,具体是与不是还有待证实。然后是跟新IO板的固件。接下来启动的fmu想必是一个很重要的进程。然后又是根据硬件版本进行初始化,启动传感器。后面那几个估计也是传感器,具体还需要查。Mtd呢肯定是跟存储设备相关的。

       最后,ArduPilot便是对于我们来讲最重要的一个进程,这个进程启动基本上就表示我们的飞控可以正常使用了。

 

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有