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

Atmega8 i zmienne float - problemy

krzysztofh 04 Mar 2010 11:41 3955 28
  • #1 7781756
    krzysztofh
    Poziom 29  
    Piszę program na Atmegę8 z zastosowaniem przetwornika ADC i poległem na zmiennych.
    Będę odczytywał wartość napięcie w zakresie 0,370 - 0,693 i zamieniał je na temperaturę.
    Zdjąłem charakterystykę f(v) = mc+b i próbuję zaprząc procesor do działania.
    Na razie przetwornik (chyba) nie ruszył, bo nie widać zmian na wyświetlaczu, ale póki co zająłem się sprawdzaniem obliczeń i mam podejrzenia że procek nie radzi sobie ze zmiennymi typu float.

    oto fragmenty programu:
    
    void adc_init(void)
    {
      ADMUX |= _BV(REFS0);
      ADMUX |= _BV(REFS1);
      ADMUX |= _BV(ADLAR);
      ADCSRA |= _BV(ADEN);
      ADCSRA |= _BV(ADPS0); 
      ADCSRA |= _BV(ADPS1); 
    //  ADMUX = 0;
    }
    
    int getadc(uint8_t channel)
    {
      ADMUX |= channel;
      ADCSRA |= _BV(ADSC);
      while (ADCSRA & _BV(ADSC)) {}
      return(ADCW);
    }
    
    int convert(int value)
    {
      static const float c1=20.0;
      static const float v1=0.693; 
      static const float c2=200.0;
      static const float v2=0.370;
      float  m = (v2-v1) / (c2-c1);
      return ((int)((float)value -(v1 - m*c1))/m);
    }
    
    int main(void)
    {
      port_init();
      adc_init();
      while(1)
      {
        _delay_ms(100);
        pomiar = getadc(0);
        temp_current = convert2degree(pomiar);
      }
    }
    


    To fragment programu, jak wcześniej wspomniałem.
    Wynik jest wyświetlany na trzech LEDach siedmiosegmentowych z multipleksowaniem sterowanym przerwaniem procesora.
    Wyświetlanie jest poprawne jeżeli się poda bezpośrednio zmienną.
    Natomiast wyświetlenie np m w postaci np a=1/m daje wynik 211, a powinno być 577
    Wykonywałem też inne sprawdzenia z deklarowanymi stałymi i tez jest źle:

    float a = v1*10;, temp_current = a*100; = 181 czyli źle
    float a = v1*100; temp_current = a; = 69 czyli dobrze.

    Zastanawiam się nad wymnożeniem stałych v1 i v2 o tysiąc czyli podawania mV, a nie V, a później podawanie do funkcji convert wartości pomiar*1000.

    Za wszelką pomoc z góry dziękuję, bo w kwestii programowania uC orłem nie jestem.
    Przeczytałem sporo wątków na Elektrodzie ale jednoznacznej odpowiedzi nie mam.
  • #2 7781814
    loocasm
    Poziom 15  
    Nigdzie nie podałeś (albo ja niedowidzę) jakiego typu jest zmienna temp_current ... a to pewnie jest przyczyną problemów
  • #4 7781911
    loocasm
    Poziom 15  
    Przy mnożeniu floatów przez stałe często widzi się "na siłę" robione ułamki, np. zamiast a*100 -- a*100.0, zaś przy przypisaniu tego do inta można jeszcze to rzutować... Może takie niepozorne zabiegi pomogą
  • #5 7781956
    Konto nie istnieje
    Konto nie istnieje  
  • #6 7782027
    loocasm
    Poziom 15  
    Swoją drogą też racja :)
  • #7 7836161
    krzysztofh
    Poziom 29  
    Po powrocie z urlopu uruchomiłem przetwornik. Program na razie w okrojonej wersji aby przetestować sam ADC i wyświetlanie na trzech LEDach siedmiosegmentowych.

    Przetwornik w zasadzie pracuje i pokazuje prawidłową wartość, ale dwie kwestie mnie niepokoją i prosił bym o pomoc w ich wyjaśnieniu.

    Otóż, pierwsza sprawa to wahania napięcia. Podczas pracy przetwornika napięcie zasilające waha się w sposób cykliczny, czyli to nie jest sprawa stabilności zasilacza, bo po wyłączeniu przetwornika napięcie jest bardzo stabilne. Zmiany następują mniej więcej co 1-1,5s. Te wahania przekładają się oczywiście na pomiar, bo napięcie na wejściu PC0, gdzie dokonywany jest pomiar zmienia się na trzecim miejscu po przecinku i to objawia się skakaniem ostatniej cyfry na wyświetlaczu. Na podłączonym mierniku widać te zmiany także. Ewidentnie praca przetwornika wpływa jakoś na tą kwestię.
    Druga sprawa to odczyt wartości "pomiar" w przerwaniu. Jak odczytuję wynik 8-bitowy (ADCH) to przy ustawieniu 0,2V na wejściu jest na wyświetlaczu 20 czyli OK.
    Jak odczytuję z dokładnością 10-cio bitową (ADCW) wyświetlacz pokazuje zmieniające się dziwne wartości niewiele mające wspólnego z tym co powinno być. W przedmiotowym przykładzie powinienem odczytać wartość 80, a tak nie jest.

    Wejście PC0 jest podpięte poprzez rezystor 10k do suwaka precyzyjnego potencjometru, który pozwala na zmiany napięcia na suwaku w zakresie 0-1V
    Pin AREF ma podpięty C 100n.

    Do pinów zasilania Atmegi z obu stron są podpięte kondensatory 100n oraz na wejściu zasilania jest 100µ.
    Całość jest zmontowana na płytce stykowej bez specjalnego bałaganu.

    Poniżej część programu. Usunąłem fragmenty mniej istotne aby nie zaciemniać, np sterowanie przyciskami itd. w każdym razie bez wpływu na działanie programu.

    
    
    #define F_CPU 1000000L     //1 Mhz
    #include <avr/io.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <util/delay.h>
    #include<avr/interrupt.h>
    #include <wyswietlacze.h>
    
    
    volatile uint8_t wysw = 0;     // zmienna przechowująca numer aktywnego wyświetlacza
    volatile uint8_t temp_sto;     // zmienna przechowująca setki temp
    volatile uint8_t temp_dzi;     // zmienna przechowująca dziesiątki temp
    volatile uint8_t temp_jed;     // zmienna przechowująca jedności temperatury
    int temp_set = 100;           // zmienna przechowująca temp ustawianą
    volatile uint8_t temp_wysw = 0; // zmienna przechowująca temp do wyświetlenia
    volatile uint16_t pomiar;     // wartość zmierzona po konwersji
    float wynik;
    int temp_current;
    
    void port_init(void)
    {
    DDRD=0xff;     //Port PD0-PD7 jako wyjścia                   /11111111
    PORTD=0x7f;    //Port PD0-PD6 podciągnięciem do jedynki      /01111111
    DDRB=0x0f;     //Port PB0-PB3 jako wyjścia,                  /001111
    PORTB=0x37;    //Port PB0-PB2, PB4-PB5 z podciągnięciem do 1 /110111
    DDRC=0x3e;     //Port PC0 jako wejście                       /111110
    PORTC=0x20;    //Port PC5 z podciągnięciem do 1              /100000
    
    }
    
    void adc_init(void)
    {
      ADMUX |= _BV(REFS0);  //wewnętrzne źródło referencyjne 2,56V z zewnętrznym C podpiętym do pinu AREF
      ADMUX |= _BV(REFS1);  //"REFS0" = 1 i "REFS1" = 1
      ADMUX |= _BV(ADLAR);  //zapis wyniku z wyrównaniem do lewej (osiem starszych bitów wyniku w rejestrze ADCH)
      ADCSRA |= _BV(ADEN);  //zezwolenie na konwersję - aktywacja przetwornika ADC "ADEN"
      ADCSRA |= _BV(ADPS0); 
      ADCSRA |= _BV(ADPS1); //preskaler 8 "ADPS0 i ADPS1 = 1" 
      ADCSRA |= _BV(ADIE);  //włączenie przerwania od zakończenia konwersji
      ADCSRA |= _BV(ADFR);  //free running
      ADMUX &= ~_BV(MUX0);  //wybór wejścia pomiarowego "ADMUX" = 0
      ADCSRA |= _BV(ADSC);  //start konwersji
    }
    
    
    int main(void)
    {
      port_init();
      adc_init();
    
      TCCR1B |= (1 << WGM12);  // Ustawia timer1 w tryb CTC
      OCR1A = 625;             // Ustawia wartość pożądaną na 200Hz dla preskalera 8
      TCCR1B |= (1 << CS11);   // Ustawia timer1 z preskalerem Fcpu/8
      TIMSK |= (1 << OCIE1A);  // Zezwolenie na przerwania dla CTC
      sei();                   // Zezwolenie globalne na przerwania
    
    int i;
      while(1)
      {
        _delay_ms(100);
    
        temp_current = pomiar;
        _delay_ms(400);
    
        temp_wysw = temp_current;
        temp_sto = (temp_wysw / 100);
        temp_dzi = (temp_wysw / 10)%10;
        temp_jed = temp_wysw % 10;
      }
    }
    
    ISR(ADC_vect)
    {
       pomiar = ADCH;
    }
    
    
    ISR(TIMER1_COMPA_vect)  //wywołanie przerwania
    {
      switch (wysw)
      {
        case 0:
          PORTB &= ~(1 << 2);     //zeruje bit 2 portu B - włącza wyświetlacz nr 3
          PORTB |= (1 << 1);      //ustawia bit 1 portu B - wyłącza wyświetlacz nr 2
          PORTB |= (1 << 0);      //ustawia bit 0 portu B - wyłącza wyświetlacz nr 1
          PORTD = cyfra(temp_sto);
          wysw++;
        break;
    
        case 1:
          PORTB |= (1 << 2);      //ustawia bit 2 portu B - wyłącza wyświetlacz nr 3
          PORTB &= ~(1 << 1);     //zeruje bit 1 portu B - włącza wyświetlacz nr 2
          PORTB |= (1 << 0);      //ustawia bit 0 portu B - wyłącza wyświetlacz nr 1
          PORTD = cyfra(temp_dzi);
          wysw++;
        break;
    
        case 2:
          PORTB |= (1 << 2);      //ustawia bit 2 portu B - wyłącza wyświetlacz nr 3
          PORTB |= (1 << 1);      //ustawia bit 1 portu B - wyłącza wyświetlacz nr 2
          PORTB &= ~(1 << 0);     //zeruje bit 0 portu B - włącza wyświetlacz nr 1
          PORTD = cyfra(temp_jed);
          wysw=0;
        break;
      }
    }
    
  • Pomocny post
    #8 7837168
    flapo213
    Poziom 21  
    Mała sugestia odnośnie pomiaru i wahania odczytów. Nie tak dawno robiłem odczyt z czujnika temperatury (termistora 5%), irytowała mnie strasznie szalejąca wartość pomiaru. W pierwszej chwili zrobiłem na szybko uśrednianie na podstawie zwykłej średniej arytmetycznej postaci:

    wynik = (N_0+N_1+...N_n)/n,

    ale nie dało mi to zadowalającego efektu. Aby w tego typu uśrednianiu uzyskać zadowalający efekt trzeba by uśredniać ogromną liczbę pomiarów, co niesie za sobą oczywiście czasochłonność oraz długie oczekiwanie na wynik. Przedstawię Ci prosty sposób na zniwelowanie tego przykrego efektu.

    Cały widz polega na tym że z przetwornika otrzymujesz wartości np takie :

    0x60, 0x61, 0x68, 0x55, 0x67, 0x60, 0x63, 0x60, 0x,80, 0x76, 0x65, 0x60 itd.

    Dla ułatwienia dodam że 0x60 jest wartością poprawną. Jak zastosujesz średnią arytmetyczną to nie będzie ona dla tylu pomiarów równa 0x60 tylko trochę więcej.

    Co zrobić w takim razie, odpowiedź jest prosta skoro przetwornik odczytał Ci 4 razy 0x60 to prawdopodobnie jest to wartość oczekiwana. Oczywiście może się zdarzyć identyczna liczba tych samych wartości. Więc napisz sobie funkcyjkę która będzie Ci przeszukiwać zbiór np 20 elementów (czyli 20 odczytów) i wybierze Ci odpowiednią najbardziej powtarzającą się wartość. Ja napisałem taką funkcjkę tylko ze zmienną liczbą próbek bo im więcej tym lepiej, dobry efekt uzyskuje się już przy 30 - 50 próbek. Wiesz zawsze to lepiej niż 1000 uśrednionych wartości. Uzyskałem stabilność lepszą niż DS18x20 (stabilność nie oznacza dokładności,tylko wahania pomiarów).

    Niestety dodam że napisanie takiego algorytmu nie jest proste, aby to dobrze zrobić należy wykonać blokowy algorytm najlepiej a później kod w C.

    Pozdrawiam i życzę sukcesów
  • #9 7837493
    Kabuto15
    Poziom 19  
    A propos uśredniania , filtry medianowe również mogą być przydatne.
  • #10 7839193
    krzysztofh
    Poziom 29  
    Dzięki za sugestie. Może będę musiał tak zrobić, choć nie wiem jak się do tego zabrać, ale nurtuje mnie ten temat, gdyż zmieniające się okresowo pomiary wynikają ewidentnie z wahań napięcia, które obserwuje na mierniku podpiętym do suwaka potencjometru. Tam zmiany są w miliwoltach, ale wartość zasilania VCC waha się bardziej. Problem ustaje po wyłączeniu przetwornika. Początkowo myślałem, że niewydala zasilacz i wahania wynikają ze zmian obciążenia przy różnej liczbie zapalanych segmentów LED, ale dokonywałem zmian wartości wyświetlanych na LEDach poprzez ustawianie zmiennej za pomocą przycisków - docelowo służą one do ustawiania temperatury w piecu - i o dziwo przy wyłączonym przetworniku napięcie było stabilne. Czy praca przetwornika może powodować jakieś zjawisko wzbudzania się układu lub coś podobnego?
    Czasami obserwowałem okresy kilku sekundowe bez wahań i wtedy wyświetlacz stabilnie pokazywał jedną wartość.
    Co to może być?
  • #11 7839699
    Kabuto15
    Poziom 19  
    Może być kilka przyczyn, żeby je wyeliminować można: zastanowić się nad prowadzeniem ścieżek przetwornika, filtracja zasilania - niskoczęstotliwościowa (dodać kondensatorów o sporej pojemności za stabilizatorem), użyć źródła referencyjnego dla przetwornika (np. na TL431).
  • #12 7839782
    krzysztofh
    Poziom 29  
    Kabuto15 napisał:
    Może być kilka przyczyn, żeby je wyeliminować można: zastanowić się nad prowadzeniem ścieżek przetwornika, filtracja zasilania - niskoczęstotliwościowa (dodać kondensatorów o sporej pojemności za stabilizatorem), użyć źródła referencyjnego dla przetwornika (np. na TL431).


    Na razie jest na płytce stykowej.
    Co do kondensatora to o ile wiem, raczej nie jest wskazane pakowanie większej pojemności niż 100µ po stronie wyjściowej 7805.
    Tak czy inaczej jeszcze będę robił próby na innym zasilaczu.
  • #13 7839962
    flapo213
    Poziom 21  
    Jest jeszcze jedna rzecz którą powinieneś rozważyć przy projektowaniu płytki, mianowicie pamiętaj o tym iż przetwornik ma wewnętrzne podciąganie na liniach ADC więc twój pomiar może być niewiarygodny. Najlepiej zastosuj wzmacniacz operacyjny w funkcji wtórnika a napięcie referencyjne pociągnij z AREF które równa sie 2,56V.

    Masz tu kawałek schematu:


    Atmega8 i zmienne float - problemy
  • #14 7839977
    Kabuto15
    Poziom 19  
    Mnie uczono, że pojemności nigdy za mało. Duże pojemności zmniejszają tętnienia napięcia. I dodatkowo należy pamiętać o 100nF blisko nóżek układu w celu filtracji zakłóceń wysokoczęstotliwościowych. Choć w Twoim przypadku oczywiście może występować inny problem. Napisz jaki masz pobór prądu i ile bierze przetwornik.

    I tak jak pisze flapo213 ATmega8 ma skopane AREF (poprawka: VCC) bo wewnętrznie podłączone jest w AVCC (błąd całej serii kontrolerów). Może zamień go na ATmega16.
  • #15 7840010
    Konto nie istnieje
    Poziom 1  
  • #16 7840072
    Kabuto15
    Poziom 19  
    Sorki za pomyłkę. Chodziło mi o nie odseparowanie AVCC i VCC oczywiście.
  • #17 7840200
    Konto nie istnieje
    Poziom 1  
  • #18 7840314
    michalko12
    Specjalista - Mikrokontrolery
    atom1477 napisał:
    No.
    A wracając do tematu: Wyniki nie powinny aż tak skakać. Mi zwykle nie skaczą bardziej niż o 1...2LSB (a w układach pracują przetwornice impulsowe, silniki sterowane przez PWM, czy inne cuda).
    krzysztofh: masz źle poprowadzona masę pewnie.


    Jestem ciekaw, ile osób zwraca uwagę na impedancję wejścia przetwornika, a może inaczej, zalecaną impedancję źródła sygnału.
  • #19 7840484
    Konto nie istnieje
    Poziom 1  
  • #20 7840634
    michalko12
    Specjalista - Mikrokontrolery
    atom1477 napisał:
    Pewnie niewiele.
    A po co mnie zacytowałeś?
    Ja akurat nie stosuję żadnych wtórników a wyniki mam dobre.

    To miało się tylko do tego:
    Cytat:
    krzysztofh: masz źle poprowadzona masę pewnie.

    i nie chodziło konkretnie o Ciebie tylko o to, że w większości przypadków obwiniane jest zasilanie, a temat jest trochę bardziej złożony.

    Podstawą jest zrozumienie działa takiego przetwornika. W parametrach ADC podawana jest rezystancja wejścia na poziomie setek MΩ i tak naprawdę parametr ten na niewiele się zdaje. Dużo mówiącymi są ten rysunek:
    Atmega8 i zmienne float - problemy
    i ten akapit:
    Cytat:
    The ADC is optimized for analog signals with an output impedance of approximately 10kΩ or
    less. If such a source is used, the sampling time will be negligible. If a source with higher imped-
    ance is used, the sampling time will depend on how long time the source needs to charge the
    S/H capacitor, with can vary widely. The user is recommended to only use low impedant sources
    with slowly varying signals, since this minimizes the required charge transfer to the S/H
    capacitor.


    W tym przypadku może pomóc niewielki kondensator na wejściu (1n) powodujący to, że CS/H będzie miał skąd pobierać ładunek ograniczając przy tym szumy samplowania na wejściu.
  • #21 7840943
    krzysztofh
    Poziom 29  
    Dziękuję wszystkim za podpowiedzi. Jak czas pozwoli to dzisiaj wieczorkiem będą dalsze próby z ustabilizowaniem przetwornika i pomiaru.
    O efektach nie omieszkam napisać.

    Edit.

    Informuję szanowne grono wsparcia, że po poprawieniu jakości zasilania układu oraz dodania C 100n pomiędzy PC0 i GND przetwornik się uspokoił. Cyfry na wyświetlaczu nie skaczą.
    Dzięki za pomoc.
    Teraz czas na dalsze zmiany w projekcie.
  • #22 7858166
    Konto nie istnieje
    Poziom 1  
  • #23 7858175
    krzysztofh
    Poziom 29  
    Szanowni koledzy proszę znów o pomoc w interpretacji wartości odczytu przetwornika.
    Otóż, jak dokonuje pomiaru 8-bitowego (ADCH) wynik wyświetlany jest na wyświetlaczy poprawnie. Np. dla wartości 0,685V zmierzone multimetrem przetwornik pokazuje wartość 0,68 czyli poprawnie gdyż (0,685*256)/2,56. Jeżeli tą wartość pomnożę przez dziesięć, czyli już przestanie się mieścić w zmiennej 8-bitowej wynik jest wyświetlany jako 680 czyli OK, bo zmienne w programie odpowiedzialne za przechowywanie wartości z przetwornika są 16-bitowe.
    Jeżeli natomiast zwiększę precyzję odczytu do 10 bitów (ADCW), najstarsza cyfra jest wyświetlana w postaci bliżej nieokreślonej, tzn wyświetlają się segmenty, które nie przypominają cyfr. Dwie młodsze cyfry wyświetlane są poprawnie, choć niewiele mają wspólnego z poprawnym wynikiem. Jak wartość odczytu podzielę przez 100 lub 50 pojawiają się poprawne cyfry, choć sam wynik jest oczywiście dla mnie nie zrozumiały. W przedmiotowym przykładzie powinna się wyświetlić liczba 274, bo (0,685*1024)/2,56.
    Bardzo proszę o pomoc bo dalej nie mogę ruszyć z projektem, którym ma być piec z regulacją i stabilizacją temperatury. W załączeniu schemat.
    
    
    #define F_CPU 1000000L     //1 Mhz
    #include <avr/io.h>
    #include <inttypes.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <wyswietlacze.h>
    
    
    volatile uint8_t wysw = 0;     // zmienna przechowująca numer aktywnego wyświetlacza
    volatile uint8_t temp_sto;     // zmienna przechowująca setki temp
    volatile uint8_t temp_dzi;     // zmienna przechowująca dziesiątki temp
    volatile uint8_t temp_jed;     // zmienna przechowująca jedności temperatury
    int temp_set = 100;           // zmienna przechowująca temp ustawianą
    volatile uint16_t temp_wysw = 0; // zmienna przechowująca temp do wyświetlenia
    volatile uint16_t pomiar;      //zmienna przechowująca wartość po konwersji
    float wynik;
    volatile uint16_t temp_current;  // zmienna przechowująca temp zmierzoną po konwersji
    
    void port_init(void)    //inicjalizacja portów
    {
    DDRD=0xff;     //Port PD0-PD7 jako wyjścia                   /11111111
    PORTD=0x7f;    //Port PD0-PD6 podciągnięciem do jedynki      /01111111
    DDRB=0x0f;     //Port PB0-PB3 jako wyjścia,                  /001111
    PORTB=0x37;    //Port PB0-PB2, PB4-PB5 z podciągnięciem do 1 /110111
    DDRC=0x3e;     //Port PC0 jako wejście                       /111110
    PORTC=0x20;    //Port PC5 z podciągnięciem do 1              /100000
    
    }
    
    void adc_init(void)
    {
      ADMUX |= _BV(REFS0);  //wewnętrzne żródło referencyjne 2,56V z zewnętrznym C podpiętym do pinu AREF
      ADMUX |= _BV(REFS1);  //"REFS0" = 1 i "REFS1" = 1
      ADMUX |= _BV(ADLAR);  //wybór sposobu zapisu wyniku z wyrównaniem do lewej (osiem starszych bitów wyniku w rejestrze ADCH)
      ADCSRA |= _BV(ADEN);  //zezwolenie na konwersję - aktywacja przetwornika ADC "ADEN"
      ADCSRA |= _BV(ADPS0); //preskaler 
      ADCSRA |= _BV(ADPS1); //preskalerem 8 "ADPS0 i ADPS1 = 1" 
      ADCSRA |= _BV(ADIE);  //włączenie przerwania od zakończenia konwersji
      ADCSRA |= _BV(ADFR);  //free running
      ADMUX &= ~_BV(MUX0);  //wybór wejścia pomiarowego "ADMUX" = 0
      ADCSRA |= _BV(ADSC);  //start konwersji
    }
    
    void led_init(void)
    {
      TCCR1B |= (1 << WGM12);  // Ustawia timer1 w tryb CTC
      OCR1A = 625;             // Ustawia wartość pożądaną na 200Hz dla preskalera 8
      TCCR1B |= (1 << CS11);   // Ustawia timer1 z preskalerem Fcpu/8
      TIMSK |= (1 << OCIE1A);  // Zezwolenie na przerwania dla CTC
    }
    
    
    int main(void)
    {
      port_init();
      adc_init();
      led_init();
      sei();
    
      while(1)
      {
        _delay_ms(100);
    
        temp_current = pomiar;
        temp_wysw = temp_current;
        temp_sto = (temp_wysw / 100);
        temp_dzi = (temp_wysw / 10)%10;
        temp_jed = temp_wysw % 10;
    
    
      }
    }
    
    ISR(ADC_vect)
    {
       pomiar = ADCW;
    }
    
    
    ISR(TIMER1_COMPA_vect)
    {
      switch (wysw)
      {
        case 0:
          PORTB &= ~(1 << 2);     //zeruje bit 2 portu B - włącza wyświetlacz nr 3
          PORTB |= (1 << 1);      //ustawia bit 1 portu B - wyłącza wyświetlacz nr 2
          PORTB |= (1 << 0);      //ustawia bit 0 portu B - wyłącza wyświetlacz nr 1
          PORTD = cyfra(temp_sto);
          wysw++;
        break;
    
        case 1:
          PORTB |= (1 << 2);      //ustawia bit 2 portu B - wyłącza wyświetlacz nr 3
          PORTB &= ~(1 << 1);     //zeruje bit 1 portu B - włącza wyświetlacz nr 2
          PORTB |= (1 << 0);      //ustawia bit 0 portu B - wyłącza wyświetlacz nr 1
          PORTD = cyfra(temp_dzi);
          wysw++;
        break;
    
        case 2:
          PORTB |= (1 << 2);      //ustawia bit 2 portu B - wyłącza wyświetlacz nr 3
          PORTB |= (1 << 1);      //ustawia bit 1 portu B - wyłącza wyświetlacz nr 2
          PORTB &= ~(1 << 0);     //zeruje bit 0 portu B - włącza wyświetlacz nr 1
          PORTD = cyfra(temp_jed);
          wysw=0;
        break;
      }
    }
    
  • #24 7859168
    michalko12
    Specjalista - Mikrokontrolery
    temp_tys = temp_wysw /1000;
    temp_wysw %= 1000;
    temp_sto = temp_wysw /100;
    temp_wysw %= 100;
    temp_dzi = temp_wysw /10;
    temp_jed = temp_wysw %10; 


    I dodatkowo masz błąd w funkcji cyfra(), która nie zabezpiecza się przed argumentem >=10 i stąd te dziwne znaczki na wyświetlaczu.
    Jeśli temp_wysw wynosiło >=1000 funkcja cyfra przyjmowała wartości powyżej 9 i prawdopodobnie pobierałeś patern spoza tablicy, domniemam bo nie pokazałeś tej funkcji.
  • #25 7861791
    krzysztofh
    Poziom 29  
    michalko12 - chyba podążamy w dobrym kierunku ale wyników nie potrafię dalej zinterpretować.

    poprawiłem kod i teraz jest tak (tylko część główna):
    
    int main(void)
    {
      port_init();
      adc_init();
      led_init();
      sei();
    
      while(1)
      {
        _delay_ms(100);
    
        temp_current = pomiar;
        temp_wysw = temp_current;
        
        if (temp_wysw > 999)
        {
          temp_sto = (temp_wysw / 100) % 10;
          temp_dzi = (temp_wysw / 10) % 10;
          temp_jed = temp_wysw % 10;
        }
        else
        {
          temp_sto = (temp_wysw / 100);
          temp_dzi = (temp_wysw / 10)%10;
          temp_jed = temp_wysw % 10;
        }
      }
    } 
    
    


    Wyświetlacze są trzy więc nie obsługuje tysięcy. Z resztą nie spodziewam się temperatur większych niż 200°C. Teraz wyświetlane jest oczywiście mierzone przez przetwornik napięcie póki co.

    Krzaki z ostatniej cyfry zniknęły i wyświetlane liczby zdają się pokazywać pewną prawidłowość, która jest niestety dla mnie tajemnicą.
    Otóż ogrzewam w ręku czujnik (dioda na kawałku radiatora) i obserwuję napięcie na niej (multimetr). Zmiany są w zakresie 0,665V - 0,685V. Tym zmianom odpowiadają liczby na wyświetlaczu (128, 256, 320, 384, 448, 512, 576, 640, 704). Oczywiście cyfry migają wraz ze wzrostem temperatury, ale daje się zaobserwować, że dla określonej temp (napięcia wejściowego) wyświetlana cyfra zachowuje się przez pewien czas stabilnie, co jest zrozumiałe.
    Ciekawie wyglądają te cyfry jeżeli przedstawi się je binarnie:
    128 - 0000 0000 1000 0000 (najwyższa temp, najniższe napięcie na diodzie)
    256 - 0000 0001 0000 0000
    320 - 0000 0001 0100 0000
    384 - 0000 0001 1000 0000
    448 - 0000 0001 1100 0000
    512 - 0000 0010 0000 0000
    576 - 0000 0010 0100 0000
    640 - 0000 0010 1000 0000
    704 - 0000 0010 1100 0000 (najwyższe napięcie z podanego wcześniej zakresu)

    Po dodaniu warunku rozpoznającego liczbę zapisaną w zmiennej temp_current, cyfry, a właściwie najstarsza cyfra przynajmniej zaczęła wyglądać normalnie, co mogłoby wskazywać na liczbę większą od tysiąca. Ale skąd tam taka liczba, skoro dopiero w okolicy napięcia 2,5V powinna się pojawić wartość 1000, a ja podaję dużo mniejsze wartości na wejście ACD.

    Dla jasności podam zawartość pliku wyswietlacze.h
    
    ////////////////////////////////////////////////////////////////////////////////
    // wyswietlacze.h - plik nagłówkowy z przydatnymi funkcjami do
    //                  obsługi wyświetlaczy  
    // wersja: 1.0              data: 7 marca 2008
    // zmieniona przez Krzysztof
    // autor oryginału: Ravender 
    ////////////////////////////////////////////////////////////////////////////////
    // Funkcje
    //
    // uint8_t cyfra(uint8_t) - zwraca podaną cyfrę z pamięci danych
    //
    //Plik ten należy umieścić w folderze \avr\include
    
    #include <avr/pgmspace.h>
    #include <inttypes.h>
    
    #define A 0
    #define B 1
    #define C 2
    #define D 3
    #define E 4
    #define F 5
    #define G 6
    
    prog_uint8_t cyfry[11] PROGMEM = {
    (1<<G),                                           //0 (PD0-PD6) low
    (1<<A)|(1<<D)|(1<<E)|(1<<F)|(1<<G),               //1 (PD1-PD2) low
    (1<<C)|(1<<F),                                    //2 (PD0-PD2, PD3-PD4, PD6) low
    (1<<E)|(1<<F),                                    //3 (PD0,PD3, PD6) low
    (1<<A)|(1<<E)|(1<<D),                                    //4 (PD1-PD2, PD5-PD6) low
    (1<<B)|(1<<E),                                    //5 (PD0,PDB2-PD3, PD5-PD6) low
    (1<<B),                                           //6 (PD0, PD2-PD6) low
    (1<<D)|(1<<E)|(1<<F)|(1<<G),                      //7 (PD0-PD2) low
    (0<<A)|(0<<B)|(0<<C)|(0<<D)|(0<<E)|(0<<F)|(0<<G), //8 (PD0-PD6) low
    (1<<E),                                           //9 (PD0-PD3, PD5-PD6) low
    (1<<A)|(1<<B)|(1<<C)|(1<<D)|(1<<E)|(1<<F)|(1<<G), //  (PD0-PD6) high - wygaszenie wyświetlacza
    };
    
    uint8_t cyfra(uint8_t cyfra)
    {
      return pgm_read_byte(&cyfry[cyfra]);
    }
    
  • #26 7861912
    michalko12
    Specjalista - Mikrokontrolery
    Nie wiem co kombinujesz z diodą i jak masz ją podłączoną. Bez dodatkowych wzmacniaczy nie obejdzie się, a w tym kodzie co podesłałeś był mały błąd. To co poniżej powinno wystarczyć.


    int main(void)
    {
      port_init();
      adc_init();
      led_init();
      sei();
    
      while(1)
      {
        _delay_ms(100);
    
        temp_current = pomiar;
        temp_wysw = temp_current;
       
          temp_sto = (temp_wysw / 100) % 10;
          temp_dzi = (temp_wysw % 100) / 10;    // temp_dzi = (temp_wysw / 10) % 10;
          temp_jed = temp_wysw % 10;
    
      }
    }

    Właśnie zauważyłem schemat, zbyt dobrej rozdzielczości to ty nie uzyskasz przy takim podłączeniu. W każdym bądź razie takie liczby powinny wyświetlać się w zależności od napięcia, nie biorąc pod uwagę błędów konwersji i odchyłki napięcia referencyjnego .

    U(V)		ADC
    ----------------
    0,65 		260
    0,655		262
    0,66 		264
    0,665		266
    0,67 		268
    0,675		270
    0,68 		272
    0,685		274
    0,69 		276
    


    i jeszcze jedno, zmień to na wyrównanie do prawej
    Cytat:
    ADMUX |= _BV(ADLAR); //wybór sposobu zapisu wyniku z wyrównaniem do lewej (osiem starszych bitów wyniku w rejestrze ADCH)
  • #27 7862096
    krzysztofh
    Poziom 29  
    Zakładając, że mamy do czynienia z liczba 4-cyfrową to ten kod by wystarczył, ale ja spodziewam się trzycyfrowej.

    Swoją drogą dla liczby 4-cyfrowej obie wersje kodu są chyba poprawne np:
    1724
    temp_dzi = (temp_wysw % 100) / 10;
    1724 %100 = 24, 24 / 10 = "2"
    temp_dzi = (temp_wysw / 10) % 10;
    1724 /10 = 172, 172 %10 - "2"

    Sprawa podanych przez Ciebie spodziewanych wyników jest jak najbardziej słuszna, ale jak już podałem na wyświetlaczu widać co innego i z tym nie bardzo mogę sobie poradzić.

    A dlaczego do prawej.
    W takim razie nie będę mógł odczytać całego rejestru (chyba).
  • Pomocny post
    #28 7862249
    michalko12
    Specjalista - Mikrokontrolery
    krzysztofh napisał:
    Zakładając, że mamy do czynienia z liczba 4-cyfrową to ten kod by wystarczył, ale ja spodziewam się trzycyfrowej.

    Swoją drogą dla liczby 4-cyfrowej obie wersje kodu są chyba poprawne np:
    1724
    temp_dzi = (temp_wysw % 100) / 10;
    1724 %100 = 24, 24 / 10 = "2"
    temp_dzi = (temp_wysw / 10) % 10;
    1724 /10 = 172, 172 %10 - "2"

    Sprawa podanych przez Ciebie spodziewanych wyników jest jak najbardziej słuszna, ale jak już podałem na wyświetlaczu widać co innego i z tym nie bardzo mogę sobie poradzić.

    A dlaczego do prawej.
    W takim razie nie będę mógł odczytać całego rejestru (chyba).


    A co to ma do odczytu rejestru? ADCW to odczyt 16 bitowy niezależnie od tego czy wynik jest dosunięty do prawej czy lewej.
    Wynik 260 dosunięty do prawej to 260, a do lewej to 16640
  • #29 7862278
    krzysztofh
    Poziom 29  
    Po zmianie tego bitu pojawiły się oczekiwane liczby. Prawie oczekiwane bo obarczone błędem konwersji i odchyłki napięcia referencyjnego, ale to już detal. Mam nadzieję że dalej sobie poradzę.

    Wielkie dzięki michalko12, bardzo mi pomogłeś.
REKLAMA