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

[Lập trình PIC] Truyền Thông Nối Tiếp RS232 - Xử Lý Chuỗi Trong CCS

+ Phần này sẽ giúp bạn viết chương trình có sử dụng giao tiếp với máy tính (PC) . Điều này rất cần thiết khi bạn muốn VĐK khi hoạt động có thể truyền dữ liệu cho PC xử lý , hoặc nhận giá trị từ PC để xử lý và điều khiển ( dùng PC điều khiển động cơ , nhiệt độ , hay biến PC thành dụng cụ đo các đại lượng điện, Oscilocope , . . .) .
+ Viết chương trình lập trình cho VĐK để giao tiếp máy tính là công việc rất phức tạp khi viết bằng ASM , rất khó hiểu đối với những người mới bắt đầu lập trình . Đặc biệt là khi viết cho những con VĐK không hỗ trợ từ phần cứng ( 8951 thì phải (?) ) . Thật may là phần lớn PIC hiện nay đều hỗ trợ phần này nên việc lập trình có dễ dàng hơn . Nhưng nếu chương trình của bạn yêu cầu truyền hay nhận nhiều loại dữ liệu ( số 8 , 16 ,32 bit , dương , âm , chuỗi , . . .) thì việc viết chương trình xử lý và phân loại chúng là điều “ kinh dị “ .



+ Nếu bạn đã lập trình ASM cho vấn đề này rồi thì bạn sẽ thấy sao dễ dàng quá vậy khi giải quyết vấn đề này với C khi dùng CCS . Rất đơn giản ! CCS cung cấp rất nhiều hàm phục vụ cho giao tiếp qua cổng COM và vô số hàm xử lý chuỗi . Chương này sẽ giải quyết điều đó .
+ Một yếu tố quan trọng là khi nào thì VĐK biết PC truyền data => có thể lập trình bắt tay bằng phần mềm hay đơn giản là dùng ngắt . Các ví dụ về ngắt , xem phần ngắt .




I / TRUYỀN THÔNG VỚI PC QUA CỔNG COM :
+ Để sử dụng giao thức này , phải có 2 khai báo như ví dụ sau :
#use delay (clock = 40000000 ) // nếu VDK đang dùng OSC 40Mhz
#use rs232 (baud=19200 , parity=n , xmit=pin_C6 , rcv=pin_C7 )
// baud= 19200 , không chẵn lẻ , chân truyền C6 , chân nhận C7


+Các hàm liên quan :
Printf ( )
Getc ( ) putc ( )
Getch ( ) putchar ( )
Getchar ( ) fputc ( )
Fgetc ( ) puts ( )
Gets ( ) fputs ( )
Fgets ( )
Kbhit ( )
Assert ( ) => mới trên CCS 3.222
Perror ( ) => mới trên CCS 3.222
Set_uart_speed ( )
Setup_uart ( )



+ Tất cả các hàm trên đòi hỏi phải khai báo chỉ thị tiền xử lý #use RS232 ( . . . . .) . Chi tiết chỉ thị này xem phần Chỉ thị tiền xử lý .
+ Hàm perror ( ) đòi hỏi thêm #include<errno.h > . Hàm assert() đòi hỏi thêm #include<assert.h> .


1 / printf ( string )
Printf ( cstring , values . . . )
+ Dùng xuất chuỗi theo chuẩn RS232 ra PC .
+ string là 1 chuỗi hằng hay 1 mảng ký tự ( kết thúc bởi ký tự null ) .
+ value là danh sách các biến , cách nhau bởi dấu phẩy .
+ Bạn phải khai báo dạng format của value theo kiểu %wt .Trong đó w có thể có hoặc không, có giá trị từ 1-9 chỉ rõ có bao nhiêu ký tự được xuất ra ( mặc định không có thì có bao nhiêu ra bấy nhiêu ), hoặc 01-09 sẽ chèn thêm 0 cho đủ ký tự hoặc 1.1-1.9 cho trường hợp số thực . còn t là kiểu giá trị .
+ t có thể là :
C : 1 ký tự
S : chuỗi hoặc ký tự
U : số 8 bit không dấu
x : số 8 bit kiểu hex ( ký tự viết thường ,VD : 1ef )
X : số 8 bit kiểu hex ( ký tự viết hoa ,VD : 1EF )
D : số 8 bit có dấu
e : số thực có luỹ thừa VD : e12

f : số thực
Lx : số hex 16 /32 bit ( ký tự viết thường )
LX : hex 16 /32 bit ( ký tự viết hoa )
Lu : số thập phân không dấu
Ld : số thập phân có dấu
% : ký hiệu %
VD :
Specifier Value=0x12 Value=0xfe
%03u 018 254
%u 18 254
%2u 18 *
%5 18 254
%d 18 -2
%x 12 Fe
%X 12 FE
%4X 0012 00FE
* Result is undefined - Assume garbage.
VD :
Int k =6 ;
Printf ( “ hello “ );
Printf ( “ %u “ , k );


2 / KBHIT ( ) :
+ Thường thì chúng ta dùng RC6 và RC7 cho RX và TX trong giao tiếp cổng COM , VDK PIC trang bị phần cứng phục vụ việc này với thanh ghi gởi và nhận và các bit bào hiệu tương ứng . Do đó khi dùng RS232 hỗ trợ từ phần cứng thì KHBIT ( ) trả về TRUE nếu 1 ký tự đã được nhận ( trong bộ đệm phần cứng ) và sẵn sàng cho việc đọc , và trả về 0 nếu chưa sẵn sàng .
+ Hàm này có thể dùng hỏi vòng xem khi nào có data nhận từ RS232 để đọc .


Nguồn : TRẦN XUÂN TRƯỜNG

[Lập trình ARM-LPC2378] Truyền Thông Nối Tiếp Không Đồng Bộ- UART

Nội dung
- - Giới thiệu về UART
- Các thanh ghi liên quan tới UART
- Cấu hình PINSEL
- Sử dụng Hyper Terminal


Giới thiệu về UART
Cổng COM









UART
+ + Viết tắt của: Universal Asynchronous Receiver/ Transmitter - Bộ thu/ phát không đồng bộ đa năng, là một loại thu/ phát không đồng bộ dùng để thực hiện việc chuyển đổi dữ liệu song song thành dữ liệu nối tiếp và truyền nhận nối tiếp giữa các bên phát và thu.
- + LPC2378 hỗ trợ 4 UARTs: UART 0, UART 1, UART 2, UART 3
- + UART 0/2/3: TXD, RXD
- + UART 1: TXD, RXD, CTS, RTS, DTR, DSR, RI…
-


Các thanh ghi liên quan tới UART
Các thanh ghi
- + UnRBR ( Receive Buffer Register, 1 byte) : thanh ghi chứa byte dữ liệu nhận.
- + UnTHR ( Transmit Holding Register, 1 byte): Thanh ghi chứa byte dữ liệu sẽ được gửi.
- + UnLCD (Line Control Register, 1 byte): Thanh ghi điều khiển, quyết định định dạng dữ liệu được truyền nhận (DataLength, Stop bit, Parity…)
- + UnIER (Interrupt Identification Register, 4 bytes ): thanh ghi để active 3 ngắt sử dụng cho UART
- + UnllR ( Interrupt Identification Register, 4 bytes) : thanh ghi cho biết có ngắt đang xảy ra hay không, nếu có thì là ngắt nào
- + UnDLL ( Divisor Latch LSB Register, 1 byte): quyết định baudrate của UART
- + UnDLM (Divisor Latch MSB Register, 1 byte)


Thanh ghi UnLSR
- + Nhận dữ liệu:
- Ta xét trạng thái ở bit 0
0: Thanh ghi UnRBR rỗng
1: Thanh ghi UnRBR có chứa dữ liệu
Int getkey (void)
{
While (!(UxLSR & 0x01));
Return (UxRBR);
}
- + Truyền dữ liệu:
+ Ta xét trạng thái ở bit 5
0: Thanh ghi UnTHR có chứa dữ liệu
1: Thanh ghi UnTHR rỗng
Int sendchar (int ch)
{
While (!(UxLSR & 0x20));
Return (UxTHR = ch);
}




Cấu hình PINSEL


Cấu hình PINSEL – UARTO





Cấu hình PINSEL – UART1










Sử dụng Hyper Terminal


Hyper Terminal
Các bạn Download phần mềm Hyper Terminal hercules 3.2.4 Tại Đây!


Các thông tin truyền nhận qua cổng COM
+ Baudrate: 9600 bps ( bits per second )
+ Data length: 8 bits
+ Parity: None
+ Stop bit: 1
+ Flow control: None


Thiết lập các thông số cho UART
+ Khi sử dụng thanh ghi UxDLL và UxDLM thì bit DLAB trong thanh ghi UxLCR phải là 1.
UxLCR = 0x83; // 8 bits, no Parity, 1 Stop bit
UxDLL = 78; // 9600 Baud Rate @ 12.0 MHZ PCLK
UxDLM = 0; // High divisor latch = 0
UxLCR = 0x03; // DLAB = 0
+ Sau khi thiết lập xong cho bit DLAB = 0 để sử dụng thanh ghi UnRBR, UnTHR

(Nguồn: ĐHKHTN TP.HCM)

Cơ Bản Và Ghép Nối Về Chuẩn Giao Tiếp RS232

Ngày nay các thiết bị đo lường, điều khiển ... đều phải giao tiếp với máy tính để quan sát thông số và chế độ hoạt động của thiết bị như thế nào? Chuẩn giao tiếp được coi là đơn giản và dễ dùng đó là RS232. Hầu như các thiết bị đều được giao tiếp với máy tính thông qua chuẩn này. Bài viết này sẽ nói về cơ bản chuẩn giao tiếp RS232: Tổng quan chung về RS232, Sơ đồ ghép nối, Giao diện phần mềm.


I - Tổng quan chuẩn RS232

1) Đặt vấn đề

Vấn đề giao tiếp giữa PC và vi điều khiển rất quan trọng trong các ứng dụng điều khiển, đo lường... Ghép nối qua cổng nối tiếp RS232 là một trong những kỹ thuật được sử dụng rộng rãi để ghép nối các thiết bị ngoại vi với máy tính.Nó là một chuẩn giao tiếp nối tiếp dùng định dạng không đồng bộ, kết nối nhiều nhất là 2 thiết bị , chiều dài kết nối lớn nhất cho phép để đảm bảo dữ liệu là 12.5 đến 25.4m, tốc độ 20kbit/s đôi khi là tốc độ 115kbit/s với một số thiết bị đặc biệt. Ý nghĩa của chuẩn truyền thông nối tiếp nghĩa là trong một thời điểm chỉ có một bit được gửi đi dọc theo đường truyền.

Có hia phiên bản RS232 được lưu hành trong thời gian tương đối dài là RS232B và RS232C. Nhưng cho đến nay thì phiên bản RS232B cũ thì ít được dùng còn RS232C hiện vẫn được dùng và tồn tại thường được gọi là tên ngẵn gọn là chuẩn RS232

Các máy tính thường có 1 hoặc 2 cổng nối tiếp theo chuẩn RS232C được gọi là cổng Com. Chúng được dùng ghép nối cho chuột, modem, thiết bị đo lường...Trên main máy tính có loại 9 chân hoặc lại 25 chân tùy vào đời máy và main của máy tính. Việc thiết kế giao tiếp với cổng RS232 cũng tương đối dễ dàng, đặc biệt khi chọn chế độ hoạt động là không đồng bộ và tốc độ truyền dữ liệu thấp.

2) Ưu điểm của giao diện nối tiếp RS232

+ Khả năng chống nhiễu của các cổng nối tiếp cao
+ Thiết bị ngoại vi có thể tháo lắp ngay cả khi máy tính đang được cấp điện
+ Các mạch điện đơn giản có thể nhận được điện áp nguồn nuôi qua công nối tiếp

3) Những đặc điểm cần lưu ý trong chuẩn RS232

+ Trong chuẩn RS232 có mức giới hạn trên và dưới (logic 0 và 1) là +-12V. Hiện nay đang được cố định trở kháng tải trong phạm vi từ 3000 ôm - 7000 ôm
+ Mức logic 1 có điện áp nằm trong khoảng -3V đến -12V, mức logic 0 từ +-3V đến 12V
+ Tốc độ truyền nhận dữ liệu cực đại là 100kbps ( ngày nay có thể lớn hơn)
+ Các lối vào phải có điện dung nhỏ hơn 2500pF
+ Trở kháng tải phải lớn hơn 3000 ôm nhưng phải nhỏ hơn 7000 ôm
+ Độ dài của cáp nối giữa máy tính và thiết bị ngoại vi ghép nối qua cổng nối tiếp RS232 không vượt qua 15m nếu chúng ta không sử model
+ Các giá trị tốc độ truyền dữ liệu chuẩn : 
50,75,110,750,300,600,1200,2400,4800,9600,19200,28800,38400....56600,115200 bps

