Redefining the I2C Lua interface (was: I2C for LM3S devices)

classic Classic list List threaded Threaded
39 messages Options
12
Martin Guy Martin Guy
Reply | Threaded
Open this post in threaded view
|

Redefining the I2C Lua interface (was: I2C for LM3S devices)

On 5 June 2011 17:47, Laurent Dufrechou <[hidden email]> wrote:

> Ok here is a proposal to allow the lm3s and str912 to work altogether:
>
> void platform_i2c_send_start( unsigned id )
>
> void platform_i2c_send_stop( unsigned id )
>
> int platform_i2c_send_address( unsigned id, u16 address, int direction )
>
> int platform_i2c_send_byte( unsigned id, u8 data )
>
> int platform_i2c_recv_byte( unsigned id, int ack )
>
> +
>
> int platform_i2c_send_byte_and_stop( unsigned id, u8 data )
>
> int platform_i2c_recv_byte_and_stop( unsigned id, int ack )
[...]
> int platform_i2c_start_and_send_address( unsigned id, u16 address, int
> direction )
>
>

Hi Laurent

> Am I missing any use case?
On the contrary, you have too many and inappropriate ones!

I am currently looking at implementing I2C on avr32 platforms, so
maybe we can define a common platform interface for the i2c platform
interface.

The ideal would be the smallest set of primitives that
1) allows us to generate all possible I2C protocols, reacting to (and
informing the caller about) all possible error conditions during
reception/transmission, such as lack of ACK when it is required, or
loss of bus arbitration.
2) covers all hardware implementations, from the highest level
intelligent hardware that does everything for you automatically, down
to bit-banging interfaces where you toggle and read the signals
explicitly.

An example of the lowest level can be found at
https://secure.wikimedia.org/wikipedia/en/wiki/I2C#Example_of_bit-banging_the_I2C_Master_protocol
whose user interface to this code fragment seems to consist of two
byte-oriented functions:
  bool nak = i2c_write_byte(bool send_start, bool send_stop, unsigned char data)
  unsigned char data = i2c_read_byte(bool nack, bool send_stop).

For the AVR32, to send data you program the 7-bit device address and
the direction bit into one control register, then write you write the
first byte of data to be transmitted into another and this second
write triggers the hardware to do the whole sequence: start, address
and the first data byte all in one go. You then have to poke more data
into the second register while the first set of data is being
transmitted if you want to transmit multiple bytes in one message:
failure to do so automatically ends the current packet with a STOP.

Unfortunately, the current elua module interface is too low level for
either of these implementations in that it has i2c.start(),
i2c.address() and i2c.write() as separate functions.  For both of
these above interfaces, the bit-banging and the AVR32, this forces the
driver to collect up information from a predefined sequence of start()
address() read() write() calls, storing them in private, persistent
data, and then use these data when the final call happens (read or
write) that gives sense to the single I2C operation.
  It also creates the question of whether the driver should detect
improper sequences of these Lua primitives, or how it should react to
them, for example, you call read() without a preceding address() or
address() without a preceding start().
It also fails to allow you to detect loss of bus arbitration when
there are multiple masters on the same bus.

The interface in wikipedia seems to be about the lowest possible
sensible level: (read/write byte with optional start, stop, ack/nak
bits), though it doesn't really handle loss of bus arbitration, and
there is no distinction between addresses and data.

By comparison, the AVR32 hardware performs combined write/read
sequences with a second start bit in a single operation, such as are
used to access EEPROMs. Trying to map a sequence of
i2c.start/address/write/read calls into this single operation would
require a semi-intelligent state machine to parse the sequence of
calls.

So how about changing the eLua Lua primitives for i2c to something a
bit higher level, that reflects the range of possible I2C protocol
sequences. There are only about three major variants as far as I can
see: master write, master read and combined write-read sequences, plus
a probe no-op to see if there is a slave present on a given address.
Expressing a meaningful i2c transaction in a single Lua function call
would also means that, on loss of bus arbitration during a message,
the generic i2c code could just wait for the bus to become free again
and retry the message without making client Lua cose detect and handle
this condition.
It would also mean that the more intelligent devices that perform
whole I2C operations in hardware could be used.  With the present
i2c.*() Lua interface (and the resulting C platform interface),
implementing eLua I2C on such devices is needlessly complex, even
without contempleting its DMA capabilities direct to from EEPROM...

I've been staring at this all day and have now had enough, but I
thought I'd write to let you know you're not alone, and to raise the
issue of changing the Lua module interface, since this impacts on the
C platform interface.

My own difficulty with changing the i2c interfaces is that I wouldn't
want to reimplement the existing str9 code and I don't have str9 (or
lm3s) hardware to test such hings on

Thoughts?

    M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Laurent Dufrechou Laurent Dufrechou
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

I Martin,

After trying to hack all the day I've found this interface could match lm3s
behaviour and possibly str9 too.
(need confirmation for str9)

int platform_i2c_start_and_send_address( unsigned id, u16 address, int
direction )
int platform_i2c_send_byte( unsigned id, u8 data )
int platform_i2c_recv_byte( unsigned id, int ack )
int platform_i2c_send_byte_and_stop( unsigned id, u8 data )
int platform_i2c_recv_byte_and_stop( unsigned id, int ack )

Basicaly it eliminate the low level start/stop into little higher functions.

This also can also be reduced to:
int platform_i2c_start_and_send_address( unsigned id, u16 address, int
direction )
int platform_i2c_send_byte( unsigned id, u8 data, u8 do_stop )
int platform_i2c_recv_byte( unsigned id, int ack, u8 do_stop )

Reading your post, it seems Avr32 looks like lm3s no?
Does the interface I just proposed match your use case?

What I see, is that in your case, being slow emit automatically a stop :)
So with the API I proposed, you must guarantee i2c_send_byte must be called
faster enough, and force a sleep when do_stop=true...

You can improve a little more this with:
int platform_i2c_start_and_send_address( unsigned id, u16 address, int
direction )
int platform_i2c_send_byte( unsigned id, u8* data, u8 len, u8 do_stop )
int platform_i2c_recv_byte( unsigned id, u8* data, u8 len, u8 do_stop )

with int return being being an OR of all aknowledges (thus you know ack is
ok if = 0 at end of all transfer)

This last proposal can be better if you want to implement DMA into the
functions.
And you can implement all sort of I2C transfer example:

platform_i2c_start_and_send_address(0, 0x52, i2c.transmit);
platform_i2c_send_byte(0, [0xFA], 1, false);
platform_i2c_recv_byte(0, &data, 6, true);

Will perform start + address + send 0xFA + read 6 data + stop

What cannot be done with such an interface in your case?
I think changing the interface in this way shouldn't be a big deal for STR9.
(merge start into address and call stop if u8 stop == true.)

Just I wanted to be sure it match all cases...

For me Master read/write and combination is all covered with such interface.
Here is where I am today...

I've not thought about I2C slave. (Master is enough for me now)

By the way, specifying start in send/recv_byte is not necessary as start is
only used before address.

Laurent

_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
jbsnyder jbsnyder
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

I haven't been doing much i2c programming recently, but here are a few
thoughts.  If I weren't certain of the range of usage paradigms I
would take a look at some other "mature" platform APIs:
http://mbed.org/handbook/I2C
http://arduino.cc/en/Reference/Wire
http://labdasar.ee.itb.ac.id/lab/installer/embedded/ecos-3.0.cygwin/ecos-3.0/doc/html/ref/i2c-api.html
http://code.bsd64.org/browse/freebsd/RELENG_8_0/src/sys/dev/iicbus/iic.h

One must note that, of course, there are different design constraints
for these other projects and eLua including that most of these are
using C or C++ rather than a VM-based language.

eLua's platform C API can also be a bit lower level than the Lua API
as well in case some commands might need to be bundled together for
efficiency/timing purposes. Perhaps we could aim for keeping things
fairly generic, simple and atomic at the platform C API level and then
perhaps provide some convenience or group some operations within the
higher level Lua API?

It looks like a couple of the above APIs will attach address sending
to the start/begin transmission command or the write command itself,
where write accepts a variable length message, and recv can take a
variable length reply from that address. Some of them include
semantics (like arduino) to use send to queue messages and then send
them when the transmission is ended, then ACK/NACK status is returned.

Just a few thoughts.

FWIW: I'm not too concerned about running in slave mode either at this point.

On Sun, Jun 5, 2011 at 4:58 PM, Laurent Dufrechou
<[hidden email]> wrote:

> I Martin,
>
> After trying to hack all the day I've found this interface could match lm3s
> behaviour and possibly str9 too.
> (need confirmation for str9)
>
> int platform_i2c_start_and_send_address( unsigned id, u16 address, int
> direction )
> int platform_i2c_send_byte( unsigned id, u8 data )
> int platform_i2c_recv_byte( unsigned id, int ack )
> int platform_i2c_send_byte_and_stop( unsigned id, u8 data )
> int platform_i2c_recv_byte_and_stop( unsigned id, int ack )
>
> Basicaly it eliminate the low level start/stop into little higher functions.
>
> This also can also be reduced to:
> int platform_i2c_start_and_send_address( unsigned id, u16 address, int
> direction )
> int platform_i2c_send_byte( unsigned id, u8 data, u8 do_stop )
> int platform_i2c_recv_byte( unsigned id, int ack, u8 do_stop )
>
> Reading your post, it seems Avr32 looks like lm3s no?
> Does the interface I just proposed match your use case?
>
> What I see, is that in your case, being slow emit automatically a stop :)
> So with the API I proposed, you must guarantee i2c_send_byte must be called
> faster enough, and force a sleep when do_stop=true...
>
> You can improve a little more this with:
> int platform_i2c_start_and_send_address( unsigned id, u16 address, int
> direction )
> int platform_i2c_send_byte( unsigned id, u8* data, u8 len, u8 do_stop )
> int platform_i2c_recv_byte( unsigned id, u8* data, u8 len, u8 do_stop )
>
> with int return being being an OR of all aknowledges (thus you know ack is
> ok if = 0 at end of all transfer)
>
> This last proposal can be better if you want to implement DMA into the
> functions.
> And you can implement all sort of I2C transfer example:
>
> platform_i2c_start_and_send_address(0, 0x52, i2c.transmit);
> platform_i2c_send_byte(0, [0xFA], 1, false);
> platform_i2c_recv_byte(0, &data, 6, true);
>
> Will perform start + address + send 0xFA + read 6 data + stop
>
> What cannot be done with such an interface in your case?
> I think changing the interface in this way shouldn't be a big deal for STR9.
> (merge start into address and call stop if u8 stop == true.)
>
> Just I wanted to be sure it match all cases...
>
> For me Master read/write and combination is all covered with such interface.
> Here is where I am today...
>
> I've not thought about I2C slave. (Master is enough for me now)
>
> By the way, specifying start in send/recv_byte is not necessary as start is
> only used before address.
>
> Laurent
>
> _______________________________________________
> eLua-dev mailing list
> [hidden email]
> https://lists.berlios.de/mailman/listinfo/elua-dev
>
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
BogdanM BogdanM
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

In reply to this post by Martin Guy
Hi,

On Sun, Jun 5, 2011 at 11:12 PM, Martin Guy <[hidden email]> wrote:
On 5 June 2011 17:47, Laurent Dufrechou <[hidden email]> wrote:
> Ok here is a proposal to allow the lm3s and str912 to work altogether:
>
> void platform_i2c_send_start( unsigned id )
>
> void platform_i2c_send_stop( unsigned id )
>
> int platform_i2c_send_address( unsigned id, u16 address, int direction )
>
> int platform_i2c_send_byte( unsigned id, u8 data )
>
> int platform_i2c_recv_byte( unsigned id, int ack )
>
> +
>
> int platform_i2c_send_byte_and_stop( unsigned id, u8 data )
>
> int platform_i2c_recv_byte_and_stop( unsigned id, int ack )
[...]
> int platform_i2c_start_and_send_address( unsigned id, u16 address, int
> direction )
>
>

Hi Laurent

> Am I missing any use case?
On the contrary, you have too many and inappropriate ones!

I am currently looking at implementing I2C on avr32 platforms, so
maybe we can define a common platform interface for the i2c platform
interface.

The ideal would be the smallest set of primitives that
1) allows us to generate all possible I2C protocols, reacting to (and
informing the caller about) all possible error conditions during
reception/transmission, such as lack of ACK when it is required, or
loss of bus arbitration.
2) covers all hardware implementations, from the highest level
intelligent hardware that does everything for you automatically, down
to bit-banging interfaces where you toggle and read the signals
explicitly.

