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

[AVR-GCC] Jak skonfigurować ADC w ATMega8 do odczytu potencjometru na LCD?

snow 17 Gru 2005 14:09 5375 11
REKLAMA
  • #1 2091723
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Próbuje uruchomić przetwornik A/C w tym procesorze i jakoś nie bardzo mi wychodzi. Chciałem zrobić żeby wartość zczytana z potencjometru była wysyłana na wyświetlacz. Wyświetlanie zmiennych i tekstu zdefiniowanych w programie chodzi, tylko czytanie przretwornika mi nie wychodzi:

    Mam dwa pliki nagłowkowe:

    1. lcd.h:

    #define LCD_H
    
    #include <avr/io.h>
    //
    #define LCD  PORTD
    #define E  1
    #define RS  0
    //
    #define SET_E   LCD |= _BV(E)
    #define CLR_E   LCD &= ~_BV(E)
    //
    #define SET_RS  LCD |= _BV(RS)
    #define CLR_RS  LCD &= ~_BV(RS)
    
    // funkcja opóźniająca o x*1ms
    void waitms(char x)
    {
    unsigned char a, b; // zmnienne licznikowe
    for( ; x > 0; --x) // ta pętla zostanie wykonana x-razy
      for(b = 10; b > 0; --b) // a ta 10 razy
    	for(a = 100; a > 0; --a) // natomiast ta 100 razy
    	__asm("nop"); // dodatkowa instrukcja opóźniająca o 1 cykl
    	// razem to da opóźnienie ok. x * 1ms
    	// x od 0 do 255
    }
    
    // pcodedura zapisu bajtu do wyświetlacza LCD
    // bez rozróżnienia instrukcja/dana
    void write_to_lcd(char x)
    {
    SET_E; // ustaw na E stan wysoki
    LCD = ((LCD & 0x0F) | (x & 0xF0)); // zapis pierwszej połówki bajtu
    CLR_E; // opadające zbocze na E -> zapis do wyświetlacza
    SET_E; // ustaw na E stan wysoki
    LCD = ((LCD & 0x0F) | ((x & 0x0F) << 4)); // zapis drugiej połowki bajtu
    CLR_E; // opadające zbocze na E -> zapis do wyświetlacza
    waitms(1); // czekaj 1ms
    }
    
    // procedura zapisu instrukcji do wyświetlacza LCD
    void write_command(char x)
    {
    CLR_RS; // niski stan na RS -> zapis instrukcji
    write_to_lcd(x); // zapis do LCD
    }
    
    // procedura zapisu danej do wyświetlacza LCD
    void write_char(char x)
    {
    SET_RS; // wysoki stan na RS -> zapis danej
    write_to_lcd(x); // zapis do LCD
    }
    
    // procedura zapisu tekstu do wyświetlacza LCD
    void write_text(char * s)
    {
    while(*s) // do napotkania 0
      {
      write_char(*s); // zapisz znak wskazywany przez s na LCD
      s++; // zwiększ s (przygotuj nastepny znak)
      }
    }
    
    void lowerline(void) 
    { 
    write_command(0xA8); 
    }
    
    // procedura inicjalizacji wyświetlacza LCD
    void lcd_init(void)
    {
    waitms(15); // czekaj 15ms na ustabilizowanie się napięcia zasilającego
    CLR_E; // E = 0
    CLR_RS; // RS = 0
    char i; // zmianna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      SET_E; // E = 1
      LCD &= 0x3F; //
      CLR_E; // E = 0
      waitms(5); // czekaj 5ms
      }
    SET_E; // E = 1
    LCD &= 0x2E; //
    CLR_E; // E = 0
    waitms(1); // czekaj 1ms
    write_command(0x28); // interfejs 4-bity, 2-linie, znak 5x7
    write_command(0x08); // wyłącz LCD, kursor i miganie
    write_command(0x01); // czyść LCD
    write_command(0x06); // bez przesuwania w prawo
    write_command(0x0C); // włącz LCD, bez kursora i mrugania
    }


    2. adc.h:

    
    void InitADC(void) 
    { 
     ADMUX = (1<<REFS1) | (1<<REFS0); 
     // wewnętrzne Vref = 2,56V i ustawiony kanał 0 
     ADCSRA = (1<<ADEN) | (1<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); 
     // ADC włączony, przerwanie włączone, preskaler = 64 
     }
    
    
    void StartADC(void) 
    { 
    ADCSRA |= (1<<ADSC);
    }



    No i wkońcu część głowna:

    #include <avr/io.h>
    #include "lcd.h"
    #include "adc.h"
    
    unsigned int pomiar;
    
    int main(void)
    {
    DDRD=0xFF;
    DDRC=0XFE;
    PORTD=0xFF;
    
    lcd_init();
    
    write_char(pomiar);
    
    while(1);
    {
    //inicjalizacja ADC
    InitADC();
    StartADC();
    pomiar=ADCL>>6;
    pomiar|=2<<ADCH;
    }
    return 0;
    }  




    Co tu moze być źle? Jakby znalazł się ktoś kto by wprowadził poprawki to by było miło :)

    Próbowałem coś napisać na podstawie tematów znalezionych w archiwum, ale coś mi nie wychodzi.
  • REKLAMA
  • #2 2091787
    Dexter77
    Poziom 28  
    Posty: 889
    Pomógł: 134
    Ocena: 19
    W petli glownej while(1) robisz inicjalizacje, odczyt i tak w kolko a nie ma procedury wyswietlajacej. Zamiast procedury write_char(pomiar) daj Init ADC
    a na koncu petli whle(1) daj procedure write_char(pomiar)
    Pozdro
    Dexter
  • REKLAMA
  • #3 2091842
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    No niestety to nic nie daje.
  • #4 2092027
    Dexter77
    Poziom 28  
    Posty: 889
    Pomógł: 134
    Ocena: 19
    No tak bo pomiar jednak troche trwa czasu a Ty nie sprawdzasz czy juz sie zakonczyl. Wlaczasz przerwanie od ADC ale nie wlaczasz przerwan globalnych ani nie ma obslugi tego przerwania. Moja propozycja nie wlaczaj przerwania od ADC ale po procedurze StartADC() daj petle sprawdzajaca flage zakonczenia pomiaru przez przetwornik (ADIF) . Gdy flaga nie ustawiona to krecisz sie w petli gdy ustawiona to wychodzisz z petli zerujesz flege zakonczenia pomiaru i wywolujesz procedure wyswietlania wyniku.
    Pozdro
    Dexter
  • REKLAMA
  • #5 2092035
    Tomcio112
    Poziom 12  
    Posty: 20
    Pomógł: 1
    Startujesz przetwornik, ale nie czekasz na zakończenie pomiaru.
    Włączasz flagę przerwania od przetwornika, ale nie masz obsługi przerwania i nie masz włączenia przerwań (sei()). Jeśli nie chcesz robić tego na przerwaniach to po starcie konwersji musisz sprawdzać flagę ADIF w rejestrze ADCSRA. Po wystąpieniu jedynki można odczytywać rejestry wyniku. Po czy aby skasować flagę trzeba "zapisać do niej" jedynkę.
  • #6 2092185
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Teraz jest tak i nadal nie działa:

    
    #include <avr/io.h> 
    #include "lcd.h" 
    #include "adc.h" 
    
    unsigned int pomiar; 
    
    int main(void) 
    { 
    DDRD=0xFF; 
    DDRC=0XFE; 
    PORTD=0xFF; 
    
    lcd_init(); 
    InitADC(); 
    
    while(1); 
    { 
    StartADC();
    while (bit_is_clear(ADCSRA,ADIF))
    	{};
    	pomiar=ADCL>>6; 
    	pomiar|=2<<ADCH;
    	ADCSRA |= 0x10;
    	write_char(pomiar);
    	
    }
    return (0); 
    }
    


    Nie wiem czy to sprawdzanie dobrze napisałem.
  • REKLAMA
  • #7 2092449
    Tomcio112
    Poziom 12  
    Posty: 20
    Pomógł: 1
    Sprawdzanie jest OK.
    Błąd jest tu:
    - pomiar=ADCL>>6;
    - pomiar|=2<<ADCH;
    W rejestrze ADMUX bit ADLAR masz na 0 czyli:
    pomiar = ADCL;
    pomiar1=ADCH<<8;
    pomiar1 |= pomiar;
    pomiar1 musi być typu unsigned int
    Jak nie potrzebujesz dokładności 10-bit to ustaw ADLAR na 1 wtedy interesuje Cię 8 najstarszych bitów pomiaru i:
    pomiar = ADCL;
    pomiar = ADCH; <- wynik
  • #8 2092526
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    No dobrze, zmieniłem więc zapewne teraz jest dobrze, tylko mam takie pytanie. Dlaczego wyswietlacz jest ciągle czysty? Jeżeli w pętli wykonuje instrukcje wysyłania zmiennej na wyswietlacz to jest on czysty, a jeżeli np. write_char(125); umieszcze przed petla while(1) to na wyswietlaczu pojawia się znak "}". Gdy ta instrukcja jest w pętli while(1) to wyswietlacz jest pusty :roll:
  • #9 2092607
    Tomcio112
    Poziom 12  
    Posty: 20
    Pomógł: 1
    Niestety jest to związane z czasem odpowiedzi sterownika HD44780 (bo zakładam że to siedzi w LCD). Widocznie któraś z instrukcji (wysyłanie danych itp.) nie kończy działania (czekasz 1ms wg datasheet powinno być dobrze) a już następuje kolejny zapis (proponuje odczyt statusu). Dodatkowo jak wiesz zapis do DDRAM powoduje inkrementacje (dekrementacje) adresu. Nie wiem co się dzieje gdy adres wychodzi poza zakres widzialny (HD44780 jest uniwersalnym sterownikiem dla wyświetlaczy do chyba 40 znaków) na wyświetlaczu, bo nie przerabiałem tego.
  • #10 2096259
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Udało się praktycznie rozwiązać problem, tzn. przetwornik jest czytany (w trybie free run uruchomilem wiec sprawdzanie stanu flagi jest niepotrzebne). Na wyświetlaczu jest wyświetlana aktualna wartość zczytana z przetwornika, natomiat chcialem się dowiedzieć jaki typ zmiennej "wynik" obsłuży mi taką operacje:

    wynik=(pomiar/255)*2.54;


    Pozdrawiam
  • Pomocny post
    #11 2096484
    Tomcio112
    Poziom 12  
    Posty: 20
    Pomógł: 1
    Jakaś zmienna zmiennoprzecinkowa: "float" lub jeśli będziesz korzystał z biblioteki math to wtedy lepiej "double". Trzeba tylko pamiętać że jest to dość "kodożerne". Wiąże się to z działaniami na zmiennych wielobajtowych.
    Z tego co testowałem avr-gcc w tym zakresie, to jak chcesz oszczędzić pamięć programu lepiej przechodzić na stały przecinek. Ma to jednak sens dla zmiennych do "uint32_t". Przy stosowaniu zmiennych "uint64_t" to to samo jak stosowanie zmiennej typu "double".
  • #12 2098808
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Udało mi się zrobić wkońcu. Dzięki wszystkim za pomoc. Temat można zamknąć.

Podsumowanie tematu

✨ Dyskusja dotyczy konfiguracji przetwornika analogowo-cyfrowego (ADC) w mikrokontrolerze ATMega8 z wykorzystaniem kompilatora AVR-GCC oraz wyświetlacza LCD sterowanego kontrolerem HD44780. Problem polegał na niepoprawnym odczycie wartości z potencjometru i wyświetlaniu jej na LCD. Wskazano, że konieczne jest uruchomienie ADC poza przerwaniami i sprawdzanie flagi zakończenia konwersji (ADIF) w rejestrze ADCSRA, a także poprawne odczytanie wyników z rejestrów ADCL i ADCH, uwzględniając ustawienie bitu ADLAR w ADMUX. Omówiono również kwestie związane z czasem odpowiedzi sterownika LCD i koniecznością odpowiedniego opóźnienia lub odczytu statusu, aby uniknąć problemów z wyświetlaniem. W końcowej fazie autor uruchomił tryb free run ADC, co wyeliminowało potrzebę sprawdzania flagi. Poruszono także temat doboru typu zmiennej do przechowywania wyniku przeliczenia wartości ADC na napięcie, rekomendując typy zmiennoprzecinkowe float lub double, z uwzględnieniem ograniczeń pamięci i wydajności mikrokontrolera. Ostatecznie problem został rozwiązany, a wartość z potencjometru poprawnie wyświetlana na LCD.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA