III. Truyền thông nối tiếp không đồng bộ với AVR (UART).
2. Sử dụng UART:.
2.1 Truyền dữ liệu.
Trước hết chúng ta sẽ thực hiện một ví dụ rất đơn giản để hiểu cách khởi động USART và truyền các gói dữ liệu 8 bit. Mạch điện mô phỏng trong hình 3. Giả sử
chúng ta muốn định dạng cho khung truyền gồm 1 bit start, 8 bit dữ liệu, không kiểm tra parity và 1 bit stop. Tốc độ baud 57600 (57.6k). Dữ liệu cần truyền là các giá trị
liên tục của bảng mã ASCII. Đoạn code trong list 1 trình bày cách thực hiện ví dụ này. List 1. Khởi động và truyền dữ liệu không đồng bộ bằng USART
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <avr/io.h> #include <avr/delay.h>
//chuong trinh con phat du lieu
void uart_char_tx(unsigned char chr){
while (bit_is_clear(UCSRA,UDRE)) {}; //cho den khi bit UDRE=1 UDR=chr;
}
int main(void){
//set baud, 57.6k ung voi f=8Mhz, xem bang 70 trang 165, Atmega32 datasheet UBRRH=0;
UBRRL=8;
//set khung truyen va kich hoat bo nhan du lieu
UCSRA=0x00;
UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); UCSRB=(1<<TXEN);
while(1){
for (char i=32; i<128; i++){ uart_char_tx(i); //phat du lieu _delay_ms(100);
} } }
Trước hết tôi sẽ giải thích cách khởi động USART trong các dòng code từ 12 đến 18. Nếu bạn xem lại bảng 3 trong trang 9 của tài liệu này (hoặc bảng 70, trang 165 datasheet của chip atmega32), ứng với tần số xung nhịp 8Hhz, không sử dụng chế độ
nhân đôi tốc độ (U2X=0), đểđạt được tốc bộ baud 57600 thì giá trị cần gán cho thanh ghi UBRR là 8 (xem cột 2, bảng 3). Hai dòng 12 và 13 trong list 1 thực hiện gán 8 cho thanh ghi UBRR thông qua 2 thanh ghi UBRRH và UBRRL. Trong dòng 16, thanh ghi UCSRA được gán bằng 0. Nếu bạn xem lại phần giải thích bạn sẽ thấy thanh ghi UCSRA chủ yếu chứa các bit trạng thái, riêng 2 bit U2X và MPCM là 2 bit điều khiển, 2 bit này bằng 0 nghĩa là chúng ta không sử dụng chếđộ nhân đôi tốc độ và không sử dụng truyền thông đa xử lí. Phần quan trọng nhất chính là đặt giá trị cho 2 thanh ghi USCRB và UCSRC. Với thanh ghi UCSRC (dòng 17) trước hết chúng ta phải set bit URSEL để báo rằng chúng ta không muốn truy cập thanh ghi UBRRH mà là thanh ghi UCSRC (2 thanh ghi này có cùng địa chỉ), tiếp theo chúng ta chỉ set 1 cho 2 bit UCSZ1 và UCSZ0, bạn xem lại bảng 2 để thấy rằng nếu UCSZ1=1, UCSZ0=1 cùng với việc bit UCSZ2=0(nằm trong thanh ghi UCSRB) thì độ dài dữ liệu truyền
được chọn là 8 bit. Các bit trong thanh ghi UCSRC không được set sẽ mặc định mang giá trị 0, bao gồm UMSEL = 0 (chế độ truyền thông không đồng bộ), UPM1:0=00 ( không sử dụng kiểm tra parity, xem bảng 1), USBS=0 (1 bit stop) và UCPOL=0 (bit
này không sử dụng khi truyền không đồng bộ). Sau cùng, trong dòng 18, chúng ta chỉ
set bit TXEN =1 nghĩa là chỉ kích hoạt bộ phát dữ liệu, các thành phần khác như bộ
nhận, các ngắt…không được sử dụng trong ví dụ này.
Trong các bài trước tôi đã giới thiệu bạn về trình phục vụ ngắt và trong phần này tôi sẽ trình bày cách viết một chương trình con bằng ngôn ngữ C trong WinAVR, đó là
đoạn chương trình uart_char_tx ở dòng 5. Chương trình con là 1 đoạn code bao gồm các câu lệnh cùng thực hiện một nhiệm vụ chung cụ thể nào đó. Trong trường hợp này là nhiệm vụ truyền 1 tham số 8 bit ra đường TxD của USART thông qua thanh ghi UDR. Như trình bày trong phần mô tả bit UDRE của thanh ghi UCSRA, quá trình truyền chỉđược bắt đầu khi bit UDRE bằng 1, vì thế dòng code 6 làm nhiệm vụ kiểm tra bit UDRE, câu lệnh while (bit_is_clear(UCSRA,UDRE)) {}; được hiểu là quá trình lặp sẽ “lẩn quẩn” nếu bit UDRE bằng 0 (bit_is_clear). Khi bit UDRE bằng 1 thì dòng code 7 sẽ xuất biến chr ra thanh ghi UDR cũng là xuất ra chân TxD của module USART. Trong ngôn ngữ C có 2 cách cơ bản để viết chương trình con. Với cách 1 chương trình con được khai báo và viết trực tiếp phía trước chương trình chính main như cách mà tôi thực hiện trong ví dụ 1 này. Cách viết này đễ hiểu và thích hợp cho các đoạn chương trình con ngắn nhưng chúng có thể làm tổng quan chương trình của bạn trở nên rắc rối khi có quá nhiểu chương trình con viết trước main. Bạn có thể khắc phục nhược điểm này bằng cách đặt các chương trình con phía sau main như cách mà chúng ta đã làm với các trình phục vụ ngắt. Nếu theo đúng quy cách của ngôn ngữ C, khi đặt chương trình con sau main bạn phải khai báo tên chương trình phía trước main, nếu bạn đặt chương trình con uart_char_tx phía sau main thì phần trước main bạn sẽ đặt dòng khai báo trước: void uart_char_tx(unsigned char chr);. Tuy WinAVR cho
phép bạn bỏ qua khai báo trước này nhưng tôi khuyên bạn nên viết đúng cách để tạo thói quen và cũng như để dễ chuyển chương trình sang các trình biên dịch C khác sau này nếu cần thiết. Phần cuối cùng trong đoạn code là gọi lại chương trình uart_char_tx
để truyền các dữ liệu là các số từ 32 đến 127.
Để thực hiện mô phỏng bằng proteus bạn hãy vẽ một mạch điện đơn giản như
trong hình 3. Chip Atmega32 có thể được tìm với từ khóa mega32. Trong mạch điện mô phỏng có một thiết bịđầu cuối ảo (Virtual Terminal) là một thiết bị kết nối và hiển thị kết quả truyền thông không đồng bộ, chúng ta dùng để kiểm tra dữ liệu được truyền bằng chip AVR. Bạn có thể tìm thiết bị này trong trong danh sách các dụ cụảo (virtual instruments), nhấn vào nút công cụ và sau đó chọn terminal trong danh sách
để chọn thiết bịđầu cuối ảo. Kết nối thiết bịảo với chip Atmega32 như trong hình 3, chú ý là phải “đấu chéo” 2 chân TxD và RxD. Bên cạnh việc gán chương trình cho chip AVR, bạn phải set thông số cho thiết bịảo trước khi thực hiện mô phỏng. Hãy mở hộp thoại “edit component” của thiết bịảo (bằng cách right click rồi left click trên thiết bịảo). Theo mặc định thiết bịđầu cuối được định dạng khung truyền là 1 bit start+8 bit dữ liệu+1 bit stop tương tự như cách chúng ta cài đặt cho AVR trong vì dụ
1, vì thế bạn chỉ cần thay đổi tốc độ baud thành 57600 trong hộp thoại “edit
component” là hoàn tất (xem hình 4). Khi chạy mô phỏng, thiết bịđầu cuối ảo sẽ hiển thị các ký tự ASCII của các số từ 32 đến 127.
Hình 3. Mô phỏng ví dụ 1.
Hình 4. Cài đặt thông số cho thiết bịảo.