new
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
Copyright 2021 Peter Harrison
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
BasicEncoder provides a class for reading a rotary encoder knob.
|
||||
It is not suited to motor encoders.
|
||||
The switch, if present, needs a deparate button library .
|
||||
|
||||
Decoding logic based on https://www.mikrocontroller.net/articles/Drehgeber
|
||||
*/
|
||||
|
||||
#ifndef BASIC_ENCODER_H_
|
||||
#define BASIC_ENCODER_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
uint8_t SREG;
|
||||
|
||||
class BasicEncoder
|
||||
{
|
||||
public:
|
||||
BasicEncoder(int8_t pinA, int8_t pinB, uint8_t active_state = LOW, uint8_t steps = 4)
|
||||
: m_pin_a(pinA), m_pin_b(pinB), m_pin_active(active_state), m_steps_per_count(steps) {
|
||||
pinMode(pinA, INPUT_PULLUP);
|
||||
pinMode(pinB, INPUT_PULLUP);
|
||||
m_previous_state - pin_state();
|
||||
m_change = 0;
|
||||
}
|
||||
~BasicEncoder() {}
|
||||
|
||||
void begin() { reset(); }
|
||||
|
||||
int8_t pin_state() {
|
||||
int8_t state_now = 0;
|
||||
if (digitalRead(m_pin_a) == m_pin_active) {
|
||||
state_now |= 2;
|
||||
}
|
||||
if (digitalRead(m_pin_b) == m_pin_active) {
|
||||
state_now |= 1;
|
||||
}
|
||||
return state_now;
|
||||
}
|
||||
|
||||
// to update the encoder changes
|
||||
// call this method in a timmer interrupt for best performance
|
||||
// it could also be called in the main loop
|
||||
// this takes about 10-15us using digitalRead on Arduino Nano
|
||||
void service() {
|
||||
int8_t state_now = pin_state();
|
||||
state_now ^= state_now >> 1; // two bit gray-to-binary
|
||||
int8_t difference = m_previous_state - state_now;
|
||||
// bit 1 has the direction, bit 0 is set if changeed
|
||||
if (difference & 1) {
|
||||
m_previous_state = state_now;
|
||||
int delta = (difference & 2) - 1;
|
||||
if (m_reversed) {
|
||||
delta = -delta;
|
||||
}
|
||||
m_change += delta;
|
||||
m_steps += delta;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
|
||||
// try this if there are unwanted transitions at the detents
|
||||
// it is half resolution so counts are doubled
|
||||
const int8_t decode_table[16] PROGMEM = {0, 0, -2, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, -2, 0, 0};
|
||||
void flaky_encoder_service(void) {
|
||||
// just get the bits - no gray-to-binary conversion needed
|
||||
int8_t state_now = pin_state();
|
||||
static int8_t encoder_state = state_now;
|
||||
encoder_state = ((encoder_state << 2) | state_now) & 0x0f;
|
||||
encoder_change += (int8_t)pgm_read_byte(&decode_table[encoder_state]);
|
||||
}
|
||||
|
||||
****************************************************************/
|
||||
|
||||
// Read changes frequently enough that overflows cannot happen.
|
||||
int8_t get_change() {
|
||||
uint8_t sreg = SREG; // save the current interrupt enable flag
|
||||
noInterrupts();
|
||||
int8_t change = m_change;
|
||||
// the switch statement can make better code because only optimised
|
||||
// operations are used instead of generic division
|
||||
switch (m_steps_per_count) {
|
||||
case 4:
|
||||
m_change %= 4;
|
||||
change /= 4;
|
||||
break;
|
||||
case 2:
|
||||
m_change %= 2;
|
||||
change /= 2;
|
||||
break;
|
||||
default:
|
||||
m_change = 0;
|
||||
break;
|
||||
}
|
||||
SREG = sreg; // restore the previous interrupt enable flag state
|
||||
return change;
|
||||
}
|
||||
|
||||
int get_count() {
|
||||
uint8_t sreg = SREG; // save the current interrupt enable flag
|
||||
noInterrupts();
|
||||
int count = m_steps / m_steps_per_count;
|
||||
SREG = sreg; // restore the previous interrupt enable flag state
|
||||
return count;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
uint8_t sreg = SREG; // save the current interrupt enable flag
|
||||
noInterrupts();
|
||||
m_steps = 0;
|
||||
m_change = 0;
|
||||
SREG = sreg; // restore the previous interrupt enable flag state
|
||||
}
|
||||
|
||||
void set_reverse() { m_reversed = true; }
|
||||
|
||||
void set_forward() { m_reversed = false; }
|
||||
|
||||
private:
|
||||
int8_t m_pin_a = 0;
|
||||
int8_t m_pin_b = 0;
|
||||
uint8_t m_pin_active = LOW;
|
||||
uint8_t m_steps_per_count = 4;
|
||||
bool m_reversed = false;
|
||||
volatile int m_change = 0;
|
||||
int8_t m_previous_state = 0;
|
||||
int m_steps = 0;
|
||||
};
|
||||
#endif // BASIC_ENCODER_H_
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,137 @@
|
||||
# BasicEncoder
|
||||
|
||||
A simple library class for reading encoders as used in control knobs.
|
||||
|
||||
The complete code is in a single header file ```BasicEncoder.h``` so that you can just add the file to your project directly ratrher than instal a complete library.
|
||||
|
||||
This class does not handle the pushbutton usually found on these rotary encoders. For that it is better to use a separate pushbutton library.
|
||||
|
||||
Most of the logic in this code was derived from this page:
|
||||
|
||||
https://www.mikrocontroller.net/articles/Drehgeber
|
||||
|
||||
|
||||
## Initialisation
|
||||
|
||||
The simplest constructor needs only the pin names for the encoder A and B lines.
|
||||
|
||||
#include <BasicEncoder.h>
|
||||
BasicEncoder encoder(2,3);
|
||||
|
||||
|
||||
This will create an encoder object using pins 2 and 3 for the two required input lines.
|
||||
|
||||
The pins will be initialised as inputs with pullups.
|
||||
|
||||
The encoder object will assume that the resting state of the inputs is high and that there are 4 signal steps per detent. That is typical for the most common type of inexpensive encoder knob.
|
||||
|
||||
You can optionally specify that the pins rest in the low state and that the number of steps per detent is different with constructors like
|
||||
|
||||
|
||||
#include <basicEncoder.h>
|
||||
BasicEncoder encoder1(2,3,HIGH,2) / pins rest low, 2 steps per detent
|
||||
|
||||
|
||||
If the count is reversed then just swap the two pins. Alternatively, the direction of counting can be reversed or restored to normal in software with
|
||||
|
||||
encoder.set_reverse();
|
||||
encoder.set_forward();
|
||||
|
||||
If the encoder will be polled (see below) then you are free to choose any two digital inputs pins.
|
||||
|
||||
If a pin change interrupt (see interrupts below)will be used then both channels should be in the same group so that they are serviced by the same interrupt. Pick pairs of pins from the following lists.
|
||||
|
||||
* PCINT0 - D8, D9, D10, D11, D12, D13,
|
||||
* PCINT1 - A0, A1, A2, A3, A4, A5
|
||||
* PCINT2 - D0, D1, D2, D3, D4, D5, D6, D7
|
||||
|
||||
Although it is possible to put more than one encoder on the same group the code may get a little messy so it is best to use different groups if more than pne encoder is to be connected.
|
||||
|
||||
With polling, it will not matter except that the interrupt service routine may start to get a little time-consuming.
|
||||
|
||||
## Update the counts
|
||||
|
||||
To make the encoder class react to any movement of the actual encoder device, it must regularly, and frequently examine the state of the input lines.
|
||||
|
||||
To have the encoder check the lines and respond to changes, you must call the ```service()``` method. e.g.
|
||||
|
||||
encoder.service();
|
||||
|
||||
## Polling
|
||||
|
||||
Polling is the name given to methods where the code is made to go and look for any changes. Many Arduino programs poll for changes by calling a function at the beginning of ```loop()```. So long as the rest of the code in ```loop()``` is short and executes quickly, this is likely to be adequate. The encoder should be polled as frequently as possible and certainly often enough to reliably detect changes.
|
||||
|
||||
## Polling with a timer interrupt
|
||||
|
||||
To be sure that the encoder is polled frequently enough, it is probably best to call the ```service()``` method from a timer interrupt running at seveeral hudreds of kHertz or more. If you use the ```TimerOne``` library, then the polling might be set up like this:
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BasicEncoder.h>
|
||||
#include <TimerOne.h>
|
||||
|
||||
BasicEncoder encoder(2, 3);
|
||||
|
||||
void timer_service() {
|
||||
encoder.service();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Timer1.initialize(1000);
|
||||
Timer1.attachInterrupt(timer_service);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int encoder_change = encoder.get_change();
|
||||
if (encoder_change) {
|
||||
Serial.println(encoder.get_change());
|
||||
}
|
||||
}
|
||||
|
||||
In this example the TimerOne library is used to generate an interrupt every 1000 microseconds (1kHz). Changes are accumulated in the service routine and processed as needed by the main program loop. This works well even on switches with a lot of bounce. The entire function is compact and could be faster with hard coded pin reads or by using digitalReadFast if you wanted to modify the library source code.
|
||||
|
||||
## Reading changes
|
||||
|
||||
The encoder object tracks changes in the actual encoder and keeps a tally of the number of steps and the direction of rotation. There are two ways to get at this information:
|
||||
|
||||
#### ```get_change()```
|
||||
|
||||
By calling the ```get_change()``` method your program can receive the number of counts since the last call to the method as a signed integer. This is a destructive call in that the change count is reset to zero during the call. Thus you should assign the change to a local variable so that you can use the value later in the code. The example above shows this method. The default contructor assums that there are four steps per detent, or click, of the encoder knob so the number returned by ```get_change()``` is the number of clicks, not the number of signal changes. In that example the printed value will almost certainly be just +1 or -1 because the loop executes very quickly. Try adding a delay in ```loop()``` to see larger changes being reported.
|
||||
|
||||
#### ```get_count()```
|
||||
|
||||
The ```get_count()``` method will also return the number of clicks (not signal changes) recorded by the encoder object. This time however, the number returned will be the accumulated count since the last time the ```reset()``` method was called. The value is not cleared when read.
|
||||
|
||||
### Motor encoders
|
||||
|
||||
Polled encoders are not likely to work well for motor applications. If you specifically want motor applications there are many ways to optimise the code for better performance at high frequencies. Such optimisations may rely on the encoder channels being clean. That is, the pulses switch reliably without any contact bounce. The technique used in this code is reliable even with low quality encoders that have considerable contact bounce.
|
||||
|
||||
## Bouncing encoders
|
||||
|
||||
On the subject of contact bounce, the code assumes that the detents of a typical control knob coincide with stable states of the control signals. It is possible that some controls have detents that coincide with transitions and there may be some jitter in the output even when the knob is at rest. Encoder controls without detents may come to rest at such a position by chance.
|
||||
|
||||
If this is a problem in your application the article linked in the comments provides an alternative solution that uses a lookup table to decode the state transitions.
|
||||
|
||||
https://www.mikrocontroller.net/articles/Drehgeber
|
||||
|
||||
|
||||
## Pin Change Interrupts
|
||||
|
||||
If you like, this service routine could be called from the pin change interrupt.
|
||||
|
||||
See the examples for code setup to use pin change interrupt operation.
|
||||
|
||||
See also:
|
||||
|
||||
https://playground.arduino.cc/Main/PinChangeInterrupt/
|
||||
https://thewanderingengineer.com/2014/08/11/arduino-pin-change-interrupts/
|
||||
|
||||
## Multiple Encoders
|
||||
|
||||
The library should work with multiple encoder instances. There is an example `two-encoders` that demonstrates this using a timer interrupt for polling. Pin-change interrupt implementations may also be fine if the encoders are in different groups though that has not yet been tested.
|
||||
|
||||
## A note about interrupts
|
||||
|
||||
In a few places, the code needs to disable interrupts to ensure that data values are not corrupted. Immediately before a call to `noInterrupts()`, the current value of the status register, `SREG`, is saved into a local variable. Once the critical code section is complete, the saved state of the interrupt enable flag is restored by simply copying the saved value of the status register back. The other flags are not critical in this context.
|
||||
|
||||
This method is used rather than simply enabling interrupts because interrupts may already have been disabled at the time the function is called and to re-enable interrupts when they should not be enabled might cause hard to track bugs in the user code.
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2021 Peter Harrison - Helicron
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
This example creates a single encoder instance that is updated by
|
||||
running the service method directly in the main program loop. This
|
||||
must be called frequently enough to avoid missing any pin changes. That
|
||||
means several hundred times a second in many cases. If a change is detected
|
||||
in the main loop, the current count is sent to the serial port.
|
||||
|
||||
You can add a delay into the loop in this eample to see just how often you
|
||||
need to call the service method and still get reliable counts.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BasicEncoder.h>
|
||||
|
||||
const int8_t pinA = 2;
|
||||
const int8_t pinB = 3;
|
||||
|
||||
BasicEncoder encoder(pinA, pinB);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println(F("Polling in loop()"));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
encoder.service();
|
||||
int encoder_change = encoder.get_change();
|
||||
if (encoder_change) {
|
||||
Serial.println(encoder.get_count());
|
||||
}
|
||||
// Even a short delay here will affect performance.
|
||||
// Uncomment and change the delay to see what happens.
|
||||
//delay(10);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2021 Peter Harrison - Helicron
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
This example creates two independent encoders and samples them using a
|
||||
timer interrupt. If either changes, the current count for both is sent to
|
||||
the serial port.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BasicEncoder.h>
|
||||
#include <TimerOne.h>
|
||||
|
||||
BasicEncoder encoderA(2, 3);
|
||||
BasicEncoder encoderB(10, 11);
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
void timer_service() {
|
||||
encoderA.service();
|
||||
encoderB.service();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Timer1.initialize(1000);
|
||||
Timer1.attachInterrupt(timer_service);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int encoder_a_change = encoderA.get_change();
|
||||
int encoder_b_change = encoderB.get_change();
|
||||
if (encoder_a_change || encoder_b_change) {
|
||||
Serial.print(encoderA.get_count());
|
||||
Serial.print(' ');
|
||||
Serial.print(encoderB.get_count());
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright 2021 Peter Harrison - Helicron
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
This example creates a single encoder instance that is updated by
|
||||
pin-change interrupts associated with the two pins. If a change is detected
|
||||
in the main loop, the current count is sent to the serial port.
|
||||
|
||||
Note that if you enable pin change interrupts for other pins in the same
|
||||
group, you may cause conflicts.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BasicEncoder.h>
|
||||
|
||||
const int8_t pinA = 2;
|
||||
const int8_t pinB = 3;
|
||||
|
||||
BasicEncoder encoder(pinA, pinB);
|
||||
|
||||
void pciSetup(byte pin) // Setup pin change interupt on pin
|
||||
{
|
||||
*digitalPinToPCMSK(pin) |= bit(digitalPinToPCMSKbit(pin)); // enable pin
|
||||
PCIFR |= bit(digitalPinToPCICRbit(pin)); // clear outstanding interrupt
|
||||
PCICR |= bit(digitalPinToPCICRbit(pin)); // enable interrupt for group
|
||||
}
|
||||
|
||||
void setup_encoders(int a, int b) {
|
||||
uint8_t old_sreg = SREG; // save the current interrupt enable flag
|
||||
noInterrupts();
|
||||
pciSetup(a);
|
||||
pciSetup(b);
|
||||
encoder.reset();
|
||||
SREG = old_sreg; // restore the previous interrupt enable flag state
|
||||
}
|
||||
|
||||
ISR(PCINT2_vect) // pin change interrupt for D0 to D7
|
||||
{
|
||||
encoder.service();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Interrupts");
|
||||
setup_encoders(pinA,pinB);
|
||||
encoder.set_reverse();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int encoder_change = encoder.get_change();
|
||||
if (encoder_change) {
|
||||
Serial.println(encoder.get_count());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2021 Peter Harrison - Helicron
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
This example creates a single encoder instance that is updated by
|
||||
a timer interrupt running at 1kHz. If a change is detected
|
||||
in the main loop, the current count is sent to the serial port.
|
||||
|
||||
Be sure that the timer interrupt is frequent enough that it cannot miss
|
||||
a change in pin state.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BasicEncoder.h>
|
||||
#include <TimerOne.h>
|
||||
|
||||
BasicEncoder encoder(2, 3);
|
||||
|
||||
/****************************************************************/
|
||||
void timer_service() {
|
||||
encoder.service();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Timer1.initialize(1000);
|
||||
Timer1.attachInterrupt(timer_service);
|
||||
encoder.set_reverse();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int encoder_change = encoder.get_change();
|
||||
if (encoder_change) {
|
||||
Serial.println(encoder.get_count());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
BASICENCODER KEYWORD1
|
||||
Encoder KEYWORD2
|
||||
Rotary KEYWORD2
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "BasicEncoder",
|
||||
"version": "1.1.3",
|
||||
"description": "BasicEncoder counts pulses from one or more simple rotary encoder control knobs.",
|
||||
"keywords": "encoder, rotary encoder, quadrature",
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/micromouseonline/BasicEncoder.git"
|
||||
},
|
||||
"authors":
|
||||
[
|
||||
{
|
||||
"name": "Peter Harrison",
|
||||
"url": "http://micromouseonline.com",
|
||||
"maintainer": true
|
||||
}
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"frameworks": ["arduino"],
|
||||
"platforms": ["atmelavr"]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
name=BasicEncoder
|
||||
version=1.1.3
|
||||
author=Peter Harrison
|
||||
maintainer=Peter Harrison
|
||||
sentence=BasicEncoder counts pulses from one or more simple rotary encoder control knobs.
|
||||
paragraph=Could also be used for low freuency odometry encoders but not suited to motor encoders.
|
||||
category=Signal Input/Output
|
||||
url=https://github.com/micromouseonline/BasicEncoder
|
||||
architectures=*
|
||||
Reference in New Issue
Block a user