Witam. Robię miernik częstotliwości na ATmega8 z wyświetlaniem na LCD HD44780 w trybie 4-bit (mam zrobiony prototyp). Opanowałem już wyświetlanie (z obsługą BF), ale teraz przystawiło mnie z pomiarem liczby impulsów zliczanych na wejściu T1 mikrokontrolera. Wynik co prawda jest wyświetlany, lecz jest on niestabilny w taki sposób, że skaczą 2 ostatnie cyfry wyniku. Odświeżanie to czas 0,64s. W tym czasie mikrokontroler zlicza impulsy w 16-bitowym TC1 i pomocniczo w rejestrze Cnt w przerwaniu od TC1. Czas bramkowania jest generowany przez TC0 (odpowiednia liczba pustych okrążeń i precyzyjnie dobrany czas ostatniego okrążenia) tak aby sumaryczny czas okrążeń wyniósł 0,64s. Oto fragment kodu jaki stworzyłem w asm:
A przerwania wyglądają tak:
Pierwszy listing to fragment kodu ze zdefiniowanymi rejestrami i wstępnymi ustawieniami (przed pętlą główną, w której jak dotąd nie mam nic). Drugi listing to przerwania: T1-inkrementacja licznika pomocniczego po jego przepełnieniu, natomiast T0-zliczanie impulsów zewnętrznych i wyświetlanie na LCD. Nie przedstawiłem tutaj procedur obsługujących LCD bo to akurat działa perfekcyjnie. Problem tkwi w tym, że liczba impulsów zliczonych jest za każdym razem nieco inna (odchyłka to okolo 50 impulsów przy sygnale 1,8 MHz z generatora kwarcowego przy czasie bramkowania 0,64 s). Co może być przyczyną takiego zachowania?
.nolist
.include "m8def.inc"
.list
;-------
.equ LCDport = PortD
.equ LCDpin = PinD
.equ LCDio = DDRD
.equ RS = 6
.equ RW = 4
.equ CE = 7
.equ DB4 = 3
.equ DB5 = 2
.equ DB6 = 1
.equ DB7 = 0
;-------
.def Acc = R16 ; akumulator
.def ref1 = R17 ; rejestr pętli opóźniającej
.def ref2 = R18 ; rejestr pętli opóźniającej
.def Func = R15 ; rejestr funkcji (rezerwa)
.def Reg0 = R3 ; zliczanie - bajt LSB do konwersji
.def Reg1 = R4 ; zliczanie - bajt do konwersji
.def Reg2 = R5 ; zliczanie - bajt MSB do konwersji
;-------
.def Cnt = R9 ; dodatkowy rejestr zliczania impulsów
.def Reg12 = R22 ; rejestr przechowujacy BCD (cyfra2, cyfra1)
.def Reg34 = R23 ; rejestr przechowujacy BCD (cyfra4, cyfra3)
.def Reg56 = R24 ; rejestr przechowujacy BCD (cyfra6, cyfra5)
.def Reg78 = R25 ; rejestr przechowujacy BCD (cyfra8, cyfra7)
.def Flag = R19 ; rejestr-flaga ostatniego okrążenia timera TC0
.def Del = R20 ; rejestr zgrubnego ustawienia czasu bramkowania
.equ DelVal = 143 ; wartosć zgrubnego ustawienia czasu bramkowania (puste okrążenia)
.equ TimTC0 = 35 ; wartość dokładnego ustawienia czasu bramkowania (ostatnie okrążenie)
.cseg
;=============================
.macro tabl ; makro ładujące adres
ldi ZL, low(@0 << 1) ; załaduj do rejestru Z adres tablicy z tekstem
ldi ZH, high(@0 << 1) ; załaduj do rejestru Z adres tablicy z tekstem
.endmacro
;=============================
.org 0 rjmp reset ; External Pin, Power-on Reset, Brown-out Reset, and Watchdog Reset
.org INT0addr reti ; External Interrupt Request 0
.org INT1addr reti ; External Interrupt Request 1
.org OC2addr reti ; Timer/Counter2 Compare Match
.org OVF2addr reti ; Timer/Counter2 Overflow
.org ICP1addr reti ; Timer/Counter1 Capture Event
.org OC1Aaddr reti ; Timer/Counter1 Compare Match A
.org OC1Baddr reti ; Timer/Counter1 Compare Match B
.org OVF1addr rjmp IntT1 ; Timer/Counter1 Overflow
.org OVF0addr rjmp IntT0 ; Timer/Counter0 Overflow
.org SPIaddr reti ; Serial Transfer Complete
.org URXCaddr reti ; USART, Rx Complete
.org UDREaddr reti ; USART Data Register Empty
.org UTXCaddr reti ; USART, Tx Complete
.org ADCCaddr reti ; ADC Conversion Complete
.org ERDYaddr reti ; EEPROM Ready
.org ACIaddr reti ; Analog Comparator
.org TWIaddr reti ; Two-wire Serial Interface
.org SPMRaddr reti ; Store Program Memory Ready
;=============================
reset:
;*********************************************************
; ustawienie portów mikrokontrolera (bit 5 wejście T1)
;*********************************************************
ldi Acc,0b11011111 ; ustawienie potru LCDio jako wyjście
out LCDio,Acc ; 0-wejście, 1-wyjście
ldi Acc,0b11111111
out LCDport,Acc ; podciągnij wszystkie piny do plusa
;*********************************************************
; ustawienie stosu
;*********************************************************
ldi Acc, low(RAMEND)
out SPL, Acc
ldi Acc, high(RAMEND)
out SPH, Acc
;*********************************************************
; ustawienie timerów i przerwań i rejestrów
;*********************************************************
ldi Acc,1<<TOIE1|1<<TOIE0
out TIMSK,Acc ; ustawienie maski przerwań
ldi Del,DelVal ; zgrubny czas bramkowania (przerwania od TC0)
ldi Acc,1<<CS02|0<<CS01|0<<CS00
out TCCR0,Acc ; TC0 clk/256, start
ldi Acc,1<<CS12|1<<CS11|1<<CS10
out TCCR1B,Acc ; TC1 zliczanie zewnętrzne, start
clr Flag ; zeruj rejestr flag
clr Cnt
clr Reg0
clr Reg1
clr Reg2
clr Reg12
clr Reg34
clr Reg56
clr Reg78
A przerwania wyglądają tak:
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Przepełnienie zliczania impulsów wejściowych
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IntT1:
inc Cnt ;zwiększenie najstarszego licznika impulsów
reti
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Zatrzymanie zliczania i wyświetlenie (co 0,64 s)
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IntT0:
push Acc
cpi Flag,0xFF ; sprawdź rejestr Flag
breq wykon ; jeśli ustawiony - skocz do procedury
dec Del ; rejestr Flag nie był ustawiony, zmniejsz Del
brne skip1 ; jeśli Del różne od zera, wyjdź
ldi Acc,TimTC0 ; dokładny czas bramkowania (przerwania od TC0)
com Acc ; zanegowanie bajtu (ujednolicenie wpisywania wartości)
out TCNT0,Acc ; dokładny czas bramkowania (przerwania od TC0)
ser Flag ; rejestr DEL=0, ustaw rejestr Flag
rjmp skip1 ; wyjdź
wykon:
ldi Acc,0<<CS12|0<<CS11|0<<CS10
out TCCR1B,Acc ; TC1 zliczanie zewnętrzne, wstrzymane
ldi Acc,0<<CS02|0<<CS01|0<<CS00
out TCCR0,Acc ; TC0 clk/256, stop odmierzania czasu
rcall konw ; konwersja BIN na BCD zliczonych 24 bitów
;ldi Acc,0b00000001 ; wyczyść LCD
;rcall CmdWr ; Zapisz komendę z Acc do LCD
tabl freq ; załaduj tablicę znaków (makro)
ldi Acc,0x80 ; załaduj początkowy adres tekstu
rcall TekstWr ; wyświetl tekst na LCD
ldi Acc,0xC0 ; adres początku 2 linii LCD
rcall FreqWr ; wyświetlenie częstotliwości na LCD
clr Cnt ; wyzerowanie rejestrów licznika impulsów
clr Acc ; wpisanie zera do rejestru pomocniczego
out TCNT1H,Acc ; wyzerowanie rejestrów licznika impulsów
out TCNT1L,Acc ; wyzerowanie rejestrów licznika impulsów
ldi Del,DelVal ; aktualizacja zgrubnego rejestru
clr Flag ; wyzerowanie rejestru Flag
ldi Acc,0
out TCNT0,Acc ; wyzerowanie TC0
ldi Acc,1<<CS02|0<<CS01|0<<CS00
out TCCR0,Acc ; TC0 clk/256, start
ldi Acc,1<<CS12|1<<CS11|1<<CS10
out TCCR1B,Acc ;TC1 zliczanie zewnętrzne, wznowione
skip1:
pop Acc
reti
Pierwszy listing to fragment kodu ze zdefiniowanymi rejestrami i wstępnymi ustawieniami (przed pętlą główną, w której jak dotąd nie mam nic). Drugi listing to przerwania: T1-inkrementacja licznika pomocniczego po jego przepełnieniu, natomiast T0-zliczanie impulsów zewnętrznych i wyświetlanie na LCD. Nie przedstawiłem tutaj procedur obsługujących LCD bo to akurat działa perfekcyjnie. Problem tkwi w tym, że liczba impulsów zliczonych jest za każdym razem nieco inna (odchyłka to okolo 50 impulsów przy sygnale 1,8 MHz z generatora kwarcowego przy czasie bramkowania 0,64 s). Co może być przyczyną takiego zachowania?
