PDA

View Full Version : My I2CWRITE - timings and tricks



FinchPJ
- 29th February 2008, 16:38
Recent endeavours with a 18F4550 at 48 MHz talking to both a 24LC256 EEPROM and a DAC8571 on the I2C bus have taught me a lot about I2CWRITE which I am now sharing "for the record".
I wont bother mentioning that
I2CWRITE SDA, SCL, $A0, 0, [Buffer\64]
is illegal and wont work - just please use variables ONLY (not constants).

No, my experimentation with the DAC revealed an undocumented feature of I2CWRITE (aka useful bug) - namely that if you don’t bother reading the PBP manual properly and you write:
I2CWRITE SDA, SCL, %10011000, %00010000, DAC
where DAC is a word to send to the DAC, the compiler does not complain and compiles BUT when you run the program, it doesn’t send a stop condition. This can be most useful, leaving the way to leaving the I2C bus connected to the DAC with it listening and waiting for more data which can then be sent as:
I2CWRITE SDA, SCL, DAC
and the bus can be closed down and restored either by doing it properly (note the brackets around the data!!!):
I2CWRITE SDA, SCL, %10011000, %00010000, [DAC]
or by a
GOSUB I2CStop 'see later for this routine
The same technique works fine for the EEPROM also.

Sometimes PicBasic commands are deceptive - take:
I2CWRITE SDA, SCL, ControlByte, Address, [Buffer\64]

This looks like a pretty slick and fast way of sending data to an EEPROM such as the 24LC256. And intuitively it seems that it is best to send the maximum number of bytes in one go..... However:

I2C bus timings for the 24LC256 are defined as a maximum bit rate of 400 KHz (assuming Vcc >2.5v, 100 KHz if Vcc <2.5v), giving a minimum pulse of 2.5 uSec, but also a minimum CLK hi of 0.6 uSec (if Vcc>2.5v) and minimum CLK low of 1.3 uSec. It also needs at least 5 mSec to complete a write, regardless of the size of the write which can be a maximum page size of 64 bytes. In practice this means that in PBP each I2CWrite takes 40 uSec per byte (5 uSec per bit).

And if you think you can do better than MELabs and write your own routines - e.g.:


I2CWrBuff: 'I2CWRITE SDA, SCL, CntrB, Addr, [str Buffer\8]
HIGH SDA 'Start
HIGH SCL 'Start
LOW SDA 'Start
LOW SCL 'Start
j = CntrB
gosub I2COut
j = Addr.highbyte 'Note must send Address Hi, Low
GOSUB I2cOut
j = Addr.lowbyte
GOSUB I2cOut
for i = 0 to 7
Value = Buffer[i] 'Buffer[i].Lowbyte is syntax error
j = Value.lowbyte 'Send array words Lo, Hi
GOSUB I2cOut
j = Value.highbyte
GOSUB I2cOut
next
I2CStop: LOW SDA 'Stop
HIGH SCL 'Stop
HIGH SDA 'Stop
RETURN
I2COut: SHIFTOUT SDA, SCL, 1, [j] '1=MSBFIRST
INPUT SDA
HIGH SCL 'Receive Acknowledge
LOW SCL
RETURN

Don’t bother because this takes longer than I2CWRITE (because of the pacing of SHIFTOUT.

My task then was to maximise the sample rate of an ADC read while storing the result in a 24LC256 EEPROM. So:

Each I2CWrite requires a 3 byte header (Control byte, Address word), and then the data. Without batching the data for a page write, this means the minimum sample interval is determined by having to wait 5 mSec for the I2CWrite to finish = 200 Hz.

On the other hand if we batch the maximum amount of data to send together with an array write of 64 bytes (64 byte page max), the actual I2CWrite command will take at least:
120 + 40*64 = 2680 uSec = 370 Hz.

So the optimum number of bytes (b) to send in a batch is achieved by solving the equation:

b*(40*b + 120) > 5000

4*b^2 +12b -500 > 0

b > 11.046

So transferring 12 bytes for each I2Cwrite achieves the fastest sample rate of 600 uSec per sample or 1.67 KHz.

However, as the datasheet says:
Write operations are limited to writing bytes within a single physical page, regardless of the number of bytes actually being written. Physical page boundaries start at addresses that are integer multiples of the page buffer size (or ‘page size’) and end at addresses that are integer multiples of [page size - 1].

This means that the answer to my puzzle is the optimum solution is to send 16 bytes (to avoid crossing a page boundary) - this gives a sample interval of 760 uSec or 1.32 KHz.

As my 18F4550 has a 10bit ADC, I am going to need to send 8 words of data at a time.

My routine (including the method of timing the loops using TIMER0) follows:


T0CON=%00010011 'Timer 0: Off[0], 16Bit[0], Fosc/4[0], [1], 1:16 prescaler [0011]
'16*4/48 usec per tick = 1.333 (4/3) uSec per tick
ADC_in: lcdout $FE, 1, "ADC to Memory"
pbuff =0
Addr = 0
block = 1
CntrB = (%10100000|(Block<<1))
t0con.7 = 1 'Turn Timer0 on
ADC1: Tick.Lowbyte = TMR0L 'Read Low first - High latched
Tick.highbyte = TMR0H
TMR0H = 0 'Write High first - Latched until write Low
TMR0L = 0
high led2 'Indicate acquisition - Menu (gButt) turns off
adcin 0, value
'10 bit to 16 bit and swap LSB/MSB because
'STR sends LSB,MSB and DAC expects MSB,LSB, ie:
'xxxxxx9876543210 > 210xxxxx,xxxxxxxx + xxxxxxxx,A9876543
'Buffer[pBuff] = (value<<13) + (Value>>2)
Buffer[pBuff] = Tick 'Borrow the data area to display timings - remove later
pbuff = pbuff +1
if pbuff = 8 then
I2CWRITE SDA, SCL, CntrB, Addr, [str Buffer\8]
pbuff = 0
addr = addr + 16 ' 8 words
if addr=32768 then
addr = 0
block = block +1
CntrB = (%10100000|(Block<<1))
endif
if block = 3 then Memful
endif
if pBuff <>0 then
pauseus 987 '13 uSec per loop
else
pauseus 237 '763 uSec per loop
endif
gosub gButt 'Routine to check for Button press (has two states: short/long)
if Butt = 0 then ADC1
goto Menu


Enjoy and if I have made any mistakes - be kind.
Peter Finch

FinchPJ
- 1st March 2008, 13:28
Oops - just realised was running the 18F4550 at 20 MHz - so all the timings are out by a factor of 2'ish. ie my code example is actually sampling at 2 KHz. Same principles apply though.
The Xtal frequency is 20 MHz - what with pre and post scalers etc it gets difficult to track what the Oscillator frequency is on the 18F4550 which has two potentially different clocks speeds in it.
Also my ADC manipulation comments had an error, but the code was correct - should have been:


'10 bit to 16 bit and swap LSB/MSB because
'STR sends LSB,MSB and DAC expects MSB,LSB, ie:
'xxxxxx9876543210 > 10xxxxxx,xxxxxxxx + xxxxxxxx,98765432
Buffer[pBuff] = (value<<13) + (Value>>2)

Peter

FinchPJ
- 2nd March 2008, 19:52
The speed of my 18F4550 was frustrating me - I knew it has a 20 MHz crystal and I set the prescaler etc to do the usual divide by 5, multiply by 12 but it appeared to only run at 20 MHz. When I tried on the PICDEM-FSUSB demo board it ran at 48 MHz. I have finally twigged the difference and the explanation - I program my PICDEM board using ICD2 and the "real" board using the Microcode bootloader from Mecanique.

Of course, the requisite R of TFM states clearly that the configuration fuses are set by the bootloader program (here 20 MHz) NOT my PBP program I am loading in with its OSC 48 - so I need to use OSC 20 with the loader and of course this also explains why the USB fails - not recognised by the PC because it is not running at 48 MHz. We actually chucked away a USB plug because we concluded that it MUST be the reason for the program to fail, there being no other logical explanation - the next one worked but of course we didnt use the loader for that - Doh!

Ioannis
- 3rd March 2008, 13:54
...
I2CWRITE SDA, SCL, $A0, 0, [Buffer\64]
is illegal and wont work - just please use variables ONLY (not constants).

.....

I2CWRITE SDA, SCL, %10011000, %00010000, DAC


Hi Peter.
On the first example you state that it won't work because of the constants. But on the second example you are using constants! Now I got confused. Does it work or not? From my experience the control may well be a constant.

Ioannis

FinchPJ
- 3rd March 2008, 15:53
Well spotted - I apologise I was not as rigorous as I should have been - of course that was a throwaway comment on a topic already well documented in this forum. You are correct I am using a constant for the control byte - This is OK! If I had been more rigourous I would have been more specific and said please dont use constants for the Address when using a memory chip - that MUST be a variable - as per the PBP manual:

The Address size sent (byte or word) is determined by the size of the variable that is used. If a byte-sized variable is used for the Address, an 8-bit address is sent. If a word-sized variable is used, a 16-bit address is sent. Be sure to use the proper sized variable for the device you wish to communicate with. Constants should not be used for the address as the size can vary dependent on the size of the constant. Also, expressions should not be used as they can cause an improper Address size to be sent..

Of course these contraints do not apply to talking to the DAC, although I could have used byte variables.

Peter Finch

Ioannis
- 3rd March 2008, 21:40
OK. We agree totaly.

Ioannis