Giới thiệu về giao tiếp I2C:

- I2C là một loại bus giao tiếp ngoại vi “Inter-Integrated Circuit” được phát triển bới hãng Philips, thường được sử dụng để giao tiếp 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.

- I2C là giao tiếp nối tiếp đồng bộ thực hiện trên hai đường dây : Clock (SCL) và Data  (SDA). Các bít dữ liệu được truyền từng bit một theo các khoảng thời gian được thiết lập bởi một tín hiệu CLOCK.

giaotiep_i2cỨng dụng của giao tiếp I2C :

Do tính đơn giản của nó giao tiếp này được ứng dụng rất nhiều trong thực tế như trong IC DS1307 (Real time Clock), trong hiển thị LCD, giao tiếp với EEPROM hay các cảm biến nhiệt độ, áp suất...ADC,DAC, trong các thiết bị IoT. 

Master và Slave :

Master :

 Giữ vai trò điều khiển các tín hiệu đường truyền (Bus I2C).

- Tạo xung Clock trong suốt quá trình giao tiếp với Slave.

- Phát các tín hiệu Start bắt đầu cho quá trình truy xuất.

- Gửi địa chỉ của thiết bị Slave cần truy xuất.

- Phát tín hiệu đọc hoặc ghi (R/W) tới Slave.

- Truyền dữ liệu tới thiết bị Slave.

- Nhận dữ liệu từ Slave gửi lên.

- Tạo tín hiệu Not-ACK khi kết thúc nhận dữ liệu 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ỉ  trả lời khi đúng địa chỉ của Slave .

- Nhận dữ liệu từ Master gửi tới đồng thời gửi bit ACK sau mỗi 8 Clock.

- Truyền dữ liệu tới Master, chờ tín hiệu ACK từ Master để tiếp tục gửi đi.

- Thực hiện chức năng chuyên dụng của Slave ( EEPROM, Real Time Clock, Driver LCD, IoTs...).

Các tín hiệu bắt đầu (Start) và kết thúc (Stop) trong đường truyền :

 SDA=SCL=1 (high level) : Bus I2C ở trạng thái sẵn sàng cho một giao tiếp.

Điều kiện START:  Tín hiệu  SDA  tác động cạnh xuống và  SCL =1 (high level).

Điều kiện STOP:     SDA  tác động cạnh lên  và SCL =1 (high level).

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 truyền nhận dữ liệu kế tiếp. 

I2C_Bus

- Dữ liệu được truyền trên bus I2C theo từng bit tại mỗi sườn lên của xung Clock  (SCL) quá trình 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 với một bit ACK, tại xung clock thứ 9 trên báo hiệu Slave chỉ định đã nhận đủ 8 bit. Thiết bị truyền khi nhận được bít ACK sẽ tiếp tục thực hiện quá trình truyền hoặc kết thúc.

I2C_Bus

Địa chỉ thiết bị:

Mỗi thiết bị tham gia vào giao tiếp này đều có một địa chỉ duy nhất, có độ dài là 7 bit như vậy sẽ có  2^7=128 thiết bị. Byte đầu tiên được gửi  sẽ bao gồm 7 bit địa chỉ và một bít thứ 8 điều khiển hướng truyền R/W. 

Bit R/W = 0 (low level) : Ghi dữ liệu từ Master vào Slave.

Bit R/W = 1 (high level) : Đọc dữ liệu từ Slave gửi cho Master .

I2C_Bus

Cơ chế truyền dữ liệu tới Slave:

‐  Ban đầu Master sẽ tạo xung START để chuẩn bị đường truyền

‐  Sau đó Master sẽ gửi địa chỉ của thiết bị (Slave) cùng với bit  R/W= 0 ( Ghi dữ liệu)

‐  Khi nhận được xung ACK báo đã nhận đúng thiết bị Slave, Master bắt đầu gửi dữ 

    liệu đến Slave theo từng byte một. Theo sau mỗi byte này đều là một xung ACK. 

‐ Master sau khi truyền byte cuối sẽ tạo xung STOP báo hiệu kết  thúc quá trình truyền. 

I2C_Bus

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à: 

-  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ỉ 

I2C_Bus

Chương trình  sử dụng CCS:

Cấu hình :

 - 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 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

- 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.

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

 - Chương trình chính sống trong vòng lặp While

while (TRUE) {

Nội dung chương trình         

   }

Ghi dữ liệu tới Slave :

 i2c_start();                 // Master tạo tín hiệu Start

 i2c_write(0xa0);       // Gửi địa chỉ bít hướng R/W = 0 (Mode write) đến Slave

 i2c_write(data);      // Gửi dữ liệu byte thứ nhất

 i2c_write(data1);   // Gửi dữ liệu byte thứ hai

………………….

 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 Start

 i2c_write(0xa1);             //  Gửi địa chỉ bít hướng R/W = 1 (Mode read) đến Slave

 buf = i2c_read();           // Đọc dữ liệu byte thứ nhất

 buf2= 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-ACK

 i2c_stop();                 // Master tạo tín hiệu Stop

Chương trình cho Slave:

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

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ỉ bit R/W

   . State = 1-0x7F: Đọc dữ liệu từ Master bằng lệnh i2c_read()

   . State = 0x80: Đọc địa chỉ 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 == 0x80i2c_write(data);

if(state == 0x81) i2c_write(data1);

…………………………………………

if(state == 0x8n) i2c_write(datan);

Trong bài tập này các bạn mở Proteus vẽ mạch như hình bên dưới :

I2C_Bus

Chúng ta sẽ thực hiện giao tiếp I2C với 2 PIC 16F876A một con làm Master và một con làm Slave. Master sẽ gửi 3 bytes gồm 1 byte địa chỉ 0xa0 và 2 byte data command 0x01 và 0x02. Sau delay 200ms Master phát ra lệnh đọc dữ liệu từ Slave. Slave có nhiệm vụ đọc 3 byte này, nếu đúng như giá trị Master đã gửi (0xa0,0x01,0x02) thì bắt đầu thực hiện gửi 2 byte dữ liệu.

Byte thứ nhất là giá trị đọc được từ ADC 8 bit kênh AN0, byte thứ 2 đơn giản là chỉ đếm lên từ 0--100. Master sẽ nhận 2 byte này và xuất ra LCD. Các bạn có thể tham khảo bài :  Hiển thị LCD.

Download bài học Giao tiếp I2C hướng dẫn học lập trình vi điều khiển PIC

Xem video mô phỏng bài học

Nếu bạn thấy hay hãy đăng ký , chia sẻ giúp tôi nhé chúng tôi sẽ thường xuyên cập nhật kiến thức mới về vi điều khiển PIC tại đây