Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers/abp2: add abp2 driver #20398

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/abp2/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(RIOTMAKE)/driver_with_saul.mk
10 changes: 10 additions & 0 deletions drivers/abp2/Makefile.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ifneq (,$(filter abp2_spi,$(USEMODULE)))
FEATURES_REQUIRED += periph_spi
endif

ifneq (,$(filter abp2_i2c,$(USEMODULE)))
FEATURES_REQUIRED += periph_i2c
endif

USEMODULE += ztimer
USEMODULE += ztimer_msec
5 changes: 5 additions & 0 deletions drivers/abp2/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PSEUDOMODULES += abp2_spi
PSEUDOMODULES += abp2_i2c

USEMODULE_INCLUDES_abp2 := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_abp2)
242 changes: 242 additions & 0 deletions drivers/abp2/abp2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/*
* Copyright (C) 2024 CNRS, France
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup drivers_abp2
* @{
*
* @file
* @brief Honeywell ABP2 series pressure and temperature sensor driver
*
* @author David Picard <david.picard@clermont.in2p3.fr>
* @}
*/

#include <assert.h>
#include "abp2.h"
#include "abp2_params.h"
#include "periph/spi.h"
#include "periph/gpio.h"
#include "ztimer.h"
#include <errno.h>
#include "debug.h"

#define ENABLE_DEBUG 0
#define MAX_LOOPS_TIMEOUT (10) /**< Timeout (ms). */
#define DEV (dev->params.spi)
#define ABP2_CMD_MEAS (0xAA) /**< Start a measurement. */
#define ABP2_CMD_NOP (0xF0) /**< NOP command. */
#define ABP2_RX_BUF_LEN (8) /**< Length of the receive buffer. */

/** Data to send in order to start a measurement. */
static const uint8_t data_tx_meas_start[] = { ABP2_CMD_MEAS, 0x00, 0x00 };

