Elektroda.pl
Elektroda.pl
X
Elektroda.pl
Computer Controls
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

LPC1114 - timer 32bitowy dziwne dzialanie

12 Paź 2013 00:36 2223 22
  • Poziom 10  
    Witam wszystkich.

    Problem na pierwszy rzut oka wydaje sie błachy, ale spotkałem sie z bardzo dziwnym zachowaniem mikrokontrolera. Chodzi tu programowanie jego peryferii a mianowicie o timer 32bitowy.

    Co chciałem zrobić:
    Chciałem uruchomic timer z czestotliwoscia równą częstotliwości PCLK.
    Po odpowiednim skonfigurowaniu pętli PLL i odpowiednich rejestrów moje PCLK = 72Mhz. Ustawiam preskaler na wartosc 0 dzieki temu co kazdy tik zegara PCLK, timer counter jest inkrementowany. Oto kawalek kodu:

    Code:
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<9);  //zalaczenie zegara
    
       LPC_TMR32B0->PR=0;                //preskaler
       //nadanie odpowiednich priorytetów dla przerwania
        NVIC_EnableIRQ(TIMER_32_0_IRQn);
       NVIC_SetPriority(TIMER_32_0_IRQn, 1);   


    Następnie chce tak zrobić że jeżeli TC (timer counter) zliczy do wartości 100000 to wywoła sie przerwanie, ale TC nie zostanie wyzerowany. Kożystam tutaj z rejestru MR1, ktorego ustawiam na 100000. Dodatkowo ustawiam odpowiednio rejestr MCR tak zeby było generowane przerwanie timera jak TC osiągnie wartość MR1. Czyli ustawiam mu 3 bit. Oto kolejny kawałek kodu:

    Code:
    //wlaczenie przerwania od porownania i reset przy porownaniu
    
    LPC_TMR32B0->MCR |= 1<<3;      
    LPC_TMR32B0->MR1=100000;    //wartosc porownania



    Te kody ktore przedstawilem do tej pory to kody ktore inicjalzuja prace timera i to znajduje sie w procedurze o nazwie init_timer();

    W przerwaniu gdy ono wystapi rejestr MR1 jest zwiekszany o kolejne 100000, zeby poraz kolejny wygenerowac przerwanie, po zliczeniu przez TC 100000 impulsow.
    Oto jak wyglada moje przerwanie dla timera 32bit:


    Code:
    void TIMER32_0_IRQHandler(void)
    
    {
          //w zasadzie tu sie nic nie wykonuje
            LPC_TMR32B0->MR1 += 100000;   //przesuwamy o wartosc

       LPC_TMR32B0->IR = 1;      //pozwolenie na ponowne wejscie do przerw.
       __nop();
       __nop();
    }




    Oto jak wyglada moja funkcja main():

    Code:
    int main(void) 
    
    {
            int a = 0;  //zmienna

       //inicjalizacja
       init_timer(); 

            //uruchomienie timera
            LPC_TMR32B0->TC = 0;      //zerujemy licznik
       LPC_TMR32B0->TCR |= 1;   //wlaczenie timera

            while(1)
            {
                   a = 1;
                   LCD_WriteText("aaaaaaaaaaa");
                   delay_us(100000); //czekaj
                   a = 2;
                   LCD_WriteText("                  ");
                   delay_us(100000); //czekaj
             }
    }   


    Jak program powinien dzialac:
    Po zainicjalizowaniu i uruchomieniu timera na ekranie w petli powinienem widzieć migajacy tekst.

    A co sie dzieje:
    Program wypisuje "aaaaaaaaaaaa" i sie zawiesza jak tylko poraz pierwszy wejdzie
    do przerwania. Na poczatku pomyslalem ze to problem z wyjsciem z przerwania, ale nic bardziej mylnego bo instrukcja zakonczenia przerwania jest w procedurze przerwania. Potem pomyslałem ze jakis problem z wypisywaniem tekstu na wysiwetlaczu, ale to akurat działą poprawnie. To dodałem zmienna a i ja tez zmienialem w petli. Zdebagowalem program no i okazuje sie ze rowniez i zmienna nie jest zmieniana co kolejne opoznienia czasowe.

    ROZWIAZANIE:
    Gdy używam zamiast MR1 rejstru MR0 z odpowiednio skonfigurowanym
    (MCR |= 1<<0) to wszystko działa jak trzeba. Natomiast dla rejstrów MR1, MR2 i MR3 program sie wiesza.

    Gdzie popełniam błąd. A moze tak poprostu jest. Jak by ktos mógł to prosił bym zeby spróbował odpalic taki program gdzieś u siebie.
    Ja potrzebuje uzywac wszystkich 4 rejestrow MR0...3 ale nie moge bo na trafiłem na cos dziwnego, co kompletnie wydaje mi sie nie logiczne.

    Pracuje w srodowisku Keil ARM uVision4.
    Prosze o pomoc. I z góry dzięuję.

    Dodano po 14 [minuty]:

    Ok. Przesiedzialem dzisiaj nad tym caly dzień! Ale przedchwilą pomyslałem zeby zerknąć jak sie programuje rejestr IR ktory pozwalan na wyjście z przerwani, no i trzeba go odpowiednio wysterowac tez dla innych rejestrow porownujcych MR0...3.

    Tak że, nie ma to jak samemu sobie odpowiedzieć na pytanie. Ale czasem tak jest ze caly dzien brnie sie w głeboko w d...ie, zeby wieczorem po napisaniu tekstu, wpasć po 5 min na ta to gdzie jest problem.

    Gdzieś kiedy kumpel mi mówił że w jednej firmie zainstalowano takiego misia do którego nalezało pójść i powiedzieć mu na czym polega problem zanim poszło sie do osoby przełozonej na wyzszym stanowisku. I połowe problemu ludzie potrafili sami rozwiązac jak powiedzieli misiowi wszystko po kolei co zrobili i co sie dzieje i gdzie widzą problem.

    Mi widocznie tez tego było potrzeba :D
    Pozdrawiam.
    Temat do zamknięcia.
  • Computer Controls
  • Specjalista - Mikrokontrolery
    KrukersRadek napisał:
    Po odpowiednim skonfigurowaniu pętli PLL i odpowiednich rejestrów moje PCLK = 72Mhz


    Nie przesadziłeś troszkę? To nie STM32. MainClock może być skonfigurowany na 100MHz, ale PCLK może być już tylko połową z tego. Ta sztuka na razie działa, ale trafisz na inną która już może robić problemy. Wszystko zależy też od napięcia zasilania i temperatury.
  • Poziom 10  
    Smigają elegancko na takiej prędkości :D Fakt troszke podkręcone, ale jeszcze zadna sztuka mnie nie zawiodła. Może rzeczywiście nie sprawdzałem tego przy róznych temperaturach pracy. Choć procek sie nie grzeje, a temp otoczenia w polsce nie ma duzego rozrzutu. A co do PCLK to wcale nie musi być połową MainClock, wystarczy ustawić preskaler SYSAHBCLKDIV na 1 i mamy takie samo taktowanie jak na MainClock.
  • Computer Controls
  • Specjalista - Mikrokontrolery
    KrukersRadek napisał:
    A co do PCLK to wcale nie musi być połową MainClock, wystarczy ustawić preskaler SYSAHBCLKDIV na 1 i mamy takie samo taktowanie jak na MainClock.

    Ja nigdzie nie napisałem, że MUSI być POŁOWĄ, tak samo nie napisałem, że MainClock musi mieć 100MHz. Chodziło mi tylko o to, że producent przewidział dla PCLK max 50MHz i co się z tym wiąże jeszcze to max 20MHz bez żadnych dodatkowych cykli opóźniających dla FLASH. Mamy możliwość konfiguracji do 3 cykli więc teoretycznie do 60MHz dostęp do flash jest niezagrożony. Te 50MHz z powietrza się nie wzięło. Była tu osoba co i do 100MHz rozpędziła ten uC i bez problemu działał, no ale na stabilność pracy ma wpływ wiele czynników zewnętrznych.
    Nie mam zamiaru Ci mówić, że tak nie wolno. Zwracam tylko uwagę, że jeżeli nie chce się mieć nieprzewidzianych sytuacji trzeba trzymać się zaleceń producenta.
  • Specjalista - Mikrokontrolery
    A ja powiem: tak nie wolno. Producent gwarantuje działanie układu do 50 MHz. Jeżeli ustawisz zegar na 72 MHz - możesz mieć różne problemy, trudne do przewidzenia i usunięcia.
    Czy chciałbyś jeździć samochodem z hamulcami, które "na ogół działają", tylko raz na 50 hamowań nie zadziałają?

    Jeden drobiazg - kasuj znaczniki w IIR na początku obsługi przerwania, jako pierwszą czynność po wykryciu ich ustawienia, a nie na końcu. To ważne, przynajmniej z dwóch powodów.
  • Poziom 10  
    A czemu na początku? Kto tak Cie uczył?
  • Specjalista - Mikrokontrolery
    KrukersRadek napisał:
    A czemu na początku?

    Po to żeby rdzeń zdążył "załapać", że przerwanie jest obsłużone zanim z niego wyjdziesz.

    KrukersRadek napisał:
    Kto tak Cie uczył?

    A kto Ciebie uczył, że koniecznie musi być na końcu? Dlaczego niby tak? Może tylko dlatego, że "tak się zawsze robiło"?

    4\/3!!
  • Specjalista - Mikrokontrolery
    Napisałem "przynajmniej z dwóch powodów". Jeden już wyjaśnił Freddie - wypróbowane w praktyce na różnych uC z Cortexami. Drugi powód - to zmniejszenie prawdopodobieństwa zgubienia zdarzenia przy dużym obciążeniu procesora. Jeśli obsługa przerwania opóźni się, to kasując znacznik na początku zwiększasz szanse na to, że przy wystąpieniu kolejnego zdarzenia jeszcze w czasie obsługi przerwania spowoduje ono powtórne zgłoszenie tego samego przerwania. To akurat nie ma związku z zależnościami czasowymi wynikającymi z budowy rdzenia - ma to zastosowanie do każdego mikrokontrolera z kasowanymi programowo znacznikami przerwań.

    Kto mnie tak uczył? Sam się tak nauczyłem na podstawie lektury dokumentacji kilkudziesięciu rdzeni procesorowych i odpowiednio większej liczby mikrokontrolerów oraz własnych z nimi doświadczeń.
  • Poziom 10  
    No ok, ale ja sie z tym nie zgadzam, bo uwazam ze jest to po to zeby zgłosic ze przerwanie zostało obsluzone jezeli faktycznie tak jest a nie jezeli jestemy w połowei listy rokazow procedury przerwania;/. Pozatym robiąc tak jak ty mówisz zwiększam prawdopodobieństwo zapchania stosu i wysypania sie pamieci. Dajac to na koncu, z dwiema instrukacjami nop zeby rdzen "załapał" to przerwanie nie zostanie ponownie wygenerowane dopoki bierzace nie zakonczy rozkazow, co z koleji pozwoli na bezpieczna prace na stosie. Fakt ze procek tez calkowicie odejdzie od wykonywania programu, bo bedzie siedzial tylko w tym przerwaniu, ale dzieki takiemu roziwzaniu nie bedzie tak olbrzymiego skolejkowania tych przerwan i oddalimy sie od bledu stosu. Takie jest moje zdanie, jezeli sie myle to poprawiajcie mnie, żeby inni mogli kiedys skorzystac z tej dysusji :)
  • Specjalista - Mikrokontrolery
    KrukersRadek napisał:
    Takie jest moje zdanie, jezeli sie myle to poprawiajcie mnie, żeby inni mogli kiedys skorzystac z tej dysusji

    Przecież jeśli na początku skasujesz flagi, to układ wcale nie wejdzie automatycznie do innego przerwania lub ponownie do tego samego (nawet jeśli w trakcie jego działania pojawi się nowe zdarzenie, zmieniające flagę). No chyba ze tak masz ustawione priorytety, ale wtedy miejsce kasowania flag również nie ma żadnego znaczenia.

    Innymi słowy - miejsce przestawienia flagi nie ma żadnego znaczenia, byle dać układowi czas na "załapanie" zmiany przed wyjściem z przerwania. Z tego powodu lepiej skasować je na samym początku. Nie sądzę aby istniał układ w którym takie działanie powodowałoby jakikolwiek problem.

    Przy okazji pomyśl o flagach kasowanych automatycznie, przez sprzęt - np. kasujących się po odczytaniu rejestru z odebranymi danymi. Przecież to robisz zwykle na samym początku (a na pewno nie na końcu) - powoduje to jakiś problem?

    4\/3!!
  • Poziom 10  
    Pisałem programy w asmblerze na rozne procki i rozne architektury, i rowniez programy pod DOSa itd. Wszedzie gdzie używałem przerwan to zawsze instrukcje taką stawiałem na koncu bo ma to wtedy logiczny sens. Np. pod 8086 i jego dalsze wersje istnial rokaz ktory pozwlal na genrowanie przerwan, tam ten rozkaz musial byc na koncu bo inaczej wygenerowalo by sie przerwanie w przerwaniu doslownie rekurencyjnie.

    Wiem ze tu w ARM obsluga jest znacznie bogatsza i lepsza, bo kazde przerwanie ma swoje flagi.

    To co mówisz Freddie ma sens, ale z punktu widzenia prostego człowieka kazdy uznał by ze przerwnaie jest obslozone kiedy wszystkie jego instrukcje zostana wykonane.

    Pozatym pomyslalem teraz o tym stosie to rzeczywiscie, nic sie nie stanie bo stos bedzie zmieniony dopiero jak NVIC pozwoli na wejscie do procedury bo to wtedy adres najprawdopodobniej rzucany jest na stos. Choć patrzac dalej te rdzenie mają rejstr który utrzymuje adres powrotu z procedury, wiec moze i nawet w to wszystko caly stos nie jest zamieszany :) Oby tak było. Ja jednak piszac mam pewne zasady i trzymam zawsze sie logicznego punktu widzenia, wteyd uwazam ze jest to dobre.
  • Specjalista - Mikrokontrolery
    Weź pod uwagę to, że kasując flagi na końcu przerwania, czas od wejścia do obsługi przerwania do jej skasowania może być bardzo zróżnicowany w zależności od tego czy nałożą się przerwania z wyższym priorytetem. Może to skutkować tym, że w trakcie obsługi przerwania np. od UART nadejdą nowe dane a Ty je zamaskujesz bo wykasujesz flagę należącą tak naprawdę do innego znaku. Owszem, zależy to też od samej obsługi przerwania, bo można siedzieć w przerwaniu do momentu wyczyszczenia bufora odbiorczego i nie wnikać w stan flag od przerwań, wtedy flagi chce się czy nie kasuje się na końcu.
  • Poziom 10  
    To prawda, wszystko zalezy tutaj od sytuacji :) Zgadzam sie z przedmówcą.

    Jeszcze pytanko do was.

    Zalozmy ze na jednym timerze generuje przerwania dla wszystkich MR0..3 i te 4 rejestry ustawiam na wartosc powiedzmy 100. W procedurze przerwania umieszczam instrukcję

    zmienna++;

    Pytanie brzmi. Ile razy zostanie obslozone przerwanie. Czy 4 razy osobno od kazdego z MRx. Czy bedzie to sygnał w tym samym czasie i zostanie wygenrowane raz?

    Uznajemy ze wszystkie MRx maja mozlowosc wygnerowania tego przerwania.
  • Specjalista - Mikrokontrolery
    Jedno przerwanie. W procedurze przerwania kopiujesz rejestr flag do tymczasowej zmiennej, skopiowane flagi sprzętowe kasujesz i obsługę przerwania robisz na podstawie kopi. Taka jest praktyka obsługi przerwań wieloźródłowych.
  • Poziom 10  
    Masz tu pewność? Wierze ze mówisz mi to z doświadczenia bo robiles juz takie rzeczy. Zeby mi sie potem nie okazało ze moja zmienna ma 4 a nie jeden :D
  • Specjalista - Mikrokontrolery
    Jeśli będziesz kasował po jednej fladze to będziesz miał 4 przerwania, ale to jest działanie bez sensu.
  • Poziom 10  
    No ok, mi akurat jest potrzebne jak najszybsza obsluga. Nie zaryzykuje na 4krotnym niepotrzebnym wchodzeniu do perwania tylko dlatego zeby wyglądało to na obslugę kazdego MRx z osobna.

    Dzięki za pomoc.
  • Specjalista - Mikrokontrolery
    O stosie piszesz herezje, o nakładaniu przerwań też. Czym innym jest odblokowanie zgłaszania przerwań przez 8259A w starym PC (to się robiło na końcu procedury obsługi przerwania), a czym innym wyzerowanie znacznika zdarzenia powodującego przerwanie - to ZAWSZE powinno się robić jak najwcześniej w procedurze obsługi przerwania, żeby nie zgubić kolejnego zdarzenia. Jak już pisałem wyżej - to czysta logika i nie zależy to od procesora. Przy Cortexach dochodzi jeszcze problem opóźnienia zerowania znacznika, o czym już było.

    W żadnym normalnym systemie przerwań to samo przerwani się nie zagnieździ. Taka groźba istniała w starych PC 20..30 lat temu, bo x86 miały jednopoziomowy system przerwań, a oprogramowanie udawało wielopoziomowy poprzez obniżanie priorytetu procesora w obsłudze przerwania, co ZAWSZE jest groźne. Cortexy mają system wielopoziomowy, więc udawać nic nie trzeba i priorytetu się nie obniża.

    Pisałem kiedyś o tym, o tu:
    www.elektroda.pl/rtvforum/viewtopic.php?t=2479135
  • Specjalista - Mikrokontrolery
    BlueDraco napisał:
    W żadnym normalnym systemie przerwań to samo przerwani się nie zagnieździ


    W AVR jest to możliwe, a dla większości jest to normalny system przerwań.
  • Specjalista - Mikrokontrolery
    KrukersRadek:

    Typowo obsługa przerwania timera wygląda tak:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    A teraz sam pomyśl, gdzie umieścisz instrukcję inkrementacji licznika i jaką informację miałby ten licznik zawierać.
  • Specjalista - Mikrokontrolery
    BlueDraco napisał:
    KrukersRadek:

    Typowo obsługa przerwania timera wygląda tak:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    A teraz sam pomyśl, gdzie umieścisz instrukcję inkrementacji licznika i jaką informację miałby ten licznik zawierać.


    Sam sobie zaprzeczyleś odnośnie idei kasowania flag na początku obsługi przerwania. Ostatnia flaga może być kasowana przed samym wyjściem.

    Chyba taka opcja jest dużo lepsza?
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Druga wersja:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Dodatkowa zaleta jest taka że kasujesz flagi które nie są potrzebne, a zostały ustawione.
  • Specjalista - Mikrokontrolery
    W różnych programach używałem wszystkich trzech wariantów (czyli również tych, które pokazałeś), wrzuciłem tutaj jako przykład pierwszy i najprostszy. :)
  • Poziom 11  
    BlueDraco napisał:
    O stosie piszesz herezje, o nakładaniu przerwań też.


    Ja tylko zauważyłem, że Cortex M0 (LPC1114) ma ghosty na irq (PIO) i jest możliwe krótko po sobie manie przerwania po mimo że układ zewnętrzny sprokurował jedno.
    Dodałbym że CM0 jest przetaktowany na 60Mhz i po zmniejszeniu częstotliwości do 20Mhz jest dokładnie to samo :P
    Moim zdaniem jest jakiś błąd w tym układzie albo ja coś skopałem.