An example of the lowest level can be found at
https://secure.wikimedia.org/wikipedia/en/wiki/I2C#Example_of_bit-banging_the_I2C_Master_protocol
whose user interface to this code fragment seems to consist of two
byte-oriented functions:
 bool nak = i2c_write_byte(bool send_start, bool send_stop, unsigned char data)
 unsigned char data = i2c_read_byte(bool nack, bool send_stop).


I agree with you; however, many chip manufacturers take a different approach on this. They implement a sort of hardware I2C state machine which makes things quite weird (at least for me) and hard to use. For example, on STR9/STM32 there are different flags one most look for in order to detect events such as "address byte sent OK and acknowledged" and "data sent OK and acknowledged", even though they are conceptually identical from the transmitter side. I have no idea why they've chosen to make such a simple interface so cumbersome to use (except maybe for some strange multi-master configurations that are seldom used in practice), but we don't have many choices here.
 
For the AVR32, to send data you program the 7-bit device address and
the direction bit into one control register, then write you write the
first byte of data to be transmitted into another and this second
write triggers the hardware to do the whole sequence: start, address
and the first data byte all in one go. You then have to poke more data
into the second register while the first set of data is being
transmitted if you want to transmit multiple bytes in one message:
failure to do so automatically ends the current packet with a STOP.

LOL. And I though STR9 was weird :) This is a very bad restriction, as I2C itself doesn't have this kind of timing constraint (send bytes in sequence without pauses). 
 
Unfortunately, the current elua module interface is too low level for
either of these implementations in that it has i2c.start(),
i2c.address() and i2c.write() as separate functions.  For both of
these above interfaces, the bit-banging and the AVR32, this forces the
driver to collect up information from a predefined sequence of start()
address() read() write() calls, storing them in private, persistent
data, and then use these data when the final call happens (read or
write) that gives sense to the single I2C operation.
 It also creates the question of whether the driver should detect
improper sequences of these Lua primitives, or how it should react to
them, for example, you call read() without a preceding address() or
address() without a preceding start().

I'd let the user worry about that for the moment, but you're right, it might be something worth implementing. 
 
It also fails to allow you to detect loss of bus arbitration when
there are multiple masters on the same bus.

This is on purpose :) I thought there wasn't much point in making the interface more complex for a situation so rare in practice (the multiple master configuration).
 
The interface in wikipedia seems to be about the lowest possible
sensible level: (read/write byte with optional start, stop, ack/nak
bits), though it doesn't really handle loss of bus arbitration, and
there is no distinction between addresses and data.

By comparison, the AVR32 hardware performs combined write/read
sequences with a second start bit in a single operation, such as are
used to access EEPROMs. Trying to map a sequence of
i2c.start/address/write/read calls into this single operation would
require a semi-intelligent state machine to parse the sequence of
calls.

The low-level interface is as generic as it gets (as usual). The higher level interfaces I read here (LM3S included) seem to address specifically the common use scenarios over I2C interfaces:

1. write: send start, send I2C address+w, send device register address, send bytes to write, send stop
2. read: send start, send I2C address+w, send device register address, send start, send I2C address+r, read and ACK everything except the last byte, send stop

It limits the functionality a bit (for example the master could in theory send a START at any time to a slave in order to reset it if anything goes wrong with the comunication) but since this is what we have in most hardware it seems the right thing to implement.
 

So how about changing the eLua Lua primitives for i2c to something a
bit higher level, that reflects the range of possible I2C protocol
sequences. There are only about three major variants as far as I can
see: master write, master read and combined write-read sequences, plus
a probe no-op to see if there is a slave present on a given address.

Right, thanks, I forgot about the last one. It is also used to detect end of operation while writing data to an I2C EEPROM (while the memory writes the data it won't acknowledge its slave addres). So we agree, there are 3 basic operations that should be taken into consideration: read, write and query. 
And yes, in the light of the information from you and Laurent I agree that we must change our I2C platform interface. Do you agree with this format (high level description):

1. init( id, speed )
2. write( id, i2caddr, devaddr, bytes ) (could be simply 'write( id, i2caddr, bytes )' but I want to make it symetrical with number 3 below).
3. read( id, i2caddr, devaddr, numbytes ) ("devaddr" could have a special value meaning "don't send an address" as some devices don't need an address for a read operation (for example an I2C EEPROM which supports sequential reads: specify an address just on the first 'read' command, after which the device's internal address register increments automatically). I'm still not sure if this optimization is just asking for trouble or it can actually be implemented on all (or at least most) of our platforms).
4. query (id, i2caddr)
 
Expressing a meaningful i2c transaction in a single Lua function call
would also means that, on loss of bus arbitration during a message,
the generic i2c code could just wait for the bus to become free again
and retry the message without making client Lua cose detect and handle
this condition.

Absolutely.
 
It would also mean that the more intelligent devices that perform
whole I2C operations in hardware could be used.  With the present
i2c.*() Lua interface (and the resulting C platform interface),
implementing eLua I2C on such devices is needlessly complex, even
without contempleting its DMA capabilities direct to from EEPROM...

I've been staring at this all day and have now had enough, but I
thought I'd write to let you know you're not alone, and to raise the
issue of changing the Lua module interface, since this impacts on the
C platform interface.

My own difficulty with changing the i2c interfaces is that I wouldn't
want to reimplement the existing str9 code and I don't have str9 (or
lm3s) hardware to test such hings on

Don't worry about that, I'll be happy to implement them myself once we agree on an interface. I'm actually glad this happens now, when the only (official) implementation of I2C is on STR9 :)

Best,
Bogdan


_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
BogdanM BogdanM
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)



On Mon, Jun 6, 2011 at 9:21 AM, Bogdan Marinescu <[hidden email]> wrote:
Hi,

On Sun, Jun 5, 2011 at 11:12 PM, Martin Guy <[hidden email]> wrote:
On 5 June 2011 17:47, Laurent Dufrechou <[hidden email]> wrote:
> Ok here is a proposal to allow the lm3s and str912 to work altogether:
>
> void platform_i2c_send_start( unsigned id )
>
> void platform_i2c_send_stop( unsigned id )
>
> int platform_i2c_send_address( unsigned id, u16 address, int direction )
>
> int platform_i2c_send_byte( unsigned id, u8 data )
>
> int platform_i2c_recv_byte( unsigned id, int ack )
>
> +
>
> int platform_i2c_send_byte_and_stop( unsigned id, u8 data )
>
> int platform_i2c_recv_byte_and_stop( unsigned id, int ack )
[...]
> int platform_i2c_start_and_send_address( unsigned id, u16 address, int
> direction )
>
>

Hi Laurent

> Am I missing any use case?
On the contrary, you have too many and inappropriate ones!

I am currently looking at implementing I2C on avr32 platforms, so
maybe we can define a common platform interface for the i2c platform
interface.

The ideal would be the smallest set of primitives that
1) allows us to generate all possible I2C protocols, reacting to (and
informing the caller about) all possible error conditions during
reception/transmission, such as lack of ACK when it is required, or
loss of bus arbitration.
2) covers all hardware implementations, from the highest level
intelligent hardware that does everything for you automatically, down
to bit-banging interfaces where you toggle and read the signals
explicitly.

An example of the lowest level can be found at
https://secure.wikimedia.org/wikipedia/en/wiki/I2C#Example_of_bit-banging_the_I2C_Master_protocol
whose user interface to this code fragment seems to consist of two
byte-oriented functions:
 bool nak = i2c_write_byte(bool send_start, bool send_stop, unsigned char data)
 unsigned char data = i2c_read_byte(bool nack, bool send_stop).


I agree with you; however, many chip manufacturers take a different approach on this. They implement a sort of hardware I2C state machine which makes things quite weird (at least for me) and hard to use. For example, on STR9/STM32 there are different flags one most look for in order to detect events such as "address byte sent OK and acknowledged" and "data sent OK and acknowledged", even though they are conceptually identical from the transmitter side. I have no idea why they've chosen to make such a simple interface so cumbersome to use (except maybe for some strange multi-master configurations that are seldom used in practice), but we don't have many choices here.
 
For the AVR32, to send data you program the 7-bit device address and
the direction bit into one control register, then write you write the
first byte of data to be transmitted into another and this second
write triggers the hardware to do the whole sequence: start, address
and the first data byte all in one go. You then have to poke more data
into the second register while the first set of data is being
transmitted if you want to transmit multiple bytes in one message:
failure to do so automatically ends the current packet with a STOP.

LOL. And I though STR9 was weird :) This is a very bad restriction, as I2C itself doesn't have this kind of timing constraint (send bytes in sequence without pauses). 
 
Unfortunately, the current elua module interface is too low level for
either of these implementations in that it has i2c.start(),
i2c.address() and i2c.write() as separate functions.  For both of
these above interfaces, the bit-banging and the AVR32, this forces the
driver to collect up information from a predefined sequence of start()
address() read() write() calls, storing them in private, persistent
data, and then use these data when the final call happens (read or
write) that gives sense to the single I2C operation.
 It also creates the question of whether the driver should detect
improper sequences of these Lua primitives, or how it should react to
them, for example, you call read() without a preceding address() or
address() without a preceding start().

I'd let the user worry about that for the moment, but you're right, it might be something worth implementing. 
 
It also fails to allow you to detect loss of bus arbitration when
there are multiple masters on the same bus.

This is on purpose :) I thought there wasn't much point in making the interface more complex for a situation so rare in practice (the multiple master configuration).
 
The interface in wikipedia seems to be about the lowest possible
sensible level: (read/write byte with optional start, stop, ack/nak
bits), though it doesn't really handle loss of bus arbitration, and
there is no distinction between addresses and data.

By comparison, the AVR32 hardware performs combined write/read
sequences with a second start bit in a single operation, such as are
used to access EEPROMs. Trying to map a sequence of
i2c.start/address/write/read calls into this single operation would
require a semi-intelligent state machine to parse the sequence of
calls.

The low-level interface is as generic as it gets (as usual). The higher level interfaces I read here (LM3S included) seem to address specifically the common use scenarios over I2C interfaces:

1. write: send start, send I2C address+w, send device register address, send bytes to write, send stop
2. read: send start, send I2C address+w, send device register address, send start, send I2C address+r, read and ACK everything except the last byte, send stop

It limits the functionality a bit (for example the master could in theory send a START at any time to a slave in order to reset it if anything goes wrong with the comunication) but since this is what we have in most hardware it seems the right thing to implement.
 

So how about changing the eLua Lua primitives for i2c to something a
bit higher level, that reflects the range of possible I2C protocol
sequences. There are only about three major variants as far as I can
see: master write, master read and combined write-read sequences, plus
a probe no-op to see if there is a slave present on a given address.

Right, thanks, I forgot about the last one. It is also used to detect end of operation while writing data to an I2C EEPROM (while the memory writes the data it won't acknowledge its slave addres). So we agree, there are 3 basic operations that should be taken into consideration: read, write and query. 
And yes, in the light of the information from you and Laurent I agree that we must change our I2C platform interface. Do you agree with this format (high level description):

1. init( id, speed )
2. write( id, i2caddr, devaddr, bytes ) (could be simply 'write( id, i2caddr, bytes )' but I want to make it symetrical with number 3 below).
3. read( id, i2caddr, devaddr, numbytes ) ("devaddr" could have a special value meaning "don't send an address" as some devices don't need an address for a read operation (for example an I2C EEPROM which supports sequential reads: specify an address just on the first 'read' command, after which the device's internal address register increments automatically). I'm still not sure if this optimization is just asking for trouble or it can actually be implemented on all (or at least most) of our platforms).

Just occured to me that we also need to take into account the size of 'devaddr' somehow, so the above is not good enough. Still thinking about a good solution.

Best,
Bogdan 


_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Martin Guy Martin Guy
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

In reply to this post by jbsnyder
On 6 June 2011 05:17, James Snyder <[hidden email]> wrote:
> I would take a look at some other "mature" platform APIs:

Many thanks for these suggestions. Whether they are mature or not
doesn't necessarily mean they are well-designed. They may just be old
;-).
In fact, they are all totally different:

> http://mbed.org/handbook/I2C

frequency() read() write() start() stop().   The same as eLua's current.

> http://arduino.cc/en/Reference/Wire

bizarre.

> http://labdasar.ee.itb.ac.id/lab/installer/embedded/ecos-3.0.cygwin/ecos-3.0/doc/html/ref/i2c-api.html

