Giao tiếp SPI với 93LC96B

Giao tiếp 93LC96B và PIC sử dụng SPI, đây là giao tiếp dễ dùng nhất , đơn giản nhất , tốc độ cao nhất trong nhóm, hoạt động theo cơ chế hand-shaking, bắt tay.

Giao tiếp 93LC96B và PIC sử dụng SPI, đây là giao tiếp dễ dùng nhất , đơn giản nhất , tốc độ cao nhất trong nhóm, hoạt động theo cơ chế hand-shaking, bắt tay.

Giao tiếp này cần  ít nhất 2 dây truyền dữ liệu trở lên . Nếu 1 VDK chỉ  cần gởi data thì chỉ cần dây clock và SDO, dây này nối với thiết bị nhận ở cổng SDI, nếu cả gởi và nhận thì cần thêm dây SDI nối với cổng SDO của thiết bị nhận, dây clock là nối chung. Ngoài ra cần thêm một dây điều khiển chọn chíp nữa nối với cổng CS của thiết bị nhận.

Với PIC có sẵn phần cứng SPI ta có thể khai báo các chân giao tiếp theo datasheet và sử dụng các hàm sẵn có gồm

Setup_spi (mode )

-      Dùng thiết lập giao tiếp SPI . Hàm thứ 2 dùng với VDK có 2 bộ SPI .

-      Tham số mode :là các hằng số sau , có thể OR giữa các nhóm bởi dấu |

  • I SPI_MASTER , SPI_SLAVE , SPI_SS_DISABLED
  • II SPI_L_TO_H , SPI_H_TO_L
  • III SPI_CLK_DIV_4 , SPI_CLK_DIV_16 , SPI_CLK_DIV_64 , SPI_CLK_T2

Spi_read ( data )

Spi_write ( value )

Các giá trị trong hàm là các con số 8bit, chức năng là ghi hoặc đọc một dữ liệu vào một địa chỉ của SPI slave

Spi_data_is_in ( ) Hàm này dùng kiểm tra xem giá  trị nhận về SPI đã đủ 1 byte chưa, Hàm trả về TRUE ( 1 ) nếu data nhận được đầy đủ ( 8 bit ) từ SPI , trả về false nếu chưa nhận đủ.

 

Với các PIC không có sẵn thiết kế phần cứng ta có thế tự viết code giao tiếp dựa trên lý thuyết về chuẩn giao tiếp,

Code demo

  1. Sử dụng 16F887 (có phần cứng SPI)

Mạch kết nối với một EEPROM 93LC46B qua các cổng PORTC như hình vẽ

 

 

Reading Help:

Syntax:
#use spi (options)
Elements:
option 
- may be any of the following separated by a comma:
MASTER- Set the device as the master. (default).
SLAVE - Set the device as the slave.
BAUD=n - Target bits per second, default is as fast as possible.
CLOCK_HIGH=n - High time of clock in us (not needed if BAUD= is used).
(default=0).
CLOCK_LOW=n - Low time of clock in us (not needed if BAUD= is used).
(default=0).
DI=pin - Optional pin for incoming data.
DO=pin - Optional pin for outgoing data.
CLK=pin - Clock pin.

