Skip to content

Commit 39ef0eb

Browse files
authored
Merge pull request #1 from adafruit/init_lib
library, examples and docs
2 parents 8c6e554 + 5bc26a6 commit 39ef0eb

File tree

7 files changed

+437
-16
lines changed

7 files changed

+437
-16
lines changed

adafruit_lps28.py

Lines changed: 302 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
21
# SPDX-FileCopyrightText: Copyright (c) 2025 Liz Clark for Adafruit Industries
32
#
43
# SPDX-License-Identifier: MIT
@@ -16,22 +15,316 @@
1615
1716
**Hardware:**
1817
19-
.. todo:: Add links to any specific hardware product page(s), or category page(s).
20-
Use unordered list & hyperlink rST inline format: "* `Link Text <url>`_"
18+
* `Adafruit LPS28 Pressure Sensor <https://www.adafruit.com/product/6067>`_"
2119
2220
**Software and Dependencies:**
2321
2422
* Adafruit CircuitPython firmware for the supported boards:
2523
https://circuitpython.org/downloads
2624
27-
.. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies
28-
based on the library's use of either.
29-
30-
# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
31-
# * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
25+
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
26+
* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
3227
"""
3328

34-
# imports
29+
import time
30+
31+
from adafruit_bus_device.i2c_device import I2CDevice
32+
from adafruit_register.i2c_bits import RWBits
33+
from adafruit_register.i2c_struct import ROUnaryStruct, UnaryStruct
34+
from adafruit_register.i2c_struct_array import StructArray
35+
from micropython import const
36+
37+
try:
38+
import typing
39+
40+
from busio import I2C
41+
except ImportError:
42+
pass
3543

3644
__version__ = "0.0.0+auto.0"
3745
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LPS28.git"
46+
47+
_DEFAULT_ADDR = const(0x5C)
48+
_WHOAMI = const(0x0F)
49+
_WHOAMI_VAL = const(0xB4)
50+
_THS_P = const(0x0C)
51+
_CTRL_REG1 = const(0x10)
52+
_CTRL_REG2 = const(0x11)
53+
_CTRL_REG3 = const(0x12)
54+
_CTRL_REG4 = const(0x13)
55+
_IF_CTRL = const(0x0E)
56+
_INTERRUPT_CFG = const(0x0B)
57+
_FIFO_CTRL = const(0x14)
58+
_FIFO_WTM = const(0x15)
59+
_REF_P = const(0x16)
60+
_RPDS = const(0x18)
61+
_INT_SOURCE = const(0x24)
62+
_FIFO_STATUS1 = const(0x25)
63+
_FIFO_STATUS2 = const(0x26)
64+
_STATUS = const(0x27)
65+
_PRESS_OUT = const(0x28)
66+
_TEMP_OUT = const(0x2B)
67+
_FIFO_DATA_OUT_PRESS_XL = const(0x78)
68+
_FIFO_STATUS_WTM_IA = const(0x80)
69+
_FIFO_STATUS_OVR_IA = const(0x40)
70+
_FIFO_STATUS_FULL_IA = const(0x20)
71+
_STATUS_TEMP_OVERRUN = const(0x20)
72+
_STATUS_PRESS_OVERRUN = const(0x10)
73+
_STATUS_TEMP_READY = const(0x02)
74+
_STATUS_PRESS_READY = const(0x01)
75+
76+
_ODR_MAP = {
77+
0: const(0b0000),
78+
1: const(0b0001),
79+
4: const(0b0010),
80+
10: const(0b0011),
81+
25: const(0b0100),
82+
50: const(0b0101),
83+
75: const(0b0110),
84+
100: const(0b0111),
85+
200: const(0b1000),
86+
}
87+
88+
_AVG_MAP = {
89+
4: const(0b000),
90+
8: const(0b001),
91+
16: const(0b010),
92+
32: const(0b011),
93+
64: const(0b100),
94+
128: const(0b101),
95+
512: const(0b111),
96+
}
97+
98+
_FIFO_MODE_MAP = {
99+
"BYPASS": const(0b000),
100+
"FIFO": const(0b001),
101+
"CONTINUOUS": const(0b010),
102+
"CONTINUOUS_TO_FIFO": const(0b011),
103+
"BYPASS_TO_CONTINUOUS": const(0b100),
104+
"CONTINUOUS_TO_BYPASS": const(0b111),
105+
}
106+
107+
108+
class LPS28:
109+
"""Driver for the LPS28 pressure sensor."""
110+
111+
_ctrl_reg1 = UnaryStruct(_CTRL_REG1, "B")
112+
_ctrl_reg1_ro = ROUnaryStruct(_CTRL_REG1, "B")
113+
_chip_id = ROUnaryStruct(_WHOAMI, "B")
114+
_raw_pressure_xlsb = ROUnaryStruct(_PRESS_OUT, "B")
115+
_raw_pressure_lsb = ROUnaryStruct(_PRESS_OUT + 1, "B")
116+
_raw_pressure_msb = ROUnaryStruct(_PRESS_OUT + 2, "B")
117+
_raw_temperature = ROUnaryStruct(_TEMP_OUT, "<h")
118+
_boot = RWBits(1, _CTRL_REG2, 7)
119+
_sw_reset = RWBits(1, _CTRL_REG2, 2)
120+
_raw_fifo_pressure = ROUnaryStruct(_FIFO_DATA_OUT_PRESS_XL, ">I")
121+
_data_rate = RWBits(4, _CTRL_REG1, 3)
122+
_averaging = RWBits(3, _CTRL_REG1, 0)
123+
_int_polarity = RWBits(1, _CTRL_REG3, 3)
124+
_int_open_drain = RWBits(1, _CTRL_REG3, 1)
125+
trigger_one_shot = RWBits(1, _CTRL_REG2, 0)
126+
"""Start a one-shot pressure measurement"""
127+
threshold_pressure = UnaryStruct(_THS_P, ">H")
128+
"""Pressure threshold for interrupt generation (16-bit value)"""
129+
full_scale_mode = RWBits(1, _CTRL_REG2, 6)
130+
"""Enable full-scale mode (False = 1260 hPa, True = 4060 hPa)"""
131+
lpf_odr9 = RWBits(1, _CTRL_REG2, 5)
132+
"""Enable low-pass filter with ODR/9 cutoff"""
133+
sda_pullup = RWBits(1, _IF_CTRL, 4)
134+
"""Enable I2C SDA pull-up"""
135+
int_pulldown_disable = RWBits(1, _IF_CTRL, 2)
136+
"""Disable interrupt pin internal pull-down"""
137+
auto_ref_pressure = RWBits(1, _INTERRUPT_CFG, 7)
138+
"""Enable automatic reference pressure update"""
139+
reset_arp = RWBits(1, _INTERRUPT_CFG, 6)
140+
"""Reset automatic reference pressure"""
141+
auto_zero = RWBits(1, _INTERRUPT_CFG, 5)
142+
"""Enable auto-zeroing of pressure readings"""
143+
reset_auto_zero = RWBits(1, _INTERRUPT_CFG, 4)
144+
"""Reset auto-zeroing function"""
145+
pressure_high = RWBits(1, _INTERRUPT_CFG, 1)
146+
"""Enable high-pressure threshold interrupt"""
147+
pressure_low = RWBits(1, _INTERRUPT_CFG, 2)
148+
"""Enable low-pressure threshold interrupt"""
149+
latch_interrupt = RWBits(1, _INTERRUPT_CFG, 3)
150+
"""Enable latching of interrupt events"""
151+
int_source = ROUnaryStruct(_INT_SOURCE, "B")
152+
"""Interrupt source flags"""
153+
fifo_unread_samples = ROUnaryStruct(_FIFO_STATUS1, "B")
154+
"""Number of unread FIFO samples (0-127)"""
155+
status = ROUnaryStruct(_STATUS, "B")
156+
"""Sensor status flags (pressure/temp ready, overruns)"""
157+
fifo_status = ROUnaryStruct(_FIFO_STATUS2, "B")
158+
"""FIFO status flags (full, watermark, overrun)"""
159+
fifo_stop_on_watermark = RWBits(1, _FIFO_CTRL, 3)
160+
"""Stop FIFO when watermark level is reached"""
161+
_fifo_mode = RWBits(3, _FIFO_CTRL, 0)
162+
fifo_watermark = UnaryStruct(_FIFO_WTM, "B")
163+
"""FIFO watermark threshold (0-127 samples)"""
164+
reference_pressure = UnaryStruct(_REF_P, ">h")
165+
"""Reference pressure value (16-bit)"""
166+
pressure_offset = UnaryStruct(_RPDS, ">h")
167+
"""Pressure offset adjustment (16-bit)"""
168+
data_ready_pulse = RWBits(1, _CTRL_REG4, 6)
169+
"""Data-ready interrupt as a pulse"""
170+
data_ready_int = RWBits(1, _CTRL_REG4, 5)
171+
"""Enable data-ready interrupt"""
172+
pressure_threshold_int = RWBits(1, _CTRL_REG4, 4)
173+
"""Enable pressure threshold interrupt"""
174+
fifo_full_int = RWBits(1, _CTRL_REG4, 2)
175+
"""Enable FIFO full interrupt"""
176+
fifo_watermark_int = RWBits(1, _CTRL_REG4, 1)
177+
"""Enable FIFO watermark interrupt"""
178+
fifo_overrun_int = RWBits(1, _CTRL_REG4, 0)
179+
"""Enable FIFO overrun interrupt"""
180+
181+
def __init__(self, i2c_bus: I2C, address: int = _DEFAULT_ADDR) -> None:
182+
"""Initialize the LPS28 sensor.
183+
184+
:param i2c_bus: The I2C bus instance.
185+
:param address: The I2C address of the sensor (default: 0x5C).
186+
"""
187+
self.i2c_device = I2CDevice(i2c_bus, address)
188+
189+
if self._chip_id != _WHOAMI_VAL:
190+
raise RuntimeError("Failed to find LPS28")
191+
self.reset()
192+
self.data_rate = 200
193+
self.averaging = 4
194+
self.full_scale_mode = True
195+
self.interrupt_pin(True, False)
196+
self.drdy_pulse = True
197+
198+
@property
199+
def pressure(self) -> float:
200+
"""Pressure in hPa."""
201+
raw = self._raw_pressure_msb << 16 | self._raw_pressure_lsb << 8 | self._raw_pressure_xlsb
202+
divisor = 2048.0 if self.full_scale_mode else 4096.0
203+
return raw / divisor
204+
205+
@property
206+
def temperature(self) -> float:
207+
"""Temperature in °C."""
208+
return self._raw_temperature / 100
209+
210+
def reset(self) -> None:
211+
"""Perform a software reset of the sensor."""
212+
self._sw_reset = True
213+
214+
def reboot_memory(self) -> None:
215+
"""Reboot the memory content of the sensor."""
216+
self._boot = True
217+
218+
@property
219+
def data_rate(self) -> int:
220+
"""Output data rate in Hz.
221+
222+
:param value: Desired data rate in Hz.
223+
:raises ValueError: If the provided value is not a valid data rate.
224+
"""
225+
raw_value = self._data_rate
226+
for rate, bits in _ODR_MAP.items():
227+
if bits == raw_value:
228+
return rate
229+
return raw_value
230+
231+
@data_rate.setter
232+
def data_rate(self, value: int) -> None:
233+
if value not in _ODR_MAP:
234+
raise ValueError(f"Invalid data rate. Must be one of: {sorted(_ODR_MAP.keys())}")
235+
self._data_rate = _ODR_MAP[value]
236+
237+
@property
238+
def averaging(self) -> int:
239+
"""Number of pressure and temperature samples to average
240+
241+
:param value: Desired averaging factor.
242+
:raises ValueError: If the provided value is not a valid averaging setting.
243+
"""
244+
raw_value = self._averaging
245+
for rate, bits in _AVG_MAP.items():
246+
if bits == raw_value:
247+
return rate
248+
return raw_value
249+
250+
@averaging.setter
251+
def averaging(self, value: int) -> None:
252+
if isinstance(value, int):
253+
if value not in _AVG_MAP:
254+
raise ValueError(
255+
f"Invalid output data rate. Must be one of: {sorted(_AVG_MAP.keys())}"
256+
)
257+
self._averaging = _AVG_MAP[value]
258+
else:
259+
self._averaging = value
260+
261+
def interrupt_pin(self, polarity: bool, open_drain: bool) -> None:
262+
"""Configure the interrupt pin settings.
263+
264+
:param polarity: True for active-high, False for active-low.
265+
:param open_drain: True for open-drain output, False for push-pull.
266+
"""
267+
self._int_polarity = polarity
268+
self._int_open_drain = open_drain
269+
270+
@property
271+
def data_ready(self) -> bool:
272+
"""Check if data is ready to be read.
273+
274+
Returns:
275+
bool: True if data is ready
276+
"""
277+
return bool(self.status & _STATUS_PRESS_READY)
278+
279+
@property
280+
def fifo_mode(self) -> str:
281+
"""FIFO mode
282+
283+
Available modes:
284+
'BYPASS', 'FIFO', 'CONTINUOUS',
285+
'CONTINUOUS_TO_FIFO', 'BYPASS_TO_CONTINUOUS',
286+
'CONTINUOUS_TO_BYPASS'
287+
288+
:return: The current FIFO mode as a string.
289+
:raises ValueError: If an invalid FIFO mode is given.
290+
"""
291+
raw_value = self._fifo_mode
292+
for mode, bits in _FIFO_MODE_MAP.items():
293+
if bits == raw_value:
294+
return mode
295+
return raw_value
296+
297+
@fifo_mode.setter
298+
def fifo_mode(self, value: str) -> None:
299+
if isinstance(value, str):
300+
if value not in _FIFO_MODE_MAP:
301+
raise ValueError(
302+
f"Invalid FIFO mode. Must be one of: {sorted(_FIFO_MODE_MAP.keys())}"
303+
)
304+
value = _FIFO_MODE_MAP[value]
305+
self._fifo_mode = 0b000
306+
time.sleep(0.01)
307+
self._fifo_mode = value
308+
309+
@property
310+
def fifo_ready(self) -> bool:
311+
"""Check if FIFO watermark level is reached.
312+
313+
:return: True if FIFO watermark level is reached, False otherwise.
314+
"""
315+
return bool(self.fifo_status & _FIFO_STATUS_WTM_IA)
316+
317+
@property
318+
def fifo_pressure(self) -> float:
319+
"""Reads and removes the next FIFO pressure sample in hPa.
320+
321+
:return: Pressure value in hPa.
322+
"""
323+
buffer = bytearray(3)
324+
with self.i2c_device as i2c:
325+
i2c.write_then_readinto(bytes([_FIFO_DATA_OUT_PRESS_XL]), buffer)
326+
raw = (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]
327+
if raw & 0x800000:
328+
raw -= 0x1000000
329+
divisor = 2048.0 if self.full_scale_mode else 4096.0
330+
return raw / divisor

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
# Uncomment the below if you use native CircuitPython modules such as
2626
# digitalio, micropython and busio. List the modules you use. Without it, the
2727
# autodoc module docs will fail to generate with a warning.
28-
# autodoc_mock_imports = ["digitalio", "busio"]
28+
autodoc_mock_imports = ["digitalio", "busio", "adafruit_register"]
2929

3030
autodoc_preserve_defaults = True
3131

docs/examples.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,21 @@ Ensure your device works with this simple test.
66
.. literalinclude:: ../examples/lps28_simpletest.py
77
:caption: examples/lps28_simpletest.py
88
:linenos:
9+
10+
One Shot test
11+
--------------
12+
13+
Test for One Shot Reading
14+
15+
.. literalinclude:: ../examples/lps28_oneshot.py
16+
:caption: examples/lps28_oneshot.py
17+
:linenos:
18+
19+
FIFO Mode test
20+
---------------
21+
22+
Test for FIFO Mode
23+
24+
.. literalinclude:: ../examples/lps28_fifo.py
25+
:caption: examples/lps28_fifo.py
26+
:linenos:

docs/index.rst

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,12 @@ Table of Contents
2424
.. toctree::
2525
:caption: Tutorials
2626

27-
.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave
28-
the toctree above for use later.
27+
Adafruit LPS28 Pressure Sensor Learn Guide <https://learn.adafruit.com/adafruit-lps28-pressure-sensor>
2928

3029
.. toctree::
3130
:caption: Related Products
3231

33-
.. todo:: Add any product links here. If there are none, then simply delete this todo and leave
34-
the toctree above for use later.
32+
Adafruit LPS28 (LPS28DFW) Pressure Sensor - STEMMA QT / Qwiic <https://www.adafruit.com/product/6067>
3533

3634
.. toctree::
3735
:caption: Other Links

0 commit comments

Comments
 (0)