Two functions:
uint32 cyg_i2c_tx(const cyg_i2c_device* device, const cyg_uint8*
tx_data, cyg_uint32 count)
uint32 cyg_i2c_rx(const cyg_i2c_device* device, cyg_uint8* rx_data,
cyg_uint32 count);
where cyg_i2c_device encodes "which i2c bus", "slave address" and "speed"
looks like the closest yet... until you try to do a write-read
combined message. Then you have to use a different, big hairy
transaction-oriented interface, because the "simple" interface above
doesn't allow tx-rx without an intervening stop.

> http://code.bsd64.org/browse/freebsd/RELENG_8_0/src/sys/dev/iicbus/iic.h

start, stop, read, write, rdwr(?), and repeated-start.  Again, too
low-level for high level hardware

> eLua's platform C API can also be a bit lower level than the Lua API
> as well in case some commands might need to be bundled together for
> efficiency/timing purposes.

No, I need to keep the whole I2C message, including a combined
write-read sequence, intact down to the platform level, otherwise the
hardware generates a STOP automatically if the read request doesn't
arrive in time.
If your basic primitives are start() stop() readbyte() writebyte(),
the higher level description can easily be implemented in terms of
them.  But if the platform interface is at a lower level than this, it
is quite difficlt to reconstruct the original combined message by
spotting a special sequence of starts, tx and rx messages.  You would
still end up having to buffer everything from the start to the first
stop and then performing the whole operation at that time.

> Perhaps we could aim for keeping things
> fairly generic, simple and atomic at the platform C API level

Of these words I think understand "atomic": it means "a single
operation that either completes in its entirety or doesn't happen at
all", which suggests keeping entire I2C messages as single objects all
the way down.

 and then
> perhaps provide some convenience or group some operations within the
> higher level Lua API?


> Some of them include
> semantics (like arduino) to use send to queue messages and then send
> them when the transmission is ended, then ACK/NACK status is returned.

Exactly what I was hoping to avoid, since you have to copy and buffer
a potentially infinite amount of data, then send it when they say
"stop", which means you have to be able to allocate arbitrary amounts
of memory inside the device driver.

> FWIW: I'm not too concerned about running in slave mode either at this point.

Nor am I personally, but if we are designing a world-class API that
will be with eLua forever, maybe thinking about it would be a good
idea at this point.  I gather that the buttons on the Mizar32 LCD
display are supposed to work in master mode with the AVR32 SoC as
slave so that you don't have to do busy-wait polling to detect button
presses (you prod the button-controlling PIC, go into slave mode and
then wait for the buttons to poke you).  However, this may just be a
case of poor design, and the LCD/button hardware module is not one of
those scheduled to be ready for the initial product release, so we can
still review its design.

   M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
jbsnyder jbsnyder
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

On Mon, Jun 6, 2011 at 8:09 AM, Martin Guy <[hidden email]> wrote:
> On 6 June 2011 05:17, James Snyder <[hidden email]> wrote:
>> I would take a look at some other "mature" platform APIs:
>
> Many thanks for these suggestions. Whether they are mature or not
> doesn't necessarily mean they are well-designed. They may just be old
> ;-).

True.  I just meant they probably had been abused a little and maybe
therefore could cover a variety of use cases.

> In fact, they are all totally different:
>
>> http://mbed.org/handbook/I2C
>
> frequency() read() write() start() stop().   The same as eLua's current.

Well, except for the separate address command.  If you look at the
usage example the read and write take the address, and length of
command as well.

>
>> http://arduino.cc/en/Reference/Wire
>
> bizarre.

Kind of agree here, mentioned it though since it is the poster child
hobbyist microcontroller platform and it has been replicated onto
other platforms other than the 8-bit AVR now.  That said I prefer the
mbed one to it.

>
>> http://labdasar.ee.itb.ac.id/lab/installer/embedded/ecos-3.0.cygwin/ecos-3.0/doc/html/ref/i2c-api.html
>
> Two functions:
> uint32 cyg_i2c_tx(const cyg_i2c_device* device, const cyg_uint8*
> tx_data, cyg_uint32 count)
> uint32 cyg_i2c_rx(const cyg_i2c_device* device, cyg_uint8* rx_data,
> cyg_uint32 count);
> where cyg_i2c_device encodes "which i2c bus", "slave address" and "speed"
> looks like the closest yet... until you try to do a write-read
> combined message. Then you have to use a different, big hairy
> transaction-oriented interface, because the "simple" interface above
> doesn't allow tx-rx without an intervening stop.

Right, this actually also looks fairly close to one of the models
supported by mbed.

>
>> http://code.bsd64.org/browse/freebsd/RELENG_8_0/src/sys/dev/iicbus/iic.h
>
> start, stop, read, write, rdwr(?), and repeated-start.  Again, too
> low-level for high level hardware
>
>> eLua's platform C API can also be a bit lower level than the Lua API
>> as well in case some commands might need to be bundled together for
>> efficiency/timing purposes.
>
> No, I need to keep the whole I2C message, including a combined
> write-read sequence, intact down to the platform level, otherwise the
> hardware generates a STOP automatically if the read request doesn't
> arrive in time.
> If your basic primitives are start() stop() readbyte() writebyte(),
> the higher level description can easily be implemented in terms of
> them.  But if the platform interface is at a lower level than this, it
> is quite difficlt to reconstruct the original combined message by
> spotting a special sequence of starts, tx and rx messages.  You would
> still end up having to buffer everything from the start to the first
> stop and then performing the whole operation at that time.

I think I understand this better having read some of the additional
messages from you and Bogdan about how the different hardware
implementations are somewhat bizarre in automating or requiring
certain behaviors and timings. *sigh*

In light of this it sounds like the eLua and C APIs might need to be
fairly high level, leaving the platform variability to the hardware
platform and driver.

>
>> Perhaps we could aim for keeping things
>> fairly generic, simple and atomic at the platform C API level
>
> Of these words I think understand "atomic": it means "a single
> operation that either completes in its entirety or doesn't happen at
> all", which suggests keeping entire I2C messages as single objects all
> the way down.

Simple/single all or nothing operations that don't do more than they
have to, minimizing side effects.  I think what I was envisioning for
this originally wasn't quite right, but what you're saying here
probably sounds about right.  Keep the entire message as a single
object or transaction all the way down and report associated
error/status messages (ACK/NACK) as necessary if the attempt fails at
some stage.

>
>  and then
>> perhaps provide some convenience or group some operations within the
>> higher level Lua API?
>
>
>> Some of them include
>> semantics (like arduino) to use send to queue messages and then send
>> them when the transmission is ended, then ACK/NACK status is returned.
>
> Exactly what I was hoping to avoid, since you have to copy and buffer
> a potentially infinite amount of data, then send it when they say
> "stop", which means you have to be able to allocate arbitrary amounts
> of memory inside the device driver.

Yeah, I don't think this is a great idea either.  Although as with the
single operation, you could return an error on the queuing telling the
user that the message or buffer is too long. What I dislike more
however is making some separate state object to keep track of in
between queuing operations that is inconsistent with how the rest of
eLua behaves for modules like this.

>
>> FWIW: I'm not too concerned about running in slave mode either at this point.
>
> Nor am I personally, but if we are designing a world-class API that
> will be with eLua forever, maybe thinking about it would be a good
> idea at this point.

Indeed.  I think we should plan for it in the API, I'm just not as
worried about getting that tested and implemented on all platforms at
this stage.  I'm not sure if that makes it difficult to design in or
not :-)

>  I gather that the buttons on the Mizar32 LCD
> display are supposed to work in master mode with the AVR32 SoC as
> slave so that you don't have to do busy-wait polling to detect button
> presses (you prod the button-controlling PIC, go into slave mode and
> then wait for the buttons to poke you).  However, this may just be a
> case of poor design, and the LCD/button hardware module is not one of
> those scheduled to be ready for the initial product release, so we can
> still review its design.

I'm not sure how common the use case is for slave mode.  Based on
looking at some of the APIs mentioned previously it sounds like it
wouldn't be terribly hard to incorporate the functionality.  As for
Mizar32's use of it, that's another matter :-)

>
>   M
> _______________________________________________
> eLua-dev mailing list
> [hidden email]
> https://lists.berlios.de/mailman/listinfo/elua-dev
>
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Martin Guy Martin Guy
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

In reply to this post by BogdanM
On 6 June 2011 08:21, Bogdan Marinescu <[hidden email]> wrote:
> It limits the functionality a bit (for example the master could in theory
> send a START at any time to a slave in order to reset it if anything goes
> wrong with the comunication)

True. In reality, I2C does not define the data protocol format at all
- that is left to the device manufacturers to decide.

It seems to me that we're looking at the three controllers we know,
the handful of devices we have seen, and are trying to find common
patterns between them. The risk with this is the devices we don't know
about - and if you're implementing a generic I2C interface for a
world-class language, we're only likely to succeed in covering the few
cases we've seen.

A different way to approach the problem is to start from the
definition of I2C protocol and make primitives that reflect that,
ensuring that we cover all possibilities including devices that are
not among those we happen to have used so far.

Wikipedia says:

----------
There are four potential modes of operation for a given bus device,
although most devices only use a single role and its two modes:
    master transmit — master node is sending data to a slave
    master receive — master node is receiving data from a slave
    slave transmit — slave node is sending data to the master
    slave receive — slave node is receiving data from the master
----------

Ok, nothing very new here.  Then it says:

----------
I²C defines three basic types of messages, each of which begins with a
START and ends with a STOP:
    Single message where a master writes data to a slave;
    Single message where a master reads data from a slave;
    Combined messages, where a master issues at least two reads and/or
writes to one or more slaves.

In a combined message, each read or write begins with a START and the
slave address. After the first START, these are also called repeated
START bits; repeated START bits are not preceded by STOP bits, which
is how slaves know the next transfer is part of the same message.
----------

Then there are some other protocols built using I2C which further
limit the message structures:

----------
Pure I²C systems support arbitrary message structures.
SMBus is restricted to nine of those structures, such as read word N
and write word N, involving a single slave.
PMBus extends SMBus with a Group protocol, allowing multiple such
SMBus transactions to be sent in one combined message. The terminating
STOP indicates when those grouped actions should take effect.
----------

Let's assume for the moment that we want to provide an I2C interface,
not an SMBus or PMBus one. If we get our I2C primitives right, we can
be sure that these more highly defined protocols can be implemented as
a Lua library that uses it if necessary.

> 1. init( id, speed )
> 2. write( id, i2caddr, devaddr, bytes ) (could be simply 'write( id,
> i2caddr, bytes )' but I want to make it symetrical with number 3 below).

Why?

> 3. read( id, i2caddr, devaddr, numbytes )
> 4. query (id, i2caddr)

I had been considering the register address inside the slave device as
just part of the transmitted data, not as part of I2C protocol.
In particular, the device register address, in the examples I've seen,
can be one, two or three bytes, so you'd need to include a devaddr_len
field as well. But that has nothing to do with I2C, just with the way
that certain devices we have seen use it.
I2C does not define the data format in use; the
write-address-then-read-data is just the way that one or two specific
devices use it.

That said, the AVR32 hardware also mimics this, allowing you to send
one, two or three bytes of addressing info and then automatically
sending a second start bit and address and switching into reading
mode.

10-bit slave addressing (instead of 7-bit slave addressing) is another
issue.  It is done thus:
Slave address = 1 1 1 1 0 a9 a8 R/W
First data byte = a7 a6 a5 a4 a3 a2 a1 a0
(Slave addresses 1111XXXX are reserved for extensions like this)

But both of these can be covered by a 7-bit address and arbitrary
data, leaving it up to the user:
to put the device register address at the start of their data
packet(s) or, for 10-bit addressing, to specify the reserved 11110AAR
address and put the other byte of the address into the start of the
data.
Again, if the Lua I2C interface is right, we know that we will be able
to write Lua functions to cover these cases.


So what does that leave us with?  Well, the I2C specification, which
has three primitives:
    Single message where a master writes data to a slave;
    Single message where a master reads data from a slave;
    Combined messages, where a master issues at least two reads and/or
writes to one or more slaves.

The "one or more slaves" is a bit surprising: it implies that a
combined message with multiple starts can address several different
slavesand that we've only seen examples that use the same slave
address (read device register, or read EEPROM contents at a specified
address).

It's asp interesting that the first two cases, the single messages,
are special cases of the combined messages, with only one data item
inside them, so if we provide a single combined message primitive,
that allows us to do everything I2C.
Anything less that this only addresses a subset of I2C devices - the
ones that we happen to have seen.

What does that suggest for a Lua interface? Well, we can encoding a
single message as a table and the combined message either as a table
of single messages or as a varargs list of them.

The single message would contain the 7-bit slave address, a bool
saying whether we are reading or writing (true for "reading", since
the I2C "read/write" bit is high for a read).
If it's a read, we'll then need the maximum number of bytes to read.
It it's a write, we'll then need the number of bytes to send and the
data itself, which can both be encoded in a singLua string.

The return value for each message would be:
For each read message, the number of bytes actually read and the data
itself (these two can be expressed as a string, since Lua strings are
8-bit clean and encode the string length)
For each write message, the number of bytes actually written.

The combined messages are then either a table of single messages
returning a table of results, or a varargs list of them returning a
varargs list of results.
I don't have enough practice with Lua to know which is more
convenient, or if it's practical to accept both variants.

So, as an example, to write a byte value to a location in a 24LC32
EEPROM we need a single message, For clarity, I'll use the vargargs
and multiple return syntax:

function eeprom_write_byte(id, chip_select, address, datum)
  -- id = i2c bus id
  -- chip_select = 0..7
  -- address = 0..65535
  -- datum = 0..255 (a number)
  local slave_address = 0x50 + chip_select
  local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
  nwritten = i2c.transfer( id, [ i2c.WRITE, slave_address,
string.char(ah, al, datum) ] )
  assert( nwritten == 3 )
end

To read a byte from such an EEPROM we need a combined message with a
write and a read:

function eeprom_read_byte(id, chip_select, address)
  -- id = i2c bus id
  -- chip_select = 0..7
  -- address = 0..65535
  local slave_address = 0x50 + chip_select
  local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
  wresult, rresult = i2c.transfer( id,
    [ i2c.WRITE, slave_address, string.char(ah, al) ],
    [ i2c.READ, slave_address, 1 ]
  )
  if ( wresult ~= 2 or #rresult ~= 1) then return nil end
  return string.code(rresult)
end

Here, I'm using 7-bit addresses so write 7-bit address 0x50 to encode
the 8-bit EEPROM command byte 1010aaaR.

The "query" or "probe" operation is implemented in the example code
I've seen as a write message with 0 data in it, which succeeds or
fails according to whether the slave address gets acked or not. To add
this to the above model, we could say that if nobody acknowledges the
command byte (containing the slave address), we return nil instead of
0.


Well, thanks for following my thought process, but the result looks
very different from all the other eLua module interfaces, none of
which take tables as parameters, and none of which return multiple
results.
Anything less than this is not capable of expressing the full I2C
protocol, so the next choice is whether we want to be able to express
the I2C protocol or just the common cases of it for the devices we
know?

I know the rest of the eLua modules just provide the things that most
people want most frequently with minimum fuss, so restricting the I2C
interface to the common cases that we know is more in keeping with the
other eLua module interfaces.

Another option is simply not to support combined data packets.   I see
you can use the 24LC32 EEPROMs just by writing the memory address in
one packet with no data bytes to write, then issuing a "read" command
in a second and it remembers the memory address from one operation to
the next.
The DS1337 Real Time Clock can be used the same way and on the AVR32
we can just not use their clever self-reversing hardware.

Does anyone know of an I2C device that *needs* combined messages (with
repeated start bits) to work?  If not, Bogdan's suggestion is close
(modulo the strange desire for read/write function parameter symmetry
and without the variable-length device register address, which is just
data).

If we can dump combine messages, we can use:

i2c.setspeed(id, speed)
id = which I2C bus to talk on
speed = a number in Hz.
Returns: the actual speed set (or nil on error?)

i2c.write(id, address, data)
address = the 7-bit slave address
data = a string
Returns: an integer if the write was successful, reflecting the number
of bytes written
        nil if the slave did not respond to its address

i2c.read(id, address, maxbytes)
maxbytes = the maximum number of bytes to read (and acknowledge)
Returns: a string containing the data that was returned.
        nil if the address byte was not acknowledged

It would then be an option to provide packet assemblers to do the
other things that can be implemented using these:
- probe(id, address) is write(id, address, "")
- read/write value from/to device register with address of length 1, 2
or 3 bytes
- other?
but these can be written in Lua or C, as you prefer, using the
single-message read/write primitives.

The EEPROM examples then become:

-- Write a byte to a 24LS32, returning true on success, false on failure
function eeprom_write_byte(id, chip_select, address, datum)
  -- id = i2c bus id
  -- chip_select = 0..7
  -- address = 0..65535
  -- datum = 0..255 (a number)
  local slave_address = 0x50 + chip_select
  local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
  return ( i2c.write( id, slave_address, string.char(ah, al, datum) ) == 3 )
end

-- Read a byte from a 24LS32, returning a 1-byte string on success or
nil on failure
function eeprom_read_byte(id, chip_select, address)
  -- id = i2c bus id
  -- chip_select = 0..7
  -- address = 0..65535
  local slave_address = 0x50 + chip_select
  local ah, al = bit.rshift( address, 8 ), bit.band( address, 255 )
  if i2c.write( id, slave_address, string.char(ah, al) ) ~= 2 then
    return nil
  end
  local result = i2c.read( id, slave_address, 1 )
  if #result ~= 1 then return nil end
  return result
end


>> My own difficulty with changing the i2c interfaces is that I wouldn't
>> want to reimplement the existing str9 code and I don't have str9 (or
>> lm3s) hardware to test such hings on
>
> Don't worry about that, I'll be happy to implement them myself once we agree
> on an interface. I'm actually glad this happens now, when the only
> (official) implementation of I2C is on STR9 :)

That's a relief!

    M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Tony-12 Tony-12
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

Wouldn't the I2C bus specifications be a better starting place than Wikipedia?  It looks like they're available via here

What would be really nice is an I2C book equivalent to Wilfred Voss's  A Comprehensive Guide to the Controller Area Network, which discusses "gotchas" in the CAN standards document.  I did some searching, and didn't find anything equivalent.

--Tony

On Mon, Jun 6, 2011 at 11:05 AM, Martin Guy <[hidden email]> wrote:
On 6 June 2011 08:21, Bogdan Marinescu <[hidden email]> wrote:
> It limits the functionality a bit (for example the master could in theory
> send a START at any time to a slave in order to reset it if anything goes
> wrong with the comunication)

True. In reality, I2C does not define the data protocol format at all
- that is left to the device manufacturers to decide.

It seems to me that we're looking at the three controllers we know,
the handful of devices we have seen, and are trying to find common
patterns between them. The risk with this is the devices we don't know
about - and if you're implementing a generic I2C interface for a
world-class language, we're only likely to succeed in covering the few
cases we've seen.

A different way to approach the problem is to start from the
definition of I2C protocol and make primitives that reflect that,
ensuring that we cover all possibilities including devices that are
not among those we happen to have used so far.

Wikipedia says:

----------
There are four potential modes of operation for a given bus device,
although most devices only use a single role and its two modes:
   master transmit — master node is sending data to a slave
   master receive — master node is receiving data from a slave
   slave transmit — slave node is sending data to the master
   slave receive — slave node is receiving data from the master
----------

Ok, nothing very new here.  Then it says:

----------
I²C defines three basic types of messages, each of which begins with a
START and ends with a STOP:
   Single message where a master writes data to a slave;
   Single message where a master reads data from a slave;
   Combined messages, where a master issues at least two reads and/or
writes to one or more slaves.

In a combined message, each read or write begins with a START and the
slave address. After the first START, these are also called repeated
START bits; repeated START bits are not preceded by STOP bits, which
is how slaves know the next transfer is part of the same message.
----------

Then there are some other protocols built using I2C which further
limit the message structures:

----------
Pure I²C systems support arbitrary message structures.
SMBus is restricted to nine of those structures, such as read word N
and write word N, involving a single slave.
PMBus extends SMBus with a Group protocol, allowing multiple such
SMBus transactions to be sent in one combined message. The terminating
STOP indicates when those grouped actions should take effect.
----------

Let's assume for the moment that we want to provide an I2C interface,
not an SMBus or PMBus one. If we get our I2C primitives right, we can
be sure that these more highly defined protocols can be implemented as
a Lua library that uses it if necessary.

> 1. init( id, speed )
> 2. write( id, i2caddr, devaddr, bytes ) (could be simply 'write( id,
> i2caddr, bytes )' but I want to make it symetrical with number 3 below).

Why?

> 3. read( id, i2caddr, devaddr, numbytes )
> 4. query (id, i2caddr)

I had been considering the register address inside the slave device as
just part of the transmitted data, not as part of I2C protocol.
In particular, the device register address, in the examples I've seen,
can be one, two or three bytes, so you'd need to include a devaddr_len
field as well. But that has nothing to do with I2C, just with the way
that certain devices we have seen use it.
I2C does not define the data format in use; the
write-address-then-read-data is just the way that one or two specific
devices use it.

That said, the AVR32 hardware also mimics this, allowing you to send
one, two or three bytes of addressing info and then automatically
sending a second start bit and address and switching into reading
mode.

10-bit slave addressing (instead of 7-bit slave addressing) is another
issue.  It is done thus:
Slave address = 1 1 1 1 0 a9 a8 R/W
First data byte = a7 a6 a5 a4 a3 a2 a1 a0
(Slave addresses 1111XXXX are reserved for extensions like this)

But both of these can be covered by a 7-bit address and arbitrary
data, leaving it up to the user:
to put the device register address at the start of their data
packet(s) or, for 10-bit addressing, to specify the reserved 11110AAR
address and put the other byte of the address into the start of the
data.
Again, if the Lua I2C interface is right, we know that we will be able
to write Lua functions to cover these cases.


So what does that leave us with?  Well, the I2C specification, which
has three primitives:
   Single message where a master writes data to a slave;
   Single message where a master reads data from a slave;
   Combined messages, where a master issues at least two reads and/or
writes to one or more slaves.

The "one or more slaves" is a bit surprising: it implies that a
combined message with multiple starts can address several different
slavesand that we've only seen examples that use the same slave
address (read device register, or read EEPROM contents at a specified
address).

It's asp interesting that the first two cases, the single messages,
are special cases of the combined messages, with only one data item
inside them, so if we provide a single combined message primitive,
that allows us to do everything I2C.
Anything less that this only addresses a subset of I2C devices - the
ones that we happen to have seen.

What does that suggest for a Lua interface? Well, we can encoding a
single message as a table and the combined message either as a table
of single messages or as a varargs list of them.

The single message would contain the 7-bit slave address, a bool
saying whether we are reading or writing (true for "reading", since
the I2C "read/write" bit is high for a read).
If it's a read, we'll then need the maximum number of bytes to read.
It it's a write, we'll then need the number of bytes to send and the
data itself, which can both be encoded in a singLua string.

The return value for each message would be:
For each read message, the number of bytes actually read and the data
itself (these two can be expressed as a string, since Lua strings are
8-bit clean and encode the string length)
For each write message, the number of bytes actually written.

The combined messages are then either a table of single messages
returning a table of results, or a varargs list of them returning a
varargs list of results.
I don't have enough practice with Lua to know which is more
convenient, or if it's practical to accept both variants.

So, as an example, to write a byte value to a location in a 24LC32
EEPROM we need a single message, For clarity, I'll use the vargargs
and multiple return syntax:

function eeprom_write_byte(id, chip_select, address, datum)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 -- datum = 0..255 (a number)
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
 nwritten = i2c.transfer( id, [ i2c.WRITE, slave_address,
string.char(ah, al, datum) ] )
 assert( nwritten == 3 )
end

To read a byte from such an EEPROM we need a combined message with a
write and a read:

function eeprom_read_byte(id, chip_select, address)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
 wresult, rresult = i2c.transfer( id,
   [ i2c.WRITE, slave_address, string.char(ah, al) ],
   [ i2c.READ, slave_address, 1 ]
 )
 if ( wresult ~= 2 or #rresult ~= 1) then return nil end
 return string.code(rresult)
end

Here, I'm using 7-bit addresses so write 7-bit address 0x50 to encode
the 8-bit EEPROM command byte 1010aaaR.

The "query" or "probe" operation is implemented in the example code
I've seen as a write message with 0 data in it, which succeeds or
fails according to whether the slave address gets acked or not. To add
this to the above model, we could say that if nobody acknowledges the
command byte (containing the slave address), we return nil instead of
0.


Well, thanks for following my thought process, but the result looks
very different from all the other eLua module interfaces, none of
which take tables as parameters, and none of which return multiple
results.
Anything less than this is not capable of expressing the full I2C
protocol, so the next choice is whether we want to be able to express
the I2C protocol or just the common cases of it for the devices we
know?

I know the rest of the eLua modules just provide the things that most
people want most frequently with minimum fuss, so restricting the I2C
interface to the common cases that we know is more in keeping with the
other eLua module interfaces.

Another option is simply not to support combined data packets.   I see
you can use the 24LC32 EEPROMs just by writing the memory address in
one packet with no data bytes to write, then issuing a "read" command
in a second and it remembers the memory address from one operation to
the next.
The DS1337 Real Time Clock can be used the same way and on the AVR32
we can just not use their clever self-reversing hardware.

Does anyone know of an I2C device that *needs* combined messages (with
repeated start bits) to work?  If not, Bogdan's suggestion is close
(modulo the strange desire for read/write function parameter symmetry
and without the variable-length device register address, which is just
data).

If we can dump combine messages, we can use:

i2c.setspeed(id, speed)
id = which I2C bus to talk on
speed = a number in Hz.
Returns: the actual speed set (or nil on error?)

i2c.write(id, address, data)
address = the 7-bit slave address
data = a string
Returns: an integer if the write was successful, reflecting the number
of bytes written
       nil if the slave did not respond to its address

i2c.read(id, address, maxbytes)
maxbytes = the maximum number of bytes to read (and acknowledge)
Returns: a string containing the data that was returned.
       nil if the address byte was not acknowledged

It would then be an option to provide packet assemblers to do the
other things that can be implemented using these:
- probe(id, address) is write(id, address, "")
- read/write value from/to device register with address of length 1, 2
or 3 bytes
- other?
but these can be written in Lua or C, as you prefer, using the
single-message read/write primitives.

The EEPROM examples then become:

-- Write a byte to a 24LS32, returning true on success, false on failure
function eeprom_write_byte(id, chip_select, address, datum)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 -- datum = 0..255 (a number)
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
 return ( i2c.write( id, slave_address, string.char(ah, al, datum) ) == 3 )
end

-- Read a byte from a 24LS32, returning a 1-byte string on success or
nil on failure
function eeprom_read_byte(id, chip_select, address)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255 )
 if i2c.write( id, slave_address, string.char(ah, al) ) ~= 2 then
   return nil
 end
 local result = i2c.read( id, slave_address, 1 )
 if #result ~= 1 then return nil end
 return result
end


>> My own difficulty with changing the i2c interfaces is that I wouldn't
>> want to reimplement the existing str9 code and I don't have str9 (or
>> lm3s) hardware to test such hings on
>
> Don't worry about that, I'll be happy to implement them myself once we agree
> on an interface. I'm actually glad this happens now, when the only
> (official) implementation of I2C is on STR9 :)

That's a relief!

   M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev


_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Martin Guy Martin Guy
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

On 6 June 2011 23:24, Tony <[hidden email]> wrote:
> Wouldn't the I2C bus specifications be a better starting place than
> Wikipedia?  It looks like they're available via here

Nice. I'm assuming the wikipedia entry is a condensation of that. Am I wrong?

> What would be really nice is an I2C book equivalent to Wilfred Voss's  A
> Comprehensive Guide to the Controller Area Network, which discusses
> "gotchas" in the CAN standards document.  I did some searching, and didn't
> find anything equivalent.

CAN?  ??

Glad you enjoyed my mind-dump :)

    M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
BogdanM BogdanM
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

In reply to this post by Tony-12
Hi,

On Tue, Jun 7, 2011 at 12:24 AM, Tony <[hidden email]> wrote:
Wouldn't the I2C bus specifications be a better starting place than Wikipedia?  It looks like they're available via here

You are right, however Wikipedia is an easier read (at least for me) than a formal spec.
 
What would be really nice is an I2C book equivalent to Wilfred Voss's  A Comprehensive Guide to the Controller Area Network, which discusses "gotchas" in the CAN standards document.  I did some searching, and didn't find anything equivalent.

That's probably because I2C is really an extremely simple bus, orders of magnitude less complex than CAN. I imagine that it's a bit hard to fill a book with only I2C material :) The fact that different manufacturers choose to implement it in an overly complex manner is a different problem.

Best,
Bogdan



On Mon, Jun 6, 2011 at 11:05 AM, Martin Guy <[hidden email]> wrote:
On 6 June 2011 08:21, Bogdan Marinescu <[hidden email]> wrote:
> It limits the functionality a bit (for example the master could in theory
> send a START at any time to a slave in order to reset it if anything goes
> wrong with the comunication)

True. In reality, I2C does not define the data protocol format at all
- that is left to the device manufacturers to decide.

It seems to me that we're looking at the three controllers we know,
the handful of devices we have seen, and are trying to find common
patterns between them. The risk with this is the devices we don't know
about - and if you're implementing a generic I2C interface for a
world-class language, we're only likely to succeed in covering the few
cases we've seen.

A different way to approach the problem is to start from the
definition of I2C protocol and make primitives that reflect that,
ensuring that we cover all possibilities including devices that are
not among those we happen to have used so far.

Wikipedia says:

----------
There are four potential modes of operation for a given bus device,
although most devices only use a single role and its two modes:
   master transmit — master node is sending data to a slave
   master receive — master node is receiving data from a slave
   slave transmit — slave node is sending data to the master
   slave receive — slave node is receiving data from the master
----------

Ok, nothing very new here.  Then it says:

----------
I²C defines three basic types of messages, each of which begins with a
START and ends with a STOP:
   Single message where a master writes data to a slave;
   Single message where a master reads data from a slave;
   Combined messages, where a master issues at least two reads and/or
writes to one or more slaves.

In a combined message, each read or write begins with a START and the
slave address. After the first START, these are also called repeated
START bits; repeated START bits are not preceded by STOP bits, which
is how slaves know the next transfer is part of the same message.
----------

Then there are some other protocols built using I2C which further
limit the message structures:

----------
Pure I²C systems support arbitrary message structures.
SMBus is restricted to nine of those structures, such as read word N
and write word N, involving a single slave.
PMBus extends SMBus with a Group protocol, allowing multiple such
SMBus transactions to be sent in one combined message. The terminating
STOP indicates when those grouped actions should take effect.
----------

Let's assume for the moment that we want to provide an I2C interface,
not an SMBus or PMBus one. If we get our I2C primitives right, we can
be sure that these more highly defined protocols can be implemented as
a Lua library that uses it if necessary.

> 1. init( id, speed )
> 2. write( id, i2caddr, devaddr, bytes ) (could be simply 'write( id,
> i2caddr, bytes )' but I want to make it symetrical with number 3 below).

Why?

> 3. read( id, i2caddr, devaddr, numbytes )
> 4. query (id, i2caddr)

I had been considering the register address inside the slave device as
just part of the transmitted data, not as part of I2C protocol.
In particular, the device register address, in the examples I've seen,
can be one, two or three bytes, so you'd need to include a devaddr_len
field as well. But that has nothing to do with I2C, just with the way
that certain devices we have seen use it.
I2C does not define the data format in use; the
write-address-then-read-data is just the way that one or two specific
devices use it.

That said, the AVR32 hardware also mimics this, allowing you to send
one, two or three bytes of addressing info and then automatically
sending a second start bit and address and switching into reading
mode.

10-bit slave addressing (instead of 7-bit slave addressing) is another
issue.  It is done thus:
Slave address = 1 1 1 1 0 a9 a8 R/W
First data byte = a7 a6 a5 a4 a3 a2 a1 a0
(Slave addresses 1111XXXX are reserved for extensions like this)

But both of these can be covered by a 7-bit address and arbitrary
data, leaving it up to the user:
to put the device register address at the start of their data
packet(s) or, for 10-bit addressing, to specify the reserved 11110AAR
address and put the other byte of the address into the start of the
data.
Again, if the Lua I2C interface is right, we know that we will be able
to write Lua functions to cover these cases.


So what does that leave us with?  Well, the I2C specification, which
has three primitives:
   Single message where a master writes data to a slave;
   Single message where a master reads data from a slave;
   Combined messages, where a master issues at least two reads and/or
writes to one or more slaves.

The "one or more slaves" is a bit surprising: it implies that a
combined message with multiple starts can address several different
slavesand that we've only seen examples that use the same slave
address (read device register, or read EEPROM contents at a specified
address).

It's asp interesting that the first two cases, the single messages,
are special cases of the combined messages, with only one data item
inside them, so if we provide a single combined message primitive,
that allows us to do everything I2C.
Anything less that this only addresses a subset of I2C devices - the
ones that we happen to have seen.

What does that suggest for a Lua interface? Well, we can encoding a
single message as a table and the combined message either as a table
of single messages or as a varargs list of them.

The single message would contain the 7-bit slave address, a bool
saying whether we are reading or writing (true for "reading", since
the I2C "read/write" bit is high for a read).
If it's a read, we'll then need the maximum number of bytes to read.
It it's a write, we'll then need the number of bytes to send and the
data itself, which can both be encoded in a singLua string.

The return value for each message would be:
For each read message, the number of bytes actually read and the data
itself (these two can be expressed as a string, since Lua strings are
8-bit clean and encode the string length)
For each write message, the number of bytes actually written.

The combined messages are then either a table of single messages
returning a table of results, or a varargs list of them returning a
varargs list of results.
I don't have enough practice with Lua to know which is more
convenient, or if it's practical to accept both variants.

So, as an example, to write a byte value to a location in a 24LC32
EEPROM we need a single message, For clarity, I'll use the vargargs
and multiple return syntax:

function eeprom_write_byte(id, chip_select, address, datum)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 -- datum = 0..255 (a number)
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
 nwritten = i2c.transfer( id, [ i2c.WRITE, slave_address,
string.char(ah, al, datum) ] )
 assert( nwritten == 3 )
end

To read a byte from such an EEPROM we need a combined message with a
write and a read:

function eeprom_read_byte(id, chip_select, address)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
 wresult, rresult = i2c.transfer( id,
   [ i2c.WRITE, slave_address, string.char(ah, al) ],
   [ i2c.READ, slave_address, 1 ]
 )
 if ( wresult ~= 2 or #rresult ~= 1) then return nil end
 return string.code(rresult)
end

Here, I'm using 7-bit addresses so write 7-bit address 0x50 to encode
the 8-bit EEPROM command byte 1010aaaR.

The "query" or "probe" operation is implemented in the example code
I've seen as a write message with 0 data in it, which succeeds or
fails according to whether the slave address gets acked or not. To add
this to the above model, we could say that if nobody acknowledges the
command byte (containing the slave address), we return nil instead of
0.


Well, thanks for following my thought process, but the result looks
very different from all the other eLua module interfaces, none of
which take tables as parameters, and none of which return multiple
results.
Anything less than this is not capable of expressing the full I2C
protocol, so the next choice is whether we want to be able to express
the I2C protocol or just the common cases of it for the devices we
know?

I know the rest of the eLua modules just provide the things that most
people want most frequently with minimum fuss, so restricting the I2C
interface to the common cases that we know is more in keeping with the
other eLua module interfaces.

Another option is simply not to support combined data packets.   I see
you can use the 24LC32 EEPROMs just by writing the memory address in
one packet with no data bytes to write, then issuing a "read" command
in a second and it remembers the memory address from one operation to
the next.
The DS1337 Real Time Clock can be used the same way and on the AVR32
we can just not use their clever self-reversing hardware.

Does anyone know of an I2C device that *needs* combined messages (with
repeated start bits) to work?  If not, Bogdan's suggestion is close
(modulo the strange desire for read/write function parameter symmetry
and without the variable-length device register address, which is just
data).

If we can dump combine messages, we can use:

i2c.setspeed(id, speed)
id = which I2C bus to talk on
speed = a number in Hz.
Returns: the actual speed set (or nil on error?)

i2c.write(id, address, data)
address = the 7-bit slave address
data = a string
Returns: an integer if the write was successful, reflecting the number
of bytes written
       nil if the slave did not respond to its address

i2c.read(id, address, maxbytes)
maxbytes = the maximum number of bytes to read (and acknowledge)
Returns: a string containing the data that was returned.
       nil if the address byte was not acknowledged

It would then be an option to provide packet assemblers to do the
other things that can be implemented using these:
- probe(id, address) is write(id, address, "")
- read/write value from/to device register with address of length 1, 2
or 3 bytes
- other?
but these can be written in Lua or C, as you prefer, using the
single-message read/write primitives.

The EEPROM examples then become:

-- Write a byte to a 24LS32, returning true on success, false on failure
function eeprom_write_byte(id, chip_select, address, datum)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 -- datum = 0..255 (a number)
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
 return ( i2c.write( id, slave_address, string.char(ah, al, datum) ) == 3 )
end

-- Read a byte from a 24LS32, returning a 1-byte string on success or
nil on failure
function eeprom_read_byte(id, chip_select, address)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255 )
 if i2c.write( id, slave_address, string.char(ah, al) ) ~= 2 then
   return nil
 end
 local result = i2c.read( id, slave_address, 1 )
 if #result ~= 1 then return nil end
 return result
end


>> My own difficulty with changing the i2c interfaces is that I wouldn't
>> want to reimplement the existing str9 code and I don't have str9 (or
>> lm3s) hardware to test such hings on
>
> Don't worry about that, I'll be happy to implement them myself once we agree
> on an interface. I'm actually glad this happens now, when the only
> (official) implementation of I2C is on STR9 :)

That's a relief!

   M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev


_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev



_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Laurent Dufrechou Laurent Dufrechou
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

Hello,

 

So we end with address inside write and read funtions ?

That’s ok for me.

 

So if I read everything well:

i2c.setspeed(id, speed)
i2c.write(id, address, data)
i2c.read(id, address, maxbytes)

For repeated start case, I think there is no issue with such device, because repeated start is a trick to avoid to send a stop and thus permit to  keep the control in case of multiple master.

So devices that support repeated start ALSO supports “stop and resend start”.

I’ve read it on a website, but can’t re-find the address.

 

Bogdan are you ok with this?

If yes can we define the C interface so we could have a try?

I’ve got 5 I2C device to test here, that can be a good test J

 

Reading their doc so far, I see no issue with such interface.

 

Regards,

Laurent

 

De : [hidden email] [mailto:[hidden email]] De la part de Bogdan Marinescu
Envoyé : mardi 7 juin 2011 08:31
À : eLua Users and Development List (www.eluaproject.net)
Objet : Re: [eLua-dev] Redefining the I2C Lua interface (was: I2C for LM3S devices)

 

Hi,

On Tue, Jun 7, 2011 at 12:24 AM, Tony <[hidden email]> wrote:

Wouldn't the I2C bus specifications be a better starting place than Wikipedia?  It looks like they're available via here

 

You are right, however Wikipedia is an easier read (at least for me) than a formal spec.

 

What would be really nice is an I2C book equivalent to Wilfred Voss's  A Comprehensive Guide to the Controller Area Network, which discusses "gotchas" in the CAN standards document.  I did some searching, and didn't find anything equivalent.

 

That's probably because I2C is really an extremely simple bus, orders of magnitude less complex than CAN. I imagine that it's a bit hard to fill a book with only I2C material :) The fact that different manufacturers choose to implement it in an overly complex manner is a different problem.

 

Best,

Bogdan

 

 

On Mon, Jun 6, 2011 at 11:05 AM, Martin Guy <[hidden email]> wrote:

On 6 June 2011 08:21, Bogdan Marinescu <[hidden email]> wrote:
> It limits the functionality a bit (for example the master could in theory
> send a START at any time to a slave in order to reset it if anything goes
> wrong with the comunication)

True. In reality, I2C does not define the data protocol format at all
- that is left to the device manufacturers to decide.

It seems to me that we're looking at the three controllers we know,
the handful of devices we have seen, and are trying to find common
patterns between them. The risk with this is the devices we don't know
about - and if you're implementing a generic I2C interface for a
world-class language, we're only likely to succeed in covering the few
cases we've seen.

A different way to approach the problem is to start from the
definition of I2C protocol and make primitives that reflect that,
ensuring that we cover all possibilities including devices that are
not among those we happen to have used so far.

Wikipedia says:

----------
There are four potential modes of operation for a given bus device,
although most devices only use a single role and its two modes:
   master transmit — master node is sending data to a slave
   master receive — master node is receiving data from a slave
   slave transmit — slave node is sending data to the master
   slave receive — slave node is receiving data from the master
----------

Ok, nothing very new here.  Then it says:

----------
I²C defines three basic types of messages, each of which begins with a
START and ends with a STOP:
   Single message where a master writes data to a slave;
   Single message where a master reads data from a slave;
   Combined messages, where a master issues at least two reads and/or
writes to one or more slaves.

In a combined message, each read or write begins with a START and the
slave address. After the first START, these are also called repeated
START bits; repeated START bits are not preceded by STOP bits, which
is how slaves know the next transfer is part of the same message.
----------

Then there are some other protocols built using I2C which further
limit the message structures:

----------
Pure I²C systems support arbitrary message structures.
SMBus is restricted to nine of those structures, such as read word N
and write word N, involving a single slave.
PMBus extends SMBus with a Group protocol, allowing multiple such
SMBus transactions to be sent in one combined message. The terminating
STOP indicates when those grouped actions should take effect.
----------

Let's assume for the moment that we want to provide an I2C interface,
not an SMBus or PMBus one. If we get our I2C primitives right, we can
be sure that these more highly defined protocols can be implemented as
a Lua library that uses it if necessary.


> 1. init( id, speed )
> 2. write( id, i2caddr, devaddr, bytes ) (could be simply 'write( id,
> i2caddr, bytes )' but I want to make it symetrical with number 3 below).

Why?


> 3. read( id, i2caddr, devaddr, numbytes )

> 4. query (id, i2caddr)

I had been considering the register address inside the slave device as
just part of the transmitted data, not as part of I2C protocol.
In particular, the device register address, in the examples I've seen,
can be one, two or three bytes, so you'd need to include a devaddr_len
field as well. But that has nothing to do with I2C, just with the way
that certain devices we have seen use it.
I2C does not define the data format in use; the
write-address-then-read-data is just the way that one or two specific
devices use it.

That said, the AVR32 hardware also mimics this, allowing you to send
one, two or three bytes of addressing info and then automatically
sending a second start bit and address and switching into reading
mode.

10-bit slave addressing (instead of 7-bit slave addressing) is another
issue.  It is done thus:
Slave address = 1 1 1 1 0 a9 a8 R/W
First data byte = a7 a6 a5 a4 a3 a2 a1 a0
(Slave addresses 1111XXXX are reserved for extensions like this)

But both of these can be covered by a 7-bit address and arbitrary
data, leaving it up to the user:
to put the device register address at the start of their data
packet(s) or, for 10-bit addressing, to specify the reserved 11110AAR
address and put the other byte of the address into the start of the
data.
Again, if the Lua I2C interface is right, we know that we will be able
to write Lua functions to cover these cases.


So what does that leave us with?  Well, the I2C specification, which
has three primitives:
   Single message where a master writes data to a slave;
   Single message where a master reads data from a slave;
   Combined messages, where a master issues at least two reads and/or
writes to one or more slaves.

The "one or more slaves" is a bit surprising: it implies that a
combined message with multiple starts can address several different
slavesand that we've only seen examples that use the same slave
address (read device register, or read EEPROM contents at a specified
address).

It's asp interesting that the first two cases, the single messages,
are special cases of the combined messages, with only one data item
inside them, so if we provide a single combined message primitive,
that allows us to do everything I2C.
Anything less that this only addresses a subset of I2C devices - the
ones that we happen to have seen.

What does that suggest for a Lua interface? Well, we can encoding a
single message as a table and the combined message either as a table
of single messages or as a varargs list of them.

The single message would contain the 7-bit slave address, a bool
saying whether we are reading or writing (true for "reading", since
the I2C "read/write" bit is high for a read).
If it's a read, we'll then need the maximum number of bytes to read.
It it's a write, we'll then need the number of bytes to send and the
data itself, which can both be encoded in a singLua string.

The return value for each message would be:
For each read message, the number of bytes actually read and the data
itself (these two can be expressed as a string, since Lua strings are
8-bit clean and encode the string length)
For each write message, the number of bytes actually written.

The combined messages are then either a table of single messages
returning a table of results, or a varargs list of them returning a
varargs list of results.
I don't have enough practice with Lua to know which is more
convenient, or if it's practical to accept both variants.

So, as an example, to write a byte value to a location in a 24LC32
EEPROM we need a single message, For clarity, I'll use the vargargs
and multiple return syntax:

function eeprom_write_byte(id, chip_select, address, datum)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 -- datum = 0..255 (a number)
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
 nwritten = i2c.transfer( id, [ i2c.WRITE, slave_address,
string.char(ah, al, datum) ] )
 assert( nwritten == 3 )
end

To read a byte from such an EEPROM we need a combined message with a
write and a read:

function eeprom_read_byte(id, chip_select, address)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
 wresult, rresult = i2c.transfer( id,
   [ i2c.WRITE, slave_address, string.char(ah, al) ],
   [ i2c.READ, slave_address, 1 ]
 )
 if ( wresult ~= 2 or #rresult ~= 1) then return nil end
 return string.code(rresult)
end

Here, I'm using 7-bit addresses so write 7-bit address 0x50 to encode
the 8-bit EEPROM command byte 1010aaaR.

The "query" or "probe" operation is implemented in the example code
I've seen as a write message with 0 data in it, which succeeds or
fails according to whether the slave address gets acked or not. To add
this to the above model, we could say that if nobody acknowledges the
command byte (containing the slave address), we return nil instead of
0.


Well, thanks for following my thought process, but the result looks
very different from all the other eLua module interfaces, none of
which take tables as parameters, and none of which return multiple
results.
Anything less than this is not capable of expressing the full I2C
protocol, so the next choice is whether we want to be able to express
the I2C protocol or just the common cases of it for the devices we
know?

I know the rest of the eLua modules just provide the things that most
people want most frequently with minimum fuss, so restricting the I2C
interface to the common cases that we know is more in keeping with the
other eLua module interfaces.

Another option is simply not to support combined data packets.   I see
you can use the 24LC32 EEPROMs just by writing the memory address in
one packet with no data bytes to write, then issuing a "read" command
in a second and it remembers the memory address from one operation to
the next.
The DS1337 Real Time Clock can be used the same way and on the AVR32
we can just not use their clever self-reversing hardware.

Does anyone know of an I2C device that *needs* combined messages (with
repeated start bits) to work?  If not, Bogdan's suggestion is close
(modulo the strange desire for read/write function parameter symmetry
and without the variable-length device register address, which is just
data).

If we can dump combine messages, we can use:

i2c.setspeed(id, speed)
id = which I2C bus to talk on
speed = a number in Hz.
Returns: the actual speed set (or nil on error?)

i2c.write(id, address, data)
address = the 7-bit slave address
data = a string
Returns: an integer if the write was successful, reflecting the number
of bytes written
       nil if the slave did not respond to its address

i2c.read(id, address, maxbytes)
maxbytes = the maximum number of bytes to read (and acknowledge)
Returns: a string containing the data that was returned.
       nil if the address byte was not acknowledged

It would then be an option to provide packet assemblers to do the
other things that can be implemented using these:
- probe(id, address) is write(id, address, "")
- read/write value from/to device register with address of length 1, 2
or 3 bytes
- other?
but these can be written in Lua or C, as you prefer, using the
single-message read/write primitives.

The EEPROM examples then become:

-- Write a byte to a 24LS32, returning true on success, false on failure
function eeprom_write_byte(id, chip_select, address, datum)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 -- datum = 0..255 (a number)
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255)
 return ( i2c.write( id, slave_address, string.char(ah, al, datum) ) == 3 )
end

-- Read a byte from a 24LS32, returning a 1-byte string on success or
nil on failure
function eeprom_read_byte(id, chip_select, address)
 -- id = i2c bus id
 -- chip_select = 0..7
 -- address = 0..65535
 local slave_address = 0x50 + chip_select
 local ah, al = bit.rshift( address, 8 ), bit.band( address, 255 )
 if i2c.write( id, slave_address, string.char(ah, al) ) ~= 2 then
   return nil
 end
 local result = i2c.read( id, slave_address, 1 )
 if #result ~= 1 then return nil end
 return result
end



>> My own difficulty with changing the i2c interfaces is that I wouldn't
>> want to reimplement the existing str9 code and I don't have str9 (or
>> lm3s) hardware to test such hings on
>
> Don't worry about that, I'll be happy to implement them myself once we agree
> on an interface. I'm actually glad this happens now, when the only
> (official) implementation of I2C is on STR9 :)

That's a relief!


   M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev

 


_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev

 


_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Martin Guy Martin Guy
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

