1. Trang chủ >
  2. Giáo Dục - Đào Tạo >
  3. Cao đẳng - Đại học >

7 The I[sup(2)]C on the PIC18Fxx2

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



Xem Thêm
Tải bản đầy đủ (.pdf) (687 trang)

×