Bài 5.1 - Giao tiếp UART chế độ Multi-Processor

( 30 Votes )

Nội dung Các bài cần tham khảo trước
  1. Chế độ Multi-Processor trong AVR UART.
  2. Sử dụng Multi-Processor.
    Download ví dụ

 

I. Chế độ Multi-Processor trong AVR UART.

       AVR hỗ trợ một khả năng giao tiếp UART ở chế độ “đa xử lí” (Multi-processor) hay Master-Slaves. Điều đầu tiên bạn cần biết là chế độ này không phải là chuẩn của UART mà chỉ đặc biệt trên các chip AVR (và có thể trên một số chip khác của Atmel). Bit MPCM (bit 0) trong thanh ghi UCSRA là nhân tố quan trọng nhất để quyết định chế độ hoạt động này. Cấu hình mạng Master-Slave dùng UART được tóm tắt như sau: 
      - Trên mạng này chỉ có 1 Master và có thể có nhiều Slaves, các đường TxD và RxD của các Slaves được nối chung với nhau (nối song song). Các Slaves và Master được nối với nhau theo kiểu “bắt chéo”, TxD chung của Slaves nối với RxD của Master và ngược lại. Mỗi Slave mang 1 địa chỉ riêng do người dùng gán, đặc biệt có thể có nhiều Slave trùng địa chỉ vẫn không ảnh hưởng đến hoạt động của mạng.
      - Các Slaves và Master phải được cài đặt khung truyền và baudrate như nhau (cũng như truyền thông UART thông thường). Khung truyền trong chế độ Master-Slaves có thể 5, 6, 7,8 hay 9 bit nhưng thông thường khung 9 bit được chọn. Bài này cũng hướng dẫn dựa trên khung 9 bit. Trong khung truyền 9 bit, 8 bit đầu tiên được chứa trong thanh ghi dữ liệu UDR như thông thường và bit thứ cao nhất là bit TXB8 trong thanh ghi USCRB (trường hợp phát) hay bit RXB8 trong thanh ghi UCSRB (trường hợp thu).
      - Bit MPCM (bit 0) trong thanh ghi UCSRA cho phép một chip làm việc ở chế độ Master-Slave. Tuy nhiên bit này chỉ  có tác dụng ở chip Slaves, để một chip làm việc như một Slave (chờ lệnh từ Master) thì bit MPCM của chip này phải được set lên 1. Bit MPCM của Master không cần set.
     Cơ chế làm việc của chế độ Master-Slaves được giải thích như sau: lúc đầu, các bit MPCM trên tất cả các Slaves đều được set lên 1, ngắt nhận dữ liệu RXCIE của các  Slaves được kích hoạt và chúng đang ở chế độ chờ “lệnh” từ Master. Khi chip Master muốn thực hiện một “cuộc gọi” với một Slave nào đó, nó sẽ phát ra một “gói địa chỉ” bao gồm 8 bits chứa địa chỉ của Slave cần gọi và bit cao nhất (TXB8) luôn bằng 1 (xem hình 1).

UART vs RS232

Hình 1. Gói địa chỉ.

     Khi tất cả 9 bit được các Slaves nhận, bit cao nhất sẽ được Slaves chứa trong bit RXB8. Nếu bit này bằng 1 các Slaves biết rằng đây là gói địa chỉ, ngắt RXCIE sẽ xảy ra trên tất cả các Slaves. Quá trình này được chip thực hiện một cách hoàn toàn tự động. Trong trình phục vụ ngắt RXCIE (SIG_UART_RECV) người lập trình sẽ thực hiện so sánh giá trị 8 bits địa chỉ nhận về với địa chỉ của từng Slave. Nếu một Slave nhận thấy địa chỉ mà Master gởi khớp với địa chỉ của nó, người lập trình cần reset bit MPCM về 0 để tách Slave này ra khỏi chế độ chờ (chờ địa chỉ). Tiếp theo Master sẽ gởi liên tiếp các “gói dữ liệu” trên đường truyền. Khác với gói địa chỉ, bit cao nhất (TXB8) trong gói dữ liệu bằng 0 chứ không bằng 1. Trên chip Master, người lập trình cần viết 2 đoạn chương trình phát gói địa chỉ và gói dữ liệu riêng biệt. Đối với các Slaves, do bit cao nhất nhận về RXB8=0, ngắt RXCIE chỉ duy nhất xảy ra trên Slave có bit MPCM=0. Như thế, tất cả các Slaves khác sẽ bỏ qua gói này (ngắt RXCIE không xảy ra, không ảnh hưởng đến các việc khác) chỉ duy nhất Slave có địa chỉ trùng trước đó nhận dữ liệu. Một chú ý rất quan trọng là sau khi byte dữ liệu cuối cùng được nhận, Slave (chip được chọn) phải set lại bit MPCM lên 1 (do người lập trình thực hiện) để đưa Slave trở lại trạng thái chờ các cuộc gọi tiếp theo.
     Như vậy, bằng cách nào đó Slave phải biết trước được số lượng bytes dữ liệu mà Master muốn gởi để kịp thời set bit MPCM lên 1 sau byte cuối. Có một số cách để biết trước số lượng bytes mà Master sẽ gởi như “thỏa thuận” trước số bytes cố định cho mỗi cuộc gọi; hoặc đơn giản Master dùng byte dữ liệu đầu tiên (sau byte địa chỉ) để báo số lượng bytes sẽ gởi tiếp theo; hoặc hay hơn có thể ghép thông số chỉ lượng bytes cần truyền vào gói địa chỉ nếu như không có quá nhiều Slaves trên mạng và số lượng bytes truyền cũng không quá lớn. Nhưng dù cách nào đi nữa, cần có sự “thỏa thuận” khi lập trình cho Master và Slave. Có một “dấu hiệu” khác có thể được dùng để phân biệt giữa gói dữ liệu và gói địa chỉ đó là trạng thái bit RXB8, bằng việc kiểm tra trạng thái bit này chúng ta sẽ biết được gói nào là dữ liệu (RXB8=0) và gói nào là địa chỉ (RXB8=1). Tuy nhiên cách này không nhận biết được byte dữ liệu cuối cùng được gởi vì vậy không được sử dụng để set bit MPCM lên 1.

II. Sử dụng Multi-Processor. 

     Trong ví dụ bài này tôi dùng phương pháp đơn giản là “thỏa thuận” trước giữa Master và Slave số lương bytes trong một lần truyền, cụ thể chúng ta sẽ thiết lập một mạng Master-Slaves với 1 Master và 2 Slaves. Các Slave có địa chỉ lần lượt là 1 và 2, chúng ta dùng 2 chân PC0 và PC1 để set địa chỉ cho Slaves (việc này giúp chúng ta có thể sử dụng 1 chương trình chung cho 2 Slaves). Master chỉ đơn giản gởi đến mỗi Slave 1 gói địa chỉ và 2 bytes dữ liệu. Các Slaves sẽ hiển thị 2 bytes dữ liệu lên 2 dòng của LCD. Mạch điện mô phỏng ví dụ trình bày trong hình 2. 

UART vs RS232

Hình 2. Ví dụ mạng Master-Slaves dùng UART.

Chúng ta cần viết 2 đoạn chương trình riêng cho Master và Slaves. Đoạn chương trình cho Master được trình bày trong List1.
List 1. Chương trình cho Master.

UART vs RS232 

     Với chip Master, như đã trình bày chúng ta cần viết riêng 2 đoạn chương trình con phục vụ phát gói dữ liệu và gói địa chỉ. Trong list 1, hai đoạn chương trình này có tên uart_char_tx uart_address_tx nằm từ dòng 38 đến 48. Đây chỉ đoạn code phát uart thông thường (xem bài AVR5 – Giao tiếp UART) cộng thêm với việc set và reset bit TXB8. Trong đoạn chương trình phát gói dữ liệu, bit TXB8 được reset về 0 bằng câu lệnh UCSRB &= ~(1<<TXB8); trong khi ở đoạn chương trình phát gói địa chỉ bit này được set lên 1, UCSRB |= (1<<TXB8); (chú ý bit TXB8 nằm trong thanh ghi USCRB).
     Phần cài đặt cho UART (từ dòng 14 đến dòng 20) bạn đọc hãy xem lại bài AVR5. Chú ý đến các dòng từ 24 đến 30. Đây là phần gởi địa chỉ và dữ liệu đến các Slave. Trước khi muốn gởi dữ liệu đến Slave1, chúng ta cần gọi chương trình con phát địa chỉ uart_address_tx(1) như trong dòng 24, tiếp theo là phát 2 bytes dữ liệu theo cách thông thường (ví dụ byte1=200, byte2=123). Tương tự chúng ta có thể phát2 bytes dữ liệu đến Slave2 theo cách này (dòng 28, 29 và 30).

List 2. Chương trình cho Slaves.
UART vs RS232

     Do chúng ta sử dụng TextLCD để hiển thị kết quả nhận về từ Master, cần include thư viện myLCD.h (dòng 6). Thư viện stdio.h chứa các hàm xử lí chuỗi ký tự giúp ích cho việc hiển thị LCD (chúng ta sẽ dùng hàm sprintf) nên cũng cần được include vào (dòng 5). Biến my_address chứa địa chỉ của Slave, u_data chứa giá trị nhận về từ UART, biến ind là chỉ số chỉ số bytes nhận về. Gói dữ liệu nhận về chứa trong mảng alldata[3], mảng dis[5] là mảng ký tự tạm thời hiển thị lên LCD (xem các khai báo biến trong 2 dòng 14. 15). Địa chỉ Slave do 2 chân PC0 và PC1 quyết định, việc đọc đỉa chỉ này được thực hiện với dòng lệnh my_address=PINC & 0x03. Bằng cách chọn địa chỉ “động” như thế chúng ta không cần viết riêng chương trình cho mỗi Slave.
     Các dòng lệnh từ 24 đến 30 cài đặt thông số cho UART, chú ý cần cho phép ngắt RXCIE xảy ra dòng 29 và 30). Phần nội dung quan trọng nhất được viết trong trình phục vụ ngắt ISR(SIG_UART_RECV) (từ dòng 44 đến 60). Khi một ngắt RXCIE việc đầu tiên cần làm là đọc giá trị nhận về vào biến u_data (dòng 45), nếu đây là byte đầu tiên nhận về (tức ind=0, byte địa chỉ) thì chúng ta cần so sánh xem địa chỉ có khớp không (dòng 47). Nếu đúng là địa chỉ của Slave này thì cần reset bit MPCM về 0 để sẵn sàng nhận dữ liệu (dòng 48), tăng biến ind lên 1. Nếu byte nhận về không phải là byte đầu tiên mà là byte dữ liệu (biến ind khác 0) chúng ta sẽ gán byte nhận về vào mảng alldata và tăng biến chỉ số ind (các dòng từ 52 đến 54). Vì trong ví dụ này chúng ta thỏa thuận trước Master chỉ gởi 2 bytes dữ liệu đến mỗi Slave nên khi biến ind bằng 3, tức là đã nhận đủ 2 bytes dữ liệu chúng ta cần set lại bit MPCM để kết thúc quá trình nhận, đưa Slave về lại trạng thái chờ, đồng thời trả biến chỉ số ind về 0 (làm lại từ đầu) (xem các dòng 55 đến 57).
     Khi mô phỏng, bạn hãy nạp chương trình trong List 1 cho chip Master và list2 cho 2 Slaves. Cần set xung clock 8MHz. Nếu bạn thực hiện đúng kết quả sẽ hiển thị như trong hình 1.