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

5 SPI Examples: A Digital Potentiometer and a Serial EEPROM

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



Vdd



335



MCP41xxx

LCD

Vdd



PA0



PIC

PW0

RB4

SDO

SCK



CS#

SI

SCK



PB0

Vss



Vdd

VL

Vss

PW0 voltage varies

between 0 and Vdd, used

for contrast control to LCD



FIGURE 11.6 PIC to MCP41xxx digital potentiometer interface.



The MCP41xxx digital potentiometer comes in 10 K (MCP41010), 50 K

(MCP41050), and 100 K (MCP41100) configurations and uses an SPI port for setting the 8-bit wiper register for the potentiometer. In the configuration shown in

Figure 11.6, a wiper value of 255 sets the PW0 output voltage to approximately

255/256 * Vdd, while a value of 0 sets the PW0 output voltage to ground. The

wiper register is set to 0x80 on power-up. Higher potentiometer values reduce the

static current that is drawn by the potentiometer when it is active. For example, a

50 K potentiometer with Vdd = 5 V draws 5 V/50 K = 100 μA static current

through the potentiometer resistance, while a 100 K potentiometer reduces this

current by 50% to 50 μA.

Figure 11.7 shows the command protocol for the MCP41xxx. Each transaction

consists of 2 bytes, a command byte and a data byte. The CS# (Chip Select) input

must be brought low to enable the device before any data is sent and brought high

after transmission is finished in order to execute the command. The wiper register

is set by the command byte 0x11 followed by the wiper register value. The shutdown command opens (disconnects) the potentiometer by opening the PA0 terminal and shorting the PW0 and PB terminals. This reduces total static current

draw of the MCP41xxx to less than 1 μA. The data byte for the shutdown command is ignored but it still must be sent for the command to be recognized. If

MCP41xxx shutdown mode were to be used with the LCD application of Figure

11.6, you would want to reverse the PA0 and PB0 connections so that VL of the

LCD is shorted to Vdd during shutdown, blanking the display. This would mean

that a wiper code of 255 sets the PW0 voltage to near ground, while a code of 0 sets

the PW0 voltage to Vdd.



336



Microprocessors



Must be low for

device enable



Input data latched

on rising edge



Potentiometer data registers

loaded on rising edge



CS#

1 2 3



4 5



6



7



8



9



2



10 11 12 13 14 15



SCK

MSb

SI



LSb MSb



LSb



X X C1 C0 X X X P0 D7 D6 D5 D4 D3 D2 D1 D0



Data Byte

Command Byte

X : don’t care bits

C1, C0: command bits, “01” set wiper register to data byte, “10” shutdown

P0: must be “1” to select potentiometer for command

Sample commands: 0x11 - write wiper register, 0x21 - shutdown potentiometer



FIGURE 11.7 MCP41xxx command protocol.



Figure 11.8 gives code for testing the PIC to MCP41xxx interface. The

loop of main() prompts the user for an 8-bit value and sends this as the

wiper register value to the MCP41xxx via the spi_setpotmtr(unsigned char c){}

function. Within the spi_setpotmtr() function, the chip select of the MCP41xxx is

brought low by the command bitclr(PORTB, POTCS) statement, where POTCS is

defined as 4. This is equivalent to writing RB4 = 0, but the bitclr macro is used so

that changing to a different PORTB pin for chip select only requires modifying the

#define POTCS 4 statement. After the chip select is asserted, the command byte

(0x11) is written followed by the data byte passed to the function in the c parameter.

Observe that after a byte is written to SSPBUF, the while(!SSPIF) loop waits for the

SSPIF to become nonzero, indicating that the transmission is finished. The

statement SSPIF = 0 is then used to manually reset the SSPIF bit before the next

transmission. The MCP41xxx chip select is negated by the bitset(PORTB,POTCS)

statement before exiting spi_setpotmtr(). The SPI initialization code in main() uses

a positive clock polarity (CKP = 0) and data transmitted on the rising edge (CKE

= 1), as that matches the SPI specifications in the MCP41xxx datasheet. The SCK

frequency of FOSC/16 gives an SCK of approximately 1.8 MHz for the 29.4912

MHz FOSC of the PIC18F242 reference board. This SCK frequency is safely below

the maximum 10 MHz SCK frequency of the MCP41050 device used for testing.

while(1){}



Synchronous Serial IO



#include



#include "config.h"

Include files for configuration

#include "serial.c"

asynchronous serial port IO

#include "serio.c"



}



//RB4 is select for potentiometer

#define POTCS 4



337



bits and



Function for setting potentiometer wiper register



spi_setpotmtr(unsigned char c){

bitclr(PORTB, POTCS); // select potmtr

SSPBUF = 0x11; // write command

while(!SSPIF); // wait until transmited

SSPIF = 0;

// reset

SSPBUF = c;

// write data

while(!SSPIF); // wait until transmited

SSPIF = 0;

// reset

bitset(PORTB, POTCS); // deselect potmtr

}

main(void){

unsigned char pv;

// set select line for output

bitclr(TRISB,POTCS);

bitset(PORTB, POTCS); // deselect pot



Assert Chip Select

Write command byte, wait for

transmit to end, then reset SSPIF



}

wait for

} Write data byte, then reset SSPIF

transmit to end,

Negate Chip Select



} Configure RB4 as an output, ensure

that it is high, deselecting MCP41xxx



serial_init(95,1); // 19200 in HSPLL mode, crystal = 7.3728 MHz

// configure SPI port for potentiometer

CKE = 1; // data transmitted rising edge of SCK

CKP = 0; // clk idle is low

bitclr(TRISC,3); //SCK, output

bitclr(TRISC,5); // SDO, output

bitset(TRISC,4); // SDI pin is input, unused

// SPI Master Mode FOSC/16

SSPM3 = 0; SSPM2 = 0; SSPM1 = 0; SSPM0 = 1;

SSPEN = 0; // reset Sync Serial port

SSPEN = 1; // enable Sync Serial port

SSPIF = 0; // clear SPIF bit



}



Configure SPI port.

Must use CKE=1, CKP=0

as that is compatible with

datasheet specs for

MCP41xxx.

Use FOSC/16, sets SCK

as approx. 1.8 Mhz for

29.49 MHz FOSC



pcrlf(); printf("Potentiometer test started"); pcrlf();

while(1) {

printf("Input value (0-255): ");

Prompt user for 8-bit input value,

scanf("%d", &pv);

send to potentiometer and

pcrlf();

printf("Sending %d to pot.",pv);

use voltmeter to check

pcrlf();

potentiometer output value.

spi_setpotmtr(pv);

}



}



}



ON THE CD



FIGURE 11.8 Test code for PIC to MCP41xxx interface.



The 25LC640 Serial EEPROM

Figure 11.9 shows a PIC18 to 25LC640 serial EEPROM [13] interface. The 25LC640

is a 64 Kb serial EEPROM with an internal 8K x 8 organization and uses an SPI

port for communication. The HOLD# input allows a data transfer to be interrupted

mid-stream and the WP# input disables write operations to the device. These capabilities are not needed in this example, so these pins are tied high to disable them.



Microprocessors



25LC640

64K x 8 EEPROM



PIC



CS#



RB7

SDI



Vdd



Vdd



HOLD# and WP# (write

protect) disabled by tieing

them to Vdd



SO

SI



WP#

HOLD#

Vss

SCK



SDO

SCK

RB4



MCP41xxx Potentiometer

CS#

SI

SCK

Vss



Vdd

PA0

PW0

PB0



Each SPI peripheral requires

? a unique port line for CS#

?

?



FIGURE 11.9 PIC to 25LC640 serial EEPROM interface.



A serial EEPROM is useful as nonvolatile data storage when the 256 bytes of onchip data EEPROM available in the PIC18 is not sufficient. The MCP41xxx from

the previous example is included in Figure 11.9 to illustrate how multiple SPI peripherals coexist in the same system. Observe that the PIC18 uses RB7 as the EEPROM chip select, while RB4 is used as the potentiometer chip select. It is important

that only one SPI peripheral be enabled at a time via its chip select to avoid confusion over which peripheral is being accessed. Without using external logic, each additional SPI peripheral requires a unique port line for chip select control.

Figure 11.10 gives the read operation details for the 25LC640. The first byte

sent is the command byte, which has a value of 0x03.

CS#



SI data transmitted on

rising edge, so use CKE=1



SI



????



SO



FIGURE 11.10



8 Clks



SCK idle low, so use

CKP = 0



~

~



8 Clks



AddrHi AddrLo



xxxx



16-bit address



0x03



Read

Cmd



8 Clks



~

~



8 Clks



~

~



SCK



~

~



338



don’t

care

Rd Data



25LC640 read operation.



????



SO data transmitted after

falling edge, so use SMP=1

Can be repeated to sequentialy

read memory contents; internal

address counter increments on

each operation.



Synchronous Serial IO



339



The next 2 bytes are the most significant byte and least significant byte of the

16-bit address. Because the 25LC640 has only 8K locations (213), the upper 3 bits of

the address MSB are ignored. This address value is loaded into an internal address

counter within the 25LC640. The next SPI operation returns the contents of this

address via the SO data line (the input data on the SI line is ignored by the

25LC640). This also increments the internal address counter. At this point, the operation can be terminated by negating CS# or another SPI operation will return the

byte at the successive memory location. The entire memory contents of the

25LC640 can be sequentially read in this manner without sending another address

value. The internal address counter rolls over to 0x0000 once the value 0x1FFF (213

– 1, or 8191) is reached. As with all SPI transmissions, data in each byte is transmitted MSb first. The 25LC640 inputs data on the rising edge of SCK with output

data produced after the SCK falling edge. PIC18 configuration bits of CKP = 0

(SCK idle low), CKE = 1 (output data stable on rising edge), and SMP = 1 (input

data sampled at end of the SCK period) satisfy the SPI communication requirements of the 25LC640.

Figure 11.11 shows the write operation sequence for the 25LC640, which consists of a write command byte (0x2), the 16-bit address, and up to 32 data bytes. The

write operation is triggered after CS# is negated and has a worst-case completion

time of 5 ms. Once the bytes have been received by the serial EEPROM, it takes the

same amount of time to write 1 byte as 32 bytes, so it is more efficient to write as

many bytes at a time as possible.



CS#



0x02



AddrHi AddrLo



Byte Write

16-bit address

Cmd



8 Clks



8 Clks



~

~



~

~



8 Clks



~

~

~ ~ ~

~ ~ ~



????



8 Clks



Byte0



Byte1



Byte31



Write

Data



Write

Data



????



Write

Data



}



SI



8 Clks



~

~



~

~



8 Clks



~

~



SCK



1 to 32 bytes



FIGURE 11.11



25LC640 write operation.



The incoming bytes are placed into an internal 32-byte write buffer using the

lower 5 bits of the address, while the upper 8 bits of the address determine the

memory page that is written. The starting address of a 32-byte page has its last 5 bits

as all zeros, while the ending address has all ones for the last 5 bits. If the write command address does not begin on a 32-byte page boundary, multiple bytes sent in

the write command may cause page wrapping to occur if the internal address



Microprocessors



counter increments past a page boundary as shown in Figure 11.12b. In most applications, either a single byte is written and thus page wrapping cannot occur, or

the starting address is forced to align to a page boundary and a complete page of 32

bytes is written.

(a) Write the 16 byte string “hello_and_howdy!” beginning at location 0x???0 (page start)

page end : last 5 bits

page start : last 5 bits

of address = “11111”

of address = “00000”

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

address : 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F

h e l l o _ a n d _ h o w d y ! - - - - - - - - - - - - - - - -



‘-’ locations are unchanged

(b) Write the 16 byte string “hello_and_howdy!” beginning at location 0x??1A

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

address : 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F

a n d _ h o w d y ! - - - - - - - - - - - - - - - - h e l l o _



‘-’ locations are unchanged



FIGURE 11.12



Page boundary wrapping during write.



An internal write enable latch must be set before any write command is issued.

Figure 11.13 shows a C function named spi_memwren() that accomplishes this by

