Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)

一、开篇

        首先告诉大家一个坏消息,DJI提供了SDK~~~~

        然后再来一个好消息,本篇博文很多干货哦~~~~~

        最近比较纠结的一个问题ardupilot和pixhawk原生代码到底有什么区别和联系。经过和群友的讨论,最终方向一致认为单独的pixhawk原生代码是可以正常飞行的(群友Mr一直在用这套试飞的),其上电以后在rcS(nsh的启动脚本,其存储在ROMFS中,后面会详细介绍)中开启相应的进程对无人机进行姿态等复杂的控制,关于这个大家最先认识到的应该是各种飞行模式的确定,关于flight_mode后续再结合ardupilot介绍;ardupilot这套源代码前一篇博文已经分析过了,而且前一篇博文里面涉及的网站链接都是关于这个ardupilot的,很多人称之为APM,但是这个APM的HAL层也可以使用pixhawk硬件,此APM非彼apm,官网有介绍。Ardupilot是基于pixhawk原生代码开发的上层应用,即有了常见的mian和loop函数,正如前一篇博文中给出的一幅图,ardupilot把底层的PX4Firmware和NuttX等都抽象为HAL,developer只需要关心上层应用即可,基于pixhawk原生设计上层控制逻辑。这个非常类似于CC26xx的stack和application部分,而TI关于这个做的更好。ardupilot其实是开源的、玩具级飞控,它为了兼容各种乱七八糟的板子、机型,有很多冗余的代码,这些冗余的代码让我们理解飞控本身增加了难度和学习成本。如果我们需要一款专业的飞控,完全可以去掉这些冗余的代码,只保留我们需要的精简过的功能,这样也就能大大降低它代码的复杂程度。从程序设计的方法论来看(面向对象):就是要把一个个对象确立好,把问题的复杂程度控制住,才能设计出逻辑清晰的程序出来。但是我的博文写的没什么逻辑,水平有限,大家凑合看吧。

        也就是说,假如你打算开一家无人机公司,不需要从最底层一行一行的代码写起,可以直接把这套开源的pixhawk原生代码搞透彻(这套可以不是一己之力可以完成的,这个开源项目N人N年了),然后基于它进行上层开发,加入自己NB的算法,听说进DJI的第一年就是给你一个pixhawk,玩一年。非常怀疑DJI现在是不是也是基于pixhawk(虽然DJI成立2006,ETH Zurichpixhawk项目成立2009年),有待考证或者有在DJI工作的给大家普及一下呗。

        好吧,用哪套源码分析都行,我用的ardupilot,最后还是需要到底层的PX4Firmware。感谢群友彩虹小羊、灰灰、Mr等提供的帮助(排名按字母顺序,哈哈)。


三、实验平台

Software Version:ArduCopter(Ver_3.3)

Hardware Version:pixhawk

IDE:eclipse Juno (Windows)

四、基本知识介绍

1、关于坐标系

1)GeographicCoordinate System

        Represents position on earth with alongitude and latitude value (Geographic_coordinate_system). Additionally thealtitude may may be included. The altitude can be expressed as distance fromthe earth center or as altitude above the mean sea level. All in all, thisgives a spherical coordinate system. 

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第1张图片

2)Earth-Fixed frame

        Cartesian coordinate system at the center of the earth. Thepositive z axis goes through the north pole. The x and y axes are on theequatorial plane.

3)Body Fixed Frame

        The x axis points in forward (defined bygeometry and not by movement) direction. (= roll axis)

        The y axis points to the right(geometrically) (= pitch axis)

        The z axis points downwards (geometrically)(= yaw axis)

4)EulerAngles

        Usually a conversion between a earth fixed“ground” frame and the body fixed “in-air” frame is described via Euler-Angles.There are multiple conventions of the Euler angles.

        The rotation order for the Tait-Bryan angles is Z Y’X” (see thefigure):
rotation of  around Z (yaw)
rotation of  around Y' (pitch)
rotation of  around X” (roll) 

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第2张图片
Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第3张图片


2、pixhawk的HAL

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第4张图片

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第5张图片

3、pixhawk的控制图

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第6张图片

五、正文

