Hướng dẫn bài 6 timer/counter 0

mta_cdt

Super Moderator
Thành viên BQT
#1
Do 2 hôm nay mình đang bị ho khù khụ nên làm video không hợp lý lắm mình xin phép trình bày bằng hình ảnh mong bà con thông cảm.

- Timer là bộ định thời tức là xác định 1 khoảng thời gian nào đó, ứng dụng để đo độ rộng xung, hoặc là làm đồng hồ đếm thời gian, quét led 7seg, trong lập trình nâng cao ứng dụng rộng rãi hơn nhiều
- counter là bộ đếm sự kiện ứng dụng để đếm sản phẩm trên 1 băng truyền, đếm xung của encorder....

Atmega16 có 3 bộ timer/couter trong đó.
- timer0 8 bit có hỗ trợ 1 kênh pwm giá trị tối đa là 255.
- timer 16 bit có hỗ trợ 2 kênh pwm giá trị tối đa là 65535
- timer2 8 bit hỗ trợ 1 kênh pwm giá trị tối đa là 255

Phần này ta sẽ tìm hiểu về timer/counter 0 (sau này viết tắt là T/C0).
T/C0 có thể đếm xung hệ thống ( lấy từ dao động nội hoặc ngoại) sau khi qua bộ chia tần, hoặc là xung bên ngoài lấy từ T0(PB0). Việc chọn bộ chia và tín hiệu để kích cụ thể bạn có thể xem bên dưới bằng cách cấu hình 3 bit cs00,cs01,cs02 của thanh ghi TCCR0.
Các thanh ghi điều khiển T/C0 là
- TCNT0 : là thanh ghi 8 bit chưa giá trị vận hành của T/C0. Thanh ghi này cho phép đọc và ghi đều được.
- thanh ghi TCCR0 chính là thanh ghi điều khiển của T/C0 thanh ghi này
2013-10-14_093138.png
Sử dụng ở chế độ Timer và counter thì chỉ cần quan tâm đến 3 bit cuối CS00, CS01,CS02 các bit còn lại dùng ở chế độ pwm.

[codientu.org]_fig3.jpg
- TIMSK: thanh ghi mặt nạ ngắt T/C. trong đó khi set TOIE0=1 cho phép ngắt tràn T/0
2013-10-14_094951.png
- TIFR thanh ghi cờ nhớ cho tất cả bộ T/C
TIR.png
T/C0 hoạt động như sau: khi nhận được 1 xung thì TCNT0 tăng thêm 1 đơn vị. Cho đến khi TCNT0 đạt đến 255, nếu có thêm 1 tín hiệu kích nữa thì TCNT0 sẽ trở về 0 đồng thời bật bit TOV0(bit0 của TIFR) được set lên 1. Nếu cho phép ngắt tràn thì sẽ có 1 ngắt tràn xẩy ra, căn cứ vào tín hiệu kích ngắt và ngắt tràn ta có thể tạo ra 1 bộ định thời hoặc 1 bộ đếm sự kiện.
 
Sửa lần cuối:

mta_cdt

Super Moderator
Thành viên BQT
#2
Đầu tiên thử làm việc với chế độ counter
TIR.png

+ thiết lập TCCR0 ở chế độ đếm sự kiện
+ đọc TCNT0 hiển thị lên lcd16x2
counter.png
sau khi chọn chip và thạch anh bạn chọn timer 0 và chọn tín hiệu kích là falling edge (sườn xuống) hoặc là rising edge (sườn lên).
và thiết lập lcd
thiet lap lcd.png
giờ ta bắt đầu code như sau
Mã:
/*****************************************************
This program was produced by the
CodeWizardAVR V2.05.0 Professional
Automatic Program Generator
© Copyright 1998-2010 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com

Project :
Version :
Date    : 10/14/2013
Author  : NeVaDa
Company :
Comments:


Chip type              : ATmega16
Program type            : Application
AVR Core Clock frequency: 8.000000 MHz
Memory model            : Small
External RAM size      : 0
Data Stack size        : 256
*****************************************************/

#include <mega16.h>
#include <delay.h>


// Alphanumeric LCD Module functions
#include <alcd.h>

// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTA=0x00;
DDRA=0x00;

// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=P
PORTB=0x01;      // treo pullup
DDRB=0x00;

// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;

// Timer/Counter 0 initialization
// Clock source: T0 pin Falling Edge
// Mode: Normal top=0xFF
// OC0 output: Disconnected
TCCR0=0x06;  // cs2=1,cs1=1,cs0=0
TCNT0=0x00;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer1 Stopped
// Mode: Normal top=0xFFFF
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;

// USART initialization
// USART disabled
UCSRB=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// ADC initialization
// ADC disabled
ADCSRA=0x00;

// SPI initialization
// SPI disabled
SPCR=0x00;

// TWI initialization
// TWI disabled
TWCR=0x00;

// Alphanumeric LCD initialization
// Connections specified in the
// Project|Configure|C Compiler|Libraries|Alphanumeric LCD menu:
// RS - PORTC Bit 0
// RD - PORTC Bit 1
// EN - PORTC Bit 2
// D4 - PORTC Bit 4
// D5 - PORTC Bit 5
// D6 - PORTC Bit 6
// D7 - PORTC Bit 7
// Characters/line: 16
lcd_init(16);

while (1)
      {  lcd_gotoxy(0,0);
      lcd_putsf("counter:");
      lcd_putchar(TCNT0/100+48);// hien thi hang tram tcnt0
      lcd_putchar((TCNT0%100)/10+48);  // hien thi hang chuc tcnt0
      lcd_putchar(TCNT0%10+48);    // hien thi hang don vi tcnt0
      // Place your code here

      }
}
sơ đồ mạch
TIR.png
đối với avr studio bạn sau khi add thư viện textlcd.h như bài trước mình code như sau
Mã:
#define F_CPU 8000000
#include <util/delay.h>
#include <avr/io.h>
#include "textlcd.h"


int main(void)
{
DDRC=0xFF;
PORTB=0x01;// treo pull up portb.1
TCCR0=0x06;// cs02=1,cs01=1;cs00=0 chon xung kich suon xuong tren T0
lcd_init();
while(1)
{
lcd_gotoxy(0,0);
lcd_putsf("counter:",8);
lcd_putc(TCNT0/100+48);// hien thi len lcd
lcd_putc((TCNT0%100/10)+48);
lcd_putc((TCNT0%10)+48);
}
}
toàn bộ code và file mô phỏng download ở file đính kèm
 

Đính kèm

Sửa lần cuối:

mta_cdt

Super Moderator
Thành viên BQT
#3
tiếp theo đến timer
ví dụ đơn giản led nối với PORTD.0 cứ 1s thay đổi trạng thái 1 lần sử dụng timer.

TIR.png
- để giải quyết bài toàn này ta có thể dùng delay nhưng nhược điểm delay là trong khi delay cpu hoàn toàn không làm việc còn timer thì cpu vẫn làm việc khi timer đếm cụ thể bạn có thể xem hình dưới.
thiet lap lcd.png

Giả sử mình dùng thạch anh 8MHZ. chu kỳ là 1/8MHZ= 0,125us
vậy 1s cần có 1s/0,125us=8 triệu xung
TCNT0 là thanh ghi 8 bit giá trị max là 255 cộng thêm 1 chu kỳ tràn từ 255 về 0 là 256. 8 triệu lớn hơn 256 rất nhiều vì vậy ta chọn bộ chia cao nhất 1024 lúc này ta cần 8000000/1024=7812.5 lấy tròn là 7813 xung
Nếu mà khởi tạo TCNT0=4 thì cần có 7813/(256-4)=31 31 lần tràn
bây giờ chúng ta code như sau
- đầu tiên với codevison avr

thiet lap lcd.png
ô clock soure chọn giao động lấy từ hệ thống
clock value chọn 7.813khz là tần số sau khi đã qua bộ chia
tích chọn overflow interrupt chọn ngắt tràn
tại ô timer value gõ 04 tức là giá trị khởi tạo là của tcnt0 là 4
Và chọn đầu ra cho portD.0

- các bước thực hiện như sau
+ thiết lập thanh ghi TCCR0 ở bộ chia phù hợp
+ gán giá trị TCNT0 ở giá trị khởi tạo
+ set TIMSK=0x01 cho phép ngắt tràn timer 0
+ set biến i của SREG cho phép ngắt toàn cục
Mã:
/*****************************************************
This program was produced by the
CodeWizardAVR V2.05.0 Professional
Automatic Program Generator
© Copyright 1998-2010 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com

Project :
Version :
Date    : 10/14/2013
Author  : NeVaDa
Company :
Comments:


Chip type              : ATmega16
Program type            : Application
AVR Core Clock frequency: 8.000000 MHz
Memory model            : Small
External RAM size      : 0
Data Stack size        : 256
*****************************************************/

#include <mega16.h>
int i;
// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0=0x04;
i++; // moi lan ngat tran thi i tang len 1
if(i==31){PORTD.0^=1;i=0;}    // khi dem den 31 thi dao trang thai PORTD,0 gán i=0 de bat dau lai
// Place your code here

}

// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTA=0x00;
DDRA=0x00;

// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;

// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x01;    // dat dau ra

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 7.813 kHz
// Mode: Normal top=0xFF
// OC0 output: Disconnected
TCCR0=0x05;
TCNT0=0x04;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer1 Stopped
// Mode: Normal top=0xFFFF
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x01;        // cho phep ngat tran timer 0

// USART initialization
// USART disabled
UCSRB=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// ADC initialization
// ADC disabled
ADCSRA=0x00;

// SPI initialization
// SPI disabled
SPCR=0x00;

// TWI initialization
// TWI disabled
TWCR=0x00;

// Global enable interrupts
#asm("sei")    // cho phep ngat toan cuc

while (1)
      {
      // Place your code here

      }
}
Đối với avr studio ta code như sau
Mã:
#define F_CPU 8000000
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
int i;// bien dem so ngat tran
int main(void)
{
DDRD=0x01; //D0 là dau ra
TCCR0= (1<<CS02)|(1<<CS00);// chon bo chia 1024
TCNT0=4;// khoi tao tcnt0=4
TIMSK=0x01;// cho phep ngat tran timer 0
sei(); // seti=1 cho phep ngat toan cuc
while(1)
{

}
}
// trinh phuc vu ngat timer 0
ISR(TIMER0_OVF_vect)
{
TCNT0=0x04;// gán tro lai 0x04 cho TCNT0
i++;// moi lan tran i tang len 1
if(i==31){PORTD^=1;i=0;}// dao trang thai D0 gán i=0

}
mạch mô phỏng và project có thể down ở file đính kèm
bài tập luyên tập
- bạn dùng timer để tạo 1 đồng hồ đếm thời gian của 1 trận đá bóng hiển thị trên LCD
- như bài 4 mình dùng delay quét led 7seg bây giờ bạn dùng timer quét và so sánh sự khác biệt
 

Đính kèm

anndt_55

Học sinh trung cấp
#4
Bác nói cho em rõ hơn phần sei(); với cái i++ ở cuối được không ạ?
Nếu không có i++ thì sảy ra trường hợp gì ạ? Thanks!
 

mta_cdt

Super Moderator
Thành viên BQT
#5
sei(); là cho phép tất cả các ngắt hoạt động nếu như không có dòng này thì không có ngắt nào cho phép cả.
i++; là mỗi lần ngắt tăng nên 1 khi đếm đến 31 thì tự động gán lại bằng 0. Nếu mà không i++ thì mỗi lần ngắt xảy ra D0 đổi trạng thái 1 lần thời gian đổi trạng thái là 1/31 giây.
 

mta_cdt

Super Moderator
Thành viên BQT
#7
Cho em hỏi là tại sao là 31, với giá trị khác được không?
Giả sử mình dùng thạch anh 8MHZ. chu kỳ là 1/8MHZ= 0,125us
vậy 1s cần có 1s/0,125us=8 triệu xung
TCNT0 là thanh ghi 8 bit giá trị max là 255 cộng thêm 1 chu kỳ tràn từ 255 về 0 là 256. 8 triệu lớn hơn 256 rất nhiều vì vậy ta chọn bộ chia cao nhất 1024 lúc này ta cần 8000000/1024=7812.5 lấy tròn là 7813 xung
Nếu mà khởi tạo TCNT0=4 thì cần có 7813/(256-4)=31 31 lần tràn
 

anndt_55

Học sinh trung cấp
#9
À anh Lành ơi, cho em hỏi cách chọn TCNT0 có phải là lấy thời gian đặt trước chia cho giá trị chia tần rùi lấy 256 trừ đi giá trị vừa rùi đúng không ạ?
 

mta_cdt

Super Moderator
Thành viên BQT
#10
Đâu phải thế đâu chú, chú lấy tần số thạch anh chia cho bộ chia thì ra tần số xung, lấy nghịch đảo cái tần số xung này ra chu kỳ đếm tức là cứ sau thời gian đúng bằng chu kỳ này thì TCNT0 tăng nên 1 khi đến 255 đếm thêm 1 xung nữa thì bị reset về 0 nếu như có cho phép ngắt thì xẩy ra ngắt.
Khi nhẩy về 0 thì chú có thể gán thẳng luôn TCNT0 bằng giá trị chú đặt thôi.
tức là (256-giá trị đặt TCNT0)*(1/tần số thạch anh)*bộ chia= thời gian xẩy ra ngắt.
 

Quảng cáo Google