Trong phần này tiếp tục là tập hợp các bài tập ví dụ cho Vi điều khiển Atmega8. Chúng ta sẽ làm quen với việc sử dụng ngôn ngữ C để thực hiện một số ví dụ minh họa. Các bài tập gồm có việc sử dụng cổng cửa có sẵn để bật tắt đèn LED; vòng lặp và điều kiện; sử dụng mảng; ngắt phần cứng ngoài INT0; sử dụng bộ định thời Timer, biến đổi ADC, truyền thông chuẩn USART…
Các ví dụ vẫn sử dụng bộ dao động nội 1MHz.
Bài 1: Sử dụng cổng cửa bật tắt đèn LED
#ifndef F_CPU
#define F_CPU 1000000UL // Xác định tốc độ xung nội 1MHz
#endif
#include <avr/io.h> // Gọi thư viện chuẩn
#include <util/delay.h> // Gọi thư viện tạo trễ
int main(void)
{
DDRB = 0xFF; // PortB là lối ra
while(1) // Lặp vĩnh viễn
{
PORTB = 0xFF; // Bật LEDs trên PortB
_delay_ms(1000); // chờ 1000ms
PORTB= 0x00; // Tắt hết LEDs
_delay_ms(1000); // 1 s
}
}
Bài 1a: Read Input Port
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
uint8_t z,c;
int main(void)
{
DDRB = 0xFF;
DDRC = 0x00;
while(1)
{
z = PINC;
c = z & 0b00000001;
if (c == 0)
{
PORTB = 0xF0;
}
else
{
PORTB = 0x0F;
}
}
}
Bài 2: Sử dụng vòng lặp for
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRB = 0xFF;
while(1)
{
PORTB = 0xFF;
_delay_ms(500);
PORTB= 0x00;
_delay_ms(500);
//Dùng vòng lặp for để rút ngắn chương trình
for (uint8_t LED = 0; LED <= 7; LED++)
{
PORTB |= (1 << LED);
_delay_ms(500);
}
}
}
// Đoạn chương trình này đã được thực hiện bởi vòng FOR
/* PORTB |= (1<<0);
_delay_ms(500);
PORTB |= (1<<1);
_delay_ms(500);
PORTB |= (1 << 2);
_delay_ms(500);
PORTB |= (1<<3);
_delay_ms(500);
PORTB |= (1 << 4);
_delay_ms(500);
PORTB |= (1 << 5);
_delay_ms(500);
PORTB |= (1<<6);
_delay_ms(500);
PORTB |= (1 << 7);
_delay_ms(500);
*/
Bài 3: Sử dụng mảng Array[ ]
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
// Khai báo mảng
uint8_t LED[10] = {0b00000000, 0b00000001, 0b00000100, 0b00010000, 0b01000000,
0b11000000, 0b11110000, 0b11111100, 0b11111111, 0b10101010};
int main(void)
{
DDRB = 0xFF;
while(1)
{
// Thay đổi điều kiện so sánh i để quan sát tác động
for (uint8_t i = 0; i < 10; i++)
{
PORTB = LED[i];
_delay_ms(500);
}
}
}
Bài 4: Sử dụng ngắt ngoài External Interrupt 0
/*
* c_exint.c
*
* Created: 10/27/2018 9:31:47 PM
* Author: Tung Le
*/
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
#include <avr/interrupt.h> // Gọi thư viện ngắt
#include <util/delay.h>
//Khai báo mảng & biến
uint8_t LED[10] = {0b00000000, 0b00000001, 0b00000100, 0b00010000, 0b01000000,
0b11000000, 0b11110000, 0b11111100, 0b11111111, 0b10101010};
uint8_t i = 0;
// Chương trình ngắt ngoài 0
ISR (INT0_vect)
{
PORTB = LED[i];
i++;
//
if (i == 9)
{
i = 0;
}
}
int main(void)
{
DDRB = 0xFF;
DDRD = 0b00000000; // PortD lối vào
PORTB = 0x00;
PORTD = 0x00;
GICR |= (1 << INT0); // Cho phép ngắt ngoài
sei(); // Cho phép ngắt toàn cục
while(1)
{
// nothing
// chỉ chạy chương trình ngắt
}
}
Bài 5: Đưa chương trình ngắt vào chương trình bằng cấu trúc điều kiện If..else
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
uint8_t LED[10] = {0b00000000, 0b00000001, 0b00000100, 0b00010000, 0b01000000,
0b11000000, 0b11110000, 0b11111100, 0b11111111, 0b10101010};
uint8_t i = 0;
uint8_t flag = 0;
ISR (INT0_vect)
{
flag = 1; // chỉ bật cờ rồi thoát
}
int main(void)
{
DDRB = 0xFF;
DDRD = 0b00000000;
PORTB = 0x00;
PORTD = 0x00;
GICR |= (1 << INT0);
sei();
while(1)
{
for(uint8_t i = 0; i < 10; i++)
{
PORTB = LED[i];
_delay_ms(500);
}
// Sử dụng cấu trúc điều kiện
if (flag == 1)
{
flag = 0;
//
for(uint8_t j = 10; j > 0; j--)
{
PORTB = LED[j];
_delay_ms(500);
}
}
}
}
/* If else conditions
* If (cond)
* {
* // do something
* }
* else
* {
* // do something else
* }
*/
Bài 6: Sử dụng ngắt bộ định thời Timer
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
uint8_t LED[10] = {0b00000000, 0b00000001, 0b00000100, 0b00010000, 0b01000000,
0b11000000, 0b11110000, 0b11111100, 0b11111111, 0b10101010};
uint8_t i = 0;
ISR (INT0_vect)
{
PORTB = LED[i];
i++;
//
if (i == 9)
{
i = 0;
}
}
// Chương trình ngắt khi bộ đếm đạt ngưỡng
ISR (TIMER1_COMPA_vect)
{
PORTB ^= (1 << 0);
}
int main(void)
{
DDRB = 0xFF;
DDRD = 0b00000000;
PORTB = 0x00;
PORTD = 0x00;
//INT0
GICR |= (1 << INT0);
//CTC, non PWM, chia xung 1024
TCCR1B |= (1 << CS12) | (1 << CS10) | (1<< WGM12);
TIMSK |= (1 << OCIE1A); // ngắt khi đạt ngưỡng
OCR1A = 1000; // giá trị ngưỡng đếm
sei();
while(1)
{
//nothing
}
}
Bài 7: Tạo xung PWM bằng Timer2
#ifndef F_CPU
#define F_CPU 1000000UL // Xác định tốc độ xung 1MHz
#endif
#include <avr/io.h>
int main(void)
{
DDRB = 0xFF;
PORTB = 0x00;
OCR2 = 128; // PWM 50% (max 256)
TCCR2 |= (1 << COM21); // chế độ không đảo
TCCR2 |= (1 << WGM21) | (1 << WGM20);// Fast PWM
TCCR2 |= (1 << CS21); // chia xung 8 & bật PWM
while(1)
{
// chân PB3 (OC2) phát xung PWM tần số 488Hz
// 1MHz/(8x256)
}
}
Bài 8: Sử dụng ADC
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
uint8_t LED[10] = {0b00000000, 0b00000001, 0b00000100, 0b00010000, 0b01000000,
0b11000000, 0b11110000, 0b11111100, 0b11111111, 0b10101010};
uint8_t i = 0;
uint8_t ADCread = 0;
uint8_t gotADC = 0;
ISR (INT0_vect)
{
ADCSRA |= (1 << ADSC); // Bật ADC 1 lần
}
ISR(ADC_vect)
{
// Vì chỉ cần độ chính xác vừa phải
// ta dịch 10bit ADC sang phải thanh ghi kết quả
ADCread = ADCH; // Đọc ra 8bit
gotADC = 1; // bật cờ báo
}
int main(void)
{
DDRB = 0xFF;
DDRC = 0x00;
DDRD = 0b00000000;
PORTB = 0x00;
PORTC = 0x00;
PORTD = 0x00;
GICR |= (1 << INT0);
//
ADMUX |= (1 << ADLAR); // Dịch phải
//Cho phép ADC, ngắt khi ADC chạy xong, chia xung 128
ADCSRA |= (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
sei();
while(1)
{
for(uint8_t i = 0; i < 10; i++)
{
PORTB = LED[i];
_delay_ms(500);
}
//
if (gotADC == 1)
{
gotADC = 0;
PORTB = ADCread;
_delay_ms(1000);
}
}
}
Bài 9: Truyền thông theo chuẩn USART
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
uint8_t LED[10] = {0b00000000, 0b00000001, 0b00000100, 0b00010000, 0b01000000,
0b11000000, 0b11110000, 0b11111100, 0b11111111, 0b10101010};
uint8_t i = 0;
char ADCread = 0;
uint8_t gotADC = 0;
uint8_t data = 0;
uint8_t gotdata = 0;
ISR (INT0_vect)
{
ADCSRA |= (1 << ADSC);
}
// Chương trình ngắt khi USART nhận được dữ liệu
ISR(USART_RXC_vect)
{
data = UDR; // Đọc dữ liệu
gotdata = 1;
}
//
ISR(ADC_vect)
{
ADCread = ADCH;
gotADC = 1;
}
// Dùng hàm để rút gọn chương trình chính
// Hàm truyền dữ liệu
void USART_Send(char bytedata)
{
// Chờ thanh ghi dữ liệu trống thì truyền
//while ((UCSRA & (1 << UDRE)) == 0) {};
while (!(UCSRA & (1<<UDRE)));
UDR = bytedata;
}
// Hàm khởi tạo chức năng USART
void UASRT_Init(void)
{
// Cho phép truyền và nhận, ngắt khi nhận được dữ liệu
UCSRB |= (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);
// 8bit dữ liệu, 1 stop, 0 parity
UCSRC |= (1 << UCSZ1) | (1 << UCSZ0);
UBRRH = 0;
UBRRL = 6; // hệ số khi tính Baudrate
}
int main(void)
{
DDRB = 0xFF;
DDRC = 0x00;
DDRD = 0b00000010; // Chân Tx tại PD1 cần đặt là lối ra
PORTB = 0x00;
PORTC = 0x00;
PORTD = 0x00;
GICR |= (1 << INT0);
//
ADMUX |= (1 << ADLAR);
ADCSRA |= (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
//
// Gọi hàm khởi tạo USART
UASRT_Init();
sei();
while(1)
{
for(i = 0; i < 10; i++)
{
PORTB = LED[i];
_delay_ms(500);
}
//
if (gotADC == 1)
{
gotADC = 0;
PORTB = ADCread;
// Gọi hàm gửi dữ liệu
USART_Send(ADCread);
//
_delay_ms(1000);
}
// Khi nhận được dữ liệu
if (gotdata == 1)
{
gotdata = 0;
PORTB = data;
_delay_ms(2000);
}
}
}
Cách thức điều chỉnh và tác động của Fuse & Lock bits sẽ được trình bày trong bài sau.
