راه اندازی تایمرکانتر در ATtiny26

از پاپیروس
پرش به ناوبریپرش به جستجو

Makefile

MCU=attiny26
FUSE_L=0xe1
FUSE_H=0x17
F_CPU=1000000
CC=avr-gcc
LD=avr-ld
OBJCOPY=avr-objcopy
SIZE=avr-size
AVRDUDE=avrdude
CFLAGS=-std=c99 -Wall -g -Os -mmcu=${MCU} -DF_CPU=${F_CPU} -I.
TARGET=main
CPUNAME=at26
DATE=`(date +%F_%T)| sed 's/://g' | sed 's/-//g'`

SRCS = main-c.c

all:
	${CC} ${CFLAGS} -o bin/${TARGET}.o ${SRCS}
	${LD} -o bin/${TARGET}.elf bin/${TARGET}.o
	${OBJCOPY} -j .text -j .data -O ihex bin/${TARGET}.o bin/${TARGET}.hex
	${SIZE} -C --mcu=${MCU} bin/${TARGET}.elf

flash:
	${AVRDUDE} -p ${MCU} -c usbasp -B10 -U flash:w:bin/${TARGET}.hex:i -F -P usb

readfb:
	avrdude -c usbasp -p ${MCU} -U lfuse:r:fusebits/lf-${CPUNAME}-${DATE}.hex:h -U hfuse:r:fusebits/hf-${CPUNAME}-${DATE}.hex:h -U efuse:r:fusebits/ef-${CPUNAME}-${DATE}.hex:h

clean:
	rm -f *.c~ bin/*.o bin/*.elf bin/*.hex
fuse:
	$(AVRDUDE) -p ${MCU} -c usbasp -B10 -U hfuse:w:${FUSE_H}:m -U lfuse:w:${FUSE_L}:m

راه‌اندازی تایمر/کانتر صفر

تایمر صفر

شماتیک

Left

کد برنامه (فایل main.c)

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

volatile char myvar = 150; //it could be from 0 to 255
ISR(TIMER0_OVF0_vect)
{
	PORTA ^= (1 << PA7); //toggle LED on PA7 (pin 11 of ATtiny26)
	TIFR |=( 1<<TOV0); //reset flag. it seems it's not needed
	TCNT0 = myvar; //setting initial value of timer. if ignore, timer/counter start from 0 to 255. if set to myvar, it starts from myvar to 255
}

static inline void initTC0(void) {
	TCCR0 |= (1<<CS02) | (0<<CS01) | (1<<CS00); //prescaler CK/1024
	TIMSK |= (1<<TOIE0); //enable  TIMER0_OVF0_vect interrupt
	TCNT0 = myvar; //setting initial value of timer. if ignore, timer/counter start from 0 to 255. if set to myvar, it starts from myvar to 255
}


int main(void)
{
	initTC0();
	DDRA |= (1<<PA7); //set PA7 as output
	sei();
	
	while(1){
	}
}

کانتر صفر

شماتیک

Left

کد برنامه (فایل main.c)

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

volatile char myvar = 251; //it could be from 0 to 255
ISR(TIMER0_OVF0_vect)
{
	PORTA ^= (1 << PA7); //toggle LED on PA7 (pin 11 of ATtiny26)
	TIFR |=( 1<<TOV0); //reset flag. it seems it's not needed
	TCNT0 = myvar; //setting initial value of timer. if ignore, timer/counter start from 0 to 255. if set to myvar, it starts from myvar to 255
}

static inline void initTC0(void) {
	TCCR0 |= (1<<CS02) | (1<<CS01) | (1<<CS00); //set clock source as: External Pin T0 (pin 9 of ATtiny26) on rising edge. in order to set on falling edge, change (1<<CS00) to (0<<CS00)
	TIMSK |= (1<<TOIE0); //enable  TIMER0_OVF0_vect interrupt
	TCNT0 = myvar; //setting initial value of timer. if ignore, timer/counter start from 0 to 255. if set to myvar, it starts from myvar to 255
}


int main(void)
{
	initTC0();
	DDRA |= (1<<PA7) | (1<<PA6); //set PA7 and  PA6 as output. PA7 is for demonstrating LED as over follow action and PA6 is for demonstrating LED as input external clock on T0  (pin 9 of ATtiny26) 
	DDRB &= ~(1 << PB6); //set T0  (pin 9 of ATtiny26)  as input
	PORTB|= (1 << PB6); //set T0  (pin 9 of ATtiny26)  pull-up
	sei();
	
	while(1){
		PORTA ^= (1 << PA6); //toggle LED on PA6 (pin 12 of ATtiny26) in order to create external clock 
		_delay_ms(100); //wait for 100 miliseconds
	}
}

راه‌اندازی تایمر/کانتر یک

منبع کلاک می تواند کلاک CPU سیستم (CK) یا کلاک پرسرعت ۶۴ مگاهرتزی درونی (PCK) باشد. البته هر کدام که انتخاب شود با استفاده از prescaler می‌توان به فرکانس مورد نظر رسید. یرای استفاده از PCK کافی‌ست مد asynchronous فعال گردد. بعد از فعال شدن این مد، کلاک مورد استفاده تایمر/کانتر شماره یک از CK به PCK تغییر پیدا می‌کند.

فعال کردن مد asynchronous

PLLCSR |= (1<<PLLE); //enable PLL
while ( ((PLLCSR & (1 << PLOCK)) == 0) ){ //wait for PLL lock
}
PLLCSR |= (1<<PCKE); //enable PCK

در صورت نیاز برای برگشت به CK می‌توان:

  1. از PLLCSR &= ~(1<<PLLE); استفاده کرد. در این حالت برای برگشت به PCK، باید قطعه کد بالا را مجددا به کار برد.
  2. یا از PLLCSR &= ~(1<<PCKE); استفاده کرد. در این حالت برای برگشت به PCK، فقط کافی‌ست از PLLCSR |= (1<<PCKE); استفاده شود.

حالتی که فقط از overfollow استفاده می‌کنیم. (مقایسه‎‌گرهای A و B استفاده نمی‌شوند)

شماتیک

Left

کد برنامه (فایل main.c)

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

ISR(TIMER1_OVF1_vect)
{
	PORTA ^= (1 << PA7); //toggle LED on PA7 (pin 11 of ATtiny26)
	TIFR |=( 1<<TOV1); //reset flag. it seems it's not needed
}

static inline void initTC1(void) {

	PLLCSR |= (1<<PLLE); //enable PLL
	while ( ((PLLCSR & (1 << PLOCK)) == 0) ){ //wait for PLL lock
	}
	PLLCSR |= (1<<PCKE); //enable PCK

	TCCR1B |= (1<<CS13) | (1<<CS12) | (1<<CS11) | (1<<CS10); //prescaler CK/16384 or PCK/16384
	TCCR1B |= (1<<CTC1); //enable CTC1
	TIMSK |= (1<<TOIE1); //enable  TIMER1 Over follow  interrupt
	OCR1C = 0x7E; //maximum value of Timer1. this option works only if CTC1 bit set. otherwise timer/counter counts to 0xFF and after next clock over follow occurred.
	
}


int main(void)
{
	initTC1();
	DDRA |= (1<<PA7); //set PA7 as output
	sei();
	
	while(1){
	}
}

حالتی که از مقایسه‎‌گرهای A و B استفاده می‌کنیم. (از overfollow استفاده نمی‌شود.)

شماتیک

Left

کد برنامه (فایل main.c)

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

ISR(TIMER1_CMPA_vect)
{
	//do everything you want;
}

ISR(TIMER1_CMPB_vect)
{
	//do everything you want
}


static inline void initTC1(void) {
	TCCR1A |= (0<<COM1A1) | (1<<COM1A0); //toggle OC1A (PB1 or pin 2 of ATtiny26)
	TCCR1A |= (0<<COM1B1) | (1<<COM1B0); //toggle OC1B (PB3 or pin 4 of ATtiny26)
	TCCR1B |= (1<<CS13) | (1<<CS12) | (1<<CS11) | (1<<CS10); //prescaler setting:  CK/16384 or PCK/16384
	TCCR1B |= (1<<CTC1); //enable CTC1

	TIMSK |= (1<<OCF1A); // Timer/Counter1 Compare Match 1A
	TIMSK |= (1<<OCF1B); // Timer/Counter1 Compare Match 1B
	
	OCR1A = 0x1E; //compare value for comparator A
	OCR1B = 0x22; //compare value for comparator B
	OCR1C = 0x3F; //maximum value of Timer1. this option works only if CTC1 bit set. otherwise timer/counter counts to 0xFF and after next clock over follow occurred.
	
}


int main(void)
{
	initTC1();
	DDRB |= (1<<PB1) | (1<<PB3); //set OC1A and OC1B (PB1 and PB3)  as output
	sei();
	
	while(1){
	}
}

با نوشتن TCCR1A |= (1<<FOC1A); (یا متناظر آن FOC1B) می‌توان فارغ از مقدار تایمر روی پین OC1A (یا OC1B) اثر گذاشت (شرایط compare match را ایجاد کرد). البته تحت هیچ شرایطی با این کار وقفه‌ای تحریک نخواهد شد.

راه‌اندازی PWM

شماتیک

Left

کد برنامه (فایل main.c)

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

static inline void initTC1(void) {
	TCCR1A |= (1<<PWM1A); //enable PWM A
	TCCR1A |= (0<<COM1A1) | (1<<COM1A0); //set OC1A and ~OC1A as output pin for PWM
	TCCR1B |= (0<<CS13) | (1<<CS12) | (1<<CS11) | (1<<CS10); //prescaler setting:  CK/64 or PCK/64
	TCCR1B |= (1<<CTC1); //enable CTC1

	OCR1A = 0x10; //compare value for comparator A
	OCR1C = 0xFE; //maximum value of Timer1. this option works only if CTC1 bit set. otherwise timer/counter counts to 0xFF and after next clock over follow occurred.

	PLLCSR |= (1<<PLLE); //enable PLL
	while ( ((PLLCSR & (1 << PLOCK)) == 0) ){ //wait for PLL lock
	}
	PLLCSR |= (1<<PCKE); //enable PCK
}


int main(void)
{
	initTC1();
	DDRB |= (1<<PB1) | (1<<PB0); //set OC1A and ~OC1A (PB1 and PB0)  as output
	sei();
	
	while(1){
	}
}