ATmage128, TIM0_OVF, hodiny

Dobrý den,
Mám na Vás prosbu. Dostal se mi pod ruku ATmega128, a tak se mi naskytla možnost si s ním trochu pohrát, než ho budu muset vrátit. Jsem student, proto nemám moc financí, abych si tento modul mohl pořídit, a i kdybych si pořídil, tak bych ho dlouho nepoužíval. Mikroprocesorová technika je pěkná věc, ale mě osobně více baví mechanika a elektronika více(hlavně ve škole programování mikroprocesorů nemám rád.) Mám zadaný domácí úkol, který musím v úterý odprezentovat na tomto modulu při jeho vrácení. Rád bych od Vás dostal rady, popřípadě napsaný programu. Mým úkolem je zobrazit čas na tomto displeji. Nemusí být použitá klávesnice! Displej je EL1602A 2 řádky a 16 sloupců (dále jen 2x16). Vedle procesoru ATmega128 je IO(zda se nemýlím) HD44780, tím by měl být řízený displej. K času. Nemusí ukazovat aktuální čas. Má ukazovat někde na displeji sekundy a minuty(hodiny jsou bonus). Při 60 sekundách musí dojít ke změně minut a sekundy se musí vynulovat. Po překročení 60 minut se musí minuty i sekundy vynulovat…Program musí být napsaný v programovacím jazyce assembler. Já osobně používám AVR studio 4 a PKDesign AVR ISP programmer. Bylo mi ve škole řečeno, že tento mikroprocesor je na frekvenci 14745600Hz. Povedlo se mi zobrazit na displeji slovíčko pokus, které bliká(zdrojový kód níže, abych jen nežádal a sám se nesnažil něco udělat.)Jenže můj hlavní kámen úrazu je to, že umím naprogramovat pouze pauzu odčítaním proměnných, a ta není přesná. Máme mít řešenou pauzu pomocí TIM0_OVF, kterému ani po přečtení datasheetu, ani jiných stránek, které to yvsvětlují nepochopil. Takže k jádru věci. Rád bych vás poprosil o podprogram, který mi zařídí tuto pauzu, popř. program celý, jestli by byl někdo tak laskavý. Mnoho podprogramů jsem použil od prácí mých spolužáků, takže někde ani nevím, proč to tam je.

Děkuji předem všem, kdo mi podají pomocnou ruku! Děkuji moc!

Kod vypsání na displej pokus s nepřesným zpožděním odečítaním proměnných:
;Ladislav
;15.4.2016

.NOLIST
.include “m128def.inc”
.LIST

.DEF NulReg = r0
.DEF Stat = r1
.DEF Tmp = r16
.DEF Dl1 = r17
.DEF Dl2 = r18
.DEF LCDreg = r19

;datový segment
.DSEG
.ORG 0x0110
Buf4s: .byte 0x04

;programový segment
.CSEG
.ORG 0x0000
jmp Reset ;

Reset: ldi Tmp, low(RAMEND)
out SPL, Tmp
ldi Tmp, high(RAMEND)
out SPH, Tmp
ldi Tmp, 0xFF
out DDRD, Tmp
clr NulReg
out PortD, NulReg
out PortB, NulReg
jmp main

; nastaveni portu B na vystupni
PB_O: ldi Tmp, 0xFF
out DDRB, Tmp
ret

pause:
push Tmp
ldi Tmp, 200
pau1: call pause_500us
dec Tmp
brne pau1
pop Tmp
ret

;pauza 1s
pause_1s:
rcall pause_100ms
rcall pause_100ms
rcall pause_100ms
rcall pause_100ms
rcall pause_100ms
rcall pause_100ms
rcall pause_100ms
rcall pause_100ms
rcall pause_100ms
rcall pause_100ms
ret

; pauza 100ms
pause_100ms:
rcall pause_10ms
rcall pause_10ms
rcall pause_10ms
rcall pause_10ms
rcall pause_10ms
rcall pause_10ms
rcall pause_10ms
rcall pause_10ms
rcall pause_10ms
rcall pause_10ms
ret

; pauza 10ms
pause_10ms:
rcall pause_1ms
rcall pause_1ms
rcall pause_1ms
rcall pause_1ms
rcall pause_1ms
rcall pause_1ms
rcall pause_1ms
rcall pause_1ms
rcall pause_1ms
rcall pause_1ms
ret

; pauza 1ms
pause_1ms:
rcall pause_500us
rcall pause_500us
ret

; pauza 500us
pause_500us:
ldi Dl1, 10 ; Nastav konstanty pro 05 ms
dly2: ldi Dl2, 240
dly1: dec Dl2 ; Dekrementuj Dl2 dokud neni nulovy
brne dly1
dec Dl1 ; Dekrementuj Dl1 dokud neni nulovy
brne dly2
ret

