You are on page 1of 121

LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

VI ĐIỀU KHIỂN AVR


Vi điều khiển AVR được giới thiệu lần đầu tiên vào năm 1996 bởi hãng
Atmel
Hiện nay nay ở việt nam có rất nhiều dòng vi điều khiển như
8051,PIC,AVR,ARM,…ngoài AVR thì Atmel cũng khá nỗi tiếng ở thị
trường với nam với dòng vi điều khiển 8051 như
AT89C51,AT89C52,AT89S51,AT89S52,…nhưng so với 8051 thì AVR
có nhiều điểm nỗi trội hơn hẳn 8051 mà họ sản xuất.
Phạm vi ứng dụng của AVR ở các hãng điện tử nước ngoài khá phổ biến
như các hãng VIKING,SMEG,…các sản phẩm của họ đa phần là sử dụng
vi điều khiển AVR bao gồm các mạch điều khiển của tủ lạnh,bếp từ,bếp
hồng ngoại,bếp điện,lò vi sóng,máy rửa bát,…
Ngoài ra chúng ta có thể làm được nhiều mạch ứng dụng trong nhiều lĩnh
vực khác nhau từ vi điều khiển AVR.
Là dòng vi điều khiển mới mẻ nên khá mạnh và hỗ trợ nhiều modul chức
năng tích hợp trong chip.Từ đó giúp người học dễ dàng tiếp cận và phát
triển cho các ứng dụng riêng của mình.

BÀI 1
VI ĐIỀU KHIỂN ATMEGA16
1.GIỚI THIỆU SƠ QUA
-Bộ nhớ Flash: 16 Kbtye
-Bộ nhớ RAM: 2 Kbyte
-Bộ nhớ EEPROM: 512 Byte
-Tốc độ dao động: 0 → 16Mhz
-Điện áp nguồn nuôi: 4.5V → 5.5V
2.SƠ ĐỒ CHÂN
*Sơ đồ chân như hình vẽ
-Để ATmega16 hoạt động được thì ta cần nối một số chân đặc biệt sau
+GND (chân 11 và chân 31): Nối GND của nguồn cấp
+VCC (chân 10): Nối với VCC của nguồn cấp
+AVCC (chân 30): Nối VCC của nguồn cấp.Đây là chân cấp nguồn cho
Modul ADC nội tích hợp trong vi điều khiển
+AREF (chân 32): Nối tụ gốm 100pF để lọc nhiễu,trong trường hợp
dùng
chân này để cấp điện áp tham chiếu cho Modul ADC
nội
thì sẽ nối chân này với nguồn điện áp tham chiếu.

1
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
+RESET (chân 9): Nối với mạch reset
+XTAL1,XTAL2 (chân 12 và chân 13): Nối với bộ dao động thạch anh
ngoài.Trong trường hợp dùng thạch anh nội thì có thể để trống 2 chân
này.

2
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

3
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
3.CÁC CHÂN IO
-ATmega16 có tổng cộng 40 chân trong đó có 32 chân IO.Các chân IO đó
nằm trên 4 cổng A,B,C,D.Mỗi cổng có 8 chân.
-Các cổng IO có các thanh ghi điều hướng tương ứng là:
DDRA,DDRB,DDRC,DDRD.
-Để thiết lập cổng là đầu ra Output thì các bit tương ứng trong thanh ghi
DDR của cổng đó phải được thiết lập ở mức logic 1.Khi cổng là Output
thì chúng ta truy cập cổng bằng thanh ghi PORT.
-Để thiết lập cổng là đầu ra Input thì các bit tương ứng trong thanh ghi
DDR của cổng đó phải được thiết lập ở mức logic 0.Khi cổng là Input thì
chúng ta truy cập cổng bằng thanh ghi PIN.
-Khi lập trình trên phần mềm CodeVisionAVR thì chúng ta có thể truy
cập các cổng này ở cả chế độ byte và cả ở chế độ bit.
-Từ đó chúng ta có thể thiết lập chế độ IO cho các cổng như sau
DDRX.i=0 => Chân có trọng số bit thứ i của cổng X là đầu vào Input
Lúc này ta xử lý cổng bằng PINX.i
DDRX.i=1 => Chân có trọng số bit thứ i của cổng X là đầu ra Output
Lúc này ta xử lý cổng bằng PORTX.i
+Trong đó i là 1 trong các giá trị từ 0 đến 7
+X là các ký tự A,B,C hoặc D
*Ví dụ
-Thiết lập cổng A là đầu ra,chân thứ 4 của cổng B là đầu vào chúng ta
làm như sau.
DDRA=0xff; (Tất cả các chân cổng A là đầu ra)
DDRB.3=0; (chân số 4 của cổng B là đầu vào)
-Bên trong các cổng của ATmega16 có tích hợp điện trở băng nội.các
điện trở băng nội này hay dùng khi cổng ở chế độ Input.Khi cổng là Input
chúng ta có thể kích hoạt điện trở băng nội của cổng đó bằng cách cho
các chân PORTX.i của cổng đó tương ứng lên mức logic 1.
-Ví dụ
DDRB.0=0;PORTB.0=1; (chân B0 là Input và được kích hoạt điện trở
băng nội).
*Cách thức truy cập IO
-Truy cập dạng byte (truy cập trên toàn bộ thanh ghi)
DDRA=0xff;//truy cập trên tất cả 8 bit trên thanh ghi DDRA
PORTA=0x80;//truy cập trên toàn bộ PORTA
if(PINA==0xff) //thanh ghi đầu vào của cổng A
-Truy cập trên từng bit,từng chân IO
DDRA.0=1;//chỉ khởi tạo 1 mình bit DDRA.0=1
PORTA.0=1;//chỉ truy cập đến chân A0
PORTA.1=1;//chỉ truy cập đến chân A1
PORTA.2=0;//chỉ truy cập đến chân A2
PORTB.0=0;//chỉ truy cập đến chân B0

4
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 2
CHUYỂN ĐỔI CƠ SỐ
-Có 3 hệ cơ số sử dụng phổ biến nhất trong lập trình là cơ số 2,cơ số 10
và cơ số 16.
-Trong C thường dùng các ký hiệu sau để đặc trưng cho hệ cơ số
0x là ký hiệu của hệ hexa (Cơ số 16)
0b là ký hiệu của hệ nhị phân (Cơ số 2)
hệ thập phân thì không có ký hiệu chỉ viết số (Cơ số 10)
-Các hệ cơ số khác ít dùng nên không cần quan tâm
1.CHUYỂN ĐỔI GIỮA HỆ THẬP PHÂN VÀ NHỊ PHÂN
*Chuyển từ thập phân sang nhị phân
-Ta lấy số thập phân chia liên tiếp cho 2 sau đó kết quả sẽ là các phần
dư,vị trí đầu tiên lấy từ phần cuối cùng của phép chia đảo ngược về.
-Ví dụ 1: Chuyển đổi số thập phân 112 sang hệ nhị phân
unsigned char x=112;
=> x=112= 0b01110000

5
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

*Chuyển từ nhị phân sang thập phân


-Lấy lần lượt các bit nhị phân nhân với lũy thừa của 2 với số mũ là trọng
số tương ứng với bit đó.Sau đó cộng tất cả lại ta sẽ được số thập phân
tương ứng.
-Ví dụ 2: Chuyển đổi 2 số x,y sang hệ thập phân
unsigned char x=0b01110000;
unsigned char y=0b10001001;
x=0b01110000= 0*27 + 1*26 + 1*25 + 1*24 + 0*23 + 0*22 + 0*21 + 0*20
= 0 + 64 + 32 + 16 + 0 + 0 + 0 + 0 =112
y=0b10001001= 1*27 + 0*26 + 0*25 + 0*24 + 1*23 + 0*22 + 0*21 + 1*20
= 128 + 0 + 0 + 0 + 8 + 0 + 0 + 1 =137

2.CHUYỂN ĐỔI GIỮA HỆ THẬP PHÂN VÀ HEXA


*Chuyển đổi từ thập phân sang hexa

6
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Ta lấy số thập phân chia cho 16,sau đó lấy các phần nguyên của phép
chia tiếp tục chia cho 16,kết quả sẽ là các phần dư của phép chia.Vị trí bắt
đầu lấy là phần dư cuối cùng của phép chia ngược về.
-Ví dụ 1: Chuyển đổi số thập phân x sang hệ hexa
unsigned int x=4589;
=> x= 4589 = 0x11ed (A=10,B=11,C=12,D=13,E=14,F=15)

*Chuyển từ hexa sang thập phân


-Ta lấy từng phần trong số hexa nhân với lũy thừa của 16,số mũ tương
ứng với ví trí của nó.
-Ví dụ 2: Chuyển đổi số hexa x sang hệ thập phân
unsigned int x=0x11ed;
 x=0x11ed= 1*163 + 1*162 + 14*161 + 13*160
= 4096 + 256 + 224 + 13 = 4589

3.CHUYỂN ĐỔI GIỮA HỆ NHỊ PHÂN VÀ HEXA


-Để chuyển đổi từ nhị phân sang hexa ta phân tích số nhị phân thành các
nhóm 4 bit,sau đó chuyển đổi từng nhóm đó thành giá trị hexa tương ứng.
-Để chuyển đổi từ hexa sang nhị phân ta phân tích các thành phần hexa
thành các nhóm 4 bit nhị phân.

7
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Trong trường hợp số nhị phân không đủ 4 bit thì ta bù vào các bit 0 vào
phía trước để đủ 4 bit rồi nhóm lại.
-Bảng chuyển đổi qua lại giữa nhị phân và hexa như sau.

Hệ Thập Phân Hệ Nhị Phân Hệ Hexa


Số Thập Phân bit 3 bit 2 bit 1 bit 0 Số Hexa
0 0 0 0 0 0
1 0 0 0 1 1
2 0 0 1 0 2
3 0 0 1 1 3
4 0 1 0 0 4
5 0 1 0 1 5
6 0 1 1 0 6
7 0 1 1 1 7
8 1 0 0 0 8
9 1 0 0 1 9
10 1 0 1 0 A
11 1 0 1 1 B
12 1 1 0 0 C
13 1 1 0 1 D
14 1 1 1 0 E
15 1 1 1 1 F

-Ví dụ 1: Chuyển đổi số hexa 0xf4 sang hệ nhị phân tương ứng
=> y=0xf4=0b11110100
Vì dựa vào bảng ta thấy f=0b1111 và 4=0b0100
-Ví dụ 2: Chuyển đổi số nhị phân 0b11011100 sang hệ hexa tương ứng
=> y=0b11011100=0xdc
Vì 0b1101=d và 0b1100=c

BÀI 3
LED ĐƠN
1.CÁC LOẠI LED ĐƠN THƯỜNG DÙNG
*Có nhiều cách để phân loại và gọi tên led đơn,sau đây là một số led
đơn sử dụng phổ biến trong cuộc sống.
-Led đơn 3mm có đường kính 3mm

8
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Led đơn 5mm có đường kính 5mm
-Led đơn 3mm,led đơn 5mm,…
-Led đơn màu đỏ,màu xanh,màu vàng,màu trắng,…
-Led đơn sáng đục,led đơn siêu sang,led đơn sáng trong
*Ý nghĩa và ứng dụng của led đơn
-Dùng để hiển thị
-Làm đèn báo sự cố
-Dùng để thắp sáng
-Dùng nhiều trong ngành quảng cáo led
2.CÁCH PHÂN CỰC CHO LED ĐƠN
*Có 2 cách phân cực phổ biến hay dùng với led đơn
-Cách 1: Các led nối chung chân âm.
+Ở trường hợp này các led tích cực ở mức cao
Khi chân điều khiển ở mức cao thì led sáng
Khi chân điều khiển ở mức thấp thì led tắt
Vì thế mà người ta gọi là đấu led tích cực ở mức cao

-Cách 2: Các led nối chung chân dương


+Ở trường hợp này các led tích cực ở mức thấp
Khi chân điều khiển ở mức thấp thì led sáng
Khi chân điều khiển ở mức cao thì led tắt
Vì thế mà người ta gọi là đấu led tích cực ở mức thấp

9
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

3.CÁCH TÍNH CHỌN ĐIỆN TRỞ HẠN DÒNG CHO LED


-Thông thường khi mua led nhà sản xuất sẽ cho 2 thông số điện áp led và
dòng điện hoạt động của led.
-Ví dụ: Led đỏ và led vàng có Uled=1.8V → 2.0V
Iled =10mA → 20mA
+Giả sử tôi chọn Uled=2V và Iled=15mA.Tôi dùng điện áp cấp cho mạch
led là 5V (U=5V)
+Theo như sơ đồ ta có
U=Iled*R + Uled
U-Uled 5V-2V 3V
=> R= = = =200 (ôm) => Chọn R=220 ôm
Iled 15mA 0.015A

10
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

4.VÍ DỤ
*Ví dụ 1: Điều khiển 8 led đơn nối PORTC
-Sáng chạy
-Sáng dần
-Sáng dồn
-Sáng giữa ra 2 bên
-Sáng từ 2 bên vào giữa
#include <mega16.h>
#include <delay.h>
void sangchay(signed char n)
{
signed char i;
PORTC=0x00;
delay_ms(400);
for(i=0;i<n;i++)
{
PORTC=(1<<i);
delay_ms(400);
}
}
void sangdan(signed char n)
{
signed char i;
PORTC=0x00;
delay_ms(400);

11
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
for(i=0;i<n;i++)
{
PORTC=PORTC|(1<<i);
delay_ms(400);
}
}
void sangdon(signed char n)
{
signed char i,j;
unsigned char p,q;
PORTC=0x00;
delay_ms(400);
p=0;q=0;
for(i=n-1;i>=0;i--)
{
for(j=0;j<=i;j++)
{
p=q|(1<<j);
PORTC=p;
delay_ms(400);
}
q=q|(1<<i);
}
}
void sanggiuara(signed char n)
{
signed char i;
PORTC=0x00;
delay_ms(400);
for(i=0;i<=n/2;i++)
{
PORTC=(0x10<<i)|(0x08>>i);
delay_ms(400);
}
}
void sangngoaivao(signed char n)
{
signed char i;
PORTC=0x00;
delay_ms(400);
for(i=0;i<=n/2;i++)
{
PORTC=(0x80>>i)|(0x01<<i);

12
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
delay_ms(400);
}
}
void main(void)
{
DDRC=0xff;
while(1)
{
sangchay(8);
sangdan(8);
sangdon(8);
sanggiuara(8);
sangngoaivao(8);
}
}

BÀI 4
IC GHI DỊCH 74HC595
1.SƠ ĐỒ CHÂN

13
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

14
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

2.NGUYÊN LÝ HOẠT ĐỘNG


Mỗi khi đưa bit mới vào bộ đệm thì bit trước đó sẽ được dịch sang vị trí
tiếp theo và bit mới sẽ thế chỗ vị trí bit cũ.
-Chân SH_CP: Tạo xung đưa dữ liệu tại chân DS vào bộ đệm của
IC74HC595.Xung này tích cực ở sườn lên
-Chân ST_CP: Tạo xung đưa dữ liệu từ bộ đệm ra các đầu ra Q.
Xung này tích cực ở sườn lên
-Chân DS: Chứa liệu cần xử lý
-Chân MR: Dùng để reset bộ đệm,chân này tích cực ở mức thấp.
Khi nào muốn reset bộ đệm ta cho chân này xuống mức thấp.
-Chân OE: Chân cho phép IC74HC595 hoạt động,chân này tích cực ở
mức
thấp.Muốn cho 74HC595 hoạt động cần nối chân này với
mức
thấp
-Các chân Q0→Q7: Là các chân đầu ra của IC74HC595

15
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Chân Q7’: Nếu dùng nhiều IC74HC595 thì chân Q7’ này dùng để nối
vào
chân DS của con 74HC595 tiếp theo nó.
3.VÍ DỤ
*Ví dụ 1: Điều khiển 8 led đơn nối IC74HC595
-Sáng chạy
-Sáng dần
-Sáng dồn
-Sáng giữa ra 2 bên
-Sáng từ 2 bên vào giữa
#include <mega16.h>
#include <delay.h>
#define ck PORTC.0
#define da PORTC.1
#define cp PORTC.2
void dich8bit(unsigned char n)
{
signed char i;
unsigned char x;
for(i=0;i<8;i++)
{
x=(n<<i)&0x80;
if(x==0) da=0;
else da=1;
ck=0;ck=1;//dua data tu chan da vao bo dem
}
cp=0;cp=1;//xuat data tu bo dem ra cac dau ra Q
}
void sangchay(signed char n)
{
signed char i;
unsigned char p;
dich8bit(0x00);
delay_ms(400);
for(i=0;i<n;i++)
{
p=(1<<i);
dich8bit(p);
delay_ms(400);
}
}
void sangdan(signed char n)
{

16
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
signed char i;
unsigned char p;
dich8bit(0x00);
delay_ms(400);
p=0;
for(i=0;i<n;i++)
{
p=p|(1<<i);
dich8bit(p);
delay_ms(400);
}
}
void sangdon(signed char n)
{
signed char i,j;
unsigned char p,q;
dich8bit(0x00);
delay_ms(400);
p=0;q=0;
for(i=n-1;i>=0;i--)
{
for(j=0;j<=i;j++)
{
p=q|(1<<j);
dich8bit(p);
delay_ms(400);
}
q=q|(1<<i);
}
}
void sanggiuara(signed char n)
{
signed char i;
unsigned char p;
dich8bit(0x00);
delay_ms(400);
for(i=0;i<=n/2;i++)
{
p=(0x10<<i)|(0x08>>i);
dich8bit(p);
delay_ms(400);
}
}

17
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
void sangngoaivao(signed char n)
{
signed char i;
unsigned char p;
dich8bit(0x00);
delay_ms(400);
for(i=0;i<=n/2;i++)
{
p=(0x80>>i)|(0x01<<i);
dich8bit(p);
delay_ms(400);
}
}
void main(void)
{
DDRC=0xff;
while(1)
{
sangchay(8);
sangdan(8);
sangdon(8);
sanggiuara(8);
sangngoaivao(8);
}
}

18
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 5
LED 7 ĐOẠN,LED 7 THANH
1.CẤU TẠO CỦA LED 7 ĐOẠN
-Led 7 đoạn có nhiều loại,nhưng loại mà chúng ta thường dùng là loại
được tạo thành bởi 7 thanh led đơn và 1 dấu chấm dp ở góc phải phí
dưới.Nên người ta gọi là led 7 đoạn hay còn gọi là led 7 thanh tùy từng
người.

-Led 7 đoạn thường có 2 loại nếu phân theo cách đấu led
+Loại A chung: Tất cả các thanh được nối chung chân dương
+Loại K chung: Tất cả các thanh được nối chung chân âm
2.CÁC TẠO MÃ LED 7 ĐOẠN
-Cách tạo bảng mã cho led 7 đoạn phụ thuộc vào đấu nối phần cứng (phụ
thuộc vào cách nối các thanh a,b,c,d,e,f,g,dp với các chân của vi điều
khiển).Sơ đồ đấu nối như thế nào thì ta phải tra mã led tương ứng với sơ
đồ đó.

19
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

-Chú ý: Dùng led A chung thì thanh nào sáng sẽ ở mức 0,thanh nào tắt sẽ
ở mức 1.Còn led K chung thì ngược lại.
-Nếu như sơ đồ kết nối như trên và led 7 đoạn đang dùng là loại A chung
(Chung dương) thì ta sẽ tạo được bảng mã led tương ứng với các số cần
hiển thị như sau.
-Ví dụ: Để hiển thị số 0 thì tất cả các thanh đều sáng chỉ mỗi thanh g và
thanh dp tắt.Vì thế mà a=0,b=0,c=0,d=0,e=0,f=0,g=1,dp=1.

Mã Nhị Phân (Trọng Số Bit) Mã Hexa


Số Cần Hiển Thị 7 6 5 4 3 2 1 0
PORTC
dp g f e d c b a
0 1 1 0 0 0 0 0 0 0xc0
1 1 1 1 1 1 0 0 1 0xf9
2 1 0 1 0 0 1 0 0 0xa4
3 1 0 1 1 0 0 0 0 0xb0
4 1 0 0 1 1 0 0 1 0x99
5 1 0 0 1 0 0 1 0 0x92
6 1 0 0 0 0 0 1 0 0x82

20
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
7 1 1 1 1 1 0 0 0 0xf8
8 1 0 0 0 0 0 0 0 0x80
9 1 0 0 1 0 0 0 0 0x90

=> Muốn hiển thị số 5 trên led 7 đoạn ta chỉ cần đặt PORTC=0x92
-Tương tự ta có thể tự tạo các mã cho các ký tự A,B,C,…
-Đối với led K chung cách thức tạo mã cũng tương tự nhưng mức tích cực
sẽ thay đổi.
3.CÁCH TÍNH ĐIỆN TRỞ HẠN DÒNG CHO LED 7 ĐOẠN
-Led 7 đoạn có nhiều loại,loại 5V,loại 12V,…,tùy theo từng loại mà ta
đấu nối cho phù hợp.
-Giả sử ta dùng loại led 7 đoạn 5V và nguồn nuôi cho vi điều khiển AVR
cũng là 5V.Nếu ta xem mỗi thanh của led 7 đoạn giống như 1 led đơn thì
ta cũng áp dụng cách tính điện trở hạn dòng cho mỗi thanh tương tự như
tính toán trên led đơn.
-Từ đó ta có mỗi thanh sẽ nối trở R=220 ôm

4.VÍ DỤ
*Ví dụ 1: Viết chương trình đếm số từ 00 → 99 sử dụng led 7 đoạn A
chung (Chung dương) không quét led.
#include <mega16.h>
#include <delay.h>

21
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void num2s(unsigned char n)
{
PORTC=maled[n/10];//xuat ma led hang chuc ra PORTC
PORTD=maled[n%10];//xuat ma led hang don vi ra PORTD
}
void main(void)
{
unsigned char i;
DDRC=0xff;
DDRD=0xff;
PORTC=0xff;//tat led chuc
PORTD=0xff;//tat led don vi
while(1)
{
for(i=0;i<100;i++)
{
num2s(i);
delay_ms(400);
}
}
}

22
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

*Ví dụ 2: Đếm các số từ 00 → 99 dùng led 7 đoạn A chung sử dụng


phương pháp quét led.
#include <mega16.h>
#include <delay.h>
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void num2s(unsigned char n)
{
unsigned char m[2];
signed char i;
m[0]=n/10;//tach hang chuc
m[1]=n%10;//tach hang don vi
for(i=0;i<2;i++)
{
PORTC=maled[m[i]];
PORTD=(1<<i);
delay_ms(4);
PORTD=0x00;
}
}
void main(void)
{
unsigned char i,j;
DDRC=0xff;
DDRD=0xff;
PORTD=0x00;//tat tat ca cac led
while(1)
{
for(i=0;i<100;i++)
{
for(j=0;j<40;j++) num2s(i);//vong lap j de giu trang thai quan sat
}
}
}

23
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 6
TEXT LCD
1.SƠ ĐỒ CHÂN

24
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

-Chân 1 (GND): Nối GND của nguồn


-Chân 2 (VCC): Nối VCC của nguồn
-Chân 3 (VEE): Nối biến trở,dùng để điều chỉnh độ tương phản của
LCD
-Chân 4 (RS): Chọn chế độ lệnh hoặc dữ liệu
=0: Chế độ lệnh
=1: Chế độ dữ liệu
-Chân 5 (RW): Chọn chế độ đọc hoặc ghi
=0: Chế độ ghi
=1: Chế độ đọc
-Chân 6 (EN): Chân tạo xung giao tiếp LCD
Xung này tích cực ở sườn xuống (1→0)
-Chân 7→14 (D0 → D7): Các chân dữ liệu LCD
-Chân 15 (A): Chân dương led đèn nền
-Chân 16 (K): Chân âm led đèn nền
2.CÁC LỆNH THƯỜNG DÙNG
-Xóa màn hình: DB=0x01
-Trở về đầu dòng: DB=0x02 hoặc 0x03
Bộ đếm địa chỉ RAM AC=0,nội dung DDRAM giữ nguyên
*Các lệnh Entry Mode Set
-Giảm AC,không dịch hiển thị: DB=0x04
-Giảm AC,dịch hiển thị sang phải: DB=0x05
-Tăng AC,không dịch hiển thị: DB=0x06

25
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Tăng AC,dịch hiển thị sang trái: DB=0x07
*Các lệnh điều khiển hiển thị
-Bật hiển thị,bật con trỏ,nhấp nháy ký tự hiện tại: DB=0x0f
-Bật hiển thị,bật con trỏ,không nhấp nháy ký tự hiện tại: DB=0x0e
-Bật hiển thị,tắt con trỏ,nhấp nháy ký tự: DB=0x0d
-Bật hiển thị,tắt con trỏ,không nháy ký tự: DB=0x0c
*Các lệnh chức năng
-Mode 4 bit,2 dòng,ký tự=5x8 điểm ảnh: 0x28
-Mode 4 bit,2 dòng,ký tự=5x10 điểm ảnh: 0x2C
-Mode 8 bit,2 dòng,ký tự=5x8 điểm ảnh: 0x38
-Mode 8 bit,2 dòng,ký tự=5x10 điểm ảnh: 0x3C
*Địa chỉ dòng 3 và dòng 4 của LCD 16x4 và 20x4
Với LCD 16x4:
- Địa chỉ đầu dòng thứ 3 là 0x80 + 16 = 0x90
- Địa chỉ đầu dòng thứ 4 là 0xC0 + 16 = 0xD0
Với LCD 20x4:
- Địa chỉ đầu dòng thứ 3 là 0x80 + 20 = 0x94
- Địa chỉ đầu dòng thứ 4 là 0xC0 + 20 = 0xD4
3.VÍ DỤ
-Viết chương trình hiển thị dòng chữ VI DDIEU KHIEN ATmega16 trên
LCD 16x2.
#include <mega16.h>
#include <delay.h>
#define RS PORTC.5
#define RW PORTC.6
#define EN PORTC.7
#define LCD PORTD
void lenhlcd(unsigned char n)
{
RS=0;RW=0;
LCD=n;
EN=1;
EN=0;
delay_ms(10);
}
void datalcd(unsigned char n)
{
RS=1;RW=0;
LCD=n;
EN=1;
EN=0;
delay_ms(10);
}

26
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
void gotoxy(unsigned char x,unsigned char y)
{
unsigned char address;
address=0x80+64*(x-1)+(y-1);
lenhlcd(address);
}
void textlcd(unsigned char x,unsigned char y,unsigned char *s)
{
signed char i;
for(i=0;s[i]!=0;i++)
{
gotoxy(x,y+i);
datalcd(s[i]);
}
}
void lcd_init(void)
{
lenhlcd(0x38);
lenhlcd(0x06);
lenhlcd(0x0c);
delay_ms(400);
}
void main(void)
{
DDRC=0xff;
DDRD=0xff;
lcd_init();
lenhlcd(0x01);
textlcd(1,3,"VI DIEU KHIEN");
textlcd(2,5,"ATmega16");
while(1);
}

27
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 7
LED MA TRẬN
1.CẤU TẠO LED MA TRẬN
*Ví dụ led matix 8x8

28
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

29
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

-Các hàng là chung dương hoặc chung âm,tương tự đối với cột cũng vậy
2.QUÉT LED MA TRẬN
Có 2 loại quét led ma trận phổ biến là quét theo hàng và quét theo cột
*Nguyên lý quét
-Quét theo cột tại 1 thời điểm chỉ có 1 cột được bật
-Quét theo hang tại 1 thời điểm chỉ có 1 hàng bật
-Để quét theo cột ta có thể làm như sau
+Tắt tất cả các cột (1)
+Xuất mã tương ứng của cột i ra các hàng (2)
+Bật cột i,tắt các cột còn lại (3)
+Tạo trễ vài trăm us → vài nghìn ms (4)
+Tắt cột i (5)
+Quay lại bước (1),tăng i và lặp lại
-Để quét theo hàng ta có thể làm như sau
+Tắt tất cả các hàng (1)
+Xuất mã tương ứng của hàng i ra các cột (2)
+Bật hàng i,tắt các hàng còn lại (3)
+Tạo trễ vài trăm us → vài nghìn ms (4)
+Tắt hàng i (5)
+Quay lại bước (1),tăng i và lặp lại
3.VÍ DỤ

