[Bascom] Pomiar czasu trwania stanu niskiego

To forum jest dla wszystkich pasjonatów mikrokontrolerów AVR Atmela. Wymiana doświadczeń i pomoc dla początkujących w pisaniu programów zarówno w C, Asemblerze jak i BASCOM. Zapraszam znawców tematu, aby pomogli wszystkim początkującym!
agrala
-
Posty: 34
Rejestracja: 17 sty 2011, 21:20
Lokalizacja: Kalisz

[Bascom] Pomiar czasu trwania stanu niskiego

Post autor: agrala » 18 sty 2011, 6:35

Witam.
Jestem tu nowy i mam na imię Artur.
Chciałem zrobić na ATmedze8A układ który mierzyłby mi czas trwania stanu niskiego.
Czas trwania impulsów to ok 0,5-20ms. Impusly bedą występować w przedziale od 25 do 240Hz.
Napisałem program do uC który miałby to mierzyć.

Kod: Zaznacz cały

$regfile = "m8def.dat" 
$crystal = 16000000 
Config Lcd = 16 * 2 

Enable Interrupts 

Config Lcdpin = Pin , Db4 = Portc.3 , Db5 = Portc.2 , Db6 = Portc.1 , Db7 = Portc.0 , E = Portc.4 , Rs = Portc.5 
Config Timer1 = Timer , Prescale = 64       'maks=0,261s, min=0,000004s 
Enable Timer1 

Config Int0 = Falling 
On Int0 Poczatek 
Enable Int0 

Stop Timer1 
Timer1 = 0 

Dim Wynik As Word 

Dim Flaga As Bit 
Dim Wynikczas As Single 

Cls 
Cursor Off 

Do 
Locate 1 , 1 
Lcd Wynikczas ; Spc(6) 

Waitms 100 

Loop 
End 

Poczatek: 

If Flaga = 0 Then 
Load Timer1 , 0 
Start Timer1 
Config Int0 = Rising 
Else 
Stop Timer1 
Wynik = Timer1 
Wynikczas = Wynik * 0.000004 
Load Timer1 , 0 
Config Int0 = Falling 
End If 

Toggle Flaga 
Return
Użyłemdrugiego uC jako generator by sprawdzić czy licznik będzie poprawnie liczył.

Kod: Zaznacz cały

$regfile = "m8def.dat" 
$crystal = 4000000 
Config Pinb.1 = Output 
Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Up , Compare B Pwm = Disconnect , Prescale = 8 

Config Portd = Input 

Config Portd.0 = Output 
Config Portd.1 = Output 
Config Portd.2 = Output 
Config Portd.3 = Output 
Config Portd.4 = Output 

Do 
Set Portd.0 
Set Portd.1 
Set Portd.2 
Set Portd.3 
Set Portd.4 

If Pind.0 = 0 Then 
Pwm1a = 50 
End If 

If Pind.1 = 0 Then 
Pwm1a = 100 
End If 

If Pind.2 = 0 Then 
Pwm1a = 150 
End If 

If Pind.3 = 0 Then 
Pwm1a = 200 
End If 

If Pind.4 = 0 Then 
Pwm1a = 250 
End If 

Loop 
End 
Return
Z dokładności pomiaru jestem bardzo zadowolony.
Jednak występuje pewien problem podczas wyświetlania wyniku na wyświetlaczu.
Na wyświetlaczu pojawia się prawidłowy wynik oraz "wskakuje" jakaś liczba nie mająca nic wspólnego z pomiarem. Wygląda to tak że np. podaję czas 0,0002s a dostaje na wyświetlaczu 0,00019995 i wsakuje jakś wartość 0,2xxxx (dużow większa niż poprawny wynik). Możnaby powiedzieć że na 10 wyświetleń wyniku 7 razy jest podawany poprawny wynik a 3 razy jest jakaś dziwna wartość.
Można to lepej zauważyć gdy wynik wyświetlanu jest np. co sekundę.
Proszę o pomoc w rozwiązaniu problemu.

Awatar użytkownika
matrix
Użytkownik
Posty: 1795
Rejestracja: 15 mar 2006, 5:21
Lokalizacja: Sochaczew
Kontakt:

Post autor: matrix » 19 sty 2011, 22:44

Według mnie to w ogóle dziwnie się do tego wziąłeś. Przerwanie od timera wywołujesz co 0,20971520s? Czemu to ma służyć?

Ja bym zrobił tak:

Kod: Zaznacz cały

on timerX licze   'w przypadku przerwania skok do danego podprogramu
1. wywołał etykietę, niekoniecznie od przerwania zewnętrznego, na przykład:

Kod: Zaznacz cały

if pinX.y=0 then gosub licze   'jesli dany pin=0 to skocz do etykiety licze
2. skok do podprogramu licze
3. podprogram

Kod: Zaznacz cały

licze:
timerX=z   'wpisanie do timera odpowiedniej wartosci

while pinX.y=0   'dopoki dany pin = 0 to
enable timerX    'zezwalam na prace timera
wend
return


licze:
incr zmienna_a   'zwiekszam wartosc zmiennej pomocniczej
return
dalej odpowiednie obliczenia zależne co jaki czas wywołałem przerwanie i (raczej) po kłopocie.

Całość jest tylko teoretyczna i niesprawdzona w procku.

agrala
-
Posty: 34
Rejestracja: 17 sty 2011, 21:20
Lokalizacja: Kalisz

Post autor: agrala » 20 sty 2011, 6:37

matrix pisze:Przerwanie od timera wywołujesz co 0,20971520s?

Wydaje mi się że przerwanie od timera jest raczej co 0,26214s timer liczy co 0,000004s więc przepłni się dopiero po 0,26214s (0,000004*65535=0,26214). Oczywiście mogę się mylić gdyż jestem osobą początkującą.
Jeżeli myślisz że wartością która "przebija" na wyświetlaczu podczas wyświetlania wyniku jest właśnie to przerwanie to muszę powiedzięc że liczby te są różne (np. 0,7xxx) ale zawsze poniżej jednego.
matrix pisze:Czemu to ma służyć?

Urządzenie to ma służyć do pomiaru czasu wtysku w silniku benzynowym.
Sprawdzę wieczorkiem Twój pomysł i dam znać co z tego wyszło.

Awatar użytkownika
matrix
Użytkownik
Posty: 1795
Rejestracja: 15 mar 2006, 5:21
Lokalizacja: Sochaczew
Kontakt:

Post autor: matrix » 20 sty 2011, 9:38

Przepraszam z tym timerem. Pomyliłem się w obliczeniach. Rzeczywiście przerwanie wywoływane jest co 0,262144s

agrala
-
Posty: 34
Rejestracja: 17 sty 2011, 21:20
Lokalizacja: Kalisz

Post autor: agrala » 20 sty 2011, 11:23

Moge wiedzieć czemu pytałeś co ile jest przerwanie od timera?

Awatar użytkownika
matrix
Użytkownik
Posty: 1795
Rejestracja: 15 mar 2006, 5:21
Lokalizacja: Sochaczew
Kontakt:

Post autor: matrix » 20 sty 2011, 18:58

Bo nie wiem czemu chcesz wywoływać przerwania co 0,2s a nie dokładnie co 0,1s, (albo częściej). To ostatnie łatwiejsze do dalszych obliczeń.

Mój zamysł jest taki że:
1. wywołuję przerwanie co 0,1s (lub częściej)
2. zwiększam jakąś zmienną w przerwaniu
3. gdy poziom niski na danym pinie skończy się, biorę końcową wartość zwiększanej zmiennej i obliczam ją z jednostką czasu przerwania.

W ten sposób mam dokładną informację jak długo trwa stan niski.

agrala
-
Posty: 34
Rejestracja: 17 sty 2011, 21:20
Lokalizacja: Kalisz

Post autor: agrala » 20 sty 2011, 20:17

matrix pisze:Bo nie wiem czemu chcesz wywoływać przerwania co 0,2s a nie dokładnie co 0,1s, (albo częściej). To ostatnie łatwiejsze do dalszych obliczeń.
W kodzie który podałem jest mi to do niczego nie potrzebne. Startuje timer i go zatrzymuje, wyświetlam wynik i go zeruje. On się nie przepełnia więc go nie ma przerwania od niego. Chyba nie zrozumiaeś tego kodu.
matrix pisze:Mój zamysł jest taki że:
1. wywołuję przerwanie co 0,1s (lub częściej)
2. zwiększam jakąś zmienną w przerwaniu
3. gdy poziom niski na danym pinie skończy się, biorę końcową wartość zwiększanej zmiennej i obliczam ją z jednostką czasu przerwania.
W ten sposób mam dokładną informację jak długo trwa stan niski.
No i fajnie, zgadza się tylko że wydaje mi się że nie rozumiesz o co mi chodzi. Ja nie chce zliczać czasu ile trawło X stanów niskich tylko ile trwał jeden stan niski i wyświetlać to np. co sekundę lub częściej na wyświetlaczu.
Kod który podałeś zrozumiałem w ten sposób:

Kod: Zaznacz cały

$regfile = "m8def.dat"
$crystal = 16000000
Config Lcd = 16 * 2

Config Lcdpin = Pin , Db4 = Portc.3 , Db5 = Portc.2 , Db6 = Portc.1 , Db7 = Portc.0 , E = Portc.4 , Rs = Portc.5

Config Timer1 = Timer , Prescale = 64                    
Dim Wynik As Word
Dim Czas As Single
Config Portb.0 = Input
Set Portb.0
On Timer1 Licze

Cls
Cursor Off
Do

If Pinb.0 = 0 Then Gosub Licze
Locate 1 , 1
Lcd Czas , Spc(6)

Loop
End

Licze:
Timer1 = 250
While Pinb.0 = 0
Enable Timer1
Wend
Incr Czas
Return
Podałeś dwa razy podprogram "Licze" więc go połączyłem bo tak się nie da skompilować.
Program działa dokładnie tak jak piszesz - zwiększa się zmienna, tylko nie o to mi chodzi.
Jest jaksiś błąd bo zmienna się zwiększa jak jest stan wysoki a nie niski. Gdy zmienie

Kod: Zaznacz cały

While Pinb.0 = 0 na While Pinb.0 = 1
to wtedy zmienna wzrasta tylko gdy jest stan niski na porcie b.0.
Timer1=250 wziąłem z palca tylko do sprawdzenia czy działa.
Masz jakiś pomysł jak to przerobić by liczyć tylko czas jednego stanu niskiego i rzucać to na wyświetlacz z częstotliwością 1Hz lub 2Hz?
Coś tam kombinowałem ale nic mi z tego nie wychodziło - wkurzyłem się tylko i wyszedł mi z tego kodu zegarek :grin:

Awatar użytkownika
c4v2
Użytkownik
Posty: 427
Rejestracja: 22 lis 2005, 15:14
Lokalizacja: z przed monitora

Post autor: c4v2 » 20 sty 2011, 22:55

Pańskiego programu nie sprawdzałem. Na pierwszy rzut oka brakuje mi formatu zmiennej przeznaczonej do wyświetlenia na wyświetlaczu LCD. Czy próbował Pan "sformatować" zmienną "Wynikczas" funkcją FUSING?

Awatar użytkownika
matrix
Użytkownik
Posty: 1795
Rejestracja: 15 mar 2006, 5:21
Lokalizacja: Sochaczew
Kontakt:

Post autor: matrix » 20 sty 2011, 23:15

Może popełniłem gdzieś tam błąd przy pisaniu bo piszę sterownik do samochodu. Przed chwilą sprawdzałem poniższy kod i działa. Trzeba ustawić fusbity dla 8MHz.

Kod: Zaznacz cały

dim zliczone as word
config pind.0 = input
set pind.0
Config Timer1 = Timer , Prescale = 64
Timer1 = 53036
on timer1 prze_timer1
stop timer1

do
If Pind.0 = 0 Then Gosub zliczanie 
loop

zliczanie:
enable timer1
start timer1
while pind.0 = 0
 'pusta pętla
wend
stop TIMER1
disable timer1
Timer1 = 53036
cls
home
lcd zliczone
wait 3
zliczone=0 
return

prze_timer1:
  incr ZLICZONE
return
Przerwanie wywoływane dokładnie co 0.1s. Trzeba dodać obliczenia żeby wyszedł czas w odpowiednich jednostkach. Działa bo sprawdzałem. Wait 3 zastosowałem tylko dlatego że dodałem ten krótki kod do mojego kodu sterownika. Disable timer1 i enable timer1 dałm w kodzie dla pewności że się zatrzyma.

Awatar użytkownika
c4v2
Użytkownik
Posty: 427
Rejestracja: 22 lis 2005, 15:14
Lokalizacja: z przed monitora

Post autor: c4v2 » 21 sty 2011, 2:07

Pewno działa, ale poleceniem:
matrix pisze:

Kod: Zaznacz cały

set pind.0
nie da się włączyć rezystora podciągającego.
Natomiast polecenie:

Kod: Zaznacz cały

set portd.0
na pewno włączy rezystor podciągający.

agrala
-
Posty: 34
Rejestracja: 17 sty 2011, 21:20
Lokalizacja: Kalisz

Post autor: agrala » 21 sty 2011, 8:37

c4v2 pisze:Pańskiego programu nie sprawdzałem. Na pierwszy rzut oka brakuje mi formatu zmiennej przeznaczonej do wyświetlenia na wyświetlaczu LCD. Czy próbował Pan "sformatować" zmienną "Wynikczas" funkcją FUSING?
Na Pana to trzeba mieć wygląd i pieniądze :lol:
Czy Fusing coś w ogóle zmieni?
Nie wydaje mi się ale spróbuję. Nie używałem tego bo chciałem sprawdzić czy program działa tak jak trzeba a dopiero potem bawić się takimi rzeczami.

[ Dodano: 2011-01-21, 16:56 ]
Fusing nic nie zmieniło.
Kolego matrix mam problem z Twoim kodem - nie działa, cały czas 0 na LCD.
Ustawiłem timer=25 więc przerwanie powinno być co 0,0001s bo moj generatorek daje impulsy od od 0,0002 do 0,001s.
Mogłbys napisać jak ma wyglądać cały kod. Może nie działa przez to że źle go poskładałem.

Awatar użytkownika
matrix
Użytkownik
Posty: 1795
Rejestracja: 15 mar 2006, 5:21
Lokalizacja: Sochaczew
Kontakt:

Post autor: matrix » 21 sty 2011, 18:47

Kurcze byłem wczoraj już trochę padnięty i nie testowałem tego na procku. Wklep to:

Kod: Zaznacz cały

dim zliczone as word
config pind.0 = input
set portd.0
Config Timer1 = Timer , Prescale = 1
Timer1 = 65535
on timer1 prze_timer1
stop timer1
dim czas as single


do
If Pind.0 = 0 Then Gosub zliczanie
loop

zliczanie:
enable timer1
start timer1
while pind.0 = 0
 'pusta pętla
wend
stop TIMER1
disable timer1
Timer1 = 65535
cls
home
lcd zliczone
lowerline
czas = zliczone / 100
lcd czas ; "sek."
wait 3
cls
zliczone=0
return

prze_timer1:
  incr ZLICZONE
  home
  lcd  ZLICZONE
return
Nie wiem czy dobrze skaluje czas, ale działa na pewno. Właśnie mam to przed sobą. Nawet widać na lcd jak zwiększana jest zmienna w przerwaniach. Jeśli czas stanu niskiego będzie nie dłuższy niż 1sek. to można zmienną ZLICZONE zadeklarować jako byte a nie word.

A fusy takie. Wewnętrzny oscylator 8MHz.

Kod: Zaznacz cały

$PROG &HFF , &HA4 , &HD9 , &H00
Choć nie powinienem pisać za kogoś (i nie wiem czy dobrze :wink: ) to musiałem popełnić ten pseudoprogram bo kolega mi na ambicję wszedł.

agrala
-
Posty: 34
Rejestracja: 17 sty 2011, 21:20
Lokalizacja: Kalisz

Post autor: agrala » 21 sty 2011, 22:05

Czym Ci wszedłem na ambicję?
Jeżeli dobrze rozumiem to zmienna jest zwiększana co 0,000000125s.

Wgrywam taki program"

Kod: Zaznacz cały

$regfile = "m8def.dat"
$crystal = 8000000
Config Lcd = 16 * 2

Config Lcdpin = Pin , Db4 = Portc.3 , Db5 = Portc.2 , Db6 = Portc.1 , Db7 = Portc.0 , E = Portc.4 , Rs = Portc.5
Dim Zliczone As Word
config pind.0 = input
set portd.0
Config Timer1 = Timer , Prescale = 1
Timer1 = 65535
on timer1 prze_timer1
stop timer1
dim czas as single


do
If Pind.0 = 0 Then Gosub zliczanie
loop

zliczanie:
enable timer1
start timer1
while pind.0 = 0
 'pusta pętla
wend
stop TIMER1
disable timer1
Timer1 = 65535
cls
home
lcd zliczone
lowerline
czas = zliczone / 100
lcd czas ; "sek."
wait 3
cls
zliczone=0
return

prze_timer1:
  incr ZLICZONE
  home
  lcd  ZLICZONE
return
Nie wiem, może popełniam jakiś błąd ale program nie działa :sad:
W pierszej lini pojawia się: _
a w drugiej nic, jak podam stan niski to w pierwszej nie ma nic a w dugiej 0,0sek.
Obojętnie jakie czasy podam to zawsze jest to samo.
Czy programu czasami nie rozwala Wait w podprogramie zliczanie?

Awatar użytkownika
matrix
Użytkownik
Posty: 1795
Rejestracja: 15 mar 2006, 5:21
Lokalizacja: Sochaczew
Kontakt:

Post autor: matrix » 21 sty 2011, 22:35

W chwili wgrania programu nie powinno się nic pojawić, a w zasadzie pojawi się pewnie kursor, bo nie ma instrukcji

Kod: Zaznacz cały

cursor off
Co do przerwania to powinno występować co 0,0001ms (tak pokazuje kalkulator przerwań).

Co do działania programu to działa na bank. Sorki za złą jakość filmu ale mam tylko nokię do nagrywania.

-> FILM <- pomiaru.

agrala
-
Posty: 34
Rejestracja: 17 sty 2011, 21:20
Lokalizacja: Kalisz

Post autor: agrala » 21 sty 2011, 23:16

Tak, to był kursor.
Faktycznie działa na filmie.
Czy nie zrobiłem jakiegoś błędu w sklecaniu tego kodu?
Możliwe że wiem gdzie jest problem o ile dobrze złożyłem w całość program.
Twój program mierzy najmnieszy czas 0,01s a mój generatorek daje od 0,0002 do 0,001s.

[ Dodano: 2011-01-22, 09:43 ]
Zaczynam się gubić :cry:
Co ile jest w końcu zwiększana zmienna ZLICZONE?
Mi nie wychodzi że co 0,0001s
Jeden cykl trwa 0,000000125s a timer przepełnia się co 1 cykl czyli zmiena powinna się zwiększać co 0,000000125s

ODPOWIEDZ