1、关于两个控制器和任务优先级

        在PX4Firmware/src/modules中的mc_att_control:姿态控制器和mc_pos_control位置控制器(mc:multicopter),整个系统都是围绕着这两个控制器。

        mc_att_control – Multirotor attitude controller

        mc_pos_control – Multirotor position controller

        The PX4 firmware is organized in priority bands:

1) (interrupt level) fast sensordrivers

2) watchdog/system state monitors

3) actuator outputs (PWM outputdriver thread, IO comms sender thread)

4) attitude controller(s)

5) slow/blocking sensor drivers(must not block attitude controllers)

6) destination/positioncontroller(s)

7) default priority - generic usercode, shell commands, random crap, all RR scheduled

8) logger, parameter syncer

9) idle

2、   关于.mk文件( 必须深入理解

        了解整个代码运行过程的最简便方法就是通过其makefile了解文件的包含关系和调用关系。请仔细阅读arducopter中的.mk和modules/PX4Firmware中的.mk文件。.mk文件就是在Linux下编写的一些脚本文件(makefile),类似于编译过程中的条件编译,便于直接使用命令行进行编译链接。

        1)首先介绍上层应用的.mk。在ardupilot/mk/PX4/Tool中px4_common.mk文件中是关于所有PX4平台的通用编译文件,config_px4fmu_v2_APM.mk中针对pixhawk的特有硬件的编译文件。下面实例代码是config_px4fmu_v2_APM.mk中的代码,.mk脚本主要目的是为了选择需要编译的 MODULES
# Makefile for the px4fmu-v2_APM configuration
include $(SKETCHBOOK)/mk/PX4/px4_common.mk
MODULES += drivers/lsm303d
MODULES += drivers/l3gd20
MODULES += drivers/mpu9250
MODULES += drivers/boards/px4fmu-v2
MODULES += drivers/pwm_input
MODULES += modules/uavcan
MODULES += lib/mathlib
MODULES += drivers/px4io
MODULES += drivers/px4flow
MODULES += drivers/oreoled 

        Makefile的文件夹,指定编译的规则,编译顺序:

ardupilot/ArduCopter/Makefile àmk/apm.mkà environ.mk, targets.mk, sketch_sources.mk, board_px4.mkà find_tools.mk,px4_targets.mk

        尤其是上面的这个px4_targets.mk文件,该文件中涉及了所有的需要编译的文件,最后使用命令行px4-v2编译整个工程,它是最终的编译过程的执行者,注意其中px4-v2规则的详细执行情况,它会去调用PX4Firmwaremakefile。下面针对pixhawk硬件平台摘录比较重要一部分。

………
/*指定PX4Firmware、PX4NuttX、uavcan */
# these can be overridden in developer.mk
PX4FIRMWARE_DIRECTORY ?= $(SKETCHBOOK)/modules/PX4Firmware
PX4NUTTX_DIRECTORY ?= $(SKETCHBOOK)/modules/PX4NuttX
UAVCAN_DIRECTORY ?= $(SKETCHBOOK)/modules/uavcan
PX4_ROOT := $(shell cd $(PX4FIRMWARE_DIRECTORY) && pwd)
NUTTX_ROOT := $(shell cd $(PX4NUTTX_DIRECTORY) && pwd)
NUTTX_SRC := $(NUTTX_ROOT)/nuttx/
UAVCAN_DIR=$(shell cd $(UAVCAN_DIRECTORY) && pwd)/
………
/*选择硬件平台*/
# we have different config files for V1 and V2
PX4_V1_CONFIG_FILE=$(MK_DIR)/PX4/config_px4fmu-v1_APM.mk
PX4_V2_CONFIG_FILE=$(MK_DIR)/PX4/config_px4fmu-v2_APM.mk
PX4_V4_CONFIG_FILE=$(MK_DIR)/PX4/config_px4fmu-v4_APM.mk
………
/*针对pixhawk,请详细了解该部分的实现*/
px4-v2: $(BUILDROOT)/make.flags CHECK_MODULES $(MAVLINK_HEADERS) $(PX4_ROOT)/Archives/px4fmu-v2.export $(SKETCHCPP) module_mk px4-io-v2
	$(RULEHDR)/* PX4_ROOT  = ardupilot/modules/PX4Firmware*/
	$(v) rm -f $(PX4_ROOT)/makefiles/$(PX4_V2_CONFIG_FILE)
	$(v) cp $(PX4_V2_CONFIG_FILE) $(PX4_ROOT)/makefiles/nuttx/
	$(PX4_MAKE) px4fmu-v2_APM
	$(v) /bin/rm -f $(SKETCH)-v2.px4
	$(v) arm-none-eabi-size $(PX4_ROOT)/Build/px4fmu-v2_APM.build/firmware.elf
	$(v) cp $(PX4_ROOT)/Images/px4fmu-v2_APM.px4 $(SKETCH)-v2.px4
	$(v) $(SKETCHBOOK)/Tools/scripts/add_git_hashes.py $(HASHADDER_FLAGS) "$(SKETCH)-v2.px4" "$(SKETCH)-v2.px4"
	$(v) echo "PX4 $(SKETCH) Firmware is in $(SKETCH)-v2.px4"
/*最后编译生成的*.px4文件,直接下载到飞控板中即可实现飞行控制*/
………
px4-clean: clean CHECK_MODULES px4-archives-clean px4-cleandep
	$(v) /bin/rm -rf $(PX4_ROOT)/makefiles/build $(PX4_ROOT)/Build $(PX4_ROOT)/Images/*.px4 $(PX4_ROOT)/Images/*.bin
	$(v) /bin/rm -rf $(PX4_ROOT)/src/modules/uORB/topics $(PX4_ROOT)/src/platforms/nuttx/px4_messages
px4-cleandep: clean
	$(v) find $(PX4_ROOT)/Build -type f -name '*.d' | xargs rm -f
	$(v) find $(UAVCAN_DIRECTORY) -type f -name '*.d' | xargs rm -f
	$(v) find $(SKETCHBOOK)/$(SKETCH) -type f -name '*.d' | xargs rm -f
px4-v2-upload-solo: px4-v2
	scp $(SKETCH)-v2.px4 [email protected]:/tmp/
	ssh [email protected] PYTHONUNBUFFERED=1 loadPixhawk.py /tmp/ArduCopter-v2.px4
	ssh [email protected] rm /tmp/ArduCopter-v2.px4;
………
px4-v2-upload: px4-v2
	$(RULEHDR)
	$(v) $(PX4_MAKE) px4fmu-v2_APM upload
px4-upload: px4-v1-upload
px4-archives-clean:
	$(v) /bin/rm -rf $(PX4_ROOT)/Archives

        2)接下来就是PX4Firmware中的.mk。如果仔细了解了上面所讲述的,那么这部分就很easy了,不再细讲,自己去看代码吧。需要说明的是在PX4Firmware的makefile文件中有一个README.txt文本,该文本详细的介绍了PX4Firmware中的.mk的调用关系和作用。补充一点,PX4Firmware/makefile/nuttx中的config_px4fmu-v2_default.mk包含了所有基本的模块,从这里再一次验证了开篇讲述的ardupilot的代码是基于pixhawk原生代码PX4Firmware“阉割”而来的。所以在后期做开发的过程中编写应用程序只是按照ardupilot现有的子模块添加自己的就可以,参考mc_att_control实现。

        在PX4Firmware/src/modules中添加一个新的文件夹,命名为summer

        在summer文件夹中创建module.mk文件,并输入以下内容:

MODULE_COMMAND = summer
SRCS = summer.cpp

        在summer文件夹中创建summer.c文件,编译需要实现的功能代码。

        注册新添加的应用到NuttShell中:PX4Firmware/makefiles/nuttx/config_px4fmu-v2_default.mk文件中添加如下内容:

MODULES += modules/summer

        任务的启动就是靠NuttShell的内建命令启动的,包含rcS,特别是rcS一定要了解,下面会详细介绍

3、关于编译链接库(上层应用库  libraries

        在ardupilot/Arducopter中有一个make.inc文件,指定了编译ArduCopter时需要编译哪些依赖库,它对系统及硬件进行了抽象,以获取更好的可移植性。

        http://dev.ardupilot.com/wiki/apmcopter-programming-libraries/

        如果想添加或者删除编译的文件,就在此处进行修改。如下是官方默认的。

LIBRARIES += AP_Common
LIBRARIES += AP_Menu
LIBRARIES += AP_Param
LIBRARIES += StorageManager
LIBRARIES += GCS
LIBRARIES += GCS_MAVLink
LIBRARIES += AP_SerialManager
LIBRARIES += AP_GPS
LIBRARIES += DataFlash
LIBRARIES += AP_ADC
LIBRARIES += AP_Baro
LIBRARIES += AP_Compass
LIBRARIES += AP_Math
LIBRARIES += AP_InertialSensor
LIBRARIES += AP_AccelCal
LIBRARIES += AP_AHRS
LIBRARIES += AP_NavEKF
………

4、启动脚本(rcS:有两个,比较重要,任务分工)

        所谓启动脚本其实是用shellscript写的启动文件以前写过比较简单的脚本文件,还是参考的鸟哥的那本书,还有就是Linux启动文件也是用的这个写的,有兴趣的可以去Linux平台下查看一下,会写C的学这个很快,该死的C++什么时候能学会啊。

        1)其中之一就是负责较为底层driver里面的(比如mpu9250的register map、驱动),在使用命令px4-v2-upload以后,会在modules/PX4Firmware目录下生成build文件夹,内部是编译生成的文件(不编译没有),在modules/PX4Firmware/build/px4fmu-v2_APM.build/romfs_scratch/init.d文件下就是rcS和rc.APM,这个rcS是自启动脚本(setMODE autostart)。rcS脚本会挂载SD卡(这就是没有SD飞控板不能使用的原因所在),然后跳转rc.APM中,该脚本会在pixhawk系统中创建binfs,判断硬件类型,然后启动orb(对,就是那个uORB),然后启动各种传感器,然后咔咔咔,咔咔咔,完了,自己去看脚本吧。摘一部分鉴赏,关于shell脚本的编写自己百度吧,跟C语言的风格很像。

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第7张图片

        其中类似于mpu9250 start就启动了mpu9250模块的驱动,理解该部分需要结合modules/PX4Firmware/src/drivers/mpu9250中的CMakeLists.txt和module.mk这两个,其中CMakeLists.txt是使用Cmake写的,和module.mk类似,在最新的pixhawk原生代码中删除了make,使用清一色的Cmake。

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第8张图片

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第9张图片

        上面标红的MODULE_COMMAND = mpu9250就是为了以后可以直接在.mk文件直接使用的命令mpu9250。理解了上述以后,其他的driver中的所有的传感器模块、各种总线和modules/PX4Firmware/src/modules中的各种算法(mc_att_control、land_detector等等)都是按照此类方法实现的,不在赘述,但是这个rcS只是负责启动driver中的,modules/PX4Firmware/src/modules需要靠下面的一个rcS启动,两个rcS各自分工。全部都是.mk啊,.mk,.mk~~~~

        下一阶段主要应该就是放在这个modules/PX4Firmware/src/modules内部了,整个控制响应过程全靠它了,尤其是commander,校磁什么的都在里面,各种calibration,见过2000+行的函数么?!怕么? ~~~   168MHZ的主频呢,怕啥

        PS:需要注意的是,在modules/PX4Firmware/src/drivers/boards/px4fmu-v2中有几个.c文件,其中px4fmu_spi.c和board_config.h。

        px4fmu_spi.c 会在入口函数__start()中被调用初始化SPI,下面会提到

* Name: stm32_spiinitialize
 * Description:
 *   Called to configure SPI chip select GPIO pins for the PX4FMU board.

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第10张图片

        board_config.h:如下的代码是不是很熟悉,用过单片机的肯定都见过,尤其是pixhawk使用的又是STM32,再加上ST公司的芯片在各个高校中的普及程度,虽然我没用过,我是菜鸟。

Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)_第11张图片

        2)另外一个rcS就是modules/PX4Firmware/ROMFS/px4fmu_common/init.d中的rcS,PX4FMU的自启动文件,它主要是负责启动modules/PX4Firmware/modules里面的关于各自控制算法的模块。同时,该rcS会以脚本的形式启动同一级目录中的rc.mc_apps、rc_io等等。详细实现过程参见源代码。

        地址:https://github.com/PX4/Firmware/tree/master/ROMFS/px4fmu_common/init.d

        举一例说明问题,rc.mc_apps:关于姿态估计、位置估计和姿态控制和位置控制的,源代码如下。

#!nsh
# Standard apps for multirotors:
# att & pos estimator, att & pos control.
# The system is defaulting to INAV_ENABLED = 1
# but users can alternatively try the EKF-based
# filter by setting INAV_ENABLED = 0
if param compare INAV_ENABLED 1
then
	attitude_estimator_q start  //姿态估计
	position_estimator_inav start//位置估计
else
	if param compare LPE_ENABLED 1
	then
		attitude_estimator_q start
		local_position_estimator start
	else
		ekf2 start
	fi
fi
if mc_att_control start //姿态控制
then
else
	# try the multiplatform version
	mc_att_control_m start
fi
if mc_pos_control start //位置控制
then
else
	# try the multiplatform version
	mc_pos_control_m start
fi
# Start Land Detector
land_detector start multicopter //启动落地检测

        应该能了解代码中的xxxxx start的含义了吧~~~~还不了解的话请拨打114,有专人为您解答

六、 再深入一点

1、主控STM32F4的选择(肯定很多人不知道这个)

        在ardupilot/modules/PX4NuttX/nuttx/arch/arm/src/stm32中,主要是通过在stm32_start.c中包含头文件,board.h中包含并且配置了STM32的时钟(Clock,stm32.h中包含另外,stm32.h中还包含了关于stm32的各种Peripherals的头文件,即各种外设(spicanuarti2c等等)的驱动

        在ardupilot/modules/PX4NuttX/nuttx/arch/arm/src/stm32文件下有头文件chip.h,内部通过条件编译执行进行主控MCU的选择。实例代码如下。

/* STM32 F2 Family ******************************************************************/
#elif defined(CONFIG_STM32_STM32F20XX)
#  include "chip/stm32f20xxx_pinmap.h"
/* STM32 F3 Family ******************************************************************/
#elif defined(CONFIG_STM32_STM32F30XX)
#  include "chip/stm32f30xxx_pinmap.h"
/* STM32 F4 Family ******************************************************************/
#elif defined(CONFIG_STM32_STM32F40XX)
#  include "chip/stm32f40xxx_pinmap.h"
#else
#  error "No pinmap file for this STM32 chip"
#endif 

2、入口函数(__start())(这个就更不知道了吧)

        在ardupilot/modules/PX4NuttX/nuttx/arch/arm/src/stm32文件下有定义文件stm32_start.c,内部有个上电/重启的函数入口__start(void)。仔细看,一行行的看,别偷懒,代码附加列出了几个小问题,检查自己一下都会么?!!!

/****************************************************************************
 * Public Functions
 ****************************************************************************/
/****************************************************************************
 * Name: _start
 *
 * Description:
 *   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();
  stm32_lowsetup();
  stm32_gpioinit();
  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.
    inline的好处。
*/
  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.
   * 知道上述所讲的几个segments的分布么?!*/
  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();//初始化SPI(飞控板上的传感器都是通过SPI通信)和LEDs。  
                         // 还记上面提到的px4fmu_spi.c么?! 
  showprogress('F');
  /* Then start NuttX */
  showprogress('\r');
  showprogress('\n');
  os_start();//还记得上一篇博文的这个么?
  /* Shoulnd't get here */
  for(;;);
}
        轻松一下:飞控板上为什么使用SPI而不用I2C?如下是官方原版解释,说白了就是速度问题,如下权当练练English~~~

        I2C was not intended by Philips as a high rate sensor bus - this is what SPI has been invented for. Running multiple sensors at a suitably high rateover I2C is not recommended. Our hardware is designed to rely on SPI for allcritical sensors, and is available globally. Given the individual cost of thediscovery kit and sensor boards and power supply, one of the available Pixhawkkits is actually not more expensive, and will save a lot of trouble duringdevelopment and operation.

