logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[ATmega32][C] Wyświetlacz 7seg + pomiar temperatury

Barbossa 26 Cze 2010 20:25 3561 14
REKLAMA
  • #1 8234758
    Barbossa
    Poziom 10  
    Witam,

    pracuję nad czujnikiem temperatury do silnika pewnej maszyny. Mam dwa problemy.

    1. Czy istnieje sensor temperatury typu DS18B20 (1-wire) o zakresie do 200 C ?
    2. Czy istnieje możliwość wyeliminowania mrugania czterech wyświetlaczy 7seg ? Jak to zrealizować w C ? Wyświetlacze gasną podczas wykonywania pomiaru temperatury przez DS18B20. Zmontowałem wyświetlacze z tranzystorami na płytce stykowej i wyświetlam na nich liczby w sposób dynamiczny (dzieki temu zamiast 32 pinów wykorzystuje tylko 12, ale pojawia się problem gaśnięcia).

    Już teraz mruganie rzuca się w oczy, a w momencie dołożenia obsługi klawiatury (czas oczekiwania na zaprzestanie drgań styków) problem stanie się jescze bardziej uciążliwy.

    Czytałem coś o rejestrze i obsłudze przerwań, ale jestem początkujący i nie mam zielonego pojęcia, jak to zrealizować na mikrokontrolerze.

    Kod programu:
    
    /*
       Plik "main.c"
    
    */
    
    
    #define F_CPU 1000000UL  // 1 MHz
    
    #include <stdio.h>
    #include <avr/io.h>
    #include <util/delay.h>
    #include "ds18b20.h"
    
    void delay_ms(int ms)
    	{
    	volatile long unsigned int i;
    	for(i=0;i<ms;i++)
    		_delay_ms(1);
    	}
    
    int main(void)
    {
      /* Zmienna przechowuje aktualną wartość temperatury */        
    	double temp;
      /* W tablicy zapisywane będą dane odczytane z układu ds18b20 */
    	unsigned char ds18b20_pad[9];
    	//char waz[] = { 0x04, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f };
    	//                 0     1     2     3     4     5     6     7      8     9
    	char liczba[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
    	//char liczba[] = {0xf3, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
    	char liczbaK[] = {0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00, 0x10};
    	char wys[]= {0xfe, 0xfd, 0xfb, 0xf7};
    	
    	PORTA=0xFF;
    	DDRA=0xFF;
    	
    	PORTB=0xFF;
    	DDRB=0xFF;
    	
     
    	int wynik=0;
      
        int zapis[4];
    
      while(1)
      {
        /* Funkcja 'ds18b20_ConvertT' wysyła do układu ds18b20 
           polecenie pomiaru */      
         if(ds18b20_ConvertT())
        {
    
           /* 750ms - czas konwersji */
           _delay_ms(750);
    
          /* Odczyt z układu ds18b20, dane zapisywane są w tablicy ds18b20_pad. 
             Dwie pierwsze pozycje w tablicy to kolejno mniej znaczący bajt i bardziej 
         znaczący bajt wartość zmierzonej temperatury */            
           ds18b20_Read(ds18b20_pad);
              
          /* Składa dwa bajty wyniku pomiaru w całość. Cztery pierwsze bity mniej
             znaczącego bajtu to część ułamkowa wartości temperatury, więc całość
             dzielona jest przez 16 */       
           temp = ((ds18b20_pad[1] << 8) + ds18b20_pad[0]) / 16.0 ;
        }
    	
    	wynik=temp*10;
    	
    	zapis[0]=wynik/1000; //tysiace
    	zapis[1]=(wynik-(zapis[0]*1000))/100; //setki
    	zapis[2]=(wynik-(zapis[0]*1000)-(zapis[1]*100))/10; //dziesiatki
    	zapis[3]=wynik%10; //jednosci
    	
    	for(int i=0;i<200;i++)
    	{
    		for(int j=0;j<4;j++)// wyswietlacze
    		{
    			if(j==0) // wyswietlacz pierwszy
    			{
    				PORTB=wys[0];
    				PORTA=liczba[zapis[0]];
    				delay_ms(1);
    				PORTA=0xff;
    				PORTB=0xff;
    			}
    			if(j==1) // wyswietlacz drugi
    			{
    				PORTB=wys[1];
    				PORTA=liczba[zapis[1]];
    				delay_ms(1);
    				PORTA=0xff;
    				PORTB=0xff;
    			}
    			if(j==2) // wyswietlacz trzeci
    			{
    				PORTB=wys[2];
    				PORTA=liczbaK[zapis[2]];
    				delay_ms(1);
    				PORTA=0xff;
    				PORTB=0xff;
    			}
    			if(j==3) // wyswietlacz czwarty
    			{
    				PORTB=wys[3];
    				PORTA=liczba[zapis[3]];
    				delay_ms(1);
    				PORTA=0xff;
    				PORTB=0xff;
    			}
    		}
    	}
    	
      }
    }
  • REKLAMA
  • #2 8236068
    tomek3800
    Poziom 2  
    Barbossa napisał:


    Czy istnieje sensor temperatury typu DS18B20 (1-wire) o zakresie do 200 C ?



    Poczytaj o termoparach. Będziesz miał zakres nawet do 1000*C
  • REKLAMA
  • #3 8236200
    PO.
    Poziom 20  
    Zrealizować 1w na przerwaniach a nie na opóźnieniach.

    A autor nie chce analoga tylko coś na 1wire.
  • REKLAMA
  • #4 8238966
    flapo213
    Poziom 21  
    Witaj,

    Hmm z czunjnikami półprzewodnikowymi na temperaturę powyżej 200C to będzie bardzo ciężko (półprzewodniki nie tolerują aż tak wysokich temperatur, właściwości półprzewodników niestety),

    słusznie jeden z poprzedników zaznaczył że najlepszym rozwiązaniem byłaby termopara z wzmacniaczem różnicowym.

    Jeśli byś potrzebował kawałka schematu jak to hardwarowo wykonać to daj znać co da się zrobić w tym zakresie.

    Co do mrugania wyświetlacza to odświeżanie twojego wyświetlacza powinno być w przerwaniu o dużym priorytecie ponieważ jak masz inne przerwania to one wywłaszczą czas i znowu będziesz miał mruganie. Absolutnie nie dawaj odświeżania wyświetlacza w pętli głównej programu.

    Pomyśl poważnie o termoparze.

    Pozdrawiam i życzę sukcesów.
  • #5 8240100
    Barbossa
    Poziom 10  
    skorzystam jednak z wejścia analogowego ATmega32 i czujnika pt100, ale wciąż mam problem z przerwaniami :/ nie mam zielonego pojęcia, jak w C rozwiązać problem odświeżania wyświetlacza. Znalazłem gdzieś podobny przykład, który przerobiłem, ale mimo wszystko nie działa.

    // Pliki naglowkowe
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/signal.h>
    //#define F_CPU 1000000UL  // 1 MHz
    
    #include <stdio.h>
    // Numery wyswietlaczy
    char wys[]= {0xfe, 0xfd, 0xfb, 0xf7};
    // Tablica liczb i znakow
    //               0     1     2     3     4      5     6     7     8     9     E      r     c     o
    char znak[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x86, 0xaf, 0xa7, 0xa3};
    // Tablica zapamietujaca ktory znak ma zostac wyswietlony
    int co[] = {0, 0, 0, 0};
    // Index wyswietlacza
    int wysw = 0;
    
    SIGNAL (SIG_OVERFLOW0)
    {
    	TCNT0 = 250; // załadowanie do licznika wartości początkowej
    	PORTD = 0xFF; // wygaszenie wszystkich cyfr wyswietlacza
    	wysw++; // zwiększenie o 1 zmiennej wysw
    	// w zależności od wartości wysw włącz odpowiednią cyfrę na wyświetlaczu
    	switch(wysw){
    		case 1:
    		{
    			PORTD = znak[co[0]]; // na wyświetlaczu wyświetl odpowiedni znak
    			PORTB = wys[0]; // włącz pierwszą cyfrę wyświetlcza
    			break;
    		}
    		case 2:
    		{
    			PORTD = znak[co[1]]; // na wyświetlaczu wyświetl odpowiedni znak
    			PORTB = wys[1]; // włącz druga cyfrę wyświetlcza
    			break;
    		}
    		case 3:
    		{
    			PORTD = znak[co[2]]; // na wyświetlaczu wyświetl odpowiedni znak
    			PORTB = wys[2]; // włącz trzecia cyfrę wyświetlcza
    			break;
    		}
    		case 4:
    		{
    			PORTD = znak[co[3]]; // na wyświetlaczu wyświetl odpowiedni znak
    			PORTB = wys[3]; // włącz czwarta cyfrę wyświetlcza
    			break;
    		}
    		case 5:
    		{
    			wysw = 0; // zeruj zmienną wysw
    			break; // opuść instrukcję switch
    		}
    	}
    }
    
    // Program glowny
    int main(void)
    {
    	int pomiar = 0;
    	int wynik = 0;
    	
    	// Ustawienia portow IN/OUT
    		// Port B - OUT - sterowanie wyswietlaczami
    	PORTB = 0xff;
    	DDRB = 0xff;
    		// Port D - OUT - sterowanie wyswietlanymi liczbami
    	PORTD = 0xff;
    	DDRD = 0xff;
    		// Port C - IN/OUT - bity 0-3 - przyciski (IN) bit 4 - LED (OUT)
    	PORTC = 0xff;
    	DDRC = 0xf8; // 11111000 (bin)
    	
    	TCCR0 = 0x04;
    	TCCR1B = 0x03;
    
    	// wpisanie wartości początkowej
    	TCNT0 = 128;
    	// odblokowanie przerwania od licznika
    	TIMSK = 130;
    	// globalne odblokowanie przerwań
    	sei();
    	// pętla nieskoczona
    	while(1)
    	{
    		pomiar=(float)(ADC*REFS0)/1024;
    		wynik=pomiar*1000;
    	
    		co[0]=wynik/1000; //tysiace
    		co[1]=(wynik-(co[0]*1000))/100; //setki
    		co[2]=(wynik-(co[0]*1000)-(co[1]*100))/10; //dziesiatki
    		co[3]=wynik%10; //jednosci
    	}
    	return 0;
    }


    mógłby ktoś nawytykać mi błędów, po których skorygowaniu wyświetlacz będzie się odświeżał niezależnie od pobierania pomiarów i obsługi klawiatury ?
  • #6 8240224
    tmf
    VIP Zasłużony dla elektroda
    Przerwanie zrób w trybie CTC, nie będzies z musiał ładować wartości początkowej do licznika. Zerowanie wyświetlacza (PORTD = 0xFF;) jest bez sensu, wywal to. I teraz clue programu. Źle sterujesz wyświetlaniem. Na razie masz tak, że kolejne wyświetlacze są sterowane przez chwilkę (dosłownie kilka instrukcji), z wyjątkiem ostatniego. Powinieneś zrobić to tak, że w jednym przerwaniu timera wystawiasz sterowanie tylko jednego wyświetlacza. Reszty nie ruszasz. W kolejnym kolejnego itd. W efekcie uzystkasz sterowanie multipleksowe z duty cycle równym 1/n, gdzie n to ilość wyświetlaczy. Nic nie powinno wtedy mrugać.

    Dodano po 1 [minuty]:

    A BTW, tablica co powinna być volatile.
  • REKLAMA
  • #7 8324716
    jacynka84
    Poziom 26  
    Niestety nie znam C , ale może logicznie się da.
    Pisałem oscyloskop który miał na ekranie zarówno pisać linię i napięcie obok, oraz skalę, problemem było gdy w instrukcji "For next" było wszystko, wywaliłem za tę pętelkę wszystko prócz zapisania tablicy wynikami i Lcd linią.
    Może też zrób coś jak "For next" dla wyświetlania samego wyniku reszta po za tą pętlą?
    ja niestety w Bascomie, ale mechanika działania może ta sama.
  • #9 8387540
    Kociejsko
    Poziom 14  
    Kolego ... cały program do przeróbki ...
    nie chce cię martwić ale tą drogą to daleko nie zajedziesz.

    Co należy zrobić

    1. Ustawić sobie systemowego ticka co 1 ms. ( przerwanie wywołujące się co 1 ms)
    2. W tym przerwaniu co każdy takt przełączać wyswietlacz i zapodawać odpowiednią wartość na segmenty.
    3. pomiar temperatury zrobić można także w tym przerwaniu i za każdym razem kolekcjonować próbki ... po 100 próbkach bedziesz miał uśrednioną temepraturę ze 100 ms czasu. i co 100 ms aktualizować wskazanie wyświetlacza.
    4. Wszystkie kody cyfr wyświetlacza umieścić w pamięci programu a nie w ramie.
    5. tak samo w pamięci programu charakterystykę termopary.

    Wtedy wszytko będzie jak należy
  • #10 9781363
    alarmowanie
    Poziom 10  
    Witam podłączam się do tematu ponieważ też mam mały problem z 7seg z tym że dopiero myślę nad projektem i zastanawiam się jak pogodzić przerwanie multipleksujące co najmniej 8 wyświetlaczy a fajnie by było jakby szło 10 :P i przerwanie odbioru uarta. Generalnie urządzenie będzie miało za zadanie odbierać dane z rs485 atmega32 albo 16 i kwarc 16Mhz jeszcze nie wiem będzie musiała przetworzyć dane i wyświetlić na konkretnej parze 7seg chciałem podkreślić ze na magistrali rs485 będę miał jeszcze kilka urządzeń które też będą nadawały aha no i obsługa 5 switchy
  • #11 9781627
    Kociejsko
    Poziom 14  
    Nie powinno być większego problemu.
    Odebranie jednego znaku na porcie szeregowym zajmuje relatywnie mało czasu ( 115200 to jest 8us ).

    Przerwanie od multipleksowania displejów nie trwa cały czas świecenia pojedynczego znaku (np 1ms ) tylko trwa tyle co przełączenie aktywnego znaku ( da się załatwić 5 komendami bez problemu i wyrobić się między znakami z uarta) więc przerwania zbytnio nie będą sobie wchodzić w drogę.

    obróbkę danych i obliczenia da się zrobić w kontekście pętli Main więc nie ma to związku z przerwaniami.
  • #12 9784100
    alarmowanie
    Poziom 10  
    Kociejsko no niby tak też w ten sposób myślę tylko zastanawiam się jeżeli inne układy będą nadawać wtedy przerwanie od odbioru musi się włączyć i sprawdzić czy dostał swój adres jeśli tak to odbiera jeśli nie wychodzi z przerwania a układ który jest odpowiedzialny za obsługę pieca będzie dużo nadawał więc po dłuższym namyśle zastosuję lcd graficzny 128x64 i wtedy mogę sobie wielkość cyfr temperatury ustawić jaka mi się wymarzy i też będzie wyraźnie, nie wiem czy ten temat już można zamknąć ??
  • #13 9784204
    nsmarcin
    Poziom 12  
    Barbossa usuń z kodu _delay_ms(750) i powinno działać, nie będzie to co prawda idealna konfiguracja ds18b20 ale myślę, że spokojnie Ci to wystarczy
  • #14 9802013
    Kociejsko
    Poziom 14  
    Ale odbieranie znaku, a analiza ramki to dwie inne rzeczy. W przerwaniu tylko odbierasz znaki i zapisujesz do buforu. A w pętli głównej co jeden obieg sprawdzasz czy w buforze są znaki. Jeżeli są to z kontekstu Maina wybierasz znaki z bufora i analizujesz i parsujesz. Potem uznajesz czy adres w ramce był dla tego urządzenia czy nie. A przerwanie jedynie wpisuje do bufora.
  • #15 9805931
    dominon
    Poziom 18  
    Co do wyświetlania to możesz "odświeżać" wyświetlacz w przerwaniu czyli:

    - tworzysz zmienną w której będziesz przechowywał temperaturę po odczycie z 1-Wire itp (modyfikacja zmiennej będzie się odbywała po zakończonym odczycie z czujnika w ten sposób wyświetlacze nie będą gasły ani mrugały bo zmienna będzie mieć zawsze jakąś wartość),
    - generujesz przerwanie tak aby "odświeżanie" jednej cyfry odbywało się z częstotliwością ok 50Hz (wystarczy ten 8-bit timer),
    - w przerwaniu robisz skok do odpowiedniej funkcji "wyświetlającej" (ustawiającej odpowiednie stany portów wg. np tablicy z wartościami stanu portów odpowiadających cyfrom od 0-9. Bo przecież i tak po jednej cyfrze będziesz odświeżał).

    Zrobiłem w ten sposób termometr dwu punktowy na sześciu wyświetlaczach siedmiosegmentowych (temperatura zawsze dodatnia bo to był termometr do serwerowni i temperatura do dziesiątych części stopnia), oba czujniki na 1-Wire, do tego UART (do komunikacji z programem monitorującym temperaturę i informacją o ewentualnym błędzie czujnika bądź jego braku). Całość postawiona na Atmega8, zostało dużo wolnego miejsca w pamięci oraz kilka porów.
REKLAMA