časovač

Tak nějak nevím, kde mám začít a jestli si ze mě neděláš legraci (klid - to chce čas - žádnej učenej z nebe nespadnul, ale občas se čertit musím :smiling_imp:) …

Psal jsem Ti, že psát čísla přímo do programu, že je nepřehledné a nějaké následné korekce jsou jen těžko řešitelné s obrovskou možností zavlečení chyby a Ty tam napíšeš tohle :

[code] .
.
LDI XH,8
LDI XL,1 ;nastavení zpoždění cca 50ms
.
.

.
.
LDI XH,0
LDI XL,2 ;nastavení zpoždění cca 50us
.
.

.
.
LDI XH,0
LDI XL,2 ;nastavení zpoždění cca 50us
.
.

.
.
LDI XH,0
LDI XL,80 ;nastavení zpoždění cca 2ms
.
.[/code]

Co třeba takhle (všimni si, že nejsou ani nutné komentáře…) :

[code].equ Wait50ms = 0x0801
.equ Wait2ms = 0x0050
.equ Wait50us = 0x0002

.
.
LDI XH, High(Wait50ms)
LDI XL, Low(Wait50ms)
.
.

.
.
LDI XH, High(Wait50us)
LDI XL, Low(Wait50us)
.
.

.
.
LDI XH, High(Wait50us)
LDI XL, Low(Wait50us)
.
.

.
.
LDI XH, High(Wait2ms)
LDI XL, Low(Wait2ms)
.
.[/code]

To samé tady :

LDI TMP,02 ;set OCIE0 OUT TIMSK,TMP ;TCNT0=OCR0 LDI TMP,0b00001010 ;set WGM01,CS01 OUT TCCR0,TMP

Podívej se do prvního příspěvku, jak by to mělo vypadat. Když budeš přecházet na jiný procesor, tak může se stát, že čísla budeš muset přepsat. Když to tam budeš mít pomocí názvů jednotivých bitů (např. OCIE0), tak se to upraví samo. Navíc když se bude tento bit v jiném procesoru jmenovat jinak, napíše Ti chybu. Když tam budeš mít číslo, tak se může stát, že strávíš mraky času hledáním, proč Ti to nechodí.


Přerušení :

Vybírat do programu jeden přerušovací vektor ze všech dostupných sice spoří místo, ale přesto se to (většinou) nedělá. Je to hlavně kvůli tomu, kdyby sis náhodně povolil přerušení od jiného zdroje, než od toho, který používáš. Z tohoto důvodu by bylo lepší, kdyby sis místo tohoto :

[code] .ORG 0
RJMP RESET

.ORG OC0addr
RJMP PRERUSENI
[/code]

upravil a použil kód z datasheetu (to už je upravený kód pro ATmega16) :

[code].org 0
jmp RESET ; Reset Handler
jmp EXT_INT0 ; IRQ0 Handler
jmp EXT_INT1 ; IRQ1 Handler
jmp TIM2_COMP ; Timer2 Compare Handler
jmp TIM2_OVF ; Timer2 Overflow Handler
jmp TIM1_CAPT ; Timer1 Capture Handler
jmp TIM1_COMPA ; Timer1 CompareA Handler
jmp TIM1_COMPB ; Timer1 CompareB Handler
jmp TIM1_OVF ; Timer1 Overflow Handler
jmp TIM0_OVF ; Timer0 Overflow Handler
jmp SPI_STC ; SPI Transfer Complete Handler
jmp USART_RXC ; USART RX Complete Handler
jmp USART_UDRE ; UDR Empty Handler
jmp USART_TXC ; USART TX Complete Handler
jmp ADC_INT ; ADC Conversion Complete Handler
jmp EE_RDY ; EEPROM Ready Handler
jmp ANA_COMP ; Analog Comparator Handler
jmp TWSI ; Two-wire Serial Interface Handler
jmp EXT_INT2 ; IRQ2 Handler
jmp TIM0_COMP ; Timer0 Compare Handler
jmp SPM_RDY ; Store Program Memory Ready Handler

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

EXT_INT0: ; IRQ0 Handler
EXT_INT1: ; IRQ1 Handler
TIM2_COMP: ; Timer2 Compare Handler
TIM2_OVF: ; Timer2 Overflow Handler
TIM1_CAPT: ; Timer1 Capture Handler
;TIM1_COMPA: ; Timer1 CompareA Handler - Použitý vektor je zapoznámkovaný, aby překladač nehlásil duplicitu návěští.
TIM1_COMPB: ; Timer1 CompareB Handler
TIM1_OVF: ; Timer1 Overflow Handler
TIM0_OVF: ; Timer0 Overflow Handler
SPI_STC: ; SPI Transfer Complete Handler
USART_RXC: ; USART RX Complete Handler
USART_UDRE: ; UDR Empty Handler
USART_TXC: ; USART TX Complete Handler
ADC_INT: ; ADC Conversion Complete Handler
EE_RDY: ; EEPROM Ready Handler
ANA_COMP: ; Analog Comparator Handler
TWSI: ; Two-wire Serial Interface Handler
EXT_INT2: ; IRQ2 Handler
TIM0_COMP: ; Timer0 Compare Handler
SPM_RDY: ; Store Program Memory Ready Handler
reti ; Return from accidentaly called interrupt

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

TIM1_COMPA: ; Timer1 CompareA Handler
…Tady je to Tvoje přerušení…
reti
[/code]

Důležitá poznámka - vždy musíš použít kód z datasheetu procesoru, který programuješ. Jednak nejsou vektory u všech procesorů stejné a jednak jenom některé procesory mají JMP. Některé mají RJMP. Rozdíl mezi nima je v tom, že RJMP zabírá v programové paměti 1 word, JMP zabírá 2 wordy. Například ATmega8 má RJMP a ATmega16 JMP - a to i přesto, že jsou prakticky z jedné řady. Nejmarkantnější je to u řady ATmega48/88/168/328. U této řady jsou názvy a počet vektorů shodné. Jenže ATmega48 a ATmega88 mají vektory od sebe vzdálené jen 1 word (RJMP adresa), ale ATmega168 a ATmega328 mají vektory od sebe vzdálené 2 wordy - JMP adresa (nebo RJMP adresa + NOP). Je to kvůli adresaci. Zatímco RJMP může skákat ±2K wordů - tedy procesory se 4KB nebo 8KB zvládne naadresovat celé, pro adresaci 16KB (8K wordů) už to nestačí. Proto u těchto procesorů tabulka vektorů počítá s instrukcí JMP adresa - tedy 2 wordy (4 byty).


A teďka ke Tvému provedení časování :

Použít 40Khz (25us) čítač kvůli načasování delaye v programu, jehož přesnost není kritická je sice poněkud nezvyklé, nicméně proč ne. V tomto provedení ale musíš (kromě časové náročnosti) počítat s odchylkou až -25us od nastaveného času (pozice Timeru v okamžiku zápisu do “časovacího dvojregistru”). Může se totiž stát, že zapíšeš 2 (50us) do XH:XL takt před přetečením timeru a v tu chvíli máš z 50us jenom 25us, protože první odečtení proběhne těsně po nastavení hodnoty. Program obsluhy přerušení by měl vypadat asi nějak takhle :

TIM1_COMPA: ; Timer1 CompareA Handler in ShovavaciRegistr, SREG sbiw XH:XL, 1 out SREG, ShovavaciRegistr reti

Pak bych napsal čekací smyčku nějak takhle :

Delay: ; Čekací smyčka movw Y, X mov Temp, YH or Temp, YL brne Delay ret

A volání by pak mohlo vypadat takhle :

. . LDI XH, High(Wait50ms) LDI XL, Low(Wait50ms) RCALL Delay . .

Nebo ještě lépe

. . LDI YH, High(Wait50ms) LDI YL, Low(Wait50ms) MOVW X, Y RCALL Delay . .

Instrukce MOVW pro čtení a zápis z/do časovacího registru XH:XL je použita proto, aby nemohlo dojít k tomu, že by v XH:XL byla zapsána jenom polovina dat. A to v případě, že by se stalo, že provedeš instrukci LDI XH, High(Wait50ms) a v tuto chvíli dojde k přerušení. Pokud byla v XL zrovna 0, pak se v přerušení provede dekrementace XH:XL (0x0800-0x0001=0x07FF) a po návratu z přerušení by se provedla instrukce LDI XL, Low(Wait50ms). V tuto chvíli by v XH:XL nebyla hodnota 0x0801, ale 0x0701 - a to je velký rozdíl. V krajním případě, pokud by navíc došlo k dopočítání do 0 a nastavil by se příznak Z, pak by se v (případě Tvého provedení) čekání vůbec neprovedlo.

Další (a asi ještě lepší) variantou by byl následující zápis, používající ProgramStatusRegistr (vyhradíš si registr a používáš ho na indikaci různých stavů a probíhajících akcí v programu) :

TIM1_COMPA: ; Timer1 CompareA Handler in ShovavaciRegistr, SREG sbrs ProgramStatusRegistr, PSR_DelayInAction rjmp TIM1_COMPA_Exit sbiw XH:XL, 1 brne TIM1_COMPA_Exit cbr ProgramStatusRegistr, (1<<PSR_DelayInAction) TIM1_COMPA_Exit: out SREG, ShovavaciRegistr reti

Čekací smyčka by pak mohla být jednodušší :

Delay: ; Čekací smyčka sbr ProgramStatusRegistr, (1<<PSR_DelayInAction) DelayLoop: sbrc ProgramStatusRegistr, PSR_DelayInAction rjmp DelayLoop ret

A volání :

. . LDI XH, High(Wait50ms) LDI XL, Low(Wait50ms) RCALL Delay . .

Výhodou je, že když to uděláš takhle, tak máš XH:XL volné k běžnému použití a nemusíš řešit nedělitelnost zápisu hodnoty do XH:XL, protože přerušení s ní začne pracovat až v okamžiku, kdy nastavíš bit PSR_DelayInAction v registru ProgramStatusRegistr. V tuto chvíli s XH:XL zase nepracuješ v hlavním programu, protože běží čekací smyčka. XH:XL je blokované na odečítání delaye pouze v případě, že je nastavený bit PSR_DelayInAction v registru ProgramStatusRegistr. Těch možností provedení je nepřeberné množství, ale tohle by byly asi dvě nejjednodušší a na rozdíl od toho Tvého řešení i čisté a korektní.

Když jsem psal, že obsluha přerušení musí být co nejkratší, to ještě neznamená, že tam smí být maximálně pár instrukcí, nicméně v rutině obsluhující přerušení by nemělo být žádné čekání. Pokud by bylo nutné na něco čekat, je třeba nastavit si nějaký příznak a přerušení ukončit a v příštím přerušení dokončit rozdělanou práci = rozfázovat si to, co potřebuješ udělat. Ale tímhle se zatím zabývat nemusíš, to přijde na řadu někdy později.