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

[atmega16] [atmega16][c/avr-gcc] UART w przerwaniach - nieoczekiwane działanie programu

bckthd 28 Cze 2012 15:10 2412 25
  • #1 11051297
    bckthd
    Poziom 9  
    Witam.
    Wziąłem się ostatnio za komunikację z komputerem. Napisałem program który na razie tylko wysyła ciąg znaków. Oczywiście chciałem aby wszystko odbywało się w przerwaniach.
    Napotkałem na taki problem, który nawet nie wiem czym może być spowodowany.
    Oto kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Ten kod nie działa poprawnie. Zamiast wysyłać na pc "tekst " to wysyła tylko znak 02 czyli start of text.
    Co dziwne wystarczy dać delay powyżej 300 ms w funkcji uart_tx() (gdziekolwiek) i kod ładnie działa. Myślałem najpierw że może chodzić o to, że po wyjściu z tej funkcji tablica charów znika i uart nie ma co wysyłać. Jednak gdyby tak było to nic by nie dało danie delaya przed włączeniem przerwania od pustego bufora nadawczego.
    Problem chyba leży w linijce
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    w funkcji uart_tx(). Już tłumaczę dlaczego tak myślę.
    Gdy zmieniłem funkcję uart_tx() tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    a txt[] to była globalna tablica w której na sztywno był wpisany tekst, to działo się to samo. Czyli tablica ze znakami była dostępna przez cały czas.
    pomogło dopiero wyrzucenie
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    do funkcji głównej.

    Przepraszam za zawiłość.
    Czy ma ktoś może jakieś pomysły?
  • #2 11051405
    stanleysts
    Poziom 27  
    Zmień funkcję w przerwaniu: ISR(USARTUDRE_vect), postaraj się wmontować tam while, coś na kształt:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Nie czekasz nigdzie aż się bufor transmisji opróżni i wysyłasz cały czas coś.
  • #3 11051605
    bckthd
    Poziom 9  
    No właśnie takiego czekania w pętli chciałem uniknąć. Tą funkcję spełnia przerwanie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    jest to przerwanie od pustego bufora nadawczego. Czyli kiedy dane z bufora zostana przesłane do rejestru przesuwnego, można do bufora wpisać kolejny bajt do wysłania.
    Bardzo nie lubię czekania w pętli ponieważ blokuje to wykonywanie programu. Po to są przerwania żeby je używać.
  • #4 11051937
    LordBlick
    VIP Zasłużony dla elektroda
    stanleysts napisał:
    zmień funkcję w przerwaniu: ISR(USARTUDRE_vect), postaraj się wmontować tam while, coś na kształt:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Nie czekasz nigdzie aż się bufor transmisji opróżni i wysyłasz cały czas coś.
    Bzdura. Skoro występuje to przerwanie, to tylko wtedy gdy ten bit jest ustawiony. Odsyłam do noty katalogowej.
    bckthd napisał:
    No właśnie takiego czekania w pętli chciałem uniknąć. Tą funkcję spełnia przerwanie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    Zła nazwa przerwania, powinno być USART_UDRE_vect:
    http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
    Naprawdę nie było żadnych ostrzeżeń przy kompilacji ?
  • #5 11052213
    bckthd
    Poziom 9  
    LordBlick napisał:
    bckthd napisał:
    No właśnie takiego czekania w pętli chciałem uniknąć. Tą funkcję spełnia przerwanie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    Zła nazwa przerwania, powinno być USART_UDRE_vect:
    http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
    Naprawdę nie było żadnych ostrzeżeń przy kompilacji ?


    No własnie też taką dałem nazwe przerwania i wtedy wywalało mi błędy przy kompilacji. Więc sprawdziłem w pliku avr/iom16a.h i tam było
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    W pliku avr/iom16.h jest tak jak napisałeś tyle że mi dołącza avr/iom16a.h bo mam atmegę16a-pu i tak mam zdefiniowana w makefile . Nie wiem czym się ona różni od atmegi bez "a".
  • #7 11052420
    bckthd
    Poziom 9  
    LordBlick napisał:
    To masz kopnita wersję avr-libc. Na pewno jest nowsza wersja.
    http://lists.gnu.org/archive/html/avr-libc-commit/2011-05/msg00011.html


    Masz rację jest to jakaś nieścisłość ale to nie jest powodem mojego problemu. Nazwa wektora w m16a jest inna ale to samo podstawia co dla m16 UART_UDRE_vect. Dla pewności zmieniłem w makefile na m16 bez "a" i wektory nazwałem wg dokumentacji libc, bo atmega16 od atmegi16a różni się tylko poborem prądu itp, a od strony programowania jest identyczna. Nie przynisło to jednak zadnego efektu więc inna nazwa wektora nie ma związku z moim problemem.
  • #8 11052451
    LordBlick
    VIP Zasłużony dla elektroda
    Na moje oko ten wskaźnik powinien on być uint16_i i bez gwiazdki - coś tu z nim może być problem... Generalnie kompilator sobie radzi z takimi problemami, ale nie zawsze.
  • #9 11052495
    bckthd
    Poziom 9  
    chodzi Ci o to ?
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Wskaźnik jest zawsze typu adresowego, a podaje się typ zmiennej, na którą wskaźnik będzie wskazywał. Ta gwiazdka właśnie informuje że jest to zmienna wskaźnikowa.

    http://pl.wikibooks.org/wiki/C/Wska%C5%BAniki
  • #11 11052555
    bckthd
    Poziom 9  
    Jest wybrany. wartość 0x86 to binarnie 10000110, a URSEL jest na najstarszym bicie.
    Nie potrzebuje ustawiać UBRRH bo preskaler ustawiam na 25 czyli w najstarszych bitach będzie 0 czyli standardowa wartość.
  • #12 11052581
    LordBlick
    VIP Zasłużony dla elektroda
    bckthd napisał:
    Nie potrzebuje ustawiać UBRRH bo preskaler ustawiam na 25 czyli w najstarszych bitach będzie 0 czyli standardowa wartość.
    Ale dobry zwyczaj nie pozostawiac niczego przypadkowi.
    W sumie to tablicę z tekstem lepiej wrzucać w pgm_space, bo tak to kompilator tworzy niejawny kod przepisujący do RAM-u, a to trwa chwilę. Jakby to było wywołane przed inicjalizacją USARTa raz to było bez znaczenia, a tak co chwila w pętli...
    http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html
  • #13 11052623
    bckthd
    Poziom 9  
    LordBlick napisał:
    bckthd napisał:
    Nie potrzebuje ustawiać UBRRH bo preskaler ustawiam na 25 czyli w najstarszych bitach będzie 0 czyli standardowa wartość.
    Ale dobry zwyczaj nie pozostawiac niczego przypadkowi.
    W sumie to tablicę z tekstem lepiej wrzucać w pgm_space, bo tak to kompilator niejawnie musi przepisywać do RAM-u, a to trwa chwilę.
    http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html


    W takim razie ustawię te bity UBRRH jednak wątpię, że jest to powodem tego problemu gdyż komunikacja działa normalnie gdy w funkcji uart_tx() jest jednorazowe opóźnienie przed załączeniem transmisji.

    Wiem że stały tekst lepiej trzymać we flashu jednak chciałem sobie zrobić funkcję która by przyjęła cokolwiek, czy to stały tekst czy wygenerowany przez program.
    Całkiem możliwe że to przez to opóźnienie przy wpisywaniu do ramu, jednak zastanawia mnie jedno, że ten delay można dać nawet po uruchomieniu transmisji
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    i wtedy transmisja działa.

    LordBlick napisał:
    Jakby to było wywołane przed inicjalizacją USARTa raz to było bez znaczenia, a tak co chwila w pętli...


    Nie tak co chwila- raz na 4 sekundy
  • #14 11052646
    LordBlick
    VIP Zasłużony dla elektroda
    Jakbyś w pętli zmieniał tekst, to by wysyłało poprzednią wersję, lub jej część, zależnie od opóźnienia..
    Warto także zaglądać do plików lss, aby wiedzieć, co tak na prawdę kompilator zmajstrował.
  • #15 11052663
    bckthd
    Poziom 9  
    LordBlick napisał:
    Jakbyś w pętli zmieniał tekst, to by wysyłało poprzednią wersję, lub jej część, zależnie od opóźnienia..


    Gdzie tam zmieniam jakiś tekst w petli?. Co 4 sekundy wpisuje do tablicy tekst i go wysyła. Nic podczas wysyłania nie zmienia w tekście. W ciągu 4 sekund chyba zdąży się wysłac parę znaków.

    Mam takie pytanie: czy jak jest wczytywany ten tekst do ramu to czy program może się dalej wykonywac podczas? czy musi poczekać aż zakończy się wczytywanie do ramu? wydaje się mi że może on tylko jedną czynność na raz wykonywać
  • #16 11052705
    LordBlick
    VIP Zasłużony dla elektroda
    bckthd napisał:
    Gdzie tam zmieniam jakiś tekst w petli?
    A czy ja o tym pisałem ?
    bckthd napisał:
    Mam takie pytanie: czy jak jest wczytywany ten tekst do ramu to czy program może się dalej wykonywac podczas?
    Przerwanie działa niezależnie.
  • #17 11052712
    bckthd
    Poziom 9  
    LordBlick napisał:
    bckthd napisał:
    Mam takie pytanie: czy jak jest wczytywany ten tekst do ramu to czy program może się dalej wykonywac podczas?
    Przerwanie działa niezależnie.


    Tak ale ten tekst jest wczytywany zanim przerwanie zostaje właczone.
  • Pomocny post
    #18 11052808
    krru
    Poziom 33  
    bckthd napisał:

    Oto kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod



    Nie wiem, czy to może być przyczyną, ale taka deklaracja oznacza "wskaźnik na ulotne dane", a potrzebny jest "ulotny wskaźnik" czyli

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Oczywiście volatile z przodu też może być niezależnie.
  • #19 11052844
    bckthd
    Poziom 9  
    krru napisał:
    bckthd napisał:

    Oto kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod



    Nie wiem, czy to może być przyczyną, ale taka deklaracja oznacza "wskaźnik na ulotne dane", a potrzebny jest "ulotny wskaźnik" czyli

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Oczywiście volatile z przodu też może być niezależnie.


    Strzał w dziesiątkę :). Wielkie dzięki!
    A mógłbys rozwinąć dlaczego tak jest bo średnio rozumiem.
  • #20 11052887
    sulfur
    Poziom 24  
    bckthd napisał:
    Jednak gdyby tak było to nic by nie dało danie delaya przed włączeniem przerwania od pustego bufora nadawczego.
    Możesz się mylić, ponieważ zmienne automatyczne trafiają na stos. Pytanie tylko, czy to coś szkodzi, bo adres wierzchołka stosu będzie mniejszy niż adres wskazujący przez zmienną wskaźnik. Dane dostępne pod tym adresem mimo wszystko powinny być poprawne.
  • #21 11053043
    bckthd
    Poziom 9  
    Już rozumiem. Robiłem wskaźnik na zmienną typu volatile char a sam wskaźnik nie był volatile. Tylko dalej nie wiem czemu działało to jak się dało delay. Powinno wtedy też nie działać.
    heh. Jest to troche dziwne.
    Na razie nie zamykam, może ktoś będzie w stanie wytłumaczyć to zjawisko.
  • #22 11053127
    stanleysts
    Poziom 27  
    Najlepiej zobacz jak obie wersje były zapisane w asemblerze, to może pokazać Ci co było nie tak.
  • #23 11053201
    bckthd
    Poziom 9  
    Problem w tym, że ja z asemblerem nie jestem obeznany. Coś tam kiedyś się uczyłem jednak nie miałem zbyt dużo wolnego czasu.

    Mam jeszcze jedno pytanie.
    Moja funkcja uart_tx wygląda aktualnie tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    W tym momencie ona działa, jednak nie jestem pewny czy będzie niezawodna.
    Chodzi o to że zmienna temp czyli tak naprawde wskaźnik do tablicy charów będzie żyła tylko w tej funkcji. To mi nie przeszkadza bo jak już przypisze jej wartość(wskaźnik) do mojej zmiennej wskaźnikowej to mam już globalnie adres do pierwszego elementu tablicy ze znakami. Nie wiem tylko jak długo ta tablica będzie zarezerwowana w pamięci. Teraz to działa bo nie robie nic innego podczas wysyłania napisu. Jednak co by było gdyby między przerwaniami wysyłania znaku program coś robił i nadpisałby w pamieci tą tablicę? Przez jaki czas ta tablica jest chroniona przed nadpisaniem? Czy tylko podczas wykonywania operacji przez funkcję której ta tablica jest argumentem?
    Chodzi oczywiście nie o jakąś tablice lokalną, tylko o tekst przekazywany do argumentu tej funkcji.
    Pasowałoby tu coś takiego co zarezerwuje miejce w pamięci dla tej tablicy i zwolni np. po wysłaniu całej tablicy.
  • #24 11053418
    krru
    Poziom 33  
    Jeśli chodzi o to volatile, to jego działanie być może np. tak, że w kompilator umieścił zmieną wskaźnik w rejestrze i tam ją trzymał. Wywołanie delay (to chyba jest makro rozwijane w odpowiednie pętle) spowodowało, że rejestr był potrzebny i musiał zostać zapisany do pamięci.

    A jeśli chodzi o te ostatnie problemy to zwykle wysyłanie RS ma własny bufor (najczęściej kołowy). Dane przeznaczone do wysłania są kopiowane (tutaj jest pewna strata wydajności w porównaniu z przekazaniem samego wskaźnika) do tego bufora i wtedy ich dalsze istnienie w programie głównym nie jest wymagane. Prościej też można skomponować jakiś napis z części, wręcz można wypisywać pojedycze znaki - można wywołać kolejną operację wysyłania zanim poprzednia się skończy - odpowiednie testy zostaną połączone w buforze (o ile jest wystarczająco duży).
    Jednak w prockach typu AVR nie zawsze są zasoby na takie 'wzorcowe' programowanie i niezbędne są różne sztuczki, ale wtedy trzeba wszystko starannie przemyśleć.
  • Pomocny post
    #25 11053503
    szulat
    Poziom 23  
    bckthd napisał:

    Chodzi o to że zmienna temp czyli tak naprawde wskaźnik do tablicy charów będzie żyła tylko w tej funkcji. To mi nie przeszkadza bo jak już przypisze jej wartość(wskaźnik) do mojej zmiennej wskaźnikowej to mam już globalnie adres do pierwszego elementu tablicy ze znakami. Nie wiem tylko jak długo ta tablica będzie zarezerwowana w pamięci.

    jeżeli wywolujesz tak: funkcja("staly tekst"); to do funkcji trafia wskaźnik do tablicy w ramie skopiowanej z flasha. wszystkie takie tablice są kopiowane tylko raz na samym początku (przed main()) i pozostają w ramie na zawsze, więc nie wpływa to na szybkość działania i jest w pełni bezpieczne - nikt ci danych nie ruszy (chyba że sam je zmienisz poslugujac sie wskaznikiem) - ale przy wiekszej ilości tekstów ten prosty sposób spowoduje że ram zapcha się bardzo szybko.
  • #26 11054250
    bckthd
    Poziom 9  
    Wielkie dzięki za odpowiedzi. O to własnie mi chodziło.
REKLAMA