Trouble writing Prop EEPROM
I'm sure I'm doing something stupid here, but I've been looking at this for about 6 hours and I can't seem to figure out what I'm doing wrong... any ideas?· I have some fixed data (stored in a DAT block) that I am trying to write over at runtime, but just in the EEPROM (I'm not concerned about RAM).· Here's the relevant snippet of my code:
I make a call to PrepareI2C and then I call a series of EEWriteLong's.· This uses the i2cobject.spin by James Burrows.· What I get out is this:
So,·in the first write, the EEPROM data is 75, I try to write a 104 and I get a 75 back ... after receiving an ACK... any clues about what I'm doing wrong?· I have to believe it is something simple about the timing or how I am doing the start-up call.· I don't need to add a %1 to the EEPROM_Addr to enable the write bit do I (for the R/W bit?)· ... I tried that and it didn't work either...
Thanks,
Steve
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Steve, N5AC
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
_stack = 50
i2cSCL = 28
i2cSDA = 29
EEPROM_Addr = %1010_0000
EEWAIT = 400_000
OBJ
i2c: "i2cObject"
PRI PrepareI2C
' setup i2cobject
i2c.init(i2cSDA, i2cSCL, true)
i2c.Start
if i2c.devicePresent(EEPROM_Addr) <> true
debug.str(string("EEPROM NOT PRESENT? "))
else
debug.str(string("EEPROM PRESENT -- PROGRAMMING MODE"))
debug.crlf
PRI EEWriteLong(addr, data)
EEByte(addr, data & $FF)
EEByte(addr+1, (data >> 8) & $FF)
EEByte(addr+2, (data >> 16) & $FF)
EEByte(addr+3, (data >> 24) & $FF)
PRI EEByte(addr, data) | ack, data2
data2 := i2c.readLocation(EEPROM_ADDR, addr, 16, 8)
waitcnt(EEWAIT + cnt)
debug.str(string("B4 = "))
debug.str(NUM.ToStr(data2,NUM#DEC))
debug.str(string(", DATA = "))
debug.str(NUM.ToStr(data,NUM#DEC))
debug.str(string(", ACK = "))
ack := i2c.writeLocation(EEPROM_ADDR, addr, data, 16, 8)
waitcnt(EEWAIT + cnt)
debug.str(NUM.ToStr(ack,NUM#DEC))
debug.str(string(", NEW = "))
data2 := i2c.readLocation(EEPROM_ADDR, addr, 16, 8)
waitcnt(EEWAIT + cnt)
debug.str(NUM.ToStr(data2,NUM#DEC))
debug.crlf
I make a call to PrepareI2C and then I call a series of EEWriteLong's.· This uses the i2cobject.spin by James Burrows.· What I get out is this:
[color=#ff0000][color=#ff0000][color=#ff0000]B4 = 75, DATA = 104, ACK = 0, NEW = 75 B4 = 0, DATA = 1, ACK = 0, NEW = 0 B4 = 0, DATA = 0, ACK = 0, NEW = 0 B4 = 0, DATA = 0, ACK = 0, NEW = 0 [/color][/color][/color]
So,·in the first write, the EEPROM data is 75, I try to write a 104 and I get a 75 back ... after receiving an ACK... any clues about what I'm doing wrong?· I have to believe it is something simple about the timing or how I am doing the start-up call.· I don't need to add a %1 to the EEPROM_Addr to enable the write bit do I (for the R/W bit?)· ... I tried that and it didn't work either...
Thanks,
Steve
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Steve, N5AC

Comments
I don't see anything wrong with what you've posted.
You do have unnecessary WAITCNTs after the readLocation calls. You only need them after writes.
I've had problems sometimes with James' routines and the boot EEPROM setup. I don't know why. You might try the other I2C routines in the Object Exchange.
I have used Mike Greens "minimal_i2c_driver" without issue - it does exactly what it says on the tin !
Sample shown is for aux eeprom on different pins...(26,27)
Pub write(value)
' Write value to EEprom
·· outa[noparse][[/noparse]26] := 1 ' set SCL high
·· dira[noparse][[/noparse]26] := 1
·· dira[noparse][[/noparse]27] := 0 ' set SDA as input
·· Auxeeprom.i2cWritepage(26,$A0,$7FFC,@value,2)
·· waitcnt(clkfreq / 200 + cnt)
note: wait after write ...
Pub GetVal(Gval)
'Get stored value from Aux EEprom
·repeat 9
···· outa[noparse][[/noparse]26] := 0
···· outa[noparse][[/noparse]26] := 1
·Auxeeprom.i2cReadPage(26, $A0, $7FFC, @Gval, 2)
·setval := gval
The toggle(x9) at the start of the GetVal routine is something suggested by Mike on a forum post which counteracts spurious read error after a reboot.
Quattro
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
'Necessity is the mother of invention'
Post Edited (QuattroRS4) : 3/16/2007 5:58:50 PM GMT
Thanks,
Steve
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Steve, N5AC
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
'Necessity is the mother of invention'
Thanks
Write and Read (and Start and Stop) are low level I2C routines used by the ReadPage and WritePage and the WriteWait routines. They could be used to access some other kind of I2C device like an A/D converter or clock. Look at any I2C device datasheet for a description of the pieces that make up an I2C transaction.
I would really like to get a grasp on the concept Mike has here. It appears that the pointer for where the variable liaves in RAM is the same pointer used on the EEPROM to write to, but that doesn't make sense yet. What if the EEPROM was full with a program? Furthermore, what if I need to write beyond the 32k limit? I am thinking this isn't the best code example for what I am trying to do, which is store permanent user input on the EEPROM, that even if the EEPROM is reprogrammed, it stays. I have two EEPROMS on the board, so using the second EEPROM was actually the plan for storing permanent user input, but to learn I am trying to write over the single EEPROM on a Prop Stick.
The current plan is two have both EEPROMS on the same pins, with a few other I2C devices, both pins pulled up with 4.7k. I like the idea of writing and reading a variable to the EEPROMS, but if stricktly an address is required, that will work. Any suggestions on a good example or object to look at? Beans HITT version worked, but it is noted *not for general I2C stuff, and I want to use othe stuff on the pins.
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 Green = 13 Red = 14 i2cACK = 0 ' I2C Acknowledge i2cNAK = 1 ' I2C No Acknowledge i2cXmit = 0 ' I2C Direction Transmit i2cRecv = 1 ' I2C Direction Receive i2cBoot = 28 ' I2C Boot EEPROM SCL Pin i2cEEPROM = $A0 ' I2C EEPROM Device Address VAR Long Num byte databyte, getdata, newval, getbyte OBJ spk : "Piezospeaker.spin" 'beep(pin, freq, dur) PUB Init outa[noparse][[/noparse]13..14] := %00 'led state dira[noparse][[/noparse]13..14] := %11 dira[noparse][[/noparse]15..16] := %00 Databyte := $FE i2cWritepage(28, %000, $7000, @databyte, 1) waitcnt(clkfreq/200 + cnt) i2cReadPage(28, %000, $7000, @databyte, 1) TestEeprom repeat '------------------------------------------------------------------------------------- PUB TestEeprom if databyte == 255 'tests positive, regardless of databyte value+++++++++++++++++++++++++++++ BeepGreen if databyte > 0 BeepRed '------------------------------------------------------------------------------------- PUB i2cStart(i2cSCL) | i2cSDA ' SDA goes HIGH to LOW with SCL HIGH i2cSDA := i2cSCL + 1 outa[noparse][[/noparse]i2cSCL]~~ ' Initially drive SCL HIGH dira[noparse][[/noparse]i2cSCL]~~ outa[noparse][[/noparse]i2cSDA]~~ ' Initially drive SDA HIGH dira[noparse][[/noparse]i2cSDA]~~ outa[noparse][[/noparse]i2cSDA]~ ' Now drive SDA LOW outa[noparse][[/noparse]i2cSCL] ~ ' Leave SCL LOW PUB i2cStop(i2cSCL) | i2cSDA ' SDA goes LOW to HIGH with SCL High i2cSDA := i2cSCL + 1 outa[noparse][[/noparse]i2cSCL] ~~ ' Drive SCL HIGH outa[noparse][[/noparse]i2cSDA] ~~ ' then SDA HIGH dira[noparse][[/noparse]i2cSCL] ~ ' Now let them float dira[noparse][[/noparse]i2cSDA] ~ ' If pullups present, they'll stay HIGH PUB i2cWrite(i2cSCL, i2cData) : ackbit | i2cSDA ' Write i2c data. Data byte is output MSB first, SDA data line is valid ' only while the SCL line is HIGH. Data is always 8 bits (+ ACK/NAK). ' SDA is assumed LOW and SCL and SDA are both left in the LOW state. i2cSDA := i2cSCL + 1 ackbit := 0 i2cData <<= 24 repeat 8 ' Output data to SDA outa[noparse][[/noparse]i2cSDA] := (i2cData <-= 1) & 1 outa[noparse][[/noparse]i2cSCL] ~~ ' Toggle SCL from LOW to HIGH to LOW outa[noparse][[/noparse]i2cSCL] ~ dira[noparse][[/noparse]i2cSDA] ~ ' Set SDA to input for ACK/NAK outa[noparse][[/noparse]i2cSCL] ~~ ackbit := ina[noparse][[/noparse]i2cSDA] ' Sample SDA when SCL is HIGH outa[noparse][[/noparse]i2cSCL] ~ outa[noparse][[/noparse]i2cSDA] ~ ' Leave SDA driven LOW dira[noparse][[/noparse]i2cSDA] ~~ PUB i2cRead(i2cSCL, ackbit): i2cData | i2cSDA ' Read in i2c data, Data byte is output MSB first, SDA data line is ' valid only while the SCL line is HIGH. SCL and SDA left in LOW state. i2cSDA := i2cSCL + 1 i2cData := 0 dira[noparse][[/noparse]i2cSDA]~ ' Make SDA an input repeat 8 ' Receive data from SDA outa[noparse][[/noparse]i2cSCL] ~~ ' Sample SDA when SCL is HIGH i2cData := (i2cData << 1) | ina[noparse][[/noparse]i2cSDA] outa[noparse][[/noparse]i2cSCL] ~ outa[noparse][[/noparse]i2cSDA] := ackbit ' Output ACK/NAK to SDA dira[noparse][[/noparse]i2cSDA] ~~ outa[noparse][[/noparse]i2cSCL] ~~ ' Toggle SCL from LOW to HIGH to LOW outa[noparse][[/noparse]i2cSCL] ~ outa[noparse][[/noparse]i2cSDA] ~ ' Leave SDA driven LOW PUB i2cReadPage(i2cSCL, i2cAddr, addrReg, dataPtr, count) : ackbit ' Read in a block of i2c data. Device address is i2cAddr. Device starting ' address is addrReg. Data address is at dataPtr. Number of bytes is count. ' The device address is modified using the upper 3 bits of the 19 bit addrReg. i2cAddr |= addrReg >> 15 & %1110 i2cStart(i2cSCL) ackbit := i2cWrite(i2cSCL, i2cAddr | i2cXmit) ackbit := (ackbit << 1) | i2cWrite(i2cSCL, addrReg >> 8 & $FF) ackbit := (ackbit << 1) | i2cWrite(i2cSCL, addrReg & $FF) i2cStart(i2cSCL) ackbit := (ackbit << 1) | i2cWrite(i2cSCL, i2cAddr | i2cRecv) repeat count - 1 byte[noparse][[/noparse]dataPtr++] := i2cRead(i2cSCL, i2cACK) byte[noparse][[/noparse]dataPtr++] := i2cRead(i2cSCL, i2cNAK) i2cStop(i2cSCL) return ackbit PUB i2cWritePage(i2cSCL, i2cAddr, addrReg, dataPtr, count) : ackbit ' Write out a block of i2c data. Device address is i2cAddr. Device starting ' address is addrReg. Data address is at dataPtr. Number of bytes is count. ' The device address is modified using the upper 3 bits of the 19 bit addrReg. ' Most devices have a page size of at least 32 bytes, some as large as 128. i2cAddr |= addrReg >> 15 & %1110 i2cStart(i2cSCL) ackbit := i2cWrite(i2cSCL, i2cAddr | i2cXmit) ackbit := (ackbit << 1) | i2cWrite(i2cSCL, addrReg >> 8 & $FF) ackbit := (ackbit << 1) | i2cWrite(i2cSCL, addrReg & $FF) repeat count ackbit := ackbit << 1 | ackbit & $80000000 ' "Sticky" sign bit ackbit |= i2cWrite(i2cSCL, byte[noparse][[/noparse]dataPtr++]) i2cStop(i2cSCL) return ackbit PUB BeepGreen LightGreenOn spk.beep(15, 1200, 500) 'low pitch LightGreenOff PUB BeepRed LightRedOn spk.beep(15, 700, 500) 'hi pitch LightRedOff PUB LightGreenOn outa[noparse][[/noparse]13] := 1 waitcnt(cnt + 80_000_00) PUB LightRedOn outa[noparse][[/noparse]14] := 1 waitcnt(cnt + 80_000_00) PUB LightGreenOff outa[noparse][[/noparse]13] := 0 waitcnt(cnt + 80_000_00) PUB LightRedOff outa[noparse][[/noparse]14] := 0 waitcnt(cnt + 80_000_00)Post Edited (originator) : 3/17/2007 11:14:06 AM GMT
This is all explained in the comments and the example calls in the Basic_I2C_Routines source.
I was stuck on the PCF8575 code, where I kept everything separate: device + address + R/W, data1, data2, stop. Here is the dumb part, I was thinking the chip address was the 3 pin chip select and the addrReg was the devicde ID!
Thanks
Post Edited (originator) : 3/17/2007 10:08:49 PM GMT
If you look at the source of the "Basic_I2C_Driver" object, you'll see the ReadPage and WritePage routines, then routines that use these to read and write a byte, a word, and a long. The latter routines are for convenience since they take the value to be written as a parameter or return the value read as the result of the call. Keep in mind that all the write routines do a paged write, so all affected EEPROM locations have to fit within a page. Reading is not an issue since the affected locations only have to be within the same 64K EEPROM. To do what Chip suggests and not have to worry about page boundaries, you have to write a byte at a time like:
PRI saveLong(address,value) | startTime repeat 4 if writeByte(i2c#BootPin,i2c#EEPROM,address,value&$FF) abort ' write failure ... perhaps there's no EEPROM there? startTime := cnt ' prepare to check for a timeout repeat while i2c.WriteWait(i2c#BootPin, i2c#EEPROM, address) if cnt - startTime > clkfreq / 10 abort ' waited more than a 1/10 second for the write to finish value >>= 8 ' shift value and address++ ' increment addressThe reason for supplying the address and value separately is in case you don't want to change the value until the next reboot.
If, say, you want to backup some long's value ... call it "x", you'd do: "saveLong(@x,x)". The saved value will be reloaded on the next reboot.
I am not clear from notes in Beans/Beaus EEPROM Write code thread, that F11 nukes the entire EEPROM, even data stored above the 256K mark on a 512?
***edit is there no way to view the EEPROM with F8? I tried to write a byte at EEPROM, $7000, Save EEPROM file, Open file, and nothing as at $7000
Post Edited (originator) : 3/17/2007 10:36:49 PM GMT