Electronics > Microcontrollers
please someone unmuddle me. trying to get a rotary encoder working
drspastic:
hi, im not very good at this arduino business. i managed to get the display attached and working, setup variables.
now im trying to graft in some code from: https://esp32io.com/tutorials/esp32-rotary-encoder
i got so far but i dont know where to put the rest and what loop should go where. the rotary controller ultimately should change the variable vfoa
--- Code: ---#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <ezButton.h> // the library to use for SW pin on encoder
#define CLK_PIN 34 // ESP32 pin GPIO25 connected to the rotary encoder's CLK pin
#define DT_PIN 35 // ESP32 pin GPIO26 connected to the rotary encoder's DT pin
#define SW_PIN 32 // ESP32 pin GPIO27 connected to the rotary encoder's SW pin
#define DIRECTION_CW 0 // clockwise direction rotary
#define DIRECTION_CCW 1 // counter-clockwise direction rotary
#define TFT_CS 5
#define TFT_RST 2
#define TFT_DC 0
#define vfoa 0740.00000
#define vfob 2400.00000
#define TFT_BL 22 // LED back-light
int counter = 0; //rotary
int direction = DIRECTION_CW; //rotary
int CLK_state; //rotary
int prev_CLK_state; //rotary
ezButton button(SW_PIN); // create ezButton object that attach to pin 32;
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); // hardware SPI
void setup(void) {
tft.initR(INITR_BLACKTAB); // Init ST7735S chip, black tab
// configure encoder pins as inputs
pinMode(CLK_PIN, INPUT);
pinMode(DT_PIN, INPUT);
button.setDebounceTime(50); // set debounce time to 50 milliseconds
// read the initial state of the rotary encoder's CLK pin
prev_CLK_state = digitalRead(CLK_PIN);
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
tft.setRotation(1);
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_GREEN);
tft.setTextSize(2);
tft.setCursor(0, 20);
tft.println(vfoa);
tft.println("mhz");
tft.setTextColor(ST77XX_YELLOW);
tft.setCursor(0, 60);
tft.println(vfob);
tft.println("mhz");
}
void loop() { }
--- End code ---
im having to work this out bit at a time learning as i go. any suggestions welcome, except ask ai because its always wrong which is why im trying to do it myself. ;D
drspastic:
ps. im concerned about interupts and reaction time because i will need the finished unit to switch values rather fast and have the rotary knob live at all times
MarkF:
In the past, my approach to processing rotary encoder events falls in to two general sections.
* Section 1:
I setup a fixed interrupt at 200 Hz or greater. (You can optimize the rate after everything is working.) When this interrupt occurs, I check if the encoder has changed. If changed, I increment or decrement a global variable that counts the changes. The interrupt also checks for switches and setting global flags. (Do switch debounce here also.) This minimizes the execution time within the interrupt only checking rotation counts and switches.
I DO NOT do interrupt-on-change to detect encoder changes. The interrupts can quickly be overloaded by switch bouncing in the encoder. Noise on the encoder lines will keep the processor in the interrupt routine and prevent any other processing. (Others will argue this point but I consider interrupt-on-change a bad design in this case.)
* Section 2:
In your main execution loop, I check if the global counter is non-zero. If non-zero, add the global count to the value being controlled. Then zero the global count and wait for more changes from the interrupt. Also, do similar processing for switch activation. This is also where I draw any displays and any other processing that needs to be done. This main loop does not have to be fast because the encoder counter is being handled at the fast interrupt rate. You just use the cumulative count at the beginning of each time through the loop.
A note here:
You can also add rotation acceleration in the main loop by checking if the cumulative count is greater than a triggering value and then increasing the count.
Example main loop code to do encoder acceleration:
int cCount=0; // global cumulative count from interrupt
int speed=0; // sample value being controlled
loop()
{
// check for encoder change
if (cCount != 0) {
// perform acceleration
if (cCount > 2) cCount += 5;
if (cCount < -2) cCount -= 5;
// update speed with encoder change
speed += cCount;
// restart count from interrupt
cCount = 0;
}
// TODO: more loop processing
}
rhodges:
--- Quote from: MarkF on January 16, 2025, 09:19:48 pm ---In your main execution loop, I check if the global counter is non-zero. If non-zero, add the global count to the value being controlled. Then zero the global count and wait for more changes from the interrupt.
--- End quote ---
Be careful of a race condition. A guard flag set and cleared around this code can alert the interrupt handler to skip the check.
MarkF:
--- Quote from: rhodges on January 16, 2025, 11:22:50 pm ---
--- Quote from: MarkF on January 16, 2025, 09:19:48 pm ---In your main execution loop, I check if the global counter is non-zero. If non-zero, add the global count to the value being controlled. Then zero the global count and wait for more changes from the interrupt.
--- End quote ---
Be careful of a race condition. A guard flag set and cleared around this code can alert the interrupt handler to skip the check.
--- End quote ---
Two points:
1) The interrupt does not check any thing and doesn't need to. It just increments or decrements the global count.
2) In my actual main loop, reading the global variable is a little more complex.
(If you minimize the execution time between checking the count and clearing the count, it don't think even this is required.)
temp = cCount;
cCount = 0;
// use temp instead of cCount to avoid any changes during processing
if (temp != 0) {
. . .
}
This will minimize the time between reading and clearing the count from the interrupt.
Also, any race condition really doesn't matter.
The chance of getting an interrupt during the small time between checking and clearing the count is minimal.
It would be rare and it really does not matter if you miss an encoder count or not.
The user will not notice any rare missing counts.
In practice, the bigger issue is switch bounce on the encoder pins.
Navigation
[0] Message Index
[#] Next page
Go to full version