From 4c6741235ed36704a5f3e981ce013d102795916b Mon Sep 17 00:00:00 2001 From: William Clark Date: Mon, 6 Nov 2023 14:41:32 +0000 Subject: [PATCH] SPI and I2C both working interchangeably --- bme680.c | 97 ++++++++++++++++++++++----------- main.c | 25 ++++++--- registers.h | 4 ++ spi.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++ spi.h | 14 +++++ 5 files changed, 253 insertions(+), 39 deletions(-) create mode 100644 spi.c create mode 100644 spi.h diff --git a/bme680.c b/bme680.c index 023da5d..d102676 100644 --- a/bme680.c +++ b/bme680.c @@ -7,70 +7,93 @@ #include "registers.h" -static int write_spi_page(bme680_t *bme680, uint8_t page_value); static void calc_temp_comp(bme680_t *bme680); static void calc_press_comp(bme680_t *bme680); static void calc_hum_comp(bme680_t *bme680); static void calc_gas_res(bme680_t *bme680); +static int set_spi_page(bme680_t *bme680, uint8_t no); + + /********************************************************************/ static int write_dev(bme680_t *bme680, uint8_t reg, uint8_t value) { - uint8_t tmp; + uint8_t req_page = REG_SPI_PAGE(reg); if (BME680_IS_SPI(bme680->mode)) { - if ((tmp = REG_SPI_PAGE(reg)) != bme680->spi_page) { - (void) write_spi_page(bme680, tmp); - bme680->spi_page = tmp; + if (req_page != bme680->spi_page) { + set_spi_page(bme680, req_page); + bme680->spi_page = req_page; } + reg &= 0x7F; } return bme680->dev.write(reg, value); } -static int write_spi_page(bme680_t *bme680, uint8_t page_value) { - uint8_t status_byte = (!!page_value) << 4; - return bme680->dev.write(REG_STATUS, status_byte); -} /********************************************************************/ static int read_dev(bme680_t *bme680, uint8_t reg, uint8_t *dst, uint32_t size) { - uint8_t tmp; + uint8_t req_page = REG_SPI_PAGE(reg); - /* Might have to change the SPI page for this reg if using SPI */ if (BME680_IS_SPI(bme680->mode)) { - if ((tmp = REG_SPI_PAGE(reg)) != bme680->spi_page) { - (void) write_spi_page(bme680, tmp); - bme680->spi_page = tmp; + + if (req_page != bme680->spi_page) { + set_spi_page(bme680, req_page); + bme680->spi_page = req_page; } + reg |= 0x80; } - /* some registers, like id and reset, have diff addr in i2c/spi */ return bme680->dev.read(reg, dst, size); } + +/********************************************************************/ +static int set_spi_page(bme680_t *bme680, uint8_t page_no) { + uint8_t status_reg = page_no << 4; +// printf("page=%d (%s)\n", page_no, REG_SPI_PAGE_MAP(page_no)); + return bme680->dev.write(REG_STATUS, status_reg); +} + + +/********************************************************************/ +static int read_id(bme680_t *bme680, uint8_t *id) { + + uint8_t id_reg; + + /* force spi page 0. special case */ + if (BME680_IS_SPI(bme680->mode)) { + set_spi_page(bme680, 0); + bme680->spi_page = 0; + id_reg = 0x50 | 0x80; + } else { + id_reg = REG_ID; + } + + return bme680->dev.read(id_reg, id, 1); +} + /********************************************************************/ int bme680_init(bme680_t *bme680, uint8_t mode) { - uint8_t car; - uint8_t id_reg; + uint8_t id; int i; bme680->mode = mode; + bme680->spi_page = 0; if (bme680->dev.init() != 0) { return 1; } - /* id reg depends on spi or i2c */ - id_reg = BME680_IS_SPI(mode) ? 0x50 : REG_ID; - if (read_dev(bme680, id_reg, &car, 1) != 0) { + if (read_id(bme680, &id) != 0) { return 1; } - if (car != 0x61) { + if (id != 0x61) { return 1; } @@ -95,12 +118,22 @@ int bme680_deinit(bme680_t *bme680) { /********************************************************************/ int bme680_reset(bme680_t *bme680) { - uint8_t reg = BME680_IS_SPI(bme680->mode) ? 0x60 : REG_RESET; uint8_t magic = 0xB6; + uint8_t reg; int ret; - - ret = write_dev(bme680, reg, magic); - usleep(2000); /* sleep for 5 ms */ + + /* force page 0. special case */ + if (BME680_IS_SPI(bme680->mode)) { + set_spi_page(bme680, 0); + bme680->spi_page = 0; + reg = 0x60 | 0x80; + } else { + reg = REG_RESET; + } + + ret = bme680->dev.write(reg, magic); + usleep(2000); /* sleep for 2 ms */ + return ret; } @@ -133,17 +166,16 @@ int bme680_configure(bme680_t *bme680) { /* write out all 10 setpoints */ /* those not explicitly set are defaulted to 0 (which has no effect) */ for(i=0; i<10; i++) { - err |= bme680->dev.write(0x6D - i, bme680->cfg.gas_wait[9 - i]); - err |= bme680->dev.write(0x63 - i, bme680->cfg.res_heat[9 - i]); - err |= bme680->dev.write(0x59 - i, bme680->cfg.idac_heat[9 - i]); + err |= write_dev(bme680, 0x6D - i, bme680->cfg.gas_wait[9 - i]); + err |= write_dev(bme680, 0x63 - i, bme680->cfg.res_heat[9 - i]); + err |= write_dev(bme680, 0x59 - i, bme680->cfg.idac_heat[9 - i]); } - /* `run_gas' bit */ ctrl_gas1 = bme680->cfg.setpoint | (1 << 4); ctrl_gas0 = 0; /* := (1 << 3) to turn off current going to heater */ - err |= bme680->dev.write(REG_CTRL_GAS_1, ctrl_gas1); - err |= bme680->dev.write(REG_CTRL_GAS_0, ctrl_gas0); + err |= write_dev(bme680, REG_CTRL_GAS_1, ctrl_gas1); + err |= write_dev(bme680, REG_CTRL_GAS_0, ctrl_gas0); // TODO: check gas_valid_r and heat_stab_r in 0x2B @@ -171,7 +203,7 @@ int bme680_start(bme680_t *bme680) { int bme680_poll(bme680_t *bme680) { uint8_t meas_status = 0; - uint8_t gas_measuring = 0; // is only 1 during gas measurement + uint8_t gas_measuring = 0; uint8_t any_measuring = 0; int err = 0; @@ -204,6 +236,7 @@ int bme680_read(bme680_t *bme680) { err |= read_dev(bme680, 0x25, buffer, 2); bme680->adc.hum = (buffer[0] << 8) | buffer[1]; + /* adc readings are only 20-bit when the IIR filter is enabled. * otherwise, it depends on the oversample settings. * note: humidity is not IIR filtered, and always 16-bit. diff --git a/main.c b/main.c index f91f6b8..fcb3143 100644 --- a/main.c +++ b/main.c @@ -5,6 +5,7 @@ #include "bme680.h" #include "i2c.h" +#include "spi.h" #define AMBIENT_TEMP_GUESS 19.0 #define HEATER_TARGET 300.0 @@ -16,14 +17,20 @@ int main(void) { int i; /* 1. Assign functions for interacting with the device */ - bme680.dev.init = i2c_init; - bme680.dev.read = i2c_read; - bme680.dev.write = i2c_write; - bme680.dev.deinit = i2c_deinit; + +// bme680.dev.init = i2c_init; +// bme680.dev.read = i2c_read; +// bme680.dev.write = i2c_write; +// bme680.dev.deinit = i2c_deinit; + + bme680.dev.init = spi_init; + bme680.dev.read = spi_read; + bme680.dev.write = spi_write; + bme680.dev.deinit = spi_deinit; /* 2. set the device mode */ - mode = BME680_MODE_FLOAT | BME680_I2C | BME680_ENABLE_GAS; - /* BME680_MODE_INT | BME680_SPI; */ + mode = BME680_MODE_FLOAT | BME680_SPI | BME680_ENABLE_GAS; + /* BME680_MODE_INT | BME680_I2C; */ /* 3. initialise device, and check its id */ @@ -65,7 +72,7 @@ int main(void) { /* 7-bit word. Each step/lsb is equiv. to 1/8 mA; so max 16 mA */ /* a value of 20 would be equal to 2.5 mA */ /* this s.p. field is allowed to be left as 0 if no preload is required. */ - bme680.cfg.idac_heat[i] = BME680_IDAC(0); + bme680.cfg.idac_heat[i] = BME680_IDAC(100); /* define the time between the start of heating and start of resistance sensing in this s.p.*/ /* Bosch datasheet suggests ~30 - 40ms is usually all that is required to get up to temp. */ @@ -85,6 +92,7 @@ int main(void) { exit(EXIT_FAILURE); } + /* 8. Start forced measurement. After it finishes, it should remember the previous config. */ if (bme680_start(&bme680) != 0) { fprintf(stderr, "bme680_start()\n"); @@ -92,6 +100,7 @@ int main(void) { exit(EXIT_FAILURE); } + /* 9. poll the meas_status register until all scheduled conversions are done */ if (bme680_poll(&bme680) != 0) { fprintf(stderr, "bme680_poll()\n"); @@ -99,6 +108,7 @@ int main(void) { exit(EXIT_FAILURE); } + /* 10. read the ADC's and perform a conversion */ if (bme680_read(&bme680) != 0) { fprintf(stderr, "bme680_read()\n"); @@ -106,6 +116,7 @@ int main(void) { exit(EXIT_FAILURE); } + /* 11. use data ! */ if (BME680_IS_FLOAT(bme680.mode)) { puts("float mode"); diff --git a/registers.h b/registers.h index 1566ec3..1873200 100644 --- a/registers.h +++ b/registers.h @@ -1,7 +1,11 @@ #ifndef REGISTERS_H #define REGISTERS_H +/* SPI page number 0 => 0x00 to 0x7F */ +/* SPI page number 1 => 0x80 to 0xFF */ +/* SPECIAL CASE: 0x73 is reachable in both page 1 and 0 !!!!*/ #define REG_SPI_PAGE(v) ((v > 0x7F) ? 0 : 1) +#define REG_SPI_PAGE_MAP(v) (v == 0 ? "LOW" : "HIGH") #define REG_STATUS 0x73 #define REG_RESET 0xE0 diff --git a/spi.c b/spi.c new file mode 100644 index 0000000..35a12b1 --- /dev/null +++ b/spi.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi.h" + +#define SPI_DEVICE "/dev/spidev0.0" + + +/* + * Pinout config for this example + * + * MOSI - GPIO 10 (physical pin 19) + * MISO - GPIO 9 (physical pin 21) + * SCLK - GPIO 11 (physical pin 23) + * CE0 - GPIO 8 (physical pin 24) + * + */ + +#define SPI_MODE 0 +#define SPI_BITS 8 +#define SPI_SPEED 500 * 1000 /* 500 KHz */ +#define SPI_DELAY 0 + +static int fd; + +//static void print_access(const char *name, uint8_t reg, uint8_t *dst, uint32_t size); + +int spi_init(void) { + + uint8_t mode = SPI_MODE; + uint8_t bits = SPI_BITS; + uint32_t speed = SPI_SPEED; + + if ((fd = open(SPI_DEVICE, O_RDWR)) < 0) { + fprintf(stderr, "spi open(%s)\n", SPI_DEVICE); + return SPI_ERR; + } + + + if (ioctl(fd, SPI_IOC_RD_MODE, &mode) == -1) { + fprintf(stderr, "SPI_IOC_RD_MODE\n"); + return SPI_ERR; + } + + if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) { + fprintf(stderr, "SPI_IOC_WR_MODE\n"); + return SPI_ERR; + } + + if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1) { + fprintf(stderr, "SPI_IOC_WR_BITS_PER_WORD\n"); + return SPI_ERR; + } + + if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits) == -1) { + fprintf(stderr, "SPI_IOC_RD_BITS_PER_WORD\n"); + return SPI_ERR; + } + + if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1) { + fprintf(stderr, "SPI_IOC_WR_MAX_SPEED_HZ\n"); + return SPI_ERR; + } + + if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) == -1) { + fprintf(stderr, "SPI_IOC_RD_MAX_SPEED_HZ\n"); + return SPI_ERR; + } + + + puts("spi_init"); + return SPI_OK; +} + +/* bme680 auto-increments for spi reads */ +int spi_read(uint8_t reg, uint8_t *dst, uint32_t size) { + + struct spi_ioc_transfer tr[2] = {0}; + reg |= 0x80; + + /* write first msg for the register addr */ + tr[0].tx_buf = (unsigned long) ® + tr[0].rx_buf = (unsigned long) 0; + tr[0].len = 1; + + /* write second msg for rx data */ + tr[1].tx_buf = (unsigned long) 0; + tr[1].rx_buf = (unsigned long) dst; + tr[1].len = size; + + if (ioctl(fd, SPI_IOC_MESSAGE(2), tr) < 0) { + fprintf(stderr, "spi_read()\n"); + return SPI_ERR; + } + +// print_access("spi_read", reg, dst, size); + return SPI_OK; +} + +/* bme680 does NOT auto-increment for spi writes, so one at a time */ +int spi_write(uint8_t reg, uint8_t value) { + + struct spi_ioc_transfer tr[2] = {0}; + reg &= 0x7F; + + /* write first msg for the register addr */ + tr[0].tx_buf = (unsigned long) ® + tr[0].rx_buf = (unsigned long) 0; + tr[0].len = (uint32_t) 1; + + /* write second msg for the tx data */ + tr[1].tx_buf = (unsigned long) &value; + tr[1].rx_buf = (unsigned long) 0; + tr[1].len = (uint32_t) 1; + + if (ioctl(fd, SPI_IOC_MESSAGE(2), tr) < 0) { + fprintf(stderr, "spi_write()\n"); + return SPI_ERR; + } + +// print_access("spi_write", reg, &value, 1); + return SPI_OK; +} + +int spi_deinit(void) { + puts("spi_deinit"); + if (fd) { + close(fd); + } + + return SPI_OK; +} + +/* +static void print_access(const char *name, uint8_t reg, uint8_t *dst, uint32_t size) { + printf("%s: %.2X (%d) [", name, reg, size); + for(uint32_t i=0; i + +#define SPI_OK 0 +#define SPI_ERR 1 + +int spi_init (void); +int spi_read (uint8_t reg, uint8_t *dst, uint32_t size); +int spi_write (uint8_t reg, uint8_t value); +int spi_deinit (void); + +#endif