Skip to content

Commit d2f8d4d

Browse files
committed
drivers/abp2: driver for SPI sensors, prepare for I2C
Honeywell ABP2 pressure sensors series. Implement all sensors features, only supporting the SPI version of the sensor. Prepare future support for the I2C interface by emphasizing where to implement the code that will support the I2C bus version.
1 parent 2812361 commit d2f8d4d

File tree

6 files changed

+705
-2
lines changed

6 files changed

+705
-2
lines changed

drivers/abp2/abp2.c

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
/*
2+
* Copyright (C) 2024 CNRS, France
3+
*
4+
* This file is subject to the terms and conditions of the GNU Lesser
5+
* General Public License v2.1. See the file LICENSE in the top level
6+
* directory for more details.
7+
*/
8+
9+
/**
10+
* @ingroup drivers_abp2
11+
* @{
12+
*
13+
* @file
14+
* @brief Honeywell ABP2 series pressure and temperature sensor driver
15+
*
16+
* @author David Picard <[email protected]>
17+
* @}
18+
*/
19+
20+
#include <assert.h>
21+
#include "abp2.h"
22+
#include "abp2_params.h"
23+
#include "periph/spi.h"
24+
#include "periph/gpio.h"
25+
#include "ztimer.h"
26+
#include <errno.h>
27+
#include "debug.h"
28+
29+
#define ENABLE_DEBUG 0
30+
#define MAX_LOOPS_TIMEOUT (10) /**< Timeout (ms). */
31+
#define ABP2_CMD_MEAS (0xAA) /**< Start a measurement. */
32+
#define ABP2_CMD_NOP (0xF0) /**< NOP command. */
33+
#define ABP2_RX_BUF_LEN (8) /**< Length of the receive buffer. */
34+
35+
/** Data to send in order to start a measurement. */
36+
static const uint8_t data_tx_meas_start[] = { ABP2_CMD_MEAS, 0x00, 0x00 };
37+
38+
/** Data to send in order to start a measurement and read the data of the previous measurement. */
39+
static const uint8_t data_tx_meas_read[] = { ABP2_CMD_MEAS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
40+
41+
/** Data to send in order to read the data of the previous measurement. */
42+
static const uint8_t data_tx_nop_read[] = { ABP2_CMD_NOP, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
43+
44+
static const int32_t cntPMax = 15099494; /**< Output at maximum pressure (counts). */
45+
static const int32_t cntPMin = 1677722; /**< Output at minimum pressure (counts). */
46+
47+
int abp2_init(abp2_t *dev, const abp2_params_t *params)
48+
{
49+
uint8_t status = 0xFF; /* sensor status byte */
50+
int res = 0;
51+
int count = 10;
52+
53+
assert(dev && params);
54+
dev->params = params;
55+
56+
#if defined(MODULE_ABP2_SPI)
57+
res = spi_init_cs(params->spi, params->cs);
58+
#else
59+
#pragma message("implement I2C code here")
60+
#endif
61+
if (res) {
62+
return -EIO;
63+
}
64+
65+
/* test status byte: the busy flag should clear in about 5ms */
66+
while ((status & ABP2_STATUS_BUSY) && count) {
67+
#if defined(MODULE_ABP2_SPI)
68+
spi_acquire(params->spi, params->cs, SPI_MODE_0, params->clk);
69+
status = spi_transfer_byte(params->spi, params->cs, false, ABP2_CMD_NOP);
70+
spi_release(params->spi);
71+
#else
72+
#pragma message("implement I2C code here")
73+
#endif
74+
ztimer_sleep(ZTIMER_USEC, 1000);
75+
count--;
76+
}
77+
/* the busy flag should be clear */
78+
if (status & ABP2_STATUS_BUSY) {
79+
res = -ETIMEDOUT;
80+
}
81+
82+
return res;
83+
}
84+
85+
int abp2_measure(const abp2_t *dev)
86+
{
87+
uint8_t status = 0xFF; /* sensor status byte */
88+
int res = 0;
89+
uint8_t data_rx[ABP2_RX_BUF_LEN];
90+
91+
#if defined(MODULE_ABP2_SPI)
92+
spi_acquire(dev->params->spi, dev->params->cs, SPI_MODE_0, dev->params->clk);
93+
spi_transfer_bytes(dev->params->spi, dev->params->cs, false, data_tx_meas_start, data_rx,
94+
sizeof(data_tx_meas_start));
95+
spi_release(dev->params->spi);
96+
#else
97+
#pragma message("implement I2C code here")
98+
#endif
99+
status = data_rx[0];
100+
if (status & ABP2_STATUS_MASK) {
101+
return -ENODATA;
102+
}
103+
104+
return res;
105+
}
106+
107+
int abp2_read(const abp2_t *dev, int32_t *press, int32_t *temp)
108+
{
109+
int res = 0;
110+
uint8_t status = 0xFF; /* sensor status byte */
111+
int loopsTimeout = MAX_LOOPS_TIMEOUT;
112+
abp2_raw_t rawData;
113+
114+
res = abp2_measure(dev);
115+
if (res) {
116+
return res;
117+
}
118+
/* wait until the busy flag clears or until timeout: */
119+
while (((status = abp2_getstatus(dev)) & ABP2_STATUS_BUSY) && loopsTimeout) {
120+
loopsTimeout--;
121+
ztimer_sleep(ZTIMER_USEC, 1000); /* wait 1ms */
122+
}
123+
if (!loopsTimeout) {
124+
return -ETIMEDOUT;
125+
}
126+
/* check sensor errors: */
127+
if (status & ABP2_STATUS_MASK) {
128+
return -ENODATA;
129+
}
130+
/* read ADC conversion results: */
131+
res = abp2_read_raw(dev, &rawData);
132+
if (res) {
133+
return res;
134+
}
135+
/* convert to physical quantities: */
136+
*press = abp2_pressure(dev, &rawData);
137+
*temp = abp2_temperature(dev, &rawData);
138+
139+
return 0;
140+
}
141+
142+
int abp2_read_nb(const abp2_t *dev, int32_t *press, int32_t *temp)
143+
{
144+
int res = 0;
145+
abp2_raw_t rawData;
146+
147+
/* start an ADC conversion and read previous results: */
148+
res = abp2_measure_read(dev, &rawData);
149+
if (res) {
150+
return res;
151+
}
152+
/* convert to physical quantities: */
153+
*press = abp2_pressure(dev, &rawData);
154+
*temp = abp2_temperature(dev, &rawData);
155+
156+
return 0;
157+
}
158+
159+
uint8_t abp2_getstatus(const abp2_t *dev)
160+
{
161+
uint8_t status = 0xFF; /* sensor status byte */
162+
163+
#if defined(MODULE_ABP2_SPI)
164+
spi_acquire(dev->params->spi, dev->params->cs, SPI_MODE_0, dev->params->clk);
165+
status = spi_transfer_byte(dev->params->spi, dev->params->cs, false, ABP2_CMD_NOP);
166+
spi_release(dev->params->spi);
167+
#else
168+
#pragma message("implement I2C code here")
169+
#endif
170+
171+
return status;
172+
}
173+
174+
int abp2_read_raw(const abp2_t *dev, abp2_raw_t *raw_values)
175+
{
176+
uint8_t status = 0xFF; /* sensor status byte */
177+
uint8_t data_rx[ABP2_RX_BUF_LEN];
178+
179+
#if defined(MODULE_ABP2_SPI)
180+
spi_acquire(dev->params->spi, dev->params->cs, SPI_MODE_0, dev->params->clk);
181+
spi_transfer_bytes(dev->params->spi, dev->params->cs, false, data_tx_nop_read, data_rx,
182+
sizeof(data_tx_nop_read));
183+
spi_release(dev->params->spi);
184+
#else
185+
#pragma message("implement I2C code here")
186+
#endif
187+
status = data_rx[0];
188+
if (status & ABP2_STATUS_MASK) {
189+
return -ENODATA;
190+
}
191+
raw_values->cntPress = (uint32_t)data_rx[1] << 16 | (uint32_t)data_rx[2] << 8 | data_rx[3];
192+
raw_values->cntTemp = (uint32_t)data_rx[4] << 16 | (uint32_t)data_rx[5] << 8 | data_rx[6];
193+
194+
return 0;
195+
}
196+
197+
int abp2_measure_read(const abp2_t *dev, abp2_raw_t *raw_values)
198+
{
199+
uint8_t status = 0xFF; /* sensor status byte */
200+
int res = 0;
201+
uint8_t data_rx[ABP2_RX_BUF_LEN];
202+
203+
#if defined(MODULE_ABP2_SPI)
204+
spi_acquire(dev->params->spi, dev->params->cs, SPI_MODE_0, dev->params->clk);
205+
spi_transfer_bytes(dev->params->spi, dev->params->cs, false, data_tx_meas_read, data_rx,
206+
sizeof(data_tx_meas_read));
207+
spi_release(dev->params->spi);
208+
#else
209+
#pragma message("implement I2C code here")
210+
#endif
211+
status = data_rx[0];
212+
if (status & ABP2_STATUS_MASK) {
213+
return -ENODATA;
214+
}
215+
raw_values->cntPress = (uint32_t)data_rx[1] << 16 | (uint32_t)data_rx[2] << 8 | data_rx[3];
216+
raw_values->cntTemp = (uint32_t)data_rx[4] << 16 | (uint32_t)data_rx[5] << 8 | data_rx[6];
217+
218+
return res;
219+
}
220+
221+
int32_t abp2_pressure(const abp2_t *dev, const abp2_raw_t *raw_values)
222+
{
223+
int32_t press = 0;
224+
225+
/* int64_t in intermediate calculation prevents overflow */
226+
int64_t diffCnt = (raw_values->cntPress - cntPMin);
227+
int64_t diffRng = (dev->params->rangeMax - dev->params->rangeMin);
228+
int64_t diffCntRng = cntPMax - cntPMin;
229+
int64_t numerator = diffCnt * diffRng;
230+
231+
press = numerator / diffCntRng + dev->params->rangeMin;
232+
233+
return press;
234+
}
235+
236+
int32_t abp2_temperature(const abp2_t *dev, const abp2_raw_t *raw_values)
237+
{
238+
(void)(dev); /* keep unused parameter to homogenize function prototypes */
239+
/* 64-bit constant avoids integer overflow */
240+
return ((raw_values->cntTemp * 200000LL) >> 24) - 50000;
241+
}

drivers/abp2/abp2_saul.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (C) 2024 CNRS, France
3+
*
4+
* This file is subject to the terms and conditions of the GNU Lesser
5+
* General Public License v2.1. See the file LICENSE in the top level
6+
* directory for more details.
7+
*/
8+
9+
/**
10+
* @ingroup drivers_abp2
11+
* @{
12+
*
13+
* @file
14+
* @brief ABP2 adaption to the SAUL framework.
15+
*
16+
* @author David Picard <[email protected]>
17+
* @}
18+
*/
19+
20+
#include <string.h>
21+
#include <stdio.h>
22+
23+
#include "saul.h"
24+
#include "abp2.h"
25+
26+
static int read_press(const void *dev, phydat_t *press)
27+
{
28+
abp2_raw_t rawData;
29+
30+
if (abp2_measure_read((abp2_t *)dev, &rawData)) {
31+
return -ECANCELED;
32+
}
33+
34+
/* abp2_pressure() returns an int32_t in thousandths of user units.
35+
* i.e. µbar in this context.
36+
* Convert to tenths of millibars to fit in phydat_t.val[0],
37+
* a int16_t, while maintaining a 0.1 mbar resolution */
38+
press->val[0] = abp2_pressure((abp2_t *)dev, &rawData) / 100;
39+
press->scale = -4;
40+
press->unit = UNIT_BAR;
41+
42+
return 1;
43+
}
44+
45+
static int read_temp(const void *dev, phydat_t *temp)
46+
{
47+
abp2_raw_t rawData;
48+
49+
if (abp2_measure_read((abp2_t *)dev, &rawData)) {
50+
return -ECANCELED;
51+
}
52+
53+
/* abp2_temperature() returns milli-degrees Celsius */
54+
temp->val[0] = abp2_temperature((abp2_t *)dev, &rawData);
55+
temp->scale = -3;
56+
temp->unit = UNIT_TEMP_C;
57+
58+
return 1;
59+
}
60+
61+
const saul_driver_t abp2_saul_driver_press = {
62+
.read = read_press,
63+
.write = saul_write_notsup,
64+
.type = SAUL_SENSE_PRESS,
65+
};
66+
67+
const saul_driver_t abp2_saul_driver_temp = {
68+
.read = read_temp,
69+
.write = saul_write_notsup,
70+
.type = SAUL_SENSE_TEMP
71+
};

drivers/abp2/include/abp2_params.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ extern "C" {
8080
#endif
8181
#ifndef ABP2_SAUL_INFO
8282
/** @brief Sensor driver name in the SAUL framework */
83-
#define ABP2_SAUL_INFO { .name = "abp2" }
83+
#define ABP2_SAUL_INFO \
84+
{ { .name = "Pressure sensor (abp2)" }, \
85+
{ .name = "Temperature sensor (abp2)" } \
86+
}
8487
#endif
8588
/** @} */
8689

@@ -103,7 +106,7 @@ static const abp2_params_t abp2_params[] =
103106
/**
104107
* @brief Additional meta information to keep in the SAUL registry
105108
*/
106-
static const saul_reg_info_t abp2_saul_info[] =
109+
static const saul_reg_info_t abp2_saul_info[][2] =
107110
{
108111
ABP2_SAUL_INFO
109112
};

0 commit comments

Comments
 (0)