16
Giao tiếp I2C Giới thiệu I2C: I2C là một loại bus giao tiếp ngoại vi được phát triển bới hãng Philips, thường được sử dụng để điều khiển nhiều thiết bị điều khiển khác được bố trí trong các khối mạch tương đối xa nhau (<1.5m ). I2C là giao tiếp nối tiếp thực hiện trên hai đường dây : Clock (SCL) và Data (SDA). Sơ đồ Giao tiếp I2C:

Giao tiếp I2C

Embed Size (px)

Citation preview

Page 1: Giao tiếp I2C

Giao tiếp I2C

 

Giới thiệu I2C:I2C là một loại bus giao tiếp ngoại vi được phát triển bới hãng Philips, thường được sử dụng để điều khiển nhiều thiết bị điều khiển khác được bố trí trong các khối mạch tương đối xa nhau (<1.5m ). I2C là giao tiếp nối tiếp thực hiện trên hai đường dây : Clock (SCL) và Data (SDA).

Sơ đồ Giao tiếp I2C:

Page 2: Giao tiếp I2C

MÔ TẢ CHI TIẾT

Master : Giữ vai trò điều khiển Bus I2C.Tạo xung Clock ( SCL) trong suốt quá trình giao tiếp.Tạo các tín hiệu Start bắt đầu quá trình truy xuất.Phát địa chỉ của thiết bị Slave cần truy xuất.Gửi tín hiệu R/W tới Slave.Truyền dữ liệu tới thiết bị Slave.Nhận dữ liệu từ SlaveTạo tín hiệu Not-ACK khi kết thúc nhận từ Slave.Tạo tín hiệu Stop kết thúc quá trình truy xuất.

Slave :Nhận địa chỉ và bit R/W từ Master (Chỉ “Response” khi đúng địa chỉ của Slave) .Nhận dữ liệu từ Master gửi bit ACK sau mỗi 8 Clock.Truyền dữ liệu tới Master, chờ ACK từ Master để tiếp tục gửi. Thực hiện chức năng chuyên dụng của khối thiết bị ngoại vi ( RAM, EEPROM, ADC,DAC ngoài…).

Page 3: Giao tiếp I2C

Các tín hiệu Start và Stop : SDA=SCL=HIGH: Bus I2C ở trạng thái “Free” sẵn sàng cho một giao tiếp.Điều kiện START:   SDA  ↓ và  SCL =HIGH.Điều kiện STOP:  SDA  ↑ và SCL =HIGH.Cả hai điều kiện START và STOP đều được tạo ra bởi Master. Sau tín hiệu START, bus I2C ở trạng thái  ”busy” thực hiện tác vụ truy xuất nối tiếp. Sau  STOP  bus I2C ở trạng thái “free” cho lần kế tiếp.

Định dạng dữ liệu truyền : - Dữ liệu được truyền trên bus I2C theo từng bit tại mỗi cạnh lên của xung Clock ( SCL), sự thay đổi bit dữ liệu trên SDA xảy ra khi SCL đang ở mức thấp. Số lượng byte có thể truyền trong một lần tùy ý, tối đa là 128 bytes.

- Bit MSB sẽ được truyền trước. Sau 8 xung clock 8 bit dữ liệu đã được truyền đi, thiết bị nhận sẽ kéo SDA xuống mức thấp tương ứng một bit ACK tại xung clock thứ 9 trên báo hiệu đã nhận đủ 8 bit. Thiết bị truyền khi nhận được bit ACK sẽ tiếp tục thực hiện  quá  trình truyền hoặc kết thúc.

Page 4: Giao tiếp I2C

Định dạng địa chỉ thiết bị: Mỗi thiết bị ngoại vi tham gia vào bus i2c đều có một địa chỉ duy nhất, có độ dài là 7 bit như vậy trên một bus I2C ta có thể phân biệt tối đa 128 thiết bị. Byte đầu tiên được gửi từ Master sẽ bao gồm 7 bit địa chỉ và bit thứ 8 gọi là bit R/W. Bit R/W = 0 : Ghi dữ liệu từ Master vào Slave.Bit R/W = 1 : Đọc dữ liệu từ Slave .

Phương thức truyền dữ liệu tới Slave:‐ Master tạo xung START ‐ Master gửi 7 bit địa chỉ của Slave +  bit  R/W= 0 (8 clock)‐ Tại thời điểm clock thứ 9, Master đọc xung ACK từ Slave. ACK = 0 khi Slave có địa chỉ tương ứng đã nhận dạng,Master bắt đầu gửi dữ liệu đến Slave theo từng byte một mất 9 clock do theo sau mỗi byte (8 clock) được gửi từ Master, Slave phải xác nhận bằng một xung ACK=0. ‐Kết thúc quá trình truyền, Master sau khi truyền byte cuối sẽ tạo xung STOP báo hiệu kết thúc.

