Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/iio/chemical/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 6 kB image not shown  

Quelle  sps30_i2c.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Sensirion SPS30 particulate matter sensor i2c driver
 *
 * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
 *
 * I2C slave address: 0x69
 */

#include <linux/unaligned.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/types.h>

#include "sps30.h"

#define SPS30_I2C_CRC8_POLYNOMIAL 0x31
/* max number of bytes needed to store PM measurements or serial string */
#define SPS30_I2C_MAX_BUF_SIZE 48

DECLARE_CRC8_TABLE(sps30_i2c_crc8_table);

#define SPS30_I2C_START_MEAS 0x0010
#define SPS30_I2C_STOP_MEAS 0x0104
#define SPS30_I2C_READ_MEAS 0x0300
#define SPS30_I2C_MEAS_READY 0x0202
#define SPS30_I2C_RESET 0xd304
#define SPS30_I2C_CLEAN_FAN 0x5607
#define SPS30_I2C_PERIOD 0x8004
#define SPS30_I2C_READ_SERIAL 0xd033
#define SPS30_I2C_READ_VERSION 0xd100

static int sps30_i2c_xfer(struct sps30_state *state, unsigned char *txbuf, size_t txsize,
     unsigned char *rxbuf, size_t rxsize)
{
 struct i2c_client *client = to_i2c_client(state->dev);
 int ret;

 /*
 * Sensor does not support repeated start so instead of
 * sending two i2c messages in a row we just send one by one.
 */

 ret = i2c_master_send(client, txbuf, txsize);
 if (ret < 0)
  return ret;
 if (ret != txsize)
  return -EIO;

 if (!rxsize)
  return 0;

 ret = i2c_master_recv(client, rxbuf, rxsize);
 if (ret < 0)
  return ret;
 if (ret != rxsize)
  return -EIO;

 return 0;
}

static int sps30_i2c_command(struct sps30_state *state, u16 cmd, void *arg, size_t arg_size,
        void *rsp, size_t rsp_size)
{
 /*
 * Internally sensor stores measurements in a following manner:
 *
 * PM1:   upper two bytes, crc8, lower two bytes, crc8
 * PM2P5: upper two bytes, crc8, lower two bytes, crc8
 * PM4:   upper two bytes, crc8, lower two bytes, crc8
 * PM10:  upper two bytes, crc8, lower two bytes, crc8
 *
 * What follows next are number concentration measurements and
 * typical particle size measurement which we omit.
 */

 unsigned char buf[SPS30_I2C_MAX_BUF_SIZE];
 unsigned char *tmp;
 unsigned char crc;
 size_t i;
 int ret;

 put_unaligned_be16(cmd, buf);
 i = 2;

 if (rsp) {
  /* each two bytes are followed by a crc8 */
  rsp_size += rsp_size / 2;
 } else {
  tmp = arg;

  while (arg_size) {
   buf[i] = *tmp++;
   buf[i + 1] = *tmp++;
   buf[i + 2] = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE);
   arg_size -= 2;
   i += 3;
  }
 }

 ret = sps30_i2c_xfer(state, buf, i, buf, rsp_size);
 if (ret)
  return ret;

 /* validate received data and strip off crc bytes */
 tmp = rsp;
 for (i = 0; i < rsp_size; i += 3) {
  crc = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE);
  if (crc != buf[i + 2]) {
   dev_err(state->dev, "data integrity check failed\n");
   return -EIO;
  }

  *tmp++ = buf[i];
  *tmp++ = buf[i + 1];
 }

 return 0;
}

static int sps30_i2c_start_meas(struct sps30_state *state)
{
 /* request BE IEEE754 formatted data */
 unsigned char buf[] = { 0x03, 0x00 };

 return sps30_i2c_command(state, SPS30_I2C_START_MEAS, buf, sizeof(buf), NULL, 0);
}

static int sps30_i2c_stop_meas(struct sps30_state *state)
{
 return sps30_i2c_command(state, SPS30_I2C_STOP_MEAS, NULL, 0, NULL, 0);
}

static int sps30_i2c_reset(struct sps30_state *state)
{
 int ret;

 ret = sps30_i2c_command(state, SPS30_I2C_RESET, NULL, 0, NULL, 0);
 msleep(500);
 /*
 * Power-on-reset causes sensor to produce some glitch on i2c bus and
 * some controllers end up in error state. Recover simply by placing
 * some data on the bus, for example STOP_MEAS command, which
 * is NOP in this case.
 */

 sps30_i2c_stop_meas(state);

 return ret;
}

static bool sps30_i2c_meas_ready(struct sps30_state *state)
{
 unsigned char buf[2];
 int ret;

 ret = sps30_i2c_command(state, SPS30_I2C_MEAS_READY, NULL, 0, buf, sizeof(buf));
 if (ret)
  return false;

 return buf[1];
}

static int sps30_i2c_read_meas(struct sps30_state *state, __be32 *meas, size_t num)
{
 /* measurements are ready within a second */
 if (msleep_interruptible(1000))
  return -EINTR;

 if (!sps30_i2c_meas_ready(state))
  return -ETIMEDOUT;

 return sps30_i2c_command(state, SPS30_I2C_READ_MEAS, NULL, 0, meas, sizeof(num) * num);
}

static int sps30_i2c_clean_fan(struct sps30_state *state)
{
 return sps30_i2c_command(state, SPS30_I2C_CLEAN_FAN, NULL, 0, NULL, 0);
}

static int sps30_i2c_read_cleaning_period(struct sps30_state *state, __be32 *period)
{
 return sps30_i2c_command(state, SPS30_I2C_PERIOD, NULL, 0, period, sizeof(*period));
}

static int sps30_i2c_write_cleaning_period(struct sps30_state *state, __be32 period)
{
 return sps30_i2c_command(state, SPS30_I2C_PERIOD, &period, sizeof(period), NULL, 0);
}

static int sps30_i2c_show_info(struct sps30_state *state)
{
 /* extra nul just in case */
 unsigned char buf[32 + 1] = { 0x00 };
 int ret;

 ret = sps30_i2c_command(state, SPS30_I2C_READ_SERIAL, NULL, 0, buf, sizeof(buf) - 1);
 if (ret)
  return ret;

 dev_info(state->dev, "serial number: %s\n", buf);

 ret = sps30_i2c_command(state, SPS30_I2C_READ_VERSION, NULL, 0, buf, 2);
 if (ret)
  return ret;

 dev_info(state->dev, "fw version: %u.%u\n", buf[0], buf[1]);

 return 0;
}

static const struct sps30_ops sps30_i2c_ops = {
 .start_meas = sps30_i2c_start_meas,
 .stop_meas = sps30_i2c_stop_meas,
 .read_meas = sps30_i2c_read_meas,
 .reset = sps30_i2c_reset,
 .clean_fan = sps30_i2c_clean_fan,
 .read_cleaning_period = sps30_i2c_read_cleaning_period,
 .write_cleaning_period = sps30_i2c_write_cleaning_period,
 .show_info = sps30_i2c_show_info,
};

static int sps30_i2c_probe(struct i2c_client *client)
{
 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
  return -EOPNOTSUPP;

 crc8_populate_msb(sps30_i2c_crc8_table, SPS30_I2C_CRC8_POLYNOMIAL);

 return sps30_probe(&client->dev, client->name, NULL, &sps30_i2c_ops);
}

static const struct i2c_device_id sps30_i2c_id[] = {
 { "sps30" },
 { }
};
MODULE_DEVICE_TABLE(i2c, sps30_i2c_id);

static const struct of_device_id sps30_i2c_of_match[] = {
 { .compatible = "sensirion,sps30" },
 { }
};
MODULE_DEVICE_TABLE(of, sps30_i2c_of_match);

static struct i2c_driver sps30_i2c_driver = {
 .driver = {
  .name = KBUILD_MODNAME,
  .of_match_table = sps30_i2c_of_match,
 },
 .id_table = sps30_i2c_id,
 .probe = sps30_i2c_probe,
};
module_i2c_driver(sps30_i2c_driver);

MODULE_AUTHOR("Tomasz Duszynski ");
MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_SPS30");

Messung V0.5
C=94 H=93 G=93

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.