Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2C lock #27

Merged
merged 5 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions src/eeprom/eeprom.c
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
#include "eeprom.h"
#include "sensor_api.h"
#include <hw/i2c.h>
#include <string.h>
#include <time.h>

/** The address of the EEPROM on the I2C bus. */
#define EEPROM_ADDR 0x50

/** Address for reading the EEPROM. */
#define EEPROM_READ (EEPROM_ADDR | 0x1)

/** Address for writing to the EEPROM. */
#define EEPROM_WRITE (EEPROM_ADDR & 0xFE)

/** Defines a small buffer for the dummy write request. */
struct dummy_write_t {
i2c_send_t header; /**< The send header containing address information. */
Expand All @@ -32,25 +28,32 @@ errno_t eeprom_read(uint8_t addr, int bus, void *buf, size_t n) {
{
.stop = 0,
.len = 1,
.slave = {.fmt = I2C_ADDRFMT_7BIT, .addr = EEPROM_WRITE},
.slave = {.fmt = I2C_ADDRFMT_7BIT, .addr = EEPROM_ADDR},
},
};
dummy_write.byte_address = addr;

errno_t err = devctl(bus, DCMD_I2C_SEND, &dummy_write, sizeof(dummy_write), NULL);
// Lock the bus
errno_t err = devctl(bus, DCMD_I2C_LOCK, NULL, 0, NULL);
if (err != EOK) return err;

err = devctl(bus, DCMD_I2C_SEND, &dummy_write, sizeof(dummy_write), NULL);
if (err != EOK) goto return_defer;

// Start sequential read into buffer
i2c_sendrecv_t read_header = {
.stop = 1,
.send_len = 0,
.recv_len = n,
.slave = {.fmt = I2C_ADDRFMT_7BIT, .addr = EEPROM_WRITE},
.slave = {.fmt = I2C_ADDRFMT_7BIT, .addr = EEPROM_ADDR},
};
memcpy(buf, &read_header, sizeof(read_header));
err = devctl(bus, DCMD_I2C_SENDRECV, buf, n + sizeof(read_header), NULL);
if (err != EOK) return err;
return EOK;
if (err != EOK) goto return_defer;

return_defer:
devctl(bus, DCMD_I2C_UNLOCK, NULL, 0, NULL); // Unlock I2C bus
return err;
}

/**
Expand Down
34 changes: 29 additions & 5 deletions src/sensors/lsm6dso32/lsm6dso32.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

/** Acceleration due to gravity in m/s^2. */
Expand Down Expand Up @@ -79,13 +80,15 @@ enum imu_reg {

/** Macro to early return an error. */
#define return_err(err) \
if (err != EOK) return err
if (err != EOK) goto return_defer;

/** A list of data types that can be read by the LSM6DSO32. */
static const SensorTag TAGS[] = {TAG_TEMPERATURE, TAG_LINEAR_ACCEL, TAG_ANGULAR_VEL};

/**
* Write data to a register of the LSM6DSO32.
* WARNING: This function does not lock the I2C bus. It is meant to be called multiple times and therefore the bus must
* be locked by the caller.
* @param sensor A pointer to a LSM6DSO32 sensor instance.
* @param reg The address of the register to write to.
* @param data The byte of data to write to the register.
Expand All @@ -108,6 +111,7 @@ static errno_t lsm6dso32_write_byte(Sensor const *sensor, const uint8_t reg, con

/**
* Read data from register address `reg`.
* WARNING: This function does not lock the I2C bus, as it is meant to be used in a continuous stream of calls.
* @param sensor A reference to an LSM6DSO32 sensor instance.
* @param reg The register address to read from.
* @param buf The buffer to read data into.
Expand All @@ -123,10 +127,10 @@ static errno_t lsm6dso32_read_byte(Sensor *sensor, uint8_t reg, uint8_t *buf) {
read_cmd[sizeof(read_hdr)] = reg; // Data to be send is the register address

errno_t err = devctl(sensor->loc.bus, DCMD_I2C_SENDRECV, read_cmd, sizeof(read_cmd), NULL);
return_err(err);
if (err != EOK) return err;

*buf = read_cmd[sizeof(read_hdr)]; // Received byte will be the last one
return EOK;
return err;
}

/**
Expand All @@ -140,13 +144,18 @@ static errno_t lsm6dso32_read_byte(Sensor *sensor, uint8_t reg, uint8_t *buf) {
static errno_t lsm6dso32_read(Sensor *sensor, const SensorTag tag, void *buf, size_t *nbytes) {
static errno_t err;

err = devctl(sensor->loc.bus, DCMD_I2C_LOCK, NULL, 0, NULL); // Lock I2C bus
if (err != EOK) return err;

switch (tag) {
case TAG_TEMPERATURE: {
int16_t temp;
err = lsm6dso32_read_byte(sensor, OUT_TEMP_L, (uint8_t *)(&temp)); // Read low byte
return_err(err);
err = lsm6dso32_read_byte(sensor, OUT_TEMP_H, (uint8_t *)(&temp) + 1); // Read high byte
return_err(err);
devctl(sensor->loc.bus, DCMD_I2C_UNLOCK, NULL, 0, NULL); // Unlock bus

*(float *)buf = (float)(temp) / 256.0f + 25.0f; // In degrees Celsius
*nbytes = sizeof(float);
break;
Expand All @@ -171,6 +180,7 @@ static errno_t lsm6dso32_read(Sensor *sensor, const SensorTag tag, void *buf, si
err = lsm6dso32_read_byte(sensor, OUTZ_H_A, (uint8_t *)(&z) + 1);
return_err(err);

devctl(sensor->loc.bus, DCMD_I2C_UNLOCK, NULL, 0, NULL); // Unlock bus
// Converts milli-Gs per LSB to m/s^2
float conversion_factor =
(float)((LSM6DSO32Context *)(sensor->context.data))->acc_fsr * MILLI_UNIT_PER_LSB_TO_UNIT * GRAVIT_ACC;
Expand Down Expand Up @@ -201,6 +211,7 @@ static errno_t lsm6dso32_read(Sensor *sensor, const SensorTag tag, void *buf, si
err = lsm6dso32_read_byte(sensor, OUTZ_H_G, (uint8_t *)(&z) + 1);
return_err(err);

devctl(sensor->loc.bus, DCMD_I2C_UNLOCK, NULL, 0, NULL); // Unlock bus
// Converts millidegrees per second per LSB to degrees per second
float conversion_factor =
(float)((LSM6DSO32Context *)(sensor->context.data))->gyro_fsr * MILLI_UNIT_PER_LSB_TO_UNIT;
Expand All @@ -214,7 +225,14 @@ static errno_t lsm6dso32_read(Sensor *sensor, const SensorTag tag, void *buf, si
default:
return EINVAL;
}
return EOK;

return err;

// Returning an error that requires cleanup
return_defer:
devctl(sensor->loc.bus, DCMD_I2C_UNLOCK, NULL, 0, NULL); // Unlock bus
*nbytes = 0;
return err;
}

/**
Expand All @@ -225,7 +243,10 @@ static errno_t lsm6dso32_read(Sensor *sensor, const SensorTag tag, void *buf, si
static errno_t lsm6dso32_open(Sensor *sensor) {

// Perform software reset
errno_t err = lsm6dso32_write_byte(sensor, CTRL3_C, 0x01);
errno_t err = devctl(sensor->loc.bus, DCMD_I2C_LOCK, NULL, 0, NULL);
if (err != EOK) return err;

err = lsm6dso32_write_byte(sensor, CTRL3_C, 0x01);
return_err(err);

// TODO: We will want to operate in continuous mode for our use case (polling)
Expand All @@ -242,8 +263,11 @@ static errno_t lsm6dso32_open(Sensor *sensor) {
// TODO: what full-scale selection? Keep 500 for now
((LSM6DSO32Context *)(sensor->context.data))->gyro_fsr = 500;
err = lsm6dso32_write_byte(sensor, CTRL2_G, 0xA1);
return_err(err);

// TODO: what filter configuration will give the best measurements?
return_defer:
devctl(sensor->loc.bus, DCMD_I2C_UNLOCK, NULL, 0, NULL);
return err;
}

Expand Down
64 changes: 47 additions & 17 deletions src/sensors/ms5611/ms5611.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@
#include <math.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

/** Number of calibration coefficients */
#define NUM_COEFFICIENTS 8

/** Macro to early return an error. */
#define return_err(err) \
if (err != EOK) return err
if (err != EOK) goto return_defer

/** Defines the universal gas constant. */
#define R 8.31432
Expand Down Expand Up @@ -91,7 +92,15 @@ static errno_t ms5611_reset(SensorLocation *loc) {
uint8_t reset_cmd[sizeof(i2c_send_t) + 1];
memcpy(reset_cmd, &reset, sizeof(reset));
reset_cmd[sizeof(reset)] = CMD_RESET;
return devctl(loc->bus, DCMD_I2C_SEND, &reset_cmd, sizeof(reset_cmd), NULL);

errno_t err = devctl(loc->bus, DCMD_I2C_LOCK, NULL, 0, NULL); // Lock I2C bus
return_err(err);

err = devctl(loc->bus, DCMD_I2C_SEND, &reset_cmd, sizeof(reset_cmd), NULL);

return_defer:
devctl(loc->bus, DCMD_I2C_UNLOCK, NULL, 0, NULL); // Unlock I2C bus
return err;
}

/**
Expand All @@ -108,8 +117,16 @@ static errno_t ms5611_read_dreg(SensorLocation *loc, uint8_t dreg, uint32_t *val
uint8_t conversion_cmd[sizeof(conversion) + 1];
memcpy(conversion_cmd, &conversion, sizeof(conversion));
conversion_cmd[sizeof(conversion)] = CMD_ADC_CONV + dreg;
errno_t result = devctl(loc->bus, DCMD_I2C_SEND, &conversion_cmd, sizeof(conversion_cmd), NULL);
return_err(result);

// Lock I2C bus before sending command
errno_t err = devctl(loc->bus, DCMD_I2C_LOCK, NULL, 0, NULL);
if (err != EOK) return err;

err = devctl(loc->bus, DCMD_I2C_SEND, &conversion_cmd, sizeof(conversion_cmd), NULL);
if (err != EOK) {
devctl(loc->bus, DCMD_I2C_UNLOCK, NULL, 0, NULL);
return err;
}

// Wait for appropriate conversion time
switch (dreg & 0xF) {
Expand All @@ -135,15 +152,22 @@ static errno_t ms5611_read_dreg(SensorLocation *loc, uint8_t dreg, uint32_t *val
uint8_t read_cmd[sizeof(read) + 3];
memcpy(read_cmd, &read, sizeof(read));
read_cmd[sizeof(read)] = CMD_ADC_READ;
result = devctl(loc->bus, DCMD_I2C_SENDRECV, &read_cmd, sizeof(read_cmd), NULL);
return_err(result);
err = devctl(loc->bus, DCMD_I2C_SENDRECV, &read_cmd, sizeof(read_cmd), NULL);

if (err != EOK) {
devctl(loc->bus, DCMD_I2C_UNLOCK, NULL, 0, NULL);
return err;
}

// Unlock I2C bus
err = devctl(loc->bus, DCMD_I2C_UNLOCK, NULL, 0, NULL);

*value = 0;
*value += read_cmd[sizeof(read)] * 65536;
*value += read_cmd[sizeof(read) + 1] * 256;
*value += read_cmd[sizeof(read) + 2];

return EOK;
return err;
}

/**
Expand All @@ -154,8 +178,8 @@ static errno_t ms5611_read_dreg(SensorLocation *loc, uint8_t dreg, uint32_t *val
static errno_t ms5611_open(Sensor *sensor) {

// Load calibration into PROM
errno_t op_status = ms5611_reset(&sensor->loc);
return_err(op_status);
errno_t err = ms5611_reset(&sensor->loc);
if (err != EOK) return err;
usleep(10000); // Takes some time to reset

// PROM read command buffer
Expand All @@ -165,21 +189,27 @@ static errno_t ms5611_open(Sensor *sensor) {

// Read calibration data into sensor context
MS5611Context *ms5611_context = (MS5611Context *)sensor->context.data;
err = devctl(sensor->loc.bus, DCMD_I2C_LOCK, NULL, 0, NULL); // Lock I2C bus
if (err != EOK) return err;

for (uint8_t i = 0; i < NUM_COEFFICIENTS; i++) {

// Read from PROM
prom_read_cmd[sizeof(prom_read)] = CMD_PROM_RD + sizeof(uint16_t) * i; // Command to read next coefficient
op_status = devctl(sensor->loc.bus, DCMD_I2C_SENDRECV, &prom_read_cmd, sizeof(prom_read_cmd), NULL);
return_err(op_status);
err = devctl(sensor->loc.bus, DCMD_I2C_SENDRECV, &prom_read_cmd, sizeof(prom_read_cmd), NULL);
if (err != EOK) break;

// Store calibration coefficient
memcpy_be(&ms5611_context->coefs[i], &prom_read_cmd[sizeof(prom_read)], sizeof(uint16_t));
}

err = devctl(sensor->loc.bus, DCMD_I2C_UNLOCK, NULL, 0, NULL); // Unlock I2C bus
if (err != EOK) return err;

// Get the ground pressure
size_t nbytes;
errno_t pressure_read = sensor->read(sensor, TAG_PRESSURE, &ms5611_context->ground_pressure, &nbytes);
return pressure_read;
err = sensor->read(sensor, TAG_PRESSURE, &ms5611_context->ground_pressure, &nbytes);
return err;
}

/**
Expand All @@ -194,10 +224,10 @@ static errno_t ms5611_read(Sensor *sensor, const SensorTag tag, void *buf, size_

// Read D registers with configured precision
uint32_t d1, d2;
errno_t dread_res = ms5611_read_dreg(&sensor->loc, D1 + PRECISIONS[sensor->precision], &d1);
return_err(dread_res);
dread_res = ms5611_read_dreg(&sensor->loc, D2 + PRECISIONS[sensor->precision], &d2);
return_err(dread_res);
errno_t err = ms5611_read_dreg(&sensor->loc, D1 + PRECISIONS[sensor->precision], &d1);
if (err != EOK) return err;
err = ms5611_read_dreg(&sensor->loc, D2 + PRECISIONS[sensor->precision], &d2);
if (err != EOK) return err;

// Extract context
MS5611Context *ctx = (MS5611Context *)sensor->context.data;
Expand Down
Loading
Loading