Licznik (miernik) częstotliwości na Atmega8

Kilka lat temu (4-5?) zakupiłem generator ZOPAN KZ1405 i już po kilku tygodniach użytkowania zaczął mi dokuczać brak cyfrowego odczytu nastawianej częstotliwości. Dodatkowo, jest to stary sprzęt i do osiągnięcia względnej stabilizacji parametrów potrzebuje dość długiego czasu (ok. 30-60min), po którym to, co mamy na jego wyjściu m/w zgadza się z tym, co odczytujemy ze skali przy gałce. Zrodził się więc pomysł zbudowania prostego miernika częstotliwości, który pokrył by zakres zopana i pozwolił na orientacyjne nastawy w/w, troszkę szybciej niż po 45minutach 😉
Poniżej prezentuję więc to, co wówczas się urodziło 🙂

Koncepcja

Miało być prosto. Założenia były takie, aby wsadzić Atmege8 a do tego coś co będzie multipleksować wyjścia, żeby móc wysterować osiem wyświetlaczy 7-mio segmentowych. Jakieś zewnętrzny zegar odniesienia, tak aby f była troszkę bardziej stabilna niż z kwarcu dołączanego do atmegi, dodatkowo jakiś dzielnik na wejściu aby zwiększyć zakres (bardziej aby pokryć te 10MHz które osiąga KZ1405), no i możliwość ustawiania czasu bramkowania. To by było chyba na tyle jeśli chodzi o założenia. W praktyce zostało kilka pinów więc układ został dodatkowo wyposażony w komunikację po RS232 z komputerem, której swoją drogą nigdy nie wykonałem, bo nie miałem takiej potrzeby.
Takie były założenia i koncepcja, przejdźmy więc do tego co z tego wyszło.

Schemat, zasada działania…

Poniżej znajduje się schemat.

No to lecimy… Sygnał z wejścia jest podawany na prosty wzmacniacz zbudowany na elementach T9, R1, R18, R19 i D1. Jego zadaniem jest „formowanie” sygnału wejściowego tak aby mógł zostać bezpiecznie podany na wejście układów cyfrowych. Sygnał z jego wyjścia (kolektor T9) jest podawany na wejście CLK0 licznika 7490, który pełni w układzie rolę dzielnika częstotliwości, a jego podział wynosi 1:10. Z wyjścia Q3 licznika, sygnał jest podawany na bramkę NAND U5 (został tutaj zastosowany układ TTL 7400 – poczwórna bramka NAND). Układ logiczny złożony z trzech bramek układu U5, działa jak swego rodzaju przełącznik, który sterowany sygnałem logicznym podawanym z wyjść PD5 (pin11) oraz PD6(pin12) mikrokontrolera U1, wybiera, czy sygnał na wyjściu ma być pobierany z wyjścia Q3 (pin 11) układu U4, czy bezpośrednio ze wzmacniacza wejściowego T9. I tak: jeżeli na PD5 pojawi się stan wysoki, a na PD6 niski, to wówczas pojawi się również stan wysoki na wejściu A (pin 13) bramki NAND, a z kolei na jej wyjściu otrzymamy zanegowany sygnał podawany na jej wejście B (pin 12), z kolei druga bramka (piny: 1,2,3 układu U5), otrzyma na swoim wejściu A (pin 2) stan niski, co spowoduje że niezależnie od stanów podawanych na jej wejście B (pin 1), na jej wyjściu zawsze będzie panował stan wysoki. Wyjścia obydwóch bramek są z kolei podłączone do wejść kolejnej bramki NAND. I tak: jeżeli jedno jej wejście znajduje się w permanentnym stanie wysokim (patrz opis powyżej), a na drugim pojawia się zanegowany sygnał, wówczas na jej wyjściu (pin 8) pojawia się powtórnie zanegowany sygnał. Czyli mamy dokładnie stan taki jaki panuje na wejściu A (pin 12) pierwszej bramki. Jeśli sytuacja zostałaby odwrócona i teraz stan wysoki pojawiłby się na wyjściu PD6 układu U1, a niski na PD5 wówczas sytuacja wyglądałaby dokładnie odwrotnie i na wyjściu trzeciej bramki (pin 8) pojawiłby się sygnał z wejścia B pierwszej bramki (pin 1). W ten sposób możemy sterować z którego wejścia chcemy mieć sygnał na wejściu T0 układu U1.
Dodatkowo, do wyjść PD5 oraz PD6, zostały podłączone diody LED D3 oraz D4, wraz z rezystorami ograniczającymi prąd R21, R22 które sygnalizują aktualnie wybrany tryb podziału.
Jeśli chodzi o wejście sygnału to w zasadzie wszystko. Do mikrokontrolera U1, mamy jeszcze podłączony zewnętrzny zegar OSC1, do pinu PB1 skonfigurowanego jako wyjście mamy podłączoną diodę led D2 wraz z rezystorem ograniczającym jej prąd, która to sygnalizuje bramkowanie. Do PB0 (skonfigurowanego jako wejście) jest podłączony przełącznik chwilowy S1 który służy do ustawiania czasu bramkowania oraz analogicznie do PD7 podłączony jest S2, którym wybieramy odpowiedni dzielnik. Zarówno przy S1, jak i S2 nie ma rezystora podciągającego, gdyż jest on ustawiony programowo. Wejście reset (pin 1) jest na sztywno podłączone do + zasilania, przez rezystor R1 (tak, tak, nie jest to najbardziej eleganckie rozwiązanie).
Dalej mamy porty PC0…PC3, które sterują wejściami dekodera kodu BCD na wyświetlacz 7-mio segmentowy, którego wyjścia z kolei są podłączone równolegle do segmentów wszystkich ośmiu wyświetlaczy, a te z kolei są kolejno odświeżane poprzez ich sekwencyjne włączanie i wyłączanie za pośrednictwem tranzystorów T1..T8.