writing the command byte 0x06 to the 25LC640. The write enable latch is reset after

a write operation is complete so this function must be called before every write.

#define MEMCS 7

#define MEM_WREN



6



RB7 drives CS#



// do write enable

spi_memwren(){

bitclr(PORTB,MEMCS); // Assert CS

SSPBUF = MEM_WREN;

while(!SSPIF); // wait until transmited

SSPIF = 0;

// reset

bitset(PORTB,MEMCS); // Negate CS

}



ON THE CD



CS#

SCK



8 Clks



~

~



340



0x06

????

SI ????

Write Enable Cmd



FIGURE 11.13 C code for setting the write enable latch (see CD-ROM file

./code/chap11/F_11_16_spimemtst.c).



The write operation is internally self-timed, which means that the internal programming of the target locations continues until the device detects that the write

has successfully completed. The 5 ms write completion time is a worst-case time;

placing a software delay loop after the write operation could satisfy this constraint.



Synchronous Serial IO



341



However, this is a waste of processor clock cycles and the write can actually complete much sooner than that. An internal status register provides a bit named WIP

(write in progress, STATUS[0]) that is a “1” while the write operation is underway.

Before starting a write operation, this bit can be polled via the read status command.

Figure 11.14 shows a C function named spi_memrdsr() that reads the 25LC640

status register by sending the command byte 0x05 followed by a dummy byte that

causes the status register byte to be returned to the PIC via the EEPROM SO output.



ON THE CD



SCK



8 Clks



SI ????



0x05



8 Clks



~

~



// read status register

unsigned char spi_memrdsr(){

bitclr(PORTB,MEMCS); // Assert CS

SSPBUF = MEM_RDSR;

while(!SSPIF); // wait until transmited

SSPIF = 0;

// reset

// dummy data for read

SSPBUF = 0;

while(!SSPIF); // wait until transmited

SSPIF = 0;

// reset

bitset(PORTB,MEMCS); // Negate CS

return(SSPBUF); // return status

}



CS#



~

~



#define MEMCS 7

#define MEM_RDSR 5 //rd status cmd



xxxx



Read Status don’t

care

Cmd

SO



Status



FIGURE 11.14 C Code for reading the EEPROM status register (see CD-ROM file

./code/chap11/F_11_16_spimemtst.c).



Both the



spi_memrdsr()



and



spi_memwren()



functions are used by the



spi_memrw() function of Figure 11.15 that reads or writes 32 bytes to the serial EEP-



ROM. For a write operation, the write_flag parameter is nonzero and the 32 bytes

contained in buf are written to the starting location specified by addr. Before beginning a read or write operation, a do-while{} loop polls the EEPROM status register via the spi_memrdsr() function and loops while the write-in-progress bit

(status[0]) is one, indicating a previous write is still underway. Once the write-inprogress bit returns as zero, the loop exits and the spi_memwren() function is called

to set the write enable latch. The 16-bit address is split into high byte and low byte

by the statements addr_hi = addr >> 8 and addr_lo = addr & 0x00FF, respectively.

The write operation begins by asserting the chip select line via bitclr(PORTB,MEMCS).

The write command byte (0x02) is then sent, followed by the high address byte, and

then the low address byte. A for{} loop then sends the 32 bytes stored in the buf parameter. The bitset(PORTB,MEMCS) statement terminates the write operation by

negating the chip select.



342



Microprocessors



#define MEMCS 7

#define MEM_WRITE 2

#define MEM_READ 3

//write or read 32 bytes

