Even more minimal I2C driver
scanlime
Posts: 106
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.
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[noparse][[/noparse]EEPROM_SCL]~~ ' SCL high dira[noparse][[/noparse]EEPROM_SCL]~~ ' Drive SCL outa[noparse][[/noparse]EEPROM_SCL]~ ' SCL low outa[noparse][[/noparse]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[noparse][[/noparse]EEPROM_SDA]~~ ' Drive SDA low outa[noparse][[/noparse]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[noparse][[/noparse]EEPROM_SDA] := shiftreg > 0 ' SDA = shiftreg MSB outa[noparse][[/noparse]EEPROM_SCL]~~ ' Drive SCL high shiftreg <-= 1 ' Right one bit outa[noparse][[/noparse]EEPROM_SCL]~ ' Drive SCL low dira[noparse][[/noparse]EEPROM_SDA]~ ' Let SDA float outa[noparse][[/noparse]EEPROM_SCL]~~ ' Drive SCL high if ina[noparse][[/noparse]EEPROM_SDA] ' Check for ACK abort E_EEPROM outa[noparse][[/noparse]EEPROM_SCL]~ ' Drive SCL low shiftreg ->= 16 ' Next byte shiftreg := LONG[noparse][[/noparse]address] address += 4 bytecount := 4 ' Undo the one extra address step we took above (because of the header) address -= 4 ' I2C Stop dira[noparse][[/noparse]EEPROM_SDA]~~ ' Drive SDA low outa[noparse][[/noparse]EEPROM_SCL]~~ ' Drive SCL high dira[noparse][[/noparse]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))
Comments
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Links to other interesting threads:
· Home of the MultiBladeProps: TriBlade,·RamBlade,·SixBlade, website
· Single Board Computer:·3 Propeller ICs·and a·TriBladeProp board (ZiCog Z80 Emulator)
· Prop Tools under Development or Completed (Index)
· Emulators: CPUs Z80 etc; Micros Altair etc;· Terminals·VT100 etc; (Index) ZiCog (Z80) , MoCog (6809)·
· Prop OS: SphinxOS·, PropDos , PropCmd··· Search the Propeller forums·(uses advanced Google search)
My cruising website is: ·www.bluemagic.biz·· MultiBlade Props: www.cluso.bluemagic.biz
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Pullmoll's Propeller Projects
No, long[noparse][[/noparse]a] means "the long at address a&!3."
I guess I was halfway asleep yesterday [noparse]:)[/noparse] A long array or the second "dimension" of long[noparse]/noparse[noparse]/noparse is scaled by 4, but not the first one.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Pullmoll's Propeller Projects