Thứ Tư, 24 tháng 10, 2012

Giao Tiếp AVR Với Máy Tính, Giao Diện RS232

I. Giới thiệu

Bài viết này sẽ nói về cách giao tiếp giữa AVR và máy tính cá nhân (PC) theo một cách đơn giản nhưng khá toàn diện. Nó đơn giản vì tôi sẽ dùng một giao diện khá “cổ điển” để giao tiếp giữa AVR và PC, giao diện RS232 thông qua các cổng COM. Toàn diện vì tôi sẽ hướng dẫn các bạn từ cách mắc mạch chuyển giữa AVR và PC, cách viết chương trình giao tiếp theo chuẩn RS232 trên máy tính và trên AVR. 


Cụ thể bài này bao gồm:

- Sơ lượt sơ đồ và chức năng các chân cổng COM trên máy tính.
- Mạch chuyển kết nối AVR với PC qua cổng COM.
- Tạo cổng COM ảo trên PC cho mục đích mô phỏng.
- Sử dụng các hàm trong thư viện xuất nhập chuẩn của C như printf, scanf…trong WinAVR.
- Viết chương trình giao tiếp RS232 cho AVR.
- Sử dụng Hyper Terminal của Windows trong giao tiếp RS232.
- Viết chương trình truy xuất cổng COM trên PC (Visual C++, Visual Basic)

II. Sơ lược về cổng COM

Cổng COM hay cổng nối tiếp (COM Port, Serial Port) là cổng giao tiếp thuộc vào dạng “lão làng” trên PC, cả máy tính để bàn và Laptop. Ngày nay với sự xuất hiện và “bành trướng” của chuẩn USB thì cổng COM (và cả cổng LPT hay cổng song song) đang dần biến mất. Giao tiếp thông qua cổng COM là giao tiếp theo chuẩn nối tiếp RS232. Chuẩn này có tốc độ khá chậm nếu đem so sánh với USB. Tuy nhiên, với dân robotics hay control thì COM-RS232 lại rất được ưa chuộng vì tính đơn giản và cũng vì…sự chậm chạp này. Các cổng COM trên các máy tính hiện tại (nếu có) đa số là dạng cổng “đực” 9 chân (male 9 pins). Tuy nhiên, đâu đó vẫn còn tồn tại loại cổng COM 25 chân, loại này về hình dáng khá giống cổng LPT nhưng là loại male trong khi cổng LPT là female. Hình 1 thể hiện 2 dạng của cổng COM và bảng 1 tóm tắt chức năng các chân của cổng này.

Hình 1. Cổng COM 9 chân và 25 chân.
Bảng 1: Các chân trên cổng COM

Đáng chú ý nhất trong các chân của cổng COM là 3 chân 0V SG (signal ground), chân phát dữ liệu TxD và chân nhận dữ liệu RxD. Đây là 3 chân cơ bản phục vụ truyền thông theo chuẩn RS232 và tương thích với UART trên AVR. Các chân còn lại cũng có thể được sử dụng nếu người dùng có 1 ít kiến thức về tổ chức thanh ghi của PC. Tuy nhiên, trong đa số trường hợp giao tiếp qua cổng COM thì chỉ 3 chân trên được sử dụng.

Như đã trình bày trong bài AVR5-UART, chuẩn RS232 và UART nhìn chung là như nhau về mặt khung truyền, tốc độ baud…nhưng khác nhau về mức điện áp và cực. Xem lại ví dụ so sánh trong hình 2.

Trong chuẩn UART (trên AVR), mức 1 tương ứng điện áp cao (5V, TTL) trong khi đối với RS232 thì mức 1 tương ứng với điện áp thấp (điện áp âm, có thể -12V). Như thế rõ ràng cần một “cầu chuyển” (converter) kết nối giữa 2 chuẩn này. May mắn là chúng ta không cần phải tự thiết kế cầu chuyển này vì đã có các IC chuyên dụng. MAX232 là một trong các IC chuyển UART-RS232 được sử dụng nhiều nhất. Tất nhiên, bạn hoàn toàn có thể tự tạo một mạch chuyển đơn giản chỉ với một vài linh kiện như tụ điện, điện trở, diode và transisotor nhưng tính ổn định thì tôi không đảm bảo. Hình 3 mô tả cách dùng IC Max232 để kết nối giữa UART trên AVR và cổng COM của PC.

Hình 3. Kết nối AVR với PC thông qua Max232.
Mạch điện trên chỉ có tác dụng thay đổi mức điện áp cho phù hợp giữa RS232 và UART, nó hoàn toàn không làm thay đổi phương thức giao tiếp của các chuẩn này và vì thế việc lập trình trên PC và AVR đều không có gì thay đổi. Thật ra Max232 có đến 2 cầu chuyển, trong hình 3 chúng ta chỉ sử dụng cầu chuyển 1. Chân phát TxD (chân 3) của cổng COM được nối với chân R1IN (Receive 1 Input) của Max232 thì tương ứng chân R1OUT (Receive 1 Output) phải nối với chân nhận RX của AVR. Tương tự cho trường hợp T1IN và T1OUT. Giá trị các tụ điện 10uF là tương đối chuẩn, tuy nhiên khi bạn thay bằng tụ 1uF thì mạch vẫn hoạt động nhưng khoảng cách truyền (cab nối) sẽ ngắn hơn (nếu dài quá sẽ phát sinh lỗi truyền thông). Các điện trở trong hình 3 chỉ có tác dụng làm bảo vệ cổng COM và các IC, bạn có thể không cần dùng các điện trở này vẫn không ảnh hưởng hoạt động của mạch. VCC và GND là nguồn của mạch AVR.

Chú ý: nếu muốn thực hiện giao tiếp giữa 2 máy tính với nhau thông qua cổng COM, bạn cần dùng1 cab chéo (chân TxD của PC1 nối với RxD của PC2 và ngược lại) để nối 2 cổng COM lại với nhau.

III. Tạo cổng COM ảo cho mô phỏng

Muốn thực hiện giao tiếp giữa AVR và PC thông qua cổng COM thì hiển nhiên bạn cần có cái cổng COM, ngoài ra bạn cần tự làm một mạch AVR và cầu chuyển Max232. Thật không may là không phải máy tính nào cũng có cổng này, nếu bạn chỉ muốn học cách giao tiếp AVR-PC hoặc chỉ muốn kiểm tra một giải thuật nào đó thì có lẽ mô phỏng là giải pháp được ưa thích hơn. Cho mục đích mô phỏng giao tiếp RS232, Proteus lại một lần nữa hữu ích khi cho phép mô phỏng truyền nhận dữ liệu với cổng COM. Như thế vấn đề còn lại là làm sao tạo các cổng COM ảo trên máy tính và kết nối chúng với nhau để thực hiện mô phỏng giao tiếp. Do tính chất của các cổng COM là chỉ được “mở” (open) 1 lần duy nhất, nghĩa là 2 phần mềm không thể cùng mở 1 cổng. Ý tưởng của chúng ta là tạo ra 2 cổng COM ảo được “nối chéo” sẵn với nhau (ví dụ COM2 và COM3). Trong phần mềm Proteus ngõ ra của UART được nối với COM2. Trong phần mềm trên PC (ví dụ Hyper Terminal) chúng ta kết nối với COM3. Bằng cách này chúng ta đã có thể thực hiện giao tiếp giữa AVR (mô hình Proteus) với PC (phần mềm Hyper Terminal).
Có một vài phần mềm tốt có khả năng tạo cổng COM ảo và kết nối ảo giữa chúng đúng như yêu cầu của chúng ta. Trong phần này tôi sẽ giới thiệu 2 phần mềm như thế, trong đó có 1 phần mềm miễn phí (Virtual Serial Port Emulator) và 1 phần mềm thu phí (Eltima Virtual Serial Port Driver).

Virtual Serial Port Emulator (VSPE): là một phần mềm tạo cổng COM và kết nối ảo tốt của Eterlogic. Điều đặc biệt là phiên bản dành cho Windows 32 bits hoàn toàn miễn phí, vì vậy đây là phần mêm đầu tiên bạn phải để ý khi muốn tạo dùng cho mục đích học tập.
Trước tiên bạn hãy download phần mềm VSPE bản mới nhất tại website chính thức của Eterlogic:http://www.eterlogic.com/Products.VSPE.html (nhấn vào nút Download ở phía cuối trang web). Giải nén file zip và chạy file SetupVSPE.exe để cài đặt. Sau khi cài đặt hãy tìm và chạy chương trình VSPE. Giao diện của VSPE như trong hình 4.

Hình 4. Giao diện phần mềm VSPE.
Sử dụng VSPE khá đơn giản, bạn chỉ việc nhấn vào nút “Create New Device” được tô đỏ trong hình 4, hoặc vào menu “Device” và chọn “Create”, để tạo 1 cổng COM ảo. Trong hộp thoại “Specify device type” bạn chọn như bên dưới và nhấn Next. Sau đó bạn có thể chọn tên cho cổng COM mình muốn tạo trong (ví dụ COM2).

Hình 5. Tạo cổng COM2 ảo bằng VSPE.
Bạn có thể tiến hành tạo bao nhiêu cổng COM ảo tùy thích. Ví dụ bạn tạo 2 cổng COM2 và COM3, bước tiếp theo chúng ta sẽ “đấu chéo” 2 cổng này với nhau để mô phỏng việc truyền dữ liệu qua RS232. Từ giao diện chính của VSPE bạn nhấn tiếp vào nút “Create new Device…”. Lần này, trong hộp thoại “Specify device type” bạn không chọn Connector nữa mà chọn “Serial Redirector” như trong hình 6. Sau đó nhấn next, chọn 2 cổng COM ảo đã tạo lúc trước và nhấn vào nút Finish.

Hình 6. Tạo kết nối giữa 2 cổng COM.
Sau khi hoàn tất bạn sẽ thấy các cổng COM ảo và kết nối giữa chúng được thể hiện trong giao diện VSPE như ở hình 7. Bạn có thể “minimize” giao diện VSPE để ẩn nó vào taskbar. Chú ý là nếu bạn đóng chương trình VSPE lại (tắt) thì các cổng COM ảo cũng biến mất.

Hình 7. Các cổng COM ảo và kết nối tạo bằng VSPE.
Virtual Serial Port Driver (VSPD): là một phần mềm tạo cổng COM và kết nối ảo tốt của Eltima Software. Đây là phần mềm có thu phí, bạn có thể download bản dùng thử 14 ngày tại website chính thức của Eltima Software: http://www.eltima.com/products/vspdxp/
So với VSPE thì VSPD dễ sử dụng và ổn định hơn (vì là phần mềm thương mại). Sau khi download bản trial và tiến hành cài đặt, bạn hãy tìm và chạy file “Configure Virtual Serial Port Driver”. Giao diện của VSPD như trong hình 8.

Hình 8. Giao diện phần mềm VSPD.
Trong tab “Manager ports” phần mềm tự động đề nghị 1 cặp cổng COM ảo có thể được tạo ra, bạn có thể chọn lại tùy thích và nhấn “Add pair” để tạo 2 cổng COM này. Khác với VSPE, cổng COM ảo do VSPD tạo ra sẽ xuất hiện trong “Device list” của Windows và không bị mất đi khi người dùng tắt phần mềm VSPD. Hãy chạy trình “Device manager” của Windows, trong mục Ports (COM & LPT) bạn sẽ thấy các cổng COM ảo được tạo thành (xem ví dụ trong hình 9).

Hình 9. Các cổng COM ảo và kết nối giữa chúng được tạo bởi VSPD.
IV. Sử dụng thư viện xuất nhập chuẩn stdio.h trong WinAVR
Những ai đã từng học ngôn ngữ lập trình C chắc sẽ không quên chương trình “hello world” đầu tiên của mình:
Chương trình này chỉ làm 1 việc đơn giản là in dòng chữ “hello, world” lên màn hình. Việc in dòng chữ được thực hiện bởi lệnh “printf” trong dòng 3. Lệnh printf nằm trong thư viện stdio gọi là thư viện xuất nhập chuẩn (standard input/output). Lệnh printf trong stdio không chỉ được dùng để in lên màn hình mà có thể in lên bất kỳ thiết bị xuất nào (output device), ngay cả in ra 1 file trên ổ cứng máy tính…Cho AVR, nếu bạn sử dụng trình dịch CodevisionAVR của HPinfotech, khi bạn gọi lệnh printf thì chuỗi dữ liệu sẽ được in ra (xuất ra) module UART (tất nhiên bạn phải cài đặt các thanh ghi của UART để kích hoạt UART trước). Như thế CodevisionAVR tự hiểu UART là thiết bị xuất/nhập mặc định cho các lệnh trong thư viện stdio (printf, scanf…). Tuy nhiên, với WinAVR (avr-gcc) mọi chuyện lại khác, để sử dụng các lệnh xuất nhập chuẩn chúng ta cần khai báo một thiết bị xuất nhập và hàm xuất nhập “cơ bản”. Hàm xuất nhập cơ bản là hàm do người dùng định nghĩa, nhiệm vụ của nó là xuất (hoặc nhập) một ký tự ra một thiết bị xuất nhập nào đó. Ví dụ trong bài AVR5 - giao tiếp UART chúng ta định nghĩa một hàm “uart_char_tx” xuất ký tự ra UART như sau:

Hoặc trong bài TextLCD chúng ta khảo sát hàm “putChar_LCD” xuất một ký tự ra LCD như bên dưới:

Cả 2 hàm “uart_char_tx” và “putChar_LCD” như ví dụ trên đều có thể được dùng làm hàm xuất nhập “cơ bản” cho các hàm như printf...trong thư viện xuất nhập chuẩn sdtio. Nếu giả sử hàm “uart_char_tx” được dùng thì khi gọi hàm hàm printf, chuỗi dữ liệu sẽ được xuất ra UART. Ngược lại, trường hợp hàm “putChar_LCD” được sử dụng như hàm cơ bản thì hàm printf của stdio sẽ xuất chuỗi dữ liệu lên LCD. Bằng phương thức này, trình dịch avr-gcc cho phép chúng ta tiếp cận thư viện stdio một cách mềm dẻo hơn, bạn có thể dùng các hàm của stdio để xuất/nhập dữ liệu vào bất kỳ thiết bị nào như UART terminal, TextLCD, Graphic LCD hay thậm chí SD, MMC card…một khi bạn định nghĩa được hàm xuất nhập “cơ bản”. 

Để minh họa cho cách sử dụng các hàm trong thư viện stdio, tôi sẽ trình bày một ví dụ xuất dữ liệu ra TextLCD và uart bằng các hàm printf…của stdio. Mạch điện mô phỏng cho ví dụ được này thể hiện trong hình 10 bên dưới.

Hình 10. Mô phỏng ví dụ xuất dữ liệu với thư viện stdio.
Tất cả các dữ liệu hiển thị trên LCD và uart terminal trong hình 10 đều được thực hiện thông qua các hàm printf và fprintf. Ngoài ra trong ví dụ này, người dùng có thể nhập 1 ký tự từ bàn phím và mã ASCII của phím đó sẽ được in ra trên Terminal. Đoạn code trình bày trong List1.
List 1. Xuất dữ liệu ra LCD và UART bằng thư viện xuất nhập chuẩn stdio

Để sử dụng các hàm trong thư viện xuất nhập chuẩn, chúng ta cần include file header của thư viện như trong dòng code 4 “#include <stdio.h>”. Chú ý khi sử dụng avr-gcc, các hàm liên quan đến avr (avr-libc) nằm trong thư mục con “/avr/” của thư mục include nên khi kính kèm phải chỉ rõ thư mục con này. Ví dụ header io.h hoặc interrupt.h chứa các hàm chuyên biệt cho avr, khi đính kèm các file này chúng ta ghi cụ thể như: “#include <avr/io.h>”…Tuy nhiên, các file header của ngôn ngữ C chuẩn (như stdio.h, math.h, …) thì nằm trực tiếp ở thư mục include, khi đính kèm các file này phải ghi trực tiếp như trong dòng code 4. Ngoài ra do ví dụ này có sử dụng LCD, bạn cần copy và include thư viện myLCD.h như trong dòng 5 (xem lại bài TextLCD).

Như đã trình bày ở trên, để sử dụng các hàm trong stdio chúng ta cần có các hàm xuất/nhập cơ bản. Các dòng code từ 7 đến 11 là hàm xuất dữ liệu ra uart có tên “uart_char_tx”, hàm này sẽ được dùng làm hàm cơ bản cho các hàm xuất của stdio sau này. Thực chất hàm “uart_char_tx” đã được trình bày trong bài học về UART, ở đây có một thay đổi nhỏ là dòng code 8 “if (chr==’\n’) uart_char_tx(‘\r’)”, dòng này có nghĩa là khi gặp người dùng muốn xuất ra “ký tự” ‘\n’ thì hàm “uart_char_tx” sẽ xuất ra thêm ký tự ‘\r’ . Như thế, nếu sau này bắt gặp một dấu hiệu xuống dòng ‘\n’ (có mã ASCII là 10, gọi là Line Feed – LF) ở cuối câu thì một tổ hợp mã '\r'+'\n' (mã '\r' = 13 gọi là Carriage Return – CR) sẽ được gởi để thực hiện xuống dòng. Để nắm rõ hơn vấn đề này bạn tìm hiểu thêm về CRLF (Carriage Return Line Feed) trong Windows.

Hai dòng code 13 và 14 rất quan trọng khi muốn sử dụng thư viện stdio. Ý nghĩa của 2 dòng này là tạo 2 “FILE” ảo (hay còn gọi là stream) dành cho việc xuất dữ liệu. Chúng ta khảo sát dòng 14: tạo stream cho UART. 

static FILE uartstd= FDEV_SETUP_STREAM(uart_char_tx, NULL,_FDEV_SETUP_WRITE);
Chúng ta tạo 1 biến tên uartstd (người dùng tự đặt tên tùy ý) có kiểu là FILE (một dạng thiết bị ảo), sau đó dùng macro “FDEV_SETUP_STREAM” để khởi tạo và cài đặt các thông số cho uartstd. Macro này có chức năng mở 1 thiết bị xuất nhập (fdevopen) và gán các “công cụ” cho việc xuất nhập ra thiết bị.
#define FDEV_SETUP_STREAM(put, get, rwflag)

Các thông số kèm theo “FDEV_SETUP_STREAM” bao gồm 1 hàm cơ bản gọi là “put”, một hàm cơ bản gọi là “get” và một cờ chỉ chức năng xuất hoặc nhập của thiết bị được mở. Cụ thể, trong dòng code 13, biến uartstd là một “thiết bị ảo” được dùng cho việc xuất dữ liệu (do thông số _FDEV_SETUP_WRITE). Công cụ để xuất ra uartstd là hàm “uart_char_tx” mà chúng ta đã tạo phía trên. Không có hàm nhận dữ liệu về từ uartstd (thông số get = NULL). Bạn có thể hình dung thế này: biến uartstd là một tờ giấy, hàm “uart_char_tx” là một “con dấu” (stamp) cho phép in một ký tự lên tờ giấy uartstd. Chúng ta gán “uart_char_tx” cho usrtstd thì sau này tất cả việc in ấn lên tờ giấy uartstd sẽ do “con dấu” “uart_char_tx” thực hiện. Hàm “uart_char_tx” vì thế gọi là hàm xuất cơ bản.
Tương tự như thế, trong dòng code 13 chúng ta tạo 1 “tờ giấy” khác tên lcdstd và hàm cơ bản cho nó lá hàm “putChar_LCD”, hàm này đã được định nghĩa sẵn trong thư viện myLCD.h. 
 
Các dòng code trong chương trình chính từ dòng 17 đến 25 dùng khởi động UART và TextLCD, bạn có thể xem lại các bài liên quan để hiểu thêm. Sau khi khởi động, UART và LCD đã sẵn sàng cho việc xuất dữ liệu. Bây giờ chúng ta có thể dùng các hàm trong thư viện stdio như printf hay sprint…để xuất dữ liệu. Bạn hay quan sát hình 10 vì tôi sẽ dùng nó để so sánh đối chiếu với các dòng code sau. Dòng 27 “printf("In lan 1")”, mục đích là in chuỗi “In lan 1” lên LCD bằng hàm printf. Tuy nhiên, xem trên hình 10 bạn không nhìn thấy chuỗi này xuất hiện. Xem tiếp dòng code 28 “fprintf(&lcdstd," www.hocavr.com ")” và xem lại hình 10, lần này bạn đã thấy chuỗi ký tự “www.hocavr.com” xuất hiện trên LCD, nghĩa là việc in đã thành công với hàm fprintf. Hàm fprintf là hàm xuất dữ liệu ra một thiết bị ảo, trong đó tham số thứ nhất của hàm trỏ đến thiết bị và tham số thứ hai là chuỗi dữ liệu cần in. Trong trường hợp này chúng ta đã dùng fprintf để xuất chuỗi “www.hocavr.com” ra thiết bị ảo lcdstd và đã thành công. Vậy với hàm printf ở dòng 27 thì sao? Hãy khảo sát tiếp các dòng từ 30 đến 32. Dòng 30 chúng ta lại một lần nữa dùng hàm printf “printf("In lan 3")” để in dòng “In lan 3” lên LCD nhưng vẫn không thành công (xem LCD trong hình 10). Ở dòng 31 chúng ta gán “stdout=&lcdstd” trong đó stdout là một biến (thật ra là 1 stream hay một thiết bị ảo) có sẵn của ngôn ngữ C, biến này qui định thiết bị mặc định dùng cho việc xuất nhập dữ liệu, khi gán stdout trỏ đến lcdstd như dòng 31 nghĩa là chúng ta khai báo LCD là thiết bị xuất nhập mặc định. Vì vậy, trong dòng 32 chúng ta gọi hàm printf “printf("In lan 4: %i", x)” chúng ta đã thành công. Lần này, quan sát trên LCD bạn sẽ thấy dòng “In lan 4: 8205” xuất hiện. Ở đây 8205 là giá trị của biến x trong câu lệnh ở dòng 32. Tóm lại, hàm fprintf cho phép in trực tiếp ra một thiết bị ảo được chỉ định trong khi đó muốn dùng hàm printf chúng ta cần gán thiết bị xuất nhập mặc định trước cho biến stdout. Hãy quan sát đoạn code từ dòng 34 đến 37 và ba dòng đầu trong Terminal ở hình 10, chắc chắn bạn đã tự lý giải được các dòng code này.
Cuối cùng là trình phục vụ ngắt nhận dữ liệu của UART trong các dòng code từ 41 đến 44. Trong trình này, chúng ta chỉ thực hiện việc đơn giản là in dòng “Ma ASCII:” kèm theo đó là giá trị nhận về từ UART chứa trong thanh ghi UDR: “fprintf(&uartstd,"Ma ASCII: %i\n", UDR)”.
Để tìm hiểu đầy đủ về thư viện stdio trong WinAVR bạn cần đọc tài liệu “avr-libc Manual”, phần Standard IO facilities. 
 
Trong bài 2 chúng ta sẽ tìm hiểu về Terminal trên máy tính và cách viết chương trình giao tiếp trên máy tinh bằng Visual Basic và Visual C++ 6.

(Sưu Tầm)

Không có nhận xét nào:

DBS M05479
Quang Cao