分享

PX4飞控学习与开发(七)-Pixhawk源码中的功能模块分析_pixhawk中servno output功能函数helirsc

 netouch 2023-07-03 发布于北京

本篇博客主要介绍Firmware固件中各功能模块的基本结构。

功能模块的编译

上篇博客内容中的demo我们可以发现,如果我们需要给Pixhawk模块新增一个功能模块,一般的做法是新建一个文件夹,所有这个功能模块需要处理的事情均在该文件夹下完成。这个文件夹的结构大致如下:

example
   |--example.c/example.cpp/example.hpp/example.hpp
   |--CMakeLists.txt
   |--....

其中 example.c/example.cpp/example.hpp/..等是实现具体功能的程序文件,CMakeLists.txt 可以视为编译文件列表。以上已是新增功能模块的最低要求。为了能添加一些可以在地面站修改的参数,往往还会增加params.c文件,即基本结构变为:

example
|--example.c/example.cpp/example.hpp/example.hpp
|--CMakeLists.txt
|--params.c
|--....

当一个功能模块里面还包含一些子功能模块的时候,也可以嵌套文件夹放入文件子功能模块文件,以 drivers层下的 gps模块为例,它的结构如下:

gps
|-- >device
|-- >src
|--ashtech.cpp/ashtch.h/mtk.cpp/mtk.h/....
|--CMakeLists.txt
|--gps.cpp
|--params.c
|--definitions.h

它包含了一个 device文件夹,而该文件夹下又包含了一个 src文件夹,之后才是代码文件。从文件夹的命名也可以推测出,可实现 gps定位功能的设备不限于一种,src文件夹就是用于收录这些可实现卫星定位功能设备的驱动文件,实际实现时,可根据具体设备连接情况调用相应的文件执行。现在我们需要关注的是系统如何能编译到这些代码。

通过demo文件 ,我们知道,系统能够编译到新建的代码是因为 CMakeLists.txt文件中的:

...
SRCS
my_example_app.c
...

据此,我们也可以推测,只要我们新建的 xx.c文件或 xx.cpp文件能够在 CMakeLists.txt中出现即可让系统找到并编译。我们可以打开 gps下的 CMakelists.txt文件验证我们的猜想,发现果然有如下内容:

...
SRCS
gps.cpp
devices/src/gps_helper.cpp
devices/src/mtk.cpp
devices/src/ashtech.cpp
devices/src/ubx.cpp
devices/src/uwb.cpp
...

其中 devices/src/..是文件目录。由此,我们可以再次重申下我们的推测:

  • 新建的.cpp或.c等源文件必须在CMakeLists.txt里申明。

对于头文件,源文件调用时,只需在文件开始的地方包含该头文件:#inlude "xxx.h"#include "xx.hpp"即可。

功能模块的入口函数

demo 实际上是独立运行的,就是说它不需要被别的文件调用,它有自己主文件。这也 Nuttx系统的工作特点之一,可以并行处理多进程任务。每一个进程有且仅有一个主文件 (xx.c 或.cpp) 和若干个附属文件(也可以没有)。例如,demo中的主文件就是

my_example_app.c

主文件中必须有一个入口函数,也就是进程开始执行的地方。入口函数要先用 __EXPORT申明,然后再定义。以 demo为例,其申明和定义的格式为:

...
__EXPORT int my_example_app_main(int argc, char *argv[]);//入口函数申明格式
...
int my_example_app_main(int argc, char *argv[])
//入口函数定义格式
{
...
}
....

argc是 argument count 的缩写,表示传入 main函数的参数个数;argv是 argument vector 的缩写,*argv[]表示传入入口函数的命令和参数序列,并且第一个参数argv[0] 一定是命令的名称。以 gps进程的启动命令为例:
gps start
则有,argc=2, *argv[0]="gps",*agrv[1]="start"

功能模块的基本结构

Pixhawk 中任何一个功能模块(如位置控制、姿态控制、各个传感器驱动模块等)或者说一个进程工作基本模式如下图所示:
在这里插入图片描述


图1 模块基本工作模式

下面以一个示例来分析如何实现图中功能模块的工作模式。

例1: 利用 Pixhawk 飞控和控制台实现加法功能,即控制台输入 加数 a和 被加数 b, 然后
飞控计算后,再返回结果到控制台。

为了能将该任务依照图2.6给定的方式完成,程序应当具备以下特点:

  • 一共包含五个指令:进程启动指令、进程停止指令、进程状态指令、进程帮助指令以及用户操作指令。
  • 用户操作指令的格式为: my_example_app test -a num1 -b num2,其中 my_example_app是指令的名称, -a num1 -b num2均是指令参数。如一个正常命令为:
my_example_app test -a 100 -b 2
  • 指令输入后,应当反馈计算的结果到控制台,然后系统继续等待新的指令, 比如上面 100 加 2 的例子,应返回:
The result is: 102
  • 启动指令为:my_example_app start, 输入后,若当前已启动,返回信息:"Task is already running, now is waiting for data to my_example_app"; 若当前未启动,则启动状态设为 true,并返回信息:"Task start successful!"
  • 停止指令为:my_example_app stop, 输入后,启动状态转化为 false。
  • 状态指令为:my_example_app status,输入后,若当前未启动,返回信息:"Task is is not running, please start first"; 若当前已启动,则返回信息 Task is already running, now is waiting for data to my_example_app!
  • 帮助指令为:my_example_app help,输入后,返回以下信息:
This is my_example_app module, using for addition opreation. You can use the following command:
my_example_app start //---- start the module
my_example_app stop //---- stop the module
my_example_app status
my_example_app help
//---- show the current state of the module
//---- show what you can do in this module
my_example_app test -a num1 -b num2
//----caculate your data,
there is a limit that "a<=10000 and a>=-10000", and ’b’ has the same limitation.
  • 当指令不符合规范时,返回信息:
command is not right, please check if it is one of the following command
:
my_example_app start //---- start the module
my_example_app stop //---- stop the module
my_example_app status
my_example_app help
//---- show the current state of the module
//---- show what you can do in this module
my_example_app test -a num1 -b num2
//----
caculate your data, there is a limit that "a<=10000 and a>=-10000", and ’b’ has the same limitation.
  • 当输入数据超范围时,返回警告信息 “Your data is beyond the limitation

实现: 考虑到源码中主要是用 C++ 进行编程,这里我们将 demo 用的 .c文件改为 .cpp文件,从而文件中需要创建一个类,我们创建如下:

#include <px4_posix.h>//包含了打印信息函数:PX4_INFO
#include <px4_defines.h>//含有OK等定义
#include <px4_getopt.h>//用于解析指令
#include <stdlib.h>//基本函数库,如字符串转浮点数据的函数: atof
#include <string.h>//用于字符串处理

extern "C" __EXPORT int my_example_app_main(int argc, char *argv[]);

class AddtionCall
{
public:
  /**
   * Constructor
   */
   AddtionCall();//构造函数

  /**
   * Destructor, also kills task.
   */
    ~AddtionCall();//析构函数

  int    start();
  int    stop();
  void   status();
  void   help();
  void   my_example_app(int argc, char *argv[]);

private:
  float   _num1 ;//加数
  float   _num2;//被加数
  float   _result ;//结果
  bool    _status; //状态标志
};     

如此,我们定义了一个名为Addtioncall的类,里面包含了需要实现各个指令的公有函数,和用于运算与状态标记的变量。

C++中类的成员(即类里面定义的函数、变量等)可分类三种:public、protected、private;public 表明该数据成员、成员函数是对所有用户(类对象)开放的,所有用户都可以直接进行调用;private 表示私有,私有的意思就是除了 class 自己的成员(如类里的各种函数)可以使用之外,任何类对象都不可以直接使用;protected对于继承和友元类来说,就是 public 的,可以自由使用,没有任何限制,而对于其他的外部class,protected 就变成 private。

类函数具体的实现可参考C++相关资料, 这里主要以加法运算函数为例:

void AddtionCall::my_example_app(int argc, char *argv[])
{
  bool error_flag=false;
  int myoptind = 1;
  int ch;
  const char *myoptarg = nullptr;

  while ((ch = PX4_getopt(argc, argv, "a:b:", &myoptind, &myoptarg)) != EOF){
    switch (ch) {
    case 'a':
      _num1= atof(myoptarg);
      break;

    case 'b':
      _num2 = atof(myoptarg);
      break;
    default:
      PX4_WARN("unrecognized flag");
      error_flag = true;
      break;
    }
  }

  if (error_flag){
      ;
  }
  else{
      if (_num1<=10000&&_num1>=-10000&&_num2<=10000&&_num2>=-10000){
          _result=_num1+_num2;
          PX4_INFO("The result is: %f",(double)_result);
      }
      else{
        PX4_WARN("Your data is beyond the limitation!");
      }     
  }
  
}

该段函数比较关键的是对指令参数的提取,主要利用了头文件 #include<px4_getopt.h>中的

int PX4_getopt(int argc, char *argv[], const char *options, int *myoptind,
const char **myoptarg)

函数来解析指令。如我们想传入某一个参数值,那么必须以-cmd_name cmd_value(指令名称和参数值,参数值有时候也可以没有)的形式传入,其中符号"-" 标识其后是一个指令名,这有利于函数PX4_getopt在其字符串参数 *options中对指令进行检索,如上述函数参数中:

*options="a:b:"//这里的冒号”:“表示指令后有参数值,需要提取到*myoptarg中,若没有冒号,表示该指令后不带参数值

而我们输入的命令是

my_example_app test -a num1 -b num2

那么函数PX4_getopt根据符号"-"检测到指令 ab并对照 *options, 发现确实有指令 a和 b,然后根据冒号提取后面的参数值 num1num2,我们最后用 atof函数将提取到的参数值转换为浮点型数据。

最后,我们打开 QGC 地面站的控制台,输入指令测试,得到我们期望的测试结果如下图所示。
在这里插入图片描述


图3 控制台运算结果  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多