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
|

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

On 15 June 2011 18:36, Laurent Dufrechou <[hidden email]> wrote:
>> 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?

>>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.

In terms of execution speed it is about the same, since we must wait
for the ACKs on sending, and must wait until the data arrives on
receiving. I guess it uses more electrical power to do this with the
CPU than with the hardware.
My own problem with this is how disgusting it is to have to do it this way!

> I prefer this approach but your restrain too much to your CPU way of doing.

Yes.  I see that you can implement more of I2C with your interface,
but it doesn't express the possibility of different slave addresses
between packets, which is needed for the synchronized data acquisition
case.
What do you think of just two functions:

platform_i2c_master_send( id, address, &data, 20, false );
platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means stop.
(hey, I moved the STOP flag to the end :)

On AVR32, I need to know all the sending data and the receiving data
at the same time, but I can do the one repeated-start case that it can
perform by recognising the first part (send with 1-3 bytes and no
stop), make a copy of the address and the 1-3 bytes, then use this
data if the next call is a receive from the same address with stop.

If you can't change the slave address between packets on LMS3/STR9,
just check that it is the same?

    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)

>What do you think of just two functions:
>
>platform_i2c_master_send( id, address, &data, 20, false );
>platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means
stop.
>(hey, I moved the STOP flag to the end :)

I see, with a static flag I can see that there is an ongoing transfer and
thus I can ignore address parameter.
It Seems OK for me.

I will try it tomorrow and I'll keep you updated.

Laurent

_______________________________________________
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 15 June 2011 21:58, Laurent Dufrechou <[hidden email]> wrote:
>>What do you think of just two functions:
>>
>>platform_i2c_master_send( id, address, &data, 20, false );
>>platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means
> stop.
>
> I see, with a static flag I can see that there is an ongoing transfer and
> thus I can ignore address parameter.

I'm not sure why you would ignore it... if I understand your datasheet
(first download link at
http://www.luminarymicro.com/products/LM3S8962.html#Datasheet
pages 510/511) you have to program the slave address again after a
repeated start. Does their SDK API hides this?

Anyway...

    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)

Hi,

Thanks for this thread. I'll jump in as soon as I can, at this point I simply can't find enough time to get a better view of the I2C implementations on our supported targets and come up with a generic interface on top of them. It's a bit ironic that we're having such problems with an interface as simple as I2C, but life can be ironic at times :)
Martin, please see below.

On Thu, Jun 16, 2011 at 2:27 AM, Martin Guy <[hidden email]> wrote:
On 15 June 2011 21:58, Laurent Dufrechou <[hidden email]> wrote:
>>What do you think of just two functions:
>>
>>platform_i2c_master_send( id, address, &data, 20, false );
>>platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means
> stop.

I love the simplicity of this and if it proves to be enough I'll not hesitate in implementing it. It hides some lower level I2C operations but as we learnt they aren't always possible to implement on our platforms. Still thinking what to do with the lower level ops (send start, send stop and so on). As I see it at the moment, since they're probably rarely needed in practice a good place for them would be in a platform specific module (stm32.i2c or str9.i2c for example). Or maybe still in the main I2C interface (since they are after all fundamental I2C operations), letting the user know if the platform is unable to implement them. I can see this turning into a philosophical discussion fast, so I'll leave it for now ;)
One question: how does your interface above handle situations like reading from an I2C EEPROM? The sequence of operations is this:

1. Send start and I2C address with WRITE bit on
2. Get acknowledge from slave (the EEPROM)
3. Send the memory address to read from to slave (2 or 3 bytes). Do NOT send stop.
4. Send start again (repeated start) and I2C address with READ bit on
5. Read data, acknowledge all bytes but the last one
6. Send STOP

(for a full reference of the protocol check for example the datasheet of 24LC256 here : http://wulfden.org/downloads/datasheets/24LC256.pdf)
I don't know how to implement steps 3-4 above with your interface. 

Best,
Bogdan


_______________________________________________
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)

Hi,

 

platform_i2c_master_send( 0, address, &data, 3, do_stop=false );
platform_i2c_master_recv( 0, address, &data_rcv, 50, do_stop=true );

 

Will work since first function:

-           will send an address + data in write mode and do not send stop. (last parameter is =false).

And next function:

-          you resend address + get 50 data with a stop at end.(last parameter is =true).

For the 2nd function since, you have not stop the transfer with 1st function, it will do logically a repeated start.

 

I think this is the idea

 

Laurent

 

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

 

Hi,

 

Thanks for this thread. I'll jump in as soon as I can, at this point I simply can't find enough time to get a better view of the I2C implementations on our supported targets and come up with a generic interface on top of them. It's a bit ironic that we're having such problems with an interface as simple as I2C, but life can be ironic at times :)

Martin, please see below.

 

On Thu, Jun 16, 2011 at 2:27 AM, Martin Guy <[hidden email]> wrote:

On 15 June 2011 21:58, Laurent Dufrechou <[hidden email]> wrote:
>>What do you think of just two functions:
>>
>>platform_i2c_master_send( id, address, &data, 20, false );
>>platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means
> stop.

 

I love the simplicity of this and if it proves to be enough I'll not hesitate in implementing it. It hides some lower level I2C operations but as we learnt they aren't always possible to implement on our platforms. Still thinking what to do with the lower level ops (send start, send stop and so on). As I see it at the moment, since they're probably rarely needed in practice a good place for them would be in a platform specific module (stm32.i2c or str9.i2c for example). Or maybe still in the main I2C interface (since they are after all fundamental I2C operations), letting the user know if the platform is unable to implement them. I can see this turning into a philosophical discussion fast, so I'll leave it for now ;)

One question: how does your interface above handle situations like reading from an I2C EEPROM? The sequence of operations is this:

 

1. Send start and I2C address with WRITE bit on

2. Get acknowledge from slave (the EEPROM)

3. Send the memory address to read from to slave (2 or 3 bytes). Do NOT send stop.

4. Send start again (repeated start) and I2C address with READ bit on

5. Read data, acknowledge all bytes but the last one

6. Send STOP

 

(for a full reference of the protocol check for example the datasheet of 24LC256 here : http://wulfden.org/downloads/datasheets/24LC256.pdf)

I don't know how to implement steps 3-4 above with your interface. 

 

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)

Hi,

On Thu, Jun 16, 2011 at 12:46 PM, Laurent Dufrechou <[hidden email]> wrote:

Hi,

 

platform_i2c_master_send( 0, address, &data, 3, do_stop=false );
platform_i2c_master_recv( 0, address, &data_rcv, 50, do_stop=true );

 

Will work since first function:

-           will send an address + data in write mode and do not send stop. (last parameter is =false).

And next function:

-          you resend address + get 50 data with a stop at end.(last parameter is =true).

For the 2nd function since, you have not stop the transfer with 1st function, it will do logically a repeated start.


Thanks, it makes sense now :)

Best,
Bogdan

 

De : [hidden email] [mailto:[hidden email]] De la part de Bogdan Marinescu
Envoyé : jeudi 16 juin 2011 08:22


À : eLua Users and Development List (www.eluaproject.net)
Objet : Re: [eLua-dev] Redefining the I2C Lua interface (was: I2C for LM3S devices)

 

Hi,

 

Thanks for this thread. I'll jump in as soon as I can, at this point I simply can't find enough time to get a better view of the I2C implementations on our supported targets and come up with a generic interface on top of them. It's a bit ironic that we're having such problems with an interface as simple as I2C, but life can be ironic at times :)

Martin, please see below.

 

On Thu, Jun 16, 2011 at 2:27 AM, Martin Guy <[hidden email]> wrote:

On 15 June 2011 21:58, Laurent Dufrechou <[hidden email]> wrote:
>>What do you think of just two functions:
>>
>>platform_i2c_master_send( id, address, &data, 20, false );
>>platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means
> stop.

 

I love the simplicity of this and if it proves to be enough I'll not hesitate in implementing it. It hides some lower level I2C operations but as we learnt they aren't always possible to implement on our platforms. Still thinking what to do with the lower level ops (send start, send stop and so on). As I see it at the moment, since they're probably rarely needed in practice a good place for them would be in a platform specific module (stm32.i2c or str9.i2c for example). Or maybe still in the main I2C interface (since they are after all fundamental I2C operations), letting the user know if the platform is unable to implement them. I can see this turning into a philosophical discussion fast, so I'll leave it for now ;)

One question: how does your interface above handle situations like reading from an I2C EEPROM? The sequence of operations is this:

 

1. Send start and I2C address with WRITE bit on

2. Get acknowledge from slave (the EEPROM)

3. Send the memory address to read from to slave (2 or 3 bytes). Do NOT send stop.

4. Send start again (repeated start) and I2C address with READ bit on

5. Read data, acknowledge all bytes but the last one

6. Send STOP

 

(for a full reference of the protocol check for example the datasheet of 24LC256 here : http://wulfden.org/downloads/datasheets/24LC256.pdf)

I don't know how to implement steps 3-4 above with your interface. 

 

Best,

Bogdan

 


_______________________________________________
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
You're right but :) I was thinking of the (dumb) case where a guy want to
do:

platform_i2c_master_send( id, address, &data_a, 20, false );
platform_i2c_master_send( id, address, &data_b, 20, false );
platform_i2c_master_send( id, address, &sync, 1, true );

Where someone, wants to send multiple bursts without resending the address
(on line 2 and 3).

The advantage of separating address sending in its own function call is that
such crazy cases are de-facto taken into account.

But still I see how to handle such case with some kind of simple state
machine.

Regards,
Laurent

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

On 15 June 2011 21:58, Laurent Dufrechou <[hidden email]>
wrote:
>>What do you think of just two functions:
>>
>>platform_i2c_master_send( id, address, &data, 20, false );
>>platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means
> stop.
>
> I see, with a static flag I can see that there is an ongoing transfer and
> thus I can ignore address parameter.

I'm not sure why you would ignore it... if I understand your datasheet
(first download link at
http://www.luminarymicro.com/products/LM3S8962.html#Datasheet
pages 510/511) you have to program the slave address again after a
repeated start. Does their SDK API hides this?

Anyway...

    M

_______________________________________________
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 16 June 2011 08:22, Bogdan Marinescu <[hidden email]> wrote:
> I can see this
> turning into a philosophical discussion fast, so I'll leave it for now ;)

I have no problems with that, because it is a question of the guiding
principles for good API design.

Question: How do you go about designing a good API for I2C that will
be able to drive all devices and usage situations, including ones we
have never heard of, and that can be implemented on I2C master
devices, including those we have never tried to implement it on?

Answer: Make the API reflect the I2C specification, which is where
these two classes of devices meet.

To be more precise, defining a good API is choosing a language that:
1) is generic enough to let you ask for any operation that is valid.
2) is restrictive enough that you cannot express invalid operations.
3) is not so complex as to be awful to use
It's the conflict between these requirements that helps us refine the
best API for the circumstances.

If we make the API talk I2C, then for the device driver interfaces that we have:
- for the devices that work in terms of START bits, you implement the
valid I2C operations that the API asks for using legal sequences of
those low-level primitives
- for the crippled ones like AVR32, you implement the things they are
able to do and fail the valid operations that they cannot perform

That's enough philosophy :)  I'll answer the specific questions in the
next post.

    M
_______________________________________________
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 16 June 2011 08:22, Bogdan Marinescu <[hidden email]> wrote:
> I simply can't find enough time to get a better view of the I2C
> implementations on our supported targets and come up with a generic
> interface on top of them.

If the generic interface can express I2C well, it will be appropriate
for all I2C hardware.
Specific pieces of hardware are cases that test whether the API is
appropriate, but are not what define it.
For example, if you look at the broken AVR32 hardware, you might
conclude (like I did) that the only valid case of repeated start is
when you send 1, 2 or 3 bytes and receive N.  Fortunately, Laurent
stopped me from doing that! (Thanks, L!)

>> platform_i2c_master_send( id, address, &data, 20, false );
>> platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means stop.
>
> I love the simplicity of this and if it proves to be enough I'll not
> hesitate in implementing it. It hides some lower level I2C operations but as
> we learnt they aren't always possible to implement on our platforms.
> Still thinking what to do with the lower level ops (send start, send stop and so on)

They can be used to implement the valid I2C operations. For example:

  platform_i2c_master_send( id, address, data, nbytes, true );
is
  platform_i2c_send_start( id );
  platform_i2c_send_address( id, address, PLATFORM_I2C_DIRECTION_TRANSMITTER  );
  for (i=0; i<nbytes; i++) platform_i2c_send_byte( id, data[i] );
  platform_i2c_send_stop( id );

In fact, if we add checking for success and "if (stop)
platform_i2c_send_stop( id )", that is the implementation of the new
interface on the existing platforms.  We don't even need to remove the
existing platform functions, just make them static when the old
interface is removed.

> As I see it at the moment, since they're probably rarely needed in
> practice a good place for them would be in a platform specific module
> (stm32.i2c or str9.i2c for example)

If we get the platform interface right, that can express all valid I2C
operations and no invalid ones, they won't be necessary.
A good API would not allow people to say START START STOP ADDRESS STOP

The questions are:
1)  is it sufficient to express everything that is valid I2C?
2) does it allow people to express things that are not valid I2C?

As far as i can see, it allows any valid I2C sequence to be sent,
including arbitrary sequences of writes and reads with repeated
starts, maybe to different slave devices, which is valid I2C and is
used to initialize a series of data acquisition devices and have them
all start capturing data in sync when the final STOP is sent.

The only invalid I2C that I can see it allowing is if they forget to
set STOP on the very last message before the program exits.

   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)

In reply to this post by Martin Guy
Just to come back on why I don't like having address with data in same
function is:
How do you do this whit your model?:

void XXXX_writeRegister(u8 reg, u8 data)
{
        I2C_startAndSendAddress(I2C_0, XXXXXX, I2C_WRITE);
        I2C_sendBytes(I2C_0, &reg, 1, false); //no stop
        I2C_sendBytes(I2C_0, &data, 1, true); //repeat start and stop
}

In your function definition, each time I will have to concatenate all my
data in buffer.
Ex:
void XXXX_writeRegister(u8 reg, u8 data)
{
        Buff[0]=reg;
        Buff[1]=data;
        I2C_startAndSendAddress_and_sendBytes(I2C_0, startAndSendAddress
&buff, 2, true); //do stop
}

This why I separated address from data.
I find it more near from hardware. Now if avr32 does not support this model,
let's go for your option.

Both approaches are ok :)

I will be interested to see NXP components works... :)
Any thought?


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

On 15 June 2011 21:58, Laurent Dufrechou <[hidden email]>
wrote:
>>What do you think of just two functions:
>>
>>platform_i2c_master_send( id, address, &data, 20, false );
>>platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means
> stop.
>
> I see, with a static flag I can see that there is an ongoing transfer and
> thus I can ignore address parameter.

I'm not sure why you would ignore it... if I understand your datasheet
(first download link at
http://www.luminarymicro.com/products/LM3S8962.html#Datasheet
pages 510/511) you have to program the slave address again after a
repeated start. Does their SDK API hides this?

Anyway...

    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)

How about (high level:

write_to_address( id, address, data, len send_stop)
write( id, data, len, send_stop ) --> (no address involved)
read_from_address( id, address, data, len, send_stop )
read( id, data, len, send_stop ) --> (no address involved)

The sequence for buffer operations would be:

1. write_to_address
2. write
3. repeat 2 with all data to write

(the same goes for read).
At Lua level we could further abstract this by calling write_to_address/write or read_from_address/read automatically from the I2C module. As an advantage, the state machine required by this would be platform independent since we'd implement it directly in the I2C module code.

Would this fix the problem?

Best,
Bogdan

On Thu, Jun 16, 2011 at 4:52 PM, Laurent Dufrechou <[hidden email]> wrote:
Just to come back on why I don't like having address with data in same
function is:
How do you do this whit your model?:

void XXXX_writeRegister(u8 reg, u8 data)
{
       I2C_startAndSendAddress(I2C_0, XXXXXX, I2C_WRITE);
       I2C_sendBytes(I2C_0, &reg, 1, false); //no stop
       I2C_sendBytes(I2C_0, &data, 1, true); //repeat start and stop
}

In your function definition, each time I will have to concatenate all my
data in buffer.
Ex:
void XXXX_writeRegister(u8 reg, u8 data)
{
       Buff[0]=reg;
       Buff[1]=data;
       I2C_startAndSendAddress_and_sendBytes(I2C_0, startAndSendAddress
&buff, 2, true); //do stop
}

This why I separated address from data.
I find it more near from hardware. Now if avr32 does not support this model,
let's go for your option.

Both approaches are ok :)

I will be interested to see NXP components works... :)
Any thought?


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

On 15 June 2011 21:58, Laurent Dufrechou <[hidden email]>
wrote:
>>What do you think of just two functions:
>>
>>platform_i2c_master_send( id, address, &data, 20, false );
>>platform_i2c_master_recv( id, address, &data_rcv, 50, true ); //true means
> stop.
>
> I see, with a static flag I can see that there is an ongoing transfer and
> thus I can ignore address parameter.

I'm not sure why you would ignore it... if I understand your datasheet
(first download link at
http://www.luminarymicro.com/products/LM3S8962.html#Datasheet
pages 510/511) you have to program the slave address again after a
repeated start. Does their SDK API hides this?

Anyway...

   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 16 June 2011 15:52, Laurent Dufrechou <[hidden email]> wrote:

> Just to come back on why I don't like having address with data in same
> function is:
> How do you do this whit your model?:
>
> void XXXX_writeRegister(u8 reg, u8 data)
> {
>        I2C_startAndSendAddress(I2C_0, XXXXXX, I2C_WRITE);
>        I2C_sendBytes(I2C_0, &reg, 1, false); //no stop
>        I2C_sendBytes(I2C_0, &data, 1, true); //repeat start and stop
> }

You don't. Sending data bytes after a start without an address is not
valid I2C protocol; every START condition must always be followed by a
slave address.
But I think maybe the "//repeat start" comment is a mistake in the
above; it looks like sendBytes knows nothing about repeated starts or
slave addresses.

> In your function definition, each time I will have to concatenate all my
> data in buffer.
> Ex:
> void XXXX_writeRegister(u8 reg, u8 data)
> {
>        Buff[0]=reg;
>        Buff[1]=data;
>        I2C_startAndSendAddress_and_sendBytes(I2C_0, startAndSendAddress
> &buff, 2, true); //do stop
> }

That's right.  I2C does not define anything about what the data bytes
after the slave address mean: if the data selects a device's internal
register and then the data for that register, that is just data as far
as I2C is concerned. So yes, for devices that take "register_number;
register_value" as data you have to put the register number and value
in a single buffer.

If you want to have a primitive to read/write registers in a specific
I2C device like an EEPROM, the right place to do that is in a Lua
module built on top of the eLua i2c interface.

> I find it more near from hardware. Now if avr32 does not support this model,
> let's go for your option.

No, you can't do half a write and then the other half with AVR32,
because you can't stop the sending hardware in the middle of the data
of a packet and then carry on and stop later. You have to have all the
data already available at the start of the transfer.

    M
_______________________________________________
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 16 June 2011 16:04, Bogdan Marinescu <[hidden email]> wrote:
> How about (high level:
> write_to_address( id, address, data, len send_stop)
> write( id, data, len, send_stop ) --> (no address involved)
> read_from_address( id, address, data, len, send_stop )
> read( id, data, len, send_stop ) --> (no address involved)
> The sequence for buffer operations would be:
> 1. write_to_address
> 2. write
> 3. repeat 2 with all data to write

The register_number+value or eeprom_memory_address+data_to_write are
not part of I2C.
That's the format that DS1337 Real time clock and 24LC32A EEPROM, for
example, impose on top of the I2C data stream to get separate pieces
of information that they need, but that's nothing to do with I2C
itself.

The register+value formats and the address+data formats are a higher
level than I2C: devices imposes another level of structure on top of
the I2C protocol, by saying that different bytes of the data have
specific meanings. The matching place to do that in the API is a
higher level EEPROM API that which concatenates the register number
and its value (or EEPROM memory address and data) into a single string
and calls the i2c API with that.

    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)



On Thu, Jun 16, 2011 at 5:44 PM, Martin Guy <[hidden email]> wrote:
On 16 June 2011 16:04, Bogdan Marinescu <[hidden email]> wrote:
> How about (high level:
> write_to_address( id, address, data, len send_stop)
> write( id, data, len, send_stop ) --> (no address involved)
> read_from_address( id, address, data, len, send_stop )
> read( id, data, len, send_stop ) --> (no address involved)
> The sequence for buffer operations would be:
> 1. write_to_address
> 2. write
> 3. repeat 2 with all data to write

The register_number+value or eeprom_memory_address+data_to_write are
not part of I2C.
That's the format that DS1337 Real time clock and 24LC32A EEPROM, for
example, impose on top of the I2C data stream to get separate pieces
of information that they need, but that's nothing to do with I2C
itself.

The register+value formats and the address+data formats are a higher
level than I2C: devices imposes another level of structure on top of
the I2C protocol, by saying that different bytes of the data have
specific meanings. The matching place to do that in the API is a
higher level EEPROM API that which concatenates the register number
and its value (or EEPROM memory address and data) into a single string
and calls the i2c API with that.

I completely agree with you, but my point was that at the lowest I2C level (that we can afford :) ) we need to be able to do write without an address too. Having to send the device address each time we write something is a limitation in the interface.
It gets even more interesting with reads, where sometimes the protocol requires that the master acknowledges every byte read from the slave EXCEPT the last one. If we do chunked reads (read_from_address followed by read) we can't know in advance when the last one will be sent. I'm guessing that the read functions need another parameter (ack_last_byte).
While writing this, it occured to me that this simple interface might still not be good enough for EEPROMs (I'm coming back to this use case all the time because it's the one I know best). After an EEPROM receives the data to write it needs some time to write it, during this time it will not be accesible and it will not ACK its device address. This is how the master knows when the EEPROM finished the write operation: it polls it by sending start+its address on the I2C bus and checks for an ACK. This is called acknowledge polling. From the 24LC256 manual:

Since the device will not acknowledge during a write
cycle, this can be used to determine when the cycle is
complete (This feature can be used to maximize bus
throughput). Once the Stop condition for a Write
command has been issued from the master, the device
initiates the internally timed write cycle. ACK polling
can be initiated immediately. This involves the master
sending a Start condition, followed by the control byte
for a Write command (R/W =  0). If the device is still
busy with the write cycle, then no ACK will be returned.
If no ACK is returned, the Start bit and control byte must
be resent. If the cycle is complete, then the device will
return the ACK and the master can then proceed with
the next Read or Write command.

Not sure how to implement this with our interface. Maybe with a write_to_address with an empty (NULL) buffer, assuming that reads/writes return an error if the slave device didn't ACK (which is a good idea anyway). A bit ugly, but it should work.

Best,
Bogdan


   M


_______________________________________________
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 16 June 2011 17:00, Bogdan Marinescu <[hidden email]> wrote:
> at the lowest I2C level
> (that we can afford :) ) we need to be able to do write without an address
> too.

The current implementation of i2c.write( id, data1, [data2], [data3],
... ) relies on this, yes, but that's going to have to be rewritten
too.  If you want to take a variable number of parameters in the new
i2c.send(), you'll have to concatenate their values into a Lua buffer
before calling the C platform interface with the whole data packet.
In fact, that might be a good feature to keep, making it easier for
users to construct structured I2C data packets.
Keep that thought in mind a moment...

Anyway, AVR32 cannot write data without an address; the hardware can
only handles complete I2C packets, so that's that.
If you go back to function calls that write half an I2C packet, the
AVR32 is going to have to collect them up until it gets a stop and
then send them all.  That means being able to allocate arbitrary
amounts of RAM in the C device driver, which I don't think is
desirable.

