yyrkoon

Implementing an I2C slave device.

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

yyrkoon, Fmilburn and bluehash 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.

Rickta59, Fmilburn, yyrkoon 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.

yyrkoon, NurseBob and bluehash 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

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