Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (9.59 MB, 687 trang )
Synchronous Serial IO
SEN
SSPCON2[0]
Set to “1” to initiate a START condition, automatically
cleared when completed.
R/W#
SSPSTAT[2]
349
“1” when transmit in progress, “0” otherwise.
If this bit OR’ed with SEN, RSEN, PEN, RCEN, ACKEN is
“0”, the MSSP is idle.
BF
SSPSTAT[0]
In receive mode, “1” when SSPBUF is full, “0” otherwise.
In transmit mode, “1” when transmit is in progress (does
not include ACK receipt), “0” otherwise.
SSPIF
PIR1[3]
Set to “1” after 8-bit transmission plus acknowledgment
receipt is complete.
TRISC3 TRISC[3]
Must be “1” so that RC3/SCK/CSL pin is an input to allow
open-drain drive by MSSP module.
TRISC4 TRISC[4]
Must be “1” so that RC4/SDI/SDA pin is an input to allow
open-drain drive by MSSP module.
Table 11.5 gives the actions that can be performed in the I2C Master Mode configuration. A complete I2C transaction is built by sequencing through a combination
of the actions in Table 11.5. The actions cannot be queued; in other words, transmit
data cannot be written to the SSPBUF register until the start condition has completed.
TABLE 11.5 Available Actions in I2C Master Mode Configuration
Action
Description
Perform Start Condition
Set SEN bit, wait for it to be reset by hardware
completion.
Perform Repeated Start
Condition
Set RSEN bit, wait for it to be reset by hardware
completion.
Perform Stop Condition
Set PEN bit, wait for it to be reset by hardware
completion.
Perform an ACK/NAK
Copy acknowledge value (0 or 1) to ACKDT bit, set
ACKEN and wait it for to be reset by hardware
completion.
Transmit Data
Copy data to SSPBUF; SSPIF bit is set when transmission
complete and acknowledgment received.
Receive Data
Configure the I2C port to receive data by setting the
RCEN bit; the BF bit is set when data is received.
350
Microprocessors
All of the actions of Table 11.5 involve waiting for status bits to be reset, indicating operation completion. The C code functions presented in this chapter that
implement these actions rely on the watchdog timer to escape any infinite wait
loops due to protocol or hardware failure. Furthermore, status information is
tracked via a persistent variable so that when a WDT expiration occurs, the function that caused the problem is reported. This is defensive programming and provides a method for debugging I2C interface problems.
Figure 11.21 shows this strategy used in implementing the start condition via
the C function i2c_start(). The #define statements give the possible values for the
#define I2C_IDLE_ERR
1
#define I2C_START_ERR
2
#define I2C_RSTART_ERR
3
#define I2C_STOP_ERR
4
#define I2C_GET_ERR
5
#define I2C_PUT_ERR
6
#define I2C_MISSACK_ERR
7
#define I2C_ACK_ERR
8
#define I2C_NAK_ERR
9
// error variable for acknowledge
persistent char i2c_errstat;
}
Status codes for tracking I2C bus
actions
Variable for tracking I2C function calls
i2c_idle(){ // wait for idle condition
unsigned char byte1;
unsigned char byte2;
asm("clrwdt");
i2c_errstat = I2C_IDLE_ERR;
do {
// byte1 has R/W bit.
byte1 = SSPSTAT & 0x04;
byte2 = SSPCON2 & 0x1F;
}while (byte1 | byte2);
asm("clrwdt");
i2c_errstat = 0;
}
}
I2C interface is idle if R/W#,
SEN, RSEN, PEN, RCEN, and ACKEN
bits are all clear.
i2c_start(){
i2c_idle();
i2c_errstat = I2C_START_ERR;
Remember this function for error tracking
SEN = 1;
// initiate start
Do START condition. If WDT
while (SEN);// wait until start finished
expires, track error with i2c_errstat.
asm("clrwdt");
Clear variable used for tracking function calls.
i2c_errstat = 0;
}
}
void i2c_print_err(){
pcrlf(); printf("I2C bus error is ");
switch (i2c_errstat) {
case 0: printf("None");break;
case I2C_IDLE_ERR : printf("Idle");break;
case I2C_START_ERR : printf("Start");break;
case I2C_STOP_ERR : printf("Stop");break;
case I2C_GET_ERR
: printf("Get");break;
case I2C_PUT_ERR
: printf("Put");break;
case I2C_MISSACK_ERR : printf("Missing Ack");break;
case I2C_ACK_ERR
: printf("Ack");break;
case I2C_NAK_ERR
: printf("Nak");break;
default: printf("Unknown");
}pcrlf();
}
ON THE CD
}
Utility function
for printing
i2c_errstat value
in case of error.
FIGURE 11.21 i2c_idle(), i2c_start(), i2c_print_err() functions
(see CD-ROM file ./code/common/i2cmsu.c).
Synchronous Serial IO
persistent char i2c_errstat
351
variable used for error tracking. Recall that the per-
sistent modifier protects the variable from being touched by the initialization run-
time C code, so this variable can track actions across processor resets. The
i2c_idle() function waits until the I2C port is idle and then returns. The call to
i2c_idle() by i2c_start() is not strictly necessary in a single master system, but is
included here for completeness purposes; the i2c_idle() calls used in the following
functions can be removed if performance is an issue. Within i2c_start(), the statement i2c_errstat = I2C_START_ERR records the current function being executed for
error tracking purposes. The start condition is initiated by the statement SEN = 1;
the while(SEN){} loop waits for the MSSP hardware to reset this bit indicating start
condition completion. If the watchdog timer expires during this time, the main()
code that detects the timeout can use the utility function i2c_print_err() to print
the i2c_errstat value to help track the source of the timeout.
Figure 11.22 shows the functions i2c_rstart() (repeated start condition),
i2c_stop() (stop condition), and i2c_ack(unsigned char ackbit) (perform acknowledge with value ackbit). These functions use the i2c_errstat variable in the
same manner as the i2c_start() function. The ackbit parameter of i2c_ack() is
written to the ACKDT bit to specify the acknowledge bit value (0 = ACK, 1 =
NAK).
Figure 11.23 shows functions for performing single-byte transfers in I2C master mode. The i2c_put(unsigned char byte) function transmits byte over the I2C
port; transmission is triggered by the statement SSPBUF = byte. The
while(!SSPIF){} loop exits when the transmission and acknowledgment from the
slave is complete. The ACKSTAT bit contains the value of the received acknowledgment. The expected value is typically “0” for normal operation; this function
performs a software reset via asm(“reset”) and sets the error status with i2c_errstat = I2C_MISSACK_ERR if a “1” (NAK) is received. It is expected that the main()
code will detect this software reset condition and use i2c_print_err() to display an
appropriate error message.
The i2c_put_noerr(unsigned char byte) also transmits byte over the I2C port,
but it returns the value of the ACKSTAT bit instead of performing error checking.
In some cases, the NAK condition is returned intentionally by an I2C slave to indicate a not ready condition; this function is provided for use in those situations. The
master must know a priori when it is valid for a slave device to return a NAK condition. The i2cput_byte() function waits for an idle condition before calling
i2c_put().
352
Microprocessors
i2c_rstart(){// repeated start
i2c_idle();
i2c_errstat = I2C_RSTART_ERR;
RSEN = 1;
// initiate start
// wait until start finished
while (RSEN);
asm("clrwdt");
i2c_errstat = 0;
}
i2c_stop() {
i2c_idle();
i2c_errstat = I2C_STOP_ERR;
PEN=1;
// initiate stop, PEN=1
//wait until stop finished
while (PEN);
asm("clrwdt");
i2c_errstat = 0;
}
}
}
Performed Repeated Start Condition
Perform Stop Condition
i2c_ack(unsigned char ackbit){
// send acknowledge
asm("clrwdt");
ACKDT = ackbit;
if (ackbit)
i2c_errstat = I2C_NAK_ERR;
else i2c_errstat = I2C_ACK_ERR;
//initiate acknowlege cycle
ACKEN = 1;
// wait until acknowledge cycle finished
while(ACKEN);
asm("clrwdt");
i2c_errstat = 0;
}
ON THE CD
ACK bit value, “0” is an acknowledge,
“1” is a not-acknowledge
}
Initiatiate Acknowledgement,
wait for completion
FIGURE 11.22 i2c_rstart(), i2c_stop(), i2c_ack() functions (see CDROM file ./code/common/i2cmsu.c).
The i2c_get(unsigned char ackbit) function receives a byte from the I2C port
and sends an acknowledgment with value ackbit. The RCEN (receive enable) bit is
used to initiate the receive condition; the RCEN bit is reset and the BF flag (buffer
full) is set when SSPBUF contains new data. The BF bit is cleared upon reading the
SSPBUF register. The i2cget_byte() function waits for an idle condition before
calling i2c_get().
The SSPADD register within the MSSP subsystem sets the bit rate of the I2C
port as given by Equation 11.2.
BR =
FOSC
(4 * (SSPADD + 1))
(11.2)
Listing 11.1 gives the function used in these examples to initialize the I2C port
to Master mode. The bitrate parameter is written to the SSPADD register to set the
SCL clock frequency, and the RC3/SCK/SCL and RC4/SDI/SDA pins are config-
Synchronous Serial IO
unsigned char i2c_put(unsigned char byte ) {
i2c_errstat = I2C_PUT_ERR;
SSPIF = 0;
//clear interrupt flag
SSPBUF = byte; // write byte
while(!SSPIF); // wait for finish an ack
i2c_errstat = 0;
asm("clrwdt");
if (ACKSTAT) {
//no acknowledge returned, so reset
i2c_errstat = I2C_MISSACK_ERR;
asm("reset");
}
return(0);
}
}
}
353
Inititiate transmit by writing byte to
SSPBUF register.
SSPIF set when transmit is complete.
If returned ACK bit is “1”, set error
variable and do software reset.
unsigned char i2c_put_noerr(unsigned char byte ) {
i2c_errstat = I2C_PUT_ERR;
SSPIF = 0;
//clear interrupt flag
SSPBUF = byte; // write byte
Same as i2c_put() but do not
while(!SSPIF); // wait for finish an ack
do software reset on NAK, instead
i2c_errstat = 0;
return the value of the acknowledgement
asm("clrwdt");
bit.
if (ACKSTAT) return(1);
return(0);
}
}
unsigned char i2c_putbyte(unsigned char byte) {
i2c_idle();
return(i2c_put(byte));
}
}
Check for idle condition
before sending byte.
unsigned char i2c_get(unsigned char ackbit) {
unsigned char byte;
i2c_errstat = I2C_GET_ERR;
RCEN = 1; //initiate read event
while(RCEN); // wait until finished
asm("clrwdt");
while (!BF); //also check buffer full
asm("clrwdt");
byte = SSPBUF; // read data
i2c_errstat = 0;
i2c_ack(ackbit);
return(byte);
}
Configure for reception and wait for
byte to be received.
}
Read byte from SSPBUF and send
acknowledgement
}
unsigned char i2c_getbyte(unsigned char ackbit) {
i2c_idle();
Check for idle condition
return(i2c_get(ackbit));
before initiating receive.
}
}
FIGURE 11.23 Functions for performing single-byte I2C transmit/receive
ON THE CD (see CD-ROM file ./code/common/i2cmsu.c).
ured as inputs. The i2c_init() function completes the list of support functions
used in the examples of this book for performing I2C transfers on the PIC18F242.
354
Microprocessors
LISTING 11.1 C function for initializing I2C Master mode (see CD-ROM file
./code/common/i2cmsu.c).
i2c_init(char bitrate){
// enable I2C Master Mode
SSPM3 = 1; SSPM2 = 0;SSPM1
SSPADD = bitrate;
//
SSPEN = 1;
bitset(TRISC,3);
bitset(TRISC,4);
//
SSPIF = 0;
//
i2c_errstat = 0;
//
}
= 0; SSPM0 = 0;
set bus clk
SDA, SCL pins are inputs
clear SPIF bit
clear error status
Figure 11.24(a) shows how the functions i2c_start(), i2c_put(), and
are used to write 2 bytes of data to an I2C slave device.
i2c_stop()
(a) Write two bytes to slave
bitclr(addr,0);
i2c_put(addr);
S
i2c_put(data1);
0 A
addr
R/W#=0
(write)
data1
(b) Read two bytes from slave
bitset(addr,0);
i2c_put(addr);
The
i2c_start()
data2
A P
Data sent by
PIC18
i2c_stop()
ACK bit value
data1 = i2c_get(0); data2 = i2c_get(1); Send
1 A
data1
addr
R/W#=1
(read)
ACK sent by PIC18
i2c_start()
to slave
FIGURE 11.24
A
ACK sent by slave,
read by i2c_put()
i2c_start()
S
i2c_put(data2);
NAK on
last byte
A
data2
Data returned by
slave
N P
i2c_stop()
Using the support functions to implement I2C transfers.
function call begins the transaction, followed by an
i2c_put(addr) that sends the address of the slave. The bitclr(addr,0) statement be-
fore the i2c_put(addr) ensures that the R/W# bit (LSb of the address) is cleared to
“0”, indicating a write operation (master transfers data to slave). The next two
i2c_put() function calls send two data bytes to the slave. The transaction is ended
by an i2c_stop() function call. Figure 11.24(b) shows how the functions
Synchronous Serial IO
355
i2c_start(), i2c_put(), i2c_get(), and i2c_stop() are used to read two data bytes
from an I2C slave device. The i2c_start() function call begins the data transfer, followed by an i2c_put(addr) that sends the address of the slave. The bitset(addr,0)
statement before the i2c_put(addr) ensures that the R/W# bit (LSb of the address)
is set to “1”, indicating a read operation (slave transfers data to master). The next
two i2c_get() function calls read two data bytes from the slave. The “0” parameter
used in the first i2c_get(0) function call is the acknowledge bit value sent by the
PIC18 to the slave after the byte is read from the slave. This value is “0” (an ACK)
for all bytes read from the slave except for the last byte, in which a “1” (a NAK) is
sent by the master to tell the slave that it should not start another data transfer. The
transaction is ended by an i2c_stop() function call.
Sample Question: Assume an I2C device requires a command byte written to it to tell it
what internal register to return on the next read transaction. Write a sequence of
function calls using the functions discussed in this section to accomplish this action.
Assume the variables dev_addr, cmd, and data are used for the device address,
command byte, and returned data byte, respectively. Assume the device requires an ACK
bit value of “1” to halt the read transaction.
Answer: We need a write transaction followed by a read transaction as shown
in Listing 11.2.
LISTING 11.2
Sample question solution.
i2c_start();
bitclr(dev_addr,0)
i2c_put(dev_addr);
i2c_put(cmd);
i2c_rstart();
bitset(dev_addr,1)
i2c_put(dev_addr);
data = i2c_get(1);
i2c_stop();
//
//
//
//
//
//
//
//
the LSb must be 0 for a write transaction
send the I2C device address
send command byte
start new transaction
the LSb must be 1 for a read transaction
send the I2C device address
get data, send ACK of ‘1’ to halt read
stop the transaction
Observe that the LSb of the device address is forced to be a “0” in the write transaction, and a “1” in the read transaction. Function calls of i2c_start()/i2c_stop()
could be used to replace the i2c_rstart() function call; in a multiple bus master situation, this gives other bus masters a chance to control the bus.
Sample Question: Assuming FOSC = 29.4912 MHz, what SSPADD value is required for
an I2C bus rate of 100 kHz?
Answer: Equation 11.2 can be solved for SSPADD as SSPADD =
[FOSC/(4*BR)] – 1. Thus, SSPADD = [29.4912E6/(4*100e3)] – 1 = 72.7 = ~73.
356
Microprocessors
11.8 THE 24LC515 SERIAL EEPROM
The 24LC515 512K serial EEPROM has an internal organization of 64K x 8 and
uses an I2C port for communication. Figure 11.25 shows a PIC18 to 24LC515 interface using the I2C port. The write protect (WP) pin on the 24LC515 can be used
to disable writes to the device; it can be left open or tied to Vss to enable writes. The
A2 input is an unused input that must be tied high for the device to function correctly. The A1, A0 inputs are used to personalize the device address by connecting
them to either Vdd or ground. This allows up to four 24LC515 devices to exist on
the same I2C bus.
A2 must be high
for device to function
Vdd
PIC
10 kΩ
SCL
SDA
10 kΩ
Vdd
SCL
SDA
Vss
A2
WP
A1
A0
Write protect disabled if low
or left open (internal pulldown)
A1,
} ConnectVss toA0 to either
Vdd or
personalize
address
24LC515 Serial EEPROM
FIGURE 11.25
PIC18 to 24LC515 I2C interface.
Figure 11.26 shows the address byte format for the 24LC515. The upper 4 bits
are fixed at “1010”. The 64K x 8 organization of the 24LC515 means that addresses are 16 bits with a range 0x0000 to 0xFFFF. However, the internal organization of the 64 x 8 memory is split into two 32K memory blocks, each with its own
internal 15-bit address counter. The B (block select) bit of the address byte determines whether the current operation is to the low memory block (0x0000 through
0x7FFF) or high memory block (0x8000 through 0xFFFF). The least significant bit
of the address byte is the R/W# bit as with all I2C address bytes.
Figure 11.27 shows the write operation for the 24LC515. The I2C address byte
is followed by the high and low address bytes of the starting location for the write.
The most significant bit of the high address byte is a don’t care as the block select
bit within the I2C address byte determines which memory block is being written;
these 15 address bits are written to the internal address counter for the 32K block
selected by the block select bit of the address byte. The internal page size of the
24LC515 is 64 bytes, so up to 64 bytes can be written in one write operation. Page
wrapping occurs in the same way as discussed in Figure 11.12 for the 25LC640 serial EEPROM, except the starting page boundary is when the lower 6 bits of the address are all zeros and the ending page boundary has the lower 6 bits as all ones.
Synchronous Serial IO
357
When doing multiple byte writes, the best practice is to write a complete page at
one time and force the starting address to begin on a page address.
Address byte format for 24LC515 serial EEPROM
7
6
5
4
3
2
1
1
0
1
0
B
A1
A0 R/W#
0
B : Memory block select, if “0” then operation is to low memory block (0x0000-0x7FFF),
if “1” then operation is to high memory block (0x8000-0xFFFF)
A1, A0: Used to personalized address, up to four LC515 EEPROMs can be on bus.
R/W#: “1” if read operation, “0” if write operation
Addressing Examples:
A0
B
0
0
0
1
0
0
1
1
0
1
0
1
0
1
1
1
FIGURE 11.26
Write Operation
Memory address
high byte
R/W#
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
Address
0xA0
0xA1
0xA8
0xA9
0xA2
0xA3
0xAA
0xAB
0xA4
0xA5
0xAC
0xAD
0xA6
0xA7
0xAE
0xAF
Operation
Write to low block
Read from low block
Write to high block
Read from high block
Write to low block
Read from low block
Write to high block
Read from high block
Write to low block
Read from low block
Write to high block
Read from high block
Write to low block
Read from low block
Write to high block
Read from high block
Address byte format for the 24LC515.
Memory address Write
low byte
data
A
Wdata
AP
}
S I2C addr 0 A Addr (hi) A Addr (lo) A Wdata
Write
data
~
~
A1
MSb of address high byte is a don’t care
FIGURE 11.27
1 to 64 bytes
Write operation for the 24LC515.
The worst-case write completion time is 5 ms. However, the end-of-write condition can be polled by sending the write command and checking the ACK bit status as shown in Figure 11.28. If the acknowledgment bit returns as “1” (a NAK), a
write is still in progress. Once the acknowledge bit returns as “0”, the next operation can be started. It is more efficient to poll for end-of-write than to place a delay
of 5 ms after each write operation.
Microprocessors
ACK returns as “0”
when write is not in progress
~
~
~
~
ACK returns as “1”
(NAK) if write is in progress
S I2C addr 0 N P S I2C addr 0 N P S I2C addr 0 A P
Send write command to poll for end-of-write
FIGURE 11.28
Polling for end-of-write.
Figure 11.29 shows read operation sequencing for the 24LC515. A sequential
read (Figure 11.29a) returns the memory contents pointed to by the internal address counter of the 24LC515. Each data byte returned by the 24LC515 increments
the internal address counter for the selected block. An acknowledgment bit of “0”
returned by the PIC18 causes the 24LC515 to output another data byte. An acknowledgment bit of “1” returned by the PIC18 causes the 24LC515 to release the
SDA line and to stop sending data. A sequential read can access the contents of one
entire 32K memory block, either high or low, as determined by the block select bit
sent in the I2C address byte at the beginning of the read transaction. When the internal address counter reaches the end of a 32K block (either 0x7FFF or 0xFFFF),
it wraps around to the beginning of the block.
Read
data
A Rdata
A Rdata
ACK=1 (NAK) ends read,
24LC515 halts data output
A
Rdata
R/W# = 1,
Read operation
Any number of bytes
Repeated Start
Condition
A
Rdata
NP
}
S I2C addr 0 A Addr (hi) A Addr (lo) A R I2C addr 1 A Rdata
Use write operation to set internal address
counter. Repeated Start condition begins
new transaction.
Read
data
Read
data
~
~
(b) Random Read
Memory address
high byte
FIGURE 11.29
NP
}
S I2C addr 1 A Rdata
ACK=0 continues read,
next byte output by
24LC515.
~
~
(a) Sequential Read
}
358
Any number of bytes
Read operations for the 24LC515.
Figure 11.29(b) shows how to use the write command to set the internal address counter before beginning a sequential read. Sending a repeated start condition after the EEPROM address bytes halts the write command. Only the internal
Synchronous Serial IO
359
address counter is affected by the write operation; an internal write to memory contents is not started. A repeated start condition holds SCL low while SDA is high for
one-half of an I2C bit time, then brings SCL high for one-half of an I2C bit time
while SDA is high, and then pulls SDA low while SCL is high to signal the start of a
transaction. To send a repeated start condition, the i2c_rstart() function is used
instead of the i2c_start() function.
The i2c_memwrite() function in Figure 11.30 implements the write operation of
Figure 11.27. The 64 bytes pointed to by the buf parameter are written beginning at
memory address addr, with parameter i2caddr containing the I2C address of the
#include
#include
#include
#include
#include
#include
"config.h"
"serial.c"
"serio.c"
"delay.h"
"i2cmsu.c"
#define EEPROM 0xA0
#define BLKSIZE 64
I2C utility functions
I2C EEPROM address with A1=0, A0=0, block select =0
I2C EEEPROM page size
no
high mem block?
yes
//write a block
i2c_memwrite(char i2caddr,
unsigned int addr, char *buf){
Block Select = 1
unsigned char addr_lo, addr_hi;
char k,ack;
i2c_start()
Send write cmd
addr_lo = addr & 0x00FF;
addr_hi = (addr >> 8);
ack=i2c_put_noerr()
if (addr & 0x8000) {
// if MSB set , set block select bit
i2caddr = i2caddr | 0x08;
}
bitclr(i2caddr,0);
//R/W# = 0;
// check if last write complete
do {
i2c_start();
Write in
ack = i2c_put_noerr(i2caddr); progress
i2c_stop();
polling
} while(ack);
i2c_stop()
“1”, write
in progress
}
// now do page write
i2c_start();
i2c_put(i2caddr); // send write command
i2c_put(addr_hi); // send high address byte
i2c_put(addr_lo); // send low address byte
for (k=0;k
i2c_put(buf[k]);
// send data
}
i2c_stop();
}
ACK?
“0”, write
finished
i2c_start()
Send write cmd i2c_put()
Send addr high i2c_put()
Send addr low i2c_put()
Send data i2c_put()
no
64 bytes?
yes
i2c_stop()
Exit
ON THE CD
FIGURE 11.30 C function for a page write to the 24LC515 (see CD-ROM file
./code/common/i2c_memutil.c).