3、 Init sensors

        飞控板是干嘛的?

        控制无人机飞行滴(逼格高点就是通过各种传感器检测无人机的实时姿态信息和位置信息并结合MCU输出控制信息)。

        那sensor使用前要干嘛呢?

        !#¥@¥!@%¥!#%

        初始化,初始化,初始化~~~~

        上面那么多传感器必须得初始化以后才能使用啊,所以需要先初始化用到的所有的传感器,在哪配置呢,你找到没?! 在ardupilot/modules/PX4Firmware/src/modules/sensors文件中有定义文件sensor.c(关于飞控板中的各个sensor的初始化函数,以task的方式启动,初始化完成以后kill掉这个task,都是POSIX接口的API)。

        首先是sensors_main函数

int sensors_main(int argc, char *argv[])
{
 ………
if (OK != sensors::g_sensors->start()) {  //2293 line number
delete sensors::g_sensors;
sensors::g_sensors = nullptr;
warnx("start failed");
return 1;
}
………
        然后捏:start()函数
Int Sensors::start()
{
ASSERT(_sensors_task == -1);
/* start the task */
/*创建任务进程对sensors进行初始化以后,在task_main()函数执行的最后会调用px4_task_exit()函数退出该任务进程*//* px4_task_spawn_cmd()创建任务的接口函数是posix接口的,具体实现参见源码*/
_sensors_task = px4_task_spawn_cmd("sensors", SCHED_DEFAULT, 
 SCHED_PRIORITY_MAX - 5,
   2000,
   (px4_main_t)&Sensors::task_main_trampoline,
   nullptr);
/* wait until the task is up and running or has failed */
while (_sensors_task > 0 && _task_should_exit) {
usleep(100);
}
if (_sensors_task < 0) {
return -ERROR;
}
return OK;
} 

        然后捏:task_main_trampoline()函数

Void Sensors::task_main_trampoline(int argc, char *argv[])
{
sensors::g_sensors->task_main();
} 

        再然后捏:task_main()函数,大牌终于出场了

Void Sensors::task_main()
{
/* start individual sensors */
int ret = 0;
do { /* create a scope to handle exit with break *//*do_while用的精妙*/
ret = accel_init();/* return 0 */
if (ret) { break; }
ret = gyro_init();
if (ret) { break; }
ret = mag_init();
if (ret) { break; }
ret = baro_init();
if (ret) { break; }
ret = adc_init();
if (ret) { break; }
break;
} while (0);
………//2059
/*
 * do subscriptions  这就是所谓的IPC使用的uORB模型( publish_subscribe)吧 ,我也不懂 
 */
unsigned gcount_prev = _gyro_count;
………
_gyro_count = init_sensor_class(ORB_ID(sensor_gyro), &_gyro_sub[0],
&raw.gyro_priority[0], &raw.gyro_errcount[0]);
………
/* get a set of initial values */
accel_poll(raw);//通过uORB模型获取acc值
gyro_poll(raw);
mag_poll(raw);
baro_poll(raw);
diff_pres_poll(raw);
parameter_update_poll(true /* forced */);
rc_parameter_map_poll(true /* forced */);
………
/* check vehicle status for changes to publication state */
vehicle_control_mode_poll();
/* the timestamp of the raw struct is updated by the gyro_poll() method */
/* copy most recent sensor data */
gyro_poll(raw);
accel_poll(raw);
mag_poll(raw);
baro_poll(raw);
………
/* check battery voltage */
adc_poll(raw);
diff_pres_poll(raw);
/* Inform other processes that new data is available to copy */
if (_publishing && raw.timestamp > 0) {
orb_publish(ORB_ID(sensor_combined), _sensor_pub, &raw);
}
/* keep adding sensors as long as we are not armed,
 * when not adding sensors poll for param updates
 */
if (!_armed && hrt_elapsed_time(&_last_config_update) > 500 * 1000) {
_gyro_count = init_sensor_class(ORB_ID(sensor_gyro), &_gyro_sub[0],
&raw.gyro_priority[0], &raw.gyro_errcount[0]);
_mag_count = init_sensor_class(ORB_ID(sensor_mag), &_mag_sub[0],
       &raw.magnetometer_priority[0], &raw.magnetometer_errcount[0]);
_accel_count = init_sensor_class(ORB_ID(sensor_accel), &_accel_sub[0],
 &raw.accelerometer_priority[0], &raw.accelerometer_errcount[0]);
_baro_count = init_sensor_class(ORB_ID(sensor_baro), &_baro_sub[0],
&raw.baro_priority[0], &raw.baro_errcount[0]);
_last_config_update = hrt_absolute_time();
} else {
/* check parameters for updates */
parameter_update_poll();
/* check rc parameter map for updates */
rc_parameter_map_poll();
}
/* Look for new r/c input data */
rc_poll();
perf_end(_loop_perf);
}
warnx("exiting.");
_sensors_task = -1;
px4_task_exit(ret);/* px4_task_exit ()终止任务的接口函数也是posix接口的*/
}

        PS:关于IPC使用的 uORB的简单介绍(摘自官网)。

        The micro Object Request Broker (uORB) application is used toshare data structures between threads and applications,Communications between processes / applications (e.g. sendingsensor values from the sensors app to the attitude filter app) is a key part ofthe PX4 software architecture. Processes (often called nodes in this context) exchange messagesover named buses, called topics.In PX4, a topic contains only one message type, e.g. the vehicle_attitude topic transports a message containing the attitude struct (roll, pitch and yaw estimates). Nodes can publish a message on a bus/topic (“send” data)or subscribe toa bus/topic (“receive” data). They are not aware of who they are communicatingwith. There can be multiple publishers and multiple subscribers to a topic.This design pattern prevents locking issues and is very common in robotics. Tomake this efficient, there is always only one message on the bus and no queue iskept.

        详细介绍:https://pixhawk.org/dev/shared_object_communication

4、如何获取精确时间(timestamp

        在控制过程中多数环节都是使用经典的PID控制算法,为了获取较为实时的响应最重要的时间变量,这就涉及如何获取高精度的时间问题。

        Pixhawk主控使用ST公司的STM32F4系列处理器,其主频达168MHZ,内部有高精度RTC,以RTC为基准获取精确计时。根据分析源码发现,在modules/PX4Firmware/src/drivers中有一个头文件drv_hrt.h(High-resolutiontimer with callouts and timekeeping),内部对其作出了一部分的介绍,微妙(us)级的精确计时,在中断上下文中调用,与其他函数并行执行不会被堵塞。摘抄几个典型函数如下。

/** Get absolute time.*/
__EXPORT extern hrt_abstime hrt_absolute_time(void);
/** Compute the delta between a timestamp taken in the past and now.
* This function is safe to use even if the timestamp is updated by an interrupt during execution. */
__EXPORT extern hrt_abstime hrt_elapsed_time(const volatile hrt_abstime *then);
/** Store the absolute time in an interrupt-safe fashion.
* This function ensures that the timestamp cannot be seen half-written by an interrupt handler.*/
__EXPORT extern hrt_abstime hrt_store_absolute_time(volatile hrt_abstime *now);
/* initialise a hrt_call structure */
__EXPORT extern void	hrt_call_init(struct hrt_call *entry);
/* Initialise the HRT. */
__EXPORT extern void	hrt_init(void);

        针对上述hrt_absolute_time(void)函数做阐述,其他的都类似,它的原型在modules/PX4Firmware/unittests中的hrt.cpp。

hrt_abstime hrt_absolute_time()
{
	struct timeval te;
	gettimeofday(&te, NULL); // get current time
	hrt_abstime us = static_cast(te.tv_sec) * 1e6 + te.tv_usec; // caculate us
	return us;
}

        然后捏:直接进入操作系统(NuttX),轮到操作系统上场了,modules/PX4NuttX/nuttx/schedclock_gettimeofday.c,自己跟踪进去看吧,不在赘述。

七、总结

        一句话两句话也写清楚,那就不写了吧~~~其实也不知道写啥了。

        反正就是这套代码不简单,不简单。

        接下来可能是继续研究ardupilot这套代码,这套的话就是到上层应用了,即关于loop函数和scheduler_task的问题了;或者转变到pixhawk的原生代码上,这套就是直接到控制算法上,commander。。。。。

你可能感兴趣的:(UAV)