Furthermore, if you make the C platform interface deal only in whole
I2C packets, you can be sure to be able to implement I2C on other
future hardware platforms that also only deal in complete I2C packets.
AVR32 is just the first example we have found that needs this.

> If we do chunked reads (read_from_address followed by read) we
> can't know in advance when the last one will be sent. I'm guessing that the
> read functions need another parameter (ack_last_byte).

Yuck!  Why would you want to read half a response?

> After an EEPROM receives the data to
> write it needs some time to write it, during this time it will not be
> accesible and it will not ACK its device address.

Hmm.  Well, once you define the eeprom-reading and eeprom-writing
functions in Lua on top of the I2C module, you can move the check
there.  Assuming you have already probed for the presence of the
EEPROM, one idea is something like:
I'm also assuming the new i2c.send takes the same mutiple data
parameters as i2c.write

-- read data from eeprom with 16-bit address sent high byte first.
function i2c.eeprom.read( id, slave, mem_addr, nbytes )
  -- i2c.send: assuming nil means failure and some integer means the
number of bytes written
  repeat until i2c.send( id, slave, false, msb( mem_addr ), lsb( mem_addr ) )
  -- i2c.recv: assuming nil means failure, otherwise a string of the
data received
  return i2c.recv( id, slave, true, nbytes )
end

function i2c.eeprom.write( id, slave, mem_addr, data )
  repeat
    result = i2c.send( id, slave, true, msb( mem_addr ), lsb( mem_addr ), data )
  until result
  -- How many of the data bytes were written?
  if result < 2
    return nil
  else
    return result - 2
  fi
end

Again, the fact that these are in Lua reflects the fact that the
EEPROM data format is not part of I2C.  It's just one device that
happens to fill the I2C data with a 16-bit address in high-byte then
low-byte order, followed by the data to write to EEPROM.

My own problem with i2c.probe(id, address) is that AVR32 cannot send
empty data packets.
Do you happen to know what an I2C EEPROM does if you send it a single
0 byte (just the MSB of the memory address).
Ignores it, I hope; the spec doesn't say.

    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)



On Thu, Jun 16, 2011 at 6:54 PM, Martin Guy <[hidden email]> wrote:
On 16 June 2011 17:00, Bogdan Marinescu <[hidden email]> wrote:
> at the lowest I2C level
> (that we can afford :) ) we need to be able to do write without an address
> too.

The current implementation of i2c.write( id, data1, [data2], [data3],
... ) relies on this, yes, but that's going to have to be rewritten
too.  If you want to take a variable number of parameters in the new
i2c.send(), you'll have to concatenate their values into a Lua buffer
before calling the C platform interface with the whole data packet.
In fact, that might be a good feature to keep, making it easier for
users to construct structured I2C data packets.

I know you're probably sick and tired of my EEPROM examples :) but I'm going to get back to that again. In one of my applications I actually read the whole content of a serial EEPROM (32KB (not Kb)) into a file in the filesystem. Reading the whole data and writing it in one step is simply not good enough because I'd need too much memory for the operation, so instead I'm reading it in chunks of 256 bytes. For this kind of application, an "I2C data packet" is an undefined concept, as the packet can be as large as the memory itself, as small as 1 byte of data or an arbitrary size (256 bytes of data). I specify the memory address (0) only at the beginning, then I keep on reading data until I have enough, at which point I send a "stop". Yes, I can also do this by issuing repeated "read" commands with increasing addresses, but that adds an overhead. And I'm not worried about the actual impact of the overhead (which is very small), I just feel that the I2C interface introduces limitations that shouldn't be there ...
 
Keep that thought in mind a moment...

Anyway, AVR32 cannot write data without an address; the hardware can
only handles complete I2C packets, so that's that.
If you go back to function calls that write half an I2C packet, the
AVR32 is going to have to collect them up until it gets a stop and
then send them all.  That means being able to allocate arbitrary
amounts of RAM in the C device driver, which I don't think is
desirable.

Furthermore, if you make the C platform interface deal only in whole
I2C packets, you can be sure to be able to implement I2C on other
future hardware platforms that also only deal in complete I2C packets.
AVR32 is just the first example we have found that needs this.

> If we do chunked reads (read_from_address followed by read) we
> can't know in advance when the last one will be sent. I'm guessing that the
> read functions need another parameter (ack_last_byte).

Yuck!  Why would you want to read half a response?

Hopefully my example above answers your questions.
 

> After an EEPROM receives the data to
> write it needs some time to write it, during this time it will not be
> accesible and it will not ACK its device address.

Hmm.  Well, once you define the eeprom-reading and eeprom-writing
functions in Lua on top of the I2C module, you can move the check
there.  Assuming you have already probed for the presence of the
EEPROM, one idea is something like:
I'm also assuming the new i2c.send takes the same mutiple data
parameters as i2c.write

-- read data from eeprom with 16-bit address sent high byte first.
function i2c.eeprom.read( id, slave, mem_addr, nbytes )
 -- i2c.send: assuming nil means failure and some integer means the
