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

[driver] Add best practice driver for soft graycode / quadrature decoding #580

Closed
wants to merge 3 commits into from

Conversation

TomSaw
Copy link
Contributor

@TomSaw TomSaw commented Mar 17, 2021

Hi βœ‹, missed the gentle communication - here's my next πŸ’’

Many modern MCUs have builtin quadrature-decoding support. However encoders alias rotary knobs kept being so usefull in small projects with small chips. Raphael commited the complement in 2019: modm/driver/encoder/encoder_output.hpp. So, let's don't discriminate the roots:

It's a port of well known, best practice solution: mikrocontroller.net/articles/Drehgeber.

Things to discus

  1. I've redundantly added static_assert checking < UnderlyingType> in two methods: update() and getValue(). I found, this makes the compiler printing the assert-failure-string always on top. There may be a better technique.

  2. getValue() could have been specialized for each value in < DIVISION >. Because of the difference of just 3 tiny lines, i found the it better using a switch-statement.

  3. Alternative implemention of getValue() as functor: operator()(). To integrate the latest changes to a local variable v, one would type: v += encoder(); instead of v += encoder.getValue();

  4. Following an example i've seen in another modm-module, i've put the credits to Peter Dannegger's ANSI-C foundation into encoder.lb ... but this may not be sufficient.

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 17, 2021

Example follows as soon as a future in the codebase is approved.

Copy link
Member

@salkinium salkinium left a comment

Choose a reason for hiding this comment

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

Alternative implemention of getValue() as functor: operator()(). To integrate the latest changes to a local variable v, one would type: v += encoder(); instead of v += encoder.getValue();

Meh, we don't use operator() for anything else, so it would not be consistent with the rest of modm. I would actually prefer the getter, since it's explicit, otherwise it looks like a function call.

src/modm/driver/encoder/encoder_impl.hpp Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder_impl.hpp Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder_impl.hpp Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder_impl.hpp Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder.hpp Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder.lb Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder.lb Outdated Show resolved Hide resolved
@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 17, 2021

Easy fixes done... let's generalize the Dividor

@rleh
Copy link
Member

rleh commented Mar 17, 2021

What is the use-case for this driver?
Typically I would wire an the A and B signals of an encoder to Ch1 and Ch2 of any STM32 "Advanced" or "General purpose" timer and let the timer hardware handle it.
Like here: https://github.com/roboterclubaachen/micro-motor/blob/master/src/hardware_rev2.hpp#L368-L390

With this driver you have to call update() at a very high frequency (>100kHz with some high-resolution encoders), otherwise some encoder ticks will be lost unnoticed.

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 17, 2021

What is the use-case for this driver?
Typically I would wire an the A and B signals of an encoder to Ch1 and Ch2 of any STM32 "Advanced" or "General purpose" timer and let the timer hardware handle it.
Like here: https://github.com/roboterclubaachen/micro-motor/blob/master/src/hardware_rev2.hpp#L368-L390

With this driver you have to call update() at a very high frequency (>100kHz with some high-resolution encoders), otherwise some encoder ticks will be lost unnoticed.

Hi Raphael. On small MCUs there's no such support. I've noted this in the opening message: "Many modern MCUs have builtin quadrature-decoding support. However encoders alias rotary knobs kept being so usefull in small projects with small chips."

Another case is, when you want an encoder on the fly, just for a quick debugging and no sufficient timer-pins are free.
Also there could be many encoders.. build some music-equipment f.e. where the hardware-decoders may not be sufficient.

@rleh
Copy link
Member

rleh commented Mar 17, 2021

I don't know how this works on other microcontrollers (AVR, SAM, ....).
Maybe some of them don't have hardware features like STM32, so this "bitbang" driver is useful there.

In my opinion this driver belongs to the "bitbang" category (like e.g. the spi.bitbang driver) and should be named accordingly.

@rleh
Copy link
Member

rleh commented Mar 17, 2021

On small MCUs there's no such support.

Which MCU are you using?

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 17, 2021

On small MCUs there's no such support.

Which MCU are you using?

Various ATmega... still. I like them somehow

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 17, 2021

I don't know how this works on other microcontrollers (AVR, SAM, ....).
Maybe some of them don't have hardware features like STM32, so this "bitbang" driver is useful there.

In my opinion this driver belongs to the "bitbang" category (like e.g. the spi.bitbang driver) and should be named accordingly.

That's a thing i would give in your hands!

Edit: Keeping it in :driver, encoder and encoder_output hang up together so the familiarity is exposed. encoders docs will tell you, that it's a Soft version and anyone who cares for the performance-drawbacks knows, there's hardware support.

@TomSaw TomSaw changed the title [driver] Add best practice driver for soft graycode / quadrature decoding [driver] Add best practice software driver for soft graycode / quadrature decoding Mar 17, 2021
@TomSaw TomSaw changed the title [driver] Add best practice software driver for soft graycode / quadrature decoding [driver] Add best practice driver for soft graycode / quadrature decoding Mar 17, 2021
@rleh
Copy link
Member

rleh commented Mar 17, 2021

I propose to call this driver modm:driver:encoder_input.bitbang.
Someday someone will take the effort to redesign the (horrible) STM32 timer HAL (see #275, #337) and will add a STM32 specific modm:driver:encoder_input driver...

Unsure if this driver belong to src/modm/driver/ or src/modm/platform/. What do you think @salkinium?

And maybe we should rename modm:driver:encoder.output to modm:driver:encoder_output.bitbang for consistency (but this would break backward compatibility)

src/modm/driver/encoder/encoder.hpp Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder.hpp Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder.hpp Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder_impl.hpp Outdated Show resolved Hide resolved
src/modm/driver/encoder/encoder_impl.hpp Outdated Show resolved Hide resolved
@salkinium
Copy link
Member

Unsure if this driver belong to src/modm/driver/ or src/modm/platform/.

In modm/driver. I would even argue that the bitbang I2C and SPI classes belong in modm/driver, since they only depend on the GPIO interface, not even the implementation.

modm:driver:encoder_input.bitbang and odm:driver:encoder_output.bitbang

Yes, let's rename them.

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 17, 2021

Unsure if this driver belong to src/modm/driver/ or src/modm/platform/.

In modm/driver. I would even argue that the bitbang I2C and SPI classes belong in modm/driver, since they only depend on the GPIO interface, not even the implementation.

modm:driver:encoder_input.bitbang and odm:driver:encoder_output.bitbang

Yes, let's rename them.

modules are renamed.

How will the classes be named?
BitBangEncoderInput
BitBangEncoderOutput

Same for the preamble structs ... this should become encoder_[input/output] too, right?

@salkinium
Copy link
Member

BitBangEncoder[In,Out]put, encoder_[input/output]

Yeah… kind of an eyesore, but it's consistent.

@TomSaw TomSaw force-pushed the encoder branch 2 times, most recently from 38ca7a3 to 65fc036 Compare March 17, 2021 13:53
@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 17, 2021

BitBangEncoder[In,Out]put, encoder_[input/output]

Yeah… kind of an eyesore, but it's consistent.

It entlightens one fair enough about the hidden gems from software-encoding.

@TomSaw TomSaw force-pushed the encoder branch 3 times, most recently from 83eb468 to ce2b908 Compare March 17, 2021 18:26
@TomSaw TomSaw force-pushed the encoder branch 2 times, most recently from aa02245 to 0b91ea0 Compare March 20, 2021 06:45
@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 20, 2021

The module-names are prefixed with the bitbang-classifier: encoder_[in/out]pu.bitbang
I found that filenames on the other hand are postfixed with the classifier: f.e. bitbang_i2c_master.hpp

  • For the encoder-files, i followed the postfix example

That's it from my side 😏 All implemented βœ… and tested βœ…

Copy link
Member

@salkinium salkinium left a comment

Choose a reason for hiding this comment

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

Very nice! Let's merge once the CI passes.

"DeltaType is to small for POSTSCALER.");

using Phases = modm::platform::SoftwareGpioPort<SignalA, SignalB>;

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
static_assert(Phases::number_of_ports() == 1, "Signal A/B must be on the same GPIO port to prevent signal tearing!");

Otherwise the atomic read properties may not be true.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Very nice mechanism.. Damn i β™₯ love β™₯ the way you've implemented the low level stuff

Copy link
Member

Choose a reason for hiding this comment

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

I actually think it's just Phases::number_of_ports without a function call… I don't even remember the code I wrote.

Copy link
Member

Choose a reason for hiding this comment

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

Damn i β™₯ love β™₯ the way you've implemented the low level stuff

Thanks, half of the credit goes to C++17 with the relaxed constexpr rules:
#19 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I actually think it's just Phases::number_of_ports without a function call… I don't even remember the code I wrote.

It is and has already been concidered in the last commits ;)

Copy link
Contributor Author

@TomSaw TomSaw Mar 22, 2021

Choose a reason for hiding this comment

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

Thanks, half of the credit goes to C++17 with the relaxed constexpr rules:

Thanks for the interresting background!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What's the magic spell to get these shift maps?

Copy link
Member

Choose a reason for hiding this comment

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

src/modm/driver/encoder/bitbang_encoder_output_impl.hpp Outdated Show resolved Hide resolved
@TomSaw TomSaw force-pushed the encoder branch 2 times, most recently from 8a379a5 to e6ffb6a Compare March 21, 2021 19:34
@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 22, 2021

If and how can i prevent in unittests-linux-generic ?

Please synchronize the modm documentation:
$ python3 tools/scripts/synchronize_docs.py
and then commit the results!
Exited with code exit status 1

@rleh
Copy link
Member

rleh commented Mar 22, 2021

python3 tools/scripts/synchronize_docs.py

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 22, 2021

python3 tools/scripts/synchronize_docs.py

Shame on me...

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 22, 2021

CI stucks with...

ERROR: In 'modm:platform:uart:2': Traceback (most recent call last): OSError: [Errno 28] No space left on device: '/root/project/build/test_all_5ilvyiug/modm/src/modm/platform/uart/uart_hal_2_impl.hpp'.

... and i wonder how - if any - i could have messed this up πŸ™„

@salkinium
Copy link
Member

Ugh, CircleCI has made some recent changes in their free plan and now it's making us so much trouble. I'll try to rerun it, or we'll simply merge it without the F7 job. You're contributions didn't touch the HAL anyways.

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 22, 2021

You're #588 hunting for an alternative... No worries on my side :)

@rleh
Copy link
Member

rleh commented Mar 22, 2021

Why did you close this pull request? @TomSaw

@salkinium
Copy link
Member

Thanks @TomSaw for sticking around for our review, it's a very beatiful driver now!

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 22, 2021

The pleasure is on my side. I've learned a lot and feels good to dive deeper into the catacombs of contribution.

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 22, 2021

Excuse the little disasters due to my github-newbieness @rleh , do you mean, it was a bad thing to delete the encoder branch after it has been merged into develop?

@salkinium
Copy link
Member

The pleasure is on my side. I've learned a lot and feels good to go deeper into the catacombs of contribution.

That's good to hear, because I was worried we scared you off by descending on you like a plague of judgemental locusts…

after it has been merged into develop?

Emphasis on AFTER 😜 I think you mistook my force push to your encoder branch with a merge into develop, but I wanted to check the CI after the rebase, which was good, because I messed it up and had to fix it.

@TomSaw
Copy link
Contributor Author

TomSaw commented Mar 23, 2021

I was worried we scared you off by descending on you like a plague of judgemental locusts

I think any generic concept benefits from perfection. Forget the Pareto-principle here: The expensive fine-works pay out squared^squared^squared when you later build applications! out of the generic puzzle-pieces.

I also find lot's of satisfaction, making one thing perfect rather than going for quantity. You have my fullest respect when judging my lines cause it's for the good! - Amen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

4 participants