• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Jak poprawnie odczytać wartość z analogowego pinu ?

VPS Starter Arubacloud
0 głosów
256 wizyt
pytanie zadane 15 listopada 2020 w C i C++ przez tonn204 Mądrala (7,470 p.)

Cześć, próbuję zbudować prosty termometr i mam problem. Ustawiłem wszystko związane z ADC (wydaje mi się, że poprawnie) i teraz tylko nie wiem jak poprawnie odczytać wartość, która została mi przekonwertowana. Którego rejestru mam do tego użyć? Muszę zrobić dodatkowo jeszcze jakieś przesunięcia bitowe? Dodam, że pin którego używam to PC1 (ADC1). Dziękuję za odpowiedzi.

/*
    Program to control Atmega8A
*/

#define F_CPU 8000000L

#include <avr/io.h>
#include <util/delay.h>
#include <hd44780.c>

int main(void)
{
    char buffer[33];
    uint16_t temperature;

    // Insert code
    DDRB |= (1 << PB0);
    ADCSRA |= (1 << ADEN);
    ADMUX |= (1 << REFS0);
    ADMUX = 0x01;

    LCD_Initalize();

    while(1)
    {
        ADCSRA |= (1 << ADSC);
        while(ADCSRA & (1 << ADSC));

        temperature = ADCL;
        itoa(temperature, buffer, 10);
        LCD_Clear();
        LCD_GoTo(2,0);
        LCD_WriteText("Temperatura : ");
        LCD_GoTo(5,1);
        LCD_WriteText(buffer);
        LCD_WriteText(" C");
        _delay_ms(500);

        PORTB |= (1 << PB0);
        _delay_ms(1000);      //  delay 1 second
        PORTB &= ~(1 << PB0);
        _delay_ms(1000);      //  delay 1 second
    }

    return 0;
}

 

komentarz 15 listopada 2020 przez Oscar Nałogowiec (29,340 p.)
edycja 15 listopada 2020 przez Oscar

A to czytałeś?

Manual

Niepotrzebnie ustawiasz ADMUX dwa razy. Za pierwszym ustawiasz napięcie odniesienia na VCC a potem zerujesz te bity, ustawiająć zewnętrzne napięcie. Co masz podpięte do pinu AREF? Przydałby się przynajmniej kondensator.

Przetwornik ADC ile razy napięcie wejściowe jest większe od napięcia odniesienia podzielonego przez 1024. Ustawienie poprawnego napięcia odniesienia jest krytyczne. Masz do wyboru - napięcie zasilania, wewnętrzne źródło napięcia mniej więcej 2.5V, napięcie zewnętrzne podane na pin AREF.

ADC nie może być taktowane szybciej niż 200kHz, jaki masz zegar procesora?

Tak jest dodatkowy dzielnik częstotliwości, u ciebie jest ustawiony na 2.

By otrzymać wynik najpierw odczytujesz ADCL, a potem ADCH. Łaczysz to w 16 bitową liczbę i 10 najmłodszych bitów to wynik przetwarzania. Wzór na napięcie wyjściowe jest w podanym wyżej manualu. Masz w ogóle miernik (tzw multimetr)?

 

 

komentarz 15 listopada 2020 przez tonn204 Mądrala (7,470 p.)

Do AREF nie mam nic podpięte. ADMUX ustawiałem drugi raz, bo chciałem ustawić, z którego pinu ma być odczytana wartość (PC1 - ADC1). A co do taktowanie mam ustawione jeżeli o to chodzi na 8Mhz.  Odczytanie wyniku to powinno wyglądać tak ? Bez żadnych przesunięć bitowych i innych operacji w tym stylu?  

 ADCSRA |= (1 << ADSC);
 while(ADCSRA & (1 << ADSC));
 temperature = ADCL;
 temperature += ADCH;

Usunąłem tą linie gdzie drugi raz zapisywałem do rejestru ADMUX teraz wyświetla mi się wynik 0

/*
    Program to control Atmega8A
*/

#define F_CPU 8000000L

#include <avr/io.h>
#include <util/delay.h>
#include <hd44780.c>

int main(void)
{
    char buffer[33];
    uint16_t temperature;

    // Insert code
    DDRB |= (1 << PB0);
    ADCSRA |= (1 << ADEN);
    ADMUX |= (1 << REFS0);
    //ADMUX = 0x01;

    LCD_Initalize();

    while(1)
    {
        ADCSRA |= (1 << ADSC);
        while(ADCSRA & (1 << ADSC));

        temperature = ADCL;
        temperature += ADCH;

        itoa(temperature, buffer, 10);
        LCD_Clear();
        LCD_GoTo(2,0);
        LCD_WriteText("Temperatura : ");
        LCD_GoTo(5,1);
        LCD_WriteText(buffer);
        LCD_WriteText(" C");
        _delay_ms(500);

        PORTB |= (1 << PB0);
        _delay_ms(1000);      //  delay 1 second
        PORTB &= ~(1 << PB0);
        _delay_ms(1000);      //  delay 1 second
    }

    return 0;
}

 

komentarz 15 listopada 2020 przez Oscar Nałogowiec (29,340 p.)
edycja 15 listopada 2020 przez Oscar

Ponieważ korzystasz tylko z jednego wejścia, wpisz od razu odpowiednią wartość do ADMUX. Dla ciebie będzie to ( (1 << REFS0) | 1).

Do AREF powinieneś podpiąć jakiś kondensator, taki jak do odfiltrowywania zasilania, 100nF.

Oczywiście ADCL i H trzeba złożyć z przesunięciem

        temperature = ADCL;
        temperature |= (ADCH << 8);

Być może kompilator od razu zrozumie symbol ADC jako 16-bitowy rejestr.

    temperature = ADC;

Na razie zwolnij maksymalnie zegar przetwornika, ustawiając trzy najmłodsze bity ADCSRA. Wydaje mi się, że najlepiej najpierw włączyć bit ADEN, a potem dodać te trzy najmłodsze bity.

ADCSRA = _BV(ADEN);
ADCSRA = _BV(ADPS0) | _BV(ADPS1) | _BV(ADPS2) | _BV(ADEN);

Nie wie czy taki zapis jest ciągle obsługiwany przez kompilator avrgcc.

komentarz 15 listopada 2020 przez tonn204 Mądrala (7,470 p.)

Zmieniłem kod i wynik dalej wynosi wyświetla się 0. Może to faktycznie wina kondensatora. Mam tylko pytanie, jak mam go podłączyć ? Jedną nóżkę do ARFE, a drugą, np. do masy? Czy obydwie na tym po prostu dwie nóżki równolegle ? Mam użyć ceramicznego ?Sprawdzałem też z odczytanie rejestru ADC, ale wynik dalej wyświetla się zero.

/*
    Program to control Atmega8A
*/

#define F_CPU 8000000L

#include <avr/io.h>
#include <util/delay.h>
#include <hd44780.c>

int main(void)
{
    char buffer[33];
    uint16_t temperature;

    // Insert code
    DDRB |= (1 << PB0);
    ADCSRA = _BV(ADEN);
    ADCSRA = _BV(ADPS0) | _BV(ADPS1) | _BV(ADPS2) | _BV(ADEN);
    ADMUX |=  ((1 << REFS0) | 1);

    LCD_Initalize();

    while(1)
    {
        ADCSRA |= (1 << ADSC);
        while(ADCSRA & (1 << ADSC));

        temperature = ADCL;
        temperature |= (ADCH << 8);
        // temperature = ADC;
        
        itoa(temperature, buffer, 10);
        LCD_Clear();
        LCD_GoTo(2,0);
        LCD_WriteText("Temperatura : ");
        LCD_GoTo(5,1);
        LCD_WriteText(buffer);
        LCD_WriteText(" C");
        _delay_ms(500);

        PORTB |= (1 << PB0);
        _delay_ms(1000);      //  delay 1 second
        PORTB &= ~(1 << PB0);
        _delay_ms(1000);      //  delay 1 second
    }

    return 0;
}

 

komentarz 15 listopada 2020 przez Oscar Nałogowiec (29,340 p.)
Pomiędzy AREF a masę, w miarę możliwości najkrótsze połączenie.

Jak rozumiem, kompilator łyknął rejestr ADC, fajnie. Czyli sam potrafi odczytać te dwa rejestry jako 1 16-bitowy.

A możesz sprawdzić, czy po wpisaniu wartości do rejestrów ADCSRA i ADMUX odczytuje się z nich to co się wpisało? Jeśli coś jest źle ustawione to ADC po prostu nie działa i nie działają jego rejestry.
komentarz 15 listopada 2020 przez tonn204 Mądrala (7,470 p.)

Kondensator podpięty. Do rejestru ADCSRA i ADMUX zapisałem wartość 10, a w zmiennej temperature zapisałem sumę tych dwóch rejestrów. Gdy zapisane są dziesiątki wszystko jest ok i wyświetla się wynik 20, ale jak   zmienię wartość jednego z rejestrów na np. 20 to wynik zamiast  20 + 10 = 30 wyświetla mi się 14 . To może świadczyć o jakimś zepsuciu tych całego ADC?

/*
    Program to control Atmega8A
*/

#define F_CPU 8000000L

#include <avr/io.h>
#include <util/delay.h>
#include <hd44780.c>

int main(void)
{
    char buffer[33];
    uint16_t temperature;

    // Insert code
    DDRB |= (1 << PB0);
    /*ADCSRA = _BV(ADEN);
    ADCSRA = _BV(ADPS0) | _BV(ADPS1) | _BV(ADPS2) | _BV(ADEN);
    ADMUX |=  ((1 << REFS0) | 1);*/
    ADCSRA = 10;
    ADMUX = 10;

    LCD_Initalize();

    while(1)
    {
        ADCSRA |= (1 << ADSC);
        while(ADCSRA & (1 << ADSC));

        temperature = ADCSRA + ADMUX;
        itoa(temperature, buffer, 10);

        LCD_Clear();
        LCD_GoTo(2,0);
        LCD_WriteText("Temperatura : ");
        LCD_GoTo(5,1);
        LCD_WriteText(buffer);
        LCD_WriteText(" C");
        _delay_ms(500);

        PORTB |= (1 << PB0);
        _delay_ms(1000);      //  delay 1 second
        PORTB &= ~(1 << PB0);
        _delay_ms(1000);      //  delay 1 second
    }

    return 0;
}

 

komentarz 15 listopada 2020 przez Oscar Nałogowiec (29,340 p.)
edycja 15 listopada 2020 przez Oscar

Nie, raczej działa. Różnica pomiędzy 30 (oczekiwane) a 14 to 16 - czyli 1 bit.  Rejestry IO to nie pamięć, nie zawsze odczytuje się to co się zapisało. Chociażby bit startu przetwarzania - programista go ustawia, a on po jakimś czasie sam się zeruje. Ale skoro nie są to same zera, ADC jest włączony. Nie mam co prawda atmegi8a, ale chyba zaraz wyciągne moją płytkę deweloperską z Atmegą32...

Powtarzam pytanie - masz jakiś woltomierz/multimetr?

Zerknąłem do kod programów, które kiedyś uruchamiałem na tej płytce. Jest tam tak (miałem podłączone zewnętrzne napięcie odniesienia):

    /// ADC na porcie A, najmłodsze bity
    DDRA = 0xF0;

    // skonfigurowac ADC
    ADMUX = 0;
    ADCSRA = _BV(ADPS0) | _BV(ADPS1) | _BV(ADPS2) | _BV(ADEN);

    for (;;) 
    {
        ADMUX = 0;
        _delay_ms(10);
        ADCSRA |= _BV(ADSC);
        loop_until_bit_is_clear(ADCSRA, ADSC);
        val = ADC/2;    //< dzielenie przez 2 bo dalej potrzebuje wartość do 500
        
        serwo_val[0] = val > 500 ? 500 : val;

        // drugi kanal
        ADMUX = 1;
        _delay_ms(10);
        ADCSRA |= _BV(ADSC);
        loop_until_bit_is_clear(ADCSRA, ADSC);
        val = ADC/2;

        serwo_val[1] = val > 500 ? 500 : val;

    }

To prosty program, który używając dwóch potencjometrów podłączonych do pinów A1 i A0 steruje dwoma serwomechanizmami modelarskimi (nazwijmy to modulacja PWM). Sterowanie serwami jest na przerwaniach z timera, ADC są sterowane normalnie w pętli głównej.

 

W twoim kodzie dodałbym jeszcze liczniki liczące ile razy wykonuje się pętla oczekiwania na konwersję:

        int waiting = 0;
        while(ADCSRA & (1 << ADSC))
             waiting ++;

I można wyświetlić zmienną waiting.

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
1 odpowiedź 201 wizyt
pytanie zadane 4 listopada 2020 w C i C++ przez tonn204 Mądrala (7,470 p.)
0 głosów
1 odpowiedź 475 wizyt
pytanie zadane 5 marca 2017 w Offtop przez Mavimix Dyskutant (8,420 p.)
0 głosów
0 odpowiedzi 138 wizyt

93,020 zapytań

141,985 odpowiedzi

321,287 komentarzy

62,366 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Wprowadzenie do ITsec, tom 2

Można już zamawiać tom 2 książki "Wprowadzenie do bezpieczeństwa IT" - będzie to około 650 stron wiedzy o ITsec (17 rozdziałów, 14 autorów, kolorowy druk).

Planowana premiera: 30.09.2024, zaś planowana wysyłka nastąpi w drugim tygodniu października 2024.

Warto preorderować, tym bardziej, iż mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy dodatkowe 15% zniżki! Dziękujemy zaprzyjaźnionej ekipie Sekuraka za kod dla naszej Społeczności!

...