Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Electronics ➜ Microprocessors ➜ Buttons, I2C, interrupts and port-expanders

Buttons, I2C, interrupts and port-expanders

Postings by administrators only.

Refresh page


Pages: 1  2 3  

Posted by Nick Gammon   Australia  (23,102 posts)  Bio   Forum Administrator
Date Reply #15 on Sat 27 Aug 2011 09:58 PM (UTC)
Message
The chips defaults to all lines input.

You shouldn't need that extra code. If you are in handleKeypress then it should only be because the interrupt fired ... unless you have changed other things around.

Quote:

The only differences is that I'm using pin 3 and interrupt 1 on the arduino 2009 instead of pin 2 and interrupt 0.


So why are you testing pin 2 in your changes?

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Greete   (16 posts)  Bio
Date Reply #16 on Sat 27 Aug 2011 10:18 PM (UTC)

Amended on Sat 27 Aug 2011 10:19 PM (UTC) by Greete

Message
Hi Nick!
this morning I got a solder and changed pin 19 and soldered a wire to pin 2 to bypass the problem with the connector so now I'm using exact your hardware setup.
For software I'm using your second code and deleted any reference to chip2 (I'm using just one chip).
This further modification will fix the problem and uses less code:

    while(digitalRead(2) == LOW){// ADDEDD
      if (expanderRead (chip1, INFTFA))
        keyValue1 |= expanderRead (chip1, INTCAPA) << 8;    // read value at time of interrupt
      if (expanderRead (chip1, INFTFB))
        keyValue1 |= expanderRead (chip1, INTCAPB);        // port B is in low-order byte
    }


Strangely, even this one!

     keyValue1 |= expanderRead (chip1, INTCAPA) << 8;    // read value at time of interrupt
      if (expanderRead (chip1, INFTFB))
     keyValue1 |= expanderRead (chip1, INTCAPB);        // port B is in low-order byte 
// ADDED, attemp to reset the interrupt again
expanderRead (chip1, INTCAPA)
expanderRead (chip1, INTCAPB);


Looks like I have to reset MCP interrupt 2 times!
Top

Posted by Nick Gammon   Australia  (23,102 posts)  Bio   Forum Administrator
Date Reply #17 on Sat 27 Aug 2011 11:10 PM (UTC)
Message
Hmm, strange.

You shouldn't need the while loop.

However I admit I tested with the B port initially and not the A one.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Greete   (16 posts)  Bio
Date Reply #18 on Wed 31 Aug 2011 10:18 PM (UTC)

Amended on Wed 31 Aug 2011 10:19 PM (UTC) by Greete

Message
I've made a simple experiment. I have added a parameter for check when the while loop is necessary.

    int count = 0;//just for test
    while(digitalRead(2) == LOW){// ADDEDD
      Serial.print ("count: ");//just for test
      Serial.println (count);//just for test
      if (expanderRead (chip1, INFTFA))
        keyValue1 |= expanderRead (chip1, INTCAPA) << 8;// read value at time of interrupt
      if (expanderRead (chip1, INFTFB))
        keyValue1 |= expanderRead (chip1, INTCAPB); // port B is in low-order byte
      count++;//just for test
    }


Most of the time I just get count: 0 but pressing many buttons at the same time will increase the count to 1 or 2!
Top

Posted by Nick Gammon   Australia  (23,102 posts)  Bio   Forum Administrator
Date Reply #19 on Thu 01 Sep 2011 01:47 AM (UTC)
Message
Try the amendment I suggested:


// Read port values, as required. Note that this re-arms the interrupts.
  if (expanderRead (INFTFA) || expanderRead (INFTFB))
    {
    keyValue |= expanderRead (INTCAPA) << 8;    // read value at time of interrupt
    keyValue |= expanderRead (INTCAPB);        // port B is in low-order byte
    }



- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Greete   (16 posts)  Bio
Date Reply #20 on Thu 01 Sep 2011 11:52 AM (UTC)

Amended on Fri 02 Sep 2011 10:08 AM (UTC) by Greete

Message
Hi Nick!
I have try the amendment you suggested but still have the freeze.
At this time the only way to avoid the freeze is the while loop, must be some timing problem of the mcp chip.
Notice that in normal situation nobody will press all those buttons but I'm trying to build a toy for my son of 4 years old so...

[edit]
the only difference with this code is I'm able to press 2 buttons in the same time but only if one is in the bank a and the other is in b.
The other code let me press just one button at time.
Top

Posted by Greete   (16 posts)  Bio
Date Reply #21 on Tue 10 Jan 2012 12:56 PM (UTC)

Amended on Tue 10 Jan 2012 04:04 PM (UTC) by Greete

Message
Hi Nick!
Always a big pleasure visit your site, it's always full of useful informations!

I'm trying to use your code for the MCP23017 for the SPI version of the same chip, the MCP23S17. Reading your notes about SPI I have modified like the following to use both chip for experiments:

#include <SPI.h>
#include "pins_arduino.h"

// Author: Nick Gammon
// Date: 20 February 2011

// Demonstration of an interrupt service routine connected to the MCP23017

#include <Wire.h>

// MCP23017 registers (everything except direction defaults to 0)
#define SS   53 //I'm using arduinoMega2560
#define IODIRA   0x00   // IO direction  (0 = output, 1 = input (Default))
#define IODIRB   0x01
#define IOPOLA   0x02   // IO polarity   (0 = normal, 1 = inverse)
#define IOPOLB   0x03
#define GPINTENA 0x04   // Interrupt on change (0 = disable, 1 = enable)
#define GPINTENB 0x05
#define DEFVALA  0x06   // Default comparison for interrupt on change (interrupts on opposite)
#define DEFVALB  0x07
#define INTCONA  0x08   // Interrupt control (0 = interrupt on change from previous, 1 = interrupt on change from DEFVAL)
#define INTCONB  0x09
#define IOCON    0x0A   // IO Configuration: bank/mirror/seqop/disslw/haen/odr/intpol/notimp
//#define IOCON 0x0B  // same as 0x0A
#define GPPUA    0x0C   // Pull-up resistor (0 = disabled, 1 = enabled)
#define GPPUB    0x0D
#define INFTFA   0x0E   // Interrupt flag (read only) : (0 = no interrupt, 1 = pin caused interrupt)
#define INFTFB   0x0F
#define INTCAPA  0x10   // Interrupt capture (read only) : value of GPIO at time of last interrupt
#define INTCAPB  0x11
#define GPIOA    0x12   // Port value. Write to change, read to obtain value
#define GPIOB    0x13
#define OLLATA   0x14   // Output latch. Write to latch output.
#define OLLATB   0x15

#define chip1 0x20  // MCP23017 is on I2C address 0x20


volatile boolean keyPressed;

// set register "reg" on expander to "data"
// for example, IO direction
void expanderWriteBoth (const byte address, const byte reg, const byte data ) 
{
  if (SS>0){
    digitalWrite(SS, LOW);
    SPI.transfer (reg);
    SPI.transfer (data);
    SPI.transfer (data);
    digitalWrite(SS, HIGH);
  } 
  else {
    Wire.beginTransmission (address);
    Wire.send (reg);
    Wire.send (data);  // port A
    Wire.send (data);  // port B
    Wire.endTransmission ();
  }
} // end of expanderWrite

// read a byte from the expander
unsigned int expanderRead (const byte address, const byte reg) 
{
  unsigned int data = 0;
  if (SS>0){
    digitalWrite(SS, LOW);
    SPI.transfer (reg);
    digitalWrite(SS, HIGH);
    digitalWrite(SS, LOW);
    data = SPI.transfer (0);
    digitalWrite(SS, HIGH);
  } 
  else {
    Wire.beginTransmission (address);
    Wire.send (reg);
    Wire.endTransmission ();
    Wire.requestFrom (address, (byte) 1);
    data = Wire.receive();
  }
  return data;
} // end of expanderRead

// interrupt service routine, called when pin D2 goes from 1 to 0
void keypress ()
{
  keyPressed = true;   // set flag so main loop knows
}  // end of keypress

void setup ()
{
  Serial.begin (38400); 
  if (SS>0){
    SPI.begin ();
    SPI.setClockDivider(SPI_CLOCK_DIV8);
    digitalWrite(SS, HIGH);
  } 
  else {
    Wire.begin ();
  }

  // expander configuration register
  expanderWriteBoth (chip1, IOCON, 0b01100100); // mirror interrupts, disable sequential mode, open drain

  // enable pull-up on switches
  expanderWriteBoth (chip1, GPPUA, 0xFF);   // pull-up resistor for switch - both ports

  // invert polarity
  expanderWriteBoth (chip1, IOPOLA, 0xFF);  // invert polarity of signal - both ports
 
  // enable all interrupts
  expanderWriteBoth (chip1, GPINTENA, 0xFF); // enable interrupts - both ports
 
  // no interrupt yet
  keyPressed = false;

  // read from interrupt capture ports to clear them
  expanderRead (chip1, INTCAPA);
  expanderRead (chip1, INTCAPB);

  // pin 19 of MCP23017 is plugged into D18 of the ArduinoMega which is interrupt 5
  pinMode (18, INPUT);      // make sure input
  digitalWrite (18, HIGH);  // enable pull-up as we have made the interrupt pins open drain

  attachInterrupt(5, keypress, FALLING);

}  // end of setup

void handleKeypress ()
{
  unsigned int keyValue1 = 0;

  delay (100);  // de-bounce before we re-enable interrupts

  keyPressed = false;

  // check first chip
  if (expanderRead (chip1, INFTFA))
    keyValue1 |= expanderRead (chip1, INTCAPA) << 8;    // read value at time of interrupt
  if (expanderRead (chip1, INFTFB))
    keyValue1 |= expanderRead (chip1, INTCAPB);        // port B is in low-order byte

  // show state of first 16 buttons
  for (byte button = 0; button < 16; button++)
  {
    // this key down?
    if (keyValue1 & (1 << button))
    {
      Serial.print ("Button ");
      Serial.print (button + 1, DEC);
      Serial.println (" now down");
    }  // end of if this bit changed

  } // end of for each button

}  // end of handleKeypress

volatile unsigned long i;

void loop ()
{

  // some important calculations here ...


  // was there an interrupt?
  if (keyPressed)
    handleKeypress ();

}  // end of loop


The I2C version works but the SPI not. I've changed some pin since I'm using an Mega2560 but I'm suspect that the SPI routine that read data is not correct, can you help me?
Top

Posted by Nick Gammon   Australia  (23,102 posts)  Bio   Forum Administrator
Date Reply #22 on Tue 10 Jan 2012 08:01 PM (UTC)
Message
Here:


    digitalWrite(SS, LOW);
    SPI.transfer (reg);
    digitalWrite(SS, HIGH);
    digitalWrite(SS, LOW);
    data = SPI.transfer (0);
    digitalWrite(SS, HIGH);


I wouldn't raise SS during a "transaction". Try:


    digitalWrite(SS, LOW);
    SPI.transfer (reg);
    data = SPI.transfer (0);
    digitalWrite(SS, HIGH);

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Greete   (16 posts)  Bio
Date Reply #23 on Wed 11 Jan 2012 01:25 PM (UTC)
Message
Thanks Nick!
Unfortunatly nothing works. I've added Serial.print ("Int Triggered!!!"); to keypress function. Pressing button has no result so it looks like the chip has not been correctly configured. I'll try to change chip and use an arduino uno, just to be sure that is not an hardware problem...
Top

Posted by Rossco   (5 posts)  Bio
Date Reply #24 on Thu 31 May 2012 08:19 PM (UTC)
Message
Hi Nick,

I have found your MCP23017 code most useful for a system I have been setting up. I am new to Arduino and the MCP23017 but have got a lot more from the centipede shield with your code than from the samples provided with the shield.

I hope you might help me understand one last issue I have with MCP chip features.

I had been recommended the chip for its ability to store pin status at the time of the interrupt. What I really need, and am now doubting is a capability, is to store multiple input pin states without reading the ports between inputs. Reading the databook again I am not sure that pin states continue to be stored if the interrupt is not cleared.

The implication was that I could get quick timing data from interrupts generated by the chip and read the ports later to find out with pin(s) had generated the interrupt.

The multiple inputs are caused by a ball projectile passing a photosensor array to start/stop a timer. I need to know where the centre of the ball passes the array and would need to know the 5 sensors its profile triggered to approximate. I only need to start the timer with the first sensor picked up by the mcp.

Do you see anyway to do this without extra hardware?

Any pointers would be most appreciated.

Kind regards,

Rossco

Top

