Tải bản đầy đủ (.docx) (24 trang)

Nghiên cứu sử dụng Atmega 8

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.42 MB, 24 trang )

Phần 1: Tổng quan về VĐK Atmega8:
- Tốc độ tối đa: 16MHz.
- Dung lượng bộ nhớ chương trình: 8 KB.
- Bộ nhớ EEPROM: 512 Byte.
- Dung lượng bộ nhớ RAM: 1 KB.
Bộ nhớ chương trình có khả năng ghi 10.000 lần, bộ nhớ EEPROM có thể ghi 100.000 lần. Hỗ trợ
bootloader, có khả năng tự ghi vào bộ nhớ chương trình, cập nhật chương trình cho chip mà
không cần mạch nạp.
- Timer 8 bit: 2.
- Timer 16 bit: 1.
- ADC: 6 kênh, 10 bit.
- Giao tiếp: TWI (I2C), UART, SPI
Điện áp hoạt động:
Atmega8L: 2.7V – 5.5V.
Atmega8: 4.5V – 5.5V.

Sơ đồ chân:

Hệ thống Clock:
Nguồn Clock:
Chip có thể hoạt động với các nguồn Clock tương ứng với việc thiết lập các FUSE tương ứng:


Ta chỉ tập trung vào hai nguồn clock đó là sử dụng thạch anh ngoài và sử dụng mạch RC tích hợp
trong chip (dao động nội).
Sử dụng thạch anh ngoài:


Để chip có thể hoạt động thì cần được FUSE đúng.
Khi xuất xưởng thì mặc định chip được FUSE sử dụng dao động nội với tần số 1MHz.
Nguồn RESET:


Atmega8 có 4 cách RESET:
- Reset khi cấp nguồn.
- Reset ngoài (thông qua chân RESET).
- Watchdog RESET.
- Reset khi nguồn bị sụt áp.

Điều khiển vào ra (IO):
Atmega8 có 3 cổng vào ra: cổng B, cổng C, cổng D.
Mỗi cổng được cấu hình, điều khiển thông qua 3 thanh ghi: DDRx, PORTx và PINx. (x: B, C, D).
Các thanh ghi này có thể truy xuất từng bit để có thể điều khiển từng chân (Pin) của mỗi cổng.
- DDRx: quy định chiều của chân, DDRx=1: chân được cấu hình làm đầu ra, ngược lại DDRx=0
quy định chân làm đầu vào.
- PORTx: nếu PORTx=1 khi chân được cấu hình làm đầu vào thì sẽ kích hoạt điện trở treo dương
tại chân tương ứng. Để vô hiệu hóa trở treo này thì PORTx phải được gán 0 hoặc chân được cấu
hình làm đầu ra (DDRx=1).
Nếu chân được cấu hình làm đầu ra (DDRx=1):
Nếu PORTx=1 thì chân tương ứng sẽ được đưa lên cao (1 – VCC), ngược lại nếu PORTx=0 thì
chân tương ứng sẽ được đưa xuống thấp (0 – GND)
- PINx: Đọc dữ liệu từ chân VĐK, độc lập với cấu hình chiều của chân (cả khi DDRx=0 và
DDRx=1) trạng thái của chân có thể được đọc thông qua các bit của thanh ghi PINx.
Chú ý:
Nếu bit thứ 2 (PUD) của thanh ghi SFIOR được ghi giá trị 1 thì trở treo sẽ bị vô hiệu hóa bất chấp
các thiết lập thông qua các thanh ghi PORTx, DDRx như đã nói ở trên.


- Mạch vi điều khiển cơ bản:
+ Mạch sử dụng thạch anh:

+ Mạch sử dụng dao động nội:



* Mạch nạp cho VĐK: ở đây mình dùng mạch nạp ISP do mình thiết kế, việc sử dụng mạch nạp
khác là hoàn toàn tương tự:
Bạn kết nối các chân tương ứng của mạch nạp vào các chân của chip: MOSI, MISO, SCK, RESET,
VCC, GND.
* Sử dụng AVR Studio để viết chương trình:
Khởi động AVR Studio chọn Project -> New Project


Ở mục 1 chọn AVR GCC (sử dụng ngôn ngữ C).
Mục 2 đánh vào tên Project.
Mục 3 chọn nơi lưu Project.

Chọn Next


Sau đó Finish.


Cửa sổ 1: Quản lý file mã nguồn.
Cửa sổ 2: Soạn thảo mã lệnh.
Ví dụ 1: điều khiển Led đơn:
Chương trình sẽ nhấp nháy các led nối với PORTD với tần số 1Hz (500ms ON và 500ms OFF)


Mã lệnh:
Mã:

#define F_CPU 8000000
#include <util/delay.h>

#include <avr/io.h>
int main() {
DDRD=0xff;
while(1) {
PORTD=0xff;
_delay_ms(500);
PORTD=0x00;
_delay_ms(500);
}
}

Dòng thứ nhất khai báo tần số dao động, ta sẽ cấu hình cho chip chạy ở tần số 8MHz (Sẽ minh
họa cách cấu hình Fuse sau).
Dòng thứ 2 khai báo thư viện để sử dụng hàm _delay_ms.
Dòng thứ 3 khai báo thư viện định nghĩa các thanh ghi của Atmega8.
Chương trình chính đầu tiên cấu hình PORTD làm đầu ra sau đó tạo ra một vòng lặp vô hạn. Để
cho led sáng ta xuất 1 ra các chân của PORTD: PORTD=0xff;
Để cho led tắt ta xuất giá trị 0: PORTD=0x00;
Sau đó ta có thể kiểm tra kết quả với mô phỏng Proteus hoặc nạp vào mạch và kiểm tra.
Cách Fuse cho chip:


Ở mục 1 chọn Atmega, mục 2 chọn Atmega8, ấn Advance

Ấn Write Fuse.
Trường hợp sử dụng thạch anh thì Fuse như sau:


Ví dụ 2: Đọc trạng thái phím ấn.
Chương trình sẽ liên tục kiểm tra phím ấn, nếu phím được ấn thì bật led và nếu phím nhấn không được ấn

thì tắt led.

Code:


Mã:

#define F_CPU 8000000
#include <util/delay.h>
#include <avr/io.h>
int main() {
DDRB=0x00;
PORTB=0xff;
DDRD=0xff;
while(1) {
if(!(PINB&1)) {
PORTD=1;
} else {
PORTD=0;
}
}
}

Chương trình chính sẽ cấu hình PORTB làm đầu vào, PORTD làm đầu ra.
DDRB=0x00 và PORTB=0xff sẽ cho phép trở treo nội tại PORTB vì vậy bình thường giá trị tại
chân nối với nút bấm luôn là 1, khi ta ấn phím lúc này chân nút bấm được nối GND và có giá trị 0.
Để đọc trạng thái cổng B ta dùng thanh ghi PINB.
Atmega8 có 3 bộ định thời/bộ đếm (Timer/Counter): Chúng có thể dùng để đếm thời gian, đếm
sự kiện xảy ra bên ngoài VĐK, tạo ra các xung điều rộng PWM…
Phần này ta sẽ tìm hiểu về Timer/Counter 0 (T/C 0). T/C 0 là bộ đếm/ định thời 8 bit. Thanh ghi

TCNT0 có độ rộng 8 bit. T/C0 có thể đếm xung hệ thống (xung hệ thống có thể là thạch anh
ngoài hay dao động nội… mà ta đã nói ở phần trước), xung hệ thống sau khi qua bộ chia tần hoặc
xung bên ngoài tại chân T0. Ta có thể chọn nguồn xung để đếm thông qua việc cấu hình các
thanh ghi điều khiển TC0 (các bit CS2..0 của thanh ghi TCCR0).
TCNT0 sẽ tăng lên 1 mỗi khi nhận được 1 xung, sau khi TCNT0 đạt đến 0xff (255) nếu nhận thêm
1 xung nữa nó sẽ trở về 0x00. Đồng thời lúc này cờ TOV0 (bit thứ 0 của thanh ghi TIFR) sẽ được
gán bằng 1. Lưu ý là TCNT0 là thanh ghi có thể đọc và ghi vì vậy ta có thể ghi giá trị vào nó bất cứ
lúc nào.
Sử dụng T/C0:
- Chọn nguồn xung clock:
Ta cấu hình chọn nguồn xung clock thông qua việc ghi giá trị vào 3 bit CS02..0 của thanh ghi
TCCR0


Ví dụ 1:
Tạo ra thời gian trễ 200ms sử dụng TC0:
Vì ta sử dụng xung hệ thống là 8MHz nên 1 chu kỳ sẽ là 1/8MHz=0,125us.
Vậy 200ms ta cần đếm 200ms/0,125us=1.600.000 xung.
TCNT0 là thanh ghi 8 bit nên giá trị lớn nhất là 255: (cộng với 1 chu kỳ đếm từ 255 về 0 là 256).
Giá trị 1.600.000 lớn hơn rất nhiều so với 256 vì vậy ta sẽ chọn bộ chia tần là 1024, lúc này ta cần
đếm 1.600.000 xung/1024=1562,5 xung, lấy chẵn là 1562.
Nếu ta khởi tạo TCNT0 bằng 0 thì cứ sau 256 xung TCNT0 sẽ tràn (TOV0=1) vậy sẽ có
1562/256=6,1 lấy chẵn là 6 lần tràn.
Ta sẽ dùng một biến đếm số lần tràn này, khi biến đạt giá trị 6 là đủ 200ms.


Dựa trên phân tích trên ta bắt tay vào viết code: chương trình của chúng ta sẽ nhấp nháy led nối
với chân PD0 như ở phần trước đã làm. Ở đây ta tìm hiểu thêm cách làm sao tác động vào từng
bit củamộtPORT.
Mã lệnh:

Mã:

#define F_CPU 8000000
#include <util/delay.h>
#include <avr/io.h>
char t;
//bien dem so lan tran
//dat 6 la du 200ms
void delay();
//chuong trinh con delay 200ms
int main() {
DDRD|=0x01;
//khoi tao chan PD0 lam dau ra
TCCR0=0b101;
//bo chia tan 1024
PORTD&=0b11111110;
//tat led noi voi chan PD0
//khong lam anh huong toi cac chan khac cua PORTD
TCNT0=0;
while(1) {
PORTD&=0b11111110;
delay();
PORTD|=0b00000001;
delay();
}
}
void delay() {
while(1) {

Bộ truyền thông nối tiếp trên Atmega8 có thể hoạt động ở nhiều chế độ và ở đây ta chỉ xét chế

độ bất đồng bộ.
- Khởi tạo nguồn clock cho bộ truyền thông, khởi tạo tốc độ Baud.
Để khởi tạo tốc độ Baud ta ghi giá trị tương ứng với tốc độ Baud vào thanh ghi UBRR.
Liên hệ giữa UBRR và tốc độ Baud cho bởi công thức sau:


Ví dụ chúng ta sử dụng xung hệ thống là 3.6864MHz, ta cần dùng tốc độ Baud là 9600
Có 2 trường hợp: nếu ta chọn U2X=0 thì UBRR=3.686.400/9600/16-1=23.
Nếu ta chọn U2X=1 thì UBRR=3.686.400/9600/8-1=47.
- Định dạng khung truyền: ta chỉ xét định dạng khung truyền là 1 bit start, 8 bit dữ liệu, 1 bit stop,
không kiểm tra chẵn lẻ.
* Các thanh ghi cấu hình, điều khiển bộ USART:
- Thanh ghi UDR:
+ Để truyền dữ liệu đi thì ta ghi dữ liệu cần truyền vào thanh ghi này và bộ USART sẽ gửi dữ liệu
cần truyền đi.
+ Sau khi nhận đươc dữ liệu thì thanh ghi này sẽ chứa dữ liệu nhận được.
- Thanh ghi USRCA:


Các bit ta quan tâm:
+ bit 7 - RXC: cờ này bằng 1 khi có dữ liệu nhận được và sẽ được xóa thành 0 khi không có dữ liệu
trong bộ đệm (UDR).
+ bit 6 – TXC: cờ này được bật mỗi khi truyền xong dữ liệu. Muốn xóa thì ta ghi giá trị 1 vào
bit này.
+ bit 5 – UDRE: UDRE được set thành 1 khi UDR là rỗng và sẵn sàng cho truyền dữ liệu.
- Thanh ghi UCSRB:

Tạm thời ta quan tâm các bit sau:
- bit 4 - RXEN: cho phép nhận, ghi giá trị 1 vào bit này cho phép USART nhận dữ liệu.
- bit 3 – TXEN: cho phép truyền dữ liệu, ghi 1 vào bit này cho phép USART truyền dữ liệu.

- bit 2 – UCSZ2: kết hợp với các bit UCSZ1..0 trong thanh ghi UCSRC để quy định số bit dữ liệu
trong 1 khung truyền.
- Thanh ghi UCSRC:

Có cùng địa chỉ với thanh ghi UBRRH:
- bit 7 – URSEL: xác định truy cập UBRRH hay UCSRC, khi URSEL=1: truy cập UCSRC, khi
URSEL=0: truy cập UBRRH.
- bit 6 – UMSEL: chọn chế độ của USART: =0: truyền bất đồng bộ, =1 truyền đồng bộ. Và chúng
ta chỉ tìm hiểu truyền bất đồng bộ.
- bit 3 – USBS: =1: 2 bit stop; =0: 1 bit stop.
- bit UCSZ1..0: kết hợp với UCSZ2 ở trên quy định số bit dữ liệu, ta chỉ tìm hiểu kiểu truyền 8 bit
dữ liệu UCSZ2=0; UCSZ1=1; UCSZ0=1;
Ví dụ về sử dụng USART:
Chương trình sẽ bật led và gửi đi ký tự ‘O’ khi nhận được ký tự ‘A’; tắt led và gửi đi ký tự ‘F’ khi
nhận được ký tự ‘B’.
Để có thể mô phỏng ví dụ này ta cần sử dụng thêm một phần mềm tạo cổng COM ảo đó là VSPE và


Terminal
VSPE bạn tải tại đây:
Eterlogic - VSPE: tool for serial ports emulation
Terminal bạn tải tại đây:
Terminal
Sử dụng VSPE và Terminal:
Truyền nhận dữ liệu giữ 8951 và pc | Cộng đồng cơ điện tử Việt Nam | Mechatronics

Mã lệnh:
Mã:

#define F_CPU 3686400


#include <avr/io.h>
#include <util/delay.h>

unsigned char c;

void uart_char_tx(char cData) {
while(bit_is_clear(UCSRA, UDRE));
UDR=cData;
while(bit_is_clear(UCSRA, TXC));
UCSRA|=1<<(TXC);


}

int main() {

Bộ TWI của Atmega 8 có thể hoạt động ở nhiều chế độ.
Coi như bạn đã có những kiến thức cơ bản về chuẩn giao tiếp TWI (I2C) và chúng ta sẽ tìm hiểu
cách sử dụng module TWI của Atmega8 và ví dụ ở đây là giao tiếp với EEPROM 24C512.
Sử dụng TWI:
- Cấu hình cho phép TWI hoạt động, chọn tốc độ xung tại chân SCL, chọn chế độ Master – Slaver.
- Giao tiếp với các ngoại vi.
+ Để gửi START: ghi 1 vào bit TWSTA của thanh ghi TWCR.
+ Để gửi STOP: ghi 1 vào bit TWSTO của thanh ghi TWCR.
+ Để gửi ACK: ghi 1 vào bit TWEA của thanh ghi TWCR.
+ Dữ liệu cần gửi đi hoặc nhận được sẽ được chứa trong thanh ghi TWDR: muốn gửi 1 byte thì ghi byte
cần gửi vào TWDR, dữ liệu nhận được sẽ được chứa trên TWDR.

PHP:


#define F_CPU 4000000
#include <avr/io.h>
#include <util/delay.h>
int main() {


}

//cau hinh TWI
TWSR=0x00;
TWBR=12;
TWCR=(1<//START
TWCR=(1<while(!(TWCR&(1<//============
TWDR=0xa0;//ADDR 24C512 + W
TWCR=(1<while(!(TWCR&(1<TWDR=0x00;//addr 0
TWCR=(1<while(!(TWCR&(1<TWDR=0x00;//addr 0
TWCR=(1<while(!(TWCR&(1<TWDR=0x14;//value
TWCR=(1<while(!(TWCR&(1<TWCR=(1<

while(1);
return 0;

SPI của Atmega8 có thể hoạt động ở nhiều chế độ:
Ta chỉ tìm hiểu đại diện chế độ master:
Dữ liệu luôn luôn truyền theo hướng Master tới Slaver thông qua chân MOSI và từ Slaver tới
Master thông qua chân MISO.
Khi ghi muốn truyền dữ liệu ta sẽ ghi dữ liệu cần truyền vào thanh ghi dữ liệu SPI (SPI Data
Register - SPDR).
Mỗi khi gửi xong 1 byte thì bit SPIF sẽ được thiết lập: ta sẽ dùng để kiểm tra xem khi nào thì gửi
xong 1 byte.
SPI Control Register – SPCR:

Ta quan tâm đến các bit:
- Bit 6 – SPE: SPI Enable: ghi 1 vào bit này cho phép SPI hoạt động.
- Bit 5 – DORD: Data Order: khi bit này bằng 1 thì khi truyền SPI sẽ truyền bit thấp nhất (LSB)
trước, ngược lại khi bit bằng 0 thì SPI sẽ truyền bit cao nhất trước (MSB).
- Bit 4 – MSTR: Master – Slaver select: bằng 1 chọn chế độ Master, bằng 0 chọn chế độ Slaver.


- Bit 1..0: Chọn tốc độ xung SCK:

SPI Status Register:

- Bit 7 – SPIF: SPI Interrupt Flag: được đặt bằng 1 khi truyền xong 1 byte và ta sẽ kiểm tra bit này
để xem khi nào truyền xong 1 byte.
- Bit 0 – SPI2X: Nhân đôi tốc độ SCK
Để sử dụng SPI ta qua các bước sau:
- Cấu hình bộ SPI, chế độ master.
- Cho phép bộ SPI hoạt động.

- Truyền, nhận dữ liệu.
Ví dụ:
PHP:

#define F_CPU 4000000
#include <avr/io.h>
#include <util/delay.h>


unsigned char uWrite, uRead;
unsigned char SPI_Byte(unsigned char c);
int main() {
DDRC=0xff;
DDRD=0x00;
//init SPI
DDRB=0b101000;
SPSR=0;
SPCR=0b01010000;

}

while(1) {
if(!(PIND&0x80)) {
_delay_ms(50);
if(!(PIND&0x80)) {
while(!(PIND&0x80));
if(uWrite=='a') {
uWrite='b';
uRead=SPI_Byte(uWrite);
PORTC=0;

} else {
uWrite='a';
uRead=SPI_Byte(uWrite);
PORTC=1;
}
}
}
}
return 0;

unsigned char SPI_Byte(unsigned char c) {
SPDR=c;
while(!(SPSR&(1<c=SPDR;
return c;
}


Ngắt là 1 tín hiệu khẩn cấp được gửi đến bộ vi xử lý, yêu cầu bộ vi xử lý dừng các công việc hiện tại
nhẩy đến thực hiện nhiệm vụ khẩn cấp đó nhiệm vụ này được gọi là trình phục vụ ngắt. Sau khi
thực hiện xong nhiệm vụ này thì vi xử lý tiếp tục làm các công việc tiếp theo.
Khi có 2 tín hiệu ngắt đến cùng 1 lúc thì tín hiệu nào ưu tiên cao hơn sẽ được thực hiện. Ở
atmega8 có 19 tín hiệu ngắt từ mức ưu tiên cao xuống thấp như sau.


Ngắt thường được sử dụng để thực hiện các công việc mà không biết trước thời điểm như trong
truyền thông, đếm sự kiện...
Phần này thì chúng ta chỉ nghiên cứu phần ngắt ngoài.
Để cho phép các ngắt hoạt động thì cần set bit I trong thanh ghi SREG lên 1.
Ở chip atmega8 có 2 ngắt ngoài được ký hiệu là INT0 và INT1. Các thanh ghi điều khiển ngắt

ngoài
- thanh ghi MCUCR

đây là thanh ghi điều khiển kiểu tác động ngắt theo sườn âm hay sườn dương, hay mức.
4 bit cao thì không cần quan tâm nhiều 4 bit thấp có bit 0 và 1 điều khiển int0, bit 2 3 điều khiển
int 1.


thanh ghi GICR

ở thanh ghi này thì chỉ cần quan tâm đến 2 bit là INT1 tức là cho phép ngắt INT1 hoạt động và
INT0 cho phép ngắt INT0 hoạt động.
thanh ghi cờ ngắt.

Ở thanh ghi này thì chỉ cần quan tâm đến 2 bit là INTF1 và INTF0. Khi có 1 sự kiện ngắt trên
INT1 thì bit INTF1 bật 1, khi có 1 sự kiện ngắt ở trên INT0 thì INTF0 bật 1.



×