Zliczanie impulsów, Timery, help...

To forum jest dla wszystkich pasjonatów wiecznie młodych mikrokontrolerów '51. 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!
ODPOWIEDZ
cristof_w
-
Posty: 66
Rejestracja: 24 sie 2006, 5:09

Zliczanie impulsów, Timery, help...

Post autor: cristof_w » 23 paź 2007, 18:27

Witam. Jak w temacie, potrzebuję pomocy w kwestii wyświetlenia zliczanych impulsów. Napisałem programik (zakładam, że jest dobrze, aczkolwiek nie mam 100% pewności). Skonfigurowałem odpowiednio Timery jako counter/czasomierz, ustawiłem rejstr TMOD, przerwanie itp. Użyłem dwóch Timerów, T0-ma zliczać impulsy zewnętrzne podane na wejście Timeta T0, natomiast Timer T1 ma za zadanie odliczać czas zliczania. Dla próby, chciałbym zrobić coś takiego, że np. po zliczeniu dwóch impulsów ma zapalić się dioda (jako sygnalizacja zliczenia tychże dwóch impulsów). I właśnie nie wiem jak mam to zrealizować programowo. Poniżej przedstawiam prgoram.
Pozdrawiam i proszę o pomoc chętne osoby.

Kod: Zaznacz cały

#include <8051.h>
#include <stdio.h>

#define PortLED  P2_4
#define PortLED2  P2_5
#define SECOND 200   //1sek to 20 przerwan od Timer

void main(void)
{	

	TMOD=0x06;//Konfiguracja Timerow
	
	TH0=0xFE; 
	TL0=0xFE; 

	TR0=1;
	
	TH1 = 0x87;
	TL1 = 0;
	TR1 = 1;
	ET1 = 1;
	EA = 1;

	while(1) {
		
		while(!INT0) {};
		while(INT0) {};	
		TH0=0;
		TL0=0;	
		}
	}
void T1_int(void) interrupt 3 using 1 
	{
	static data unsigned char count=0;
	TH1 =0x87;
	TL1 =0;

	if (++count ==SECOND)
		{
		count =0;
		INT0=PortLED=!PortLED;//Sygnalizacja uplyniecia czasu zliczania
		}
	}

snow
Użytkownik
Posty: 794
Rejestracja: 16 sty 2007, 3:44
Lokalizacja: lubelskie
Kontakt:

Post autor: snow » 23 paź 2007, 18:42

W przerwaniu zrób tylko count++; a warunek

Kod: Zaznacz cały

if (count==SECOND) 
sprawdzaj w while, bo taki zapis:

Kod: Zaznacz cały

if (++count ==SECOND)
Jest chyba niepoprawny, chociaż może się mylę bo nie znam tego kompilatora.

cristof_w
-
Posty: 66
Rejestracja: 24 sie 2006, 5:09

Post autor: cristof_w » 23 paź 2007, 19:52

Witaj, zapis, który użyłem na początku jest dobry (mój kompilator tego niewywala). Jednakże zamieniłem na tak jak Ty napisałeś. otrzymałem:

Kod: Zaznacz cały

#include <8051.h>
#include <stdio.h>

#define PortLED  P2_4
#define PortLED2  P2_5
#define SECOND 200   //1sek to 20 przerwan od Timer
static data unsigned char count=0;
void main(void)
{	

	TMOD=0x06;//Konfiguracja Timerow
	
	TH0=0xFE; 
	TL0=0xFE; 

	TR0=1;
	
	TH1 = 0x87;
	TL1 = 0;
	TR1 = 1;
	ET1 = 1;
	EA = 1;

	while(1) {
		
		if (count==SECOND)
			{
			INT0=!PortLED;
			}
		while(!INT0) {};
		while(INT0) {};	
		TH0=0;
		TL0=0;	
		}
	}
void T1_int(void) interrupt 3 using 1 
	{
	
	TH1 =0x87;
	TL1 =0;
		
	count++;
		
	}
Ale czy to wnosi jakieś zmiany do całości?
Pozdrawiam.

snow
Użytkownik
Posty: 794
Rejestracja: 16 sty 2007, 3:44
Lokalizacja: lubelskie
Kontakt:

Post autor: snow » 23 paź 2007, 20:42

cristof_w pisze:Dla próby, chciałbym zrobić coś takiego, że np. po zliczeniu dwóch impulsów ma zapalić się dioda (jako sygnalizacja zliczenia tychże dwóch impulsów). I właśnie nie wiem jak mam to zrealizować programowo
Tmer ci liczy zmienną count od 0 do char

Jak szybko następuje przerwanie? Zmienna się w przerwaniu zwiększa zatem te 200 które zadeklarowałeś osiągnie po jakimś czasie.

Warunek count==SECOND występuje w czasie jednego przerwania później jest >SECOND aż do momentu gdy skończy się zakres counta więc to musisz jakoś przewidzieć.

Spróbuj ustawić timer tak żeby przepełnienie timera było co 1s (wtedy count się będzie incrementował co 1s) i zrób:

Kod: Zaznacz cały

        if (count>1)
            {
            zapal_leda;
            } 
to ci po 2s powinna dioda zaświecić się (po zliczeniu 2 impulsów).

cristof_w
-
Posty: 66
Rejestracja: 24 sie 2006, 5:09

Post autor: cristof_w » 23 paź 2007, 21:09

Hm...
Tego niesprawdziłem, ale nie weim czy niemylimy tutaj dwóch kwestii. Ja mam na myśli zliczanie impulsów zewnętrznych, podawanych na wejście Timera T0 (napisałem to w pierwszym poście), więc czas w Timerze 1 nie msui być sekunda*200, może to byc dowolny czas. Dlatego tak naprawdę nie mogę zrozumieć jak się mają te przekształcenia z conter do mojego problemu. Mogę prosić jakieś większe wyjaśnienie?
Pozdrawiam.

EDIT: Niestety nie działa.

snow
Użytkownik
Posty: 794
Rejestracja: 16 sty 2007, 3:44
Lokalizacja: lubelskie
Kontakt:

Post autor: snow » 23 paź 2007, 21:31

No fakt troche namotałem chyba bo źle zrozumiałem posta :)

Jeżeli chcesz zliczać ilość impulsów zliczonych przez licznik to licznik posiada rejestr w którym są zliczane impulsy (przynajmniej tak jest w przypadku AVR). Jego wartość zczytjuesz przez zwykłe podstawienie do zmiennej. Np. w AVR by wyglądało tak:

TCNT0 - rejestr licznika

Kod: Zaznacz cały

a=TCNT0
Proste :) Podejrzewam że tu jest podobnie. Jeżeli ma ci pózniej zapalić leda przy dwóch impulsach to normalnie przez porównanie IF'em robisz if (a==2) zapal_led;

A co do przekształceń to zawsze należy w przerwaniach robić jak najmniej operacji - najlepiej jedynie inkrementować zmienną gdyż przy bardzo krótkich czasach może sie okazać że przerwania w timerze najdą na siebie. Ale to tak przy okazji ;)

Mam nadzieje że jakoś pomogłem mimo wszystko ;)

cristof_w
-
Posty: 66
Rejestracja: 24 sie 2006, 5:09

Post autor: cristof_w » 24 paź 2007, 7:12

Witam.
Zatem się zrozumieliśmy oco dokładnie mnie chodzi.
Timer T0 ma rejestry: TH0 i TL0.
I teraz ustawiłem ten Timer w tryb 2, wpisując do rejestrów następujące wartości:

Kod: Zaznacz cały

TH0=0xFE; 
    TL0=0xFE;
Chodziło oto, żeby po dwóch impulsach się przeładował. Wtedy ustawia się jakaś flagaTFx na 1 (czy jakś tak). Szedłem takim tropem...z tego co Ty piszesz, to może ustawie Timer) w normalny tryb, tzn. 1 i zrobiłbym tak:

Kod: Zaznacz cały

TH0=a;  if (a==2) PortLED2=0; 
W przerwaniu zostawiłbym counter tak jak był dotychczas, w wresji pierwotnej, żeby Timer1 zliczał poprostu sobie czas, pomiary zliczania impulsów by się odbywały w trakcie zliczania, przerwanie przerywa zlcizanie i na nowo jest odmierzany czas.

Co sądzisz o takim rozwiązaniu? W którym miejscu powinienem ten powyższy kawałek kodu umieścić? W funkcji main? Możesz to jakoś rozpisać?
Pozdrawiam.

snow
Użytkownik
Posty: 794
Rejestracja: 16 sty 2007, 3:44
Lokalizacja: lubelskie
Kontakt:

Post autor: snow » 24 paź 2007, 11:55

Generalnie to jeżeli w rejestrach TH0 i TL0 wpiszesz jakąś wartość to kolejne impulsy zaczną zwiększać TL0 raczej niż TH0. Poniżej program który zlicza ilość impulsów na wejściu licznika i na wyświetlaczu pokazuje ile zliczył. Gdy liczba impulsów przekracza 50 następuje wyzerowanie rejestru licznika i liczy od 0 do 50 - i tak w kółko. Przeanalizuj go bo '51 nie znam w ogóle ale zasada powinna być identyczna.

Kod: Zaznacz cały

#include <avr/io.h>
#include <avr/interrupt.h>
#include "lcd.h"

char impulsy;   //zmienna do zliczania impulsów
char imp[10];  //zmienna pomocnicza do konwersji liczby na ciąg ASCII do wyświetlenia

int main(void)	//Program główny
{
DDRA=0xff;
DDRB=0xF8;
DDRC=0x7F;
DDRD=0xFF;
PORTA=0xFF;
PORTB=0xFF;
PORTC=0x7F;
PORTD=0xFF;

TCCR0=(1<<CS00)|(1<<CS01)|(1<<CS02); //Ustawienie Timera0 do pracy jako licznik, zliczanie impulsów zboczem opadającym na Wejściu T0 procesora

sei();					 //Odblokowanie przerwań

lcd_init(LCD_DISP_ON);  //Inicjalizacja wyświetlacza


while (1)
{
impulsy=TCNT0;			//Wstawienie wyników licznika znajdujących się w TCNT0 do zmiennej 'impulsy'
if (TCNT0>50) TCNT0=0; //Warunek na zerowanie licznika gdy liczba zliczonych impulsów przekroczy 50

itoa(impulsy,imp,10);  //konwersja liczby impulsów do zmiennej 'imp' umozliwiajace wyswietlenie na LCD

lcd_gotoxy(0,0);       //Przejscie do pierwszej linii wyswietlacza 2x16
lcd_puts("Impulsy= "); //Wyświetlenie napisu
lcd_puts(imp);		   //wyświetlenie liczby zliczonych impulsów na wyświetlaczu
lcd_puts("   ");

}
return (0);			//Powrót do początku programu
}

cristof_w
-
Posty: 66
Rejestracja: 24 sie 2006, 5:09

Post autor: cristof_w » 24 paź 2007, 17:55

Witaj.
Dzięki za program, właśnie mniej więcej o coś takiego mnie chodzi, tylko, że pod '51.
Co do analizy tego programu...nie jestem jeszcze za dobry w C, ale widze, że pod ACR jest to zupełnie inaczej, niby podobnie, ale inaczej. Przerobiłem program, kod poniżej, ale niestety niedziała. Może jescze jakieś pomysły? Może coś się rzuca w oczy? Coś brakuje albo coś jest nie tak? Pozdrawiam.

Kod: Zaznacz cały

#define PortLED  P2_5 
#define PortLED2  P2_4 
#define SECOND 200    //1sek to 20 przerwan od Timer 

void main(void) 
{    

   TMOD=0x06; 
    
   TH0=0;       
   TL0=0;    
   TR0=1; 
    
   TH1 = 0x87; 
   TL1 = 0; 
   TR1 = 1; 
   ET1 = 1; 
   EA = 1; 

   while(1) { 
       
   	int a;
   	a=TH0;
   	if (a==2) PortLED2=0;
      TH0=0;      //zerowanie licznikow TH0    
      TL0=0;      //i TL0 przed pomiarem 
    
      }; 
   } 
void T1_int(void) interrupt 3 using 1 
   { 
   static data unsigned char count=0; 
   TH1 =0x87; 
   TL1 =0; 

   if (++count ==SECOND) 
      { 
      count =0; 
      PortLED=!PortLED; 
      } 
   }

snow
Użytkownik
Posty: 794
Rejestracja: 16 sty 2007, 3:44
Lokalizacja: lubelskie
Kontakt:

Post autor: snow » 24 paź 2007, 19:46

Jaki to licznik? 8bit? Jak 8 bit to nie ma High i Low zatem musisz czytać TL0:

a=TL0;

bo TH0 by chyba wystąpiło przy 16-bitowym liczniku.

Zresztą nawet przy 16-bitowym w którym występują rejestry H i L zawsze zwiększany jest najpierw rejestr Low a później High.

Poza tym sygnał musisz podać na wejście licznika (zapewne istnieje noga oznaczona jako T0 w procesorze lub jakoś inaczej) - raczej nie jest obojętne na którą podasz swoje impulsy. Może na złego pina się ze źródłem impulsów podpiąłeś.

cristof_w
-
Posty: 66
Rejestracja: 24 sie 2006, 5:09

Post autor: cristof_w » 25 paź 2007, 14:32

Więc tak.
Rzeczywiście zmieniłem i przypisałem obecnie a=TL0; .
Program zadziałał, owszem. Jednakże cos jest nie tak i nie mogę do tego dojść. Próbowałem dla kilku wartości zmiennej a, ale dioda cały czas zapala się po jednym impulsie (nieważne jaką wartość ma zmienna a).
W czym może tkwić problem?
Co więcej, po przerwaniu dioda PortLED2 cały czas się świeci, niegaśnie, tez nie wiem dlaczego. Jakieś sugestie?
Pozdrawiam.

snow
Użytkownik
Posty: 794
Rejestracja: 16 sty 2007, 3:44
Lokalizacja: lubelskie
Kontakt:

Post autor: snow » 25 paź 2007, 15:05

Może spróbuj zadeklarować przed while'em wartośc początkową zmiennej "a" jako: a=0;

I sprawdzaj warunek tak:

Kod: Zaznacz cały

if (a==2) 
{
PortLED2=0;
}
else
{
PortLED2=1;
}

if (a>2) a=0;

cristof_w
-
Posty: 66
Rejestracja: 24 sie 2006, 5:09

Post autor: cristof_w » 25 paź 2007, 16:19

Przetestowałem na wszystkie sposoby i niestety nie działa...Po pierwszym impulsie dioda zapala się delikatnie i odrazu gaśnie-taki jest rezultat. Dołożyłem także zerowanie T0 po wyrażeniu PortLED2=0; i niestety nie działa.
Ogólnie to chodzi oto, żeby uzyskać nastepujący efekt:
Zliczanie impulsów podczas odmierzenia czasu, następnie przerwanie (i w tym momencie, czyli w trakcie trwania przerwania, żeby tutaj nastąpiło wyświetlenie ilości zliczonych impulsów, czyli zapalenie PortLED2).
Czy dobrze rozumiem, że odbywa się to tak iż (prgoramowo):wykonywane są polecenia w funkcji while, następnie przerwanie i znowu funkcja while, i tak w kółko. Jeżeli tak, to może trzeba pokombinować z kodem w przerwaniu?

snow
Użytkownik
Posty: 794
Rejestracja: 16 sty 2007, 3:44
Lokalizacja: lubelskie
Kontakt:

Post autor: snow » 25 paź 2007, 16:34

Przerwania sa wykonywane niezależnie od kodu głównego. Czy to nie ma być częstotliwościomierz? ;) Podejrzewam że może masz coś sprzętowo nie tak. Jeżeli masz ustawiony pin jako wejście to powinno to działać. Nie wiem niestety jak z '51 jest :(

Mogę ci podesłać na PW kod do częstotliwościomierza na AVR w C to sobie zobaczysz, ale specjalnie się to nie różni od kodu który wyżej podałem.

cristof_w
-
Posty: 66
Rejestracja: 24 sie 2006, 5:09

Post autor: cristof_w » 25 paź 2007, 16:39

W '51 poprostu trzeba odpowiednio skonfigurować Timery jako licznik/czasomierz i to jest w sumie najważniejsze. Nie ma mowy nic o ustawieniu portów/zbocze opadające itp. Poprostu trzeba tylko sygnał podac na wejscie Timera T0. Reszta to poprostu odpowiedni kod w C...
I nie mam pomysłu dlaczego to nie działa...
To jest w umie czetotliwosciomierz, tak jak piszesz, masz racje. Możesz kod podesłać, przejrzę, czemu nie.
Pozdro.
Edit:
Może ktoś jeszcze wie w czym może tkwić problem?
Pozdrawiam.

[ Dodano: 2007-10-29, 08:11 ]
Witam.
Więc tak. Jest pomysł, żeby na końcu obsługi T1 negować TR0 a main sprawdzać stan TR0 i jeśli jest on TR0 to wyświetlić wynik.

Na końcu T1 zanegowałem zatem TR0 : TR0=!TR0; Dało to tyle, że rzeczywiście T0 nie zlicza już cały czas, tylko jesto to tak:
Liczy, przerwanie (nieliczy i tutaj ma być wyświetlany wynik), liczy i tak w kółko.
Jednakże nie wyświetla mnie wyniku wtedy gdy nieliczy (czyli na czas trwania przerwania). Źle pozmieniałem w main? A wygląda to tak:
while(1)
{
int a;
a=TL0;

if (a==2)
{
if (TR0=0) {
PortLED2=0;
TH0=0; //zerowanie licznikow TH0
TL0=0; //i TL0 przed pomiarem
}
}
}
Probowalem na różne sposoby, może powinno to wyglądać inaczej?
Pozdrawiam.

ODPOWIEDZ