源代码解析.docx
《源代码解析.docx》由会员分享,可在线阅读,更多相关《源代码解析.docx(45页珍藏版)》请在冰豆网上搜索。
源代码解析
Pixhawk源码笔记一:
APM代码基本结构
Pixhawk源码笔记一:
APM代码基本结构
基础知识
详细参考:
第一部分:
介绍
详细参考:
ArduPilot代码分为5个主要部分,基本结构分类如下:
vehicledirectories
AP_HAL
libraries
toolsdirectories
externalsupportcode
1、vehicledirectories模型类型
当前共有4种模型:
ArduPlane,ArduCopter,APMrover2andAntennaTracker。
都是.pde文件,就是为了兼容arduino平台,以后可能会放弃。
2、AP_HAL硬件抽象层
硬件抽象层,使得在不同硬件平台上的移植变得简单。
其中AP_HAL目录定义了一个通用的接口。
其他的目录AP_HAL_XXX针对不同硬件平台进行详细的定义。
例如AP_HAL_AVR目录对于AVR平台,AP_HAL_PX4对应PX4平台,AP_HAL_Linux对应Linux平台。
3、toolsdirectories工具目录
主要提供支持。
Forexamples,tools/autotestprovidestheautotestinfrastructurebehindthesiteandtools/Replayprovidesourlogreplayutility.
4、externalsupportcode外部支持代码
对于其他平台,需要外部支持代码。
例如Pixhawk、PX4的支持代码如下:
PX4NuttX–板载实时系统。
thecoreNuttXRTOSusedonPX4boards
PX4Firmware–PX4固件。
thebasePX4middlewareanddriversusedonPX4boards
uavcan–飞行器CAN通信协议。
theuavcanCANBUSimplementationusedinArduPilot
mavlink–Mavlink通信协议。
themavlinkprotocolandcodegenerator
5、系统编译
针对不同的硬件板,编译可以采用“makeTARGET”的形式。
makeapm1–theAPM1board
makeapm2–theAPM2board
makepx4-v1–thePX4v1
makepx4-v2–thePixhawk
如果要移植到新的硬件,可以在mk/targets.mk文件中添加。
比如:
makeapm2-octa-j8
或者:
makepx4-v2-j8
采用8通道并行编译方式,针对APM、Pixhawk硬件板(AVR、STM32),编译八旋翼代码。
第二部分:
学习sketch例程代码
sketch,是指使用.pde文件编写的主程序。
开始之前,你可以试着阅读、编译并运行下面的sketches
libraries/AP_GPS/examples/GPS_AUTO_test
libraries/AP_InertialSensor/examples/INS_generic
libraries/AP_Compass/examples/AP_Compass_test
libraries/AP_Baro/examples/BARO_generic
libraries/AP_AHRS/examples/AHRS_Test
例如,下面的编译方法,将在Pixhawk上安装AP_GPS例程sketch。
cdlibraries/AP_GPS/examples/GPS_AUTO_test
makepx4-clean
makepx4-v2
makepx4-v2-upload
正确理解sketch例程代码,我们以GPS_AUTO_test.pde代码为例(目录ardupilot\libraries\AP_GPS\examples\GPS_AUTO_test),主要几个特点:
1、pde文件包含很多includes;
2、定义了hal引用声明;
3、代码非常粗糙;
4、setup()和loop()函数
1、include文件
pde文件转变为C++文件后,提供必要的库引用支持。
2、hal引用声明
定义如下:
constAP_HAL:
:
HAL&hal=AP_HAL_BOARD_DRIVER;//pixhawk等价于AP_HAL_PX4
该定义,方便访问硬件接口,比如console终端、定时器、I2C、SPI接口等。
实际的定义是在HAL_PX4_Class.cpp中定义,如下:
constHAL_PX4AP_HAL_PX4;
hal是针对AP_HAL_PX4的引用。
经常使用的方法如下:
终端字符输出。
hal.console->printf()andhal.console->printf_P()toprintstrings(usethe_PtouselessmemoryonAVR)
获取当前运行时间。
hal.scheduler->millis()andhal.scheduler->micros()togetthetimesinceboot
延时。
hal.scheduler->delay()andhal.scheduler->delay_microseconds()tosleepforashorttime
IO输入输出。
hal.gpio->pinMode(),hal.gpio->read()andhal.gpio->write()foraccessingGPIOpins
I2C操作,hal.i2c
SPI操作,hal.spi
3、setup()和loop()
每个sketch都有一个setup()和loop()函数。
板子启动时,setup()被调用。
这些调用都来自HAL代码中的main()函数调用(HAL_PX4_Class.cpp文件main_loop())。
setup()函数只调用一次,用于初始化所有libraries。
Loop()循环被调用,执行主任务。
4、AP_HAL_MAIN()宏指令
每一个sketch(.pde文件)最底部,都有一个“AP_HAL_MAIN();”指令,它是一个HAL宏,用于定义一个C++main函数,整个程序的入口。
它真正的定义在AP_HAL_PX4_Main.h中。
#defineAP_HAL_MAIN()\
extern"C"__EXPORTintSKETCH_MAIN(intargc,char*constargv[]);\
intSKETCH_MAIN(intargc,char*constargv[]){\
hal.init(argc,argv);\
returnOK;\
}
作为程序的起点,在AP_HAL_MAIN()里,就正式调用了hal.init()初始化代码。
程序的执行过程就是:
程序起点AP_HAL_MAIN()àhal.init()àhal.main_loop()àsketch中的setup()和loop()。
Pixhawk源码笔记二:
APM线程
Pixhawk源码笔记一:
APM代码基本结构,参见:
这里,我们对APM线程进行讲解。
如有问题,可以交流30175224@。
新浪@WalkAnt,转载本博客文章,请注明出处,以便更大范围的交流,谢谢。
第三部分 APM线程
详细参考:
对于APM1、APM2硬件板,不支持多线程,所以只能通过简单的定时器加回调函数来实现。
类似PX4和Linux硬件板支持Posix标准的多线程。
线程一般是指基于多任务操作系统的并行任务,我们首先要明白的几个概念如下:
1、定时回调
2、HAL专属线程
3、驱动专属线程
4、APM驱动与板级驱动
5、板级专属线程、任务
6、AP_Scheduler任务调度系统
7、信号灯(任务队列互锁用)
8、locklessdatastructures
如果你对操作系统运行机制比较了解,那就很好理解了。
1、定时回调Thetimercallbacks
每个飞控平台都提供一个1kHz的定时器(见AP_HAL),通过“注册”一个定时器函数来获取1kHz定时功能。
所有注册的定时器将被顺序调用。
调用形式如下:
hal.scheduler->register_timer_process(AP_HAL_MEMBERPROC(&AP_Baro_MS5611:
:
_update));
定时器优先级为181,高于主进程的180。
上面代码是以MS5611气压计驱动为例,其中AP_HAL_MEMBERPROC()宏,主要作用是将一个C++成员函数包装起来,作为一个回调参数。
其定义在AP_HAL_Namespace.h文件中,如下:
//macrotohidethedetailsofAP_HAL:
:
MemberProc
#define AP_HAL_MEMBERPROC(func)fastdelegate:
:
MakeDelegate(this,func)
使用hal.scheduler->millis()andhal.scheduler->micros()可以记录时间。
好了,你可以试着自己边一个简单的sketch,在setup()和loop()函数中练习一下1秒钟向USB终端输出一个时间或字符。
2、HAL专属线程
以PX4为例,HAL专属线程有:
1、UART线程,用于读、写串行接口数据(包括USB);
2、定时器线程,支持1kHz定时功能;
3、IO线程,支持写microSD、EEPROM、FRAM等。
对于Pixhawk,请准备一条调试电缆,连接到nshconsole(serial5端口),波特率57600。
如果已经连接,试下”ps”命令,你会得到如下信息:
PIDPRISCHDTYPENPSTATENAME
00FIFOTASKREADYIdleTask()
1192FIFOKTHREADWAITSIGhpwork()
250FIFOKTHREADWAITSIGlpwork()
3100FIFOTASKRUNNINGinit()
37180FIFOTASKWAITSEMAHRS_Test() AHRS线程
38181FIFOPTHREADWAITSEM(20005400) 定时器线程
3960FIFOPTHREADREADY(20005400) UART线程
4059FIFOPTHREADWAITSEM(20005400) IO线程
10240FIFOTASKWAITSEMpx4io()
13100FIFOTASKWAITSEMfmuservo()
30240FIFOTASKWAITSEMuavcan()
上面的线程为定时器线程(优先级181),UART线程(60),IO线程(59),以及其他线程诸如:
px4io,fmuservo,uavcan,lpwork,hpworkandidletasks
线程的主要目的是在不干扰主进程的情况下,在后台处理一些低优先级任务。
例如AP_Terrainlibrary,需要向microSD卡写地形文件,它的实现方式如下:
hal.scheduler->register_io_process(AP_HAL_MEMBERPROC(&AP_Terrain:
:
io_timer));
注意:
IO线程优先级59,相比定时器181优先级慢了很多。
3、Driver专属线程
没什么好说的,请参考英文原版,需要提的一点是,我们可以利用register_io_process()和register_timer_process()来处理驱动的访问。
4、APM驱动与板级(原生)驱动
我们可以看到MPU6000驱动有两个版本:
一个是APM版本,在libraries/AP_InertalSensor/AP_InertialSensor_MPU6000.cpp,另一个为原生代码版本,在PX4Firmware/src/drivers/mpu6000。
注意,对于Pixhawk,APM代码使用的是Pixhawk原生驱动,因为原生驱动已经做得很好了。
libraries/AP_InertialSensor/AP_InertialSensor_PX4.cpp中可以查看详情。
在非PX4平台上,我们使用AP_InertialSensor_MPU6000.cpp驱动,在PX4平台上,我们就用PX4原生驱动AP_InertialSensor_PX4.cpp
5、板级专属线程、任务
在上面第2节“HAL专属线程”讲到”ps”命令显示的线程。
很多都不是AP_HAL_PX4Schedule启动的线程,这些线程列举如下:
idletask–calledwhenthereisnothingelsetorun
init–usedtostartupthesystem
px4io–handlethecommunicationwiththePX4IOco-processor
hpwork–PX4稍低优先级驱动线程。
handlethreadbasedPX4drivers(mainlyI2Cdrivers)
lpwork–PX4非常低优先级驱动线程。
handlethreadbasedlowprioritywork(eg.IO)
fmuservo–AUX输出。
handletalkingtotheauxillaryPWMoutputsontheFMU
uavcan–handletheuavcanCANBUSprotocol
这些任务的启动,由rc.APM脚本文件(ardupilot\mk\PX4\ROMFS\init.d\rc.APM)指定。
PX4启动时,会读取该文件。
rc.APM属于nsh类型脚本。
作为练习,你可以修改rc.APM脚本文件,增加一些sleep和echo命令,那么当PX4启动时,通过debugconsole(也就是serial5)可以显示出来。
更多内容,可以参考英文原版。
原生线程的启动代码如下:
hrt_call_every(&_call,1000,_call_interval,(hrt_callout)&MPU6000:
:
measure_trampoline,this);
等同于AP_HAL中的hal.scheduler->register_timer_process()。
上述代码的意思是,HRT(highresolutiontimer)高精度定时器,以1000微妙的周期调用MPU6000:
:
measure_trampoline函数。
这些操作是禁止中断的,最多占用数十微妙的时间。
上面的优先级非常高。
下面的方法,是稍低优先级。
work_queue(HPWORK,&_work,(worker_t)&HMC5883:
:
cycle_trampoline,this,1);
用于处理I2C设备。
大概花几百微妙的操作时间。
是可以被中断的任务。
如果是最低优先级,那么参数改为LPWORK,这样的任务一般需要花费更长的时间。
6、AP_Scheduler任务调度系统
用于飞行器主线程,提供了简单的机制控制每个操作花费了多少时间。
例如:
1、等待一个新IMU采样;2、在每一个IMU采样周期之间调用一系列其他任务。
每一个飞行器都有一个AP_Scheduler:
:
Tasktable任务列表,参考代码(ardupilot\libraries\AP_Scheduler\ Scheduler_test.pde )类似如下:
staticconstAP_Scheduler:
:
Taskscheduler_tasks[]PROGMEM={
{ins_update,1,1000},
{one_hz_print,50,1000},
{five_second_call,250,1800},
};
结构体第1列,循环调用的任务函数。
第2列,调用频率(也叫tick,一个tick,就是一个最小时间单元,pixhawk为2.5ms)。
第3列为最大可能占用的操作时间,scheduler.run()会传递当前可用的时间(微秒),如果时间不够,那么这个任务就pass掉了,不执行。
注意,AP_Scheduler:
:
Tasktable列表必须具备以下条件:
1、他们不能被阻塞。
2、在飞行时,他们不能调用sleepfunction
3、他们必须有可预估的最坏的运行时间。
你可以修改Scheduler_test.pde,加入自己的代码来读取气压计、罗盘、GPS、更新AHRS输出roll/pitch。
7、信号灯
有3种方法可以避免多线程访问冲突:
1、信号灯;2、locklessdata;3、PX4ORB。
例如:
I2C驱动可以通过信号灯,确保同一时间,只有一个I2C设备被使用。
可以查看ardupilot\libraries\AP_Compass\AP_Compass_HMC5843.cpp了解:
获得信号灯:
_i2c_sem->take
(1);
释放信号灯:
_i2c_sem->give();
8、LocklessDataStructures
LocklessDataStructures比信号灯要方便,例子见:
the_shared_datastructureinlibraries/AP_InertialSensor/AP_InertialSensor_MPU9250.cpp
theringbuffersusedinnumerousplaces.Agoodexampleislibraries/DataFlash/DataFlash_File.cpp
Goandhavealookatthesetwoexamples,andprovetoyourselfthattheyaresafeforconcurrentaccess.ForDataFlash_Filelookattheuseofthe_writebuf_headand_writebuf_tailvariables.
9、PX4ORB
ORB(ObjectRequestBroker)是PX4的互斥机制。
另外两种PX4驱动通信机制,列举如下:
ioctl calls(seetheexamplesinAP_HAL_PX4/RCOutput.cpp)
/dev/xxx read/write calls(see_timer_tickinAP_HAL_PX4/RCOutput.cpp)
想要学习Pixhawk源码的朋友有福了,后边我会陆续的将Pixhawk的源码学习笔记整理出来分享给大家。
敬请关注:
新浪微博@WalkAnt,3017224@。
欢迎交流。
Pixhawk源码笔记三:
串行接口UART和Console
这里,我们对APMUARTConsole接口进行讲解。
如有问题,可以交流30175224@。
新浪@WalkAnt,转载本博客文章,请注明出处,以便更大范围的交流,谢谢。
第四部分 串行接口UART和Console
详细参考:
UART很重要,用于调试输出,数传、GPS模块等。
1、5个UART
目前共定义了5个UART,他们的用途分别是:
uartA–串行终端,通常是MicroUSB接口,运行MAVLink协议。
uartB–GPS1模块。
uartC–主数传接口,也就是Pixhawktelem1接口。
uartD–次数传接口,也就是telem2接口。
uartE–GPS2模块。
有些UART具备双重角色,比如通过修改SERIAL2_PROTOCOL参数