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

ATmega16 [C] - Samowolne odpalanie przerwania po mrugnięciu kontaktem etc.

tictac1991 21 Sie 2013 21:01 1788 9
REKLAMA
  • #1 12653056
    tictac1991
    Poziom 10  
    Posty: 16
    Witam mam bardzo nietypowy jak dla mnie problem z układem który ostatnio wykonałem.

    Mianowicie jest to centralka alarmowa oparta o uC ATmega16. Działa na tej zasadzie że pod wejście PB2(INT2) podpięty jest kontrakton (zwiera do masy) do którego przykładam magnes i albo rozbrajam albo uzbrajam centralkę.

    Z grubsza to wygląda tak: PB2(INT2) skonfigurowane jest jako wejście z podciągnięciem do VCC(+) a przerwanie z tego wejścia aktywowane jest zboczem narastającym. Po przyłożeniu i puszczeniu magnesu do kontraktonu odpalane jest przerwanie a w jego procedurze ustawiania jest tylko "flaga_przycisk" która została zadeklarowania jako "volatile", następnie w głównej pętli (jeżeli ustawiona jest flaga "flaga przycisk" to czekam aż drgania styków ustaną i sprawdzam czy przycisk na pewno jest puszczony jeżeli tak to jeżeli alarm jest uzbrojony to go rozbrajam i zeruje flagę a jeżeli nie to go uzbrajam i zeruje flagę.

    Wszystko działa ok do momentu kiedy np. nie mrugnę kontaktem w pomieszczeniu albo np. w drugim pokoju ktoś nie załączy czegoś do gniazdka albo nie dotknę czymś metalowym jakiejś nóżki procesora albo jakiejś innej metalowej rzeczy na płytce bo w tym momencie przerwanie samowolnie się odpala i wykonuje się wyżej opisana procedura.

    Dodam że napięcie na całym uC jak i na samej nóżce PB2(INT2) w momencie np. mrugnięcia kontaktem jest stałe i nie "skacze".

    Układ zasilany jest z zasilacza impulsowego 12V/2A ponieważ takiego potrzebuje do zasilenia syreny etc, napięcie jest dalej stabilizowane za pomocą LM7805 wraz z kondensatorami do filtracji i małymi kondensatorami ceramicznymi przy stabilizatorze jak i przy nóżce zasilania uC.

    Ma ktoś jakiś pomysł dlaczego tak się może dziać? Bardzo proszę o pomoc.

    Poniżej cały kod zródłowy w C do centralki:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • REKLAMA
  • Pomocny post
    #2 12653132
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    Kilka uwag:
    - korzystaj z funkcji i rozbijaj program na funkcje. Niektórzy twierdzą, że jeśli blok ma więcej niż 10 instrukcji to należy go wyrzucić do funkcji. Moze to przesada, ale ułatwia czytanie kodu.
    - nie zostawiaj niewykorzystanych pinów jako wejścia w układzie - łapią zakłócenia, co w centralce alarmowej nie jest zalecane.
    - wewnętrzny pull up ma ok. 50 kom, to dużo za dużo, jak na centralkę. Daj zewnętrzny rezystor np. 4k7 i problemy znikną lub ulegną ograniczeniu.
    - pokaż schemat i projekt PCB, w 95% jest to wina złego routowania sygnałów lub braku właściwej filtracji.
    - zasadniczo niedobrze jest podłączać elementy generujące nieprzewidywalną ilość przerwań (drgań) pod wejście przerwania. O wiele lepiej czytać je okresowo w przerwaniu timera i tam też robić debouncing.
    - debouncing poprzez delay jest OKDR. Zobacz na blogu http://mikrokontrolery.blogspot.com jak się debouncing robi. W ogóle zapomnij o funkcjach delay, do niczego nie są potrzebne, a w 90% prowadzą do paskudnego kodu i paskudnego stylu programowania.
  • REKLAMA
  • #3 12653143
    Ture11
    Poziom 39  
    Posty: 5419
    Pomógł: 573
    Ocena: 1128
    Cóż, pozostaje zapytać, jaka jest długość kabla od czujnika do procesora, i jaki typ.

    Napięcie, które "nie skacze" zostało zmierzone czym, bo powinno być oscyloskopem... Czy tak?

    Może kolega spróbować zewnętrznego rezystora podciągającego, nie sądzę jednak, by to coś zmieniło.

    Najlepiej byłoby, gdyby był schemat z podanymi długościami przewodów. Wydaje mi się, że do mrugnięcia od kontaktronu nie musi kolega wykrywać zbocza narastającego, ani w sumie nawet przerwania, tu nie ma znaczenia, czy alarm pojawi się na 20uS po wykryciu, czy 20mS, bo itak czas reakcji człowieka jest dłuższy... Chyba, że centralka jest czymś bardzo zajęta w tym czasie...
  • REKLAMA
  • Pomocny post
    #4 12653237
    BlueDraco
    Specjalista - Mikrokontrolery
    Posty: 6479
    Pomógł: 939
    Ocena: 421
    TMF: inni twierdzą, że procedura - to fragment kodu przeznaczony do WIELOKROTNEGO użycia, co oznacza, że nie ma sensu tworzenie procedur wywoływanych w jednym miejscu programu. ;) To tak z podstaw teorii programowania strukturalnego... Rozbijanie kodu na jednokrotnie wywoływane procedury może lekko zwiększa czytelność, ale zwiększa zajętość pamięci RAM i ROM oraz zmniejsza wydajność.

    Autor: użycie przerwania od styku i odczekiwanie w pętli głównej to fatalny pomysł (przerwanie zgłosi się wielokrotnie itd.) Lepiej odpytywać stan wejścia w przerwaniu timera, które i tak jest potrzebne. Z kolei przerwanie, które TYLKO ustawia znaczniek testowany w pętli jest zupełnie bez sensu.

    Zapewne masz długi przewód do tego styku. Daj na nodze procesora rezystor podciągający najwyżej 4k7, kondensator min. 100 nF do masy i ew. rezystor szeregowy do czujnika, poniżej 1k. Co prawda możesz niby zastąpić kondensator właściwym oprogramowaniem odczytu zestyku w przerwaniu timera, ale będzie on również gasił ew. impulsy zakłóceń, które mogłyby elektrycznie, a nie tylko logicznie zakłócić pracę uC.
  • #5 12654241
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    BlueDraco napisał:
    TMF: inni twierdzą, że procedura - to fragment kodu przeznaczony do WIELOKROTNEGO użycia, co oznacza, że nie ma sensu tworzenie procedur wywoływanych w jednym miejscu programu. ;) To tak z podstaw teorii programowania strukturalnego... Rozbijanie kodu na jednokrotnie wywoływane procedury może lekko zwiększa czytelność, ale zwiększa zajętość pamięci RAM i ROM oraz zmniejsza wydajność.


    W jaki sposób miałoby zwiększać zajętość pamięci RAM (!), bo ROM to przy kiepskim kompilatorze to mogę sobie wyobrazić. Niemniej mamy XXI wiek, kompilatory to nie proste programiki napisane przez zapaleńców. Potrafią sobie poradzić z taką sytuacją i osadzić funkcję zamiast ją wywoływać. Z drugiej strony co jest ważniejsze - czytelność kodu ułatwiająca jego poprawne napisanie i przetestowanie, czy straconych kilka taktów?

    Dodano po 2 [minuty]:

    Czy czasem takie działanie jakie proponujesz nie podchodzi pod premature optimization?
  • #6 12654286
    BlueDraco
    Specjalista - Mikrokontrolery
    Posty: 6479
    Pomógł: 939
    Ocena: 421
    Zajętość RAM: proste - stos. Dodaj do tego narzut na przekazywanie zbędnych argumentów.

    Sugerujesz, że programista powinien podzielić program na procedury i polegać na tym, że mądry współczesny kompilator z LTO z powrotem scali to w jeden liniowy kod? No cóż, można i tak, ale skoro chcemy uzyskać liniowy kod w jednym kawałku, to może prościej byłoby go tak napisać, nie uważasz?

    Jednym ze skutków takiego sztucznego dzielenia kodu na procedury inicjowania poszczególnych peryferiali jest to, że ustalenie zawartości rejestrów sterujących np. kierunkiem linii portów lub ich własnościami elektrycznymi następuje w wyniku kilkunastu operacji logicznych zamiast w wyniku pojedynczego zapisu.

    Nie zrozum mnie źle - ja nie rozdzieram szat z powodu takich technik programowania, o jakich piszesz; ja tylko pokazuję, że techniki, które ja preferuję też mają istotne zalety i można przytoczyć sensowne argumenty za jednym i drugim podejściem.

    Moje podejście istotnie redukuje długość kodu i skraca czas wykonania. Być może moja ulubiona tablica inicjowania peryferiali wygląda nieco przerażająco zajmując np. 80 czy 100 kolejnych wierszy tekstu programu, ale mam wszystko w jednym miejscu i dokładnie widzę, jak zaprogramowane są porty, a te 100 linii zastępuje 500 linii rozsianych po różnych plikach źródłowych w programie napisanym z użyciem SPL lub w sposób, który Ty preferujesz.
  • #7 12654379
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    Nie zrozum mnie źle, nie uważam, że twój sposób jest błędny. Ale warto podyskutować na temat ciekawszy niż skróć przewody, żeby zmniejszyć zakłócenia :)
    Zgadzamy się że należy w funkcję ująć najmniejszy wielokrotnie wykorzystywany fragment kodu, prawda?
    Reszta to kontrowersje. Oczywiście to co piszę nie znaczy, że np. wszystko co ma więcej niż 10 linii należy podzielić. Ale z drugiej strony jak masz wielokrotne warunki, tak jak w kodzie autora, to śledzenie tego jest tragedią. Konstrukcja typu:
    if(costam) funkcja(); else funkcja(); 

    jest czytelniejsza niż:
    if(costam)
    {
    500 linii kodu z kolejnymi ifami;
    } else
    {
    500 linii kodu z ifami;
    }

    Owszem, wierzę w kompilator i jego możliwości optymalizacji kodu. Jeśl ktoś nie wierzy to ma konstrukcje języka C: inline, static inline, ma atrybuty (always_inline) lub (flatten). To gwarantuje brak narzutów. Nie zgodzę się co do nadmiernego zużycia stosu. Stos jest wykorzystywany wielokrotnie i potrzebujesz tyle wolnego miejsca ile zajmuje największe wywołanie. W efekcie to miejsce i tak masz, nic dodatkowego nie tracisz, bo przecież gdzieś w kodzie masz większe wywołania. A przecież takie funkcje (które istotnie funkcjami nie są) ani nie są wywoływane rekurencyjnie, ani zwykle nie są przesadnie zagnieżdżone.
    Także uważam, że podejście, które prezentuję ma istotne zalety:
    - lepsza czytelność kodu,
    - łatwiejsze debugowanie,
    - zmiana w kodzie funkcji nie pociąga za sobą nieprzewidzianych zmian w reszcie kodu,
    - przejrzyste argumenty i wyniki, dzięki czemu analiza jej poprawności jest łatwiejsza.
    Wady:
    - potencjalne zwiększenie objętości kodu,
    - potencjalne zwolnienie wykonywania programu.
    Co do inicjalizacji peryferii. Można w jednym miejscu i ma to istotnie zalety o których piszesz. Ja preferują inicjalizację lokalną, to znaczy, że np. moduł odpowiedzialny za SPI inicjuje sobie SPI. Dzięki temu w main mam np. SPI_init() i nie obchodzi mnie co to robi. Zaleta - jeśli moduł działa dobrze to jest ok, ale istotnie śledzenie w jakim ostatecznie stanie znajdzie się układ peryferyjny jest trudne. Z trzeciej strony - zapuszczam debugger, breakpointa po inicjalizacjach i na liście peryferii sobie przeglądam czy wszystko jest skonfigurowane jak chciałem. To można zrobić nawet bez debuggera - wykorzystując symulator. Wymaga to oczywiście jego posiadania (Atmel daje do swoich procków) i posiadania wygodnego IDE, które ładnie pokazuje peryferia z rozbiciem na opisowe funkcje i poszczególne bity/pola bitowe, ale tu też mi Atmel Studio pomaga. W Eclipse dostałbym po prostu wartość hex rejestru i czekałoby mnie mozolne porównywanie z notą procka, ponieważ z ARMami tak masz, rozumiem sensowność twojego podejścia :)
    BTW, tak to wygląda na podglądzie:
    ATmega16 [C] - Samowolne odpalanie przerwania po mrugnięciu kontaktem etc.
  • REKLAMA
  • #8 12654864
    BlueDraco
    Specjalista - Mikrokontrolery
    Posty: 6479
    Pomógł: 939
    Ocena: 421
    Tak, zZgadzamy się że należy w funkcję ująć najmniejszy wielokrotnie wykorzystywany fragment kodu. Oczywiście są wyjątki od tej reguły i kontrowersje co do znaczenia słowa "najkrótszy". ;)

    Wyobraź sobie, że w środowiskach dla różnych ARMów bazujących na Eclipse (i nie tylko) też jest podglądanie rejestrów z opisem pól bitowych. Ja akurat tego (ani w ogóle debuggera) nie używam, bo te części moich projektów, w których miewam błędy, działają w czasie rzeczywistym i ich zatrzymanie powodowałoby często uszkodzenie urządzenia. Te, które dają się debugować, złośliwie zwykle nie zawierają błędów, więc tam z kolei nie ma czego podglądać.

    A sztuczne dzielenie kodu na procedury może zostać częściowo zastąpione przez przyzwoite formatowanie i dobrze umieszczone komentarze.

    Wielką tablicę inicjującą pisze się sekcja po sekcji, więc rejestry poszczególnych peryferiali są inicjowane kolejno - to akurat jest b. czytelne, w czym dodatkowo pomagają komentarze i odstępy. Za to przypisanie portów do peryferiali i ustawienie ich charakterystyki robię na początku tablicy w kilku liniach tekstu, po jednej na rejestr, a nie po 4 na każdą nogę każdego peryferiala, co wielokrotnie skraca kod inicjujący, nie tylko w wykonaniu, ale również w zapisie - i w konsekwencji podnosi jego czytelność.
  • #9 12654923
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    Skraca i przyśpiesza to pewne, chociaż nie wiem jakie znaczenie ma szybkość wykonywania kodu, wykonywanego jednokrotnie. Natomiast czy czytelność się poprawia to wątpię, ale może źle sobie wyobrażam twoją "Wielką tablicę". Podam przykład. Inicjuję SPI, ale żeby ono poprawnie działało muszę określić kierunki wykorzystywanych przez SPI pinów IO. Dla mnie jest czytelniej jeśli zmiana tych pinów IO następuje razem z konfiguracją interfejsu, bo wiem czemu ona służy. Natomiast jeśli gdzieśtam razem zainicjuję wszystkie IO, a dalej w tej tablicy SPI, to przeglądając fragment inicjujący piny będę się zastanawiał dlaczego ten pin np. jest wyjściem i czemu służy, a przeglądając część inicjującą SPI będę się zastanawiał, czy kierunki pinów są właściwe.
    Co do podglądu w Eclipse, to mam niewielkie doświadczenia, głównie z CodeWarrior, gdzie podgląd przypomina pracę na linuksowej konsoli :)
  • #10 12657658
    tictac1991
    Poziom 10  
    Posty: 16
    Dołożenie kondensatora 100nF do nóżki od kontraktonu i do masy + zewnętrzny pull up 1k (taki akurat miałem) pomogło i problem zniknął - przy mruganiu światłem w pomieszczeniu etc. układ działa poprawnie.

    Co do rozbijania kodu na funkcje stwierdziłem, że w tym przypadku wystarczy jedyne funkcja "czas()" bo prawie żaden inny blok instrukcji nie jest powtarzany po kilka razy.

    Dzięki wszytkim za pomoc i za rady odnośnie debouncingu ;)

Podsumowanie tematu

✨ Użytkownik zgłosił problem z centralą alarmową opartą na mikrokontrolerze ATmega16, która nieprawidłowo reagowała na mrugnięcia kontaktem. Zastosowane przerwanie na pinie PB2(INT2) aktywowało się w wyniku drgań styków, co prowadziło do niepożądanych reakcji. Uczestnicy dyskusji zasugerowali kilka rozwiązań, w tym zastosowanie zewnętrznego rezystora podciągającego (4.7kΩ) oraz kondensatora (100nF) do masy w celu eliminacji zakłóceń. Zmiana ta okazała się skuteczna, a problem został rozwiązany. Dodatkowo poruszono kwestie dotyczące organizacji kodu, debouncingu oraz optymalizacji pamięci.
Podsumowanie wygenerowane przez AI na podstawie treści dyskusji.
REKLAMA