Struct variables and interrupts

Dear all,

I am attempting to use interrupts in a time sensible sketch I wrote some year ago. Here is a simplified sketch I am trying to have compiled, with no success:

#define INPUTS 3

struct digitalInput {const byte pin; volatile byte state;} 
digitalInput[INPUTS] = {
{40, HIGH}, 
{41, HIGH}, 
{38, HIGH}, 
};

void setup(){
for (int j = 0; j < INPUTS; j++){
  pinMode(digitalInput[j].pin, INPUT_PULLUP);
  digitalInput[j].state = digitalRead(digitalInput[j].pin);
  attachInterrupt(digitalPinToInterrupt(digitalInput[j].pin), stateHandle, CHANGE);
}
} 

void loop(){
}

void stateHandle(){
digitalInput[j].state = !digitalInput[j].state;
}

The error is

Arduino:1.6.11 (Windows 7), Scheda:"Arduino Due (Programming Port)"

C:\mydeal\prova\prova.ino: In function 'void stateHandle()':

prova:23: error: 'j' was not declared in this scope

 digitalInput[j].state = !digitalInput[j].state;

              ^

exit status 1
'j' was not declared in this scope

How can I solve this?

Thanks in advance!!

As the compiler hinted, by declaring 'j' in this scope.

If you don't know what scope means, read What is Scope?

all your pins share the same interrupt and the ISR is called without reference to the pin that triggered it... so you need to do some magic in the ISR to decide which pins it was, or attach different ISR to different pins but for that you probably would need to use pins supporting discrete interrupts

I suspect it would be simpler to have the ISR update a regular variable and a flag to say the interrupt has happened and then use code in loop() to update the struct when it sees the flag has been set.

You cannot know what value J will have when the ISR is triggered

It seems very strange to be using volatile for part of the struct. The purpose of volatile is to prevent the compiler from optimizing away a variable that it thinks is not used in the program.

...R

Which Arduino supports external interrupts on pins 38, 40 and 41?

Thank you all very much.

@Whandall as far as I know arduino DUE supports interrupts on all digital pins, but I could be wrong.

Ok, I have to define a single interrupt function for each input pin. The code here takes only three pins, but the Whole code will define more or less 30 digital pins, so I was trying to figure if it was possible to write all those similar functions in a small code.

Any idea? I will go the "one void function per pin" way eventually, or follow Robin suggestion, no problem :wink:

(thanks!!)

Whandall:
Which Arduino supports external interrupts on pins 38, 40 and 41?

that was kinda my point when I wrote "attach different ISR to different pins but for that you probably would need to use pins supporting discrete interrupts"

A DUE does support interrupts on all pins indeed - but still you wouldn't know which one triggered the ISR

Ok, thanks. I was almost sure about that :smiley:

Let turn my question this way: is there a way to define a portion of code like this ...

attachInterrupt(digitalPinToInterrupt(digitalInput[0].pin), state0Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[1].pin), state1Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[2].pin), state2Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[3].pin), state3Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[4].pin), state4Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[5].pin), state5Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[6].pin), state6Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[7].pin), state7Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[8].pin), state8Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[9].pin), state9Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[10].pin), state10Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[11].pin), state11Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[12].pin), state12Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[13].pin), state13Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[14].pin), state14Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[15].pin), state15Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[16].pin), state16Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[17].pin), state17Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[18].pin), state18Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[19].pin), state19Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[20].pin), state20Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[21].pin), state21Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[22].pin), state22Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[23].pin), state23Handle, CHANGE);

... into something smaller?

AND

Is there a way to define something like:

void state0Handle(){
digitalInput[0].state = !digitalInput[0].state;
}

void state1Handle(){
digitalInput[1].state = !digitalInput[1].state;
}


void state2Handle(){
digitalInput[2].state = !digitalInput[2].state;
}


void state3Handle(){
digitalInput[3].state = !digitalInput[3].state;
}


void state4Handle(){
digitalInput[4].state = !digitalInput[4].state;
}


(etc...)

... into something smarter?

thank you VERY much!

what's wrong with it in your view? (besides being verbose)

other question is what are you trying to measure? do you really need an interrupt to update the value of the pin? if you need to read the pin somewhere, read it there... why do you need a structure for that or have ISR maintaining that in memory whereas it's already in a register you just have to read? your structure (if you want to have that in a structure) could have a pointer to the port to read and the mask to apply to extract the right bit in PIO_PDSR

Nothing is wrong with it in my view, I am only trying to learn something new :slight_smile:

The sketch I am trying to modify was written (by me) some year ago; it is used to record button presses on an arcade control panel and then send to a PC emulated keyboard presses. I wrote an instructable, just in case you want to read the full code and see the full (open source) project https://www.instructables.com/id/Jammarduino-DUE-DIY-PC-to-Jamma-Interface-for-Arca/.
The sketch I wrote work like a charm, but I am well aware that commercial "pro" solutions use interrupts to record buttons presses; I have no clue how they do it (closed source underpower). Unfortunately I am not a coder and I am trying to approach the task the "pro" way trying to get some help from skilled coders (this community is full of).

So, any help would be much appreciated at his point.

I am well aware that commercial "pro" solutions use interrupts to record buttons presses

said who?

buttons are "slow" in computer time and they bounce usually.. if you arduino is just looking at detecting button presses, scan whole PORTs in the loop and compare to previous version and send the right event. you don't need an interrupt for that

No pro solutions do NOT use interrupts to record button presses!

Mark

J-M-L, my code work exactly as you said, with a debounce routine too.

I don't want to direct link to closed source products, but it's well known that commercial jamma interfaces use per-pin interrupts. This is a copy/paste from a manufacturer site: "Does not use a matrix - no ghost keys. All inputs are completely separate and generate their own interrupt - this is the fastest possible method."

I don't think interrupts are really necessary while playing on 30 fps games (I made some maths and testing reported at the end of the instructable) but I am curious anyway to see if interrupts could give a tangible advantage by implementing them.

J-M-L:
said who?

buttons are "slow" in computer time and they bounce usually.. if you arduino is just looking at detecting button presses, scan whole PORTs in the loop and compare to previous version and send the right event. you don't need an interrupt for that

Barito:
I don't want to direct link to closed source products, but it's well known that commercial jamma interfaces use per-pin interrupts. This is a copy/paste from a manufacturer site: "Does not use a matrix - no ghost keys. All inputs are completely separate and generate their own interrupt - this is the fastest possible method."

what they mean by "Does not use a matrix" is that every button is directly connected to a pin and there is no serialization to read the buttons - which to some extent is what you do.

sure an ISR is the hardware way of triggering an action but I would argue that reading a 32 bit PORT, doing a change compare and trigger actions at 84 MHz is going to be totally invisible to the fastest gamer and would better match your appetite for code simplification.

I would also argue that such an approach makes it even easier to detect "simultaneous" (at sampling rate) double key presses - which would otherwise trigger two consecutive serialized interrupts in your approach without knowing which one was pressed first (if there is a such a need).

last - if there was such a coding - there is no point using ISR and then calling digitalRead() which is "super slow" in respect to the reaction time of your ISR

Yes, I agree and we reached a point of interest here. Would you (or anyone reading with some spare time :slight_smile: ) be so kind to show me how that PORTs scan and masking could be done?

Just in case, this is the whole code posted on the instructables (with some minor update):

// Interfaccia PC-to-Jamma
// Per Arduino DUE
// Sezione controlli (mappatura secondo MAME default) e 
// blocco frequenze >15KHz.
// il circuito complessivo prevede l'uso di un amplificatore
// integrato THS7374.
//
// by Barito, 2017

#include <Keyboard.h>

#define INPUTS 24

const int HSyncPin = A8;
const int disablePin = A11;
const int bypassPin = A10;
const int ledPin = 13;

const int BYPASS = 1;

const int delayTime = 20;

boolean startBlock = 0;

//31KHz  -> 32 us
//25KHz -> 40 us
//15KHz -> 66 us
unsigned long periodoSum = 0;
unsigned long periodoIst = 0;
unsigned long FREQKHz=0;
unsigned long periodoMedio=0;
int samplesm;
const int samples = 50;

struct digitalInput {const byte pin; boolean state; long dbTime; const byte key; const byte key_shift;} 
digitalInput[INPUTS] = {
{40, HIGH, 0, 49, 49}, //1 - P1 START
{41, HIGH, 0, 50, 177}, //2 - P2 START (ESC)

{38, HIGH, 0, 218, 189}, //up arrow - P1 UP (tilde)
{36, HIGH, 0, 217, 112}, //down arrow - P1 DWN (p)
{34, HIGH, 0, 216, 176}, //left arrow - P1 LEFT (ENTER)
{32, HIGH, 0, 215, 179}, //right arrow - P1 RIGHT (TAB)
{30, HIGH, 0, 128, 53},  //left ctrl - P1 B1 (5)
{28, HIGH, 0, 130, 54},  //left alt - P1 B2 (6)
{26, HIGH, 0, 180, 223}, //space - P1 B3 (+)
{24, HIGH, 0, 129, 129}, //left shift - P1 B4
{22, HIGH, 0, 122, 122}, //z - P1 B5
{2, HIGH, 0, 120, 222}, //x - P1 B6 (-)

{39, HIGH, 0, 114, 114}, //r - P2 UP
{37, HIGH, 0, 102, 102}, //f - P2 DWN
{35, HIGH, 0, 100, 100}, //d - P2 LEFT
{33, HIGH, 0, 103, 103}, //g - P2 RIGHT
{31, HIGH, 0, 97, 97},   //a - P2 B1
{29, HIGH, 0, 115, 115}, //s - P2 B2
{27, HIGH, 0, 113, 113}, //q - P2 B3
{25, HIGH, 0, 119, 119}, //w - P2 B4
{23, HIGH, 0, 105, 105}, //i - P2 B5
{3, HIGH, 0, 107, 107}, //k - P2 B6

{42, HIGH, 0, 53, 53}, //5 - P1 COIN
{43, HIGH, 0, 54, 54}, //6 - P2 COIN
};

void setup(){
  
for (int j = 0; j < INPUTS; j++){
  pinMode(digitalInput[j].pin, INPUT_PULLUP);
  digitalInput[j].state = digitalRead(digitalInput[j].pin);
  digitalInput[j].dbTime = millis();}  

  pinMode(HSyncPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  pinMode(bypassPin, OUTPUT);
  pinMode(disablePin, OUTPUT);

  digitalWrite(disablePin, HIGH);
  digitalWrite(ledPin, LOW);
  
  if (BYPASS){digitalWrite(bypassPin, HIGH);}
  else {digitalWrite(bypassPin, LOW);}

Keyboard.begin();

} // chiudo setup

void loop(){
generalInputs();
shiftInputs();
freqBlock();
}

void generalInputs(){
//general input handling
for (int j = 1; j < INPUTS; j++){
 if (millis()-digitalInput[j].dbTime > delayTime && digitalRead(digitalInput[j].pin) !=  digitalInput[j].state){
    digitalInput[j].state = !digitalInput[j].state;
    digitalInput[j].dbTime = millis();
    if(digitalInput[0].state == HIGH){ //shift button
      if (digitalInput[j].state == LOW){
        Keyboard.press(digitalInput[j].key);}
      else {Keyboard.release(digitalInput[j].key);}
    }
    else{
      if (digitalInput[j].state == LOW){
        startBlock = 1;
        Keyboard.press(digitalInput[j].key_shift);}
      else {Keyboard.release(digitalInput[j].key_shift);}
    }
  }
}  
}

void shiftInputs(){
//reversed input handling (P1 START) - shift button
if (millis()-digitalInput[0].dbTime > delayTime && digitalRead(digitalInput[0].pin) !=  digitalInput[0].state){
    digitalInput[0].state = !digitalInput[0].state;
    digitalInput[0].dbTime = millis();
    if (digitalInput[0].state == HIGH && startBlock == 0){
      Keyboard.press(digitalInput[0].key);
      delay(30);
      Keyboard.release(digitalInput[0].key);
    }
    else{startBlock = 0;}
}
}

void freqBlock(){
periodoSum = 0;  
samplesm = samples;
for(unsigned int i=0; i<samples; i++){
  periodoIst = pulseIn(HSyncPin,HIGH);
  if(periodoIst < 100 && periodoIst > 10){
  periodoSum += periodoIst;} 
  else {samplesm--;}
  periodoMedio = (periodoSum/samplesm)+5;}

FREQKHz=1000/periodoMedio;

if(FREQKHz<21){
  digitalWrite(disablePin, LOW);
  digitalWrite(ledPin, HIGH);}
else {
  digitalWrite(disablePin, HIGH);
  digitalWrite(ledPin, LOW);}
}

For sure there's room for improvements, but this is my best and I need some help to go further on this.

Barito:
Ok, thanks. I was almost sure about that :smiley:

Let turn my question this way: is there a way to define a portion of code like this ...

attachInterrupt(digitalPinToInterrupt(digitalInput[0].pin), state0Handle, CHANGE);

attachInterrupt(digitalPinToInterrupt(digitalInput[1].pin), state1Handle, CHANGE);
attachInterrupt(digitalPinToInterrupt(digitalInput[2].pin), state2Handle, CHANGE);




... into something smaller?

As pointed out, interrupts may not be the best solution for your application. But, to your question, I'd bind the pin information with a pointer to the handler function right in the structure. May not be any shorter, but I prefer it stylistically.

void handle38();
void handle40();
void handle41();

struct digitalInput {
  const uint8_t pin;
  volatile uint8_t state;
  void (* const handler)();
};

digitalInput inputs[] = {
  {38, HIGH, handle38},
  {40, HIGH, handle40},
  {41, HIGH, handle41}
};

const uint8_t numEntries = sizeof(inputs) / sizeof(inputs[0]);

void setup() {
  for (uint8_t index = 0; index < numEntries; index++) {
    attachInterrupt(digitalPinToInterrupt(inputs[index].pin), inputs[index].handler, CHANGE);
  }
}

void loop() {
}

void handle38() {
}

void handle40() {
}

void handle41() {
}

Thanks! I have left the interrupts way because thanks to the brainstorming here I realized that my suspects where correct for this application and the use of interrupts manufacturers declare is just a "litter box".

I am in the hope someone more skilled than me (everyone here essentially :slight_smile: ) will have a chance to help me implementing the code I shared.

Barito:
Yes, I agree and we reached a point of interest here. Would you (or anyone reading with some spare time :slight_smile: ) be so kind to show me how that PORTs scan and masking could be done?

there is plenty of information the web for this, here is the first answer from a google search and the pinout mapping


(image source = this post from graynomad)

The ARM Cortex Mx architecture uses a nested vector interrupt controller which actually can give you interrupts on any of the 63 pins on the Due. You really need to spend some time digesting the contents of chapter 31 of the SAM3X datasheet which details the PIO controller. In there you will find all your answers. It does mean that you'll need to break away from the use of one-size-fits-all libraries and do some bare-metal port/bit manipulations, but that's what it's all about, no?

Thanks. Dealing with direct port manipulation asks for some study on my side. I will take some time to digest all the info you pointed me to.

In case someone with port manipulation at hand would speed up the process with some other indications or even direct help (I feel I have reached my "no coder in real life" limit :slight_smile: ), feel free to post

Much appreciated!