Второй 8-битный таймер ATmega8 - TIMER2. Кроме перывания по переполнению счетчика, расмотренное в предыдущем посте, TIMER2 имеет второе прерывание по значению компаратора. Т.е. не надо будет ждать переполнения счетчика, прерывание сработает когда счетчик достигнет определенного значения. Это позволяет более точно устанавливать частоту срабатывания и по сути является производным генератором частоты. Штука архиполезная, позволяет выставлять значения портов по какой либо-функции или наоборот снимать показания с портов с некой периодичностью. Используется для PWM, но PWM оставим на потом, сейчас будет полезно разобраться с CTC режимом работы компаратора, когда его можно использовать как счетчик.
CTC режим (Clear Timer on Compare Match Mode), это режим при котором зачение таймера сбрасывается при достижении занчения определенного в регистре OCR2. Для TIMER2 данный режим устанавливается в TCCR2 регистре флагом WGM21:
В этом же регистре флагами CS22, CS21, CS20 выставляется prescaler:
Включается прерывание в регистре TIMSK установкой флага OCIE2:
Значение компаратора при котором генерируется прерывание заносится в регистр OCR2. Это обычный числовой регистр.
Теперь побробуем составить алгоритм. В прошлый раз отмеряли секунду по формуле:
частота_кварца = prescaler * (1<<разрядность_тамера)* значение_счетчика
Тогда в счетчике было 64, а таймер срабатывал на 256. Оставим prescaler также в 1:1024, в счетчик закиним на этот раз 128, а в компаратор 64. Тогда прерывание будет вызываться раз в полсекунды.
Готовая программа на Си не сильно отличается от предыдущего примера:
/* timer2.c LED Blink via 8-bit Timer2 in CTC mode for AVR ATmega8 */ #define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> register unsigned char myTimer asm("r28"); ISR(TIMER2_COMP_vect) { myTimer--; if (!myTimer) { PORTB ^= (1<<PB5); myTimer=0x80; } } int main(void) { DDRB |= (1<<PB5); // pinMode(13,OUTPUT); в Wiring // timer2 setup TIMSK = (1<<OCIE2); // timer2: compare interrup is enable TCCR2 = (1<<WGM21); // set CTC mode TCCR2|= (1<<CS22) |(1<<CS21)| (1<<CS20); // prescaler 1/1024 OCR2 = 0x40; myTimer=0x80; sei(); for (;;) {} return 0; }
Прошивка вышла на 132 байта. Посмотрим на ассемблерную версию:
avr-gcc -mmcu=atmega8 -Wall -Os -S timer2.c
.file "timer2.c" __SP_H__ = 0x3e __SP_L__ = 0x3d __SREG__ = 0x3f __tmp_reg__ = 0 __zero_reg__ = 1 .text .global __vector_3 .type __vector_3, @function __vector_3: push r1 push r0 in r0,__SREG__ push r0 clr __zero_reg__ push r24 push r25 /* prologue: Signal */ /* frame size = 0 */ /* stack size = 5 */ .L__stack_usage = 5 ldi r24,lo8(-1) add r24,r28 mov r28,r24 brne .L1 in r24,0x18 ldi r25,lo8(32) eor r24,r25 out 0x18,r24 ldi r28,lo8(-128) .L1: /* epilogue start */ pop r25 pop r24 pop r0 out __SREG__,r0 pop r0 pop r1 reti .size __vector_3, .-__vector_3 .section .text.startup,"ax",@progbits .global main .type main, @function main: /* prologue: function */ /* frame size = 0 */ /* stack size = 0 */ .L__stack_usage = 0 sbi 0x17,5 ldi r24,lo8(-128) out 0x39,r24 ldi r24,lo8(8) out 0x25,r24 in r24,0x25 ori r24,lo8(7) out 0x25,r24 ldi r24,lo8(64) out 0x23,r24 ldi r28,lo8(-128) /* #APP */ ; 29 "timer2.c" 1 sei ; 0 "" 2 /* #NOAPP */ .L5: rjmp .L5 .size main, .-main .ident "GCC: (GNU) 4.8.1"
здесь все тривиально: немного подправив ассемблерный исходник из предыдущего поста получаем такой текст на ассемблере:
.equ DDRB, 0x17 .equ PB5, 0x05 .equ PORTB, 0x18 .equ SREG, 0x3F .equ SPL, 0x3D .equ SPH, 0x3E .equ TIMSK, 0x39 .equ TCCR2, 0x25 .equ OCR2, 0x23 .equ value, 0x80 ; R28 is COUNTER .org 0x00 ; начало rjmp RESET ; Reset Handler reti ;rjmp EXT_INT0 ; IRQ0 Handler reti ;rjmp EXT_INT1 ; IRQ1 Handler rjmp vector_3; reti ;rjmp TIM2_COMP ; Timer2 Compare Handler reti ;rjmp TIM2_OVF ; Timer2 Overflow Handler reti ;rjmp TIM1_CAPT ; Timer1 Capture Handler reti ;rjmp TIM1_COMPA ; Timer1 CompareA Handler reti ;rjmp TIM1_COMPB ; Timer1 CompareB Handler reti ;rjmp TIM1_OVF ; Timer1 Overflow Handler reti ;rjmp TIM0_OVF ; Timer0 Overflow Handler reti ;rjmp SPI_STC ; SPI Transfer Complete Handler reti ;rjmp USART_RXC ; USART RX Complete Handler reti ;rjmp USART_UDRE ; UDR Empty Handler reti ;rjmp USART_TXC ; USART TX Complete Handler reti ;rjmp ADC ; ADC Conversion Complete Handler reti ;rjmp EE_RDY ; EEPROM Ready Handler reti ;jmp ANA_COMP ; Analog Comparator Handler reti ;rjmp TWSI ; Two-wire Serial Interface Handler reti ;rjmp SPM_RDY ; Store Program Memory Ready Handler vector_3: dec r28 brne exit_9 ldi r28,lo8(value) ldi r25, 0x20 in r24, PORTB; r24=PORTB eor r24, r25; r24 = r24 xor r25 out PORTB, r24; PORTB=r25 exit_9: reti RESET: eor r1, r1 out SREG, r1 ldi r16, hi8(0x045F); top memory for ATmega8 out SPH,r16 ldi r16, lo8(0x045F) out SPL,r16 main: sbi DDRB, PB5; порт PB5 на передачу ldi r24, 0x80 out TIMSK,r24; set TIMSK; enable TIM2_COMP interrupt ldi r24, 0x0F; out TCCR2,r24; set TCCR2; prescaler = 1:1024 and CTC mode ldi r24, 0x64 out OCR2, r24; load in OCR2 compare value ldi r28, lo8(value); sei; loop: rjmp loop; main loop
я здесь немного сократил запись в TCCR2 регистр.
(1<<WGM21) | (1<<SC22) | (1<<SC21) | (1<<SC20)равняется 00001111b или 15.
Компилируем, проверяем, прошиваем:
$ avr-as -mmcu=atmega8 -o timer2.o timer2.asm
$ avr-ld -o timer2.elf timer2.o
$ avr-objdump -S timer2.elf
timer2.elf: file format elf32-avr
Disassembly of section .text:
00000000 <__ctors_end>:
0: 1a c0 rjmp .+52 ; 0x36 <RESET>
2: 18 95 reti
4: 18 95 reti
6: 0f c0 rjmp .+30 ; 0x26 <vector_3>
8: 18 95 reti
a: 18 95 reti
c: 18 95 reti
e: 18 95 reti
10: 18 95 reti
12: 18 95 reti
14: 18 95 reti
16: 18 95 reti
18: 18 95 reti
1a: 18 95 reti
1c: 18 95 reti
1e: 18 95 reti
20: 18 95 reti
22: 18 95 reti
24: 18 95 reti
00000026 <vector_3>:
26: ca 95 dec r28
28: 29 f4 brne .+10 ; 0x34 <exit_9>
2a: c0 e8 ldi r28, 0x80 ; 128
2c: 90 e2 ldi r25, 0x20 ; 32
2e: 88 b3 in r24, 0x18 ; 24
30: 89 27 eor r24, r25
32: 88 bb out 0x18, r24 ; 24
00000034 <exit_9>:
34: 18 95 reti
00000036 <RESET>:
36: 11 24 eor r1, r1
38: 1f be out 0x3f, r1 ; 63
3a: 04 e0 ldi r16, 0x04 ; 4
3c: 0e bf out 0x3e, r16 ; 62
3e: 0f e5 ldi r16, 0x5F ; 95
40: 0d bf out 0x3d, r16 ; 61
00000042 <main>:
42: bd 9a sbi 0x17, 5 ; 23
44: 80 e8 ldi r24, 0x80 ; 128
46: 89 bf out 0x39, r24 ; 57
48: 8f e0 ldi r24, 0x0F ; 15
4a: 85 bd out 0x25, r24 ; 37
4c: 84 e6 ldi r24, 0x64 ; 100
4e: 83 bd out 0x23, r24 ; 35
50: c0 e8 ldi r28, 0x80 ; 128
52: 78 94 sei
00000054 <loop>:
54: ff cf rjmp .-2 ; 0x54 <loop>
$ avr-objcopy --output-target=ihex timer2.elf timer2.hex
$ avrdude -v -patmega8 -carduino -P/dev/ttyUSB0 -b19200 -D -Uflash:w:./timer2.hex:i
avrdude: Version 6.0.1, compiled on Aug 11 2014 at 12:06:25
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2009 Joerg Wunsch
System wide configuration file is "/etc/avrdude.conf"
User configuration file is "/home/flanker/.avrduderc"
User configuration file does not exist or is not a regular file, skipping
Using Port : /dev/ttyUSB0
Using Programmer : arduino
Overriding Baud Rate : 19200
AVR Part : ATmega8
Chip Erase delay : 10000 us
PAGEL : PD7
BS2 : PC2
RESET disposition : dedicated
RETRY pulse : SCK
serial program mode : yes
parallel program mode : yes
Timeout : 200
StabDelay : 100
CmdexeDelay : 25
SyncLoops : 32
ByteDelay : 0
PollIndex : 3
PollValue : 0x53
Memory Detail :
Block Poll Page Polled
Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
eeprom 4 20 128 0 no 512 4 0 9000 9000 0xff 0xff
flash 33 10 64 0 yes 8192 64 128 4500 4500 0xff 0x00
lfuse 0 0 0 0 no 1 0 0 2000 2000 0x00 0x00
hfuse 0 0 0 0 no 1 0 0 2000 2000 0x00 0x00
lock 0 0 0 0 no 1 0 0 2000 2000 0x00 0x00
calibration 0 0 0 0 no 4 0 0 0 0 0x00 0x00
signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00
Programmer Type : Arduino
Description : Arduino
Hardware Version: 2
Firmware Version: 1.18
Topcard : Unknown
Vtarget : 0.0 V
Varef : 0.0 V
Oscillator : Off
SCK period : 0.1 us
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e9307
avrdude: safemode: lfuse reads as 0
avrdude: safemode: hfuse reads as 0
avrdude: reading input file "./timer2.hex"
avrdude: writing flash (86 bytes):
Writing | ################################################## | 100% 0.10s
avrdude: 86 bytes of flash written
avrdude: verifying flash memory against ./timer2.hex:
avrdude: load data flash data from input file ./timer2.hex:
avrdude: input file ./timer2.hex contains 86 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 0.08s
avrdude: verifying ...
avrdude: 86 bytes of flash verified
avrdude: safemode: lfuse reads as 0
avrdude: safemode: hfuse reads as 0
avrdude: safemode: Fuses OK (H:FF, E:00, L:00)
avrdude done. Thank you.