Hải Đăng PPK gửi vào
- 156193 lượt xem
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ả
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