• Announcements

    • bluehash

      Forum Upgrade   03/11/2017

      Hello Everyone, Thanks for being patient while the forums were being fixed and upgraded. Please see details and report issues in this thread. Thanks!
yyrkoon

Implementing an I2C slave device.

20 posts in this topic

I was wondering if anyone knows of a good read concerning implementing an I2C slave device. What I'm looking for, is something that covers kind of a high level discussion of what needs doing. Without a bunch of specification, or physical characteristic discussion. Meaning, I do not care about the electrical / physical characteristics of such a device, and I have a hard time digesting specification type books.

Mostly, what I really need to know is a processor / language agnostic view of how to implement the slave addressing stuff. Such as device addresses, and register addressing.  Code examples would be cool, in any language, but are not strictly speaking, necessary.

Additionally, I'm also interested in implementing a slave device using the 1-wire protocol too.

 

EDIT:

Additional information, which may help someone else help me. As a hobby project, for the purpose of learning, I'm attempting to turn an MSP430G2553 into a slave device that could potentially be used as an I2C slave device that could possibly be an ADC, PWM, GPIO expander, or a combination of all mentioned plus more. However, the reading material does not necessarily have to be specific to the MSP430G2553, or any MSP430 for that matter.

Share this post


Link to post
Share on other sites

Clavier's Short Guide to I²C Slaves on the MSP430x2xx Family

Read section 17.3.4.1 of the User's Guide, and the example code.

Slave mode is somewhat easier than master mode because you do not have to care about getting the transaction sequence correct; you just react to the master's requests.

The slave address is an arbitrary but unique 7-bit number. Just put it into the I2COA ("own address") register; the USCI module will automatically handle transactions to this address.
You do not need to configure a clock source; the clock signal is supplied by the master.

When the master has written a byte, you get an RXIFG interrupt. Your interrupt handler must read that byte from RXBUF. (You can set the TXNACK bit after reading RXBUF, this will tell the master to stop after the following byte.)

When the master wants to read a byte, you get a TXIFG interrupt. Your interrupt handler must write a byte to TXBUF.

If your code is slow, the USCI module will automatically stop the bus via clock stretching until you have reacted.

You can get notifications when start and stop conditions happen (STTIFG and STPIFG), but that is not always necessary.

The I²C protocol itself defines only byte reads and writes. If you have registers, you have to handle the register address yourself. Typically, the first write after a start condition is the register address, and all following writes (and all reads) are from/to the specified register (and often the register address automatically increments).

As a slave, you have no control over what the master does; you must react to any write and read requests at any time. (If you really have nothing to read, just send the last byte again, or some garbage byte.)

bluehash, Fmilburn and yyrkoon like this

Share this post


Link to post
Share on other sites
6 hours ago, Clavier said:

Clavier's Short Guide to I²C Slaves on the MSP430x2xx Family

Read section 17.3.4.1 of the User's Guide, and the example code.

Slave mode is somewhat easier than master mode because you do not have to care about getting the transaction sequence correct; you just react to the master's requests.

The slave address is an arbitrary but unique 7-bit number. Just put it into the I2COA ("own address") register; the USCI module will automatically handle transactions to this address.
You do not need to configure a clock source; the clock signal is supplied by the master.

When the master has written a byte, you get an RXIFG interrupt. Your interrupt handler must read that byte from RXBUF. (You can set the TXNACK bit after reading RXBUF, this will tell the master to stop after the following byte.)

When the master wants to read a byte, you get a TXIFG interrupt. Your interrupt handler must write a byte to TXBUF.

If your code is slow, the USCI module will automatically stop the bus via clock stretching until you have reacted.

You can get notifications when start and stop conditions happen (STTIFG and STPIFG), but that is not always necessary.

The I²C protocol itself defines only byte reads and writes. If you have registers, you have to handle the register address yourself. Typically, the first write after a start condition is the register address, and all following writes (and all reads) are from/to the specified register (and often the register address automatically increments).

As a slave, you have no control over what the master does; you must react to any write and read requests at any time. (If you really have nothing to read, just send the last byte again, or some garbage byte.)

@Clavier , Awesome, thanks. I would have responded sooner, but someone *cough* @bluehash moved my post, and now I'm not getting notifications . . . Anyway, you pretty much answered, all the questions I had. Which mainly was if I use USCI, would I have to handle all the bus com stuff. e.g. device address, and all that. But I do need to figure out what you're talking about, concerning setting the slave address in some register. Because I'm going to need to do that via dip switches. e.g. physically.

I did also find this on github last night -> https://github.com/wendlers/msp430-i2cslave  Apparently written by one of the people from the German branch of TI.

Share this post


Link to post
Share on other sites

I have done both of these tasks for more than one client.

The 1-Wire protocol speed ends up being about 15kHz, which is slow but reliable. It's really cool to see on an scope though.

I developed the I2C slave code using the sample TI code as the starting point.

Here are the research materials that I referred to while writing my I2C code:

  1. http://www.nxp.com/documents/application_note/AN10216.pdf
  2. http://www.nxp.com/documents/user_manual/UM10204.pdf
  3. http://i2c.info/i2c-bus-specification
  4. http://www.ti.com/lit/an/slva704/slva704.pdf

My slave code just follows the logic of the transaction.

yyrkoon, Fmilburn, bluehash and 1 other like this

Share this post


Link to post
Share on other sites

Part of what I was trying to figure out is would I be able to use PWM ADC, as well as I2C plus 1 each GPI, and GPO, all on a G2553, which seems perfectly doable. After that, I was not sure how USCI handles I2C coms. But if it's as simple as setting a constant to the device address value, then again, No problem . ..

Share this post


Link to post
Share on other sites

I've never gotten the interrupt driven I2C Master to work reliably but I have gotten the interrupt driven I2C working like a charm.

I've done this working on the 2955, the 2553, and the 5529.

I've used a state machine to divide up all the functionality between the I2C, the Serial IO, and the GPIO.

It's weird to realize that I have never used the ADC nor the PWM yet.

Share this post


Link to post
Share on other sites
On 3/19/2017 at 3:31 PM, zeke said:

I've never gotten the interrupt driven I2C Master to work reliably but I have gotten the interrupt driven I2C working like a charm.

I've done this working on the 2955, the 2553, and the 5529.

I've used a state machine to divide up all the functionality between the I2C, the Serial IO, and the GPIO.

It's weird to realize that I have never used the ADC nor the PWM yet.

The ADC is pretty easy. There's documentation all over the web, which is how I prefer "things". Reading blog posts that go into detail as well as explaining their code. That way, if I don't get the initial explanation, the code, and code explanation usually does the job pretty good. Usually, blog posts are also short, and to the point. Which is why I normally do not do well with specifications. I just want to know what I need to do, in order to achieve and end goal. Not memorize ever_single_minute_detail.

To do what I need to achieve, I may have to go to the 2955, for the added program ram, and flash. But, I'm 99% sure what I need to do can be done on a 2553.

@zeke, would you mind going into high level detail about this state machine you mentioned ? Perhaps a very simplistic pseudo code example ? One of the things that has me a little "worried", is wondering if the interrupts could potentially preempt one another. Thus, having missed interrupts. However, I'd probably want to use hardware I2C, and for PWM, I'll probably have to use interrupts on the timers, to get a proper frequency, and duty cycle. *Maybe* I could get away with a polled GPI, but the ADC's . . . yeah not sure.

EDIT:

Ideally, I'd prefer to do everything in hardware if at all possible. So, PWM, ADC, and I2C all in hardware using interrupts, and as for the GPI, it doesn't matter.

 

Share this post


Link to post
Share on other sites

So, on the surface, it looks like PWM is very simple. I could potentially get away with a fixed period, and make a variable duty cycle. e.g. *something* communicates with the MSP430 over I2C and gives a value between 1-99. Which would represent duty cycle. Then, it's just a matter of performing simple math with the value in CCR0, and put that result into CCR1.

But there is still the potential for pin / resource conflicts that I'm not fully clear on yet.

 

Share this post


Link to post
Share on other sites

This is my approach to state machines. Your mileage may vary.

  1. Determine all of the sub-systems that you will want to service
    1. Commands,
    2. Controls and Inputs,
    3. User Interface, and
    4. Data
  2. Setup a system tick timer that fires its interrupt on a regular consistent basis.
    1. This system doesn't have to go into LPM4.
    2. If it does then periodically wake up the system and cycle through the software service loops then go back to sleep.
  3. Setup a series of service flags that are set during the interrupt service routine and cleared after being serviced:
    1. Flag(s) for Commands,
    2. Flag(s) for Controls and Inputs,
    3. Flag(s) for User Interface, and
    4. Flag(s) for Data
  4. Setup a variable that acts like the system timer odometer.
    1. Every Odometer == (DesiredInterval%ServiceFlag_n_now) set the ServiceFlag_n
  5. Decide how often you will service the other functional blocks of your code. For example,
    1. Update the 2x20 LCD display every one second, or
    2. Update the Serial Console every 250ms, or
    3. Retrieve the Temperature from a sensor every 15 minutes.
  6. Setup an Interrupt Service Routine to catch any characters coming into the Serial Port Buffer.
    1. Stuff them into the Input Ring Buffer
    2. Set a flag that there's something to service.
  7. In the main loop, scan all of the service flags to see if any are set.
    1. Call the servicing function for each set flag.
    2. Clear the service flag at the end of that process.
  8. Configure the program to repeat continuously until Kingdom Come.


I've left out significant details about setting up all of the peripherals and general variables so don't forget to do that stuff.

This is just the basic gist of my state machines on a bare metal level.

nickds1, yyrkoon, bluehash and 1 other like this

Share this post


Link to post
Share on other sites

@zeke thanks,

That, more or less seems to be what I do myself. In this case however, there won't be a user interface, nor anything related to "feedback" over serial as an I2C slave can't communicate with the master unless the master initiates the communications. There also won't be an LCD, but I do not think that was you point, and I'm not sure If I will be able to use UART for serial debugging or not. However, this very well may be a chance, and a reason  for me to use the logic analyzer I bought a few months ago.

Now, I need to try and figure out how to program the 28pin G2553's. With the 20pin dip package MCU's I've just been using a ZIF socket on the launchpad, and moving programmed parts to a socket on the board which needed the MCU. Now, I'm kind of at a loss. My buddy wulf has put JTAG, and spy-by wire headers on the prototype, but . . . I've no clue where to start.

Share this post


Link to post
Share on other sites
On 3/22/2017 at 9:10 AM, yyrkoon said:

My buddy wulf has put JTAG, and spy-by wire headers on the prototype, but . . . I've no clue where to start.

@yyrkoon,

One place you might explore is under the "code examples" sections for the G2553.  In particular, I'd start with:

msp430g2xx3_uscib0_i2c_04.c ( USCI_B0 I2C Master RX single bytes from MSP430 Slave )

msp430g2xx3_uscib0_i2c_05.c ( USCI_B0 I2C Slave TX single bytes to MSP430 Master )

and

msp430g2xx3_uscib0_i2c_06.c ( USCI_B0 I2C Master TX single bytes to MSP430 Slave )

msp430g2xx3_uscib0_i2c_07.c ( USCI_B0 I2C Slave RX single bytes from MSP430 Master )

As the examples note, you need the external pullup resistors.  These examples will compile under CCS, IAR and GCC and while simplistic, they are a starting point for seeing the I2C comm in the debugger.

I've used similar examples for the F2013 and F5529 to get those ICs to talk to each other, and subsequently to write code for the F5529 to talk to an sx1509 port expander (an adventure in reading and understanding docs...).  FWIW, I would never have gotten very far without using both my logic analyzer and my oscilloscope...

As to the jtag header and spy-by-wire headers (how I build all of my prototypes) - make sure there's the 47k resistor for the reset line (most devices do not need the capacitor placed), and you may want to account for using an external supply instead of the FET.  The debuggers generally are limited in their supply capacity to ~50-60 mA.  If your circuit is driving other devices, that can lead to failure for the debugger to even start.

HTH

Bob

yyrkoon likes this

Share this post


Link to post
Share on other sites

@yyrkoon,

This is a schematic of a typical programming setup.

58dc210297ee2_MicroSBW.PNG.88d7d4a5c3b5baa3725890f2c852ba81.PNG

It doesn't matter what MSP430 that you use because they all can be wired up like this.

To program this unit, I connect another G2 LaunchPad to this circuit using a four wire cable between P1 of this circuit and either J3 or J4 of the G2 LaunchPad board.

Then program the circuit as if you were programming the G2 LaunchPad.

 

 

 

yyrkoon likes this

Share this post


Link to post
Share on other sites

So, we've got the capes( Beaglebone ), and remote boards(I2C slave boards ) made and assembled for testing. For the moment, we're testing over cat5e, that's only about 1 foot in length, but later we're going to test 300m ( 900 feet ) worth of cable. So for those who are not aware of this "technology". We're attempting differential I2C which is supposed to be capable of up to 300 meters.

So, I really do not want to sounds stupid . . . but essentially, you're transmitting over cat5e using CANBUS hardware, and what I think is reverse logic 24vdc carrier. So basically, an amplified signal.

Anyway, it seems to be working on at least 1 foot, which from what I understand is fairly difficult with normal I2C signalling. So far, I've only queried the remote board via i2cdetect from the i2c-tools Linux utility set. Well, we've also dumped the registers( i2c-dump ) and everything seems to be in sync with our test jig.

This specific board does not have an MSP430 on it, and this specific one probably wont. But in the future we'll be doing all kind of crazy stuff over differential I2C. So if you're interested, stay tuned ! Which by the way, wulf, and I will be selling beaglebone capes sometime in the future.

Share this post


Link to post
Share on other sites

If it matters, I have done I2C via 1Wire over that distance and farther.

If you slow the speeds down then you should be fine.

Share this post


Link to post
Share on other sites

By default, the beaglebone buses have to operate at 100khz. It's been my impression that 400Khz is standard, and 800Khz is "fast" speed. Anyway, the beaglebone buses must be 100Khz, but ours doesn't. But from what I understand, with differential I2C, you don't need to worry about all of that. High speed may not work but 400Khz is supposed to work. But, thats why we're testing. To find out what we can get away with. Reliably.

Share this post


Link to post
Share on other sites
1 hour ago, zeke said:

If it matters, I have done I2C via 1Wire over that distance and farther.

If you slow the speeds down then you should be fine.

You're talking about the 1wire protocol or just one wire of I2C as in sda / scl ?

Share this post


Link to post
Share on other sites

I'm talking about both, actually.

The DS28EA00 has two GPIO lines.

Using 1Wire protocol, I turned them on and off in a way that looked like very slow I2C to the pressure transducer that was attached to those two GPIO lines.

Essentially, using the 1Wire protocol, I spoke I2C to a slave device.

Does that make sense?

yyrkoon likes this

Share this post


Link to post
Share on other sites
2 hours ago, zeke said:

I'm talking about both, actually.

The DS28EA00 has two GPIO lines.

Using 1Wire protocol, I turned them on and off in a way that looked like very slow I2C to the pressure transducer that was attached to those two GPIO lines.

Essentially, using the 1Wire protocol, I spoke I2C to a slave device.

Does that make sense?

Yes, and no. I know what you're saying, just not sure how that would work lol . . . But i really need to understand the low levels of both better anyway.

Share this post


Link to post
Share on other sites

It was a bit of a mind bender for me at first but then I just read the I2C spec and it did not specify that the communication *had* to be at 100kHz. 

The way I choose to understand things is that the I2C slave device has a communication state machine inside of it. All I have to do is put in one bit and turn the crank once. Then repeat. Over and over. Then the slave device will just do its job merrily. 

Frida likes this

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now