SPI and I2C both working interchangeably

This commit is contained in:
William Clark 2023-11-06 14:41:32 +00:00
parent d1527d19a3
commit 4c6741235e
5 changed files with 253 additions and 39 deletions

View File

@ -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.

25
main.c
View File

@ -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");

View File

@ -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

152
spi.c Normal file
View File

@ -0,0 +1,152 @@
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#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) &reg;
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) &reg;
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<size; i++) {
printf("%.2X", dst[i]);
if (i < (size - 1)) {
printf(", ");
}
}
printf("]\n");
}
*/

14
spi.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef SPI_H
#define SPI_H
#include <stdint.h>
#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