Help needed with interrupts

I have 4 rotary encoders driving 4 quadrature encoder chips, LS7184, that output some pulses and an up/down direction line.

I bring the pulses into the arduino on D2, D3 (INT0, 1) and D4, D5 (PCINT20, 21).
The up/down lines go in on D6, D7, D14, D15.

I have the ISRs written to set bits in a byte when an interrupt occurs. I want to send the byte out over the serial lines, or at least make an LED flash at the top of loop when I see that an interrupt bit has been set.

I can’t seem to get any interrupt created tho. Looking for some help.

There are variables here that read 3 switches and an octal DAC every 5mS and send that out blink without delay style, that works great and is deleted here to meet post length.


/* Sketch for Remote Control Transmitter board.
 Set up to read from 4-channel ADC and send int values to the Receive board over the serial interface every 10mS.
 2 INTs & 2 PCINT's used to detect pulses from four Quadrature Encoders and send out over the RS232 as well with direction.
 Also send the state of 2 push buttons.
 Octal ADC MCP3208
 S3  LED on D8
 Revision to use interrupts
 Rewired board some:
 D0 - RX - no change
 D1 - Tx - no change
 D2 - INT0, Optical encoder 0 clk via LS7184
 D3 - INT1, Optical encoder 1 clk
 D4 - PCINT20, mechanical encoder 0 clk
 D5 - PCINT21, mechanical encoder 1 clk
 D6 - Optical encoder 0 Up/Down 
 D7 - Optical encoder 1 Up/Down
 D8 - S3 status from Rx card - no change
 D9 - Sync status from Rx card - no change
 D10 - ADC_SS - no change
 D11 - MOSI - no change
 D12 - MISO - no change
 D13 - SCK - no change
 D14 - mechanical encoder 0 Up/Down
 D15 - mechanical encoder 1 Up/Down
 D16 - S0 switch input - no change
 D17 - S1 switch input - no change
 D18 - S2 switch input - no change
 D19 - not used

// call libraries

#include <SPI.h>

// declare the pins used

// Port B
byte ADC_SS = 10; // SPI SS for ADC
// D11,12,13 - SPI for ADC
byte Syncpin = 9; // Sync output
byte S3pin = 8; // Switch2 output, Hi/Lo received from Serial

// Port C
byte D19 = 19; // not used
byte S2pin = 18; // Switch2 input, Hi/Lo
byte S1pin = 17; // Switch1 input, Hi/Lo
byte S0pin = 16; // Switch0 input, Hi/Lo

byte updown3 = 15;
byte updown2 = 14;

// Port D
byte updown1 = 7;
byte updown0 = 6;
byte enc_clk3 = 5;
byte enc_clk2 = 4;
byte enc_clk1 = 3;
byte enc_clk0 = 2;
// D1, D0 used by Serial

// declare variables

byte ADCsyncbyte = 0x33; // b00110011 sync byte when send ADC data
byte Encsyncbyte = 0xAA; // B10101010 sync byte when send quadrature encoder data

int ADCaddress = 0x0600;     // 600, 640, 680, 6C0, 700, 740, 780, 7C0

byte dummyADC = 0;
byte highADC = 0;
byte lowADC = 0;
int x= 0;

byte volatile quadEncoder = 0; // volatile for the interrupts

/* quadEncoder bit assigments:
 BIT7 - optical encoder 1 up/down direction
 BIT6 - optical encoder 0 up/down direction
 BIT5 - mechanical encoder 1 pulse
 BIT4 - mechanical encoder 0 pulse
 BIT3 - optical encoder 1 pulse
 BIT2 - optical encoder 0 pulse
 BIT1 - mechanical encoder 1 up/down direction
 BIT0 - mechanical encoder 0 up/down direction
 Works out well for masking off & mixing in results!

// other variables used in the interrupts 
byte volatile dir0;
byte volatile dir1;
byte volatile dir2;
byte volatile dir3;

byte volatile p;
byte volatile oldPortD;

unsigned long previousMillis;

byte address_count;

void setup(){

  // setup the I/O pins

  pinMode (ADC_SS, OUTPUT);
  digitalWrite (ADC_SS, HIGH);

  pinMode(enc_clk0, INPUT);
  digitalWrite (enc_clk0, HIGH); // enable pullup resistor
  pinMode(enc_clk1, INPUT);
  digitalWrite (enc_clk1, HIGH); // enable pullup resistor
  pinMode(enc_clk2, INPUT);
  digitalWrite (enc_clk2, HIGH); // enable pullup resistor
  pinMode(enc_clk3, INPUT);
  digitalWrite (enc_clk3, HIGH); // enable pullup resistor

  pinMode(updown0, INPUT);
  digitalWrite (updown0, HIGH); // enable pullup resistor
  pinMode(updown1, INPUT);
  digitalWrite (updown1, HIGH); // enable pullup resistor
  pinMode(updown2, INPUT);
  digitalWrite (updown2, HIGH); // enable pullup resistor
  pinMode(updown3, INPUT);
  digitalWrite (updown3, HIGH); // enable pullup resistor

  pinMode(S0pin, INPUT);
  digitalWrite (S0pin, HIGH); // enable pullup resistor
  pinMode(S1pin, INPUT); 
  digitalWrite (S1pin, HIGH); // enable pullup resistor
  pinMode(S2pin, INPUT);
  digitalWrite (S2pin, HIGH);  // enable pullup resistor
  pinMode(S3pin, OUTPUT);
  digitalWrite (S3pin, LOW);  // add new wiring to drive LED anode High when S3 on receiver is closed 
  pinMode(Syncpin, INPUT);
  digitalWrite (Syncpin, LOW);  // add new wiring to drive LED anode High when Sync message from receiver is returned 

  // free pin  
  pinMode (D19, INPUT);
  digitalWrite (D19, HIGH);  

  PCMSK2 = _BV (PCINT4) | _BV (PCINT5); // want pins 20, 21  >> PCMSK2 is port D

  PCIFR = _BV (PCIF2) ;  // Clear any existing interrupts

  PCICR |= _BV (PCIE2) ; // enable pin change interrupts for PCINT20,21  on port D

  // Open Serial interface
  Serial.begin (115200);
  // Open SPI interface
  SPI.begin (); // leave blank, we are master

}  // end void setup
 >  1  Reset 
 >  2  External Interrupt Request 0  (pin D2)          (INT0_vect)
 >  3  External Interrupt Request 1  (pin D3)          (INT1_vect)
 >  6  Pin Change Interrupt Request 2 (pins D0 to D7)  (PCINT2_vect)

// ISRs for Hardware interrupts

// Hardware interrupt 0

ISR (INT0_vect) // 
  // optical encoder 0 pulse
  dir0 = PORTD & B01000000;  // 40
  quadEncoder = quadEncoder  | B00000100 | dir0; // 04

// Hardware interrupt 1

ISR (INT1_vect) // 
  // optical encoder 1 pulse
  dir1 = PORTD & B10000000; // 80
  quadEncoder = quadEncoder | B0001000 | dir1; // 08

// PCINTs for mechanical encoders 0 & 1
ISR (PCINT2_vect)  // PCINT2 for port D
  byte p = PORTD;
  // mechanical encoder 0 pulse
  if ((p & 0x10) != (oldPortD & 0x10))  
    // portD4, PCINT20, has changed
    dir2 = PORTC & B00000001; // 01
    quadEncoder = quadEncoder | B00010000 | dir2; // 10
  // mechanical encoder 0 pulse
  if ((p & 0x20) != (oldPortD & 0x020))
    // portD5, PCINT21, has changed
    dir3 = PORTC & B00000010; // 02
    quadEncoder = quadEncoder | B00100000 | dir3; // 20

  // remember for next time
  oldPortD = p;

} // end of PCINT2_vect

Watch for an encoder clock pulse, if a pulse occurs then capture that in the ISR with the direction, and at the top of loop set the bits/direction for any of the four that occurred and RS232 message.
 On the receive side, set the output bits accordingly & clear.
 Change the bias resistor on the LS7184s to shorten up the pulses that create the interrupt, currently around 25uS.


void loop(){

  // send out quad encoder data if any occured
  if ((quadEncoder & B00111100) !=0){ 

    // serial.println for serial monitor testing
    // serial.write for actual use
    Serial.println (Encsyncbyte, HEX); // syncbyte = B10101010, 0xAA
    Serial.println (quadEncoder, HEX);
    quadEncoder = quadEncoder & B11000011;  // clear the interrupt bits
  /* added to make sure loop was running
        digitalWrite (Syncpin, HIGH);
    delay (50);
    digitalWrite (Syncpin, LOW); 
    delay (50);}

  // Split up for DAC & INT responses?    
  if (Serial.available()>0){                          
    digitalWrite (Syncpin, HIGH);                    // show receive comimg back
  else {
    digitalWrite (Syncpin, LOW);

} // end void loop

LS7183_LS7184.pdf (190 KB)

I don't see the interrupt configuration for INT0 and INT1. And I think that by default the interrupt is triggered on low level. How fast is the encoder moving? Try adding EIMSK = 3; to your startup code maybe?

Were those the ones missing? Or you can't get any interrupt going?

This may have to do with the Arduino, but I normally call sei(); to enable interrupts. I think Arduino does that for you, but I am not 100% sure. It does.

Have you tried stripping down the code to the bare minimum to see if this is caused by any object?

I saw this in someone else’s code - maybe need to add to top of the sketch:

#include <avr/interrupt.h>

From what I've seen with a scope, the encoder chip (LS7184) (and there are 4 of them) can put out a pair of pulses maybe 1mS apart or so when its spun really fast by hand (like snapping the shaft in your fingers fast). They are intended to have manually controlled wheels on them for steering, controlling a motor, that kind of thing on the far end, so not determining the speed say of a continually rotating shaft.

Just found this PCINT library, with testing showing speed of operation, by Michael Schwager/GreyGnome.

Gonna check it out tonight.

This playground article seems to be based on the same:

You would be better off using just a pulse or 2 (depending on shaft speed) to determine speed. You need some time to elapse between pulses to determine speed. You would be better off using multiple AVR chips, 1 for each axis, to do something like this because you have no way of controlling when the interrupts are routed to the program, and thus you might have overlapping interrupts that are not processed properly. Multiple smaller chips reporting to a larger controller chip - several of the tiny series chip, with a 328 as the master.

You can work out the programming on the arduino - just do it for 1 axis. Then program a slave chip and a 328 (Arduino) to communicate and then add slave devices. I used some ATtiny2313 chips for a project, less than $3.00US and the operation is simplified.

Think of it as distibuted processing. A complex set of operations, broken down into simpler blocks, and combined to do the complex operation.

Typically, in larger systems these types of operations were done by specialized hardware and the results pulled from that hardware as needed. With individula AVR's you can do the same thing, but with much smaller boards.

The avr/interrupt.h is needed on top of the code, but since you had declared the interrupt vectors and compiled I don't think that is the problem.

I'll have another look later on.

Will be sending Pulse & direction to this card via serial link, recreating the pulse on the far end:

P091.037.IBL2403.UM.pdf (2 MB)