You are on page 1of 20

Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.

ORG

Bài 2 : NGÔN NGỮ C CƠ BẢN


I. Giải thuật lập trình.
Giải thuật lập trình có tác dụng định hướng cho lập trình viên trước khi
bắt tay vào viết mã lệnh, nó giúp lập trình viên có cái nhìn khái quát và phân rõ
nhiệm vụ, chức năng cho từng khối lệnh.
Giải thuật lập trình được cô đọng lại thành lưu đồ giải thuật với các khối
chức năng được mô tả theo quy ước chung theo bảng 1.

Hình dạng Chức năng

Dữ liệu vào - ra

Xử lý

Kiểm điều kiện

Hướng xử lý

Gọi chương trình con hoặc hàm

Điểm bắt đầu hoặc kết thúc

Điểm ghép nối

Bảng 1. Mô tả biểu tượng – chức năng cơ bản của lưu đồ giải thuật.
Để xây dựng lưu đồ giải thuật, bạn cần phải trả lời các câu hỏi:
+Mục đích của chương trình, xây dựng để làm gì, cần phải có chức năng
gì?
+Cần các hàm, các chương trình con như thế nào để đạt được từng chức
năng đã đặt ra?
Từ câu trả lời, bạn đã có khái quát cấu trúc của chương trình, sau đó,
bằng các biểu tượng ở bảng 1, ta có thể lập thành lưu đồ, cuối cùng là chi tiết
hóa bằng mã lệnh. Nếu là người có nhiều kinh nghiệm, có thể các bước trên đã
nằm sẵn trong kỹ năng của bạn và chỉ việc viết code theo định hướng có sẵn
trong tư duy, tuy nhiên nếu là người mới thì làm việc có trình tự và quy tắc
giúp ta đạt được mục đích 1 cách nhanh chóng và hiệu quả nhất có thể.
Ví dụ: ai đó yêu cầu bạn dùng AVR làm cho 1 dãy đèn led gồm 8 bóng
phát sáng lần lượt từng bóng từ phải sang trái và mỗi trạng thái duy trì 1s và cứ
như thế xoay vòng mãi.

Trần Thừa – 2010 36


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

+Đầu tiên, đề yêu cầu điều khiển ngoại vi là led, do đó dữ liệu của chân
điều khiển phải là dữ liệu xuất, ta cấu hình hướng dữ liệu xuất, việc cấu hình
này chỉ cần làm 1 lần.
+Thứ 2, cần điều khiển 8 bóng led thì 1 PORT vi điều khiển AVR là đầy
đủ. Vì vậy ta sẽ thao tác dữ liệu trên thanh ghi PORT (PORTA chẳng hạn).
+Thứ 3, nếu xem dữ liệu dữ liệu thanh ghi là 8 bit tương đương với 8
led, từng bóng lần lượt phát sáng, ta thấy tại mỗi thời điểm chỉ có 1 trong 8 bit
ở mức cao, và hình như bit cao này, sau 1 giây lại dịch trái 1 bit. Từ đây ta có
thể suy ra, chỉ cần cho dữ liệu lúc ban đầu bằng 1, sau đó, mỗi 1s lại nhân nó
lên 2 lần (dịch trái 1 bit tương đương nhân lên 2 lần), và sẽ lặp lại 8 lần. Như
vậy ta có thể xây dựng lưu đồ như sau:

Bắt đầu

Cấu hình hướng


dữ liệu PORTA

Cho PORTA=1
Số lần=0

Trì hoãn 1s

PORTA x 2

Đúng Sai
Số lần=8?

Tùy mỗi người mà cách giải quyết 1 vấn đề sẽ khác nhau, tuy nhiên,
nếu làm nhiều và rút kinh nghiệm ta sẽ có các hướng giải quyết hợp lý nhất.
Bởi vì tài nguyên vi điều khiển là hạn hẹp nên việc cân nhắc sử dụng tài
nguyên đó như thế nào cho hợp lý rất đáng được quan tâm.

Trần Thừa – 2010 37


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

II. Cấu trúc chương trình viết bằng ngôn ngữ C.


Để tìm hiểu cấu trúc 1 chương trình viết bằng ngôn ngữ C, ta sẽ xem xét
1 ví dụ đơn giản như sau:

Ví dụ 1:
/* CHUONG TRINH DICH LED
NAM TRONG DVD TAI LIEU LAP TRINH C CHO VI DIEU KHIEN AVR
TAI LIEU NAY CO NGUON TU WEBSITE WWW.EEELAB.ORG
*/
#include <delay.h>
#define led_port PORTD //DINH NGHIA PORTB LA
led_port
char i; // KHAI BAO BIEN i, KIEU
DU LIEU CUA I LA CHAR
//CHUONG TRINH CHINH
void main() {
DDRD=0xff;
while(1){

for(i=0;i<=7;i++){
led_port=0x01;
led_port=led_port<<i;
delay_ms(500);
}
}

1. Khái niệm câu lệnh


Một câu lệnh (statement) xác định một công việc mà chương trình phải
thực hiện để xử lý dữ liệu đã được mô tả và khai báo. Các câu lệnh được ngăn
cách với nhau bởi dấu chấm phẩy “ ; ”.
2. Chú thích.
Khi viết chương trình đôi lúc ta cần phải có vài lời ghi chú về 1 đoạn
chương trình nào đó để dễ nhớ và dễ điều chỉnh sau này. Phần nội dung ghi chú
không thuộc về chương trình (khi biên dịch phần này bị bỏ qua). Trong ngôn

Trần Thừa – 2010 38


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

ngữ C, nội dung chú thích phải được viết trong cặp dấu /* và */ (nếu chú thích
trên nhiều dòng), hoặc đặt sau cặp dấu // (nếu chú thích trên 1 dòng).
Chú ý: Trình biên dịch MikroC có chức năng khóa 1 đoạn mã lệnh bằng
cặp dấu chú thích, khi cần khóa 1 đoạn lệnh, ta bôi đen đoạn lệnh đó và bấm
vào nút {…} hoặc bấm tổ hợp phím Ctr+Shift+dấu chấm, khi muốn phục hồi
lại đoạn lệnh đó, ta bấm vào nút {…} hoặc tổ hợp phím Ctr+Shift+ dấu phẩy.
3. Chỉ thị tiền xử lý.
- Chỉ thị sự gộp vào của các tập tin nguồn khác: #include
- Chỉ thị việc định nghĩa các macros hoặc ký hiệu: #define
Chỉ thị đầu tiên được sử dụng trước hết là nhằm gộp vào nội dung của
các tập tin cần có (header file), không thể thiếu trong việc sử dụng một cách tốt
nhất các hàm của thư viện chuẩn. Cú pháp :
#include tên_thư_viện.h
Đối với MikroC, chỉ thị include các thư viện của phần mềm vào chương
trình được thực hiện tự động qua công cụ Library Manager. Nếu dùng các thư
viện tự định nghĩa, ta vẫn dùng lệnh include để gộp tập tin thư viện.
Chỉ thị thứ hai là dùng định nghĩa các biến, cú pháp :
#define tên đối_tượng_cần_gán_tên

4. Khai báo biến(biến toàn cục).


Khai báo các biến thuộc các kiểu dữ liệu khác nhau. Cú pháp:
Kiểu dữ liệu tên_biến_1, tên_biến_2 ;
Ví dụ: char var1, var_2 ;
int var1, var2,
var3, var4 ;
5. Khai báo các prototype
Đây là phần khai báo đầu hàm (phần này không bắt buộc).
Cú pháp:
Kiểu_kết_quả_trả_về Tên_hàm (danh sách đối số vào)
Lưu ý: Nếu 1 hàm được cài đặt sau 1 hàm khác, nó không thể được gọi
trong các hàm cài đặt trước nó. Tuy nhiên, nếu khai báo prototype của hàm
ta có thể cài đặt hàm ở bất cứ đâu và có thể gọi hàm ở bất kỳ hàm nào cài
đặt trước hoặc sau nó.
6. Chương trình chính.
Bắt buộc phải có
void main( )
{
Các khai báo cục bộ trong hàm main; /* Các khai báo này chỉ tồn tại
trong hàm mà thôi, có thể là khai báo biến hay khai báo kiểu.*/

Trần Thừa – 2010 39


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Các câu lệnh dùng để định nghĩa hàm main;


}

7. Cài đặt các hàm.


Kiểu_dữ_liệu_trả_về tên_hàm( các tham số)
{
Các khai báo cục bộ trong hàm.
Các câu lệnh dùng để định nghĩa hàm
return kết_quả_trả_về;
}
Một chương trình C bắt đầu thực thi từ hàm main (thông thường là từ
câu lệnh đầu tiên đến câu lệnh cuối cùng).

III. Bộ chữ viết, tên và hằng trong ngôn ngữ C.


1. Bộ chữ viết trong C.
Bộ chữ viết trong ngôn ngữ C bao gồm những ký tự, ký hiệu sau: (phân
biệt chữ in hoa và in thường):
 26 chữ cái latin lớn A,B,C...Z
 26 chữ cái latin nhỏ a,b,c ...z.
 10 chữ số thập phân 0,1,2...9.
 Các ký hiệu toán học: +, -, *, /, =, <, >, (, )
 Các ký hiệu đặc biệt: :. , ; " ' _ @ # $ ! ^ [ ] { } ...
 Dấu cách hay khoảng trống.
2. Tên (danh biểu).
Tên hay còn gọi là danh biểu (identifier) được dùng để đặt cho chương
trình, hằng, kiểu, biến, chương trình con... Tên có hai loại là tên chuẩn và tên
do người lập trình đặt.
Tên chuẩn là tên do C đặt sẵn như tên kiểu: int, char, float,…; tên hàm:
sin, cos...
Tên do người lập trình tự đặt để dùng trong chương trình của mình. Sử
dụng bộ chữ cái, chữ số và dấu gạch dưới (_) để đặt tên, nhưng phải tuân thủ
quy tắc:
 Bắt đầu bằng một chữ cái hoặc dấu gạch dưới.
 Không có khoảng trống ở giữa tên.
 Không được trùng với từ khóa(tên các lệnh hoặc đối tượng đã định
nghĩa trong các thư viện).
 Độ dài tối đa của tên là 32 ký tự, tuy nhiên cần đặt sao cho rõ ràng, dễ
nhận biết và dễ nhớ.
 Không cấm việc đặt tên trùng với tên chuẩn nhưng khi đó ý nghĩa của
tên chuẩn không còn giá trị nữa.
Ví dụ: Tên do người lập trình đặt: Chieu_dai, Chieu_Rong, Chu_Vi,
Dien_Tich

Trần Thừa – 2010 40


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Tên không hợp lệ: Do Dai, 12A2…


3. Hằng (Constant).
Là đại lượng không đổi trong suốt quá trình thực thi của chương trình.
Hằng số có thể là một chuỗi ký tự, một ký tự, một con số xác định.
Chúng có thể được biểu diễn hay định dạng với nhiều dạng thức khác nhau.

IV. Các kiểu dữ liệu.


1. Các kiểu dữ liệu cơ bản.
a. Định dạng số học.
Đây là cách biểu diễn các hệ thống số đếm trong trình biên dịch C.
-Thập phân: Biểu diễn giống như số thập phân bình thường. Ví dụ 120
-Thập lục phân: Số thập lục phân được biểu diễn bắt đầu bằng “0X” (chữ
“X” viết tắt của HEX và không phân biệt hoa thường). Ví dụ 0X0F
-Nhị phân: Số nhị phân được biểu diễn bắt đầu bằng “0B” (chữ “B” viết tắt
cửa BINARY và không phân biệt hoa thường). Ví dụ 0b01101100
b. Các kiểu dữ liệu số học.

Tên kiểu Kích thước Khoảng giá trị


(byte)

(unsigned) char 1 0..255

signed char 1 -128..127

(signed) short (int) 1 -128..127

unsigned short (int) 1 0..255

(signed ) int 2 -32768..32767

unsigned int 2 0..65535

(signed) long (int) 4 -2147483648..2147483648

unsigned long (int) 4 0..4294967295

c. Kiểu số thực

Tên kiểu Kích thước Khoảng giá trị

float 4 -1.5*1045..+3.4*1038

Trần Thừa – 2010 41


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

double 4 -1.5*1045..+3.4*1038

long double 4 -1.5*1045..+3.4*1038


Ngoài ra ta còn có kiểu dữ liệu void, kiểu này mang ý nghĩa là kiểu rỗng
không chứa giá trị gì cả. Đối với AVR có thêm 1 kiểu dữ liệu đặc biệt là kiểu
bit, kiểu này chỉ có giá trị 0 hoặc 1.
d. Ép kiểu.
Khi thực hiện các phép toán, nếu các số hạng không cùng kiểu dữ liệu,
MikroC sẽ tự động chuyển các số hạng này về cùng kiểu dữ liệu trước rồi mới
tính toán. Việc ép kiểu cũng có thể thực hiện bởi lập trình viên. Nguyên tắc ép
kiểu: Số hạng có kiểu đơn giản hơn được chuyển sang kiểu của số hạng có kiểu
phức tạp hơn.
Ép kiểu bắt buộc:
<(type)> object
Chuyển đổi kiểu đối tượng (object) thành kiểu mới (type).
Ví dụ:
/* Cho 2 biến a và b có kiểu dữ liệu là char */
char a, b;

/* Chuyển kiểu dữ liệu của biến a thành int*/


(unsigned int) a;

/* Dòng lệnh sau sẽ chuyển biến a sang kiểu double sau đó


chuyển biến b sang dạng double sau đó lấy a+b kết quả thuộc
kiểu double */
(double) a + b; // equivalent to ((double) a) + b;

2. Các kiểu dữ liệu có cấu trúc


a. Mảng: là kiểu dữ liệu có cấu trúc đơn giản và được sử dụng phổ biến
nhất chứa 1 tập hợp các phần tử có cùng kiểu.
Cú pháp khai báo mảng:
Cú pháp: Kiểu Tên_mảng [số phần tử] ;
Kiểu dữ liệu: là kiểu dữ liệu mà mỗi phần tử của mảng được định dạng.
Tên mảng: đây là một cái tên đặt đúng theo quy tắc đặt tên của danh
biểu. Tên này cũng mang ý nghĩa là tên biến mảng.
Số phần tử: là một hằng số nguyên, cho biết số lượng phần tử tối đa
trong mảng là bao nhiêu (hay nói khác đi kích thước của mảng là gì). Giá trị
này có thể bỏ trống gọi là khai báo không tường minh.
Để truy xuất từng phần tử của mảng, ta dùng cú pháp:
Tên_mảng [Số_thứ_tự_của phần_tử];
Trong đó số thứ tự của phần tử có giá trị từ 0 đến “số phần tử” trừ đi 1.
Khởi tạo giá trị đầu cho mảng: Giá trị của các phần tử trong mảng có thể
được khởi tạo lúc khai báo bằng cú pháp sau:

Trần Thừa – 2010 42


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Kiểu Tên_mảng [ ]= {Các giá trị cách nhau bởi dấu phẩy};
Nếu kích thước của mảng được chỉ định thì số giá trị không được vượt
quá kích thước của mảng này. Ngược lại, nếu không chỉ định thì trình biên dịch
tự động cập nhật giá trị này cho phù hợp.
Nếu khai báo 1 mảng kiểu char (character – ký tự). Thì ta có thể viết gọn
lại dưới dạng chuỗi.
Ví dụ:
const char msg1[] ={“T”, “E”,“S”,“T”} ;
const char msg2] = “TEST ” ;
Hai hằng số trên có giá trị như nhau.

