Shop OBEX P1 Docs P2 Docs Learn Events
problems Controlling eeprom#2 — Parallax Forums

problems Controlling eeprom#2

glentechglentech Posts: 35
edited 2006-08-31 18:46 in Propeller 1
I have been trying with some success to read and write to an eeprom. I am trying to load an array from memory. I am using the i2cobject created by james burrows with modifications in the readlocation method as follows.

i2cstart
ackbit :=(ackbit << 1) | i2cwrite(deviceaddress | 1,8)

repeat place from 1 to 11
byte[noparse][[/noparse]mydata][noparse][[/noparse]place]:=i2cread(_i2cack)

byte[noparse][[/noparse]mydata][noparse][[/noparse]12]:=i2cread(_i2cnak)

i2cstop

everything works great untill i try 14 or 15 read and then it makes the next read go haywire. I have had no problems with a similar multiwrite. Any ideas anyone?

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2006-08-23 01:38
    Do you still have the device addressing part of readlocation where it does a start, then three writes (of the device and memory address information)? I've been able to read and write very large arrays of data. You might want to try the I2C assembly routines in my "Operating System". The "OS_loaderInit" module is designed to use standalone for I2C access.
  • glentechglentech Posts: 35
    edited 2006-08-23 13:44
    Yes I didnt want to post the whole program as I have it but it is doing a seqential read as far as I can tell. I have used the program essentially unchanged except for what I have shown. The reads come back perfect every time until I try to read too many. Then it set the address pointer or something wierd because then every other read goes wild.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-08-23 14:40
    Your basic scheme looks fine and should work. The only "boundary condition" is at the end of the device where the internal address register wraps around. With writes, as you probably know, the wraparound occurs at the write page boundary which is 32, 64, or 128 bytes depending on the device (size mostly).

    As always, the problem is in the details. When I get to a place like this where things should work, but don't, I'll sometimes take my program, strip out everything but the part that seems to be the problem, add "instrumentation" to display the progress of things on a TV or VGA screen and see what's really happening. With the Propeller and SPIN, it's easy enough to add a little "debugger" to let me enter a byte count or specify read vs. write so I don't have to recompile every time I want to try something a little different.
  • JavalinJavalin Posts: 892
    edited 2006-08-23 20:15
    Hi Glentech,

    Can you post the program - I cannot really see whats going on without

    Thanks
  • glentechglentech Posts: 35
    edited 2006-08-23 21:11
    I am not immediately in front of the computer I have been using so I will try to remake this the best I can.
    the call from the main method goes something like this.

    i2cObject.readLocation(EEPROM_ADDR, eepromlocation, 16, 8,@mydata)

    I am using the propeller tv screen to debug this.
    The top object is my own creation but has the necessary constants and variables as far as I know.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-08-23 21:39
    Again, I don't see any obvious reason why this would not work, particularly when it works for 12 times and breaks at 13 or 14.
    What do you mean by "reads go wild". What kind of data are you seeing? Can you tell what locations the data comes from? I used the random operator (?) with a known starting point to generate an EEPROM full of pseudo-random data, then used that for testing the multibyte read stuff. The data is close to random, yet you can go back in and determine where you are in the sequence. You could also just fill the EEPROM with pairs of bytes in sequential numeric order. When the "read goes wild" you might be able to determine where it read from and that way deduce what happened.
  • glentechglentech Posts: 35
    edited 2006-08-23 23:55
    What is odd about the wild read is that it as near as I can tell is that it does not always return data from the same location. I have even tried different pull up resistors in the thought that maybe it was my setup. Its almost like it crashes the chip. I can totally remove power between tries with the same result. The reason why it seems like it crashes the chip is that it returns data that is nothing I have put there nor is it %11111111 as all unprogrammed memory contains. (I think)
  • Mike GreenMike Green Posts: 23,101
    edited 2006-08-24 00:14
    I2C should be timing insensitive as long as minimum setup times and pulse widths are obeyed and it would be hard in SPIN to violate those since at 400KHz, the minimum pulse widths are 1us clock high and 1.3us clock low. Minimum data setup is about 0.25us and many EEPROMs are rated for 1MHz clocking. Unfortunately, there's no error checking on reads. When I wrote my own multibyte routines using James' low level routines, I could read and write 32 bytes at a time without problems. I don't think I have copies anymore since the assembly I2C driver is working.
  • glentechglentech Posts: 35
    edited 2006-08-24 01:12
    I am a previous bs2 user and so assembly is a bit over my head but where can I find your assembly routine? I'd be curious to know how I could do it with your routines. Anyway it seems to me like I'm missing something on the spin routine maybe with the ackknowlege bits or something that adds up with too many reads.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-08-24 03:06
    Attached is a copy of the I2C routines. Have a look at the comments. Like most I/O drivers, you need to call the "start" routine before you use it the first time. I'm sure you'll have questions.
    Mike
  • glentechglentech Posts: 35
    edited 2006-08-24 16:16
    Well here are the questions and I hope they are not too elementary. For starters where in the object are the %10100001 and %10100000 for read and write on the Microchip eeprom. The next question is why do you need to start the r/writer in a cog of its own. If it is in its own cog how do you command it. Is this written for no pullup resistors? I took a quick look at the OS but I couldn't decipher the read and write commands. I would like to load a byte array of a specific size (say 30) so in my mind the method call would look something like this.

    eeprom.read(addresstoread,30,@mydata)
  • JavalinJavalin Posts: 892
    edited 2006-08-24 16:22
    Glentech,

    I see you've posted code - that'll help me work out whats going wrong. I am about to go away for the weekend and will not be able to look at it until next Tuesday onwards - so please bear with me. If you have'nt heard anything send me another PM

    James
  • Mike GreenMike Green Posts: 23,101
    edited 2006-08-24 17:00
    Glentech,
    1) The %10100001 and %10100000 are added in the assembly code just before they're transmitted
    2) The I/O routines are in a separate cog because they're written in assembly language and all assembly language gets run
    in its own cog. That's the way the Propeller works. The COGINIT instruction loads a block of 512 longs into a cog from
    the HUB RAM and it starts executing at location zero of the cog's memory.
    3) When you start an assembly cog, you can pass a single address to it as a parameter that appears in the PAR register.
    The I/O routines periodically watch that location (2 longs) for a non-zero operation code to appear in the high order
    byte of the first long. When that appears, the routines fetch both longs and execute the operation "requested".
    When the I/O routines are done, they store the updated information back into the 2 longs including a zero in the
    lower 4 bits of the high order byte. There are PUB routines in the "OS_loaderInit" object (readEEPROM and writeEEPROM)
    that will manage this exchange for you. They wait for any previous operation to complete, set up the 2 longs, and
    wait again for the operation to complete. They return TRUE if there were no NAKs received on write cycles and FALSE
    otherwise. Both routine accept an EEPROM address, RAM address for the data, and a count of bytes to transfer.
    The writeEEPROM routine does not wait for the EEPROM write cycle to complete, but you can either insert a 5ms
    wait or attempt to read zero bytes from the EEPROM repeatedly until the readEEPROM succeeds (returns TRUE).
    The EEPROM address is 23 bits with bits 22..19 containing a "pin pair" number (pins 28-SCL/29-SDA would be 14),
    bits 18..16 containing the device address code (for bits 3..1 of the %10100000), and bits 15..0 containing the 64K
    address within the EEPROM. In other words, if you had 8 - 24LC512 EEPROMs on each pair of I/O pins, you would
    have a linear address space of 16 (pin pairs) x 8 (devices per I2C buss) x 64K (bytes per EEPROM) = 8MB.
    ' example of writing, then reading back in a block of 16 bytes from the boot EEPROM at location $7FF0
    CON
      pinPair = 28
    OBJ
      i2c : "OS_loaderInit"
    VAR
      byte readBuffer[noparse][[/noparse]16], writeBuffer[noparse][[/noparse]16]
    PUB main
      i2c.writeEEPROM(pinPair<<18+$7FF0,@writeBuffer,16)
      waitcnt(clkfreq / 200 + cnt)
      i2c.readEEPROM(pinPair<<18+$7FF0,@readBuffer,16)
    
    


    If you want to check for errors, you could do something like "if not i2c.readEEPROM(....)"
    Mike

    Post Edited (Mike Green) : 8/24/2006 5:03:48 PM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2006-08-24 17:13
    Regarding pullups: A pullup is assumed on the SDA pin so the routines switch between input (high) and output low (low).
    For the SCL pin, the routines check the I/O pin. If it's 28, the routines switch between output high and output low. If not,
    they switch between input (high) and output low (low).

    The first time any pin pair is used, the routines will do an I2C reset sequence where they'll put out up to 9 clocks until SDA is high while the clock is high. This should reset any I2C device that's in some indeterminate state (according to Philips).
  • JavalinJavalin Posts: 892
    edited 2006-08-29 10:36
    Hi Glentech,

    Back from a great weekend!

    Right a quick look at the modified i2cObject shows a few potential issues.·:

    (1) mydata is not an array - or at least is not clear that it may point to an array.· If "mydata" is a pointer maybe a mydataPtr would be a better name.· This should point to a least 13 byte array, and the pointer should be "@mydatarray"
    PUB readLocation(deviceAddress, deviceRegister, addressbits, databits ,mydata) : i2cData | ackbit,place
    

    (2) Related to the above - the "repeat place..." loop the byte[noparse][[/noparse]mydata][noparse][[/noparse]place] won't work unless the mydata is a pointer to @mydataarray as at least a 13 byte array

    (3) Otherwise - try having the "mydataarray" in the i2cObject and reading into it directly.· I.e declare the array in the i2cobject "byte· myDataArray[noparse][[/noparse]20]" then change the repeat loop to be "byte[noparse][[/noparse]@mydataarray][noparse][[/noparse]place]:=i2cread(_i2cack)".· Finally a small function to return the pointer to the array to other objects:
    PUB getPointer : result
      return @mydataarray
    

    If this is no help - let me know and i'll try running it from home.

    James
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-08-29 14:44
    Mike:
    In your code segment: i2c.writeEEPROM(pinPair<<18+$7FF0,@writeBuffer,16)
    What does the 18 represent?
    Is it part of the chip address?
    If it is, how dows it corrlate to the actual address of the chips address?
    Also, do you or does the code take care of 'roll over' automatically?
    (ie, you hit the end of one chip in a read/write, does it move to the next chip or go to 0 of the current chip?)

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

    Propeller + Hardware - extra bits for the bit bucket =· 1 Coffeeless KaosKidd

    ·
  • JavalinJavalin Posts: 892
    edited 2006-08-29 17:28
    KK,

    << and >> are binary shift operators. So in that code I guess he is shifting pinPair left 18 places, then adding $7FFO

    James
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-08-29 18:55
    Thanks Javalin.
    From the way the post is writtem and the statement, the chip address appeared to be part of the param, but I couldn't tell.
    He's moving the pinpair over( like you said), and shifting in 0's for the boot prom address.
    What I need to do is figure out how to shift in an chip address as well.
    I think it would be something like: Address = Pinpair << 2 + chip address << 16 + $####

    If this is the case, then a simple single sontinious address map could be derived by some simple math, assuming all the eeproms were the same size on the bus:
    ChipArrayRead(GloabalAddress, BytesToGet, PinPair,@Destination) | ChipAddress, BaseAddress, WorkingAddress
    ChipAddress = INT(GlobalAddress / NumberofChips)
    BaseAddress = GlobalAddress - (ChipAddress * ChipSize)
    WorkingAddress = PinPair << 2 + ChipAddress << 16 + BaseAddress
    I2C.ReadEEPROM(WorkingAddress,@Destination,BitesToGet)

    Is this about right? It's not. The Chipaddress needs to be decoded (??) before being shifted in... I'm not sure how that woud be done...

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

    Propeller + Hardware - extra bits for the bit bucket =· 1 Coffeeless KaosKidd

    ·
  • JavalinJavalin Posts: 892
    edited 2006-08-29 19:05
    I think what you are asking is how to use multiple eeprom's as a single block of memory.

    It can be done fairly easily as the addressing on the chips allows for 8 devices sequencially on the bus. With a bit of math you can use some of the device bus address and the register on the eeprom as single reference to a large block of eeprom!

    James
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-08-29 20:03
    Javalin.
    THat's exactly what I'm trying to do... treat multipule eeproms on the same bus as a single block of memory.
    Thats how I understand Mike Green's code to work... I think...

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

    Propeller + Hardware - extra bits for the bit bucket =· 1 Coffeeless KaosKidd

    ·
  • Mike GreenMike Green Posts: 23,101
    edited 2006-08-29 20:27
    Javalin and Kaos,
    The EEPROM address is intended to be a continuous addressing space. The lower 16 bits (15..0) get sent to the EEPROM as two address bytes. The next 3 bits (18..16) are used to create the device addressing code (%1010xxx0). The remaining 4 bits (22..19) are used to select a pair of I/O pins with SCL on the even pin and SDA on the next odd pin. The left shift 18 bits is used to position the pin number of the SCL pin (always even) so that the high order bits fall into bits 22..19. This gives a theoretical space of 8MB. If you're transferring multiple bytes, the device is only addressed on the first byte. The device addresses wrap around at the end of the device, so don't try to transfer a block of data across a device boundary (usually 32K). In addition, the write routines do not wait for the write to complete. If you try to access the device too soon after a write, the read or write will get an error. If you keep retrying, the device will eventually respond and do what you want. This is the ideal way to do successive writes because the device responds when it's ready which may be sooner than a fixed time (like 5ms or 10ms). Multiple byte writes make use of a page which can be 64 or 128 bytes depending on the device size. If you try to write past the page boundary, the write address will wrap around in the page buffer. This is unlikely to be what you want! The best way to handle all this is to either write single bytes or blocks of 64 bytes on a 64 byte boundary. For reading, either read single bytes or never cross a 32K boundary.

    32K devices ignore bit 15 of the address, so don't assume you have a 64K address space unless you know what chip is there! The file system in the OS checks to see if the second half of a 64K address space actually has a unique device there when you
    use the "probe" command to see what's on an I2C bus.

    If you're using a Ramtron FRAM device, you can ignore the paged write stuff. Writing works just like reading.

    I hope this helps. Mike
  • glentechglentech Posts: 35
    edited 2006-08-29 21:12
    Javalin,

    Maybe I didn't make it clear or else I am not understanding something but I am using an array outside of the i2cobject named mydata. The call to the i2cobject has the pointer @mydata in the call. It may be a fluke that it works until I try to read 14 or more.
  • JavalinJavalin Posts: 892
    edited 2006-08-29 21:14
    Hi,

    I guess'd that.

    Perhaps I was being too subtle. How big is your array, and how is it declared in your calling object?

    James
  • glentechglentech Posts: 35
    edited 2006-08-29 21:39
    I have varied my array size but always used an array bigger than the read. (30) I have declared it in the var like this
    byte mydata[noparse][[/noparse]30]
  • JavalinJavalin Posts: 892
    edited 2006-08-31 09:37
    Glentech,

    Sorry about the silence. I'll get this tested tonight and let you know.

    James
  • JavalinJavalin Posts: 892
    edited 2006-08-31 18:46
    Glentech,

    Attached is version 1.4 (beta) of i2cObject. I've added a pageRead and pageWrite with example in the i2cObject and i2cDemoApp respectivly.

    Hopefully this will solve your issues.

    (note that this is beta - thus will not go onto the object exchange yet)

    James
Sign In or Register to comment.