- Giao thức truyền thông I2C là gì?
- I2C Communication hoạt động như thế nào?
- Sử dụng giao tiếp I2C ở đâu?
- I2C trong Arduino
- Thành phần bắt buộc
- Sơ đồ mạch
- Giải thích làm việc
- Lập trình I2C trong Arduino
- Giải thích lập trình Arduino thành thạo
- Giải thích lập trình Slave Arduino
Trong hướng dẫn trước của chúng tôi đã tìm hiểu về giao tiếp SPI trong Arduino. Hôm nay chúng ta sẽ tìm hiểu về một Giao thức truyền thông nối tiếp khác: I2C (Inter Integrated Circuits). So sánh I2C với SPI, I2C chỉ có hai dây trong khi SPI sử dụng bốn và I2C có thể có nhiều Master và Slave, trong khi SPI chỉ có thể có một chủ và nhiều nô lệ. Vì vậy, có nhiều hơn một vi điều khiển trong một dự án cần phải là bậc thầy thì I2C mới được sử dụng. Giao tiếp I2C thường được sử dụng để giao tiếp với Con quay hồi chuyển, gia tốc kế, cảm biến áp suất khí quyển, màn hình LED, v.v.
Trong hướng dẫn Arduino I2C này, chúng ta sẽ sử dụng giao tiếp I2C giữa hai bo mạch arduino và gửi (0 đến 127) giá trị cho nhau bằng cách sử dụng chiết áp. Các giá trị sẽ được hiển thị trên màn hình LCD 16x2 được kết nối với mỗi Arduino. Ở đây một Arduino sẽ đóng vai trò là Master và một Arduino khác sẽ đóng vai trò là Slave. Vì vậy, chúng ta hãy bắt đầu với phần giới thiệu về giao tiếp I2C.
Giao thức truyền thông I2C là gì?
Thuật ngữ IIC là viết tắt của “ Inter Integrated Circuits ”. Nó thường được ký hiệu là I2C hoặc I bình phương C hoặc thậm chí là giao thức giao diện 2 dây (TWI) ở một số nơi nhưng tất cả đều có nghĩa giống nhau. I2C là một giao thức truyền thông đồng bộ có nghĩa là cả hai thiết bị đang chia sẻ thông tin phải chia sẻ một tín hiệu đồng hồ chung. Nó chỉ có hai dây để chia sẻ thông tin, trong đó một dây được sử dụng cho tín hiệu vòi và dây kia được sử dụng để gửi và nhận dữ liệu.
I2C Communication hoạt động như thế nào?
Giao tiếp I2C lần đầu tiên được giới thiệu bởi Phillips. Như đã nói trước đó, nó có hai dây, hai dây này sẽ được kết nối trên hai thiết bị. Ở đây một thiết bị được gọi là chủ và thiết bị còn lại được gọi là tớ. Giao tiếp nên và sẽ luôn xảy ra giữa hai Master và một Slave. Ưu điểm của giao tiếp I2C là có thể kết nối nhiều hơn một nô lệ với một Master.
Giao tiếp hoàn chỉnh diễn ra thông qua hai dây này là Đồng hồ nối tiếp (SCL) và Dữ liệu nối tiếp (SDA).
Đồng hồ nối tiếp (SCL): Chia sẻ tín hiệu đồng hồ được tạo ra bởi chính với phụ
Dữ liệu nối tiếp (SDA): Gửi dữ liệu đến và đi giữa Master và slave.
Tại bất kỳ thời điểm nào, chỉ có chủ mới có thể bắt đầu giao tiếp. Vì có nhiều hơn một slave trong bus, master phải tham chiếu đến mỗi slave bằng cách sử dụng một địa chỉ khác nhau. Khi được giải quyết, chỉ nô lệ có địa chỉ cụ thể đó sẽ trả lời lại thông tin trong khi những người khác tiếp tục thoát. Bằng cách này, chúng ta có thể sử dụng cùng một bus để giao tiếp với nhiều thiết bị.
Các mức điện áp của I2C không được xác định trước. Giao tiếp I2C linh hoạt, có nghĩa là thiết bị được cấp nguồn bằng điện áp 5v, có thể sử dụng 5v cho I2C và thiết bị 3.3v có thể sử dụng 3v cho giao tiếp I2C. Nhưng điều gì sẽ xảy ra nếu hai thiết bị đang chạy trên các điện áp khác nhau, cần giao tiếp bằng I2C? Một 5V I2C xe buýt không thể được kết nối với 3.3V thiết bị. Trong trường hợp này, các bộ dịch chuyển điện áp được sử dụng để phù hợp với các mức điện áp giữa hai bus I2C.
Có một số tập hợp các điều kiện định khung giao dịch. Khởi tạo quá trình truyền bắt đầu với một cạnh xuống của SDA, được định nghĩa là điều kiện 'BẮT ĐẦU' trong sơ đồ dưới đây, nơi cái chính để SCL ở mức cao trong khi đặt SDA ở mức thấp.
Như thể hiện trong sơ đồ bên dưới, Cạnh rơi của SDA là phần cứng kích hoạt điều kiện START. Sau đó, tất cả các thiết bị trên cùng một bus sẽ chuyển sang chế độ nghe.
Theo cách tương tự, cạnh lên của SDA dừng quá trình truyền được hiển thị dưới dạng điều kiện 'DỪNG' trong sơ đồ trên, trong đó thiết bị chính để SCL ở mức cao và cũng giải phóng SDA để ở mức CAO. Vì vậy, cạnh lên của SDA dừng quá trình truyền.
Bit R / W cho biết hướng truyền của các byte tiếp theo, nếu nó là CAO có nghĩa là phụ sẽ truyền và nếu nó thấp có nghĩa là chủ sẽ truyền.
Mỗi bit được truyền trên mỗi chu kỳ đồng hồ, vì vậy cần 8 chu kỳ đồng hồ để truyền một byte. Sau mỗi byte được gửi hoặc nhận, chu kỳ đồng hồ thứ chín được giữ cho ACK / NACK (được thừa nhận / không được thừa nhận). Bit ACK này được tạo ra bởi slave hoặc master tùy thuộc vào tình huống. Đối với bit ACK, SDA được đặt thành mức thấp bởi chính hoặc phụ ở chu kỳ đồng hồ thứ 9. Vì vậy, nó thấp nó được coi là ACK nếu không NACK.
Sử dụng giao tiếp I2C ở đâu?
Giao tiếp I2C chỉ được sử dụng cho giao tiếp khoảng cách ngắn. Nó chắc chắn đáng tin cậy ở một mức độ nào đó vì nó có xung đồng hồ được đồng bộ hóa để làm cho nó trở nên thông minh. Giao thức này chủ yếu được sử dụng để giao tiếp với cảm biến hoặc các thiết bị khác phải gửi thông tin đến chủ. Nó rất tiện dụng khi một bộ vi điều khiển phải giao tiếp với nhiều mô-đun phụ khác bằng cách sử dụng tối thiểu chỉ dây. Nếu bạn đang tìm kiếm một giao tiếp tầm xa, bạn nên thử RS232 và nếu bạn đang tìm kiếm một giao tiếp đáng tin cậy hơn, bạn nên thử giao thức SPI.
I2C trong Arduino
Hình ảnh bên dưới cho thấy các chân I2C có trong Arduino UNO.
Dòng I2C | Ghim trong Arduino |
SDA | A4 |
SCL | A5 |
Trước khi bắt đầu lập trình I2C bằng hai Arduino. Chúng ta cần tìm hiểu về thư viện Wire được sử dụng trong Arduino IDE.
các thư viện
1. Wire.begin (địa chỉ):
Sử dụng: Thư viện này được sử dụng để thực hiện giao tiếp với các thiết bị I2C. Điều này Khởi tạo thư viện Wire và tham gia bus I2C với tư cách là chủ hoặc tớ.
Địa chỉ: Địa chỉ nô lệ 7-bit là tùy chọn và nếu địa chỉ không được chỉ định, nó sẽ tham gia bus với tư cách là địa chỉ chính như thế này.
2. Wire.read ():
Sử dụng: Hàm này được sử dụng để đọc một byte đã được nhận từ thiết bị chính hoặc thiết bị phụ, được truyền từ thiết bị phụ đến thiết bị chính sau một cuộc gọi tới requestFrom () hoặc được truyền từ thiết bị chính đến thiết bị phụ.
3. Wire.write ():
Sử dụng: Chức năng này được sử dụng để ghi dữ liệu vào thiết bị phụ hoặc thiết bị chính.
Slave to Master: Slave ghi dữ liệu vào một master khi Wire.RequestFrom () được sử dụng trong master.
Master to Slave: Để truyền từ thiết bị chính đến thiết bị phụ, Wire.write () được sử dụng giữa các lệnh gọi tới Wire.beginTransmission () và Wire.endTransmission ().
Wire.write () có thể được viết thành:
- Wire.write (giá trị)
value: một giá trị để gửi dưới dạng byte đơn.
- Wire.write (chuỗi):
string: một chuỗi để gửi dưới dạng một chuỗi byte.
- Wire.write (dữ liệu, độ dài):
data: một mảng dữ liệu để gửi dưới dạng byte
length: số byte cần truyền.
4. Wire.beginTransmission (địa chỉ):
Sử dụng: Chức năng này được sử dụng để bắt đầu truyền đến thiết bị I2C với địa chỉ phụ đã cho. Sau đó, xây dựng hàng đợi byte để truyền bằng hàm write () và sau đó truyền chúng bằng cách gọi hàm endTransmission () . 7-bit địa chỉ của thiết bị được truyền đi.
5. Wire.endTransmission ();
Sử dụng: Hàm này được sử dụng để kết thúc quá trình truyền tới thiết bị phụ đã được bắt đầu bởi beginTransmission () và truyền các byte đã được xếp hàng bởi Wire.write ().
6. Wire.onRequest ();
Sử dụng: Hàm này được gọi khi một chủ yêu cầu dữ liệu bằng Wire.requestFrom () từ thiết bị phụ. Ở đây chúng ta có thể đưa vào hàm Wire.write () để gửi dữ liệu đến master.
7. Wire.onReceive ();Sử dụng: Hàm này được gọi khi thiết bị phụ nhận được dữ liệu từ một thiết bị chính. Ở đây chúng ta có thể bao gồm Wire.read (); chức năng đọc dữ liệu được gửi từ master.
8. Wire.requestFrom (địa chỉ, số lượng);
Sử dụng: Hàm này được sử dụng trong master để yêu cầu byte từ thiết bị phụ. Hàm Wire.read () được sử dụng để đọc dữ liệu được gửi từ thiết bị phụ.
địa chỉ: địa chỉ 7 bit của thiết bị để yêu cầu byte từ
số lượng: số byte để yêu cầu
Thành phần bắt buộc
- Arduino Uno (2-Nos)
- Mô-đun màn hình LCD 16X2
- Chiết áp 10K (4-Nos)
- Breadboard
- Kết nối dây
Sơ đồ mạch
Giải thích làm việc
Ở đây để thể hiện giao tiếp I2C trong Arduino, chúng tôi sử dụng Hai Arduino UNO với Hai màn hình LCD 16X2 được gắn vào nhau và sử dụng hai chiết áp ở cả hai arduino để xác định các giá trị gửi (0 đến 127) từ chủ đến tớ và tớ thành tớ bằng cách thay đổi chiết áp.
Chúng tôi lấy giá trị analog đầu vào tại chân A0 của arduino từ (0 đến 5V) bằng cách sử dụng chiết áp và chuyển chúng thành giá trị Analog sang Digital (0 đến 1023). Sau đó, các giá trị ADC này tiếp tục được chuyển đổi thành (0 đến 127) vì chúng ta chỉ có thể gửi dữ liệu 7-bit thông qua giao tiếp I2C. Giao tiếp I2C diễn ra thông qua hai dây ở chân A4 & A5 của cả hai arduino.
Các giá trị trên màn hình LCD của Slave Arduino sẽ được thay đổi bằng cách thay đổi POT ở phía chính và ngược lại.
Lập trình I2C trong Arduino
Hướng dẫn này có hai chương trình một cho Arduino chính và một cho Arduino nô lệ. Các chương trình hoàn chỉnh cho cả hai bên được đưa ra ở cuối dự án này với một Video trình diễn.
Giải thích lập trình Arduino thành thạo
1. Trước hết chúng ta cần bao gồm thư viện Wire để sử dụng các chức năng giao tiếp I2C và thư viện LCD để sử dụng các chức năng LCD. Cũng xác định các chân LCD cho LCD 16x2. Tìm hiểu thêm về giao diện LCD với Arduino tại đây.
#include
2. Trong thiết lập void ()
- Chúng tôi bắt đầu giao tiếp nối tiếp với tốc độ truyền 9600.
Serial.begin (9600);
- Tiếp theo, chúng tôi bắt đầu giao tiếp I2C tại chân (A4, A5)
Wire.begin (); // Bắt đầu giao tiếp I2C tại chân (A4, A5)
- Tiếp theo, chúng tôi khởi tạo mô-đun màn hình LCD ở chế độ 16X2 và hiển thị thông báo chào mừng và xóa sau năm giây.
lcd.begin (16,2); // Khởi tạo màn hình LCD lcd.setCursor (0,0); // Đặt Con trỏ ở dòng đầu tiên của Display lcd.print ("Thông báo về mạch"); // In SỐ MẠCH trong LCD lcd.setCursor (0,1); // Đặt Con trỏ ở dòng thứ hai của Display lcd.print ("I2C 2 ARDUINO"); // In I2C ARDUINO ở LCD delay (5000); // Trì hoãn 5 giây lcd.clear (); // Xóa màn hình LCD
3. Trong vòng lặp void ()
- Đầu tiên, chúng ta cần lấy dữ liệu từ Slave, vì vậy chúng ta sử dụng requestFrom () với địa chỉ slave 8 và chúng ta yêu cầu một byte
Wire.requestFrom (8,1);
Giá trị nhận được được đọc bằng Wire.read ()
byte MasterReceive = Wire.read ();
- Tiếp theo chúng ta cần đọc giá trị analog từ POT của arduino chính gắn vào chân A0
int potvalue = analogRead (A0);
Chúng tôi chuyển đổi giá trị đó theo một byte là 0 thành 127.
byte MasterSend = map (potvalue, 0,1023,0,127);
- Tiếp theo, chúng ta cần gửi các giá trị đã chuyển đổi đó để chúng ta bắt đầu truyền với arduino nô lệ với địa chỉ 8
Wire.beginTransmission (8); Wire.write (MasterSend); Wire.endTransmission ();
- Tiếp theo, chúng tôi hiển thị các giá trị nhận được từ arduino nô lệ với độ trễ là 500 micro giây và chúng tôi liên tục nhận và hiển thị các giá trị đó.
lcd.setCursor (0,0); // Đặt Currsor tại dòng một của LCD lcd.print (">> Master <<"); // In >> Master << tại LCD lcd.setCursor (0,1); // Đặt Con trỏ ở dòng hai của LCD lcd.print ("SlaveVal:"); // In SlaveVal: trong LCD lcd.print (MasterReceive); // In MasterReceive trong LCD nhận được từ Slave Serial.println ("Master Nhận từ Slave"); // In trong Serial Monitor Serial.println (MasterReceive); chậm trễ (500); lcd.clear ();
Giải thích lập trình Slave Arduino
1. Tương tự như master, trước hết chúng ta cần bao gồm thư viện Wire để sử dụng các chức năng giao tiếp I2C và thư viện LCD để sử dụng các chức năng LCD. Cũng xác định các chân LCD cho LCD 16x2.
#include
2. Trong thiết lập void ()
- Chúng tôi bắt đầu giao tiếp nối tiếp với tốc độ truyền 9600.
Serial.begin (9600);
- Tiếp theo, chúng ta bắt đầu giao tiếp I2C tại chân (A4, A5) với địa chỉ phụ là 8. Ở đây, điều quan trọng là chỉ định địa chỉ phụ.
Wire.begin (8);
Tiếp theo chúng ta cần gọi hàm khi Slave nhận giá trị từ master và khi Master yêu cầu giá trị từ Slave
Wire.onReceive (nhậnEvent); Wire.onRequest (requestEvent);
- Tiếp theo, chúng tôi khởi tạo mô-đun màn hình LCD ở chế độ 16X2 và hiển thị thông báo chào mừng và xóa sau năm giây.
lcd.begin (16,2); // Khởi tạo màn hình LCD lcd.setCursor (0,0); // Đặt Con trỏ ở dòng đầu tiên của Display lcd.print ("Thông báo về mạch"); // In SỐ MẠCH trong LCD lcd.setCursor (0,1); // Đặt Con trỏ ở dòng thứ hai của Display lcd.print ("I2C 2 ARDUINO"); // In I2C ARDUINO ở LCD delay (5000); // Trì hoãn 5 giây lcd.clear (); // Xóa màn hình LCD
3. Tiếp theo chúng ta có hai chức năng một cho sự kiện yêu cầu và một cho sự kiện nhận
Đối với sự kiện yêu cầu
Khi Master request giá trị từ slave, chức năng này sẽ được thực thi. Hàm này lấy giá trị đầu vào từ Slave POT và chuyển đổi nó dưới dạng 7-bit và gửi giá trị đó đến master.
void requestEvent () { int potvalue = analogRead (A0); byte SlaveSend = map (potvalue, 0,1023,0,127); Wire.write (SlaveSend); }
Đối với sự kiện nhận
Khi Master gửi dữ liệu đến slave với địa chỉ slave (8) thì chức năng này sẽ được thực thi. Hàm này đọc giá trị nhận được từ cái chính và lưu trữ trong một biến kiểu byte .
void getEvent (int howMany { SlaveReceive = Wire.read (); }
4. Trong vòng lặp Void ():
Chúng tôi hiển thị giá trị nhận được từ chính liên tục trong mô-đun màn hình LCD.
void loop (void) { lcd.setCursor (0,0); // Đặt Currsor tại dòng một của LCD lcd.print (">> Slave <<"); // In >> Slave << tại LCD lcd.setCursor (0,1); // Đặt Con trỏ ở dòng hai của LCD lcd.print ("MasterVal:"); // In MasterVal: trong LCD lcd.print (SlaveReceive); // In giá trị SlaveReceive trong LCD nhận được từ Master Serial.println ("Slave nhận từ Master:"); // In trong Serial Monitor Serial.println (SlaveReceive); chậm trễ (500); lcd.clear (); }
Bằng cách xoay Potentiometer ở một bên, bạn có thể thấy các giá trị khác nhau trên màn hình LCD ở một bên:
Vì vậy, đây là cách giao tiếp I2C diễn ra trong Arduino, ở đây chúng tôi sử dụng hai Arduinos để chứng minh không chỉ gửi dữ liệu mà còn nhận dữ liệu bằng giao tiếp I2C. Vì vậy, bây giờ bạn có thể giao diện bất kỳ cảm biến I2C nào với Arduino.
Mã hóa hoàn chỉnh cho Master và Slave Arduino được cung cấp bên dưới với một video trình diễn