I2C Slave mode support?

ChibiOS public support forum for all topics not covered by a specific support forum.

Moderators: RoccoMarco, lbednarz, utzig, tfAteba, barthess

User avatar
Giovanni
Site Admin
Posts: 14461
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: I2C Slave mode support?

Postby Giovanni » Tue Nov 19, 2013 9:17 am

I propose the synchronous solution because initially the I2C driver was callback-based much like the SPI one. The problem was that the driver was very large and unreliable because STM32 I2C "limitations" so we decided to switch to a synchronous API which allowed us to work around all the problems.

Another advantage of the synchronous API is that it allows for SW I2C emulation that was our backup solution.

Another reason would be that if we want to make a single master/slave driver then both master and slave APIs should share a common design philosophy.

I don't know the I2C in depth but I imagine that we should follow this protocol:
1) A thread waits for an address match and gets what the master has to transmit.
2) Then the thread has to decode the request and decide how to answer (return data, NACK, other?).

Are two functions sufficient for this? something like: i2cSlaveWaitMessage() and i2cSlaveAnswerMessage().

My question is, what the behavior of the driver should be if a master sends an address but the slave is not yet in i2cSlaveWaitMessage()? Lets imagine this in terms of state machine.

Giovanni

genosensor
Posts: 65
Joined: Thu Oct 03, 2013 1:06 am
Location: Santa Cruz, California
Has thanked: 1 time
Been thanked: 1 time

Re: I2C Slave mode support?

Postby genosensor » Tue Nov 19, 2013 11:04 am

This all sounds very similar to the experience I had many years ago implementing I2C on the MSP430169.

There, we finally ended up with a synchronous master and a callback/ISR oriented slave.
The I2C master initiates transactions, so there, synchronous code works well.
Having blocking calls for the slave forces one into an event loop, as the slave must react to events as the arrive (potentially from multiple masters). Once one is forced into an event loop, one might as well just have callbacks, as the event loop dictates that one can longer encode state in the flow of the code (i.e. control structures) But, that's just my own opinion.

I thought the proposal I made for a synchronous slave API would work well.
A thread waits for an address match indicating either a master write or master read has begun.
Depending on which, the thread then provides the necessary buffer to receive data from of provide data to the master.
NACK would be returned to the master only if the address does not match any slave's.
This is why the address should be set up once and remain valid.

To your specific question,
Whenever the address matches, the I2C standard stipulates that the slave will pull the I2C clock low until it can send back some response (or specifically aborts the transaction) The whole I2C bus is thus held up until the slave responds.

In terms of (high level) driver states:

IDLE = No message transaction in progress
MATCHED_READ = Address Matched for Master Read (query to be answered)
READING = transferring reply from slave to master
MATCHED_WRITE = Address Matched for Master Write (data to be stored)
WRITING = transferring message body from mater to slave

Transitions:
IDLE --> MATCHED_READ --> READING --> IDLE
IDLE --> MATCHED_WRITE --> WRITING --> IDLE

Once the address matching criteria are established, I think the three functions I described in my previous message would be sufficient for this state machine to run in the event handling loop of a single (dedicated) thread.

i2cAwaitAddressMatch() blocks until the driven enters the MATCH_READ or MATCH_WRITE states (indicating which was entered).
i2cSlaveAccept() handles the MATCH_WRITE state.
i2cSalveAnswer() handles the MATCH_READ state
Both block until the driver reenters the IDLE state.
I could perhaps define the act of calling i2CAwaitAddressMatch() while in either MATCHED_READ or MATCHED_WRITE
as aborting the transaction in progress, sending a NAK if possible. However, I don't think the I2C protocol allows the slave to NAK in some cases. I'll look into this.

Another option would be to define:

Code: Select all

msg_t  i2cSlaveAwaitMessage(uint8 *inputBuffer, size_t size, i2caddr_t *accessAdr);
/*
  receive incoming master write into inputBuffer, returning the number of bytes received.
  If non-NULL, *accessAdr = matched address.
  returns a I2CQUERY error code whenever a master requests a response.
  Slave must quickly call i2cSlaveAnswer() in this case
*/

This combines the functions of AddressMatched() and Accept()
Perhaps this is better. No need to wake the thread twice for each master write.
On the other hand, all writes, regardless of their accessAdr, would have to go into the same buffer.
On could not decide, for instance, to put ALL CALL messages somewhere special in RAM (without copying them there).

Whether two functions or three really is a detail. I'd like to move ahead with testing.

Initially, I'll try to implement the synchronous API atop the more efficient and flexible asynchronous one.
If I run into trouble with that, I'll fall back on the synchronous only approach.
Even if the sync API is finally implemented on the async one for the STM32, you could decide that the synchronous API is the only slave I2C to be supported across all processors.

Note:
Wait is not a transitive verb in English. I prefer Accept or AwaitMessage(), but WaitForMessage() would also be O.K.

User avatar
Giovanni
Site Admin
Posts: 14461
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: I2C Slave mode support?

Postby Giovanni » Tue Nov 19, 2013 11:11 am

Being the driver meant to run under a RTOS I imagine we can have a thread serving the I2C slave, the need for callbacks is not so strong.

Anyway, please go ahead with your testing, we can define details along the way, as long the KISS principle is respected I am happy, a simple driver means less problems and support headache.

Giovanni

genosensor
Posts: 65
Joined: Thu Oct 03, 2013 1:06 am
Location: Santa Cruz, California
Has thanked: 1 time
Been thanked: 1 time

Re: I2C Slave mode support?

Postby genosensor » Tue Dec 10, 2013 11:48 pm

I have largely completed my update of the STM32 I2Cv1 driver to add support for slave mode transactions.
Although I've tested only on the STM32L1xx, the driver should also work for other STM32 chips employing it.
(Looks like those are the STM32F1xx and the STM32F4xx)

The code is available at:

https://github.com/brentr/ChibiOS-RT

Anyone interested may clone the repo with:

Code: Select all

git  clone   git@github.com:brentr/ChibiOS-RT.git

This is all based on the 2.6x stable series.

Most of the changes are in i2cslave.h (new file) and i2c_lld.*
Note that there is no i2cslave.c, as the slave mode support is integrated into the existing i2c driver when, in halconf.h:

Code: Select all

#define HAL_USE_I2C_SLAVE           TRUE

The only change I made outside the driver was to add a macro to initialize virtual timers allocated in automatic storage.
I called this chVTInit(). If there's a better/more standard way to get this done, please let me know.

The existing, admittedly basic, tests are in the projects i2cmaster and i2ctest.
To run these without change, connect the PB8 and PB9 pins of two boards together and pull each up to VDD through ~4Kohm.
Run the i2cmaster project on one board and i2cslave on the other.
If you have the latest version of OpenOCD and are interfacing via STLINKv2, progress messages will appear in the debugger via DCC.
Otherwise, you'll need to modify the test code to output text to the serial port of your choosing.

The only bug I know of at this time is that the master cannot send zero length messages.
(But slaves have no problem receiving them :-)
I'll be trying to fix that this week.

In the coming weeks, I intend to:

1) update of the I2C driver template
2) provide more thorough test programs for the multimaster API

I tried to follow the documentation patterns I saw in the original code, however, I am unfamiliar with Doxygen, so I expect I may have some @keywords wrong. Please let me know about this and other issues by replying on this thread.

This slave mode I2C API provides both an ISR callback interface and one based on a devoted I2C event server thread.
Note that, for a given I2C channel, one must choose to use either the callback or the event server.
The event server interface is implemented entirely on top of the callback interface.

The I2Ctest project implements the exact same same slave behavior via either callbacks or a server thread depending on compile-time switches.
It may help to study it to compare the alternative APIs.
For simple processing, use of the callback interface will minimize latency and memory.
It is probably a better choice for interfacing high-rate sensors (like accelerometers) to the I2C bus.
But, the server thread API is easier to debug and puts fewer constraints on message processing.
So, it might be a better choice for implementing more complex messaging protocols.
Of course, for the best of both worlds, it is easy to define callbacks to wake custom threads.
See the end of platforms/STM32/I2Cv1/i2c_lld.c to see an example of this for the event server thread API.
Attachments
i2ctests.zip
i2cmaster and i2cslave test projects
(14.02 KiB) Downloaded 299 times

joaquin
Posts: 38
Joined: Sat Jan 22, 2011 8:44 pm
Has thanked: 1 time
Been thanked: 3 times

Re: I2C Slave mode support?

Postby joaquin » Sat Apr 05, 2014 10:48 pm

In a project I am developing, I have the need to implement slave I2C. Does anyone knows of any advance in this area?
Thanks,
Joaquin

genosensor
Posts: 65
Joined: Thu Oct 03, 2013 1:06 am
Location: Santa Cruz, California
Has thanked: 1 time
Been thanked: 1 time

Re: I2C Slave mode support?

Postby genosensor » Sun Apr 06, 2014 1:10 am

What sort of advance were you seeking?
Can you be more specific as to why the slave support offered in this thread will not meet your needs?

User avatar
Giovanni
Site Admin
Posts: 14461
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: I2C Slave mode support?

Postby Giovanni » Sun Apr 06, 2014 7:50 am

The proposed change is likely to be its final form, feedback is welcome.

The code will probably end up in 3.0.

Giovanni

joaquin
Posts: 38
Joined: Sat Jan 22, 2011 8:44 pm
Has thanked: 1 time
Been thanked: 3 times

Re: I2C Slave mode support?

Postby joaquin » Sun Apr 06, 2014 8:02 am

I need to connect to an I2C bus, hearing on the the data that some sensors send to a different master, and for some addresses my device will be responsible to give data to the master. On other I2C bus my device will be the only master.

Genosensor, the implementation as you have done seems to be perfect for me, although I have not yet implemented it. My question was about the fussion with the main trunk: your last post was on october and seemed mature for me, but in version 2.6.3 on february was not still implemented, i wondered if there were some issue still pending to be solved. Giovanni has just answered with the solution.

I will use your version, and many thanks for contributing this code, and to Giovanni for developing this wonderful OS

steved
Posts: 825
Joined: Fri Nov 09, 2012 2:22 pm
Has thanked: 12 times
Been thanked: 135 times

Re: I2C Slave mode support?

Postby steved » Wed Aug 20, 2014 6:10 pm

Wondered if anyone has already ported this code across to 3.0?

Otherwise its my next task.

User avatar
Giovanni
Site Admin
Posts: 14461
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: I2C Slave mode support?

Postby Giovanni » Wed Aug 20, 2014 6:26 pm

Hi,

Not yet, but feel free to try :)

Giovanni


Return to “General Support”

Who is online

Users browsing this forum: No registered users and 5 guests