Truyền thông nối tiếp không đồng bộ với AVR (UART)

Một phần của tài liệu Điều khiển cơ cấu từ xa sử dụng sự trợ giúp của máy tính (Trang 69)

c. Biến hằng và toán tử

2.1.3.8Truyền thông nối tiếp không đồng bộ với AVR (UART)

Vi điều khiển Atmega16 có 1 module truyền thông nối tiếp USART. Có 3 chân chính liên quan đến module này đó là chân xung nhịp - XCK (chân số 1), chân truyền dữ liệu – TxD (Transmitted Data) và chân nhận dữ liệu – RxD (Reveived Data). Trong đó chân XCK chỉ đƣợc sử dụng nhƣ là chân phát hoặc nhận xung giữ nhịp trong chế độ truyền động bộ. Tuy nhiên, chúng ta không khảo sát chế độ truyền thông đồng bộ, vì thế chỉ cần quan tâm đến 2 chân TxD và RxD. Vì các chân truyền/nhận dữ liệu chỉ đảm nhiệm 1 chức năng độc lập (hoặc là truyền, hoặc là nhận), để kết nối các chip AVR với nhau (hoặc kết nối AVR với thiết bị hỗ trợ UART khác) bạn phải đấu “chéo” 2 chân này. TxD của thiết bị thứ nhất kết nối với RxD của thiết bị 2 và ngƣợc lại. Module USART trên chip Atmega16 hoạt động “song công” (Full Duplex

Operation), nghĩa là quá trình truyền và nhận dữ liệu có thể xảy ra đồng thời.

a. Thanh ghi:

Cũng nhƣ các thiết bị khác trên AVR, tất cả hoạt động và tráng thái của module USART đƣợc điều khiển và quan sát thông qua các thanh ghi trong vùng nhớ I/O. Có 5 thanh ghi đƣợc thiết kế riêng cho hoạt động và điều khiển của USART, đó là:

UDR: hay thanh ghi dữ liệu, là 1 thanh ghi 8 bit chứa giá trị nhận đƣợc và phát đi của USART. Thực chất thanh ghi này có thể coi nhƣ 2 thanh ghi TXB (Transmit data Buffer) và RXB (Reveive data Buffer) có chung địa chỉ. Đọc UDR thu đƣợc giá trị thanh ghi đệm dữ liệu nhận, viết giá trị vào UDR tƣơng đƣơng đặt giá trị vào thanh ghi đệm phát, chuẩn bị để gởi đi. Chú ý trong các khung truyền sử dụng 5, 6 hoặc 7 bit dữ liệu, các bit cao của thanh ghi UDR sẽ không đƣợc sử dụng

UCSRA (USART Control and Status Register A): là 1 trong 3 thanh ghi điều khiển hoạt động của module USART.

70

Thanh ghi UCSRA chủ yếu chứa các bit trạng thái nhƣ bit báo quá trình nhận kết thúc (RXC), truyền kết thúc (TXC), báo thanh ghi dữ liệu trống (UDRE), khung truyền có lỗi (FE), dữ liệu tràn (DOR), kiểm tra parity có lỗi (PE)…Bạn chú ý một số bit quan trọng của thanh ghi này:

* UDRE (USART Data Register Empty) khi bit bày bằng 1 nghĩa là thanh ghi dữ liệu UDR đang trống và sẵn sàng cho một nhiệm vụ truyền hay nhận tiếp theo. Vì thế nếu bạn muốn truyền dữ liệu đầu tiên bạn phải kiểm tra xem bit UDRE có bằng 1 hay không, sau khi chắc chắn rằng UDRE=1 hãy viết dữ liệu vào thanh ghi UDR để truyền đi.

* U2X là bit chỉ định gấp đôi tốc độ truyền, khi bit này đƣợc set lên 1, tốc độ truyền so cao gấp 2 lần so với khi bit này mang giá trị 0.

* MPCM là bit chọn chế độ hoạt động đa xử lí (multi-processor).

UCSRB (USART Control and Status Register B): đây là thanh ghi quan trọng điều khiển USART. Vì thế chúng ta sẽ khảo sát chi tiết từng bit của thanh ghi này.

* RXCIE (Receive Complete Interrupt Enable) là bit cho phép ngắt khi quá trình nhận kết thúc. Việc nhận dữ liệu truyền bằng phƣơng pháp nối tiếp không đồng bộ thƣờng đƣợc thực hiện thông qua ngắt, vì thế bit này thƣờng đƣợc set bằng 1 khi USART đƣợc dung nhận dữ liệu.

* TXCIE (Transmit Complete Interrupt Enable) bit cho phép ngắt khi quá trình truyền kết thúc.

* UDRIE (USART Data Register Empty Interrupt Enable) là bit cho phép ngắt khi thanh ghi dữ liệu UDR trống.

* RXEN (Receiver Enable) là một bit quan trọng điều khiển bộ nhận của USART, đề kích hoạt chức năng nhận dữ liệu bạn phải set bit này lên 1.

* TXEN (Transmitter Enable) là bit điều khiển bộ phát. Set bit này lên 1 bạn sẽ khởi động bộ phát của USART.

* UCSZ2 (Chracter size) bit này kết hợp với 2 bit khác trong thanh ghi UCSRC quy định độ dài của dữ liệu truyền/nhận. Chúng ta sẽ khảo sát chi tiết khi tìm hiểu thanh ghi UCSRC.

* RXB8 (Receive Data Bit 8) gọi là bit dữ liệu 8. Bạn nhớ lại rằng USART trong AVR có hỗ trợ truyền dữ liệu có độ dài tối đa 9 bit, trong khi thanh ghi dữ liệu là thanh ghi 8 bit. Do đó, khi có gói dữ liệu 9 bit đƣợc nhận, 8 bit đầu sẽ chứa trong thanh ghi UDR, cần có 1 bit khác đóng vai trò bit thứ chín, RXD8 là bit thứ chín này. Bạn chú ý là các bit đƣợc đánh số từ 0, vì thế bit thứ chín sẽ có chỉ số là 8, vì lẽ đó mà

71

bit này có tên là RXD8 (không phải RXD9).

* TXB8 (Transmit Data Bit 8), tƣơng tự nhƣ bit RXD8, bit TXB8 cũng đóng vai trò bit thứ 9 truyền thông, nhƣng bit này đƣợc dung trong lúc truyền dữ liệu.

UCSRC (USART Control and Status Register C): thanh ghi này chủ yếu quy định khung truyền và chế độ truyền. Tuy nhiên, có một rắc rối nho nhỏ là thanh ghi này lại có cùng địa chỉ với thanh ghi UBRRH (thanh ghi chứa byte cao dùng để xác lập tốc độ baud), nói một cách khác 2 thanh ghi này là 1. Vì thế bit 7 trong thanh ghi này, tức bit URSEL là bit chọn thanh ghi. Khi URSEL=1, thanh ghi này đƣợc chip AVR hiểu là thanh ghi điều khiển UCSRC, nhƣng nếu bit URSEL=0 thì thanh ghi UBRRH sẽ đƣợc sử dụng.

Các bit còn lại trong thanh ghi UCSRC đƣợc mô tả nhƣ sau:

* UMSEL (USART Mode Select) là bit lựa chọn giữa 2 chế độ truyền thông đồng bộ và không đồng bộ. Nếu UMSEL=0, chế độ không đồng bộ đƣợc chọn, ngƣợc lại nếu UMSEL=1, chế độ đồng bộ đƣợc kích hoạt.

* Hai bit UPM1 và UPM0( Parity Mode) đƣợc dùng để quy định kiểm tra pariry. Nếu UPM1:0=00, parity không đƣợc sử dụng (mode này khá thông dụng), UPM1:0=01 không đƣợc sử dụng, UPM1:0=10 thì parity chẵn đƣợc dùng, UPM1:0=11 parity lẻ đƣợc sử dụng (xem thêm bảng 1).

Bảng 1.12 Bảng chọn kiểm tra parity.

* USBS (Stop bit Select), bit Stop trong khung truyền bằng AVR USART có thể là 1 hoặc 2 bit, nếu USBS=0 thì Stop bit chỉ là 1 bit trong khi USBS=1 sẽ có 2 Stop bit đƣợc dùng.

* Hai bit UCSZ1 và UCSZ2 (Character Size) kết hợp với bit UCSZ2 trong thanh ghi UCSRB tạo thành 3 bit quy định độ dài dữ liệu truyền. Bảng 2 tóm tắt các giá trị có

72

Bảng 1.13 Bảng độ dài dữ liệu truyền.

* UCPOL (Clock Pority) là bit chỉ cực của xung kích trong chế độ truyền thông đồng bộ. nếu UCPOL=0, dữ liệu sẽ thay đổi thay đổi ở cạnh lên của xung nhịp, nếu

UCPOL=1, dữ liệu thay đổi ở cạnh xuống xung nhịp. Nếu bạn sử dụng chế độ truyền thông không đồng bộ, hãy set bit này bằng 0..

 UBRRL và UBRRH (USART Baud Rate Register): 2 thanh ghi thấp và cao quy định tốc độ baud.

Nhắc lại là thanh ghi UBRRH dùng chung địa chỉ thanh ghi UCSRC, bạn phải set bit này bằng 0 nếu muốn sử dụng thanh ghi UBRRH. Nhƣ bạn quan sát trong hình trên, chỉ có 4 bit thấp của UBRRH đƣợc dùng, 4 bit này kết hợp với 8 bit trong thanh ghi UBRRL tạo thành thanh ghi 12 bit quy định tốc độ baud. Chú ý là nếu bạn viết giá trị vào thanh ghi UBRRL, tốc độ baud sẽ tức thì đƣợc cập nhật, vì thế bạn phải viết giá trị vào thanh ghi UBRRH trƣớc khi viết vào thanh ghi UBRRL.

Giá trị gán cho thanh ghi UBRR không phải là tốc độ baud, nó chỉ đƣợc USART dùng để tính tốc độ baud. Bảng 3 hƣớng dẫn cách tính tốc độ baud dựa vào giá trị của thanh ghi UBRR và ngƣợc lại, cách tính giá trị cần thiết gán cho thanh ghi UBRR khi đã biết tốc độ baud.

73

Bảng 1.13 Bảng tính tốc độ baud.

Trong các công thức trong bảng 3, fOSC là tốc tần số xung nhịp của hệ thống (thạch anh hay nguồn xung nội…). Để tiện cho bạn theo dõi, tôi đính kèm bảng ví dụ cách đặt giá trị cho UBRR theo tốc độ baud mẫu.

b. Sử dụng UART:.

Thông thƣờng, để sử dụng module USART trên AVR bạn phải thực hiện 3 việc quan trọng, đó là: cài đặt tốc độ baud (thanh ghi UBRR), định dạng khung truyền (UCSRB, UCSRC) và cuối cùng kích hoạt bộ truyền, bộ nhận, ngắt…Nhƣ đã đề cập, trong tài liệu này tôi chủ yếu đề cập đến phƣơng pháp truyền thông không đồng bộ, việc xác lập các thông số hoạt động chủ yếu dựa trên chế độ này. Trong hầu hết các ứng dụng, tốc độ baud và khung truyền thƣờng không đổi, trong trƣờng hợp này chúng ta có thể khởi tạo trực tiếp USART ở phần đầu trong main và sau đó chỉ cần truyền hoặc nhận dữ liệu mà không cần thay đổi các cài đặt. Tuy nhiên, nếu trƣờng hợp giao tiếp “linh hoạt” ví dụ bạn đang chế tạo một thiết bị có khả năng giao tiếp với một thiết bị đầu cuối khác (nhƣ máy tính chẳng hạn), lúc này bạn nên cho phép ngƣời dùng thay đổi tốc độ baud hoặc các thông số khác để phù hợp với thiết bị đầu cuối. Đối với những ứng dụng kiểu này bạn nên viết 1 chƣơng trình con để khởi động USART và có thể gọi lại nhiều lần khi cần thay đổi. Phần tiếp theo chúng ta sẽ viết một số chƣơng trình ví dụ minh họa cách sử dụng module truyền thông USART từ đơn giản đến phức tạp. Các ví dụ sẽ đƣợc thực hiện cho chip Atmega16 với giả sử nguồn xung nhịp hệ thống là 8MHz.

c. 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.

74

List 1. Khởi động và truyền dữ liệu không đồng bộ bằng USART

#include <mega16.h> #include <delay.h> unsigned char i;

#define FRAMING_ERROR (1<<FE) #define PARITY_ERROR (1<<UPE) #define DATA_OVERRUN (1<<DOR)

#define DATA_REGISTER_EMPTY (1<<UDRE) #define RX_COMPLETE (1<<RXC)

// USART Transmitter buffer

#define TX_BUFFER_SIZE 8

char tx_buffer[TX_BUFFER_SIZE]; #if TX_BUFFER_SIZE <= 256

unsigned char tx_wr_index,tx_rd_index,tx_counter; #else

unsigned int tx_wr_index,tx_rd_index,tx_counter; #endif

// USART Transmitter interrupt service routine interrupt [USART_TXC] void usart_tx_isr(void) { if (tx_counter) { --tx_counter; UDR=tx_buffer[tx_rd_index++]; #if TX_BUFFER_SIZE != 256

if (tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0; #endif

} }

#ifndef _DEBUG_TERMINAL_IO_

// Write a character to the USART Transmitter buffer #define _ALTERNATE_PUTCHAR_

75

void putchar(char c) {

while (tx_counter == TX_BUFFER_SIZE); #asm("cli")

if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0)) {

tx_buffer[tx_wr_index++]=c; #if TX_BUFFER_SIZE != 256

if (tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0; #endif ++tx_counter; } else UDR=c; #asm("sei") } #pragma used- #endif

// Standard Input/Output functions #include <stdio.h>

// Declare your global variables here void main(void)

{

//Cài đặt USART

// Communication Parameters: 8 Data, 1 Stop, No Parity // USART Receiver: On

// USART Transmitter: On // USART Mode: Asynchronous // USART Baud Rate: 9600

UCSRA=0x00; UCSRB=0x48; UCSRC=0x86; UBRRH=0x00; UBRRL=0x33;

76 ACSR=0x80; SFIOR=0x00; ADCSRA=0x00; SPCR=0x00; // TWI initialization // TWI disabled TWCR=0x00;

// Global enable interrupts #asm("sei")

while (1) {

// Place your code here for (i=32; i<128; i++) {

putchar(i); //phat du lieu delay_ms(100);

} } }

Nhận dữ liệu.

Quá trình nhận dữ liệu chỉ xảy ra khi bit RXEN trong thanh ghi UCSRB đƣợc set bằng 1 và tất nhiên chân nhận dữ liệu RxD phải đƣợc nối với một nguồn phát (chân TxD của một chip UART khác chẳng hạn). Các thông số truyền thông nhƣ tốc độ baud và khung truyền trong bộ nhận phải đƣợc cài đặt nhƣ của bộ phát. Nếu không có lỗi trong quá trình truyền và nhận dữ liệu, sau khi nhận dữ liệu sẽ đƣợc chứa trong thanh ghi UDR và bit RXC (Reveice Complete) trong thanh ghi UCSRA sẽ tự động đƣợc set lên 1. Sau khi thanh ghi UDR đƣợc đọc, bit RXC lại tự động reset về 0 để chuẩn bị cho quá trình nhận dữ liệu kế tiếp. Nhƣ thế về cơ bản chúng ta có 2 cách đọc dữ liệu nhận về. Cách thứ nhất là cách hỏi vòng (polling), kiểm tra nếu bit RXC = 1 thì đọc giá trị thanh ghi UDR (và đọc cả bit RXB8 trong thanh ghi UCSRB nếu frame truyền 9 bit đƣợc dùng). Cách thứ hai là sử dụng ngắt “nhận hoàn tất” (Receive

Complete Interrupt), bằng cách set bit cho pháp ngắt nhận hoàn tất, tức bit RXCIE trong thanh ghi UCSRB, và bit cho phép ngắt toàn cục (bit I) thì một ngắt sẽ xảy ra

77

khi dữ liệu đã đƣợc nhận và chứa trong thanh ghi UDR, chúng ta chỉ cần đọc giá trị của thanh ghi UDR trong trình phục vụ ngắt là xong. Theo kinh nghiệm, sử dụng ngắt là phƣơng pháp tốt nhất cho đa số các trƣờng hợp nhận dữ liệu UART, vì chúng ta không cần quan tâm thời điểm mà dữ liệu gởi đến, tránh lãng phí thời gian dành cho việc “hỏi vòng”. Vì thế trong phần tiếp theo sẽ trình bày một ví dụ minh họa quá trình nhận dữ liệu bằng phƣơng pháp ngắt.

Trong ví dụ dƣới đây em xin trình bầy quá trình truyền và nhận dữ liệu qua ngắt trong Atmeg16.

Nhận dữ liệu USART không đồng bộ bằng phƣơng pháp ngắt. #include <mega16.h>

#include <delay.h>

// USART Receiver buffer

#define RX_BUFFER_SIZE 8

char rx_buffer[RX_BUFFER_SIZE]; #if RX_BUFFER_SIZE <= 256

unsigned char rx_wr_index,rx_rd_index,rx_counter; #else

unsigned int rx_wr_index,rx_rd_index,rx_counter; #endif

// This flag is set on USART Receiver buffer overflow bit rx_buffer_overflow;

// USART Receiver interrupt service routine interrupt [USART_RXC] void usart_rx_isr(void) {

char status,data; status=UCSRA; data=UDR;

if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) {

rx_buffer[rx_wr_index++]=data; #if RX_BUFFER_SIZE == 256

// special case for receiver buffer size=256 if (++rx_counter == 0)

{ #else

if (rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0; if (++rx_counter == RX_BUFFER_SIZE)

78 rx_counter=0; #endif rx_buffer_overflow=1; } } } #ifndef _DEBUG_TERMINAL_IO_

// Get a character from the USART Receiver buffer #define _ALTERNATE_GETCHAR_ #pragma used+ char getchar(void) { char data; while (rx_counter==0); data=rx_buffer[rx_rd_index++]; #if RX_BUFFER_SIZE != 256

if (rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0; #endif #asm("cli") --rx_counter; #asm("sei") return data; } #pragma used- #endif

// USART Transmitter buffer #define TX_BUFFER_SIZE 8

char tx_buffer[TX_BUFFER_SIZE]; #if TX_BUFFER_SIZE <= 256

unsigned char tx_wr_index,tx_rd_index,tx_counter; #else

unsigned int tx_wr_index,tx_rd_index,tx_counter; #endif

// USART Transmitter interrupt service routine interrupt [USART_TXC] void usart_tx_isr(void) {

if (tx_counter) {

--tx_counter;

79

#if TX_BUFFER_SIZE != 256

if (tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0; #endif

} }

#ifndef _DEBUG_TERMINAL_IO_

// Write a character to the USART Transmitter buffer #define _ALTERNATE_PUTCHAR_

#pragma used+ void putchar(char c) {

while (tx_counter == TX_BUFFER_SIZE); #asm("cli")

if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0)) {

tx_buffer[tx_wr_index++]=c; #if TX_BUFFER_SIZE != 256

if (tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0; #endif ++tx_counter; } else UDR=c; #asm("sei") } #pragma used- #endif

// Standard Input/Output functions #include <stdio.h>

void main(void) {

long int temp; MCUCR=0x00; MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x00;

// Cài đặt bộ truyền nhận UART

// Communication Parameters: 8 Data, 1 Stop, No Parity // USART Receiver: On

80

// USART Transmitter: On // USART Mode: Asynchronous // USART Baud Rate: 9600 UCSRA=0x00;

UCSRB=0x98; UCSRC=0x86; UBRRH=0x00; UBRRL=0x33;

// Analog Comparator initialization // Analog Comparator: Off

// Analog Comparator Input Capture by Timer/Counter 1: Off ACSR=0x80;

SFIOR=0x00;

// Global enable interrupts #asm("sei")

while (1) {

// Place your code here temp=getchar(); delay_ms(100); putchar(temp); } }

Đoạn câu lệnh trên mô tả chƣơng trình truyền nhận của Atmega16. temp=getchar(); là lệnh nhận dữ liệu từ cổng UART, và lƣu vào biến temp; sau đó trễ 100 ms bằng câu lệnh

delay_ms(100); sau đó gửi chính dữ liệu vừa nhận đƣợc lên cổng UART. Chƣơng trình viết trên trình dịch CodevisionAVR.

Để ứng dụng vào để tài, ta sử dụng linh hoạt tính năng truyền nhận của bộ truyền sau đó xử lý các công việc theo ý muốn ngƣời lập trình.

81

CHƢƠNG III THIẾT KẾ HỆ THỐNG ĐIỀU KHIỂN TÒA NHÀ 3.1 NỘI DUNG CỤ THỂ

Truyền thông tin từ máy tính qua bộ truyền, từ bộ nhận nhận tín hiệu và xử lý tín hiệu truyền đến. Thực hiện các công việc theo yêu cầu của bài toán đặt ra.

+ Thiết kế chƣơng trình điều khiển trên máy tính.

+ Truyền nhận dữ liệu không dây giữa máy tính và vi điều khiển. + Xử lý tín hiệu truyền nhận.

+ Gửi tín hiệu điều khiển từ máy tính. Nhận tín hiệu ở vi điều khiển. + Điều khiển các cơ cấu chấp hành từ vi điều khiển.

Điều khiển các khởi động từ, van dầu, van khí nén.

Nghiên cứu tổng quan các vấn đề trên. Tiến hành viết chƣơng trình giao diện trên máy tính. Và viết chƣơng trình xử lý thông tin trên Vi điều khiển. Xây dựng mạch truyền nhận để làm công việc theo yêu cầu.

3.2 CẤU TRÚC HỆ THỐNG

3.2.1 Phần mềm giao diện trên máy tính.

Phần mềm giao diện trên máy tính đƣợc viết bằng chƣơng trình visual basic 2012. Lập trình hƣớng đối tƣợng. Trên chƣơng trình chứa dữ liệu điều khiển cơ cấu chấp hành ở phía nhận là vi điều khiển.

Với phần mềm lập trình trên máy tính là Visual Studio, ở đây sử dụng ngôn ngữ lập trình là

Một phần của tài liệu Điều khiển cơ cấu từ xa sử dụng sự trợ giúp của máy tính (Trang 69)