Premium Content

New Signal Chain Resources from Texas Instruments:

Networking Processor Peripherals With I2C

Here's a way to use Philips' simple two-wire serial buss to meet your I/O needs.

Date Posted: February 14, 2008 12:00 AM
Author: Mark Hastings

In this example, the 10 bytes of data are defined with a structure. The application may use these variables just as it would any other local or global variables. If the structure is defined as global during compile time, most compilers will flatten it out so that it doesn’t have to calculate the offset each time an element is referenced. In other words, there will be no code penalty for using such a structure.

Implementation
Now that the interface between the master and slave is designed, it’s time to write some code. Given the availability of I2C in a wide range of capable microprocessors, many vendors also supply I2C-friendly development tools and libraries. You’ll still need to write some of your own code, but these will accelerate your development. For example, Cypress PSoC microcontrollers contain low-level I2C hardware that can be customized using PSoC Designer and application-specific EzI2C user modules.

Other than basic hardware setup commands, like I2C_Start( ) and I2C_Stop( ) that enable and disable the interface, the bulk of the code will be implemented in the Interrupt Service Routine (ISR). The lowlevel I2C hardware understands I2C bus Start and Stop conditions and sets a status flag when the slave address and R/W bit are received. It doesn’t check for an address match, but requires the firmware to perform that task.

The flow chart shows the basic firmware flow (Fig. 6). Note that some hardware details specific to the manufacturers hardware aren’t covered in the flowchart.

For many applications in which each byte is independent of the other, this interface works well. A good example can be seen in the example application (See code listing 1).Each of the three bytes is independent of each other.

This example consists of an 8-pin PSoC CY8C27143-24PXI microcontroller, two LEDs, two current-limiting resistors for the LEDs, a pushbutton, and a potentiometer to simulate a variable voltage. Internally, the following components are instantiated: ADC, PGA, two LED drivers, and the EzI2C’s User Module. The code in the listing is the only firmware the user must write for this application. The I2C interface code is handled in the ISR as discussed. The I2C master can monitor the ADC value, check the switch status, and set the state of the LEDs in this application. This interface can be reused across many projects without having to modify the interface again.

Figure 7 shows the memory representation down to the actual memory locations. The project used 1076 bytes of flash and 19 bytes of RAM. The I2C code comes to about 275 bytes, well under the 512 bytes allotted for this interface.

Some applications require handshaking between the master and slave instead of just anonymous data read and writes. Extending this interface to perform handshaking is a minor addition to the master and slave/application code. There are many ways to add this functionality.

For instance, if an ADC result is more than 8 bits, it would be possible for the host to read the MSB of one ADC conversion and the LSB of the next conversion. If the readings are very stable, you might not get into trouble. But if the result is between two values, for example 0x0200 and 0x01FF, you could accidentally get a reading of 0x02FF.

To avoid this, we can add a command byte or semaphore. Listing 2 shows a modified structure from the previous example. An additional element has been added to the structure “bCMD,” and the ADC result variable was changed from an 8-bit value “bADC” to a 16-bit value “iADC.”

Now instead of the peripheral firmware blindly updating the ADC result, it waits for a command or semaphore from the master. The command could be any nonzero value of bCMD, or bCMD could be a wide range of commands that the slave/ peripheral can perform. To keep it simple, the LEDs and the switches will continue to update constantly. The iADC value, on the other hand, will only update when the bCMD value is set to a non-zero value.

The application now monitors bCMD, and when it is non-zero, it will put the latest ADC result in iADC and then set bCMD to zero. The master will then monitor bCMD and only retrieve iADC when bCMD returns to zero. In this way, the master will never get an ADC result that’s out of sync. The rule for the command/ semaphore is that the master may set it, and the slave can only clear it. This is the implementation of the top layer “Optional Command Protocol” discussed previously. There’s no need to make it any more complicated than that (Listing 3).

The big hurdle in developing such an interface is writing the I2C driver code in first place. The driver in this case was written in M8C assembly language. I’d rather use C, but at the time and with the tools available, it was the best way to guarantee fast and efficient code. This interface works for most I2C slave applications.

Once the driver was written, I found I could create a new custom peripheral in under an hour. This has been extremely useful in quickly implementing runtime debugging. Variables can be monitored with an I2C master while the slave code is running.

microcontrollers
Part Inventory
Go
powered by:
 

 
You must log on before posting a comment.

Are you a new visitor? Register Here
    There are no comments to display. Be the first one!