PX4通过I2C方式添加自定义传感器(3)

2023-05-16

添加自定义传感器并实现数据的发送和订阅

1、前期准备

1.1 建立文件夹和相关文件配置

我是在src/drivers/distance_sensor文件夹下操作的,当然其他文件夹下都类似。首先建立了两个文件夹angle_source和angle_detection,前面一个主要是基类的定义,里面有一些函数的申明,后面一个主要是存放主要的程序和实现消息的发布。angle_source这个文件夹我先不说,因为这个文件夹比较简单我是直接把src/drivers/airspeed文件夹下的内容拷贝到我的文件夹下的,然后对它进行一些名字上的修改。我们还是来看angle_detection文件夹吧。angle_detection可以看到我们建了两个文件,一个.cpp文件一个Cmakelists文件。.cpp文件就是存放代码的,cmakelists文件就是跟编译有关,有了这个文件我们才能让自己写的代码能够参与编译。

1.2 Cmakelists文件编写

我们先看一下代码

px4_add_module(
        MODULE angle_detection
        MAIN angle_sensor
    COMPILE_FLAGS
        -Wno-sign-compare
    SRCS
                angle_sensor.cpp
    DEPENDS
        platforms__common
    )
# vim: set noet ft=cmake fenc=utf-8 ff=unix :

关于这个文件的编写,随便找一个Cmakelists文件模仿一下就好了。需要说一下,MAIN就是主函数的名字,SRCS对应的是你.cpp文件的名字,比如我的就是angle_sensor.cpp。

1.3 编译文件nuttx_px4fmu-v2_default.cmake的修改

这个文件就是整个固件的编译文件了,我们只需要在其中加一句就行,这样就和我们刚刚写的Cmakelists文件对接上了,我们的程序就可以参与整体的编译了。这里写图片描述
红线所划之处就是我添加的模块。这样我们前期的准备工作就结束了。下面就是具体的编程了。

2、基类的编写

2.1 基类.cpp文件编写

这个其实就是我前面所说的angle_source文件夹下内容的编写,我也说了就是直接把src/drivers/airspeed文件夹下的内容拷贝到我的文件夹下的,然后对它进行一些名字上的修改。
这里我直接就把我的这块程序复制过来了,就不做过多介绍了。

#include <px4_config.h>
#include <drivers/device/device.h>

#include <drivers/device/i2c.h>

#include <systemlib/airspeed.h>
#include <systemlib/err.h>
#include <systemlib/param/param.h>
#include <systemlib/perf_counter.h>

#include <drivers/drv_airspeed.h>
#include <drivers/drv_hrt.h>
#include <drivers/device/ringbuffer.h>

#include <uORB/uORB.h>
#include <uORB/topics/angle_sensor.h>
#include <uORB/topics/subsystem_info.h>

#include <drivers/distance_sensor/angle_source/angle.h>

Angle::Angle(int bus, int address, unsigned conversion_interval, const char *path) :
        I2C("Angle", path, bus, address, 100000),
    _sensor_ok(false),
    _last_published_sensor_ok(true), /* initialize differently to force publication */
    _measure_ticks(0),
    _collect_phase(false),
    _diff_pres_offset(0.0f),
    _angle_pub(nullptr),
    _angle_orb_class_instance(-1),
    _subsys_pub(nullptr),
    _class_instance(-1),
    _conversion_interval(conversion_interval),
    _sample_perf(perf_alloc(PC_ELAPSED, "aspd_read")),
    _comms_errors(perf_alloc(PC_COUNT, "aspd_com_err"))
{
    // enable debug() calls
    _debug_enabled = false;

    // work_cancel in the dtor will explode if we don't do this...
    memset(&_work, 0, sizeof(_work));
}

Angle::~Angle()
{
    /* make sure we are truly inactive */
    stop();

    if (_class_instance != -1) {
        unregister_class_devname(AIRSPEED_BASE_DEVICE_PATH, _class_instance);
    }

    orb_unadvertise(_angle_pub);

    // free perf counters
    perf_free(_sample_perf);
    perf_free(_comms_errors);
}

int
Angle::init()
{
    /* do I2C init (and probe) first */
    if (I2C::init() != PX4_OK) {
        return PX4_ERROR;
    }

    /* register alternate interfaces if we have to */
    _class_instance = register_class_devname(AIRSPEED_BASE_DEVICE_PATH);

    /* advertise sensor topic, measure manually to initialize valid report */
    measure();
    angle_sensor_s data = {};

    /* measurement will have generated a report, publish */
        _angle_pub = orb_advertise_multi(ORB_ID(angle_sensor), &data,&_angle_orb_class_instance,
                                   ORB_PRIO_HIGH-_class_instance);

        if (_angle_pub == nullptr) {
        PX4_WARN("uORB started?");
    }

    return PX4_OK;
}

int
Angle::probe()
{
    /* on initial power up the device may need more than one retry
       for detection. Once it is running the number of retries can
       be reduced
    */
        _retries = 4;
    int ret = measure();

    // drop back to 2 retries once initialised
    _retries = 2;
    return ret;
}

int
Angle::ioctl(device::file_t *filp, int cmd, unsigned long arg)
{
    switch (cmd) {

    case SENSORIOCSPOLLRATE: {
            switch (arg) {

            /* switching to manual polling */
            case SENSOR_POLLRATE_MANUAL:
                stop();
                _measure_ticks = 0;
                return OK;

            /* external signaling (DRDY) not supported */
            case SENSOR_POLLRATE_EXTERNAL:

            /* zero would be bad */
            case 0:
                return -EINVAL;

            /* set default/max polling rate */
            case SENSOR_POLLRATE_MAX:
            case SENSOR_POLLRATE_DEFAULT: {
                    /* do we need to start internal polling? */
                    bool want_start = (_measure_ticks == 0);

                    /* set interval for next measurement to minimum legal value */
                    _measure_ticks = USEC2TICK(_conversion_interval);

                    /* if we need to start the poll state machine, do it */
                    if (want_start) {
                        start();
                    }

                    return OK;
                }

            /* adjust to a legal polling interval in Hz */
            default: {
                    /* do we need to start internal polling? */
                    bool want_start = (_measure_ticks == 0);

                    /* convert hz to tick interval via microseconds */
                    unsigned ticks = USEC2TICK(1000000 / arg);

                    /* check against maximum rate */
                    if (ticks < USEC2TICK(_conversion_interval)) {
                        return -EINVAL;
                    }

                    /* update interval for next measurement */
                    _measure_ticks = ticks;

                    /* if we need to start the poll state machine, do it */
                    if (want_start) {
                        start();
                    }

                    return OK;
                }
            }
        }
        break;

    case SENSORIOCGPOLLRATE:
        if (_measure_ticks == 0) {
            return SENSOR_POLLRATE_MANUAL;
        }

        return (1000 / _measure_ticks);

    case SENSORIOCRESET:
        /* XXX implement this */
        return -EINVAL;

    case AIRSPEEDIOCSSCALE: {
            struct airspeed_scale *s = (struct airspeed_scale *)arg;
            _diff_pres_offset = s->offset_pa;
            return OK;
        }

    case AIRSPEEDIOCGSCALE: {
            struct airspeed_scale *s = (struct airspeed_scale *)arg;
            s->offset_pa = _diff_pres_offset;
            s->scale = 1.0f;
            return OK;
        }

    default:
        /* give it to the superclass */
        return I2C::ioctl(filp, cmd, arg);
    }
}

void
Angle::start()
{
    /* reset the report ring and state machine */
    _collect_phase = false;

    /* schedule a cycle to start things */
        work_queue(HPWORK, &_work, (worker_t)&Angle::cycle_trampoline, this, 1);
}

void
Angle::stop()
{
    work_cancel(HPWORK, &_work);
}

void
Angle::update_status()
{
    if (_sensor_ok != _last_published_sensor_ok) {
        /* notify about state change */
        struct subsystem_info_s info = {};
        info.present = true;
        info.enabled = true;
        info.ok = _sensor_ok;
        info.subsystem_type = subsystem_info_s::SUBSYSTEM_TYPE_DIFFPRESSURE;

        if (_subsys_pub != nullptr) {
            orb_publish(ORB_ID(subsystem_info), _subsys_pub, &info);

        } else {
            _subsys_pub = orb_advertise(ORB_ID(subsystem_info), &info);
        }

        _last_published_sensor_ok = _sensor_ok;
    }
}

void
Angle::cycle_trampoline(void *arg)
{
    Angle *dev = (Angle *)arg;
    dev->cycle();

    dev->update_status();
}

可以看到我就是把类的名字进行了修改。这个类有什么作用呢?我们不妨再简单看一下这段程序,其实也就是几个函数的定义而已,比如我们看到有初始化函数Angle::init(),还有启动函数start(),还有一个名字是ioctl(),这个函数就是对文件的操作,比如到时候设置采集的频率等等。

2.2 基类Cmakelists文件修改

这个修改也比较简单了,就是几个名字的修改,不做过多介绍,代码如下:

px4_add_module(
        MODULE drivers__angle_source
    COMPILE_FLAGS
    SRCS
                angle.cpp
    DEPENDS
        platforms__common
    )

2.3 基类.h文件编写

这个文件就是头文件了,对类和基本函数的申明咯,到时候其他文件只要包含了这个头文件,相应的函数和类就可以使用了,而不必重复声明。代码如下:

#pragma once

#include <string.h>
#include <drivers/device/i2c.h>
#include <drivers/drv_airspeed.h>
#include <drivers/drv_hrt.h>
#include <px4_config.h>
#include <px4_defines.h>
#include <px4_workqueue.h>
#include <systemlib/airspeed.h>
#include <systemlib/perf_counter.h>
#include <uORB/uORB.h>
#include <uORB/topics/angle_sensor.h>
#include <uORB/topics/subsystem_info.h>

/* Default I2C bus */
static constexpr uint8_t PX4_I2C_BUS_DEFAULT = PX4_I2C_BUS_EXPANSION;

class __EXPORT Angle : public device::I2C
{
public:
        Angle(int bus, int address, unsigned conversion_interval, const char *path);
        virtual ~Angle();

    virtual int init();

    virtual int ioctl(device::file_t *filp, int cmd, unsigned long arg);

private:
    /* this class has pointer data members and should not be copied */
        Angle(const Angle &);
        Angle &operator=(const Angle &);

protected:
    virtual int probe();

    /**
    * Perform a poll cycle; collect from the previous measurement
    * and start a new one.
    */
    virtual void    cycle() = 0;
    virtual int measure() = 0;
    virtual int collect() = 0;

    /**
     * Update the subsystem status
     */
    void update_status();

    work_s          _work;
    bool            _sensor_ok;
    bool            _last_published_sensor_ok;
    uint32_t        _measure_ticks;
    bool            _collect_phase;
    float           _diff_pres_offset;

        orb_advert_t        _angle_pub;
    int         _angle_orb_class_instance;

    orb_advert_t        _subsys_pub;

    int         _class_instance;

    unsigned        _conversion_interval;

    perf_counter_t      _sample_perf;
    perf_counter_t      _comms_errors;

    /**
    * Initialise the automatic measurement state machine and start it.
    *
    * @note This function is called at open and error time.  It might make sense
    *       to make it more aggressive about resetting the bus in case of errors.
    */
    void    start();

    /**
    * Stop the automatic measurement state machine.
    */
    void    stop();

    /**
    * Static trampoline from the workq context; because we don't have a
    * generic workq wrapper yet.
    *
    * @param arg        Instance pointer for the driver that is polling.
    */
    static void cycle_trampoline(void *arg);

    /**
    * add a new report to the reports queue
    *
    * @param report     differential_pressure_s report
    */
        void    new_report(const angle_sensor_s &volt_data);
};

3、继承类的编写

从这里开始就进入到我们真正需要好好编写的地方了,需要根据自己的需要进行适当的修改。

-首先我们来看一些基本的常量的定义:

/* I2C bus address */
#define I2C_ADDRESS 0x48    /* 7-bit I2C ADDRESS */
#define SET_CMD 0x40|3 /*Choose the channel */
#define Anglesensor_BUS PX4_I2C_BUS_EXPANSION

#define angle_PATH  "/dev/angle_detection"

第一个定义的是I2C的地址,还是那句话这里的地址是7位的,这里需要根据具体的器件地址进行修改,地址如果不对了,什么数据都读不出来了。第二个定义的是一个控制命令,因为我的器件在读之前需要先进行通道的选择等一系列操作,所以需要先写入一个控制命令,如果你的器件在读之前也需要进行写控制命令也可以在这里进行定义。第三个定义的是I2C的总线有PX4_I2C_BUS_EXPANSION和PX4_I2C_BUS_ONBOARD可以选择,因为我的器件属于板外扩展的,所以就选择这个。第四个是路径的定义,定义了路径之后,一切都可以以文件的形式进行操作了,但是关于文件操作具体如何实现,这个我也不太清楚。

- 接着就是类的声明了

class Anglesensor:public Angle
{
public:

                Anglesensor( int bus , int address,const char *path);
                virtual int collect();

protected:

        virtual int measure();
//        virtual int collect();
        virtual void cycle();


};


Anglesensor::Anglesensor( int bus , int address=I2C_ADDRESS,const char *path=angle_PATH):
Angle(bus,address,10000,path)
{
    _device_id.devid_s.devtype=DRV_DIFF_PRESS_DEVTYPE_MS5525;
}

看到这里的类就是对我们刚刚建立的基类的公有继承了,比较简单。

- 然后就是相关函数的定义

int
Anglesensor::measure()
{
        int ret=-EIO;

        /*
         * Send the command to begin a measurement.
         */
        uint8_t cmd=SET_CMD;
        ret = transfer(&cmd,1,nullptr, 0);
        if (OK != ret) {
        perf_count(_comms_errors);
        }

        return ret;
}

int
Anglesensor::collect()
{
        int ret = -EIO;

        uint8_t val;
        angle_sensor_s volt_data;  /*define a struct to collect data*/

    /*read the data*/

        perf_begin(_sample_perf);
        ret = transfer(nullptr, 0, &val,1);
        if (ret < 0) {
        perf_count(_comms_errors);
        return ret;
        }
        PX4_INFO("volt data1:%d",val);   //add by klb 5/4
        volt_data.error_count=perf_event_count(_comms_errors);
        volt_data.volt=val;
        volt_data.volt_b=val/50;           //transfer to volt,b:the integer before point
        volt_data.volt_a=(val%50)/10;      //a:the integer after point
        volt_data.device_id=0;
        PX4_INFO("volt data2:%d",volt_data.volt);   //add by klb 5/4
   /*to publish a topic */


    if (_angle_pub != nullptr && !(_pub_blocked)) {

        orb_publish(ORB_ID(angle_sensor), _angle_pub, &volt_data);
    }
        ret = OK;
                perf_end(_sample_perf);


        return ret;
}

void
Anglesensor::cycle()
{
    int ret;

    if(_collect_phase){
        ret=collect();

        if(OK!=ret){
            perf_count(_comms_errors);
            start();
            _sensor_ok=false;
            return;
        }
        _collect_phase=false;

        if(_measure_ticks>USEC2TICK(10000)){
            work_queue(HPWORK,
                       &_work,
                       (worker_t)&Angle::cycle_trampoline,
                       this,
                       _measure_ticks-USEC2TICK(10000));

            return;
        }
    }


    ret=measure();
    if(OK!=ret){
        DEVICE_DEBUG("measure error");
    }
        _sensor_ok=(ret==OK);
        _collect_phase=true;

        /*schedule a fresh cycle call when the measurement is done*/
        work_queue(HPWORK,
                   &_work,
                   (worker_t)&Angle::cycle_trampoline,
                   this,
                   USEC2TICK(10000));

}

这里定义了measure()函数就是在读之前先发送控制命令的,collect()函数就是进行数据的读取,及一些简单的对数据结果的处理。处理完了之后我们看到有对数据的发布:

   /*to publish a topic */


    if (_angle_pub != nullptr && !(_pub_blocked)) {

        orb_publish(ORB_ID(angle_sensor), _angle_pub, &volt_data);
    }

这里的angle_sensor是我自己定义的一个topic,关于topic的定义也比较简单相关的文章也很多,你可以之间搜uORB添加自定义主题,就会有相关的文章。定义了主题之后,通过公告、发布、订阅,这样其他的app也可以使用你这个传感器的数据了,我们后面还会有这方面的操作。
关于cycle()这个函数看起来比较复杂,其实就可以理解为是一种循环进行读取。

- 最后就是一些提供给nsh调试的函数

上一篇文章也提到了,我们看一下代码:

/**
 * Local function in support of the shell command
 */

namespace anglesensor {
    Anglesensor *cal;
    int start(int i2c_bus);
    int stop();
    int test();
    int reset();

/**
 * @brief start
 * @param i2c_bus
 * start the driver
 */
int
start(int i2c_bus)
{
    int fd;
    if(cal!=nullptr){
        PX4_ERR("already started");
        return PX4_ERROR;
    }

    /*creat the driver*/
    cal=new Anglesensor(i2c_bus);

    if (cal==nullptr){
        goto fail;
    }


    if(OK!=cal->init()){
        PX4_ERR("i2c not started!");
        goto fail;
    }
    /*set the poll rate to default,starts automatic data collection*/
    fd=open(angle_PATH,O_RDONLY);
    if (fd<0){
        PX4_ERR("failed to open");
        goto fail;
    }

    if(px4_ioctl(fd,SENSORIOCSPOLLRATE,SENSOR_POLLRATE_DEFAULT)<0){
        PX4_ERR("failed to set default");
       goto fail;
    }

    return PX4_OK;
fail:
    if(cal!=nullptr){
        delete cal;
        cal=nullptr;
    }
    PX4_WARN("driver not started on bus %d",i2c_bus);
    return PX4_ERROR;
}

/**
 * @brief stop
 * @stop the driver
 */
int
stop()
{
    if(cal!=nullptr){
        delete cal;
        cal=nullptr;
    }else {
        PX4_ERR("driver not running");
        return PX4_ERROR;
    }
    return PX4_OK;

}

/**
 * @brief test
 * perform some basic funcational tests one driver;
 * make sure we can collect data from the sensor in polled
 * and automatic modes
 */
int
test()
{
//    struct angle_sensor_s volt_da;
    ssize_t sz;
    int ret;

    int fd=px4_open(angle_PATH,O_RDONLY);

    if(fd<0){
        PX4_ERR("%s open failed try'angle_sensor start'if the driver is not running",angle_PATH);
        return PX4_ERROR;
    }
    /*do a simple demmand read*/

    struct angle_sensor_s volt_da;
    int angle_sub_fd=orb_subscribe(ORB_ID(angle_sensor));
    orb_copy(ORB_ID(angle_sensor),angle_sub_fd,&volt_da);


    PX4_INFO("volt data3:%d",volt_da.volt);
    sz=px4_read(fd,&volt_da,sizeof(volt_da));

    if(sz!=sizeof(volt_da)){
        PX4_ERR("immediate read failed");
        return PX4_ERROR;
    }
    PX4_INFO("single read");
    PX4_INFO("transfer volt data: %d.%d V",volt_da.volt_b,volt_da.volt_a);

    /*start the sensor polling at 2HZ*/
    if(OK!=px4_ioctl(fd,SENSORIOCSPOLLRATE,2)){
        PX4_ERR("failed to set 2 HZ poll rate");
        return PX4_ERROR;
    }
    /*read the sensor 5x and report each value*/
    for(unsigned i=0;i<5;i++){
        px4_pollfd_struct_t fds;

        /*wait for data to be ready*/
        fds.fd=fd;
        fds.events=POLLIN;
        ret=px4_poll(&fds,1,2000);

        if(ret!=1){
            PX4_ERR("time out waiting for the sensor data");
            return PX4_ERROR;

        }
        /*now to get it*/
        sz=px4_read(fd,&volt_da,sizeof(volt_da));

        if(sz!=sizeof(volt_da)){
            PX4_ERR("periodic read failed");
        }

        PX4_INFO("periodic read %u",i);
        PX4_INFO("volt data: %d.%d",volt_da.volt_b,volt_da.volt_a);
    }
    /*reset the sensor polling to default rate*/
    if(OK!=px4_ioctl(fd,SENSORIOCGPOLLRATE,SENSOR_POLLRATE_DEFAULT)){
        PX4_ERR("failed to set default rate");
        return PX4_ERROR;
    }
    return PX4_OK;
}
/**
 * @brief reset
 * @reset the driver
 */
int reset()
{
    int fd=px4_open(angle_PATH,O_RDONLY);

    if(fd<0){
        PX4_ERR("failed");
        return PX4_ERROR;
    }

    if(px4_ioctl(fd,SENSORIOCSPOLLRATE,0)<0){
        PX4_ERR("driver reset failed");
        return PX4_ERROR;
    }

    if(px4_ioctl(fd,SENSORIOCSPOLLRATE,SENSOR_POLLRATE_DEFAULT)<0){
        PX4_ERR("driver poll restart failed");
        return PX4_ERROR;
    }
    return PX4_ERROR;
}
}

start()函数就是进行一些初始化的设置,另外如果我们需要nsh调试看我们能否得到传感器的数据时,必须首先要进行start()。关于test()函数,其实就是两个功能:
(1). 进行单个数据的读取;
(2). 设置读取频率为2HZ,连续读取五次。
但是在这里我也遇到了问题,

  sz=px4_read(fd,&volt_da,sizeof(volt_da));

    if(sz!=sizeof(volt_da)){
        PX4_ERR("immediate read failed");
        return PX4_ERROR;
    }
     }

就是在这里进行读的时候总是出现错误,显示“immediate read failed”,我琢磨了很久也不知道是什么原因,我觉得可能是路径方面出了问题,但是找相关的资料也找不到。但是还好,虽然这里读不出来,但是主题是成功地发布了,而且可以成功地进行订阅。所以我在这加了一句消息订阅进行检测:

 struct angle_sensor_s volt_da;
    int angle_sub_fd=orb_subscribe(ORB_ID(angle_sensor));
    orb_copy(ORB_ID(angle_sensor),angle_sub_fd,&volt_da);


    PX4_INFO("volt data3:%d",volt_da.volt);

既然成功了,我就没管它了。于是下面我们就进行在其他app里进行订阅,看看能否成功。

4、 在px4_simple_app中进行订阅

px4_simple_app在src/examples路径下,这是已经为我们提供好了的一个app例程,我们只需要简单进行修改就好了。还是直接看我修改后的文件吧:

/**
 * @file px4_simple_app.c
 * Minimal application example for PX4 autopilot
 *
 * @author Example User <mail@example.com>
 */

#include <px4_config.h>
#include <px4_tasks.h>
#include <px4_posix.h>
#include <unistd.h>
#include <stdio.h>
#include <poll.h>
#include <string.h>
#include <math.h>

#include <uORB/uORB.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/angle_sensor.h>

__EXPORT int px4_simple_ap_main(int argc, char *argv[]);

int px4_simple_ap_main(int argc, char *argv[])
{
    PX4_INFO("Hello KLB!");

        /* subscribe to sensor_combined topic */
        int sensor_sub_fd = orb_subscribe(ORB_ID(angle_sensor));
        /* limit the update rate to 5 Hz */
        orb_set_interval(sensor_sub_fd, 200);

        /* one could wait for multiple topics with this technique, just using one here */
        px4_pollfd_struct_t fds[] = {
                { .fd = sensor_sub_fd,   .events = POLLIN },
                /* there could be more file descriptors here, in the form like:
                 * { .fd = other_sub_fd,   .events = POLLIN },
                 */
        };

        int error_counter = 0;

        for (int i = 0; i < 20; i++) {
                /* wait for sensor update of 1 file descriptor for 1000 ms (1 second) */
                int poll_ret = px4_poll(fds, 1, 1000);

                /* handle the poll result */
                if (poll_ret == 0) {
                        /* this means none of our providers is giving us data */
                        PX4_ERR("Got no data within a second");

                } else if (poll_ret < 0) {
                        /* this is seriously bad - should be an emergency */
                        if (error_counter < 10 || error_counter % 50 == 0) {
                                /* use a counter to prevent flooding (and slowing us down) */
                                PX4_ERR("ERROR return value from poll(): %d", poll_ret);
                        }

                        error_counter++;

                } else {

                        if (fds[0].revents & POLLIN) {
                                /* obtained data for the first file descriptor */
                                struct angle_sensor_s raw;
                                /* copy sensors raw data into local buffer */
                                orb_copy(ORB_ID(angle_sensor), sensor_sub_fd, &raw);
                                PX4_INFO("num:%d \n\t\t    volt:%d.%d V",
                                         raw.volt,
                                         raw.volt_b,
                                         raw.volt_a);


                        }

                        /* there could be more file descriptors here, in the form like:
                         * if (fds[1..n].revents & POLLIN) {}
                         */
                }
        }

        PX4_INFO("exiting");

    return 0;
}

这里说一下两个地方:
(1). orb_set_interval(sensor_sub_fd, 200); 这个是对接收频率的设置,设置间隔为200ms也就是5HZ

(2).然后我在这里是设置了读取20次

另外需要注意的一点是,px4_simple_app也是需要加入到编译中去的,所以也需要在px4fmu-v2_default当中加入的,具体的方法和上面的一样。

4、加入自启动

为什么要加入自启动呢?这跟我们上面说的一个地方有关,上面我提到,如果我们要获取传感器的数据,我们首先需要先输入angle_sensor start这个命令,也就是启动命令。那么这是在调试中我们可以这么办,那么在实际板子上谁给你输入这个启动命令呢?没有人,那你就只能自启动了。
我们来看看自启动在哪操作,其实也是很简单。我们找到src/Firmware/ROMFS/px4fmu_common/init.d下的rc.sensor这个文件,这里就是对传感器的自启动了。
这里写图片描述
这里一定要找对,是在PX4FMU_V2下进行添加,红色线标注的部分就是添加的内容,这样他就可以自启动啦。我们直接输入px4_simple_ap就可以直接看到数据啦!

5、nsh调试

我们直接来看看调试的结果啦:
这里写图片描述
我们看到结果正是我们想要的,这样一切就大功告成了!
ALL GLORY TO GOD !

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PX4通过I2C方式添加自定义传感器(3) 的相关文章

  • PX4模块设计之四十六:dataman模块

    PX4模块设计之四十六 xff1a dataman模块 1 dataman模块简介2 模块入口函数dataman main3 dataman模块重要函数3 1 start3 2 stop3 3 status3 4 task main 4 A
  • px4仿真无法起飞问题(Failsafe enabled: no datalink)

    报错信息 问题描述 xff1a 使用JMAVSim和gazebo仿真px4起飞时报错如下 xff1a WARN commander Failsafe enabled no datalink 说不安全 解决方法 打开QGC 就可以起飞了
  • 【px4】运行mavsdk中的offboard example

    运行MAVSDK中的offboard例子时无人机不执行 想控制无人机前后左右移动 xff0c 在按照官方教程实现offboard 插件的时候 发现用action插件能正常起飞和降落 但是一旦执行到offboard的插件代码的时候就会自动降落
  • 什么是I2C通信

    ARM体系 I2C通信 一 什么是I2C 1 I2C总线是由Philips公司开发的一种简单 双向二线制同步串行总线 它只需要两根线即可在连接于总线上的设备之间传送信息 2 主设备用于启动总线传送数据 xff0c 并产生时钟以开放传送的设备
  • pixhawk px4 commander.cpp

    对于复杂的函数 xff0c 要做的就是看函数的输入是什么 来自哪里 xff0c 经过处理后得到什么 给谁用 xff0c 这样就可以把程序逻辑理清 中间的分析就是看函数如何处理的 span class hljs keyword extern
  • px4下载指定版本的固件、git用法

    https hub fastgit org PX4 PX4 Autopilot git describe tag 查看当前版本号 git tag l 查看所有版本 xff0c 也就是打个tag git checkout v1 9 1 跳转到
  • PX4 OffBoard Control

    终于还是走上了这一步 xff0c 对飞控下手 xff0c 可以说是一张白纸了 记录一下学习的过程方便以后的查阅 目录 一 ubuntu18 04配置px4编译环境及mavros环境 二 PX4的OffBoard控制 1 搭建功能包 2 编写
  • PX4——Range Finder 篇

    Range Finder 此处选用的是 Benewake 下的 Lidar 参数设置 General Configuration 除了官方的参数设置外 xff0c 我在 EKF2 中还找到了 EKF2 RNG AID 参数 xff0c 用来
  • S5PC100 I2C总线

    I2C 使用2根双向信号线来传递数据 SCL 时钟线 SDA 数据线 特点 半双功 xff0c 仅需要2根线 一般在PCU 上占2个PIN I2C 总线 上 都是 oc od 输出 xff0c 所以使用上拉电阻 当总线空闲的时候 都是输出
  • 海思文件系统缺少文件himm 、i2c_read 、i2c_write 、ssp_read 、ssp_write

    原因 xff1a 海思根文件默认没有把himm i2c read i2c write ssp read ssp write工具集成在bin里 xff0c 但是在sdk中 解决办法 xff1a cd osdrv tools board reg
  • I2C总线

    目录 1 硬件概述 1 1 常用器件 1 2 总线结构 1 3 工作原理 2 协议概述 2 1 传输格式 2 2 传输特点 2 3 读和写 2 4 时钟同步和总线仲裁 3 转换器概述 4 缓存器概述 1 硬件概述 1 1 常用器件
  • 串口通信协议 UART+I2C+SPI

    UART 异步 串行 全双工 I2C SPI 不同通信协议比较 UART UART协议详解 UART通信 xff0c 接收与发送 xff08 详细版 xff0c 附代码 xff09 UART串行通信详解 待整理 UART是Universal
  • 大神浅谈无人机飞控软件设计 系统性总结

    写在前面 深感自己对飞控软件 算法的知识点过于杂乱 很久没有进行系统的总结了 因此决定写几篇文章记录一些飞控开发过程的知识点 主要是针对一些软件 算法部分进行讨论 如内容有错误 欢迎指出 1 飞控软件的基本模块 无人机能够飞行主要是依靠传感
  • linux内核I2C子系统详解——看这一篇就够了

    1 I2C通信协议 参考博客 I2C通信协议详解和通信流程分析 2 通过KXTF9 2050芯片分析I2C协议 参考博客 通过KXTF9 2050芯片分析I2C协议 3 I2C子系统框架 1 I2C子系统分为三层 I2C核心层 I2C适配器
  • STM32基础---BH1750 硬件 IIC 驱动程序+ 测试demo

    STM32基础 BH1750 硬件 IIC 驱动程序 测试demo STM32CudeMx MDK ARM BH1750 简介 产品介绍 产品特点 测量程序步骤 指令集合结构 从 写指示 到 读出测量结果 的测量时序实例 STM32Cube
  • 【总线】I2C 通信协议

    目录 I2C 总线协议概述 参数总结 I2C 的工作原理 寻址 读 写位 数据帧 I2C数据传输的步骤 具有多个从机的单个主机 具有多个从机的多个主机 I2C的优缺点 优点 缺点 文章参考 I2C 总线协议概述 I2C 总线广泛应用在 OL
  • ESP8266如何使用u8g2(I2C)驱动SH1106的OLED

    u8g2是一款单色图形库 非常适合12864之类的显示屏 基本上支持了市面上的主流显示控制器 可是偏偏对SH1106不是那么友好 使用起来不是很方便 打开IDE的示例程序可以看到对SH1106列出来的几乎都SPI通信方式 仅仅只有一条是使用
  • Raspberry Pi上设置I2C LCD

    在本教程中 我将向您展示使用I2C设置LCD所需的一切 连接LCD I2C也被称为两线接口 因为它仅使用两根线来发送和接收数据 实际上 如果算上Vcc和地线 则是4线 但是电源始终可以来自其他来源 连接启用I2C的LCD 将LCD与I2C背
  • ESP8266 I2C从机不确认数据

    我有一个 TM4C123 处理器作为 I2C 主处理器 一个 ESP8266 作为从处理器 对于 ESP 我使用的是 Arduino IDE 并在 2 5 2 版安装了 ESP8266 支持 它应该支持 I2C 从模式 但是 我无法让它工作
  • Linux、ARM:为什么仅当启动时存在 I2C GPIO 扩展器时才创建 gpiochip

    在 imx6sx 硬件平台 NXP 嵌入式 ARM 上使用 Linux 3 14 52 问题是设备树中指定的 PCF8575 I2C GPIO 扩展器不会实例化为 sys class gpio 结构中的设备 除非它们在内核启动期间存在 这些

