Viet Dev

Cover image for Arduino - Chuẩn giao tiếp 1-Wire
Tuan for Unicloud Group

Posted on

Arduino - Chuẩn giao tiếp 1-Wire

Arduino - Chuẩn giao tiếp 1-Wire

Trong chương này chúng ta sẽ cùng tìm hiểu thêm về 1 chuẩn giao tiếp
truyền nhận dữ liệu nữa đó là chuẩn giao tiếp 1-Wire, các ví dụ thực
hành đọc cảm biến bằng chuẩn giao tiếp 1-Wire, kết hợp chuẩn giao tiếp
1-Wire với các chuẩn giao tiếp khác như I2C hay SPI.

1-Wire

1-Wire là gì?

Chuẩn giao tiếp 1-Wire được nghiên cứu và phát triển bởi Dallas
Semiconductor (Maxim). Không như các chuẩn giao tiếp đã được đề cập
trước đó như I2C cần 2 dây hoặc SPI cần 4 dây để truyền nhận dữ liệu.
1-Wire chỉ cần một dây để có thể truyền và nhận dữ liệu.

Chuẩn giao tiếp 1-Wire là chuẩn giao tiếp không đồng bộ và bán song công
(half-duplex: tại một thời điểm, tín hiệu chỉ có thể chạy theo một
hướng). Vì 1-Wire chỉ sử dụng một dây để nối nguồn và truyền dữ liệu,
nên khi ở trạng thái rãnh (không có dữ liệu trên đường truyền) thì nó
cần phải ở mức cao, do đó cần kết nối dây này với nguồn thông qua một
điện trở. Điện trở này thường được gọi là điện trở kéo lên (pull-up
resistor). Tùy theo các thiết bị mà giá trị điện trở này có thể thay
đổi, cần tham khảo datasheet của thiết bị để biết rõ.

1-Wire thường sử dụng các cổng logic CMOS/TTL tương ứng với mức logic
0 thì điện áp đỉnh ở mức 0.8V và điện áp tối thiểu cho mức logic
12.2V. Các thiết bị 1-Wire có nguồn cấp trong khoảng
2.8V đến 6V.

1-Wire có hai chế độ làm việc là standardoverdrive. Khi làm
việc ở chế độ standard thì tốc độ truyền dữ liệu là 15.4kbps,
với chế độ overdrive125kbps.

Chuẩn giao tiếp 1-Wire tuân theo mô hình master-slave. Trên một đường
truyền dữ liệu có thể gắn nhiều thiết bị slave, nhưng chỉ có duy nhất
một thiết bị là master. Mỗi một thiết bị slave sẽ có duy nhất 64-bit địa
chỉ lưu trữ trong bộ nhớ ROM của mình. Nhờ thế, khi kết nối vào chung
một bus dữ liệu thì thiết bị master sẽ có thể nhận biết được giữa các
slave. Trong 8 byte (64 bit) này sẽ được chia ra làm 3 phần chính:

  • Bắt đầu là LSB (least significant bit), byte đầu tiên bao gồm 8 bit được gửi đi là mã họ thiết bị (family codes) giúp xác định đó là loại thiết bị nào. 6 byte tiếp theo lưu trữ một địa chỉ riêng của từng thiết bị với 48 bit tùy biến. Byte cuối cùng MSB (most significant bit) là byte kiểm tra tính toàn vẹn của dữ liệu - cyclic redundancy check (CRC), đây là byte giúp kiểm tra xem tín hiệu gửi đi có bị lỗi hay không.

Với số lượng 2^48 bit địa chỉ được tạo ra thì vấn đề về địa chỉ không
phải là vấn đề chính trong chuẩn giao tiếp này.

Khi có nhiều thiết bị 1-Wire trên một mạch thì nó thường được gọi là
mạng 1-Wire hoặc MicroLAN. Trong mạng này, yêu cầu chỉ có duy nhất
một master và phải có ít nhất là một slave. Có thế kết nối
bao nhiêu slave tùy thích, nhưng khi vượt quá con số 20 thì việc
truyền nhận dữ liệu có thể sẽ xảy ra lỗi.

Đường truyền giữa master và slave có thể lên tới 200 mét. Khi sử dụng
dây ở khoảng cách lớn thì cần được bảo quản tốt hơn, như tránh băng qua
các đường dây điện để tránh tín hiệu sẽ bị nhiễu. Khi sử dụng ở khoảng
cách xa, loại dây rẻ nhất và dễ sử dụng nhất là dây CAP5 được sử dụng
trong mạng LAN (dây cable internet).

1-Wire hoạt động như thế nào?

Chuẩn giao tiếp 1-Wire sử dụng khái niệm time slot (khe thời gian). Một
time slot là một khoảng thời gian trong đó mức logic 1 hoặc 0 sẽ được
ghi hoặc đọc. Time slot có khoảng thời gian là 60µs khi hoạt động ở chế
độ standard, và 8µs với chế độ overdrive.

Có 3 thao tác hoạt động cơ bản của 1 Wire là Reset/Present, Read,
Write
.

  1. Reset/Present.

    Master sẽ kéo tín hiệu truyền xuống mức thấp trong khoảng 480µs đến
    640µs. Khoảng thời gian này được hiểu là khoảng thời gian reset.
    Sau khoảng thời gian này, nếu có slave sẽ gửi trả tín hiệu
    present. Tức là slave sẽ kéo tín hiệu xuống mức thấp trong
    khoảng 60µs đến 240µs. Nếu không có tín hiệu present trả về thì
    master sẽ hiểu là không có slave nào được kết nối vào mạng, và các
    quá trình tiếp theo sẽ không được diễn ra.

  2. Write.

    Đối với bit 1 master kéo đường truyền xuống mức thấp trong
    khoảng 1 đến 15µs, sau đó sẽ giải phóng đường truyền về mức cao.

    Đối với bit 0 master sẽ kéo đường truyền xuống mức thấp trong
    khoảng 60µs đến 120µs, sau đó sẽ giải phóng đường truyền về mức cao.

    Giữa các lần gửi bit (0 hoặc 1), phải có khoảng thời gian nghỉ (recovery time) tối thiểu 1µs.
  3. Read.

    Master sẽ kéo tín hiệu truyền xuống mức thấp trong khoảng 0-15µs.
    Nếu slave muốn gửi bit 1 sẽ giải phóng đường truyền trở về mức
    cao, nếu muốn gửi bit 0 slave sẽ giữ đường truyền ở mức thấp
    trong khoảng thời gian 15µs đến 60µs.

Các khoảng thời gian trong các hoạt động trên được xác định ở chế độ
standard

Tổng<br>
hợp

Tiến trình hoạt động (Workflow)

Workflow

  1. Trước khi bắt đầu xác định một slave để làm việc, master cần đưa ra
    lệnh reset để xác định là có slave nào đó có nằm trên đường
    truyền bằng cách phản hồi lại tín hiêu present.

  2. Sau khi đã xác định có thiết bị slave được kết nối, master sẽ chọn
    tất cả hay chỉ 1 slave (dựa trên địa chỉ của thiết bị) để làm việc.
    Hoặc sẽ xác định slave tiếp theo bằng thuật toán tìm kiếm nhị phân.

  3. Sau khi đã xác định được slave làm việc thì tất cả các slave khác sẽ
    được bỏ qua, và tất cả các tín hiệu truyền đi sẽ chỉ được nhận bởi
    thiết bị master và slave đã được chọn.

  4. Nếu muốn giao tiếp với một slave khác, master sẽ bắt đầu lại quá
    trình từ bước 1.

Khi một thiết bị được chọn, master có thể đưa ra các lệnh cụ thể cho
thiết bị, gửi dữ liệu đến nó hoặc đọc dữ liệu từ nó. Bởi vì mỗi loại
thiết bị thực hiện các chức năng khác nhau và phục vụ cho mục đích khác
nhau, mỗi máy có một giao thức duy nhất khi nó đã được chọn. Mặc dù mỗi
loại thiết bị có thể có các giao thức và tính năng khác nhau nhưng tất
cả chúng đều có quy trình lựa chọn giống nhau và tuân theo tiến trình
như trên.

Trên đây là phần giới thiệu về chuẩn giao tiếp 1-Wire, tiếp theo chúng
ta sẽ cùng thực hành một số ví dụ để có thể hiểu rõ cách thức hoạt động
của chuẩn giao tiếp này.

Ví dụ chuẩn giao tiếp 1-Wire

Ở phần này chúng ta sẽ tìm hiểu về ví dụ đọc dữ liệu từ cảm biến đo
nhiệt độ DS18B20. Sẽ có hai ví dụ với cảm biến này là:

  • Một master và một slave.

  • Một master và nhiều slave.

Một master và một slave

Linh kiện cần thiết:

  • Board Arduino Uno

  • DS18B20 (hoặc DS18S20, hoặc DS1822)

  • Màn hình OLED 0.96"

  • Điện trở 4k7Ω

Giới thiệu DS18B20

Đầu dò nhiệt độ sử dụng DS18B20, đầu ra digital, được tích hợp trong ống
thép không rỉ, độ nhạy cao, đo chính xác nhiệt độ trong môi trường ẩm
ướt, với chuẩn giao tiếp 1-Wire. DS18B20 cung cấp 9-12 bit cấu hình đọc
nhiệt độ theo chuẩn giao tiếp 1-Wire, do đó chúng ta chỉ cần 1 dây để
kết nối với một vi xử lý.

Hình ảnh cảm biến nhiệt độ<br>
DS18B20

Thông tin cảm biến

  • Điện áp đầu vào: 3.0-5.5V.

  • Không thấm nước, không rỉ.

  • Khoảng nhiệt độ đo: -55 ° C đến +125 ° C.

  • Độ chính xác từ ± 0.5 ° C trong khoảng -10 ° C đến +85 ° C.

  • Đầu ra : VCC (Red), DATA (Blue), GND (Black).

Kết nối với board Arduino Uno

DS18B20 Arduino Uno
Đỏ 5V
Đen GND

Bảng sơ đồ kết nối cảm biến DS18B20 và board Arduino Uno

Hình ảnh kết nối với board Arduino<br>
Uno

Hình ảnh kết nối của DS18B20 với Arduino<br>
Uno

Chú ý

  • Dây data của DS18B20 cần phải được gắn với một điện trở kéo 4k7Ω
    và được nối với nguồn 5V.

  • Người dùng có thể kết nối dây vàng của cảm biến đến bất cứ chân
    digital nào của vi điều khiển.

Thư viện sử dụng

OneWire

Adafruit-GFX

Adafruit_SSD1306

Giải thích một số hàm trong thư viện OneWire

  • OneWire myWire(pin): Tạo một đối tượng myWire thuộc thư viện
    OneWire bằng một pin cụ thể. Mặc dù có thể kết nối với nhiều thiết
    bị trên cùng một dây, nhưng nếu có quá nhiều thì bạn có thể chia nhỏ
    ra và kết nối vào các pin riêng và tạo ra các đối tượng 1-Wire với
    từng pin.

  • myWire.(addrArray): Tìm thiết bị được kế nối kế tiếp. addArray
    là một mảng gồm 8 bit. Nếu tìm thấy một thiết bị, addArray sẽ chứa
    địa chỉ của thiết bị đó và sẽ trả về giá trị là true. Nếu không
    tìm thấy thiết bị nào sẽ trả về false.

  • myWire.reset_search(): Tìm kiếm lại từ đầu, thiết bị tìm thấy sẽ
    là thiết bị đầu tiên.

  • myWire.reset(): Reset 1-wire bus. Thường sử dụng hàm này trước
    khi bắt đầu giao tiếp với một thiết bị bất kỳ.

  • myWire.select(addrArray): Chọn một thiết bị để làm việc dựa trên
    địa chỉ của nó. Sử dụng lệnh này sau khi đã reset.

  • myWire.skip(): Bỏ qua bước lựa chọn thiết bị. Cách này chỉ dùng
    được khi chỉ có một thiết bị slave.

  • myWire.write(num, power): Gửi 1 byte, và nếu sau khi bạn cần sử
    dụng nguồn sau khi gửi thì set power = 1, nếu không thì bỏ trống
    hoặc set power = 0 (đối với cảm biến DS18B20 có thể làm việc trong
    parasite mode - dây đỏ và đen nối chung xuống GND).

  • myWire.read(): Đọc 1 byte.

  • myWire.crc8(dataArray, length): Tính bit CRC trên dữ liệu được
    nhận.

Mã nguồn

//  Khai báo thư viện sử dụng
#include <OneWire.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
OneWire  ds(2); //  Khai báo chân 2 lấy dữ liệu (cần có điện trở 4k7 để kéo lên nguồn)
//  Thiết lập các giá trị ban đầu
void setup()
{
  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(18, 14);
  display.println("1-Wire Example");
  display.display();
  delay(2000);
  display.clearDisplay();
}

void loop(void)
{
  byte i;
  byte present = 0;
  byte type_s;        //  Biến lưu giá trị xác định loại cảm biến
  byte data[12];      //  Biến lưu giá trị nhiệt độ (HEX)
  byte addr[8];       //  Biến lưu địa chỉ cảm biến (gồm 8 bit)
  float celsius, fahrenheit;

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("No more addresses.");
    display.display();
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }
  //  In địa chỉ ROM ra Serial và OLED
  Serial.print("ROM =");
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("ROM = ");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
    display.print(addr[i], HEX);
  }
  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();
  switch (addr[0]) { // Dùng byte đầu tiền trong ROM để xác định loại cảm biến
    case 0x10:
      Serial.println("  Chip = DS18S20");
      display.setCursor(0,8);
      display.println("Chip = DS18S20");
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      display.setCursor(0,8);
      display.println("Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      display.setCursor(0,8);
      display.println("Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // Gửi lệnh bắt đầu giao tiếp (đối với cảm biến DS18B20)

  delay(1000);              // Cần một khoảng thời gian chờ, ít nhất là 750ms
  present = ds.reset();
  ds.select(addr);
  ds.write(0xBE);           // Gửi lệnh bắt đầu đọc dữ lliệu (đối với cảm biến DS18B20)

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  int16_t raw = (data[1] << 8) | data[0]; // Chuyển đổi giá trị nhiệt độ từ 16 bit
  if (type_s) {
    raw = raw << 3;                       // Độ phân giải mặc định 9 bit
    if (data[7] == 0x10) {
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // Tại độ phân giải thấp hơn, các bit thấp đều không xác định được do đó, chúng ta cho nó bằng
    if (cfg == 0x00) raw = raw & ~7;        //  Độ phân giải 9 bit, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3;   //  Độ phân giải 10 bit, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1;   //  Độ phân giải 11 bit, 375 ms
    //  Độ phân giải mặc định 12 bit, thời gian chuyển đổi 750 ms
  }
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;

  //  In các giá trị nhiêt độ ra Serial và OLED
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");
  display.setCursor(0,16);
  display.print("Temp = ");
  display.print(String(celsius));
  display.print((char)247);display.print("C");
  display.setCursor(42, 24);
  display.print(String(fahrenheit));
  display.print((char)247);display.print("F");
  display.display();
  delay(1500);
}
Enter fullscreen mode Exit fullscreen mode

Output

Kết quả trả về trên cổng Serial trên máy tính sẽ có dạng như sau:

ROM = 28 FF 3C D6 63 16 4 83
  Chip = DS18B20
  Data = 1 DB 1 4B 46 7F FF C 10 95  CRC=95
  Temperature = 29.69 Celsius, 85.44 Fahrenheit
No more addresses.
Enter fullscreen mode Exit fullscreen mode

Kết quả xuất hiện trên Oled sẽ có dạng như sau:

ROM = 28 FF 3C D6 63 16 4 83
Chip = DS18B20
Temperature = 29.69 °C
              85.44 °F
Enter fullscreen mode Exit fullscreen mode

Một master và nhiều slave

Đối với ví dụ này sẽ có hai cách kết nối cảm biến như sau:

Normal Power Mode (Nguồn<br>
Tweaking4All.com)

Các cảm biến có thể nối song song với nhau và các dây được nối như khi
sử dụng một cảm biến.

Parasite Power Mode (Nguồn<br>
Tweaking4All.com)

Parasite mode về cơ bản là sẽ nối dây đỏ và dây đen của cảm biến xuống
GND, và chỉ có một dây vàng là được nối lên nguồn.

Ví dụ này sẽ sử dụng hai cảm biến DS18B20 kết nối theo kiểu Normal
Power
như hình.

Mã nguồn

Ở ví dụ này ta sẽ sử dụng chung một mã nguồn trên để thiết lập việc lấy dữ liệu.

Kết quả

Vì ở đây chỉ kết nối hai cảm biến nên trên OLED và Serial sẽ hiện kết
quả lấy được của cả hai cảm biến.

Serial

ROM = 28 49 BA 3 0 0 80 B
  Chip = DS18B20
  Data = 1 E5 1 FF FF 7F FF FF FF D3  CRC=D3
  Temperature = 30.31 Celsius, 86.56 Fahrenheit
ROM = 28 FF 3C D6 63 16 4 83
  Chip = DS18B20
  Data = 1 DE 1 4B 46 7F FF C 10 C3  CRC=C3
  Temperature = 29.87 Celsius, 85.77 Fahrenheit
No more addresses.
Enter fullscreen mode Exit fullscreen mode

OLED sẽ lần lượt xuất hiện.

ROM = 28 49 BA 3 0 0 80 B
Chip = DS18B20
Temperature = 30.31 Celsius
86.56 Fahrenheit

ROM = 28 FF 3C D6 63 16 4 83
Chip = DS18B20
Temperature = 29.87 Celsius
85.77 Fahrenheit

Enter fullscreen mode Exit fullscreen mode




Tổng kết

Qua phần này chúng ta đã tìm hiểu được về một chuẩn giao tiếp mới là
1-Wire, và các cách lập trình sử dụng chuẩn giao tiếp này. Dưới đây là
một số trang web để bạn có thể tham khảo thêm nếu muốn tìm hiểu sâu về
chuẩn giao tiếp này:

1-Wire Turtorial

Overview of 1-Wire Technology and Its Use

1-Wire® Devices

Latest comments (0)