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

2023-05-16

PX4 I2C通信方式传感器驱动分析(以ets_airspeed为例)

1、说明

这篇文章我们就来看看I2C传感器的驱动过程,当然里面也有很多东西我不是很理解,所以仅谈我领悟的一些东西。我就以etc_airspeed.c这个传感器的代码来分析一下,这个代码在src->drivers文件夹下,但是不同的固件位置不一定相同,比如有的就在src\driversdifferential_pressure\ets文件夹下,需要自己找一下。简单说下airspeed这个传感器,就是空速计,核心就是两部分:空速管(也称皮托管)和两片气压传感器,正好手头上有这个玩意,如下图:
皮托管
气压传感器
皮托管由两个同心圆管组成,内圆管为总压管,外套管为静压管(如图侧面开了小孔与大气相通)。空速管测量飞机速度的原理是这样的当飞机向前飞行时,气流便冲进空速管,在管子末端的气压传感器会感受到气流的冲击力量,即动压。飞机飞得越快,动压就越大。如果将空气静止时的压力即静压和动压相比就可以知道冲进来的空气有多快,也就是飞机飞得有多快。


2、 类的继承和定义

我们先来看一下关于ETSAirspeed这个类的定义

class ETSAirspeed : public Airspeed
{
public:
    ETSAirspeed(int bus, int address = I2C_ADDRESS, const char *path = ETS_PATH);

protected:

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

};

从第一行代码可以看到,这个类是对Airspeed类的public继承方式(这里需要对C++有一定了解),所以我们再来追寻一下Airspeed这个类的定义(建议使用Source Insight来看代码,这个软件对代码的搜索功能很是强大),好了我们追寻到了src\drivers\airspeed文件夹下,这里有一个Airspeed.h,这里就对Airspeed类进行了定义如下(我们只简单看一小段):

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

可以看到仍然是对其他类的继承,我们继续追寻,最后追寻到src\drivers\device\nuttx文件夹下有一个I2C.hpp文件

class __EXPORT I2C : public CDev
{

public:

    static int  set_bus_clock(unsigned bus, unsigned clock_hz);

    static unsigned int _bus_clocks[BOARD_NUMBER_I2C_BUSES];

protected:

到这里我们终于不用继续追寻了,我们看到我们想要的I2C这个类了,这个类可以好好看看,因为这里面对使用I2C所用到的各种函数都进行了申明,比如设置总线频率的函数:

static int  set_bus_clock(unsigned bus, unsigned clock_hz);

再比如transfer这个函数,这里也有比较详细的介绍:

    /**
     * Perform an I2C transaction to the device.
     *
     * At least one of send_len and recv_len must be non-zero.
     *
     * @param send      Pointer to bytes to send.
     * @param send_len  Number of bytes to send.
     * @param recv      Pointer to buffer for bytes received.
     * @param recv_len  Number of bytes to receive.
     * @return      OK if the transfer was successful, -errno
     *          otherwise.
     */
    int     transfer(const uint8_t *send, unsigned send_len, uint8_t *recv, unsigned recv_len);

所以总结一下就是,任何的以I2C通信方式的传感器,在写驱动的时候归根结底都是对基类I2C的继承。 有了基类的继承我们在用这些函数的时候就可以直接使用了,而不必重新申明和定义。

3、几个重要函数的介绍

3.1 transfer()函数介绍

先来看代码,这个函数的代码在I2C.cpp中:

int
I2C::transfer(const uint8_t *send, unsigned send_len, uint8_t *recv, unsigned recv_len)
{
    px4_i2c_msg_t msgv[2];
    unsigned msgs;
    int ret = PX4_ERROR;
    unsigned retry_count = 0;

    if (_dev == nullptr) {
        PX4_ERR("I2C device not opened");
        return 1;
    }

    do {
        DEVICE_DEBUG("transfer out %p/%u  in %p/%u", send, send_len, recv, recv_len);
        msgs = 0;

        if (send_len > 0) {
            msgv[msgs].frequency = _bus_clocks[get_device_bus() - 1];
            msgv[msgs].addr = get_device_address();
            msgv[msgs].flags = 0;
            msgv[msgs].buffer = const_cast<uint8_t *>(send);
            msgv[msgs].length = send_len;
            msgs++;
        }

        if (recv_len > 0) {
            msgv[msgs].frequency = _bus_clocks[get_device_bus() - 1];
            msgv[msgs].addr = get_device_address();
            msgv[msgs].flags = I2C_M_READ;
            msgv[msgs].buffer = recv;
            msgv[msgs].length = recv_len;
            msgs++;
        }

        if (msgs == 0) {
            return -EINVAL;
        }

        ret = I2C_TRANSFER(_dev, &msgv[0], msgs);

        /* success */
        if (ret == PX4_OK) {
            break;
        }

        /* if we have already retried once, or we are going to give up, then reset the bus */
        if ((retry_count >= 1) || (retry_count >= _retries)) {
            I2C_RESET(_dev);
        }

    } while (retry_count++ < _retries);

    return ret;
}

先对这里的几个参数简单说一下,

  • *send即为指针,指向需要发送到设备的数据
  • send_len即为需要发送的数据的长度
  • *recv即为指针,指向接收数据的变量
  • recv_len即为要接收数据的长度
    上一篇文章我们着重强调了这个transfer函数,为什么强调呢?假如我们如果要在单片机上进行实验,需要给这个设备发送一个控制指令(对于我的角度传感器,我需要发送一个通道选择指令为0x40|3),那通常我们会怎么做呢?(1)发送写命令0x90 (2)发送控制命令0x40|3。但是在飞控上使用transfer()函数我们就不能这么做了,只需要
    uint8_t cmd = SET_CMD;
    ret = transfer(&cmd, 1, nullptr, 0);
    if (OK != ret) {
        perf_count(_comms_errors);
    }

其中SET_CMD=0x40|3,可见不需要给器件先发送写指令0x90,这是为什么呢?我们回到刚刚那段transfer()函数代码,其中有一句:

msgv[msgs].addr = get_device_address();

由此可见,每一次使用transfer()函数都会自动地先发送写和读指令,而地址的定义是在最前面:

#define I2C_ADDRESS 0x75    /* 7-bit address. 8-bit address is 0xEA */

看到没有这里的地址是7位!!! 这也是我上一篇文章中强调的。

3.2 measurea() 函数介绍

这个函数比较简单,我们还是先来看代码:

int
ETSAirspeed::measure()
{
    int ret;

    /*
     * Send the command to begin a measurement.
     */
    uint8_t cmd = READ_CMD;
    ret = transfer(&cmd, 1, nullptr, 0);

    if (OK != ret) {
        perf_count(_comms_errors);
    }

    return ret;
}

说过了transfer()函数,这段代码就比较容易了,就是如果你的传感器在读取数据之前需要发一些控制的指令,就放在这一段就好了。

3.3 collect()函数介绍

先看代码:

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

    /* read from the sensor */
    uint8_t val[2] = {0, 0};

    perf_begin(_sample_perf);

    ret = transfer(nullptr, 0, &val[0], 2);

    if (ret < 0) {
        perf_count(_comms_errors);
        return ret;
    }

    float diff_pres_pa_raw = (float)(val[1] << 8 | val[0]);

    differential_pressure_s report;
    report.timestamp = hrt_absolute_time();

    if (diff_pres_pa_raw < FLT_EPSILON) {
        // a zero value indicates no measurement
        // since the noise floor has been arbitrarily killed
        // it defeats our stuck sensor detection - the best we
        // can do is to output some numerical noise to show
        // that we are still correctly sampling.
        diff_pres_pa_raw = 0.001f * (report.timestamp & 0x01);
    }

    // The raw value still should be compensated for the known offset
    diff_pres_pa_raw -= _diff_pres_offset;

    report.error_count = perf_event_count(_comms_errors);

    // XXX we may want to smooth out the readings to remove noise.
    report.differential_pressure_filtered_pa = diff_pres_pa_raw;
    report.differential_pressure_raw_pa = diff_pres_pa_raw;
    report.temperature = -1000.0f;
    report.device_id = _device_id.devid;

