I'm trying to connect an uno to a Pi with the Uno as an i2c slave to the pi, however the arduino runs a wide variety of interrupts alongside it's i2c interrupt.
If any of these interrupts (ADC_vect for a freeer running analog conversion and a falling edge interrupt opn pin 2) is in progress when the Pi tries to contact the uno the Pi ends up getting a garbage response (every byte of i2c callback it receives gets values of 255) and any later attempts by the Pi to send i2c commands or receive i2c callbacks from the Uno are junk too. If an i2c check ( i2cdetect -y 1 ) is run on the Pi at this point it is like the arduino no longer exists, the Un's i2c address is not shown. This sorry state of affairs can only be fixed by pressing the arduino's reset switch.
I've worked out that my solution is to have a short section in the main loop of the arduino code during which all other (ADC_vect and pin2) interrupts except the i2c one are disabled, and that so long as the Pi only tries to send and receive i2c from the Uno when the uno is in this bit of the code I don't get the crash. But for the Pi to know when it is safe to send an i2c message I need another wire from the Uno to the Pi, the Uno can send this wire low when it is ok for the Pi to i2c it and leave it high at all other times. I am not bothered about the fact that the Pi may sometimes have to not send an i2c message if it wants to send but finds that, at the time it tries, the other wire is high (the uno saying "you can't i2c me at present"), I don't mind some i2c messages being lost or ignored so long as I don't get a crash of my i2c system and don't get any garbage data being sent across and believed true by whichever of the pi or uno is receiving it.
But in this situation the Pi must check that one of it's input pins is low before it sends an i2c message, and it mustn't pause for long between the check and the sending of the i2c. So I wanted to know how I can get the Pi to do some kind of atomic block which will encompass both the checking of a GPIO input and then if the input is low the Pi should do it's i2c stuff IMMEDIATELY. Can you suggest how this might be done?
I'm using C on the Pi, and arduino language (with some low level AVR C code instructions) on the Uno. I've included some sections of my code below, this code just shows the i2c stuff not the ADC_vect, the pin 2 interrupt or the output(on uno)/input(on pi) pin used to signal when it is safe for the pi to send an i2c message and callback request:
On the Pi I have some code which include3s the below as it's key i2c elements
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
// The I2C bus: This is for V2 pi's. For V1 Model B you need i2c-0
static const char *devName = "/dev/i2c-1";
unsigned char ByteMessage[31]; //the array IntegerMessage are the numbers we'll send to the arduino
unsigned char ReplyVals[31];
char ADDRESS=0x04;
//various code omitted here not relevant to this specific question.......
//This next bit ensures that there is an I2C device ready to connect to
printf("Text entered\n");
printf("I2C: Connecting\n");
printf("Will send:");
for(unsigned char j=0; j<31 ;j++){
printf("%d ,", ByteMessage[j]);
}
printf("\n");
int file;
if ((file = open(devName, O_RDWR)) < 0) {
fprintf(stderr, "I2C: Failed to access %d\n", devName);
exit(1);
}
printf("I2C: acquiring buss to 0x%x\n", ADDRESS);
if (ioctl(file, I2C_SLAVE, ADDRESS) < 0) {
fprintf(stderr, "I2C: Failed to acquire bus access/talk to slave 0x%x\n", ADDRESS);
exit(1);
}
//Now the sending of data happens
//this is where I'm going to want to put the checking of the GPIO input and an if loop which only lets the next stuff happen should the gpio pin be low
write(file, ByteMessage, 31); //writes to the "file" of the i2c bus, cmd is the buffer, the third argument is the number of bytes to write
usleep(100);
unsigned char buf[31]; //increase the 4 if more bytes are being sent
read(file, buf, 31); //the 31 says how many bytes to read
for(unsigned char j=0; j<31 ;j++){
ReplyVals[j] = buf[j];
}
printf("Received: ");
for(unsigned char j=0; j<31 ;j++){
printf("%d ,", ReplyVals[j]);
}
printf("\n");
usleep(10000);
close(file);
On the arduino the important stuff is based around the code as follows:
#include <Wire.h>
//this address must match the one that the pi is sending to
#define SLAVE_ADDRESS 0x04
byte numberss[31];
byte i2cBuffer[31];
//various irrelvant stuff omitted here
void setup() {
//more various stuff omitted
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onReceive(receiveData);
Wire.onRequest(sendData);
}
//main loop function omitted
// callback for received data
void receiveData(int byteCount){
EIFR=1;
while(Wire.available()) {
for(byte i=0; i<31;i++){
numberss[i]=Wire.read();
}
}
}
// callback for sending data
void sendData(){
EIFR=1;//prevents triggering of any pin 2 interrupts and ensures they won't happen straight after this ISR is finished
Wire.write(i2cBuffer,31); //only one wire.write is allowed in a request callback, 32 bytes is the max that can be sent, the 32 here says how many bytes to send
//Wire.write(ReplyNumberBeta);
//Wire.write(ReplyNumberGamma);
//Wire.write(ReplyNumberDelta);
}
Thanks