; vyslani znaku na displej
LCD_znak:
push Tmp
in Tmp, PortD
andi Tmp, 0b11100000 ; E=RW=RS=0
ori Tmp, 0b00000100 ; RS=1…data
out PortD, Tmp
nop
out PortB, LCDreg ;PortB=data
nop
ori Tmp, 0b00010000 ;E=1 potv. plat. dat
out PortD, Tmp
nop
andi Tmp, 0b11100000 ;E=0 zrus. plat. dat
out PortD, Tmp
call pause_1ms
pop Tmp
ret

;Ridici signaly pro LCD displej:
; RS…PORTD.2
; R/W…PORTD.3
; E…PORTD.4
;
;Vyslani povelu na displej
LCD_povel:
push Tmp
in Tmp, PortD
;E=0…zrus plat.dat
;RW=0…Write
;RS=0…povel
andi Tmp, 0b11100000
out PortD, Tmp
nop
out PortB, LCDreg;PortB=povel
nop
ori Tmp, 0b00010000;E=1(potvrdit platnost dat)
out PortD, Tmp
nop
andi Tmp, 0b11100000;E=0(zrusit platnost dat)
out PortD, Tmp
call pause_1ms
pop Tmp
ret

;inicializace displeje
Disp_init:
call PB_O
call pause_500us
ldi LCDreg, 0b00111000 ; Set, 8bit data, 2linky, 5x7
call LCD_povel
call pause_1ms
ldi LCDreg, 0b00000010 ; kurzor na prvni pozici
call LCD_povel
call pause_1ms
ldi LCDreg, 0b00000001 ; vymazat displej
call LCD_povel
call pause_1ms
ldi LCDreg, 0b00000110 ; posouvani kurzoru
call LCD_povel
call pause_1ms
ldi LCDreg, 0b00001110 ; zapnut displej, kurzor a blikani
call LCD_povel
call pause_1ms
ret

;pauza
Cekej:
call pause
ret

;vstupni retezec na displej
Print_horni_radek:
call Disp_init
call pause
prnt2:
lpm LCDreg, Z+
cpi LCDreg, 0
brne prnt1
ret
prnt1:
call LCD_znak
rjmp prnt2

Print_dolni_radek:
ldi LCDreg, 0b11000000;nast. adr. DD RAM 0x40(adresa 1.bajtu ve 2.radku)
call LCD_povel
rjmp prnt2

;programova smycka
main:
ldi ZL, low(Text1 * 2)
ldi ZH, high(Text1 * 2)
call Print_horni_radek
ldi ZL, low(Text2 *2)
ldi ZH, high(Text2 * 2)
call Print_dolni_radek
call pause_1s

ldi		ZL, low(Text3 *2)
ldi		ZH, high(Text3 * 2)
call	Print_horni_radek
ldi		ZL, low(Text4 * 2)
ldi		ZH, high(Text4 * 2)
call	Print_dolni_radek
call	pause_1s

rjmp	main

Text1: .DB "pokus 4 "
.DB 0x00, 0x00
Text2: .DB "pokus 3 "
.DB 0x00, 0x00
Text3: .DB "pokus 2 "
.DB 0x00, 0x00
Text4: .DB "pokus "
.DB 0x00, 0x00


Příloha mi zlobila, takže přikládám odkaz na ten displej:
imgur.com/s1ied6o

Na to, aby ses pomocí 8-bitového čítače 0 dostal s časem na 1 sekundu, musíš dělit frekvenci krystalu tak dlouho, až Ti vyjde 1.

Jeden z možných postupů :

Vezmi frekvenci 14745600Hz a vyděl počtem cyklů 8-bitového čítače : 14745600/256 = 57600 Hz => je to celé číslo, můžeš pokračovat.
Varianta 1: Necháš prescaler 1 a ve dvojregistru (XH:XL, YH:YL, ZH:ZL a myslím, že se dá použít i dvojice registrů R25:R24) nastavíš hodnotu 57600 a v přerušení odečítáš. Jakmile dosahe nuly, přičteš 1 sek. k času a nastavíš dvojregistr znova na 57600.

Varianta 2:
Zkusíš vydělit toto číslo prescalery a pokusíš se dostat do rozmezí 1-255, aby sis vystačil s 8-bitovým registrem a nemusel používat dvojregistr.
57600/8 = 7200
57600/32 = 1800
57600/64 = 900
57600/128 = 450
57600/256 = 225 => Tenhle prescaler vyhovuje a použiješ ho.
57600/1024 = 56,25 => desetinné číslo nelze použít.

Nastavíš tedy čítač na prescaler 256 a další postup je shodný s variantou 1 s tím rozdílem, že stačí jeden registr a přednastavuje se na hodnotu 225. V tuhle chvíli máš frekvenci 1 sekunda.

Takže přidám do kódu
pauza_1s_timer
ldi Tmp, 0b00000110 ;/256 prescaler
out TCCR0, Tmp
ret
in Tmp, TIMSK
ori Tmp, 0xE1 ; <—Tady napsat 225? E1=225
out TIMSK, Tmp
sei
reti

Bohužel při mé hlouposti dále nechápu, jak se dále to číslo 225 mám využít, kde ho napsat(jestli ne tam, kde jsem naznačil, a jak ten timer použiji. U odečítaní proměnných zavolám a jen se dekrementuje číslo. Tohle je pro mne španělská vesnice.
Každopádně děkuji za rychlou odpověď.
PS:Tyto části kódu jsem pouze upravoval, které jsem našel na internetu k uplně jinému programu. Doufám, že jsem to, co potřebuji.

Program, který jsem dal k mému prvnímu příspěvku jsem použil, ale v hlavní smyčce místo pauzu na odečítaní proměnných jsem zkusil ten program, který, jsem napsal jako citaci
pauza_1sOVF:
in Tmp, TIMSK
ori Tmp, 0xE1
out TIMSK, Tmp
ldi Tmp, 0b00000110
out TCCR0, Tmp
ret
sei
reti

Displej rychle problíkává uvedený text v tabulce, že nelze pozorovat zrakem, zda se jedná o pokus 2 nebo pokus 4. Jaks říkal, něco se tam musí ještě nejspíše dodat. Potom samotný čas na displej budu psát nejspíše obrovskou tabulkou konstant, na kterou se budu odkazovat.(obrovská smyčka), ale to je maličkost. Prvně se musí vyřešit tenhle delay.

To, co jsem Ti popsal není čekací smyčka, ale nastavení čítače. Pak musíš povolit přerušení TIM0OVF (nebo tak nějak) a na přerušovací vektor pověsíš obsluhu přerušení (na to je tato úloha zaměřena - zpracování přerušení). V této obsluze odečteš od nějakého registru jedničku a pokud dosahne nuly, nastavíš ho na 225 a nastavíš příznak “přičti vteřinu”.

V hlavní smyčce budeš kontrolovat příznak “přičti vteřinu”. Pokud bude příznak nastavený, tak jej vynuluješ, přičteš k času vteřinu, přepíšeš displej a vrátíš se do smyčky.

Tu vteřinu můžeš případně přičíst i v přerušení, ale zápis na displej trvá dlouho na to, aby mohl být v přerušení - to je druhá věc, kterou asi chce úloha ukázat - obsluha přerušení MUSÍ být co možná nejkratší. Samozřejmě nesmíš zapomenout uložit stav SREG na vstupu do obsluhy a na konci jej zase obnovit.

Ne, promíň, vůbec nechápu. Mohl bys mi prosím tě tu dát část zdrojového kódu prosím?

Myslím, že jsem to napsal dostatečně srozumitelně. Ty ses ani nenamáhal podívat se do datasheetu, abys zjistil, jak se nastavuje přerušení pro TOV0 a do registru TIMSK cpeš nějaký nesmyslný číslo. Pokud máte za úkol použít časovač na odčasování vteřiny, pak jste se to museli probírat. Nasměruju Tě a poradím, ale nebudu tady dělat za nikoho domácí úkol. Na to musí Tvoje vědomosti stačit.

Napovím :
Podívej se na vektory přerušení, ať víš, kam dát obsluhu od přerušení TOV0.
Podívej se, co znamenají jednotlivé bity TIMSK

další kroky budou následovat…

Ohledně datasheetu - stáhni si datasheet, který není označen jako “Summary” (Je to hned na první straně pod označením procesoru). To je jenom shrnutí a těch informací je tam málo. Celý datasheet má cca 6,5MB a skoro 400 stran a obsahuje i seznam registrů a instrukcí. Všechny registry jsou tam dost detailně popsány.

Napíšu Ti, jak má program vypadat :

[code]0000: Skok na RESET

//------------------------
Tabulka skoků na přerušovací rutiny (= tabulka přerušovacích vektorů) - Klidně vykopíruj z datasheetu.
//------------------------
Přerušovací rutiny - opět můžeš vykopírovat z datasheetu
Vynech akorát TIM0_TOV
reti
//------------------------
TIM0_TOV:
Uschovat SREG
Odečít 1 od registru pro odčasování 1 sek.
Když není nula, skok na konec TOV
Přednastavit znovu na 225
Nahodit příznak pro přičtení vteřiny
konec TOV:
Obnovit SREG
reti
//------------------------
RESET:
Nastavit IO piny
Nastavit časovač
Inicializovat displej
Přednastavit hodnoty registrů
Povolit přerušení
MAIN:
Zkontrolovat příznak pro přičtení vteřiny
Ne - jdi na MAIN
Vyresetovat příznak pro přičtení vteřiny
Přičíst vteřinu
Vytisknout čas na displej
Jdi na MAIN
//------------------------[/code]

Tady máš kostru programu - je to téměř instrukce po instrukci…

Děkuji moc za rychlou odpověď. S tímhle už bych to měl zmáknout. :slight_smile: