PDA

View Full Version : Even more minimal I2C driver



scanlime
05-15-2010, 12:52 AM
Just sharing a little code snippet that I thought might be useful to others.

I'm working on a library that needs to make some of its DAT data persistent. After a batch of modifications to this data, it wants to flush the whole block to EEPROM. The data may not be aligned on EEPROM page boundaries, and it may be multiple pages. I'm also really trying to save RAM, so I wanted the smallest I2C routines I could find.

So, I wrote a single compact routine that does just this. It compiles to 180 bytes, which is about half the size of the "Minimal I2C Driver". I think this routine is a bit unconventional in that it combines the page loop, I2C header, and I2C data into one compact set of nested loops. This means it only does one thing, but it does that pretty well and with very little memory. If this code is useful, feel free to take it. I'm releasing it into the public domain. Code is below and attached.




CON
EEPROM_SCL = 28
EEPROM_SDA = 29
EEPROM_ADDRESS = $A0

E_EEPROM = -50

PUB EEWrite(address, size) | shiftreg, bytecount
'' Copy data from RAM to EEPROM, writing all EEPROM pages that
'' overlap the specified data range. On error, aborts with E_EEPROM.

outa[EEPROM_SCL]~~ ' SCL high
dira[EEPROM_SCL]~~ ' Drive SCL
outa[EEPROM_SCL]~ ' SCL low
outa[EEPROM_SCL]~~ ' SCL high

' The 24LC256 has 64-byte pages. Page-align the beginning and ending addresses,
' and convert 'size' into a page count.

size += address
address &= !63
repeat ((size + 63) - address) >> 6

' Issue page-write I2C commands.

' I2C Start
dira[EEPROM_SDA]~~ ' Drive SDA low
outa[EEPROM_SCL]~ ' Drive SCL low

' We have a 32-bit data buffer that starts out filled with the three-byte header,
' then we re-fill it with longs from hub memory. We're sending the least significant
' byte first, but most significant bit first. To be consistent with this byte order,
' we need to swap the bytes in our address.

shiftreg := EEPROM_ADDRESS | (address & $FF00) | (address << 16)
bytecount := 3

' One 64-byte pages: 16 data longs plus header
repeat 17

shiftreg ->= 8 ' Left-justify the least-significant byte

' Loop over each byte in the shift register (3 for header, 4 for data)
repeat bytecount
repeat 8
dira[EEPROM_SDA] := shiftreg > 0 ' SDA = shiftreg MSB
outa[EEPROM_SCL]~~ ' Drive SCL high
shiftreg <-= 1 ' Right one bit
outa[EEPROM_SCL]~ ' Drive SCL low

dira[EEPROM_SDA]~ ' Let SDA float
outa[EEPROM_SCL]~~ ' Drive SCL high
if ina[EEPROM_SDA] ' Check for ACK
abort E_EEPROM
outa[EEPROM_SCL]~ ' Drive SCL low

shiftreg ->= 16 ' Next byte

shiftreg := LONG[address]
address += 4
bytecount := 4

' Undo the one extra address step we took above (because of the header)
address -= 4

' I2C Stop
dira[EEPROM_SDA]~~ ' Drive SDA low
outa[EEPROM_SCL]~~ ' Drive SCL high
dira[EEPROM_SDA]~ ' Float SDA high

' Wait for page write. Datasheet says the max write time is 5ms.
' This is just a really conservative hardcoded delay. It's 5ms at 96 MHz.
' (This takes less memory than polling for completion.)
waitcnt(cnt + constant(96_000_000 / 1000 * 5))

scanlime
05-15-2010, 02:31 AM
Ugh, just noticed a subtle but really annoying bug in this driver- "shiftreg > 0" should be "shiftreg => 0". Any long-aligned block of four zeroes was being written as four FFs :)

Cluso99
05-15-2010, 05:31 AM
Nice work Micah. I am sure it will be useful.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Links to other interesting threads:

· Home of the MultiBladeProps: TriBlade (http://forums.parallax.com/showthread.php?p=786418),·RamBlade (http://forums.parallax.com/showthread.php?p=849265),·SixBlade (http://forums.parallax.com/showthread.php?p=780033), website (http://bluemagic.biz/cluso.htm)
· Single Board Computer:·3 Propeller ICs·and a·TriBladeProp board (ZiCog Z80 Emulator) (http://forums.parallax.com/showthread.php?p=790917)
· Prop Tools under Development or Completed (Index) (http://forums.parallax.com/showthread.php?p=753439)
· Emulators: CPUs Z80 etc; Micros Altair etc;· Terminals·VT100 etc; (Index) (http://forums.parallax.com/showthread.php?p=778427) ZiCog (Z80) (http://forums.parallax.com/showthread.php?p=788511) , MoCog (6809) (http://forums.parallax.com/showthread.php?p=811043)·
· Prop OS: SphinxOS (http://forums.parallax.com/showthread.php?p=819353)·, PropDos (http://www.orrtech.us/propdos/) , PropCmd (http://obex.parallax.com/objects/440/)··· Search the Propeller forums (http://www.google.com/advanced_search?q=+site:forums.parallax.com&num=20&hl=en&lr=)·(uses advanced Google search)
My cruising website is: ·www.bluemagic.biz (http://www.bluemagic.biz)·· MultiBlade Props: www.cluso.bluemagic.biz (http://www.cluso.bluemagic.biz)

pullmoll
05-15-2010, 06:31 AM
Hmm.. I see a LONG [address] and then an increment of address by 4. Isn't the LONG[] array supposed to be indexed with a scale factor of 4, i.e. @long[ 1] == @long[ 0] + 4?

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Pullmoll's Propeller Projects (http://pmbits.ath.cx/prop/)

mpark
05-15-2010, 09:50 AM
pullmoll said...
Hmm.. I see a LONG [address] and then an increment of address by 4. Isn't the LONG[] array supposed to be indexed with a scale factor of 4, i.e. @long[ 1] == @long[ 0] + 4?


No, long[a] means "the long at address a&!3."

pullmoll
05-15-2010, 03:52 PM
mpark said...

pullmoll said...
Hmm.. I see a LONG [address] and then an increment of address by 4. Isn't the LONG[] array supposed to be indexed with a scale factor of 4, i.e. @long[ 1] == @long[ 0] + 4?


No, long[a] means "the long at address a&!3."


I guess I was halfway asleep yesterday :) A long array or the second "dimension" of long[][] is scaled by 4, but not the first one.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Pullmoll's Propeller Projects (http://pmbits.ath.cx/prop/)