Wyświetlacz

Działa to w ten sposób, że liczba (wartość), którą chcemy wyświetlić na kolejnych ośmiu pozycjach wyświetlacza, jest rozbijana na pojedyncze cyfry, a każda z nich kolejno przetwarzana na kod BCD, który jest zadawany z wyjść PC0..PC3 MCU U1. W tym samym czasie wyjścia PC4,PC5, PB5, PB4, PB3, PB2 włączają odpowiednią pozycję wyświetlacza aż dojdziemy do ostatniej pozycji. Następnie cały ten proces powtarza się w kółko, z częstotliwością na tyle wysoką, że ludzkie oko nie jest w stanie tego zarejestrować.

Przyjrzymy się fragmentowi kodu, który odpowiada za ten proces…

void enableT(uint8_t x) 
{ 
	PORTB |= (1 << PB2); 
	PORTB |= (1 << PB3); 
	PORTB |= (1 << PB4); 
	PORTB |= (1 << PB5); 

	PORTC |= (1 << PC4); 
	PORTC |= (1 << PC5); 
	PORTD |= (1 << PD2); 
	PORTD |= (1 << PD3); 


	if(x == 7) 
		PORTB &=~(1 << PB2); 
	else if(x == 6) 
		PORTB &=~(1 << PB3); 
	else if(x == 5) 
		PORTB &=~(1 << PB4); 
	else if(x == 4) 
		PORTB &=~(1 << PB5); 

	else if(x == 0) 
		PORTC &=~(1 << PC4); 
	else if(x == 1) 
		PORTC &=~(1 << PC5); 
	else if(x == 2) 
		PORTD &=~(1 << PD2); 
	else if(x == 3) 
		PORTD &=~(1 << PD3); 
} // end of enableT()

	while(1)
	{
	   …
	   enableT(n);
	   PORTC &= 0xF0; //clear last 4 bit 
	   PORTC |= buff[7-n];//-'0'; 

	   if(n>=7) 
		   n=0; 
	   else 
		   n++;
	   ...
	}