On 11 June 2011 11:37, Laurent Dufrechou <[hidden email]> wrote:
> So we end with address inside write and read funtions ?
> i2c.setspeed(id, speed)
> i2c.write(id, address, data)
> i2c.read(id, address, maxbytes)
>
> For repeated start case, I think there is no issue with such device, because
> repeated start is a trick to avoid to send a stop and thus permit to  keep
> the control in case of multiple master.

No, that was just an idea for discussion (which never took place), and
may be wrong.
I don't understand I2C yet, so these ideas may be based on gross
misunderstanding of I2C.

In particular, I don't have an answer to the question "Is repeated
start an optional feature of an I2C impementation?"
The specification certainly doesn't say so.

As was suggested (thanks!) I am now working from the I2C specification
and it turns out that the Wikipedia entry is easier to understand
because it is incomplete and imprecise.

As yet, it is quite unclear to me what is needed. At present (to
condense my long email) it seems to be a choice between something that
is as simplistic as the rest of eLua but can't do I2C, or something
that does I2C but is too complex for beginners to understand.  There
may be some middle ground that satisfies both needs, but "what" is not
clear to me yet.

If you are in a hurry and want to get started, you could try
implementing the above scheme in a Git fork and let us know how the
attempt goes. Unfortunately, I need to understand things before I
implement them, and I don't understand the problem well enough yet.

    M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Laurent Dufrechou Laurent Dufrechou
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

I found the link explaining the repeated start.
And why it is optional in single master mode.
http://www.i2c-bus.org/repeated-start-condition/

Laurent

-----Message d'origine-----
De : [hidden email]
[mailto:[hidden email]] De la part de Martin Guy
Envoyé : samedi 11 juin 2011 18:57
À : eLua Users and Development List (www.eluaproject.net)
Objet : Re: [eLua-dev] Redefining the I2C Lua interface (was: I2C for LM3S
devices)

On 11 June 2011 11:37, Laurent Dufrechou <[hidden email]>
wrote:
> So we end with address inside write and read funtions ?
> i2c.setspeed(id, speed)
> i2c.write(id, address, data)
> i2c.read(id, address, maxbytes)
>
> For repeated start case, I think there is no issue with such device,
because
> repeated start is a trick to avoid to send a stop and thus permit to  keep
> the control in case of multiple master.

No, that was just an idea for discussion (which never took place), and
may be wrong.
I don't understand I2C yet, so these ideas may be based on gross
misunderstanding of I2C.

In particular, I don't have an answer to the question "Is repeated
start an optional feature of an I2C impementation?"
The specification certainly doesn't say so.

As was suggested (thanks!) I am now working from the I2C specification
and it turns out that the Wikipedia entry is easier to understand
because it is incomplete and imprecise.

As yet, it is quite unclear to me what is needed. At present (to
condense my long email) it seems to be a choice between something that
is as simplistic as the rest of eLua but can't do I2C, or something
that does I2C but is too complex for beginners to understand.  There
may be some middle ground that satisfies both needs, but "what" is not
clear to me yet.

If you are in a hurry and want to get started, you could try
implementing the above scheme in a Git fork and let us know how the
attempt goes. Unfortunately, I need to understand things before I
implement them, and I don't understand the problem well enough yet.

    M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev

_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Laurent Dufrechou Laurent Dufrechou
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

In reply to this post by Martin Guy
And the counter example of my last mail :)
http://mbed.org/forum/mbed/topic/586/?page=1#comment-7935

Seems that some chips are totally untolerant :), so a support for repeated
start seems mandatory for better coverage...

How about the C api I proposed in previous mail?

int platform_i2c_start_and_send_address( unsigned id, u16 address, int
direction )
int platform_i2c_send_byte( unsigned id, u8 do_stop, u8* data, u8 len)
int platform_i2c_recv_byte( unsigned id, u8 do_stop, u8* data, u8 len)

Just adds a "do_stop" vars on top of what you proposed to manage the
repeated start case.
Should cover the restart cases.

To be discussed.
Laurent

-----Message d'origine-----
De : Laurent Dufrechou [mailto:[hidden email]]
Envoyé : dimanche 12 juin 2011 13:56
À : 'eLua Users and Development List (www.eluaproject.net)'
Objet : RE: [eLua-dev] Redefining the I2C Lua interface (was: I2C for LM3S
devices)

I found the link explaining the repeated start.
And why it is optional in single master mode.
http://www.i2c-bus.org/repeated-start-condition/

Laurent

-----Message d'origine-----
De : [hidden email]
[mailto:[hidden email]] De la part de Martin Guy
Envoyé : samedi 11 juin 2011 18:57
À : eLua Users and Development List (www.eluaproject.net)
Objet : Re: [eLua-dev] Redefining the I2C Lua interface (was: I2C for LM3S
devices)

On 11 June 2011 11:37, Laurent Dufrechou <[hidden email]>
wrote:
> So we end with address inside write and read funtions ?
> i2c.setspeed(id, speed)
> i2c.write(id, address, data)
> i2c.read(id, address, maxbytes)
>
> For repeated start case, I think there is no issue with such device,
because
> repeated start is a trick to avoid to send a stop and thus permit to  keep
> the control in case of multiple master.

No, that was just an idea for discussion (which never took place), and
may be wrong.
I don't understand I2C yet, so these ideas may be based on gross
misunderstanding of I2C.

In particular, I don't have an answer to the question "Is repeated
start an optional feature of an I2C impementation?"
The specification certainly doesn't say so.

As was suggested (thanks!) I am now working from the I2C specification
and it turns out that the Wikipedia entry is easier to understand
because it is incomplete and imprecise.

As yet, it is quite unclear to me what is needed. At present (to
condense my long email) it seems to be a choice between something that
is as simplistic as the rest of eLua but can't do I2C, or something
that does I2C but is too complex for beginners to understand.  There
may be some middle ground that satisfies both needs, but "what" is not
clear to me yet.

If you are in a hurry and want to get started, you could try
implementing the above scheme in a Git fork and let us know how the
attempt goes. Unfortunately, I need to understand things before I
implement them, and I don't understand the problem well enough yet.

    M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev

_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Martin Guy Martin Guy
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

On 12 June 2011 14:17, Laurent Dufrechou <[hidden email]> wrote:
> And the counter example of my last mail :)
> http://mbed.org/forum/mbed/topic/586/?page=1#comment-7935

Good find. So yes, there are devices that require repeated starts.  Thanks.
They mention two devices that need a write-read sequence with repeated
start: the MLX90614 temperature sensor and the Lego Mindstorms
Ultrasonic sensor.  Someone suggests a third primitive to do
send-and-receive-with-repeated-start to cover the cases that we have
seen where repeated start is useful.

But read also the last paragraph of this comment in that thread
http://mbed.org/forum/mbed/topic/586/?page=1#comment-3735
which mentions a need to synchronize several data acquisition devices
by configuring them all with repeated starts to different slave
addresses, then the single stop is what triggers them all to start
their data acquisition in sync.

This is why implementing the I2C specification is how to get it right.
That way we can be sure to have covered the devices that we do not
know about, and there are more of those than the few we have on hand.

> How about the C api I proposed in previous mail?
>
> int platform_i2c_start_and_send_address( unsigned id, u16 address, int
> direction )
> int platform_i2c_send_byte( unsigned id, u8 do_stop, u8* data, u8 len)
> int platform_i2c_recv_byte( unsigned id, u8 do_stop, u8* data, u8 len)
>
> Just adds a "do_stop" vars on top of what you proposed to manage the
> repeated start case.
> Should cover the restart cases.

AVR32 must have the whole transfer available as a single unit at the
lowest level; the above interface is too low level.
and cannot be implemented on AVR32 without send_byte having to make a
copy of an unknown number of bytes and then sending them all when it
is told "that's all".  Anything more naive would create a race
condition.


Some further points:

- I suggested that the speed setup take a numeric parameter instead of
the current two symbols HIGH and LOW, since the bus speed does not
have two discrete values, but takes a speed up to those maximum
speeds.   I2C has variants that can run at 0-100kHz, 0-400kHz, 0-1MHz
and 0-3.4MHz, while SMBus runs at 10kHz-100kHz. These are all
expressible as the usual double precision float or as a long integer,
and returning the "actual speed set" is coherent with the
speed-setting primitives in the other modules.

- in the new Lua interface, if we also change the names "read" and
"write", that removes all overlap between the old and new interfaces.
That way the two interfaces can coexist in the transition period,
while we write the new code, test it and then, when it seems OK,
remove the old one.
  The I2C language for what we are calling "read" and "write" are
"master send" and "master receive" (completed by "slave send" and
"slave receive"), so your "send" and "recv" names would both achieve
this and reflect standard I2C terminology.

However, Bogdan has the last word on API names. Meanwhile, let's try
to understand what functionality is required.

     M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Laurent Dufrechou Laurent Dufrechou
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

>AVR32 must have the whole transfer available as a single unit at the lowest
level; the above interface is too low level.
>and cannot be implemented on AVR32 without send_byte having to make a copy
of an unknown number of bytes and then sending them all when >it is told
"that's all".  Anything more naive would create a race condition.

How do you do on AVR32 to send address then 20 data and receive 50 data?
On LM3S it should be:
platform_i2c_start_and_send_address(0, address)
platform_i2c_send_byte( 0, false, &data,     20);
platform_i2c_recv_byte( 0, true,  &data_rcv, 50); //true means stop.

You mean that i2c send can't be called 2 consecutive times on avr32?
Or do you mean you need something like...
I2C_transfer transfer_struct[3] = {
  {I2C_ADDR,     false, &null,    0x12},
  {I2C_TRANSMIT, false, &data,      20},
  {I2C_RECEIVE,  true,  &data_rcev, 50}
};

Platform_i2c_transfer(&transfer_struct, 3);

:/

Do I misunderstand anything, or my example is too simple :)?

Laurent

-----Message d'origine-----
De : Martin Guy [mailto:[hidden email]]
Envoyé : dimanche 12 juin 2011 18:15
À : eLua Users and Development List (www.eluaproject.net)
Cc : Laurent Dufrechou
Objet : Re: [eLua-dev] Redefining the I2C Lua interface (was: I2C for LM3S
devices)

On 12 June 2011 14:17, Laurent Dufrechou <[hidden email]>
wrote:
> And the counter example of my last mail :)
> http://mbed.org/forum/mbed/topic/586/?page=1#comment-7935

Good find. So yes, there are devices that require repeated starts.  Thanks.
They mention two devices that need a write-read sequence with repeated
start: the MLX90614 temperature sensor and the Lego Mindstorms
Ultrasonic sensor.  Someone suggests a third primitive to do
send-and-receive-with-repeated-start to cover the cases that we have
seen where repeated start is useful.

But read also the last paragraph of this comment in that thread
http://mbed.org/forum/mbed/topic/586/?page=1#comment-3735
which mentions a need to synchronize several data acquisition devices
by configuring them all with repeated starts to different slave
addresses, then the single stop is what triggers them all to start
their data acquisition in sync.

This is why implementing the I2C specification is how to get it right.
That way we can be sure to have covered the devices that we do not
know about, and there are more of those than the few we have on hand.

> How about the C api I proposed in previous mail?
>
> int platform_i2c_start_and_send_address( unsigned id, u16 address, int
> direction )
> int platform_i2c_send_byte( unsigned id, u8 do_stop, u8* data, u8 len)
> int platform_i2c_recv_byte( unsigned id, u8 do_stop, u8* data, u8 len)
>
> Just adds a "do_stop" vars on top of what you proposed to manage the
> repeated start case.
> Should cover the restart cases.

AVR32 must have the whole transfer available as a single unit at the
lowest level; the above interface is too low level.
and cannot be implemented on AVR32 without send_byte having to make a
copy of an unknown number of bytes and then sending them all when it
is told "that's all".  Anything more naive would create a race
condition.


Some further points:

- I suggested that the speed setup take a numeric parameter instead of
the current two symbols HIGH and LOW, since the bus speed does not
have two discrete values, but takes a speed up to those maximum
speeds.   I2C has variants that can run at 0-100kHz, 0-400kHz, 0-1MHz
and 0-3.4MHz, while SMBus runs at 10kHz-100kHz. These are all
expressible as the usual double precision float or as a long integer,
and returning the "actual speed set" is coherent with the
speed-setting primitives in the other modules.

- in the new Lua interface, if we also change the names "read" and
"write", that removes all overlap between the old and new interfaces.
That way the two interfaces can coexist in the transition period,
while we write the new code, test it and then, when it seems OK,
remove the old one.
  The I2C language for what we are calling "read" and "write" are
