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

Jak zastąpić delay_ms w funkcji SMS_answer i Send_SMS2? Timer nie działa poprawnie

marek-czarny 23 Lut 2017 09:29 1311 20
  • #1 16300599
    marek-czarny
    Poziom 10  
    Witam Forumowiczów

    Mam mały problem z zastąpieniem "delay_ms" wewnątrz funkcji.
    O ile w kodzie
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

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


    wszystko działa poprawnie to niestety w przypadku tej części kodu
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

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

    część po "else" się nie wykonuje. Po przesłaniu przez UART komendy "ODCZYT" na terminalu dostaję tylko napis "TEST", zapala się dioda i na tym koniec. Timer odlicza w dół prawidłowo. Co można z tym zrobić?

    pozdrawiam
  • #2 16300611
    dondu
    Moderator na urlopie...
    Nie piszesz nic o zmiennych Timer1 i Timer2.

    Jeżeli zmienne te są zmieniane w przerwaniu, to być może występuje sytuacja, w której Twój kod nie trafia na moment w którym np. zmienna Timer2 ma wartość równą 200. W takim przypadku powinieneś użyć operatorów >= lub <= w zależności co jest właściwe.
  • #3 16300629
    marek-czarny
    Poziom 10  
    Tak zmienne Timer1 i Timer2 są dekrementowane w przerwaniu
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Zauważyłem, ze pierwsza część kodu ta z Timerem1 działa poprawnie jeżeli przycisk "START" zostanie wciśnięty i jest trzymany. Jeżeli tylko go nacisnę i puszczę to zachowuje się tak samo jak przy odbieraniu stringa przez UART.
  • #4 16300698
    tmf
    VIP Zasłużony dla elektroda
    @marek-czarny Zapewne zmienna Timer1 jest zadeklarowana bez volatile. Popraw to i będzie ok. Ponieważ zmienne te jak sądzę są 16-bitowe to dla poprawności należałoby jeszcze zadbać o atomowość dostępu do nich w kodzie aplikacji.
  • #5 16300715
    marek-czarny
    Poziom 10  
    zmienne Timer1, Timer2 są "volatile"
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #6 16300733
    tmf
    VIP Zasłużony dla elektroda
    @marek-czarny No to tak jak pisał @dondu zmień te == na <=. Poza tym, twój kod w LED1 działa tak, że jeśli Timer1!=0 to nadawana jest mu wartość 500. Jeśli ta funkcja jest okresowo wywoływana to po prostu Timer1 ciągle ma wartość 500 lub niewiele mniejszą (zależy od ustawień timera). Pokaż cały kod, a nie wyrwane z kontekstu funkcje, których nie można analizować, bo nie wiadomo jak są wywoływane.
  • #7 16300793
    marek-czarny
    Poziom 10  
    Zmienna Timer1 czy Timer2 ma nadawaną wartość po wejściu w funkcję. Wyświetlam sobie to na LCD i widzę jak się zmienia.
    Jeżeli "START" nie jest wciśnięty to nic się nie dzieje zmienna Timer1 jest równa zero. Dopiero po wejściu w funkcję LED1 timer odlicza do zera.
    To samo z Timer2 - jest wyzerowany dopóki w buforze UART-a nic nie ma. W momencie gdy zadziała zdarzenie funkcja "void SMS_answer" odpala funkcję "Send_SMS2();" w której zmiennej Timer2 nadawana jest wartość. Timer odlicza prawidłowo do zera i się zatrzymuje ale przy wartości 200 powinna się wykonać druga część kodu a nie chce.....
    Cały kod
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #8 16300817
    dondu
    Moderator na urlopie...
    Odpowiedź już dostałeś:

    dondu napisał:
    Jeżeli zmienne te są zmieniane w przerwaniu, to być może występuje sytuacja, w której Twój kod nie trafia na moment w którym np. zmienna Timer2 ma wartość równą 200. W takim przypadku powinieneś użyć operatorów >= lub <= w zależności co jest właściwe.

    ... lub przerobić algorytm na inny.
  • #9 16300971
    marek-czarny
    Poziom 10  
    @dondu niestety operatory o których piszesz nic nie wnoszą. Nie ma też zmian odnośnie wartości zmiennej Timer - nie ma znaczenia czy jest 200, 199, 203
    dzięki za chęci
  • #11 16301036
    marek-czarny
    Poziom 10  
    A co ja robię? Przecież w tych funkcjach o których piszę i w pętli głównej nie ma ani jednego delay'a
  • #12 16301094
    tmf
    VIP Zasłużony dla elektroda
    @marek-czarny Ale IMHO twój kod jest błędny w założeniu. W main masz w kółko testowane key i naciśnięcie przycisku. Keśli key==0 naciskasz przycisk to odpalany jest LED1. Ta funkcja tylko sprawdza czy Timer1==0 i jeśli tak to nadaje mu 500, zapala LED itd. Natomiast jeśli nie jest równy, to sprawdza czy przypadkiem nie jest równy 200 na co szansa jest mała biorąc pod uwagę, że w międzyczasie wykonywane są bardzo czasochłonne funkcje. Także, żeby wykonała się sekwencja po else musiałbyś mieć piekielne szczęście.
  • #13 16301123
    dondu
    Moderator na urlopie...
    Jeszcze raz powtórzę:

    dondu napisał:
    Jeżeli zmienne te są zmieniane w przerwaniu, to być może występuje sytuacja, w której Twój kod nie trafia na moment w którym np. zmienna Timer2 ma wartość równą 200. W takim przypadku powinieneś użyć operatorów >= lub <= w zależności co jest właściwe.

    ... lub przerobić algorytm na inny.



    dondu napisał:
    Pozbądź się delay-ów. Opóźnienia licz za pomocą timera.
    Wtedy wyłapiesz dokładnie moment, w których ma się dziać to co planujesz.


    Jednym z tych delayów jest funkcja uart_puts() oraz zapewne funkcje obsługi LCD - czytaj punkt 5: http://mikrokontrolery.blogspot.com/2011/04/problemy-c-przerwania.html

    Masz więc sporo delayów w pętli głównej.

    Edit:

    Przy okazji pytanie: W jakim celu wyświetlasz na LCD z tak ogromną częstotliwością, skoro oko tego i tak nie zauważy, a zabiera mikrokontrolerowi prawie 100% mocy obliczeniowej, z czego 95% spędza w delay-ach?
  • #14 16301505
    marek-czarny
    Poziom 10  
    tmf napisał:
    @marek-czarny Ale IMHO twój kod jest błędny w założeniu. W main masz w kółko testowane key i naciśnięcie przycisku. Keśli key==0 naciskasz przycisk to odpalany jest LED1. Ta funkcja tylko sprawdza czy Timer1==0 i jeśli tak to nadaje mu 500, zapala LED itd. Natomiast jeśli nie jest równy, to sprawdza czy przypadkiem nie jest równy 200 na co szansa jest mała biorąc pod uwagę, że w międzyczasie wykonywane są bardzo czasochłonne funkcje. Także, żeby wykonała się sekwencja po else musiałbyś mieć piekielne szczęście.


    Jeżeli naciskam przycisk START i go przytrzymuję to funkcja LED1 wykonuje się zawsze i nie ma to nic wspólnego ze szczęściem.
    Program wchodzi w funkcję nadaje Timerowi wartość 500, zapala diodę i wysyła napis na terminal a następnie po osiągnięciu przez Timer1 wartości 200 gasi diodę, wysyła napis i ustawia zmienną key na jeden dlatego żeby funkcja wykonała się tylko raz następnie w pętli głównej zmienna key jest zerowana żeby można było ponownie wejść w funkcję LED1.
    Powtarzam funkcja wykonuje się zawsze!!!
    Chciałem ten sam schemat zastosować po odebraniu komendy przez UART.
    Co do wyświetlania w pętli wartości timera to się zgadzam, ze niepotrzebnie ale to niczego nie zmienia w działaniu programu
  • #15 16303368
    dondu
    Moderator na urlopie...
    Z programu wynika, że przerwanie wykonuje się 100Hz, co oznacza, że stosujesz kwarc 16MHz - czy tak?

    Jeśli tak, to 500 oznacza 5 sekund, 300 to 3 sekundy, a 200 to 2 sekundy.

    Nie piszesz jak długo przytrzymywany jest przycisk.

    Program wchodzi w funkcję LED1() tylko wtedy, gdy przycisk jest naciśnięty. Jeśli przytrzymanie przycisku trwa krócej niż 3 sekundy, to program nie wchodzi w funkcję LED1(), przez co nie wykona się druga część if-a sprawdzająca wartość 200.


    W przypadku funkcji SMS_answer() która wywołuje Send_SMS2() która to z kolei ewentualnie sprawdza raz i tylko raz warunek Timer2==200. Jak już pisaliśmy musisz mieć szczęście, by trafić dokładnie na ten moment. W większości przypadków wejdzie tylko raz w warunek !Timer2.

    Swoją drogą:
    - nie wiemy ile razy w ciągu odbierania danych z usart (po zakończeniu, czy też za każdym bajtem) wykona się funkcja SMS_answer(), bo nie widzimy źródła register_uart_str_rx_event_callback(),
    - masz rezystor pull-down na pinie przycisku?
  • #16 16303386
    marek-czarny
    Poziom 10  
    Problem rozwiązany.
    Wystarczyło wyprowadzić dodatkowy warunek w pętli głównej po zdarzeniu UART EVENT
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    a zmienną key1 ustawiać na "1" wewnątrz funkcji "Send_SMS2" żeby funkcja wykonała się tylko raz.
    Mało tego w pętli specjalnie dodałem oprócz wyświetlania zmiennej Timer2 kolejnych zmiennych m i n odpowiedzialnych za zmianę stanu diod LED1 i LED2 żeby zobaczyć czy program się nie blokuje. Zmienne inkrementowane w przerwaniu
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Wszystko działa bez żadnych problemów więc szanowni koledzy nie mieli racji co do opóźnień w wyświetlaniu i funkcjach uart_puts więc jeżeli wasze rady sprowadzają się tylko do tego aby nie wyświetlać w pętli na LCD lub nie używać funkcji to sobie darujcie. Idąc tym tropem to w zasadzie żaden program nie powinien działać . No i prześledźcie jak działają zmienne Timer - one są uruchamiane dopiero po wejściu w funkcję więc tu nie ma przypadku, że program "szczęśliwie " trafi na jakąś wartość Timera.
  • #17 16303403
    tmf
    VIP Zasłużony dla elektroda
    @marek-czarny Nie rozwiązałeś problemu, tylko go co najwyżej ominąłeś w niezbyt elegancki sposób. Poza tym ciągle masz problemy z atomowym dostępem do zmiennych, w efekcie program tylko pozornie działa poprawnie.
  • #18 16303419
    dondu
    Moderator na urlopie...
    Masz rację, że wcześniej nie przeanalizowałem dokładnie Twojego kodu i zrobiłem to dopiero dzisiaj stąd mój poprzedni post.
    Wynika to z faktu chaosu jaki masz w programie i braku poprawnego formatowania kodu co ułatwia jego analizę. Dbaj o to, wklejając go na forum.

    Co do szczęścia nadal nie masz racji, bo właśnie to poprawiłeś, każąc mu sprawdzać warunki cały czas, podczas gdy wcześniej robił to tylko raz lub tyle razy ile razy odebrał bajt, czego nie wiemy, bo nie odpowiedziałeś na pytanie.

    Dlatego wnioski w stosunku do nas wyciągaj ostrożnie.

    Generalna uwaga dot opóźnień jest jednak nadal istotna, choć jak widać starasz się je zwalczać, bo założyłeś ten temat. Pamiętaj jednak o ukrytych opóźnieniach w funkcjach bibliotecznych, które używasz.

    No i atomowość, o której pisze tmf - znowu masz szczęście, że działa, ale prędzej czy później nie będzie.
  • #19 16303444
    marek-czarny
    Poziom 10  
    tmf napisał:
    @marek-czarny Nie rozwiązałeś problemu, tylko go co najwyżej ominąłeś w niezbyt elegancki sposób. Poza tym ciągle masz problemy z atomowym dostępem do zmiennych, w efekcie program tylko pozornie działa poprawnie.


    Zadając pytanie na forum liczyłem na coś więcej niż tylko " pozbądź się delay-ów" i "nie wyświetlaj w pętli głównej" stąd moja irytacja.
    Piszesz o niezbyt eleganckim sposobie - może i tak ale innego nie znam na razie a jeżeli kolega wie jak to można zrobić lepiej to proszę o przykład

    Dodano po 2 [minuty]:

    dondu napisał:

    No i atomowość, o której pisze tmf - znowu masz szczęście, że działa, ale prędzej czy później nie będzie.


    Proszę o przybliżenie zagadnienia
  • #20 16303466
    dondu
    Moderator na urlopie...
    Zmienne int są zmiennymi dwubajtowymi. Jeśli nastąpi przerwanie w momencie kiedy gdzieś w programie głównym następuje na przykład sprawdzanie warunku na tych zmiennych może wystąpić sytuacja, że jeden z bajtów takiej liczby zostanie zmieniony przez przerwanie.

    Rezultatem może być nieprawidłowe zinterpretowanie wartości zmiennej przez warunek w programie głównym.
    To samo dotyczy sytuacji w odwrotną stronę.

    Dlatego też sprawdzając zmienne, czy też dokonując zmiany ich wartości w programie głównym, powinieneś na ten czas zablokować co najmniej to przerwanie, które je używa. Najwygodniej jest zablokować przerwania globalne.
  • #21 16303472
    tmf
    VIP Zasłużony dla elektroda
    @marek-czarny Programowanie nie jest dla osób szybko się irytujących. Rozwiązując problem, rozwiązuje się go po kolei. Obsługa LCD wprowadza znaczące opóźnienia, zapewne w kodzie tych funkcji masz delay lub oczekiwanie na jakiś stan. Efektywnie odpowiada to sytuacji w której delay, w dodatku losowy, wpakowałbyś do pętli głównej. Może tego nie dostrzegasz, ale nie zarzucaj niekompetencji innym. Teraz pomyśl - porównujesz zmienną Timer używająć operatora == - porównanie będzie prawdziwe wyłącznie, gdy zmienna ma określoną wartość. Dopóki czas obiegu pętli głównej jest dużo krótszy niż kolejne dekrementacje zmiennej timer wszystko jest ok. W miarę jak się będzie wydłużał narasta problem - przy pewnych wartościach powstanie aliasing opóźnienia timera z opóźnieniem pętli i będą bardzo ciekawe efekty, w efekcie problem super trudny do wykrycia. Jeśli czas obiegu pętli przekroczy kolejne tyknięcia timera, to twoje opóźnienia w ogóle staną się pobożnym życzeniem. Do tego dodajmy błąd o którym już piszę trzeci raz, którego nie chcesz zauważyć - atomowy dostęp do zmiennej 16 bitowej na 8-bitowy MCU. Przeanalizuj sobie co się stanie jeśli w chwili przypisania wartości zmiennej Timer lub porównania wartości tej zmiennej wystąpi przerwanie timera modyfikujące tą zmienną... Twój kod ma wiele takich losowych pułapek. Są też problemy kosmetyczne - np. dekrementacja zmiennych w przerwaniu. Przypisujesz zmienne Timer zmiennym xx, gdzie obie są volatile uin16_t - jaki to ma sens?
    Do tego zaczynasz tworzyć co raz bardziej skomplikowane warunki, używając zmiennych globalnych, modyfikowanych w funkcjach. To bardzo zły styl programowania, dramatycznie zmniejszający czytelność kodu. Jak to wszystko zrobić dobrze - żeby odpowiedzieć na to pytanie najpierw trzebaby wiedzieć co chcesz osiągnąć. Odpowiedź, że opóżnienie bez delay nic nie wnosi :)
REKLAMA