-
Notifications
You must be signed in to change notification settings - Fork 134
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
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright (c) 2021, Thomas Sommer | ||
* | ||
* This file is part of the modm project. | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
// ---------------------------------------------------------------------------- | ||
|
||
#include <modm/board.hpp> | ||
#include <modm/driver/encoder/bitbang_encoder_input.hpp> | ||
#include <modm/math/algorithm/prescaler.hpp> | ||
#include <modm/processing/timer.hpp> | ||
|
||
using namespace modm::platform; | ||
|
||
// Connect the encoders outputs to D7 and D8 Pins (usually the outer pins) | ||
// The common third pin (usually in the middle) is connected to GND. | ||
// Don't add any resistors or filters. It's all in the MCU and the driver. | ||
modm::BitBangEncoderInput<Board::D11, Board::D12, 4> encoder; | ||
|
||
MODM_ISR(TIMER2_COMPA) | ||
{ | ||
encoder.update(); | ||
} | ||
|
||
void | ||
init_Timer2() | ||
{ | ||
constexpr float f = 1_kHz; | ||
constexpr auto prescaler = | ||
modm::Prescaler::from_list(SystemClock::Timer, f * 2, {0, 1, 8, 32, 64, 128, 256, 1024}); | ||
constexpr auto ocr = (prescaler.frequency / f) - 1; | ||
static_assert(ocr <= 255, "Can't configure Timer2 for desired f"); | ||
|
||
// Timer in CTC Mode | ||
TCCR2A = (1 << WGM21); | ||
OCR2A = ocr; | ||
TIMSK2 |= (1 << OCIE2A); | ||
TCCR2B |= prescaler.index; | ||
} | ||
|
||
int | ||
main() | ||
{ | ||
Board::initialize(); | ||
LedD13::setOutput(); | ||
|
||
encoder.connect(); | ||
|
||
init_Timer2(); | ||
enableInterrupts(); | ||
|
||
int value(0); | ||
|
||
modm::ShortPeriodicTimer heartbeat(500ms); | ||
modm::ShortPeriodicTimer outputValue(1000ms); | ||
|
||
while (true) | ||
{ | ||
if (heartbeat.execute()) Board::LedD13::toggle(); | ||
if (outputValue.execute()) | ||
{ | ||
value += encoder.getIncrement(); | ||
MODM_LOG_INFO << "value: " << value << modm::endl; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<library> | ||
<extends>modm:arduino-nano</extends> | ||
<options> | ||
<option name="modm:build:build.path">../../../build/arduino_nano/encoder_input</option> | ||
</options> | ||
<modules> | ||
<module>modm:build:scons</module> | ||
<module>modm:processing:timer</module> | ||
<module>modm:driver:encoder_input.bitbang</module> | ||
</modules> | ||
</library> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Copyright (c) 2021, Thomas Sommer | ||
* | ||
* This file is part of the modm project. | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
// ---------------------------------------------------------------------------- | ||
|
||
#ifndef MODM_BITBANG_ENCODER_INPUT_HPP | ||
#define MODM_BITBANG_ENCODER_INPUT_HPP | ||
|
||
#include <bit> | ||
#include <modm/platform.hpp> | ||
#include <type_traits> | ||
|
||
namespace modm | ||
{ | ||
/** | ||
* @ingroup modm_driver_bitbang_encoder_input | ||
* @author Thomas Sommer | ||
* | ||
* @brief This driver decodes a AB (incremental) encoder signal | ||
* | ||
* @tparam SignalA First modm::platform::Gpio pin to input the encoder signal. | ||
* @tparam SignalB Second modm::platform::Gpio pin to input the encoder signal. | ||
* @tparam POSTSCALER n_cycles to count as one in-/decrement. | ||
* @tparam DeltaType Must be signed integer and fit at least POSTSCALER. The Bigger | ||
* DeltaType, the more inc-/decrements can be stored temporarily. | ||
*/ | ||
template<typename SignalA, typename SignalB, uint8_t POSTSCALER = 4, | ||
std::signed_integral DeltaType = int8_t> | ||
class BitBangEncoderInput | ||
{ | ||
static_assert(std::popcount(POSTSCALER) == 1, | ||
"POSTSCALER must be an integer to basis 2 and not 0: 1, 2, 4, 8, 16, ..."); | ||
static_assert(POSTSCALER <= std::numeric_limits<DeltaType>::max(), | ||
"DeltaType is to small for POSTSCALER."); | ||
|
||
using Signals = modm::platform::SoftwareGpioPort<SignalA, SignalB>; | ||
|
||
static_assert(Signals::number_of_ports == 1, | ||
"Signal A/B must be on the same GPIO port to prevent signal tearing!"); | ||
|
||
uint8_t inline getRaw(); | ||
|
||
public: | ||
using ValueType = DeltaType; | ||
BitBangEncoderInput() : raw_last(0), delta(0){}; | ||
|
||
// Connect SingalA and SignalB and store power-up state | ||
inline void | ||
connect(); | ||
|
||
// Call @1kHz for manual movement | ||
inline void | ||
update(); | ||
|
||
ValueType | ||
getIncrement(); | ||
|
||
private: | ||
uint8_t raw_last; | ||
DeltaType delta; | ||
}; | ||
} // namespace modm | ||
|
||
#include "bitbang_encoder_input_impl.hpp" | ||
|
||
#endif // MODM_BITBANG_ENCODER_INPUT_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#!/usr/bin/env python3 | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (c) 2019, Thomas Sommer | ||
# | ||
# This file is part of the modm project. | ||
# | ||
# This Source Code Form is subject to the terms of the Mozilla Public | ||
# License, v. 2.0. If a copy of the MPL was not distributed with this | ||
# file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
# ----------------------------------------------------------------------------- | ||
|
||
def init(module): | ||
module.name = ":driver:encoder_input.bitbang" | ||
module.description = """ | ||
# Quadrature Encoder Input | ||
|
||
This driver decodes a AB (incremental) encoder signal. | ||
Ported from code created by Peter Dannegger: | ||
https://www.mikrocontroller.net/articles/Drehgeber. | ||
""" | ||
|
||
|
||
def prepare(module, options): | ||
module.depends(":architecture:atomic") | ||
return True | ||
|
||
|
||
def build(env): | ||
env.outbasepath = "modm/src/modm/driver/encoder" | ||
env.copy("bitbang_encoder_input.hpp") | ||
env.copy("bitbang_encoder_input_impl.hpp") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
#ifndef MODM_BITBANG_ENCODER_INPUT_IMPL_HPP | ||
#error "Don't include this file directly, use 'bitbang_encoder_input.hpp' instead!" | ||
#endif | ||
|
||
#include <cmath> | ||
|
||
template<typename SignalA, typename SignalB, uint8_t POSTSCALER, | ||
std::signed_integral DeltaType> | ||
inline uint8_t | ||
modm::BitBangEncoderInput<SignalA, SignalB, POSTSCALER, DeltaType>::getRaw() | ||
{ | ||
const uint8_t read = Signals::read(); | ||
// convert graycode to binary | ||
uint8_t raw = 0; | ||
if (read & 0b10) raw = 3; | ||
if (read & 0b01) raw ^= 1; | ||
return raw; | ||
} | ||
|
||
template<typename SignalA, typename SignalB, uint8_t POSTSCALER, | ||
std::signed_integral DeltaType> | ||
inline void | ||
modm::BitBangEncoderInput<SignalA, SignalB, POSTSCALER, DeltaType>::connect() | ||
{ | ||
Signals::setInput(::Gpio::InputType::PullUp); | ||
|
||
// Tare power-on state | ||
modm::delay(10us); | ||
raw_last = getRaw(); | ||
} | ||
|
||
template<typename SignalA, typename SignalB, uint8_t POSTSCALER, | ||
std::signed_integral DeltaType> | ||
inline void | ||
modm::BitBangEncoderInput<SignalA, SignalB, POSTSCALER, DeltaType>::update() | ||
{ | ||
uint8_t raw = getRaw(); | ||
const uint8_t diff = raw_last - raw; | ||
if (diff & 0b01) | ||
{ | ||
raw_last = raw; | ||
delta += (diff & 0b10) - 1; // bit 1 = direction (+/-) | ||
} | ||
} | ||
|
||
template<typename SignalA, typename SignalB, uint8_t POSTSCALER, | ||
std::signed_integral DeltaType> | ||
DeltaType | ||
modm::BitBangEncoderInput<SignalA, SignalB, POSTSCALER, DeltaType>::getIncrement() | ||
{ | ||
::modm::atomic::Lock _; | ||
DeltaType val = delta; | ||
|
||
delta &= (POSTSCALER - 1); // mask out higher bits | ||
|
||
constexpr uint8_t shift = std::log2(POSTSCALER); // Number of fraction bits | ||
return val >> shift; // return whats left without fractions | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Otherwise the atomic read properties may not be true.
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.
Very nice mechanism.. Damn i ♥ love ♥ the way you've implemented the low level stuff
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 actually think it's just
Phases::number_of_ports
without a function call… I don't even remember the code I wrote.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.
Thanks, half of the credit goes to C++17 with the relaxed constexpr rules:
#19 (comment)
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.
It is and has already been concidered in the last commits ;)
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.
Thanks for the interresting background!
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.
What's the magic spell to get these shift maps?
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.
Here's the full example: https://github.com/modm-io/modm/blob/develop/examples/stm32f469_discovery/ports/main.cpp