|
| 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 | +} |
0 commit comments