From 8a0277171ac075c65534dc42571a3ee291534f76 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Sat, 1 Oct 2022 21:10:20 +0300 Subject: [PATCH] add bmpe280 --- BMPE280/BMP280.c | 345 ++++++++++++++++++++++++++++++++++++ BMPE280/BMP280.h | 66 +++++++ BMPE280/Makefile | 57 ++++++ BMPE280/Readme | 1 + BMPE280/bmp280.cflags | 1 + BMPE280/bmp280.config | 2 + BMPE280/bmp280.creator | 1 + BMPE280/bmp280.creator.user | 174 ++++++++++++++++++ BMPE280/bmp280.cxxflags | 1 + BMPE280/bmp280.files | 5 + BMPE280/bmp280.includes | 1 + BMPE280/i2c.c | 249 ++++++++++++++++++++++++++ BMPE280/i2c.h | 39 ++++ BMPE280/main.c | 90 ++++++++++ 14 files changed, 1032 insertions(+) create mode 100644 BMPE280/BMP280.c create mode 100644 BMPE280/BMP280.h create mode 100644 BMPE280/Makefile create mode 100644 BMPE280/Readme create mode 100644 BMPE280/bmp280.cflags create mode 100644 BMPE280/bmp280.config create mode 100644 BMPE280/bmp280.creator create mode 100644 BMPE280/bmp280.creator.user create mode 100644 BMPE280/bmp280.cxxflags create mode 100644 BMPE280/bmp280.files create mode 100644 BMPE280/bmp280.includes create mode 100644 BMPE280/i2c.c create mode 100644 BMPE280/i2c.h create mode 100644 BMPE280/main.c diff --git a/BMPE280/BMP280.c b/BMPE280/BMP280.c new file mode 100644 index 0000000..e2d1e6e --- /dev/null +++ b/BMPE280/BMP280.c @@ -0,0 +1,345 @@ +/* + * This file is part of the bmp280 project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "i2c.h" +#include "BMP280.h" + +/** + * BMP280 registers + */ +#define BMP280_REG_HUM_LSB 0xFE +#define BMP280_REG_HUM_MSB 0xFD +#define BMP280_REG_HUM (BMP280_REG_HUM_MSB) +#define BMP280_REG_TEMP_XLSB 0xFC /* bits: 7-4 */ +#define BMP280_REG_TEMP_LSB 0xFB +#define BMP280_REG_TEMP_MSB 0xFA +#define BMP280_REG_TEMP (BMP280_REG_TEMP_MSB) +#define BMP280_REG_PRESS_XLSB 0xF9 /* bits: 7-4 */ +#define BMP280_REG_PRESS_LSB 0xF8 +#define BMP280_REG_PRESS_MSB 0xF7 +#define BMP280_REG_PRESSURE (BMP280_REG_PRESS_MSB) +#define BMP280_REG_ALLDATA (BMP280_REG_PRESS_MSB) // all data: P, T & H +#define BMP280_REG_CONFIG 0xF5 /* bits: 7-5 t_sb; 4-2 filter; 0 spi3w_en */ +#define BMP280_REG_CTRL 0xF4 /* bits: 7-5 osrs_t; 4-2 osrs_p; 1-0 mode */ +#define BMP280_REG_STATUS 0xF3 /* bits: 3 measuring; 0 im_update */ +#define BMP280_STATUS_MSRNG (1<<3) // measuring flag +#define BMP280_STATUS_UPDATE (1<<0) // update flag +#define BMP280_REG_CTRL_HUM 0xF2 /* bits: 2-0 osrs_h; */ +#define BMP280_REG_RESET 0xE0 + #define BMP280_RESET_VALUE 0xB6 +#define BMP280_REG_ID 0xD0 + +#define BMP280_REG_CALIBA 0x88 +#define BMP280_CALIBA_SIZE (26) // 26 bytes of calibration registers sequence from 0x88 to 0xa1 +#define BMP280_CALIBB_SIZE (7) // 7 bytes of calibration registers sequence from 0xe1 to 0xe7 +#define BMP280_REG_CALIB_H1 0xA1 // dig_H1 +#define BMP280_REG_CALIBB 0xE1 + +#define BMP280_MODE_FORSED (1) // force single measurement +#define BMP280_MODE_NORMAL (3) // run continuosly + +static struct { + // temperature + uint16_t dig_T1; // 0x88 (LSB), 0x98 (MSB) + int16_t dig_T2; // ... + int16_t dig_T3; + // pressure + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; // 0x9e, 0x9f + // humidity (partially calculated from EEE struct) + uint8_t unused; // 0xA0 + uint8_t dig_H1; // 0xA1 + int16_t dig_H2; // 0xE1... + uint8_t dig_H3; // only from EEE + uint16_t dig_H4; + uint16_t dig_H5; + int8_t dig_H6; + // data is ready + uint8_t rdy; +} __attribute__ ((packed)) CaliData = {0}; + +// data for humidity calibration of BME280 +static uint8_t EEE[BMP280_CALIBB_SIZE] = {0}; + +static struct{ + BMP280_Filter filter; // filtering + BMP280_Oversampling p_os; // oversampling for pressure + BMP280_Oversampling t_os; // -//- temperature + BMP280_Oversampling h_os; // -//- humidity + uint8_t ID; // identificator + uint8_t regctl; // control register base value [(params.t_os << 5) | (params.p_os << 2)] +} params = { + .filter = BMP280_FILTER_OFF, + .p_os = BMP280_OVERS16, + .t_os = BMP280_OVERS16, + .h_os = BMP280_OVERS16, + .ID = 0 +}; + +static BMP280_status bmpstatus = BMP280_NOTINIT; + +BMP280_status BMP280_get_status(){ + return bmpstatus; +} + +// setters for `params` +void BMP280_setfilter(BMP280_Filter f){ + params.filter = f; +} +void BMP280_setOSt(BMP280_Oversampling os){ + params.t_os = os; +} +void BMP280_setOSp(BMP280_Oversampling os){ + params.p_os = os; +} +void BMP280_setOSh(BMP280_Oversampling os){ + params.h_os = os; +} + +// get compensation data, return 1 if OK +static int readcompdata(){ + FNAME(); + if(!i2c_read_data8(BMP280_REG_CALIBA, BMP280_CALIBA_SIZE, (uint8_t*)&CaliData)){ + DBG("Can't read calibration A data"); + return FALSE; + } + /* + // convert big-endian into little-endian + uint8_t *arr = (uint8_t*)&CaliData; + for(int i = 0; i < (int)sizeof(CaliData); i+=2){ + register uint8_t val = arr[i]; + arr[i] = arr[i+1]; + arr[i+1] = val; + }*/ + if(params.ID == BME280_CHIP_ID){ + if(!i2c_read_reg8(BMP280_REG_CALIB_H1, &CaliData.dig_H1)){ + WARNX("Can't read dig_H1"); + return FALSE; + } + if(!i2c_read_data8(BMP280_REG_CALIBB, BMP280_CALIBB_SIZE, EEE)){ + WARNX("Can't read rest of dig_Hx"); + return FALSE; + } + // E5 is divided by two parts so we need this sex + CaliData.dig_H2 = (EEE[1] << 8) | EEE[0]; + CaliData.dig_H3 = EEE[2]; + CaliData.dig_H4 = (EEE[3] << 4) | (EEE[4] & 0x0f); + CaliData.dig_H5 = (EEE[5] << 4) | (EEE[4] >> 4); + CaliData.dig_H6 = EEE[6]; + } + CaliData.rdy = 1; + DBG("Calibration rdy"); + return TRUE; +} + +// do a soft-reset procedure +int BMP280_reset(){ + if(!i2c_write_reg8(BMP280_REG_RESET, BMP280_RESET_VALUE)){ + DBG("Can't reset\n"); + return FALSE; + } + return TRUE; +} + +// read compensation data & write registers +int BMP280_init(){ + bmpstatus = BMP280_NOTINIT; + if(!i2c_read_reg8(BMP280_REG_ID, ¶ms.ID)){ + DBG("Can't read BMP280_REG_ID"); + return FALSE; + } + DBG("Got device ID: 0x%02x", params.ID); + if(params.ID != BMP280_CHIP_ID && params.ID != BME280_CHIP_ID){ + WARNX("Not BMP/BME\n"); + return FALSE; + } + if(!BMP280_reset()){ + WARNX("Can't reset"); + return FALSE; + } + uint8_t reg = 1; + while(reg & BMP280_STATUS_UPDATE){ // wait while update is done + if(!i2c_read_reg8(BMP280_REG_STATUS, ®)){ + DBG("Can't read status"); + return FALSE; + } + } + if(!readcompdata()){ + DBG("Can't read calibration data\n"); + return FALSE; + }else{ + DBG("T: %d, %d, %d", CaliData.dig_T1, CaliData.dig_T2, CaliData.dig_T3); + DBG("\P: %d, %d, %d, %d, %d, %d, %d, %d, %d", CaliData.dig_P1, CaliData.dig_P2, CaliData.dig_P3, + CaliData.dig_P4, CaliData.dig_P5, CaliData.dig_P6, CaliData.dig_P7, CaliData.dig_P8, CaliData.dig_P9); + if(params.ID == BME280_CHIP_ID){ // read H compensation + DBG("H: %d, %d, %d, %d, %d, %d", CaliData.dig_H1, CaliData.dig_H2, CaliData.dig_H3, + CaliData.dig_H4, CaliData.dig_H5, CaliData.dig_H6); + } + } + // write filter configuration + reg = params.filter << 2; + if(!i2c_write_reg8(BMP280_REG_CONFIG, reg)){ + DBG("Can't save filter settings\n"); + return FALSE; + } + reg = (params.t_os << 5) | (params.p_os << 2); // oversampling for P/T, sleep mode + if(!i2c_write_reg8(BMP280_REG_CTRL, reg)){ + DBG("Can't write settings for P/T\n"); + return FALSE; + } + params.regctl = reg; + if(params.ID == BME280_CHIP_ID){ // write CTRL_HUM only AFTER CTRL! + reg = params.h_os; + if(!i2c_write_reg8(BMP280_REG_CTRL_HUM, reg)){ + DBG("Can't write settings for H\n"); + return FALSE; + } + } + DBG("OK, inited"); + bmpstatus = BMP280_RELAX; + return TRUE; +} + +// @return 1 if OK, *devid -> BMP/BME +void BMP280_read_ID(uint8_t *devid){ + if(devid) *devid = params.ID; +} + +// start measurement, @return 1 if all OK +int BMP280_start(){ + if(!CaliData.rdy || bmpstatus == BMP280_BUSY){ + DBG("rdy=%d, status=%d", CaliData.rdy, bmpstatus); + return FALSE; + } + uint8_t reg = params.regctl | BMP280_MODE_FORSED; // start single measurement + if(!i2c_write_reg8(BMP280_REG_CTRL, reg)){ + DBG("Can't write CTRL reg\n"); + return FALSE; + } + bmpstatus = BMP280_BUSY; + return TRUE; +} + +// return T in degC +static inline float compTemp(int32_t adc_temp, int32_t *t_fine){ + int32_t var1, var2; + var1 = ((((adc_temp >> 3) - ((int32_t) CaliData.dig_T1 << 1))) + * (int32_t) CaliData.dig_T2) >> 11; + var2 = (((((adc_temp >> 4) - (int32_t) CaliData.dig_T1) + * ((adc_temp >> 4) - (int32_t) CaliData.dig_T1)) >> 12) + * (int32_t) CaliData.dig_T3) >> 14; + *t_fine = var1 + var2; + return ((*t_fine * 5 + 128) >> 8) / 100.f; +} + +// return P in Pa +static inline float compPres(int32_t adc_press, int32_t fine_temp) { + int64_t var1, var2, p; + var1 = (int64_t) fine_temp - 128000; + var2 = var1 * var1 * (int64_t) CaliData.dig_P6; + var2 = var2 + ((var1 * (int64_t) CaliData.dig_P5) << 17); + var2 = var2 + (((int64_t) CaliData.dig_P4) << 35); + var1 = ((var1 * var1 * (int64_t) CaliData.dig_P3) >> 8) + + ((var1 * (int64_t) CaliData.dig_P2) << 12); + var1 = (((int64_t) 1 << 47) + var1) * ((int64_t) CaliData.dig_P1) >> 33; + if (var1 == 0){ + return 0; // avoid exception caused by division by zero + } + p = 1048576 - adc_press; + p = (((p << 31) - var2) * 3125) / var1; + var1 = ((int64_t) CaliData.dig_P9 * (p >> 13) * (p >> 13)) >> 25; + var2 = ((int64_t) CaliData.dig_P8 * p) >> 19; + p = ((p + var1 + var2) >> 8) + ((int64_t) CaliData.dig_P7 << 4); + return p/256.f; +} + +// return H in percents +static inline float compHum(int32_t adc_hum, int32_t fine_temp){ + int32_t v_x1_u32r; + v_x1_u32r = fine_temp - (int32_t) 76800; + v_x1_u32r = ((((adc_hum << 14) - (((int32_t)CaliData.dig_H4) << 20) + - (((int32_t)CaliData.dig_H5) * v_x1_u32r)) + (int32_t)16384) >> 15) + * (((((((v_x1_u32r * ((int32_t)CaliData.dig_H6)) >> 10) + * (((v_x1_u32r * ((int32_t)CaliData.dig_H3)) >> 11) + + (int32_t)32768)) >> 10) + (int32_t)2097152) + * ((int32_t)CaliData.dig_H2) + 8192) >> 14); + v_x1_u32r = v_x1_u32r + - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) + * ((int32_t)CaliData.dig_H1)) >> 4); + v_x1_u32r = v_x1_u32r < 0 ? 0 : v_x1_u32r; + v_x1_u32r = v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r; + return (v_x1_u32r >> 12)/1024.f; +} + +void BMP280_process(){ + if(bmpstatus != BMP280_BUSY) return; + // BUSY state: poll data ready + uint8_t reg; + if(!i2c_read_reg8(BMP280_REG_STATUS, ®)) return; + if(reg & BMP280_STATUS_MSRNG) return; // still busy + bmpstatus = BMP280_RDY; // data ready +} + +// read data & convert it +int BMP280_getdata(float *T, float *P, float *H){ + if(bmpstatus != BMP280_RDY) return FALSE; + bmpstatus = BMP280_RELAX; + uint8_t datasz = 8; // amount of bytes to read + if(params.ID != BME280_CHIP_ID){ + DBG("Not BME!\n"); + if(H) *H = 0; + datasz = 6; + } + uint8_t data[8]; + if(!i2c_read_data8(BMP280_REG_ALLDATA, datasz, data)){ + DBG("Can't read data"); + return FALSE; + } +#ifdef EBUG + printf("\tgot data: "); + for(int i = 0; i < datasz; ++i){ + printf("0x%02x ", data[i]); + } + printf("\n"); +#endif + int32_t p = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); + DBG("puncomp = %d", p); + int32_t t = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); + DBG("tuncomp = %d", t); + int32_t t_fine; + float Temp = compTemp(t, &t_fine); + DBG("tfine = %d", t_fine); + if(T) *T = Temp; + if(P) *P = compPres(p, t_fine); + if(H && params.ID == BME280_CHIP_ID){ + int32_t h = (data[6] << 8) | data[7]; + DBG("huncomp = %d", h); + *H = compHum(h, t_fine); + } + return TRUE; +} diff --git a/BMPE280/BMP280.h b/BMPE280/BMP280.h new file mode 100644 index 0000000..97367a0 --- /dev/null +++ b/BMPE280/BMP280.h @@ -0,0 +1,66 @@ +/* + * This file is part of the BMP280 project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#define BMP280_I2C_ADDRESS (0x76) + +#define BMP280_CHIP_ID 0x58 +#define BME280_CHIP_ID 0x60 + +typedef enum{ // K for filtering: next = [prev*(k-1) + data_ADC]/k + BMP280_FILTER_OFF = 0, // k=1, no filtering + BMP280_FILTER_2 = 1, // k=2, 2 samples to reach >75% of data_ADC + BMP280_FILTER_4 = 2, // k=4, 5 samples + BMP280_FILTER_8 = 3, // k=8, 11 samples + BMP280_FILTER_16 = 4, // k=16, 22 samples + BMP280_FILTERMAX +} BMP280_Filter; + +typedef enum{ // Number of oversampling + BMP280_NOMEASUR = 0, + BMP280_OVERS1 = 1, + BMP280_OVERS2 = 2, + BMP280_OVERS4 = 3, + BMP280_OVERS8 = 4, + BMP280_OVERS16 = 5, + BMP280_OVERSMAX +} BMP280_Oversampling; + +typedef enum{ + BMP280_NOTINIT, // wasn't inited + BMP280_BUSY, // measurement in progress + BMP280_ERR, // error in I2C + BMP280_RELAX, // relaxed state + BMP280_RDY, // data ready - can get it +} BMP280_status; + +int BMP280_reset(); +int BMP280_init(); +void BMP280_read_ID(uint8_t *devid); +void BMP280_setfilter(BMP280_Filter f); +void BMP280_setOSt(BMP280_Oversampling os); +void BMP280_setOSp(BMP280_Oversampling os); +// BME280 (humidity) +void BMP280_setOSh(BMP280_Oversampling os); +BMP280_status BMP280_get_status(); +int BMP280_start(); +void BMP280_process(); +int BMP280_getdata(float *T, float *P, float *H); + diff --git a/BMPE280/Makefile b/BMPE280/Makefile new file mode 100644 index 0000000..8b34d7f --- /dev/null +++ b/BMPE280/Makefile @@ -0,0 +1,57 @@ +# run `make DEF=...` to add extra defines +PROGRAM := bmp280 +LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all +LDFLAGS += -lusefull_macros +SRCS := $(wildcard *.c) +DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 +OBJDIR := mk +CFLAGS += -O2 -Wall -Wextra -Wno-trampolines -std=gnu99 +OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) +DEPS := $(OBJS:.o=.d) +TARGFILE := $(OBJDIR)/TARGET +CC = gcc +#TARGET := RELEASE + +ifeq ($(shell test -e $(TARGFILE) && echo -n yes),yes) + TARGET := $(file < $(TARGFILE)) +else + TARGET := RELEASE +endif + +ifeq ($(TARGET), DEBUG) + .DEFAULT_GOAL := debug +endif + +release: $(PROGRAM) + +debug: CFLAGS += -DEBUG -Werror +debug: TARGET := DEBUG +debug: $(PROGRAM) + +$(TARGFILE): $(OBJDIR) + @echo -e "\t\tTARGET: $(TARGET)" + @echo "$(TARGET)" > $(TARGFILE) + +$(PROGRAM) : $(TARGFILE) $(OBJS) + @echo -e "\t\tLD $(PROGRAM)" + $(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM) + +$(OBJDIR): + @mkdir $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPS) +endif + +$(OBJDIR)/%.o: %.c + @echo -e "\t\tCC $<" + $(CC) $< -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ + +clean: + @echo -e "\t\tCLEAN" + @rm -rf $(OBJDIR) 2>/dev/null || true + +xclean: clean + @rm -f $(PROGRAM) + +.PHONY: clean xclean diff --git a/BMPE280/Readme b/BMPE280/Readme new file mode 100644 index 0000000..80a3204 --- /dev/null +++ b/BMPE280/Readme @@ -0,0 +1 @@ +Code for BMP280/BME280 diff --git a/BMPE280/bmp280.cflags b/BMPE280/bmp280.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/BMPE280/bmp280.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/BMPE280/bmp280.config b/BMPE280/bmp280.config new file mode 100644 index 0000000..5047553 --- /dev/null +++ b/BMPE280/bmp280.config @@ -0,0 +1,2 @@ +#define _XOPEN_SOURCE 9999 +#define _POSIX_C_SOURCE 333333L diff --git a/BMPE280/bmp280.creator b/BMPE280/bmp280.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/BMPE280/bmp280.creator @@ -0,0 +1 @@ +[General] diff --git a/BMPE280/bmp280.creator.user b/BMPE280/bmp280.creator.user new file mode 100644 index 0000000..24ef76e --- /dev/null +++ b/BMPE280/bmp280.creator.user @@ -0,0 +1,174 @@ + + + + + + EnvironmentId + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + false + true + *.md, *.MD, Makefile + false + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + Builtin.BuildSystem + + true + true + Builtin.DefaultTidyAndClazy + 2 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + 0 + 0 + 0 + + /tmp/1/home/eddy/BMP280 + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Развёртывание + Развёртывание + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/BMPE280/bmp280.cxxflags b/BMPE280/bmp280.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/BMPE280/bmp280.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/BMPE280/bmp280.files b/BMPE280/bmp280.files new file mode 100644 index 0000000..8d07a48 --- /dev/null +++ b/BMPE280/bmp280.files @@ -0,0 +1,5 @@ +BMP280.c +BMP280.h +i2c.c +i2c.h +main.c diff --git a/BMPE280/bmp280.includes b/BMPE280/bmp280.includes new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/BMPE280/bmp280.includes @@ -0,0 +1 @@ +. diff --git a/BMPE280/i2c.c b/BMPE280/i2c.c new file mode 100644 index 0000000..3e402e2 --- /dev/null +++ b/BMPE280/i2c.c @@ -0,0 +1,249 @@ +/* + * This file is part of the bmp280 project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint8_t lastaddr = 0; +static int I2Cfd = -1; + +/** + * @brief i2c_read_reg8 - read 8-bit addressed register (8 bit) + * @param regaddr - register address + * @param data - data read + * @return state + */ +int i2c_read_reg8(uint8_t regaddr, uint8_t *data){ + if(I2Cfd < 1) return FALSE; + struct i2c_smbus_ioctl_data args; + union i2c_smbus_data sd; + args.read_write = I2C_SMBUS_READ; + args.command = regaddr; + args.size = I2C_SMBUS_BYTE_DATA; + args.data = &sd; + if(ioctl(I2Cfd, I2C_SMBUS, &args) < 0){ + WARN("i2c_read_reg8, ioctl()"); + return FALSE; + } + if(data) *data = sd.byte; + return TRUE; +} + +/** + * @brief i2c_write_reg8 - write to 8-bit addressed register + * @param regaddr - address + * @param data - data + * @return state + */ +int i2c_write_reg8(uint8_t regaddr, uint8_t data){ + if(I2Cfd < 1) return FALSE; + struct i2c_smbus_ioctl_data args; + union i2c_smbus_data sd; + sd.byte = data; + args.read_write = I2C_SMBUS_WRITE; + args.command = regaddr; + args.size = I2C_SMBUS_BYTE_DATA; + args.data = &sd; + if(ioctl(I2Cfd, I2C_SMBUS, &args) < 0){ + WARN("i2c_write_reg8, ioctl()"); + return FALSE; + } + return TRUE; +} + +/** + * @brief i2c_read_reg16 - read 16-bit addressed register (to 16-bit data) + * @param regaddr - address + * @param data - data + * @return state + */ +int i2c_read_reg16(uint16_t regaddr, uint16_t *data){ + if(I2Cfd < 1) return FALSE; + struct i2c_msg m[2]; + struct i2c_rdwr_ioctl_data x = {.msgs = m, .nmsgs = 2}; + m[0].addr = lastaddr; m[1].addr = lastaddr; + m[0].flags = 0; + m[1].flags = I2C_M_RD; + m[0].len = 2; m[1].len = 2; + uint8_t a[2], d[2] = {0}; + a[0] = regaddr >> 8; + a[1] = regaddr & 0xff; + m[0].buf = a; m[1].buf = d; + if(ioctl(I2Cfd, I2C_RDWR, &x) < 0){ + WARN("i2c_read_reg16, ioctl()"); + return FALSE; + } + if(data) *data = (uint16_t)((d[0] << 8) | (d[1])); + return TRUE; +} + +/** + * @brief i2c_write_reg16 - write 16-bit data value to 16-bit addressed register + * @param regaddr - address + * @param data - data to write + * @return state + */ +int i2c_write_reg16(uint16_t regaddr, uint16_t data){ + if(I2Cfd < 1) return FALSE; + union i2c_smbus_data d; + d.block[0] = 3; + d.block[1] = regaddr & 0xff; + d.block[2] = data >> 8; + d.block[3] = data & 0xff; + struct i2c_smbus_ioctl_data args; + args.read_write = I2C_SMBUS_WRITE; + args.command = regaddr >> 8; + args.size = I2C_SMBUS_I2C_BLOCK_DATA; + args.data = &d; + if(ioctl(I2Cfd, I2C_SMBUS, &args) < 0){ + WARN("i2c_write_reg16, ioctl()"); + return FALSE; + } +/* printf("Block: "); + for(int i = 0; i < 4; ++i) printf("0x%02x ", d.block[i]); + printf("\n");*/ + return TRUE; +} + +/** + * @brief i2c_set_slave_address - set current slave address + * @param addr - address + * @return state + */ +int i2c_set_slave_address(uint8_t addr){ + if(I2Cfd < 1) return FALSE; + if(ioctl(I2Cfd, I2C_SLAVE, addr) < 0){ + WARN("i2c_set_slave_address, ioctl()"); + return FALSE; + } + lastaddr = addr; + return TRUE; +} + +/** + * @brief i2c_open - open I2C device + * @param path - full path to device + * @return state + */ +int i2c_open(const char *path){ + if(I2Cfd > 0) close(I2Cfd); + I2Cfd = open(path, O_RDWR); + if(I2Cfd < 1){ + WARN("i2c_open, open()"); + return FALSE; + } + return TRUE; +} + +void i2c_close(){ + if(I2Cfd > 0) close(I2Cfd); +} + +#if 0 +// Don't work :( +/** + * @brief read_regN8 - read up to I2C_SMBUS_BLOCK_MAX bytes from 8-bit addressed register + * @param regaddr - address + * @param data - data to read + * @param N - amount of bytes + * @return state + */ +static int read_regN8(uint8_t regaddr, uint8_t *data, uint16_t N){ + if(I2Cfd < 1 || N > I2C_SMBUS_BLOCK_MAX || N == 0 || !data) return FALSE; + struct i2c_smbus_ioctl_data args = {0}; + union i2c_smbus_data sd = {0}; + sd.block[0] = N; + DBG("block: %d, %d, %d", sd.block[0], sd.block[1], sd.block[2]); + DBG("Try to get %d bytes from 0x%02x", N, regaddr); + args.read_write = I2C_SMBUS_READ; + args.command = regaddr; + args.size = I2C_SMBUS_BLOCK_DATA; + args.data = &sd; + if(ioctl(I2Cfd, I2C_SMBUS, &args) < 0){ + WARN("read_regN8, ioctl()"); + return FALSE; + } + DBG("block: %d, %d, %d", sd.block[0], sd.block[1], sd.block[2]); + memcpy(data, sd.block+1, N); + return TRUE; +} +#endif + +/** + * @brief read_data16 - read data from 16-bit addressed register + * @param regaddr - address + * @param N - amount of bytes + * @param array - data read + * @return state + */ +int i2c_read_data16(uint16_t regaddr, uint16_t N, uint8_t *array){ + if(I2Cfd < 1 || N == 0 || !array) return FALSE; + struct i2c_msg m[2]; + struct i2c_rdwr_ioctl_data x = {.msgs = m, .nmsgs = 2}; + m[0].addr = lastaddr; m[1].addr = lastaddr; + m[0].flags = 0; + m[1].flags = I2C_M_RD; + m[0].len = 2; m[1].len = N; + uint8_t a[2]; + a[0] = regaddr >> 8; + a[1] = regaddr & 0xff; + m[0].buf = a; m[1].buf = array; + if(ioctl(I2Cfd, I2C_RDWR, &x) < 0){ + WARN("i2c_read_data16, ioctl()"); + return FALSE; + } + return TRUE; +} + +/** + * @brief read_data8 - read data from 8-bit addressed register + * @param regaddr - address + * @param N - amount of bytes + * @param array - data read + * @return state + */ +int i2c_read_data8(uint8_t regaddr, uint16_t N, uint8_t *array){ + if(I2Cfd < 1 || N < 1 || N+regaddr > 0xff || !array) return FALSE; +#if 0 + uint16_t rest = N; + do{ + uint8_t l = (rest > I2C_SMBUS_BLOCK_MAX) ? I2C_SMBUS_BLOCK_MAX : (uint8_t)rest; + if(!read_regN8(regaddr, array, l)){ + DBG("can't read"); + return FALSE; + } + rest -= l; + regaddr += l; + array += l; + }while(rest); +#endif + for(uint16_t i = 0; i < N; ++i){ + if(!i2c_read_reg8((uint8_t)(regaddr+i), array++)){ + DBG("can't read @%dth byte", i); + return FALSE; + } + } + return TRUE; +} diff --git a/BMPE280/i2c.h b/BMPE280/i2c.h new file mode 100644 index 0000000..42a8a65 --- /dev/null +++ b/BMPE280/i2c.h @@ -0,0 +1,39 @@ +/* + * This file is part of the bmp280 project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +int i2c_open(const char *path); +void i2c_close(); +int i2c_set_slave_address(uint8_t addr); +int i2c_read_reg8(uint8_t regaddr, uint8_t *data); +int i2c_write_reg8(uint8_t regaddr, uint8_t data); +int i2c_read_data8(uint8_t regaddr, uint16_t N, uint8_t *array); +int i2c_read_reg16(uint16_t regaddr, uint16_t *data); +int i2c_write_reg16(uint16_t regaddr, uint16_t data); +int i2c_read_data16(uint16_t regaddr, uint16_t N, uint8_t *array); + diff --git a/BMPE280/main.c b/BMPE280/main.c new file mode 100644 index 0000000..6f58129 --- /dev/null +++ b/BMPE280/main.c @@ -0,0 +1,90 @@ +/* + * This file is part of the bmp280 project. + * Copyright 2022 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "BMP280.h" +#include "i2c.h" + +typedef struct{ + char *device; + int slaveaddr; + int help; +} glob_pars; + +static glob_pars G = {.device = "/dev/i2c-3", .slaveaddr = BMP280_I2C_ADDRESS}; + +static myoption cmdlnopts[] = { + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), _("show this help")}, + {"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("I2C device path")}, + {"slave", NEED_ARG, NULL, 'a', arg_int, APTR(&G.slaveaddr), _("I2C slave address (0x76 or 0x77)")}, + end_option +}; + + +int main(int argc, char **argv){ + initial_setup(); + parseargs(&argc, &argv, cmdlnopts); + if(G.help) showhelp(-1, cmdlnopts); + if(G.slaveaddr < 0 || G.slaveaddr > 0x7f) ERRX("I2C address should be 7-bit"); + if(!i2c_open(G.device)) ERR("Can't open %s", G.device); + if(!i2c_set_slave_address((uint8_t)G.slaveaddr)){ + WARN("Can't set slave address 0x%02x", G.slaveaddr); + goto clo; + } + if(!i2c_read_reg8(0, NULL)) ERR("Can't connect!"); + while(!BMP280_init()) sleep(1); + uint8_t devid; + BMP280_read_ID(&devid); + DBG("ID: 0x%02x", devid); + while(!BMP280_start()){ + DBG("Trying to start"); + sleep(1); + } + while (1){ + BMP280_process(); + BMP280_status s = BMP280_get_status(); + if(s == BMP280_RDY){ // data ready - get it + float T, P, H; + int ntries = 0; + for(; ntries < 3; ++ntries) if(BMP280_getdata(&T, &P, &H)) break; + if(ntries == 3){ + WARNX("Can't read data"); + continue; + } + float mm = P * 0.00750062f; + printf("T=%.1f, P=%.1fPa (%.1fmmHg)", T, P, mm); + if(devid == BME280_CHIP_ID){ // got humidity too + printf(", H=%.1f%%", H); + } + printf("\n"); + sleep(5); + while(!BMP280_start()) usleep(1000); + }else if(s == BMP280_ERR){ + printf("Error in measurement\n"); + BMP280_reset(); + BMP280_init(); + } + } + +clo: + i2c_close(); + return 0; +}