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.