Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

[ARM][LPC-2138][CrossWorks] Pomiar przesunięcia fazy-problem

borsukh 11 Nov 2008 19:00 3197 5
Computer Controls
  • #1
    borsukh
    Level 10  
    Witam,
    Mam do mikrokontrolera podłączony układ, który w wielkim uproszczeniu dostaje sygnał PWM z uC i zwraca go z pewnym przesunięciem fazy z powrotem . Chciałbym mierzyć to przesunięcie fazowe. W tym celu podaje do tego układu sygnał o częstotliwości 40 kHz z PWM, a sygnał wyjściowy układu dołączam do wejścia CAPTURE 0.0. W trakcie działania programu TIMER0 jest cały czas aktywny i zlicza. Aby zmierzyć przesunięcie badam ilość zliczeń TIMERA 0 pomiędzy zboczem opadającym sygnału PWM (generowanie przerwania przy zrównaniu PWMMR2 z counterem - zapis aktualnego stanu TIMER0 w timerval1), a zboczem opadającym sygnału wchodzącego do CAPTURE 0.0 (generowanie przerwania przy zboczu opadającym i zapis aktualnego stanu TIMER0 w timerval2). Następnie obliczam wartość ticksnumber = timerval2 - timerval1 , która to wartość powinna dać mi ilość zliczeń timera między zboczami obu sygnałów. I chce sobie ją wypisać printf (przez UART). Oczywiście tak się nie dzieje.

    Szanowni koledzy już drugi dzień z tym walczę i nie wiem w czym problem. Próbowałem już masy rozwiązań, które utwierdziły mnie w przekonaniu, że nie rozumiem jakiejś podstawowej rzeczy. Poniżej podaje najważniejszy kod (komentarze po angielsku):

    Code:

    void main(void)
    {
      init_PLL();
      GPIO_Initialize();
      ctl_global_interrupts_enable();
      UARTInitialize(115200);
      TIMER0_InitTimer();
      init_PWM(40000); 
      while (1)
        {
        }
    }


    void TIMER0_InitTimer()
    {
      T0TCR = 0x02;   //disable and reset timer0 counter
      T0TCR = 0x00;  //reset to low
      T0IR = 0xff;           //clear interrupts
      T0TC = 0;
      PINSEL0 |= 0x20; //set pin 0.2 as CAP0.0
      T0CCR = 0x06; // capture on CAP 0.0 falling edge and generate interrupt
      ctl_set_isr(4, 0, CTL_ISR_TRIGGER_NEGATIVE_EDGE,Anemometer_ISR2, 0); // interrupt enable for TIMER0
      ctl_unmask_isr(4);
      T0TCR = 0x01; //enable timer0 counter
    }


    void init_PWM (unsigned long frequency)
    {

      PINSEL0 |= 0x00008000;          /* Enable P0.7 as PWM2 output */
      PWMPR    = 0x00000000;           /* Load prescaler  */
      PWMPCR = 0x00000400;       /* PWM channel 2 single edge control, output enabled */
      PWMMCR = (1 << 6);          /* On MR2 match with timer reset the counter */
      PWMMR0 = (liblpc2000_get_pclk(liblpc2000_get_cclk(OSCILLATOR_CLOCK_FREQUENCY))/ frequency);                         
      PWMMR2 = (liblpc2000_get_pclk(liblpc2000_get_cclk(OSCILLATOR_CLOCK_FREQUENCY))/ (2*frequency));                         
      PWMLER = 0xF;                    /* enable shadow latch for match 1 - 3   */
      PWMTCR = 0x00000002;         /* Reset counter and prescaler           */
      PWMTCR = 0x00000009;        /* enable counter and PWM, release counter from reset */
     
      ctl_set_isr(8, 0, CTL_ISR_TRIGGER_NEGATIVE_EDGE, Anemometer_ISR1, 0); // interrupt enable for PWM
      ctl_unmask_isr(8);
    }


    void Anemometer_ISR1()    //falling edge of PWM signal
    {
      timerval1 = T0TC;     //save current TIMER0 value
      PWMIR = 0xff;       //clear PWM interrupts
    }


    void Anemometer_ISR2()  //falling edge on Capture 0.0
    {
      timerval2 = T0CR0;   //save current TIMER0 value
      ctl_global_interrupts_disable(); //disable interrupts to compute ticksnumber and invoke printf function
      ticksnumber = timerval2 - timerval1;
      printf("%ld\n", ticksnumber);
      T0IR = 0xff;    //clear timer interrupts
      ctl_global_interrupts_enable(); //enable interrupts
    }


    przykładowe wyjście UART
    Quote:

    280022120
    280078134
    280134145
    280191020
    280247034
    280303654
    280359667
    280416458


    Zdaję sobie sprawę że void Anemometer_ISR1() i void Anemometer_ISR2() mogą być zupełnie źle, ale nie wypracowałem nic bliskiego rozwiązaniu problemu więc podaje najlogiczniejsze dla mnie rozwiązanie.
  • Computer Controls
  • #2
    slomo
    Level 17  
    Pierwsza rzecz,która rzuca sie w oczy to nie uzywa sie funkcji printf w procedurze obsługi przerwania.

    pz
  • Computer Controls
  • #3
    borsukh
    Level 10  
    Zdaję sobie sprawę z tego że printf zabiera mase cykli zegara, ale w zasadzie jak go przerzucam do maina to i tak nic nie pomaga więc nie w tym rzecz...
  • #4
    Freddie Chopin
    MCUs specialist
    czy Crossworks jest taki madry, aby sam potwierdzac przerwania w ich ISRach? w kazdym z nich brakuje przypisania dowolnej wartosci do rejestru VIC odpowiedzialnego za adres... osobiscie nie sadze, aby Crossworks zalatwial to sam, bo w koncu oparty jest na gcc, ktory tego nie zalatwia. bez tego kolejne wywolanie przerwan nie bedzie mozliwe raczej...

    VICVectAddr=0;

    do tego funkcje obslugi przerwania nie sa deklarowane jako takowe

    void __attribute__ ((interrupt("IRQ"))) costam(void)

    wylaczanie oblusi przerwan w ISRze jest bezsensu, bo po wejsciu do trybu IRQ sa one domyslnie wylaczone. za to aktywowanie ich w tym przerwaniu jest najkrotsza droga do problemu, bo moze nastapic lawinowe wchodzenie do tej samej funkcji, co skonczy sie szybka smiercia programu poprzez przepelnienie stosu.

    sprawdz czy ten PWM dziala, bo datasheet zaklada, ze reset timera nastepuje dla MR0 (dla ktorego zreszta prawidlowo ustawiles dluzszy okres), za to wlaczasz w rejestrze konfiguracyjnym reset od MR2 (a przynajmniej tak pisze, nie znam na pamiec numerow bitow - to tyczy sie calosci kodu).

    podales przykladowy wynik dzialania programu, to wypadaloby tez napisac co w nim jest zle, bo jedyne co z niego widze to to, ze ladne cyferki i ladnie rosna [;

    kod powinien przewidywac opcje overflow timera ktorym mierzysz przesuniecie fazowe - kolejna funkcja obslugi przerwania (jesli tych przewiniec moze byc wiecej) lub warunek sprawdzajacy czy wartosc 2 nie jest przypadkiem mniejsza niz wartosc 1 (co oznacza ze miedzy ich zapisaniem nastapil overflow. mozna by tez resetowac timer w obsludze tego przerwania ktore ma sie wykonac wczesniej, wtedy w tym drugim ISRze nie byloby potrzebne zadne odejmowanie.

    wyniki jakie daje twoj kod swiadcza o tym, ze albo to co mierzysz ma opoznienie zalezne od pory dnia, albo jedno z przerwan nigdy sie nie wykonuje (ciagle powiekszenie wyniku).

    JTAG w dlon i po prostu sprawdz, czy twoje funkcje aby na pewno sie wykonuja...

    swoja droga mam pare drobnych rad:
    1. wszystki te kosmosy typu rejestr = 0x34232 sa ... kosmiczne - nikt nie powie ci czy masz tam blad, bo nikt nie zna na pamiec numerow bitow... zrob sobie dobry naglowek i uzywaj go. przyklad:

    Code:

    #define PWMTCR_CounterEnable_BIT   0
    #define PWMTCR_CounterReset_BIT      1
    #define PWMTCR_PWMEnable_BIT      3

    #define PWMTCR_CounterEnable      (1<<PWMTCR_CounterEnable_BIT)
    #define PWMTCR_CounterReset         (1<<PWMTCR_CounterReset_BIT)
    #define PWMTCR_PWMEnable         (1<<PWMTCR_PWMEnable_BIT)


    a potem tylko:

    Code:

    PWMTCR=PWMTCR_CounterEnable|PWMTCR_PWMEnable;   // start the counter and PWM


    od razu wiadomo czy robisz co trzeba...

    2. jak juz bylo pisane - printf to porazka

    3. zanim zaczniesz sie zastanawiac to uzyj JTAGa i sprawdz, czy przerwania aby na pewno sie wykonuja. jesli go nie masz, to podlacz gdzies dwie diodki i kaz im mrugac jak wystapi przerwanie.

    4. fajne funkcje do wlaczania przerwan, ale co one robia? bo jesli tylko ustawiaja VICa (a pewnie wlasnie to robia), to przydaloby sie jeszcze wlaczenie przerwan od danych peryferiow.

    5. jesli rejestry Match dla PWMa ustawiasz przed jego odpaleniem nie ma potrzeby uzywania LATCHa i tych shadow registers.

    ogolnie nie wiele idzie wywnioskowac z tego kodu na szybko.

    4\/3!!
  • #5
    borsukh
    Level 10  
    Dzięki Freddie za zajęcie się tematem i uwagi. Postaram się uzupełnić informacje odpowiadając na twoje pytania:

    1. Funkcja ctl_set_isr() załatwia za użytkownika poprawne uruchomienie nowego przerwania, prototyp:

    void ctl_set_isr(unsigned int vector,unsigned int priority, CTL_ISR_TRIGGER_t trigger, CTL_ISR_FN_t isr, CTL_ISR_FN_t *oldisr);

    Gdzie

    vector - wektor przerwania
    priority - priorytet przerwania od 0 do 255
    trigger - typ zdarzenia jaki wywoła przerwanie
    isr - nazwa funkcji obsługi przerwania
    oldisr - nieważne :)

    Innymi słowy CrossWorks załatwia to sam, sprawdziłem działa, program wchodzi do obsługi przerwań. Uarta mam zrobionego na przerwaniach za pomocą ww funkcji i działa jak złoto. Faktycznie aktywowanie przerwań w przerwaniu może nie być dobrym pomysłem, zmienię. Natomiast czy jak podczas obsługi przerwania wystąpi inne przerwanie to nie zostanie ono zakolejkowane? do wykonania jako następne? Wtedy byłby problem bo zapisywałby wartość timera w chwili obsługi zakolejkowanego przerwania a nie w chwili zbocza...

    2. PWM działa, przy MR2 nie ma resetu, mój błąd - pozostałości komentarza po innej konfiguracji.

    3. W przykładowym wyniku zrobiłem faktycznie wieeelki skrót myślowy, jest to wynik dla sytuacji, w której przesunięcie fazowe między sygnałami jest stałe, więc mogę się spodziewać rezultatów zbliżonych, a tu jak sam zauważyłeś cyferki ładnie rosną. Ponadto dla częstotliwości 40kHz którą używam, przy częstotliwości zegara proca = 60mHz na jeden okres przypada 60mHz/40 kHz = 1500 zliczeń timera (preskaler = 0) i jest to maksymalny wynik jakiego mogę się spodziewać (w sytuacji przesunięcia fazowego 360° ).

    Pominąłem kwestie overflowu, ale wiem o nim. Chciałem na razie uruchomić coś prostszego. Resetowanie timera zabiera kilka(naście) cykli zegara i wprowadza błąd pomiaru.

    4. Kosmiczne rejestry mi nie przeszkadzają, aczkolwiek masz rację że byłoby lepiej zrobić jak mówisz.

    Quote:
    zanim zaczniesz sie zastanawiac to uzyj JTAGa i sprawdz, czy przerwania aby na pewno sie wykonuja. jesli go nie masz, to podlacz gdzies dwie diodki i kaz im mrugac jak wystapi przerwanie


    Próbowałem używać debuggera ale nie wchodzi mi tam w ogóle do przerwań nie wiem czemu, mimo że mam ustawione breakpointy. Wiem jednak że działają bo wstawiałem sobie printf (wiem że porażka :D) do przerwań i wyrzucało na ekran wartości timerval1 i 2 zatem do obsługi przerwań wchodzi.

    Jak się tak teraz zastanawiam, to trudno żeby wchodziło mi do obsługi przerwań w trybie debuggera skoro są one wywoływane przez rzeczywiste sygnały. Jeśli istnieje możliwość symulacji wartości portów w CrossWorks to niech ktoś światły wytłumaczy mi jak to zrobić.
  • #6
    Freddie Chopin
    MCUs specialist
    borsukh wrote:
    Natomiast czy jak podczas obsługi przerwania wystąpi inne przerwanie to nie zostanie ono zakolejkowane? do wykonania jako następne? Wtedy byłby problem bo zapisywałby wartość timera w chwili obsługi zakolejkowanego przerwania a nie w chwili zbocza...

    tego i tak nie zmiesz wylaczajac przerwania, bo po ich ponownej aktywacji to co mialo sie wywolac i tak sie wywola. no chyba ze wylaczanie przerwan to przy okazji zastopowanie timera, ale nie sadze. jesli istnieje takie ryzyko, ze przesuniecie fazy moze byc wieksze niz jeden okres twojego timera, to algorytm musi zostac zmieniony...

    Quote:
    3. W przykładowym wyniku zrobiłem faktycznie wieeelki skrót myślowy, jest to wynik dla sytuacji, w której przesunięcie fazowe między sygnałami jest stałe, więc mogę się spodziewać rezultatów zbliżonych, a tu jak sam zauważyłeś cyferki ładnie rosną. Ponadto dla częstotliwości 40kHz którą używam, przy częstotliwości zegara proca = 60mHz na jeden okres przypada 60mHz/40 kHz = 1500 zliczeń timera (preskaler = 0) i jest to maksymalny wynik jakiego mogę się spodziewać (w sytuacji przesunięcia fazowego 360° ).

    dodaj w kazdym z przerwan wyrzucanie linijki typu:
    Przerwanie jeden/dwa: interval1/2 = xxx...

    wtedy bedziesz wiedzial, czy przerwania wywoluja sie w prawidlowej kolejnosci i w jaki sposob zmieniaja sie zmiennne... problem moze byc tego typu, ze owy printf moze trwac zbyt dlugo... /;

    problem jest tylko taki, ze 40kHz to troche zbyt szybko dla wolnego uart'a

    Quote:
    Próbowałem używać debuggera ale nie wchodzi mi tam w ogóle do przerwań nie wiem czemu, mimo że mam ustawione breakpointy. Wiem jednak że działają bo wstawiałem sobie printf (wiem że porażka :D) do przerwań i wyrzucało na ekran wartości timerval1 i 2 zatem do obsługi przerwań wchodzi.

    Jak się tak teraz zastanawiam, to trudno żeby wchodziło mi do obsługi przerwań w trybie debuggera skoro są one wywoływane przez rzeczywiste sygnały. Jeśli istnieje możliwość symulacji wartości portów w CrossWorks to niech ktoś światły wytłumaczy mi jak to zrobić.

    JTAG to debuggowanie rzeczywistego systemu - symulator sprawy nie zalatwi.

    4\/3!!