Weird result with a 3-wire interface

I have been trying to get the DS1302 (Data Sheet) to work with the arduino, but I kept getting weird results when getting and setting time with the device. To identify the problem, I wrote up a sketch to write a number to the RTC's RAM, then read that value back. It worked for a few values, then I started to get discrepancies between what I was trying to write and what was being read back. Here's the code, and the Serial print is attached as a csv if you want to view that. Sorry about the crazy comments; it was my way of diagnosing the issue by breaking everything down for myself.

//DEFINE PINS
//IO enable line
#define CE 2
//IO line; both recieve and transmit for data
#define IO 3
//Clock line
#define SCLK 4

void setup(){
//setup the pinmodes and serial
Serial.begin(9600);
pinMode(SCLK, OUTPUT);
pinMode(CE, OUTPUT);

//Set the write enable bit to zero (enable)
digitalWrite(CE,HIGH);
out((byte)0x8E);
out((byte)0);
digitalWrite(CE,LOW);
}

// A counter value used to test RAM IO
int counter = 0;

void loop(){

delay(100);

//Tell Arduino to write the counter value to the first register in RAM
//Enable the IO line
digitalWrite(CE,HIGH);
//Tell Arduino which register to write to
out((byte)0xC0);
//Tell Arduino what to write
out((byte)counter);
//Disable the IO line
digitalWrite(CE,LOW);

delay(100);

//Tell Arduino to read the value of the first register in RAM
//Enable the IO line
digitalWrite(CE,HIGH);
//Tell Arduino which register to read from
out((byte)0xC1);
//Read the RAM output
byte input = in();
//Disable the IO line
digitalWrite(CE,LOW);

delay(100);

//Print out the value recieved from RAM on a new line
Serial.println(input);

delay(100);

//Increment the counter
counter++;

delay(100);
}

//Wtite a byte to the RTC, set CE first
void out(byte data){

for(int i=0;i<8;i++){
pinMode(IO,OUTPUT);
//Write the i^th bit of the data byte to the IO line
digitalWrite(IO,bitRead(data,i));
delayMicroseconds(1);
//Pulse the clock
digitalWrite(SCLK, HIGH);
delayMicroseconds(1);
digitalWrite(SCLK, LOW);
delayMicroseconds(1);
}
}

//read the value output by the RTC
byte in(){
//The value to return
int value=0;

for(int i=0;i<8;i++){
pinMode(IO,INPUT);
//Store the value obtained by reading the IO line
int temp = (int)digitalRead(IO);
delayMicroseconds(1);
//Pulse the clock
digitalWrite(SCLK,HIGH);
delayMicroseconds(1);
digitalWrite(SCLK,LOW);
delayMicroseconds(1);
//Binary to decimal conversion by multiplying 2^i by the IO value
value += temp*(int)pow(2.0, (float)i);
}
return (byte)value;
}

What am I doing wrong? I'm almost certain that it's a code issue because I'm not very familiar with C/C++.

data.txt (4.2 KB)

This could easily be the problem...

value += temp*(int)pow(2.0, (float)i);

Use a bitwise-left-shift (and bitwise-or) instead...

if ( temp )
{
  value |= (1 << i );
}

pow is not guaranteed to produce correct results for integer results, since it
can use logarithms internally... So inefficient compared to integer methods too.

Thank you so much, I was super confused as to why this wasn't working

The pinMode() function is sticky. Once it is set it remains set until you change it. You should move them out of your loops to speed up the I/O a bit.

No need to add an extra microsecond of delay after setting SCLK low. There will be plenty of time between setting the pin LOW and the next time it gets set HIGH. You can probably also take out the "delayMicroseconds(1)" between setting it HIGH and setting it LOW. The functions will take enough time by themselves.

digitalRead() returns an int so no need to cast it to int.