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

drivers: add driver for FXOS8700 3-axis accelerometer/magnetometer #8978

Merged
merged 1 commit into from
May 14, 2018

Conversation

Hyungsin
Copy link

@Hyungsin Hyungsin commented Apr 18, 2018

This PR provides a driver for the FXOS8700 sensor, which reads 3-axis acceleration and magnetic field.
Driver implementation style is similar to HDC1000 driver.

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch 3 times, most recently from 9dda5b2 to caffc42 Compare April 18, 2018 22:19
@jnohlgard
Copy link
Member

Nice! This sensor can be found on the frdm-kw41z board.

Copy link
Member

@jnohlgard jnohlgard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments below. I will test on the frdm-kw41z board. I can provide a board config for that particular setup when I get it running.

*/
static const saul_reg_info_t fxos8700_saul_info[] =
{
HDC1000_SAUL_INFO
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy pasta

DEBUG("[fxos8700] Could not read WHOAMI (%d)\n", rv);
return FXOS8700_NOBUS;
}
dev->whoami = config;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any use case for saving this value? It seems like it is never used by the implementation.

} fxos8700_params_t;

/**
* @brief Device descriptor for a AT30TSE75x device
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy pasta, and weird indentation

} fxos8700_t;

/**
* @brief Individual hybrid measurement
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird indent

uint8_t ydr:1; /*!< bit: 0 Start Conversion Event In */
uint8_t xdr:1; /*!< bit: 1 Synchronization Event In */
} bit; /*!< Structure used for bit access */
uint8_t reg; /*!< Type used for register access */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and everywhere else: use /** (and of course /**<) for doxygen comments in RIOT.

uint8_t xdr:1; /*!< bit: 1 Synchronization Event In */
} bit; /*!< Structure used for bit access */
uint8_t reg; /*!< Type used for register access */
} FXOS8700_STATUS_Type;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type, and the other bit fields below, are not actually used by the implementation.
Additionally, it is not required by a compiler to pack the bit fields in the struct, so this bit representation will not be guaranteed to match the hardware register bits. It is more robust to use bit mask macros or other bit operations in the implementation. The compiler should be smart enough to optimize the accesses in a good way when one of the operands is a numeric literal or static constant.

@jnohlgard
Copy link
Member

The cache does not handle multiple instances.
I understand that your projects use the caching to reduce the power consumption while reading multiple successive reads, but I wonder if we should generalize the cache into its own module and let it wrap any sensor driver, with configurable cache time to live. Most sensors have the same kind of read function API where you pass a driver instance as the first argument and an output buffer as the next argument with error codes as the return value, that would be easily wrapped by a generic cache implementation.

@jnohlgard
Copy link
Member

Otherwise we will likely end up with various cache implementations in each sensor driver, with plenty of opportunity for mistakes and difficulties testing them in a reliable way.

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch from b22891e to 495a94a Compare April 19, 2018 05:40
@jnohlgard
Copy link
Member

I think it is a good verification to read the whoami register during init, to check that the device is responding and that the connected device is actually a FXOS8700 sensor by comparing the actual value against the expected value. Just that it does not need to be saved in the context struct.

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch from 495a94a to 921de7a Compare April 19, 2018 05:53
@Hyungsin
Copy link
Author

@gebart. Thank you for the quick review! I resolved you comments.

WHOAMI comparison is still here but its value is not saved now.

Regarding the cache, I don't know how many sensors in RIOT are relevant (one sensor should provide multiple types of information). If this cache functionality is useful for many other sensors, we can think of generalizing it. But I think this should be another PR.

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch 2 times, most recently from 52a33f5 to 933f2dd Compare April 19, 2018 06:03
Copy link
Member

@jnohlgard jnohlgard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor code style issues.

int32_t acc_raw_z = (int32_t) ((data[4]<<6) | (data[5]>>2));
acc->x = (int16_t)(acc_raw_x*244/1000);
acc->y = (int16_t)(acc_raw_y*244/1000);
acc->z = (int16_t)(acc_raw_z*244/1000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code style: add spaces around operators

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, for clarity it may be better to have parentheses around (raw * 244) / 1000, though it changes nothing in the generated code since the associativity is left to right for the multiplication and division operators.

if (mag) {
mag->x = (int16_t) ((data[6] <<8) | data[7]);
mag->y = (int16_t) ((data[8] <<8) | data[9]);
mag->z = (int16_t) ((data[10]<<8) | data[11]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spaces around bit shift operators


int fxos8700_read_cached(const fxos8700_t *dev, fxos8700_measurement_t* acc, fxos8700_measurement_t* mag)
{
uint32_t now = xtimer_now_usec();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: It may be less computationally expensive to use xtimer_now and convert the renew interval to ticks instead, since you can precompute that us to tick conversion at compilation time rather than do a conversion from ticks to us in every read. See this as an optional comment, and maybe just remember for future drivers, I assume this function will be called somewhat infrequently since the renew interval is set to 1 second.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I will remember for the future driver implementations which require frequent xtimer access 👍

@jnohlgard
Copy link
Member

Are you using this sensor externally or integrated on any dev board? Do you have any updated board configurations you want to add here?

@aabadie aabadie added Type: new feature The issue requests / The PR implemements a new feature for RIOT Area: drivers Area: Device drivers labels Apr 19, 2018
Copy link
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rough pass of review. I found problems with doxygen and style and have minor comments in the driver implementation (return code, defines, etc)

*/

/**
* @ingroup driver_fxos8700
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be drivers_fxos8700

static int fxos8700_read_regs(fxos8700_t* dev, uint8_t reg, uint8_t* data, size_t len)
{
i2c_acquire(dev->p.i2c);
if(i2c_read_regs(dev->p.i2c, dev->p.addr, reg, (char*) data, len) <= 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space after if here and in other places below

return FXOS8700_BUSERR;
}

while(!(ready & 0x08)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space after while here and below. Maybe unscrustify the file ?

@@ -0,0 +1,205 @@
/*
* Copyright (C) 2017 UC Berkeley
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be 2018 ?

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2017 Hyung-Sin Kim
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not UC Berkeley ?

}
#endif

/** @} */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should be after #endif below

if (config != FXOS8700_WHO_AM_I_VAL) {
i2c_release(dev->p.i2c);
DEBUG("[fxos8700] WHOAMI is wrong (%2x)\n", config);
return FXOS8700_NOBUS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add another return status for this case ? Something line FXOS8700_NODEV ?

/* Configure the ODR to maximum (400Hz in hybrid mode) */
config = 0x00;
if (fxos8700_write_regs(dev, FXOS8700_REG_CTRL_REG1, &config, 1) != FXOS8700_OK) {
return FXOS8700_NOBUS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be FXOS8700_BUSERR instead ? Here and in other places below.

#define FXOS8700_RENEW_INTERVAL 1000000ul
#endif

#define ACCEL_ONLY_MODE 0x00
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe put parenthesis around these hexadecimal defines

i2c_acquire(dev->p.i2c);
if(i2c_read_regs(dev->p.i2c, dev->p.addr, reg, (char*) data, len) <= 0) {
DEBUG("[fxos8700] Can't read register 0x%x\n", reg);
i2c_release(dev->p.i2c);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use for example:

#define DEV_I2C (dev->p.i2c)

at the beginning of this file. Same with addr attribute and maybe others

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the style of the current HDC1000 implementation. Is this style a strict requirement?

Copy link
Contributor

@aabadie aabadie Apr 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that was more a suggestion for future maintenance, technically it doesn't change anything.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Thanks!

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch from 72211b9 to b7484c4 Compare April 19, 2018 17:16
@Hyungsin
Copy link
Author

@gebart @aabadie
Thank you for the reviews and I resolved comments again. 👍

@gebart, I'm using FXOS8700 on the hamilton board, which I plan to include in RIOT.
Basically, all sensor/clock/radio drivers I have modified/added so far are related to the hamilton.

#define MAG_ONLY_MODE (0x01)
#define HYBRID_MODE (0x03)

static fxos8700_measurement_t acc_cached, mag_cached;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of those static variable; why no having them as attributes of fxos8700_t ?
Because otherwise this will be a problem with multiple instances of this device as already reported by @gebart here.That could be a best temporary solution until we have a module for managing cache.
Maybe saul could do it ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree and updated this PR reflecting this comment. Thanks!

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch 4 times, most recently from ef2c5c5 to 7e388d3 Compare April 19, 2018 21:49
* @brief Device descriptor for a FXOS8700 device
*/
typedef struct {
fxos8700_measurement_t acc_cached;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The attributes of this struct are missing documentation, same with the fxos8700_measurement_t struct

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved

@aabadie aabadie added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Apr 19, 2018
@aabadie
Copy link
Contributor

aabadie commented Apr 19, 2018

Could you also add a test application ?

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch 2 times, most recently from ab7d496 to 64b1963 Compare April 19, 2018 23:44
@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch from 4b747d3 to 2e6a557 Compare April 20, 2018 17:04
@Hyungsin
Copy link
Author

@aabadie, @gebart, I addressed your comments.
and @gebart, let me test this with my device also and I will let you know. It worked before but there could be some bugs while rearranging codes.

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch 2 times, most recently from 1098e15 to 9195661 Compare April 20, 2018 17:11
@Hyungsin
Copy link
Author

Hyungsin commented Apr 20, 2018

@gebart, I checked that it works at least in my case (hamilton), as below.

Dev: fxos8700 Type: SENSE_ACCEL
Data: [0] 15mg
[1] 1005mg
[2] 156mg

Can you retry?

@jnohlgard
Copy link
Member

I will check again tomorrow

@jnohlgard
Copy link
Member

My frdm-kw41z is giving strange values, I will try again with a frdm-k22f board tomorrow

Copy link
Member

@jnohlgard jnohlgard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the source of my problems. Negative values of acceleration are not handled correctly, see my comment below regarding the bit shift

}

/* Read all data at once */
if (fxos8700_read_regs(dev, FXOS8700_REG_OUT_X_MSB, &data[0], 12)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change magic 12 into sizeof something or a macro definition.

if (acc) {
int32_t acc_raw_x = (int32_t) ((data[0] << 6) | (data[1] >> 2));
int32_t acc_raw_y = (int32_t) ((data[2] << 6) | (data[3] >> 2));
int32_t acc_raw_z = (int32_t) ((data[4] << 6) | (data[5] >> 2));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the shift of 6 loses the sign bit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gives reasonable results for negative numbers:

        int32_t acc_raw_x = (int16_t) ((data[0] << 8) | data[1]) >> 2;
        int32_t acc_raw_y = (int16_t) ((data[2] << 8) | data[3]) >> 2;
        int32_t acc_raw_z = (int16_t) ((data[4] << 8) | data[5]) >> 2;
        acc->x = (int16_t) ((acc_raw_x * 244) / 1000);
        acc->y = (int16_t) ((acc_raw_y * 244) / 1000);
        acc->z = (int16_t) ((acc_raw_z * 244) / 1000);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point. Thanks!!

int32_t acc_raw_z = (int32_t) ((data[4] << 6) | (data[5] >> 2));
acc->x = (int16_t) ((acc_raw_x * 244) / 1000);
acc->y = (int16_t) ((acc_raw_y * 244) / 1000);
acc->z = (int16_t) ((acc_raw_z * 244) / 1000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are really discarding quite a lot of bits of precision with this division. You could just as well configure the sensor to +/- 8 g range and use x * 976 / 1000 instead and get the benefit of an increased sensor range without any difference in accuracy compared to the current implementation

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch 3 times, most recently from 80b99b8 to 48d3bd9 Compare April 23, 2018 20:12
@Hyungsin
Copy link
Author

Hyungsin commented Apr 23, 2018

@gebart, thank you for improving this driver!

  • I made accelerator's scale configurable and replaced some magic numbers with macros.

@Hyungsin
Copy link
Author

@gebart @aabadie, any comment? good to be merged?

Copy link
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codewise it looks good now.

Copy link
Member

@jnohlgard jnohlgard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My comment regarding the lost bits when using 2 or 4 g full scale range still applies, but I don't intend to block this PR just for that.

@jnohlgard
Copy link
Member

Optionally, change the division from 1000 to 100 and change the SAUL scale from -3 to -4 when +/- 2 G range is selected

@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch from ba6ef8b to 67673b2 Compare May 13, 2018 00:51
@Hyungsin Hyungsin force-pushed the forupstream_fxos8700 branch from 67673b2 to beaf08b Compare May 13, 2018 00:52
@Hyungsin
Copy link
Author

Hyungsin commented May 13, 2018

@gebart, I updated, but I think this driver does not lose any bit regardless of resolution setting since it provides "FXOS8700_USE_ACC_RAW_VALUES" mode.

@jnohlgard
Copy link
Member

👍

@jnohlgard
Copy link
Member

@Hyungsin in future PRs, please create fixup/squash commits for changes you add after the initial review to make the review process easier. You can amend typos or other minor corrections, but for anything a little bigger, such as when you added the different scaling factor configurations in this PR, use a separate commit. Then squash the commits when the reviewer requests it. It makes it easier for the reviewer to know what has changed between iterations.

@Hyungsin
Copy link
Author

@gebart, good point. I should keep this in mind!

@aabadie
Copy link
Contributor

aabadie commented May 14, 2018

All green here. Let's merge.

@aabadie aabadie merged commit 372aadd into RIOT-OS:master May 14, 2018
@cladmi cladmi added this to the Release 2018.07 milestone Jul 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: drivers Area: Device drivers CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants