Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[ATMEGA8][C/AvrStudio] Pomiar temperatury zaburza multipl.

21 Sie 2010 15:45 2144 20
  • Poziom 8  
    Witam! //pierwszy post na forum, pierwsze poważne starcie z AVR

    Skonstruowałem następujący układ do pomiaru temperatury z dokładnością do jedności (st. Celsiusza):
    - ATMEGA8
    - Sensor DS18B20
    - Podwójny wyświetlacz siedmiosegmentowy; ot z rodziny takich: http://www.goldmine-elec-products.com/images/G17024B.jpg

    Wszystko działa dobrze (o tym za moment). Wyświetlacz jest poprawnie multipleksowany (obie cyfry wyświetlają się dobrze), sensor pobiera poprawną temperaturę.

    Wyświetlacz LED jest multipleksowany timerem o częstych przerwaniach (kilkadziesiąt Hz). Temperatura jest aktualizowana z sensora w timerze o długim czasie trwania (przerwanie co kilka sekund).

    Problem:

    W momencie, gdy uC wpada w przerwanie, które aktualizuje temperaturę, wyświetlacz przestaje się multipleksować (wyświetla jedną cyfrę). Trwa to ~0.5s. I wygląda brzydko (!), a potem wraca do normy na kolejny cykl. Jeśli jednak tylko rzucić okiem np. na YouTube, wiele filmów przedstawia taki sam układ, który działa "płynnie" (aktualizacja temperatury nie niszczy multipleksacji).

    Gdzie tkwi mój błąd (leży pewnie w założeniach timerów wyświetlacza oraz sensora, lecz nie mogę go odnaleźć)?

    Kod głównego pliku programu (jak już wspomniałem - wyświetlacz oraz sensor działają poprawnie - problem tkwi w synchronizacji obydwu procesów):

    Code:
    #include <stdlib.h>
    
    #include <avr/io.h>         
    #include <stdio.h>       
    #include <avr/interrupt.h>
    #include <compat/ina90.h>   
     
    #include "dualDisplay.h"
    #include "ds18b20.h"

    char temp[20] = "00";

    // SENSOR TIMER EVENT
    ISR(TIMER1_COMPA_vect)
    {
       therm_read_temperature(temp);
    }

    // LED MULTIPLEX TIMER EVENT
    ISR(TIMER0_OVF_vect)
    {
       showDigit(-1);
       if(PORTB == _BV(0)) {      
          showDigit((int)(atoi(temp)/10));
          PORTB |= _BV(1);
           PORTB &= ~_BV(0);
       }
       else {
          showDigit((int)(atoi(temp)%10));
          PORTB |= _BV(0);
          PORTB &= ~_BV(1);   
       }
    }

    void sensorTimerOn()
    {
       TCCR1B |= (1 << CS12) | (1 << CS10);
    }

    void ledMultiplexTimerOn()
    {
       TCCR0 |= (1 << CS01);
         TCNT0 = 0x00;
    }


    int main()
    {
       DDRD |= 0xff; //LCD
       DDRB |= (_BV(0) | _BV(1)); // LED DISPLAY

       therm_read_temperature(temp); // FIRST READING

       sensorTimerOn();
       ledMultiplexTimerOn();
       TIMSK = (1 << TOIE0) | (1 << OCIE1A); // ENABLE TIMERS
       
       sei();
       
       for(;;) {}

       return 0;
    }


    Dziękuję za pomoc!
    Pozdrawiam!
  • Pomocny post
    Poziom 15  
    Problem pewnie leży w procedurze.
    Code:
    therm_read_temperature(temp);
    Tam zapewne masz jakiegoś delay-a ustawionego (na czas potrzebny na konwersje temperatury przez DS-a). Musiał byś pokombinować, żeby przebudować tą funkcję a delay-a wbudować najlepiej w obsługę przerwań z timera z którego korzystasz.
    Np. Timer przerywa częściej, za każdym razem zwiększasz pewien licznik. Np. jedno przerwanie to 10 ms. Wysyłasz komendę konwersji do DS-a i czekasz 750 ms/10ms jakieś 75 przerwań z timera i odczytujesz zawartość ds-a. Nie korzystając z delay_ms.
  • Poziom 38  
    Sprawdź czy w #include "ds18b20.h" nie ma przypadkiem _delay ms(750);
    Albo w którymś pliku .c.
  • Poziom 8  
    enemyhilator napisał:
    Problem pewnie leży w procedurze.
    Code:
    therm_read_temperature(temp);
    Tam zapewne masz jakiegoś delay-a ustawionego (na czas potrzebny na konwersje temperatury przez DS-a). Musiał byś pokombinować, żeby przebudować tą funkcję a delay-a wbudować najlepiej w obsługę przerwań z timera z którego korzystasz.
    Np. Timer przerywa częściej, za każdym razem zwiększasz pewien licznik. Np. jedno przerwanie to 10 ms. Wysyłasz komendę konwersji do DS-a i czekasz 750 ms/10ms jakieś 75 przerwań z timera i odczytujesz zawartość ds-a. Nie korzystając z delay_ms.


    Faktycznie - to brzmi logicznie. Ale przepisanie poniższego kodu w taki sposób będzie bardzo trudne. Zastanawiam się więc nad równie prostym wyświetlaczem, który nie będzie wymagał multipleksacji (jakieś pomysły?)

    Code:
    #include <stdlib.h>
    
    #include <avr/io.h>         
    #include <stdio.h>
    #include <util/delay.h>
    #include "ds18b20.h"
    #include "dualDisplay.h"

    inline __attribute__((gnu_inline)) void therm_delay(uint16_t delay){
       while(delay--)
          asm volatile("nop");
    }

    uint8_t therm_reset(){
       uint8_t i;
       //Pull line low and wait for 480uS
       THERM_LOW();
       THERM_OUTPUT_MODE();
       therm_delay(us(240));
       //Release line and wait for 60uS
       THERM_INPUT_MODE();
       therm_delay(us(60));
       //Store line value and wait until the completion of 480uS period
       i=(THERM_PIN & (1<<THERM_DQ));
       therm_delay(us(240));
       //Return the value read from the presence pulse (0=OK, 1=WRONG)
       return i;
    }

    void therm_write_bit(uint8_t bit){
       //Pull line low for 1uS
       THERM_LOW();
       THERM_OUTPUT_MODE();
       therm_delay(us(1));
       //If we want to write 1, release the line (if not will keep low)
       if(bit) THERM_INPUT_MODE();
       //Wait for 60uS and release the line
       therm_delay(us(60));
       THERM_INPUT_MODE();
    }

    uint8_t therm_read_bit(void){
       uint8_t bit=0;
       //Pull line low for 1uS
       THERM_LOW();
       THERM_OUTPUT_MODE();
       therm_delay(us(1));
       //Release line and wait for 14uS
       THERM_INPUT_MODE();
       therm_delay(us(14));
       //Read line value
       if(THERM_PIN&(1<<THERM_DQ)) bit=1;
       //Wait for 45uS to end and return read value
       therm_delay(us(15));
       return bit;
    }

    uint8_t therm_read_byte(void){
       uint8_t i=8, n=0;
       while(i--){//Shift one position right and store read value
          n>>=1;
          n|=(therm_read_bit()<<7);
       }
       return n;
    }

    void therm_write_byte(uint8_t byte){
       uint8_t i=8;
       while(i--){
          //Write actual bit and shift one position right to make
          //the next bit ready
          therm_write_bit(byte&1);
          byte>>=1;
       }
    }

    void therm_read_temperature(char *buffer){
       // Buffer length must be at least 12bytes long! ["+XXX.XXXX C"]
       uint8_t temperature[2];
       int8_t digit;
       uint16_t decimal;
       //Reset, skip ROM and start temperature conversion
       therm_reset();
       therm_write_byte(THERM_CMD_SKIPROM);
       therm_write_byte(THERM_CMD_CONVERTTEMP);
       //Wait until conversion is complete
       while(!therm_read_bit());
       //Reset, skip ROM and send command to read Scratchpad
       therm_reset();
       therm_write_byte(THERM_CMD_SKIPROM);
       therm_write_byte(THERM_CMD_RSCRATCHPAD);
       //Read Scratchpad (only 2 first bytes)
       temperature[0]=therm_read_byte();
       temperature[1]=therm_read_byte();
          //therm_reset();
       //Store temperature integer digits and decimal digits
       digit=temperature[0]>>4;
       digit|=(temperature[1]&0x7)<<4;
       //Store decimal digits
          //decimal=temperature[0]&0xf;
          //decimal*=THERM_DECIMAL_STEPS_12BIT;
       //Format temperature into a string [+XXX.XXXX C]
       //sprintf(buffer, "%+d.%04u C", digit, decimal);
       sprintf(buffer, "%d", digit);
    }
  • Pomocny post
    Poziom 13  
    Pomyśl nad zastosowaniem rejestru przesuwnego. Np 74HC595. Można te układy łączyć. Potrzebne będą trzy linie procesora. Jedna do ustawienia wprowadzanego bitu druga to zegar rejestru przesuwnego a trzecia służy do przeniesienia danych z rejestru na wyjścia. Układ ma 8-io bitowe wyjście czyli do podwójnego wyświetlacza będą potrzebne dwie takie kostki. Jeśli chcesz to jutro jak znajdę chwilę czasu to Ci to rozrysuję jak podłączyć.
  • Poziom 8  
    kondziom napisał:
    Pomyśl nad zastosowaniem rejestru przesuwnego. Np 74HC595. Można te układy łączyć. Potrzebne będą trzy linie procesora. Jedna do ustawienia wprowadzanego bitu druga to zegar rejestru przesuwnego a trzecia służy do przeniesienia danych z rejestru na wyjścia. Układ ma 8-io bitowe wyjście czyli do podwójnego wyświetlacza będą potrzebne dwie takie kostki. Jeśli chcesz to jutro jak znajdę chwilę czasu to Ci to rozrysuję jak podłączyć.

    McMonster napisał:
    http://www.protostack.com/forum/blog.php?u=2&b=35

    Już ktoś to kiedyś rozrysował, nie trzeba się powtarzać.

    Wygląda doskonale! (a przynajmniej na pierwszy rzut oka) Z początkiem tygodnia zakupie te układy i dam znać, jak poszło ;-)
  • Poziom 32  
    Kup od razu kilka, rejestry przesuwające, a szczególnie 74595, to strasznie przydatna rzecz. Na pewnym znanym serwisie aukcyjnym u jednego użytkownika za niecałe 3 zł można kupić 5 sztuk w DIP lub SMD i trochę innych części elektronicznych w niskiej cenie.
  • Poziom 8  
    Sprawa rozwiązana - 2x 74HC595 załatwiło problem. Faktycznie - przydatne i proste zarazem układy. Teraz pozostał tylko stabilizator 7805 z 9v na 5v i można trawić płytkę.

    Pozdrawiam!
  • Moderator Mikrokontrolery Projektowanie
    A nie prościej było zamiast pakować dodatkowe scalaki, napisać porządnie obsługę 1-wire? Przecież za pomocą UARTa można to zrobić prawie całkowicie sprzętowo bez zaangażowania procesora. Tak naprawdę to poprawiając nieco twoje procedury do 1-wire można z łatwością zlikwidować ich wpływ na multipleksowanie.
  • Poziom 13  
    Ale do sterowania tymi układami wystarczy 3 linie a nie 10 i nie są one drogie. To zależy wszystko od tego co dla kogo jest prostszym rozwiązaniem i każdy ma swoje racje.
  • Moderator Mikrokontrolery Projektowanie
    Skoro pierwotnie starczało mu IO na multipleksowanie to przecież nagle mu ich nie ubyło, prawda? A zawsze układ bez 3 dodatkowych scalaków jest prostszy niż z nimi. Ale już pomijając nawet ten fakt, to sprawne procedury obsługi 1-wire i tak kiedys mu się przydadzą, więc poprawa tego co ma nie będzie czasem straconym.
  • Poziom 8  
    tmf napisał:
    Skoro pierwotnie starczało mu IO na multipleksowanie to przecież nagle mu ich nie ubyło, prawda? A zawsze układ bez 3 dodatkowych scalaków jest prostszy niż z nimi. Ale już pomijając nawet ten fakt, to sprawne procedury obsługi 1-wire i tak kiedys mu się przydadzą, więc poprawa tego co ma nie będzie czasem straconym.
    Starczało. Lecz podejście do sprawy mam czysto hobbystyczne, toteż chciałbym, aby było jak najprościej. A z tymi scalakami jest. Druga sprawa to to, iż układ będzie fancy-termometrem pokojowym zasilanym baterią 9v. Dzięki temu, że rejestry "trzymają wyświetlacz", mogę tylko co 20s wybudzać uC i aktualizować temperaturę, aby następnie ponownie go uśpić. Zawsze trochę mA "do przodu" (choć ogólnie nie jestem do końca przekonany, czy te rejestry same więcej nie pożerają ;P).

    Anyway - dzięki za podsunięte pomysły!
  • Poziom 16  
    Eee tam, panowie wszystko jest do zrobienia, ja tak przebudowałem funkcję DS,a że nie dość, że nie blokuje przerwań, to jeszcze mierzy i kontroluje 4 jego błędy, więc odrobinę chęci, pomyślunku i wszystko da się zrobić zamiast angażować kolejne niepotrzebne elementy do projektu. W skrócie, delaye zamienić na instrukcje odpowiednio napisane if, oraz odmierzanie czasu podpiąć pod odpowiednio spreparowany timer, co do błędów to wykorzystać timeouty, oraz kontrolę CRC. Pozdrawiam.
  • Moderator Mikrokontrolery Projektowanie
    overheat napisał:
    tmf napisał:
    Skoro pierwotnie starczało mu IO na multipleksowanie to przecież nagle mu ich nie ubyło, prawda? A zawsze układ bez 3 dodatkowych scalaków jest prostszy niż z nimi. Ale już pomijając nawet ten fakt, to sprawne procedury obsługi 1-wire i tak kiedys mu się przydadzą, więc poprawa tego co ma nie będzie czasem straconym.
    Starczało. Lecz podejście do sprawy mam czysto hobbystyczne, toteż chciałbym, aby było jak najprościej. A z tymi scalakami jest. Druga sprawa to to, iż układ będzie fancy-termometrem pokojowym zasilanym baterią 9v. Dzięki temu, że rejestry "trzymają wyświetlacz", mogę tylko co 20s wybudzać uC i aktualizować temperaturę, aby następnie ponownie go uśpić. Zawsze trochę mA "do przodu" (choć ogólnie nie jestem do końca przekonany, czy te rejestry same więcej nie pożerają ;P).

    Anyway - dzięki za podsunięte pomysły!


    No to żeś pojechał. Myślisz, że te rejestry prądu nie biorą? Zapewniam cię, że 3 rejestry biorą go więcej niż nieuśpiony procesor.
  • Poziom 13  
    W dokumentacji piszą że pobiera 80uA przy zasilaniu 6V i 25st Celsiusza. Ja bardziej martwiłbym się samym wyświetlaczem. Przecież on zje taką baterię w tydzień. Jakby bardzo ograniczyć jego prąd to dłużej ale wtedy w dzień nic nie będzie widać.
  • Poziom 36  
    McMonster napisał:
    http://www.protostack.com/forum/blog.php?u=2&b=35
    Już ktoś to kiedyś rozrysował, nie trzeba się powtarzać.


    Swego czasu robiłem podobny układ na rejestrach 74164, rezygnując z rezystorów (wykorzystując wewnętrzne rezystory scalaka). Układ się grzał porządnie, ale pracował kilka lat i się nie spalił. Do tego cały wyświetlacz wisiał na kilkunastometrowym kablu (był sterowany z procesora 8751).

    Jeszcze prościej można załatwić temat scalonym sterownikiem wyświetlacza na I2C - SAA1064.

    Zalety
    - dwie linie sterujące, zamiast trzech (standardowe I2C, które mogą sterować również inne układy)
    - brak oporników na segmentach
    - programowa regulacja jasności

    Wady - nieco wyższa cena

    http://www.datasheetcatalog.com/datasheets_pdf/S/A/A/1/SAA1064.shtml

    Układ może sterować bezpośrednio dwie cyfry lub w trybie multipleksowanym cztery (potrzebne dwa dodatkowe tranzystory przełączające pary cyfr).
  • Poziom 38  
    skąd bierzecie informacje o scalakach różnego typu? Zaraz ktoś wyjedzie ze sterownikiem pralki na SPI
  • Poziom 36  
    gaskoin napisał:
    skąd bierzecie informacje o scalakach różnego typu
    Elementarna umiejętność operowania wyszukiwarkami. Umiejętne operowanie przyciskiem "szukaj" jest cenniejsze od umiejętności pisania, czytania i liczenia ;) .
  • Poziom 8  
    marek_Łódź napisał:
    McMonster napisał:
    http://www.protostack.com/forum/blog.php?u=2&b=35
    Już ktoś to kiedyś rozrysował, nie trzeba się powtarzać.


    Swego czasu robiłem podobny układ na rejestrach 74164, rezygnując z rezystorów (wykorzystując wewnętrzne rezystory scalaka). Układ się grzał porządnie, ale pracował kilka lat i się nie spalił. Do tego cały wyświetlacz wisiał na kilkunastometrowym kablu (był sterowany z procesora 8751).

    Jeszcze prościej można załatwić temat scalonym sterownikiem wyświetlacza na I2C - SAA1064.

    Zalety
    - dwie linie sterujące, zamiast trzech (standardowe I2C, które mogą sterować również inne układy)
    - brak oporników na segmentach
    - programowa regulacja jasności

    Wady - nieco wyższa cena

    http://www.datasheetcatalog.com/datasheets_pdf/S/A/A/1/SAA1064.shtml

    Układ może sterować bezpośrednio dwie cyfry lub w trybie multipleksowanym cztery (potrzebne dwa dodatkowe tranzystory przełączające pary cyfr).
    Czym go zasilałeś/na ile starczało ogniwo (jeśli ogniwo)?
  • Poziom 36  
    overheat napisał:
    Czym go zasilałeś/na ile starczało ogniwo (jeśli ogniwo)?
    Który? Układ na 74164 nie bardzo się nadaje do zasilania bateryjnego. W przypadku SAA1064 pobór energii jest zależny bezpośrednio od jasnosci świecenia wyświetlacza, a dane można znaleźć w datasheet (link powyżej).
    kondziom napisał:
    Ja bardziej martwiłbym się samym wyświetlaczem. Przecież on zje taką baterię w tydzień. Jakby bardzo ograniczyć jego prąd to dłużej ale wtedy w dzień nic nie będzie widać.
    Nic dodać, nic ująć. Licząc delikatnie średni prąd 20mA (trochę przymało na dwie cyfry, chyba, że akurat będzie -1 stopień Celsjusza), bateria 2000mAh starczy na 4 doby, chyba że temperatura wyswietlana będzie na żądanie.