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

introduce PX4 sensor classes and use to simplify mpu6000 #11216

Merged
merged 3 commits into from
Jan 18, 2019

Conversation

dagar
Copy link
Member

@dagar dagar commented Jan 14, 2019

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

  • split gyro publication to enable running the rate control much faster
  • add a high rate raw logging mechanism
  • kill off the remaining IOCTLs

@dagar dagar force-pushed the pr-px4driver_classes branch from ad86815 to 35faa48 Compare January 14, 2019 21:29
@dagar dagar force-pushed the pr-px4driver_classes branch from 35faa48 to a3fc81c Compare January 15, 2019 00:22
@dagar dagar requested review from davids5 and dakejahl January 15, 2019 01:09
@dagar
Copy link
Member Author

dagar commented Jan 15, 2019

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

Copy link
Member

@davids5 davids5 left a 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();
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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.

src/drivers/imu/mpu6000/mpu6000.h Outdated Show resolved Hide resolved
int device_type = MPU_DEVICE_TYPE_MPU6000;
enum Rotation rotation = ROTATION_NONE;

while ((ch = px4_getopt(argc, argv, "T:XISsZzR:a:", &myoptind, &myoptarg)) != EOF) {
Copy link
Member

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?

@dakejahl
Copy link
Contributor

dakejahl commented Jan 17, 2019

Let's kill the *_main.cpp files for all drivers ASAP. Maybe with ModuleBaseMulti, or maybe even a new base class that isn't templated.

Otherwise this looks pretty good.


state = px4_enter_critical_section();
_reset_wait = hrt_absolute_time() + 30000;
px4_leave_critical_section(state);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this really necessary?

Copy link
Member

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);
Copy link
Contributor

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.

Copy link
Member

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:

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

  2. 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)

  3. Design the chip rest and sensors reset so they work and do not kill the RT of the system.

Copy link
Member Author

Choose a reason for hiding this comment

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

  1. Are we doing the correct power management on boards other than fmu-v2/v3?

Copy link
Member

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);
Copy link
Contributor

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

Copy link
Member

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);
Copy link
Contributor

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

Copy link
Member

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
Copy link
Contributor

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.

Copy link
Member

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.

Copy link
Member

@davids5 davids5 left a 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);
Copy link
Member

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:

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

  2. 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)

  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);
Copy link
Member

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);
Copy link
Member

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);
Copy link
Member

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
Copy link
Member

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;
Copy link
Member

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;
Copy link
Member

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?

Copy link
Member Author

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%).
Copy link
Member

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();
Copy link
Member

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();
Copy link
Member

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.

Copy link
Member Author

@dagar dagar Jan 18, 2019

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

@dagar
Copy link
Member Author

dagar commented Jan 18, 2019

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.

@dagar
Copy link
Member Author

dagar commented Jan 18, 2019

I verified a full fresh calibration matches current master (parameters and published orb topics).

@dagar dagar merged commit 52c848a into master Jan 18, 2019
@dagar dagar deleted the pr-px4driver_classes branch January 18, 2019 15:39
bkueng added a commit that referenced this pull request Feb 11, 2019
LorenzMeier pushed a commit that referenced this pull request Feb 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants