Giao tiếp I2C với nhiều module

I. I2C LÀ GÌ?

Tất nhiên là đã có bài viết giới thiệu về I2C trên Arduino.vn Tuy nhiên bài viết này mình xin nói về 1 hướng khác. Các bạn quan tâm có thể xem lại những bài viết về I2C của các tác giả khác nhé!

Đầu năm 1980 Phillips đã phát triển một chuẩn giao tiếp nối tiếp 2 dây được gọi là I2C. I2C là tên viết tắt của cụm từ Inter-Intergrated Circuit. Đây là đường Bus giao tiếp giữa các IC với nhau. I2C mặc dù được phát triển bới Philips, nhưng nó đã được rất nhiều nhà sản xuất IC trên thế giới sử dụng. I2C trở thành một chuẩn công nghiệp cho các giao tiếp điều khiển, có thể kể ra đây một vài tên tuổi ngoài Philips như: Texas Intrument(TI), MaximDallas, analog Device, National Semiconductor ... Bus I2C được sử dụng làm bus giao tiếp ngoại vi cho rất nhiều loại IC khác nhau như các loại Vi điều khiển 8051, PIC, AVR, ARM... chip nhớ như: RAM tĩnh (Static Ram), EEPROM, bộ chuyển đổi tương tự số (ADC), số tương tự(DAC), IC điểu khiển LCD, LED...

Cấu tạo và nguyên lý hoạt động

I2C sử dụng hai đường truyền tín hiệu:

  • Một đường xung nhịp đồng hồ(SCL) chỉ do Master phát đi ( thông thường ở 100kHz và 400kHz. Mức cao nhất là 1Mhz và 3.4MHz).
  • Một đường dữ liệu(SDA) theo 2 hướng.

Có rất nhiều thiết bị có thể cùng được kết nối vào một bus I2C, tuy nhiên sẽ không xảy ra chuyện nhầm lẫn giữa các thiết bị, bởi mỗi thiết bị sẽ được nhận ra bởỉ một địa chỉ duy nhất với một quan hệ chủ/tớ tồn tại trong suốt thời gian kết nối. Mỗi thiết bị có thể hoạt động như là thiết bị nhận hoặc truyền dữ liệu hay có thể vừa truyền vừa nhận. Hoạt động truyền hay nhận còn tùy thuộc vào việc thiết bị đó là chủ (master) hãy tớ (slave).

Một thiết bị hay một IC khi kết nối với bus I2C, ngoài một địa chỉ (duy nhất) để phân biệt, nó còn được cấu hình là thiết bị chủ hay tớ.Tại sao lại có sự phân biệt này ? Đó là vì trên một bus I2C thì quyền điều khiển thuộc về thiết bị chủ. Thiết bị chủ nắm vai trò tạo xung đồng hồ cho toàn hệ thống, khi giữa hai thiết bị chủ-tớ giao tiếp thì thiết bị chủ có nhiệm vụ tạo xung đồng hồ và quản lý địa chỉ của thiết bị tớ trong suốt quá trình giao tiếp. Thiết bị chủ giữ vai trò chủ động, còn thiết bị tớ giữ vai trò bị động trong việc giao tiếp.

Về lý thuyết lẫn thực tế I²C sử dụng 7 bit để định địa chỉ, do đó trên một bus có thể có tới 2^7 địa chỉ tương ứng với 128 thiết bị có thể kết nối, nhưng chỉ có 112 , 16 địa chỉ còn lại được sử dụng vào mục đích riêng. Bit còn lại quy định việc đọc hay ghi dữ liệu (1 là write, 0 là read)

Điểm mạnh của I²C chính là hiệu suất và sự đơn giản của nó: một khối điều khiển trung tâm có thể điều khiển cả một mạng thiết bị mà chỉ cần hai lối ra điều khiển.

Ngoài ra I2C còn có chế độ 10bit địa chỉ tương đương với 1024 địa chỉ, tương tự như 7 bit, chỉ có 1008 thiết bị có thể kết nối, còn lại 16 địa chỉ sẽ dùng để sử dụng mục đích riêng (Mình chưa rõ lắm)

Vậy, làm thế nào để nó có thể giao tiếp với nhiều thiết bị?

II. LÀM THẾ NÀO?

Vâng, như mình đã nói ở trên, Mỗi thiết bị có 1 địa chỉ được cài sẵn hoặc 1 địa chỉ thiết bị duy nhất để thiết bị chủ (Master) có thể giao tiếp. 2 chân SDA VÀ SCL là 2 chân của giao tiếp I2C, trong đó chân SCL là chân Clock, có tác dụng đồng bộ hóa việc truyền dữ liệu giữa các thiết bị, và việc tạo ra xung clock đó là do thiết bị chủ (Master). Chân còn lại là chân SDA là chân truyền dữ liệu (DATA). 2 chân này luôn hoạt động ở chế độ mở, vì vậy để sử dụng được cần phải có trở kéo. tức là nối +5v => trở => I2C bởi các thiết bị trên bus i2c hoạt động ở mức thấp. Giá trị thường được sử dụng cho các điện trở là từ 2K cho tốc độ vào khoảng 400 kbps, và 10K cho tốc độ thấp hơn khoảng 100 kbps.

Hãy tưởng tượng bạn là nhân viên phát bưu phẩm, đến 1 khu phố trên tay có bưu phẩm cần chuyển phát. Tính năng của bạn có thể phát và nhận bưu phẩm để chuyển đi cho cả khu phố, tuy nhiên, yếu tố quan trọng là bạn cần phải có địa chỉ. mỗi 1 thiết bị sẽ có 1 địa chỉ riêng để thiết bị chủ có thể truy cập để lấy dữ liệu 

Hải Đăng PPK

Ví dụ cảm biến gia tốc ADXL345

Có 1 địa chỉ duy nhất cho riêng module và đồng thời bổ xung bên trong có 3 địa chỉ riêng biệt cho các trục X,Y,Z. Nếu chúng ta cần đọc các dữ liệu từ trục X, trước hết chúng ta cần đến địa chỉ chính của Module, sau đó mới đến địa chỉ của trục X. Việc tìm tên các địa chỉ này các bạn cần tìm trong datasheet của linh kiện. Đây là datasheet của cảm biến gia tốc ADXL345 Mở ra đọc tại mục Register map các bạn có thể thấy địa chỉ của trục X là 0x32 và 0x33

0x32 DATAX0  X-Axis Data 0

0x33  DATAX1  X-Axis Data 1

Trong ví dụ dưới đây mình sẽ đưa ra 1 trường hợp về việc kết nối arduino với 2 cảm biến (Cảm biến gia tốc 10 bậc tự do GY - 80 và Cảm biến gia tốc 6 bậc tự do GY521), cả 2 cảm biến đều sử dụng giao tiếp I2C

Cảm biến gia tốc GY-521 

Cảm biến gia tốc GY-80

Dưới đây là cách kết nối 2 module với arduino, các bạn có thể thấy hơi mâu thuẫn so với ban đầu mình nói, tại sao không có điện trở nào kéo ở đây? Thực tế thì bên trong module đã có điện trở kéo, vì thế chúng ta không cần kéo gì nữa cả laugh

Bây giờ để giao tiếp với chúng, ta cần phải biết được địa chỉ của nó là gì, đối với mỗi 1 cảm biến trên 1 module kia sẽ có 1 địa chỉ riêng. Các bạn cần phải tìm chúng trong datasheet của mỗi loại. Ở đây mình đưa ra các địa chỉ của các cảm biến để các bạn tiện theo dõi

Đối với GY-80, có 4 địa chỉ: 0x53  cho cảm biến gia tốc,  0x69 cho 3 trục con quay,  0x1E  3 trục Từ trường và 0x77 cho cảm biến áp lực và nhiệt kế  

Đối với GY-521, chỉ có một địa chỉ và đó là  0x68. Các bạn cũng có thể kiểm tra và xác định được địa chỉ của chúng bằng cách sử dụng bản sketch mẫu I2C Scanner . Nạp code đó vào và nó sẽ tự cho các bạn biết các địa chỉ có trên thiết bị. 

Sau khi đã tìm thấy địa chỉ của các thiết bị, chúng ta cũng cần phải tìm ra địa chỉ của các thanh ghi bên trong của chúng để đọc dữ liệu. Ví dụ, nếu chúng ta muốn đọc dữ liệu cho trục X từ các cảm biến 3 trục gia tốc của GY-80, chúng ta cần phải tìm địa chỉ nơi dữ liệu của trục X được lưu trữ. Bằng cách đọc datasheet của các cảm biến của GY-80 và ở đây là datasheet của cảm biến 3 trục gia tốc ADXL345 mà mình đã nêu ở phần đầu bài viết, chúng ta có thể thấy rằng dữ liệu cho trục X được lưu trữ trong hai thanh ghi, DATAX0 với một địa chỉ 0x32  và DATAX1 với một địa chỉ 0x33.

Bây giờ chúng ta chỉ việc code và hãy nghiên cứu xem code đã làm những gì?

#include <Wire.h>
int ADXLAddress = 0x53; // Địa chỉ của cảm biến gia tốc trong module GY80
#define X_Axis_Register_DATAX0 0x32 // địa chỉ của data0 trục X trong cảm biến gia tốc ADXL345 trong module GY-80
#define X_Axis_Register_DATAX1 0x33 //địa chỉ của data1 trục X trong cảm biến gia tốc ADXL345 trong module GY-80
#define Power_Register 0x2D // thanh ghi điều khiển năng lượng cung cấp
int X0,X1,X_out;
void setup() {
  Wire.begin(); // Khởi tạo thư viện WIRE
  Serial.begin(9600);
  delay(100);
  // kích hoạt tính năng đo lường
  Wire.beginTransmission(ADXLAddress);//bắt đầu việc truyền tải yêu cầu tới các cảm biến
  Wire.write(Power_Register);
  // Cho phép đo
  Wire.write(8);  
  Wire.endTransmission();
}
void loop() {
  Wire.beginTransmission(ADXLAddress); // Bắt đầu truyền đến cảm biếnr 
  //Thu thập dữ liệu từ các thanh ghi
  Wire.write(X_Axis_Register_DATAX0);
  Wire.write(X_Axis_Register_DATAX1);
  
  Wire.endTransmission(); // Kết thúc việc truyền dữ liệu từ 2 thanh ghi
  Wire.requestFrom(ADXLAddress,2); // Yêu cầu truyền 2 byte từ 2 thanh ghi
  
  if(Wire.available()<=2) {  // 
    X0 = Wire.read(); // Đọc dữ liệu từ thanh ghi
    X1 = Wire.read();   
  }
  
  Serial.print("X0= ");
  Serial.print(X0);
  Serial.print("   X1= ");
  Serial.println(X1);
}

 

Việc lấy dữ liệu từ các cảm biến trên module khác cũng tương tự. Như vậy các bạn đã biết cách sử dụng giao tiếp I2C. Và rõ ràng 2 module này có vẻ không được nhiều bạn quan tâm sử dụng lắm bởi giá thành cũng như là về vấn đề các bạn cần ứng dụng đến không nhiều. Vì thế mình xin tiếp tục đưa ra ví dụ thứ 2 mà khá nhiều bạn còn băn khoăn. Đó là việc sử dụng LCD 1602 module i2c và module thời gian thực DS1307 với arduino uno.

 

Trước khi đọc tiếp phần này, mình lại 1 lần nữa mong muốn các bạn đọc datasheet của linh kiện.

Ở đây, đối với LCD1602 dùng module I2C. địa chỉ tùy thuộc vào jump các bạn cắm trên module i2c. Các bạn có thể đọc datasheet của LCD này tại đây  Mình lấy ví dụ địa chỉ OPEN là 0x27 Hoặc nó có thể thay đổi từ 0X20 - 0X27

Với module thời gian thực DS1307 các bạn có thể đọc datasheet tại đây. Địa chỉ của module DS1307 là 0x68. Làm thế nào để biết nó là 0X68 thì các bạn hãy đọc lại phần đầu bài viết, phần I2C SCANNER.. Bên trong DS1307 sẽ có các địa chỉ để đọc các giá trị giờ, phút, giây...

Như vậy để kết nối 2 module cùng dùng i2c các bạn chỉ cần gọi địa chỉ của LCD là 0x27, địa chỉ của DS1307 là 0X68 là có thể sử dụng đồng thời 2 module trên cùng 2 chân SDA, SCL của Arduino

#include <Wire.h>
#include "RTClib.h"
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x20,16,2); // 0x27 là địa chỉ của lcd 16x2
RTC_DS1307 RTC;

void setup () {
   lcd.init(); 
  lcd.backlight(); //đèn nền bật
  // cài đặt số cột và số dòng 
  lcd.begin(16, 2);
  // in logo lên màn hình
  lcd.print("www.arduino.vn");  
  lcd.setCursor(0, 1);
  lcd.print("haidangppk");
  delay (2500);
  lcd.clear();
    
   // Serial.begin(9600);
    Wire.begin();
Wire.beginTransmission(0x68);// địa chỉ của ds1307
Wire.write(0x07); // 
Wire.write(0x10); // 
Wire.endTransmission();

    RTC.begin();
  if (! RTC.isrunning()) {
    Serial.println("RTC is NOT running!");
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }
}

void loop () {
   DateTime now = RTC.now();
   lcd.setCursor(6, 0);
   lcd.print(now.hour(), DEC);
   lcd.print(":");
   lcd.print(now.minute(), DEC);
   lcd.print(":");
   lcd.print(now.second(), DEC);
   lcd.print(" "); 
    
   lcd.setCursor(5, 1);
   lcd.print(now.day(), DEC);
   lcd.print("/");
   lcd.print(now.month(), DEC);
   lcd.print("/");
   lcd.print(now.year(), DEC);
   lcd.print(""); 
  
   delay(1000);
}

Bài viết của mình có thể có sai sót, mong các bạn góp ý. Chúc các bạn thành công!

Trần Hải Đăng

lên
24 thành viên đã đánh giá bài viết này hữu ích.
Các dự án được truyền cảm hứng

Select any filter and click on Apply to see results

Các bài viết cùng tác giả

Module âm thanh WTV020 - Module âm thanh kết hợp với thẻ nhớ micro SD - Một giải pháp thông minh cho túi tiền của bạn

Bài viết này mình xin giới thiệu tới các bạn 1 loại module âm thanh khá dễ sử dụng tuy nhiên lại ít phổ biến trong cộng đồng Arduino VN. Đó là Module WTV-020. Bạn đã từng xem những con robot có thể phát ra tiếng nói và thậm chí xử lý được các tình huống và phát ra tiếng nói theo từng trường hợp. Ngoài việc xử dụng modul Micro SD Card ra, Module WTV-020 là 1 sự lựa chọn đáng để các bạn quan tâm khi các bạn có ý định khởi động 1 dự án có liên quan đến âm thanh.

P/S: mình đã hoàn thành được 1 dự án có sử dụng module này và khá hài lòng về kết quả mà nó mang lại cho mình (kể cả về kinh tế) heart

Đây là loại module có thể sử dụng trong các hệ thống định vị GPS, hệ thống nhà thông minh, các thiết bị y tế, thiết bị gia dụng (bếp, nồi cơm điện, lò vi sóng), máy chơi game, các thiết bị học tập và các công cụ như "sách nói", các dạng phương tiện giao thông thông minh (trạm thu phí, bãi đậu xe), thiết bị thông tin liên lạc (điện thoại), công nghiệp kiểm soát (thang máy), đồ chơi có phát ra âm thanh..v..v..

lên
17 thành viên đã đánh giá bài viết này hữu ích.

LCD Graphic 128x64 dòng KS0108 VÀ ST7920 - Viết, vẽ và làm mọi thứ với LCD

Graphic LCD (gọi tắt là GLCD) loại chấm không màu là các loại màn hình tinh thể lỏng nhỏ dùng để hiển thị chữ, số hoặc hình ảnh. Khác với Text LCD, GLCD không được chia thành các ô để hiển thị các mã ASCII vì GLCD không có bộ nhớ CGRAM (Character Generation RAM). GLCD 128x64 có 128 cột và 64 hàng tương ứng có 128x64=8192 chấm (dot). Mỗi chấm tương ứng với 1 bit dữ liệu, và như thế cần 8192 bits hay 1024 bytes RAM để chứa dữ liệu hiển thị đầy mỗi 128x64 GLCD. Tùy theo loại chip điều khiển, nguyên lý hoạt động của GLCD có thể khác nhau, trong bài này tôi giới thiệu loại GLCD được điều khiển bởi chip KS0108 của Samsung, có thể nói GLCD với KS0108 là phổ biến nhất trong các loại GLCD loại này (chấm, không màu). Hình 1 là hình ảnh thật của 1 GLCD 128x64 điều khiển bởi KS0108.

lên
18 thành viên đã đánh giá bài viết này hữu ích.