0

I have been given to understand the ATTiny88 is very similar to the Arduino Uno / ATMega328 MCU. There does not seem to be a tag for the ATTiny88, so that is why I chose the tags I did. I do not have a high enough reputation to create a tag, yet. Thanks to the generous help of timemage, Edgar Bonet, and others, my MCU board is now responding, albeit very erratically, to pulses on pins 12 and 13, designated Phase_B and Phase_A, respectively, in the code below. I have not yet entirely ruled out a hardware issue (I will do so), but a software issue is still likely. I don't quite know what. The MCU is registering pulses, but it very often misses large numbers of turns in either direction. What's more, it frequently reports the wrong direction, strongly favoring a CW rotation, but sometimes reporting a CCW rotation when the knob is turned CW. It is definitely not an overflow issue, as the anomalous effects are seen in abundance when in the mid range of the variables.

#include <Arduino.h>
#include <TM1637Display.h>
#include "PinChangeInterrupt.h"

volatile uint8_t counter = 0; volatile bool Update = false; volatile int currentStatePhase_B; volatile int lastStatePhase_B; volatile int pulses = 0;

// Shutdown Output #define SW 11

// Rotary Encoder Inputs #define Phase_A 13 #define Phase_B 12 // TM1637 I/O ports #define DIO 3 #define CLK 4 TM1637Display display(CLK, DIO);

void setup() {

// Set encoder pins as inputs pinMode(Phase_A,INPUT); pinMode(Phase_B,INPUT); pinMode(SW, OUTPUT);

// Read the initial state of Phase_B
lastStatePhase_B = digitalRead(Phase_B);

// Call updateEncoder() when any high/low changed seen
// on interrupt 0 (pin 2), or interrupt 1 (pin 3)
attachPCINT(digitalPinToPCINT(Phase_A), updateEncoder, RISING);
attachPCINT(digitalPinToPCINT(Phase_B), updateEncoder, RISING);

display.setBrightness(0x0f); display.clear(); display.showNumberDec(0, false); }

void loop() { if (Update){ // Show decimal numbers with/without leading zeros display.showNumberDec(pulses, false); delay(1000); display.showNumberDec(counter, false); Update = false; } }

void updateEncoder(){ // Read the current state of Phase_B currentStatePhase_B = digitalRead(Phase_B);

// If last and current state of Phase_B are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStatePhase_B != lastStatePhase_B  &amp;&amp; currentStatePhase_B == 1){
    // If the Phase_A state is different than the Phase_B state then
    // the encoder is rotating CCW so decrement
    if (digitalRead(Phase_A) != currentStatePhase_B) {
        counter --;
    } else {
        // Encoder is rotating CW so increment
        counter ++;
    }
}
if (counter &gt; 1023){
  counter = 1023;
}
if (counter &lt; 0){
  counter = 0;
}  
pulses ++;
// Remember last Phase_B state
lastStatePhase_B = currentStatePhase_B;
Update = true;

}

1 Answers1

0

OK, I have an answer. The MCU is still missing a lot of pulses, but I am fairly certain this is a hardware issue, or at least mostly so. I found some code by Ralph S. Bacon and Marko Pinteric. The YouTube video can be found here

As should always be the case, the ISR is brief. Here it is a single line that merely sets a Boolean variable true. In the loop section, the code repeatedly checks to see if the interrupt has occurred. The checkRotaryEncoder function then verifies the pin values to make sure the encoder is in a valid state (not bouncing). If so, then the counter value is updated with the result.

#include <Arduino.h>
#include <TM1637Display.h>
#include "PinChangeInterrupt.h"

uint8_t lrmem = 3; int lrsum = 0; int num = 0; unsigned int counter = 0;

// Shutdown Output #define SW 11 // Rotary Encoder Inputs #define Phase_A 12 #define Phase_B 13 // TM1637 Display I/O #define DIO 3 #define CLK 4 TM1637Display display(CLK, DIO);

// A turn counter for the rotary encoder (negative = anti-clockwise) int rotationCounter = 200; // Flag from interrupt routine (moved=true) volatile bool rotaryEncoder = false;

// Rotary encoder has moved (interrupt tells us) but what happened? // See https://www.pinteric.com/rotary.html int8_t checkRotaryEncoder() { // Reset the flag that brought us here (from ISR) rotaryEncoder = false;

static uint8_t lrmem = 3;
static int lrsum = 0;
static int8_t TRANS[] = {0, -1, 1, 14, 1, 0, 14, -1, -1, 14, 0, 1, 14, 1, -1, 0};

// Read BOTH pin states to deterimine validity of rotation (ie not just switch bounce)
int8_t l = digitalRead(Phase_A);
int8_t r = digitalRead(Phase_B);

// Move previous value 2 bits to the left and add in our new values
lrmem = ((lrmem &amp; 0x03) &lt;&lt; 2) + 2 * l + r;
// Convert the bit pattern to a movement indicator (14 = impossible, ie switch bounce)
lrsum += TRANS[lrmem];
/* encoder not in the neutral (detent) state */
if (lrsum % 4 != 0)
{
    return 0;
}
/* encoder in the neutral state - clockwise rotation*/
if (lrsum == 4)
{
    lrsum = 0;
    return 1;
}
/* encoder in the neutral state - anti-clockwise rotation*/
if (lrsum == -4)
{
    lrsum = 0;
    return -1;
}
// An impossible rotation has been detected - ignore the movement
lrsum = 0;
return 0;

}

void setup() { // Set encoder pins as inputs pinMode(Phase_A,INPUT); pinMode(Phase_B,INPUT); // Set Power Switch as output pinMode(SW, OUTPUT); // Initialize display display.setBrightness(0x0f); display.clear(); display.showNumberDec(0, false); // Set up interrupt pins and vector attachPCINT(digitalPinToPCINT(Phase_A), rotary, CHANGE); attachPCINT(digitalPinToPCINT(Phase_B), rotary, CHANGE); }

void loop() { // Has rotary encoder moved? if (rotaryEncoder) { // Get the movement (if valid) int8_t rotationValue = checkRotaryEncoder(); // If valid movement, do something if (rotationValue != 0) { counter += rotationValue; if (counter > 1023){ counter = 1023; } else if (counter < 0){ counter = 0; } display.showNumberDec(counter, false); } } }

// Interrupt routine just sets a flag when rotation is detected void rotary() { rotaryEncoder = true; }

Any comments are welcom. In addition, Ralph Bacon used the IRAM_ATTR directive to create the rotary() ISR. My understanding of this directive is it is not necessary in most cases. On the other hand, presumably the code will execute faster if it is pushed into flash RAM. I could not make this work. The compiler complains, "expected initializer before 'rotary'" Is this by chance because the ATTiny88 does not have flash RAM? If not, then what?