Need help writing to/reading from I2C EEPROM chip

Started by rhese December 28, 2010
Hi all,

I'm working on a data logger project using the BX-24 and a Microchip 24AA1025 1024K I2C Serial EEPROM. Communicating via I2C is totally new to me, so I don't doubt that I'm messing something up here. I'm using code from M24LC256.bas, which I have tried to modify to match the chip I'm using. The problem is, I don't get the same data out that I'm putting in. For example, if the following values are read into the EEPROM:
1, 840, 17, 32
1, 841, 26, 30
1, 842, 26, 30
1, 843, 19, 39
1, 844, 18, 29
...I get this back out:
1, 771, 18, 29
1, 8195, 0, 29
1, 7683, 0, 29
1, 7683, 0, 29
1, 9987, 0, 29
1

Below is my code. I didn't include all code, just the parts relevant to reading & writing data to the EEPROM chip. On the EEPROM chip itself, I've got pins 1, 2, & 3 tied together and to +5V, pin 4 to ground, pin 5 & 6 have 2k Ohm resistors tying them to +5V and they also connect to pins on the BX-24. Pin 7 goes to ground & pin 8 goes to +5V.

Can anyone point to what I'm doing wrong? Thank you for your time.

'************************
Public EEPROM_Pointer As New UnsignedLong
Dim ADCdata(0 to 3) As Integer
Dim Current_EEPROM_Address As Long
Dim intDay As Integer
Dim intMinute As Integer
Dim lngTick As Long
Dim intTemp As Integer
Dim intRH As Integer
Dim Stopwatch As Single

Const Sample_Rate As Single = 60.0 '(1 minute)
Const EEPROM_Start_Address As Long = 0
Const EEPROM_End_Address As Long = &h1ffff '131071 (I hope)
'************************
Sub Log_Data()
Dim I As Integer

Call putPin(Logging_LED_Pin, 1) 'Turn on the Data Logging LED
Call Init_eeprom() 'Wake up the EEPROM chip
EEPROM_Pointer = CuLng(EEPROM_Start_Address)

Do Until GetPin(Logging_Switch_Pin) = 0
'At the period of the sample rate, record the elapsed days & minutes
If (Timer - Stopwatch) >= Sample_Rate Then
'Code to sample variables intDay, intMinute, intTemp, & intRH goes here
'...

'Write the Days, Minutes, Temperature, and RH to the EEPROM chip
ADCdata(0) = intDay
ADCdata(1) = intMinute
ADCdata(2) = intTemp
ADCdata(3) = intRH
Call write_ram_to_eeprom(MemAddress(ADCdata), 8, 7, EEPROM_Pointer)
EEPROM_Pointer = EEPROM_Pointer + 8

'Check to see if we've crossed the EEPROM chip's memory boundaries
If (EEPROM_Pointer > &hfffd&) AND (EEPROM_Pointer < &h10000) Then
EEPROM_Pointer = CuLng(&h10000)
End If

Current_EEPROM_Address = CLng(EEPROM_Pointer)

'Check to see if we are out of storage space
If Current_EEPROM_Address > EEPROM_End_Address Then
'Light LED 4 and Trap the program here
Call putPin(Attention_LED_Pin, 1)
Call putPin(Logging_LED_Pin, 0)
Do
Call Sleep(256.0)
Loop
End If

'Wait until the next sample period
Stopwatch = Timer
End If
Loop

'Turn off the Data Logging LED
Call putPin(Logging_LED_Pin, 0)

End Sub
'************************
Sub Upload_Data()
Dim I As Integer
Dim intRAM As Integer
I = 0

'Wait for the serial hardware to stabilize
Call Sleep(8.0)
Call putPin(Upload_LED_Pin, 1) 'Turn on the Uploading Data LED
Call Init_eeprom() 'Wake up the EEPROM chip
'Get the starting & ending EEPROM address for the logged data
EEPROM_Pointer = CuLng(EEPROM_Start_Address)
If Current_EEPROM_Address = EEPROM_Start_Address Then
Current_EEPROM_Address = EEPROM_End_Address
End If

'Covert and print all EEPROM data from the starting to ending address
Do Until EEPROM_Pointer > CuLng(Current_EEPROM_Address)
'Read the two bytes that make up each integer from next EEPROM address
intRAM = MemAddress(ADCData)+I
Call read_eeprom_into_ram(intRAM, 2, 7, EEPROM_Pointer)
debug.print CStr(ADCData(I))
'Increment EEPROM index to the next stored integer
EEPROM_Pointer = EEPROM_Pointer + 2

'Check to see if we've crossed the EEPROM chip's memory boundaries
If (EEPROM_Pointer > &hfffd&) AND (EEPROM_Pointer < &h10000) Then
EEPROM_Pointer = CuLng(&h10000)
End If

I = I + 1
'Once all four data integers (per data line) are printed do a carriage return
If I = 4 Then
I = 0
End If
Loop

debug.print "** End of File **"
'Reset the EEPROM address pointer & warning LED if it was on
Current_EEPROM_Address = EEPROM_Start_Address
If GetPin(Attention_LED_Pin) = 1 Then
Call putPin(Attention_LED_Pin,0)
End If

'Turn off the Uploading Data LED
Call putPin(Upload_LED_Pin, 0)

End Sub
'************************
'I2C_include.bas, based on the file M24LC256.bas
'I2C subroutines, specifically for reading & writing to Microchip 24AA1025 I2C Serial EEPROM

Option Explicit
Private Const SCL_PIN as byte = 5 'clock pin on BX-24
Private Const SDA_PIN as byte = 6 'data pin on BX-24
'************************
'Wake up eeprom: Call this at start of any program using eeprom, to init chip.
Public Sub Init_eeprom()
Call I2C_start()
Call I2C_stop()
End Sub
'************************
Public Sub write_ram_to_eeprom(ByVal ram_address As Integer, ByVal length As Byte, ByVal eeprom_device as Byte, ByVal eeprom_address as UnsignedLong)
dim i as integer, pointer as integer, outbyte as byte
dim tempaddr as new UnsignedLong

'Prepare to write first byte:
Call I2C_start()
Call I2C_out_byte((&HA0) + (eeprom_device * 2)) 'send device address (0-7; default would be 0 if only one chip)
Call I2C_ack()

tempaddr = eeprom_address
pointer = MemAddress(tempaddr)
Call I2C_out_byte(RAMpeek(pointer+1)) 'send high byte of eeprom address
Call I2C_ack()

Call I2C_out_byte(RAMpeek(pointer)) 'send low byte of eeprom address
Call I2C_ack()

'Write between 1 and 128 bytes:
for i = ram_address to (ram_address + cint(length)-1)
outbyte = RamPeek(i)
Call I2C_out_byte(outbyte)
Call I2C_ack()
next

'Wrap it up:
Call I2C_stop()
Call Sleep(0.005) 'Give 5ms to complete write operation.

End Sub
'************************
Public Sub read_eeprom_into_ram(ByVal ram_address As Integer, ByVal length As Byte, ByVal eeprom_device as Byte, ByRef eeprom_address as UnsignedLong)
dim i as integer, pointer as integer, inbyte as byte

'first control byte:
Call I2C_start()
Call I2C_out_byte((&HA0) + (eeprom_device * 2)) 'send device address (0-7; default would be 0 if only one chip)
Call I2C_ack()

pointer = MemAddress(eeprom_address)
Call I2C_out_byte(RAMpeek(pointer+1)) 'high byte of eeprom address
Call I2C_ack()

Call I2C_out_byte(RAMpeek(pointer)) 'low byte of eeprom address
Call I2C_ack()

'second control byte:
Call I2C_start()
Call I2C_out_byte(&HA1 + (eeprom_device * 2))
Call I2C_ack()'Returns a single byte of data from EEPROM

