Support Forum

Share your projects and post your questions

Register   or   Sign In
The Forum

IO Pi Plus and GPIO Interrupt

The IO Pi Plus is a 32 channel MCP23017 GPIO expander for the Raspberry Pi

11/10/2017

Posted by:
OliverRasp

OliverRasp Avatar

Hi,

I try to use 8 buttons (at the end there will be 24) on the IO Pi Plus bus1. I dont like to poll the button pins each 200ms, so I connected the IA and IB to the GPIO Pin 23 and 24 of the Raspberry. To divide the voltage I'm using this level shifter: XCSOURCE® 5STK IIC I2C

The code is written in C++ including the wiringPi library. The demo program works for just 3 to 5 interrupts, after that the program crashes.

If I'm using INT_EDGE_FALLING as signal to call my callback function on the wiringPi library I will get this error message:

terminate called after throwing an instance of 'std::runtime_error'
what(): Failed to read from slave
Aborted

If I'm using INT_EDGE_BOTH as signal to call my callback function on the wiringPi library I will get this error message:
terminate called after throwing an instance of 'std::runtime_error'
what(): Failed to write to i2c device for read
Aborted


At the end I need this type of signal watching: INT_EDGE_BOTH, because I need a callback for button down and button up.

Find the Code attached. Has any one some idea, what I'm doing wrong?

Best regards

Oliver




#include 
#include
#include
#include
#include
#include
#include

#include
#include "ABE_IoPi.h"


using namespace std;

using namespace ABElectronics_CPP_Libraries;


IoPi *bus1;


void printPressedButtonNumber()
{
printf("%d\n", bus1->read_interrupt_capture(0));
printf("%d\n", bus1->read_interrupt_capture(1));
}


void interruptCallback()
{
cout set_port_direction(0, 0xFF); // set bank 0 to be inputs
bus1->set_port_direction(1, 0xFF); // set bank 1 to be inputs


/*
Normally you would need to use a resistor to act as a pull-up so there is a
constant 5V supply to the pin when the button is not pressed but luckily the
IO Pi comes with pull-up resistors inside of the chip which you can enable
using the following method.
*/
bus1->set_port_pullups(0, 0xFF); // disable internal pullups for port 0
bus1->set_port_pullups(1, 0xFF); // disable internal pullups for port 1

/*
One downside of using a pull-up resistor rather than a pull-down is that
when the button is not pressed the IO Pi will always read the pin as active,
so it would read 1 or on when the button is off and 0 or on when the button
is pressed, which is backwards to what you would normally expect from a switch.
This can be easily fixed with another useful method called invert_pin.

invert_pin does exactly what it says, it inverts the reading on an input pin
so 1 becomes 0 and 0 becomes 1 and everything starts to look how it should
again. The method uses the same variables as setPinPullup with the first
setting the pin to invert and the second enables or disables the inverter
with 1 or 0.
*/
bus1->invert_port(0, 0xFF);
bus1->invert_port(1, 0xFF);

// Set the interrupt polarity to be active high and mirroring disabled, so
// pins 1 to 8 trigger INT A and pins 9 to 16 trigger INT B
bus1->set_interrupt_polarity(0);
bus1->mirror_interrupts(1);

// Set the interrupts default value to trigger when 5V is applied to any pin
bus1->set_interrupt_defaults(0, 0x00); // 0xFF);
bus1->set_interrupt_defaults(1, 0x00); // 0xFF);

// Set the interrupt type to be 1 for ports A and B so an interrupt is
// fired when the pin matches the default value
bus1->set_interrupt_type(0, 0xFF);
bus1->set_interrupt_type(1, 0xFF);

// Enable interrupts for pins 1 to 16
bus1->set_interrupt_on_port(0, 0xFF);
bus1->set_interrupt_on_port(1, 0xFF);

bus1->reset_interrupts();


// sets up the wiringPi library
// http://www.science.smith.edu/dftwiki/index.php/Tutorial:_Interrupt-Driven_Event-Counter_on_the_Raspberry_Pi
if (wiringPiSetupGpio () < 0)
{
// http://www.fayewilliams.com/2011/10/19/using-errno/
fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno));
return 1;
}

pinMode(23, INPUT); // Set button as INPUT
pullUpDnControl(23, PUD_UP); // PUD_UP); // PUD_DOWN); // Enable pull-up resistor on button

// set Pin xyz to generate an interrupt on high-to-low transitions
// and attach callback function to the interrupt
// INT_EDGE_FALLING, INT_EDGE_RISING, INT_EDGE_BOTH
if ( wiringPiISR (23, INT_EDGE_FALLING, &interruptCallback) < 0 )
{
cout << "Unable to setup ISR" << endl;
fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno));
return 1;
}


pinMode(24, INPUT); // Set button as INPUT
pullUpDnControl(24, PUD_DOWN); // PUD_UP); // PUD_DOWN); // Enable pull-up resistor on button

// set Pin xyz to generate an interrupt on high-to-low transitions
// and attach callback function to the interrupt
if ( wiringPiISR (24, INT_EDGE_FALLING, &interruptCallback) < 0 )
{
cout << "Unable to setup ISR" << endl;
fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno));
return 1;
}


cout << "Press 'ESC' to Exit" << endl;

const static char ESC = 0x1B;
char c = 0;

do
{
c = getchar();
} while(c != ESC);


}
catch (exception &e)
{
cout << e.what();
}

delete bus1;

return (0);
}

11/10/2017

Posted by:
andrew

andrew Avatar

Hi Oliver

The error messages "Failed to write to i2c device for read" and "Failed to read from slave" are from the IO Pi library read_byte_data() function when it tries to do a read or write to the i2c port and it can not access it for some reason. Do you have any other I2C devices connected to the same Raspberry Pi which could be trying to use the I2C port at the same time? If not then it could possibly be a conflict between the wiring pi library and are used in the IO Pi library for I2C communication.

It may be worth posting the problem you are getting in the C++ forum on RaspberryPi.org as there will hopefully be more people there who have experienced the same issues before.

12/10/2017

Posted by:
OliverRasp

OliverRasp Avatar

Hello Andrew,

thank you for your reply. Yes there is another working i2c component. I'm using a MCP23017 as analog to digital converter for 2 potentiometer. It's connected to a GPIO of the Raspberry. Could this be the problem? Could this be solved?

Best regards

Oliver

12/10/2017

Posted by:
andrew

andrew Avatar

Would it be possible to combine the code for your ADC with the IO Pi code so you can manage when both devices are accessing the I2C port?

When a program accesses the I2C port it opens a connection, does the read or write and then closes the connection. While the connection is open, access to the port is blocked to any other program that tries to use it and the second program will throw an error. By moving all of the I2C code on the Raspberry Pi into a single program you can control when the port is being accessed to make sure both devices don't try to open a connection at the same time.

12/10/2017

Posted by:
OliverRasp

OliverRasp Avatar

Now I'm at home, and had a closer look at my Raspberry. The breadboard containing the potentiometer and the MCP23017 was not connected. So the MCP23017 could not be the Problem. But what is the idea behind "combine the code for your ADC with the IO Pi code"?

About the current Problem, do you have an alternative library instead of wiring pi library do get rid of the <sys/ioctl.h> and <linux/i2c-dev.h> conflict?

12/10/2017

Posted by:
andrew

andrew Avatar

The idea of combing the code was so that if the ADC was accessing the I2C port alongside the IO Pi you could make sure only one of them writes to the bus at any moment but as the device wasn't connected I don't think that could be the problem.

The only other thing I can think of which could cause the issue is if the interruptCallback() function is triggering more than once in quick succession for some reason then it could try to call the read_interrupt_capture() functions a second time before the first time finishes causing the i2c error.

There is a wiring pi module for the MCP23017 which we use on the IO Pi Plus so it may be possible to use that instead of our library which would mean all GPIO and I2C access is then done through wiring pi.

My knowledge of C++ on the Raspberry Pi is a bit limited, I normally use Python for Raspberry Pi development and I only learned how to program in C++ when a customer asked for a C++ library for our boards so you may have more luck with the wiring pi side of your code at the Raspberry Pi forums where they have more experience with the language.

15/10/2017

Posted by:
OliverRasp

OliverRasp Avatar

I found a workaround. The problem is a method reentrance, so using a lock flag helped so far.


bool interruptIsProccessing = false;

void printPressedButtonNumber()
{
printf("%d\n", bus1->read_interrupt_capture(0));
printf("%d\n", bus1->read_interrupt_capture(1));
}

void interruptCallback()
{
if(interruptIsProccessing) {
return;
}

interruptIsProccessing = true;
cout << "interruptCallback" << endl;

try
{
printPressedButtonNumber();
}
catch (exception &e)
{
cout << "interruptCallback-Exception: " << e.what();
}

interruptIsProccessing = false;
}


15/10/2017

Posted by:
OliverRasp

OliverRasp Avatar

There is still one Problem I came accross. The interrupt fires as long I'm pressing the button. But I only want to have one interrupt by button down.

Is there a way to get another interrupt on button up? This is not working:
if ( wiringPiISR (23, INT_EDGE_BOTH, &interruptCallback)

Using this at buttons on the raspberry pi gpios it works. How would I determine when the button is released by using the io pi plus?

My project will be a electronic music instrument, so I need to play a sound as long as the button is pressed and had to stop when the button is released.

15/10/2017

Posted by:
andrew

andrew Avatar

The IO Pi library contains a function called set_interrupt_type(port, value) which allows you to set the interrupts to fire on state change.

The parameters for the function are:

port - 0 = pins 1 to 8, port 1 = pins 9 to 16
value - 1 = interrupt is fired when the pin matches the default value, 0 = the interrupt is fired on state change

If you change the two lines in your code which call the function to the following it should then fire the interrupts every time a button is pressed or released.


bus1->set_interrupt_type(0, 0x00);
bus1->set_interrupt_type(1, 0x00);

16/10/2017

Posted by:
OliverRasp

OliverRasp Avatar

Work like a charm!

Thank you a lot for your support :-)

01/07/2018

Posted by:
OliverRasp

OliverRasp Avatar

Hi, paused the project till now. I rewired the components to the new Raspberry Pi 3 Modell B+ (brand new model). So the IO Pi Plus is stacked on the Pi. I connected 16 Buttons to the Bus1 (original setup from months ago). Because the level shifter was removed I had to rewire it. I'm using 5V and ground from Bus1 and 3V and Ground from the 40 pins of the IO Pi Plus (Raspberry Socket). When powering the Pi, it wount boot up. If i remove one of the voltage wires, the Pi boots up as suggested. I'm using the original PowerSupply form my previous Raspberry Pi 3.

Do's some one have any idea?

01/07/2018

Posted by:
andrew

andrew Avatar

It may be worth using a multimeter to check the 5V and 3.3V pins to make sure they are not shorted to ground as this would stop the Pi from booting.

When you rewired the level shifter did you replace the MOSFET on the IO Pi? If so then it may be worth checking you put it back the correct way around otherwise that could cause problems with the I2C pins on the Pi which could also stop the Pi from booting. The MOSFET should be oriented so the .3 is nearest the white line on the left of the board. Also, make sure there are no solder bridges between the MOSFET pins.

You could also try using the 5V from the Raspberry Pis GPIO header instead of from Bus 1.

06/07/2018

Posted by:
OliverRasp

OliverRasp Avatar

It's an issue of the Raspberry Pi 3 Modell B+. I changed the setup back to the old Raspberry Pi 3 Modell and then Raspberry boot up when the level shifter voltage pins are connected (5V and 3.3V).
Do you have any idea what to change at the Raspberry Pi 3 Modell B+? The problems occures also when I remove the IO Pi and connect the level shifter 5V and 3.3V to the Raspberry Pi pins only. The old Model works, the new one do's not.

06/07/2018

Posted by:
andrew

andrew Avatar

As far as I know the 3 and 3 B+ work the same on the I2C bus so I am not sure why it would work on the 3 and not the B+. Are you using the same SD card in both Pis or do you have a separate one set up for the B+? If you have a different SD card then it may be worth checking that I2C is configured the same on both cards.

06/07/2018

Posted by:
OliverRasp

OliverRasp Avatar

Yes, I'm using different SD cards. I will check the I2C configuration again. But the problem also happens without the IO Pi HAT but wired level shifter (5v 3.3v gnd). I also checked the power supply, but it should be ok (original Raspberry Pi Powersupply 5.1V 2.5A).

06/07/2018

Posted by:
andrew

andrew Avatar

The 3B+ uses the same processor as the 3B so they should be identical when it comes to I2C communication. The only main difference is the extra metal shield on the 3B+ next to the 3.3V and I2C pins which covers up the Wifi circuit. Is there any way your IO Pi or level shifter could be shorting out to the metal shield as that would stop the Pi from working?

The only other thing I can think of is there is a fault with the GPIO header on your 3B+.

Sign in to post your reply


Note: documents in Portable Document Format (PDF) require Adobe Acrobat Reader 5.0 or higher to view.
Download Adobe Acrobat Reader or other PDF reading software for your computer or mobile device.

Home

Shop

Learn

Forum

FAQ

Contact

0 item

Your cart is empty

Please browse our shop to order from the wide range of Raspberry Pi boards and accessories.

Subtotal:£0.00
View Basket Continue to Checkout