"master send" and "master receive" (completed by "slave send" and
"slave receive"), so your "send" and "recv" names would both achieve
this and reflect standard I2C terminology.

However, Bogdan has the last word on API names. Meanwhile, let's try
to understand what functionality is required.

     M

_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Ronan Paixão-2 Ronan Paixão-2
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

I'm not used to AVR32, but I have used I2C on normal Atmega's.

Maybe one could try something similar to Arduino's I2C API: file:///D:/apps/Elo/arduino-0022/reference/Wire.html

Or maybe just a simple, direct implementation like this example (also for atmega): http://sparkfun.com/datasheets/Sensors/Magneto/hmc5843.zip

2011/6/12 Laurent Dufrechou <[hidden email]>
>AVR32 must have the whole transfer available as a single unit at the lowest
level; the above interface is too low level.
>and cannot be implemented on AVR32 without send_byte having to make a copy
of an unknown number of bytes and then sending them all when >it is told
"that's all".  Anything more naive would create a race condition.

How do you do on AVR32 to send address then 20 data and receive 50 data?
On LM3S it should be:
platform_i2c_start_and_send_address(0, address)
platform_i2c_send_byte( 0, false, &data,     20);
platform_i2c_recv_byte( 0, true,  &data_rcv, 50); //true means stop.

You mean that i2c send can't be called 2 consecutive times on avr32?
Or do you mean you need something like...
I2C_transfer transfer_struct[3] = {
 {I2C_ADDR,     false, &null,    0x12},
 {I2C_TRANSMIT, false, &data,      20},
 {I2C_RECEIVE,  true,  &data_rcev, 50}
};

Platform_i2c_transfer(&transfer_struct, 3);

:/

Do I misunderstand anything, or my example is too simple :)?

Laurent

-----Message d'origine-----
De : Martin Guy [mailto:[hidden email]]
Envoyé : dimanche 12 juin 2011 18:15
À : eLua Users and Development List (www.eluaproject.net)
Cc : Laurent Dufrechou
Objet : Re: [eLua-dev] Redefining the I2C Lua interface (was: I2C for LM3S
devices)

On 12 June 2011 14:17, Laurent Dufrechou <[hidden email]>
wrote:
> And the counter example of my last mail :)
> http://mbed.org/forum/mbed/topic/586/?page=1#comment-7935

Good find. So yes, there are devices that require repeated starts.  Thanks.
They mention two devices that need a write-read sequence with repeated
start: the MLX90614 temperature sensor and the Lego Mindstorms
Ultrasonic sensor.  Someone suggests a third primitive to do
send-and-receive-with-repeated-start to cover the cases that we have
seen where repeated start is useful.

But read also the last paragraph of this comment in that thread
http://mbed.org/forum/mbed/topic/586/?page=1#comment-3735
which mentions a need to synchronize several data acquisition devices
by configuring them all with repeated starts to different slave
addresses, then the single stop is what triggers them all to start
their data acquisition in sync.

This is why implementing the I2C specification is how to get it right.
That way we can be sure to have covered the devices that we do not
know about, and there are more of those than the few we have on hand.

> How about the C api I proposed in previous mail?
>
> int platform_i2c_start_and_send_address( unsigned id, u16 address, int
> direction )
> int platform_i2c_send_byte( unsigned id, u8 do_stop, u8* data, u8 len)
> int platform_i2c_recv_byte( unsigned id, u8 do_stop, u8* data, u8 len)
>
> Just adds a "do_stop" vars on top of what you proposed to manage the
> repeated start case.
> Should cover the restart cases.

AVR32 must have the whole transfer available as a single unit at the
lowest level; the above interface is too low level.
and cannot be implemented on AVR32 without send_byte having to make a
copy of an unknown number of bytes and then sending them all when it
is told "that's all".  Anything more naive would create a race
condition.


Some further points:

- I suggested that the speed setup take a numeric parameter instead of
the current two symbols HIGH and LOW, since the bus speed does not
have two discrete values, but takes a speed up to those maximum
speeds.   I2C has variants that can run at 0-100kHz, 0-400kHz, 0-1MHz
and 0-3.4MHz, while SMBus runs at 10kHz-100kHz. These are all
expressible as the usual double precision float or as a long integer,
and returning the "actual speed set" is coherent with the
speed-setting primitives in the other modules.

- in the new Lua interface, if we also change the names "read" and
"write", that removes all overlap between the old and new interfaces.
That way the two interfaces can coexist in the transition period,
while we write the new code, test it and then, when it seems OK,
remove the old one.
 The I2C language for what we are calling "read" and "write" are
"master send" and "master receive" (completed by "slave send" and
"slave receive"), so your "send" and "recv" names would both achieve
this and reflect standard I2C terminology.

However, Bogdan has the last word on API names. Meanwhile, let's try
to understand what functionality is required.

    M

_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev


_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Martin Guy Martin Guy
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

In reply to this post by Laurent Dufrechou
On 12 June 2011 19:25, Laurent Dufrechou <[hidden email]> wrote:
>>AVR32 must have the whole transfer available as a single unit at the lowest
> level; the above interface is too low level.
>>and cannot be implemented on AVR32 without send_byte having to make a copy
> of an unknown number of bytes and then sending them all when >it is told
> "that's all".  Anything more naive would create a race condition.
>
> How do you do on AVR32 to send address then 20 data and receive 50 data?

You can't. It turns out that the AVR32 TWI silicon is pretty limited
in what it can do.
The datasheet is here
http://www.atmel.com/dyn/resources/prod_documents/doc32058.pdf
with the TWI chapter starting at page 220.

As master, all it can do is:
- write one or more data bytes to a slave, then stop
- read one or more data bytes from a slave, then stop
- write one, two or three data bytes to a slave, then a repeated
start, then read one or more data bytes from the same slave, then stop

It cannot do:
- arbitrary repeated start sequences, so it can't synchronize multiple
data acquisition devices
- probing for the presence of devices by writing the address and no
data bytes. Their SDK's example code for twi_probe() just sends a
single byte with value 0, hoping that writing a 0 bytes doesn't do
anything funny to the probed devices.

You can only implement full I2C on AVR32 by bit-banging the clock and
data pins as GPIO lines.

> On LM3S it should be:
> platform_i2c_start_and_send_address(0, address)
> platform_i2c_send_byte( 0, false, &data,     20);
> platform_i2c_recv_byte( 0, true,  &data_rcv, 50); //true means stop.
>
> You mean that i2c send can't be called 2 consecutive times on avr32?

"LMS3" is a chip, not a software library, and chips don't have function calls.
Maybe you're talking about the LMS3 SDK library functions? I don't know those.

The Atmel SDK only has read() write() functions, which each emit
complete transfers from the START to the STOP bit.
It implements the write-then-read-with-repeated-start with some weird
"internal device address" and "internal device address size" bit
fields in a transfer config structure, and it prefixes these 1, 2 or 3
bytes to the data proper. But I wouldn't want to infect the eLua API
with this nonsense; I'd just say that if they try to do a send-recv
and send more than three bytes, then the transfer fails on AVR32.


That seems to leave the folloing options:

1) Redefine the eLua I2C APIs to express full I2C transfers as an
arbitrary sequence of sends and receives.

This makes for a much more complex API, requiring a table of tables or
a varargs list of tables, each encoding slave_address, direction and
data (for send) or nbytes (for receive). That seems out of keeping
with the rest of eLua, but it does ensure that any I2C device can
definitely be driven by eLua, and ensures that protocols built on top
of I2C can definitely be implemented using it (SMBus, ACPI etc).

2) Leave the interface as it is and implement I2C on AVR32 as a
bit-banging interface, completely forgetting their TWI hardware.

This also impacts on future implementations for other hardware that
does anything higher-level that "send a start" "send a byte" "send a
stop".

3) Redefine the eLua I2C APIs to give the 3 common cases of send,
receive and send-then-receive-with-repeated-start

That would be something like
platform_i2c_master_exists(unsigned id)
actual_speed = platform_i2c_master_setup(unsigned id, u32 speed)
success = platform_i2c_master_send(unsigned id, u8 slave_address, char
*send_buf, u32 nbytes_to_send)
success = platform_i2c_master_recv(unsigned id, u8 slave_address, char
*recv_buf, u32 nbytes_to_receive)
success = platform_i2c_master_send_recv(unsigned id, u8 slave_address,
char *send_buf, u32 nbytes_to_send, char *recv_buf, u32
nbytes_to_receive)

Where "success" indicates that the slave acknowledged its address and
all the sent bytes were acknowledged.

"probe" becomes "send(slave, NULL, 0)"

The Lua interface needs redefining too, to express each kind of whole
transfer in a single function call instead of the current granular
interface.

These are pretty easy to implement in terms of the existing start(),
send_byte(), recv_byte() stop() primitives, or whatever your SDK
provides.

I suggest calling them platform_i2c_master_*  to leave the door open
to adding slave functionality in future without having to redefine the
existing master functions.  Note that some devices have single I2C
interfaces that can be switched between master and slave modes, while
others have separate master and slave hardware interfaces.

    M
_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
Laurent Dufrechou Laurent Dufrechou
Reply | Threaded
Open this post in threaded view
|

Re: Redefining the I2C Lua interface (was: I2C for LM3S devices)

> On LM3S it should be:
> platform_i2c_start_and_send_address(0, address)
> platform_i2c_send_byte( 0, false, &data,     20);
> platform_i2c_recv_byte( 0, true,  &data_rcv, 50); //true means stop.
>
> You mean that i2c send can't be called 2 consecutive times on avr32?

>"LMS3" is a chip, not a software library, and chips don't have function
calls.
>Maybe you're talking about the LMS3 SDK library functions? I don't know
those.

Yeah sure, I mean these functions call could match perfectly the underlying
LM3S hardware and STR9.

>1) Redefine the eLua I2C APIs to express full I2C transfers as an
>arbitrary sequence of sends and receives.

I follow you, it is too complex also from my point of view

>2) Leave the interface as it is and implement I2C on AVR32 as a
>bit-banging interface, completely forgetting their TWI hardware.

Not efficient as you said.

>3) Redefine the eLua I2C APIs to give the 3 common cases of send,
>receive and send-then-receive-with-repeated-start
>
>That would be something like
>platform_i2c_master_exists(unsigned id)
>actual_speed = platform_i2c_master_setup(unsigned id, u32 speed)
>success = platform_i2c_master_send(unsigned id, u8 slave_address, char
>*send_buf, u32 nbytes_to_send)
>success = platform_i2c_master_recv(unsigned id, u8 slave_address, char
>*recv_buf, u32 nbytes_to_receive)
>success = platform_i2c_master_send_recv(unsigned id, u8 slave_address,
>char *send_buf, u32 nbytes_to_send, char *recv_buf, u32
>nbytes_to_receive)
>
>Where "success" indicates that the slave acknowledged its address and
>all the sent bytes were acknowledged.
>
>"probe" becomes "send(slave, NULL, 0)"

I prefer this approach but your restrain too much to your CPU way of doing.
I mean if you use the function as I proposed (and that is almost identical
to initial I2C elua implementation)
means:
-one function to specify address
-one to send and specify stop
-one to receive and specify stop

I can implement on LM3S and STR9 full I2C protocol and thus very
efficiently.
And you can also implement a slightly limited I2C protocol too.

Stop must be always =true in your case.

The one I proposed is more open for LM3S and STR9, and stay simple, so you
can implement
it easily with limitation for AVR32 where stop is always TRUE.

In your case it could be:
Static static_address;

platform_i2c_start_and_send_address(0, address, direction)
{
        Static_address = address
}

platform_i2c_send_byte( id, do_stop, *data, len);
{
        If(!do_stop)
                return NOT_SUPPORTED;
        Avr32_i2c_master_send(id, static_address, data, len);
}

Etc...

Do you see where I want to go?

_On the other side_, your interface is OK AND simple.
Just it is a bit limitative I think (especially for hardware supporting
complex I2C transfer).
For my elua needs, still it seems OK...

So I let eLua guys choose.
I'm open to the two options.

>platform_i2c_master_exists(unsigned id)
>actual_speed = platform_i2c_master_setup(unsigned id, u32 speed)

OK

>I suggest calling them platform_i2c_master_*  to leave the door open
>to adding slave functionality in future without having to redefine the
>existing master functions.  Note that some devices have single I2C
>interfaces that can be switched between master and slave modes, while
>others have separate master and slave hardware interfaces.

I like this.
   
Laurent

_______________________________________________
eLua-dev mailing list
[hidden email]
https://lists.berlios.de/mailman/listinfo/elua-dev
12