Link here

Inter-IC (IIC, I²C, I2C) Serial Bus
A tutorial for using the 9S12/HCS12/MC9S12 I2C bus protocol for instrument control applications


The Freescale 9S12 (HCS12) microcontroller implements an Inter-IC (IIC, I2C or I2C) serial port. Many peripheral chips can communicate via this synchronous clocked 2-wire serial interface. For a general description of I2C, see the Wikipedia I2C article. This I2C tutorial provides a more detailed view, with instruction for using the PDQ Board's I2C software drivers which implement the I2C protocol.

A separate page provide the I2C bus electrical specifications.

 

Overview of the I2C Bus

The I2C bus on the PDQ Single Board Computer (SBC) comprises an SDA bi-directional data line and an SCL bi-directional clock line; a shared ground connection among the devices is also required. This is an open-drain, multi-drop bus, meaning that multiple devices can be hooked to the bus and communicate with one another. Each device on a given I2C bus must have a unique even-number address between 0 and 254. The bus master transmits this address in the first byte of a message to identify the recipient of the message.

The I2C bus is best suited for communications over a short distance among a number of devices. Connecting to the I2C bus is simple: all the SDA data lines of the HCS12 and peripherals are tied together, and all the SCL clock lines are tied together, as shown on the following diagram1). All devices must share a common ground.

 

I2C Hardware

The I2C bus comprises two hardware pins on the HCS12 processor, PORTJ pins 6 and 7.

All devices on an I2C bus must use open drain (or open collector) pins. There is a pull-up resistor on each line. The I2C hardware automatically detects bus contention if the bus master expects a high signal and detects a low. This situation causes a bus arbitration lost error as discussed below. The following diagram (modeled on the Freescale Application Note) illustrates the connections to the two wires of the bus.

Diagram of the two wire open-drain multi-drop I2C bus, from the Freescale Application Note AN2318/D
Fig. 1  Diagram of the two wire open-drain I2C bus showing the multi-drop data (SDA) and clock (SCL) lines with pull-up resistors.

Port J pin 6 is used for the SDA line and PJ7 for the SCL line. The 4.7K pull-up resistors in the diagram are already incorporated in the processor, as are 100 ohm series resistors (not shown in the diagram) to protect the processor from out-of-spec voltages that may inadvertently be applied to the pins.

 

I2C protocol

You should become familiar with the I2C communication protocol. However, you won't need to know it in detail as we've already coded the needed interrupt service routine (also called an interrupt handler) for you. So you can just use the pre-coded driver.

An I2C specification defines the hardware and software components of the I2C protocol. According to the I2C protocol, a standard I2C message is composed of four parts:

  • START signal;
  • slave address and R/W bit transmission;
  • data transfer; and
  • STOP signal.

Data is sent one byte at a time, but each byte is implemented as 8 clocked data bits followed by a 9th bit for the acknowledgment. Before the message starts, the bus is idle: both the SDA and SCL lines are high, and no master is engaging the bus. A START signal is defined as a high-to-low transition of SDA while SCL is high. This brings all slaves out of their idle states.

The first byte transmitted after the START signal is the slave address and direction bit transmitted by the master. The slave address is an even number between 2 and 254. The least significant bit of this first byte is the R/W bit, with 1 meaning that the master is reading from the slave, and 0 meaning that the master is writing to the slave. Only the slave that has the specified address will respond by sending back an acknowledge bit. This is accomplished by pulling the SDA line low during the ninth clock cycle. Note that no two slaves on the I2C bus may have the same address, and a master must not transmit an address that equals its own address.

After the slave is addressed, the data transfer proceeds on a byte-by-byte basis in the direction specified by the R/W bit as described above. The data line may change state only while SCL is low, and must be stable while SCL is high. This allows a slave to latch the stable data on either the rising or falling edge of the SCL line. There is one clock pulse for each data bit, and the most-significant data bit is transmitted first. During the ninth clock bit, the receiving device pulls SDA low as an acknowledge signal. If the master is the receiver, it signals the END OF DATA message to the slave by failing to send an active-low acknowledge when the last byte of data is received.

The master can generate an optional STOP signal to free the bus. A STOP signal is defined as a low-to-high transition of SDA while SCL is high.

The I2C hardware automatically synchronizes the SCL signals to the slowest active device on the bus, and automatically detects bus conflicts such as more that one master being active at a time.

 

I2C software drivers

A suite of software driver routines implement the I2C protocol for you, making it easy to use the I2C bus. An interrupt-based response to I2C messages is configured, and messages are sent from and received to designated frame buffers. Any errors in the message processing are responded to in a fail-safe manner, and error message codes are made available to the C-language application program.

The pre-coded I2C software driver implements the I2C protocol interrupt service routine flow diagram recommended in Freescale’s Application Note AN2318/D, Using the I2C Bus with HCS12 Microcontrollers, and illustrated by the following software flowchart:

Software block diagram showing the ISR (interrupt service routine) flowchart for the I2C bus data transmission I2C protocol.
Fig. 2  Software block diagram showing the ISR (interrupt service routine) flowchart for the I2C bus data transmission I2C protocol.

The following table summarizes the driver's functions and macros.

Pre-coded Control Routines for the Inter-IC (I2C) Bus
I2C Functions Declared in the \Include\Mosaic\SERIAL.h File
(click on function names to see their definitions)
IIC_104KHZ_23PERCENT IIC_RCV_BUF_OFFSET IIC_XMIT_BUF_SIZE
IIC_10KHZ_13PERCENT IIC_RCV_BUF_OVERFLOW IIC_XMIT_BUFFER
IIC_96KHZ_25PERCENT IIC_RCV_BUF_PTR IICFrequencies()
IIC_ARB_LOST_ERROR IIC_RCV_BUF_SIZE IICInit()
IIC_ERROR IIC_RCV_BUFFER IICReceive()
IIC_MASTER_RECEIVER IIC_TIMEOUT_ERROR IICReceiveFrame()
IIC_NAK_ERROR IIC_XMIT_BUF_EMPTY IICSend()
IIC_NUM_BYTES_TO_RCV IIC_XMIT_BUF_OFFSET IICSendFrame()
IIC_NUM_BYTES_TO_XMIT IIC_XMIT_BUF_OVERFLOW IICSendNextFrame()
IIC_RCV_BUF_FULL IIC_XMIT_BUF_PTR

After initializing the bus using the IICInit() function, you can use the IICReceive() and IICReceiveFrame() functions to receive data, and the IICSend(), IICSendFrame(), and IICSendNextFrame() functions to transmit data. IICFrequencies() and its interactive Forth version IIC.FREQUENCIES print a table of values to assist in initializing the I2C port. The remaining entries in the above table are macros comprising default baud rates, error codes, and buffer pointers. The error codes are bitmask values that can be decoded to describe any errors that occurred during a transmission or reception. The send and receive functions return an error value of 0 if no error occurred.

 

Selecting an I2C clock frequency

The first decision is the selection of the I2C clock frequency and the associated data hold time parameter. As discussed above, 100 KHz is a good clock frequency target for a moderately loaded I2C bus. The data hold time is the percentage of the clock period between the falling edge of SCL to the change in the SDA line. Recall that all changes in the state of the SDA line must occur while the SCL clock line is low. The 100 kHz I2C bus specification requires that the hold time be less than 35% of the clock period, and 25% is a good target.

The IICInit() function accepts a clock specifier parameter. The simplest choice is to use one of the pre-defined clock specifier constants IIC_96KHZ_25PERCENT, IIC_104KHZ_23PERCENT, or IIC_10KHZ_13PERCENT to specify the clock. Each name states the clock frequency and the data hold time. If these default values are not convenient, you may specify clock frequencies spanning the range from 2 kHz to 1 MHz by passing the appropriate parameter to the IICInit() function.

The IICFrequencies() function and its interactive counterpart IIC.FREQUENCIES print out a useful table of the available I2C clock frequencies along with the corresponding IICInit() clock specifier parameter. This function prints a formatted table of all 192 possible contents of the IBFD (I2C Bus Frequency Divider) register in both hex and decimal, followed by the corresponding decimal I2C bus frequency in kHz (rounded to the nearest kHz) and the corresponding decimal data hold time as a percentage of clock period. To use this function, enter the Forth version of the function name interactively at the terminal, like this:

IIC.FREQUENCIES

It will print a table of values. Examine the table to select the IBFD register contents that yield the desired I2C bus clock frequency and the data hold time as a percentage of the clock period, and then pass the selected IBFD constant to the IICInit() function. If you prefer to work strictly in C, you can invoke the IICFrequencies() function from a compiled C program to create the printout. When executed, the first few lines of the resulting printout look like this:

Pass the selected IBFD constant to IICInit (or to IIC.INIT in Forth).
0xIBFD  IBFD  ''SCL''.KHZ  ''SDA''.HOLD%  (decimal)
0x0            0          1000       35
0x1            1           909       32
0x2            2           833       33
. . .
 

Initializing the I2C bus

The IICInit() function configures the interface. Its function prototype is:

void IICInit ( int ibfd_contents, int my_slave_address)

This routine initializes and configures the I2C software for interrupt-based operation compatible with the I2C driver functions built into the operating system. The input parameters are the contents to be written to the IBFD baud rate register, and the slave I2C address. The ibfd_contents parameter is typically one of the pre-defined constants IIC_96KHZ_25PERCENT, IIC_104KHZ_23PERCENT, or IIC_10KHZ_13PERCENT, or a parameter selected from the table printed by IIC.FREQUENCIES as described above.

The my_slave_address input parameter should be an even number between 2 and 254. In other words, the address should occupy a single byte with the least significant bit equal to zero; this satisfies the requirements of the I2C protocol as implemented on the HCS12 processor. To prevent contention, the assigned slave address must be unique on the connected I2C network. `IICInit() configures the I2C control register for interrupt-enabled I2C transfers starting in the slave reception mode with standard acknowledgement. PORTJ pins PJ6 and PJ7 (SDA and SCL, respectively) are set high if they are outputs to ensure that they idle in the inactive high state. This routine ATTACHes the I2C interrupt service routine which runs during data transfers; note that the Attach() routine disables interrupts for many milliseconds the first time it runs while EEPROM is being programmed; be careful to ensure that this does not adversely affect your application. After the EEPROM is initialized by this routine, subsequent invocations of this function will not need to re-write the EEPROM, so interrupts will not be disabled.

Enable interrupts before using I2C
The IICInit() routine does NOT globally enable interrupts; the application must invoke ENABLE_INTERRUPTS or some equivalent interrupt-enabling function such as StartTimeslicer() before attempting an I2C data transfer.

IICInit() erases all of the I2C control variables and buffers, including: IIC_XMIT_BUF_EMPTY, IIC_RCV_BUF_FULL, IIC_ERROR, IIC_XMIT_BUF_OFFSET, IIC_RCV_BUF_OFFSET, IIC_MASTER_RECEIVER, IIC_NUM_BYTES_TO_XMIT, IIC_NUM_BYTES_TO_RCV IIC_XMIT_BUFFER, and IIC_RCV_BUFFER. The IIC_XMIT_BUF_SIZE and IIC_RCV_BUF_SIZE variables are set to their default values of decimal 32 bytes each, and the IIC_XMIT_BUF_PTR and IIC_RCV_BUF_PTR variables are initialized to point to the default 32-byte buffers in reserved system RAM.

If the default 32 byte transmit and receive buffers are not large enough for the message size in your application, you can create larger transmit and receive buffers. First allocate the larger transmit and receive buffers in common RAM, then call the IICInit() routine, then store the buffer sizes into IIC_XMIT_BUF_SIZE and IIC_RCV_BUF_SIZE, and write the respective 16-bit buffer base addresses into IIC_XMIT_BUF_PTR and IIC_RCV_BUF_PTR.

Note that the transmit and receive buffers are linear frame buffers, as opposed to circular ring buffers. The received data must be extracted from the IIC_RCV_BUFFER before the next receive operation begins, and the data in the IIC_XMIT_BUFFER must be transmitted before writing a new frame into the transmit buffer.

 

I2C master receiving a frame of data

After initializing the bus using the IICInit() function, the IIC master HCS12 device can use the IICReceive() and IICReceiveFrame() functions to receive data from slaves on the IIC bus. IICReceive() is the simplest receive function. It starts the interrupt-based reception process and exits without waiting for the reception to end. The calling program should monitor the IIC_RCV_BUF_FULL system variable which is set by the IIC interrupt service routine and, when it goes true (nonzero), the application should get the data out of the IIC_RCV_BUFFER. The IICReceiveFrame() function automates this process, and provides a simpler way to receive data without monitoring the IIC_RCV_BUF_FULL variable.

The IICReceive() function prototype is:

int IICReceive ( uint timeout_cnt, int numbytes, int slave_iic_address)

This routine returns an error value that is 0 if the operation was completed successfully, and returns a bitmasked nonzero error value if an error occurred. IICReceive() receives numbytes of data from a remote slave on the I2C bus having the specified slave_iic_address, and places the received data into the IIC_RCV_BUFFER. `IICReceive() sets up the control variables and registers, waits and pauses until the IIC bus is not busy, and, if the timeslicer is running, enforces a maximum waiting time of timeout_cnt timeslice counts. Because the default timeslice period is 1.024 milliseconds, the timeout_count is approximately equal to the number of milliseconds the routine will wait before a timing out. If a timeout occurs, the IIC_TIMEOUT_ERROR error flag is returned.

Note that if the timeslicer is not running (that is, if StartTimeslicer() was not executed), then no timeout is enforced, and if the slave is not responding for some reason, this routine will Pause() indefinitely.

When the I2C bus becomes available, this routine initiates the reception by sending the slave_iic_address with its least significant bit (the R/W bit) set to indicate that we (the master) are reading.

Note that there is a slight chance that the IIC bus will be busy when we start receiving (if someone else grabbed the bus just as we were about to grab it); this condition will be detected by the interrupt service routine and will result in an error code containing IIC_ARB_LOST_ERROR being returned by this function. If the returned error flag is nonzero, the calling routine must test for and respond to the following error conditions: IIC_TIMEOUT_ERROR, IIC_ARB_LOST_ERROR, IIC_NAK_ERROR, and IIC_RCV_BUF_OVERFLOW. The I2C Error Handling subsection below details how to perform error analysis.

IICReceive() does not wait for the IIC data reception to end; the IIC interrupt service routine can keep working on the reception after IICReceive() exits. The calling program can determine when all of the requested bytes have arrived by monitoring the IIC_RCV_BUF_FULL variable. The application must use, examine, or retrieve the data out of the IIC_RCV_BUFFER before the next reception occurs (otherwise the data will be overwritten).

The IICReceiveFrame() function provides a simpler way to receive data without monitoring the IIC_RCV_BUF_FULL variable. Its function prototype is:

int IICReceiveFrame(uint timeout_cnt,xaddr buffer,int numbytes,int slave_iic_address)

This routine first calls the IICReceive() function described above. Then it waits and calls Pause() until the IIC_RCV_BUF_FULL flag is set, and, if the timeslicer is running, enforces the timeout_cnt timeslice counts maximum waiting period while waiting for the receive buffer to fill. If there is no timeout, IICReceiveFrame() moves the data that has been received in the IIC_RCV_BUFFER to the location specified by the xaddr buffer input parameter. This routine does not exit until the received bytes have been moved out of the IIC_RCV_BUFFER. Consequently, the next requested reception can’t corrupt prior data. The specified timeslice count is used both while waiting for the I2C bus to be available, and again for the receive buffer to fill, so the maximum timeout is twice the specified timeout period.

Note that if the timeslicer is not running (that is, if StartTimeslicer() was not executed), then no timeout is enforced, and if the slave is not responding for some reason, this routine will Pause() indefinitely. There is a slight chance that the I2C bus will be busy when we start receiving (if someone else grabbed the bus just as we were about to grab it); this condition will be detected by the interrupt service routine and will result in an error code containing IIC_ARB_LOST_ERROR being returned. If the returned error flag is nonzero, the calling routine must test for and respond to the following error conditions: IIC_TIMEOUT_ERROR, IIC_ARB_LOST_ERROR, IIC_NAK_ERROR, and IIC_RCV_BUF_OVERFLOW. The I2C Error Handling subsection below details how to perform error analysis.

 

I2C master sending a frame of data

After initializing the bus using the IICInit() function, the I2C master HCS12 device can use the IICSend(), IICSendFrame(), and IICSendNextFrame() functions to transmit data to slaves on the I2C bus. IICSend() is the simplest send function. It starts the interrupt-based transmission process to send the data that has been pre-loaded into the IIC_XMIT_BUFFER, and exits without waiting for the transmission to end. IICSendFrame() copies data from a specified buffer in memory into the dedicated IIC_XMIT_BUFFER, and then calls IICSend(). IICSendNextFrame() first waits and calls Pause() until the IIC_XMIT_BUF_EMPTY flag is true to ensure that any prior transfer is complete, and then calls IICSendFrame(). Thus IICSendNextFrame() is the most bullet-proof transmission routine, as it guarantees that data in the IIC_XMIT_BUFFER is not overwritten by the send process.

Each of these three sending functions returns an error code equal to zero if no error occurred. A nonzero return value means that an error occurred. The I2C Error Handling subsection below details how to perform error analysis.

IICSend() is the fundamental I2C data transmission function. Its prototype is:

int IICSend ( uint timeout_cnt, int numbytes, int slave_iic_address)

This routine sends data to a remote slave having the specified slave_iic_address. IICSend() transmits numbytes of data that have been pre-loaded into the IIC_XMIT_BUFFER, sets up the control variables and registers, waits and calls Pause() until the I2C bus is not busy, and, if the timeslicer is running, enforces a maximum waiting time of timeout_cnt timeslice counts, where the default timeslice period is 1.024 milliseconds (see this glossary entry for MsecTimeslicePeriod() for additional information). If a timeout occurs, the IIC_TIMEOUT_ERROR error flag is returned. If the timeslicer is not running (that is, if StartTimeslicer() was not executed), then no timeout is enforced, and if the slave is not responding for some reason, this routine will Pause() indefinitely. If there is no timeout error, IICSend() initiates the transmission by sending the slave_iic_address with its least significant bit (the R/W bit) cleared to indicate that we (the master) are writing. There is a slight chance that the I2C bus will be busy when we start transmitting (if someone else grabbed the bus just as we were about to grab it); this condition will be detected by the interrupt service routine and will set the IIC_ARB_LOST_ERROR variable. Also note that errors posted during the frame transmission by the background interrupt service routine may not be included in the error flag returned by this routine, as transmission may continue after this routine exits. If the returned error flag is nonzero, the calling routine must test for and respond to the following error conditions: IIC_TIMEOUT_ERROR, IIC_ARB_LOST_ERROR, IIC_NAK_ERROR, and IIC_XMIT_BUF_OVERFLOW.

Even though IICSend() waits for the hardware I2C bus to be not-busy, this routine can still step on the control variables of a previous write by this processor which has not yet completed. To avoid this problem, use IICSendNextFrame() to wait until the prior frame transmission initiated by this (master) processor has completed before sending another one. Its function prototype is:

int IICSendFrame (uint timeout_cnt,xaddr buffer,int numbytes,int slave_iic_address)

IICSendFrame() copies numbytes of data from the specified xaddr buffer into the dedicated IIC_XMIT_BUFFER and calls IICSend() to send the data to the specified remote slave on the I2C bus.

For a more bullet-proof transmission function that returns only after the transmission has completed, use IICSendNextFrame(); its prototype is:

int IICSendNextFrame(uint timeout_cnt,xaddr buffer,int numbytes,int slave_iic_address)

IICSendNextFrame() first waits and calls Pause() until the IIC_XMIT_BUF_EMPTY flag is true to ensure that any prior transfer is complete. If no timeout occurs, it then calls IICSendFrame() which copies numbytes bytes of data from the specified buffer at xaddr into the IIC_XMIT_BUFFER and sends the data to the specified remote slave having the specified slave_iic_address. The advantage of this routine is that it sends a new frame only after the prior transmission has ended, thereby avoiding contention between sequential transmissions and ensuring data integrity. As with the other send routines, errors posted during the frame transmission by the background interrupt service routine may not be included in the error flag returned by this routine, as transmission may continue after this routine exits. If the returned error flag is nonzero, the calling routine must test for and respond to the following error conditions: IIC_TIMEOUT_ERROR, IIC_ARB_LOST_ERROR, IIC_NAK_ERROR, and IIC_XMIT_BUF_OVERFLOW.

 

I2C slave data transmission and reception

The above high level send and receive routines are for masters only. This section briefly describes how slaves should manage I2C data transmission and reception.

A slave receiver simply gets the bytes that are sent by the master placed into its dedicated receive buffer named IIC_RCV_BUFFER. The foreground task of a slave receiver should monitor the IIC_RCV_BUF_OFFSET system variable until it equals the expected number of incoming bytes. Then the slave should move the bytes out of the IIC_RCV_BUFFER starting at buffer offset 0. At this point, the received data is in a safe place and will not be overwritten by another incoming message from the master.

A slave transmitter just sends the bytes that are in its dedicated IIC_XMIT_BUFFER when the I2C bus master requests the data. The foreground task of a slave transmitter must put the required number of bytes into the IIC_XMIT_BUFFER (starting at buffer offset 0) before they are demanded by the master, and let the master get them.

Note that there is nothing to prevent a single device from being an I2C bus master at some times, and an I2C bus slave at other times.

 

I2C clock stretching

Although the master device usually determines the clock speed, the slave also has a say in the matter. The I2C clock, SCL, can be controlled by both the master and the slave, so they aren't constrained to a predefined baud rate. Uunlike RS232/RS485, the clock speed can be changed by the slave, and the slave can even insert delays in communication.

Why is this needed? When the master is reading from the slave, it's the slave that places the data on the SDA line, but the master that initiates the clock pulses. But what if the slave isn't ready to send the next bit? With simple slave devices this usually isn't a problem, but when the slave device is itself a microprocessor with its own agenda, delays may be inevitable. To avoid a condition in which the master sends SCL clock pulses that the slave cannot respond to, the I2C protocol allows clock stretching — the slave may hold the SCL line low as long as necessary.

When it is ready, the slave releases the clock line, allowing it to be pulled high by the pull-up resistor. From the masters point of view, after finishing its assertion of a low clock pulse, it reads the SCL line to see if the clock has gone high. If not, the slave must be holding it low. The master then continues to monitor the SCL line and patiently waits until it is released by the slave before continuing. Not only must the master wait until it observes the clock line going high, but it must wait an additional minimum time (4 μs for standard 100 kbit/s I2C) before it continues.

Clock stretching is handled by the physical layer – the HCS12 hardware driver for the I2C port handles this automatically.

Clock stretching
While transmitting or receiving a byte or frame of data, the slave device may introduce a delay of any duration by holding the clock line low. Whether transmitting or receiving, it may stretch the clock in between individual bits of a byte, or in between bytes. The master's hardware driver automatically accommodates this behavior, so that no bits are lost. There is no indication passed up to the software drivers that clock stretching has occurred — and clock stretching may be of any duration.
 

I2C clock hold

A slave device may require time to digest a frame of data before it is ready to accept another. A data frame may contain a command that requires milliseconds, seconds or even minutes for the slave to execute.

There are two ways a slave can assure it has enough time to implement its current command before accepting another.

  1. Clock stretching – After receiving a frame of data containing a command the slave can stretch the clock by holding the SCL line low before the slave sends a positive acknowledgment. In this case the master's driver routine, for example IICSendNextFrame(), is forced to wait before returning. This wait can not produce a timeout error, and no indication that it occurred is passed back to the calling routine. See Clock stretching above.
  2. Clock hold – After receiving a frame of data containing a command the slave can send a positive acknowledgment, allowing the master's driver routine, for example IICSendNextFrame(), to finish its operation and return promptly. Then the slave may pull SCL low, causing bus unavailability. Subsequently, the master's software driver, for example IICSendNextFrame(), will be forced to wait until SCL is released before initiating another data exchange. If IICSendNextFrame() is forced to wait too long it will return with a timeout error.

Which of these two techniques is used depends on the behavior of the particular slave. You'll need to examine its datasheet to see what it does.

In the second case, the case of clock hold, if the slave has set the SCL line low, it is still safe for the master to attempt another transmission using the IICSendFrame() and IICSendNextFrame() routines. These transmitting driver routines will wait until the slave releases the SCL line before resuming transmission. They provide for a timeout in the case that the slave doesn't release SCL in a reasonable amount of time.

When calling IICSendNextFrame() you should pass it a parameter, timeout_cnt, which specifies the maximum time to wait measured in ticks of the timeslice clock. By default, each tick of the timeslice clock is 1.024 milliseconds. Before calling IICSendNextFrame() you should call StartTimeslicer() to start the timeslice clock.

If the transmit buffer is empty and the I2C bus is available then IICSendNextFrame() will send the next frame of data promptly. If the I2C bus is not available (because its SCL line is held low by the slave, or for any other reason), IICSendNextFrame() will wait for the I2C bus to become available, or in the case that the timeslice clock is not running it will return immediately with an appropriate error code. If the timeslice clock is running, IICSendNextFrame() will wait, executing Pause() so that your other tasks have time to run.

If the I2C bus does not become available, IICSendNextFrame() will wait a maximum time of (timeout_cnt * 1.024 milliseconds) before returning with an error flag. After calling IICSendNextFrame() you must then test for, and respond to, the various possible error conditions, as described in the glossary description of IICSendNextFrame() and in the section on I2C Error Handling. One error condition you are looking for is IIC_TIMEOUT_ERROR which will occur if the slave has been holding SCL low longer than the maximum timeout that you specified. In that case, you can conclude that the slave has failed or that the bus is permanently unavailable, or try again.

So IICSendNextFrame() is safe with respect to the slave's holding SCL low. It just waits until the slave is ready, then resumes communication. Because IICSendNextFrame() executes Pause() while waiting there is little penalty in waiting. Pause() allows your other tasks to continue running; only the task calling IICSendNextFrame() needs to wait for it to return.

Clock hold
A slave device may acknowledge successful reception of a frame of data allowing the master's sending routine, for example IICSendNextFrame(), to return promptly, but then hold the SCL low delaying subsequent data transmission. On the master side, you may still safely invoke IICSendNextFrame() to attempt to initiate a data transfer. IICSendNextFrame() will wait until the slave releases SCL before transmitting. If it is forced to wait too long, it will return with an IIC_TIMEOUT_ERROR.
 

I2C error handling

Each I2C send and receive function returns an integer error value equal to 0 if the operation was completed successfully, and returns a bitmasked nonzero error value if an error occurred. For convenience, the returned error value is also stored in the 16-bit variable named IIC_ERROR. This system variable contains the 16-bit integer error result of the most recent I2C bus data transfer. IIC_ERROR is cleared at the start of each I2C send or receive operation. If the contents of this variable equal zero after an I2C data transfer, there was no error. If the contents are non-zero, then an error occurred.

To ensure reliable messaging, the return error value of each I2C send and receive function must be examined. If the value is nonzero, an error has occurred and corrective action (such as re-sending the data) should be taken.

Sometimes more than one error occurs during an I2C bus transaction. The error value returned by the I2C functions and stored in IIC_ERROR equals the logical OR of the component error bitmasks. To decode the errors, the application program can sequentially perform a logical AND of the original error value with the following bitmask error types:

IIC_TIMEOUT_ERROR        IIC_ARB_LOST_ERROR      IIC_NAK_ERROR
IIC_XMIT_BUF_OVERFLOW    IIC_RCV_BUF_OVERFLOW

Any logical AND operation whose result equals the tested-for error constant indicates that the named error was encountered during the data exchange.

The error signals have the following meaning:

  • IIC_TIMEOUT_ERROR — The operation exceeded the allotted time as specified by the timeout_cnt parameter passed to the I2C function. A timeout may have resulted from bus contention, or a slave's holding SCL low to indicate its unavailability, all preventing the master from initiating a transmission. Once a send routine starts transmitting, slave initiated clock stretching may be of any duration without causing a timeout.
  • IIC_ARB_LOST_ERROR — More than one master was attempting to control the I2C bus at once. This can happen when two masters try to start a data transaction at the exact same time; this is typical of multi-master bus protocols. The application program must test for this error and retry the data transaction if the error occurs.
  • IIC_NAK_ERROR — An expected acknowledge bit was not received during the data transfer.
  • IIC_XMIT_BUF_OVERFLOW — The specified number of bytes to be transmitted exceeds the size of the IIC_XMIT_BUFFER. The IIC_RCV_BUF_OVERFLOW error occurs when the number of received bytes exceeds the size of the IIC_RCV_BUFFER. If the default 32 byte transmit and receive buffers are not large enough for the message size in your application, you can create larger transmit and receive buffers. First allocate the larger transmit and receive buffers in common RAM, then call the IICInit() routine, then store the buffer sizes into IIC_XMIT_BUF_SIZE and IIC_RCV_BUF_SIZE, and write the respective 16-bit buffer base addresses into IIC_XMIT_BUF_PTR and IIC_RCV_BUF_PTR.
 

I2C data exchange summary

The driver functions handle the low-level details of I2C message management. To use the I2C bus, follow these steps:

  1. Initialize the bus by passing a clock rate specifier and a my_slave_address parameter to IICInit(). A good default clock rate specifier is IIC_104KHZ_23PERCENT. Each slave address on the I2C bus must be unique on the bus, and should be an even number between 2 and 254.
  2. If the HCS12 is an I2C bus master and wants to receive data from a remote slave, the most powerful function to call is IICReceiveFrame(). Your program should allocate a buffer in common or paged RAM that is at least as large as the incoming message size. Pass the IICReceiveFrame() function a timeout_count parameter, the 32-bit xaddress of the RAM buffer, the number of bytes to receive, and the address of the I2C slave that will respond to the request. Once the bytes have been safely stored into the buffer, the IICReceiveFrame() routine will exit and your application program can access the received data in the buffer.
  3. If the HCS12 is an I2C bus master and wants to send data to a remote slave, the most powerful function to call is IICSendNextFrame(). Your program should allocate a buffer in memory that is at least as large as the outgoing message size. Pass the IICSendNextFrame() function a timeout_count parameter, the 32-bit xaddress of the common RAM buffer, the number of bytes to send, and the address of the I2C slave that will receive the data. The IICSendNextFrame() routine will wait for any prior in-progress data transmissions to finish, copy your data from the buffer to the dedicated IIC_XMIT_BUFFER, and start an interrupt-assisted background transmission of the data.
  4. If the HCS12 is an I2C bus slave receiver, the application program should monitor the IIC_RCV_BUF_OFFSET system variable until it equals the expected number of incoming bytes. Then the slave should move the bytes out of the IIC_RCV_BUFFER starting at buffer offset 0. At this point, the received data is in a safe place and will not be overwritten by another incoming message from the master
  5. If the HCS12 is an I2C bus slave transmitter, the application program should put the required number of bytes into the IIC_XMIT_BUFFER (starting at buffer offset 0) before they are demanded by the master, and let the master get them.
 

Data transmission rates and cable length

Alternate uses of the I2C pins

If the I2C bus is not needed, the associated HCS12 pins (PORTJ pins 6 and 7) can be configured as general purpose digital I/O , but note that the PJ6 (SDA) and PJ7 (SCL) signals are conditioned with 4.7K pull-up and 100 ohm series resistors.

 

To learn more about the I2C protocol

Notes:
Reproduced from the Freescale Application Note AN2318/D, Using the I2C Bus with HCS12 Microcontrollers
This page is about: I2C Tutorial, how to Use I2C, Freescale 9S12 HCS12 MC9S12 I2C Protocol, IIC I2C Protocol, I2C Hardware Description, I2C Software Driver Library, I2C Master Slave Data Transmitter Receiver – A tutorial for using the I2C (IIC, Inter-IC) interface and I2C bus protocol of the Freeescale 9S12 HCS12 MC9S12 for instrument control applications. Multiple peripheral chips can communicate via this synchronous (clocked) 2-wire serial interface using a master slave protocol for transmitters and receivers on an open drain bus (open collector bus). I2C software drivers/library. Freescale MCU I2C interface, I2C bus, I2C spec, I2C specification, I2C protocol, IIC bus, I2C data frame, I2C error handling, I2C electrical specifications, I2C application note, using I2C, I2C interrupt service routine, I2C ISR, I2C open drain bus, IIC open collector bus, I2C interrupt handler
 
 
Navigation