Blame view

RIOT/drivers/si70xx/si70xx.c 4.5 KB
a752c7ab   elopes   add first test an...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  /*
   * Copyright (C) 2016 Bas Stottelaar <basstottelaar@gmail.com>
   *
   * 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_si70xx
   * @{
   *
   * @file
   * @brief       Implementation of Si7006/13/20/21 sensor driver.
   *
   * @author      Bas Stottelaar <basstottelaar@gmail.com>
   *
   * @}
   */
  
  
  #include "xtimer.h"
  
  #include "si70xx.h"
  
  /**
   * @brief   Utility method to perform and reconstruct a measurement.
   */
  static uint32_t si70xx_measure(const si70xx_t *dev, uint8_t command)
  {
      uint8_t result[2];
  
      i2c_acquire(dev->i2c_dev);
      i2c_write_byte(dev->i2c_dev, dev->address, command);
      i2c_read_bytes(dev->i2c_dev, dev->address, result, 2);
      i2c_release(dev->i2c_dev);
  
      /* reconstruct raw result */
      return ((uint32_t)result[0] << 8) + (result[1] & 0xfc);
  }
  
  int si70xx_test(const si70xx_t *dev)
  {
      uint8_t revision = si70xx_get_revision(dev);
  
      if (revision != SI70XX_REVISION_1 && revision != SI70XX_REVISION_2) {
          return -1;
      }
  
      uint8_t id = si70xx_get_id(dev);
  
      if (id != SI70XX_ID_SI7006 && id != SI70XX_ID_SI7013 &&
          id != SI70XX_ID_SI7020 && id != SI70XX_ID_SI7021) {
          return -2;
      }
  
      return 0;
  }
  
  int si70xx_init(si70xx_t *dev, i2c_t i2c_dev, uint8_t address)
  {
      dev->i2c_dev = i2c_dev;
      dev->address = address;
  
      /* setup the i2c bus */
      i2c_acquire(dev->i2c_dev);
      int result = i2c_init_master(dev->i2c_dev, I2C_SPEED_NORMAL);
      i2c_release(dev->i2c_dev);
  
      if (result != 0) {
          return result;
      }
  
      result = si70xx_test(dev);
      if (result < 0) {
          return result;
      }
  
      /* initialize the peripheral */
      i2c_acquire(dev->i2c_dev);
      i2c_write_byte(dev->i2c_dev, dev->address, SI70XX_RESET);
      i2c_release(dev->i2c_dev);
  
      /* sensor is ready after at most 25 ms */
      xtimer_usleep(25 * US_PER_MS);
  
      return 0;
  }
  
  uint16_t si70xx_get_relative_humidity(const si70xx_t *dev)
  {
      uint32_t raw;
      int32_t humidity;
  
      /* perform measurement */
      raw = si70xx_measure(dev, SI70XX_MEASURE_RH_HOLD);
  
      humidity = ((12500 * raw) / 65536) - 600;
  
      /* according to datasheet, values may exceed bounds, but can be clipped */
      if (humidity < 0) {
          return 0;
      }
      else if (humidity > 10000) {
          return 10000;
      }
      else {
          return (uint16_t) humidity;
      }
  }
  
  int16_t si70xx_get_temperature(const si70xx_t *dev)
  {
      uint32_t raw;
  
      /* perform measurement */
      raw = si70xx_measure(dev, SI70XX_MEASURE_TEMP_HOLD);
  
      return ((17572 * raw) / 65536) - 4685;
  }
  
  void si70xx_get_both(const si70xx_t *dev, uint16_t *humidity, int16_t *temperature)
  {
      uint32_t raw;
  
      /* read the humidity the normal way */
      *humidity = si70xx_get_relative_humidity(dev);
  
      /* read the temperature using the data from the previous measurement */
      raw = si70xx_measure(dev, SI70XX_MEASURE_TEMP_PREV);
  
      *temperature = ((17572 * raw) / 65536) - 4685;
  }
  
  uint64_t si70xx_get_serial(const si70xx_t *dev)
  {
      uint8_t out[2];
      uint8_t in_first[8] = { 0 };
      uint8_t in_second[8] = { 0 };
  
      /* read the lower bytes */
      out[0] = SI70XX_READ_ID_FIRST_A;
      out[1] = SI70XX_READ_ID_FIRST_B;
  
      i2c_acquire(dev->i2c_dev);
      i2c_write_bytes(dev->i2c_dev, dev->address, out, 2);
      i2c_read_bytes(dev->i2c_dev, dev->address, in_first, 8);
  
      /* read the higher bytes */
      out[0] = SI70XX_READ_ID_SECOND_A;
      out[1] = SI70XX_READ_ID_SECOND_B;
  
      i2c_write_bytes(dev->i2c_dev, dev->address, out, 2);
      i2c_read_bytes(dev->i2c_dev, dev->address, in_second, 8);
      i2c_release(dev->i2c_dev);
  
      /* calculate the ID */
      uint32_t id_first = ((uint32_t)in_first[0] << 24) + ((uint32_t)in_first[2] << 16) +
                          (in_first[4] << 8) + (in_first[6] << 0);
      uint32_t id_second = ((uint32_t)in_second[0] << 24) + ((uint32_t)in_second[2] << 16) +
                           (in_second[4] << 8) + (in_second[6] << 0);
  
      return (((uint64_t) id_first) << 32) + id_second;
  }
  
  uint8_t si70xx_get_id(const si70xx_t *dev)
  {
      return (si70xx_get_serial(dev) >> 24) & 0xff;
  }
  
  uint8_t si70xx_get_revision(const si70xx_t *dev)
  {
      uint8_t out[2];
      uint8_t in = 0;
  
      /* read the revision number */
      out[0] = SI70XX_READ_REVISION_A;
      out[1] = SI70XX_READ_REVISION_B;
  
      i2c_acquire(dev->i2c_dev);
      i2c_write_bytes(dev->i2c_dev, dev->address, out, 2);
      i2c_read_byte(dev->i2c_dev, dev->address, &in);
      i2c_release(dev->i2c_dev);
  
      return in;
  }