30
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
*Ví dụ 1: Viết chương trình hiển thị chữ U trên led ma trận 8x8 green
#include <mega16.h>
#include <delay.h>
unsigned char text[8]={255,128,0,63,63,0,128,255};
void quetled(void)
{
signed char i;
for(i=0;i<8;i++)
{
PORTD=text[i];
PORTC=(0x80>>i);
delay_ms(4);
PORTC=0x00;
}
}
void main(void)
{
DDRC=0xff;PORTC=0xff;
DDRD=0xff;
while(1) quetled();
}

31
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

*Ví dụ 2: Viết chương trình đếm các số từ 0 → 4 dùng led ma trận 8x8
#include <mega16.h>
#include <delay.h>
unsigned char text[5][8]={
{255,129,0,60,60,0,129,255},//0
{255,63,61,0,0,63,63,255},//1
{255,124,60,28,12,32,49,255},//2
{255,36,36,36,36,0,129,255},//3
{255,195,193,204,207,0,0,255}//4
};
void quetled(unsigned char n)
{
signed char i;
for(i=0;i<8;i++)
{
PORTD=text[n][i];
PORTC=(0x80>>i);
delay_ms(4);
PORTC=0x00;
}
}
void main(void)

32
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
{
unsigned char i,j;
DDRC=0xff;PORTC=0xff;
DDRD=0xff;
while(1)
{
for(i=0;i<5;i++)
{
for(j=0;j<30;j++) quetled(i);
}
}
}

BÀI 8
NÚT NHẤN,PHÍM BẤM
*Nút bấm thường được dùng để chọn chế độ hoạt động,thiết lập các
thông số điều khiển khiển.Nút bấm được sử dụng khá rộng dãi trong các
sản phẩm điện tử.
*Ví dụ: Đồng hồ có nút bấm chỉnh thời gian,điện thoại có nút bấm số,…
1.PHÍM NHẤN,NÚT BẤM ĐƠN

33
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

34
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

*Cách kiểm kiểm tra nút bấm trong vi điều khiển AVR
-Để kiểm tra nút bấm ta phải thiết lập chân nối nút bấm là input,đối với
avr có điện trở băng nội tại chân đó thì ta không cần phải nối thêm điện
trở băng 10k bên ngoài.Trong trường hợp không có điện trở băng nội thì
ta phải nối thêm điện trở băng ngoại khoảng 10k.
-Khi kiểm tra nút ta dùng thanh ghi PIN để kiểm tra
-Ví dụ
DDRD.2=0;PORTD.2=1;//thiết lập chân D2 là input và kéo điện
trở băng nội
if(PIND.2==0) //kiểm tra nếu nhấn phím
{
d++;
if(d>10) d=0;
while(PIND.2==0);//chống nảy phím,rung phím,chờ nhả phím
}
2.MA TRẬN PHÍM BẤM
-Ma trận phím bấm là 1 bảng phím được tạo thành bởi nhiều nút bấm.

35
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Dùng ma trận phím bấm sẽ tiết kiệm được chân cho vi điều khiển
-Dưới đây là ma trận phím 8x8

*Cách tính giá trị phím bấm


-Giá trị của phím bấm được tạo ra theo quy định của người thiết kế ra ma
trận phím đó.Vì thế giá trị của phím phụ thuộc vào người tạo ra ma trận
phím.
-Ví dụ theo bàn phím trên thì giá trị của phím có mối quan hệ với i,k như
sau
phim=4*i+k;
i=0  PORTB=0b11110111=0xf7
i=1  PORTB=0b11111011=0xfb
i=2  PORTB=0b11111101=0xfd
i=3  PORTB=0b11111110=0xfe
j=0  PINB.4=0
j=1  PINB.5=0
j=2  PINB.6=0
j=3  PINB.7=0
3.VÍ DỤ
*Ví dụ 1
#include <mega16.h>
#include <delay.h>
void main(void)

36
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
{
DDRB=0x00;PORTB=0xff;
DDRD=0xff;PORTD=0x00;
while(1)
{
if(PINB.0==0)
{
PORTD.0=~PORTD.0;
while(PINB.0==0);
}
if(PINB.1==0)
{
PORTD.1=~PORTD.1;
while(PINB.1==0);
}
if(PINB.2==0)
{
PORTD.2=~PORTD.2;
while(PINB.2==0);
}
if(PINB.3==0)
{
PORTD.3=~PORTD.3;
while(PINB.3==0);
}
if(PINB.4==0)
{
PORTD.4=~PORTD.4;
while(PINB.4==0);
}
if(PINB.5==0)
{
PORTD.5=~PORTD.5;
while(PINB.5==0);
}
if(PINB.6==0)
{
PORTD.6=~PORTD.6;
while(PINB.6==0);
}
if(PINB.7==0)
{
PORTD.7=~PORTD.7;

37
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
while(PINB.7==0);
}
}
}

*Ví dụ 2
#include <mega16.h>
#include <delay.h>
unsigned char kt=0;
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
unsigned char maphim[4]={247,251,253,254};
unsigned char quetphim(unsigned char n)
{
unsigned char i,j;
unsigned char phim=16;
for(i=0;i<4;i++)
{
PORTB=maphim[i];
for(j=0;j<n;j++)
{
if(PINB.4==0)
{
phim=4*i;
while(PINB.4==0);

38
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
}
if(PINB.5==0)
{
phim=4*i+1;
while(PINB.5==0);
}
if(PINB.6==0)
{
phim=4*i+2;
while(PINB.6==0);
}
if(PINB.7==0)
{
phim=4*i+3;
while(PINB.7==0);
}
}
}
return(phim);
}
void hienthi(unsigned char n)
{
unsigned char x,m[2];
signed char i,k;
x=n;
k=0;
do
{
m[k]=x%10;
x=x/10;
k++;
}
while(x);
for(i=0;i<k;i++)
{
PORTC=maled[m[i]];
PORTD=(0x02>>i);
delay_ms(4);
PORTD=0x00;
}
}
void main(void)
{

39
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
unsigned char p,q=16;
DDRB=0x0f;PORTB=0xff;
DDRC=0xff;
DDRD=0xff;
PORTD=0x00;
while(1)
{
p=quetphim(100);
if(p!=16)
{
q=p;
kt=1;
}
if(kt==1) hienthi(q);
}
}

BÀI 9
NGẮT
40
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
I.NGẮT
1.KHÁI NIỆM VỀ NGẮT
-Một sự kiện nào đó xảy ra gây ra sự gián đoạn một quá trình đang tiếp
diễn thì gọi là ngắt.
2.BẢNG VECTO NGẮT CỦA ATMEGA16

-Ví dụ: Xem trong bảng ta thấy chỉ số vectơ ngắt ngoài 1 là 3,chỉ số vectơ
ngắt của ngắt tràn timer1 là 9.
3.CẤU TRÚC CHƯƠNG TRÌNH NGẮT
*Chương trình ngắt là chương trình con phục vụ ngắt,trong
CodevisionAVR cấu trúc của chương trình ngắt được tổ chức như sau
interrupt[chỉ số vectơ ngắt] void tenctn(void)
{
//xử lý ngắt
}

41
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Ví dụ ngắt ngoài 1 có chỉ số vectơ ngắt là 3 do đó chương trình con xử lý
ngắt ngoài 1 có cấu trúc như sau:
interrupt[3] void ngoai1(void)
{
//xử lý ngắt ngoài 1
}
4.NGẮT TOÀN CỤC,NGẮT THÀNH PHẦN
-Các ngắt riêng biệt thì gọi là ngắt thành phần
-Ngắt toàn cục ngắt kiểm soát tất cả các ngắt thành phần
+Khi cấm ngắt toàn cục có nghĩa là cấm tất cả các ngắt thành phần
+Khi cho phép ngắt toàn cục thì ta được phép cho phép những ngắt thành
phần hoạt động nếu ta muốn.
-Ví dụ nếu ta xem mỗi lớp học của 1 trường học là ngắt thành phần và
trường học là ngắt toàn cục.Thì cánh cửa của mỗi lớp học sẽ là bit điều
khiển ngắt thành phần và cánh cửa trường học sẽ là bit điều khiển ngắt
toàn cục.Mọi người ở phòng 1 muốn ra khỏi trường thì cửa lớp của phòng
đó phải mở và cửa trường cũng phải mở.
-Trong CodevisionAVR cho nhúng 2 lệnh assembly để điều khiển ngắt
toàn cục.
#asm(“sei”); //Cho phép ngắt toàn cục (hình dung như mở cổng trường
học)
#asm(“cli”); //Cấm ngắt toàn cục (đóng cổng trường học)
5.CÁCH THIẾT LẬP NGẮT
-Thiết lập các bit chọn tác nhân gây ra ngắt
-Thiết lập các bit cho phép ngắt thành phần tương ứng
-Xóa các cờ ngắt
-Cho phép ngắt toàn cục
*Chú ý:
-Trong CodevisionAVR mỗi khi thực hiện chương trình ngắt trình biên
dịch CodevisionAVR sẽ tự xóa cờ ngắt tương ứng với ngắt đó.Vì vậy ta
không cần phải xóa cờ ngắt sau mỗi lần xảy ra ngắt.
-Đối với trình biên dịch không tự xóa cờ ngắt khi thực hiện chương trình
ngắt thì khi vào chương trình ngắt ta phải xóa ngay cờ ngắt để cờ ngắt có
thể nhận biết các lần ngắt tiếp theo.
II.NGẮT NGOÀI
1.CÁC CHÂN LIÊN QUAN
+ATmega16 có 3 chân ngắt ngoài
-Ngắt ngoài 0 nằm ở chân PD2 (INT0)
-Ngắt ngoài 1 nằm ở chân PD3 (INT1)
-Ngắt ngoài 2 nằm ở chân PB2 (INT2)

42
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

43
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

2.CÁC THANH GHI LIÊN QUAN


MCUCR
MCUCSR
GICR
GIFR
3.CHI TIẾT CÁC THANH GHI
*Thanh ghi MCUCR

SM2 SE SM1 SM0 ISC11 ISC10 ISC01 ISC00

-Các bit ISC11,ISC10: Chọn tác nhân gây ra ngắt ngoài 1

ISC11 ISC10 Tác nhân gây ngắt ngoài 1


0 0 Khi xuất hiện mức thấp tại chân INT1 (Chân PD3)
0 1 Khi có sự thay đổi mức logic tại chân INT1 (Chân
PD3)
1 0 Khi xuất hiện sườn xuống tại chân INT1 (Chân PD3)
1 1 Khi xuất hiện sườn lên tại chân INT1 (Chân PD3)

-Các bit ISC01,ISC00: Chọn tác nhân gây ra ngắt ngoài 0

ISC01 ISC00 Tác nhân gây ngắt ngoài 0


0 0 Khi xuất hiện mức thấp tại chân INT0 (Chân PD2)
0 1 Khi có sự thay đổi mức logic tại chân INT0 (Chân
PD2)
1 0 Khi xuất hiện sườn xuống tại chân INT0 (Chân PD2)
1 1 Khi xuất hiện sườn lên tại chân INT0 (Chân PD2)

*Thanh ghi MCUCSR

JTD ISC2 JTRF WDRF BORF EXTRF PORF

-Bit ISC2: chọn tác nhân gây ra ngắt ngoài 2


=0: Tác nhân gây ra ngắt ngoài 2 là sườn xuống tại chân INT2 (Chân
PB2)
=1: Tác nhân gây ra ngắt ngoài 2 là sườn lên tại chân INT2 (Chân PB2)

44
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
*Thanh ghi GICR

INT1 INT0 INT2 IVSEL IVCE

-Bit INT1: Cho phép ngắt ngoài 1


=0: Cấm ngắt ngoài 1
=1: Cho phép ngắt ngoài 1
-Bit INT0: Cho phép ngắt ngoài 0
=0: Cấm ngắt ngoài 0
=1: Cho phép ngắt ngoài 0
-Bit INT2: Cho phép ngắt ngoài 2
=0: Cấm ngắt ngoài 2
=1: Cho phép ngắt ngoài 2

*Thanh ghi GIFR

INTF1 INTF0 INTF2

-Bit INTF1: Cờ báo ngắt ngoài 1


+Khi có tác nhân gây ra ngắt ngoài 1 tại chân INT1 (PD3) thì bit này sẽ
tự động bật lên 1.
+Bit này phải được khởi tạo bằng 0 khi thiết lập.
-Bit INTF0: Cờ báo ngắt ngoài 0
+Khi có tác nhân gây ra ngắt ngoài 0 tại chân INT0 (PD2) thì bit này sẽ
tự động bật lên 1.
+Bit này phải được khởi tạo bằng 0 khi thiết lập.
-Bit INTF2: Cờ báo ngắt ngoài 2
+Khi có tác nhân gây ra ngắt ngoài 1 tại chân INT2 (PB2) thì bit này sẽ
tự động bật lên 1.
+Bit này phải được khởi tạo bằng 0 khi thiết lập.
4.VÍ DỤ
*Viết chương trình tăng giá trị biến đếm d
-Nếu ấn nút nhấn nối chân INT0 (PD2) biến d tăng 1 đơn vị
-Nếu ấn nút nhấn nối chân INT1 (PD3) biến d tăng 2 đơn vị
-Nếu ấn nút nhấn nối chân INT2 (PB2) biến d tăng 3 đơn vị
#include <mega16.h>
#include <delay.h>
unsigned int d=0;
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

45
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
void ext_init(void)
{
DDRB.2=0;PORTB.2=1;//int2 la input
DDRD.2=0;PORTD.2=1;//int0 la input
DDRD.3=0;PORTD.3=1;//int1 la input
MCUCR=(MCUCR&0xf0)|0x0a;//ngat o canh xuong tai int1,int0
MCUCSR=(MCUCSR&0xbf);//ngat canh xuong tai int2
GICR=(GICR&0x1f)|0xe0;//cho phep ca 3 ngat ngoai hoat dong
GIFR=0x00;//xoa co ngat
#asm("sei");//cho phep ngat toan cuc
delay_ms(400);//cho phan cung on dinh
d=0;
}
void hienthi(unsigned int n)
{
unsigned int x,y;
unsigned char m[5];
signed char i,k;
k=0;
x=n;
do
{
y=x%10;
x=x/10;
m[k]=y;
k++;
}
while(x);
for(i=0;i<k;i++)
{
PORTC=maled[m[i]];
PORTA=(1<<i);
delay_ms(4);
PORTA=0x00;
}
}
interrupt[2] void ngoai0(void)
{
d++;
if(d>99) d=0;
}
interrupt[3] void ngoai1(void)
{

46
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
d=d+2;
if(d>99) d=0;
}
interrupt[19] void ngoai2(void)
{
d=d+3;
if(d>99) d=0;
}
void main(void)
{
DDRC=0xff;
DDRA=0xff;PORTA=0x00;//tat het le 7 doan
ext_init();
while(1) hienthi(d);
}

47
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 10
EEPROM NỘI
1.ỨNG DỤNG BỘ EEPROM NỘI
-Các giá trị lưu vào bộ nhớ eeprom nội sẽ không bị mất đi khi bị mất điện
vì vậy chúng ta có thể dùng bộ nhớ eeprom để lưu những giá trị quan
trọng đề phòng bị mất điện.
-Trong ATmega16 có hỗ trợ 512 byte bộ nhớ eeprom nội có địa chỉ từ 0
→ 511
2.CÁC THANH GHI LIÊN QUAN
EECR → Điều khiển eeprom
EEDR → Chứa dữ liệu cần ghi hoặc đọc
EEAR(H,L) → Xác định địa chỉ ô nhớ eeprom cần làm việc
3.CHI TIẾT CÁC THANH GHI
*Thanh ghi EECR

EERIE EEWME EEWE EERE

-Bit EERIE: Cho phép ngắt sẵn sàng eeprom


=0: Cấm ngắt EEPROM
=1: Cho phép ngắt EEPROM
-Bit EEMWE: Cho phép ghi vào EEPROM
=0: Cấm ghi
=1: Cho phép ghi
-Bit EEWE: Điều khiển quá trình ghi
=1: Bắt đầu ghi,khi ghi xong bit này sẽ tự động về 0
+Bit EEMWE phải set lên 1 trước khi set bit này lên 1
-Bit WWRE: Cho phép đọc
=0: Cấm đọc
=1: Cho phép đọc
*Thanh ghi EEAR (H,L)
-Thanh ghi này có 16 bit dùng để chứa địa chỉ eeprom

48
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
EEAR8 EEARH
EEAR7 EEAR6 EEAR5 EEAR4 EEAR3 EEAR2 EEAR1 EEAR0 EEARL

*Thanh ghi EEDR


-Thanh ghi này chứa dữ liệu cần đọc hoặc ghi

MSB LSB

4.VÍ DỤ
*Viết chương trình tăng giá trị biến đếm d,giá trị của biến đếm d sẽ được
lưu vào ô nhớ có địa chỉ là 1 trong eeprom nội.Nếu bị mất điện khi có
điện trở lại giá trị của biến đếm d vẫn còn như trước lúc mất điện.
-Nếu ấn nút nhấn nối chân INT0 (PD2) biến d tăng 1 đơn vị
-Nếu ấn nút nhấn nối chân INT1 (PD3) biến d tăng 2 đơn vị
-Nếu ấn nút nhấn nối chân INT2 (PB2) biến d tăng 3 đơn vị
#include <mega16.h>
#include <delay.h>
unsigned int d=0;
unsigned char crom;
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void send_eeprom(unsigned int address,unsigned char n)
{
while((EECR&0x02)==0x02);//cho EEWE=0 thi da ghi xong
EEAR=address;//xac dinh o nho can ghi
EEDR=n;//du lieu can ghi
EECR=0x04;//EEMWE=1,cho phep ghi
EECR=0x06;//bat dau ghi
}
unsigned char read_eeprom(unsigned int address)
{
while((EECR&0x02)==0x02);//cho EEWE=0
EEAR=address;
EECR=0x01;//cho phep doc
return(EEDR);
}
void ext_init(void)
{
DDRB.2=0;PORTB.2=1;//int2 la input
DDRD.2=0;PORTD.2=1;//int0 la input
DDRD.3=0;PORTD.3=1;//int1 la input

49
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
MCUCR=(MCUCR&0xf0)|0x0a;//ngat o canh xuong tai int1,int0
MCUCSR=(MCUCSR&0xbf);//ngat canh xuong tai int2
GICR=(GICR&0x1f)|0xe0;//cho phep ca 3 ngat ngoai hoat dong
GIFR=0x00;//xoa co ngat
#asm("sei");//cho phep ngat toan cuc
delay_ms(400);//cho phan cung on dinh
}
void hienthi(unsigned int n)
{
unsigned int x,y;
unsigned char m[5];
signed char i,k;
k=0;
x=n;
do
{
y=x%10;
x=x/10;
m[k]=y;
k++;
}
while(x);
for(i=0;i<k;i++)
{
PORTC=maled[m[i]];
PORTA=(1<<i);
delay_ms(4);
PORTA=0x00;
}
}
interrupt[2] void ngoai0(void)
{
d=read_eeprom(1);
d++;
if(d>99) d=0;
send_eeprom(1,d);
}
interrupt[3] void ngoai1(void)
{
d=read_eeprom(1);
d=d+2;
if(d>99) d=0;
send_eeprom(1,d);

50
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
}
interrupt[19] void ngoai2(void)
{
d=read_eeprom(1);
d=d+3;
if(d>99) d=0;
send_eeprom(1,d);
}
void main(void)
{
DDRC=0xff;
DDRA=0xff;PORTA=0x00;//tat het le 7 doan
ext_init();
crom=read_eeprom(0);
if(crom!=0x0f)
{
send_eeprom(0,0x0f);//bien crom de tranh khoi tao khi co dien tro lai
send_eeprom(1,0);//khoi tao gia tri ban dau vao o nho 1
}
d=read_eeprom(1);
while(1) hienthi(d);
}

51
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 11
ADC
1.CÁC CHÂN LIÊN QUAN
-Chân AVCC (Chân cấp nguồn cho ADC)
-Chân AVREF (Chân cấp điện áp tham chiếu)
-Các chân ADC của ATmega16 nằm ở PORTA
ADC0 (PA0)
ADC1 (PA1)
ADC2 (PA2)
ADC3 (PA3)
ADC4 (PA4)
ADC5 (PA5)
ADC6 (PA6)
ADC7 (PA7)

52
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

2.CÁC THANH GHI LIÊN QUAN


ADMUX
ADCSRA
ADCH,ADCL (ADCW)
SFIOR

53
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
3.CHI TIẾT CÁC THANH GHI
*Thanh ghi ADMUX

REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0

-Các bit REFS1,REFS0: Chọn điện áp tham chiếu cấp cho ADC

REFS1 REFS0 Điện Áp Tham Chiếu Cấp Cho ADC


0 0 Lấy tại chân AREF
0 1 Lấy tại chân AVCC,nối 1 tụ lọc nhiễu tại AREF
1 0 -
1 1 Điện áp tham chiếu nội 2.56V,tụ lọc nhiễu nối
AREF

-Bit ADLAR: Hiệu chỉnh kết quả ADC


=0: Căn phải,toàn bộ 10 bit kết quả ADC nằm ở 10 bit thấp của
thanh ghi ADCW,tức là ADCL chứa 8 bit thấp,ADCH
chứa 2
bit cao.6 bit cao trong ADCH không chứa kết quả ADC.

D D D D D D D D D D
9 8 7 6 5 4 3 2 1 0

=1: Căn trái,toàn bộ 10 bit kết quả ADC nằm ở 10 bit cao của
thanh ghi ADCW,tức là ADCH chứa 8 bit cao,ADCL chứa
2
bit thấp.6 bit thấp trong ADCL không chứa kết quả ADC.

D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

-Các bit MUX4,3,2,1,0: Dùng để chọn kênh ADC

54
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

*Thanh ghi ADCSRA

ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0

-Bit ADEN: Cho phép ADC hoạt động


=0: Cấm ADC
=1: Cho phép ADC hoạt động
-Bit ADSC: Bit bắt đầu chuyển đổi ADC
=1: Bắt đầu quá trình chuyển đổi ADC,khi chuyển đổi hoàn thành
bit
này sẽ tự động xóa về 0 đồng thời bit ADIF tự động bật lên 1.
-Bit ADATE: Bit cho phép kích mở ADC tự động
=0: Không cho phép chế độ kích ADC tự động

55
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
=1: Cho phép chế độ kích hoạt ADC tự động,nguồn kích mở ADC
tự
động sẽ được thiết lập bởi thanh ghi SFIOR
-Bit ADIF: Cờ báo ngắt ADC
Khi chuyển đổi ADC hoàn thành bit này sẽ tự động bật lên 1,nếu ta cho
phép ngắt ADC hoạt động thì chương trình ngắt ADC sẽ được thực hiện.
-Bit ADIE: Cho phép ngắt ADC
=0: Cấm ngắt ADC
=1: Cho phép ngắt ADC
-Các bit ADPS2,ADPS1,ADPS0: Chọn nguồn xung cấp cho ADC

ADPS2 ADPS1 ADPS0 Nguồn xung cấp cho ADC


0 0 0 Favr/2
0 0 1 Favr/2
0 1 0 Favr/4
0 1 1 Favr/8
1 0 0 Favr/16
1 0 1 Favr/32
1 1 0 Favr/64
1 1 1 Favr/128

*Thanh ghi ADCH,ADCL (ADCW)


-Là các thanh ghi chứa kết quả ADC
-ADCH,ADCL là 2 thanh ghi 8 bit,còn ADCW là thanh ghi 16 bit

ADCH ADCL
ADCW

*Thanh ghi SFIOR (Chỉ thiết lập khi bit ADATE=1)

ADTS2 ADTS1 ADTS0 ACME PUD PSR2 PSR10

-Các bit ADTS2,ADTS1,ADTS0: Dùng để chọn nguồn kích mở ADC tự


động.

ADTS2 ADTS1 ADTS0 Nguồn Kích ADC Tự Dộng


0 0 0 Khi ADC rãnh
0 0 1 Khi có sự kiện Analog Comparator

56
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
0 1 0 Khi có tác nhân gây ra ngắt ngoài 0
0 1 1 Khi TCNT0 = OCR0
1 0 0 Khi timer0 bị tràn
1 0 1 Khi TCNT1 = OCR1B
1 1 0 Khi timer1 bị tràn
Khi xảy ra sự kiện input capture tại chân
1 1 1
ICP1

4.VÍ DỤ
*Ví dụ 1: Đọc điện áp ADC tại chân ADC2 (PA2)
#include <mega16.h>
#include <delay.h>
unsigned int adc=0;
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void adc_init(void)
{
DDRA=0x00;//input ADC in PORTA
ADMUX=0x40;//VREF=AVCC
ADCSRA=0x80;//ADCEN=1
#asm("cli");//disable all interrupt
delay_ms(400);//cho mot khoang time de on dinh
}
void hienthi(unsigned int n)
{
unsigned int x,y;
unsigned char m[5];
signed char i,k;
k=0;
x=n;
do
{
y=x%10;
x=x/10;
m[k]=y;
k++;
}
while(x);
for(i=0;i<k;i++)
{
PORTC=maled[m[i]];

57
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
PORTD=(0x08>>i);
delay_ms(4);
PORTD=0x00;
}
}
unsigned int read_adc(unsigned char kenh)
{
ADMUX=0x40|kenh;
hienthi(adc);//doi vai ms de kenh duoc thiet lap on dinh
ADCSRA=0x80|0x40;
while((ADCSRA&0x10)==0x00)hienthi(adc);//doi den khi ADIF=1
return(ADCW);
}
void main(void)
{
DDRC=0xff;
DDRC=0xff;PORTD=0x00;//tat het le 7 doan
adc_init();
while(1)
{
adc=read_adc(2);
hienthi(adc);
}
}

58
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
*Ví dụ 2: Đo nhiệt độ bằng LM35 tại chân ADC7
#include <mega16.h>
#include <delay.h>
unsigned int adc=0;
unsigned char
maled[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x9c,
0xc6};
void adc_init(void)
{
DDRA=0x00;//input ADC in PORTA
ADMUX=0x40;//VREF=AVCC
ADCSRA=0x80;//ADCEN=1
#asm("cli");//disable all interrupt
delay_ms(400);//cho mot khoang time de on dinh
}
void hienthi(unsigned int n)
{
unsigned int x,y;
unsigned char m[6];
signed char i,k;
k=0;
x=n;
do
{
y=x%10;
x=x/10;
m[k]=y;
k++;
}
while(x);
for(i=k+1;i>=2;i--) m[i]=m[i-2];
m[1]=10;//do
m[0]=11;//C
for(i=0;i<k+2;i++)
{
PORTC=maled[m[i]];
PORTD=(0x08>>i);
delay_ms(4);
PORTD=0x00;
}
}
unsigned int read_adc(unsigned char kenh)
{

59
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
ADMUX=0x40|kenh;
hienthi(adc);//doi vai ms de kenh duoc thiet lap on dinh
ADCSRA=0x80|0x40;
while((ADCSRA&0x10)==0x00)hienthi(adc);//doi den khi ADIF=1
return(ADCW);
}
void main(void)
{
float nhietdo;
DDRC=0xff;
DDRC=0xff;PORTD=0x00;//tat het le 7 doan
adc_init();
while(1)
{
adc=read_adc(7);//T=100*u (1)
nhietdo=adc;//u=(N*Vref)/1023 (2)
nhietdo=0.4887*nhietdo;//thay (2) vao (1) ta duoc
T=500N/1023=0.4887*N
adc=nhietdo;
hienthi(nhietdo);
}
}

60
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 12
ANALOG COMPARATOR
1.CÁC CHÂN LIÊN QUAN
AIN0 (PB2)
AIN1 (PB3)
ADC0 (PA0)
ADC1 (PA1)
ADC2 (PA2)
ADC3 (PA3)
ADC4 (PA4)
ADC5 (PA5)
ADC6 (PA6)
ADC7 (PA7)

61
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

-Trong ATmega16 có hỗ trợ 1 bộ so sánh tín hiệu Analog

2.CÁC THANH GHI LIÊN QUAN


ACSR
ADMUX
ADCSRA
SFIOR

62
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
3.CHI TIẾT CÁC THANH GHI
*Thanh ghi ACSR

ACD ACBG ACO ACI ACIE ACIC ACIS1 ACIS0

-Bit ACD: Cho phép Analog Comparator hoạt động


=0: Cho phép Analog Comparator hoạt động
=1: Cấm Analog Comparator
-Bit ACBG: Chọn điện áp đầu vào P của baộ so sánh
=0: Điện áp đầu vào P lấy tại chân AIN0
=1: Một điện áp nội cố định được cấu hình bởi BOR khi nạp chip

-Bit ACO: Bit trạng thái đầu ra của bộ Analog Comparator


-Bit ACI: Cờ báo ngắt Analog Comparator
Khi có tác nhân gây ra ngắt Analog Comparator xảy ra thì bit này tự động
set lên 1.Tác nhân gây ra ngắt Analog Comparator được thiết lập bởi 2
bit ACIS1,ACIS0.
-Bit ACIE: Cho phép ngắt Analog Comparator
=0: Cấm ngắt Analog Comparator
=1: Cho phép ngắt Analog Comparator
-Bit ACIC: Cho phép Analog Comparator là đầu vào Input Capture của
Timer1
=0: Đầu ra của bộ Analog Comparator sẽ không được kết nối đến
input capture
=1: Đầu ra của bộ Analog Comparator sẽ được kết nối đến input
Capture
-Các bit ACIS1,ACIS0: Dùng để chọn tác nhân gây ra ngắt Analog
Comparator

ACIS1 ACIS0 Tác nhân gây ra ngắt Analog Comparator


0 0 Khi có sự thay đổi trạng thái của bit ACO

63
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
0 1 -
1 0 Khi bit ACO chuyển từ mức cao xuống thấp
1 1 Khi bit ACO chuyển từ mức thấp lên mức cao

*Các thanh ghi ADMUX,ADCSA,SFIOR: Dùng để chọn điện áp đưa


tới đầu vào N của bộ Analog Comparator
ADMUX
REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1
ADCSRA
ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0
SFIOR
ADTS2 ADTS1 ADTS0 ACME PUD PSR2 PSR10

ACME ADEN MUX2..0 Analog Comparator N input


0 x xxx AIN1
1 1 xxx AIN1
1 0 000 ADC0
1 0 001 ADC1
1 0 010 ADC2
1 0 011 ADC3
1 0 100 ADC4
1 0 101 ADC5
1 0 110 ADC6
1 0 111 ADC7

4.VÍ DỤ
*So sánh điện áp đầu vào giữa 2 chân AIN0(PB2) và AIN1(PB3) nếu
điện áp chân AIN0 > AIN1 thì 8 led đơn nối PORTC sáng.Nếu AIN0 ≤
AIN1 thì 8 led đơn nối PORTC tắt.
#include <mega16.h>
#include <delay.h>
void analogcp_init(void)
{
DDRB.2=0;//AIN0 la input
DDRB.3=0;//AIN1 la input
ADCSR=0x00;
SFIOR=(SFIOR&0xf7);//ACME=0
#asm("cli");

64
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
delay_ms(400);
}
void main(void)
{
DDRC=0xff;
PORTC=0x00;
analogcp_init();
while(1)
{
if((ACSR&0x20)==0x20) PORTC=0xff;//neu bit ACO=1 thi
PORTC=0xff
else PORTC=0x00;//neu bit ACO=0 thi PORTC=0x00;
}
}

65
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 13
TIMER/COUNTER0
(BỘ TIMER/COUNTER 8 BIT)
1.CÁC CHÂN LIÊN QUAN
-Chân đưa xung ngoài vào Timer0 chân T0 (PB0)
-Chân xuất xung đầu ra của bộ Timer0 chân OC0 (PB3)

66
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

2.CÁC THANH GHI LIÊN QUAN


TCCR0
TCNT0
OCR0
TIMSK
TIFR
3.CHI TIẾT CÁC THANH GHI
*Thanh ghi TCCR0

FOC0 WGM00 COM01 COM00 WGM01 CS02 CS01 CS00

-Bit FOC0: Bit này luôn đặt bằng 0


-Bit WGM01,WGM00: Chọn chế độ hoạt động của Timer0

WGM01 WGM00 Chế độ hoạt động


0 0 Normal
0 1 PWM Phase Correct

67
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
1 0 CTC
1 1 Fast PWM

-Các bit COM01 và COM00: Dùng để chọn trạng thái đầu ra chân OC0
(PB3) với bộ timer0.
+Chế độ NON-PWM (Normal và CTC)

COM01 COM00 Trạng thái chân OC0


0 0 Chân OC0 ngắt kết nối,OC0 là IO
0 1 Đảo chân OC0 khi TCNT0 = OCR0
1 0 Xóa chân OC0=0 khi TCNT0 = OCR0
1 1 Set chân OC0=1 khi TCNT0=OCR0

Chế độ Normal giá trị trong thanh ghi TCNT0 sẽ tăng liên tục từ 0 →
255.Khi bị tràn sẽ tự động xóa về 0.
Chế độ CTC giá trị trong thanh ghi TCNT0 sẽ bắt đầu tăng từ 0 sau mỗi
chu kỳ timer0 nếu trong quá trình tăng mà xảy ra trường hợp
TCNT0=OCR0 thì TCNT0 sẽ bị xóa về 0.
Ví dụ 1: Chọn Normal và COM01:00=01

Ví dụ 2: Chọn CTC và COM01:00=01

68
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

+Chế độ Fast-PWM
Chế độ Fast-PWM giá trị trong thanh ghi TCNT0 sẽ tăng dần theo mỗi
chu kỳ timer0 từ 0 → max rồi reset về 0.
Bảng thiết lập trạng thái đầu ra chân OC0 ở chế độ Fast – PWM như sau:

COM01 COM00 Trạng thái chân OC0


0 0 Chân OC0 ngắt kết nối,OC0 là IO
0 1 Not use
Xóa chân OC0=0 khi TCNT0 = OCR0,
1 0
Set chân OC0=1 khi TCNT0 = Bottom
Set chân OC0=1 khi TCNT0 = OCR0,
1 1
Xóa chân OC0=0 khi TCNT0 = Bottom

69
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

+Chế độ PWM-Phase Correct


Ở chế độ PWM phase correct giá trị trong thanh ghi TCNT0 sẽ tăng dần
theo mỗi chu kỳ timer0 từ 0 → max sau đó sẽ giảm dần từ max về 0.
Bảng trạng thái đầu ra tại chân OC0 như sau:

COM01 COM00 Trạng thái chân OC0


0 0 Chân OC0 ngắt kết nối,OC0 là IO
0 1 Not use
Xóa chân OC0=0 khi TCNT0 = OCR0 trong
quá trình đếm lên.
1 0
Set chân OC0=1 khi TCNT0 = OCR0 trong quá
trình đếm xuống.
Set chân OC0=1 khi TCNT0 = OCR0 trong quá
trình đếm lên.
1 1
Xóa chân OC0=0 khi TCNT0 = OCR0 trong
quá trình đếm xuống.

70
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

-Các bit CS02,CS01,CS00: Dùng để chọn nguồn xung cấp cho timer0

CS02 CS01 CS00 Nguồn xung cấp cho timer0


0 0 0 No Clock (Timer0 Stop)
0 0 1 Favr/1
0 1 0 Favr/8
0 1 1 Favr/64
1 0 0 Favr/256
1 0 1 Favr/1024
1 1 0 Xung lấy tại chân T0 (PB0),sườn xuống
1 1 1 Xung lấy tại chân T0 (PB0),sườn lên

*Thanh ghi TIMSK

OCIE2 TOIE2 TICIE1 OCIE1A OCIE1B TOIE1 OCIE0 TOIE0

-Bit OCIE0: Cho phép ngắt compare timer0


=0: Cấm
=1: Cho phép compare
-Bit TOIE0: Cho phép ngắt tràn timer0
=0: Cấm
=1: Cho phép ngắt tràn timer0

*Thanh ghi TIFR

OCF2 TOV2 ICF1 OCF1A OCF1B TOV1 OCF0 TOV0

71
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

-Bit OCF0: Cờ báo ngắt tác nhân gây ra ngắt compare timer0
Khi giá trị trong thanh ghi TCNT0 = OCR0 thì bit này sẽ tự động bật lên
1
-Bit TOV0: Cờ báo ngắt tràn timer0
Khi giá trị trong thanh ghi TCNT0 bị tràn bit này sẽ tự động bật lên 1
*Thanh ghi TCNT0
Là thanh ghi 8 bit chứa giá trị đếm của timer/counter0
4.VÍ DỤ
*Ví dụ 1: Tạo xung vuông tại chân OC0 có chu kỳ 1ms,Ton=Toff,dùng
thạch anh 8Mhz.
-Ở chế độ CTC ta có:
N0
Txung=2*(OCR0+1)*
Favr
+Chọn N0=64 ta tính ra được OCR0=61
#include <mega16.h>
#include <delay.h>
void ctc_init(void)
{
//fosc=8Mhz,Txung=1ms,N=64,CTC,timer0
TCCR0=0x1b;
OCR0=61;
TCNT0=0x00;
TIMSK=0x00;
TIFR=0x00;
#asm("cli");
}
void main(void)
{
DDRB.3=1;//OC0 is output
ctc_init();
while(1);
}

72
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

*Ví dụ 2: Viết chương trình đếm thời gian giờ,phút,giây bằng timer0
#include <mega16.h>
#include <delay.h>

73
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
unsigned char gio=0,phut=0,giay=0;
unsigned char d=0;
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void timer0_init(void)
{
//fosc=8Mhz,mode Normal
TCCR0=0x05;//timer0,chia tan 1024
TCNT0=0;//sau 32640us se xay ra ngat 1 lan
TIMSK=0x01;//cho phep ngat tran timer0
TIFR=0x00;
#asm("sei");
}
void hienthi(void)
{
unsigned char i,m[6];
m[0]=gio/10;
m[1]=gio%10;
m[2]=phut/10;
m[3]=phut%10;
m[4]=giay/10;
m[5]=giay%10;
for(i=0;i<6;i++)
{
PORTC=maled[m[i]];
PORTD=(1<<i);
delay_ms(4);
PORTD=0x00;
}
}
interrupt[10] void ngattimer0(void)
{
TCNT0=0;//nap lai gia cho cho TCNT0
d++;
if(d==20)//neu d=20 thi du 1 giay
{
giay++;
d=0;
}
if(giay==60)
{
phut++;
giay=0;

74
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
}
if(phut==60)
{
gio++;
phut=0;
}
if(gio==24) gio=0;
}
void main(void)
{
DDRC=0xff;
DDRD=0xff;
PORTD=0x00;
timer0_init();
while(1)hienthi();
}

*Ví dụ 3: Viết chương trình đếm xung ngoại tại chân T0


#include <mega16.h>
#include <delay.h>
unsigned int d=0;
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void counter0_init(void)
{
//fosc=8Mhz,mode Normal

75
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
TCCR0=0x06;//timer1,xung ngoai tai chan T0
TCNT0=0;
TIMSK=0x00;//cho phep ngat tran timer1
TIFR=0x00;
#asm("cli");
}
void hienthi(unsigned int n)
{
unsigned int x,y;
unsigned char m[6];
signed char i,k;
x=n;
k=0;
do
{
y=x%10;
x=x/10;
m[k]=y;
k++;
}
while(x);
for(i=0;i<k;i++)
{
PORTC=maled[m[i]];
PORTD=(0x08>>i);
delay_ms(4);
PORTD=0x00;
}
}
void main(void)
{
DDRC=0xff;
DDRD=0xff;
PORTD=0x00;
counter0_init();
while(1)
{
d=TCNT0;
hienthi(d);
}
}

76
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 14
TIMER/COUNTER1
(BỘ TIMER/COUNTER 16 BIT)
1.CÁC CHÂN LIÊN QUAN
-Chân đưa xung ngoài vào bộ Timer1 chân T1 (PB1)
-Chân xuất xung đầu ra của Timer1 chân OC1A (PD5)
-Chân xuất xung đầu ra của Timer1 chân OC1B (PD4)
-Chân Input Capture của Timer1 chân ICP1 (PD6)

77
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

78
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
2.CÁC THANH GHI LIÊN QUAN
TCNT1
TCCR1A
TCCR1B
OCR1A(H,L)
OCR1B(H,L)
ICR1(H,L)
TIMSK
TIFR
3.CHI TIẾT CÁC THANH GHI
*Thanh ghi TCCR1A và thanh ghi TCCR1B
TCCR1A
COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
TCCR1B
ICNC1 ICES1 WGM13 WGM12 CS12 CS11 CS11
-Các bit FOC1A,FOC1B: Luôn xóa 2 bit này bằng 0
-Bit ICNC1: Thiết lập bộ lọc nhiễu cho đầu vào input capture tại chân
ICP1
=0: Bộ lọc input capture sẽ không hoạt động
=1: Bộ lọc input capture sẽ hoạt động
-Bit ICES1: Chọn tác nhân gây input capture
=0: Khi có cạnh xuống tại chân ICP1 (PD6)
=1: Khi có cạnh xuống tại chân ICP1 (PD6)
-Các bit WGM13,WGM12,WGM11,WGM10: Chọn chế độ hoạt động
của timer/counter1

Update TOV1
W13 W12 W11 W10 MODE TOP
OCR1X Set
0 0 0 0 Normal 0xffff Ngay LT MAX
0 0 0 1 PC 8 bit 0x00ff TOP BOT
0 0 1 0 PC 9 bit 0x01ff TOP BOT
0 0 1 1 PC 10 bit 0x03ff TOP BOT
0 1 0 0 CTC OCR1A Ngay LT MAX
0 1 0 1 Fast 8 bit 0x00ff BOT TOP
0 1 1 0 Fast 9 bit 0x01ff BOT TOP
0 1 1 1 Fast 10 bit 0x03ff BOT TOP
1 0 0 0 P&F ICR1 BOT BOT
1 0 0 1 P&F OCR1A BOT BOT
1 0 1 0 PC ICR1 TOP BOT
1 0 1 1 PC OCR1A TOP BOT
1 1 0 0 CTC ICR1 Ngay LT MAX
1 1 0 1 - - - -

79
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
1 1 1 0 Fast ICR1 BOT TOP
1 1 1 1 Fast OCR1A BOT TOP

-Các bit COM1A1,COM1A0 và COM1B1,COM1B0: Chọn trạng thái


đầu ra các chân OC1A,OC1B
+Chế độ Normal
Chế độ này giá trị trong thanh ghi TCNT1 sẽ tăng dần theo mỗi chu kỳ
Timer1 từ 0 → MAX sau đó bị xóa về 0.
COM1A1 COM1A0 Trạng thái chân OC1A
0 0 Not Connect (Chân OC1A là IO)
0 1 Đảo trạng thái chân OC1A khi
TCNT1=OCR1A
1 0 Xóa chân OC1A = 0 khi TCNT1 = OCR1A
1 1 Set chân OC1A = 1 khi TCNT1 = OCR1A

+Chế độ CTC
Chế độ này giá trị trong thanh ghi TCNT1 sẽ tăng dần theo mỗi chu kỳ
Timer1 từ 0 → OCR1A (nếu dung kênh A) sau đó bị xóa về 0.

COM1A1 COM1A0 Trạng thái chân OC1A


0 0 Not Connect (Chân OC1A là IO)
0 1 Đảo trạng thái chân OC1A khi
TCNT1=OCR1A
1 0 Xóa chân OC1A = 0 khi TCNT1 = OCR1A

80
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
1 1 Set chân OC1A = 1 khi TCNT1 = OCR1A

+Chế độ PWM phase correct,phase and frequency correct


Chế độ này giá trị trong thanh ghi TCNT1 sẽ tăng dần theo mỗi chu kỳ
Timer1 từ 0 → TOP sau đó giảm dần từ TOP→0.

COM1A1 COM1A0 Trạng thái chân OC1A


0 0 Not Connect (Chân OC1A là IO)
-Khi WGM13:10 = 9
Đảo trạng thái chân OC1A khi
TCNT1=OCR1A
0 1 Kênh B trường hợp này không dùng (OC1B là
IO)
-Khi WGM13:10 ≠ 9 thì OC1A và OC1A
không kết nối.Các chân OC1A,OC1B là IO
Xóa chân OC1A = 0 khi TCNT1 = OCR1A
trong quá trình đếm lên.
1 0
Set chân OC1A = 1 khi TCNT1=OCR1A trong
quá trình đếm xuống.
Set chân OC1A = 1 khi TCNT1 = OCR1A
trong quá trình đếm lên.
1 1
Xóa chân OC1A = 0 khi TCNT1=OCR1A trong
quá trình đếm xuống.

81
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

+Chế độ Fast-PWM
Chế độ này giá trị trong thanh ghi TCNT1 sẽ tăng dần theo mỗi chu kỳ
Timer1 từ 0 → TOP sau đó bị xóa về 0.

COM1A1 COM1A0 Trạng thái chân OC1A


0 0 Not Connect (Chân OC1A là IO)
-Khi WGM13:10 = 15
Đảo trạng thái chân OC1A khi
TCNT1=OCR1A
0 1 Kênh B trường hợp này không dùng (OC1B là
IO)
-Khi WGM13:10 ≠ 15 thì OC1A và OC1A
không kết nối.Các chân OC1A,OC1B là IO
Xóa chân OC1A = 0 khi TCNT1 = OCR1A.
1 0
Set chân OC1A = 1 khi TCNT1= BOTTOM
Set chân OC1A = 1 khi TCNT1 = OCR1A.
1 1
Xóa chân OC1A = 0 khi TCNT1= BOTTOM

82
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

-Các bit CS12,CS11,CS10: Dùng để chọn nguồn xung cấp cho timer1

CS12 CS11 CS10 Nguồn xung cấp cho timer1


0 0 0 No Clock (Timer1 Stop)
0 0 1 Favr/1
0 1 0 Favr/8
0 1 1 Favr/64
1 0 0 Favr/256
1 0 1 Favr/1024
1 1 0 Xung lấy tại chân T1 (PB1),sườn xuống
1 1 1 Xung lấy tại chân T1 (PB1),sườn lên
*Thanh ghi TIMSK

OCIE2 TOIE2 TICIE1 OCIE1A OCIE1B TOIE1 OCIE0 TOIE0

-Bit TICIE1: Cho phép ngắt Input Capture


=0: Không cho phép
=1: Cho phép ngắt Input Capture
-Bit OCIE1A: Cho phép ngắt Compare kênh A
=0: Cấm ngắt compare kênh A
=1: Cho phép ngắt compare kênh A
-Bit OCIE1B: Cho phép ngắt Compare kênh B
=0: Cấm ngắt compare kênh B
=1: Cho phép ngắt compare kênh B

83
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Bit TOIE1: Cho phép ngắt tràn timer1
=0: Cấm ngắt tràn timer1
=1: Cho phép ngắt tràn timer1
*Thanh ghi TIFR

OCF2 TOV2 ICF1 OCF1A OCF1B TOV1 OCF0 TOV0

-Bit ICF1: Cờ báo tác nhân gây Input Capture xảy ra tại chân ICP1
(PD6)
Khi có tác nhân gây Input Capture tại chân ICP1 thì bit này sẽ tự động bật
lên 1.
-Bit OCF1A: Cờ báo ngắt compare kênh A
Khi giá trị trong thanh ghi TCNT1 = OCR1A bit này sẽ tự động bật lên 1
-Bit OCF1B: Cờ báo ngắt compare kênh B
Khi giá trị trong thanh ghi TCNT1 = OCR1B bit này sẽ tự động bật lên 1
-Bit TOV1: Cờ báo tràn Timer1
Khi giá trị trong thanh ghi TCNT1 bị tràn bit này sẽ tự động bật lên
1.Ngoài ra tác nhân ảnh hưởng đến bit này còn được thiết lập bởi các bit
WGM13,WGM12,WGM11,WGM10.
4.VÍ DỤ
*Ví dụ 1: Viết chương trình điều chế xung vuông tại chân OC1A,OCR1B
Thạch anh dùng 8Mhz.Điều chế xung ở chế độ Fast PWM,TOP=ICR1.
-Xung tại chân OC1A có Ton=500us,Tpwm=1000us
-Xung tại chân OC1B có Ton=250us,Tpwm=1000us
+Ở chế độ Fast PWM ta có:
N1
Tpwm=(TOP+1)*
Favr
+Trong đó: N1 là hệ số chia tần của bộ timer1,Favr là tần số bộ dao động
Ở đây dùng thạch anh ngoài 8Mhz và chọn N1=8 ta sẽ tính ra được
ICR1=TOP=999
OCR1A=500
OCR1B=250
#include <mega16.h>
#include <delay.h>
void fastpwm_init(void)
{
//fosc=8Mhz,N=8,fast pwm,timer1
//TOP=ICR1,Ton(A)=500us,Ton(B)=250us,Tpwm=1000us
TCNT1=0;
ICR1H=0x03;ICR1L=0xe7;//ICR1=999
OCR1A=500;
OCR1B=250;

84
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
TCCR1A=0xa2;
TCCR1B=0x1a;
TIMSK=0x00;
TIFR=0x00;
#asm("cli");
}
void main(void)
{
DDRD.4=1;//OC1B is output
DDRD.5=1;//OC1A is output
fastpwm_init();
while(1);
}

85
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

*Ví dụ 2: Viết chương trình đếm thời gian giờ,phút,giây bằng timer1
#include <mega16.h>
#include <delay.h>
unsigned char gio=0,phut=0,giay=0;
unsigned char d=0;
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void timer1_init(void)
{
//fosc=8Mhz,mode Normal
TCCR1A=0x00;
TCCR1B=0x02;//timer1,chia tan 8
TCNT1=15535;//sau 50000us se xay ra ngat 1 lan
TIMSK=0x04;//cho phep ngat tran timer1
TIFR=0x00;
#asm("sei");
}
void hienthi(void)
{
unsigned char i,m[6];
m[0]=gio/10;
m[1]=gio%10;
m[2]=phut/10;

86
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
m[3]=phut%10;
m[4]=giay/10;
m[5]=giay%10;
for(i=0;i<6;i++)
{
PORTC=maled[m[i]];
PORTD=(1<<i);
delay_ms(4);
PORTD=0x00;
}
}
interrupt[9] void ngattimer1(void)
{
TCNT1=15535;//nap lai gia cho cho TCNT1
d++;
if(d==20)//neu d=20 thi du 1 giay
{
giay++;
d=0;
}
if(giay==60)
{
phut++;
giay=0;
}
if(phut==60)
{
gio++;
phut=0;
}
if(gio==24) gio=0;
}
void main(void)
{
DDRC=0xff;
DDRD=0xff;
PORTD=0x00;
timer1_init();
while(1)hienthi();
}

87
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

*Ví dụ 3: Viết chương trình đếm xung ngoài tại chân T1


#include <mega16.h>
#include <delay.h>
unsigned int d=0;
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void counter1_init(void)
{
//fosc=8Mhz,mode Normal
TCCR1A=0x00;
TCCR1B=0x06;//timer1,xung ngoai tai chan T1
TCNT1=0;
TIMSK=0x00;//cho phep ngat tran timer1
TIFR=0x00;
#asm("cli");
}
void hienthi(unsigned int n)
{
unsigned int x,y;
unsigned char m[6];
signed char i,k;
x=n;
k=0;
do
{
y=x%10;
x=x/10;

88
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
m[k]=y;
k++;
}
while(x);
for(i=0;i<k;i++)
{
PORTC=maled[m[i]];
PORTD=(0x08>>i);
delay_ms(4);
PORTD=0x00;
}
}
void main(void)
{
DDRC=0xff;
DDRD=0xff;
PORTD=0x00;
counter1_init();
while(1)
{
if(TCNT1>9999) TCNT1=0;
d=TCNT1;
hienthi(d);
}
}

89
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 15
GIAO TIẾP USART
1.CÁC CHÂN LIÊN QUAN
-Chân nhận dữ liệu RXD (PD0)
-Chân truyền dữ liệu TXD (PD1)
-Chân tạo xung đồng bộ XCK (PB0)

90
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

91
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
2.CÁC THANH GHI LIÊN QUAN
UCSRA
UCSRB
UCSRC
UBRR(H,L)
UDR
3.CHI TIẾT CÁC THANH GHI
*Thanh ghi UCSRA

RXC TXC UDRE FE DOR PE U2X MPCM

-Bit RXC: Cờ báo nhận


Khi nhận xong dữ liệu bit này sẽ tự động bật lên 1.Vì thế chúng ta Có thể
dựa vào bit này để xác định khi nào cần đọc data từ thanh ghi UDR.

-Bit TXC: Cờ báo truyền


Khi truyền xong dữ liệu bit này sẽ tự động bật lên 1 để báo cho vi điều
khiển biết dữ liệu đã truyền xong.
-Bit UDRE: Cờ báo thanh ghi UDR
Khi thanh ghi UDR trống thì bit này sẽ tự động bật lên 1 để báo cho vi
điều khiển biết thanh ghi UDR đang trống và sẵn sàng để đưa dữ liệu mới
vào.
-Bit FE: Cờ báo lỗi khung truyền
Bit này sẽ tự động bật lên 1 nếu ký tự tiếp theo trong bộ đệm nhận xảy ra
lỗi khung truyền khi nhận.Đó là khi bit stop của ký tự tiếp theo trong bộ
đệm nhận bằng 0.Bit này sẽ có tác dụng đến khi bộ đệm nhận được đọc
(UDR được đọc).Bit FE sẽ =0 nếu khi stop của dữ liệu nhận là 1.
-Bit DOR: Cờ báo lỗi va chạm dữ liệu
Bit này sẽ tự động bật lên 1 khi dữ liệu cũ chưa nhận xong mà đã có dữ
liệu mới truyền đến bộ đệm nhận.Bit này có hiệu lực đến khi ta đọc thanh
ghi UDR.
-Bit PE: Cờ báo lỗi Parity (Lỗi chẵn lẻ)
Bit này sẽ tự động bật lên 1 nếu xảy ra lỗi parity
-Bit U2X: Nhân đôi tốc độ USART

USART U2X=0 U2X=1

92
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

Bất đồng bộ Favr Favr


UBRR= -1 UBRR= -1
(UART) 16*baud 8*baud

Đồng bộ Favr
UBRR= -1
(USRT) 2*baud

-Bit MPCM: Cho phép giao tiếp nhiều AVR


=0: Cấm giao tiếp nhiều AVR
=1: Cho phép giao tiếp giữa nhiều AVR trên đường RXD,TXD

*Thanh ghi UCSRB

RXCIE TXCIE UDRIE RXEN TXEN UCSZ2 RXB8 TXB8

-Bit RXCIE: Cho phép ngắt nhận


=0: Cấm ngắt nhận
=1: Cho phép ngắt nhận
-Bit TXCIE: Cho phép ngắt truyền
=0: Cấm ngắt truyền
=1: Cho phép ngắt truyền
-Bit UDRIE: Cho phép ngắt thanh ghi UDR
=0: Cấm ngắt UDR
=1: Cho phép ngắt UDR
-Bit RXEN: Cho phép nhận
=0: Cấm nhận (Chân PD0 là IO)
=1: Cho phép nhận (Chân PD0 là RXD)
-Bit TXEN: Cho phép truyền
=0: Cấm truyền (Chân PD1 là IO)
=1: Cho phép truyền (Chân PD1 là TXD)
-Bit UCSZ2: Bit này kết hợp với 2 bit UCSZ1,UCSZ0 trong thanh ghi
UCSRC để chọn số bit data trong khung truyền USART

UCSZ2 UCSZ1 UCSZ0 Số bit data


0 0 0 5 bit
0 0 1 6 bit
0 1 0 7 bit

93
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
0 1 1 8 bit
1 0 0 Not use
1 0 1 Not use
1 1 0 Not use
1 1 1 9 bit

-Bit RXB8: Khi giao tiếp ở chế độ 9 bit data,bit này chính là bit thứ 9
trong khung nhận 9 bit.Bit này phải được đọc đầu tiên trước khi nhận các
bit thấp.
-Bit TXD8: Khi giao tiếp ở chế độ 9 bit data,bit này chính là bit thứ 9
trong khung truyền 9 bit.Bit này phải được truyền đi đầu tiên trước khi
truyền các bit thấp.

*Thanh ghi UCSRC

URSEL UMSEL UPM1 UPM0 USBS UCSZ1 UCSZ0 UCPOL

-Bit URSEL: Chọn thanh ghi UCSRC hay UBRRH


=0: Thanh ghi UBRRH (UBRRH=0b0xxxxxxx)
=1: Thanh ghi UCSRC (UCSRC=0b1xxxxxxx)
-Bit UMSEL: Chọn chế độ USART
=0: Giao tiếp bất đồng bộ UART
=1: Giao tiếp đồng bộ USRT
-Các bit UPM1,UPM0: Chọn chế độ Parity

UPM1 UPM0 Chế độ Parity


0 0 None
0 1 Not use
1 0 Chẵn
1 1 Lẻ

-Bit USBS: Chọn số bit Stop


=0: 1 bit stop
=1: 2 bit stop
-Các bit UCSZ1,UCSZ0: Các bit này kết hợp với bit UCSZ2 trong thanh
ghi UCSRB để chọn số bit data trong khung truyền USART

UCSZ2 UCSZ1 UCSZ0 Số bit data


0 0 0 5 bit
0 0 1 6 bit
0 1 0 7 bit

94
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
0 1 1 8 bit
1 0 0 Not use
1 0 1 Not use
1 1 0 Not use
1 1 1 9 bit

-Bit UCPOL: Thiết lập quá trình truyền và nhận trong chế độ giao tiếp
đồng bộ.

Đầu ra chân TXD Đầu vào chân RXD


UCPOL
(Truyền data) (Lấy mẫu)
Tại sườn lên của xung Tại sườn xuống của
0
XCK xung XCK
Tại sườn xuống của Tại sườn lên của chân
1
xung XCK XCK

*Thanh ghi UDR chứa dữ liệu


*Thanh ghi UBRRH,UBRRL để thiết lập tốc độ baud

USART U2X=0 U2X=1

Bất đồng bộ Favr Favr


UBRR(H,L) = -1 UBRR(H,L) = -1
(UART) 16*baud 8*baud

Đồng bộ Favr
UBRR(H,L) = -1
(USRT) 2*baud

*Các tốc độ baudrate thường dùng


110
300
600
1200
2400
4800

95
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
9600
14400
19200
28800
38400
56000
57600
115200
4.CHUẨN RS232 VÀ CHUẨN TTL
*Chuẩn RS232
-Mức 0: +3V→+12V
-Mức 1: -3V →-12V

96
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

Trên là các kí hiệu chân và hình dạng của cổng DB9


Chức năng của các chân như sau:
+Chân 1 (DCD) Data Carrier Detect: Phát tín hiệu mang dữ liệu
+Chân 2 (RxD) Receive Data: Nhận dữ liệu
+Chân 3 (TxD) Transmit Data: Truyền dữ liệu
+Chân 4 (DTR) Data Termial Ready: Đầu cuối dữ liệu sẵn sàng được
kích hoạt bởi bộ phận khi muốn truyền dữ liệu
+Chân 5 ( SG) Singal Ground: Mass của tín hiệu
+Chân 6 (DSR) Data Set Ready: Dữ liệu sẵn sàng, được kích hoạt bởi
bộ truyền khi nó sẵn sàng nhận dữ liệu
+Chân 7 (Request to Send): Yêu cầu gửi,bô truyền đặt đường này lên
mức hoạt động khi sẵn sàng truyền dữ liệu
+Chân 8 (CTS) Clear To Send: Xóa để gửi ,bô nhận đặt đường này lên
mức kích hoạt động để thông báo cho bộ truyền là nó sẵn sàng nhận tín
hiệu
+Chân 9 (RI) Ring Indicate: Báo chuông cho biết là bộ nhận đang nhận
tín hiệu rung chuông
Còn DB28 bây giờ hầu hết các main mới ra đều không có cổng này nữa.
Nên tôi không đề cập đến ở đây.
*Chuẩn TTL
-Mức 0: 0V
-Mức 1: VCC (3.3V → 5V)
*Mạch chuyển đổi RS232-TTL
-Chân RXD của AVR nối với chân số 12 của MAX232
-Chân TXD của AVR nối với chân số 11 của MAX232
-Chân 14 của MAX232 nối với chân số 2 của cổng RS232
-Chân 13 của MAX232 nối với chân số 3 của cổng RS232
-Chân số 5 của cổng RS232 nối với GND
-Các chân còn lại của MAX232 nối như hình vẽ

97
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

5.VÍ DỤ
*Ví dụ 1: Code truyền nhận dữ liệu giữa máy tính và ATmega16
#include<mega16.h>
#include<delay.h>
void UART_init(void)
{
UBRRH=0;UBRRL=103;
UCSRA=0x02;
UCSRB=0x18;
UCSRC=0x86;
DDRD.0=0;
DDRD.1=1;
#asm("cli");
delay_ms(400);
}
void send_x(unsigned char n)
{
while((UCSRA&0x20)==0x00);
UDR=n;
}
unsigned char get_x(void)
{
while((UCSRA&0x80)==0);
return(UDR);
}
void main()
{
unsigned char x;

98
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
DDRC=0xff;
PORTC=0x00;
UART_init();
while(1)
{
x=get_x();
if(x=='1')
{
PORTC.0=~PORTC.0;
x=0;
}
if(x=='2')
{
PORTC.1=~PORTC.1;
x=0;
}
if(x=='3')
{
PORTC.2=~PORTC.2;
x=0;
}
if(x=='4')
{
PORTC.3=~PORTC.3;
x=0;
}
if(x=='5')
{
PORTC.4=~PORTC.4;
x=0;
}
if(x=='6')
{
PORTC.5=~PORTC.5;
x=0;
}
if(x=='7')
{
PORTC.6=~PORTC.6;
x=0;
}
if(x=='8')
{

99
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
PORTC.7=~PORTC.7;
x=0;
}
if(x=='0')
{
PORTC=0x00;
x=0;
}
if(x=='9')
{
PORTC=0xff;
x=0;
}
if(x=='!')
{
PORTC=~PORTC;
x=0;
}
}
}
*Ví dụ 2: Code truyền nhận dữ liệu giữa máy tính và ATmega16
#include<mega16.h>
#include<delay.h>
unsigned int d=0;
unsigned char kt=1;
unsigned char x=0;
void uart_init(void)
{
//baud=9600 khi FOSC=8000000
//8 bit data,1 bit stop,parity none
UCSRA=0x02;
UCSRB=0x98;//cho phep nhan,cam truyen,cam ngat truyen va ngat
nhan
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x67;
delay_ms(400);
}
void ext_init(void)
{
MCUCR=0x0a;
MCUCSR=0x00;
GICR=0x40;

100
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
GIFR=0x00;
#asm("sei");
delay_ms(400);
d=0;
}
void send_x(unsigned char n)
{
while((UCSRA&0x20)==0x00);//wait UDRE=1;
UDR=n;
}
/*unsigned char read_x(void)
{
while((UCSRA&0x80)==0x00);//wait RXC=1;
return(UDR);
} */
void sendint(unsigned int n)
{
unsigned int x,y;
unsigned char m[6];
signed char i,k;
x=n;
k=0;
do
{
y=x%10;
x=x/10;
m[k]=y;
k++;
}
while(x);
send_x(10);
for(i=k-1;i>=0;i--)
{
send_x(m[i]);
}
send_x(11);
}
interrupt[2] void ngoai0(void)
{
d++;
while(PIND.2==0);
//delay_ms(400);
if(d>60000) d=0;

101
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
kt=1;
}
interrupt[12] void noitiep(void)
{
x=UDR;
if(x=='1')
{
PORTD.3=~PORTD.3;
x=0;
}
if(x=='2')
{
PORTD.4=~PORTD.4;
x=0;
}
if(x=='3')
{
PORTD.5=~PORTD.5;
x=0;
}
if(x=='4')
{
PORTD.6=~PORTD.6;
x=0;
}
if(x=='5')
{
PORTD.7=~PORTD.7;
x=0;
}
}
void main()
{
DDRC=0xff;
DDRD.0=0;//RXD input
DDRD.1=1;//TXD output
DDRD.2=0;//INT0 input
PORTD.2=1;//keo tro bang noi
DDRD.3=1;
DDRD.4=1;
DDRD.5=1;
DDRD.6=1;
DDRD.7=1;

102
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
uart_init();
ext_init();
while(1)
{

if(kt==1)
{
sendint(d);
kt=0;
}

}
}

103
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 16

104
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

GIAO TIẾP SPI


1.CÁC CHÂN LIÊN QUAN
-Chân SS (PB4): Chân chọn chip Slave
-Chân MOSI (PB5): Master Output,Slave Input
-Chân MISO (PB6): Master Input,Slave Output
-Chân SCK (PB7): Chân Clock SPI

105
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

2.CÁC THANH GHI LIÊN QUAN


SPCR
SPSR
SPDR
3.CHI TIẾT CÁC THANH GHI
*Thang ghi SPCR

SPIE SPE DORD MSTR CPOL CPHA SPR1 SPR0

-Bit SPIE: Cho phép ngắt SPI


=0: Cấm ngắt SPI
=1: Cho phép ngắt SPI
-Bit SPE: Cho phép SPI hoạt động
=0: Cấm SPI (Các chân PD4,PD5,PD6,PD7 là IO)
=1: Cho phép SPI hoạt động
(Các chân PD4,PD5,PD6,PD7 là SS,MOSI,MISO,SCK)
-Bit DORD: Chọn hướng truyền data
=0: Bit MSB sẽ truyền đi đầu tiên
=1: Bit LSB sẽ truyền đi đầu tiên

106
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Bit MSTR: Chọn Master hoặc Slave
=0: Chip đóng vai trò là Slave
=1: Chip đóng vai trò là Master
-Bit CPOL: Chọn trạng thái nghĩ của xung
=0: Trạng thái nghĩ của xung SCK ở mức thấp
=1: Trạng thái nghĩ của xung SCK ở mức cao
-CHPA: Chọn thời điểm thiết lập và lấy mẫu data
=0: Lấy mẫu trước trạng thái nghĩ,thiết lập sau trạng thái nghĩ
=1: Lấy mẫu sau trạng thái nghĩ,thiết lập trước trạng thái nghĩ
-Các bit SPR1,SPR0: Kết hợp với bit SPI2X trong thanh ghi SPSR để
thiết lập nguồn xung cấp cho SPI

SPI2X SPR1 SPR0 Nguồn xung cấp cho SPI


0 0 0 Favr/4
0 0 1 Favr/16
0 1 0 Favr/64
0 1 1 Favr/128
1 0 0 Favr/2
1 0 1 Favr/8
1 1 0 Favr/32
1 1 1 Favr/64

*Thanh ghi SPSR

SPIF WCOL SPI2X

-Bit SPIF: Cờ báo SPI


Bit này sẽ tự động bật lên 1 khi quá trình truyền hoặc nhận hoàn thành
-Bit WCOL: Cờ báo va chạm dữ liệu
Khi dữ liệu cũ chưa truyền xong,hoặc dữ liệu cũ chưa nhận xong mà lại
đưa dữ liệu mới vào thì bit này sẽ bật lên 1.
-Bit SPI2X: Nhân đôi tốc độ
=0: Không nhân
=1: Nhân đôi tốc độ
*Thanh ghi SPDR là thanh ghi chứa dữ liệu truyền và nhận trong
giao tiếp SPI
4.VÍ DỤ
*Ví dụ:
Viết chương trình giao tiếp SPI giữa master và slave
-Master gửi mã sáng chạy sang slave,slave xuất mã nhận được ra
PORTD.

107
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Slave gửi mã sáng dần sang master,master xuất mã nhận được ra
PORTD.
+Code master
#include<mega16.h>
#include<delay.h>
unsigned char maled[9]={0,1,2,4,8,16,32,64,128};
void spi_master(void)
{
DDRB.4=1;PORTB.4=0;//ss la output
DDRB.5=1;//mosi la output
DDRB.6=0;PORTB=1;//miso la input
DDRB.7=1;//sck la output
SPCR=0x50;
SPSR=0x00;
#asm("cli");
}
unsigned char send_read(unsigned char n)
{
SPSR=0x00;
SPDR=n;
while((SPSR&0x80)==0x00);
return(SPDR);
}
void main(void)
{
signed char i;
DDRD=0xff;
spi_master();
while(1)
{
for(i=0;i<9;i++)
{
PORTD=send_read(maled[i]);
delay_ms(400);
}
}
}
+Code slave
#include<mega16.h>
#include<delay.h>
unsigned char maled[9]={0,1,3,7,15,31,63,127,255};
void spi_slave(void)
{

108
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
DDRB.4=0;PORTB.4=1;//ss la input
DDRB.5=0;PORTB.5=1;//mosi la input
DDRB.6=1;//miso la output
DDRB.7=0;PORTB.7=1;//sck la input
SPCR=0x40;
SPSR=0x00;
#asm("cli");
}
unsigned char send_read(unsigned char n)
{
SPSR=0x00;
SPDR=n;
while((SPSR&0x80)==0x00);
return(SPDR);
}
void main(void)
{
signed char i=0;
DDRD=0xff;
spi_slave();
while(1)
{
PORTD=send_read(maled[i]);
i++;
if(i>8) i=0;
}
}

109
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

BÀI 17
GIAO TIẾP I2C VÀ TWI
110
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR

I.GIAO TIẾP I2C


