Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

Pomiar napięcia akumulatora na atmega16 w c

mirek1968 27 Apr 2012 21:59 3434 15
IGE-XAO
  • #1
    mirek1968
    Level 13  
    Mam prośbę chciałbym kontrolować napięcie mojego akumulatora w samochodzie i nie wiem jak to poprawnie zrobić oraz jak oprogramować w c .Chciałbym do tego wykorzystać atmegę16 bo taki posiadam.Proszę o proste przykłady działające i pomoc w tym temacie.Jestem początkującym amatorem w programowaniu avr.Ogólnie chodzi mi o schemat podłączenia bezpiecznego napięcia akumulatora do pinu atmegi16 i prosty program w c do jego wyświetlenia na LCD 2X16.
  • IGE-XAO
  • #2
    tmf
    Moderator of Microcontroller designs
    Podłączenie aku to po prostu dzielnik na rezystorach. Jak masz 12V to robisz dzielnik np. 1:3 i po kłopocie. Co do programu - ściągnij przykłady do książki z mojej stopki (są za darmo) i tam masz m.in. przykłądy obsługi ADC i wyświetlania na LCD.
  • #3
    kaczart
    Level 14  
    Jeżeli zastosujesz dzielnik napięcia na wejściu to zastosuj rezystory o dużej dokładności (np. 0,5%) lub potencjometr, którym sobie skalibrujesz wskazania.

    Wystarczającą dokładność otrzymasz z wbudowanego ADC, jednak ważne jest, abyś zastosował jak najdokładniejsze napięcie referencyjne.
  • IGE-XAO
  • #4
    tmf
    Moderator of Microcontroller designs
    No właśnie, stosowanie superdokładnych rezystorów jest bez znaczenia, jeśli nie zastosuje zewnętrznego napięcia referencyjnego. Z drugiej strony kalibrację można zrobić programowo i zapisać wynik w EEPROM przy pierwszym uruchomieniu. Wyjdzie taniej i prościej.
  • #5
    mirek1968
    Level 13  
    Powiedzmy że podam plus z akumulatora samochodowego na połączone szeregowo rezystory 10K i 4,7K lub zastosuję potencjometr ,połączę masę akumulatora z moją płytką avr ,potem ustawię dokładnie napięcie 1/3 z 12V na pin ADC1 tj.PA1 atmegi16 .
    Pytanie do TMF gdzie znajdę przykłady w c dla tego dzielnika napięcie 1/3 ,jak mam rozumieć ten opis- "ściągnij przykłady do książki z mojej stopki " .Proszę jeszcze o przykład prostego programiku w c tak abym mógł wyświetlić wynik pomiaru na LCD 2X16.
  • Helpful post
    #7
    tmf
    Moderator of Microcontroller designs
    mirek1968 wrote:
    Powiedzmy że podam plus z akumulatora samochodowego na połączone szeregowo rezystory 10K i 4,7K lub zastosuję potencjometr ,połączę masę akumulatora z moją płytką avr ,potem ustawię dokładnie napięcie 1/3 z 12V na pin ADC1 tj.PA1 atmegi16 .
    Pytanie do TMF gdzie znajdę przykłady w c dla tego dzielnika napięcie 1/3 ,jak mam rozumieć ten opis- "ściągnij przykłady do książki z mojej stopki " .Proszę jeszcze o przykład prostego programiku w c tak abym mógł wyświetlić wynik pomiaru na LCD 2X16.


    W c przykładów do dzielnika napięcia raczej nie znajdziesz :) Dzielnik powoduje, że napięcie z aku jest dzielone na 3, czyli zamiast 12 V masz 4, zamiast 9 V masz 3 itd. Dzięki temu możesz aku bezpiecznie podpiąć pod wejście ADC procesora. To co musisz zrobić, to wynik pomiaru z ADC pomnożyć razy wartość dzielnika, czyli np. 3. Co do przykładów - w mojej stopce jest link. Jak go klikniesz to cię przekieruje na stronę Helionu, gdzie jest ksiażka do której możesz ściągnąć przykłądy - jeden z linków na tej stronie. Po ich rozpakowaniu masz przykłady wykorzystania LCD i ADC. Polecam też wcześniejszy link do blogu o mikrokontrolerach kolegi Dondu. Z pewnością dużo zyskasz jeśli go całego przejrzysz.
  • #8
    mirek1968
    Level 13  
    Mam kilka pytań bo nie do końca mogę zrozumieć kilka terminów dotyczących funkcji przetwornika ADC atmega16.Powiedzmy że ustawiam sobie na dzielniku napięcia poprzez potencjometr dokładnie napięcie 1/3 napięcia akumulatora i podpinam do do pinu ADC1czyli PA1 mojej atmegi16.
    Czy wtedy w VREF wpisuję np. 4.0(tzn.1/3 napięcia akumulatora) ?.czy mam wpisać VREF 2,56 ? bez względu na tej mój dzielnik 1/3
    Bardzo proszę spojrzeć na mój plik main.c i poprawić błędy.Wykorzystuję pin ADC1 na pinie PA1 i nie rozumię także tego fragmentu programu.

    Code: c
    Log in, to see the code

    //Inicjalizacja ADC
    ADCSRA = (1<<ADEN) //Bit 7 - ADEN: ADC Enable (uruchomienie przetwornika)
    |(1<<ADPS0)
    |(1<<ADPS1)
    |(1<<ADPS2); //ADPS2:0: ADC Prescaler Select Bits
    //(ustawienie preskalera) preskaler= 128

    ADMUX = (1<<REFS1) | (1<<REFS0) //Bit 7:6 - REFS1:0: Reference Selection Bits
    //Internal 2.56V Voltage Reference with external
    //capacitor at AREF pin
    |(1<<MUX2) | (1<<MUX0); //Input Channel Selections (ADC5 - Pin 5 )

    [/syntax]




    A to jest cały mój plik main.c proszę o poprawki.
    Code: c
    Log in, to see the code


    #include <avr/io.h>
    #include <util/delay.h>
    #include "HD44780.h"


    //definicja napiecia referencyjnego
    #define VREF 2.56
    //definicja ADCIN (wejście ADC)
    #define ADCIN PA1
    //##############################################################################
    void main(void)
    {
    DDRB |=(1<<PB3); //enable backlight
    PORTB|=(1<<PB3);

    char wynik[]=" ";//bufor tekstowy, wyczyszczenie bufora
    float adc;//zmienna do obliczeń napięcia



    LCD_Initalize(); //inicjalizacja LCD
    LCD_GoTo(0, 0); //Ustawienie kursora w pozycji (0,0)
    LCD_WriteText("ADC test: 10 bit");

    //Inicjalizacja ADC
    ADCSRA = (1<<ADEN) //Bit 7 - ADEN: ADC Enable (uruchomienie przetwornika)
    |(1<<ADPS0)
    |(1<<ADPS1)
    |(1<<ADPS2); //ADPS2:0: ADC Prescaler Select Bits
    //(ustawienie preskalera) preskaler= 128

    ADMUX = (1<<REFS1) | (1<<REFS0) //Bit 7:6 - REFS1:0: Reference Selection Bits
    //Internal 2.56V Voltage Reference with external
    //capacitor at AREF pin
    |(1<<MUX2) | (1<<MUX0); //Input Channel Selections (ADC5 - Pin 5 )

    DDRA &=~ (1<<ADCIN); //Ustawienie Wejścia ADC

    for(;;)
    {
    ADCSRA |= (1<<ADSC);//ADSC: Uruchomienie pojedynczej konwersji

    while(ADCSRA & (1<<ADSC));//czeka na zakończenie konwersji

    adc=ADC*VREF/1024; //przeliczenie wartości na napięcie
    sprintf(wynik,"U=%1.1fV",adc); //konwersja na łańcuch znakowy
    LCD_GoTo(3, 1); //Ustawienie kursora w pozycji (1,1)
    LCD_WriteText(wynik); //Wyświetlenie wyniku
    _delay_ms(500); //opóźnienie
    }
    }[/syntax]
  • #9
    miszczo997
    Level 28  
    Vref to napięcie odniesienia i nie może być większe od mierzonego napięcia. W twoim przypadku możesz wybrać wewnętrzne napięcie odniesienia 2,56v albo zewnętrzne np napięcie zasilania. Przy wyborze vcc pomiar będzie wynosił (5V/1024)*ADCW.
    W 10 bitowym rejestrze ADCW=((ADCL | (ADCH<<8)) przetrzymywany jest 10 bitowy wynik pomiaru, bitami ADPS ustawiasz preskaler, REFS -napięcie odniesienia, MUX - wybór pinu do którego podłączysz sygnał, a ustawienie bitu ADSC spowoduje uruchomienie pojedynczej konwersji.
    //
    W zasadzie to głupoty piszę. Nie ma 10bitowego rejestru ADCW, a to jedynie makro upraszczające. Są dwa 8 bitowe rejestry ADCW i ADCH w których przetrzymywany jest pomiar, który można wyrównać do lewej albo do prawej.
  • #10
    mirek1968
    Level 13  
    Mam jeszcze pytanie dotyczące
    Code: c
    Log in, to see the code

    //Inicjalizacja ADC
    ADCSRA = (1<<ADEN) //Bit 7 - ADEN: ADC Enable (uruchomienie przetwornika)
    |(1<<ADPS0)
    |(1<<ADPS1)
    |(1<<ADPS2); //ADPS2:0: ADC Prescaler Select Bits
    //(ustawienie preskalera) preskaler= 128
    Code: c
    Log in, to see the code


    Chodzi mi jak w moim przypadku powinienem ustawić preskaler= 128? czy tak jest ok?
    Drugie pytanie dotyczy tego że jak ustawiłem napięcie 1,5V na pin PA1 i następnie pomnożyłem to *3 to wyświetla mi na LCD 4,6V? zamiast 4,5V-dlaczego?.
    Code: c
    Log in, to see the code

    adc=ADC*VREF/1024; //przeliczenie wartości na napięcie
    sprintf(wynik,"U=%1.1fV",adc*3); //konwersja na łańcuch znakowy
    Code: c
    Log in, to see the code


    Proszę jeszcze o pomoc w tych pytaniach.
  • Helpful post
    #11
    miszczo997
    Level 28  
    Zależy jaka częstotliwością taktujesz procesor. Ogólnie wg datasheeta dla 10 bitowego pomiaru ma być z przedziału 50-200khz. Dobierz taki preskaler, że jak podzielisz przez niego częstotliwość taktowania procesora to zmieścisz się w przedziale 50-200khz. Z drugim pytaniem niech ktoś inny pomoże.
  • #12
    tmf
    Moderator of Microcontroller designs
    mirek1968 wrote:

    Drugie pytanie dotyczy tego że jak ustawiłem napięcie 1,5V na pin PA1 i następnie pomnożyłem to *3 to wyświetla mi na LCD 4,6V? zamiast 4,5V-dlaczego?.
    Code: c
    Log in, to see the code

    adc=ADC*VREF/1024; //przeliczenie wartości na napięcie
    sprintf(wynik,"U=%1.1fV",adc*3); //konwersja na łańcuch znakowy
    Code: c
    Log in, to see the code


    Proszę jeszcze o pomoc w tych pytaniach.


    Dlatego, że napięcie referencyjne nie jest dokładne, jest tylko stabilne. W zależności od jego konkretnej wartości wynik może nieznacznie się różnić, jeśli potrzebujesz dużej dokładności to musisz zastosować zewnętrzne źródło napięcia referencyjnego.
  • #13
    mirek1968
    Level 13  
    Mam takie pytanie troszkę na inny temat ale też dotyczący AVR.
    W jakim celu stosuje się układ pamięci 24C32 na jednej płytce z układem zegara ds1307.
    Czy ta pamięć jest konieczna? do pracy układu zegara?
    Pytam o to ponieważ chciałbym sobie taki układ zegara ds1307 kupić i zastanawiam się czy przy okazji kupić za jedną wysyłką pamięć i nie wiem czy jest to konieczne.
    Proszę o pomoc w tym pytaniu.
  • #14
    tmf
    Moderator of Microcontroller designs
  • #15
    mirek1968
    Level 13  
    Dzięki tmf z odpowiedź.
    Mam pytanie dotyczące łączenia instrukcji programów w całość .
    Chodzi mi o to że robię sobie termometr w którym odczytam sobie temperaturę wewnętrzną ,zewnętrzną a także napięcie swojego akumulatora i czas,datę z układu DS1307.W jaki sposób połączyć kilka plików to ze sobą w sposób profesjonalny?
    Czy wszystko ująć w jednej pętli "while"?.ale wtedy są opóźnienia w realizacji programu które nie są wpisane na początku pętli programowej.
    Poniżej przedstawiam mój program w którym mam na początku komunikat powitalny potem odczyt temperatury i pomiar napięcia akumulatora proszę o poprawienie błędów w łączeniu instrukcji lub o inne cenne uwagi i przykłady poprawnego łączenia najlepiej na przykładzie mojego działającego main.c .
    W przedstawionym poniżej programie mam podzielone na kawałki i wymieszane części programu na termometr i woltomierz bo nie wiedziałem jak mam to zrobić profesjonalnie.Proszę o cenne konkretne rady.

    Code: c
    Log in, to see the code

    /*
    Plik main.c

    KURS AVR-GCC cz.5
    Wyswietlacz alfanumeryczny LCD HD44780
    (schemat i opis dzialania w artykule)

    uklad atmega16 (16MHz)
    */
    #define F_CPU 16000000L
    #define VREF 5.00
    #define ADCIN PA1
    #include <avr/io.h>
    #include <util/delay.h>
    #include <stdio.h>
    #include "hd44780.h"
    #include "ds18b20.h"

    char str[17]="Temp zew";



    int main(void)
    {


    DDRB = 0b11111101; // linie PB0,2,3,4,5,6,7 są wyjściami ,linia PB1 jest wejściem//
    PORTB = 0b00001010; // podświetlenie LCD poprzez PB3//

    /* Napisy przechowujemy w tablicach */
    char str1[] = "SKODA";
    char str2[] = " FABIA";
    char str3[] = "HATCHBACK CLAS";
    char str4[] = "U=";

    //Komunikat powitalny//


    /* Funkcja inicjalizuje wyswietlacz */
    lcd_init();


    // Wlacza wyswietlanie z widocznym kursorem
    LCD_DISPLAY(LCDDISPLAY|LCDCURSOR);

    //Ustawia kursor w pozycji:
    //pierwszy wiersz, szosta kolumna
    LCD_LOCATE(5,0);

    // Wysyla do wyswietlacza jeden znak
    LCD_WRITE_DATA('W');
    _delay_ms(300);

    LCD_WRITE_DATA('I');
    _delay_ms(300);

    LCD_WRITE_DATA('T');
    _delay_ms(300);

    LCD_WRITE_DATA('A');
    _delay_ms(300);

    LCD_WRITE_DATA('M');


    _delay_ms(800);

    // Ukrywa kursor
    LCD_DISPLAY(LCDDISPLAY);

    LCD_CLEAR;
    LCD_LOCATE(2,0);

    //Funkcja lcd_puts wysyla do
    //wyswietlacza ciag znakow
    lcd_puts(str1);
    _delay_ms(1000);

    lcd_puts(str2);
    _delay_ms(1000);

    LCD_LOCATE(1,1);
    lcd_puts(str3);
    _delay_ms(1000);

    LCD_CLEAR;
    LCD_LOCATE(0,0);

    lcd_puts("Nr.Rejestracyjny");
    _delay_ms(1000);

    LCD_LOCATE(4,1);
    lcd_puts("SRC 38YS ");

    // Czeka 2.5 sek.
    _delay_ms(1000);

    // Czysci ekran
    LCD_CLEAR;


    //Woltomierz//

    char wynik[]=" ";//bufor tekstowy, wyczyszczenie bufora
    float adc;//zmienna do obliczeń napięcia



    //Inicjalizacja ADC
    ADCSRA = (1<<ADEN) //Bit 7 - ADEN: ADC Enable (uruchomienie przetwornika)
    |(1<<ADPS0)
    |(1<<ADPS1)
    |(1<<ADPS2); //ADPS2:0: ADC Prescaler Select Bits
    //(ustawienie preskalera) preskaler= 128

    ADMUX = (1<<REFS0) //Bit 6 - REFS0:1: Reference Selection Bits
    ///VCC jako napięcie referencyjne with external
    //capacitor at AREF pin
    | (1<<MUX0); //Input Channel Selections (ADA1 - Pin 1 )

    DDRA &=~ (1<<ADCIN); //Ustawienie Wejścia ADC



    //Termometr//


    /* Zmienna przechowuje aktualną wartość temperatury */
    double temp;
    /* W tablicy zapisywane będą dane odczytane z układu ds18b20 */
    unsigned char ds18b20_pad[9];

    /* Funkcja inicjalizuje wyświetlacz */
    lcd_init();
    /* Włącza wyświetlanie */
    LCD_DISPLAY(LCDDISPLAY);
    /* Czyści ekran */
    LCD_CLEAR;

    /* Wyświetla tytuł */
    LCD_LOCATE(0,0);
    lcd_puts(str);

    while(1)
    {

    //Woltomierz//

    ADCSRA |= (1<<ADSC);//ADSC: Uruchomienie pojedynczej konwersji

    while(ADCSRA & (1<<ADSC));//czeka na zakończenie konwersji

    adc=ADC*VREF/1024; //przeliczenie wartości na napięcie
    sprintf(wynik,"%1.1fV",adc*3); //konwersja na łańcuch znakowy
    LCD_LOCATE(9, 1);
    lcd_puts(str4);
    LCD_LOCATE(11, 1); //Ustawienie kursora w pozycji (1,1)
    lcd_puts(wynik); //Wyświetlenie wyniku
    _delay_ms(500); //opóźnienie


    //Termometr//

    /* 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 ;

    /* Formułuje komunikat w tablicy 'str' */
    sprintf(str,"%4.1f\xdf""C", temp);

    LCD_LOCATE(9,0);
    /* Wysyła komunikat do wyświetlacza */
    lcd_puts(str);
    }

    }

    }
    Code: c
    Log in, to see the code

    /*
  • #16
    mirek1968
    Level 13  
    Mam jedno jeszcze pytanie na temat ustawienia pinu PA0 jako ADC
    W przypadku ustawienia PC5 jest to:
    Code: c
    Log in, to see the code

    ADMUX = (1<<REFS0) //VCC jako napięcie referencyjne
    |(1<<MUX2) | (1<<MUX0); //wybór kanału pomiarowego ADC5
    Code: c
    Log in, to see the code


    A jak powinno być w przypadku pinu PA0 czyli mojego ADC0 pytam bo nie rozumiem tego zapisu powyżej.