how to implement a SLAVE-mode MQX i2c driver??

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

how to implement a SLAVE-mode MQX i2c driver??

5,092 Views
gomer
Contributor I

Hello all.  I am currently running MQX v3.6.0 on an MCF52259, and have a Master i2c driver working great on one of the 2 i2c ports on this processor.

 

Now, I need to implement a slave driver on the other port, as this processor is one of many that exist as 'slaves' on a system-wide 400k bus.

 

My problem is, I see no documentation on how to do this, and I'm not finding much of anything on these forums.  Implementing a slave-mode driver does not appear to be as straight-forward as the master-mode driver was.  I mean, yes... obviously there is a 'slave mode' IOCTL call that I'm setting, and the slave's station address... my question is what do I do at that application layer to service this driver?

 

Any pointers in the right direction would be great!

 

Thanks.

0 Kudos
15 Replies

3,016 Views
trailman
Contributor V

Still no answer :smileysad:

I can't believe that nobody has already worked on slave mode I2C, as this is not a "niche" way to use I2C.

Any help would be apreciated.

Thanks

0 Kudos

3,016 Views
intevac_coder
Contributor III

Here's the continued thread on this topic:

 

https://community.freescale.com/message/94660#94660

 

Here you will find my code snippet.  However, it doesn't work perfectly and I have yet to figure out why.

0 Kudos

3,016 Views
trailman
Contributor V

Hi all,

 

As gilles-b, I'm looking for a slave or master/slave example.

Somebody can help ?

 

0 Kudos

3,016 Views
MasterDanilo
Contributor I

Hello all..

 

I also need this slave i2c example... Where is it? Didn't somebody forget this topic, did it? 

 

Thanks

0 Kudos

3,016 Views
gilles_b
Contributor I

Hi freescalers and other guys in the place,

 

I currently use the I2C interface of my MCF52259 as a master to periodically read the status of some sensors, using the MQX interrupt driven driver. This works fine.

 

But I also need to be able to act as a SLAVE when a master is addressing me : receive some data written the a master (request message from master) and respond to this message with some data that is read by the master (reply message) in the same transfer. The transfer initiated by the master would look like as follows : START, ADDR-WR, REQUEST-BYTES-WRITE, RESTART, ADDR-RD, REPLY-BYTES-READ, STOP.

So at the end I need a master/slave support.

 

I had a look to the latest MQX 3.7 source code and did not find any example for master/slave mode I2C, even for a simple slave mode I2C.

Did I miss something in the code ?

Is there an application note ?

 

If someone can give me some tips or a sample code (at least for a simple slave mode), It would be very nice.

 

gilles-b

0 Kudos

3,016 Views
PetrM
Senior Contributor I

Hello,

 

the rest of the handling is similar to master mode. You have to use IO_IOCTL_I2C_SET_RX_REQUEST before read, read/write functions are the same - they just have to be issued before master addresses the slave, the transfer should be finished with IO_IOCTL_I2C_STOP. And you can check the actual bus status with IO_IOCTL_I2C_GET_BUS_AVAILABILITY command.

 

PetrM

 

0 Kudos

3,016 Views
gomer
Contributor I

Well, maybe I should clarify a bit more:

 

1)  In the Master mode driver, initiation of transactions and handling of responses is easy, since my code is 'driving' that process.  However, in slave mode I'm in a 'reactionary' mode... in other words I need some way of knowing when a Master is attempting to read/write from/to me, since it's asynchronous in nature.  So... does this mean I sit and spin on an '_io_read()' until something comes in?  That seems to be the only solution I've found so far, as there does not appear to be a callback mechanism that I can register with the driver, nor a simply way for the interrupt to land in user code directly.

 

2)  I've actually tried the _io_read method above, and it seems to work sort of.  The problem is, with both the INT and POLLED versions of the drivers, that interface (at the lowest driver level) literally consumes all CPU cycles until something arrives, which means that task gives no cycles to other tasks.  Furthermore, when I accept a 'command' from that method, then try to turn around and '_io_write' data back out, the data never actually seems to get written onto the bus.

 

3)  You've confused me with your reference to IO_IOCTL_I2C_STOP... you're not suggesting my slave driver issue STOPs right?  That's the job of the Master... yeah?  So are you saying I should be looking for that 'state' in my application code, and then responding?

 

4)  Actually.. this whole sentence is a little confusing to me:  "read/write functions are the same - they just have to be issued before master addresses the slave".  I'm the slave.  My code uses read/write functions 'as normal'... but I'm supposed to issue those reads/writes **before** I know I've been addressed by the Master??

 

Perhaps you can clarify a bit more for me.  Would it be possible to maybe get some pseudo-code that might help guide me?  I can't believe I'm the first to want to implement a slave i2c MQX driver, so there must be some examples out there :smileyhappy:.

 

