add si7210 driver files

This commit is contained in:
Amir Melzer
2021-04-14 16:12:50 +02:00
parent f4551faa6b
commit a5c61eeb4f
2 changed files with 1244 additions and 0 deletions
+960
View File
@@ -0,0 +1,960 @@
/****************************************************************************
*
* Copyright (c) 2012-2021 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file si7210.cpp
* @author: Amir Melzer <amir.melzer@mavt.ethz.ch>
*
* Driver for the SI7210 connected via I2C.
*/
#include "si7210.h"
#include <px4_getopt.h>
/** driver 'main' command */
extern "C" { __EXPORT int si7210_main(int argc, char *argv[]); }
namespace si7210
{
SI7210 *g_dev_0;
SI7210 *g_dev_1;
SI7210 *g_dev_2;
SI7210 *g_dev_3;
typedef struct {
float mag_T;
float temp_C;
} si7210_measurements_t;
void start(bool, SI7210::Instance);
void test(bool, SI7210::Instance);
void reset(bool, SI7210::Instance);
void info(bool, SI7210::Instance);
void usage();
int get_i2c_address(SI7210::Instance);
const char *get_path(SI7210::Instance);
SI7210 **get_g_dev_ptr(SI7210::Instance);
/**
* Start the driver.
*
* This function only returns if the driver is up and running
* or failed to detect the sensor.
*/
void start(bool external_bus, SI7210::Instance instance)
{
if (!external_bus) {
PX4_ERR("Internal bus currently not supported");
exit(1);
}
int fd;
SI7210 **g_dev_ptr = get_g_dev_ptr(instance);
const char *path = get_path(instance);
if (*g_dev_ptr != nullptr) {
/* if already started, the command still succeeded */
PX4_ERR("driver instance %d at address 0x%x already started", instance, get_i2c_address(instance));
exit(0);
}
/* create the driver */
if (external_bus) {
#if defined(PX4_I2C_BUS_EXPANSION)
*g_dev_ptr = new SI7210(PX4_I2C_BUS_EXPANSION, instance, path);
#else
PX4_ERR("External I2C not available");
exit(0);
#endif
} else {
PX4_ERR("Internal I2C not available");
exit(0);
}
if (*g_dev_ptr == nullptr) {
goto fail;
}
if (OK != (*g_dev_ptr)->init()) {
goto fail;
}
/* set the poll rate to default, starts automatic data collection */
fd = open(path, O_RDONLY);
if (fd < 0) {
goto fail;
}
if (ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT) < 0) {
goto fail;
}
close(fd);
exit(0);
fail:
if (*g_dev_ptr != nullptr) {
delete (*g_dev_ptr);
*g_dev_ptr = nullptr;
}
PX4_ERR("driver instance %d start at address 0x%x failed", instance, get_i2c_address(instance));
exit(1);
}
void test(bool external_bus, SI7210::Instance instance)
{
int fd = -1;
const char *path = get_path(instance);
struct si7210_report p_report;
ssize_t sz;
/* get the driver */
fd = open(path, O_RDONLY);
if (fd < 0) {
PX4_ERR("%s open failed (try 'si7210 start -a %d' if the driver is not running)",
path, instance);
exit(1);
}
/* reset to Max polling rate*/
if (ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_MAX) < 0) {
PX4_ERR("reset to Max polling rate");
exit(1);
}
/* do a simple demand read */
sz = read(fd, &p_report, sizeof(p_report));
if (sz != sizeof(p_report)) {
PX4_ERR("immediate si7210 read failed");
exit(1);
}
PX4_INFO("single read");
PX4_INFO("time: %lld", p_report.timestamp);
PX4_INFO("magnetic field: %10.4f", (double)p_report.mag_T);
PX4_INFO("temperature: %10.4f", (double)p_report.temp_C);
PX4_INFO("PASS");
exit(0);
}
void
reset(bool external_bus, SI7210::Instance instance)
{
const char *path = get_path(instance);
int fd = open(path, O_RDONLY);
if (fd < 0) {
PX4_ERR("%s open failed (try 'si7210 start -a %d' if the driver is not running)",
path, instance);
exit(1);
}
if (ioctl(fd, SENSORIOCRESET, 0) < 0) {
PX4_ERR("driver reset failed");
exit(1);
}
if (ioctl(fd, SENSORIOCSPOLLRATE, SENSOR_POLLRATE_DEFAULT) < 0) {
PX4_ERR("driver poll restart failed");
exit(1);
}
exit(0);
}
void
info(bool external_bus, SI7210::Instance instance)
{
SI7210 **g_dev_ptr = get_g_dev_ptr(instance);
if (*g_dev_ptr == nullptr) {
PX4_ERR("driver instance %d at address 0x%x not running", instance, get_i2c_address(instance));
exit(1);
}
printf("state @ %p\n", *g_dev_ptr);
(*g_dev_ptr)->print_info();
exit(0);
}
void
usage()
{
PX4_INFO("required:");
PX4_INFO(" 'start', 'info', 'test', 'stop', 'reset' ");
PX4_INFO(" -i (sensor instance (0-3))");
PX4_INFO("optional:");
PX4_INFO(" -X (external bus)");
PX4_INFO("example:");
PX4_INFO(" si7210 start -i 0");
}
/* Get i2c address */
int
get_i2c_address(SI7210::Instance instance)
{
int i2c_address(0);
switch (instance) {
case SI7210::Instance::ID_0:
i2c_address = SI7210_SLAVE_ADDRESS_0;
break;
case SI7210::Instance::ID_1:
i2c_address = SI7210_SLAVE_ADDRESS_1;
break;
case SI7210::Instance::ID_2:
i2c_address = SI7210_SLAVE_ADDRESS_2;
break;
case SI7210::Instance::ID_3:
i2c_address = SI7210_SLAVE_ADDRESS_3;
break;
default:
PX4_ERR("Invalid instance, defaulting to i2c address 0x%x", SI7210_SLAVE_ADDRESS_0);
}
return i2c_address;
}
const char *
get_path(SI7210::Instance instance)
{
switch (instance) {
case SI7210::Instance::ID_0:
return HALL0_DEVICE_PATH;
case SI7210::Instance::ID_1:
return HALL1_DEVICE_PATH;
case SI7210::Instance::ID_2:
return HALL2_DEVICE_PATH;
case SI7210::Instance::ID_3:
return HALL3_DEVICE_PATH;
default:
PX4_ERR("Invalid instance, defaulting to instance 0");
return HALL0_DEVICE_PATH;
}
}
SI7210 **
get_g_dev_ptr(SI7210::Instance instance)
{
switch (instance) {
case SI7210::Instance::ID_0:
return &g_dev_0;
case SI7210::Instance::ID_1:
return &g_dev_1;
case SI7210::Instance::ID_2:
return &g_dev_2;
case SI7210::Instance::ID_3:
return &g_dev_3;
default:
PX4_ERR("Invalid instance, defaulting to instance 0");
return &g_dev_0;
}
}
} // namespace si7210
SI7210 :: SI7210(int bus, SI7210::Instance instance, const char *path) :
I2C("SI7210", path, bus, si7210::get_i2c_address(instance), SI7210_BUS_SPEED),
_running(false),
_call_interval(0),
_reports(nullptr),
_collect_phase(false),
_hall_topic(nullptr),
_orb_class_instance(-1),
_sample_perf(perf_alloc(PC_ELAPSED, "si7210_read")),
_bad_transfers(perf_alloc(PC_COUNT, "si7210_bad_transfers")),
_good_transfers(perf_alloc(PC_COUNT, "si7210_good_transfers")),
_measure_perf(perf_alloc(PC_ELAPSED, "si7210_measure")),
_comms_errors(perf_alloc(PC_COUNT, "si7210_comms_errors")),
_duplicates(perf_alloc(PC_COUNT, "si7210_duplicates")),
_got_duplicate(false),
_instance(instance),
_params_sub(-1)
{
_device_id.devid_s.devtype = DRV_HALL_DEVTYPE_SI7210;
// work_cancel in the dtor will explode if we don't do this...
memset(&_work, 0, sizeof(_work));
// default scaling
initialize_parameter_handles(_parameter_handles);
}
SI7210 :: ~SI7210()
{
/* make sure we are truly inactive */
stop();
/* free any existing reports */
if (_reports != nullptr) {
delete _reports;
}
if (_hall_topic != nullptr) {
orb_unadvertise(_hall_topic);
}
/* delete the perf counter */
perf_free(_sample_perf);
perf_free(_bad_transfers);
perf_free(_good_transfers);
perf_free(_measure_perf);
perf_free(_comms_errors);
perf_free(_duplicates);
}
int SI7210::init()
{
int ret = OK;
/* do I2C init (and probe) first */
ret = I2C::init();
/* if probe/setup failed, bail now */
if (ret != OK) {
DEVICE_DEBUG("I2C setup failed");
return ret;
}
/* allocate basic report buffers */
_reports = new ringbuffer::RingBuffer(2, sizeof(si7210_report));
if (_reports == nullptr) {
return ret;
}
up_udelay(10000);
if (collect()) {
return -EIO;
}
/* advertise sensor topic, measure manually to initialize valid report */
struct si7210_report prb;
_reports->get(&prb);
/* measurement will have generated a report, publish */
_hall_topic = orb_advertise_multi(ORB_ID(sensor_hall), &prb, &_orb_class_instance, ORB_PRIO_DEFAULT);
if (_hall_topic == nullptr) {
PX4_WARN("ADVERT FAIL");
}
return ret;
}
int
SI7210::probe()
{
uint8_t reg;
if (OK != get_regs(HREVID, &reg)) {
perf_count(_comms_errors);
return -EIO;
}
if (((reg & 0xf0) >> 4) != IDCHIPID) {
perf_count(_comms_errors);
return -EIO;
}
if ((reg & 0x0f) != REVID) {
perf_count(_comms_errors);
return -EIO;
}
return OK;
}
void
SI7210::start()
{
/* reset the report ring and state machine */
_collect_phase = false;
_running = true;
_reports->flush();
/* schedule a cycle to start things */
work_queue(HPWORK, &_work, (worker_t)&SI7210::cycle_trampoline, this, 1);
}
void
SI7210::stop()
{
_running = false;
work_cancel(HPWORK, &_work);
}
ssize_t
SI7210::read(struct file *filp, char *buffer, size_t buflen)
{
unsigned count = buflen / sizeof(si7210_report);
struct si7210_report *si7210_buf = reinterpret_cast<struct si7210_report *>(buffer);
int ret = 0;
/* buffer must be large enough */
if (count < 1) {
return -ENOSPC;
}
/* if automatic measurement is enabled */
if (_call_interval > 0) {
/*
* While there is space in the caller's buffer, and reports, copy them.
* Note that we may be pre-empted by the workq thread while we are doing this;
* we are careful to avoid racing with them.
*/
while (count--) {
if (_reports->get(si7210_buf)) {
ret += sizeof(struct si7210_report);
si7210_buf++;
}
}
/* if there was no data, warn the caller */
return ret ? ret : -EAGAIN;
}
/* manual measurement - run one conversion */
do {
_reports->flush();
/* wait for it to complete */
usleep(SI7210_CONVERSION_INTERVAL);
/* run the collection phase */
if (OK != collect()) {
ret = -EIO;
break;
}
if (_reports->get(si7210_buf)) {
ret = sizeof(struct si7210_report);
}
} while (0);
/* return the number of bytes transferred */
return ret;
}
void
SI7210::cycle_trampoline(void *arg)
{
SI7210 *dev = reinterpret_cast<SI7210 *>(arg);
/* make measurement */
dev->cycle();
}
void
SI7210::cycle()
{
bool force_update = false;
bool param_updated = false;
if (!_running) {
if (_params_sub >= 0) {
orb_unsubscribe(_params_sub);
}
} else {
if (_params_sub < 0) {
_params_sub = orb_subscribe(ORB_ID(parameter_update));
force_update = true;
}
}
/* Check if any parameter changed */
orb_check(_params_sub, &param_updated);
if (param_updated || force_update) {
struct parameter_update_s update;
orb_copy(ORB_ID(parameter_update), _params_sub, &update);
parameters_update();
}
if (_collect_phase) {
collect();
unsigned wait_gap = _call_interval - USEC2TICK(SI7210_CONVERSION_INTERVAL);
if ((wait_gap != 0) && (_running)) {
work_queue(HPWORK, &_work, (worker_t)&SI7210::cycle_trampoline, this,
wait_gap); //need to wait some time before new measurement
return;
}
}
measure();
if ((_running)) {
/* schedule a fresh cycle call when the measurement is done */
work_queue(HPWORK,
&_work,
(worker_t)&SI7210::cycle_trampoline,
this,
USEC2TICK(SI7210_CONVERSION_INTERVAL));
}
}
int
SI7210::parameters_update()
{
int ret = update_parameters(_parameter_handles, _parameters);
return ret;
}
int
SI7210::measure()
{
_collect_phase = true;
return OK;
}
int
SI7210::collect()
{
_collect_phase = false;
bool si7210_notify = true;
si7210_report prb;
uint8_t reg;
uint16_t magRawData;
uint16_t tempRawData;
int8_t otpTempOffsetData;
int8_t otpTempGainData;
/* read the most recent measurement */
perf_begin(_sample_perf);
/* capture the magnetic field measurements */
reg = ARAUTOINC_ARAUTOINC_MASK;
if (OK != set_regs(ARAUTOINC, reg)) {
return -EIO;
}
reg = DSPSIGSEL_MAG_VAL_SEL;
if (OK != set_regs(DSPSIGSEL, reg)) {
return -EIO;
}
reg = POWER_CTRL_ONEBURST_MASK;
if (OK != set_regs(POWER_CTRL, reg)) {
return -EIO;
}
if (OK != get_measurement(DSPSIGM, &magRawData)) {
return -EIO;
}
/* capture the temperate measurements */
reg = DSPSIGSEL_TEMP_VAL_SEL;
if (OK != set_regs(DSPSIGSEL, reg)) {
return -EIO;
}
reg = POWER_CTRL_ONEBURST_MASK;
if (OK != set_regs(POWER_CTRL, reg)) {
return -EIO;
}
if (OK != get_measurement(DSPSIGM, &tempRawData)) {
return -EIO;
}
if (OK != get_sensor_data(OTP_TEMP_OFFSET, &otpTempOffsetData)) {
return -EIO;
}
if (OK != get_sensor_data(OTP_TEMP_GAIN, &otpTempGainData)) {
return -EIO;
}
float _tempOffset = (float)otpTempOffsetData / 16;
float _tempGain = 1 + (float)otpTempGainData / 2048;
if (OK == ((magRawData & 0x8000) && (tempRawData & 0x8000))) {
return -EIO;
}
/* generate a new report */
prb.timestamp = hrt_absolute_time();
prb.instance = _instance;
prb.mag_T = (float)(magRawData - MAG_BIAS) * MAG_CONV;
prb.temp_C = (float)((tempRawData & ~0x8000) >> 3);
prb.temp_C = _tempGain * (TEMP_CONV_A2 * prb.temp_C * prb.temp_C + TEMP_CONV_A1 * prb.temp_C + TEMP_CONV_A0 + VDD_CONV *
VDD) + _tempOffset;
_reports->force(&prb);
//PX4_INFO("<mag field, temp[degC]>: %.2f, %.2f", (double)prb.mag_T, (double)prb.temp_C);
/* notify anyone waiting for data */
if (si7210_notify) {
poll_notify(POLLIN);
}
if (si7210_notify && !(_pub_blocked)) {
/* publish it */
orb_publish(ORB_ID(sensor_hall), _hall_topic, &prb);
}
perf_end(_sample_perf);
return OK;
}
int
SI7210::ioctl(struct file *filp, int cmd, unsigned long arg)
{
switch (cmd) {
case SENSORIOCSPOLLRATE: {
switch (arg) {
/* switching to manual polling */
case SENSOR_POLLRATE_MANUAL:
stop();
_call_interval = 0;
return OK;
/* external signalling (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:
return ioctl(filp, SENSORIOCSPOLLRATE, SI7210_MAX_DATA_RATE);
/* adjust to a legal polling interval in Hz */
default: {
/* do we need to start internal polling? */
bool want_start = (_call_interval == 0);
/* convert hz to tick interval via microseconds */
unsigned ticks = USEC2TICK(1000000 / arg);
/* check against maximum rate */
if (ticks < USEC2TICK(SI7210_CONVERSION_INTERVAL)) {
return -EINVAL;
}
/* update interval for next measurement */
_call_interval = ticks;
/* if we need to start the poll state machine, do it */
if (want_start) {
start();
}
return OK;
}
}
}
case SENSORIOCGPOLLRATE:
if (_call_interval == 0) {
return SENSOR_POLLRATE_MANUAL;
}
return 1000000 / _call_interval;
case SENSORIOCSQUEUEDEPTH: {
/* lower bound is mandatory, upper bound is a sanity check */
if ((arg < 1) || (arg > 100)) {
return -EINVAL;
}
irqstate_t flags = px4_enter_critical_section();
if (!_reports->resize(arg)) {
px4_leave_critical_section(flags);
return -ENOMEM;
}
px4_leave_critical_section(flags);
return OK;
}
case SENSORIOCRESET:
return reset();
default:
/* give it to the superclass */
return I2C::ioctl(filp, cmd, arg);
}
}
int SI7210::reset()
{
//ToDo: write a reset function
stop();
return OK;
}
int
SI7210::self_test()
{
if (perf_event_count(_sample_perf) == 0) {
collect();
}
/* return 0 on success, 1 else */
return (perf_event_count(_sample_perf) > 0) ? 0 : 1;
}
/* Get registers value */
int
SI7210::get_regs(uint8_t ptr, uint8_t *regs)
{
uint8_t data;
if (OK != transfer(&ptr, 1, &data, 1)) {
perf_count(_comms_errors);
return -EIO;
}
*regs = data;
return OK;
}
/* Set registers value */
int
SI7210::set_regs(uint8_t ptr, uint8_t value)
{
uint8_t data[2];
data[0] = ptr;
data[1] = value;
if (OK != transfer(&data[0], 2, nullptr, 0)) {
perf_count(_comms_errors);
return -EIO;
}
/* read back the reg and verify */
if (OK != transfer(&ptr, 1, &data[1], 1)) {
perf_count(_comms_errors);
return -EIO;
}
if (data[1] != value) {
//return -EIO;
}
return OK;
}
/* Get measurement value */
int
SI7210::get_measurement(uint8_t ptr, uint16_t *value)
{
uint8_t data[2];
if (OK != transfer(&ptr, 1, &data[0], 2)) {
perf_count(_comms_errors);
return -EIO;
}
*value = (uint16_t)((data[1] & 0xff) + ((data[0] & 0xff) << 8));
return OK;
}
/* Get sensor data */
int
SI7210::get_sensor_data(uint8_t otpAddr, int8_t *data)
{
uint8_t optCtrl;
uint8_t reg;
if (OK != get_regs(OTP_CTRL, &optCtrl)) {
perf_count(_comms_errors);
return -EIO;
}
if (OK != (optCtrl & OTP_CTRL_OPT_BUSY_MASK)) {
perf_count(_comms_errors);
return -EIO;
}
reg = otpAddr;
if (OK != set_regs(OTP_ADDR, reg)) {
perf_count(_comms_errors);
return -EIO;
}
reg = OTP_CTRL_OPT_READ_EN_MASK;
if (OK != set_regs(OTP_CTRL, reg)) {
perf_count(_comms_errors);
return -EIO;
}
if (OK != get_regs(OTP_DATA, (uint8_t *) data)) {
perf_count(_comms_errors);
return -EIO;
}
return OK;
}
void
SI7210::print_info()
{
perf_print_counter(_sample_perf);
perf_print_counter(_bad_transfers);
perf_print_counter(_good_transfers);
_reports->print_info("si7210 queue");
}
int
si7210_main(int argc, char *argv[])
{
const char *verb = argv[1];
bool external_bus = true;
SI7210::Instance instance = SI7210::Instance::INVALID;
int ch;
int myoptind = 1;
const char *myoptarg = nullptr;
/* jump over start/off/etc and look at options first */
while ((ch = px4_getopt(argc, argv, "X:i:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'X':
external_bus = true;
break;
case 'i':
instance = SI7210::Instance(atoi(myoptarg));
// range check
if ((instance > 3) || (instance < 0)) {
PX4_ERR("invalid instance: %d (only 0 through 3 valid)", instance);
return PX4_ERROR;
}
break;
default:
PX4_ERR("unknown command line argument");
si7210::usage();
return PX4_ERROR;
}
}
if (instance == SI7210::Instance::INVALID) {
PX4_ERR("the -i option is required");
si7210::usage();
exit(1);
}
/*
* Start/load the driver.
*/
if (!strcmp(verb, "start")) {
si7210::start(external_bus, instance);
}
/*
* Test the driver/device.
*/
if (!strcmp(verb, "test")) {
si7210::test(external_bus, instance);
}
/*
* Reset the driver.
*/
if (!strcmp(verb, "reset")) {
si7210::reset(external_bus, instance);
}
/*
* Print driver information.
*/
if (!strcmp(verb, "info")) {
si7210::info(external_bus, instance);
}
PX4_ERR("missing command");
si7210::usage();
exit(1);
}
+284
View File
@@ -0,0 +1,284 @@
/****************************************************************************
*
* Copyright (c) 2012-2021 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file si7210.h
* @author: Amir Melzer <amir.melzer@mavt.ethz.ch>
*
* Driver for the SI7210 connected via I2C.
*/
#ifndef SI7210_HPP_
#define SI7210_HPP_
#include <px4_config.h>
#include <parameters/param.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <semaphore.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <getopt.h>
#include <px4_log.h>
#include <perf/perf_counter.h>
#include <systemlib/err.h>
#include <nuttx/wqueue.h>
#include <systemlib/conversions.h>
#include <nuttx/arch.h>
#include <nuttx/clock.h>
#include <uORB/uORB.h>
#include <uORB/topics/parameter_update.h>
#include <board_config.h>
#include <drivers/drv_hrt.h>
#include <drivers/device/ringbuffer.h>
#include <drivers/device/integrator.h>
#include <drivers/device/i2c.h>
#include <drivers/drv_hall.h>
#include <mathlib/math/filter/LowPassFilter2p.hpp>
#include "parameters.h"
using namespace si7210;
#define SI7210_BUS PX4_I2C_BUS_EXPANSION
#define SI7210_SLAVE_ADDRESS_0 0x30 /* SI7210 I2C address */
#define SI7210_SLAVE_ADDRESS_1 0x31 /* SI7210 I2C address */
#define SI7210_SLAVE_ADDRESS_2 0x32 /* SI7210 I2C address */
#define SI7210_SLAVE_ADDRESS_3 0x33 /* SI7210 I2C address */
#define SI7210_MAX_DATA_RATE 50
#define SI7210_BUS_SPEED 1000*100
/* This value is set based on Max output data rate value */
#define SI7210_CONVERSION_INTERVAL (1000000 / 100) /* microseconds */
#define IDCHIPID 0x01
#define REVID 0x04
#define ARAUTOINC_ARAUTOINC_MASK 0x01
#define OTP_CTRL_OPT_BUSY_MASK 0x01
#define OTP_CTRL_OPT_READ_EN_MASK 0x02
#define POWER_CTRL_SLEEP_MASK 0x01
#define POWER_CTRL_STOP_MASK 0x02
#define POWER_CTRL_ONEBURST_MASK 0x04
#define POWER_CTRL_USESTORE_MASK 0x08
#define POWER_CTRL_MEAS_MASK 0x80
#define DSPSIGSEL_MAG_VAL_SEL 0
#define DSPSIGSEL_TEMP_VAL_SEL 1
/** I2C registers for Si72xx */
#define OTP_TEMP_OFFSET 0x1D
#define OTP_TEMP_GAIN 0x1E
#define HREVID 0xC0
#define DSPSIGM 0xC1
#define DSPSIGL 0xC2
#define DSPSIGSEL 0xC3
#define POWER_CTRL 0xC4
#define ARAUTOINC 0xC5
#define CTRL1 0xC6
#define CTRL2 0xC7
#define SLTIME 0xC8
#define CTRL3 0xC9
#define A0 0xCA
#define A1 0xCB
#define A2 0xCC
#define CTRL4 0xCD
#define A3 0xCE
#define A4 0xCF
#define A5 0xD0
#define OTP_ADDR 0xE1
#define OTP_DATA 0xE2
#define OTP_CTRL 0xE3
#define TM_FG 0xE4
/** temperature conv for Si72xx */
#define TEMP_CONV_A2 -3.83e-6F
#define TEMP_CONV_A1 0.16094F
#define TEMP_CONV_A0 -279.80F
#define VDD 3.30F
#define VDD_CONV -0.222F
/** Magnetic conv for Si72xx */
#define MAG_BIAS 0xC000
#define MAG_CONV 0.00125F
class SI7210 : public device::I2C
{
public:
enum Instance : int8_t {
INVALID = -1,
ID_0 = 0,
ID_1 = 1,
ID_2 = 2,
ID_3 = 3,
};
SI7210(int bus, SI7210::Instance instance, const char *path);
virtual ~SI7210();
virtual int init();
virtual ssize_t read(struct file *filp, char *buffer, size_t buflen);
virtual int ioctl(struct file *filp, int cmd, unsigned long arg);
/**
* Stop automatic measurement.
*/
void stop();
/**
* Diagnostics - print some basic information about the driver.
*/
void print_info();
protected:
virtual int probe();
private:
work_s _work{};
bool _running;
/* altitude conversion calibration */
unsigned _call_interval;
si7210_report _report {};
ringbuffer::RingBuffer *_reports;
bool _collect_phase;
orb_advert_t _hall_topic;
int _orb_class_instance;
perf_counter_t _sample_perf;
perf_counter_t _bad_transfers;
perf_counter_t _good_transfers;
perf_counter_t _measure_perf;
perf_counter_t _comms_errors;
perf_counter_t _duplicates;
bool _got_duplicate;
Instance _instance; /**< index of the i2c address and publisher instance */
int _params_sub{-1}; /**< notification of parameter updates */
si7210_report _last_report {}; /**< used for info() */
Parameters _parameters{}; /**< local copies of interesting parameters */
ParameterHandles _parameter_handles{}; /**< handles for interesting parameters */
/**
* Start automatic measurement.
*/
void start();
int measure(); //start measure
int collect(); //get results and publish
int parameters_update(); // update parameters
static void cycle_trampoline(void *arg);
void cycle(); //main execution
/**
* Read the specified number of bytes from SI7210.
*
* @param reg The register to read.
* @param data Pointer to buffer for bytes read.
* @param len Number of bytes to read
* @return OK if the transfer was successful, -errno otherwise.
*/
int get_data(uint8_t reg, uint8_t *data, unsigned len);
/**
* Resets the chip.
*/
int reset();
/**
* Measurement self test
*
* @return 0 on success, 1 on failure
*/
int self_test();
/**
* Get registers values
*
* @return OK if the measurement command was successful.
*/
int get_regs(uint8_t ptr, uint8_t *regs);
/**
* Set registers values
*
* @return OK if the measurement command was successful.
*/
int set_regs(uint8_t ptr, uint8_t value);
/**
* Get measurement values
*
* @return OK if the measurement command was successful.
*/
int get_measurement(uint8_t ptr, uint16_t *value);
/**
* Get si7210 data
*
* @return OK if the measurement command was successful.
*/
int get_sensor_data(uint8_t otpAddr, int8_t *data);
/* do not allow to copy this class due to pointer data members */
SI7210(const SI7210 &);
SI7210 operator=(const SI7210 &);
};
#endif /* SI7210_HPP_ */