'Read between 1 and 128 bytes:
for i = ram_address to (ram_address + cint(length) - 1)
Call I2C_in_byte(inbyte)
'debug.print ", now:"; cstr(inbyte)
if i < (ram_address + cint(length) - 1) then
Call I2C_ack() 'ack if more bytes
end if
Call RamPoke(inbyte, i)
next

Call I2C_nack() 'send nack after last byte

' send stop:
Call I2C_stop()

End Sub
'************************
Public Function read_eeprom_byte(ByVal eeprom_device as Byte, ByRef eeprom_address as UnsignedLong) as byte
dim pointer as integer
'dim dat as byte

'first control byte:
Call I2C_start()
Call I2C_out_byte((&HA0) + (eeprom_device * 2)) 'send device address (0-7; default would be 0 if only one chip)
Call I2C_ack()

pointer = MemAddress(eeprom_address)
Call I2C_out_byte(RAMpeek(pointer+1)) 'high byte of eeprom address
Call I2C_ack()

Call I2C_out_byte(RAMpeek(pointer)) 'low byte of eeprom address
Call I2C_ack()

'second control byte:
Call I2C_start()
Call I2C_out_byte(&HA1 + (eeprom_device * 2))
Call I2C_ack()

'Read byte:
Call I2C_in_byte(read_eeprom_byte)
'debug.print "dat="; cstr(dat)
Call I2C_nack() 'allow ack from eeprom

' send stop:
Call I2C_stop()

End Function
'************************
Public Sub I2C_out_byte(ByVal outbyte As Byte)
Call ShiftOut(SDA_PIN, SCL_PIN, 8, outbyte)
End Sub
'************************
Public Sub I2C_in_byte(ByRef I_byte As Byte)
I_byte = ShiftIn(SDA_PIN, SCL_PIN, 8)
End Sub
'************************
Public Sub I2C_nack() 'allow slave to acknowledge
Call PutPin(SDA_PIN, bxInputTristate)
Call PutPin(SCL_PIN, bxInputTristate)
Call PutPin(SCL_PIN, 0)
End Sub
'************************
Public Sub I2C_ack() 'send acknowledge
Call PutPin(SDA_PIN, 0)
Call PutPin(SCL_PIN, bxInputTristate)
Call PutPin(SCL_PIN, 0)
Call PutPin(SDA_PIN, bxInputTristate)
End Sub
'************************
Public Sub I2C_start() 'send start - bring SDA low while SCL is high
Call PutPin(SDA_PIN, bxInputTristate)
Call PutPin(SCL_PIN, bxInputTristate)
Call PutPin(SDA_PIN, 0)
Call PutPin(SCL_PIN, 0)
End Sub
'************************
Public Sub I2C_stop() 'send stop - bring SDA high while SCL is high
Call PutPin(SDA_PIN, 0)
Call PutPin(SCL_PIN, bxInputTristate) 'leave clock high
Call PutPin(SDA_PIN, bxInputTristate)
End Sub
'************************

Strip down code to read memory location
Eliminate all loops
For now do simple one step commands
Clear that location
Read that location
do you get zero?

Write to that location
Read that location
Did you get what you wrote?

Start out simple like bits then goto bytes
then numbers

Sent from my HTC on the Now Network from Sprint!

----- Reply message -----
From: "rhese"
Date: Tue, Dec 28, 2010 4:23 pm
Subject: [BasicX] Need help writing to/reading from I2C EEPROM chip
To: "b..."

Hi all,

I'm working on a data logger project using the BX-24 and a Microchip 24AA1025 1024K I2C Serial EEPROM. Communicating via I2C is totally new to me, so I don't doubt that I'm messing something up here. I'm using code from M24LC256.bas, which I have tried to modify to match the chip I'm using. The problem is, I don't get the same data out that I'm putting in. For example, if the following values are read into the EEPROM:
1, 840, 17, 32
1, 841, 26, 30
1, 842, 26, 30
1, 843, 19, 39
1, 844, 18, 29
...I get this back out:
1, 771, 18, 29
1, 8195, 0, 29
1, 7683, 0, 29
1, 7683, 0, 29
1, 9987, 0, 29
1