Mamy tutaj procedurę enableT() oraz nieskończoną pętlę. Procedura enableT przyjmuje jeden parametr, który jest liczbą. Ten parametr oznacza, która pozycja wyświetlacza ma zostać zapalona. Jeśli spojrzymy w ciało tej procedury to zobaczymy, że najpierw czyszczone są wszystkie wyjścia które odpowiadają za sterowanie tranzystorami T1…T8 (zapalanie odpowiednich pozycji wyświetlacza), a następnie włączana jest ta pozycja, która odpowiada wartości podanego parametru.
W głównej pętli natomiast, najpierw uruchamiamy procedurę i tym samym „aktywujemy” odpowiednią pozycję na wyświetlaczu. Następnie czyścimy 4 ostatnie bity portu C i przypisujemy do niego wartość z tablicy buff. W kolejnych pozycjach tablicy buff, znajdują się kolejne cyfry wartości która ma zostać wyświetlona. I tym sposobem, po 8mio krotnym wykonaniu pętli, zostaje odświeżony cały wyświetlacz.

Firmware

Oprócz sterowania wyświetlaczem, w kodzie mamy również obsługę przycisków oraz mamy uruchomione dwa liczniki. T0 jako liczniki oraz T1 jako timer z preskalerem ustawionym na podział 256. Licznik T0 zlicza impulsy docierające do MCU w swoim przerwaniu, z kolei T1 odpowiada za bramkowanie. Działa to w ten sposób, że każde przerwanie wygenerowane przez T0, zwiększa zmienną globalną overflows o 1, natomiast każde przerwanie timera T1, czyli naszej „bramki”, zlicza ilość impulsów i odpowiednio (w zależności od podziału i bramkowania) mnoży wynik i przygotowuje gotową wartość w zmiennej current_f. Jej zawartość jest z kolei rozbita na poszczególne cyfry (zmienna buff) i wyświetlona na wyświetlaczu. Na dole wpisu będzie dostępny cały kod wraz z gotowymi wsadami do procka.

Programowanie MCU

Podczas programowania, a właściwie przed jego rozpoczęciem, należy zwrócić uwagę na odpowiednie ustawienie fuse bitów atmegi. W naszej konfiguracji, będziemy korzystać z zewnętrznego zegara zatem konfiguracja powinna wyglądać m/w tak:

LOW: 0xE0
HIGH: 0xD9

a w avrdude wyglądać to będzie m/w tak:

-U lfuse:w:0xe0:m -U hfuse:w:0xd9:m 

Podsumowanie

Poniżej znajdziecie linka, do wszystkich plików projektu, wraz z wzorem płytki drukowanej, przygotowanej specjalnie pod obudowę Z1. Płytka jest tak zaprojektowana, że zaznaczone na niej otwory montażowe idealnie pasują pod słupki do montażu znajdujące się właśnie w Z1.
Jeśli ktoś chciał by wiedzieć w jaki sposób wykonuję swoje płytki, takie jak na zdjęciach, to zapraszam do lektury mojego artykułu o tym jak wykonuje swoje pcb.

3 odpowiedzi na “Licznik (miernik) częstotliwości na Atmega8”

  1. Zbigniew pisze:

    Zapraszamy do naszego Serwisu
    Polkoks Elektronics -Serwis i Naprawa Falowników , Naprawa Paneli HMI itp.

  2. tomash1977 pisze:

    Projekt zrobiłem i…? Szkodza czasu na badziewie. MIERNIK WOLNY I NIEDOKŁADNY. Bardzo duży błąd pomiarowy na sinusie jak i prostokącie. (z generatora DDS AD9850)

    • leniwiec pisze:

      jest tak dokładny jak użyta podstawa czasu i tak szybki jakie ustawione bramkowanie 🙂
      Podawanie sinusa do licznika to raczej kiepski pomysł, tak samo jak stosowanie AD9850 jako generatora funkcji, który po prostu jest kiepski w tej roli, a już przy niskich częstotliwościach dzieją się cuda…

Skomentuj leniwiec Anuluj pisanie odpowiedzi

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *