PDA

View Full Version : Thắc mắc về giao tiếp SPI


trangham283
29-07-2009, 09:26 AM
Xin chào mọi người,

Em đã có đọc qua tìm hiểu về giao tiếp SPI và em đang viết chương trình để thử đọc và viết vào EEPROM 25LC256 nhờ dsPIC30F2011. Em đã tham khảo tài liệu cũng như code mẫu của microchip, cụ thể là cái này:

http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en023868

đã thay đổi một số chỗ sao cho phù hợp với dsPIC của mình.

Hiện giờ em có thắc mắc về phần configuration cho thanh ghi SPI1CON của PIC, cụ thể là phần chọn Mode: word hay byte communication.

Trong tài liệu của EEPROM, theo em hiểu thì EEPROM sẽ chỉ nhận từng byte một, ("The 25LC256 contains an 8-bit instruction register), như vậy có phải em sẽ phải chọn chế độ giao tiếp byte?

Phần em không hiểu hiện giờ là: thanh đệm SPI1BUF của PIC dài 16 bit, khi viết dữ liệu cần chuyển đi cũng là viết 16 bit, như vậy khi giao tiếp theo chế độ 1 byte thì cái byte còn lại nó vứt đi đâu ạ?

namqn
29-07-2009, 10:07 AM
Xin chào mọi người,

Em đã có đọc qua tìm hiểu về giao tiếp SPI và em đang viết chương trình để thử đọc và viết vào EEPROM 25LC256 nhờ dsPIC30F2011. Em đã tham khảo tài liệu cũng như code mẫu của microchip, cụ thể là cái này:

http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en023868

đã thay đổi một số chỗ sao cho phù hợp với dsPIC của mình.

Hiện giờ em có thắc mắc về phần configuration cho thanh ghi SPI1CON của PIC, cụ thể là phần chọn Mode: word hay byte communication.

Trong tài liệu của EEPROM, theo em hiểu thì EEPROM sẽ chỉ nhận từng byte một, ("The 25LC256 contains an 8-bit instruction register), như vậy có phải em sẽ phải chọn chế độ giao tiếp byte?

Phần em không hiểu hiện giờ là: thanh đệm SPI1BUF của PIC dài 16 bit, khi viết dữ liệu cần chuyển đi cũng là viết 16 bit, như vậy khi giao tiếp theo chế độ 1 byte thì cái byte còn lại nó vứt đi đâu ạ?
Mời bạn đọc tutorial sau:

http://www.picvietnam.com/forum/showthread.php?t=2481

Thông tin liên quan đến thắc mắc của bạn đã được đề cập trong tutorial đó.

Thân,

Serenade
29-07-2009, 12:28 PM
Đương nhiên là cấu hình byte rùi bạn.

#define EE_SSPIF_BIT IFS2bits.SPI2IF
#define SPIREAD 0x03
#define SPIWRITE 0x02
#define SPIWRDI 0x04
#define SPIWREN 0x06
#define SPIRDSR 0x05
#define SPIWRSR 0x01

/************************************************** *******************/
void EE_SPIPut(BYTE v)
{
BYTE dummy;

EE_SSPIF_BIT = 0;
dummy = EE_SSPBUF_REG;
EE_SSPBUF_REG = v;
while(EE_SSPIF_BIT == 0 );
}
/************************************************** *******************/
BYTE EE_SPIGet(void)
{
EE_SPIPut(0x00);
return EE_SSPBUF_REG;
}
/************************************************** *******************/

BYTE NVMRead(DWORD address)// read a 16-bit value starting at an even address
{
BYTE msb,lsb,temp;

// wait until any work in progress is completed
do
{
SPISelectEEPROM();
EE_SPIPut(SPIRDSR);
temp = EE_SPIGet();//read status register
SPIUnselectEEPROM();
}while(temp & 0x03);

// perform a 16-bit read sequence (two byte sequential read)
SPISelectEEPROM(); // select the Serial EEPROM
EE_SPIPut(SPIREAD);// read command
EE_SPIPut(address >> 8); // address MSB first
EE_SPIPut(address & 0xff);// address LSB (word aligned)
//msb = EE_SPIGet();
lsb = EE_SPIGet();
SPIUnselectEEPROM();
return (lsb);

}//ReadNVM
/************************************************** *******************/
void NVMWrite( DWORD address, BYTE data)
{ // write a 16-bit value starting at an even address
BYTE temp;
// wait until any work in progress is completed
do
{
SPISelectEEPROM();
EE_SPIPut(SPIRDSR);
temp = EE_SPIGet();//read status register
SPIUnselectEEPROM();
}while(temp & 0x03);
// Set the Write Enable Latch
SPISelectEEPROM();
EE_SPIPut(SPIWREN);
SPIUnselectEEPROM();

// perform a 16-bit write sequence (2 byte page write)
SPISelectEEPROM(); // select the Serial EEPROM
EE_SPIPut(SPIWRITE);
EE_SPIPut(address >> 8); //address MSB first
EE_SPIPut(address & 0xff); // address LSB (word aligned)
EE_SPIPut(data);
SPIUnselectEEPROM();

}//WriteNVM

trangham283
29-07-2009, 12:32 PM
Em cảm ơn anh Nam, ý anh là đoạn này ạ? Em có đọc qua tutorial này rồi, nhưng em vẫn không hiểu lắm :"> Anh có thể làm ơn giải thích thêm cho em về thao tác truyền dữ liệu sang slave được không ạ?

Master có thể ghi dữ liệu mới vào bộ đệm SPI khi dữ liệu cũ đang được dịch ra. Khi
thanh ghi dịch trống thì dữ liệu mới sẽ được chuyển từ bộ đệm vào. Người dùng chỉ
cần chú ý kiểm tra bit SPITBF trong thanh ghi SPIxSTAT tương ứng để tránh ghi vào bộ
đệm khi nó đang chứa dữ liệu.

Cùng lúc với dữ liệu được dịch ra từ bộ đệm truyền, dữ liệu cũng được lấy vào
thanh ghi dịch, và khi bộ đệm nhận đã nhận đủ số bit cần thiết thì dữ liệu sẽ được
chuyển vào bộ đệm nhận , và bit SPIIF sẽ được bật. Nếu ngắt SPI được cho phép, bằng cách bật bit SPIIE, thì một ngắt SPI sẽ được tạo ra. Bit SMP trong thanh ghi SPIxCON
cho phép chọn vị trí lấy mẫu dữ liệu vào, ở giữa hay cuối mỗi chu kỳ của bit.

Bit SPIRBF trong thanh ghi SPIxSTAT cho biết bộ đệm nhận có dữ liệu. Nếu dữ liệu
này không được đọc ra khi thanh ghi dịch hoàn tất thao tác dịch dữ liệu mới vào thì sẽ
xảy ra tràn bộ đệm nhận, với bit SPIROV trong thanh ghi SPIxSTAT được bật. Khi đó
dữ liệu mới sẽ không được chuyển vào bộ đệm và bị mất.

Có nghĩa là nếu em config giao tiếp 8-bit, sau khi SPIxSR nhận 8 bit, 8 bit này sẽ được chép vào SPIxRXB rồi sẽ cần được đọc ra để tránh tràn và mất dũ liệu?

Em xin lỗi nếu em lại đọc sót phần quan trọng :P, nhưng em vẫn không hiểu thao tác truyền dữ liệu. Đoạn trên là nhân dữ liệu từ slave, vậy nếu em muốn gửi dữ liệu, em cần viết vào SPIxBUF, thì vẫn là viết 16 bit phải không ạ? Hay là em phải dùng lệnh mov.b? (Em đang tập viết bằng hợp ngữ)

Em cảm ơn anh nhiều ạ.

trangham283
29-07-2009, 12:42 PM
Cảm ơn bạn Serenade, rất tiếc mình đang viết chương trình bằng hợp ngữ, mình lại chưa lập trình bằng C bao giờ cả nên code bạn viết mình không hiểu lắm.

Dù sao cũng rất cảm ơn bạn!

Serenade
29-07-2009, 12:49 PM
Bắt đầu viết bằng hợp ngữ thì tốt,nhưng mình khuyên là sau này nên chuyển qua C vì những chương trình lớn viết bằng hợp ngữ không nổi đâu.

Serenade
29-07-2009, 12:59 PM
Mình sẽ nói sơ về giao tiếp spi nhé.Giao tiếp spi sử dụng chung bộ đệm truyền nhận dữ liệu,một đầu dữ liệu đẩy ra ngoài thì đồng thời đầu còn lại của bộ đệm được đưa dữ liệu vào.do đó bạn muốn nhận dữ liệu từ slave (eeprom) thì phải gửi một byte bất kỳ,còn muốn truyền dữ liệu đi thì trước hết phải đọc 1 byte dump trong vùng đệm trước khi truyền để tránh trường hợp tràn vùng đệm nhận vì bạn truyền 1 byte đi đồng nghĩ với việc bạn nhận một byte từ slave.

namqn
29-07-2009, 05:51 PM
Em cảm ơn anh Nam, ý anh là đoạn này ạ? Em có đọc qua tutorial này rồi, nhưng em vẫn không hiểu lắm :"> Anh có thể làm ơn giải thích thêm cho em về thao tác truyền dữ liệu sang slave được không ạ?

Có nghĩa là nếu em config giao tiếp 8-bit, sau khi SPIxSR nhận 8 bit, 8 bit này sẽ được chép vào SPIxRXB rồi sẽ cần được đọc ra để tránh tràn và mất dũ liệu?

Em xin lỗi nếu em lại đọc sót phần quan trọng :P, nhưng em vẫn không hiểu thao tác truyền dữ liệu. Đoạn trên là nhân dữ liệu từ slave, vậy nếu em muốn gửi dữ liệu, em cần viết vào SPIxBUF, thì vẫn là viết 16 bit phải không ạ? Hay là em phải dùng lệnh mov.b? (Em đang tập viết bằng hợp ngữ)

Em cảm ơn anh nhiều ạ.
Ngay dưới đoạn mà bạn vừa đưa lên là thông tin chọn cấu hình, nếu đặt bit MODE16 = 0 thì bạn sẽ dùng chế độ truyền thông 8-bit. Trong chế độ này, chỉ có 8 bit được dịch ra chân SDO, và 8 bit được dịch vào chân SDI.

Bạn ghi bao nhiêu bit vào SPIxBUF thì cũng chỉ có 8 bit thấp nhất được gửi sang slave khi chọn MODE16 = 0, và cũng chỉ có 8 bit thấp nhất là dữ liệu thực sự nhận được từ slave trong trường hợp này. Không nhất thiết phải dùng lệnh mov.b để lọc 8 bit dữ liệu nhận được từ slave, nhưng đó là một cách tốt.

Thân,

trangham283
30-07-2009, 02:26 AM
Cảm ơn mọi người đã giúp mình trả lời những câu hỏi trên. Hic hic đây là code mình viết, mình dùng UART để kiểm tra xem việc đọc/viết dữ liệu nhưng vẫn không thành công.

Mình trước đó đã có một code riêng để kiểm tra UART, nên mình khá chắc chắn vấn đề nằm ở cái SPI này...

Các cao thủ có thể nhìn qua code này rồi giúp em được không ạ? Cảm ơn mọi người nhiều nhiều!



;Instruction shortcuts:
.equ RDINS,3
.equ WRINS,2
.equ WRDI,4
.equ WREN,6
.equ RDSR,5
.equ WRSR,1


ADDR: .space 2 ;Current Memory address
TOSEND: .space 2
NUM: .space 2

RDATA: .space 2
OUTWORD: .space 2




call _spi_init

mov #0xFFFF,W0
mov W0,NUM

;WREN:
call cslow
mov #WREN,W0
mov W0,OUTWORD
call output
call cshigh

call cslow
mov #WRINS,W0
mov W0,OUTWORD
call output

;Write address:
mov ADDR,W0 ;MSB of address
swap W0
mov W0,OUTWORD
call output

mov ADDR,W0 ;LSB of address
mov W0,OUTWORD
call output

;Write data
mov NUM,W0 ;This would write only LSB
mov W0,OUTWORD
call output
call cshigh


;Polling:
poll:
call cslow
mov #RDSR,W0
mov W0,OUTWORD
call output
mov #0x0000,W0 ;Dummy send to read
mov W0,OUTWORD
call output
mov RDATA,W0
and #0x0001,W0 ;Is WIP bit clear?
bra nz,poll
call cshigh

send:
clr ADDR

memrd1:

call cslow
mov #RDINS,W0
mov W0,OUTWORD
call output

;Write address:
mov ADDR,W0 ;address MSB
swap W0
mov W0,OUTWORD
call output

mov ADDR,W0 ;address LSB
mov W0,OUTWORD
call output

mov #0x00,W0 ;Dummy write to retain clock
mov W0,OUTWORD
call output
call cshigh

mov RDATA,W0
mov W0,TOSEND

;Send to PC via UART

call chktr
mov TOSEND,W0
mov W0,U1TXREG
swap W0
call chktr
mov W0,U1TXREG

done:
bra done
;................................................. .............................
;Subroutine: SPI Initialization
;................................................. .............................
_spi_init:
mov #0x013E,W0 ;SS not used anyway
;SDO pin controlled by module
;Communication byte-wide
;Input sampled at middle of data output line
;CKE = 1: Output data changes on transition from CLK active to idle
;CKP = 0: Idle state for CLK at low level
;Master enable, scale: fsck = fcy/4 = 1MHz

mov W0,SPI1CON
bclr SPI1STAT,#SPIROV
bset SPI1STAT,#SPIEN
clr ADDR ;Set first memory address to write to: 0x0000
return

;Sending instructions to EEPROM
output:
mov OUTWORD,W8
mov W8,SPI1BUF

chktrans: ;Is transmit buffer empty??
mov SPI1STAT,W2
and #0x0002,W2
bra nz,chktrans

chkrd:
mov SPI1STAT,W2
and #0x0001,W2 ;Is receive buffer full?
bra z,chkrd


mov SPI1BUF,W8 ;Dump received data
mov W8,RDATA
bclr SPI1STAT,#SPIROV

return

cslow:
bclr PORTB,#RB2
return

cshigh:
bset PORTB,#RB2
return

chktr:
;Check if UART transmit buffer is full
mov U1STA,W2
and #0x0200,W2
bra nz,chktr
return

trangham283
30-07-2009, 05:21 AM
Update:

Hôm nay em đã kiểm tra mạch bằng máy đo dao động. Em cho hiện 4 kênh: SCK, CS, SDO và SDI.

Các kênh SCK, CS và SDO của PIC hoạt động đúng như em dự định, em có thấy các lệnh viết, đọc được dịch ra chân SDO một cách hợp lý. Tuy nhiên riêng kênh SDI thì luôn luôn 0. Điều này làm em nghĩ vấn đề nằm ở đoạn nhận lại dữ liệu từ EEPROM...

Mọi người có thể đọc qua code post ở phía trên của em rồi góp ý được không ạ?

Cảm ơn mọi người nhiều nhiều!

namqn
30-07-2009, 09:35 AM
Update:

Hôm nay em đã kiểm tra mạch bằng máy đo dao động. Em cho hiện 4 kênh: SCK, CS, SDO và SDI.

Các kênh SCK, CS và SDO của PIC hoạt động đúng như em dự định, em có thấy các lệnh viết, đọc được dịch ra chân SDO một cách hợp lý. Tuy nhiên riêng kênh SDI thì luôn luôn 0. Điều này làm em nghĩ vấn đề nằm ở đoạn nhận lại dữ liệu từ EEPROM...

Mọi người có thể đọc qua code post ở phía trên của em rồi góp ý được không ạ?

Cảm ơn mọi người nhiều nhiều!
Bạn kiểm tra lại phần cứng xem có lỗi hay không. Nếu không có lỗi phần cứng thì bạn kiểm tra lại timing của các tín hiệu để xem EEPROM có làm việc được ở tốc độ đó hay không.

Thân,