Below is my code. I didn't include all code, just the parts relevant to reading & writing data to the EEPROM chip. On the EEPROM chip itself, I've got pins 1, 2, & 3 tied together and to +5V, pin 4 to ground, pin 5 & 6 have 2k Ohm resistors tying them to +5V and they also connect to pins on the BX-24. Pin 7 goes to ground & pin 8 goes to +5V.

Can anyone point to what I'm doing wrong? Thank you for your time.

'************************
Public EEPROM_Pointer As New UnsignedLong
Dim ADCdata(0 to 3) As Integer
Dim Current_EEPROM_Address As Long
Dim intDay As Integer
Dim intMinute As Integer
Dim lngTick As Long
Dim intTemp As Integer
Dim intRH As Integer
Dim Stopwatch As Single

Const Sample_Rate As Single = 60.0 '(1 minute)
Const EEPROM_Start_Address As Long = 0
Const EEPROM_End_Address As Long = &h1ffff '131071 (I hope)
'************************
Sub Log_Data()
Dim I As Integer

Call putPin(Logging_LED_Pin, 1) 'Turn on the Data Logging LED
Call Init_eeprom() 'Wake up the EEPROM chip
EEPROM_Pointer = CuLng(EEPROM_Start_Address)

Do Until GetPin(Logging_Switch_Pin) = 0
'At the period of the sample rate, record the elapsed days & minutes
If (Timer - Stopwatch) >= Sample_Rate Then
'Code to sample variables intDay, intMinute, intTemp, & intRH goes here
'...

'Write the Days, Minutes, Temperature, and RH to the EEPROM chip
ADCdata(0) = intDay
ADCdata(1) = intMinute
ADCdata(2) = intTemp
ADCdata(3) = intRH
Call write_ram_to_eeprom(MemAddress(ADCdata), 8, 7, EEPROM_Pointer)
EEPROM_Pointer = EEPROM_Pointer + 8

'Check to see if we've crossed the EEPROM chip's memory boundaries
If (EEPROM_Pointer > &hfffd&) AND (EEPROM_Pointer < &h10000) Then
EEPROM_Pointer = CuLng(&h10000)
End If

Current_EEPROM_Address = CLng(EEPROM_Pointer)

'Check to see if we are out of storage space
If Current_EEPROM_Address > EEPROM_End_Address Then
'Light LED 4 and Trap the program here
Call putPin(Attention_LED_Pin, 1)
Call putPin(Logging_LED_Pin, 0)
Do
Call Sleep(256.0)
Loop
End If

'Wait until the next sample period
Stopwatch = Timer
End If
Loop

'Turn off the Data Logging LED
Call putPin(Logging_LED_Pin, 0)

End Sub
'************************
Sub Upload_Data()
Dim I As Integer
Dim intRAM As Integer
I = 0

'Wait for the serial hardware to stabilize
Call Sleep(8.0)
Call putPin(Upload_LED_Pin, 1) 'Turn on the Uploading Data LED
Call Init_eeprom() 'Wake up the EEPROM chip
'Get the starting & ending EEPROM address for the logged data
EEPROM_Pointer = CuLng(EEPROM_Start_Address)
If Current_EEPROM_Address = EEPROM_Start_Address Then
Current_EEPROM_Address = EEPROM_End_Address
End If

'Covert and print all EEPROM data from the starting to ending address
Do Until EEPROM_Pointer > CuLng(Current_EEPROM_Address)
'Read the two bytes that make up each integer from next EEPROM address
intRAM = MemAddress(ADCData)+I
Call read_eeprom_into_ram(intRAM, 2, 7, EEPROM_Pointer)
debug.print CStr(ADCData(I))
'Increment EEPROM index to the next stored integer
EEPROM_Pointer = EEPROM_Pointer + 2

'Check to see if we've crossed the EEPROM chip's memory boundaries
If (EEPROM_Pointer > &hfffd&) AND (EEPROM_Pointer < &h10000) Then
EEPROM_Pointer = CuLng(&h10000)
End If

I = I + 1
'Once all four data integers (per data line) are printed do a carriage return
If I = 4 Then
I = 0
End If
Loop

debug.print "** End of File **"
'Reset the EEPROM address pointer & warning LED if it was on
Current_EEPROM_Address = EEPROM_Start_Address
If GetPin(Attention_LED_Pin) = 1 Then
Call putPin(Attention_LED_Pin,0)
End If

'Turn off the Uploading Data LED
Call putPin(Upload_LED_Pin, 0)

End Sub
'************************
'I2C_include.bas, based on the file M24LC256.bas
'I2C subroutines, specifically for reading & writing to Microchip 24AA1025 I2C Serial EEPROM

Option Explicit
Private Const SCL_PIN as byte = 5 'clock pin on BX-24
Private Const SDA_PIN as byte = 6 'data pin on BX-24
'************************
'Wake up eeprom: Call this at start of any program using eeprom, to init chip.
Public Sub Init_eeprom()
Call I2C_start()
Call I2C_stop()
End Sub
'************************
Public Sub write_ram_to_eeprom(ByVal ram_address As Integer, ByVal length As Byte, ByVal eeprom_device as Byte, ByVal eeprom_address as UnsignedLong)
dim i as integer, pointer as integer, outbyte as byte
dim tempaddr as new UnsignedLong

'Prepare to write first byte:
Call I2C_start()
Call I2C_out_byte((&HA0) + (eeprom_device * 2)) 'send device address (0-7; default would be 0 if only one chip)
Call I2C_ack()

tempaddr = eeprom_address
pointer = MemAddress(tempaddr)
Call I2C_out_byte(RAMpeek(pointer+1)) 'send high byte of eeprom address
Call I2C_ack()

Call I2C_out_byte(RAMpeek(pointer)) 'send low byte of eeprom address
Call I2C_ack()

'Write between 1 and 128 bytes:
for i = ram_address to (ram_address + cint(length)-1)
outbyte = RamPeek(i)
Call I2C_out_byte(outbyte)
Call I2C_ack()
next

'Wrap it up:
Call I2C_stop()
Call Sleep(0.005) 'Give 5ms to complete write operation.

End Sub
'************************
Public Sub read_eeprom_into_ram(ByVal ram_address As Integer, ByVal length As Byte, ByVal eeprom_device as Byte, ByRef eeprom_address as UnsignedLong)
dim i as integer, pointer as integer, inbyte as byte

'first control byte:
Call I2C_start()
Call I2C_out_byte((&HA0) + (eeprom_device * 2)) 'send device address (0-7; default would be 0 if only one chip)
Call I2C_ack()

pointer = MemAddress(eeprom_address)
Call I2C_out_byte(RAMpeek(pointer+1)) 'high byte of eeprom address
Call I2C_ack()

Call I2C_out_byte(RAMpeek(pointer)) 'low byte of eeprom address
Call I2C_ack()

'second control byte:
Call I2C_start()
Call I2C_out_byte(&HA1 + (eeprom_device * 2))
Call I2C_ack()'Returns a single byte of data from EEPROM

'Read between 1 and 128 bytes:
for i = ram_address to (ram_address + cint(length) - 1)
Call I2C_in_byte(inbyte)
'debug.print ", now:"; cstr(inbyte)
if i < (ram_address + cint(length) - 1) then
Call I2C_ack() 'ack if more bytes
end if
Call RamPoke(inbyte, i)
next

Call I2C_nack() 'send nack after last byte

' send stop:
Call I2C_stop()

End Sub
'************************
Public Function read_eeprom_byte(ByVal eeprom_device as Byte, ByRef eeprom_address as UnsignedLong) as byte
dim pointer as integer
'dim dat as byte

'first control byte:
Call I2C_start()
Call I2C_out_byte((&HA0) + (eeprom_device * 2)) 'send device address (0-7; default would be 0 if only one chip)
Call I2C_ack()

pointer = MemAddress(eeprom_address)
Call I2C_out_byte(RAMpeek(pointer+1)) 'high byte of eeprom address
Call I2C_ack()

Call I2C_out_byte(RAMpeek(pointer)) 'low byte of eeprom address
Call I2C_ack()

'second control byte:
Call I2C_start()
Call I2C_out_byte(&HA1 + (eeprom_device * 2))
Call I2C_ack()

'Read byte:
Call I2C_in_byte(read_eeprom_byte)
'debug.print "dat="; cstr(dat)
Call I2C_nack() 'allow ack from eeprom

' send stop:
Call I2C_stop()

End Function
'************************
Public Sub I2C_out_byte(ByVal outbyte As Byte)
Call ShiftOut(SDA_PIN, SCL_PIN, 8, outbyte)
End Sub
'************************
Public Sub I2C_in_byte(ByRef I_byte As Byte)
I_byte = ShiftIn(SDA_PIN, SCL_PIN, 8)
End Sub
'************************
Public Sub I2C_nack() 'allow slave to acknowledge
Call PutPin(SDA_PIN, bxInputTristate)
Call PutPin(SCL_PIN, bxInputTristate)
Call PutPin(SCL_PIN, 0)
End Sub
'************************
Public Sub I2C_ack() 'send acknowledge
Call PutPin(SDA_PIN, 0)
Call PutPin(SCL_PIN, bxInputTristate)
Call PutPin(SCL_PIN, 0)
Call PutPin(SDA_PIN, bxInputTristate)
End Sub
'************************
Public Sub I2C_start() 'send start - bring SDA low while SCL is high
Call PutPin(SDA_PIN, bxInputTristate)
Call PutPin(SCL_PIN, bxInputTristate)
Call PutPin(SDA_PIN, 0)
Call PutPin(SCL_PIN, 0)
End Sub
'************************
Public Sub I2C_stop() 'send stop - bring SDA high while SCL is high
Call PutPin(SDA_PIN, 0)
Call PutPin(SCL_PIN, bxInputTristate) 'leave clock high
Call PutPin(SDA_PIN, bxInputTristate)
End Sub
'************************

I'm thinking of doing a similar project. I hope you are successful and share the results. I tried to access M24LC256.bas but it wasn't there in the "files" area. Where did you find a copy?

Thanks, Eric

Ah ha! As might be expected, it was something rather dumb on my part. I was mis-interpreting how to use an integer array with the read_eeprom_into_ram sub (and I was also reading out 8 more bytes than I was writing in, but that was a separate issue with my respective loops).

Instead of trying to read into the integer array via an incremental loop:
intRAM = MemAddress(ADCData)+I
Call read_eeprom_into_ram(intRAM, 2, 7, EEPROM_Pointer)
I = I + 1
etc.

I needed to read data into the entire array at once, and then loop through the array to get the data out (via serial interface to my computer):
intRAM = MemAddress(ADCData)
Call read_eeprom_into_ram(intRAM, 8, 7, EEPROM_Pointer)
'4 integers go into the array at once
'Send each stored integer to INtToString sub for conversion to ASCII and printing
For J = 0 to 3
I = J
Call IntToString(ADCData(I),10)
'Separate each ADC value with a ","
Call PutQueueStr(Com1_Out_Buffer, ", ")
Next
etc.

Richard, thanks for the suggestion to baby-step through the code.

Eric, not sure if my original reply to you is still awaiting moderator approval, but the file M24LC256.bas is here:
Files > Datasheets-Appnotes-Examples-Drawings > Comm > I2C

The file is here:
Files > Datasheets-Appnotes-Examples-Drawings > Comm > I2C

I'm trying the advice of Mr. Friedrich & doing baby steps with the code. Writing in & then reading out a single byte of data so far does work as expected, so now I need to keep inching along to see where in my code I'm getting messed up. I will keep you posted.

Thanks both for the responses!

--- In b..., "eserdahl@..." wrote:
> I'm thinking of doing a similar project. I hope you are successful and share the results. I tried to access M24LC256.bas but it wasn't there in the "files" area. Where did you find a copy?
>
> Thanks, Eric
>