Làm thế nào để truyền thông nối tiếp hoạt động trên Arduino?


16

Với tham chiếu đến Arduino Uno, Mega2560, Leonardo và các bảng tương tự:

  • Làm thế nào để truyền thông nối tiếp hoạt động?
  • Làm thế nào nhanh là nối tiếp?
  • Làm cách nào để kết nối giữa người gửi và người nhận?

Xin lưu ý: Đây là một câu hỏi tham khảo.


Bạn có thể thấy điều này thú vị về bộ đệm ở cả hai phía của Nano được kết nối với hệ thống Raspian chạy bộ ghi dữ liệu Python, chỉ sử dụng cáp lập trình USB thông thường giữa hai: arduino.stackexchange.com/questions/11710/ Thẻ
SDsolar

Câu trả lời:


16

Truyền thông nối tiếp không đồng bộ (thường được gọi là truyền thông nối tiếp) được sử dụng để gửi byte từ thiết bị này sang thiết bị khác. Một thiết bị có thể là một hoặc nhiều thứ sau đây:

  • Arduino
  • máy tính
  • GPS
  • Đầu đọc thẻ RFID
  • Màn hình LCD
  • Modem
  • Khác

Tốc độ đồng hồ và lấy mẫu dữ liệu

Không giống như truyền thông nối tiếp SPI / USB / I2C không có tín hiệu đồng hồ. Đồng hồ lấy mẫu là tốc độ mẫu theo thỏa thuận (được gọi là tốc độ truyền). Cả người gửi và người nhận phải được cấu hình để sử dụng cùng một tốc độ hoặc người nhận sẽ nhận được dữ liệu vô nghĩa (do các bit không được lấy mẫu ở cùng tốc độ mà chúng được gửi).

Việc truyền tải không đồng bộ , về cơ bản có nghĩa là các byte có thể được gửi bất cứ lúc nào, với các khoảng cách khác nhau giữa chúng. Đồ họa này minh họa một byte đơn được gửi:

Comms nối tiếp - gửi một byte

Hình trên cho thấy chữ 'F' đang được truyền đi. Trong ASCII, đây là 0x46 (ở dạng hex) hoặc 0b01000110 (ở dạng nhị phân). Các thiểu đáng kể (theo đơn đặt hàng thấp) bit được truyền đi đầu tiên, do đó trong hình trên bạn thấy các bit đến theo thứ tự: 01100010.

Thời gian "nhàn rỗi" giữa các byte được truyền dưới dạng các bit "1" liên tục (có hiệu quả, đường truyền được giữ ở mức cao liên tục).

Để chỉ ra sự bắt đầu của một byte, Bit bắt đầu luôn được chỉ định bằng cách kéo dòng xuống thấp như thể hiện trên đồ họa. Khi người nhận thấy bit start, nó đợi 1,5 lần thời gian mẫu và sau đó lấy mẫu các bit dữ liệu. Nó đợi 1,5 lần để nó:

  • Bỏ qua bit bắt đầu
  • Các mẫu nửa chừng trong bit tiếp theo

Ví dụ, nếu tốc độ baud là 9600 baud, thì tốc độ mẫu sẽ là 1/9600 = 0.00010416giây (104.16 Nhạc).

Do đó, ở 9600 baud, sau khi nhận được bit start, người nhận sẽ đợi 156,25 lượt, và sau đó lấy mẫu sau mỗi 104,16.

Thời gian bắt đầu bit

Mục đích của Stop Bit là để đảm bảo rằng chắc chắn có 1 bit giữa mỗi byte. Nếu không có bit stop, nếu một byte kết thúc bằng 0, thì phần cứng sẽ không thể nói được sự khác biệt giữa đó và bit bắt đầu của byte tiếp theo.

Để tạo đầu ra trên trên Uno, bạn có thể viết mã này:

void setup()
  {
      Serial.begin(9600);
      Serial.print("F");
  }

void loop ()
  {
  }

Số bit dữ liệu

Để tiết kiệm thời gian truyền (vào thời xa xưa, heh), bạn được phép chỉ định số lượng bit dữ liệu khác nhau. Phần cứng AtMega hỗ trợ đánh số bit dữ liệu từ 5 đến 9. Rõ ràng càng ít bit dữ liệu thì bạn càng có thể gửi ít thông tin, nhưng tốc độ sẽ càng nhanh.


Bit chẵn lẻ

Bạn có thể tùy chọn có một bit chẵn lẻ. Điều này được tính, nếu được yêu cầu, bằng cách đếm số 1 trong ký tự, và sau đó đảm bảo rằng số này là số lẻ hoặc thậm chí bằng cách đặt bit chẵn lẻ thành 0 hoặc 1 theo yêu cầu.

Ví dụ: đối với chữ "F" (hoặc 0x46 hoặc 0b01000110) bạn có thể thấy rằng có 3 chữ cái ở đó (trong 01000110). Vì vậy, chúng tôi đã có chẵn lẻ. Vì vậy, bit chẵn lẻ sẽ như sau:

  • Không có chẵn lẻ: bỏ qua
  • Chẵn lẻ chẵn lẻ: 1 (3 + 1 chẵn)
  • Chẵn lẻ lẻ: a 0 (3 + 0 là số lẻ)

Bit chẵn lẻ, nếu có, xuất hiện sau bit dữ liệu cuối cùng nhưng trước bit dừng.

Nếu người nhận không nhận được bit chẵn lẻ chính xác, đó được gọi là "lỗi chẵn lẻ". Nó chỉ ra rằng có một số vấn đề. Có thể người gửi và người nhận được cấu hình để sử dụng các tốc độ baud (bit) khác nhau hoặc có nhiễu trên đường chuyển từ 0 sang một hoặc ngược lại.

Một số hệ thống ban đầu cũng sử dụng tính chẵn lẻ "đánh dấu" (trong đó bit chẵn lẻ luôn là 1 bất kể dữ liệu) hay chẵn lẻ "khoảng trắng" (trong đó bit chẵn lẻ luôn là 0 bất kể dữ liệu).


Truyền 9 bit

Một số thiết bị truyền thông sử dụng dữ liệu 9 bit, vì vậy trong những trường hợp này, bit chẵn lẻ được chuyển thành bit thứ 9. Có các kỹ thuật đặc biệt để gửi bit thứ 9 này (các thanh ghi là các thanh ghi 8 bit để bit thứ 9 phải được đặt ở một nơi khác).


Số bit dừng

Thiết bị ban đầu có xu hướng chậm hơn một chút về mặt điện tử, vì vậy để cho người nhận có thời gian xử lý byte đến, đôi khi người ta chỉ định rằng người gửi sẽ gửi hai bit stop. Điều này về cơ bản sẽ thêm nhiều thời gian hơn khi dòng dữ liệu được giữ ở mức cao (thêm một bit thời gian) trước khi bit bắt đầu tiếp theo có thể xuất hiện. Thời gian bit thêm này cung cấp cho người nhận thời gian để xử lý byte đến cuối cùng.

Nếu người nhận không nhận được logic 1 khi bit dừng được cho là, đó được gọi là "lỗi đóng khung". Nó chỉ ra rằng có một số vấn đề. Rất có thể người gửi và người nhận được cấu hình để sử dụng các tốc độ baud (bit) khác nhau.


Ký hiệu

Thông thường, giao tiếp nối tiếp được biểu thị bằng cách cho bạn biết tốc độ, số bit dữ liệu, loại chẵn lẻ và số bit dừng, như sau:

9600/8-N-1

Điều này nói với chúng tôi:

  • 9600 bit mỗi giây
  • 8 bit dữ liệu
  • Không có chẵn lẻ (thay vào đó bạn có thể thấy: E = chẵn, O = lẻ)
  • 1 bit dừng

Điều quan trọng là người gửi và người nhận đồng ý về những điều trên, nếu không thì việc giao tiếp khó có thể thành công.


Pin-outs

Arduino Uno có các chân kỹ thuật số 0 và 1 có sẵn cho nối tiếp phần cứng:

Chân nối tiếp Arduino Uno

Để kết nối hai Arduinos, bạn trao đổi Tx và Rx như thế này:

Kết nối hai Arduinos với nhau


Tốc độ

Một loạt các tốc độ được hỗ trợ (xem hình bên dưới). Tốc độ "tiêu chuẩn" thường là bội số của 300 baud (ví dụ: 300/600/1200/2400, v.v.).

Tốc độ "không chuẩn" khác có thể được xử lý bằng cách đặt các thanh ghi thích hợp. Lớp Phần cứng thực hiện điều này cho bạn. ví dụ.

Serial.begin (115200);  // set speed to 115200 baud

Theo nguyên tắc thông thường, giả sử bạn đang sử dụng dữ liệu 8 bit, thì bạn có thể ước tính số byte bạn có thể truyền mỗi giây bằng cách chia tốc độ baud cho 10 (vì bit start và bit stop).

Do đó, ở 9600 baud, bạn có thể truyền 960 byte ( 9600 / 10 = 960) mỗi giây.


Lỗi tốc độ Baud

Tốc độ truyền trên Atmega được tạo bằng cách chia đồng hồ hệ thống, sau đó đếm đến số được đặt trước. Bảng này từ biểu dữ liệu hiển thị các giá trị đăng ký và tỷ lệ phần trăm lỗi cho đồng hồ 16 MHz (chẳng hạn như trên Arduino Uno).

Lỗi tốc độ Baud

Bit U2Xn ảnh hưởng đến ước số tốc độ xung nhịp (0 = chia cho 16, 1 = chia cho 8). Thanh ghi UBRRn chứa số lượng bộ xử lý đếm đến.

Vì vậy, từ bảng trên, chúng ta thấy rằng chúng ta nhận được 9600 baud từ đồng hồ 16 MHz như sau:

16000000 / 16 / 104 = 9615

Chúng tôi chia cho 104 chứ không phải 103 vì bộ đếm là không tương đối. Do đó, lỗi ở đây là 15 / 9600 = 0.0016gần với những gì bảng trên nói (0,02%).

Bạn sẽ nhận thấy rằng một số tốc độ truyền có số lỗi cao hơn các tốc độ khác.

Theo biểu dữ liệu, tỷ lệ phần trăm lỗi tối đa cho 8 bit dữ liệu nằm trong khoảng 1,5% đến 2,0% (xem biểu dữ liệu để biết thêm chi tiết).


Arduino Leonardo

Arduino Leonardo và Micro có một cách tiếp cận khác nhau đối với giao tiếp nối tiếp, vì chúng kết nối trực tiếp qua USB với máy tính chủ chứ không phải qua cổng nối tiếp.

Vì lý do này, bạn phải đợi Nối tiếp trở nên "sẵn sàng" (vì phần mềm thiết lập kết nối USB), với một vài dòng nữa, như thế này:

void setup()
  {
      Serial.begin(115200);
      while (!Serial)
      {}  // wait for Serial comms to become ready
      Serial.print("Fab");
  }

void loop ()
  {
  }

Tuy nhiên, nếu bạn muốn thực sự giao tiếp qua các chân D0 và D1, (chứ không phải bằng cáp USB) thì bạn cần sử dụng Nối tiếp 1 thay vì Nối tiếp. Bạn làm như vậy:

void setup()
  {
      Serial1.begin(115200);
      Serial1.print("Fab");
  }

void loop ()
  {
  }

Cấp điện áp

Lưu ý rằng Arduino sử dụng các mức TTL để liên lạc nối tiếp. Điều này có nghĩa là nó mong đợi:

  • Một bit "không" là 0V
  • Một bit "một" là + 5V

Thiết bị nối tiếp cũ hơn được thiết kế để cắm vào cổng nối tiếp của PC có thể sử dụng các mức điện áp RS232, cụ thể là:

  • Một bit "không" là +3 đến +15 volt
  • Một bit "một" là −3 đến −15 volt

Điều này không chỉ "đảo ngược" đối với các mức TTL ("một" còn âm hơn "không"), Arduino không thể xử lý các điện áp âm trên các chân đầu vào của nó (cũng không phải là các điện áp dương lớn hơn 5V).

Do đó, bạn cần một mạch giao diện để giao tiếp với các thiết bị như vậy. Chỉ dành cho đầu vào (với Arduino), một bóng bán dẫn, diode đơn giản và một vài điện trở sẽ làm điều đó:

Đảo ngược bộ đệm

Đối với giao tiếp hai chiều, bạn cần có khả năng tạo ra điện áp âm, do đó cần một mạch phức tạp hơn. Ví dụ, chip MAX 232 sẽ làm điều đó, kết hợp với bốn tụ 1 PhaF để hoạt động như các mạch bơm sạc.


Phần mềm nối tiếp

Có một thư viện gọi là SoftwareSerial cho phép bạn thực hiện giao tiếp nối tiếp (tối đa một điểm) trong phần mềm thay vì phần cứng. Điều này có lợi thế là bạn có thể sử dụng các cấu hình pin khác nhau để liên lạc nối tiếp. Nhược điểm là việc thực hiện nối tiếp trong phần mềm đòi hỏi nhiều bộ xử lý hơn và dễ bị lỗi hơn. Xem phần mềm nối tiếp để biết thêm chi tiết.


Mega2560

Arduino "Mega" có thêm 3 cổng nối tiếp phần cứng. Chúng được đánh dấu trên bảng là Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3. Chúng nên được sử dụng để ưu tiên cho SoftwareSerial nếu có thể. Để mở các cổng khác, bạn sử dụng các tên Serial1, serial2, serial3, như thế này:

Serial1.begin (115200);  // start hardware serial port Tx1/Rx1
Serial2.begin (115200);  // start hardware serial port Tx2/Rx2
Serial3.begin (115200);  // start hardware serial port Tx3/Rx3

Ngắt

Cả gửi và nhận, sử dụng thư viện Phần cứng, đều sử dụng các ngắt.

Gửi

Khi bạn thực hiện a Serial.print, dữ liệu bạn đang cố in sẽ được đặt trong bộ đệm "truyền" bên trong. Nếu bạn có 1024 byte RAM trở lên (chẳng hạn như trên Uno), bạn sẽ có bộ đệm 64 byte, nếu không, bạn sẽ có bộ đệm 16 byte. Nếu bộ đệm có chỗ, thì Serial.printtrả về ngay lập tức, do đó không trì hoãn mã của bạn. Nếu không có phòng, thì nó "chặn" chờ bộ đệm được làm trống đủ để có chỗ.

Sau đó, khi mỗi byte được truyền bởi phần cứng, một ngắt được gọi (ngắt "USART, Data Register Empty") và thường trình ngắt gửi byte tiếp theo từ bộ đệm ra khỏi cổng nối tiếp.

Nhận

Khi dữ liệu đến được nhận, một thường trình ngắt được gọi (ngắt "USART Rx Complete") và byte đến được đặt vào bộ đệm "nhận" (cùng kích thước với bộ đệm truyền được đề cập ở trên).

Khi bạn gọi, Serial.availablebạn tìm ra có bao nhiêu byte có sẵn trong bộ đệm "nhận" đó. Khi bạn gọi Serial.readmột byte được loại bỏ khỏi bộ đệm nhận và trở về mã của bạn.

Trên Arduinos có 1000 byte RAM trở lên, không cần vội vàng xóa dữ liệu khỏi bộ đệm nhận, miễn là bạn không để nó lấp đầy. Nếu nó đầy lên thì bất kỳ dữ liệu đến tiếp theo sẽ bị loại bỏ.

Lưu ý rằng vì kích thước của bộ đệm này, không có điểm nào để chờ đợi một số lượng rất lớn byte đến, ví dụ:

while (Serial.available () < 200)
  { }  // wait for 200 bytes to arrive

Điều này sẽ không bao giờ hoạt động vì bộ đệm không thể giữ được nhiều như vậy.


Lời khuyên

  • Trước khi đọc, luôn luôn đảm bảo dữ liệu có sẵn. Ví dụ, điều này là sai:

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // may not be available
      }

    Các Serial.availablebài kiểm tra chỉ đảm bảo bạn có một byte có sẵn, tuy nhiên mã cố gắng đọc hai. Nó có thể hoạt động, nếu có hai byte trong bộ đệm, nếu không, bạn sẽ nhận được -1 trả về, nó sẽ trông giống như '' 'nếu được in.

  • Hãy nhận biết mất bao lâu để gửi dữ liệu. Như đã đề cập ở trên, ở tốc độ 9600 baud, bạn chỉ truyền được 960 byte mỗi giây, vì vậy cố gắng gửi 1000 bài đọc từ một cổng tương tự, ở 9600 baud, sẽ không thành công.


Người giới thiệu


Trong đồ họa thứ 1: với các mũi tên, có vẻ như bit stop được truyền trước. Nếu bạn trao đổi Rx / Tx và hướng mũi tên, tôi nghĩ nó sẽ ít gây nhầm lẫn hơn.
ott--

Nó được dự định đọc từ trái sang phải (như câu này) và do đó những điều bên trái xảy ra đầu tiên. Đặt nó như thế này: trên một máy hiện sóng, đó là cách bạn sẽ thấy dấu vết.
Nick Gammon

Ok với máy đo dao động tôi mua cái đó. :-)
ott--

Tuy nhiên tôi đã nghĩ rằng quan điểm của bạn rất có ý nghĩa. Người khác nghĩ gì? Sẽ rõ ràng hơn nếu mũi tên được đảo ngược, và tôi đã trao đổi Rx / Tx?
Nick Gammon

1
@ linhartr22 Tôi đã sửa đổi nó thành "dữ liệu vô nghĩa" có lẽ gần hơn.
Nick Gammon
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.