Program Listing for File xensiv_pasco2.c¶
↰ Return to documentation for file (src/xensiv_pasco2.c)
/***********************************************************************************************/
#include <assert.h>
#include "xensiv_pasco2.h"
#define XENSIV_PASCO2_COMM_DELAY_MS (5U)
#define XENSIV_PASCO2_COMM_TEST_VAL (0xA5U)
#define XENSIV_PASCO2_SOFT_RESET_DELAY_MS (2000U)
#define XENSIV_PASCO2_FCS_MEAS_RATE_S (10)
#define XENSIV_PASCO2_I2C_WRITE_BUFFER_LEN (17U)
#define XENSIV_PASCO2_UART_WRITE_XFER_BUF_SIZE (8U)
#define XENSIV_PASCO2_UART_READ_XFER_BUF_SIZE (5U)
#define XENSIV_PASCO2_UART_WRITE_XFER_RESP_LEN (2U)
#define XENSIV_PASCO2_UART_READ_XFER_RESP_LEN (3U)
#define XENSIV_PASCO2_UART_ACK (0x06U)
#define XENSIV_PASCO2_UART_NAK (0x15U)
static inline uint8_t xensiv_pasco2_digit_to_ascii(uint8_t digit)
{
xensiv_pasco2_plat_assert(digit <= 0xFU);
if (digit < 10U)
{
return (uint8_t)(digit + 0x30U);
}
else
{
return (uint8_t)(digit + 0x37U);
}
}
static inline uint8_t xensiv_pasco2_ascii_to_digit(uint8_t ascii)
{
xensiv_pasco2_plat_assert(((ascii >= (uint8_t)'0') && (ascii <= (uint8_t)'9')) || ((ascii >= (uint8_t)'A') && (ascii <= (uint8_t)'F')));
if (ascii < (uint8_t)'A')
{
return (uint8_t)(ascii - (uint8_t)'0');
}
else
{
return (uint8_t)(10u + (ascii - (uint8_t)'A'));
}
}
static int32_t xensiv_pasco2_i2c_read(const xensiv_pasco2_t * dev, uint8_t reg_addr, uint8_t * data, uint8_t len)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(reg_addr <= XENSIV_PASCO2_REG_SENS_RST);
xensiv_pasco2_plat_assert(data != NULL);
return xensiv_pasco2_plat_i2c_transfer(dev->ctx, XENSIV_PASCO2_I2C_ADDR, ®_addr, 1, data, len);
}
static int32_t xensiv_pasco2_i2c_write(const xensiv_pasco2_t * dev, uint8_t reg_addr, const uint8_t * data, uint8_t len)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(reg_addr <= XENSIV_PASCO2_REG_SENS_RST);
xensiv_pasco2_plat_assert(data != NULL);
xensiv_pasco2_plat_assert((len + 1U) < XENSIV_PASCO2_I2C_WRITE_BUFFER_LEN);
uint8_t w_data[XENSIV_PASCO2_I2C_WRITE_BUFFER_LEN];
w_data[0] = reg_addr;
for (uint8_t i = 0; i < len; ++i)
{
w_data[i + 1U] = data[i];
}
uint16_t w_len = (uint16_t)((uint16_t)len + 1U);
return xensiv_pasco2_plat_i2c_transfer(dev->ctx, XENSIV_PASCO2_I2C_ADDR, w_data, w_len, NULL, 0);
}
static int32_t xensiv_pasco2_uart_read(const xensiv_pasco2_t * dev, uint8_t reg_addr, uint8_t * data, uint8_t len)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(reg_addr <= XENSIV_PASCO2_REG_SENS_RST);
xensiv_pasco2_plat_assert(data != NULL);
int32_t res = XENSIV_PASCO2_OK;
for (uint8_t i = 0; i < len; ++i)
{
uint8_t uart_buf[XENSIV_PASCO2_UART_READ_XFER_BUF_SIZE] =
{
(uint8_t)'r',
(uint8_t)',',
xensiv_pasco2_digit_to_ascii((reg_addr & (uint8_t)0xF0) >> 4U),
xensiv_pasco2_digit_to_ascii(reg_addr & (uint8_t)0x0F),
(uint8_t)'\n'
};
res = xensiv_pasco2_plat_uart_write(dev->ctx, uart_buf, XENSIV_PASCO2_UART_READ_XFER_BUF_SIZE);
if (XENSIV_PASCO2_OK == res)
{
res = xensiv_pasco2_plat_uart_read(dev->ctx, uart_buf, XENSIV_PASCO2_UART_READ_XFER_RESP_LEN);
if (XENSIV_PASCO2_OK == res)
{
data[i] = (uint8_t)((xensiv_pasco2_ascii_to_digit(uart_buf[0]) << 4) + xensiv_pasco2_ascii_to_digit(uart_buf[1]));
}
}
if (XENSIV_PASCO2_OK != res)
{
break;
}
reg_addr++;
}
return res;
}
static int32_t xensiv_pasco2_uart_write(const xensiv_pasco2_t * dev, uint8_t reg_addr, const uint8_t * data, uint8_t len)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(reg_addr <= XENSIV_PASCO2_REG_SENS_RST);
xensiv_pasco2_plat_assert(data != NULL);
int32_t res = XENSIV_PASCO2_OK;
for (uint8_t i = 0; i < len; ++i)
{
uint8_t uart_buf[XENSIV_PASCO2_UART_WRITE_XFER_BUF_SIZE] =
{
(uint8_t)'w',
(uint8_t)',',
xensiv_pasco2_digit_to_ascii((reg_addr & 0xF0U) >> 4U), xensiv_pasco2_digit_to_ascii(reg_addr & 0x0FU),
(uint8_t)',',
xensiv_pasco2_digit_to_ascii((data[i] & 0xF0U) >> 4U), xensiv_pasco2_digit_to_ascii(data[i] & 0x0FU),
(uint8_t)'\n'
};
res = xensiv_pasco2_plat_uart_write(dev->ctx, uart_buf, XENSIV_PASCO2_UART_WRITE_XFER_BUF_SIZE);
if (XENSIV_PASCO2_OK == res)
{
res = xensiv_pasco2_plat_uart_read(dev->ctx, uart_buf, XENSIV_PASCO2_UART_WRITE_XFER_RESP_LEN);
/* If command triggers a software reset ignores the sensor response */
if ((XENSIV_PASCO2_REG_SENS_RST != reg_addr) || ((uint8_t)XENSIV_PASCO2_CMD_SOFT_RESET != data[i]))
{
res = (XENSIV_PASCO2_OK == res) ?
((XENSIV_PASCO2_UART_ACK == uart_buf[0]) ? XENSIV_PASCO2_OK : XENSIV_PASCO2_ERR_COMM) :
XENSIV_PASCO2_ERR_COMM;
}
else
{
res = XENSIV_PASCO2_OK;
}
}
if (XENSIV_PASCO2_OK != res)
{
break;
}
reg_addr++;
}
return res;
}
static int32_t xensiv_pasco2_init(const xensiv_pasco2_t * dev)
{
xensiv_pasco2_plat_assert(dev != NULL);
/* Check communication */
uint8_t data = XENSIV_PASCO2_COMM_TEST_VAL;
int32_t res = xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_SCRATCH_PAD, &data, 1U);
if (XENSIV_PASCO2_OK == res)
{
res = xensiv_pasco2_get_reg(dev, (uint8_t)XENSIV_PASCO2_REG_SCRATCH_PAD, &data, 1U);
}
if ((XENSIV_PASCO2_OK == res) && (XENSIV_PASCO2_COMM_TEST_VAL == data))
{
/* Soft reset */
res = xensiv_pasco2_cmd(dev, XENSIV_PASCO2_CMD_SOFT_RESET);
xensiv_pasco2_plat_delay(XENSIV_PASCO2_SOFT_RESET_DELAY_MS);
if (XENSIV_PASCO2_OK == res)
{
/* Read the sensor status and verify if the sensor is ready */
res = xensiv_pasco2_get_reg(dev, (uint8_t)XENSIV_PASCO2_REG_SENS_STS, &data, 1U);
}
if (XENSIV_PASCO2_OK == res)
{
if ((data & XENSIV_PASCO2_REG_SENS_STS_ICCER_MSK) != 0U)
{
res = XENSIV_PASCO2_ICCERR;
}
else if ((data & XENSIV_PASCO2_REG_SENS_STS_ORVS_MSK) != 0U)
{
res = XENSIV_PASCO2_ORVS;
}
else if ((data & XENSIV_PASCO2_REG_SENS_STS_ORTMP_MSK) != 0U)
{
res = XENSIV_PASCO2_ORTMP;
}
else if ((data & XENSIV_PASCO2_REG_SENS_STS_SEN_RDY_MSK) == 0U)
{
res = XENSIV_PASCO2_ERR_NOT_READY;
}
else
{
res = XENSIV_PASCO2_OK;
}
}
}
else
{
res = XENSIV_PASCO2_ERR_COMM;
}
return res;
}
int32_t xensiv_pasco2_init_i2c(xensiv_pasco2_t * dev, void * ctx)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(ctx != NULL);
dev->ctx = ctx;
dev->read = xensiv_pasco2_i2c_read;
dev->write = xensiv_pasco2_i2c_write;
return xensiv_pasco2_init(dev);
}
int32_t xensiv_pasco2_init_uart(xensiv_pasco2_t * dev, void * ctx)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(ctx != NULL);
dev->ctx = ctx;
dev->read = xensiv_pasco2_uart_read;
dev->write = xensiv_pasco2_uart_write;
return xensiv_pasco2_init(dev);
}
int32_t xensiv_pasco2_set_reg(const xensiv_pasco2_t * dev, uint8_t reg_addr, const uint8_t * data, uint8_t len)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(data != NULL);
int32_t res = dev->write(dev, reg_addr, data, len);
xensiv_pasco2_plat_delay(XENSIV_PASCO2_COMM_DELAY_MS);
return res;
}
int32_t xensiv_pasco2_get_reg(const xensiv_pasco2_t * dev, uint8_t reg_addr, uint8_t * data, uint8_t len)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(data != NULL);
int32_t res = dev->read(dev, reg_addr, data, len);
xensiv_pasco2_plat_delay(XENSIV_PASCO2_COMM_DELAY_MS);
return res;
}
int32_t xensiv_pasco2_get_id(const xensiv_pasco2_t * dev, xensiv_pasco2_id_t * id)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(id != NULL);
return xensiv_pasco2_get_reg(dev, (uint8_t)XENSIV_PASCO2_REG_PROD_ID, &(id->u), 1U);
}
int32_t xensiv_pasco2_get_status(const xensiv_pasco2_t * dev, xensiv_pasco2_status_t * status)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(status != NULL);
return xensiv_pasco2_get_reg(dev, (uint8_t)XENSIV_PASCO2_REG_SENS_STS, &(status->u), 1U);
}
int32_t xensiv_pasco2_clear_status(const xensiv_pasco2_t * dev, uint8_t mask)
{
xensiv_pasco2_plat_assert(dev != NULL);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_SENS_STS, &mask, 1U);
}
int32_t xensiv_pasco2_get_interrupt_config(const xensiv_pasco2_t * dev, xensiv_pasco2_interrupt_config_t * int_config)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(int_config != NULL);
return xensiv_pasco2_get_reg(dev, (uint8_t)XENSIV_PASCO2_REG_INT_CFG, &(int_config->u), 1U);
}
int32_t xensiv_pasco2_set_interrupt_config(const xensiv_pasco2_t * dev, xensiv_pasco2_interrupt_config_t int_config)
{
xensiv_pasco2_plat_assert(dev != NULL);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_INT_CFG, &(int_config.u), 1U);
}
int32_t xensiv_pasco2_get_measurement_config(const xensiv_pasco2_t * dev, xensiv_pasco2_measurement_config_t * meas_config)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(meas_config != NULL);
return xensiv_pasco2_get_reg(dev, (uint8_t)XENSIV_PASCO2_REG_MEAS_CFG, &(meas_config->u), 1U);
}
int32_t xensiv_pasco2_set_measurement_config(const xensiv_pasco2_t * dev, xensiv_pasco2_measurement_config_t meas_config)
{
xensiv_pasco2_plat_assert(dev != NULL);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_MEAS_CFG, &(meas_config.u), 1U);
}
int32_t xensiv_pasco2_get_result(const xensiv_pasco2_t * dev, uint16_t * val)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(val != NULL);
xensiv_pasco2_meas_status_t meas_status;
int32_t res = xensiv_pasco2_get_measurement_status(dev, &meas_status);
if (XENSIV_PASCO2_OK == res)
{
if (meas_status.b.drdy != 0U)
{
res = xensiv_pasco2_get_reg(dev, (uint8_t)XENSIV_PASCO2_REG_CO2PPM_H, (uint8_t *)val, 2U);
*val = xensiv_pasco2_plat_htons(*val);
}
else
{
res = XENSIV_PASCO2_READ_NRDY;
}
}
return res;
}
int32_t xensiv_pasco2_set_measurement_rate(const xensiv_pasco2_t * dev, uint16_t val)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert((val >= XENSIV_PASCO2_MEAS_RATE_MIN) && (val <= XENSIV_PASCO2_MEAS_RATE_MAX));
val = xensiv_pasco2_plat_htons(val);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_MEAS_RATE_H, (uint8_t *)&val, 2U);
}
int32_t xensiv_pasco2_get_measurement_status(const xensiv_pasco2_t * dev, xensiv_pasco2_meas_status_t * status)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(status != NULL);
return xensiv_pasco2_get_reg(dev, (uint8_t)XENSIV_PASCO2_REG_MEAS_STS, &(status->u), 1U);
}
int32_t xensiv_pasco2_clear_measurement_status(const xensiv_pasco2_t * dev, uint8_t mask)
{
xensiv_pasco2_plat_assert(dev != NULL);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_MEAS_STS, &mask, 1U);
}
int32_t xensiv_pasco2_set_alarm_threshold(const xensiv_pasco2_t * dev, uint16_t val)
{
xensiv_pasco2_plat_assert(dev != NULL);
val = (uint16_t)xensiv_pasco2_plat_htons(val);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_ALARM_TH_H, (uint8_t *)&val, 2U);
}
int32_t xensiv_pasco2_set_pressure_compensation(const xensiv_pasco2_t * dev, uint16_t val)
{
xensiv_pasco2_plat_assert(dev != NULL);
val = (uint16_t)xensiv_pasco2_plat_htons(val);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_PRESS_REF_H, (uint8_t *)&val, 2U);
}
int32_t xensiv_pasco2_set_offset_compensation(const xensiv_pasco2_t * dev, uint16_t val)
{
xensiv_pasco2_plat_assert(dev != NULL);
val = (uint16_t)xensiv_pasco2_plat_htons(val);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_CALIB_REF_H, (uint8_t *)&val, 2U);
}
int32_t xensiv_pasco2_set_scratch_pad(const xensiv_pasco2_t * dev, uint8_t val)
{
xensiv_pasco2_plat_assert(dev != NULL);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_SCRATCH_PAD, &val, 1U);
}
int32_t xensiv_pasco2_get_scratch_pad(const xensiv_pasco2_t * dev, uint8_t * val)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert(val != NULL);
return xensiv_pasco2_get_reg(dev, (uint8_t)XENSIV_PASCO2_REG_SCRATCH_PAD, val, 1U);
}
int32_t xensiv_pasco2_cmd(const xensiv_pasco2_t * dev, xensiv_pasco2_cmd_t cmd)
{
xensiv_pasco2_plat_assert(dev != NULL);
return xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_SENS_RST, (const uint8_t * )&cmd, 1U);
}
int32_t xensiv_pasco2_start_single_mode(const xensiv_pasco2_t * dev)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_measurement_config_t meas_config;
int32_t res = xensiv_pasco2_get_measurement_config(dev, &meas_config);
if (XENSIV_PASCO2_OK == res)
{
if (meas_config.b.op_mode != XENSIV_PASCO2_OP_MODE_IDLE)
{
meas_config.b.op_mode = XENSIV_PASCO2_OP_MODE_IDLE;
res = xensiv_pasco2_set_measurement_config(dev, meas_config);
}
}
if (XENSIV_PASCO2_OK == res)
{
meas_config.b.op_mode = XENSIV_PASCO2_OP_MODE_SINGLE;
meas_config.b.boc_cfg = XENSIV_PASCO2_BOC_CFG_AUTOMATIC;
res = xensiv_pasco2_set_measurement_config(dev, meas_config);
}
return res;
}
int32_t xensiv_pasco2_start_continuous_mode(const xensiv_pasco2_t * dev, uint16_t val)
{
xensiv_pasco2_plat_assert(dev != NULL);
xensiv_pasco2_plat_assert((val >= XENSIV_PASCO2_MEAS_RATE_MIN) && (val <= XENSIV_PASCO2_MEAS_RATE_MAX));
xensiv_pasco2_measurement_config_t meas_config;
int32_t res = xensiv_pasco2_get_measurement_config(dev, &meas_config);
if (XENSIV_PASCO2_OK == res)
{
if (meas_config.b.op_mode != XENSIV_PASCO2_OP_MODE_IDLE)
{
meas_config.b.op_mode = XENSIV_PASCO2_OP_MODE_IDLE;
res = xensiv_pasco2_set_measurement_config(dev, meas_config);
}
}
if (XENSIV_PASCO2_OK == res)
{
val = xensiv_pasco2_plat_htons(val);
res = xensiv_pasco2_set_reg(dev, (uint8_t)XENSIV_PASCO2_REG_MEAS_RATE_H, (uint8_t *)&val, 2U);
}
if (XENSIV_PASCO2_OK == res)
{
meas_config.b.op_mode = XENSIV_PASCO2_OP_MODE_CONTINUOUS;
meas_config.b.boc_cfg = XENSIV_PASCO2_BOC_CFG_AUTOMATIC;
res = xensiv_pasco2_set_measurement_config(dev, meas_config);
}
return res;
}
int32_t xensiv_pasco2_perform_forced_compensation(const xensiv_pasco2_t * dev, uint16_t co2_ref)
{
xensiv_pasco2_measurement_config_t meas_config;
int32_t res = xensiv_pasco2_get_measurement_config(dev, &meas_config);
if (XENSIV_PASCO2_OK == res)
{
meas_config.b.op_mode = XENSIV_PASCO2_OP_MODE_IDLE;
res = xensiv_pasco2_set_measurement_config(dev, meas_config);
}
if (XENSIV_PASCO2_OK == res)
{
res = xensiv_pasco2_set_measurement_rate(dev, XENSIV_PASCO2_FCS_MEAS_RATE_S);
}
if (XENSIV_PASCO2_OK == res)
{
res = xensiv_pasco2_set_offset_compensation(dev, co2_ref);
}
if (XENSIV_PASCO2_OK == res)
{
meas_config.b.op_mode = XENSIV_PASCO2_OP_MODE_CONTINUOUS;
meas_config.b.boc_cfg = XENSIV_PASCO2_BOC_CFG_FORCED;
res = xensiv_pasco2_set_measurement_config(dev, meas_config);
}
if (XENSIV_PASCO2_OK == res)
{
/* wait until the FCS is finished */
do
{
res = xensiv_pasco2_get_measurement_config(dev, &meas_config);
} while ((XENSIV_PASCO2_OK != res) || (XENSIV_PASCO2_BOC_CFG_FORCED == meas_config.b.boc_cfg));
}
if (XENSIV_PASCO2_OK == res)
{
meas_config.b.op_mode = XENSIV_PASCO2_OP_MODE_IDLE;
res = xensiv_pasco2_set_measurement_config(dev, meas_config);
}
if (XENSIV_PASCO2_OK == res)
{
res = xensiv_pasco2_cmd(dev, XENSIV_PASCO2_CMD_SAVE_FCS_CALIB_OFFSET);
}
return res;
}
__attribute__((weak)) int32_t xensiv_pasco2_plat_i2c_transfer(void * ctx, uint16_t dev_addr, const uint8_t * tx_buffer, size_t tx_len, uint8_t * rx_buffer, size_t rx_len)
{
(void)ctx;
(void)dev_addr;
(void)tx_buffer;
(void)tx_len;
(void)rx_buffer;
(void)rx_len;
return XENSIV_PASCO2_ERR_COMM;
}
__attribute__((weak)) int32_t xensiv_pasco2_plat_uart_read(void *ctx, uint8_t * data, size_t len)
{
(void)ctx;
(void)data;
(void)len;
return XENSIV_PASCO2_ERR_COMM;
}
__attribute__((weak)) int32_t xensiv_pasco2_plat_uart_write(void *ctx, uint8_t * data, size_t len)
{
(void)ctx;
(void)data;
(void)len;
return XENSIV_PASCO2_ERR_COMM;
}
__attribute__((weak)) void xensiv_pasco2_plat_delay(uint32_t ms)
{
(void)ms;
}
__attribute__((weak)) uint16_t xensiv_pasco2_plat_htons(uint16_t x)
{
return ((uint16_t)(((x & 0x00ffU) << 8) |
((x & 0xff00U) >> 8)));
}
__attribute__((weak)) void xensiv_pasco2_plat_assert(int expr)
{
assert(expr);
}