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
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 <stdio.h>
#include <stdexcept>
#include <cerrno>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <iostream>
#include <wiringPi.h>
#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 << "interruptCallback" << endl;
printPressedButtonNumber();
}
int main(int argc, char **argv)
{
setvbuf(stdout, NULL, _IONBF, 0); // needed to print to the command line
try
{
bus1 = new IoPi(0x20);
// initialise one of the io pi buses on i2c address default address for bus 1
bus1->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
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
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
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
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
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. I haven't used their MCP23017 module so I am not sure how it works but there is some information on their website at i2c-mcp23008-mcp23017
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
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
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
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
Thank you a lot for your support :-)
01/07/2018
Posted by:
OliverRasp
Do's some one have any idea?
01/07/2018
Posted by:
andrew
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
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
06/07/2018
Posted by:
OliverRasp
06/07/2018
Posted by:
andrew
The only other thing I can think of is there is a fault with the GPIO header on your 3B+.
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.