You are on page 1of 33

MỞ ĐẦU

Chương 2 tậ p trung và o giớ i thiệu về ngô n ngữ lậ p trình C và phầ n mềm


CodeVision; Thiết kế cá c hệ thố ng nhú ng đơn giả n dù ng VĐK AVR. C là mộ t
ngô n ngữ phầ n mềm có tính ứ ng dụ ng cao. Cá c ví dụ có trong bà i họ c đều cố
gắ ng trình bà y mộ t cá ch đầy đủ vá có thể biên dịch và mô phỏ ng đượ c ngay vì
vậ y khuyến khích ngườ i họ c tích cự c thự c hà nh song song vớ i nghiên cứ u lý
thuyết.

Chương 2: THIẾT KẾ HỆ THỐNG NHÚNG VỚI VI ĐIỀU KHIỂN AVR

A. Lập trình C cho vi điều khiển AVR


Để thực hiện các ứng dụng, chúng ta có thể không nhất thiết phải luôn lập trình
bằng Assembly(ASM). Ngôn ngữ cấp cao như C sẽ giúp cho chúng ta xây dựng các ứng
dụng nhanh chóng và dễ dàng hơn, tuy nhiên không vì thế mà chúng ta “quên” ASM, lập
trình bằng C kết hợp ASM là giải pháp hay nhất. Một chú ý là chúng ta chỉ sử dụng C để
đơn giản hóa lập trình tính toán, cấu trúc điều khiển…lập trình C cho AVR không có
nghĩa là chúng ta không cần biết cấu trúc và cách thức hoạt động của chip.

* Một số khái niệm C cho AVR


Một chương trình C cho AVR thường bao gồm các thành phần như: chú thích
(comments), biểu thức (expressions), câu lệnh (statements), khối (blocks), toán tử, cấu
trúc điều khiển (Flow controls), hàm (functions)….
Chú thích (comments): có 2 cách để tạo phần chú thích trong C là chú thích từng dòng
bằng 2 dấu “//” như trong dòng đầu của đoạn ví dụ “//đây là chú thích, không được biên
dịch” hoặc chú thích block bằng cách kẹp block cần chú thích vào giữa /* ….*/. Ví dụ:
/*
Ta có thể type bất kỳ chú thích nào trong block này
Ngay cả khi xuống dòng
Phần chú thích thường có màu chữ là green
*/
Tiền xử lí (preprocessor): là một tiện ích của ngôn ngữ C, các preprocessors được trình
biên dịch xử lí trước tất cả các phần khác, các preprocessors có chức năng tương tự các
hướng dẫn (directives) trong ASM cho AVR. Các preprocessors được bắt đầu bằng dấu
“#”, trong số các preprocessors trong ngôn ngữ C có hai preprocessors được sử dụng phổ
biến nhất là #include và #define. Preprocessor #include chỉ định 1 file được đính kèm
trong quá trình biên dịch (tương đương .INCLUDE trong ASM) và #define để định nghĩa
1 chuỗi thay thế hoặc 1 macro. Xem các ví dụ sau:
#include "avr/io.h" *đính kèm nội dung file io.h trong lúc biên dịch (file io.h nằm
trong thư mục con avr của thư mục include trong thư mục cài đặt của
Codevision).*/
#define max (a,b) ((a)>(b)? (a): (b)) /*định nghĩa một macro tìm số lớn nhất trong
2 số a và b, trong chương trình nếu bạn gọi x=max(2,3) thì kết quả thu được x=3.*/
Biểu thức (Expressions): là 1 phần của các câu lệnh, biểu thức có thể bao gồm biến,
toán tử, gọi hàm…, biểu thức trả về 1 giá trị đơn. Biểu thức không phải là 1 câu lệnh
hoàn chỉnh. Ví dụ: PORTB=val.
Câu lệnh (Statement): thường là 1 dòng lệnh hoàn chỉnh, có thể bao gồm các keywords,
biểu thức và các câu lệnh khác và được kết thúc bằng dấu “;”. Ví dụ: unsigned char
val=1; val*=2; …là các câu lệnh.
Khối (Blocks): là sự kết hợp của nhiều câu lệnh để thực hiện chung 1 nhiệm vụ nào đó,
khối được bao bởi 2 dấu mở khối “{“ và đóng khối “}”. Ví dụ 1 khối:
while(1){
PORTB=val;
_delay_loop_2(65000);
val*=2;
if(!val)val=1;
}
Toán tử (Operators): là những ký hiệu báo cho trình biên dịch các nhiệm vụ cần thực
hiện, các bảng bên dưới tóm tắt các toán tử C dùng cho lập trình AVR:

Bảng 1: Các toán tử đại số: dùng thực hiện các phép toán đại số quen thuộc,
trong đó đáng chú ý là các toán tử “++” (tăng thêm 1) và “--“ (bớt đi 1).

Bảng 2: Toán tử truy cập và kích thước: toán tử [] thường được sử dụng khi ta
dùng mảng trong lúc lập trình, phần tử thứ i của mảng sẽ được truy xuất thông qua [i],
chú ý mảng trong C bắt đầu từ 0.
Bảng 3: Toán tử Logic và quan hệ: thực hiện các phép so sánh và logic, thường
được dùng làm điều kiện trong các cấu trúc điều khiển, chú ý toán tử so sánh bằng “==”,
toán tử này khác với toán tử gán “=”, trong khi y = x nghĩa là lấy giá trị của x gán cho y
thì (y== x) nghĩa là “nếu y bằng x”.

Bảng 4: Toán tử thao tác Bit (Bitwise operator): là các toán tử thực hiện trên
từng bit nhị phân của các con số, các toán tử dịch trái “<<” và dịch phải ">>" rất thường
được sử dụng khi xử lý số.

Bảng 5: Các toán tử khác: là 1 số toán tử đặc biệt rất hay sử dụng nhưng chúng
ta thường không để ý vì vai trò của chúng rất dễ nhận thấy. Đặc biệt chú ý toán tử “?:” là
1 toán tử rất đặc biệt của C so với các ngôn ngữ lập trình khác, “?:” là toán tử 3 ngôi duy
nhất có thể dùng thay thế cho cấu trúc “if” đơn giản.
* Cấu trúc điều khiển và hàm
- Cấu trúc điều khiển (Flow Controls)
Các cấu trúc điều khiển biến ý tưởng của ta thành hiện thực. Một số cấu trúc điều
khiển cơ bản trong C như sau:
“If (điều kiện) statement;”: nếu điều kiện là đúng thì thực hiện statement theo sau,
statement có thể được trình bày cùng dòng hoặc dòng sau điều khiển If. Điều kiện có thể
là một biểu thức bất kỳ, có thể là sự kết hợp của nhiều điều kiện bằng các toán tử quan hệ
AND (&&), OR (||)…. Điều kiện được cho là đúng khi nó khác 0, ví dụ if (1) thì điều kiện
hiển nhiên là đúng. Xét một vài ví dụ dùng cấu trúc if như sau:
If (!val) val=1; nghĩa là nếu val bằng 0 thì chương trình sẽ gán cho val giá trị là 1, “!” là
toán tử NOT, NOT của một số khác 0 thì bằng 0, ngược lại, NOT của 0 thì thu được kết
quả là 1. Trong ví dụ này, nếu val bằng 0 thì !val sẽ bằng 1, như thế điều kiện sẽ trở thành
đúng và câu lệnh “val=1” được thực thi.
If (x==1 && y==2) result=’A’; nghĩa là nếu x bằng 1 và y bằng 2 thì gán ký tự ‘A’ cho
biến result. Trong ví dụ này, toán tử logic “&&” được sử dụng để “nối” 2 điều kiện lại,
bạn hoàn toàn có thể sử dụng nhiều toán tử logic khác nếu cần thiết.
Trong trường hợp ta muốn thực thi nhiều câu lệnh cùng lúc nếu một điều kiện nào đó
thỏa thì ta cần đặt tất cả các câu lệnh đó trong 1 khối như bên dưới:
If (điều kiện) {
Statement1;
Statement2;

}
“If (điều kiện ) statement1; else statement2; ”: nếu điều kiện đúng thì thực hiện
statement1, ngược lại thực thi statement2. Việc đặt các statement và else…trên cùng 1
dòng hay trên những dòng khác nhau đều không ảnh hưởng đến kết quả. Tương tự trường
hợp trên, nếu có nhiều statements thì cần đặt chúng trong 1 khối.
If (điều kiện) {
Statement1;
Statement2;

}else {
Statement1;
Statement2;

}
Ngoài ra, ta cũng có thể đặt nhiều cấu trúc if…else… lồng vào nhau.
Cấu trúc switch: trong trường hợp có nhiều khả năng có thể xảy ra cho 1 biểu thức (hay
1 biến), ứng với mỗi khả năng ta cần chương trình thực hiện một việc nào đó, khi này ta
nên sử dụng cấu trúc switch. Cấu trúc này được trình bày như bên dưới.
switch (biểu thức) {
case hằng_số_1:
các statement1;
break;
case hằng_số_2:
các statement2;
break;

default:
các statement khác;
}
Hãy xét 1 ví dụ ta kết nối 2 chip AVR với nhau, 1 chip làm Master sẽ ra các lệnh
điều khiển chip Slave, chip Slave nhận mã lệnh từ Master và thực hiện các công việc
được thoả hiệp trước. Giả sử mã lệnh được lưu trong biến Command, dưới đây là chương
trình ví dụ cách xử lí của chip Slave ứng với từng mã lệnh.
switch (Command) {
case 1:
PWM=255;
ON_Motor();
break;
case 2:
PWM=0;
OFF_Motor();;
break;

default:
Get_Cmd();
break;
}Ngoài ra, bạn cũng có thể đặt nhiều cấu trúc if…else… lồng vào nhau.
Nếu Command=1, gán giá trị 255 cho biến PWM và gọi chương trình con
ON_Motor(). Trong trường hợp này, break được sử dụng, break nghĩa là thoát khỏi cấu
trúc điều khiển hiện tại ngay lập tức, như vậy sau khi thực hiện 2 lệnh, switch kết thúc mà
không cần xét đến các trường hợp khác. Bây giờ, nếu Command=2, gán giá trị 0 cho biến
PWM và gọi chương trình con OFF_Motor(), trong tất cả các trường hợp còn lại
(default), thực hiện chương trình con Get_Cmd().
“while (điều kiện ) statement1;”: là một cấu trúc lặp (loop), ý nghĩa của cấu trúc while
là khi điều kiện còn đúng thì sẽ thực hiện statement1 (hoặc các statements nếu chúng
được đặt trong 1 khối {} như trong trường hợp của if được giới thiệu ở trên). “for
(biểu_thức_1; biểu_thức_2; biểu_thức_3) statement;”: là một cấu trúc lặp khác, trong
cấu trúc for, biểu_thức_1 thường được hiểu là khởi tạo, biểu_thức_2 là điều kiện và
biểu_thức_3 là biểu thức được thực hiện sau. Cấu trúc for này tương đương với cấu trúc
while sau:
biểu_thức_1;
while (biểu_thức_2){
statement;
biểu_thức_3;}
Các biểu thức trong cấu trúc for có thể vắng mặt trong cấu trúc nhưng các dấu “;” thì
không được bỏ. Nếu ta viết for( ; ; ) tương đương với vòng lặp vô tận while (1).
Cấu trúc for thường được dùng để thực hiện 1 hay những công việc nào đó trong số lần
nào đó, ví dụ bên dưới thực hiện xuất các giá trị từ 0 đến 200 ra PORTB, sau mỗi lần
xuất sẽ gọi lệnh delay trong 65000 chu kỳ máy.
for (uint8_t i=0; i<=200; i++){
PORTB=i;
_delay_loop_2(65000);
}
Chú ý: ta có thể thực hiện việc khai báo 1 biến ngay trong cấu trúc for nếu biến lần đầu
được sử dụng. Ví dụ trên được hiểu như sau: khai báo 1 biến i kiểu byte không âm, gán
giá trị khởi đầu cho i=0 (chỉ thực hiện 1 lần duy nhất), kiểm tra điều kiện i<=200 (nhỏ
hơn hoặc bằng 200), nếu điều kiện còn đúng, thực hiện 2 statements trong block {}, sau
đó quay về để thực hiện i++ (tăng i thêm 1) rồi lại kiểm tra điều kiện i<=200 và quá trình
lặp lại. Như thế đoạn code trong {} được thực thi khoảng 201 lần trước khi biến i bằng
201 và điều kiện i<=200 sai.
- Hàm (Functions).
Ngôn ngữ C bao gồm tập hợp của rất nhiều hàm, mỗi hàm thực hiện một chức
năng cụ thể, các hàm trong C thường được thiết kế rất nhỏ gọn, để có các hàm phức tạp
người dùng cần tự tạo ra. Hàm C cho AVR được định nghĩa trong thư viện avr-libc, ngoài
các hàm C thông thường, avr-libc còn chứa rất nhiều các hàm riêng dùng riêng cho chip
AVR, các hàm này được khai báo trong các file header riêng, để sử dụng hàm nào, bạn
cần #include file header tương ứng.
Ví dụ: _delay_loop_2(65000) là một hàm được định nghĩa trong file “delay.h”
(trong thư mục C:\CAVR\avr\include\util), hàm này thực hiện việc delay khoảng 65000
chu kỳ máy. Có 4 hàm delay bạn có thể sử dụng sau khi include file đó là:
_delay_loop_1(uint8_ t _count) : delay theo một số lần chu kỳ máy nhất định, số
lượng chu kỳ delay là số 8 bit (từ 0 đến 255).
_delay_loop_2(uint16_t _count) : delay theo một số lần chu kỳ máy nhất định
(biến __count), số lượng chu kỳ delay là số 16 bit (từ 0 đến 65535).
_delay_us(double __us): delay 1 microsecond.
_delay_ms(double __ms): delay 1 milisecond.
Chú ý: để dùng 2 hàm _delay_us và _delay_ms cần định nghĩa tần số xung clock trong
Makefile (biến F_CPU), sử dụng 2 hàm này trực tiếp thường cho kết quả không như
mong muốn, ta sẽ trình bày cách sử dụng 2 hàm này trong ví dụ bên dưới.
Main: một chương trình C cho AVR phải bao gồm 1 chương trình chính main, tất cả các
nội dung chính sẽ được đặt bên trong chương trình chính. Cấu trúc chương trình chính có
thể như sau:
int main(void){
//noi dung chinh
return 0; //gia tri tra ve cho chuong trinh chinh
}
Trong đó, int là kiểu giá trị trả về của main, từ khóa void nói rằng chương trình
chính của chúng ta không cần bất kỳ tham số nào kèm theo.
* Cài đặt chương trình phần mềm CodeVisionAVR
- Chạy file setup

2
3

5
6

7
8

- Crack: Copy file Cvavrt vào thư mục Cvavr theo hình vẽ minh họa
* Hướng dẫn xây dựng một project đơn giản với AVR
Bước 1: Khởi động phần mềm CodeVisionAVR
Bước 2: Tạo một project mới

Chọn Project > OK > Yes > OK


Chọn chip và chọn tần số dao động cho chip:
Ở đây có thể chọn bất kỳ thư viện code sẵn nào mà ta muốn, CAVR sẽ tự
động tạo mã và chèn vào chương trình. Như trên ta có thể sử dụng LCD
(LCD16x2...), ADC, Timer, PWM, WDT, I2C (DS1307), 1wire (DS18B20), SPI,
UART, Analog Comparator....
Cuối cùng ta click vào biểu tượng hình bánh răng để lưu các file trong
project lại, lưu tất cả chung vào một thư mục:

Cần phải đặt tên cho 3 file quan trọng khi tạo project: C, PRJ, CWP

Bước 3: Biên dịch sang file HEX, ta bấm vào biểu tượng Build trên thanh
công cụ

Trình biên dịch sẽ hiện ra một bảng thông báo, nếu code không vấn đề gì thì
sẽ có thông báo như sau:
Nếu báo Errors thì sẽ không tạo được file Hex, còn Warnings thì vẫn tạo file
hex. Nếu có lỗi ta chỉ cần click đúp vào tên lỗi là nó tự trỏ tới dòng bị lỗi:

Bước 4: Cài đặt lại một số thông số cho project như chân giao tiếp LCD,
thạch anh.... ta làm như sau:
- Click vào biểu tượng trong hình
- Hiện ra hộp thoại và ta chọn các Tab tương ứng để thay đổi:

B. Thiết kế hệ thống nhúng với AVR


I. Hệ thống điều khiển Led đơn
1. Yêu cầu
Lập trình thực hiện chức năng sau:
- Các led được nối với PortB
- Điều khiển led sáng lần lượt từ trái qua phải và ngược lại
- Điều khiển led sáng theo cặp từ giữa ra 2 bên và ngược lại
2. Sơ đồ kết nối
3. Tạo project
4. Lập trình
5. Biên dịch và nạp chương trình lên kit
Sau khi biên dịch chương trình sử dụng phần mềm Codevision để tạo file.hex. Ta
tiến hành nạp chương trình phần mềm lên kit sử dụng phần mềm “khazama AVR
Programmer”.

(1): Chọn đường dẫn đến file.hex


(2): Chọn lõi vi điều khiển (Atmega32)
(3): Load chương trình lên kit
: Chọ đường
II. Hệ thống điều khiển hiển thị Led 7 đoạn
1. Yêu cầu
Lập trình hiển thị các số “1234”
2. Sơ đồ kết nối

3. Tạo project
4. Lập trình
III. Hệ thống điều khiển ma trận phím
1. Yêu cầu
Lập trình hiển thị phím bấm trong ma trận phím trên màn hình hiển thị LCD
16x2.
2. Sơ đồ kết nối
3. Tạo project
4. Lập trình
IV. Hệ thống điều khiển hiển thị LCD
1. Yêu cầu
Lập trình hiển thị thông điệp “BM DIEN TU SO”
2. Sơ đồ kết nối
3. Tạo project
4. Lập trình

(Chương trình khởi tạo “lcd16x2.c” tham khảo bài thí nghiệm 4)

V. Hệ thống điều khiển hiển thị ma trận led 8x8


1. Yêu cầu
Lập trình hiển thị thông điệp: “BM DIEN TU SO KHOA KT CO SO”
2. Sơ đồ kết nối

3. Tạo project
4. Lập trình
HƯỚNG DẪN ÔN TẬP

1. Viết chương trình cho bộ vi điều khiển AVR điều khiển 8 LED trên Kit
sáng theo cặp từ giữa ra 2 bên và ngược lại sau đó sáng xen kẽ nhau.
2. Viết chương trình điều khiển hiển thị dòng chữ “dai hoc thong tin lien
lac” trên ma trận LED của Kit.
3. Lập trình ngắt timer và ngắt ngoài.
4. Lập trình thời gian thực RTC với DS1307 để hiển thị thời gian và ngày
tháng trên màn hình LCD.
5. Viết chương trình cho bộ vi điều khiển AVR hiển thị các số “6789” trên 4
Led 7 đoạn của Kit.

You might also like