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 offsetdiff_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这个代码的架构和一些函数,有很多不足还望多多指教。下一篇我将通过自己添加的角度传感器来谈谈,对于添加我们自己的传感器具体我们应该怎么做。

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

  1. 硬件I2C sht3x温湿度传感器 学习过程记录

    硬件I2C sht3x温湿度传感器 stm32的硬件I2C,非中断方式 代码及stm32工程分享 软件方式实现传送门 sht3x温湿度传感器的资料解析 背景 在此之前,对sht3x这款传感器的开发也是 ...

  2. 基于uFUN开发板的心率计(一)DMA方式获取传感器数据

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

  3. pixhawk/px4如何获取及使用传感器数据

    pixhawk/px4如何获取及使用传感器数据 第一步:读取传感器数据 上一篇博文已经介绍了如何给pixhawk/px4创建一个应用程序,现在我们在上一个应用程序的基础上使用传感器数据. 应用程序为了 ...

  4. vb6编写dll读取dat文件_【STM32Cube_15】使用硬件I2C读取温湿度传感器数据(SHT30)...

    寻求更好的阅读体验,请移步Mculover666的个人博客: [STM32Cube_15]使用硬件I2C读取温湿度传感器数据(SHT30)​www.mculover666.cn 本篇详细的记录了如何使 ...

  5. gazebo 直接获取传感器数据_基于uFUN开发板的心率计(一)DMA方式获取传感器数据...

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

  6. angularjs 获取复选框的值_基于uFUN开发板的心率计(一)DMA方式获取传感器数据

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

  7. Pixhawk---超声波模块添加说明(I2C方式)

    1 说明   在Pixhawk的固件中,已经实现了串口和i2c的底层驱动,并不需要自己去写驱动.通过串口的方式添加超声波的缺点是串口不够,不能添加多个超声波模块,此时需要用到i2c的方式去添加了.在P ...

  8. STM32CubeIDE开发(十六),I2C协议采集传感器数据(SHTC1、LTR-553ALS、BMP280、LSM6DSL、MMC3680KJ)

    目录 一.I2C总线协议 二.I2C协议的两种从机应对方式 三.传感器信息 四.工程创建及引脚配置 五.STCH1传感器实现 六.LTR_553ALS传感器(light sensor [ALS] an ...

  9. postek二次开发_使用PX4的ECL进行多传感器数据融合的后处理

    写在前边ecl是开源无人机项目PX4使用的算法库,使用ekf(扩展卡尔曼滤波)进行imu等多种传感器的数据融合 然而ecl不提供数据后处理功能 能使用ecl进行多传感器数据融合的后处理是很有必要的,这 ...

最新文章

  1. 李开复:AI行业正在回归商业本质,技术公司要有服务心态落地为王
  2. SQL XQuery的Action
  3. python 全部缩进一行_Python(48)语言参考2:词法分析
  4. linux定时备份mysql数据并同步到其他服务器
  5. 第5章 定时器Timer
  6. hibernate 表关系映射详解之多对多
  7. HttpApplication类及派生的Global类
  8. 单位阶跃信号是周期信号吗_vivoS7e是5G手机吗-支持5G吗-5G信号怎么样
  9. 内容 超链接_Word高效办公:自动创建带超链接的内容目录和图表目录
  10. 安捷伦频谱仪的使用方法图解_频谱分析仪的基本使用方法(转载)
  11. MySQL中的保留字
  12. Android iOS防录屏截屏
  13. gluster容量显示处理
  14. doris 动态分区
  15. Google Adsense西联汇款邮政储蓄收款流程
  16. 仰天大笑出门去,我辈岂是蓬蒿人。
  17. “ Linux基础知识学习 ” 之 关于rc.d文件的理解 04
  18. php获取腾讯视频信息,云水日记-PHP实现腾讯视频解析源码
  19. 笨方法学python 34-38
  20. 关于hash哈希以及为什么python中dict和set的key必须为不可变对象

热门文章

  1. 网络基础配置部署思路
  2. GPU显存 - 深度学习中 GPU 和显存分析
  3. 2021宜宾叙州区二中高考成绩查询,宜宾叙州第二中学2021年录取分数线
  4. 学习c语言第一步安装软件
  5. 雄安周边各县区限购限贷政策一览2019(持续更新)
  6. IT“茫一代”:35岁成生死线 无处安放未来
  7. shiro for example: not eligible for auto-proxying
  8. BAV99与TVS在静电应用的比较
  9. 【厚积薄发系列】C++项目总结16—单例模式释放时机导致的崩溃问题分析
  10. [ES笔记]持续更新中