Chế độ đọc dữ liệu từ Slave:‐ Master tạo xung START ‐ Master gửi 7 bit địa chỉ của Slave  + bit R/W = 1‐ Sau khi Slave xác nhận địa chỉ và trả về bit ACK=0, Slave sẽ gửi từng byte ra SDA; Master sẽ nhận dữ liệu và cũng trả về bit ACK =0 sau mỗi byte. ‐ Để kết thúc quá trình nhận dữ liệu, Master gửi xung Not‐ACK qua cú pháp i2c_read(1,0) và tạo xung STOP.

Thực thi trên vi điều khiển PIC 16F876A

Khối I2C có 6 thanh ghi điều khiển hoạt động, đó là:

Page 5: Giao tiếp I2C

- SSPCON: Thanh ghi điều khiển - SSPCON2: Thanh ghi điều khiển thứ 2 - SSPSTAT: Thanh ghi trạng thái ‐ SSPBUF: Thanh ghi bộ đệm truyền nhận ‐ SSPSR: Thanh ghi dịch ‐ SSPADD: Thanh ghi địa chỉ

Firmware sử dụng CCS - Cấu hình firmware:

- Khai báo chip: chọn file <tenchip.h> có trong thư viện CCS, VD : #include <16F876A.h> - Khai báo cấu hình phần cứng: sử dụng từ khóa #fuse bao gồm các tính năng :. LP, XT, HS, RC : Chọn kiểu dao động. WDT, NOWDT : Kiểm soát Watch Dog Timer.. PROTECT, NOPROTECT : Kiểm soát sao chép firmware.LVP,NOLVP: Kiểm soát lập trình nguồn thấp.- Khai báo cấu hình chức năng sử dụng từ khóa #use bao gồm các tính năng: . delay(clock=24000000) : Khai báo tốc độ thạch anh. i2c(master, sda=PIN_C4, scl=PIN_C3, FAST) : Khai báo cấu hình I2C. rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7): Khai báo cấu hình cổng COM

Khai báo dịch vụ ngắt, sử dụng từ khóa:

INT_SSP : Ngắt nối tiếp khi thanh ghi dịch vừa thực hiện xong 8 bits.INT_EXT : Ngắt ngoàiINT_TIMER1: Ngắt TimerINT_EEPROM: Ngắt khi có sự kiện truy xuất EEPROM.

Chương trình chính main() Master:

- Chương trình chính sống trong vòng lặp Whilewhile (TRUE) {Nội dung chương trình }Ghi dữ liệu tới Slave :i2c_start(); // Master tạo tín hiệu Starti2c_write(0xa0); // Gửi địa chỉ và bít hướng R/W = 0 (Mode write) đến Slavei2c_write(data); // Gửi dữ liệu byte thứ nhấti2c_write(data1); // Gửi dữ liệu byte thứ hai………………….

Page 6: Giao tiếp I2C

i2c_write(datan); // Gửi dữ liệu byte thứ n (n<=128)i2c_stop(); // Master tạo tín hiệu Stop

Đọc dữ liệu từ Slave:i2c_start(); // Master tạo tín hiệu Starti2c_write(0xa1); // Gửi địa chỉ và bít hướng R/W = 1 (Mode read) đến Slavebuf = i2c_read(); // Đọc dữ liệu byte thứ nhấtbuf2= i2c_read(); // Đọc dữ liệu byte thứ hai……………………buf1 = i2c_read(1,0); // Đọc dữ liệu byte thứ n (n<=128) đồng thời gửi Not-ACKi2c_stop(); // Master tạo tín hiệu Stop

Scope: Master phát địa chỉ "0xa0" và 2 byte có nôi dung "1234"

firmware cho Slave

Khai báo cấu hình cho Slave:

Page 7: Giao tiếp I2C

i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0xa0)INT_SSP

Cấu hình ngắt nối tiếp :enable_interrupts(GLOBAL);enable_interrupts(INT_SSP);

Việc đọc, ghi dữ liệu của thiết bị Slave được thực thi trong ngắt tùy thuộc vào trạng thái của đường truyền I2C.

state = i2c_isr_state(); // Đọc trạng thái của I2C Slave sau sự kiện ngắt nối tiếp. State = 0 : Đọc địa chỉ và bit R/W. State = 1-0x7F: Đọc dữ liệu từ Master bằng lệnh i2c_read() . State = 0x80: Đọc địa chỉ và bit R/W=1, gửi dữ liệu tới Master bằng lệnh i2c_write();. State =0x81-0xFF : Nhận được ACK từ Master , Gửi dữ liệu tới Master.

