From ef9b49143be70817bfbfa8fd3a726e3379a94906 Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Thu, 7 Dec 2023 09:46:09 +0200 Subject: [PATCH 01/10] Move lps25h into a dedicated folder --- components/src/environment/{ => lps25h}/lps25h-i2c.adb | 0 components/src/environment/{ => lps25h}/lps25h-i2c.ads | 0 components/src/environment/{ => lps25h}/lps25h-spi.adb | 0 components/src/environment/{ => lps25h}/lps25h-spi.ads | 0 components/src/environment/{ => lps25h}/lps25h.ads | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename components/src/environment/{ => lps25h}/lps25h-i2c.adb (100%) rename components/src/environment/{ => lps25h}/lps25h-i2c.ads (100%) rename components/src/environment/{ => lps25h}/lps25h-spi.adb (100%) rename components/src/environment/{ => lps25h}/lps25h-spi.ads (100%) rename components/src/environment/{ => lps25h}/lps25h.ads (100%) diff --git a/components/src/environment/lps25h-i2c.adb b/components/src/environment/lps25h/lps25h-i2c.adb similarity index 100% rename from components/src/environment/lps25h-i2c.adb rename to components/src/environment/lps25h/lps25h-i2c.adb diff --git a/components/src/environment/lps25h-i2c.ads b/components/src/environment/lps25h/lps25h-i2c.ads similarity index 100% rename from components/src/environment/lps25h-i2c.ads rename to components/src/environment/lps25h/lps25h-i2c.ads diff --git a/components/src/environment/lps25h-spi.adb b/components/src/environment/lps25h/lps25h-spi.adb similarity index 100% rename from components/src/environment/lps25h-spi.adb rename to components/src/environment/lps25h/lps25h-spi.adb diff --git a/components/src/environment/lps25h-spi.ads b/components/src/environment/lps25h/lps25h-spi.ads similarity index 100% rename from components/src/environment/lps25h-spi.ads rename to components/src/environment/lps25h/lps25h-spi.ads diff --git a/components/src/environment/lps25h.ads b/components/src/environment/lps25h/lps25h.ads similarity index 100% rename from components/src/environment/lps25h.ads rename to components/src/environment/lps25h/lps25h.ads From 906d43d794d5a7613cc55df54f33fb8ff66ef086 Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Thu, 7 Dec 2023 16:23:13 +0200 Subject: [PATCH 02/10] Add draft of BME280 driver --- .../src/environment/bme280/bme280-i2c.adb | 80 +++++++ .../src/environment/bme280/bme280-i2c.ads | 49 ++++ components/src/environment/bme280/bme280.adb | 216 ++++++++++++++++++ components/src/environment/bme280/bme280.ads | 117 ++++++++++ examples/stm32_f4ve/bme280/README.md | 7 + examples/stm32_f4ve/bme280/bme280.gpr | 20 ++ examples/stm32_f4ve/bme280/main.adb | 100 ++++++++ scripts/build_all_examples.py | 1 + 8 files changed, 590 insertions(+) create mode 100644 components/src/environment/bme280/bme280-i2c.adb create mode 100644 components/src/environment/bme280/bme280-i2c.ads create mode 100644 components/src/environment/bme280/bme280.adb create mode 100644 components/src/environment/bme280/bme280.ads create mode 100644 examples/stm32_f4ve/bme280/README.md create mode 100644 examples/stm32_f4ve/bme280/bme280.gpr create mode 100644 examples/stm32_f4ve/bme280/main.adb diff --git a/components/src/environment/bme280/bme280-i2c.adb b/components/src/environment/bme280/bme280-i2c.adb new file mode 100644 index 000000000..85d4b525e --- /dev/null +++ b/components/src/environment/bme280/bme280-i2c.adb @@ -0,0 +1,80 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +package body BME280.I2C is + + ---------- + -- Read -- + ---------- + + procedure Read + (Data : out HAL.UInt8_Array; + Success : out Boolean) + is + use type HAL.I2C.I2C_Status; + use type HAL.UInt10; + + Status : HAL.I2C.I2C_Status; + begin + I2C_Port.Mem_Read + (Addr => 2 * HAL.UInt10 (I2C_Address), + Mem_Addr => HAL.UInt16 (Data'First), + Mem_Addr_Size => HAL.I2C.Memory_Size_8b, + Data => Data, + Status => Status); + + Success := Status = HAL.I2C.Ok; + end Read; + + ----------- + -- Write -- + ----------- + + procedure Write + (Data : HAL.UInt8_Array; + Success : out Boolean) + is + use type HAL.I2C.I2C_Status; + use type HAL.UInt10; + + Status : HAL.I2C.I2C_Status; + begin + I2C_Port.Mem_Write + (Addr => 2 * HAL.UInt10 (I2C_Address), + Mem_Addr => HAL.UInt16 (Data'First), + Mem_Addr_Size => HAL.I2C.Memory_Size_8b, + Data => Data, + Status => Status); + + Success := Status = HAL.I2C.Ok; + end Write; + +end BME280.I2C; diff --git a/components/src/environment/bme280/bme280-i2c.ads b/components/src/environment/bme280/bme280-i2c.ads new file mode 100644 index 000000000..caa4a3d19 --- /dev/null +++ b/components/src/environment/bme280/bme280-i2c.ads @@ -0,0 +1,49 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with HAL.I2C; + +generic + I2C_Port : not null HAL.I2C.Any_I2C_Port; + I2C_Address : HAL.UInt7; +package BME280.I2C is + + procedure Read + (Data : out HAL.UInt8_Array; + Success : out Boolean); + + procedure Write + (Data : HAL.UInt8_Array; + Success : out Boolean); + + package Sensor is new Generic_Sensor (Read, Write); + +end BME280.I2C; diff --git a/components/src/environment/bme280/bme280.adb b/components/src/environment/bme280/bme280.adb new file mode 100644 index 000000000..77087375e --- /dev/null +++ b/components/src/environment/bme280/bme280.adb @@ -0,0 +1,216 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +pragma Ada_2022; + +package body BME280 is + + package body Generic_Sensor is + + ------------------- + -- Check_Chip_Id -- + ------------------- + + function Check_Chip_Id (Expect : HAL.UInt8 := 16#60#) return Boolean is + use type HAL.UInt8_Array; + + Ok : Boolean; + Data : HAL.UInt8_Array (16#D0# .. 16#D0#); + begin + Read (Data, Ok); + + return Ok and Data = [Expect]; + end Check_Chip_Id; + + --------------- + -- Configure -- + --------------- + + procedure Configure + (Standby : Standby_Duration := 1000.0; + Filter : IRR_Filter_Kind := Off; + SPI_3_Wire : Boolean := False; + Success : out Boolean) + is + use type HAL.UInt8; + Data : HAL.UInt8; + begin + if Standby = 0.5 then + Data := 0; + elsif Standby = 20.0 then + Data := 7; + elsif Standby = 10.0 then + Data := 6; + else + Data := 5; + declare + Value : Standby_Duration := 1000.0; + begin + while Value > Standby loop + Value := Value / 2; + Data := Data - 1; + end loop; + end; + end if; + Data := Data * 8 + IRR_Filter_Kind'Pos (Filter); + Data := Data * 4 + Boolean'Pos (SPI_3_Wire); + + Write ([16#F5# => Data], Success); + end Configure; + + --------------- + -- Measuring -- + --------------- + + function Measuring return Boolean is + use type HAL.UInt8; + + Ok : Boolean; + Data : HAL.UInt8_Array (16#F3# .. 16#F3#); + begin + Read (Data, Ok); + + return Ok and (Data (Data'First) and 8) /= 0; + end Measuring; + + ---------------------- + -- Read_Calibration -- + ---------------------- + + procedure Read_Calibration + (Value : out Calibration_Constants; + Success : out Boolean) + is + use Interfaces; + begin + declare + Data : HAL.UInt8_Array (16#88# .. 16#A1#); + begin + Read (Data, Success); + + if not Success then + return; + end if; + + Value.T1 := Unsigned_16 (Data (16#88#)) + + Shift_Left (Unsigned_16 (Data (16#89#)), 8); + + Value.T2 := Unsigned_16 (Data (16#8A#)) + + Shift_Left (Unsigned_16 (Data (16#8B#)), 8); + + Value.T3 := Unsigned_16 (Data (16#8C#)) + + Shift_Left (Unsigned_16 (Data (16#8D#)), 8); + end; + end Read_Calibration; + + ---------------------- + -- Read_Measurement -- + ---------------------- + + procedure Read_Measurement + (Value : out Measurement; + Success : out Boolean) + is + use Interfaces; + Data : HAL.UInt8_Array (16#F7# .. 16#FE#); + begin + Read (Data, Success); + + if Success then + Value.Raw_Press := HAL.UInt20 + (Shift_Left (Unsigned_32 (Data (16#F7#)), 12) + + Shift_Left (Unsigned_32 (Data (16#F8#)), 4) + + Shift_Right (Unsigned_32 (Data (16#F9#)), 4)); + + Value.Raw_Temp := HAL.UInt20 + (Shift_Left (Unsigned_32 (Data (16#FA#)), 12) + + Shift_Left (Unsigned_32 (Data (16#FB#)), 4) + + Shift_Right (Unsigned_32 (Data (16#FC#)), 4)); + + Value.Raw_Hum := HAL.UInt16 + (Shift_Left (Unsigned_16 (Data (16#FD#)), 8) + + Unsigned_16 (Data (16#FE#))); + end if; + end Read_Measurement; + + ----------- + -- Reset -- + ----------- + + procedure Reset (Success : out Boolean) is + begin + Write ([16#E0# => 16#B6#], Success); + end Reset; + + ----------- + -- Start -- + ----------- + + procedure Start + (Mode : Sensor_Mode := Normal; + Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1; + Success : out Boolean) + is + use type HAL.UInt8; + Data : HAL.UInt8; + begin + Write ([16#F2# => Oversampling_Kind'Pos (Humidity)], Success); + + if Success then + Data := Oversampling_Kind'Pos (Temperature); + Data := Data * 8 + Oversampling_Kind'Pos (Pressure); + Data := Data * 4 + Sensor_Mode'Enum_Rep (Mode); + Write ([16#F4# => Data], Success); + end if; + end Start; + + end Generic_Sensor; + + function Temperature + (Value : Measurement; + Calibration : Calibration_Constants) return Deci_Celsius + is + Diff : constant Integer := + Integer (Value.Raw_Temp) / 8 - 2 * Integer (Calibration.T1); + + Val_1 : constant Integer := + (Diff * Integer (Calibration.T2)) / 2 ** 11; + + Val_2 : constant Integer := + (Diff / 2) ** 2 / 2 ** 11 * Integer (Calibration.T3) / 2 ** 14; + + begin + return Deci_Celsius'Small * (Val_1 + Val_2); + end Temperature; + +end BME280; diff --git a/components/src/environment/bme280/bme280.ads b/components/src/environment/bme280/bme280.ads new file mode 100644 index 000000000..4d106cdf2 --- /dev/null +++ b/components/src/environment/bme280/bme280.ads @@ -0,0 +1,117 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with Interfaces; +with HAL; + +package BME280 is + pragma Pure; + pragma Discard_Names; + + type Calibration_Constants is record + T1 : Interfaces.Unsigned_16; + T2 : Interfaces.Unsigned_16; + T3 : Interfaces.Unsigned_16; + end record; + + type Measurement is private; + + type Deci_Celsius is delta 1.0 / 2 ** 9 range -99_0.00 .. 99_0.00; + -- 1 degree celsius is 10 Deci_Celsius + + function Temperature + (Value : Measurement; + Calibration : Calibration_Constants) return Deci_Celsius; + + type Oversampling_Kind is (Skip, X1, X2, X4, X8, X16); + type IRR_Filter_Kind is (Off, X1, X2, X4, X8, X16); + type Sensor_Mode is (Sleep, Forced, Normal); + + type Standby_Duration is delta 0.5 range 0.5 .. 1000.0 + with Static_Predicate => + Standby_Duration in 0.5 | 10.0 | 20.0 + | 62.5 | 125.0 | 250.0 | 500.0 | 1000.0; + -- Inactive duration in ms + + subtype Register_Address is Natural range 16#80# .. 16#FF#; + + generic + with procedure Read + (Data : out HAL.UInt8_Array; + Success : out Boolean); + -- Data'Range should be Register_Address + + with procedure Write + (Data : HAL.UInt8_Array; + Success : out Boolean); + -- Data'Range should be Register_Address + + package Generic_Sensor is + function Check_Chip_Id (Expect : HAL.UInt8 := 16#60#) return Boolean; + + procedure Reset (Success : out Boolean); + + procedure Configure + (Standby : Standby_Duration := 1000.0; + Filter : IRR_Filter_Kind := Off; + SPI_3_Wire : Boolean := False; + Success : out Boolean); + + procedure Start + (Mode : Sensor_Mode := Normal; + Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1; + Success : out Boolean); + + function Measuring return Boolean; + + procedure Read_Measurement + (Value : out Measurement; + Success : out Boolean); + + procedure Read_Calibration + (Value : out Calibration_Constants; + Success : out Boolean); + + end Generic_Sensor; + +private + + for Sensor_Mode use (Sleep => 0, Forced => 1, Normal => 3); + + type Measurement is record + Raw_Press : HAL.Uint20; + Raw_Temp : HAL.Uint20; + Raw_Hum : HAL.Uint16; + end record; + +end BME280; diff --git a/examples/stm32_f4ve/bme280/README.md b/examples/stm32_f4ve/bme280/README.md new file mode 100644 index 000000000..35b92b62b --- /dev/null +++ b/examples/stm32_f4ve/bme280/README.md @@ -0,0 +1,7 @@ +# BME280 demo + +This example demonstrates working with BME280 humidity, pressure +and temperature sensor. + +For this demo, you need an LCD, which is usually sold as +part of the STM32 F4VE board kit. diff --git a/examples/stm32_f4ve/bme280/bme280.gpr b/examples/stm32_f4ve/bme280/bme280.gpr new file mode 100644 index 000000000..73186f553 --- /dev/null +++ b/examples/stm32_f4ve/bme280/bme280.gpr @@ -0,0 +1,20 @@ +with "../../../boards/stm32_f4ve/stm32_f4ve_full.gpr"; + +project BME280 is + + for Runtime ("Ada") use STM32_f4VE_Full'Runtime ("Ada"); + for Target use "arm-eabi"; + for Main use ("main.adb"); + for Languages use ("Ada"); + for Source_Dirs use (".", "../../shared/common/gui"); + for Object_Dir use "obj/"; + for Create_Missing_Dirs use "True"; + + package Compiler renames STM32_F4VE_Full.Compiler; + + package Ide is + for Program_Host use "localhost:4242"; + for Communication_Protocol use "remote"; + for Connection_Tool use "st-util"; + end Ide; +end BME280; diff --git a/examples/stm32_f4ve/bme280/main.adb b/examples/stm32_f4ve/bme280/main.adb new file mode 100644 index 000000000..83e21b341 --- /dev/null +++ b/examples/stm32_f4ve/bme280/main.adb @@ -0,0 +1,100 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with Ada.Real_Time; + +with HAL.Bitmap; + +with STM32.Board; +with Display_ILI9341; + +with BME280.I2C; +with STM32.Device; +with STM32.Setup; +with BMP_Fonts; +with Bitmapped_Drawing; +procedure Main is + use type Ada.Real_Time.Time; + + procedure Put_Line (Text : String) is + begin + STM32.Board.TFT_Bitmap.Set_Source (HAL.Bitmap.Black); + STM32.Board.TFT_Bitmap.Fill_Rect (Area => ((5, 5),100, 10)); + + Bitmapped_Drawing.Draw_String + (STM32.Board.TFT_Bitmap, + (5, 5), + Text, + BMP_Fonts.Font8x8, + HAL.Bitmap.Green, + HAL.Bitmap.Black); + end Put_Line; + + package BME280_I2C is new BME280.I2C + (I2C_Port => STM32.Device.I2C_1'Access, + I2C_Address => 16#76#); + + Next : Ada.Real_Time.Time := Ada.Real_Time.Clock; + + -- Bitmap : Display_ILI9341.Bitmap_Buffer renames STM32.Board.TFT_Bitmap; + + Ok : Boolean; + Calib : BME280.Calibration_Constants; + Measurement : BME280.Measurement; + Temp : BME280.Deci_Celsius; + +begin + STM32.Board.Initialize_LEDs; + STM32.Board.Display.Initialize; + STM32.Setup.Setup_I2C_Master + (Port => STM32.Device.I2C_1, + SDA => STM32.Device.PB9, + SCL => STM32.Device.PB8, + SDA_AF => STM32.Device.GPIO_AF_I2C1_4, + SCL_AF => STM32.Device.GPIO_AF_I2C1_4, + Clock_Speed => 400_000); + + BME280_I2C.Sensor.Read_Calibration (Calib, Ok); + BME280_I2C.Sensor.Start (Success => Ok); + + loop + STM32.Board.Toggle (STM32.Board.D1_LED); + BME280_I2C.Sensor.Read_Measurement (Measurement, Ok); + + if Ok then + Temp := BME280.Temperature (Measurement, Calib); + Put_Line (Temp'Image); + end if; + + Next := Next + Ada.Real_Time.Milliseconds (500); + delay until Next; + end loop; +end Main; diff --git a/scripts/build_all_examples.py b/scripts/build_all_examples.py index b566158b2..d2702f7eb 100755 --- a/scripts/build_all_examples.py +++ b/scripts/build_all_examples.py @@ -73,6 +73,7 @@ def gprbuild(project_file, debug=False): "/examples/stm32_f4ve/blinky_stm32_f4ve.gpr", "/examples/stm32_f4ve/draw/draw.gpr", "/examples/stm32_f4ve/lcd/lcd.gpr", + "/examples/stm32_f4ve/bme280/bme280.gpr", # STM32F4XX M "/boards/stm32f4xx_m/stm32f4xx_m_full.gpr", From cbe88c192202532766d0c3dd12319346c68bd15f Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Thu, 7 Dec 2023 20:25:06 +0200 Subject: [PATCH 03/10] Add humidity to BME280 sensor --- components/src/environment/bme280/bme280.adb | 69 +++++++++++++++++++- components/src/environment/bme280/bme280.ads | 15 +++++ examples/stm32_f4ve/bme280/main.adb | 4 +- 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/components/src/environment/bme280/bme280.adb b/components/src/environment/bme280/bme280.adb index 77087375e..e17345f79 100644 --- a/components/src/environment/bme280/bme280.adb +++ b/components/src/environment/bme280/bme280.adb @@ -128,7 +128,34 @@ package body BME280 is Value.T3 := Unsigned_16 (Data (16#8C#)) + Shift_Left (Unsigned_16 (Data (16#8D#)), 8); + + Value.H1 := Unsigned_8 (Data (16#A1#)); + end; + + declare + use type HAL.UInt8; + Data : HAL.UInt8_Array (16#E1# .. 16#E7#); + begin + Read (Data, Success); + + if not Success then + return; + end if; + + Value.H2 := Unsigned_16 (Data (16#E1#)) + + Shift_Left (Unsigned_16 (Data (16#E2#)), 8); + + Value.H3 := Unsigned_8 (Data (16#E3#)); + + Value.H4 := Shift_Left (Unsigned_16 (Data (16#E4#)), 4) + + Unsigned_16 (Data (16#E5#) and 16#0F#); + + Value.H5 := Shift_Right (Unsigned_16 (Data (16#E5#)), 4) + + Shift_Left (Unsigned_16 (Data (16#E6#)), 4); + + Value.H6 := Unsigned_8 (Data (16#E7#)); end; + end Read_Calibration; ---------------------- @@ -196,6 +223,46 @@ package body BME280 is end Generic_Sensor; + -------------- + -- Humidity -- + -------------- + + function Humidity + (Value : Measurement; + Temperature : Deci_Celsius; + Calibration : Calibration_Constants) return Relative_Humidity + is + Var_1 : constant Integer := + Integer (Temperature / Deci_Celsius'Small) - 76800; + Var_2 : Integer := Integer (Value.Raw_Hum) * 16384; + Var_3 : Integer := Integer (Calibration.H4) * 1048576; + Var_4 : Integer := Integer (Calibration.H5) * Var_1; + Var_5 : Integer := (Var_2 - Var_3 - Var_4 + 16384) / 32768; + + begin + -- var2 = (var1 * ((int32_t)calib_data->dig_h6)) / 1024; + Var_2 := Var_1 * Integer (Calibration.H6) / 1024; + -- var3 = (var1 * ((int32_t)calib_data->dig_h3)) / 2048; + Var_3 := Var_1 * Integer (Calibration.H3) / 2048; + -- var4 = ((var2 * (var3 + (int32_t)32768)) / 1024) + (int32_t)2097152; + Var_4 := Var_2 * (Var_3 + 32768) / 1024 + 2097152; + -- var2 = ((var4 * ((int32_t)calib_data->dig_h2)) + 8192) / 16384; + Var_2 := (Var_4 * Integer (Calibration.H2) + 8192) / 16384; + -- var3 = var5 * var2; + Var_3 := Var_5 * Var_2; + -- var4 = ((var3 / 32768) * (var3 / 32768)) / 128; + Var_4 := (Var_3 / 32768)**2 / 128; + -- var5 = var3 - ((var4 * ((int32_t)calib_data->dig_h1)) / 16); + Var_5 := Var_3 - Var_4 * Integer (Calibration.H1) / 16; + Var_5 := Integer'Max (0, Integer'Min (100 * 2 ** 10, Var_5 / 4096)); + + return Relative_Humidity'Small * Var_5; + end Humidity; + + ----------------- + -- Temperature -- + ----------------- + function Temperature (Value : Measurement; Calibration : Calibration_Constants) return Deci_Celsius @@ -207,7 +274,7 @@ package body BME280 is (Diff * Integer (Calibration.T2)) / 2 ** 11; Val_2 : constant Integer := - (Diff / 2) ** 2 / 2 ** 11 * Integer (Calibration.T3) / 2 ** 14; + (Diff / 2) ** 2 / 2 ** 12 * Integer (Calibration.T3) / 2 ** 14; begin return Deci_Celsius'Small * (Val_1 + Val_2); diff --git a/components/src/environment/bme280/bme280.ads b/components/src/environment/bme280/bme280.ads index 4d106cdf2..8927d313f 100644 --- a/components/src/environment/bme280/bme280.ads +++ b/components/src/environment/bme280/bme280.ads @@ -40,6 +40,12 @@ package BME280 is T1 : Interfaces.Unsigned_16; T2 : Interfaces.Unsigned_16; T3 : Interfaces.Unsigned_16; + H1 : Interfaces.Unsigned_8; + H2 : Interfaces.Unsigned_16; + H3 : Interfaces.Unsigned_8; + H4 : Interfaces.Unsigned_16 range 0 .. 4095; + H5 : Interfaces.Unsigned_16 range 0 .. 4095; + H6 : Interfaces.Unsigned_8; end record; type Measurement is private; @@ -51,6 +57,15 @@ package BME280 is (Value : Measurement; Calibration : Calibration_Constants) return Deci_Celsius; + Humidity_Small : constant := 1.0 / 2 ** 10; + + type Relative_Humidity is delta Humidity_Small range 0.0 .. 100.0; + + function Humidity + (Value : Measurement; + Temperature : Deci_Celsius; + Calibration : Calibration_Constants) return Relative_Humidity; + type Oversampling_Kind is (Skip, X1, X2, X4, X8, X16); type IRR_Filter_Kind is (Off, X1, X2, X4, X8, X16); type Sensor_Mode is (Sleep, Forced, Normal); diff --git a/examples/stm32_f4ve/bme280/main.adb b/examples/stm32_f4ve/bme280/main.adb index 83e21b341..df6ff3c01 100644 --- a/examples/stm32_f4ve/bme280/main.adb +++ b/examples/stm32_f4ve/bme280/main.adb @@ -70,6 +70,7 @@ procedure Main is Calib : BME280.Calibration_Constants; Measurement : BME280.Measurement; Temp : BME280.Deci_Celsius; + Humi : BME280.Relative_Humidity; begin STM32.Board.Initialize_LEDs; @@ -91,7 +92,8 @@ begin if Ok then Temp := BME280.Temperature (Measurement, Calib); - Put_Line (Temp'Image); + Humi := BME280.Humidity (Measurement, Temp, Calib); + Put_Line (Temp'Image & Humi'Image); end if; Next := Next + Ada.Real_Time.Milliseconds (500); From ed91289e5cf7a570f078c81c3615e3adaff670f5 Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Fri, 8 Dec 2023 22:26:11 +0200 Subject: [PATCH 04/10] Add pressure sensor for BME280 --- components/src/environment/bme280/bme280.adb | 84 +++++++++++++++++--- components/src/environment/bme280/bme280.ads | 31 ++++++-- examples/stm32_f4ve/bme280/main.adb | 10 ++- 3 files changed, 106 insertions(+), 19 deletions(-) diff --git a/components/src/environment/bme280/bme280.adb b/components/src/environment/bme280/bme280.adb index e17345f79..56e4d8380 100644 --- a/components/src/environment/bme280/bme280.adb +++ b/components/src/environment/bme280/bme280.adb @@ -31,6 +31,8 @@ pragma Ada_2022; +with Ada.Unchecked_Conversion; + package body BME280 is package body Generic_Sensor is @@ -110,6 +112,15 @@ package body BME280 is Success : out Boolean) is use Interfaces; + + function Cast is new Ada.Unchecked_Conversion + (Unsigned_16, Integer_16); + + function To_Unsigned (LSB, MSB : HAL.UInt8) return Unsigned_16 is + (Unsigned_16 (LSB) + Shift_Left (Unsigned_16 (MSB), 8)); + + function To_Integer (LSB, MSB : HAL.UInt8) return Integer_16 is + (Cast (To_Unsigned (LSB, MSB))); begin declare Data : HAL.UInt8_Array (16#88# .. 16#A1#); @@ -120,14 +131,19 @@ package body BME280 is return; end if; - Value.T1 := Unsigned_16 (Data (16#88#)) + - Shift_Left (Unsigned_16 (Data (16#89#)), 8); - - Value.T2 := Unsigned_16 (Data (16#8A#)) + - Shift_Left (Unsigned_16 (Data (16#8B#)), 8); + Value.T1 := To_Unsigned (Data (16#88#), Data (16#89#)); + Value.T2 := To_Integer (Data (16#8A#), Data (16#8B#)); + Value.T3 := To_Integer (Data (16#8C#), Data (16#8D#)); - Value.T3 := Unsigned_16 (Data (16#8C#)) + - Shift_Left (Unsigned_16 (Data (16#8D#)), 8); + Value.P1 := To_Unsigned (Data (16#8E#), Data (16#8F#)); + Value.P2 := To_Integer (Data (16#90#), Data (16#91#)); + Value.P3 := To_Integer (Data (16#92#), Data (16#93#)); + Value.P4 := To_Integer (Data (16#94#), Data (16#95#)); + Value.P5 := To_Integer (Data (16#96#), Data (16#97#)); + Value.P6 := To_Integer (Data (16#98#), Data (16#99#)); + Value.P7 := To_Integer (Data (16#9A#), Data (16#9B#)); + Value.P8 := To_Integer (Data (16#9C#), Data (16#9D#)); + Value.P9 := To_Integer (Data (16#9E#), Data (16#9F#)); Value.H1 := Unsigned_8 (Data (16#A1#)); end; @@ -142,9 +158,7 @@ package body BME280 is return; end if; - Value.H2 := Unsigned_16 (Data (16#E1#)) + - Shift_Left (Unsigned_16 (Data (16#E2#)), 8); - + Value.H2 := To_Integer (Data (16#E1#), Data (16#E2#)); Value.H3 := Unsigned_8 (Data (16#E3#)); Value.H4 := Shift_Left (Unsigned_16 (Data (16#E4#)), 4) + @@ -259,6 +273,56 @@ package body BME280 is return Relative_Humidity'Small * Var_5; end Humidity; + -------------- + -- Pressure -- + -------------- + + function Pressure + (Value : Measurement; + Temperature : Deci_Celsius; + Calibration : Calibration_Constants) return Pressure_Pa + is + use Interfaces; + + Var_1 : Integer_64 := + Integer_64 (Temperature / Deci_Celsius'Small) - 128000; + + Var_2 : Integer_64 := Var_1**2 * Integer_64 (Calibration.P6); + Var_3 : constant Integer_64 := 140737488355328; + Var_4 : Integer_64 := 1048576 - Integer_64 (Value.Raw_Press); + + Result : Pressure_Pa'Base; + begin + Var_2 := Var_2 + Var_1 * Integer_64 (Calibration.P5) * 131072; + Var_2 := Var_2 + Integer_64 (Calibration.P4) * 34359738368; + + Var_1 := Var_1 ** 2 * Integer_64 (Calibration.P3) / 256 + + Var_1 * Integer_64 (Calibration.P2) * 4096; + + Var_1 := (Var_3 + Var_1) * Integer_64 (Calibration.P1) / 8589934592; + + if Var_1 = 0 then + return Pressure_Pa'First; + end if; + + Var_4 := (Var_4 * 2147483648 - Var_2) * 3125 / Var_1; + Var_1 := Integer_64 (Calibration.P9) * (Var_4 / 8192)**2 / 33554432; + Var_2 := Integer_64 (Calibration.P8) * Var_4 / 524288; + + Var_4 := (Var_4 + Var_1 + Var_2) / 256 + + Integer_64 (Calibration.P7) * 16; + + Result := Pressure_Pa'Small * Integer (Var_4); + + if Result < Pressure_Pa'First then + return Pressure_Pa'First; + elsif Result > Pressure_Pa'Last then + return Pressure_Pa'Last; + else + return Result; + end if; + end Pressure; + ----------------- -- Temperature -- ----------------- diff --git a/components/src/environment/bme280/bme280.ads b/components/src/environment/bme280/bme280.ads index 8927d313f..2ab9b8f30 100644 --- a/components/src/environment/bme280/bme280.ads +++ b/components/src/environment/bme280/bme280.ads @@ -38,10 +38,19 @@ package BME280 is type Calibration_Constants is record T1 : Interfaces.Unsigned_16; - T2 : Interfaces.Unsigned_16; - T3 : Interfaces.Unsigned_16; + T2 : Interfaces.Integer_16; + T3 : Interfaces.Integer_16; + P1 : Interfaces.Unsigned_16; + P2 : Interfaces.Integer_16; + P3 : Interfaces.Integer_16; + P4 : Interfaces.Integer_16; + P5 : Interfaces.Integer_16; + P6 : Interfaces.Integer_16; + P7 : Interfaces.Integer_16; + P8 : Interfaces.Integer_16; + P9 : Interfaces.Integer_16; H1 : Interfaces.Unsigned_8; - H2 : Interfaces.Unsigned_16; + H2 : Interfaces.Integer_16; H3 : Interfaces.Unsigned_8; H4 : Interfaces.Unsigned_16 range 0 .. 4095; H5 : Interfaces.Unsigned_16 range 0 .. 4095; @@ -66,6 +75,16 @@ package BME280 is Temperature : Deci_Celsius; Calibration : Calibration_Constants) return Relative_Humidity; + Pressure_Small : constant := 1.0 / 2 ** 8; + + type Pressure_Pa is delta Pressure_Small range 30_000.0 .. 110_000.0; + -- Pressure in Pa + + function Pressure + (Value : Measurement; + Temperature : Deci_Celsius; + Calibration : Calibration_Constants) return Pressure_Pa; + type Oversampling_Kind is (Skip, X1, X2, X4, X8, X16); type IRR_Filter_Kind is (Off, X1, X2, X4, X8, X16); type Sensor_Mode is (Sleep, Forced, Normal); @@ -124,9 +143,9 @@ private for Sensor_Mode use (Sleep => 0, Forced => 1, Normal => 3); type Measurement is record - Raw_Press : HAL.Uint20; - Raw_Temp : HAL.Uint20; - Raw_Hum : HAL.Uint16; + Raw_Press : HAL.UInt20; + Raw_Temp : HAL.UInt20; + Raw_Hum : HAL.UInt16; end record; end BME280; diff --git a/examples/stm32_f4ve/bme280/main.adb b/examples/stm32_f4ve/bme280/main.adb index df6ff3c01..c4fc37914 100644 --- a/examples/stm32_f4ve/bme280/main.adb +++ b/examples/stm32_f4ve/bme280/main.adb @@ -44,10 +44,12 @@ with Bitmapped_Drawing; procedure Main is use type Ada.Real_Time.Time; + procedure Put_Line (Text : String); + procedure Put_Line (Text : String) is begin STM32.Board.TFT_Bitmap.Set_Source (HAL.Bitmap.Black); - STM32.Board.TFT_Bitmap.Fill_Rect (Area => ((5, 5),100, 10)); + STM32.Board.TFT_Bitmap.Fill_Rect (Area => ((5, 5), 100, 10)); Bitmapped_Drawing.Draw_String (STM32.Board.TFT_Bitmap, @@ -66,11 +68,12 @@ procedure Main is -- Bitmap : Display_ILI9341.Bitmap_Buffer renames STM32.Board.TFT_Bitmap; - Ok : Boolean; + Ok : Boolean; Calib : BME280.Calibration_Constants; Measurement : BME280.Measurement; Temp : BME280.Deci_Celsius; Humi : BME280.Relative_Humidity; + Press : BME280.Pressure_Pa; begin STM32.Board.Initialize_LEDs; @@ -93,7 +96,8 @@ begin if Ok then Temp := BME280.Temperature (Measurement, Calib); Humi := BME280.Humidity (Measurement, Temp, Calib); - Put_Line (Temp'Image & Humi'Image); + Press := BME280.Pressure (Measurement, Temp, Calib); + Put_Line (Temp'Image & Humi'Image & Press'Image); end if; Next := Next + Ada.Real_Time.Milliseconds (500); From 05a8f60e5b39320b171027c4e8eb0ef760db3ee4 Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Mon, 11 Dec 2023 18:14:50 +0200 Subject: [PATCH 05/10] Add XXX_Measurement_Time functions Add delay in the Reset procedure. Add more comments. --- .../src/environment/bme280/bme280-i2c.ads | 6 ++- components/src/environment/bme280/bme280.adb | 53 +++++++++++++++++- components/src/environment/bme280/bme280.ads | 54 ++++++++++++++++--- 3 files changed, 105 insertions(+), 8 deletions(-) diff --git a/components/src/environment/bme280/bme280-i2c.ads b/components/src/environment/bme280/bme280-i2c.ads index caa4a3d19..780938180 100644 --- a/components/src/environment/bme280/bme280-i2c.ads +++ b/components/src/environment/bme280/bme280-i2c.ads @@ -29,20 +29,24 @@ -- -- ------------------------------------------------------------------------------ +-- This package provides an easy way to setup BME280 connected via I2C. + with HAL.I2C; generic I2C_Port : not null HAL.I2C.Any_I2C_Port; - I2C_Address : HAL.UInt7; + I2C_Address : HAL.UInt7 := 16#76#; -- The BME280 7-bit I2C address package BME280.I2C is procedure Read (Data : out HAL.UInt8_Array; Success : out Boolean); + -- Read registers starting from Data'First procedure Write (Data : HAL.UInt8_Array; Success : out Boolean); + -- Write registers starting from Data'First package Sensor is new Generic_Sensor (Read, Write); diff --git a/components/src/environment/bme280/bme280.adb b/components/src/environment/bme280/bme280.adb index 56e4d8380..8f14613d6 100644 --- a/components/src/environment/bme280/bme280.adb +++ b/components/src/environment/bme280/bme280.adb @@ -35,6 +35,10 @@ with Ada.Unchecked_Conversion; package body BME280 is + OS_Map : constant array (Oversampling_Kind) of Natural := + [Skip => 0, X1 => 1, X2 => 2, X4 => 4, X8 => 8, X16 => 16]; + -- Map Oversamping_Kind to the integer value + package body Generic_Sensor is ------------------- @@ -206,9 +210,30 @@ package body BME280 is -- Reset -- ----------- - procedure Reset (Success : out Boolean) is + procedure Reset + (Timer : not null HAL.Time.Any_Delays; + Success : out Boolean) + is + use type HAL.UInt8; + + Data : HAL.UInt8_Array (16#F3# .. 16#F3#); begin Write ([16#E0# => 16#B6#], Success); + + if not Success then + return; + end if; + + for J in 1 .. 3 loop + Timer.Delay_Milliseconds (2); + Read (Data, Success); + + if Success and then (Data (Data'First) and 1) = 0 then + return; + end if; + end loop; + + Success := False; end Reset; ----------- @@ -273,6 +298,19 @@ package body BME280 is return Relative_Humidity'Small * Var_5; end Humidity; + -------------------------- + -- Max_Measurement_Time -- + -------------------------- + + function Max_Measurement_Time + (Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1) return Positive is + (1_250 + + (if Temperature = Skip then 0 else 2_300 * OS_Map (Temperature)) + + (if Pressure = Skip then 0 else 2_300 * OS_Map (Pressure) + 575) + + (if Humidity = Skip then 0 else 2_300 * OS_Map (Humidity) + 575)); + -------------- -- Pressure -- -------------- @@ -344,4 +382,17 @@ package body BME280 is return Deci_Celsius'Small * (Val_1 + Val_2); end Temperature; + ------------------------------ + -- Typical_Measurement_Time -- + ------------------------------ + + function Typical_Measurement_Time + (Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1) return Positive is + (1_000 + + (if Temperature = Skip then 0 else 2_000 * OS_Map (Temperature)) + + (if Pressure = Skip then 0 else 2_000 * OS_Map (Pressure) + 500) + + (if Humidity = Skip then 0 else 2_000 * OS_Map (Humidity) + 500)); + end BME280; diff --git a/components/src/environment/bme280/bme280.ads b/components/src/environment/bme280/bme280.ads index 2ab9b8f30..39202afdb 100644 --- a/components/src/environment/bme280/bme280.ads +++ b/components/src/environment/bme280/bme280.ads @@ -31,9 +31,10 @@ with Interfaces; with HAL; +with HAL.Time; package BME280 is - pragma Pure; + pragma Preelaborate; pragma Discard_Names; type Calibration_Constants is record @@ -56,8 +57,11 @@ package BME280 is H5 : Interfaces.Unsigned_16 range 0 .. 4095; H6 : Interfaces.Unsigned_8; end record; + -- Calibration constants per chip. Make visible to allow constant + -- initialised to a value known in advance. type Measurement is private; + -- Raw values from the sensor type Deci_Celsius is delta 1.0 / 2 ** 9 range -99_0.00 .. 99_0.00; -- 1 degree celsius is 10 Deci_Celsius @@ -65,15 +69,18 @@ package BME280 is function Temperature (Value : Measurement; Calibration : Calibration_Constants) return Deci_Celsius; + -- Get the temperature from raw values in 0.1 Celsius Humidity_Small : constant := 1.0 / 2 ** 10; type Relative_Humidity is delta Humidity_Small range 0.0 .. 100.0; + -- Relative humidity in percent function Humidity (Value : Measurement; Temperature : Deci_Celsius; Calibration : Calibration_Constants) return Relative_Humidity; + -- Get the humidity from raw values Pressure_Small : constant := 1.0 / 2 ** 8; @@ -84,40 +91,70 @@ package BME280 is (Value : Measurement; Temperature : Deci_Celsius; Calibration : Calibration_Constants) return Pressure_Pa; + -- Get the pressure from raw values type Oversampling_Kind is (Skip, X1, X2, X4, X8, X16); - type IRR_Filter_Kind is (Off, X1, X2, X4, X8, X16); + type IRR_Filter_Kind is (Off, X2, X4, X8, X16); type Sensor_Mode is (Sleep, Forced, Normal); + -- Sensor modes. Sleep - sensor is off, Forced - measure once and go to + -- sleep, Normal - measure continuously. type Standby_Duration is delta 0.5 range 0.5 .. 1000.0 with Static_Predicate => Standby_Duration in 0.5 | 10.0 | 20.0 | 62.5 | 125.0 | 250.0 | 500.0 | 1000.0; - -- Inactive duration in ms + -- Inactivity duration in ms subtype Register_Address is Natural range 16#80# .. 16#FF#; + -- Sensor registers addresses + + function Max_Measurement_Time + (Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1) return Positive; + -- Maximal measurement time in microseconds + + function Typical_Measurement_Time + (Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1) return Positive; + -- Typical measurement time in microseconds generic with procedure Read (Data : out HAL.UInt8_Array; Success : out Boolean); - -- Data'Range should be Register_Address + -- Read the values from the BME280 chip registers into Data. + -- Each element in the Data corresponds to a specific register address + -- in the chip, so Data'Range determines the range of registers to read. + -- The value read from register X will be stored in Data(X), so + -- Data'Range should be of the Register_Address subtype. with procedure Write (Data : HAL.UInt8_Array; Success : out Boolean); - -- Data'Range should be Register_Address + -- Write the values from Data to the BME280 chip registers. + -- Each element in the Data corresponds to a specific register address + -- in the chip, so Data'Range determines the range of registers to + -- write. The value for register X will be read from Data(X), so + -- Data'Range should be of the Register_Address subtype. package Generic_Sensor is + function Check_Chip_Id (Expect : HAL.UInt8 := 16#60#) return Boolean; + -- Read the chip ID and check that it matches - procedure Reset (Success : out Boolean); + procedure Reset + (Timer : not null HAL.Time.Any_Delays; + Success : out Boolean); + -- Issue a soft reset and wait until the chip is ready. procedure Configure (Standby : Standby_Duration := 1000.0; Filter : IRR_Filter_Kind := Off; SPI_3_Wire : Boolean := False; Success : out Boolean); + -- Configure the sensor to use IRR filtering and/or SPI 3-wire mode procedure Start (Mode : Sensor_Mode := Normal; @@ -125,16 +162,21 @@ package BME280 is Pressure : Oversampling_Kind := X1; Temperature : Oversampling_Kind := X1; Success : out Boolean); + -- Change sensor mode. Mainly used to start one measurement or enable + -- perpetual cycling of measurements and inactive periods. function Measuring return Boolean; + -- Check if a measurement is in progress procedure Read_Measurement (Value : out Measurement; Success : out Boolean); + -- Read the raw measurement values from the sensor procedure Read_Calibration (Value : out Calibration_Constants; Success : out Boolean); + -- Read the calibration constants from the sensor end Generic_Sensor; From 3e085bed975ab963736140dbfb500b454f47eb83 Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Thu, 14 Dec 2023 09:50:12 +0200 Subject: [PATCH 06/10] Improve BME280 example Check chip presense, reset it at the start, wait for first measument. Use Ada.Text_IO instead of LCD. --- examples/stm32_f4ve/bme280/README.md | 34 ++++++++++-- examples/stm32_f4ve/bme280/bme280.gpr | 2 +- examples/stm32_f4ve/bme280/main.adb | 74 +++++++++++++++++---------- 3 files changed, 78 insertions(+), 32 deletions(-) diff --git a/examples/stm32_f4ve/bme280/README.md b/examples/stm32_f4ve/bme280/README.md index 35b92b62b..5075544c6 100644 --- a/examples/stm32_f4ve/bme280/README.md +++ b/examples/stm32_f4ve/bme280/README.md @@ -1,7 +1,33 @@ # BME280 demo -This example demonstrates working with BME280 humidity, pressure -and temperature sensor. +This folder contains a demonstration program showcasing the functionality +of a temperature, humidity, and pressure sensor using the STM32 F4VE board. +The program reads sensor data and prints it over ST-Util semihosting +interface. -For this demo, you need an LCD, which is usually sold as -part of the STM32 F4VE board kit. +## Requirements + +* STM32 F4VE development board +* Any BME280 module +* ST-Link V2 debug probe +* Development environment compatible with STM32F4 microcontrollers + +## Setup + +* Attach BME280 by I2C to PB9 (SDA), PB8 (SCL) +* Attach the debug probe to the designated port on the STM32F4VE board. +* Connect the STM32 F4VE board to your development environment. + +## Usage + +Compile and upload the program to the STM32 F4VE board. Upon successful upload, +the demonstration program will run, printing sensor data over semishosting +channel. To see the output + +* launch a debug session in GNAT Studio, or +* run the debugger in the command line: + + ```sh + st-util --semihosting + arm-eabi-gdb -ex 'target remote localhost:4242' -ex cont + ``` diff --git a/examples/stm32_f4ve/bme280/bme280.gpr b/examples/stm32_f4ve/bme280/bme280.gpr index 73186f553..8a61bd8ec 100644 --- a/examples/stm32_f4ve/bme280/bme280.gpr +++ b/examples/stm32_f4ve/bme280/bme280.gpr @@ -6,7 +6,7 @@ project BME280 is for Target use "arm-eabi"; for Main use ("main.adb"); for Languages use ("Ada"); - for Source_Dirs use (".", "../../shared/common/gui"); + for Source_Dirs use ("."); for Object_Dir use "obj/"; for Create_Missing_Dirs use "True"; diff --git a/examples/stm32_f4ve/bme280/main.adb b/examples/stm32_f4ve/bme280/main.adb index c4fc37914..436cceb42 100644 --- a/examples/stm32_f4ve/bme280/main.adb +++ b/examples/stm32_f4ve/bme280/main.adb @@ -30,35 +30,18 @@ ------------------------------------------------------------------------------ with Ada.Real_Time; +with Ada.Text_IO; -with HAL.Bitmap; +with Ravenscar_Time; with STM32.Board; -with Display_ILI9341; - -with BME280.I2C; with STM32.Device; with STM32.Setup; -with BMP_Fonts; -with Bitmapped_Drawing; -procedure Main is - use type Ada.Real_Time.Time; - procedure Put_Line (Text : String); - - procedure Put_Line (Text : String) is - begin - STM32.Board.TFT_Bitmap.Set_Source (HAL.Bitmap.Black); - STM32.Board.TFT_Bitmap.Fill_Rect (Area => ((5, 5), 100, 10)); +with BME280.I2C; - Bitmapped_Drawing.Draw_String - (STM32.Board.TFT_Bitmap, - (5, 5), - Text, - BMP_Fonts.Font8x8, - HAL.Bitmap.Green, - HAL.Bitmap.Black); - end Put_Line; +procedure Main is + use type Ada.Real_Time.Time; package BME280_I2C is new BME280.I2C (I2C_Port => STM32.Device.I2C_1'Access, @@ -66,8 +49,6 @@ procedure Main is Next : Ada.Real_Time.Time := Ada.Real_Time.Clock; - -- Bitmap : Display_ILI9341.Bitmap_Buffer renames STM32.Board.TFT_Bitmap; - Ok : Boolean; Calib : BME280.Calibration_Constants; Measurement : BME280.Measurement; @@ -77,7 +58,6 @@ procedure Main is begin STM32.Board.Initialize_LEDs; - STM32.Board.Display.Initialize; STM32.Setup.Setup_I2C_Master (Port => STM32.Device.I2C_1, SDA => STM32.Device.PB9, @@ -86,18 +66,58 @@ begin SCL_AF => STM32.Device.GPIO_AF_I2C1_4, Clock_Speed => 400_000); + -- Look for BME280 chip + if not BME280_I2C.Sensor.Check_Chip_Id then + Ada.Text_IO.Put_Line ("BME280 not found."); + raise Program_Error; + end if; + + -- Reset BME280 + BME280_I2C.Sensor.Reset (Ravenscar_Time.Delays, Ok); + pragma Assert (Ok); + + -- Read calibration data into Clib BME280_I2C.Sensor.Read_Calibration (Calib, Ok); - BME280_I2C.Sensor.Start (Success => Ok); + + -- Consigure IRR filter and minimal incativity delay + BME280_I2C.Sensor.Configure + (Standby => 0.5, + Filter => BME280.X16, + SPI_3_Wire => False, + Success => Ok); + pragma Assert (Ok); + + -- Enable cycling of measurements with given oversamplig + BME280_I2C.Sensor.Start + (Mode => BME280.Normal, + Humidity => BME280.X1, + Pressure => BME280.X16, + Temperature => BME280.X2, + Success => Ok); + + -- Wait for the first measurement + Ravenscar_Time.Delays.Delay_Milliseconds + (BME280.Max_Measurement_Time + (Humidity => BME280.X1, + Pressure => BME280.X16, + Temperature => BME280.X2) / 1000 + 1); loop STM32.Board.Toggle (STM32.Board.D1_LED); + + -- Read raw values from the sensor BME280_I2C.Sensor.Read_Measurement (Measurement, Ok); if Ok then + -- Decode temperature, humidity and pressure Temp := BME280.Temperature (Measurement, Calib); Humi := BME280.Humidity (Measurement, Temp, Calib); Press := BME280.Pressure (Measurement, Temp, Calib); - Put_Line (Temp'Image & Humi'Image & Press'Image); + + Ada.Text_IO.Put_Line + ("T=" & Temp'Image & + " H=" & Humi'Image & + " P=" & Press'Image); end if; Next := Next + Ada.Real_Time.Milliseconds (500); From eb958405a86d112d69102b1bda8e0b2304e5c6c2 Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Fri, 15 Dec 2023 14:10:28 +0200 Subject: [PATCH 07/10] Add BME280 LCD demo --- boards/stm32_f4ve/src/stm32-board.ads | 6 +- examples/stm32_f4ve/bme280_lcd/README.md | 39 ++ examples/stm32_f4ve/bme280_lcd/bme280_lcd.gpr | 20 + .../stm32_f4ve/bme280_lcd/bme280_lcd_x2.png | Bin 0 -> 4559 bytes examples/stm32_f4ve/bme280_lcd/gui.adb | 148 +++++++ examples/stm32_f4ve/bme280_lcd/gui.ads | 165 +++++++ .../stm32_f4ve/bme280_lcd/gui_buttons.adb | 96 ++++ .../stm32_f4ve/bme280_lcd/gui_buttons.ads | 58 +++ examples/stm32_f4ve/bme280_lcd/main.adb | 415 ++++++++++++++++++ scripts/build_all_examples.py | 1 + 10 files changed, 945 insertions(+), 3 deletions(-) create mode 100644 examples/stm32_f4ve/bme280_lcd/README.md create mode 100644 examples/stm32_f4ve/bme280_lcd/bme280_lcd.gpr create mode 100644 examples/stm32_f4ve/bme280_lcd/bme280_lcd_x2.png create mode 100644 examples/stm32_f4ve/bme280_lcd/gui.adb create mode 100644 examples/stm32_f4ve/bme280_lcd/gui.ads create mode 100644 examples/stm32_f4ve/bme280_lcd/gui_buttons.adb create mode 100644 examples/stm32_f4ve/bme280_lcd/gui_buttons.ads create mode 100644 examples/stm32_f4ve/bme280_lcd/main.adb diff --git a/boards/stm32_f4ve/src/stm32-board.ads b/boards/stm32_f4ve/src/stm32-board.ads index 50dce6ca4..2d976c6ac 100644 --- a/boards/stm32_f4ve/src/stm32-board.ads +++ b/boards/stm32_f4ve/src/stm32-board.ads @@ -104,11 +104,11 @@ package STM32.Board is TFT_BLK : GPIO_Point renames PB1; -- LCD backlight TFT_CS : GPIO_Point renames PB12; - Display : Display_ILI9341.Display; + Display : aliased Display_ILI9341.Display; - TFT_Bitmap : Display_ILI9341.Bitmap_Buffer := Display.Buffer; + TFT_Bitmap : aliased Display_ILI9341.Bitmap_Buffer := Display.Buffer; - Touch_Panel : Touch_Panel_XPT2046.Touch_Panel; + Touch_Panel : aliased Touch_Panel_XPT2046.Touch_Panel; -------------------------- -- micro SD card reader -- diff --git a/examples/stm32_f4ve/bme280_lcd/README.md b/examples/stm32_f4ve/bme280_lcd/README.md new file mode 100644 index 000000000..880a99157 --- /dev/null +++ b/examples/stm32_f4ve/bme280_lcd/README.md @@ -0,0 +1,39 @@ +# BME280 LCD demo + +This folder contains a demonstration program showcasing the functionality +of a temperature, humidity, and pressure sensor using the STM32 F4VE board +and an LCD display included in the kit. The program features a straightforward +graphical user interface (GUI) for configuring sensor parameters. + +![Demo screenshot](bme280_lcd_x2.png) + +## Overview + +The demonstration program is designed to work with the STM32 F4VE development +board and a compatible LCD display. It provides a GUI interface to configure +sensor parameters such as temperature, humidity, and pressure. +The display includes buttons for enabling/disabling the display of +temperature (Te), humidity (Hu), and pressure (Pr) data. Additionally, +there are buttons (`x1` .. `16`) next to each data type for controlling +the oversampling parameters. Additionally, buttons labeled `No`, `x2` .. `16` +adjust the IRR filter. + +## Requirements + +* STM32 F4VE development board +* Any BME280 module +* Compatible LCD display/touch panel included in the kit +* Development environment compatible with STM32F4 microcontrollers + +## Setup + +* Attach BME280 by I2C to PB9 (SDA), PB8 (SCL) +* Attach the LCD display to the designated port on the STM32F4VE board. +* Connect the STM32 F4VE board to your development environment. + +## Usage + +Compile and upload the program to the STM32 F4VE board. Upon successful upload, +the demonstration program will run, displaying sensor data on the LCD screen. +Activate the buttons on the GUI interface using the touch panel. +Simply touch the corresponding button on the LCD screen to toggle its state. diff --git a/examples/stm32_f4ve/bme280_lcd/bme280_lcd.gpr b/examples/stm32_f4ve/bme280_lcd/bme280_lcd.gpr new file mode 100644 index 000000000..7db560d69 --- /dev/null +++ b/examples/stm32_f4ve/bme280_lcd/bme280_lcd.gpr @@ -0,0 +1,20 @@ +with "../../../boards/stm32_f4ve/stm32_f4ve_full.gpr"; + +project BME280_LCD is + + for Runtime ("Ada") use STM32_f4VE_Full'Runtime ("Ada"); + for Target use "arm-eabi"; + for Main use ("main.adb"); + for Languages use ("Ada"); + for Source_Dirs use (".", "../../shared/common/gui"); + for Object_Dir use "obj/"; + for Create_Missing_Dirs use "True"; + + package Compiler renames STM32_F4VE_Full.Compiler; + + package Ide is + for Program_Host use "localhost:4242"; + for Communication_Protocol use "remote"; + for Connection_Tool use "st-util"; + end Ide; +end BME280_LCD; diff --git a/examples/stm32_f4ve/bme280_lcd/bme280_lcd_x2.png b/examples/stm32_f4ve/bme280_lcd/bme280_lcd_x2.png new file mode 100644 index 0000000000000000000000000000000000000000..23653c238bac2f092d2c43acbfb3255dc71f7794 GIT binary patch literal 4559 zcmc&&c|6qX_x}v38%6n6cKTgzS#A<-3`V7~7V0t>Yhsk$*atIp?=5ASs8F^Fl`x9N zK4XefmYJf+HncF98QU;p8-CB|{(i6TpTF;4zvqt`uV@^8}?@{wo58X zLJ+jw%F_HC1c?zK2sZMI7}%+=vZ;cgotakVC;o`(=MJ68EJW@hICNRqe5Of#(03+m zaecPgWZcxZPAk-`B^fcL^4UtqVaJ2R1<)lIApak){-qn}c}3&-taSPJMd}l;4Pur; z)DLA?S!8fzTK7VFR`zVVKkhLWcRZsuG7!FBi>+#sal{+nu3)w7k41J}TA|vlHU5!+d>PV~J?uTi zvkjiY%vjD9yx>^ozrtpi_UEQ+dO`AU)R(tbpLs5Q{7g{ms!@EjH)XaXFhHMFK9Kg; z>%ZOC6aKz`I;VRy2(wSLWeyXkrAi)eTD890L#`qOm<8k?=s!|sYLllB1kV_+_NWpt zbM~-!Gy9j?ZC&QzWu;xoe3hQjVtR^cPMSu2d4a+TT2EpdBpK5r!LwnL&mXOrRH;%; z+uu}F$kKRZjjg6DKBssnprc-kB}VIAW1anI`maPC(lk~Q-XvX-3XW8yUYyR$aKdq_ z_@)&Em>I0K7Zc}xI$(g>JEjrGqzC6QNhbMx?eTe>GdntTAX>teJWWnFFGx3m?@uL} zoSpPToe(F81@|PikVE>o-{`2P{ZvDractnR!j1c~hWpro>@tV8{x{^1<-H4K5i zeqqurXrS76ARm85&1#;d#PU=i5nzOzJu}SD<-S>c&t6s^zzx2tXx{ApB>Q%N`i%ye zUHE+7 zy8t%X>E889?77Q+;Yit``wN0wQ?5satcg(+R-^dsbB%eoWd@sgajTLy-SQEd=N_iI zOAnf#esaAlwe+?TOTQa&sx4RkJ#^QN7e9%6n%-F2(U_hBmj!-5c3{M)Q=l)aelJ3! zW_+Y(gNY8bf2XpUmc*2}=^8)(&hRAn_Om!81jGwd(}ND*S_rbl`Nq2C;)WNLKBlva z$>W$+2$1LjQA~FssB$N@G=lPS5A;Cf#DWq=H5bRPIeZ8LoZU6k$ZaTRejhE$MQw%v z4&I(ngy1#F5xHm`gM{p9kRA%wdujMi=^f83WK7hmsX$V6>{Q$G(V|?t>}N~dQ`GHS zMO*(Yz4U@x#im)lE}4R_cp+a}>D2OEw;DcZl`*m);iXBsx|y)q8m~6PJEwq1j`|vP zN$d0)T^>_1Dh}Wgc1kWbO!AEu6XsYHg@g?w>}jL@HJ@esZ+dD4Fw|Ldz&#XrhYJjQLZ?^chggXqT62id&Lh6-67*Oj_Z%#5eYvqSxuV} zs-jz(RhUrE{aK!u-97#u8$QDL83?y?q;u4$h44Yg4>k{Xiix}d-#ydU z#wj-?A0&uv24t*@iuXPE)fJOP(N+@Sn0KXNrVCRON z)Arh+O~}H}KSGI?w z8Uk^9n&5IUYBD^FPtFn@m)Mr#S&xpB1IHm-2g+36X_z>acH_!G<7>*8D6uJWJONLY z`0&#&3|$MqNZl9{UtVy%;zP9)b^dn*d`79!CtftPcMk6Szj0_>!S!Z;B6+MR0}ep-0Y_2l;-W}{72N4^Y9@~03{;sBEo#yd z*o;-OA*1hHwLV`~M5fD6dVlTm@lz1N1*@Z@PMcHg;h-lANJl&LB65=m-C)%dMxtvE zpXG}P37C)NOJ6q>0fqcMH~0t)9eL7nl4_@9BDtM&-tW@RQ#$aIQ1E+0FKE1&@Wj7? zYaLPVI?*iIv83C8!2FA^hZ5xsQgfsvXd1iA61uy#xcq0SC~VQ6TBPZrMKZvW>>9## z#Qyq{)ZEoux=`_&;e&;nR{5i;rNWU(p+z*)8&=SAv45}F)L7haIQ4)s#l*oVDL^@Y7utE{tL7<&LghMmN}WUJ(+<^Y%L07xugt>xSUlj| z2S-?lOqDWcN`=YoBw9j>g0ND%VB+G?b3{q%nv)K&-U z@2~I~S$_k3p5jFNv*m5smQcc)svjMfz0OtZZgvokA*$V^fT7~MLlM^>7OHK6Cs`1~ zAMrAS>v@OXGPCE?d~%MQ1C`VtVBR~aY&-b1(lprNX8+Dn#hQ8{^&7Z4B4O{Wq3|7k z_1v=TT&r2%y^eda=%8wf_chE|8ZMf+k^>mFIq`MDE7U1{{?7Ra4X&MXIdVVBaBKR^ zOkvg)0l5M=F1wN-Fp6P(pK%_OzTQttQZ3WM3~8}y-z2ukoty8lt7(;PC;=&N>qc8@ z)u#q-&KcZhTl*MId?m%CgG}@yiA`~G2r8qIFZ|IT(!TKKxCQ5%T=aei=K1W>yFIq$ zi*Lwb16;u^x8Dvh68C*Bf~Sk>Maoke-N7gEQ9wvpkc00_UA7YqJb>v&e+>D4<292e z?6z*?ytGG&OHFJ8axLvMk2K-i9nmXlrRF6#G2Msu87j?8qwL!p8z+bpK3Vrxrkh!z zD}!D&d^sz?>P>2qi5(}Obl<1m;i@;`_4|?4=dS{v1lLN&b$Ow46DA_m1AS&V3T|-! zVRmGrBgk-q7}D>{FGs&*4|JaSCF&xu!8T&AKp{~>_XPI>yl-1!qxSdA5H*lAW?OQ_ zsb`L^ejBvCQWCGc`m#mqc|#tOjZMhw01%;$9ZF|U-_5b^?z_MmK%ea__ioP-H4@_C z(u;3fpCxyrNB329#XmOgBtJCw+;_|1M5~X^kp&OgeRjY=iqx0?O1*<6fv_X$yTrwGR&Y7bxs>Z8Uyj`7y!p8Qq89}Gs z*?fx%Q7L-4(&f?$&=Zm+I1dn0x8mMwe*E742T#XP$XY*sEdC3uwWcVue5v%z?`O4^ z=R9;N_FQ|w4u3bA{R45;^JVFUIBSZoUp=+I^b~L&O(H&C$PMdAS?GKbIi>8L!3r`| z##kdgR-PAH@L|idsd*Eu8w2}V7Prg>JLO&IR;UAve6}aFWi~4Ao{E~QdxP3x)~uo0 zW%$|&@4kgveY+)f&F9i-vnA`51Cy-W7pT7gj|O&!TGu^Rt3RDvZVZ2R2efDjZpw<8 zCd~a4j+$1%jjtk~gKJ+)9beWP=hS^onjKG0Yg-Jf!d%Za;X8k~(FU>9i-Yx+Fsqu_ zWE0>xUYq;G~_;O_@#?K(d6I61a7PL+i+bT@zW<(EyrkDKwx~7!{jti;PxpY$B z>7NoUs_teC)7XMRalLJzPd5{v5+BRYqvWn%^Z%b03LFXDGBvS@FpLGg1x+RP$)PK^uZ&n4^luJ>gYkg-70EV%!GdeGyc!(0f|u=-yvp}hOpgeb^cdSBOGsFPh0^v z{y(fl_Wtm%0W%|>$O{^`(~2mF^;Lt2CA@G!G*aGCw-Q|U*#TugCiLDcow1g;iij;k z>c6p!`{IMUG@SHZSjD)YAtV;1IgE*_kBU^x8B^xL0p$wqNba& z8TY9gZj-8=V2BwG(*n&4twSQd+?c=o*w2ClJA#eUsNk3;`?D(zK&5G_EJ&a%sbwWy zThuKIh)&8h%E|F~iDNk9HE_iD2kDg2I|up$46xijcB8>V(L1f?XldSEG~cNcLkYLm z`DcRsIb`;vkh7}@?NJv?B^lFmL4SLq{Yul#8x39@yY_u~aKNaTsWpL@K>>1OVV(`^ z;oFt0WO`+4f`-9pmtRwbl}*?karW4-P z?YDNq>OBsRIjs72vv6{WIlOcFg=!vf(LJeJ##PSOcyAe&XScQs@lX>?T?;eMphT!#8#|U z6NlH}cw>ykIjl}mMCf1wZRvY77~C{R2KArv(5-U_ho#T{8XpBiV~Fpi6P6xBoMx=k zG+8leao2d#y6a_)u!1Ew@X5mt*7hdWtTEB+lA)k#ZeN}MgW}shfzybTd~b=bkwsf5RIa^X9LQ5YXlKtmj7XN9cj9guZe5GnmBHvuy QE0VQ3eb$_G((CGf1DI|I-v9sr literal 0 HcmV?d00001 diff --git a/examples/stm32_f4ve/bme280_lcd/gui.adb b/examples/stm32_f4ve/bme280_lcd/gui.adb new file mode 100644 index 000000000..fa6061bbe --- /dev/null +++ b/examples/stm32_f4ve/bme280_lcd/gui.adb @@ -0,0 +1,148 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with Ada.Text_IO; + +package body GUI is + + use type GUI_Buttons.Boolean_Array; + + Prev : GUI_Buttons.Boolean_Array (Buttons'Range) := not State; + + procedure On_Click + (Button : Button_Kind; + Update : in out Boolean); + + ----------------- + -- Check_Touch -- + ----------------- + + procedure Check_Touch + (TP : in out HAL.Touch_Panel.Touch_Panel_Device'Class; + Update : out Boolean) + is + Touch : constant HAL.Touch_Panel.TP_State := TP.Get_All_Touch_Points; + Index : Natural := Buttons'First; + Min : Natural := Natural'Last; + begin + Update := False; + + if Touch'Length = 0 then + return; + end if; + + for J in Buttons'Range loop + declare + Center : constant HAL.Bitmap.Point := Buttons (J).Center; + Dist : constant Natural := + (Center.X - Touch (Touch'First).X) ** 2 + + (Center.Y - Touch (Touch'First).Y)**2; + begin + if Min > Dist then + Min := Dist; + Index := J; + end if; + end; + end loop; + + if Min < 600 then + On_Click (Button_Kind'Val (Index), Update); + end if; + end Check_Touch; + + ---------- + -- Draw -- + ---------- + + procedure Draw + (LCD : in out HAL.Bitmap.Bitmap_Buffer'Class; + Clear : Boolean := False) is + begin + if Clear then + LCD.Set_Source (HAL.Bitmap.Black); + LCD.Fill; + Prev := not State; + end if; + + if State /= Prev then + GUI_Buttons.Draw (LCD, Buttons, State, Prev); + Prev := State; + end if; + end Draw; + + ----------------- + -- Dump_Screen -- + ----------------- + + procedure Dump_Screen (LCD : in out HAL.Bitmap.Bitmap_Buffer'Class) is + Color : HAL.UInt32; + begin + for Y in 0 .. LCD.Height - 1 loop + for X in 0 .. LCD.Width - 1 loop + Color := LCD.Pixel ((X, Y)); + Ada.Text_IO.Put_Line (Color'Image); + end loop; + end loop; + end Dump_Screen; + + -------------- + -- On_Click -- + -------------- + + procedure On_Click + (Button : Button_Kind; + Update : in out Boolean) is + begin + case Button is + when Te | Hu | Pr => + State (+Button) := not State (+Button); + when Fi => + null; + when Te_X1 .. Te_16 => + Update := not State (+Button); + State (+Te_X1 .. +Te_16) := (others => False); + State (+Button) := True; + when Hu_X1 .. Hu_16 => + Update := not State (+Button); + State (+Hu_X1 .. +Hu_16) := (others => False); + State (+Button) := True; + when Pr_X1 .. Pr_16 => + Update := not State (+Button); + State (+Pr_X1 .. +Pr_16) := (others => False); + State (+Button) := True; + when Fi_No .. Fi_16 => + Update := not State (+Button); + State (+Fi_No .. +Fi_16) := (others => False); + State (+Button) := True; + end case; + end On_Click; + +end GUI; diff --git a/examples/stm32_f4ve/bme280_lcd/gui.ads b/examples/stm32_f4ve/bme280_lcd/gui.ads new file mode 100644 index 000000000..8dc4c3da0 --- /dev/null +++ b/examples/stm32_f4ve/bme280_lcd/gui.ads @@ -0,0 +1,165 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +pragma Ada_2022; + +with GUI_Buttons; +with HAL.Bitmap; +with HAL.Touch_Panel; + +package GUI is + + type Button_Kind is + ( + Te, + Te_X1, + Te_X2, + Te_X4, + Te_X8, + Te_16, + Hu, + Hu_X1, + Hu_X2, + Hu_X4, + Hu_X8, + Hu_16, + Pr, + Pr_X1, + Pr_X2, + Pr_X4, + Pr_X8, + Pr_16, + Fi, + Fi_No, + Fi_X2, + Fi_X4, + Fi_X8, + Fi_16); + + function "+" (X : Button_Kind) return Natural is (Button_Kind'Pos (X)) + with Static; + + Buttons : constant GUI_Buttons.Button_Info_Array := + [ + (Label => "Te", + Center => (23 * 1, 20), + Color => HAL.Bitmap.Dark_Red), + (Label => "x1", + Center => (23 * 2, 20), + Color => HAL.Bitmap.Dark_Red), + (Label => "x2", + Center => (23 * 3, 20), + Color => HAL.Bitmap.Dark_Red), + (Label => "x4", + Center => (23 * 4, 20), + Color => HAL.Bitmap.Dark_Red), + (Label => "x8", + Center => (23 * 5, 20), + Color => HAL.Bitmap.Dark_Red), + (Label => "16", + Center => (23 * 6, 20), + Color => HAL.Bitmap.Dark_Red), + + (Label => "Hu", + Center => (23 * 1 + 160, 20), + Color => HAL.Bitmap.Dark_Green), + (Label => "x1", + Center => (23 * 2 + 160, 20), + Color => HAL.Bitmap.Dark_Green), + (Label => "x2", + Center => (23 * 3 + 160, 20), + Color => HAL.Bitmap.Dark_Green), + (Label => "x4", + Center => (23 * 4 + 160, 20), + Color => HAL.Bitmap.Dark_Green), + (Label => "x8", + Center => (23 * 5 + 160, 20), + Color => HAL.Bitmap.Dark_Green), + (Label => "16", + Center => (23 * 6 + 160, 20), + Color => HAL.Bitmap.Dark_Green), + + (Label => "Pr", + Center => (23 * 1, 220), + Color => HAL.Bitmap.Dark_Blue), + (Label => "x1", + Center => (23 * 2, 220), + Color => HAL.Bitmap.Dark_Blue), + (Label => "x2", + Center => (23 * 3, 220), + Color => HAL.Bitmap.Dark_Blue), + (Label => "x4", + Center => (23 * 4, 220), + Color => HAL.Bitmap.Dark_Blue), + (Label => "x8", + Center => (23 * 5, 220), + Color => HAL.Bitmap.Dark_Blue), + (Label => "16", + Center => (23 * 6, 220), + Color => HAL.Bitmap.Dark_Blue), + + (Label => "Fi", + Center => (23 * 1 + 160, 220), + Color => HAL.Bitmap.Dim_Grey), + (Label => "No", + Center => (23 * 2 + 160, 220), + Color => HAL.Bitmap.Dim_Grey), + (Label => "x2", + Center => (23 * 3 + 160, 220), + Color => HAL.Bitmap.Dim_Grey), + (Label => "x4", + Center => (23 * 4 + 160, 220), + Color => HAL.Bitmap.Dim_Grey), + (Label => "x8", + Center => (23 * 5 + 160, 220), + Color => HAL.Bitmap.Dim_Grey), + (Label => "16", + Center => (23 * 6 + 160, 220), + Color => HAL.Bitmap.Dim_Grey)]; + + State : GUI_Buttons.Boolean_Array (Buttons'Range) := + [+Hu | +Te | +Pr | + +Hu_X1 | +Te_X1 | +Pr_X1 | + +Fi_No => True, + others => False]; + + procedure Check_Touch + (TP : in out HAL.Touch_Panel.Touch_Panel_Device'Class; + Update : out Boolean); + -- Check buttons touchedm update State, set Update = True if State changed + + procedure Draw + (LCD : in out HAL.Bitmap.Bitmap_Buffer'Class; + Clear : Boolean := False); + + procedure Dump_Screen (LCD : in out HAL.Bitmap.Bitmap_Buffer'Class); + +end GUI; diff --git a/examples/stm32_f4ve/bme280_lcd/gui_buttons.adb b/examples/stm32_f4ve/bme280_lcd/gui_buttons.adb new file mode 100644 index 000000000..ebaa8c1cb --- /dev/null +++ b/examples/stm32_f4ve/bme280_lcd/gui_buttons.adb @@ -0,0 +1,96 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with Bitmapped_Drawing; +with BMP_Fonts; + +package body GUI_Buttons is + + ---------- + -- Draw -- + ---------- + + procedure Draw + (Buffer : in out HAL.Bitmap.Bitmap_Buffer'Class; + Buttons : Button_Info_Array; + State : Boolean_Array) is + begin + Draw (Buffer, Buttons, State, not State); + end Draw; + + ---------- + -- Draw -- + ---------- + + procedure Draw + (Buffer : in out HAL.Bitmap.Bitmap_Buffer'Class; + Buttons : Button_Info_Array; + State : Boolean_Array; + Prev_State : Boolean_Array) is + begin + for J in Buttons'Range loop + if State (J) /= Prev_State (J) then + declare + use type HAL.Bitmap.Point; + + Button : Button_Info renames Buttons (J); + + Area : constant HAL.Bitmap.Rect := + (Button.Center - (10, 6), + Width => 20, Height => 13); + + Foreground : constant HAL.Bitmap.Bitmap_Color := + (if State (J) then HAL.Bitmap.Black else Button.Color); + + Background : constant HAL.Bitmap.Bitmap_Color := + (if State (J) then Button.Color else HAL.Bitmap.Black); + begin + Buffer.Set_Source (Background); + Buffer.Fill_Rounded_Rect (Area, 3); + + Bitmapped_Drawing.Draw_String + (Buffer, + Start => Area.Position + (1, 2), + Msg => Button.Label, + Font => BMP_Fonts.Font8x8, + Foreground => Foreground, + Background => Background); + + if not State (J) then + Buffer.Set_Source (Foreground); + Buffer.Draw_Rounded_Rect (Area, 3); + end if; + end; + end if; + end loop; + end Draw; + +end GUI_Buttons; diff --git a/examples/stm32_f4ve/bme280_lcd/gui_buttons.ads b/examples/stm32_f4ve/bme280_lcd/gui_buttons.ads new file mode 100644 index 000000000..953bad8a0 --- /dev/null +++ b/examples/stm32_f4ve/bme280_lcd/gui_buttons.ads @@ -0,0 +1,58 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with HAL.Bitmap; + +package GUI_Buttons is + + type Button_Info is record + Label : String (1 .. 2); + Center : HAL.Bitmap.Point; + Color : HAL.Bitmap.Bitmap_Color; + end record; + + type Button_Info_Array is array (Natural range <>) of Button_Info; + type Boolean_Array is array (Natural range <>) of Boolean with Pack; + + procedure Draw + (Buffer : in out HAL.Bitmap.Bitmap_Buffer'Class; + Buttons : Button_Info_Array; + State : Boolean_Array); + + procedure Draw + (Buffer : in out HAL.Bitmap.Bitmap_Buffer'Class; + Buttons : Button_Info_Array; + State : Boolean_Array; + Prev_State : Boolean_Array); + -- Optimized version of Draw, It works as Draw, but draws only buttons (J) + -- if State (J) /= Prev_State (J) + +end GUI_Buttons; diff --git a/examples/stm32_f4ve/bme280_lcd/main.adb b/examples/stm32_f4ve/bme280_lcd/main.adb new file mode 100644 index 000000000..93eb41667 --- /dev/null +++ b/examples/stm32_f4ve/bme280_lcd/main.adb @@ -0,0 +1,415 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with Ada.Real_Time; +with Ada.Text_IO; + +with Ravenscar_Time; + +with STM32.Board; +with STM32.Device; +with STM32.GPIO; +with STM32.Setup; +with STM32.User_Button; + +with HAL.Bitmap; +with HAL.Framebuffer; + +with Display_ILI9341; +with Bitmapped_Drawing; +with BMP_Fonts; + +with BME280.I2C; + +with GUI; +with GUI_Buttons; + +procedure Main is + use type Ada.Real_Time.Time; + + package BME280_I2C is new BME280.I2C + (I2C_Port => STM32.Device.I2C_1'Access, + I2C_Address => 16#76#); + + procedure Configure_Sensor; + -- Restart sensor with new settings according to GUI state + + type Sensor_Data is record + Temp : BME280.Deci_Celsius; + Humi : BME280.Relative_Humidity; + Press : BME280.Pressure_Pa; + end record; + + function Read_Sensor + (Calib : BME280.Calibration_Constants) return Sensor_Data; + + function Min (Left, Right : Sensor_Data) return Sensor_Data is + (Temp => BME280.Deci_Celsius'Min (Left.Temp, Right.Temp), + Humi => BME280.Relative_Humidity'Min (Left.Humi, Right.Humi), + Press => BME280.Pressure_Pa'Min (Left.Press, Right.Press)); + + function Max (Left, Right : Sensor_Data) return Sensor_Data is + (Temp => BME280.Deci_Celsius'Max (Left.Temp, Right.Temp), + Humi => BME280.Relative_Humidity'Max (Left.Humi, Right.Humi), + Press => BME280.Pressure_Pa'Max (Left.Press, Right.Press)); + + type Sensor_Limits is record + Min : Sensor_Data; + Max : Sensor_Data; + end record; + + procedure Make_Wider (Limits : in out Sensor_Limits); + -- Make limits a bit wider + + procedure Print + (LCD : not null HAL.Bitmap.Any_Bitmap_Buffer; + Data : Sensor_Data); + + procedure Plot + (LCD : not null HAL.Bitmap.Any_Bitmap_Buffer; + X : Natural; + Data : in out Sensor_Data; + Limits : Sensor_Limits); + + ---------------------- + -- Configure_Sensor -- + ---------------------- + + procedure Configure_Sensor is + use all type GUI.Button_Kind; + + function Oversampling + (State : GUI_Buttons.Boolean_Array) return BME280.Oversampling_Kind; + + function Filter + (State : GUI_Buttons.Boolean_Array) return BME280.IRR_Filter_Kind; + + ------------ + -- Filter -- + ------------ + + function Filter + (State : GUI_Buttons.Boolean_Array) return BME280.IRR_Filter_Kind + is + Result : BME280.IRR_Filter_Kind := BME280.Off; + begin + for J in State'Range loop + exit when State (J); + Result := BME280.IRR_Filter_Kind'Succ (Result); + end loop; + + return Result; + end Filter; + + ------------------ + -- Oversampling -- + ------------------ + + function Oversampling + (State : GUI_Buttons.Boolean_Array) return BME280.Oversampling_Kind + is + Result : BME280.Oversampling_Kind := BME280.X1; + begin + for J in State'Range loop + exit when State (J); + Result := BME280.Oversampling_Kind'Succ (Result); + end loop; + + return Result; + end Oversampling; + + Ok : Boolean; + begin + -- Consigure IRR filter and minimal incativity delay + BME280_I2C.Sensor.Configure + (Standby => 0.5, + Filter => Filter (GUI.State (+Fi_No .. +Fi_16)), + SPI_3_Wire => False, + Success => Ok); + pragma Assert (Ok); + + -- Enable cycling of measurements with given oversamplig + BME280_I2C.Sensor.Start + (Mode => BME280.Normal, + Humidity => Oversampling (GUI.State (+Hu_X1 .. +Hu_16)), + Pressure => Oversampling (GUI.State (+Pr_X1 .. +Pr_16)), + Temperature => Oversampling (GUI.State (+Te_X1 .. +Te_16)), + Success => Ok); + pragma Assert (Ok); + end Configure_Sensor; + + ---------------- + -- Make_Wider -- + ---------------- + + procedure Make_Wider (Limits : in out Sensor_Limits) is + begin + Limits.Min := + (Temp => 0.98 * Limits.Min.Temp, + Humi => 0.95 * Limits.Min.Humi, + Press => 0.999_9 * Limits.Min.Press); + + Limits.Max := + (Temp => 1.02 * Limits.Max.Temp, + Humi => 1.05 * Limits.Max.Humi, + Press => 1.000_1 * Limits.Max.Press); + end Make_Wider; + + ----------- + -- Print -- + ----------- + + procedure Print + (LCD : not null HAL.Bitmap.Any_Bitmap_Buffer; + Data : Sensor_Data) + is + use all type GUI.Button_Kind; + use type BME280.Deci_Celsius; + + Temp : constant String := BME280.Deci_Celsius'Image (Data.Temp / 10); + Humi : constant String := BME280.Relative_Humidity'Image (Data.Humi); + Press : constant String := BME280.Pressure_Pa'Image (Data.Press); + + begin + if GUI.State (+Te) then + Bitmapped_Drawing.Draw_String + (LCD.all, + Start => (0, 30), + Msg => Temp, + Font => BMP_Fonts.Font8x8, + Foreground => GUI.Buttons (+Te).Color, + Background => HAL.Bitmap.Black); + end if; + + if GUI.State (+Hu) then + Bitmapped_Drawing.Draw_String + (LCD.all, + Start => (0, 40), + Msg => Humi, + Font => BMP_Fonts.Font8x8, + Foreground => GUI.Buttons (+Hu).Color, + Background => HAL.Bitmap.Black); + end if; + + if GUI.State (+Pr) then + Bitmapped_Drawing.Draw_String + (LCD.all, + Start => (0, 50), + Msg => Press, + Font => BMP_Fonts.Font8x8, + Foreground => GUI.Buttons (+Pr).Color, + Background => HAL.Bitmap.Black); + end if; + end Print; + + ---------- + -- Plot -- + ---------- + + procedure Plot + (LCD : not null HAL.Bitmap.Any_Bitmap_Buffer; + X : Natural; + Data : in out Sensor_Data; + Limits : Sensor_Limits) + is + use all type GUI.Button_Kind; + use type BME280.Deci_Celsius; + use type BME280.Relative_Humidity; + use type BME280.Pressure_Pa; + + type Pixel_Y is delta 1.0 range 0.0 .. 1024_00.0; + -- To avoid Constraint_Error on Humidity convertion + + Y : Natural; + begin + Data := Min (Data, Limits.Max); + Data := Max (Data, Limits.Min); + + if GUI.State (+Te) then + Y := Natural + (BME280.Deci_Celsius'Base' + (LCD.Height * (Data.Temp - Limits.Min.Temp)) + / BME280.Deci_Celsius'Base' + (Limits.Max.Temp - Limits.Min.Temp)); + + Y := LCD.Height - Y; + LCD.Set_Pixel ((X, Y), HAL.Bitmap.Red); + end if; + + if GUI.State (+Hu) then + Y := Natural + (Pixel_Y + (LCD.Height * (Data.Humi - Limits.Min.Humi)) + / BME280.Relative_Humidity'Base' + (Limits.Max.Humi - Limits.Min.Humi)); + + Y := LCD.Height - Y; + LCD.Set_Pixel ((X, Y), HAL.Bitmap.Green); + end if; + + if GUI.State (+Pr) then + Y := Natural + (BME280.Pressure_Pa'Base' + (LCD.Height * (Data.Press - Limits.Min.Press)) + / BME280.Pressure_Pa'Base' + (Limits.Max.Press - Limits.Min.Press)); + + Y := LCD.Height - Y; + LCD.Set_Pixel ((X, Y), HAL.Bitmap.Blue); + end if; + end Plot; + + ----------------- + -- Read_Sensor -- + ----------------- + + function Read_Sensor + (Calib : BME280.Calibration_Constants) return Sensor_Data + is + Ok : Boolean; + Measurement : BME280.Measurement; + Temp : BME280.Deci_Celsius; + begin + BME280_I2C.Sensor.Read_Measurement (Measurement, Ok); + pragma Assert (Ok); + + Temp := BME280.Temperature (Measurement, Calib); + + return + (Temp => Temp, + Humi => BME280.Humidity (Measurement, Temp, Calib), + Press => BME280.Pressure (Measurement, Temp, Calib)); + end Read_Sensor; + + Empty : constant Sensor_Limits := + (Min => + (Temp => BME280.Deci_Celsius'Last, + Humi => BME280.Relative_Humidity'Last, + Press => BME280.Pressure_Pa'Last), + Max => + (Temp => BME280.Deci_Celsius'First, + Humi => BME280.Relative_Humidity'First, + Press => BME280.Pressure_Pa'First)); + + LCD : constant not null HAL.Bitmap.Any_Bitmap_Buffer := + STM32.Board.TFT_Bitmap'Access; + + Next : Ada.Real_Time.Time := Ada.Real_Time.Clock; + Ok : Boolean; + Calib : BME280.Calibration_Constants; + Next_Limits : Sensor_Limits; +begin + STM32.Board.Initialize_LEDs; + STM32.User_Button.Initialize; + STM32.Board.Display.Initialize; + STM32.Board.Display.Set_Orientation (HAL.Framebuffer.Landscape); + STM32.Board.Touch_Panel.Initialize; + STM32.Board.Touch_Panel.Set_Orientation (HAL.Framebuffer.Landscape); + + -- Initialize touch panel IRQ pin + STM32.Board.TFT_RS.Configure_IO + ((STM32.GPIO.Mode_In, Resistors => STM32.GPIO.Floating)); + + STM32.Setup.Setup_I2C_Master + (Port => STM32.Device.I2C_1, + SDA => STM32.Device.PB9, + SCL => STM32.Device.PB8, + SDA_AF => STM32.Device.GPIO_AF_I2C1_4, + SCL_AF => STM32.Device.GPIO_AF_I2C1_4, + Clock_Speed => 400_000); + + -- Look for BME280 chip + if not BME280_I2C.Sensor.Check_Chip_Id then + Ada.Text_IO.Put_Line ("BME280 not found."); + raise Program_Error; + end if; + + -- Reset BME280 + BME280_I2C.Sensor.Reset (Ravenscar_Time.Delays, Ok); + pragma Assert (Ok); + + -- Read calibration data into Clib + BME280_I2C.Sensor.Read_Calibration (Calib, Ok); + + Configure_Sensor; + + -- Wait for the first measurement + Ravenscar_Time.Delays.Delay_Milliseconds + (BME280.Max_Measurement_Time + (Humidity => BME280.X1, + Pressure => BME280.X1, + Temperature => BME280.X1) / 1000 + 1); + + -- Predict boundaries from the first sensor measurement + Next_Limits.Min := Read_Sensor (Calib); + Next_Limits.Max := Next_Limits.Min; + Make_Wider (Next_Limits); + + loop + declare + Limits : constant Sensor_Limits := Next_Limits; + Data : Sensor_Data; + Update : Boolean := False; -- GUI state updated + begin + GUI.Draw (LCD.all, Clear => True); -- draw all buttons + Next_Limits := Empty; + + for X in 0 .. LCD.Width - 1 loop + STM32.Board.Toggle (STM32.Board.D1_LED); + + Data := Read_Sensor (Calib); + + Next_Limits := + (Min => Min (Data, Next_Limits.Min), + Max => Max (Data, Next_Limits.Max)); + + if not STM32.Board.TFT_RS.Set then -- Touch IRQ Pin is active + GUI.Check_Touch (STM32.Board.Touch_Panel, Update); + end if; + + GUI.Draw (LCD.all); + Print (LCD, Data); + Plot (LCD, X, Data, Limits); + + if Update then + Configure_Sensor; + elsif STM32.User_Button.Has_Been_Pressed then + GUI.Dump_Screen (LCD.all); + end if; + + Next := Next + Ada.Real_Time.Milliseconds (100); + delay until Next; + end loop; + + Make_Wider (Next_Limits); + end; + end loop; +end Main; diff --git a/scripts/build_all_examples.py b/scripts/build_all_examples.py index d2702f7eb..9a14cf7b8 100755 --- a/scripts/build_all_examples.py +++ b/scripts/build_all_examples.py @@ -74,6 +74,7 @@ def gprbuild(project_file, debug=False): "/examples/stm32_f4ve/draw/draw.gpr", "/examples/stm32_f4ve/lcd/lcd.gpr", "/examples/stm32_f4ve/bme280/bme280.gpr", + "/examples/stm32_f4ve/bme280_lcd/bme280_lcd.gpr", # STM32F4XX M "/boards/stm32f4xx_m/stm32f4xx_m_full.gpr", From 8eb896f305ed2f6c638320ae5663e24568b15b3a Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Fri, 15 Dec 2023 17:29:09 +0200 Subject: [PATCH 08/10] Add BME280 connected by SPI --- .../src/environment/bme280/bme280-spi.adb | 89 +++++++++++++++++++ .../src/environment/bme280/bme280-spi.ads | 54 +++++++++++ 2 files changed, 143 insertions(+) create mode 100644 components/src/environment/bme280/bme280-spi.adb create mode 100644 components/src/environment/bme280/bme280-spi.ads diff --git a/components/src/environment/bme280/bme280-spi.adb b/components/src/environment/bme280/bme280-spi.adb new file mode 100644 index 000000000..6865abbe3 --- /dev/null +++ b/components/src/environment/bme280/bme280-spi.adb @@ -0,0 +1,89 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +package body BME280.SPI is + + ---------- + -- Read -- + ---------- + + procedure Read + (Data : out HAL.UInt8_Array; + Success : out Boolean) + is + use type HAL.UInt8; + use all type HAL.SPI.SPI_Status; + + Addr : HAL.UInt8; + Status : HAL.SPI.SPI_Status; + begin + SPI.SPI_CS.Clear; + + Addr := HAL.UInt8 (Data'First) or 16#80#; + SPI_Port.Transmit (HAL.SPI.SPI_Data_8b'(1 => Addr), Status); + + if Status = Ok then + SPI_Port.Receive (HAL.SPI.SPI_Data_8b (Data), Status); + end if; + + SPI.SPI_CS.Set; + + Success := Status = Ok; + end Read; + + ----------- + -- Write -- + ----------- + + procedure Write + (Data : HAL.UInt8_Array; + Success : out Boolean) + is + use type HAL.UInt8; + use all type HAL.SPI.SPI_Status; + + Addr : HAL.UInt8; + Status : HAL.SPI.SPI_Status; + begin + SPI.SPI_CS.Clear; + + for J in Data'Range loop + Addr := HAL.UInt8 (J) and 16#7F#; + SPI_Port.Transmit (HAL.SPI.SPI_Data_8b'(Addr, Data (J)), Status); + exit when Status /= Ok; + end loop; + + SPI.SPI_CS.Set; + + Success := Status = Ok; + end Write; + +end BME280.SPI; diff --git a/components/src/environment/bme280/bme280-spi.ads b/components/src/environment/bme280/bme280-spi.ads new file mode 100644 index 000000000..2039d8e3d --- /dev/null +++ b/components/src/environment/bme280/bme280-spi.ads @@ -0,0 +1,54 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +-- This package provides an easy way to setup BME280 connected via SPI. + +with HAL.GPIO; +with HAL.SPI; + +generic + SPI_Port : not null HAL.SPI.Any_SPI_Port; + SPI_CS : not null HAL.GPIO.Any_GPIO_Point; +package BME280.SPI is + + procedure Read + (Data : out HAL.UInt8_Array; + Success : out Boolean); + -- Read registers starting from Data'First + + procedure Write + (Data : HAL.UInt8_Array; + Success : out Boolean); + -- Write registers starting from Data'First + + package Sensor is new Generic_Sensor (Read, Write); + +end BME280.SPI; From 87bb2514f5a3e791e90bdee45fa180062fa4af44 Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Fri, 15 Dec 2023 19:45:32 +0200 Subject: [PATCH 09/10] Add README.md files --- components/src/environment/bme280/README.md | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 components/src/environment/bme280/README.md diff --git a/components/src/environment/bme280/README.md b/components/src/environment/bme280/README.md new file mode 100644 index 000000000..ce301dd4f --- /dev/null +++ b/components/src/environment/bme280/README.md @@ -0,0 +1,26 @@ +# BME280 + +> Humidity sensor measuring relative humidity, barometric pressure and +> ambient temperature + +* [Datasheet](https://www.bosch-sensortec.com/products/environmental-sensors/humidity-sensors-bme280/#documents) + +The sensor is available as a module for DIY projects from various +manufacturers, such as [Adafruit](https://www.adafruit.com/product/2652) +and [SparkFun](https://www.sparkfun.com/products/13676). It boasts high +accuracy, a compact size, and the flexibility to connect via both I2C and +SPI interfaces. + +The BME280 driver enables the following functionalities: + +* Detect the presence of the sensor. +* Perform a reset operation. +* Configure the parameters of the IRR filter and oversampling for each channel. +* Read calibration coefficients. +* Conduct measurements and calibrate the obtained values. +* Calculate the time required for measurements. + +## Examples + +* [Simple example for STM32 F4VE board](../../../../examples/stm32_f4ve/bme280) +* [Advanced example for STM32 F4VE board and LCD & touch panel](../../../../examples/stm32_f4ve/bme280) From 49eb4e10e1b8af6ae93ce9a78956abffa099261b Mon Sep 17 00:00:00 2001 From: Maxim Reznik Date: Sat, 16 Dec 2023 12:59:42 +0200 Subject: [PATCH 10/10] Add tagged types for BME280 sensors to allow a user to declare several sensor objects and process them in a uniform way. --- .../bme280/bme280-generic_sensor.ads | 93 ++++++ .../src/environment/bme280/bme280-i2c.ads | 12 +- .../environment/bme280/bme280-i2c_sensors.ads | 115 ++++++++ .../src/environment/bme280/bme280-sensors.ads | 98 +++++++ .../src/environment/bme280/bme280-spi.ads | 12 +- .../environment/bme280/bme280-spi_sensors.ads | 114 ++++++++ components/src/environment/bme280/bme280.adb | 225 --------------- components/src/environment/bme280/bme280.ads | 61 ---- .../bme280/impl/bme280-generic_sensor.adb | 156 ++++++++++ .../bme280/{ => impl}/bme280-i2c.adb | 7 +- .../bme280/impl/bme280-i2c_sensors.adb | 175 ++++++++++++ .../bme280/impl/bme280-internal.adb | 266 ++++++++++++++++++ .../bme280/impl/bme280-internal.ads | 100 +++++++ .../bme280/{ => impl}/bme280-spi.adb | 11 +- .../bme280/impl/bme280-spi_sensors.adb | 180 ++++++++++++ examples/stm32_f4ve/bme280_lcd/main.adb | 39 ++- 16 files changed, 1341 insertions(+), 323 deletions(-) create mode 100644 components/src/environment/bme280/bme280-generic_sensor.ads create mode 100644 components/src/environment/bme280/bme280-i2c_sensors.ads create mode 100644 components/src/environment/bme280/bme280-sensors.ads create mode 100644 components/src/environment/bme280/bme280-spi_sensors.ads create mode 100644 components/src/environment/bme280/impl/bme280-generic_sensor.adb rename components/src/environment/bme280/{ => impl}/bme280-i2c.adb (95%) create mode 100644 components/src/environment/bme280/impl/bme280-i2c_sensors.adb create mode 100644 components/src/environment/bme280/impl/bme280-internal.adb create mode 100644 components/src/environment/bme280/impl/bme280-internal.ads rename components/src/environment/bme280/{ => impl}/bme280-spi.adb (92%) create mode 100644 components/src/environment/bme280/impl/bme280-spi_sensors.adb diff --git a/components/src/environment/bme280/bme280-generic_sensor.ads b/components/src/environment/bme280/bme280-generic_sensor.ads new file mode 100644 index 000000000..20a6ae441 --- /dev/null +++ b/components/src/environment/bme280/bme280-generic_sensor.ads @@ -0,0 +1,93 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +-- This generic package contains shared code independent of the sensor +-- connection method. Following the Singleton pattern, it is convenient +-- when using only one sensor is required. + +with HAL.Time; + +generic + with procedure Read + (Data : out HAL.UInt8_Array; + Success : out Boolean); + -- Read the values from the BME280 chip registers into Data. + -- Each element in the Data corresponds to a specific register address + -- in the chip, so Data'Range determines the range of registers to read. + -- The value read from register X will be stored in Data(X), so + -- Data'Range should be of the Register_Address subtype. + + with procedure Write + (Address : Register_Address; + Data : HAL.UInt8; + Success : out Boolean); + -- Write the value to the BME280 chip register with given Address. + +package BME280.Generic_Sensor is + + function Check_Chip_Id (Expect : HAL.UInt8 := 16#60#) return Boolean; + -- Read the chip ID and check that it matches + + procedure Reset + (Timer : not null HAL.Time.Any_Delays; + Success : out Boolean); + -- Issue a soft reset and wait until the chip is ready. + + procedure Configure + (Standby : Standby_Duration := 1000.0; + Filter : IRR_Filter_Kind := Off; + SPI_3_Wire : Boolean := False; + Success : out Boolean); + -- Configure the sensor to use IRR filtering and/or SPI 3-wire mode + + procedure Start + (Mode : Sensor_Mode := Normal; + Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1; + Success : out Boolean); + -- Change sensor mode. Mainly used to start one measurement or enable + -- perpetual cycling of measurements and inactive periods. + + function Measuring return Boolean; + -- Check if a measurement is in progress + + procedure Read_Measurement + (Value : out Measurement; + Success : out Boolean); + -- Read the raw measurement values from the sensor + + procedure Read_Calibration + (Value : out Calibration_Constants; + Success : out Boolean); + -- Read the calibration constants from the sensor + +end BME280.Generic_Sensor; diff --git a/components/src/environment/bme280/bme280-i2c.ads b/components/src/environment/bme280/bme280-i2c.ads index 780938180..4f74ba888 100644 --- a/components/src/environment/bme280/bme280-i2c.ads +++ b/components/src/environment/bme280/bme280-i2c.ads @@ -29,10 +29,15 @@ -- -- ------------------------------------------------------------------------------ --- This package provides an easy way to setup BME280 connected via I2C. +-- This package offers a straightforward method for setting up the BME280 +-- when connected via I2C, especially useful when the use of only one sensor +-- is required. If you need multiple sensors, it is preferable to use the +-- BME280.I2C_Sensors package, which provides the appropriate tagged type. with HAL.I2C; +with BME280.Generic_Sensor; + generic I2C_Port : not null HAL.I2C.Any_I2C_Port; I2C_Address : HAL.UInt7 := 16#76#; -- The BME280 7-bit I2C address @@ -44,9 +49,10 @@ package BME280.I2C is -- Read registers starting from Data'First procedure Write - (Data : HAL.UInt8_Array; + (Address : Register_Address; + Data : HAL.UInt8; Success : out Boolean); - -- Write registers starting from Data'First + -- Write the value to the BME280 chip register with given Address. package Sensor is new Generic_Sensor (Read, Write); diff --git a/components/src/environment/bme280/bme280-i2c_sensors.ads b/components/src/environment/bme280/bme280-i2c_sensors.ads new file mode 100644 index 000000000..a39fd5966 --- /dev/null +++ b/components/src/environment/bme280/bme280-i2c_sensors.ads @@ -0,0 +1,115 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +-- This package provides a type representing the BME280 connected via the I2C +-- interface. + +with HAL.I2C; +with HAL.Time; + +with BME280.Sensors; + +package BME280.I2C_Sensors is + + Default_Address : constant HAL.UInt7 := 16#76#; + -- The typical BME280 7-bit I2C address + + type BME280_I2C_Sensor + (I2C_Port : not null HAL.I2C.Any_I2C_Port; + I2C_Address : HAL.UInt7) is limited new BME280.Sensors.Sensor with + record + Calibration : Calibration_Constants; + end record; + + overriding function Check_Chip_Id + (Self : BME280_I2C_Sensor; + Expect : HAL.UInt8 := 16#60#) return Boolean; + -- Read the chip ID and check that it matches + + overriding procedure Reset + (Self : BME280_I2C_Sensor; + Timer : not null HAL.Time.Any_Delays; + Success : out Boolean); + -- Issue a soft reset and wait until the chip is ready. + + overriding procedure Configure + (Self : BME280_I2C_Sensor; + Standby : Standby_Duration := 0.5; + Filter : IRR_Filter_Kind := Off; + SPI_3_Wire : Boolean := False; + Success : out Boolean); + -- Configure the sensor to use IRR filtering and/or SPI 3-wire mode + + overriding procedure Start + (Self : BME280_I2C_Sensor; + Mode : Sensor_Mode := Normal; + Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1; + Success : out Boolean); + -- Change sensor mode. Mainly used to start one measurement or enable + -- perpetual cycling of measurements and inactive periods. + + overriding function Measuring (Self : BME280_I2C_Sensor) return Boolean; + -- Check if a measurement is in progress + + overriding procedure Read_Measurement + (Self : BME280_I2C_Sensor; + Value : out Measurement; + Success : out Boolean); + -- Read the raw measurement values from the sensor + + overriding function Temperature + (Self : BME280_I2C_Sensor; + Value : Measurement) return Deci_Celsius is + (Temperature (Value, Self.Calibration)); + -- Get the temperature from raw values in 0.1 Celsius + + overriding function Humidity + (Self : BME280_I2C_Sensor; + Value : Measurement; + Temperature : Deci_Celsius) return Relative_Humidity is + (Humidity (Value, Temperature, Self.Calibration)); + -- Get the humidity from raw values + + overriding function Pressure + (Self : BME280_I2C_Sensor; + Value : Measurement; + Temperature : Deci_Celsius) return Pressure_Pa is + (Pressure (Value, Temperature, Self.Calibration)); + -- Get the pressure from raw values + + overriding procedure Read_Calibration + (Self : in out BME280_I2C_Sensor; + Success : out Boolean); + -- Read the calibration constants from the sensor + +end BME280.I2C_Sensors; diff --git a/components/src/environment/bme280/bme280-sensors.ads b/components/src/environment/bme280/bme280-sensors.ads new file mode 100644 index 000000000..4e614cf87 --- /dev/null +++ b/components/src/environment/bme280/bme280-sensors.ads @@ -0,0 +1,98 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with HAL.Time; + +package BME280.Sensors is + + type Sensor is limited interface; + + function Check_Chip_Id + (Self : Sensor; + Expect : HAL.UInt8 := 16#60#) return Boolean is abstract; + -- Read the chip ID and check that it matches + + procedure Reset + (Self : Sensor; + Timer : not null HAL.Time.Any_Delays; + Success : out Boolean) is abstract; + -- Issue a soft reset and wait until the chip is ready. + + procedure Configure + (Self : Sensor; + Standby : Standby_Duration := 0.5; + Filter : IRR_Filter_Kind := Off; + SPI_3_Wire : Boolean := False; + Success : out Boolean) is abstract; + -- Configure the sensor to use IRR filtering and/or SPI 3-wire mode + + procedure Start + (Self : Sensor; + Mode : Sensor_Mode := Normal; + Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1; + Success : out Boolean) is abstract; + -- Change sensor mode. Mainly used to start one measurement or enable + -- perpetual cycling of measurements and inactive periods. + + function Measuring (Self : Sensor) return Boolean is abstract; + -- Check if a measurement is in progress + + procedure Read_Measurement + (Self : Sensor; + Value : out Measurement; + Success : out Boolean) is abstract; + -- Read the raw measurement values from the sensor + + function Temperature + (Self : Sensor; + Value : Measurement) return Deci_Celsius is abstract; + -- Get the temperature from raw values in 0.1 Celsius + + function Humidity + (Self : Sensor; + Value : Measurement; + Temperature : Deci_Celsius) return Relative_Humidity is abstract; + -- Get the humidity from raw values + + function Pressure + (Self : Sensor; + Value : Measurement; + Temperature : Deci_Celsius) return Pressure_Pa is abstract; + -- Get the pressure from raw values + + procedure Read_Calibration + (Self : in out Sensor; + Success : out Boolean) is abstract; + -- Read the calibration constants from the sensor + +end BME280.Sensors; diff --git a/components/src/environment/bme280/bme280-spi.ads b/components/src/environment/bme280/bme280-spi.ads index 2039d8e3d..077be6777 100644 --- a/components/src/environment/bme280/bme280-spi.ads +++ b/components/src/environment/bme280/bme280-spi.ads @@ -29,11 +29,16 @@ -- -- ------------------------------------------------------------------------------ --- This package provides an easy way to setup BME280 connected via SPI. +-- This package offers a straightforward method for setting up the BME280 +-- when connected via SPI, especially useful when the use of only one sensor +-- is required. If you need multiple sensors, it is preferable to use the +-- BME280.SPI_Sensors package, which provides the appropriate tagged type. with HAL.GPIO; with HAL.SPI; +with BME280.Generic_Sensor; + generic SPI_Port : not null HAL.SPI.Any_SPI_Port; SPI_CS : not null HAL.GPIO.Any_GPIO_Point; @@ -45,9 +50,10 @@ package BME280.SPI is -- Read registers starting from Data'First procedure Write - (Data : HAL.UInt8_Array; + (Address : Register_Address; + Data : HAL.UInt8; Success : out Boolean); - -- Write registers starting from Data'First + -- Write the value to the BME280 chip register with given Address. package Sensor is new Generic_Sensor (Read, Write); diff --git a/components/src/environment/bme280/bme280-spi_sensors.ads b/components/src/environment/bme280/bme280-spi_sensors.ads new file mode 100644 index 000000000..d4c50a6a4 --- /dev/null +++ b/components/src/environment/bme280/bme280-spi_sensors.ads @@ -0,0 +1,114 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +-- This package provides a type representing the BME280 connected via the SPI +-- interface. + +with HAL.GPIO; +with HAL.SPI; +with HAL.Time; + +with BME280.Sensors; + +package BME280.SPI_Sensors is + + type BME280_SPI_Sensor + (SPI_Port : not null HAL.SPI.Any_SPI_Port; + SPI_CS : not null HAL.GPIO.Any_GPIO_Point) + is limited new BME280.Sensors.Sensor with + record + Calibration : Calibration_Constants; + end record; + + overriding function Check_Chip_Id + (Self : BME280_SPI_Sensor; + Expect : HAL.UInt8 := 16#60#) return Boolean; + -- Read the chip ID and check that it matches + + overriding procedure Reset + (Self : BME280_SPI_Sensor; + Timer : not null HAL.Time.Any_Delays; + Success : out Boolean); + -- Issue a soft reset and wait until the chip is ready. + + overriding procedure Configure + (Self : BME280_SPI_Sensor; + Standby : Standby_Duration := 0.5; + Filter : IRR_Filter_Kind := Off; + SPI_3_Wire : Boolean := False; + Success : out Boolean); + -- Configure the sensor to use IRR filtering and/or SPI 3-wire mode + + overriding procedure Start + (Self : BME280_SPI_Sensor; + Mode : Sensor_Mode := Normal; + Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1; + Success : out Boolean); + -- Change sensor mode. Mainly used to start one measurement or enable + -- perpetual cycling of measurements and inactive periods. + + overriding function Measuring (Self : BME280_SPI_Sensor) return Boolean; + -- Check if a measurement is in progress + + overriding procedure Read_Measurement + (Self : BME280_SPI_Sensor; + Value : out Measurement; + Success : out Boolean); + -- Read the raw measurement values from the sensor + + overriding function Temperature + (Self : BME280_SPI_Sensor; + Value : Measurement) return Deci_Celsius is + (Temperature (Value, Self.Calibration)); + -- Get the temperature from raw values in 0.1 Celsius + + overriding function Humidity + (Self : BME280_SPI_Sensor; + Value : Measurement; + Temperature : Deci_Celsius) return Relative_Humidity is + (Humidity (Value, Temperature, Self.Calibration)); + -- Get the humidity from raw values + + overriding function Pressure + (Self : BME280_SPI_Sensor; + Value : Measurement; + Temperature : Deci_Celsius) return Pressure_Pa is + (Pressure (Value, Temperature, Self.Calibration)); + -- Get the pressure from raw values + + overriding procedure Read_Calibration + (Self : in out BME280_SPI_Sensor; + Success : out Boolean); + -- Read the calibration constants from the sensor + +end BME280.SPI_Sensors; diff --git a/components/src/environment/bme280/bme280.adb b/components/src/environment/bme280/bme280.adb index 8f14613d6..37e86d7bb 100644 --- a/components/src/environment/bme280/bme280.adb +++ b/components/src/environment/bme280/bme280.adb @@ -31,237 +31,12 @@ pragma Ada_2022; -with Ada.Unchecked_Conversion; - package body BME280 is OS_Map : constant array (Oversampling_Kind) of Natural := [Skip => 0, X1 => 1, X2 => 2, X4 => 4, X8 => 8, X16 => 16]; -- Map Oversamping_Kind to the integer value - package body Generic_Sensor is - - ------------------- - -- Check_Chip_Id -- - ------------------- - - function Check_Chip_Id (Expect : HAL.UInt8 := 16#60#) return Boolean is - use type HAL.UInt8_Array; - - Ok : Boolean; - Data : HAL.UInt8_Array (16#D0# .. 16#D0#); - begin - Read (Data, Ok); - - return Ok and Data = [Expect]; - end Check_Chip_Id; - - --------------- - -- Configure -- - --------------- - - procedure Configure - (Standby : Standby_Duration := 1000.0; - Filter : IRR_Filter_Kind := Off; - SPI_3_Wire : Boolean := False; - Success : out Boolean) - is - use type HAL.UInt8; - Data : HAL.UInt8; - begin - if Standby = 0.5 then - Data := 0; - elsif Standby = 20.0 then - Data := 7; - elsif Standby = 10.0 then - Data := 6; - else - Data := 5; - declare - Value : Standby_Duration := 1000.0; - begin - while Value > Standby loop - Value := Value / 2; - Data := Data - 1; - end loop; - end; - end if; - Data := Data * 8 + IRR_Filter_Kind'Pos (Filter); - Data := Data * 4 + Boolean'Pos (SPI_3_Wire); - - Write ([16#F5# => Data], Success); - end Configure; - - --------------- - -- Measuring -- - --------------- - - function Measuring return Boolean is - use type HAL.UInt8; - - Ok : Boolean; - Data : HAL.UInt8_Array (16#F3# .. 16#F3#); - begin - Read (Data, Ok); - - return Ok and (Data (Data'First) and 8) /= 0; - end Measuring; - - ---------------------- - -- Read_Calibration -- - ---------------------- - - procedure Read_Calibration - (Value : out Calibration_Constants; - Success : out Boolean) - is - use Interfaces; - - function Cast is new Ada.Unchecked_Conversion - (Unsigned_16, Integer_16); - - function To_Unsigned (LSB, MSB : HAL.UInt8) return Unsigned_16 is - (Unsigned_16 (LSB) + Shift_Left (Unsigned_16 (MSB), 8)); - - function To_Integer (LSB, MSB : HAL.UInt8) return Integer_16 is - (Cast (To_Unsigned (LSB, MSB))); - begin - declare - Data : HAL.UInt8_Array (16#88# .. 16#A1#); - begin - Read (Data, Success); - - if not Success then - return; - end if; - - Value.T1 := To_Unsigned (Data (16#88#), Data (16#89#)); - Value.T2 := To_Integer (Data (16#8A#), Data (16#8B#)); - Value.T3 := To_Integer (Data (16#8C#), Data (16#8D#)); - - Value.P1 := To_Unsigned (Data (16#8E#), Data (16#8F#)); - Value.P2 := To_Integer (Data (16#90#), Data (16#91#)); - Value.P3 := To_Integer (Data (16#92#), Data (16#93#)); - Value.P4 := To_Integer (Data (16#94#), Data (16#95#)); - Value.P5 := To_Integer (Data (16#96#), Data (16#97#)); - Value.P6 := To_Integer (Data (16#98#), Data (16#99#)); - Value.P7 := To_Integer (Data (16#9A#), Data (16#9B#)); - Value.P8 := To_Integer (Data (16#9C#), Data (16#9D#)); - Value.P9 := To_Integer (Data (16#9E#), Data (16#9F#)); - - Value.H1 := Unsigned_8 (Data (16#A1#)); - end; - - declare - use type HAL.UInt8; - Data : HAL.UInt8_Array (16#E1# .. 16#E7#); - begin - Read (Data, Success); - - if not Success then - return; - end if; - - Value.H2 := To_Integer (Data (16#E1#), Data (16#E2#)); - Value.H3 := Unsigned_8 (Data (16#E3#)); - - Value.H4 := Shift_Left (Unsigned_16 (Data (16#E4#)), 4) + - Unsigned_16 (Data (16#E5#) and 16#0F#); - - Value.H5 := Shift_Right (Unsigned_16 (Data (16#E5#)), 4) + - Shift_Left (Unsigned_16 (Data (16#E6#)), 4); - - Value.H6 := Unsigned_8 (Data (16#E7#)); - end; - - end Read_Calibration; - - ---------------------- - -- Read_Measurement -- - ---------------------- - - procedure Read_Measurement - (Value : out Measurement; - Success : out Boolean) - is - use Interfaces; - Data : HAL.UInt8_Array (16#F7# .. 16#FE#); - begin - Read (Data, Success); - - if Success then - Value.Raw_Press := HAL.UInt20 - (Shift_Left (Unsigned_32 (Data (16#F7#)), 12) - + Shift_Left (Unsigned_32 (Data (16#F8#)), 4) - + Shift_Right (Unsigned_32 (Data (16#F9#)), 4)); - - Value.Raw_Temp := HAL.UInt20 - (Shift_Left (Unsigned_32 (Data (16#FA#)), 12) - + Shift_Left (Unsigned_32 (Data (16#FB#)), 4) - + Shift_Right (Unsigned_32 (Data (16#FC#)), 4)); - - Value.Raw_Hum := HAL.UInt16 - (Shift_Left (Unsigned_16 (Data (16#FD#)), 8) - + Unsigned_16 (Data (16#FE#))); - end if; - end Read_Measurement; - - ----------- - -- Reset -- - ----------- - - procedure Reset - (Timer : not null HAL.Time.Any_Delays; - Success : out Boolean) - is - use type HAL.UInt8; - - Data : HAL.UInt8_Array (16#F3# .. 16#F3#); - begin - Write ([16#E0# => 16#B6#], Success); - - if not Success then - return; - end if; - - for J in 1 .. 3 loop - Timer.Delay_Milliseconds (2); - Read (Data, Success); - - if Success and then (Data (Data'First) and 1) = 0 then - return; - end if; - end loop; - - Success := False; - end Reset; - - ----------- - -- Start -- - ----------- - - procedure Start - (Mode : Sensor_Mode := Normal; - Humidity : Oversampling_Kind := X1; - Pressure : Oversampling_Kind := X1; - Temperature : Oversampling_Kind := X1; - Success : out Boolean) - is - use type HAL.UInt8; - Data : HAL.UInt8; - begin - Write ([16#F2# => Oversampling_Kind'Pos (Humidity)], Success); - - if Success then - Data := Oversampling_Kind'Pos (Temperature); - Data := Data * 8 + Oversampling_Kind'Pos (Pressure); - Data := Data * 4 + Sensor_Mode'Enum_Rep (Mode); - Write ([16#F4# => Data], Success); - end if; - end Start; - - end Generic_Sensor; - -------------- -- Humidity -- -------------- diff --git a/components/src/environment/bme280/bme280.ads b/components/src/environment/bme280/bme280.ads index 39202afdb..5bbf54fb3 100644 --- a/components/src/environment/bme280/bme280.ads +++ b/components/src/environment/bme280/bme280.ads @@ -31,7 +31,6 @@ with Interfaces; with HAL; -with HAL.Time; package BME280 is pragma Preelaborate; @@ -120,66 +119,6 @@ package BME280 is Temperature : Oversampling_Kind := X1) return Positive; -- Typical measurement time in microseconds - generic - with procedure Read - (Data : out HAL.UInt8_Array; - Success : out Boolean); - -- Read the values from the BME280 chip registers into Data. - -- Each element in the Data corresponds to a specific register address - -- in the chip, so Data'Range determines the range of registers to read. - -- The value read from register X will be stored in Data(X), so - -- Data'Range should be of the Register_Address subtype. - - with procedure Write - (Data : HAL.UInt8_Array; - Success : out Boolean); - -- Write the values from Data to the BME280 chip registers. - -- Each element in the Data corresponds to a specific register address - -- in the chip, so Data'Range determines the range of registers to - -- write. The value for register X will be read from Data(X), so - -- Data'Range should be of the Register_Address subtype. - - package Generic_Sensor is - - function Check_Chip_Id (Expect : HAL.UInt8 := 16#60#) return Boolean; - -- Read the chip ID and check that it matches - - procedure Reset - (Timer : not null HAL.Time.Any_Delays; - Success : out Boolean); - -- Issue a soft reset and wait until the chip is ready. - - procedure Configure - (Standby : Standby_Duration := 1000.0; - Filter : IRR_Filter_Kind := Off; - SPI_3_Wire : Boolean := False; - Success : out Boolean); - -- Configure the sensor to use IRR filtering and/or SPI 3-wire mode - - procedure Start - (Mode : Sensor_Mode := Normal; - Humidity : Oversampling_Kind := X1; - Pressure : Oversampling_Kind := X1; - Temperature : Oversampling_Kind := X1; - Success : out Boolean); - -- Change sensor mode. Mainly used to start one measurement or enable - -- perpetual cycling of measurements and inactive periods. - - function Measuring return Boolean; - -- Check if a measurement is in progress - - procedure Read_Measurement - (Value : out Measurement; - Success : out Boolean); - -- Read the raw measurement values from the sensor - - procedure Read_Calibration - (Value : out Calibration_Constants; - Success : out Boolean); - -- Read the calibration constants from the sensor - - end Generic_Sensor; - private for Sensor_Mode use (Sleep => 0, Forced => 1, Normal => 3); diff --git a/components/src/environment/bme280/impl/bme280-generic_sensor.adb b/components/src/environment/bme280/impl/bme280-generic_sensor.adb new file mode 100644 index 000000000..dc1f0fde8 --- /dev/null +++ b/components/src/environment/bme280/impl/bme280-generic_sensor.adb @@ -0,0 +1,156 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with BME280.Internal; + +package body BME280.Generic_Sensor is + + type Null_Record is null record; + + Chip : constant Null_Record := (null record); + + procedure Read_Sensor + (Ignore : Null_Record; + Data : out HAL.UInt8_Array; + Success : out Boolean); + + procedure Write_Sensor + (Ignore : Null_Record; + Address : Register_Address; + Data : HAL.UInt8; + Success : out Boolean); + + ------------ + -- Sensor -- + ------------ + + package Sensor is new BME280.Internal + (Null_Record, Read_Sensor, Write_Sensor); + + ------------------- + -- Check_Chip_Id -- + ------------------- + + function Check_Chip_Id (Expect : HAL.UInt8 := 16#60#) return Boolean + is (Sensor.Check_Chip_Id (Chip, Expect)); + + --------------- + -- Configure -- + --------------- + + procedure Configure + (Standby : Standby_Duration := 1000.0; + Filter : IRR_Filter_Kind := Off; + SPI_3_Wire : Boolean := False; + Success : out Boolean) is + begin + Sensor.Configure (Chip, Standby, Filter, SPI_3_Wire, Success); + end Configure; + + --------------- + -- Measuring -- + --------------- + + function Measuring return Boolean is (Sensor.Measuring (Chip)); + + ---------------------- + -- Read_Calibration -- + ---------------------- + + procedure Read_Calibration + (Value : out Calibration_Constants; + Success : out Boolean) is + begin + Sensor.Read_Calibration (Chip, Value, Success); + end Read_Calibration; + + ---------------------- + -- Read_Measurement -- + ---------------------- + + procedure Read_Measurement + (Value : out Measurement; + Success : out Boolean) is + begin + Sensor.Read_Measurement (Chip, Value, Success); + end Read_Measurement; + + ----------------- + -- Read_Sensor -- + ----------------- + + procedure Read_Sensor + (Ignore : Null_Record; + Data : out HAL.UInt8_Array; + Success : out Boolean) is + begin + Read (Data, Success); + end Read_Sensor; + + ----------- + -- Reset -- + ----------- + + procedure Reset + (Timer : not null HAL.Time.Any_Delays; + Success : out Boolean) is + begin + Sensor.Reset (Chip, Timer, Success); + end Reset; + + ----------- + -- Start -- + ----------- + + procedure Start + (Mode : Sensor_Mode := Normal; + Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1; + Success : out Boolean) is + begin + Sensor.Start (Chip, Mode, Humidity, Pressure, Temperature, Success); + end Start; + + ------------------ + -- Write_Sensor -- + ------------------ + + procedure Write_Sensor + (Ignore : Null_Record; + Address : Register_Address; + Data : HAL.UInt8; + Success : out Boolean) is + begin + Write (Address, Data, Success); + end Write_Sensor; + +end BME280.Generic_Sensor; diff --git a/components/src/environment/bme280/bme280-i2c.adb b/components/src/environment/bme280/impl/bme280-i2c.adb similarity index 95% rename from components/src/environment/bme280/bme280-i2c.adb rename to components/src/environment/bme280/impl/bme280-i2c.adb index 85d4b525e..b87d02376 100644 --- a/components/src/environment/bme280/bme280-i2c.adb +++ b/components/src/environment/bme280/impl/bme280-i2c.adb @@ -59,7 +59,8 @@ package body BME280.I2C is ----------- procedure Write - (Data : HAL.UInt8_Array; + (Address : Register_Address; + Data : HAL.UInt8; Success : out Boolean) is use type HAL.I2C.I2C_Status; @@ -69,9 +70,9 @@ package body BME280.I2C is begin I2C_Port.Mem_Write (Addr => 2 * HAL.UInt10 (I2C_Address), - Mem_Addr => HAL.UInt16 (Data'First), + Mem_Addr => HAL.UInt16 (Address), Mem_Addr_Size => HAL.I2C.Memory_Size_8b, - Data => Data, + Data => (1 => Data), Status => Status); Success := Status = HAL.I2C.Ok; diff --git a/components/src/environment/bme280/impl/bme280-i2c_sensors.adb b/components/src/environment/bme280/impl/bme280-i2c_sensors.adb new file mode 100644 index 000000000..972707a1f --- /dev/null +++ b/components/src/environment/bme280/impl/bme280-i2c_sensors.adb @@ -0,0 +1,175 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with BME280.Internal; + +package body BME280.I2C_Sensors is + + procedure Read + (Self : BME280_I2C_Sensor'Class; + Data : out HAL.UInt8_Array; + Success : out Boolean); + + procedure Write + (Self : BME280_I2C_Sensor'Class; + Address : Register_Address; + Data : HAL.UInt8; + Success : out Boolean); + + package Sensor is + new BME280.Internal (BME280_I2C_Sensor'Class, Read, Write); + + ------------------- + -- Check_Chip_Id -- + ------------------- + + overriding function Check_Chip_Id + (Self : BME280_I2C_Sensor; + Expect : HAL.UInt8 := 16#60#) return Boolean is + (Sensor.Check_Chip_Id (Self, Expect)); + + --------------- + -- Configure -- + --------------- + + overriding procedure Configure + (Self : BME280_I2C_Sensor; + Standby : Standby_Duration := 0.5; + Filter : IRR_Filter_Kind := Off; + SPI_3_Wire : Boolean := False; + Success : out Boolean) is + begin + Sensor.Configure (Self, Standby, Filter, SPI_3_Wire, Success); + end Configure; + + overriding function Measuring (Self : BME280_I2C_Sensor) return Boolean is + (Sensor.Measuring (Self)); + + ---------- + -- Read -- + ---------- + + procedure Read + (Self : BME280_I2C_Sensor'Class; + Data : out HAL.UInt8_Array; + Success : out Boolean) + is + use type HAL.I2C.I2C_Status; + use type HAL.UInt10; + + Status : HAL.I2C.I2C_Status; + begin + Self.I2C_Port.Mem_Read + (Addr => 2 * HAL.UInt10 (Self.I2C_Address), + Mem_Addr => HAL.UInt16 (Data'First), + Mem_Addr_Size => HAL.I2C.Memory_Size_8b, + Data => Data, + Status => Status); + + Success := Status = HAL.I2C.Ok; + end Read; + + ---------------------- + -- Read_Measurement -- + ---------------------- + + overriding procedure Read_Measurement + (Self : BME280_I2C_Sensor; + Value : out Measurement; + Success : out Boolean) is + begin + Sensor.Read_Measurement (Self, Value, Success); + end Read_Measurement; + + ---------------------- + -- Read_Calibration -- + ---------------------- + + overriding procedure Read_Calibration + (Self : in out BME280_I2C_Sensor; + Success : out Boolean) is + begin + Sensor.Read_Calibration (Self, Self.Calibration, Success); + end Read_Calibration; + + ----------- + -- Reset -- + ----------- + + overriding procedure Reset + (Self : BME280_I2C_Sensor; + Timer : not null HAL.Time.Any_Delays; + Success : out Boolean) is + begin + Sensor.Reset (Self, Timer, Success); + end Reset; + + ----------- + -- Start -- + ----------- + + overriding procedure Start + (Self : BME280_I2C_Sensor; + Mode : Sensor_Mode := Normal; + Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1; + Success : out Boolean) is + begin + Sensor.Start (Self, Mode, Humidity, Pressure, Temperature, Success); + end Start; + + ----------- + -- Write -- + ----------- + + procedure Write + (Self : BME280_I2C_Sensor'Class; + Address : Register_Address; + Data : HAL.UInt8; + Success : out Boolean) + is + use type HAL.I2C.I2C_Status; + use type HAL.UInt10; + + Status : HAL.I2C.I2C_Status; + begin + Self.I2C_Port.Mem_Write + (Addr => 2 * HAL.UInt10 (Self.I2C_Address), + Mem_Addr => HAL.UInt16 (Address), + Mem_Addr_Size => HAL.I2C.Memory_Size_8b, + Data => (1 => Data), + Status => Status); + + Success := Status = HAL.I2C.Ok; + end Write; + +end BME280.I2C_Sensors; diff --git a/components/src/environment/bme280/impl/bme280-internal.adb b/components/src/environment/bme280/impl/bme280-internal.adb new file mode 100644 index 000000000..e88e6115d --- /dev/null +++ b/components/src/environment/bme280/impl/bme280-internal.adb @@ -0,0 +1,266 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +pragma Ada_2022; + +with Ada.Unchecked_Conversion; + +package body BME280.Internal is + + ------------------- + -- Check_Chip_Id -- + ------------------- + + function Check_Chip_Id + (Device : Device_Context; + Expect : HAL.UInt8) return Boolean + is + use type HAL.UInt8_Array; + + Ok : Boolean; + Data : HAL.UInt8_Array (16#D0# .. 16#D0#); + begin + Read (Device, Data, Ok); + + return Ok and Data = [Expect]; + end Check_Chip_Id; + + --------------- + -- Configure -- + --------------- + + procedure Configure + (Device : Device_Context; + Standby : Standby_Duration; + Filter : IRR_Filter_Kind; + SPI_3_Wire : Boolean; + Success : out Boolean) + is + use type HAL.UInt8; + Data : HAL.UInt8; + begin + if Standby = 0.5 then + Data := 0; + elsif Standby = 20.0 then + Data := 7; + elsif Standby = 10.0 then + Data := 6; + else + Data := 5; + declare + Value : Standby_Duration := 1000.0; + begin + while Value > Standby loop + Value := Value / 2; + Data := Data - 1; + end loop; + end; + end if; + Data := Data * 8 + IRR_Filter_Kind'Pos (Filter); + Data := Data * 4 + Boolean'Pos (SPI_3_Wire); + + Write (Device, 16#F5#, Data, Success); + end Configure; + + --------------- + -- Measuring -- + --------------- + + function Measuring (Device : Device_Context) return Boolean is + use type HAL.UInt8; + + Ok : Boolean; + Data : HAL.UInt8_Array (16#F3# .. 16#F3#); + begin + Read (Device, Data, Ok); + + return Ok and (Data (Data'First) and 8) /= 0; + end Measuring; + + ---------------------- + -- Read_Calibration -- + ---------------------- + + procedure Read_Calibration + (Device : Device_Context; + Value : out Calibration_Constants; + Success : out Boolean) + is + use Interfaces; + + function Cast is new Ada.Unchecked_Conversion + (Unsigned_16, Integer_16); + + function To_Unsigned (LSB, MSB : HAL.UInt8) return Unsigned_16 is + (Unsigned_16 (LSB) + Shift_Left (Unsigned_16 (MSB), 8)); + + function To_Integer (LSB, MSB : HAL.UInt8) return Integer_16 is + (Cast (To_Unsigned (LSB, MSB))); + begin + declare + Data : HAL.UInt8_Array (16#88# .. 16#A1#); + begin + Read (Device, Data, Success); + + if not Success then + return; + end if; + + Value.T1 := To_Unsigned (Data (16#88#), Data (16#89#)); + Value.T2 := To_Integer (Data (16#8A#), Data (16#8B#)); + Value.T3 := To_Integer (Data (16#8C#), Data (16#8D#)); + + Value.P1 := To_Unsigned (Data (16#8E#), Data (16#8F#)); + Value.P2 := To_Integer (Data (16#90#), Data (16#91#)); + Value.P3 := To_Integer (Data (16#92#), Data (16#93#)); + Value.P4 := To_Integer (Data (16#94#), Data (16#95#)); + Value.P5 := To_Integer (Data (16#96#), Data (16#97#)); + Value.P6 := To_Integer (Data (16#98#), Data (16#99#)); + Value.P7 := To_Integer (Data (16#9A#), Data (16#9B#)); + Value.P8 := To_Integer (Data (16#9C#), Data (16#9D#)); + Value.P9 := To_Integer (Data (16#9E#), Data (16#9F#)); + + Value.H1 := Unsigned_8 (Data (16#A1#)); + end; + + declare + use type HAL.UInt8; + Data : HAL.UInt8_Array (16#E1# .. 16#E7#); + begin + Read (Device, Data, Success); + + if not Success then + return; + end if; + + Value.H2 := To_Integer (Data (16#E1#), Data (16#E2#)); + Value.H3 := Unsigned_8 (Data (16#E3#)); + + Value.H4 := Shift_Left (Unsigned_16 (Data (16#E4#)), 4) + + Unsigned_16 (Data (16#E5#) and 16#0F#); + + Value.H5 := Shift_Right (Unsigned_16 (Data (16#E5#)), 4) + + Shift_Left (Unsigned_16 (Data (16#E6#)), 4); + + Value.H6 := Unsigned_8 (Data (16#E7#)); + end; + + end Read_Calibration; + + ---------------------- + -- Read_Measurement -- + ---------------------- + + procedure Read_Measurement + (Device : Device_Context; + Value : out Measurement; + Success : out Boolean) + is + use Interfaces; + Data : HAL.UInt8_Array (16#F7# .. 16#FE#); + begin + Read (Device, Data, Success); + + if Success then + Value.Raw_Press := HAL.UInt20 + (Shift_Left (Unsigned_32 (Data (16#F7#)), 12) + + Shift_Left (Unsigned_32 (Data (16#F8#)), 4) + + Shift_Right (Unsigned_32 (Data (16#F9#)), 4)); + + Value.Raw_Temp := HAL.UInt20 + (Shift_Left (Unsigned_32 (Data (16#FA#)), 12) + + Shift_Left (Unsigned_32 (Data (16#FB#)), 4) + + Shift_Right (Unsigned_32 (Data (16#FC#)), 4)); + + Value.Raw_Hum := HAL.UInt16 + (Shift_Left (Unsigned_16 (Data (16#FD#)), 8) + + Unsigned_16 (Data (16#FE#))); + end if; + end Read_Measurement; + + ----------- + -- Reset -- + ----------- + + procedure Reset + (Device : Device_Context; + Timer : not null HAL.Time.Any_Delays; + Success : out Boolean) + is + use type HAL.UInt8; + + Data : HAL.UInt8_Array (16#F3# .. 16#F3#); + begin + Write (Device, 16#E0#, 16#B6#, Success); + + if not Success then + return; + end if; + + for J in 1 .. 3 loop + Timer.Delay_Milliseconds (2); + Read (Device, Data, Success); + + if Success and then (Data (Data'First) and 1) = 0 then + return; + end if; + end loop; + + Success := False; + end Reset; + + ----------- + -- Start -- + ----------- + + procedure Start + (Device : Device_Context; + Mode : Sensor_Mode; + Humidity : Oversampling_Kind; + Pressure : Oversampling_Kind; + Temperature : Oversampling_Kind; + Success : out Boolean) + is + use type HAL.UInt8; + Data : HAL.UInt8; + begin + Write (Device, 16#F2#, Oversampling_Kind'Pos (Humidity), Success); + + if Success then + Data := Oversampling_Kind'Pos (Temperature); + Data := Data * 8 + Oversampling_Kind'Pos (Pressure); + Data := Data * 4 + Sensor_Mode'Enum_Rep (Mode); + Write (Device, 16#F4#, Data, Success); + end if; + end Start; + + +end BME280.Internal; diff --git a/components/src/environment/bme280/impl/bme280-internal.ads b/components/src/environment/bme280/impl/bme280-internal.ads new file mode 100644 index 000000000..75e49506c --- /dev/null +++ b/components/src/environment/bme280/impl/bme280-internal.ads @@ -0,0 +1,100 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with HAL.Time; + +generic + type Device_Context (<>) is limited private; + + with procedure Read + (Device : Device_Context; + Data : out HAL.UInt8_Array; + Success : out Boolean); + -- Read the values from the BME280 chip registers into Data. + -- Each element in the Data corresponds to a specific register address + -- in the chip, so Data'Range determines the range of registers to read. + -- The value read from register X will be stored in Data(X), so + -- Data'Range should be of the Register_Address subtype. + + with procedure Write + (Device : Device_Context; + Address : Register_Address; + Data : HAL.UInt8; + Success : out Boolean); + -- Write the value to the BME280 chip register with given Address. + +package BME280.Internal is + + function Check_Chip_Id + (Device : Device_Context; + Expect : HAL.UInt8) return Boolean; + -- Read the chip ID and check that it matches + + procedure Reset + (Device : Device_Context; + Timer : not null HAL.Time.Any_Delays; + Success : out Boolean); + -- Issue a soft reset and wait until the chip is ready. + + procedure Configure + (Device : Device_Context; + Standby : Standby_Duration; + Filter : IRR_Filter_Kind; + SPI_3_Wire : Boolean; + Success : out Boolean); + -- Configure the sensor to use IRR filtering and/or SPI 3-wire mode + + procedure Start + (Device : Device_Context; + Mode : Sensor_Mode; + Humidity : Oversampling_Kind; + Pressure : Oversampling_Kind; + Temperature : Oversampling_Kind; + Success : out Boolean); + -- Change sensor mode. Mainly used to start one measurement or enable + -- perpetual cycling of measurements and inactive periods. + + function Measuring (Device : Device_Context) return Boolean; + -- Check if a measurement is in progress + + procedure Read_Measurement + (Device : Device_Context; + Value : out Measurement; + Success : out Boolean); + -- Read the raw measurement values from the sensor + + procedure Read_Calibration + (Device : Device_Context; + Value : out Calibration_Constants; + Success : out Boolean); + -- Read the calibration constants from the sensor + +end BME280.Internal; diff --git a/components/src/environment/bme280/bme280-spi.adb b/components/src/environment/bme280/impl/bme280-spi.adb similarity index 92% rename from components/src/environment/bme280/bme280-spi.adb rename to components/src/environment/bme280/impl/bme280-spi.adb index 6865abbe3..912b2a19d 100644 --- a/components/src/environment/bme280/bme280-spi.adb +++ b/components/src/environment/bme280/impl/bme280-spi.adb @@ -64,22 +64,19 @@ package body BME280.SPI is ----------- procedure Write - (Data : HAL.UInt8_Array; + (Address : Register_Address; + Data : HAL.UInt8; Success : out Boolean) is use type HAL.UInt8; use all type HAL.SPI.SPI_Status; - Addr : HAL.UInt8; + Prefix : constant HAL.UInt8 := HAL.UInt8 (Address) and 16#7F#; Status : HAL.SPI.SPI_Status; begin SPI.SPI_CS.Clear; - for J in Data'Range loop - Addr := HAL.UInt8 (J) and 16#7F#; - SPI_Port.Transmit (HAL.SPI.SPI_Data_8b'(Addr, Data (J)), Status); - exit when Status /= Ok; - end loop; + SPI_Port.Transmit (HAL.SPI.SPI_Data_8b'(Prefix, Data), Status); SPI.SPI_CS.Set; diff --git a/components/src/environment/bme280/impl/bme280-spi_sensors.adb b/components/src/environment/bme280/impl/bme280-spi_sensors.adb new file mode 100644 index 000000000..5517a5bc1 --- /dev/null +++ b/components/src/environment/bme280/impl/bme280-spi_sensors.adb @@ -0,0 +1,180 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2023, AdaCore -- +-- -- +-- Redistribution and use in source and binary forms, with or without -- +-- modification, are permitted provided that the following conditions are -- +-- met: -- +-- 1. Redistributions of source code must retain the above copyright -- +-- notice, this list of conditions and the following disclaimer. -- +-- 2. Redistributions in binary form must reproduce the above copyright -- +-- notice, this list of conditions and the following disclaimer in -- +-- the documentation and/or other materials provided with the -- +-- distribution. -- +-- 3. Neither the name of the copyright holder nor the names of its -- +-- contributors may be used to endorse or promote products derived -- +-- from this software without specific prior written permission. -- +-- -- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- +-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- +-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- +-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- +-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- +-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- +-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- +-- -- +------------------------------------------------------------------------------ + +with BME280.Internal; + +package body BME280.SPI_Sensors is + + procedure Read + (Self : BME280_SPI_Sensor'Class; + Data : out HAL.UInt8_Array; + Success : out Boolean); + + procedure Write + (Self : BME280_SPI_Sensor'Class; + Address : Register_Address; + Data : HAL.UInt8; + Success : out Boolean); + + package Sensor is + new BME280.Internal (BME280_SPI_Sensor'Class, Read, Write); + + ------------------- + -- Check_Chip_Id -- + ------------------- + + overriding function Check_Chip_Id + (Self : BME280_SPI_Sensor; + Expect : HAL.UInt8 := 16#60#) return Boolean is + (Sensor.Check_Chip_Id (Self, Expect)); + + --------------- + -- Configure -- + --------------- + + overriding procedure Configure + (Self : BME280_SPI_Sensor; + Standby : Standby_Duration := 0.5; + Filter : IRR_Filter_Kind := Off; + SPI_3_Wire : Boolean := False; + Success : out Boolean) is + begin + Sensor.Configure (Self, Standby, Filter, SPI_3_Wire, Success); + end Configure; + + overriding function Measuring (Self : BME280_SPI_Sensor) return Boolean is + (Sensor.Measuring (Self)); + + ---------- + -- Read -- + ---------- + + procedure Read + (Self : BME280_SPI_Sensor'Class; + Data : out HAL.UInt8_Array; + Success : out Boolean) + is + use type HAL.UInt8; + use all type HAL.SPI.SPI_Status; + + Addr : HAL.UInt8; + Status : HAL.SPI.SPI_Status; + begin + Self.SPI_CS.Clear; + + Addr := HAL.UInt8 (Data'First) or 16#80#; + Self.SPI_Port.Transmit (HAL.SPI.SPI_Data_8b'(1 => Addr), Status); + + if Status = Ok then + Self.SPI_Port.Receive (HAL.SPI.SPI_Data_8b (Data), Status); + end if; + + Self.SPI_CS.Set; + + Success := Status = Ok; + end Read; + + ---------------------- + -- Read_Measurement -- + ---------------------- + + overriding procedure Read_Measurement + (Self : BME280_SPI_Sensor; + Value : out Measurement; + Success : out Boolean) is + begin + Sensor.Read_Measurement (Self, Value, Success); + end Read_Measurement; + + ---------------------- + -- Read_Calibration -- + ---------------------- + + overriding procedure Read_Calibration + (Self : in out BME280_SPI_Sensor; + Success : out Boolean) is + begin + Sensor.Read_Calibration (Self, Self.Calibration, Success); + end Read_Calibration; + + ----------- + -- Reset -- + ----------- + + overriding procedure Reset + (Self : BME280_SPI_Sensor; + Timer : not null HAL.Time.Any_Delays; + Success : out Boolean) is + begin + Sensor.Reset (Self, Timer, Success); + end Reset; + + ----------- + -- Start -- + ----------- + + overriding procedure Start + (Self : BME280_SPI_Sensor; + Mode : Sensor_Mode := Normal; + Humidity : Oversampling_Kind := X1; + Pressure : Oversampling_Kind := X1; + Temperature : Oversampling_Kind := X1; + Success : out Boolean) is + begin + Sensor.Start (Self, Mode, Humidity, Pressure, Temperature, Success); + end Start; + + ----------- + -- Write -- + ----------- + + procedure Write + (Self : BME280_SPI_Sensor'Class; + Address : Register_Address; + Data : HAL.UInt8; + Success : out Boolean) + is + use type HAL.UInt8; + use all type HAL.SPI.SPI_Status; + + Prefix : constant HAL.UInt8 := HAL.UInt8 (Address) and 16#7F#; + Status : HAL.SPI.SPI_Status; + begin + Self.SPI_CS.Clear; + + Self.SPI_Port.Transmit (HAL.SPI.SPI_Data_8b'(Prefix, Data), Status); + + Self.SPI_CS.Set; + + Success := Status = Ok; + end Write; + +end BME280.SPI_Sensors; diff --git a/examples/stm32_f4ve/bme280_lcd/main.adb b/examples/stm32_f4ve/bme280_lcd/main.adb index 93eb41667..6b16ce6f0 100644 --- a/examples/stm32_f4ve/bme280_lcd/main.adb +++ b/examples/stm32_f4ve/bme280_lcd/main.adb @@ -47,7 +47,7 @@ with Display_ILI9341; with Bitmapped_Drawing; with BMP_Fonts; -with BME280.I2C; +with BME280.I2C_Sensors; with GUI; with GUI_Buttons; @@ -55,9 +55,10 @@ with GUI_Buttons; procedure Main is use type Ada.Real_Time.Time; - package BME280_I2C is new BME280.I2C + Sensor : BME280.I2C_Sensors.BME280_I2C_Sensor := (I2C_Port => STM32.Device.I2C_1'Access, - I2C_Address => 16#76#); + I2C_Address => 16#76#, + Calibration => <>); procedure Configure_Sensor; -- Restart sensor with new settings according to GUI state @@ -68,8 +69,7 @@ procedure Main is Press : BME280.Pressure_Pa; end record; - function Read_Sensor - (Calib : BME280.Calibration_Constants) return Sensor_Data; + function Read_Sensor return Sensor_Data; function Min (Left, Right : Sensor_Data) return Sensor_Data is (Temp => BME280.Deci_Celsius'Min (Left.Temp, Right.Temp), @@ -149,7 +149,7 @@ procedure Main is Ok : Boolean; begin -- Consigure IRR filter and minimal incativity delay - BME280_I2C.Sensor.Configure + Sensor.Configure (Standby => 0.5, Filter => Filter (GUI.State (+Fi_No .. +Fi_16)), SPI_3_Wire => False, @@ -157,7 +157,7 @@ procedure Main is pragma Assert (Ok); -- Enable cycling of measurements with given oversamplig - BME280_I2C.Sensor.Start + Sensor.Start (Mode => BME280.Normal, Humidity => Oversampling (GUI.State (+Hu_X1 .. +Hu_16)), Pressure => Oversampling (GUI.State (+Pr_X1 .. +Pr_16)), @@ -291,22 +291,20 @@ procedure Main is -- Read_Sensor -- ----------------- - function Read_Sensor - (Calib : BME280.Calibration_Constants) return Sensor_Data - is + function Read_Sensor return Sensor_Data is Ok : Boolean; Measurement : BME280.Measurement; Temp : BME280.Deci_Celsius; begin - BME280_I2C.Sensor.Read_Measurement (Measurement, Ok); + Sensor.Read_Measurement (Measurement, Ok); pragma Assert (Ok); - Temp := BME280.Temperature (Measurement, Calib); + Temp := Sensor.Temperature (Measurement); return - (Temp => Temp, - Humi => BME280.Humidity (Measurement, Temp, Calib), - Press => BME280.Pressure (Measurement, Temp, Calib)); + (Temp => Temp, + Humi => Sensor.Humidity (Measurement, Temp), + Press => Sensor.Pressure (Measurement, Temp)); end Read_Sensor; Empty : constant Sensor_Limits := @@ -324,7 +322,6 @@ procedure Main is Next : Ada.Real_Time.Time := Ada.Real_Time.Clock; Ok : Boolean; - Calib : BME280.Calibration_Constants; Next_Limits : Sensor_Limits; begin STM32.Board.Initialize_LEDs; @@ -347,17 +344,17 @@ begin Clock_Speed => 400_000); -- Look for BME280 chip - if not BME280_I2C.Sensor.Check_Chip_Id then + if not Sensor.Check_Chip_Id then Ada.Text_IO.Put_Line ("BME280 not found."); raise Program_Error; end if; -- Reset BME280 - BME280_I2C.Sensor.Reset (Ravenscar_Time.Delays, Ok); + Sensor.Reset (Ravenscar_Time.Delays, Ok); pragma Assert (Ok); -- Read calibration data into Clib - BME280_I2C.Sensor.Read_Calibration (Calib, Ok); + Sensor.Read_Calibration (Ok); Configure_Sensor; @@ -369,7 +366,7 @@ begin Temperature => BME280.X1) / 1000 + 1); -- Predict boundaries from the first sensor measurement - Next_Limits.Min := Read_Sensor (Calib); + Next_Limits.Min := Read_Sensor; Next_Limits.Max := Next_Limits.Min; Make_Wider (Next_Limits); @@ -385,7 +382,7 @@ begin for X in 0 .. LCD.Width - 1 loop STM32.Board.Toggle (STM32.Board.D1_LED); - Data := Read_Sensor (Calib); + Data := Read_Sensor; Next_Limits := (Min => Min (Data, Next_Limits.Min),