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

[atmega664p][c] Problem z zapisem do zmiennej w przerwaniu

namlooc 13 Paź 2010 23:10 1802 19
  • #1 8616899
    namlooc
    Poziom 15  
    Witam!

    Zaczne od kodu, oto dwa fragmenty:

    
    ISR(USART1_RX_vect) 
    { 
    UDR0=UDR1;
    }

    Tutaj wszystko dziala, to co dostane na usart1 przesylam odrazu na usart0

    Natomiast gdy zapisuje sobie do zmiennej:
    
    char wynikGSM[30]="123";
    int ilosc_znakow=0;
    
    ISR(USART1_RX_vect) 
    { 
    wynikGSM[ilosc_znakow]=UDR1;
    ++ilosc_znakow;
    }
    

    zmienna wynik gsm ma ciagle ta sama zawartosc zadana...
  • #2 8616918
    gaskoin
    Poziom 38  
    Jak odczytujesz tą tablicę (jak również jej wskaźnik) gdzieś w pętli main czy gdziekolwiek indziej, to musi ona być zadeklarowana jako ulotna (modyfikator volatile)
  • #3 8616955
    rpal
    Poziom 27  
    gaskoin napisał:
    Jak odczytujesz tą tablicę (jak również jej wskaźnik) gdzieś w pętli main czy gdziekolwiek indziej, to musi ona być zadeklarowana jako ulotna (modyfikator volatile)

    nie w tym jest problem tylko w tym że indeks tablicy po przejściu przez 3 kolejne przerwania osiąga wartość z poza rozmiaru tablicy, dane się gdzieś odkładają ale trafiają gdzieś w kosmos. Musisz dodać warunek który sprawdza kiedy indeks tablicy przekracza jej rozmiar i wóczas go zerować.
    Jeśli zaś zmienna nie zmienia wartości znaczy to ni mniej ni więcej jak to że przerwanie nie dochodzi do skutku. NIe ma odbioru znaku albo masz złą konfigurację programu.
  • #4 8616990
    namlooc
    Poziom 15  
    Wkleje wiekszy kawalek kodu:

    #define baud ((F_CPU/(predkosc*16UL))-1)
    #define predkosc 4800
    #define F_CPU 16000000UL
    #include <avr/io.h>
    #include <avr/pgmspace.h>
    #include <stdio.h>
    #include <util/delay.h>
    #include <string.h>
    #include <stdlib.h>
    #include "lcd.h"
    #include <avr/interrupt.h>
    
    char wynikGSM[30]="\0";
    int ilosc_znakow=0;
    //---------------ustawienie transmisji--------------------
    void usart_init(void)                     //PD2->RXD   PD3->TXD   USART_1 GSM
    {
    UBRR1H = (unsigned char) (baud>>8);  // ustawiamy predkosc transmisji
    UBRR1L = (unsigned char) baud;       
     
    UCSR1B = (1<<RXEN1) | (1<<TXEN1) | (1<<RXCIE1);    // WLACZA ODBIORNIK I NADAJNIK
    
    UCSR1C = (0<<USBS1) | (3<<UCSZ10); // 8bitow danych, 2 bity stopu, brak kontroli przeplywu
    sei();
    }
    
    void usart_debug(void)                     // PD0->RXD  PD1->TXD   USART_0 DEBUG
    {
    UBRR0H = (unsigned char) (baud>>8);  // ustawiamy predkosc transmisji
    
    UBRR0L = (unsigned char) baud;       
    
    UCSR0B = (1<<RXEN0) | (1<<TXEN0);    // WLACZA ODBIORNIK I NADAJNIK
    
    UCSR0C = (0<<USBS0) | (3<<UCSZ00); // 8bitow danych, 1 bity stopu, brak kontroli przeplywu
    sei();
    }
    //---------------transmit_usart--------------------
    void usart_transmit(char data)  // TRANSMISJA ZNAKU DLA PD2->RXD   PD3->TXD  USART_1  GSM
    {
    while (!(UCSR1A & (1<<UDRE1)));// sprawdzamy czy flaga UDRE1 w rejestrze UCSR1A ma wartosc 1, czyli bufor nadawania jest pusty
    UDR1=data;                       //  UCSR1A & (1<<UDRE1)  Maska pozwalajaca nam wykryc 1 we fladze UDRE0   
    }
    
    void wyslij(char *data0)
    {
    int j;
    int x=strlen(data0);
        for(j=0;j<x;j++)
        {
        usart_transmit(data0[j]);
        }
    }
    void usart_transmit2(char data) // TRANSMISJA ZNAKU DLA PD0->RXD  PD1->TXD DEBUG KOMPUTER
    {
    while (!(UCSR0A & (1<<UDRE0)));  // sprawdzamy czy flaga UDRE0 w rejestrze UCSR0A ma wartosc 1, czyli bufor nadawania jest pusty
    UDR0=data;                          //  UCSR0A & (1<<UDRE0)  Maska pozwalajaca nam wykryc 1 we fladze UDRE0
    }
    
    void wyslij2(char *data0)
    {
    int j;
    int x=strlen(data0);
        for(j=0;j<x;j++)
        {
        usart_transmit2(data0[j]);
        }
    }
    
    //------------------------------------------------
    //---------------recive_usart_INTERRUPT-----------
    
    ISR(USART1_RX_vect) 
    { 
    wynikGSM[ilosc_znakow]=UDR1;
    ++ilosc_znakow;
    }
    //------------------------------------------------
    int main (void)
    {
    usart_init();
    usart_debug();
    
    wyslij("AT\r");
    wyslij2(wynikGSM);
    }
    


    Wysylam "AT".. w odpowiedzi dostaje "CRLFokCRLF" i chce to w przerwaniu wpisac do tablicy, jednoczesnie wiedzac ile znakow zapisal do niej. Niestety nie smiga..
  • #5 8617021
    gaskoin
    Poziom 38  
    rpal napisał:
    gaskoin napisał:
    Jak odczytujesz tą tablicę (jak również jej wskaźnik) gdzieś w pętli main czy gdziekolwiek indziej, to musi ona być zadeklarowana jako ulotna (modyfikator volatile)

    nie w tym jest problem tylko w tym że indeks tablicy po przejściu przez 3 kolejne przerwania osiąga wartość z poza rozmiaru tablicy, dane się gdzieś odkładają ale trafiają gdzieś w kosmos. Musisz dodać warunek który sprawdza kiedy indeks tablicy przekracza jej rozmiar i wóczas go zerować.
    Jeśli zaś zmienna nie zmienia wartości znaczy to ni mniej ni więcej jak to że przerwanie nie dochodzi do skutku. NIe ma odbioru znaku albo masz złą konfigurację programu.


    Chciałeś napisać nie tylko :) Oczywiście w przerwaniu powinno być nie ++ptr tylko if(++ptr >= 30) ptr = 0;

    Wybacz ale jest tak późno, że reszty kodu już nie mam mocy czytać
  • #6 8617032
    namlooc
    Poziom 15  
    gaskoin napisał:

    Chciałeś napisać nie tylko :) Oczywiście w przerwaniu powinno być nie ++ptr tylko if(++ptr >= 30) ptr = 0;

    Tablica ma na wyrost 30znakow,a dociera do mnie ledwo 6 znakow. Sluszna uwaga ze ponad 30znakow bedzie problem, niestety dalej nie rozwiazuje to mojego problemu polegajacego na tym iz w przerwaniu nie zapisuje mi nic do zmiennej globalnej ;/
  • #7 8617079
    mirekk36
    Poziom 42  
    namlooc napisał:
    ... niestety dalej nie rozwiazuje to mojego problemu polegajacego na tym iz w przerwaniu nie zapisuje mi nic do zmiennej globalnej ;/


    Pomijając wszystkie błędy i sposób pisania programu, to zachowuje ci się on prawidłowo ;) tzn, że nie widzisz w funkcji main żadnych znaków zapisanych do swojej zmiennej globalnej .... jak zwykle w tego typu wątkach, okaże się, że "czarodziejskie" słówko volatile rozwiąże twój bieżący problem.
  • #8 8617110
    namlooc
    Poziom 15  
    volatile char wynikGSM[30]="abc";

    Dalej nie smiga ;/

    Dlaczego akurat volatile? Wydaje mi sie ze nie rozumie zabardzo dlaczego i gdzie to uzyc.
  • #9 8617694
    gaskoin
    Poziom 38  
    W dużym uproszczeniu i chłopskim językiem - kompilator "widzi" obsługę przerwania i funkcję main() osobno więc niektóre zmienne traktuje albo jako stałe(ponieważ w mainie się one niby nie zmieniają, a są modyfikowane przez przerwania), albo nic nie robiące i je optymalizuje (wywala część kodu, upraszcza etc.). Słowo volatile oznacza ulotny - optymalizator nie rusza tych zmiennych i pozwala im się zmienić gdzieś indziej :)

    Oczywiście o volatile pisałem już w tym wątku, ale po co czytać odpowiedzi na swój temat :) o volatile przeczytaj w manualu do gcc
  • #10 8617697
    flapo213
    Poziom 21  
    Witaj,

    Sprawa ma się następująco odnośnie volatile. W standardzie języka C słowo kluczowe volatile określa / charakteryzuje zmienną która może zmieniać się w nieokreślonym czasie np. w procedurze przerwania jaki i w funkcji nie związanej z procedurą przerwania.

    Cała rzecz polega na tym iż to co Ty napiszesz w C jest przekładane na assemblera który nie koniecznie jest jedną linijka kodu. Może się zdarzyć tak że podczas inkrementacji zmiennej w jednej funkcji wystąpi przerwani które np. będzie w jakiś sposób bazować na tej zmiennej która teoretycznie nie została jeszcze zaaktualizowna ponieważ kode assemblerowy ciągle na nią pracował gdu wystąpiło to przerwanie i przestrzeń w RAMie przeznaczona pod ta zmienną nie została zaaktualizowana. Stąd też w takich przypadkach należy stosować typ volatile a w procedurze nie przerwaniowej wyłączać globalnie przerwania na czas jej aktualizacji.

    Pozdrawiam
  • #11 8618081
    mirekk36
    Poziom 42  
    trochę kolega flapo213 starsznie pokrętnie to wytłumaczył dlatego ja postaram się jeszcze po swojemu ;)

    Jeśli deklarujesz zmienną globalną, z której będziesz korzystał zarówno w procedurze obsługi przerwania a także w pozostałych częściach (funkcjach) swojego kodu, to bez słówka volatile będzie to się działo w uproszczeniu w ten sposób, że w ramach optymalizacji dostępu do zmiennej:

    1. Przy wejściu w przerwanie jej wartość zostanie umieszczona w jakimś wolnym rejestrze mikrokontrolera i tylko w oparciu o wartość tego rejestru będzie odbywał się dostęp do niej w ramach przerwania

    2. Przy wejściu w funkcję gdzie także będziesz używał tej zmiennej, także zostanie ona wczytana do jakiegoś innego rejestru i wszystkie działania w tej funkcji będą opierały się na wartości w tym rejestrze a nie w oparciu o faktyczną wartość zmiennej w pamięci RAM pod adresem gdzie jest zmienna globalna. Tak jest szybciej i wygodniej z punktu widzenia opcji optymalizacji kompilatora i słusznie zresztą.

    Zatem i przerwanie i funkcja będą pracować na różnych danych. W twoim przypadku przerwanie będzie napełniać zmienną wartością o której nigdy nie dowie się funkcja main ;) i odwrotnie tak samo

    Dodając słówko volatile, kompilator zaprzestanie takich "sztuczek" optymalizacyjnych w stosunku do dostępu do tejże zmiennej. Wtedy tak samo w przerwaniu jak i w funkcji programu za każdym razem będzie odwoływał się do konkretnej komórki pamięci gdzie leży zmienna. W efekcie, jeśli przerwanie zmieni jej zawartość, to w wyniku podobnego bezpośredniego odwołania się do niej w programie nie będzie ona już ulotna jak przedtem, tylko zostanie odczytana faktyczna wartość, uprzednio zmieniona już np przez przerwanie.

    To oczywiście uproszczone tłumaczenie ale mam nadzieję, że wyraźnie pokazuje co i jak. W takich sytuacjach wcale nie trzeba koniecznie wyłączać globalnych przerwań przed dostępem do takiej komórki w programie głównym. Może to być czasem konieczne - ale w specyficznych sytuacjach , nie koniecznie trzeba wyłączać wszystkie naraz przerwania no i to dłuższy temat. Przy takiej obsłudze USART można na pewno sobie darować to wyłączanie przerwań.
  • #12 8618167
    Konto nie istnieje
    Konto nie istnieje  
  • #14 8618461
    mirekk36
    Poziom 42  
    albertb napisał:
    Oba przykłady są bzdurą.


    To żeś wanął, jak przysłowiowy "łysy grzywą o beton". (przy okazji trochę kultury na forum ci proponuję i naucz się dyskutować zamiast ogłaszać wszem i wobec że inni piszą bzdury. Wiesz lepiej? Napisz co i jak , wytłumacz jeśli wiesz lepiej) A jak się okaże, że nie wiesz lepiej to może ktoś inny ci podpowie - wtedy wszyscy skorzystaja na takim wątku...

    Po pierwsze napisałem, że opis jest bardzo uproszczony, co miało na celu uświadomienie skutków korzystania ze zmiennej globalnej nie opatrzonej przydomkiem volatile w przerwaniu i w funkcji programu. (bo o to głównie tu chodzi a nie o operację a := var + var;) ... przy okazji co to za operator w C := ??? z Pascalem chyba ci się pomyliło.

    albertb napisał:
    W obu przypadkach zarówno zapis jak i odczyt będzie się odbywał na rzeczywistej zmiennej.


    Spójrz sobie na taki przykład:

    [atmega664p][c] Problem z zapisem do zmiennej w przerwaniu

    I na drugi raz nie opowiadaj bzdur (używam twojego języka!) , że zapis i odczyt takiej zmiennej nie opatrzonej volatile będzie się zawsze odbywał na rzeczywistej wartości w RAM. Pokazałem ci prosty przykład gdzie może być wręcz przeciwnie, czyli mniej więcej tak jak było w moim tłumaczeniu, panie mądrala.
  • #15 8618514
    namlooc
    Poziom 15  
    Dziekuje za wytlumaczenia, juz rozumiem.

    Tylko pomimo:
    volatile char wynikGSM[30]="abc";

    ..umieszczonego zaraz pod def i include
    dalej nie mam dostepu do tej zmiennej z poziomu przerwania..
  • #16 8618708
    michalko12
    Specjalista - Mikrokontrolery
    Człowieku zanim cokolwiek spróbujesz odebrać to main juz dawno pójdzie w powietrze.
    int main (void)
    {
    usart_init();
    usart_debug();
    
    wyslij("AT\r");
    wyslij2(wynikGSM);
    }

    To jest źle napisane!
  • #17 8618793
    gaskoin
    Poziom 38  
    Chyba, że jest najszybszym kowbojem na dzikim zachodzie :D
  • #18 8619481
    Konto nie istnieje
    Konto nie istnieje  
  • #19 8621331
    namlooc
    Poziom 15  
    michalko12 napisał:
    wyslij("AT\r");
    wyslij2(wynikGSM);


    Między tymi dwiema linijkami nie powinieneś przypadkiem poczekać na odpowiedź z modemu?
    Miales racje, dziekuje za pomoc.

    Dodano po 32 [sekundy]:

    mirekk36 napisał:
    słówko volatile rozwiąże twój bieżący problem.
    Takze miales racje, dziekuje.


    Zostal mi ostatni problem w postaci warminga:
    warning: passing arg 1 of `wyslij2' discards qualifiers from pointer target type

    Pojawil sie w momencie gdy zamienilem char na voilante char.
    Blad wystepuje w linji wywołania procedury:
    volatile char wynikGSM[90]="abc";
    .
    .
    .
    .
    void wyslij(char *data0)
    {
    int j;
    int x=strlen(data0);
        for(j=0;j<x;j++)
        {
        usart_transmit(data0[j]);
        }
    }
    
    
    main()
    {
    ..
    wyslij(wynikGSM); //warning: passing arg 1 of `wyslij2' discards qualifiers from pointer target type
    ..
  • #20 8621369
    mirekk36
    Poziom 42  
    Po pierwsze uprość sobie tę funkcję, założenie do niej jest takie,że zawsze będziesz przesyłał C-string (czyli zakończony zerem)
    
    void wyslij(char *data) {
    
       while( *data ) usart_transmit( *data++ );
    
    }


    Skoro użyłeś przydomka volatile dla zmiennej tablicowej a w deklaracji funkcji nie jest on (ten argument opatrzony takim przydomkiem) - to możesz zawsze użyć rzutowania typów:

    wyslij( (char*) wynikGSM );


    aby pozbyć się takiego warninga.
REKLAMA