Bài tập ví dụ cho Vi điều khiển Atmega8 (P2)

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.

Leave a Reply

Your email address will not be published. Required fields are marked *