    if (_airspeed_pub != nullptr && !(_pub_blocked)) {
        /* publish it */
        orb_publish(ORB_ID(differential_pressure), _airspeed_pub, &report);
    }

    ret = OK;

    perf_end(_sample_perf);

    return ret;
}

这段代码很长,但是其实很简单,我们需要了解的就一部分。首先定义了一个数组val[2],用来存放接收的数据,可见它的数据长度是16位,接收完了就是对数据的处理,这段我们就不需要了解了。

4、用于nsh调试的函数介绍

4.1 nsh调试介绍

我们在编写完代码之后除了编译,还需要调试看能否达到我们期望的结果。比如我们要添加以I2C通信机制的传感器,我们需要通过调试来看,能否得到传感器的数据。NuttShell和Unix终端命令类似。NSH通过串口或者USB转串口来与PX4FMU交互,因此可以使用类似超级终端的串口软件来与FMU交互,在Pixhawk开发中有多种nsh调试的软件,我使用的是QGroundControl里面携带的nsh调试工具。

4.2 nsh调试函数建立

我们还是先看代码:

/**
 * Local functions in support of the shell command.
 */
namespace ets_airspeed
{

ETSAirspeed *g_dev;

int start(int i2c_bus);
int stop();
int test();
int reset();
int info();

使用namespace定义了ets_airspeed这个类似于类的东西,这里面所定义的函数都可以在nsh调试中使用,比如在这段代码中定义了start(),stop(),test()等函数,这些函数通过主函数

ets_airspeed_main(int argc, char *argv[])

进行调用,这些函数具体什么功能以及如何实现,后面如果有时间我再进行介绍。下一篇应该也会涉及到这些内容。

好了,这篇文章就到这里了。简单分析了ets_airspeed.c这个代码的架构和一些函数,有很多不足还望多多指教。下一篇我将通过自己添加的角度传感器来谈谈,对于添加我们自己的传感器具体我们应该怎么做。

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

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

  • PX4模块设计之三十九:Commander模块

    PX4模块设计之三十九 xff1a Commander模块 1 Commander模块简介2 模块入口函数2 1 主入口commander main2 2 自定义子命令custom command 3 Commander模块重要函数3 1
  • mavros连接px4失败的usb-ttl原因

    问题描述 xff1a 最近在搞mavros xff0c 以方便协处理器和pixhawk通讯 xff0c 在按照官网教程安装mavros xff0c 设置px4 xff0c 连接硬件之后发现mavros卡在中间下不去 xff1a MAVROS
  • AM2322温湿度传感器(地址0XB8)---I2C总结(I2C_ModBus协议)

  • I2C总线和SPI总线

    I2C串行总线一般有两根信号线 xff0c 一根是双向的数据线SDA xff0c 另一根是时钟线SCL I2C协议 2条双向串行线 xff0c 一条数据线SDA xff0c 一条时钟线SCL SDA传输数据是 大端传输 xff08 字节高位
  • 关于github px4 gps 驱动的开发的总结

    源码编译上边已经写过文章了 遇到的几个问题 1 解决虚拟机不能共享文件夹的问题 一开始虚拟机的更新 vmware tools 是灰色的 xff0c 不能点 xff0c 然后通过关掉虚拟机 xff0c 然后再开启的时候 xff0c 在没有启动
  • px4_simple_example和uorb机制

    px4 simple app PX4 Autopilot src exampes px4 simple app xff0c 这个程序是用c语言调用orb API和poll机制订阅和发布通讯数据 xff0c 但是这个例子并不是既有接收又有发送
  • PX4-4-任务调度

    PX4所有的功能都封装在独立的模块中 xff0c uORB是任务间数据交互和同步的工具 xff0c 而管理和调度每个任务 xff0c PX4也提供了一套很好的机制 xff0c 这一篇我们分享PX4的任务调度机制 我们以PX4 1 11 3版
  • 【px4】运行mavsdk中的offboard example

    运行MAVSDK中的offboard例子时无人机不执行 想控制无人机前后左右移动 xff0c 在按照官方教程实现offboard 插件的时候 发现用action插件能正常起飞和降落 但是一旦执行到offboard的插件代码的时候就会自动降落
  • STM32 I2C_OLED显示汉字及屏幕滚动

    STM32 I2C OLED显示汉字及屏幕滚动 文章目录 STM32 I2C OLED显示汉字及屏幕滚动一 I2C以及AHT20温湿度传感器介绍二 用0 96寸OLED屏幕显示数据1 OLED介绍2 样例测试 三 汉字编码原理编码排序A0A
  • S5PC100 I2C总线

    I2C 使用2根双向信号线来传递数据 SCL 时钟线 SDA 数据线 特点 半双功 xff0c 仅需要2根线 一般在PCU 上占2个PIN I2C 总线 上 都是 oc od 输出 xff0c 所以使用上拉电阻 当总线空闲的时候 都是输出
  • PX4项目学习::(五)模块代码启动流程

    54条消息 PX4 模块代码启动流程 zhao23333的博客 CSDN博客
  • STM32设置为I2C从机模式(HAL库版本)

    STM32设置为I2C从机模式 HAL库版本 目录 STM32设置为I2C从机模式 HAL库版本 前言 1 硬件连接 2 软件编程 2 1 步骤分解 2 2 测试用例 3 运行测试 3 1 I2C连续写入 3 2 I2C连续读取 3 3 I
  • I2C接口

    I2C的结构和特点 他是一具有两条总线线路 即一条串行数据线SDA和一条串行时钟线SCL 每个连接到总线上的器件都可以通过唯一的地址联系主机 它是一个真正的多主机总线 数据传输通过冲突检测和仲裁防止数据被破坏 串行的8位双向数据传输位速率更
  • 如何在 QEMU x86 上模拟 i2c 设备?

    我正在研究 QEMU 1 5 1 6 但还没有看到任何在 i2c 总线上添加设备的文档 有人可以帮忙吗 Thanks 好吧 没人对这个问题感兴趣 我发布我自己的解决方案 由于 QEMU 不支持 I2C 总线级数据传输 因此在将多点触摸数据从
  • 找不到i2c注册宏?

    我正在 raspi 上开发 I2C 驱动程序 register I2C device static static const struct i2c board info rasp i2c devices mbed mbedID in the
  • 为什么我只收到第一个地址字节? (I2C协议)

    期望从机确认并返回数据 但它没有 这是我的协议 这是我的数据表 数据表提到 从机将通过首先发送 MSB 字节来应答 Byte0 和 byte1 包含 预测值 所有字节均由主机确认 Edit 源库 另外仅供参考 我使用的是 Arduino F
  • HAL 锁定和解锁函数如何使用以及为什么?

    我试图理解另一位程序员编写的代码 它使用了I C http en wikipedia org wiki I C2 B2C通信以将数据写入 STM32 微控制器的 EEPROM 一般来说 我理解他的代码是如何工作的 但我不明白他为什么使用HA
  • STM32F207 I2C 测试失败

    我正在使用 STM32F207 微控制器在 STM3220G EVAL 板上学习嵌入式开发 我尝试通过连接同一芯片上的两个 I2C2 和 I2C3 模块并发送 接收字符来测试 I2C 接口 这是我当前编写的代码 使用 mdk arm 5 i
  • 使用 C++ 和 i2c 工具从虚拟 i2c 写入和读取

    我正在尝试使用 C 写入和读取 I2C 总线 我的I2C总线是虚拟的 第一件事是加载内核模块i2c stub 我可以通过 bash 完成所有事情 现在我将其移植到 C 我可以打开i2c总线 获取特定地址的i2c总线 但无法读写 我正在虚拟化
  • Linux、ARM:为什么仅当启动时存在 I2C GPIO 扩展器时才创建 gpiochip

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

随机推荐

  • Jetson Xavier RC EP 的PCIe互通测试(12)

    一 硬件连接 xff1a 交叉线 X16引脚定义 xff1a 从此面看 xff1a 从上到下 xff0c 依次为A1 A2 A3 从背面看 xff1a 从上到下 xff0c 依次为B1 B2 B3 注意断开 xff1a A2 A3 B1 B
  • 将自己的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