Timer0 v rezime CTC (Bascom-ATMEGA644P)

Nevím, co všechno potřebuješ, protože z diskuze nejsem úplně moudrej. Napsal jsem Ti příklad, jak generovat pulzy pomocí časovače. Jen mám trošku obavu, jestli dokážeš dosahnout takovouhle přesnost v BASCOMu.

;==============================================================================
; Generování impulzu pro tyristory
; Délka impulzu 1-128 us
; Odchylka délky pulzu max. +4 takty MCU v případě,
; že nebude použito žádné jiné přerušení
; Fmcu = 16 MHz 
;==============================================================================

#include <m644def.inc>

;------------------------------------------------------------------------------
; Definice registrových proměnných

.def Temp=R16				; Pomocná proměnná
.def Temp2=R17				; Pomocná proměnná pro přerušení


;------------------------------------------------------------------------------
; Definice konstant

#define Tyristor_PORT portb
#define Tyristor0 0
#define Tyristor1 1
#define Tyristor2 2
#define Tyristor3 3
#define Tyristor4 4
#define Tyristor5 5
#define Tyristor6 6
#define Tyristor7 7

#define Delka_Pulzu_us 1	; Požadovaná délka impulzu v us (1-128)

;------------------------------------------------------------------------------
; Definice paměťových proměnných

.dseg
SRAM_Tyristory: .byte 1		; Sem se ukládá, které tyristory se mají zapnout
SRAM_Delka_Pulzu: .byte 1	; Sem se ukládá vypočtená hodnota pro požadovanou délku impulzu



;------------------------------------------------------------------------------
; Tabulka skoků

.cseg
.org 0

	rjmp RESET		; Reset Handler
	rjmp INT0_INT		; External Interrupt Request 0
	rjmp INT1_INT		; External Interrupt Request 1
	rjmp INT2_INT		; External Interrupt Request 2
	rjmp PCINT0_INT		; Pin Change Interrupt Request 0
	rjmp PCINT1_INT		; Pin Change Interrupt Request 1
	rjmp PCINT2_INT		; Pin Change Interrupt Request 2
	rjmp PCINT3_INT		; Pin Change Interrupt Request 3
	rjmp WDT_INT		; Watchdog Time-out Interrupt
	rjmp TIMER2_COMPA	; Timer/Counter2 Compare Match A
	rjmp TIMER2_COMPB	; Timer/Counter2 Compare Match B
	rjmp TIMER2_OVF		; Timer/Counter2 Overflow
	rjmp TIMER1_CAPT	; Timer/Counter1 Capture Event
	rjmp TIMER1_COMPA	; Timer/Counter1 Compare Match A
	rjmp TIMER1_COMPB	; Timer/Counter1 Compare Match B
	rjmp TIMER1_OVF		; Timer/Counter1 Overflow
	rjmp TIMER0_COMPA	; Timer/Counter0 Compare Match A
	rjmp TIMER0_COMPB	; Timer/Counter0 Compare match B
	rjmp TIMER0_OVF		; Timer/Counter0 Overflow
	rjmp SPI_STC		; SPI Serial Transfer Complete
	rjmp USART0_RX		; USART0 Rx Complete
	rjmp USART0_UDRE	; USART0 Data Register Empty
	rjmp USART0_TX		; USART0 Tx Complete
	rjmp ANALOG_COMP	; Analog Comparator
	rjmp ADC_INT		; ADC Conversion Complete
	rjmp EE_READY		; EEPROM Ready
	rjmp TWI_INT		; two-wire Serial Interface
	rjmp SPM_READY		; Store Program Memory Ready
	rjmp USART1_RX		; USART1 Rx Complete
	rjmp USART1_UDRE	; USART1 Data Register Empty
	rjmp USART1_TX		; USART1 Tx Complete
	rjmp TIMER3_CAPT	; Timer/Counter3 Capture Event
	rjmp TIMER3_COMPA	; Timer/Counter3 Compare Match A
	rjmp TIMER3_COMPB	; Timer/Counter3 Compare Match B
	rjmp TIMER3_OVF		; Timer/Counter3 Overflow

;------------------------------------------------------------------------------
; Rutiny obsluhy přerušení

TIMER3_COMPA:				; Timer/Counter3 Compare Match A
; Z nějakého záhadného důvodu v simulátoru AVR Studia skáče Timer 0 Compare Match A
; na adresu Timer 3 Compare Match A
; Proto je tu i tento jinak nepoužitý vektor přerušení, který má pouze MCU ATmega1284.
; MCU na vyzkoušení nemám.

TIMER0_COMPA:				; Timer/Counter0 Compare Match A
	ldi Temp2, 0
	out Tyristor_PORT, Temp2	; Vypnout všechny tyristory
	out TCCR0B, Temp2	; Zastavit čítač
	out TCNT0, Temp2	; Vynulovat čítač
	reti

;------------------------------------------------------------------------------
; Nepoužité vektory přerušení

INT0_INT:					; External Interrupt Request 0
INT1_INT:					; External Interrupt Request 1
INT2_INT:					; External Interrupt Request 2
PCINT0_INT:					; Pin Change Interrupt Request 0
PCINT1_INT:					; Pin Change Interrupt Request 1
PCINT2_INT:					; Pin Change Interrupt Request 2
PCINT3_INT:					; Pin Change Interrupt Request 3
WDT_INT:					; Watchdog Time-out Interrupt
TIMER2_COMPA:				; Timer/Counter2 Compare Match A
TIMER2_COMPB:				; Timer/Counter2 Compare Match B
TIMER2_OVF:					; Timer/Counter2 Overflow
TIMER1_CAPT:				; Timer/Counter1 Capture Event
TIMER1_COMPA:				; Timer/Counter1 Compare Match A
TIMER1_COMPB:				; Timer/Counter1 Compare Match B
TIMER1_OVF:					; Timer/Counter1 Overflow

TIMER0_COMPB:				; Timer/Counter0 Compare match B
TIMER0_OVF:					; Timer/Counter0 Overflow
SPI_STC:					; SPI Serial Transfer Complete
USART0_RX:					; USART0 Rx Complete
USART0_UDRE:				; USART0 Data Register Empty
USART0_TX:					; USART0 Tx Complete
ANALOG_COMP:				; Analog Comparator
ADC_INT:					; ADC Conversion Complete
EE_READY:					; EEPROM Ready
TWI_INT:					; two-wire Serial Interface
SPM_READY:					; Store Program Memory Ready
USART1_RX:					; USART1 Rx Complete
USART1_UDRE:				; USART1 Data Register Empty
USART1_TX:					; USART1 Tx Complete
TIMER3_CAPT:				; Timer/Counter3 Capture Event
TIMER3_COMPB:				; Timer/Counter3 Compare Match B
TIMER3_OVF:					; Timer/Counter3 Overflow

            reti

;------------------------------------------------------------------------------
; Hlavní program - inicializace MCU

RESET:  
	ldi Temp,high(RAMEND)
	out SPH,Temp
	ldi Temp,low(RAMEND)
	out SPL,Temp


	ldi XH, high(SRAM_SIZE)
	ldi XL, low(SRAM_SIZE)
	ldi YH, high(SRAM_START)
	ldi YL, low(SRAM_START)
	clr temp
Init_RAM:
	st Y+, temp
	sbiw XL, 1
	brne Init_RAM

	clr r0
	clr r1
	clr r2
	clr r3
	clr r4
	clr r5
	clr r6
	clr r7
	clr r8
	clr r9
	clr r10
	clr r11
	clr r12
	clr r13
	clr r14
	clr r15
	clr r16
	clr r17
	clr r18
	clr r19
	clr r20
	clr r21
	clr r22
	clr r23
	clr r24
	clr r25
	clr r26
	clr r27
	clr r28
	clr r29
	clr r30
	clr r31
	out SREG, r0

; Hlavní program - konec inicializace MCU

;------------------------------------------------------------------------------
; Hlavní program

	rcall Inicializace_Citace_A_Portu
	sei								; Povolit přerušení

	Temp, (1<<Tyristor0)|(1<<Tyristor3) ; Zapnout tyristory 1 a 3
	sts SRAM_Tyristory, Temp		; Sem se ukládá, které tyristory se mají zapnout


	ldi Temp, Delka_Pulzu_us*2-1	; Vypočítat hodnotu OCIE0A pro požadovanou délku impulzu
	sts SRAM_Delka_Pulzu, Temp		; Sem se ukládá hodnota OCIE0A pro požadovanou délku impulzu

	rcall Start_Impulzu

Hlavni_Smycka:

	rjmp Hlavni_Smycka

;------------------------------------------------------------------------------
Inicializace_Citace_A_Portu:
	ldi Temp, 0
	out TCNT0, Temp				; Vynulovat čítač
	out TCCR0A, Temp			; Nastavit normální režim čítače
	out TCCR0B, Temp			; Nastavit normální režim čítače, NESPOUŠTĚT ČÍTAČ
	ldi Temp, 255
	out TIFR0, Temp				; Pro jistotu vynulovat všechny příznaky přerušení
	ldi Temp, (1<<OCIE0A)
	sts TIMSK0, Temp			; Povolit přerušení při překročení hodnoty v OCIE0A
	ldi Temp, 0
	out Tyristor_PORT, Temp		; Vynulovat port (pokud jsou tyristory zapínané log. 1)
	ldi Temp, 255
	out ddrb, Temp				; Nastavit port jako výstupní
	ret

;------------------------------------------------------------------------------
Start_Impulzu:
	lds Temp, SRAM_Delka_Pulzu	; Načíst z RAM délku impulzu
	out OCR0A, Temp
	ldi Temp, (1<<CS01)
	out TCCR0B, Temp			; Spustit čítač - Prescaler=8

; Počkat 4 takty mcu kvůli synchronizaci
	nop
	nop
	nop
	nop

	lds Temp, SRAM_Tyristory	; Načíst z RAM které tyristory se mají zapnout
	out Tyristor_PORT, Temp		; Zapnout tyristory
	ret
;------------------------------------------------------------------------------

Prográmek je psaný v AVR Studiu.

Před dalším zapnutím jiného nebo jiných tyristorů musíš počkat, než dojde k vypnutí těch zapnutých - neboli než se ukončí cyklus. U délky pulzu 1 us to není až takový problém, protože od zapnutí do vypnutí procesor vykoná max. 16 instrukcí (včetně instrukce ret a části přerušovacího vektoru do vypnutí tyristorů), takže času na ostatní věci je opravdu minimum, ale u delších impulzů by bylo potřeba tohle ještě ošetřit.

Šlo by to třeba takhle :[code]
Start_Impulzu:
in Temp, Tyristor_PORT ; Načíst stav portu tyristorů
tst Temp ; Test na nulu
brne Start_Impulzu ; Není-li port nulový, opakovat

     lds Temp, SRAM_Delka_Pulzu   ; Načíst z RAM délku impulzu
     out OCR0A, Temp
     ldi Temp, (1<<CS01)
     out TCCR0B, Temp         ; Spustit čítač - Prescaler=8

; Počkat 4 takty mcu kvůli synchronizaci
nop
nop
nop
nop

     lds Temp, SRAM_Tyristory   ; Načíst z RAM které tyristory se mají zapnout
     out Tyristor_PORT, Temp      ; Zapnout tyristory
     ret

;------------------------------------------------------------------------------ [/code]

Zavoláš rutinu pro spuštění pulzu a ta čeká, dokud je některý z tyristorů zapnutý.