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.
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:
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.
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.
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.
- 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 exampleIICSendNextFrame()
, 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. - 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 pullSCL
low, causing bus unavailability. Subsequently, the master's software driver, for exampleIICSendNextFrame()
, will be forced to wait untilSCL
is released before initiating another data exchange. IfIICSendNextFrame()
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.
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.
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 holdingSCL
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:
- Initialize the bus by passing a clock rate specifier and a
my_slave_address
parameter to IICInit(). A good default clock rate specifier isIIC_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. - 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, theIICReceiveFrame()
routine will exit and your application program can access the received data in the buffer. - 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. TheIICSendNextFrame()
routine will wait for any prior in-progress data transmissions to finish, copy your data from the buffer to the dedicatedIIC_XMIT_BUFFER
, and start an interrupt-assisted background transmission of the data. - 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 - 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
- Please see I2C Bus Electrical Specifications
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.
- See: Using PORTJ