Shop OBEX P1 Docs P2 Docs Learn Events
Trouble writing Prop EEPROM — Parallax Forums

Trouble writing Prop EEPROM

Steve Hicks (N5AC)Steve Hicks (N5AC) Posts: 20
edited 2007-03-17 23:01 in Propeller 1
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:

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

  • Mike GreenMike Green Posts: 23,101
    edited 2007-03-16 17:06
    Steve,
    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.
  • QuattroRS4QuattroRS4 Posts: 916
    edited 2007-03-16 17:50
    Steve,
    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
  • Steve Hicks (N5AC)Steve Hicks (N5AC) Posts: 20
    edited 2007-03-16 19:19
    Thanks Quattro / Mike -- I pulled Mike's routines and they work great! Getting the proper returns now and data is getting written.

    Thanks,
    Steve

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔


    Steve, N5AC
  • QuattroRS4QuattroRS4 Posts: 916
    edited 2007-03-16 19:24
    Well done !

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Necessity is the mother of invention'
  • T ChapT Chap Posts: 4,223
    edited 2007-03-17 04:29
    What are the minimali2cdriver Write and Read used for, since they do not contain an address to read or write to?

    Thanks
  • Mike GreenMike Green Posts: 23,101
    edited 2007-03-17 04:47
    originator,
    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.
  • T ChapT Chap Posts: 4,223
    edited 2007-03-17 05:00
    I just was stuck on EEPROM usage, that makes sense Thanks
  • T ChapT Chap Posts: 4,223
    edited 2007-03-17 10:58
    Still trying to understand how this EEPROM write/read works. I wrote a value to dataByte of $FE, but after writing it to the EEPROM and reading it(supposeldy), it always tests posibive for 255 ($FF).

    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
  • Mike GreenMike Green Posts: 23,101
    edited 2007-03-17 13:17
    On the WritePage and ReadPage calls, you really have to have the proper value for the 2nd parameter. It's not just the chip address, it's a general device select code. For most EEPROMs, it's $A0 and you'll notice that this is predefined as EEPROM in the constants in the object file. The EEPROM won't respond if it doesn't get the right code. Any I2C device datasheet will show the device select code for the device.

    This is all explained in the comments and the example calls in the Basic_I2C_Routines source.
  • T ChapT Chap Posts: 4,223
    edited 2007-03-17 21:46
    Mike you are the man.

    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
  • Mike GreenMike Green Posts: 23,101
    edited 2007-03-17 22:16
    originator,
    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 address
    


    The 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.
  • T ChapT Chap Posts: 4,223
    edited 2007-03-17 22:29
    Yes I am getting it, this is very cool. I want to store user data(entered via GUI) for motor positions, speed, accel rates etc, the WriteLong alone will accomplish the goal perfectly. I intend to store all user data on EEPROM #2(just sub $A1),

    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?
    Bean said...


    I'm using the code to read/write from/to the last 6K (26624 thru 32767) of the 32KB EEPROM on the Propeller demo board.

    Don't forget that when you save code to the Propeller EEPROM (F11) it ALL gets erased.


    ***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
  • Mike GreenMike Green Posts: 23,101
    edited 2007-03-17 23:01
    The Propeller boot loader has responsibility for writing the program in RAM to EEPROM when directed to do so by the Propeller Tool. It has no knowledge of memory past 32K. The Propeller Tool has no knowledge of EEPROM above 32K. Bean's comment about ALL refers to all the memory on the demo board which is only 32K. Nothing above that is changed if present.
Sign In or Register to comment.