From 2e297f5e9d70d655f423115d5cb94247a48069d0 Mon Sep 17 00:00:00 2001 From: Maxim Ogorodnik Date: Mon, 29 Dec 2025 21:01:39 +0100 Subject: [PATCH] Added support for Bmp2890 thermometer-barometer sensor --- .../src/sensors/barometer/BMP280/README.md | 2 + .../src/sensors/combined/BMP280/README.md | 6 + .../src/sensors/combined/BMP280/bmp280.adb | 551 ++++++++++++++++++ .../src/sensors/combined/BMP280/bmp280.ads | 330 +++++++++++ .../src/sensors/thermometer/BMP280/README.md | 2 + .../STM32F429_Discovery/bmp280_f429disco.gpr | 29 + examples/shared/Bmp280/.gdbinit | 24 + examples/shared/Bmp280/README.md | 11 + examples/shared/Bmp280/src/bmp280_example.adb | 227 ++++++++ examples/shared/Bmp280/src/watchdogs.adb | 63 ++ examples/shared/Bmp280/src/watchdogs.ads | 63 ++ 11 files changed, 1308 insertions(+) create mode 100644 components/src/sensors/barometer/BMP280/README.md create mode 100644 components/src/sensors/combined/BMP280/README.md create mode 100644 components/src/sensors/combined/BMP280/bmp280.adb create mode 100644 components/src/sensors/combined/BMP280/bmp280.ads create mode 100644 components/src/sensors/thermometer/BMP280/README.md create mode 100644 examples/STM32F429_Discovery/bmp280_f429disco.gpr create mode 100644 examples/shared/Bmp280/.gdbinit create mode 100644 examples/shared/Bmp280/README.md create mode 100644 examples/shared/Bmp280/src/bmp280_example.adb create mode 100644 examples/shared/Bmp280/src/watchdogs.adb create mode 100644 examples/shared/Bmp280/src/watchdogs.ads diff --git a/components/src/sensors/barometer/BMP280/README.md b/components/src/sensors/barometer/BMP280/README.md new file mode 100644 index 000000000..aec3fffa6 --- /dev/null +++ b/components/src/sensors/barometer/BMP280/README.md @@ -0,0 +1,2 @@ +See components/src/sensors/combined/BMP280 + diff --git a/components/src/sensors/combined/BMP280/README.md b/components/src/sensors/combined/BMP280/README.md new file mode 100644 index 000000000..82b10ded0 --- /dev/null +++ b/components/src/sensors/combined/BMP280/README.md @@ -0,0 +1,6 @@ +The BMP280 is an absolute barometric pressure sensor especially designed for mobile applications. + +Pressure resolution 0.16 Pa + +Temperature resolution 0.01°C + diff --git a/components/src/sensors/combined/BMP280/bmp280.adb b/components/src/sensors/combined/BMP280/bmp280.adb new file mode 100644 index 000000000..a0adaf2a2 --- /dev/null +++ b/components/src/sensors/combined/BMP280/bmp280.adb @@ -0,0 +1,551 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2025, 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 BMP280 is + + function Time_Standby_To_UInt3 + (Time_Standby : Time_Standby_Type) return UInt3; + + ---------------- + -- Initialize -- + ---------------- + + procedure Initialize + (This : in out BMP280_Device; + CS : Any_GPIO_Point; + SPI : Any_SPI_Port; + Error : out Boolean) is + begin + This.Holder := + (CS => CS, + SPI => SPI, + Chip_ID => 0, + Dig_T1 => 0, + Dig_T2 => 0, + Dig_T3 => 0, + dig_P1 => 0, + dig_P2 => 0, + dig_P3 => 0, + dig_P4 => 0, + dig_P5 => 0, + dig_P6 => 0, + dig_P7 => 0, + dig_P8 => 0, + dig_P9 => 0); + + if Get_Chip_ID (This) > 0 then + Read_Trim_Registers (This); + Error := False; + else + Error := True; + end if; + + exception + when others => + Error := True; + end Initialize; + + ------------ + -- CS_Low -- + ------------ + + procedure CS_Low (This : BMP280_Device) is + begin + if This.Holder.CS /= null then + This.Holder.CS.Clear; + end if; + end CS_Low; + + ------------- + -- CS_High -- + ------------- + + procedure CS_High (This : BMP280_Device) is + begin + if This.Holder.CS /= null then + This.Holder.CS.Set; + end if; + end CS_High; + + ------------------- + -- Read_Register -- + ------------------- + + function Read_Register + (This : BMP280_Device; + Register : Register_Name) + return UInt8 + is + Buf : SPI_Data_8b (1 .. 1); + Cmd : Command := + (Cmd => Read, + Addr => Adresses (Register)) with + Address => Buf (Buf'First)'Address; + Status : HAL.SPI.SPI_Status; + begin + CS_Low (This); + This.Holder.SPI.Transmit (Buf, Status); + + if Status = HAL.SPI.Ok then + This.Holder.SPI.Receive (Buf, Status); + end if; + + if Status /= HAL.SPI.Ok then + raise Program_Error; + end if; + + CS_High (This); + return Buf (Buf'First); + end Read_Register; + + -------------------- + -- Write_Register -- + -------------------- + + procedure Write_Register + (This : BMP280_Device; + Register : Register_Name; + Data : UInt8) + is + Buf : SPI_Data_8b (1 .. 2); + Cmd : Command := + (Cmd => Write, + Addr => Adresses (Register)) with + Address => Buf (Buf'First)'Address; + Local : UInt8 := Data + with Address => Buf (Buf'First + 1)'Address; + + Status : HAL.SPI.SPI_Status; + begin + CS_Low (This); + + This.Holder.SPI.Transmit (Buf, Status); + + CS_High (This); + + if Status /= HAL.SPI.Ok then + raise Program_Error; + end if; + end Write_Register; + + ------------------- + -- Read_Register -- + ------------------- + + function Read_Register + (This : BMP280_Device; + Msb : Register_Name; + Lsb : Register_Name) + return UInt16 is + begin + return Shift_Left (UInt16 (Read_Register (This, Msb)), 8) + + UInt16 (Read_Register (This, Lsb)); + end Read_Register; + + ------------------- + -- Read_Register -- + ------------------- + + function Read_Register + (This : BMP280_Device; + Msb : Register_Name; + Lsb : Register_Name) + return Interfaces.Integer_16 + is + Tmp : UInt16 := Read_Register (This, Msb, Lsb); + Result : Interfaces.Integer_16 with Import, Address => Tmp'Address; + begin + return Result; + end Read_Register; + + ------------------------- + -- Read_Trim_Registers -- + ------------------------- + + procedure Read_Trim_Registers (This : in out BMP280_Device) is + begin + This.Holder.Dig_T1 := Integer_32 + (UInt16'(Read_Register (This, dig_T1_msb, dig_T1_lsb))); + + This.Holder.Dig_T2 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_T2_msb, dig_T2_lsb))); + + This.Holder.Dig_T3 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_T3_msb, dig_T3_lsb))); + + This.Holder.dig_P1 := Integer_32 + (UInt16'(Read_Register (This, dig_P1_msb, dig_P1_lsb))); + + This.Holder.dig_P2 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_P2_msb, dig_P2_lsb))); + + This.Holder.dig_P3 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_P3_msb, dig_P3_lsb))); + + This.Holder.dig_P4 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_P4_msb, dig_P4_lsb))); + + This.Holder.dig_P5 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_P5_msb, dig_P5_lsb))); + + This.Holder.dig_P6 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_P6_msb, dig_P6_lsb))); + + This.Holder.dig_P7 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_P7_msb, dig_P7_lsb))); + + This.Holder.dig_P8 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_P8_msb, dig_P8_lsb))); + + This.Holder.dig_P9 := Integer_32 + (Interfaces.Integer_16'(Read_Register (This, dig_P9_msb, dig_P9_lsb))); + end Read_Trim_Registers; + + ------------------- + -- Read_Register -- + ------------------- + + function Read_Register + (This : BMP280_Device; + Precision : Precision_Type; + Msb : Register_Name; + Lsb : Register_Name; + Xlsb : Register_Name) + return Interfaces.Integer_32 + is + Xlsb_Data : UInt8 := Read_Register (This, Xlsb); + Sb_Data : constant UInt32 := UInt32 + (UInt16'(Read_Register (This => This, Msb => Msb, Lsb => Lsb))); + begin + case Precision is + when Skipped | Ultra_Low_Power => + null; + + when Low_Power => + Xlsb_Data := Xlsb_Data and 2#1000_0000#; + + when Standard_Resolution => + Xlsb_Data := Xlsb_Data and 2#1100_0000#; + + when High_Resolution => + Xlsb_Data := Xlsb_Data and 2#1110_0000#; + + when Ultra_High_Resolution => + Xlsb_Data := Xlsb_Data and 2#1111_0000#; + end case; + + return Interfaces.Integer_32 + (Shift_Right (Shift_Left (Sb_Data, 8) + UInt32 (Xlsb_Data), 4)); + end Read_Register; + + ----------------- + -- Get_Chip_ID -- + ----------------- + + function Get_Chip_ID (This : BMP280_Device) return Chip_ID is + begin + return Chip_ID (Read_Register (This, id)); + end Get_Chip_ID; + + ----------- + -- Reset -- + ----------- + + procedure Reset (This : BMP280_Device) is + begin + Write_Register + (This => This, + Register => reset, + Data => 16#B6#); + end Reset; + + ---------------- + -- Get_Status -- + ---------------- + + function Get_Status (This : BMP280_Device) return Status_Type + is + Data : UInt8; + Register : Status_Type with Import, Address => Data'Address; + begin + Data := Read_Register (This, status); + return Register; + end Get_Status; + + ---------------------- + -- Is_Measuring_Set -- + ---------------------- + + function Is_Measuring_Set (Status : Status_Type) return Boolean is + begin + return Status.Measuring = 1; + end Is_Measuring_Set; + + ---------------------- + -- Is_Im_Update_Set -- + ---------------------- + + function Is_Im_Update_Set (Status : Status_Type) return Boolean is + begin + return Status.Im_Update = 1; + end Is_Im_Update_Set; + + -------------------- + -- Set_Power_Mode -- + -------------------- + + procedure Set_Power_Mode + (This : BMP280_Device; + Power_Mode : Power_Mode_Type) + is + Data : UInt8; + Register : Ctrl_Meas_Register with Import, Address => Data'Address; + begin + Data := Read_Register (This, ctrl_meas); + Register.mode := Power_Mode; + Write_Register (This, ctrl_meas, Data); + end Set_Power_Mode; + + ------------------------ + -- Set_Temp_Precision -- + ------------------------ + + procedure Set_Temp_Precision + (This : BMP280_Device; + Precision : Precision_Type) + is + Data : UInt8; + Register : Ctrl_Meas_Register with Import, Address => Data'Address; + begin + Data := Read_Register (This, ctrl_meas); + Register.osrs_t := Precision; + Write_Register (This, ctrl_meas, Data); + end Set_Temp_Precision; + + ------------------------- + -- Set_Press_Precision -- + ------------------------- + + procedure Set_Press_Precision + (This : BMP280_Device; + Precision : Precision_Type) + is + Data : UInt8; + Register : Ctrl_Meas_Register with Import, Address => Data'Address; + begin + Data := Read_Register (This, ctrl_meas); + Register.osrs_p := Precision; + Write_Register (This, ctrl_meas, Data); + end Set_Press_Precision; + + ---------------------- + -- Set_Time_Standby -- + ---------------------- + + procedure Set_Time_Standby + (This : BMP280_Device; + Time_Standby : Time_Standby_Type) + is + Data : UInt8; + Register : Config_Register with Import, Address => Data'Address; + begin + Data := Read_Register (This, config); + Register.t_sb := Time_Standby_To_UInt3 (Time_Standby); + Write_Register (This, config, Data); + end Set_Time_Standby; + + ---------------- + -- Set_Filter -- + ---------------- + + procedure Set_Filter + (This : BMP280_Device; + Filter : Filter_Type) + is + Data : UInt8; + Register : Config_Register with Import, Address => Data'Address; + begin + Data := Read_Register (This, config); + Register.filter := UInt3 (Filter); + Write_Register (This, config, Data); + end Set_Filter; + + ---------------- + -- Get_T_Fine -- + ---------------- + + function Get_T_Fine + (This : BMP280_Device; + Adc_T : Interfaces.Integer_32) + return Interfaces.Integer_32 + is + dig_T1 : constant Integer_32 := This.Holder.Dig_T1; + dig_T2 : constant Integer_32 := This.Holder.Dig_T2; + dig_T3 : constant Integer_32 := This.Holder.Dig_T3; + + var1 : Integer_32; + var2 : Integer_32; + begin + var1 := ((Adc_T / 8 - dig_T1 * 2) * dig_T2) / 2048; + var2 := Adc_T / 16 - dig_T1; + var2 := (((var2 * var2) / 4096) * dig_T3) / 16384; + return var1 + var2; + end Get_T_Fine; + + -------------- + -- Get_Temp -- + -------------- + + function Get_Temp (This : BMP280_Device) return Temperature + is + Val : constant UInt8 := Read_Register (This, ctrl_meas); + Resolution : Ctrl_Meas_Register with Import, Address => Val'Address; + Adc_T : Interfaces.Integer_32; + T : Interfaces.Integer_32; + begin + if Resolution.osrs_t = Skipped then + return Temperature'First; + end if; + + Adc_T := Read_Register + (This => This, + Precision => Resolution.osrs_t, + Msb => temp_msb, + Lsb => temp_lsb, + Xlsb => temp_xlsb); + T := (Get_T_Fine (This, Adc_T) * 5 + 128) / 256; + + return Temperature (Float (T) / 100.0); + end Get_Temp; + + --------------- + -- Get_Press -- + --------------- + + function Get_Press (This : BMP280_Device) return Pressure + is + Val : constant UInt8 := Read_Register (This, ctrl_meas); + Resolution : Ctrl_Meas_Register with Import, Address => Val'Address; + + Adc_T : Integer_32; + Adc_P : Integer_32; + Var1 : Integer_64; + Var2 : Integer_64; + P : Integer_64; + + dig_P1 : Integer_32 renames This.Holder.dig_P1; + dig_P2 : Integer_32 renames This.Holder.dig_P2; + dig_P3 : Integer_32 renames This.Holder.dig_P3; + dig_P4 : Integer_32 renames This.Holder.dig_P4; + dig_P5 : Integer_32 renames This.Holder.dig_P5; + dig_P6 : Integer_32 renames This.Holder.dig_P6; + dig_P7 : Integer_32 renames This.Holder.dig_P7; + dig_P8 : Integer_32 renames This.Holder.dig_P8; + dig_P9 : Integer_32 renames This.Holder.dig_P9; + begin + if Resolution.osrs_t = Skipped + or else Resolution.osrs_p = Skipped + then + return Pressure'First; + end if; + + Adc_T := Read_Register + (This => This, + Precision => Resolution.osrs_t, + Msb => temp_msb, + Lsb => temp_lsb, + Xlsb => temp_xlsb); + + Adc_P := Read_Register + (This => This, + Precision => Resolution.osrs_p, + Msb => press_msb, + Lsb => press_lsb, + Xlsb => press_xlsb); + + Var1 := Integer_64 (Get_T_Fine (This, Adc_T) - 128000); + Var2 := Var1 * Var1 * Integer_64 (dig_P6); + Var2 := Var2 + (Var1 * Integer_64 (dig_P5)) * 131072; + Var2 := Var2 + (Integer_64 (dig_P4) * 34359738368); + Var1 := ((Var1 * Var1 * Integer_64 (dig_P3)) / 256) + + ((Var1 * Integer_64 (dig_P2)) * 4096); + Var1 := ((140737488355328 + Var1) * Integer_64 (dig_P1)) / 8589934592; + + if Var1 = 0 then + return Pressure'First; + end if; + + P := 1048576 - Integer_64 (Adc_P); + P := ((P * 2147483648 - Var2) * 3125) / Var1; + Var1 := (Integer_64 (dig_P9) * (P / 8192) * (P / 8192)) / 33554432; + Var2 := (Integer_64 (dig_P8) * P) / 524288; + + P := (((P + Var1 + Var2) / 256) + (Integer_64 (dig_P7) * 16)); + + return Pressure (Float (P) / 25600.0); + end Get_Press; + + ------------------ + -- Set_SPI_Mode -- + ------------------ + + procedure Set_SPI_Mode (This : BMP280_Device; Mode : SPI_Mode) + is + Data : UInt8; + Register : Config_Register with Import, Address => Data'Address; + begin + Data := Read_Register (This, config); + if Mode = SPI3W then + Register.spi3w_en := 1; + else + Register.spi3w_en := 0; + end if; + Write_Register (This, config, Data); + end Set_SPI_Mode; + + ---------------------- + -- Set_Time_Standby -- + ---------------------- + + function Time_Standby_To_UInt3 + (Time_Standby : Time_Standby_Type) return UInt3 is + begin + return (if Time_Standby = 0.5 then 2#000# + elsif Time_Standby = 62.5 then 2#001# + elsif Time_Standby = 125.0 then 2#010# + elsif Time_Standby = 250.0 then 2#011# + elsif Time_Standby = 500.0 then 2#100# + elsif Time_Standby = 1000.0 then 2#101# + elsif Time_Standby = 2000.0 then 2#110# + else 2#111#); + end Time_Standby_To_UInt3; + +end BMP280; diff --git a/components/src/sensors/combined/BMP280/bmp280.ads b/components/src/sensors/combined/BMP280/bmp280.ads new file mode 100644 index 000000000..01e7f6d6e --- /dev/null +++ b/components/src/sensors/combined/BMP280/bmp280.ads @@ -0,0 +1,330 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2025, 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. -- +-- -- +------------------------------------------------------------------------------ + +-- Driver for Bmp280 sensor + +with HAL; use HAL; +with HAL.GPIO; use HAL.GPIO; +with HAL.SPI; use HAL.SPI; +with Interfaces; use Interfaces; + +package BMP280 is + + type Chip_ID is new UInt8; + + type Pressure is delta 0.0001 range 300.0 .. 1100.0; -- hPa + type Temperature is delta 0.01 range -40.0 .. 85.0; -- °C + + type Status_Type is private; + + type Power_Mode_Type is (Sleep, Forced, Normal) with Size => UInt2'Size; + + type Precision_Type is + (Skipped, + Ultra_Low_Power, + Low_Power, + Standard_Resolution, + High_Resolution, + Ultra_High_Resolution) + with Size => UInt3'Size; + + type Time_Standby_Type is delta 0.01 range 0.05 .. 4000.00 + with Static_Predicate => + Time_Standby_Type in + 0.5 | 62.5 | 125.0 | 250.0 | 500.0 + | 1000.0 | 2000.0 | 4000.0; + -- Time_Standby in ms + + type SPI_Mode is (SPI4W, SPI3W); + + type Filter_Type is range 0 .. 7; + + ------------------- + -- BMP280_Device -- + ------------------- + + type BMP280_Device is limited private; + + procedure Initialize + (This : in out BMP280_Device; + CS : Any_GPIO_Point; + SPI : Any_SPI_Port; + Error : out Boolean); + + function Get_Chip_ID (This : BMP280_Device) return Chip_ID; + + procedure Reset (This : BMP280_Device); + + function Is_Measuring_Set (Status : Status_Type) return Boolean; + function Is_Im_Update_Set (Status : Status_Type) return Boolean; + + function Get_Status (This : BMP280_Device) return Status_Type; + + procedure Set_Power_Mode + (This : BMP280_Device; + Power_Mode : Power_Mode_Type); + + procedure Set_Temp_Precision + (This : BMP280_Device; + Precision : Precision_Type); + + procedure Set_Press_Precision + (This : BMP280_Device; + Precision : Precision_Type); + + procedure Set_Time_Standby + (This : BMP280_Device; + Time_Standby : Time_Standby_Type); + + procedure Set_Filter + (This : BMP280_Device; + Filter : Filter_Type); + + function Get_Press (This : BMP280_Device) return Pressure; + + function Get_Temp (This : BMP280_Device) return Temperature; + + procedure Set_SPI_Mode (This : BMP280_Device; Mode : SPI_Mode); + +private + + type Register_Size is new UInt8; + + type Status_Type is record + Im_Update : Bit; + Reserved1 : UInt2; + Measuring : Bit; + Reserved2 : UInt4; + end record with Size => Register_Size'Size; + + for Status_Type use record + Im_Update at 0 range 0 .. 0; + Reserved1 at 0 range 1 .. 2; + Measuring at 0 range 3 .. 3; + Reserved2 at 0 range 4 .. 7; + end record; + + for Power_Mode_Type use + (Sleep => 2#00#, + Forced => 2#01#, + Normal => 2#11#); + + for Precision_Type use + ( + Skipped => 2#000#, + Ultra_Low_Power => 2#001#, + Low_Power => 2#010#, + Standard_Resolution => 2#011#, + High_Resolution => 2#100#, + Ultra_High_Resolution => 2#101#); + + type Ctrl_Meas_Register is record + mode : Power_Mode_Type; + osrs_p : Precision_Type; + osrs_t : Precision_Type; + end record with Size => Register_Size'Size; + + for Ctrl_Meas_Register use record + mode at 0 range 0 .. 1; + osrs_p at 0 range 2 .. 4; + osrs_t at 0 range 5 .. 7; + end record; + + type Config_Register is record + spi3w_en : Bit; + Reserved : Bit; + filter : UInt3; + t_sb : UInt3; + end record with Size => Register_Size'Size; + + for Config_Register use record + spi3w_en at 0 range 0 .. 0; + Reserved at 0 range 1 .. 1; + filter at 0 range 2 .. 4; + t_sb at 0 range 5 .. 7; + end record; + + type Register_Name is + (id, + reset, + status, + ctrl_meas, + config, + press_msb, + press_lsb, + press_xlsb, + temp_msb, + temp_lsb, + temp_xlsb, + + dig_T1_lsb, + dig_T1_msb, + dig_T2_lsb, + dig_T2_msb, + dig_T3_lsb, + dig_T3_msb, + dig_P1_lsb, + dig_P1_msb, + dig_P2_lsb, + dig_P2_msb, + dig_P3_lsb, + dig_P3_msb, + dig_P4_lsb, + dig_P4_msb, + dig_P5_lsb, + dig_P5_msb, + dig_P6_lsb, + dig_P6_msb, + dig_P7_lsb, + dig_P7_msb, + dig_P8_lsb, + dig_P8_msb, + dig_P9_lsb, + dig_P9_msb); + + type Register_Adresses is range 16#08# .. 16#7C# with Size => UInt7'Size; + + Adresses : constant array (Register_Name) of Register_Adresses := + (id => 16#50#, + reset => 16#60#, + status => 16#73#, + ctrl_meas => 16#74#, + config => 16#75#, + press_msb => 16#77#, + press_lsb => 16#78#, + press_xlsb => 16#79#, + temp_msb => 16#7A#, + temp_lsb => 16#7B#, + temp_xlsb => 16#7C#, + + dig_T1_lsb => 16#8#, + dig_T1_msb => 16#9#, + dig_T2_lsb => 16#A#, + dig_T2_msb => 16#B#, + dig_T3_lsb => 16#C#, + dig_T3_msb => 16#D#, + dig_P1_lsb => 16#E#, + dig_P1_msb => 16#F#, + dig_P2_lsb => 16#10#, + dig_P2_msb => 16#11#, + dig_P3_lsb => 16#12#, + dig_P3_msb => 16#13#, + dig_P4_lsb => 16#14#, + dig_P4_msb => 16#15#, + dig_P5_lsb => 16#16#, + dig_P5_msb => 16#17#, + dig_P6_lsb => 16#18#, + dig_P6_msb => 16#19#, + dig_P7_lsb => 16#1A#, + dig_P7_msb => 16#1B#, + dig_P8_lsb => 16#1C#, + dig_P8_msb => 16#1D#, + dig_P9_lsb => 16#1E#, + dig_P9_msb => 16#1F#); + + type Command_Type is (Write, Read) with Size => Bit'Size; + for Command_Type use (Write => 0, Read => 1); + + type Command is record + Cmd : Command_Type; + Addr : Register_Adresses; + end record with Size => UInt8'Size; + + for Command use record + Addr at 0 range 0 .. 6; + Cmd at 0 range 7 .. 7; + end record; + + procedure CS_Low (This : BMP280_Device); + + procedure CS_High (This : BMP280_Device); + + function Read_Register + (This : BMP280_Device; + Register : Register_Name) + return UInt8; + + function Read_Register + (This : BMP280_Device; + Msb : Register_Name; + Lsb : Register_Name) + return UInt16; + + function Read_Register + (This : BMP280_Device; + Msb : Register_Name; + Lsb : Register_Name) + return Interfaces.Integer_16; + + procedure Write_Register + (This : BMP280_Device; + Register : Register_Name; + Data : UInt8); + + procedure Read_Trim_Registers (This : in out BMP280_Device); + + function Get_T_Fine + (This : BMP280_Device; + Adc_T : Interfaces.Integer_32) + return Interfaces.Integer_32; + + function Read_Register + (This : BMP280_Device; + Precision : Precision_Type; + Msb : Register_Name; + Lsb : Register_Name; + Xlsb : Register_Name) + return Interfaces.Integer_32 with Pre => (Precision /= Skipped); + + type Holder_Type is record + CS : Any_GPIO_Point; + SPI : Any_SPI_Port; + + Chip_ID : Integer_8; + Dig_T1 : Integer_32; + Dig_T2 : Integer_32; + Dig_T3 : Integer_32; + dig_P1 : Integer_32; + dig_P2 : Integer_32; + dig_P3 : Integer_32; + dig_P4 : Integer_32; + dig_P5 : Integer_32; + dig_P6 : Integer_32; + dig_P7 : Integer_32; + dig_P8 : Integer_32; + dig_P9 : Integer_32; + end record; + + type BMP280_Device is limited record + Holder : Holder_Type; + end record; + +end BMP280; diff --git a/components/src/sensors/thermometer/BMP280/README.md b/components/src/sensors/thermometer/BMP280/README.md new file mode 100644 index 000000000..aec3fffa6 --- /dev/null +++ b/components/src/sensors/thermometer/BMP280/README.md @@ -0,0 +1,2 @@ +See components/src/sensors/combined/BMP280 + diff --git a/examples/STM32F429_Discovery/bmp280_f429disco.gpr b/examples/STM32F429_Discovery/bmp280_f429disco.gpr new file mode 100644 index 000000000..258b7db69 --- /dev/null +++ b/examples/STM32F429_Discovery/bmp280_f429disco.gpr @@ -0,0 +1,29 @@ +with "../../boards/stm32f429_discovery/stm32f429_discovery_full.gpr"; + +project Bmp280_F429Disco extends "../shared/common/common.gpr" is + + for Runtime ("ada") use Stm32F429_Discovery_Full'Runtime ("Ada"); + for Target use "arm-eabi"; + for Main use ("Bmp280_example.adb"); + for Languages use ("Ada"); + for Source_Dirs use ("../shared/Bmp280/src"); + for Object_Dir use "../shared/Bmp280/obj/stm32f429disco"; + for Create_Missing_Dirs use "True"; + + package Compiler renames Stm32F429_Discovery_Full.Compiler; + + package Builder is + for Switches ("ada") use ("-g"); + end Builder; + + package Linker is + for Default_Switches ("ada") use + ("-Wl,--gc-sections", "-Wl,--print-memory-usage", "-g"); + end Linker; + + package Binder is + for Switches ("ada") use ("-E"); + end Binder; + +end Bmp280_F429Disco; + diff --git a/examples/shared/Bmp280/.gdbinit b/examples/shared/Bmp280/.gdbinit new file mode 100644 index 000000000..618b9ce81 --- /dev/null +++ b/examples/shared/Bmp280/.gdbinit @@ -0,0 +1,24 @@ +# This command file will cause a Cortex-M3 or -M4 board to automatically +# reset immediately after a GDB "load" command executes. Note that GPS +# issues that command as part of the Debug->Init menu invocation. Manual +# "load" command invocations will also trigger the action. +# +# The reset is achieved by writing to the "Application Interrupt and Reset +# Control" register located at address 0xE000ED0C. +# +# Both the processor and the peripherals can be reset by writing a value +# of 0x05FA0004. That value will write to the SYSRESETREQ bit. If you want +# to avoid resetting the peripherals, change the value to 0x05FA0001. That +# value will write to the VECTRESET bit. Do *not* use a value that sets both +# bits. +# +# In both cases, any on-board debug hardware is not reset. +# +# See the book "The Definitive Guide to the ARM Cortex-M3 and Cortex-M4 +# Processors" by Joseph Yiu, 3rd edition, pp 262-263 for further details. + +define hookpost-load +echo Resetting the processor and peripherals...\n +set *0xE000ED0C := 0x05FA0004 +echo Reset complete\n +end \ No newline at end of file diff --git a/examples/shared/Bmp280/README.md b/examples/shared/Bmp280/README.md new file mode 100644 index 000000000..82cfcd641 --- /dev/null +++ b/examples/shared/Bmp280/README.md @@ -0,0 +1,11 @@ +This is a simple test/example for Bmp280 with +STM32F429Disco. Should be connected +to the following board's pins: + +|------|------| +| CSN | PC12 | +| IRQ | PB4 | +| SCK | PE2 | +| MISO | PE5 | +| MOSI | PE6 | + diff --git a/examples/shared/Bmp280/src/bmp280_example.adb b/examples/shared/Bmp280/src/bmp280_example.adb new file mode 100644 index 000000000..96cbd2937 --- /dev/null +++ b/examples/shared/Bmp280/src/bmp280_example.adb @@ -0,0 +1,227 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2025, 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. -- +-- -- +------------------------------------------------------------------------------ + +-- Simple test/example for STM32F429disco with Bmp280 sensor attached + +with Last_Chance_Handler; pragma Unreferenced (Last_Chance_Handler); +-- The "last chance handler" is the user-defined routine that is called when +-- an exception is propagated. We need it in the executable, therefore it +-- must be somewhere in the closure of the context clauses. + +with Ada.Exceptions; + +with HAL.SPI; +with STM32.Board; use STM32.Board; +with STM32.Device; +with STM32.GPIO; use STM32.GPIO; +with STM32.SPI; use STM32.SPI; + +with BMP280; use BMP280; +with Watchdogs; use Watchdogs; + +-- Service +with Ada.Real_Time; use Ada.Real_Time; +with HAL.Bitmap; use HAL.Bitmap; +with BMP_Fonts; +with LCD_Std_Out; + +-- We use the following pins on STM32F429Discovery: +-- CSN PC12 CSB +-- IRQ PB4 +-- SPI4 Pins: +-- PE2 SPI4_SCK +-- PE5 SPI4_MISO SDO +-- PE6 SPI4_MOSI SDA + +procedure Bmp280_Example is + + CSN : GPIO_Point renames STM32.Device.PC12; + SPI4_Pins : constant GPIO_Points := + (STM32.Device.PE2, STM32.Device.PE5, STM32.Device.PE6); + SPI : SPI_Port renames STM32.Device.SPI_4; + Device : BMP280_Device; + Error : Boolean; + + procedure Init_Pins; + -- Initialize pins + + procedure Init_SPI; + -- Initialize SPI + + procedure Print (Msg : String); + -- Print the message on display + + procedure On_Error (Msg : String); + -- Output the error message and blink the red led + + --------------- + -- Init_Pins -- + --------------- + + procedure Init_Pins is + -- Initialize pins + begin + STM32.Device.Enable_Clock (GPIO_Points'(CSN, IRQ)); + + Configure_IO + (CSN, + Config => + (Mode => Mode_Out, + Resistors => Floating, + Output_Type => Push_Pull, + Speed => Speed_Low)); + + CSN.Set; + end Init_Pins; + + -------------- + -- Init_SPI -- + -------------- + + procedure Init_SPI + -- Initialize SPI + is + use STM32.Device; + + Conf : constant GPIO_Port_Configuration := + (Mode => Mode_AF, + Resistors => Floating, + AF_Output_Type => Push_Pull, + AF_Speed => Speed_Very_High, + AF => GPIO_AF_SPI4_5); + + SPI_Conf : constant SPI_Configuration := + (Direction => D2Lines_FullDuplex, + Mode => Master, + Data_Size => HAL.SPI.Data_Size_8b, + Clock_Polarity => Low, + Clock_Phase => P1Edge, + Slave_Management => Software_Managed, + Baud_Rate_Prescaler => BRP_16, + First_Bit => MSB, + CRC_Poly => 10); + begin + Enable_Clock (SPI4_Pins); + Enable_Clock (SPI); + + Configure_IO (SPI4_Pins, Conf); + + Reset (SPI); + if not SPI.Enabled then + SPI.Configure (SPI_Conf); + SPI.Enable; + end if; + end Init_SPI; + + Period : constant Time_Span := Seconds (2); + Next_Release : Time := Clock; + BG : constant Bitmap_Color := (Alpha => 255, others => 64); + + ----------- + -- Print -- + ----------- + + procedure Print (Msg : String) is + begin + LCD_Std_Out.Put_Line (Msg); + Display.Update_Layer (1, Copy_Back => True); + end Print; + + -------------- + -- On_Error -- + -------------- + + procedure On_Error (Msg : String) is + begin + LCD_Std_Out.Put_Line (Msg); + Display.Update_Layer (1, Copy_Back => True); + + loop + STM32.Board.Toggle (Red_LED); + Next_Release := Next_Release + Period; + delay until Next_Release; + end loop; + end On_Error; + +begin + delay 2.0; + + -- Initialize GPIO/SPI + Init_Pins; + Init_SPI; + delay 0.1; + + -- Init display infrastructure + STM32.Board.Initialize_LEDs; + Display.Initialize; + Display.Initialize_Layer (1, ARGB_8888); + LCD_Std_Out.Set_Font (BMP_Fonts.Font12x12); + LCD_Std_Out.Current_Background_Color := BG; + Display.Hidden_Buffer (1).Set_Source (BG); + Display.Hidden_Buffer (1).Fill; + LCD_Std_Out.Clear_Screen; + + -- test -- + Initialize (Device, CSN'Access, SPI'Access, Error); + if Error then + On_Error ("Initialization failed"); + + else + -- Initialization is OK + Print ("Chip_ID :" & Get_Chip_ID (Device)'Img); + Print ("All tests passed"); + delay 3.0; + + -- adjust the sensor settings + Set_Filter (Device, 7); + Set_Temp_Precision (Device, Low_Power); + Set_Press_Precision (Device, Ultra_High_Resolution); + Set_Power_Mode (Device, Normal); + Set_Time_Standby (Device, 62.5); + Set_SPI_Mode (Device, SPI4W); + + loop + -- display the temperature and pressure in the loop + STM32.Board.Toggle (Green_LED); + Next_Release := Next_Release + Period; + delay until Next_Release; + LCD_Std_Out.Clear_Screen; + + LCD_Std_Out.Put_Line ("T :" & Get_Temp (Device)'Img); + LCD_Std_Out.Put_Line ("P :" & Get_Press (Device)'Img); + Display.Update_Layer (1, Copy_Back => True); + end loop; + end if; + +exception + when E : others => + LCD_Std_Out.Put_Line (Ada.Exceptions.Exception_Information (E)); +end Bmp280_Example; diff --git a/examples/shared/Bmp280/src/watchdogs.adb b/examples/shared/Bmp280/src/watchdogs.adb new file mode 100644 index 000000000..bdec3d535 --- /dev/null +++ b/examples/shared/Bmp280/src/watchdogs.adb @@ -0,0 +1,63 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2025, 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 Watchdogs is + + -------------- + -- Watchdog -- + -------------- + + protected body Watchdog is + + ------------------ + -- Set_Callback -- + ------------------ + + procedure Set_Callback (Proc : Watchdog_Callback) is + begin + Callback := Proc; + end Set_Callback; + + --------------- + -- Interrupt -- + --------------- + + procedure Interrupt is + begin + STM32.EXTI.Clear_External_Interrupt (EXTI_Line); + if Callback /= null then + Callback.all; + end if; + end Interrupt; + + end Watchdog; + +end Watchdogs; diff --git a/examples/shared/Bmp280/src/watchdogs.ads b/examples/shared/Bmp280/src/watchdogs.ads new file mode 100644 index 000000000..5780ec1dd --- /dev/null +++ b/examples/shared/Bmp280/src/watchdogs.ads @@ -0,0 +1,63 @@ +------------------------------------------------------------------------------ +-- -- +-- Copyright (C) 2025, 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. -- +-- -- +------------------------------------------------------------------------------ + +-- Watchdogs to catch IRQs + +with Ada.Interrupts.Names; + +with STM32.GPIO; use STM32.GPIO; +with STM32.Device; +with STM32.EXTI; + +package Watchdogs is + + IRQ : GPIO_Point renames STM32.Device.PB4; + + type Watchdog_Callback is access procedure; + + -- Watchdog -- + + protected Watchdog is + pragma Interrupt_Priority; + + procedure Set_Callback (Proc : Watchdog_Callback); + + private + EXTI_Line : STM32.EXTI.External_Line_Number := + IRQ.Interrupt_Line_Number; + + procedure Interrupt; + pragma Attach_Handler (Interrupt, Ada.Interrupts.Names.EXTI4_Interrupt); + + Callback : Watchdog_Callback := null; + end Watchdog; + +end Watchdogs;