-Hãy tạo một Project với tên gọi MATRIX57, với yêu cầu hiển thị các số từ 0 -> 9 trên Led ma trận.
Code gợi ý để viết chương trình cho vi điều khiển:
#include "mega32.h" #include "DotFont.h" #include "delay.h"
#define DATA_PORT PORTA //PORTA is connected to the data bus of DotMatrix
#define DATA_DDR DDRA
#define CTRL_PORT PORTC //PORTC is connected to control lines of DotMatrix
#define CTRL_DDR DDRC typedef unsigned char Byte; void DOTputChar57(char chr){
unsigned char line=1, i; int tchr=chr; for(i=0;i<5;i++) { CTRL_PORT=~line; DATA_PORT=font5x7[((tchr - 32) * 5)+i]; line<<=1; delay_us(1000); } } void main(void){ int index; int timed = 300; PORTB = 0xF0; DDRB = 0xFF;
DATA_DDR=0xFF; // DATA_PORT is selected as an ouput PORT CTRL_DDR=0xFF; PORTA = 0x00; PORTB.0 = 1; PORTB.0 = 0; PORTA = 0xFF; PORTB.1 = 1; Tóm tắt:
Lần 1: hiển thị từ pa[0] ÷ pa[4] Lần 2: hiển thị từ pa[1] ÷ pa[5]
Î lần j: hiển thị từ pa[j] ÷ pa[j+5] Lần 16: hiển thị từ pa[15] ÷ pa[19]
31 PORTB.1 = 0; while(1){ for (index=0;index<timed;index++){ DOTputChar57('0'); } for (index=0;index<timed;index++){ DOTputChar57('1'); } for (index=0;index<timed;index++){ DOTputChar57('2'); } for (index=0;index<timed;index++){ DOTputChar57('3'); } for (index=0;index<timed;index++){ DOTputChar57('4'); } for (index=0;index<timed;index++){ DOTputChar57('5'); } for (index=0;index<timed;index++){ DOTputChar57('6'); } for (index=0;index<timed;index++){ DOTputChar57('7'); } for (index=0;index<timed;index++){ DOTputChar57('8'); } for (index=0;index<timed;index++){ DOTputChar57('9'); } } }
32
Bài 5: HIỂN THỊ LCD 16x2
I. Mục đích
Tìm hiểu về cấu trúc và nguyên lý hoạt động LCD 16x2. Viết chương trình điều khiển LCD bằng AVR nhằm ứng dụng vào việc hiển thị. Phát triển 1 thư viện điều khiển LCD bằng AVR cả 2 chế độ 8 bit và 4 bit.
Từ module LCD 16x2 có thể mở rộng sang những module tương tự, ví dụ như module LCD 16x4, 40x2, …
II. Yêu cầu
1.Khởi tạo giá trị ban đầu cho LCD.
2.Viết một chương trình cho LCD để hiển thị một ký tự bất kỳ lên LCD.
3.Viết một đoạn chương trình con để hiển thị một ký tự bất kỳ ra một vị trí bất kỳ. 4.Dịch các ký tự từ trái sang phải trên LCD
III. Cơ sở lý thuyết III.1. Giới thiệu
Ngày nay, thiết bị hiển thị LCD (Liquid Crystal Display) được sử dụng trong rất nhiều các ứng dụng của VĐK. LCD có rất nhiều ưu điểm so với các dạng hiển thị khác. Nó có khả năng hiển thị kí tự đa dạng, trực quan (chữ, số và kí tự đồ họa), dễ dàng đưa vào mạch ứng dụng theo nhiều giao thức giao tiếp khác nhau, tốn rất ít tài nguyên hệ thống và giá thành rẽ …
LCD được chia sẵn thành từng ô và ứng với mỗi ô chỉ có thể hiển thị một ký tự ASCII. Cũng vì lý do chỉ hiện thị được ký tự ASCII nên loại LCD này được gọi là Text LCD (để phân biệt với Graphic LCD có thể hiển thị hình ảnh). Mỗi ô của Text LCD bao gồm các “chấm” tinh thể lỏng, việc kết hợp “ẩn” và “hiện” các chấm này sẽ tạo thành một ký tự cần hiển thị. Trong các Text LCD, các mẫu ký tự được định nghĩa sẵn. Kích thước của Text LCD được định nghĩa bằng số ký tự có thể hiển thị trên 1 dòng và tổng số dòng mà LCD có. Ví dụ LCD 16x2 là loại có 2 dòng và mỗi
dòng có thể hiển thị tối đa 16 ký tự. Một số kích thước Text LCD thông thường
gồm 16x1, 16x2, 16x4, 20x2, 20x4…Hình 1 là một ví dụ Text LCD 16x2.
Hình 40: Text LCD 16x2
Loại LCD mà chúng ta sử dụng là loại SD-DM1602A 2 dòng mổi dòng 16 kí tự, loại này do Trung Quốc sản xuất. Nó có 16 chân như hình sau:
Bảng 8:Bảng kỳ hiệu PORT của LCD Số TT Port của LCD 16x2 Ký hiệu thiết bị 1 LCD 16x2 J2 2 LCD PORT J20
33 LCD2 LCD4 LCD1 J20 LCD 1 2 3 4 5 6 7 8 LCD0 LCD6 LCD2 LCD0 LCD7 VCC LCD1 LCD5 R99 330 1 2 LCD6 LCD5 J2 LCD16x2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 LCD4 LCD7 Hình 41: Môđun màn hình hiển thị-LCD 16x2
LCD giao tiếp theo chuẩn logic TTL thông thường (5V cho logic 1 và 0V cho logic 0) cho nên có thể kết nối trực tiếp với VĐK.
+ Chân dữ liệu (những chân dùng để "ra lệnh") của LCD từ chân 7 đến chân 14 (được nhà sản xuất đặt tên là DB0-DB7)
+ Chân tín hiệu là các chân 4(RS), 5(RW), 6(E) Giả sử lập trình với port B (giao tiep 4 bit), thì ta có:
PortB0 - RS PortB1 - RW PortB2 - EN PortB3 - Không sử dụng PortB4 - D4 PortB5 - D5 PortB6 - D6 PortB7 - D7
Chúng ta có thể thay đổi PORT (PORT A, B hay C) nhưng các thứ tự chân của vi điều khiển kết nối với LCD phải đúng như trên (do cách lập trình trong thư viện lcd.h). Nếu lập trình bộ thư viện riêng, các chân phần cứng có thể bố trí thoải mái theo ý người lập trình.Khi lập trình LCD các bạn chú ý ở chân VEE (chân số 3 lcd) phải có mức điện áp gần bằng 0V, thường các mạch có 1 biến trở để chỉnh cường độ sáng tối cho LCD.
34
* Ghi chú :
- Chế độ “đọc”, nghĩa là MPU sẽ đọc thông tin từ LCD thông qua các chân DBx. - Chế độ “ghi”, nghĩa là MPU xuất thông tin điều khiển tới LCD thông qua các chân DBx.
III.2. Giao tiếp 8 bit và 4 bit.
Có 2 mode để ghi và đọc dữ liệu vào LCD đó là mode 8 bit và mode 4 bit: - Mode 8 bit: Nếu bit DL trong lệnh function set bằng 1 thì mode 8 bit được dùng. Để sử dụng mode 8 bit, tất cả các lines dữ liệu của LCD từ D0 đến D7 (từ chân 7 đến chân 14) phải được nối với 1 PORT của chip điều khiển bên ngoài (ví dụ PORTC của ATmega32 trong ví dụ của bài này). Ưu điểm của phương pháp giao tiếp này là dữ liệu được ghi và đọc rất nhanh và đơn giản vì chip điều khiển chỉ cần xuất hoặc nhận dữ liệu
trên 1 PORT. Tuy nhiên, phương pháp này có nhược điểm là tổng số chân dành cho
giao tiếp LCD quá nhiều, nếu tính luôn cả 3 chân điều khiển thì cần đến 11 đường cho giao tiếp LCD.
- Mode 4 bit: LCD cho phép giao tiếp với bộ điều khiển ngoài theo chế độ 4 bit.
Trong chế độ này, các chân D0, D1, D2 và D3 của LCD không được sử dụng (để
35
instruction và data 8 bit sẽ được ghi và đọc bằng cách chia thành 2 phần, gọi là các Nibbles, mỗi nibble gồm 4 bit và được giao tiếp thông qua 4 chân D7:4, nibble cao được xử lí trước và nibble thấp sau. Ưu điểm lớn nhất của phương pháp này tối thiểu số
lines dùng cho giao tiếp LCD. Tuy nhiên, việc đọc và ghi từng nibble tương đối khó
khăn hơn đọc và ghi dữ liệu 8 bit.
IV.Thực hành
Tiến hành tạo project mới trong CodeVisionAVR, trong quá trình tạo project chúng ta cần khởi tạo CPU (tương tự như các bài thực hành trước) và khởi tạo LCD như sau: Trong cửa sổ CodeWizardAVR chọn thẻ LCD
Hình 42: Chọn PORT kết nối Vi điều khiển với LCD
Trong list mặc định là None, các bạn chuyển thành PORT cho phù hợp với phần cứng của KIT (ví dụ thiết kế LCD ở PORTB). Sau khi chọn PORT xong chúng ta sẽ thấy các thông số khởi tạo cấu hình của LCD như sau:
36
Sau đó chọn File ->Generate, Save and Exit. Lúc này chương trình sẽ tạo ra 1 đoạn code mẫu đã khởi tạo sẵn CPU và LCD.
Hình 44: Cửa sổ soạn thảo code
Từ cửa sổ này chúng ta viết code để thực hiện các yêu cầu như trên. Dưới đây là đoạn code mẫu đã khởi tạo sẵn CPU và LCD.
#include <mega32.h>
// Alphanumeric LCD Module functions #asm
.equ __lcd_port=0x18 ;PORTB #endasm
#include <lcd.h>
// Declare your global variables here void main(void)
{
// Declare your local variables here // Input/Output Ports initialization . . .
// LCD module initialization
lcd_init(16);
while (1) {
// Place your code here };
}
Để sử dụng được LCD chúng ta lưu ý trong chương trình phải có #include <lcd.h> và hàm khởi tạo lcd_init(16) nằm trong hàm main().
Chúng ta có thể tham khảo thêm trong Help bằng cách chọn trên menu Help -> Help Topic (hoặc ấn F1). Chọn Library Functions Reference -> LCD Functions .
37
Hình 45: Thư viện giúp đỡ của LCD
Trong cửa sổ này chúng ta có thể tham khảo các hàm của LCD. Một số hàm cơ bản của LCD:
lcd_init( lcd_columns) : Hàm này dùng để khởi tạo LCD, có chức năng xoá màn hình và đặt lại con trỏở vị trí dòng 0 và cột 0. Số lượng cộ của LCD đang sử dụng phải được chỉđịnh ra, ví dụ với LCD 16 cột thì chúng ta cấu hình lcd_init(16) .
lcd_clear(void) : có chức năng xoá màn hình và đặt lại con trỏở vị trí dòng 0 và cột 0.
lcd_gotoxy(unsigned char x, unsigned char y): đặt con trỏ ở vị trí cột x và dòng y, dòng và cột bắt đầu là số 0.
void lcd_putchar(char c): hiển thị ký tự c ở vị trí hiện tại của con trỏ.
void lcd_puts(char *str): hiển thị chuỗi ký tự tại vị trí hiện tại, lưu trong RAM.
38
Bài 6: HOẠT ĐỘNG NGẮT VÀ ĐỊNH THỜI CỦA AVR
I. Mục đích
Vận dụng được hoạt động ngắt trong AVR là một tính năng quan trọng và cần
thiết nhất trong mọi ứng dụng. Hiểu được hoạt động và những tính năng của
timer trong AVR. Cấu hình và truy xuất được các thanh ghi trong timer và các thanh ghi liên quan đến hoạt động ngắt của timer.
II. Yêu cầu
1. Nhấn nút button 1 thì led đơn D4 sáng, button 2 thì led đơn D5 sáng. 2. Hiểu được các mode trong 2 bộ timer 8 bit và 16 bit của vi điều khiển 2. Cấu hình định thời đúng 1 giây (lần lượt led đơn sẽ sáng từng giây ) 3. Cứ 10 giây led 7 đoạn tăng một giá trị
III. Cơ sở lý thuyết
III.1. Hoạt động ngắt (Interrupt)
Interrupts, thường được dịch là Ngắt, là một “tín hiệu khẩn cấp” gởi đến bộ xử lí, yêu cầu bộ xử lí tạm ngừng tức khắc các hoạt động hiện tại để “nhảy” đến một nơi khác thực hiện một nhiệm vụ “khẩn cấp” nào đó, nhiệm vụ này gọi là trình phục vụ ngắt – ISR (Interrupt Service Routine ). Sau khi kết thúc nhiệm vụ trong ISR, bộ đếm chương trình sẽ được trả về giá trị trước đó để bộ xử lí quay về thực hiện tiếp các nhiệm vụ còn dang dở. Như vậy, ngắt có mức độ ưu tiên xử lý cao nhất, ngắt thường được dùng để xử lí các sự kiện bất ngờ nhưng không tốn quá nhiều thời gian. Các tín hiệu dẫn đến ngắt có thể xuất phát từ các thiết bị bên trong chip (ngắt báo bộ đếm timer/counter tràn, ngắt báo quá trình gởi dữ liệu bằng RS232 kết thúc…) hay do các tác nhân bên ngoài (ngắt báo có 1 button được nhấn, ngắt báo có 1 gói dữ liệu đã được nhận…).
Ngắt là một trong 2 kỹ thuật “bắt” sự kiện cơ bản bao gồm: hỏi vòng (Polling) và ngắt. Hãy tưởng tượng bạn cần thiết kế một mạch điều khiển hoàn chỉnh thực hiện rất nhiều nhiệm vụ bao gồm nhận thông tin từ người dùng qua các button hay keypad (hoặc keyboard), nhận tín hiệu từ cảm biến, xử lí thông tin, xuất tín hiệu điều khiển, hiển thị thông tin trạng thái lên các LCD…(bạn hoàn toàn có thể làm được với AVR), rõ ràng trong các nhiệm vụ, việc nhận thông tin người dùng (start, stop, setup, change,…) rất
hiếm xảy ra (so với các nhiệm vụ khác) nhưng lại rất “khẩn cấp”, được ưu tiên hàng
đầu. Nếu dùng Polling nghĩa là bạn cần viết 1 đoạn chương trình “chuyên” thăm dò trạng thái của các button (tạm gọi là hàm Input()) và bạn phải chèn đoạn chương trình này vào rất nhiều vị trí trong chương trình chính để tránh trường hợp bỏ sót lệnh từ người dùng, điều này thật lãng phí thời gian thực thi. Giải pháp cho vấn đề này là sử dụng ngắt, bằng cách kết nối các button với đường ngắt của chip và sử dụng chương trình Input() làm ISR của ngắt đó, bạn không cần phải chèn Input() trong lúc đang thực thi và vì thế không tốn thời gian cho nó, Input() chỉ được gọi khi người dùng nhấn các button. Đó là ý tưởng của ngắt.
Hình sau minh họa cách tổ chức ngắt thông thường trong các chip AVR, số lượng ngắt trên mỗi dòng chip là khác nhau, ứng với mỗi ngắt sẽ có vector ngắt, vector ngắt là các thanh ghi có địa chỉ cố định, được định nghĩa trước nằm trong phần đầu của bộ nhớ chương trình.
39
Hình 46:Cách tổ chức ngắt thông thường trong các chip AVR
Ví dụ ngắt ngoài 0 (external interrupt request 0) của chip Atmega32 có địa chỉ là 0x0002, và số vector ngắt là 2 (theo datasheet từ Atmel).
Trong lúc chương trình chính đang thực thi, nếu có một sự thay đổi dẫn đến ngắt xảy ra ở chân INT0, bộ đếm chương trình (Program Counter) nhảy đến địa chỉ 0x0002, giả sử ngay tại địa chỉ 0x0002 chúng ta có đặt 1 lệnh RJMP đến một trình phục vụ ngắt (IRS1 chẳng hạn), một lần nữa bộ đếm chương trình nhảy đến IRS1 để thực thi trình phục vụ ngắt, kết thúc ISR1, bộ đếm chương trình lại quay về vị trí trước đó trong chương trình chính, quá trình ngắt kết thúc.
40
Hình 47: Mô tả các khả năng xảy ra ngắt
Giả sử chúng ta kết nối các ngắt ngoài trên Atmega32 như phía trái hình trên, các button dùng tạo ra các ngắt, có 4 khả năng (tạm gọi là các MODE) có thể xảy ra khi chúng ta nhấn và thả các button. Nếu không nhấn, trạng thái các chân INT là HIGH do điện trở kéo lên, khi vừa nhấn 1 button, sẽ có chuyển trạng thái từ HIGH sang LOW, chúng ta gọi là cạnh xuống - Falling Edge, khi button được nhấn và giữ, trạng thái các chân INT được xác định là LOW và cuối cùng khi thả các button, trạng thái chuyển từ LOW sang HIGH, gọi là cạnh lên – Rising Edge. Trong những trường hợp cụ thể, 1 trong 4 MODEs trên đều hữu ích, ví dụ trong các ứng dụng đếm xung (đếm encoder của servo motor chẳng hạn) thì 2 MODE “cạnh” phải được dùng.
III.2. Hoạt động định thời (Timer)
Bộ định thời thường được sử dụng trong các bộ vi xử lý dùng để định thời gian, điều chế độ rộng xung, tốc độ động cơ và đếm sự kiện. Một bộ định thời là một chuỗi các Flip Flop (FF) với mỗi FF là một mạch chia, chuỗi này nhận một tín hiệu ngõ vào làm nguồn xung clock. Xung clock đặt vào FF, FF này chia dôi tần số xung clock. Ngõ ra của FF thứ nhất trở thành nguồn xung clock chi FF thứ hai, nguồn xung clock này cũng được chia cho 2,v.v…Vì mỗi một tần kế tiếp nhau đều chia cho 2 nên một bộ định thời có n tần sẽ chia tần số xung clock ở ngõ vào của bộ này cho 2n . Ngõ ra của tần
cuối cùng làm xung clock cho một FF báo tràn bộ định thời hay còn gọi là cờ tràn
(overflow flag), cờ tràn này đuợc kiểm tra bởi phần mềm hoặc tạo ra một ngắt. Giá trị nhị phân trong các FF của bộ định thời là số đếm của các xung clock từ khi bộ định thời bắt đầu đếm. Thí dụ một bộ định thời 16 bit sẽ đếm từ 0000h đến FFFFh. Cờ tràn được set bằng 1 khi xảy ra tràn số đếm FFFFh xuống 0000h.
Trong vi điều khiển ATmega32 có cả hai loại bộ định thời: 8 bit
(Timer0,Timer2), 16 bit (Timer1).Loại 8 bit có thể đếm được 256 trạng thái từ 0->(28- 1). Loại 16 bit có thể đếm được 65536 trạng thái từ 0 –> (216-1).
III.2.1. Bộ định thời 8 bit (TIMER0, TIMER2)
Gồm các thanh ghi sau:
1.Thanh ghi TCCRn (T/C Control Register với n=0, 2): Thanh ghi điều khiển Timer/Counter
Hoạt động của các bit tương ứng:
Bit 7: Ảnh hưởng bộ so sánh ngoài. Bit này chỉ hoạt động khi bit WGM chỉ rõ không điều khiển PWM.Tuy nhiên để bảo đảm tính tương thích với những thiết bị bit này phải được đặt là 0 khi TCCRn được khởi tạo ở chế độ hoạt động là PWM. Khi ghi 1 lên
41
FOCn lập tức một sự so sánh xuất hiện và tác động trong đơn vị phát tín hiệu điện .OCn bị chuyển theo bit COMn1 đặt bit 0. Bit FOCn hoạt động nhấp nháy. Do đó giá trị hiện tại trong bit COMn1,COMn0 sẽ ảnh hưởng đến bộ so sánh. Bit này luôn được đặt là 0. Bit 6,3: WGMn1 (Waveform Generation Mode) điều khiển bộ đếm chuỗi của bộ đếm, bắt đầu từ giá trị đếm lớn nhất và và loại xung điện được sử dụng.
Bảng 11: Những kiểu hoạt động được xác nhận bởi Timer/Counter
Bit 5,4: COMn1 và COMn0 (Compare Match Output Mode) những bít này điều khiển