Зеркало сайта: vivacious-stockings-frog.cyclic.app

ATmega8: таймер на компараторе, CTC режим

разделы: AVR , дата: 30 июля 2015г.

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