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 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.