Thank you very much.

Gomer

 

0 Kudos

3,016 Views
PetrM
Senior Contributor I

Hello,

 

ad 1) Well, in polled mode, read/write are blocking operations, you use it when there's nothing else to do. In interrupt mode they are non-blocking and driver uses internal buffers, so you can time to time check/feed the new data. There's no overflow/underrun because I2C bus is driven by both master and slave. There's no callback implemented. You can also check the state of the bus or driver by ioctls IO_IOCTL_I2C_GET_STATE and IO_IOCTL_I2C_GET_BUS_AVAILABILITY.

 

ad 2) As slave, you cannot change the direction of a transfer. The transfer direction is only write or only read until STOP or REPEATED START driven by master.

 

ad 3) I2C bus STOP is job of the master, yes. But the IO_IOCTL_I2C_STOP command must be used on both sides (master and slave) to return the driver into original state. Sorry for confusion.

 

ad 4) You're right, slave can but doesn't have to read/write (to block) before master. I meant that slave has to prepare/wait for the data anyway. The bus will wait for slave's intervention. The slave just have to check if there's something going on.

 

We're planning to add I2C slave example to the release, but for now, let's summarize:

 

MASTER:

 

i2cfd = open ("i2c0:", NULL);    // open driver as master in polled mode

param = 0x60; ioctl (i2cfd, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);   // master's I2C address is 0x60

param = 0x50; ioctl (i2cfd, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &param);   // call device with address 0x50

param = 0x10; ioctl (i2cfd, IO_IOCTL_I2C_SET_RX_REQUEST, &param);    // want to read 16 bytes in total

fread (buffer, 1, 10, i2cfd);    // initiate transfer, send destination address (first use of read/write), read 10 bytes from device
fread (buffer + 10, 1, 6, i2cfd);    // read remaining 6 bytes
ioctl (i2cfd, IO_IOCTL_FLUSH_OUTPUT, &param);    // wait for completion, retrieve ack/nack in param

ioctl (i2cfd, IO_IOCTL_I2C_STOP, NULL);    // stop transfer, release the bus

 

SLAVE:

 

i2cfd = open ("i2c0:", NULL);    // open driver as master in polled mode

ioctl (i2cfd, IO_IOCTL_I2C_SET_SLAVE_MODE, NULL);   // set SLAVE mode

param = 0x50; ioctl (i2cfd, IO_IOCTL_I2C_SET_STATION_ADDRESS, &param);   // slave's I2C address is 0x50

fwrite (buffer, 1, 16, i2cfd);    // wait for master to address slave, write 16 bytes

ioctl (i2cfd, IO_IOCTL_I2C_STOP, NULL);    // get driver to original state

 

Regards,

PetrM

 

0 Kudos

3,016 Views
intevac_coder
Contributor III

PetrM,

 

Do you have a functioning example of the Kinetis processor as an I2C slave device reading on the I2C bus?  I can get write to work as slave but reading as slave doesn't seem to work correctly.

 

Thanks!

0 Kudos

3,016 Views
gomer
Contributor I

Putting a STOP on the bus from my Slave driver, following an fwrite() broke things again.  Without actually augmenting code to prove it, I believe it's because of the following code:

 

      case IO_IOCTL_I2C_STOP:
         i2c_ptr->I2CR &= (~ (MCF52XX_I2C_I2CR_MTX | MCF52XX_I2C_I2CR_TXAK));
         if (! (i2c_ptr->I2CR & MCF52XX_I2C_I2CR_MSTA))
         {
            tmp = i2c_ptr->I2DR;
         }
         i2c_ptr->I2SR &= (~ (MCF52XX_I2C_I2SR_IAL | MCF52XX_I2C_I2SR_IIF));
         i2c_ptr->I2CR &= (~ MCF52XX_I2C_I2CR_MSTA);
         io_info_ptr->TX_IN = io_info_ptr->TX_OUT;
         io_info_ptr->RX_OUT = io_info_ptr->RX_IN;
         io_info_ptr->RX_REQUEST = 0;
         io_info_ptr->STATE = I2C_STATE_READY;
         while (i2c_ptr->I2SR & MCF52XX_I2C_I2SR_IBB)
            { };   // wait for completion
         break;

That's the same problem I ran into in the driver itself... the fact that in certain scenarios in the context of a Slave mode driver, those buffer pointers should NOT be messed with.  I denoted this in another thread in which I attached a source file of my augmented (working!) driver code.

 

It's all about timing.  The i2c HW interrupts happen 'under the hood' way faster than the application SW can get around to keep pace with them.

 

For example, only 6 micro-seconds (this is a 400k bus, BTW) following the last byte I send out, I got another address match and a byte of data in the Rx buffer.  That happened *before* my application had a chance to make this call to IOCTL_STOP.  So I had a character in my Rx buffer that just got stomped on by that call to STOP because it resets the buffer pointers.

 

Perhaps an "if(this_driver_is_Master)" guard around those two lines of code would work?  That's not very clean, for sure.

 

Anyway, for now I removed the call to IOCTL_STOP, and things are working great again.

 

Thanks,

Gomer

0 Kudos

3,016 Views
gomer
Contributor I

Thanks for your reply.

 

What might help is if I explain *how* we're using this bus.  We have defined a communication protocol between the Master and the Slave processors, and this protocol includes writing data of arbitrary size to the Slave(s) from the Master, as well as reading data of arbitrary size from the Slave(s).

 

The protocol consists of various commands, each falling into a 'Read' or 'Write' command category (from the perspective of the Master).  When the Slave recieves a 'Read' command it needs to respond with the correct data back to the Master.  The master knows how much to read based on the command it sent.

 

When a Slave recieves a 'Write' command, it needs to simply digest the incoming characters until no more characters are recieved, then do something with that data in its application code.

 

This is most likely a bit unorthodox... we may be stretching the normal pardigm of Master/Slave data transfers a bit.  We started out with 8-bit PIC processors as the Slaves on the bus (up to 22 of them), and so it was relatively easy to spin our own, streamlined, specialized Slave driver to meet this purpose.

 

Now we're migrating into MCF5225x in place of the PICs in certain places, running MQX.

 

You're SLAVE: example doesn't actually include any Read-oriented semantics... only writing data to the Master.  We need both directions.  Only when we read the initial data in (which holds the command), do we know what data to write back out (if it's indeed a Write command).

 

In Re: you're comment "Well, in polled mode, read/write are blocking operations" -- by blocking, I meant Task-blocking... like the UART driver does.  Blocking in this polled i2c driver case is simply spinning on a while() loop until an interrupt is set, thus consuming all the CPU cycles and not letting any other application task run.  We are using the preemptive scheduling model, BTW.  We have to.

 

In Re: the IOCTL_STOP command -- I understand your distinction now.  I will use it as you've suggested and see if it works.  As of right now, with the modifications I've made to the existing driver code, it is working great.  And believe me, we transfer a LOT of data over this bus, in bursty fashions sometimes.  We have a "stress test" application that runs on the Master processor which is designed simply for the purpose of stressing the he!# out of this bus and the Slave drivers on it... I hope to fire that up today or tomorrow and see what my results are.

 

I really appreciate your feedback.  I acknowledge that we may be using this bus (and thus the driver) in a bit of a specialized manner, but nevertheless... my hope is that this back and forth is still beneficial to other readers of the forum, and other folks that may find themselves wanting to use a slave-mode i2c driver in their project. 

 

Since, to-date, there is no working example of such a driver to our knowledge, then this is a good starting place :-)!  I do think that when you produce your sample Slave i2c driver, you should keep this bi-directional paradigm in mind.  A driver / sample that accomodates our relatively complex scenario would then surely also suit the needs of the more simpler requirements found in standard Master/Slave transactions.

 

Sincerely,

Gomer

0 Kudos

2,352 Views
Toojer
Contributor I

@gomer  Hey this sounds very similar to what I'm trying to implement.  It would be great if you could give me a simple sample of this code you wrote.

Much appreciated

0 Kudos

2,353 Views
Toojer
Contributor I

@gomer  Can you give a simple example of how you implemented this?  I'm trying to work through almost the exact same scenario.  Some directions would be greatly appreciated.

0 Kudos

3,016 Views
gilles_b
Contributor I

Hi Gomer,

 

Have you been able to make the slave interface work according to your needs ?

 

I think the way you want the slave to behave i not "unorthodox" but is a common way.

I also need to implement the MCF5225x as a slave, receiving some data written by a master (request message from master) and responding to this message with some data that is read by the master (reply message).

I'm currently using the I2C driver in interrupt mode as a master (works fine) and need to also act as a slave when a master is addressing me.

 

So if it works for you, I would appreciate I you could help me by posting some tips on how to do it (read/write/ioctl sequences to do on the I2C file descriptor), and the changes made to the I2C driver (if any).

 

gilles-b

0 Kudos

3,016 Views
PetrM
Senior Contributor I

Yes, the slave example will contain as much communication scenarios as possible.

 

Regarding blocking modes - as I wrote, polled mode can be used when there's nothing else to do, or you have higher priority tasks. Otherwise you should use interrupt mode where you can do the blocking at the task level.

 

Regarding the late STOP command issue, thanks for pointing that out, I think it can be solved by rearranging the code - the slave dummy read (bus release) should be just before waiting for bus idle.

 

PetrM

 

0 Kudos