Hello, I am using an Arduino Micro with ATmega 32U4 and have a problem in my project.
I am trying to implement a switch-matrix style system, which will process inputs from 10 rotary encoders. It is using the interrupt approach which works on its own, but fails when the switch-matrix aspect is added.
The A pins of the 10 encoders are all connected together to pin 7.
Similarly, B pins of encoders are connected to pin 3
Both pins 3 and 7 support interrupts on the 32U4, and are normally high with internal pull-up resistors.
Then the common pins of each encoder are each connected to a column of the switch matrix, and these columns are cycled by the program through a decade counter and inverters (active column is low), while keeping track which column is active at the moment.
This way, when the encoder is turned and triggers the ISR, the ISR will look at which column is active and will be able to determine which encoder the input is coming from. Please take a look at the rough schematic I prepared
The encoder I am using is 24 PPR and 24 detents
I know I can use a library such as encoder.h but i really wanted to do it manually and learn.
Here is how I catch the input in the interrupt:
The encoders output between 2 detents when turning clockwise:
A: 1 0 0 1 1
B: 1 1 0 0 1
and the following between 2 detents when turning anti-clockwise :
A: 1 1 0 0 1
B: 1 0 0 1 1
When the A line changes state from 1 to 0 (ISR triggered by a fall) and the B line is still high, its clockwise. Similarly when the B line changes state from 1 to 0 (ISR triggered by a fall) and the A line is still high, its anti-clockwise. So its only looking at the first section of detent cycle but seems to work.
So basically in the main program loop I have a for-loop that loops variable i from 0 to 9. This variable represents the active column and is global, so that it can be accessed by the ISRs to determine which encoder was used.
Inside this loop, I have another for-loop that is very big just to waste time and simulate the system doing something (something other than operating the encoders). After the loop is complete, I send a signal to the decade counter to move onto the next column.
The problem is that when I have an encoder connected to column 0 and use it, the column reported by the ISR is 9 (previous one) almost all the time, rarely i get the correct 0, and even sometimes its 10, which shouldn't be possible since the for-loop for the columns is for(i=0; i<10; i++)
Similarly when the encoder is connected to column 1, the reported column is 0 and so on, they all are -1 of what they should be.
Any idea what I am doing wrong here?
Thanks !
//global variable accessable by ISRs
int i = 0;
//pin used to send signal to decade counter to move to next column
int drive_line_clk = 2;
//reset pin for the decade counter
int reset_pin = 13;
//encoder outputs
int encoder_A = 7; //supports interrupt
int encoder_B = 3; //supports interrupt
//time in ms needed between interrupts
int bounce_time = 10;
void setup()
{
Serial.begin(9600);
//declare outputs
pinMode(drive_line_clk, OUTPUT);
pinMode(reset_pin, OUTPUT);
digitalWrite(reset_pin, LOW);
//encoder sense lines are normally high inputs
pinMode(encoder_A, INPUT_PULLUP);
pinMode(encoder_B, INPUT_PULLUP);
//attach interrupts to the encoders
attachInterrupt(digitalPinToInterrupt(encoder_A), interrupt_function_A, FALLING);
attachInterrupt(digitalPinToInterrupt(encoder_B), interrupt_function_B, FALLING);
//reset the count of the decade counter when all startup is done
delay(10);
digitalWrite(reset_pin, HIGH);
delay(10);
digitalWrite(reset_pin, LOW);
}
void loop()
{
//global variable i represents the active column
for(i=0; i<10; i++)
{
//this loop is simulating the system being busy to test interrupts
for(long a=0; a<1000000000; a++){}
//when done move to next column
clock_key_matrix();
}
}
void clock_key_matrix()
{
digitalWrite(drive_line_clk, HIGH);
digitalWrite(drive_line_clk, LOW);
}
void interrupt_function_A()
{
//first read what the current column is
int column = i;
static unsigned int count = 0;
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
// If interrupts come faster than bounce_time, assume it's a bounce and ignore
if (interruptTime - lastInterruptTime > bounce_time)
{
if (digitalRead(encoder_B) == HIGH)
{
count ++;
Serial.println( (String)"R "+ count + " " + column);
}
}
// Keep track of when we were here last (no more than every 5ms)
lastInterruptTime = interruptTime;
}
void interrupt_function_B()
{
//first read what the current column is
int column = i;
static unsigned int count = 0;
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
// If interrupts come faster than bounce_time, assume it's a bounce and ignore
if (interruptTime - lastInterruptTime > bounce_time)
{
if (digitalRead(encoder_A) == HIGH)
{
count ++;
Serial.println( (String)"L "+ count + " " + column);
}
}
// Keep track of when we were here last (no more than every 5ms)
lastInterruptTime = interruptTime;
}