4) Các mức điện áp đường truyền

RS 232 sử dụng phương thức truyền thông không đối xứng, tức là sử dụng tín hiệu điện áp chênh lệch giữa một dây dẫn và đất. Do đó ngay từ đầu tiên ra đời nó đã mang vẻ lỗi thời của chuẩn TTL, nó vấn sử dụng các mức điện áp tương thích TTL để mô tả các mức logic 0 và 1. Ngoài mức điện áp tiêu chuẩn cũng cố định các giá trị trở kháng tải được đấu vào bus của bộ phận và các trở kháng ra của bộ phát.
Mức điện áp của tiêu chuẩn RS232C ( chuẩn thường dùng bây giờ) được mô tả như sau:

+ Mức logic 0 : +3V , +12V
+ Mức logic 1 : -12V, -3V

Các mức điện áp trong phạm vi từ -3V đến 3V là trạng thái chuyển tuyến. Chính vì từ - 3V tới 3V là phạm vi không được định nghĩa, trong trường hợp thay đổi giá trị logic từ thấp lên cao hoặc từ cao xuống thấp, một tín hiệu phải vượt qua quãng quá độ trong một thơì gian ngắn hợp lý. Điều này dẫn đến việc phải hạn chế về điện dung của các thiết bị tham gia và của cả đường truyền. Tốc độ truyền dẫn tối đa phụ thuộc vào chiều dài của dây dẫn. Đa số các hệ thống hiện nay chỉ hỗ trợ với tốc độ 19,2 kBd .
5) Cổng RS232 trên PC

Hầu hết các máy tính cá nhân hiện nay đều được trang bị ít nhất là 1 cổng Com hay cổng nối tiếp RS232. Số lượng cổng Com có thể lên tới 4 tùy từng loại main máy tính. Khi đó các cổng Com đó được đánh dấu là Com 1, Com 2, Com 3...Trên đó có 2 loại đầu nối được sử dụng cho cổng nối tiếp RS232 loại 9 chân (DB9) hoặc 25 chân (DB25). Tuy hai loại đầu nối này có cùng song song nhưng hai loại đầu nối này được phân biệt bởi cổng đực (DB9) và cổng cái (DB25)

Ta xét sơ đồ chân cổng Com 9 chân:

Trên là các kí hiệu chân và hình dạng của cổng DB9
Chức năng của các chân như sau:
+ chân 1 : Data Carrier Detect (DCD) : Phát tín hiệu mang dữ liệu
+ chân 2: Receive Data (RxD) : Nhận dữ liệu
+ chân 3 : Transmit Data (TxD) : Truyền dữ liệu
+ chân 4 : Data Termial Ready (DTR) : Đầu cuối dữ liệu sẵn sàng được kích hoạt bởi bộ phận khi muốn truyền dữ liệu
+ chân 5 : Singal Ground ( SG) : Mass của tín hiệu
+ chân 6 : Data Set Ready (DSR) : Dữ liệu sẵn sàng, được kích hoạt bởi bộ truyền khi nó sẵn sàng nhận dữ liệu
+ chân 7 : Request to Send : yêu cầu gửi,bô truyền đặt đường này lên mức hoạt động khi sẵn sàng truyền dữ liệu
+ chân 8 : Clear To Send (CTS) : Xóa để gửi ,bô nhận đặt đường này lên mức kích hoạt động để thông báo cho bộ truyền là nó sẵn sàng nhận tín hiệu
+ chân 9 : Ring Indicate (RI) : Báo chuông cho biết là bộ nhận đang nhận tín hiệu rung chuông
Còn DB28 bây giờ hầu hết các main mới ra đều không có cổng này nữa. Nên tôi không đề cập đến ở đây.

6) Quá trình dữ liệu

a) Quá trình truyền dữ liệu

Truyền dữ liệu qua cổng nối tiếp RS232 được thực hiện không đồng bộ. Do vậy nên tại một thời điểm chỉ có một bit được truyền (1 kí tự). Bộ truyền gửi một bit bắt đầu (bit start) để thông báo cho bộ nhận biết một kí tự sẽ được gửi đến trong lần truyền bit tiếp the . Bit này luôn bắt đầu bằng mức 0.. Tiếp theo đó là các bit dữ liệu (bits data) được gửi dưới dạng mã ASCII( có thể là 5,6,7 hay 8 bit dữ liệu) Sau đó là một Parity bit ( Kiểm tra bit chẵn, lẻ hay không) và cuối cùng là bit dừng - bit stop có thể là 1, 1,5 hay 2 bit dừng.

b) Tốc độ Baud

Đây là một tham số đặc trưng của RS232. Tham số này chính là đặc trưng cho quá trình truyền dữ liệu qua cổng nối tiếp RS232 là tốc độ truyền nhận dữ liệu hay còn gọi là tốc độ bit. Tốc độ bit được định nghĩa là số bit truyền được trong thời gian 1 giây hay số bit truyền được trong thời gian 1 giây. Tốc độ bit này phải được thiết lập ở bên phát và bên nhận đều phải có tốc độ như nhau ( Tốc độ giữa vi điều khiển và máy tính phải chung nhau 1 tốc độ truyền bit)

Ngoài tốc độ bit còn một tham số để mô tả tốc độ truyền là tốc độ Baud. Tốc độ Baud liên quan đến tốc độ mà phần tử mã hóa dữ liệu được sử dụng để diễn tả bit được truyền còn tôc độ bit thì phản ánh tốc độ thực tế mà các bit được truyền.Vì một phần tử báo hiệu sự mã hóa một bit nên khi đó hai tốc độ bit và tốc độ baud là phải đồng nhất

Một số tốc độ Baud thường dùng: 50, 75, 110, 150, 300, 600, 1200, 2400, 4800, 9600, 19200, 28800, 38400, 56000, 115200 … Trong thiết bị họ thường dùng tốc độ là 19200
Khi sử dụng chuẩn nối tiếp RS232 thì yêu cầu khi sử dụng chuẩn là thời gian chuyển mức logic không vượt quá 4% thời gian truyền 1 bit. Do vậy, nếu tốc độ bit càng cao thì thời gian truyền 1 bit càng nhỏ thì thời gian chuyển mức logic càng phải nhỏ. Điều này làm giới hạn tốc Baud và khoảng cách truyền.

c) Bit chẵn lẻ hay Parity bit

Đây là bit kiểm tra lỗi trên đường truyền. Thực chất của quá trình kiểm tra lỗi khi truyền dữ liệu là bổ xung thêm dữ liệu được truyền để tìm ra hoặc sửa một số lỗi trong quá trình truyền . Do đó trong chuẩn RS232 sử dụng một kỹ thuật kiểm tra chẵn lẻ.
Một bit chẵn lẻ được bổ sung vào dữ liệu được truyền để ch thấy số lượng các bit "1" được gửi trong một khung truyền là chẵn hay lẻ.

Một Parity bit chỉ có thể tìm ra một số lẻ các lỗi chả hạn như 1,3,,5,7,9... Nếu như một bit chẵn được mắc lỗi thì Parity bit sẽ trùng giá trị với trường hợp không mắc lỗi vì thế không phát hiện ra lỗi. Do đó trong kỹ thuật mã hóa lỗi này không được sử dụng trong trường hợp có khả năng một vài bit bị mắc lỗi.
Còn cách thức truyền dẫn. Phần này tôi không đề cập các bạn vui lòng xem trong giáo trình.

II - Sơ đồ ghép nối RS232

Có rất nhiều mạch giao tiếp của RS232 giữa vi điều khiển hay các thiết bị khác. Dưới đây là những mạch giao tiếp thường được dùng.

1) Mạch chuẩn giao RS232 dùng IC Max232

Max232 là IC chuyên dùng cho giao tiếp giữa RS232 và thiết bị ngoại vi. Max232 là IC của hãng Maxim. Đây là IC chay ổn định và được sử dụng phổ biến trong các mạch giao tiếp chuẩn RS232. Giá thành của Max232 phù hợp (12K hay 10K) và tích hợp trong đó hai kênh truyền cho chuẩn RS232. Dòng tín hiệu được thiết kế cho chuẩn RS232 . Mỗi đầu truyền ra và cổng nhận tín hiệu đều được bảo vệ chống lại sự phóng tĩnh điện ( hình như là 15KV). Ngoài ra Max232 còn được thiết kế với nguồn +5V cung cấp nguồn công suất nhỏ.
Mạch giao tiếp như sau :


Đây là mạch giao tiếp 1 kênh dùng Max232. Còn giao tiếp 2 kênh thì tương tự. Mạch này được sử dụng khá nhiều trong chuẩn giao tiếp RS232.

2) Mạch chuẩn giao tiếp RS232 dùng DS275

Đây cũng là IC của hãng Maxim. DS275 được dùng trong các mạch giao tiếp của chuẩn RS232 nhưng do nó chỉ là bán song công và dùng trong các thiết kế công suất nhỏ.

Mạch giao tiếp khá đơn giản. Do bán song công nên trong các ứng dụng ít được dùng.

3) Mạch chuẩn giao tiếp RS232 dùng transitor

Mạch sử dụng 2 transior để giao tiếp RS232.

III- Phần mền giao tiếp

Giao tiếp chuẩn giữa RS232 và vi điều khiển phải thông qua phần mền giao diện để nhận biết được dữ liệu truyền lên và nhận xuống như thế nào. Hiện tại có rất nhiều cách lập trình giao tiếp cho RS232 với vi xử lý nhưng mà hay dùng nhất là bộ công cụ Visual C++. Bộ công cụ này lập trình giúp lập trình giao diện thông qua cổng RS232.Ngoài bộ công cụ này còn có bộ công cụ của Delphi cũng được dùng khá nhiều.
Trong trường hợp người dùng mà không biết lập trình giao diện thì có thể sử dụng công cụ trực tiếp của windown. Đó là Hyper Terminal. Công cụ này cho ta giao diện khá đơn giản chỉ truyền nhận dữ liệu thông qua cổng RS232. Đối với Win XP thì các bạn có thể vào đây để lấy nó ra : Start/All Program/Accessories/communations/Hyper Terminal/. Thiết lập thông số quan trọng là ok.

Bộ công cụ Hyper Terminal có giao diện khá cổ điện nên người dùng khó kiểm soát được dữ liệu truyền lên nhận xuống như thế nào. Do vậy trên mạng bây giờ có bộ công cụ lập trình sẵn cho giao tiếp cổng com. Đó là phần mền Terminal ( download tại hội quán). Phần mền này có giao diện khá hơn Hyper Terminal nhưng nó chỉ có box nhận dữ liệu và truyền dữ liệu. Các bạn có thể tham khảo!

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)

Code mẫu PIC - Giao Tiếp I2C Với IC Thời gian thực DS1307

Chương trình thực hiện giao tiếp I2C giữa PIC 16F877A và IC DS1307 để cài đặt thời gian, đọc thời gian từ DS1307, hiển thị lên LCD, truyền qua RS232.
Với LCD, chương trình sẽ đọc dữ liệu DS1307 và cập nhật LCD liên tục, còn khi truyền lên máy tính sẽ dựa vào ngắt RB0 đưa vào từ xung 1Hz của DS1307, tức là mỗi 1s sẽ truyền dữ liệu 1 lần.
Trong chương trình có sử dụng các thư viện: lcd_lib_4bit.c, ds1307.c.

Sơ đồ nguyên lý mô phỏng trên Proteus:







Truyền lên hyper terminal:





Mã nguồn chương trình chính:



#include "16f877a.h"
#include "def_877a.h"


#device *=16 ADC=10
#use delay(clock=20000000)
#FUSES NOWDT, HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use i2c(Master, sda = PIN_C4, scl=PIN_C3)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)


#include "lcd_lib_4bit.c"
#include "ds1307.c"


#define Slave_add 0x68
#define Read 1
#define Write 0


void send(int8 a);


int8 sec,min,hrs,day,month,yr,dow;


//ngat o chan RB0: Truyen len cong RS232
#int_EXT
void EXT_isr(void) //moi 1s truyen len may tinh 1 lan
{
ds1307_get_date(day,month,yr,dow);
ds1307_get_time(hrs,min,sec);
send(hrs);
putc(45);
send(min);
putc(45);
send(sec);
putc(10);
return;
}
void main()
{
enable_interrupts(INT_EXT);//cho phep ngat RB0
ext_int_edge(0,H_TO_L);//dat suon ngat
enable_interrupts(GLOBAL);//cho phep ngat toan cuc

LCD_init(); //Khoi tao LCD.
delay_ms(10);
ds1307_init();// khoi tao DS1307, tao xung 1Hz o chan 7 DS1307.
// Set date : 12-4-2012
// Set time : thu 5 - 12 gio, 59 phút 10 giây
ds1307_set_date_time(12,4,12,5,12,59,10);



while(1)
{
ds1307_get_date(day,month,yr,dow);
ds1307_get_time(hrs,min,sec);

//Truyen len LCD
//o day chi hien gio, phut, giay. cac thong tin khac thuc hien tuong tu.
LCD_PutCmd(0x80);
LCD_PutChar(hrs/10+48);
LCD_PutChar(hrs%10+48);
LCD_PutChar(45);
LCD_PutChar(min/10+48);
LCD_PutChar(min%10+48);
LCD_PutChar(45);
LCD_PutChar(sec/10+48);
LCD_PutChar(sec%10+48);
}
}
void send(int8 a)
{
if(a<10)
{
putc(a+48);
}
if(a>9&&a<100)
{
unsigned char c=a/10;
unsigned char d=a%10;
putc(c+48);
putc(d+48);
}
if(a>99)
{
unsigned char t=a/100;
unsigned char c=a/10-10*t;
unsigned char d=a%10;
putc(t+48);
putc(c+48);
putc(d+48);
}
}


Thư viện lcd_lib_4bit.c:


#include <stddef.h>

#define LCD_RS PIN_D2
//#define LCD_RW PIN_A1
#define LCD_EN PIN_D3


#define LCD_D4 PIN_D4
#define LCD_D5 PIN_D5
#define LCD_D6 PIN_D6
#define LCD_D7 PIN_D7


// misc display defines-
#define Line_1 0x80
#define Line_2 0xC0
#define Clear_Scr 0x01


// prototype statements
#separate void LCD_Init ( void );// ham khoi tao LCD
#separate void LCD_SetPosition ( unsigned int cX );//Thiet lap vi tri con tro
#separate void LCD_PutChar ( unsigned int cX );// Ham viet1kitu/1chuoi len LCD
#separate void LCD_PutCmd ( unsigned int cX) ;// Ham gui lenh len LCD
#separate void LCD_PulseEnable ( void );// Xung kich hoat
#separate void LCD_SetData ( unsigned int cX );// Dat du lieu len chan Data
// D/n Cong
#use standard_io (C)
#use standard_io (D)


//khoi tao LCD**********************************************

#separate void LCD_Init ( void )
{
LCD_SetData ( 0x00 );
delay_ms(200); /* wait enough time after Vdd rise >> 15ms */
output_low ( LCD_RS );// che do gui lenh
LCD_SetData ( 0x03 ); /* init with specific nibbles to start 4-bit mode */
LCD_PulseEnable();
LCD_PulseEnable();
LCD_PulseEnable();
LCD_SetData ( 0x02 ); /* set 4-bit interface */
LCD_PulseEnable(); /* send dual nibbles hereafter, MSN first */
LCD_PutCmd ( 0x2C ); /* function set (all lines, 5x7 characters) */
LCD_PutCmd ( 0x0C ); /* display ON, cursor off, no blink */
LCD_PutCmd ( 0x06 ); /* entry mode set, increment & scroll left */
LCD_PutCmd ( 0x01 ); /* clear display */
}


#separate void LCD_SetPosition ( unsigned int cX )
{
/* this subroutine works specifically for 4-bit Port A */
LCD_SetData ( swap ( cX ) | 0x08 );
LCD_PulseEnable();
LCD_SetData ( swap ( cX ) );
LCD_PulseEnable();
}


#separate void LCD_PutChar ( unsigned int cX )
{
/* this subroutine works specifically for 4-bit Port A */
output_high ( LCD_RS );
LCD_PutCmd( cX );
output_low ( LCD_RS );
}


#separate void LCD_PutCmd ( unsigned int cX )
{
/* this subroutine works specifically for 4-bit Port A */
LCD_SetData ( swap ( cX ) ); /* send high nibble */
LCD_PulseEnable();
LCD_SetData ( swap ( cX ) ); /* send low nibble */
LCD_PulseEnable();
}
#separate void LCD_PulseEnable ( void )
{
output_high ( LCD_EN );
delay_us ( 3 ); // was 10
output_low ( LCD_EN );
delay_ms ( 3 ); // was 5
}


#separate void LCD_SetData ( unsigned int cX )
{
output_bit ( LCD_D4, cX & 0x01 );
output_bit ( LCD_D5, cX & 0x02 );
output_bit ( LCD_D6, cX & 0x04 );
output_bit ( LCD_D7, cX & 0x08 );
}


Thư viện ds1307.c:


BYTE bin2bcd(BYTE binary_value);
BYTE bcd2bin(BYTE bcd_value);


void ds1307_init(void)
{
BYTE initsec = 0;
BYTE initmin=0;
BYTE inithr=0;
BYTE initdow=0;
BYTE initday=0;
BYTE initmth=0;
BYTE inityear=0;
i2c_start();
i2c_write(0xD0); // WR to RTC
i2c_write(0x00); // REG 0
i2c_start();
i2c_write(0xD1); // RD from RTC
initsec = bcd2bin(i2c_read() & 0x7f);
initmin = bcd2bin(i2c_read() & 0x7f);
inithr = bcd2bin(i2c_read() & 0x3f);
initdow = bcd2bin(i2c_read() & 0x7f); // REG 3
initday = bcd2bin(i2c_read() & 0x3f); // REG 4
initmth = bcd2bin(i2c_read() & 0x1f); // REG 5
inityear = bcd2bin(i2c_read(0)); // REG 6
i2c_stop();
delay_us(3);


i2c_start();
i2c_write(0xD0); // WR to RTC
i2c_write(0x00); // REG 0
i2c_write(bin2bcd(initsec)); // Start oscillator with current "seconds value
i2c_write(bin2bcd(initmin)); // REG 1
i2c_write(bin2bcd(inithr)); // REG 2
i2c_write(bin2bcd(initdow)); // REG 3
i2c_write(bin2bcd(initday)); // REG 4
i2c_write(bin2bcd(initmth)); // REG 5
i2c_write(bin2bcd(inityear)); // REG 6
i2c_start();
i2c_write(0xD0); // WR to RTC
i2c_write(0x07); // Control Register
i2c_write(0x90); // squarewave output pin 1Hz
i2c_stop();


}


void ds1307_set_date_time(BYTE day, BYTE mth, BYTE year, BYTE dow, BYTE hr, BYTE min, BYTE sec)
{
sec &= 0x7F;
hr &= 0x3F;


i2c_start();
i2c_write(0xD0); // I2C write address
i2c_write(0x00); // Start at REG 0 - Seconds
i2c_write(bin2bcd(sec)); // REG 0
i2c_write(bin2bcd(min)); // REG 1
i2c_write(bin2bcd(hr)); // REG 2
i2c_write(bin2bcd(dow)); // REG 3
i2c_write(bin2bcd(day)); // REG 4
i2c_write(bin2bcd(mth)); // REG 5
i2c_write(bin2bcd(year)); // REG 6
i2c_write(0x90); // REG 7 - 1Hz squarewave output pin
i2c_stop();
}


void ds1307_get_date(BYTE &day, BYTE &mth, BYTE &year, BYTE &dow)
{
i2c_start();
i2c_write(0xD0);
i2c_write(0x03); // Start at REG 3 - Day of week
i2c_start();
i2c_write(0xD1);
dow = bcd2bin(i2c_read() & 0x7f); // REG 3
day = bcd2bin(i2c_read() & 0x3f); // REG 4
mth = bcd2bin(i2c_read() & 0x1f); // REG 5
year = bcd2bin(i2c_read(0)); // REG 6
i2c_stop();
}


void ds1307_get_time(BYTE &hr, BYTE &min, BYTE &sec)
{
i2c_start();
i2c_write(0xD0);
i2c_write(0x00); // Start at REG 0 - Seconds
i2c_start();
i2c_write(0xD1);
sec = bcd2bin(i2c_read() & 0x7f);
min = bcd2bin(i2c_read() & 0x7f);
hr = bcd2bin(i2c_read(0) & 0x3f);
i2c_stop();


}


BYTE bin2bcd(BYTE binary_value)
{
BYTE temp;
BYTE retval;


temp = binary_value;
retval = 0;


while(1)
{
// Get the tens digit by doing multiple subtraction
// of 10 from the binary value.
if(temp >= 10)
{
temp -= 10;
retval += 0x10;
}
else // Get the ones digit by adding the remainder.
{
retval += temp;
break;
}
}


return(retval);
}




// Input range - 00 to 99.
BYTE bcd2bin(BYTE bcd_value)
{
BYTE temp;


temp = bcd_value;
// Shifting upper digit right by 1 is same as multiplying by 8.
temp >>= 1;
// Isolate the bits for the upper digit.
temp &= 0x78;


// Now return: (Tens * 8) + (Tens * 2) + Ones


return(temp + (temp >> 2) + (bcd_value & 0x0f));
}
DBS M05479
Quang Cao