1.GIỚI THIỆU VỀ GIAO TIẾP I2C
-Giao tiếp I2C là chuẩn truyền thông 2 dây được giới thiệu lần đầu vào
năm 1980 bởi hãng điện tử Philips.Sau này được nhiều hãng khác nghiên
cứu và phát triển thêm và giao tiếp i2c bây giờ đã khá phổ biến trong
ngành điện tử.
-Giao tiếp I2C thường được dùng để giao tiếp giữa các chip với nhau.
-Giao tiếp I2C gồm có 2 đường truyền.
+Đường SDA gọi là đường truyền dữ liệu.
+Đường SCL gọi là đường truyền xung clock
2.TỐC ĐỘ TRUYỀN DỮ LIỆU (CHẾ ĐỘ HOẠT ĐỘNG)
*Chế độ chuẩn (Standard mode)
-Đây là chế độ tiêu chuẩn ban đầu được phát hành vào những năm 1980
-Tiêu chuẩn này có tốc độ truyền tối đa là 100kbps
-Sử dụng 7 bit địa chỉ và 112 địa chỉ tớ (slave)
*Chế độ nhanh (Fast mode)
-Tốc độ tối đa lên đến 400kbps (cao hơn 4 lần so với chế độ chuẩn)
-Chân SDA,SCL của thiết bị tớ ở trạng thái trở kháng cao khi không cấp
nguồn.Do đó nên kéo trở băng ngoài vào 2 chân này.
*Chế cao tốc (High speed)
-Tốc độ có thể lên đến 1.7Mbps hoặc 3.4Mbps
+Mối quan hệ giao tiếp
->1 master 1 slave
->1 master nhiều slave
->nhiều master nhiều slave
-Đều dựa trên quan hệ chủ tớ,việc giao tiếp là master phải xác định được
địa chỉ của thiết bị slave cùng trạng thái đọc hoặc ghi để đưa ra quyết
định với nhau.
3.CÁC KHÁI NIỆM THƯỜNG DÙNG TRONG I2C
-Trạng thái ban đầu (khi đang rãnh): SDA=HIGH,SCL=HIGH
-Tín hiệu START:
Đường SDA sẽ chuyển từ mức HIGH xuống mức LOW trong khi đường
SCL vẫn đang ở mức HIGH.
-Tín hiệu STOP:
Đường SDA sẽ chuyển từ mức LOW lên mức HIGH trong khi đường
SCL vẫn đang ở mức HIGH.
-Tín hiệu REPEAT START:
Tín hiệu START được tạo ra,kết thúc quá trình không tạo tín hiệu STOP
mà tiếp tục tạo ra 1 tín hiệu START mới thì gọi là REPEAT START
-Dữ liệu I2C được truyền đi ở sườn lên của xung SCL
II.TWI (GIAO TIẾP I2C TRONG AVR)
1.CÁC CHÂN LIÊN QUAN

111
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Chân xung SCL (PC0)
-Chân dữ liệu SDA (PC1)

112
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
2.ĐẶC ĐIỂM
-Dùng 7 bit địa chỉ,cho phép tối đa lên đến 128 địa chỉ slave khác nhau
-Tốc độ truyền dữ liệu lên đến 400kbps
3.THIẾT LẬP TỐC ĐỘ GIAO TIẾP

TWPS1 TWPS0 TWPS


0 0 0
0 1 1
1 0 2
1 1 3

Favr
Fscl=
16+2*TWBR*4TWPS

+Fscl: Tần số xung clock tại chân SCL


+Favr: Tần số của chip AVR,có thể là dao động thạch anh hoặc dao động
nội.
+TWBR: Giá trị của thanh ghi TWBR
+TWPS: Được thiết lập bởi 2 bit TWPS1,TWPS0 trong thanh ghi status
TWSR
4.CÁC THANH GHI LIÊN QUAN
TWBR
TWSR
TWCR
TWDR
TWAR
5.CHI TIẾT CÁC THANH GHI
*Thanh ghi TWCR (Điều khiển hoạt động của TWI)

TWINT TWEA TWSTA TWSTO TWWC TWEN TWIE

-Bit TWINT: Cờ báo ngắt TWI


Khi hoàn thành 1 quá trình TWI thì bit này sẽ tự động bật lên 1,xóa bit
này về 0 bằng cách ghi 1 vào nó.
-Bit TWEA: Bit thông báo ACK
+Báo địa chỉ slave đã được xác nhận
+Một cuộc gọi chung đã được nhận,trong khi bit TWGCE trong thanh ghi
TWAR được set.
+Một byte dữ liệu đã được nhận ở master hoặc slave
-Bit TWSTA: Bit tạo tín hiệu START
+Bit này được tạo bởi master,khi nào cần bắt đầu 1 giao tiếp chúng ta cần
đặt bit này của master lên 1.

113
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
-Bit TWSTO: Bit tạo tín hiệu STOP
+Khi muốn kết thúc một cuộc gọi chung hoặc kết thúc quá trình TWI ta
đặt bit này của master lên mức 1.
+Ở chế độ slave bit này có thể được dùng để phục hồi một số điều kiện
lỗi.
-Bit TWWC: Cờ báo va chạm
+Bit này sẽ tự động set lên 1 khi xảy ra va chạm.Tức là quá trình cũ chưa
xử lý xong mà quá trình mới lại bắt đầu thì sẽ xảy ra lỗi này.
+Khi bit TWINT đang ở mức thấp mà ta lại đưa data vào thanh ghi
TWDR thì sẽ xảy ra va chạm data.Vì bit khi TWINT tự set lên 1 thì quá
trình trước đó mới hoàn thành.
-Bit TWEN: Cho phép modul TWI hoạt động
=0: Vô hiệu hóa TWI,các chân SDA và SCL là IO bình thường
=1: Cho phép TWI hoạt động,các chân SDA và SCL đóng vai trò là các
chân chức năng của TWI.
-Bit TWIE: Cho phép ngắt TWI
=0: Cấm
=1: Cho phép ngắt TWI
+Khi xong 1 quá trình TWI bit TWINT sẽ tự động set lên 1 để báo hiệu
cho chip,nếu ta bật bit này lên 1 và cho phép ngắt toàn cục thì sẽ nhảy
đến chương trình xử lý ngắt TWI thực hiện.
*Thanh ghi TWSR (Thanh ghi trạng thái)

TWS7 TWS6 TWS5 TWS4 TWS3 TWPS1 TWPS0

-Các bit TWS7,TWS6,TWS5,TWS4,TWS3 của thanh ghi này dùng để


báo trạng thái của TWI,khi một quá trình TWI xảy ra giá trị trong 5 bit
này sẽ tạo ra một mã xác nhận để thông báo cho chip biết hoạt động đang
diễn ra như thế nào.
-Các bit TWPS1,TWPS0: Dùng để thiết lập tốc độ xung clock

TWPS1 TWPS0 TWPS


0 0 0
0 1 1
1 0 2
1 1 3

Favr
Fscl=
16+2*TWBR*4TWPS

+Favr phụ thuộc vào nguồn dao động cao cho AVR,có thể là dao động
thạch anh hoặc dao động nội.

114
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
*Thanh ghi TWBR (Dùng để thiết lập tốc độ xung clock)
*Thanh ghi TWDR (Chứa dữ liệu cần truyền hoặc nhận)
*Thanh ghi TWAR (Thanh ghi địa chỉ)
TWAR={TWA6,TWA5,TWA4,TWA3,TWA2,TWA1,TWA0,TWGCE}
-Các bit TWA6,TWA5,TWA4,TWA3,TWA2,TWA1,TWA0: Chứa địa
chỉ của slave.Khi giao tiếp ta cần nạp địa chỉ của slave vào 7 bit này.
-Bit TWGCE: Dùng để cho phép 1 cuộc gọi chung
=0: Không cho phép cuộc gọi chung
=1: Cho phép tạo cuộc gọi chung
6.VÍ DỤ
-Viết chương hiển thị giờ,phút,giây dùng ds1307 sử dụng TWI
#include <mega16.h>
#include <delay.h>
#include <string.h>
#define twip PORTC
#define twid DDRC
#define pin PINC
#define slave 0x68
#define baud 32
#define write 0
#define read 1
#define start 0xa4
#define stop 0x94
#define clear 0x84
#define ack 0xc4
//----------------------------
//cac ham i2c va ds1307
void i2c_setup(void);
unsigned char ghiblock(unsigned char addr,unsigned char
dulieu[],unsigned char len);
unsigned char readblock(unsigned char addr,unsigned char
dulieu[],unsigned char len);
unsigned char outv(unsigned char bcd);
unsigned char inv(unsigned char dec);
unsigned char send(unsigned char addr,unsigned char n);
//----------------------------
unsigned char
giay=55,phut=59,gio=11,ngay=31,thang=12,nam=9,mode=1,ap=1;
unsigned char time[7];
//***************************
unsigned char
maled[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void i2c_setup(void)

115
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
{
//set SCL to 400kHz
TWSR = 0x00;
TWBR = 0x0C;
TWCR = (1<<TWEN);//enable TWI
DDRC.0=1;
delay_ms(400);
}
void twi_start(void)
{
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);
}
void twi_stop(void)
{
TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}
void twi_write(unsigned char n)
{
TWDR = n;
TWCR = (1<<TWINT)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);
}
unsigned char twi_readack(void)
{
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
while ((TWCR & (1<<TWINT)) == 0);
return TWDR;
}
unsigned char twi_readnack(void)
{
TWCR = (1<<TWINT)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);
return TWDR;
}
unsigned char send(unsigned char addr,unsigned char n)
{
unsigned char x;
twi_start();
if((TWSR&0xf8)!=0x08) return(1);
x=(slave<<1)|write;
twi_write(x);
if((TWSR&0xf8)!=0x18) return(1);

116
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
x=addr;
twi_write(x);
if((TWSR&0xf8)!= 0x28) return(1);
x=n;
twi_write(x);
if((TWSR&0xf8)!= 0x28) return(1);
twi_stop();
return 0;
}
unsigned char readtwi(unsigned char addr)
{
unsigned char x=0;
twi_start();
if((TWSR&0xf8)!=0x08) return(1);
x=(slave<<1)|write;
twi_write(x);
if((TWSR&0xf8)!=0x18) return(1);
x=addr;
twi_write(x);
if((TWSR&0xf8)!=0x28) return(1);
twi_start();
if((TWSR&0xf8)!=0x10) return(1);
x=(slave<<1)|read;
twi_write(x);
if((TWSR&0xf8)!=0x40) return(1);
x=twi_readnack();
if((TWSR&0xf8)!= 0x58) return(1);
twi_stop();
return(x);
}
unsigned char ghiblock(unsigned char addr,unsigned char
dulieu[],unsigned char len)
{
unsigned char i;
unsigned char x;
twi_start();
if((TWSR&0xf8)!=0x08) return(TWSR);//TWSR=0x08 da gui start
x=(slave<<1)|write;
twi_write(x);
if((TWSR&0xf8)!=0x18) return(TWSR);
x=addr;
twi_write(x);
if((TWSR&0xf8)!=0x28) return(TWSR);

117
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
for(i=0;i<len;i++)
{
twi_write(dulieu[i]);
if((TWSR&0xf8)!=0x28) return(TWSR);
}
twi_stop();
return(0);
}
unsigned char readblock(unsigned char addr,unsigned char
dulieu[],unsigned char len)
{
unsigned char i,x;
twi_start();
if((TWSR&0xf8)!=0x08)return(TWSR);
x=(slave<<1)|write;
twi_write(x);
if((TWSR&0xf8)!=0x18)return(TWSR);
x=addr;
twi_write(x);
if((TWSR&0xf8)!=0x28)return(TWSR);
twi_start();
if((TWSR&0xf8)!=0x10)return(TWSR);
x=(slave<<1)|read;
twi_write(x);
if((TWSR&0xf8)!=0x40)return(TWSR);//neu TWSR=0x40 da gui dia
chi slave cung bit read,da nhan ack
for(i=0;i<len-1;i++)
{
dulieu[i]=twi_readack();
if((TWSR&0xf8)!=0x50)return(TWSR);//neu TWSR=0x50 da nhan
data va tra lai ack
}
dulieu[len-1]=twi_readnack();
if((TWSR&0xf8)!=0x58)return(TWSR); //neu TWSR=0x58 da nhan
data va tra lai nack
twi_stop();
return(0);
}
unsigned char send1307(unsigned char addr,unsigned char n)
{
unsigned char x;
twi_start();
if((TWSR&0xf8)!=0x08) return(1);

118
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
x=(slave<<1)|write;
twi_write(x);
if((TWSR&0xf8)!=0x18) return(1);
x=addr;
twi_write(x);
if((TWSR&0xf8)!= 0x28) return(1);
x=inv(n);
twi_write(x);
if((TWSR&0xf8)!= 0x28) return(1);
twi_stop();
return 0;
}
unsigned char read1307(unsigned char addr)
{
unsigned char x=0;
twi_start();
if((TWSR&0xf8)!=0x08) return(1);
x=(slave<<1)|write;
twi_write(x);
if((TWSR&0xf8)!=0x18) return(1);
x=addr;
twi_write(x);
if((TWSR&0xf8)!=0x28) return(1);
twi_start();
if((TWSR&0xf8)!=0x10) return(1);
x=(slave<<1)|read;
twi_write(x);
if((TWSR&0xf8)!=0x40) return(1);
x=twi_readnack();
if((TWSR&0xf8)!= 0x58) return(1);
twi_stop();
return(outv(x));;
}
unsigned char outv(unsigned char bcd)
{
unsigned char l,h;
l=bcd&0x0f;
h=(bcd>>4)*10;
return(h+l);
}
unsigned char inv(unsigned char dec)
{
unsigned char l,h;

119
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
l=dec%10;
h=(dec/10)<<4;
return(h+l);
}
void io_init(void)
{
DDRA=0xff;
DDRB=0xff;
PORTB=0xff;
}
void hienthi(void)
{
unsigned char m[6];
signed char i;
m[0]=time[2]/10;
m[1]=time[2]%10;
m[2]=time[1]/10;
m[3]=time[1]%10;
m[4]=time[0]/10;
m[5]=time[0]%10;
for(i=0;i<6;i++)
{
PORTA=maled[m[i]];
PORTB=~(1<<i);
delay_ms(4);
PORTB=0xff;
}
}
void main(void)
{
io_init();
i2c_setup();
//doan khoi tao thoi gian
//time[2]=inv(12);time[1]=inv(30);time[0]=inv(45);
//time[4]=inv(3);time[5]=inv(10);time[6]=inv(16);
//ghiblock(0,time,7);
/*send(2,inv(10));
send(1,inv(30));
send(0,inv(45));
send(4,inv(3));
send(5,inv(10));
send(6,inv(16));*/
while(1)

120
THÁI XUÂN HUỆ 0981.018.504
LẬP TRÌNH ỨNG DỤNG VI ĐIỀU KHIỂN AVR
{
//readblock(0,time,7);
/*time[0]=readtwi(0);
time[1]=readtwi(1);
time[2]=readtwi(2);
time[4]=readtwi(4);
time[5]=readtwi(5);
time[6]=readtwi(6); */
time[0]=read1307(0);
time[1]=read1307(1);
time[2]=read1307(2);
time[4]=read1307(4);
time[5]=read1307(5);
time[6]=read1307(6);
hienthi();
}
}

121
THÁI XUÂN HUỆ 0981.018.504

You might also like