3. Cấu trúc.
Chứa 1 tập hợp các phần tử được đặt tên bởi lập trình viên, các phần tử
này có thể có 1 kiểu bất kỳ.
Khai báo và khởi tạo cấu trúc:
struct Tên_cấu_trúc {
Kiểu Phần_tử 1 ;
Kiểu Phần_tử 2 ;
……..
Kiểu Phần_tử n ;
}
Ví dụ:
struct I2CSendByte{
char control;
char dat;
}
Khai báo biến thuộc kiểu cấu trúc: Có thể khai báo lúc khai báo cấu trúc
hoặc khai báo sau khi khai báo cấu trúc đều được.
Ví dụ: Khai báo biến lúc khai báo cấu trúc:
struct I2CSendByte{
char control;
char dat;
}d1,d2;

Khai báo biến sau khi khai báo cấu trúc:

struct I2CSendByte {
char control;
char dat;
};

Trần Thừa – 2010 43


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

struct I2CSendByte d1,d2;


Khởi tạo giá trị biến cấu trúc: tương tự như mảng
Ví dụ: struct I2CSendByte d1={0xA1,'A'};
Truy xuất các phần tử trong cấu trúc: Sử dụng 2 toán tử sau:
“.” Truy xuất trực tiếp các phần tử trong cấu trúc.
“->” Truy xuất gián tiếp các phần tử trong cấu trúc. Chỉ áp dụng
đối với con trỏ.
Ví dụ:
struct mystruct{
int i;
char str[21];
double d;

} s,*sptr=&s;
s.i=3; // gán giá trị 3 cho phần tử I trong cấu trúc
sptr ->d=1.23; // gán gián tiếp giá trị 1.23 cho phần tử d.

V. Biểu thức
Biểu thức là một sự kết hợp giữa các toán tử (operator) và các toán hạng
(operand) theo đúng một trật tự nhất định.
Mỗi toán hạng có thể là một hằng, một biến hoặc một biểu thức khác.
Trong trường hợp, biểu thức có nhiều toán tử, ta dùng cặp dấu ngoặc
đơn () để chỉ định toán tử nào được thực hiện trước.
1. Các toán tử số học
Bảng tóm tắt các toán tử số học
Toán tử Ý nghĩa Độ ưu tiên
Các toán tử 2 toán hạng
+ Phép cộng 12
- Phép trừ 12
* Phép nhân 13
/ Phép chia lấy phần nguyên 13
% Phép chia lấy phần dư (không hỗ trợ kiểu float) 13
Các toán tử 1 toán hạng
+ Cộng, không làm thay đổi toán hạng 14
- Đổi dấu toán hạng 14
++ Tăng, cộng 1 đơn vị vào giá trị toán hạng. 14
-- Giảm, trừ 1 đơn vị vào giá trị toán hạng. 14

Tăng và giảm (++ & --)

Trần Thừa – 2010 44


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Toán tử ++ thêm 1 vào toán hạng của nó và – trừ bớt 1. Nói cách
khác:
x = x + 1 giống như ++x
x = x – 1 giống như x—
Cả 2 toán tử tăng và giảm đều có thể tiền tố (đặt trước) hay hậu tố
(đặt sau) toán hạng. Ví dụ: x = x + 1 có thể viết x++ (hay ++x)
Tuy nhiên giữa tiền tố và hậu tố có sự khác biệt khi sử dụng trong
1 biểu thức. Khi 1 toán tử tăng hay giảm đứng trước toán hạng của nó, C thực
hiện việc tăng hay giảm trước khi lấy giá trị dùng trong biểu thức. Nếu toán tử
đi sau toán hạng, C lấy giá trị toán hạng trước khi tăng hay giảm nó. Tóm lại:
x = 10
y = ++x //y = 11, x=11
Tuy nhiên:
x = 10
y = x++ //y = 10, x=11

