Hello, everyone,
I am using an optical encoder with 600 pulses per revolution.
For this I am programming it with PCNT (Espressif), which works well.
At this point I would like to reduce the pulse count to 300, 200, 100 pulses per revolution by adjusting some parameter of PCNT.
Unfortunately I don't know how to do this.
Could you help me?
Thank you, Roberto
In the Italian section of the forum you can write ONLY in Italian.
Your topic was moved to its current location as it is more suitable.
Could you also take a few moments to Learn How To Use The Forum.
It will help you get the best out of the forum in the future.
Thank you
This hints into direction ESP-microocntroller
but which exact ESP?
ESP8265, ESP8266, ESP32 and which subversion of ESP32?
You've made at least 28 other posts on this site. By now you should have an understanding of what's wanted in order to help us help you, right? Like code in code tags, right? So, please, start off doing this the right way. So, we don't have to play 2000 questions.
Anyways, the code you posted is not very helpful to coming to a resolution to your issue, perhaps you could read the docs and figure it out yourself?
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/index.html
You could set PCNT watch points.
Hi Stefan,
I have an ESP32-Wroom microcontroller.
Thank you, Roberto
that is not an exact type.
The different types of the ESP32-family are listed here
Dear idahowalker,
I apologise for my haste to get a useful answer and for my weak knowledge of English, which prevents me from fully understanding what is written in Espressif.
It is not easy working with translators....
However, I will apply your suggestion.
Thank you, Roberto
Hi Stefan,
my microcontroller is an ESP32-WROOM Dev Kit chip, an 38 pin dual in line package.
Sorry.... ![]()
Hello,
I have carefully read what the Espressif website reports regarding the Puse Counter (PCNT), as had been recommended to me.
I have set up my site with the following code:
//--- Counter setup for Rotary Encoder ---------------------
pcnt_config_t pcnt_config_A;// structure for A
pcnt_config_t pcnt_config_B;// structure for B
//
pcnt_config_A.pulse_gpio_num = PULSE_INPUT_PIN;
pcnt_config_A.ctrl_gpio_num = PULSE_CTRL_PIN;
pcnt_config_A.lctrl_mode = PCNT_MODE_REVERSE;
pcnt_config_A.hctrl_mode = PCNT_MODE_KEEP;
pcnt_config_A.channel = PCNT_CHANNEL_0;
pcnt_config_A.unit = PCNT_UNIT_0;
pcnt_config_A.pos_mode = PCNT_COUNT_INC;
pcnt_config_A.neg_mode = PCNT_COUNT_DEC;
pcnt_config_A.counter_h_lim = 10000;
pcnt_config_A.counter_l_lim = -10000;
//
pcnt_config_B.pulse_gpio_num = PULSE_CTRL_PIN;
pcnt_config_B.ctrl_gpio_num = PULSE_INPUT_PIN;
pcnt_config_B.lctrl_mode = PCNT_MODE_KEEP;
pcnt_config_B.hctrl_mode = PCNT_MODE_REVERSE;
pcnt_config_B.channel = PCNT_CHANNEL_1;
pcnt_config_B.unit = PCNT_UNIT_0;
pcnt_config_B.pos_mode = PCNT_COUNT_INC;
pcnt_config_B.neg_mode = PCNT_COUNT_DEC;
pcnt_config_B.counter_h_lim = 10000;
pcnt_config_B.counter_l_lim = -10000;
//
pcnt_unit_config(&pcnt_config_A);//Initialize A
pcnt_unit_config(&pcnt_config_B);//Initialize B
pcnt_counter_pause(PCNT_UNIT_0);
pcnt_counter_clear(PCNT_UNIT_0);
pcnt_counter_resume(PCNT_UNIT_0); //Start
After several tentative attempts to decelerate the action of the optical encoder, I was unable to change its reading speed.
From 600 pulses per revolution, I wanted to modify the speed reading to 50, 100, 200, etc. pulses per revolution.
I probably have not understood something... but what?
Thanks again,
Roberto
Basically you need to set up a counter incremented by each pulse which overflows after 2, 3 or pulses. That will divide your steps per rev by 2 or 3 or 4, or whatever number you want.
Thank you Jahine for your intervention.
Please tell me which of the above parameters I should intervene on and how I should do it.
I have tried a few empirical solutions, without success.
Greetings, Roberto
Basically none as far as I can see. You need to write some code to implement a counter - what would be called a "pre-scaler". That will output signals that then interfaces to the library.
What you write is correct for one counter, but in my case I have to operate both channels of an encoder simultaneously.
This I don't know how to do... ![]()
No you have one counter which has to count up and down driven by the signals from the encoder. An exercise for the reader.
So, I wrote some code for handling encoders: GitHub - gfvalvo/NewEncoder: Rotary Encoder Library
It doesn't use ESP32 PCNT, but it's still very fast. It also allows the user to create custom encoder behavior by creating classes that inherit from the main 'NewEncoder'.
The code below creates a 'ScaledEncoder' encoder class. The last parameter its constructor defines the "pre-scaler" value (4 in this example).
I tested it on an Adafruit ESP32 Feather Huzzah using this 600 PPR Encoder. The factor of 4 pre-scaling produced 150 PPR.
See the README and source code on GitHub as well as the examples to learn about the library's functions and operation.
#include "Arduino.h"
#include "NewEncoder.h"
// Use FULL_PULSE for encoders that produce one complete quadrature pulse per detnet, such as: https://www.adafruit.com/product/377
// Use HALF_PULSE for endoders that produce one complete quadrature pulse for every two detents, such as: https://www.mouser.com/ProductDetail/alps/ec11e15244g1/?qs=YMSFtX0bdJDiV4LBO61anw==&countrycode=US¤cycode=USD
class ScaledEncoder: public NewEncoder {
public:
ScaledEncoder(uint16_t scaleFactor = 1) : scale(scaleFactor), NewEncoder() {
}
ScaledEncoder(uint8_t aPin, uint8_t bPin, int16_t minValue, int16_t maxValue, int16_t initalValue, uint8_t type = FULL_PULSE, uint16_t scaleFactor = 1) :
scale(scaleFactor),
NewEncoder(aPin, bPin, minValue, maxValue, initalValue, type) {
}
virtual ~ScaledEncoder() {
}
private:
const uint16_t scale;
volatile int16_t preScaler = 0;
protected:
virtual void updateValue(uint8_t updatedState) override;
};
void ESP_ISR ScaledEncoder::updateValue(uint8_t updatedState) {
if ((updatedState & DELTA_MASK) == INCREMENT_DELTA) {
preScaler++;
//log_i("%d", preScaler);
if (preScaler >= scale) {
stateChanged = true;
preScaler = 0;
liveState.currentClick = UpClick;
liveState.currentValue++;
if (liveState.currentValue > _maxValue) {
liveState.currentValue = _maxValue;
}
}
} else if ((updatedState & DELTA_MASK) == DECREMENT_DELTA) {
preScaler--;
//log_i("%d", preScaler);
if (preScaler <= -static_cast<int16_t>(scale)) {
stateChanged = true;
preScaler = 0;
liveState.currentClick = DownClick;
liveState.currentValue--;
if (liveState.currentValue < _minValue) {
liveState.currentValue = _minValue;
}
}
}
}
ScaledEncoder encoder(27, 33, -2000, 2000, 0, FULL_PULSE, 4);
int16_t prevEncoderValue;
void setup() {
NewEncoder::EncoderState state;
Serial.begin(115200);
delay(2000);
Serial.println("Starting");
if (!encoder.begin()) {
Serial.println("Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.");
while (1) {
yield();
}
} else {
encoder.getState(state);
Serial.print("Encoder Successfully Started at value = ");
prevEncoderValue = state.currentValue;
Serial.println(prevEncoderValue);
}
}
void loop() {
int16_t currentValue;
NewEncoder::EncoderState currentEncoderState;
if (encoder.getState(currentEncoderState)) {
Serial.print("Encoder: ");
currentValue = currentEncoderState.currentValue;
if (currentValue != prevEncoderValue) {
Serial.println(currentValue);
prevEncoderValue = currentValue;
} else
switch (currentEncoderState.currentClick) {
case NewEncoder::UpClick:
Serial.println("at upper limit.");
break;
case NewEncoder::DownClick:
Serial.println("at lower limit.");
break;
default:
break;
}
}
}
Thank you Gfvalvo,
excellent suggestion, many thanks!
I'll modify my code and let you know..
Roberto
Hi gfvalvo,
I tested what you have suggested and it was perfectly fine.
I only have the one problem of handling the prescaler value (currently pre-set to "4") with a variable that was determined in the loop of the program.
Please help me with any suggestions you may have?
Thank tou, Roberto
if the pre-scale-value is used in the constructor
I assume the "4" as the last parameter of the constructor is the pre-scaler.
If you want to change this pre-scaler-value during runtime requires re-writing the NewEncoder-library.
And this development is another example why it is so important to really read how to get the best out of the forum.
Now the details are dropping in time after time and some of them like this one require fundamental changes.
best regards Stefan
Indeed, this is known as Feature Creep or Moving the Goalposts. It is the bane of design engineers everywhere.
@robymak , fortunately the new feature is easy to add, in this case. But, before I show you how, please think about ALL of your requirements for the encoder's behavior and post them here completely, clearly, and concisely. For example, what should happen to the encoder's current value when the prescaler is changed?