本节主要记录自己学px4的代码架构与Ardupilot代码架构对比文档,欢迎批评指正。
(1)px4与apm固件的区别与联系
(2)px4与apm每个文件夹的作用
(3)px4与apm无人机的启动过程
(1)pixhawk
(2)APM
(3)px4—Firmware
(4)apm—Ardupilot
这里只讲解Nuttx启动到ArduCopter多旋翼入口函数的过程,Bootloader到Nuttx的过程将在:Bootloader启动分析中讲解。
1.px4固件启动过程
(1)Bootloader启动后,单片机的入口函数:stm32_start.c
需要思考的问题:这个启动过程是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 */
到这里是PX4的关键点,很多人想了解px4或者apm怎么创建任务的,整个程序是怎么运转的,就得仔细分析这里
(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 */
//如果我们还没有执行init脚本,那么现在就执行它。
if (!already)
{
ret = nsh_script(vtbl, "init", NSH_INITPATH); //解析脚本,这里很重要,理解这个就可以知道px4是如何运转的
}
return ret;
}
(3)ret = nsh_script(vtbl, “init”, NSH_INITPATH); //解析脚本
到这里我们需要分析目录”init.d/rcS”,也就是rcS文件,看下面的文档就是rcS
#!nsh #shell脚本默认开头格式
# 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 #显示脚本运行是的冗余输出,在set命令之后执行的每一条命令以及加载命令行中的任何参数都会显示出来,每一行都会加上加号(+),提示它是跟踪输出的标识。
# 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.
#
# 串口映射 FMUv2/3/4:
# 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:这个是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.挂载procfs(进程文件系统,包含一个伪文件系统(启动时动态生成的文件系统),用于通过内核访问进程信息。)
#
mount -t procfs /proc #挂载文件系统
#
# Start CDC/ACM serial driver # 启动usb/串口驱动 (sercon)
#
sercon
# print full system version打印系统版本
ver all
#
# Default to auto-start mode.无人机的模式默认是自动模式
#
set MODE autostart #设置默认模式
set TUNE_ERR ML<set LOG_FILE /fs/microsd/bootlog.txt #设置LOG文件
#
# Try to mount the microSD card.尝试挂载SD卡
#
# 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.在MICROSD卡上查找init脚本
# 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消息总线启动(第一个需要启动的应用)
#
uorb start#开始uorb
#
# Load parameters加载参数
#
# mtd 连接板载e2rom的应用
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
# FMUv5 同时支持 PWM、I2C,RGB LED
rgbled_pwm start
#
# Set AUTOCNF flag to use it in AUTOSTART scripts
# 设置 AUTOCNF 参数用于 AUTOSTART 脚本
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
# 检查PX4IO固件是否需要更新
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
#dataman地图航迹点管理
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
#传感器系统(在commander启动前启动以确保检测是否正常运行)
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# 启动rc.sensors
commander start #中心控制程序
fi
send_event start
load_mon start
#
# Check if UAVCAN is enabled, default to it for ESCs
#检查UAVCAN是否使能,默认是ESC
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 #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开启IO或者FMU为RCPPM输入需要
#
if [ $IO_PRESENT == yes ]
then
if [ $OUTPUT_MODE != io ]
then
if px4io start #开启px4io
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;
}
#!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();
}
}
}