2. Các toán tử quan hệ và các toán tử Logic


Các toán tử quan hệ dùng trong các biểu thức điều kiện. Ý nghĩa chính
của toán tử quan hệ và toán tử logic là đúng hoặc sai.
Trong ngôn ngữ C, các biểu thức có giá trị khác 0 là đúng(true), bằng 0
là sai (false).
Trong các biểu thức sử dụng toán tử quan hệ và logic kết quả trả về là
1(1 được xem là số khác 0 tiêu biểu) nếu biểu thức đúng và trả về 0 nếu
biểu thức sai.
Bảng tóm tắt các toán tử quan hệ và logic.

Toán tử Ý nghĩa Độ ưu tiên


Các toán tử quan hệ
> Lớn hơn 10
>= Lớn hơn hoặc bằng 10
< Nhỏ hơn 10
<= Nhỏ hơn hoặc bằng 10
== Bằng 9
!= Khác 9
Các toán tử Logic
&& Logic AND 5
|| Logic OR 4
! Logic NOT 14
Bảng sự thật cho các toán tử logic.

p q p&&q p||q !p

Trần Thừa – 2010 45


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

0 0 0 0 1
0 1 0 1 1
1 0 0 1 0
1 1 1 1 0

3. Các thao tác bit

Toán tử Hoạt động Độ ưu tiên


& Thực hiện AND 2 bit tương ứng, trả về 1 nếu cả 2 8
cùng bằng 1, trả về 0 nếu 1 trong 2 bằng 0.
| Thực hiện OR 2 bit tương ứng, trả về 0 nếu cả 2 6
cùng bằng 0, trả về 1 nếu 1 trong 2 bằng 1.
^ Thực hiện XOR 2 bit tương ứng, trả về 1 nếu 2 bit 7
khác nhau và trả về 0 nếu 2 bit giống nhau.
~ Toán tử 1 toán hạng, lấy đảo từng bit 14
<< Dịch trái, dịch trái các bit, bỏ bit ngoài cùng bên 11
trái(MSB) và gán 0 cho bit ngoài cùng bên phải
(LSB).
>> Dịch phải, dịch trái các bit, bỏ bit ngoài cùng bên 11
phải(LSB) và gán 0 cho bit ngoài cùng bên trái
(MSB).

Các ví dụ:
0x1234 & 0x5678; // Bằng 0x1230
Vì:
0x1234 : 0001 0010 0011 0100
&
0x5678 : 0101 0110 0111 1000
0001 0010 0011 0000
Tương tự:
0x1234 | 0x5678; // Bằng 0x567C
0x1234 ^ 0x5678; // Bằng 0x444C
~0x1234 ; // Bằng 0xEDCB
Toán tử thao tác bit phân biệt nhau dựa vào ví dụ sau:
0x2222 & 0x5555 = 0x0000;
0x2222 & 0x5555 = 1; /*AND logic giữa 2 giá trị khác 0 (đúng AND
đúng = 1) */
~0x1234 = 0xEDCB;
!0x1234=0; // Đảo giá trị khác 0 (đúng) thành giá trị sai =0

4. Toán tử điều kiện “?:”


Toán tử điều kiện “?:” là toán tử 3 đối số trong C. Cú pháp:

Trần Thừa – 2010 46


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

(Biểu_thức_1) ? (Biểu thức 2) : ( Biểu thức 3) ;


Trong đó biểu thức 1 được tính toán trước, nếu kết quả là đúng thì biểu
thức 2 được thực hiện , nếu kết quả là sai thì biểu thức 3 được thực hiện.

Ví dụ: Tìm số lớn hơn trong 2 số a và b


max=(a>b)?a:b;
Đổi ký tự in thường thành in hoa:
c=(c>=’a’&&c<=’z’)?(c-32):c; // Trong bảng mã ASCII giá trị ký tự in
thường lớn hơn ký tự in hoa 32 đơn vị.

5. Toán tử gán
a. Gán thông thường
Không giống như các ngôn ngữ khác, C xem việc gán là 1 toán tử,
không phải là 1 lệnh.
Toán tử gán đơn giản:
Biểu thức 1 = Biểu thức 2;
Biểu thức 1 là một đối tượng hay một vị trí bộ nhớ có giá trị được gán
bằng giá trị của biểu thức 2. Biểu thức 1 phải là một biến có thể chứa giá trị,
biểu thức 2 là biểu thức bất kỳ.

b. Gán rút gọn


MikroC cho phép rút gọn các phép gán phức tạp để được biểu thức đơn
giản hơn. Cú pháp
Biểu thức 1 Toán_tử = Biểu thức 2 ;
Trong đó, toán tử có thể là các toán tử sau: +, -, *, /, %, &, |, ^, <<, >>. Như
vậy ta có các toán tử gán rút gọn sau: +=, -=, *=, /=,%=, &=, |=, ^=, <<=, >>=.
Lưu ý: nếu có khoảng trắng sẽ báo lỗi trong lúc biên dịch, ví dụ: + =, sẽ
báo lỗi.
Biểu thức gán rút gọn có thể viết đầy đủ như sau:
Biểu thức 1 = Biểu thức 1 Toán tử Biểu thức 2;

Ví dụ:
a=a+5;
a+=5; // Hai câu lệnh này hoàn toàn giống nhau.

6. Kiểu dữ liệu con trỏ


a. Mô tả
Ngôn ngữ C cung cấp cho ta một loại biến đặc biệt là biến con trỏ
(pointer) với các đặc điểm:

Trần Thừa – 2010 47


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

+Biến con trỏ không chứa dữ liệu mà chỉ chứa địa chỉ của dữ liệu hay chứa
địa chỉ của ô nhớ chứa dữ liệu mà con trỏ đang trỏ tới.
+Kích thước của biến con trỏ không phụ thuộc vào kiểu dữ liệu, con trỏ
phải có khả năng chứa được địa chỉ ô nhớ lớn nhất của bộ nhớ.
b. Khai báo
Cú pháp: Kiểu * Tên_con_trỏ
Ý nghĩa: Khai báo một biến có tên là Tên_con_trỏ dùng để chứa địa chỉ của
các biến có kiểu Kiểu.
Ví dụ 1: Khai báo 2 biến a,b có kiểu int và 2 biến pa, pb là 2 biến con trỏ kiểu
int.
Ghi chú: Nếu chưa muốn khai báo kiểu dữ liệu mà con trỏ ptr đang chỉ đến, ta
sử dụng:
void *ptr;
Sau đó, nếu ta muốn con trỏ ptr chỉ đến kiểu dữ liệu gì cũng được. Tác dụng
của khai báo này là chỉ dành ra 2 bytes trong bộ nhớ để cấp phát cho biến con trỏ ptr.
c. Thao tác
Toán tử & dùng để định vị con trỏ đến địa chỉ của một biến đang làm việc.
Cú pháp: Tên_biến_con_trỏ=&Tên_biến
Giải thích: Ta gán địa chỉ của biến Tên_biến cho con trỏ Tên_biến_con_trỏ.
Ví dụ: Gán địa chỉ của biến a cho con trỏ pa, gán địa chỉ của biến b cho con trỏ
pb.
pa=&a; pb=&b;
Khi gán địa chỉ của biến tĩnh cho con trỏ cần phải lưu ý kiểu dữ liệu của
chúng.
d. Truy xuất
Để truy cập đến nội dung của ô nhớ mà con trỏ chỉ tới, ta sử dụng cú
pháp:
*Tên_biến_con_trỏ
Với cách truy cập này thì * Tên_biến_con_trỏ có thể coi là một biến có kiểu
được mô tả trong phần khai báo biến con trỏ.
Ví dụ: Ví dụ sau đây cho phép khai báo, gán địa chỉ cũng như lấy nội
dung vùng nhớ của biến con trỏ:
int x=100; // Khởi tạo giá trị đầu cho x
int *ptr; // Khai báo con trỏ ptr
ptr=&x;// Dùng con trỏ ptr trỏ vào x
int y= *ptr ;// Lấy nội dung của x gán cho y thông qua con trỏ ptr

Trần Thừa – 2010 48


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

VI. Các lệnh có cấu trúc


1. Cấu trúc rẽ nhánh (cấu trúc điều kiện).
Cấu trúc rẽ nhánh là một cấu trúc được dùng rất phổ biến trong các ngôn ngữ
lập trình nói chung. Trong C, có hai dạng: dạng không đầy đủ và dạng đầy đủ.
a. Dạng không đầy đủ (if…).
Cú pháp: if(biểu thức điều kiện){
Các lệnh khi biểu thức điều kiện đúng;
}

Vào
Giải thích: Nếu biểu thức điều kiện đúng (khác 0)
thì thực hiện khối lệnh 1 và thoát khỏi if.
Nếu biểu thức điều kiện if sai (bằng0) thì bỏ
Bt điều Sai qua khối lệnh và thoát khỏi if.
kiện
Lưu ý: khối lệnh là tập hợp gồm từ 2 lệnh trở lên
phải đặt trong dấu ngoặc nhọn {}.
Đúng
Ví dụ:
Khối lệnh if(a==b) a++; //Nếu a=b thì a tăng 1
if (a!=b){
a++;
Ra b=a;
}

b. Dạng đầy đủ (if … else).


Cú pháp: if( biểu thức điều kiện){
Các lệnh khi biểu thức điều kiện đúng;
}
Vào else{
Các lệnh khi biểu thức điều kiện sai;
}
Bt điều Sai Giải thích: Nếu biểu thức điều kiện đúng (khác 0)
kiện thì thực hiện khối lệnh <Công việc> và thoát khỏi
if.
Đúng Nếu biểu thức điều kiện if sai (bằng 0) thì
thực hiện khối lệnh 2 và thoát khỏi if.
Khối lệnh 1 Khối lệnh 2
Ví dụ:
if(a==b) a++; //Nếu a=b thì a tăng 1
if (a!=b){
Ra a++;
b=a;
}

Trần Thừa – 2010 49


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

c. Dạng ELSE IF
Quyết định sẽ thực hiện 1 trong n khối lệnh cho trước.
Cú pháp:
if(biểu thức điều kiện 1)
khối lệnh 1;
else if (biểu thức điều kiện 2)
khối lệnh 2;

else if (biểu thức điều kiện n-1)
khối lệnh n-1;
else
khối lệnh n;

Vào
Sai
BTĐK1

Sai
BTĐK2

Sai
BTĐK(n-1)

Sai
BTĐKn

Đúng Đúng Đúng Đúng

Khối lệnh 1 Khối lệnh 2 Khối lệnh n-1 Khối lệnh n

Ra

Giải thích: Nếu biểu thức điều kiện thứ n đúng thì thực hiện khối lệnh n,
nếu tất cả biểu thức điều kiện đều cho giá trị sai thì thoát mà không làm gì
cả.

Trần Thừa – 2010 50


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

2. Cấu trúc chọn lựa SWITCH…CASE


Cấu trúc switch-case cũng giống như cấu trúc else if , nhưng nó mềm
dẻo, linh động và trực quan hơn nhiều so với sử dụng if. Tuy nhiên, nó có mặt
hạn chế là kết quả của biểu thức điều kiện phải là giá trị hằng nguyên (có giá trị
cụ thể). Một chương trình sử dụng cấu trúc switch-case thì cũng có thể sử dụng
if, nhưng ngược lại thì còn tùy vào giải thuật của chương trình.
Cú pháp:
switch(biểu thức){
case giá trị 1: Nhóm lệnh 1;
break;
case giá trị 2: Nhóm lệnh 2;
break;
…..
case giá trị n: Nhóm lệnh n;
break;
default: Nhóm lệnh n;
}
Giải thích: Switch tính toán giá trị biểu thức, sau đó so sánh giá trị này với các
giá trị theo sau case, giá trị nào giống kết quả tính toán thì nhóm lệnh của case
tương ứng sẽ được thực thi.
Nếu gặp lệnh break thì thoát ngay lập tức, nếu không có lệnh break thì
tiếp tục thực hiện các nhóm lệnh ở các case tiếp theo cho đến khi gặp break
hoặc kết thúc đoạn lệnh.
Nếu giá trong giá trị tính toán không có giá trị nào giống với giá trị của
các case thì switch sẽ thực thi đoạn lệnh trong mục default. Mục default có thể
lược bỏ, khi đó nếu rơi vào trường hợp không có giá trị phù hợp thì cấu trúc
switch sẽ thoát ra.
Lưu ý: biểu thức phải có kết quả là giá trị kiểu nguyên (char, int,
long…). Các nhóm lệnh có thể gồm nhiều lệnh nhưng không cần đặt trong dấu
ngoặc nhọn{}.