/** Data to send in order to start a measurement and read the data of the previous measurement. */
static const uint8_t data_tx_meas_read[] = { ABP2_CMD_MEAS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

/** Data to send in order to read the data of the previous measurement. */
static const uint8_t data_tx_nop_read[] = { ABP2_CMD_NOP, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

static const int32_t cntPMax = 15099494; /**< Output at maximum pressure (counts). */
static const int32_t cntPMin = 1677722; /**< Output at minimum pressure (counts). */

int abp2_init(abp2_t *dev, const abp2_params_t *params)
{
uint8_t status = 0xFF; /* sensor status byte */
int res = 0;
int count = 10;

assert(dev && params);
dev->params = params;

#if defined(MODULE_ABP2_SPI)
res = spi_init_cs(params->spi, params->cs);
#else
#pragma message("implement I2C code here")
#endif
if (res) {
return -EIO;
}

/* test status byte: the busy flag should clear in about 5ms */
while ((status & ABP2_STATUS_BUSY) && count) {
#if defined(MODULE_ABP2_SPI)
spi_acquire(params->spi, params->cs, SPI_MODE_0, params->clk);
status = spi_transfer_byte(params->spi, params->cs, false, ABP2_CMD_NOP);
spi_release(params->spi);
#else
#pragma message("implement I2C code here")
#endif
ztimer_sleep(ZTIMER_MSEC, 1);
count--;
}
/* the busy flag should be clear */
if (status & ABP2_STATUS_BUSY) {
res = -ETIMEDOUT;
}

return res;
}

int abp2_measure(const abp2_t *dev)
{
uint8_t status = 0xFF; /* sensor status byte */
int res = 0;
uint8_t data_rx[ABP2_RX_BUF_LEN];

#if defined(MODULE_ABP2_SPI)
spi_acquire(dev->params->spi, dev->params->cs, SPI_MODE_0, dev->params->clk);
spi_transfer_bytes(dev->params->spi, dev->params->cs, false, data_tx_meas_start, data_rx,
sizeof(data_tx_meas_start));
spi_release(dev->params->spi);
#else
#pragma message("implement I2C code here")
#endif
status = data_rx[0];
if (status & ABP2_STATUS_MASK) {
return -ENODATA;
}

return res;
}

int abp2_read(const abp2_t *dev, int32_t *press, int32_t *temp)
{
int res = 0;
uint8_t status = 0xFF; /* sensor status byte */
int loopsTimeout = MAX_LOOPS_TIMEOUT;
abp2_raw_t rawData;

res = abp2_measure(dev);
if (res) {
return res;
}
/* wait until the busy flag clears or until timeout: */
while (((status = abp2_getstatus(dev)) & ABP2_STATUS_BUSY) && loopsTimeout) {
loopsTimeout--;
ztimer_sleep(ZTIMER_MSEC, 1); /* wait 1ms */
}
if (!loopsTimeout) {
return -ETIMEDOUT;
}
/* check sensor errors: */
if (status & ABP2_STATUS_MASK) {
return -ENODATA;
}
/* read ADC conversion results: */
res = abp2_read_raw(dev, &rawData);
if (res) {
return res;
}
/* convert to physical quantities: */
*press = abp2_pressure(dev, &rawData);
*temp = abp2_temperature(dev, &rawData);

return 0;
}

int abp2_read_nb(const abp2_t *dev, int32_t *press, int32_t *temp)
{
int res = 0;
abp2_raw_t rawData;

/* start an ADC conversion and read previous results: */
res = abp2_measure_read(dev, &rawData);
if (res) {
return res;
}
/* convert to physical quantities: */
*press = abp2_pressure(dev, &rawData);
*temp = abp2_temperature(dev, &rawData);

return 0;
}

uint8_t abp2_getstatus(const abp2_t *dev)
{
uint8_t status = 0xFF; /* sensor status byte */

#if defined(MODULE_ABP2_SPI)
spi_acquire(dev->params->spi, dev->params->cs, SPI_MODE_0, dev->params->clk);
status = spi_transfer_byte(dev->params->spi, dev->params->cs, false, ABP2_CMD_NOP);
spi_release(dev->params->spi);
#else
#pragma message("implement I2C code here")
#endif

return status;
}

int abp2_read_raw(const abp2_t *dev, abp2_raw_t *raw_values)
{
uint8_t status = 0xFF; /* sensor status byte */
uint8_t data_rx[ABP2_RX_BUF_LEN];

#if defined(MODULE_ABP2_SPI)
spi_acquire(dev->params->spi, dev->params->cs, SPI_MODE_0, dev->params->clk);
spi_transfer_bytes(dev->params->spi, dev->params->cs, false, data_tx_nop_read, data_rx,
sizeof(data_tx_nop_read));
spi_release(dev->params->spi);
#else
#pragma message("implement I2C code here")
#endif
status = data_rx[0];
if (status & ABP2_STATUS_MASK) {
return -ENODATA;
}
raw_values->cntPress = (uint32_t)data_rx[1] << 16 | (uint32_t)data_rx[2] << 8 | data_rx[3];
raw_values->cntTemp = (uint32_t)data_rx[4] << 16 | (uint32_t)data_rx[5] << 8 | data_rx[6];

return 0;
}

int abp2_measure_read(const abp2_t *dev, abp2_raw_t *raw_values)
{
uint8_t status = 0xFF; /* sensor status byte */
int res = 0;
uint8_t data_rx[ABP2_RX_BUF_LEN];

#if defined(MODULE_ABP2_SPI)
spi_acquire(dev->params->spi, dev->params->cs, SPI_MODE_0, dev->params->clk);
spi_transfer_bytes(dev->params->spi, dev->params->cs, false, data_tx_meas_read, data_rx,
sizeof(data_tx_meas_read));
spi_release(dev->params->spi);
#else
#pragma message("implement I2C code here")
#endif
status = data_rx[0];
if (status & ABP2_STATUS_MASK) {
return -ENODATA;
}
raw_values->cntPress = (uint32_t)data_rx[1] << 16 | (uint32_t)data_rx[2] << 8 | data_rx[3];
raw_values->cntTemp = (uint32_t)data_rx[4] << 16 | (uint32_t)data_rx[5] << 8 | data_rx[6];

return res;
}

int32_t abp2_pressure(const abp2_t *dev, const abp2_raw_t *raw_values)
{
int32_t press = 0;

/* int64_t in intermediate calculation prevents overflow */
int64_t diffCnt = (raw_values->cntPress - cntPMin);
int64_t diffRng = (dev->params->rangeMax - dev->params->rangeMin);
int64_t diffCntRng = cntPMax - cntPMin;
int64_t numerator = diffCnt * diffRng;

press = numerator / diffCntRng + dev->params->rangeMin;

return press;
}

int32_t abp2_temperature(const abp2_t *dev, const abp2_raw_t *raw_values)
{
(void)(dev); /* keep unused parameter to homogenize function prototypes */
/* 64-bit constant avoids integer overflow */
return ((raw_values->cntTemp * 200000LL) >> 24) - 50000;
}
71 changes: 71 additions & 0 deletions drivers/abp2/abp2_saul.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (C) 2024 CNRS, France
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup drivers_abp2
* @{
*
* @file
* @brief ABP2 adaption to the SAUL framework.
*
* @author David Picard <david.picard@clermont.in2p3.fr>
* @}
*/

#include <string.h>
#include <stdio.h>

#include "saul.h"
#include "abp2.h"

static int read_press(const void *dev, phydat_t *press)
{
abp2_raw_t rawData;

if (abp2_measure_read((abp2_t *)dev, &rawData)) {
return -ECANCELED;
}

/* abp2_pressure() returns an int32_t in thousandths of user units.
* i.e. µbar in this context.
* Convert to tenths of millibars to fit in phydat_t.val[0],
* a int16_t, while maintaining a 0.1 mbar resolution */
press->val[0] = abp2_pressure((abp2_t *)dev, &rawData) / 100;
press->scale = -4;
press->unit = UNIT_BAR;

return 1;
}

static int read_temp(const void *dev, phydat_t *temp)
{
abp2_raw_t rawData;

if (abp2_measure_read((abp2_t *)dev, &rawData)) {
return -ECANCELED;
}

/* abp2_temperature() returns milli-degrees Celsius */
temp->val[0] = abp2_temperature((abp2_t *)dev, &rawData);
temp->scale = -3;
temp->unit = UNIT_TEMP_C;

return 1;
}

const saul_driver_t abp2_saul_driver_press = {
.read = read_press,
.write = saul_write_notsup,
.type = SAUL_SENSE_PRESS,
};

const saul_driver_t abp2_saul_driver_temp = {
.read = read_temp,
.write = saul_write_notsup,
.type = SAUL_SENSE_TEMP
};