-
Notifications
You must be signed in to change notification settings - Fork 13.6k
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
introduce PX4 sensor classes and use to simplify mpu6000 #11216
Conversation
ad86815
to
35faa48
Compare
35faa48
to
a3fc81c
Compare
The overall diff is large, but if you look at it per commit you'll see the changes to mpu6000 are actually relatively straightforward and mostly deletions. Most of the removed code now exists in PX4Accelerometer/PX4Gyroscope (to be shared with all drivers), or was removed entirely (eg poll rate setup via ioctl). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dagar - this is moving in a good direction.
|
||
// force initial publish to allocate uORB buffer | ||
// TODO: can be removed once all drivers are in threads | ||
_sensor_accel_pub.update(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dagar Can any calls in the constructor fail? If so Keeping the init() phase would make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The initial publish (advertise) could fail if out of memory, but even now in master that's only handled with a warning in init. At least when using the uORB publication wrappers like this it will try the advertise again next publish.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this will be a repeated pattern and constructors can not return a value. I would separate the construtor and the things that can return errors. This gives us a LCD for the embedded no exception model.
int device_type = MPU_DEVICE_TYPE_MPU6000; | ||
enum Rotation rotation = ROTATION_NONE; | ||
|
||
while ((ch = px4_getopt(argc, argv, "T:XISsZzR:a:", &myoptind, &myoptarg)) != EOF) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get a utility class to do this and the bus definitions?
Let's kill the Otherwise this looks pretty good. |
|
||
state = px4_enter_critical_section(); | ||
_reset_wait = hrt_absolute_time() + 30000; | ||
px4_leave_critical_section(state); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well it is 64 bit operation on a 32 bit CPU. For a variable operated on in the foreground and used in the background. I would guess it is. Have a look at the assembly to know for sure,
// Enable I2C bus or Disable I2C bus (recommended on data sheet) | ||
write_checked_reg(MPUREG_USER_CTRL, is_i2c() ? 0 : BIT_I2C_IF_DIS); | ||
|
||
px4_leave_critical_section(state); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am confused about why there are so many of these enter/exit critical sections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If reset were only called of the HRT's ISR, then enter/exit would do nothing (but kill the real-time response of the system) But I believe rest use to be called from the main. So enter/exit makes the operation atomic that insures the timing. I doubt this is close to correct for the new calling structure. Moreover this may have ONLY been needed because the chip was powered by it's IO pins prior to VDD 3v3 Sensors voltage being applied.
We should do a few things here:
-
Do the correct power management. There was a PR that came in did this. But was later augmented because of some clones were rebooting. I shared an approach with @dagar on how to do this correctly.
-
Do a call graph so we can see how the functions in the driver are called. Then comment each one, so there is a clear understanding of the context and thread the functions run on (is this an ISR, is this on Init's thread, is this on a workqueue dispatch or all 3)
-
Design the chip rest and sensors reset so they work and do not kill the RT of the system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Are we doing the correct power management on boards other than fmu-v2/v3?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be but an audit is warranted.
// 2000 deg/s = (2000/180)*PI = 34.906585 rad/s | ||
// scaling factor: | ||
// 1/(2^15)*(2000/180)*PI | ||
_px4_gyro.set_scale(0.0174532f / 16.4f);//1.0f / (32768.0f * (2000.0f / 180.0f) * M_PI_F); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Magic numbers? Do we want to clean up the code or just implement the new architecture? It does seem like a hassle, especially since this is pretty legacy at this point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it is but at least well documented :) We could move this to a const. But Keep the comments with it.
|
||
// get baseline values without self-test enabled | ||
for (uint8_t i = 0; i < repeats; i++) { | ||
up_udelay(1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loop delay from an ISR...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gosh I hope not. See comment about call graph.
hrt_cancel(&_call); | ||
|
||
} else { | ||
#ifdef USE_I2C |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eliminating this kinda crap too would be nice. I'm going to end my review here, there is a lot of cruft in this driver and probably not worth trying to clean up and then regression test it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have suggested we have create a single schedule API. Then there would be one cancel. The caller can configure things such as RT, LP, HP. The RT use the hrt, the LP and HP use work queues. We can also have FIFO, RR, Single and a priority.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dagar can we get one more level of common code to a PX4SensorBase class?
// Enable I2C bus or Disable I2C bus (recommended on data sheet) | ||
write_checked_reg(MPUREG_USER_CTRL, is_i2c() ? 0 : BIT_I2C_IF_DIS); | ||
|
||
px4_leave_critical_section(state); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If reset were only called of the HRT's ISR, then enter/exit would do nothing (but kill the real-time response of the system) But I believe rest use to be called from the main. So enter/exit makes the operation atomic that insures the timing. I doubt this is close to correct for the new calling structure. Moreover this may have ONLY been needed because the chip was powered by it's IO pins prior to VDD 3v3 Sensors voltage being applied.
We should do a few things here:
-
Do the correct power management. There was a PR that came in did this. But was later augmented because of some clones were rebooting. I shared an approach with @dagar on how to do this correctly.
-
Do a call graph so we can see how the functions in the driver are called. Then comment each one, so there is a clear understanding of the context and thread the functions run on (is this an ISR, is this on Init's thread, is this on a workqueue dispatch or all 3)
-
Design the chip rest and sensors reset so they work and do not kill the RT of the system.
|
||
state = px4_enter_critical_section(); | ||
_reset_wait = hrt_absolute_time() + 30000; | ||
px4_leave_critical_section(state); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well it is 64 bit operation on a 32 bit CPU. For a variable operated on in the foreground and used in the background. I would guess it is. Have a look at the assembly to know for sure,
// 2000 deg/s = (2000/180)*PI = 34.906585 rad/s | ||
// scaling factor: | ||
// 1/(2^15)*(2000/180)*PI | ||
_px4_gyro.set_scale(0.0174532f / 16.4f);//1.0f / (32768.0f * (2000.0f / 180.0f) * M_PI_F); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it is but at least well documented :) We could move this to a const. But Keep the comments with it.
|
||
// get baseline values without self-test enabled | ||
for (uint8_t i = 0; i < repeats; i++) { | ||
up_udelay(1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gosh I hope not. See comment about call graph.
hrt_cancel(&_call); | ||
|
||
} else { | ||
#ifdef USE_I2C |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have suggested we have create a single schedule API. Then there would be one cancel. The caller can configure things such as RT, LP, HP. The RT use the hrt, the LP and HP use work queues. We can also have FIFO, RR, Single and a priority.
void PX4Accelerometer::set_device_type(uint8_t devtype) | ||
{ | ||
// current DeviceStructure | ||
union device::Device::DeviceId device_id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this not a class?
device_id.devid_s.devtype = devtype; | ||
|
||
// copy back to report | ||
_sensor_accel_pub.get().device_id = device_id.devid; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the pub have a DeviceId and a setter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This really only exists for legacy reasons that I'd like to phase out eventually. It only exists because the mpu6000 has different device ids for the accel and gyro. https://github.com/PX4/Firmware/blob/master/src/drivers/drv_sensor.h#L64-L69
The only reason I kept it now is to avoid losing existing sensor calibrations.
@@ -108,12 +108,12 @@ class Integrator | |||
/** | |||
* Set auto reset interval during runtime. This won't reset the integrator. | |||
* | |||
* @param auto_reset_interval New reset time interval for the integrator. | |||
* @param auto_reset_interval New reset time interval for the integrator (+- 10%). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should set the % in a const header constant and docomment why the % is needed and 10 is chosen.
_sensor_gyro_pub.get().scaling = 1.0f; | ||
|
||
// set software low pass filter for controllers | ||
updateParams(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment about construction results.
|
||
void PX4Accelerometer::update(hrt_abstime timestamp, int16_t x, int16_t y, int16_t z) | ||
{ | ||
sensor_accel_s &report = _sensor_accel_pub.get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels wrong. I see what the motivation is, but the getter is used to be the setter.
.ref() my feel better.
Is the timestamp stored in the report but not published suggesting something or does all this belong in the if? If it is the latter comment it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it's a bit weird, but already a fairly common pattern. The Publication class wraps a copy of the structure and the orb handling (advertise and publish).
I would propose we review the uORB::Publication wrappers directly and update the interface to be clear and hard to misuse. https://github.com/PX4/Firmware/blob/master/src/modules/uORB/Publication.hpp
Thanks for the detailed review everyone. My goal here was mainly to gather the common portions into PX4Accelerometer and PX4Gyroscope as soon as possible so that we can 1) reduce the manually duplicated code across all drivers 2) be in a position where we can sanely make sensor pipeline changes. As for next steps with mpu6000 cleanup I think the biggest missing piece is some kind of handler for the start/stop/status boilerplate. The reason I didn't go straight for module base is that there's still a larger "board manifest" aspect missing here. I want boards to be able to cleanly and simply define which sensors they and where (bus, spi chip select or i2c address, rotation, etc). All of that needs to be pulled out of each individual sensor driver. |
I verified a full fresh calibration matches current master (parameters and published orb topics). |
Fixes regression from #11216.
WIP
This PR adds sensor driver classes that satisfy what PX4 requires from each type of driver. With these we can simplify the majority of drivers in the system that have manually duplicated the majority of this logic many times.
More importantly once all drivers start using these classes it will be relatively easy to start making more interesting changes.
Examples