随机推荐

  • 将自己的ubuntu20.04做成镜像

    系统 xff1a Ubuntu20 04 软件 xff1a systemback 硬件 xff1a 128GU盘一个 1 安装systemback sudo sh c 39 echo 34 deb arch 61 amd64 http mi
  • Orin + SC16IS752+SP3072 SPI转串口485

    文章目录 1 前言 2 修改过程 2 1 sc16is752 芯片 2 1 1引脚说明 2 1 2 设备树配置 2 2 1 源码分析 3 调试 1 前言 Orin 有四路串口 对于多数设备来说已经够用 通过SPI 转串口再转RS485在Or
  • MIPI CSI介绍

    文章目录 1 概述2 MIPI接口的演变2 1 CSI 12 2 CSI 22 3 CSI 32 4 更多关于CSI 3 其他3 1 为什么用MIPI CSI 2代替USB 3 2 MIPI CSI 2的性能亮点3 3 MIPI相机 它是如
  • 车载摄像头概述

    1 车载摄像头概述 SerDes环境 上边的摄像头是德国豪车配备的车载摄像头 我们是从国外网站上买的 为了选择车载摄像头 xff0c 你需要获得关于它的各种信息 首先 xff0c 您需要获取连接器和序列化器制造商名称的信息 xff0c 然后
  • ARM7的三级流水线过程

    看到汇编中很多关于程序返回与中断返回时处理地址都很特别 xff0c 仔细想想原来是流水线作用的效果 所以 xff0c 决定总结学习下ARM流水线 ARM7处理器采用3级流水线来增加处理器指令流的速度 xff0c 能提供0 9MIPS MHz
  • nor flash和nand flash的区别

    NOR 和 NAND 是现在市场上两种主要的非易失闪存技术 Intel 于 1988 年首先开发出 NOR flash 技术 xff0c 彻底改变了原先由 EPROM 和 EEPROM 一统天下的局面 紧接着 xff0c 1989 年 xf
  • UART通信协议

    UART 是用于控制计算机与串行设备的芯片 有一点要注意的是 xff0c 它提供了RS 232C 数据终端设备接口 xff0c 这样计算机就可以和调制解调器或其它使用RS 232C接口的串行设备通信了 作为接口的一部分 xff0c UART
  • stm32f103的IAP升级时,部分APP程序功能丢失,串口中断不起作用,怎么办?

    昨天我用stm32f103做IAP升级 xff0c APP程序是一个LED闪烁和一个串口1收发数据 结果IAP升级后 xff0c 发现APP程序只有LED闪烁但是串口1不行甚至引起死机 这种半死不活的现象是最莫名其妙的 xff0c 它一半在
  • 计算机的启动过程(详细)

    零 boot的含义 先问一个问题 xff0c 启动 用英语怎么说 xff1f 回答是boot 可是 xff0c boot原来的意思是靴子 xff0c 启动 与靴子有什么关系呢 xff1f 原来 xff0c 这里的boot是bootstrap
  • 史上最全Linux面试题(2020最新版)

    出处 xff1a https blog csdn net thinkwon article details 104588679 作者 xff1a ThinkWon 导读 xff1a 本文整理了最新的Linux面试题 xff0c 近3万字 x
  • Endnote中文参考文献格式下载地址(官网)

    Endnote中文参考文献格式官网下载地址 1 下载2 安装 1 下载 今天在写中文论文时 xff0c 发现Endnote里没有中文参考文献格式 上网找了一下有些下载尽然还要币 xff0c 最后看到其实官网里就有GBT7114格式 xff1
  • C++中String类对象的初始化和基本用法

    1 初始化的几种方法和错误案例 首先当然是包含头文件了 xff1a span class token macro property span class token directive keyword include span span c
  • C++中multimap和map容器及使用

    map multimap容器里存放的都是pair模板类的对象 xff0c 而且按照first成员从小到大排序 1 pair模板 pair模板类的定义如下 xff0c 其中有两个成员变量 xff1a first和second xff1a 2
  • Word标题前出现黑块解决办法

    最近Word写文章 xff0c 再次遇到了让人头疼的问题 xff0c 就是自己定义的多级列表 xff0c 某一级前面不显示数字而是一个黑块 之前就遇到过这个问题 xff0c 不知怎么就解决了 再次遇到就懵了 尝试了网上的几种方法 xff0c
  • 从印象笔记无法连接到服务器,到win 10 Eage浏览器无法访问

    昨天电脑上的印象笔记突然无法同步 xff0c 同步的图标上显示了红色的感叹号 按照网上很多的解决方法试了之后都无法解决 印象笔记也重新安装了 xff0c 也退出企图重新登录 xff0c 结果登录界面显示无法连接到服务器 按照官网给出的解决方
  • Crazyflie笔记一:概述开发范围、工具、特点介绍

    原文地址 xff1a http blog sina com cn s blog 402c071e0102v6ho html Crazyflie笔记一 xff1a 概述开发范围 工具 特点介绍 2015 01 04 12 48 31 转载 标
  • Ubuntu16.04如何设置自动休眠时间

    可能有的人觉得 xff0c 虚拟机几分钟没动就自己锁住了 xff0c 再次进入又要输密码非常麻烦 xff0c 那如何设置屏幕关闭时间呢 xff1f 下面就说一说 xff1a 1 打开设置选项 xff0c 在电脑的右上方 2 点击system
  • PX4使用I2C方式添加自定义传感器(1)

    PX4使用I2C方式添加自定义传感器 xff08 1 xff09 前言 毕业设计就是要在PX4上添加一个传感器 xff08 角度传感器 xff09 xff0c 由于板子上的接口数量很少 xff0c 很是宝贵 最后只能选择通过I2C通信方式
  • PX4通过I2C方式添加自定义传感器(2)

    PX4 I2C通信方式传感器驱动分析 xff08 以ets airspeed为例 xff09 1 说明 这篇文章我们就来看看I2C传感器的驱动过程 xff0c 当然里面也有很多东西我不是很理解 xff0c 所以仅谈我领悟的一些东西 我就以e
  • PX4通过I2C方式添加自定义传感器(3)

    添加自定义传感器并实现数据的发送和订阅 1 前期准备 1 1 建立文件夹和相关文件配置 我是在src drivers distance sensor文件夹下操作的 xff0c 当然其他文件夹下都类似 首先建立了两个文件夹angle sour