Đọc dữ liệu từ Master:if(state < 0x80) { incoming = i2c_read();if(state == 1) data = incoming;if(state == 2) data1 = incoming;………………………………………if(state == n) datan = incoming;}

Ghi dữ liệu tới Master:if(state == 0x80) i2c_write(data);if(state == 0x81) i2c_write(data1);…………………………………………if(state == 0x8n) i2c_write(datan);

Page 8: Giao tiếp I2C

Scope: Master phát địa chỉ "0xa1" và đọc 3 byte từ Slave

NXQ-VAD-LMC

------------------------------

cách định địa chỉ ô nhớ 24C04 với I2C ? Tình hình là mình đang nghiên cứu giao tiếp I2C của con EEPOM 24C04 với 89C51, các tài liệu về lý thuyết cũng như code mẫu mình đã đọc qua, tuy nhiên vẫn chưa thấy có tài liệu nào đi sâu chi tiết mà đa phần là code protocol I2C chung chung ... Trong quá trình làm, mình có thắc mắc như sau:Khi gởi từ con master (89) sang slave (24) cần có:+ tín hiệu Start

Page 9: Giao tiếp I2C

+ địa chỉ vật lý con tớ (cụ thể 10100000), đợi ACK+ dữ liệu cần ghi vào (tuần tự từng byte)+ tín hiệu stop ...Mình thắc mắc ở chỗ, nếu làm như vậy thì tín hiệu sẽ ghi vào ô nhớ nào, chắc là theo tuần tự từ ô 00H đến xxH ... của 24C04, tuy nhiên có những nhu cầu riêng, ví dụ mình muốn ghi từ ô thứ 10H ... vậy thì phải làm sao ? Cả việc khi đọc lại nữa, nếu chỉ có nhu cầu đọc 1 vài ô nhớ nào đó trong 24C04 thì phải làm sao ?

Mình xem trên giảng đồ xung thì nó sau màn "thủ tục chào hỏi" cứ gửi data rùi chờ ACK rùi data .... cho đến khi muốn dừng thì gửi Stop. Như vậy làm sao để chủ động vấn đề đọc/ghi dữ liệu theo địa chỉ ô nhớ mà mình muốn

Nếu bạn muốn ghi vào 24 từ ô nhớ thứ 10H thì bạn làm như sau:

+ tín hiệu Start+ địa chỉ vật lý con tớ (cụ thể 10100000), đợi ACK+ dữ liệu cần ghi vào là 10H, cụ thể số 10H này sẽ được nghi vào thanh ghi "con trỏ", đợi ACK+ dữ liệu cần ghi (từng byte, sau mỗi byte đợi ACK...), byte đầu tiên sẽ được ghi vào ô nhớ có địa chỉ chứa trong thanh ghi con trỏ (10H) sau mỗi byte thanh ghi con trỏ sẽ tự động tăng 1 đơn vị, như vậy byte thứ hai sẽ được ghi vào địa chỉ 11H...+ tín hiệu stop

Tương tự cho việc đọc bạn nhé.Chúc bạn thành công.

hi mình làm được, rùi, thanks bạn, ghi/đọc ở ô địa chỉ bất kỳ rất tốt, chỉ có cái mình ko hiểu, ở lý thuyết và một số code mẫu thì có thủ tục đợi phản hồi ACK từ con salve, nhưng khi run thì ko được, mình mov ái SDA vào cờ C và dùng vòng lặp đợi cờ C bằng 0 thì thoạt ra, xem như có phản hồi ACK, tuy nhiên khi code chạy thì nó tịt luôn vì ko có ACK trả về, nhưng khi mình làm liều bỏ luôn ko thèm đợi ACK nữa, cứ gửi và nhận, delay một xíu xem như đã có ACK thì ghi nhận tốt.

Mặc dù mình làm đã hoạt động tốt nhưng ko đúng lý thuyết I2C nên rất khó chịu ko biết có ai bị trường hợp này chưa nhỉ

--------------------------------Vụ I2C này làm với PIC thì rẹc rẹc nhưng làm với 89 thì cũng vả mồ hôi. Xin gửi các bác đoạn code I2C và cách dùng nó.Code:#include <reg2051.h>//#include <reg52.h>//#include <intrins.h>//#include "E:\KeilC_uVision4\common.h"

sbit SCL = P3^0;sbit SDA = P3^1;

//extern delay_nop();

void delay_nop(){

_nop_();_nop_();

Page 10: Giao tiếp I2C

_nop_();_nop_();

}

void i2c_start(void){

SDA = 1;delay_nop();SCL = 1;delay_nop();SDA = 0;delay_nop();SCL = 0;

}void i2c_stop(void){

SCL = 0;SDA = 0;delay_nop();SCL = 1;delay_nop();SDA = 1;

}unsigned char i2c_read(bit ack){

unsigned char data i, result;

result = 0;SDA = 1; //select input directiondelay_nop();SCL = 0;for(i = 0; i < 8; i++){

delay_nop();SCL = 1;delay_nop();result <<= 1;if(SDA == 1) result++;SCL = 0;

}delay_nop();SDA = (ack == 1)? 0:1;delay_nop();SCL = 1;delay_nop();SCL = 0;return result;

}bit i2c_write(unsigned char value){

unsigned char data i, temp;bit bdata ack;

temp = value;SCL = 0;SCL = 0;for(i = 0; i < 8; i++){

delay_nop();SDA = (temp & 0x80)? 1:0;delay_nop();SCL = 1;SCL = 1;

Page 11: Giao tiếp I2C

delay_nop();SCL = 0;SCL = 0;temp <<= 1;

}SDA = 1; // read ACKdelay_nop();SCL = 1;SCL = 1;ack = SDA;delay_nop();SCL = 0;SCL = 0;return ack;

}Cách dùng nó vào DS1307, nếu dùng cho AT24xx thì thay lại cái khai báo địa chỉ cho đúng.Code:#include <reg2051.h>//#include <reg52.h>#include <intrins.h>#include "E:\KeilC_uVision4\common.h"

#define RTC_SLAVE_ADDRESS 0xD0#define RTC_READ_COMMAND RTC_SLAVE_ADDRESS | 0x01#define RTC_WRITE_COMMAND RTC_SLAVE_ADDRESS

/*struct Time{

char second;char minute;char hour;//char day;//char date;//char month;//char year;

};*/extern i2c_start();extern i2c_stop();extern bit i2c_write(unsigned char value);extern unsigned char i2c_read(bit ack);

extern delay_us(unsigned int us);/*void delay_us(unsigned int us){

while(us--);}*/extern delay_nop();

void DS1307_init() {

unsigned char x;

i2c_start();i2c_write(RTC_WRITE_COMMAND); //Write slave addressi2c_write(0x00);i2c_start();

Page 12: Giao tiếp I2C

i2c_write(RTC_READ_COMMAND);x = i2c_read(0); // Read second with NACKi2c_stop();delay_us(10);i2c_start();i2c_write(RTC_WRITE_COMMAND);i2c_write(0x00); // Go to Second Reggisteri2c_write(x & 0x7F); // start DS1307i2c_stop();

i2c_start();i2c_write(RTC_WRITE_COMMAND);i2c_write(0x07); // Go to Control Reggisteri2c_write(0x10); // configure RTCi2c_stop();

}

struct Time DS1307_get_time(){

struct Time t;

i2c_start();i2c_write(RTC_WRITE_COMMAND);i2c_write(0x00); // Go to top of memoryi2c_start();i2c_write(RTC_READ_COMMAND);t.second = i2c_read(1); // Read with ACKt.minute = i2c_read(1);t.hour = i2c_read(0);i2c_stop();return t;

}/*struct Time DS1307_get_date_time(){

struct Time t;

i2c_start();i2c_write(RTC_WRITE_COMMAND);i2c_write(0x00); // Go to top of memoryi2c_start();i2c_write(RTC_READ_COMMAND);t.second = i2c_read(1); // Read with ACKt.minute = i2c_read(1);t.hour = i2c_read(1);t.day = i2c_read(1);t.date = i2c_read(1);t.month = i2c_read(1);t.year = i2c_read(0);i2c_stop();return t;

}*void DS1307_set_time(struct Time t){

i2c_start();i2c_write(RTC_WRITE_COMMAND);i2c_write(0x00);i2c_write(t.second);i2c_write(t.minute);i2c_write(t.hour);i2c_stop();

}

Page 13: Giao tiếp I2C

/*void DS1307_set_date_time(struct Time t){

i2c_start();i2c_write(RTC_WRITE_COMMAND);i2c_write(0x00);i2c_write(t.second);i2c_write(t.minute);i2c_write(t.hour);i2c_write(t.day);i2c_write(t.date);i2c_write(t.month);i2c_write(t.year);i2c_stop();

}*//*void DS1307_write(char addr, char value){

i2c_start();i2c_write(RTC_WRITE_COMMAND);i2c_write(addr);i2c_write(value);i2c_stop();

}unsigned char DS1307_read(char addr){

unsigned char value;

i2c_start();i2c_write(RTC_WRITE_COMMAND);i2c_write(addr);i2c_start();i2c_write(RTC_READ_COMMAND);value = i2c_read(0);i2c_stop();return value;

}*/Chúc các bác thành công.Thân ái.

-----------------------