number of bytes written
 repeat until i2c.send( id, slave, false, msb( mem_addr ), lsb( mem_addr ) )
 -- i2c.recv: assuming nil means failure, otherwise a string of the
data received
 return i2c.recv( id, slave, true, nbytes )
end

function i2c.eeprom.write( id, slave, mem_addr, data )
 repeat
   result = i2c.send( id, slave, true, msb( mem_addr ), lsb( mem_addr ), data )
 until result
 -- How many of the data bytes were written?
 if result < 2
   return nil
 else
   return result - 2
 fi
end

Again, the fact that these are in Lua reflects the fact that the
EEPROM data format is not part of I2C.  It's just one device that
happens to fill the I2C data with a 16-bit address in high-byte then
low-byte order, followed by the data to write to EEPROM.

Absolutely. I'm not using EEPROM as an example because I want to model our I2C platform interface after it, it's just an use case I'm quite familiar with in practice and I can use it to detect limitations in our interface. So far it's doing a pretty good job :)  

My own problem with i2c.probe(id, address) is that AVR32 cannot send
empty data packets.

This is getting ridiculous. At this point I wish they kept the I2C engine from their 8 bit AVR MCU line. Besides not being able to to DMA transfers (although I'm not sure if this is still true for XMEGAs) it was much better than what they offer in the AVR32 IMO. 
 
Do you happen to know what an I2C EEPROM does if you send it a single
0 byte (just the MSB of the memory address).
Ignores it, I hope; the spec doesn't say.

Hmmm, hard to say. It wouldn't be a problem for EEPROMs I think, but I don't know what's the case for other I2C devices.

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)

On 16 June 2011 19:20, Bogdan Marinescu <[hidden email]> wrote:
> In one of my applications I actually read the
> whole content of a serial EEPROM (32KB (not Kb)) into a file in the
> filesystem. Reading the whole data and writing it in one step is simply not
> good enough because I'd need too much memory for the operation, so instead
> I'm reading it in chunks of 256 bytes.

Thanks.  Yes, you would need to do the reading of the blocks at Lua
level as separate I2C transactions as you describe.

> I just feel that the I2C
> interface introduces limitations that shouldn't be there ...

The advantage of making the eLua APIs match the I2C protocol
definition is that you can be sure to implement the API on any
hardware, can be sure to drive any device, and be sure that users can
only talk valid I2C language, however they call it.
It's also a way of teaching learners exactly what valid I2C protocol means.

I'm working on the assumption that modelling the eLua APIs on the I2C
specification is the best way to guarantee that we will be able to
drive all master interfaces and talk to all slave devices, including
those we do not know, or that have not been produced yet.

On one hand, the spec does say that I2C does not impose a limit on the
amount of data present in one transfer.
On the other, ir doesn't say that it's mandatory for a master to be
able to handle infinite length transfers.

> Absolutely. I'm not using EEPROM as an example because I want to model our
> I2C platform interface after it, it's just an use case I'm quite familiar
> with in practice and I can use it to detect limitations in our interface. So
> far it's doing a pretty good job :)

I confess that starting a 32-megabyte transfer in one function call,
then reading the data of one packet in 256-bytes chunks with separate
function calls is not a usage scenario that I had thought of.

Here's another nightmare for you. I just found a device that eats
infinite-length data packets for lunch.
It's an 8-bit ADC/DAC device
http://www.nxp.com/documents/data_sheet/PCF8591.pdf
that can perform both analog-to-digital or digital-to-analog
conversion in the same time it takes to transmit or receive one byte
and it can send you an infinite amount of data, or you can send *it*
an infinite amount of data in one I2C read or write packet, assuming
that you can consume or generate it fast enough: one sample every
90us, i.e. every nine 100kHz bus clocks == 11,111 samples per second.
To do audio, you'd have to be able to make a Lua-to-C-to-Lua
transition in 90 us so as not have glitches in the output or miss
samples on input. At 100 MHz CPU clock and 4 cycles per instruction,
that's something like 250 instructions, which sounds possible in
assembler. Could you make a round trip from the Lua interpreter to C
and back in that sort of time and do something useful with the data?
Would you want to? :)

>> My own problem with i2c.probe(id, address) is that AVR32 cannot send
>> empty data packets.

> This is getting ridiculous. At this point I wish they kept the I2C engine
> from their 8 bit AVR MCU line. Besides not being able to to DMA transfers
> (although I'm not sure if this is still true for XMEGAs) it was much better
> than what they offer in the AVR32 IMO.

It gets worse. A different ADC instead
http://www.ti.com/lit/gpn/ads7823
when you just send it a single zero data byte, that means "power up
and convert 4 samples from analog to digital".  I suppose, as it's a
slave device, that one case is harmess, but what might other devices
do?  Anyway, there's nothing I can do about that.

The only difference the AVR32 limitation makes to us is that it
suggests adding i2c_master_probe( id, slave_address ) to the C
platform interface, so that AVR32 can do it's brain-damaged thing
sending a 0 byte, while allowing other platform can do something more
healthy.  If that were implemented at a higher level as send(id,
address, NULL, 0, true), the AVR32 would have to either fail that
always because it can't do it (making probe() unusable) or send a
single zero data byte when the user asked for none. I'm not sure which
is worse.

I guess that's why they call it TWI, not I2C.  Unfortunately, I have
this to work with :(

Is it too much to limit the data transfer size to the RAM you want to
dedicate to one message?
Implementing a multi-function stop-and-go interface frightens me.

   M
_______________________________________________
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)

Laurent

  Given that the AVR32 TWI hardware is so limited in what it can do,
I've decided to implement I2C by bit-banging the two pins so that it
will be able to do all of I2C instead of the restricted subset that
the hardware can handle. It also saves me from either having to
convince Bogdan to be sensible, or having to make and maintain an
incompatible fork of eLua, both of which are much more painful.

  I'm afraid that this leaves you on your own again. I can only
suggest that you try one of:
1) see if you can modify your SDK's routines to split them into what
the current API demands
2) do something like what I would have had to do if I had used the
AVR32 TWI hardware: collect the sequence of start() address() stop()
calls in static variables and then emit commands to the hardware when
you have received enough of a sequence that it can perform.
3) Don't use the chip's I2C hardware and implement those primitives as
a bit-banging interface.

Sorry, and good luck.

    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 Wed, Jun 22, 2011 at 5:28 PM, Martin Guy <[hidden email]> wrote:
> Laurent
>
>  Given that the AVR32 TWI hardware is so limited in what it can do,
> I've decided to implement I2C by bit-banging the two pins so that it
> will be able to do all of I2C instead of the restricted subset that
> the hardware can handle.

OK, so I have to admit that I haven't followed every twist and turn of
this, nor have I looked at the NXP hardware or driver implementation
that you're discussing suggestions about below, but it strikes me that
if we're going to have to bitbang i2c in order to make the eLua API
work with the hardware modules on more than one platform then the API
has got to change.  Maybe what we should do in that situation is to
branch it out into some sort of "restricted" and a "low level" API,
I'm not sure.

It sounded like some of the differences of opinion were around the
idea of being able to read and write in chunks rather than doing it as
a one-shot operation. The issue is that platforms like AVR32 can't do
the chunked operations since the hardware expects the transaction to
be described upfront?

The read and write functions could probably work in both modes, with
passed parameters indicating that the transaction shouldn't be closed
yet or something similar?  For platforms that don't support this as an
option, and this would be documented as well, would return an error
indicating that it's not a supported capability.  We already have some
precedent for this in the sense that for things like ADC only certain
timers (or none) can be used as trigger sources.  This functionality
could be referred to as "extended" i2c support or something similar?
Riffing on bogdan's example, maybe something like this:

write( id, address, data, len, send_stop )
read( id, address, data, len, send_stop )

Where in this case send_stop is an optional parameter only supported
for platforms where one can do an open-ended transaction, this would
lead to a model like this for such multi-stage:
write( id, address, data, len, 0 ) -- trying to not send a stop would
give an error on platforms like avr32
write( id, -1, data, len, 0 ) -- repeat as needed, passing -1, or
maybe nil in Lua indicates not to try sending the address again
(attempting to call write with a non-valid address on non-"extended"
platforms would result in an error
write( id, -1, data, len, 1 ) -- end

In lua the stop parameter could be an optional one, again specifically
labelled as being part of "extended" support.

I would generally be a fan of encapsulating as much functionality into
a given Lua function call as possible anyways since looping and
iterating in C code is much cheaper than looping in Lua.  I don't
hugely like having separate functions to spit the address out on the
bus etc since that's yet more byte code that has to be executed every
time the user starts an i2c operation.

Does that seem more reasonable or have I missed part of this?

> It also saves me from either having to
> convince Bogdan to be sensible, or having to make and maintain an
> incompatible fork of eLua, both of which are much more painful.

I lost track of this discussion a little.  I think that as a bottom
line the API should be able to degrade well enough to work with the
hardware that's available while incorporating a useful feature set for
a high level language (reducing the amount of bytecode that has to be
churned to get a task done, plus providing a reasonably friendly API).
At the same time, I don't think the API has to be perfect, just useful
for most of the cases where it is applied, unambiguous about what it's
doing and just attractive enough so that when people look at it they
think about getting use out of it rather than how ugly it is (there's
more to it than these, but I think these are relevant to this case). I
probably should have jumped in here and dug into more of the details
to try and help hash this out a bit better, but I've been running a
bit low on time and felt like reading up a bit on the platform and i2c
spec before chiming in again.

If we can, lets take this a few steps back and focus on hashing out
something that will work for all of us.

Best.

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