MODE=n - The mode to put the SPI bus.
ENABLE=pin - Optional pin to be active during data transfer.
LOAD=pin - Optional pin to be pulsed active after data is transferred.
DIAGNOSTIC=pin - Optional pin to the set high when data is sampled.
SAMPLE_RISE - Sample on rising edge.
SAMPLE_FALL - Sample on falling edge (default).
BITS=n - Max number of bits in a transfer. (default=32)
SAMPLE_COUNT=n - Number of samples to take (uses majority vote). (default=1
LOAD_ACTIVE=n - Active state for LOAD pin (0, 1).
ENABLE_ACTIVE=n - Active state for ENABLE pin (0, 1). (default=0)
IDLE=n - Inactive state for CLK pin (0, 1). (default=0)
ENABLE_DELAY=n - Time in us to delay after ENABLE is activated. (default=0)
DATA_HOLD=n - Time between data change and clock change.
LSB_FIRST - LSB is sent first.
MSB_FIRST - MSB is sent first. (default)
STREAM=id - Specify a stream name for this protocol.
SPI1 - Use the hardware pins for SPI Port 1.
SPI2 - Use the hardware pins for SPI Port 2.
[PCD] SPI3 - Use the hardware pins for SPI Port 3
[PCD] SPI4 - Use the hardware pins for SPI Port 4
FORCE_SW - Use a software implementation even when hardware pins are
specified.
FORCE_HW - Use the pic hardware SPI.
NOINIT - Do not initialize the hardware SPI Port.
[PCD] XFER16 - Use 16-bit transfers instead of two 8-bit transfers

 

The most common pins present on hardware SPI are: DI, DO, and CLK. These pins don’t need to be assigned values through the options; the compiler will automatically assign hardwarespecific values to these pins. Consult your PIC’s data sheet as to where the pins for hardware SPI are. If hardware SPI is not used, then software SPI will be used. Software SPI is much slower than hardware SPI, but software SPI can use any pins to transfer and receive data other than just the pins tied to the PIC’s hardware SPI pins. The MODE option is more or less a quick way to specify how the stream is going to sample data. MODE=0 sets IDLE=0 and SAMPLE_RISE. MODE=1 sets IDLE=0 and SAMPLE_FALL. MODE=2 sets IDLE=1 and SAMPLE_FALL. MODE=3 sets IDLE=1 and SAMPLE_RISE. There are only these 4 MODEs. SPI cannot use the same pins for DI and DO. If needed, specify two streams: one to send data and another to receive data. The pins must be specified with DI, DO, CLK or SPIx, all other options are defaulted as indicated above.

Dịch:

Các chân phổ biến nhất hiện có trên SPI phần cứng là: DI, DO và CLK. Các chân này không cần phải được gán giá trị thông qua các tùy chọn; trình biên dịch sẽ tự động gán các giá trị cụ thể phần cứng cho các chân này. Tham khảo bảng dữ liệu PIC của bạn để biết vị trí của các chân cho SPI phần cứng. Nếu SPI phần cứng không được sử dụng, thì SPI phần mềm sẽ được sử dụng. SPI phần mềm chậm hơn nhiều so với SPI phần cứng, nhưng SPI phần mềm có thể sử dụng bất kỳ chân nào để truyền và nhận dữ liệu ngoài các chân gắn với chân SPI phần cứng của PIC. Tùy chọn MODE ít nhiều là một cách nhanh chóng để chỉ định cách luồng sẽ lấy dữ liệu mẫu. MODE = 0 đặt IDLE = 0 và SAMPLE_RISE. MODE = 1 đặt IDLE = 0 và SAMPLE_FALL. MODE = 2 đặt IDLE = 1 và SAMPLE_FALL. MODE = 3 đặt IDLE = 1 và SAMPLE_RISE. Chỉ có 4 CHẾ ĐỘ này. SPI không thể sử dụng cùng một chân cho DI và DO. Nếu cần, hãy chỉ định hai luồng: một luồng để gửi dữ liệu và luồng khác để nhận dữ liệu. Các chân phải được chỉ định với DI, DO, CLK hoặc SPIx, tất cả các tùy chọn khác được mặc định như đã chỉ ra ở trên.

 

Có một điều ta thấy rằng, mỗi một thiết bị mà ta cần truyền SPI có một phương cách nhận hơi có chút khác nhau, ví như là chỉ lệnh đọc – ghi … nên mỗi thiết bị hầu như có code dành riêng hoặc chỉ dẫn viết code, các hàm xuất nhập SPI sẵn có của CCS C đơn  thuần mới chỉ gửi hoặc nhận data ở cổng mà chưa có hàm bắt tay với thiết bị ngoài, vì vậy thường người ta hay dùng các hàm SPI tự code.

 

  1. b.    Sử dụng software mode

Vẫn sử dụng sơ đồ kết nối trên nhưng khai báo không sử dụng module SPI và hàm sẵn có của PIC, có nghĩa là các chân RC4, RC5 và RC3 là các chân IO bình thường, đồng nghĩa với việc có thể sử dụng cho các PIC khác không có module SPI như thường.

Để giao tiếp ta có một thư viện viết riêng điều khiển truyền nhận.

Truyền dữ liệu (ghi) bao gồm 2 nhiệm vụ:

-       Thủ tục truyền ra một gói data:

Gói data có thể là 1 byte hoặc nhiều byte, ví dụ gói 1 byte thì ta cần lặp 8 lần việc xuất ra từng bit của gói từ bit cao đến bit thấp, để dễ hình dung ta dùng vòng lặp for như sau:

int i;

for(i=1; i<=8; ++i){

     // lấy trạng thái của bit trái nhất: =data & 0x80

//nếu bằng 0 thì xuất mức 0, ngược lại xuất mức 1

  • output_bit(PIN_DO, data&0x80);// PIN_DO là DATA OUT của PIC

//xuất 1 xung nhịp

  • output_high(PIN_CLK);
  • output_low(PIN_CLK);

//dịch trái data 1 bit

Data<<=1;

}

 

-       Thủ tục ghi data vào thiết bị ngoài

Cái này tùy thuộc quy định của thiết bị, ví dụ đói với

Tương tự, nhận dữ liệu(read) cũng có 2 tác vụ

-       Đọc một gói data từ thiết bị ngoài

-       Đưa data vào một biến

//

//tập tin này chứa các hàm ghi và đọc  eeprom 93LC46B

//theo phương thức Bit-banging

//code được viết cho CCS PIC C compiler

//viết lại bởi tuấn anh

//cập nhật ngày 10/8/2021

//

#define PIN_CS           pin_C2

#define PIN_CLK          pin_C3

#define PIN_DIN         pin_C5

#define PIN_DO        input(PIN_C4)

 

#define start            0x01

#define write_enable     0x30

#define write_disable    0x00       

#define opcode_write     0x40

#define opcode_read      0x80

 

//Command format  start bit 0x01 + (opcode (2 bits) + address) + data

//EEPROM only starts functioning when both clock and data lines are high, i.e when it received a start bit

 

 

void E9346B_init();

void pulse_clock();

void write_value(unsigned char value);

unsigned char read_value();

void E9346B_write(unsigned char address, unsigned long value);

unsigned long E9346B_read(unsigned char address);

void E9346B_write_state(int state);//Enable or Disable write

 

void E9346B_init()

{

    set_TRIS_C(0xD3);//C2_out,C3_out,c4_IN, C5_out

    output_C(0x00);

}

 

void pulse_clock(){

   output_high(PIN_CLK);

   delay_us(1);

   output_low(PIN_CLK);

   delay_us(1);

}

 

//xuất dữ liệu 8bit ra port IN của EEPROM

void write_value(unsigned char value)

{

   unsigned char i;

   for(i=1;i<=8;++i)

   {

        output_bit(PIN_DIN,value & 0x80);       

        pulse_clock();

        value <<= 1;

   };

}

 

//lấy dữ liệu 8bit từ port OUT của EEPROM vào một biến

unsigned char read_value()

{

    unsigned char i,temp = 0x00;

    for(i=1;i<=8;++i)

    {

        pulse_clock();       

        temp <<= 1;        

        temp |= PIN_DO;

    };   

    return temp;

}

 

//ghi dữ liệu value vào một địa chỉ address

void E9346B_write(unsigned char address, unsigned long value)

{

    unsigned char hb = 0x00;

    unsigned char lb = 0x00;

    unsigned long timeout = 0x0000;

   

    hb = ((value & 0xFF00) >> 0x08);

    lb = (value & 0x00FF);

   

    E9346B_write_state(write_enable);

    output_high(PIN_CS);

   

    write_value(start);

    write_value(opcode_write | address);

    write_value(hb);

    write_value(lb);

   

    output_low(PIN_CS);

    delay_us(20);   

   

output_high(PIN_CS);

 

    //chờ EEPROM vào trạng thái ổn định

    while((!PIN_DO) && (timeout < 10000))

    {

        delay_us(1);

        timeout++;

    };

   

    output_low(PIN_CS);

    delay_us(20);       

   

    E9346B_write_state(write_disable);

}

 

//đọc dữ liệu từ một địa chỉ vào biến

unsigned long E9346B_read(unsigned char address)

{

    unsigned char lb = 0x00;

    unsigned long hb = 0x00;

   

    output_high(PIN_CS);

   

    write_value(start);

    write_value(opcode_read | address);

    hb = read_value();

    lb = read_value();

   

    output_low(PIN_CS);

    delay_us(20);  

   

    hb <<= 0x08;

    hb |= lb;

   

    return hb;

}

 

//điều khiển cho phép ghi với 2 tham số write_enable và write_disable

void E9346B_write_state(int state)

{

    output_high(PIN_CS);

    write_value(start);

    write_value(state);

    output_low(PIN_CS);

    delay_us(10);

}