Posted by Nick Gammon   Australia  (23,102 posts)  Bio   Forum Administrator
Date Reply #25 on Thu 31 May 2012 09:46 PM (UTC)
Message
Rossco said:

Reading the databook again I am not sure that pin states continue to be stored if the interrupt is not cleared.


The datasheet seems to say it copies the first state (current state). This is useful because by the time you service the interrupt the state might have changed.

The only solution I can think of is to handle the interrupt fast enough to get the readings from the "real" port (not the saved one). So the front of the ball triggers the interrupt, and then you read the other ones.

However assuming the front of the ball hits the sensor first, doesn't that tell you where the ball is?

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Rossco   (5 posts)  Bio
Date Reply #26 on Sat 02 Jun 2012 06:31 PM (UTC)
Message
Thanks for the reply. That clarifies that the MCP doesn't work quite the way I had hoped.

I just need to try this in practice I think. I get the stuff back next week. What you propose should work in theory; the the sensor triggering the interrupt is always the one at the centre of the ball and therefore I don't need the other readings. However, in doing this I must assume the arduino will never miss the first trigger. The aim is accuracy of 0.5 m/s which relates to mm and ms resolution - if budget were not an issue the arduino would not be the first choice.

In reality the arduino may well be fast and reliable enough to read and clear the interrupt state within the ISR and still catch the leading edge of the ball every time.

Thanks again Nick, great site.

Ross
Top

Posted by Rossco   (5 posts)  Bio
Date Reply #27 on Mon 09 Jul 2012 08:04 PM (UTC)
Message
Hi Nick,

I now have the ball measurement system close to working. As your logic analyser showed, the mcp reacts very quickly, so I am for now assuming the bit set in INTCAP is first sensor to be triggered by the leading edge of the ball.

When I read the results from INTCAP following one pass of the ball through the rig, the values seem to match up. However, on the next pass the INTCAP values seem to retain the previous values as well as new ones. At the moment the only way to clear INTCAP is force a hard reset of the MCP by disconnecting power.

Can you see any reason why this would happen? reading INTCAP clears the interrupt as the datasheet states, but for some reason INTCAP maintians high bits GPIOs that were not triggered. Do I need to read INTFA/B as well to ensure the INTCAP values are true each time?

I have ensured all programme variables are cleared, so it does seem to be INTCAP itself causing the problem.

Many thanks,

Rossco
Top

Posted by Nick Gammon   Australia  (23,102 posts)  Bio   Forum Administrator
Date Reply #28 on Tue 10 Jul 2012 01:19 AM (UTC)
Message
The datasheet says INTCAP is cleared by reading from it, but why not just check INTF? That tells you which pins caused an interrupt, and INTCAP tells you the state at the time of the interrupt.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Rossco   (5 posts)  Bio
Date Reply #29 on Mon 03 Sep 2012 10:53 AM (UTC)
Message
Hi Nick,

Working on our MCP system sporadically, but think we are progressively closer to a fully functioning system. Your suggestion to use INTF instead of INTCAP seemed to do the trick in our simultations. However, we are still having some issues with the MCP registers when testing with a real ball.

When we hook up our reversed biased photodiode divder circuits (no amplifier, output taken across a single resistor) to a DAQ we are measuring expected results for the sensor outputs when a ball passes. They at least working on a comercially developed unit.

When we simulate 5 simultaneous inputs to the MCP using an arduino as a pulse generator (pull down resistors used for uconnected GPIOS), INTF/INTCAP indentify the correct triggers. However when we hook up the diode sensors to the MCP chips we get funny and unexpected results from INTF e.g. bits set are not adjacent and do not represent the ball diameter and what we are reading on the DAQ. It seems there are mis triggers.

The ball is fired at around 30 m/s through the sensor arrays. Sensors are therefore triggered for roughly 30 ms, with a slight delay between each pulse as dictatated by the ball edge profile. I wouldn't have thought this would be a problem for the MCP given the 125 ns response. Do think ground bounce or other crosstalk/noise could be the issue here? Any other ideas of what to try?

Microchip have suggested that increasing I2C speed may help. We have tried this without success, but I would have thought lower speeds would be less problematic.

Thanks,

Ross
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


170,246 views.

This is page 2, subject is 3 pages long:  [Previous page]  1  2 3  [Next page]

Postings by administrators only.

Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.