I have an old handheld transceiver that stores information (tx/rx frequencies, tx power levels, sub-audible tone frequencies, etc) on an eeprom. I don't have a programmer for this radio so my thought was to just read and write to the eeprom directly rather than going through a programmer. Before I start trying to read and write the eeprom I want to listen to the data going back and forth between the eeprom and the transceiver's controller uC. To do this I wrote an arduino sketch that uses interrupts on the scl and sda lines of the eeprom to determine whether there is a start condition, a stop condition, a data bit, or an ACK. I have attached the sketch below. My logic is as follows:
-
wait for a rising edge on SCL or a change on SDA
-
if SDA changes while SCL is high then we have a start or stop condition (start if SDA is pulled low and stop if SDA is high)
-
if we got a rising edge on SCL then read the SDA line and then wait for one of two things to happen. We know that SCL is high so we watch SDA to see if it changes and we watch SCL to see if we get a falling edge. If SDA changes before we get a falling edge on SCL then we have a start or stop condition that we can evaluate. If there is a falling edge on SCL first then it means that we have a bit of data ready to take in.
When I run this sketch I just get a series of 'start' indications with no data to follow. I know that there is data passing between the two devices that I am monitoring because I set up another Arduino with a sketch that just reads data from a spare eeprom that I am testing this all with. I can monitor that one through the serial port and see that it is indeed reading data from the eeprom.
Can anyone see an issue with my logic? I have two sketches posted. The first is the Arduino that is communicating directly with the eeprom. The second sketch is for the arduino that is snooping on the first one.
#include <Wire.h>
void setup()
{
//byte chanOne[] = {0x01,0x0B};
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
Wire.beginTransmission(0x50);
Wire.write(0x00); //write word address. This is where we will begin reading from
Wire.endTransmission(false);
}
void loop()
{
for(int x=0;x<512;x++)
{
Wire.requestFrom(0x50, 1); // request 1 byte from slave device #2
while (Wire.available()) // slave may send less than requested
{
char c = Wire.read(); // receive a byte as character
Serial.print(c, HEX); // print the character
}
Serial.println();
//while(1)
}
delay(1500);
}
int sda = 2;
int scl = 3;
volatile byte i = 0;
volatile byte dataBit = 0;
volatile byte dataByte = 0;
volatile boolean start = false;
volatile boolean _stop = false;
volatile boolean bit_ready = false;
void setup() {
Serial.begin(115200);
// put your setup code here, to run once:
pinMode(scl, INPUT);
pinMode(sda, INPUT);
//I'm not sure if I am waiting for SCL to rise or SDA to change
//so I'll just watch for both of them.
attachInterrupt(digitalPinToInterrupt(scl),scl_rising, RISING);
attachInterrupt(digitalPinToInterrupt(sda),sda_change, CHANGE);
}
void loop() {
while(!bit_ready) {} //Just sit here until we know that we have a bit
//or a 'start' or 'stop' condition
if(start)
{
Serial.println("*"); //we'll call this the start symbol
start = false;
//reset our variables
dataBit = 0;
dataByte = 0;
}
else if(_stop)
{
Serial.println("**"); //we'll call this the stop symbol
_stop = false;
}
else
{
//I need to 'or' the databit with my databyte
Serial.print("h"); //inserted for debugging....but we never get here
dataByte = (dataByte << 1) | dataBit;
i++; //counter to keep track of the full byte
if(i==7)
{
Serial.println(dataByte,HEX);
dataByte = 0;
}
if(i == 8)
{
//see if we get an acknowledge
if(dataByte) Serial.println("ACK");
dataByte = 0;
i=0;
}
}
bit_ready = false;
}
void scl_rising()
{
//data MIGHT be valid so read the sda line into data variable
//enable sda change interrupt to monitor for start and stop condition
//enable sclk_falling interrupt. We wait until sclk comes back down
//before doing anything so that we can catch 'start' and 'stop' conditions
detachInterrupt(digitalPinToInterrupt(scl));
detachInterrupt(digitalPinToInterrupt(sda));
dataBit = digitalRead(sda);
//so here we are waiting to see what happens first. If SDA changes first
//then we have a start or stop condition. If SCL falls first then
//we have a data bit ready to go. Whichever one fires first, the interrupt
//handler will disable the other one to avoid having them both fire.
attachInterrupt(digitalPinToInterrupt(sda), sda_change, CHANGE);
attachInterrupt(digitalPinToInterrupt(scl), scl_falling, FALLING);
}
void scl_falling()
{
//sda didn't change while sckl was high so we know it wasn't a start
//or stop condition. First disable sda_change and re-enable sclk_rising
//for the next bit of data or next start condition. Assuming we don't have
//a start condition we can set bit_ready to true and add the bit to our byte
//of data
detachInterrupt(digitalPinToInterrupt(sda));
detachInterrupt(digitalPinToInterrupt(scl));
bit_ready = true; //no start or stop signal so this is actual data
//I don't need to attach the sda_change interrupt because the clock
//has to rise before a start/stop/or data transfer is sent
attachInterrupt(digitalPinToInterrupt(scl),scl_rising,RISING);
}
void sda_change()
{
//if sda is low then we have a start condition. Set all of our variables
//to zero so that we can start receiving data
//if sda is high then we have a stop condition
//SDA changed before SCL had a falling edge
detachInterrupt(digitalPinToInterrupt(scl)); //we aren't interested in tracking
detachInterrupt(digitalPinToInterrupt(sda));
if(digitalRead(sda)) //the falling edge now
{
_stop = true; //SDA was released while SCL was high which is a stop
bit_ready = true;
//wait for the next start condition by monitoring the SCL and SDA
attachInterrupt(digitalPinToInterrupt(scl),scl_rising,RISING);
attachInterrupt(digitalPinToInterrupt(sda),sda_change,CHANGE);
}
else
{
start = true; //SDA was pulled low while SCL was high indicating a start
bit_ready = true;
//wait for the next rising edge on SCL
attachInterrupt(digitalPinToInterrupt(scl),scl_rising,RISING);
}
}