Второй 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.