So sánh lưu đồ giải thuật của 2 dạng switch - case (có default và không có
default).

Trần Thừa – 2010 51


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Vào Vào

Biểu thức Biểu thức

= giá trị 1 = giá trị 1


case giá trị 1 case giá trị 1

có break ? có break ?

= giá trị 2 = giá trị 2


case giá trị 2 case giá trị 2

có break ? có break ?

= giá trị n = giá trị n


case giá trị n case giá trị n

có break ? có break ?
= giá trị
ngẫu nhiên
Ra default

có break ?

Ra

Tóm lại cấu trúc switch case sẽ dựa vào kết quả tính toán của biểu thức
để chọn lựa khối lệnh phù hợp. Khi sử dụng switch case ta chiếm dụng nhiều
tài nguyên bộ nhớ hơn nếu dùng if vì vậy, cần cân nhắc nếu quỹ bộ nhớ của vi
điều khiển trong ứng dụng không còn nhiều.

3. Cấu trúc vòng lặp.


a. Vòng lặp FOR
Vòng lặp for cho phép lặp lại công việc cho đến khi điều kiện sai.
Cú pháp:
for (Biểu thức 1; biểu thức 2; biểu thức 3)
Công việc;

Trần Thừa – 2010 52


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Vào

Tính giá trị


Biểu thức 1 Tính giá trị
biểu thức 3

Kiểm điều kiện Đúng


Thực hiện
ở biểu thức 2
công việc

Sai

Ra

Giải thích:
- Biểu thức 1: thông thường là một phép gán để khởi tạo giá trị ban đầu
cho biến điều kiện.
- Biểu thức 2: là một biểu thức kiểm tra điều kiện đúng sai để dừng vòng
lặp.
- Khi biểu thức 2 vắng mặt thì nó được coi là luôn luôn đúng
- Biểu thức 3: thông thường là một phép gán để thay đổi giá trị của biến
điều kiện.
- Trong mỗi biểu thức có thể có nhiều biểu thức con. Các biểu thức con
được phân biệt bởi dấu phẩy.
Công việc: được thể hiện là 1 câu lệnh hay 1 khối lệnh. Thứ tự thực hiện
của câu lệnh for như sau:
B1: Tính giá trị của biểu thức 1.
B2: Tính giá trị của biểu thức 2.
- Nếu giá trị của biểu thức 2 là sai (=0): thoát khỏi câu lệnh for.
- Nếu giá trị của biểu thức 2 là đúng (!=0): <Công việc> được
thực hiện.
B3: Tính giá trị của biểu thức 3 và quay lại B2.

b. Vòng lặp while.


Vòng lặp while giống như vòng lặp for, dùng để lặp lại một công việc
nào đó cho đến khi điều kiện sai.

Trần Thừa – 2010 53


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Cú pháp:
while (Biểu thức điều kiện)
<công việc

Lưu đồ:
Vào

Kiểm biểu Đúng Thực hiện


thức điều kiện công việc

Sai

Ra

Giải thích:
Trước tiên, biểu thức điều kiện được kiểm tra: nếu biểu thức này có giá
trị sai(=0) thì lập tức thoát vòng lập. Nếu biểu thức này có giá trị đúng (!=0)
thì thực hiện khối lệnh công việc, sau đó lặp lại việc kiểm tra biểu thức điều
kiện.
Do việc kiểm điều kiện nằm ở đầu vòng lặp nên có thể vòng lặp sẽ
không thực hiện được lần nào.

c. Vòng lặp do while.


Vòng lặp thực hiện cho đến khi biểu thức điều kiện sai.
Cú pháp:
do{
công việc;
}while (biểu thức điều kiện);

Trần Thừa – 2010 54


Lập trình vi điều khiển AVR với ngôn ngữ C WWW.EEELABS.ORG

Lưu đồ:

Vào

Thực hiện
công việc

Đúng
Kiểm biểu
thức điều kiện

Sai

Ra

Giải thích:
Khi bắt đầu vào vòng lặp thì khối lệnh công việc được thực hiện trước,
sau đó chương trình sẽ tiến hành tính toán và kiểm tra biểu thức điều kiện, nếu
biểu thức này vẫn đúng(!=0) thì vòng lặp sẽ tiếp tục thực hiện khối lệnh công
việc, nếu biểu thức điều kiện sai(=0) thì lập tức thoát khỏi vòng lặp.
Do việc kiểm điều kiện nằm ở phía sau khối lệnh nên vòng lặp do while
thực hiện ít nhất 1 lần.

Trần Thừa – 2010 55

You might also like