spi_memrw(char *buf, unsigned int addr,

char write_flag){

unsigned char addr_lo, addr_hi, status;

char i;



no



write_flag?

yes

read status



yes



// ensure last write is finished

do {

Write-in-progress

status = spi_memrdsr();

bit is status[0]

}while (bittst(status,0));

// do write enable if write

no

if (write_flag) spi_memwren();

addr_lo = addr & 0x00FF;

Send Read

addr_hi = (addr >> 8);

Cmd (0x3)

bitclr(PORTB,MEMCS); // Assert CS

if (write_flag) SSPBUF = MEM_WRITE;

else SSPBUF = MEM_READ;

while(!SSPIF); // wait until transmited

SSPIF = 0;

// reset

SSPBUF = addr_hi; // send high byte address

while(!SSPIF); // wait until transmited

SSPIF = 0;

// reset

SSPBUF = addr_lo; // send low byte address

while(!SSPIF); // wait until transmited

SSPIF = 0;

// reset

for (i=0;i<32;i++){ // send 32 bytes

no

SSPBUF= *buf; //for read, don’t care data

while(!SSPIF); // wait until transmited

SSPIF = 0;

// reset

if (!write_flag) *buf = SSPBUF;

buf++;

}

bitset(PORTB,MEMCS); // Negate CS



write in

progress?

no

do write enable

Assert Chip Select

write_flag?

yes

Send Write

Cmd (0x2)

Send Addr Hi

Send Addr Lo

Send Write byte

(SSBUF = *buf)

write_flag?

yes

Save Read byte

*buf = SSPBUF

buf++



}



32 bytes?

yes

Negate Chip Select



no



Exit



ON THE CD



FIGURE 11.15 C code for reading/writing 32 bytes from/to the serial

EEPROM (see CD-ROM file ./code/chap11/F_11_16_spimemtst.c).



For a read operation, the write_flag parameter is zero and 32 bytes are read

from the EEPROM starting at location addr and written to the buf array during the

for{} loop. The read command byte 0x3 precedes the high and low address bytes

that specify the starting address for the read operation. There are no page boundaries for read operations and thus the entire EEPROM contents could be returned

in one call to spi_memrw() if there was enough room in buf to store these values. Of

course, there is not enough internal storage on the PIC18 to hold this many bytes,

so a buffer size of 32 bytes was chosen for read to match the write page buffer size.



Synchronous Serial IO



343



Figure 11.16 gives the C code for main() that uses the spi_memrw() function to

test the serial EEPROM. After configuring the SPI port, the user is prompted to

choose either write or read mode. In write mode, a loop prompts the user to enter

32 characters that are then written to the serial EEPROM. On each pass through the

loop, the memaddr variable that specifies the starting location is incremented by 32

so that writes are performed to successive pages. In read mode, a 32-byte page is

read and displayed each time the user enters a character, starting from location 0.

char membuf[32];

int memaddr;

main(void){

unsigned char mode,i;

// set RB7 for output

TRISB = 0x7F;

bitset(PORTB,MEMCS);

serial_init(95,1); // 19200 in HSPLL mode, crystal = 7.3728 MHz

// configure SPI port for serial eeprom

CKE = 1; // data transmitted rising edge of SCK

CKP = 0; // clk idle is low

SMP = 1; // sample data at end of period

bitclr(TRISC,3); //SCK, output

bitclr(TRISC,5); // SDO, output

bitset(TRISC,4); // SDI pin is input

// SPI Master Mode FOSC/16

SSPM3 = 0; SSPM2 = 0; SSPM1 = 0; SSPM0 = 1;

SSPEN = 0;

SSPEN = 1;

SSPIF = 0; // clear SPIF bit

pcrlf(); printf("Mem Test Started"); pcrlf();



}



SPI Port configured

for 25LC640 serial

EEPROM operation.

These are the same settings

as were used for the

MCP41xxx digital

potentiometer



memaddr = 0;

printf ("Enter 'w' for write mode, anything else reads: ");

mode = getche();

pcrlf();

while(1) {

if (mode == 'w') {

Prompt user to enter 32 bytes,

printf("Enter 32 chars.");pcrlf();

capture them, then write them

for(i = 0;i< 32;i++) {

membuf[i] = getche();

to the serial EEPROM.

}

Increment memory address

pcrlf();printf("Doing Write");pcrlf();

by 32 for next write.

spi_memrw(membuf,memaddr,1); // do write

memaddr = memaddr +32;

} else {

Read 32 bytes from the

spi_memrw(membuf,memaddr,0); // do read

serial EEPROM, then

for(i = 0;i< 32;i++) putch(membuf[i]);

display them.

pcrlf();

Repeat on each keypress.

printf("Any key continues read...");pcrlf();

getch();

memaddr = memaddr +32;

}

}



}

}



}



ON THE CD



FIGURE 11.16



main() for testing PIC18 to serial EEPROM interface.



344



Microprocessors



Figure 11.17 shows console output from testing the code of Figure 11.16. Three

32-byte strings are entered and written to the first three pages of serial EEPROM

memory. Reset is asserted to terminate the write mode and then these strings are retrieved from the serial EEPROM and displayed.



1st 32 bytes

2nd 32 bytes

3rd 32 bytes



}



Writing

strings

to memory



Pressed reset



1st 32 bytes

2nd 32 bytes

3rd 32 bytes



FIGURE 11.17

Figure 11.16.



}



Reading

Memory

Contents

after write



Console output from testing code of



The 25LC640 serial EEPROM has additional capabilities that are not covered

here, such as being able to write protect blocks of internal memory; see the

datasheet [13] for details.

Sample Question: How long does it take to transfer the data required for a page write

to the 25LC640 serial EEPROM assuming an FOSC = 40 MHz and a conservative 20

instruction overhead for every byte written over the SPI interface? Choose the highest

SCK frequency possible for the EEPROM that still meets the 25LC640 maximum SCK

specification of 3 MHz@ 5 V, industrial temperature range (do not use Timer2 to

generate the SCK).



Answer: An SCK of FOSC/16 gives 2.5 MHz, which is less than the 3 MHz

maximum for SCK. For FOSC = 40 MHz, each instruction takes 4/40 MHz

= 0.1 μs. A SCK bit time is 1/2.5 MHz = 0.4 μs. The bytes sent are the

byte write command, MSB EEPROM address, LSB EEPROM address, and 32

data bytes for a total of 35 bytes. Total bit times are 35*8 = 280 bit times.

The estimated time for the data transfer is:

35 bytes * 20 instructions * instruction time + 280 bits * SCK bit time

35 * 20 * 0.1 μs + 280 * 0.4 μs = 182 μs.



Synchronous Serial IO



345



11.6 THE I 2 C BUS

The Inter IC (I2C) bus [14] was introduced by Philips Semiconductor in the early

1980s and it has since become a de facto standard serial bus. The term bus in this

context is a formal designation and is different from the previous casual usage of

“bus” to describe groups of parallel wires. In this context, a bus is a communication

channel in which there is one transmitter and multiple receivers. All receivers see

data that is transmitted over the communication channel. Each receiver decodes

transmitted messages and uses an address within the message to determine if it is

the target of the message. The receiver that is the message target then replies back

to the message transmitter over the same communication channel. Figure 11.18

gives two examples of bus communication channels. Figure 11.18a shows how normal conversation among a group of friends is essentially bus-based communication, as the transmitter uses the name of the person as the address when sending a

message across the communication channel, which is air. The person who is addressed by name then responds to the transmitter. Figure 11.18b illustrates how Internet addresses are used on an Ethernet network for computer communication.

An Ethernet network is a bus, as all computers monitor traffic on the network and

only respond to those data packets whose header addresses match their assigned Internet address.



(a) Transmitter



Transmitter



Joe



Receivers



Yo, Bob!



Receivers



Joe

Comm

Channel Tom

(Air)



Comm

Channel Tom

(Air)



Not for

me...



Not for

me...

Wassup, Bill?



Bill



Bill



the address



Bob

the address

matches, so this receiver responds



Bob



(b) Transmitter



Receivers



Transmitter

192.168.0.3,

0x2AF4,0x43AB!



192.168.0.5

Comm

Channel

(Ethernet)

192.168.0.1



the address



192.168.0.2



192.168.0.1



Comm

Channel

(Ethernet)



Receivers

192.168.0.3 !=

192.168.0.5

192.168.0.5

192.168.0.3 !=

192.168.0.2

192.168.0.2

192.168.0.3 ==

192.168.0.3,

I will respond.



192.168.0.3

the address 192.168.0.3

matches, so this receiver responds



FIGURE 11.18



Two examples of bus communication.



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

×