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.

STM32 F0 discovery - Timer 1

lycon5 09 Gru 2013 14:06 2652 8
  • #1 09 Gru 2013 14:06
    lycon5
    Poziom 11  

    Witam,
    już dobrych kilka godzin zastanawiam się, dlaczego timer 1 nie działa tak jak chcę. A chcę, poprzez poniżej przedstawione funkcje włączać żarówkę po odliczeniu przez timer na czas też odliczany przez timer.
    Konfiguracja timera 1:

    Code:

    void Tim1_config(void)
    {
    RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
    TIM1->CR1 |= TIM_CR1_ARPE;
    TIM1->CR1 &= ~TIM_CR1_DIR;
    TIM1->ARR = 65535;
    TIM1->PSC = 48; // taktowanie mam 48MHz
    TIM1->CR1 |= TIM_CR1_CEN;
    NVIC_EnableIRQ(TIM1_CC_IRQn);
    }

    Funkcja włączająca dany kanał timera, i odliczająca daną wartość czasu
    Code:

    void Tim1_canal_start(int us_1, int kanal)
    {
       long int tmp=0;

       if(us_1>10000)
       {
          us_1=10000; //ograniczenie do 10ms
       }

       if (kanal==1)
       {
          TIM1->DIER |= TIM_DIER_CC1IE; // włączenie przerwania od 1 kanału trybu compare
          tmp= TIM1->CNT;

          if ((tmp+us_1)>65535)
          {
          TIM1->CCR1 = (tmp + us_1)%65535;
          }
          else
          {
          TIM1->CCR1 = tmp + us_1;
          }
       }
       if (kanal==2)
       {
          TIM1->DIER |= TIM_DIER_CC2IE;
          tmp= TIM1->CNT;

          if ((tmp+us_1)>65535)
          {
          TIM1->CCR2 = (tmp + us_1)%65535;
          }
          else
          {
          TIM1->CCR2 = tmp + us_1;
          }
       }
       if (kanal==3)
       {
          TIM1->DIER |= TIM_DIER_CC3IE;
          tmp= TIM1->CNT;

          if ((tmp+us_1)>65535)
          {
          TIM1->CCR3 = (tmp + us_1)%65535;
          }
          else
          {
          TIM1->CCR3 = tmp + us_1;
          }
       }
       if (kanal==4)
       {
          TIM1->DIER |= TIM_DIER_CC4IE;
          tmp= TIM1->CNT;

          if ((tmp+us_1)>65535)
          {
          TIM1->CCR4 = (tmp + us_1)%65535;
          }
          TIM1->CCR4 = tmp + us_1;
       }
    }

    i funkcja wyłączająca dany kanał:
    Code:

    void Tim1_canal_stop(int kanal)
    {
       if (kanal==1)
       {
          TIM1->DIER &= ~TIM_DIER_CC1IE;
       }
       if (kanal==2)
       {
          TIM1->DIER &= ~TIM_DIER_CC2IE;
       }
       if (kanal==3)
       {
          TIM1->DIER &= ~TIM_DIER_CC3IE;
       }
       if (kanal==4)
       {
          TIM1->DIER &= ~TIM_DIER_CC4IE;
       }
    }

    Obsługa przerwania
    Code:

    void TIM1_CC_IRQHandler(void)
    {
       //Sprawdzamy który kanał
       if(TIM1->SR&(TIM_SR_CC1IF))
       {
       TIM1->SR &= ~(TIM_SR_CC1IF);
       Zarowka_on();
       Tim1_canal_stop(1);
       Tim1_canal_start(1000,2);
       }

       if(TIM1->SR&(TIM_SR_CC2IF))
       {
       Zarowka_off();
       TIM1->SR &= ~(TIM_SR_CC2IF);
       Tim1_canal_stop(2);
       }
       NVIC_ClearPendingIRQ(TIM1_CC_IRQn);
    }

    Pętla główna
    Code:

     while(1)
        {
           Tim1_canal_start(1000,1);
            czekaj(1000000); // zwykły for tyle razy ile wprowadzimy do funkcji
        }


    Widzi ktoś jakiegoś buga? bo mi już mózg wybucha od RM0091.
    Pozdrawiam

    Dodano po 1 [godziny] 8 [minuty]:

    Matko boska, już wiem. Wychodzi na to, że jak nie ustawiam wcześniej rejestrów CCRx, to on tam ma jakieś randomowe wartości i flaga przerwania tak czy siak jest ustawiana, tak więc od razu jak włączałem mu przerwania, to on od razu w nie wchodził. Dlatego miałem takie głupoty. Przepraszam za wstawienie śmieciowego tematu, ale może ktoś będzie miał kiedyś podobny problem i znajdzie tutaj odpowiedź. Funkcja włączająca kanał powinna wyglądać tak:
    Code:

       if (kanal==1)
       {
          TIM1->DIER |= TIM_DIER_CC1IE;
          TIM1->SR &= ~(TIM_SR_CC1IF);
          tmp= TIM1->CNT;

          if ((tmp+us_1)>65535)
          {
          TIM1->CCR1 = (tmp + us_1)%65535;
          }
          else
          {
          TIM1->CCR1 = tmp + us_1;
          }
       }

    0 8
  • Computer Controls
  • #2 09 Gru 2013 14:24
    BlueDraco
    Specjalista - Mikrokontrolery

    Ta funkcja nie powinna tak wyglądać.

    Jeśli już, to po prostu:
    TIM1->CCR1 = tmp + us_1;

    Bez żadnych rozejść warunkowych w celu wykonania błędnie "zawinięcia", które wykona się samo.

    Z kolei "canal_stop" jest świetną ilustracją jak nie należy dzielić programu na procedury. Wystarczyłoby każde wywołanie tej procedury zastąpić prostą instrukcją:
    TIM1->DIER &= ~TIM_DIER_CCxIE;

    Z tym, że modyfikacja rejestru DIER zarówno w programie głównym jak i w procedurze przerwania będzie powodowała błędy w działaniu programu.

    Tu też jest gruby błąd:
    TIM1->SR &= ~(TIM_SR_CC1IF);

    Powinno być:
    TIM1->SR = ~TIM_SR_CC1IF;

    0
  • Computer Controls
  • #3 09 Gru 2013 14:41
    lycon5
    Poziom 11  

    Co do automatycznego "zwinięcia" to po prostu nie byłem pewien, czy tak się wykona automatycznie : ) ale jeśli kolega tak mówi, to pewnie tak jest.
    Co do funkcji sprawdzającej kanał, fakt, jest to strasznie nieoszczędne, ale pisałem to na szybko i chciałem żeby było w miarę widoczne co się dzieje, dlatego wrzuciłem to w funkcję. Optymalizacje planowałem w późniejszym czasie

    Cytat:

    Tu też jest gruby błąd:
    TIM1->SR &= ~(TIM_SR_CC1IF);

    Powinno być:
    TIM1->SR = ~TIM_SR_CC1IF;

    Tutaj za bardzo nie rozumiem, jak zmieniam na Twoją wersję, to nie działa tak jak chcę ; < Jak na mój gust moja wersja jest logiczniejsza, ale czekam na wyjaśnienie : )

    0
  • #4 09 Gru 2013 15:10
    BlueDraco
    Specjalista - Mikrokontrolery

    TIM1->SR = ~TIM_SR_CC1IF;
    - to "skasuj znacznik CC1IF"

    TIM1->SR &= ~(TIM_SR_CC1IF);
    - to "skasuj znacznik CC1IF oraz znaczniki innych przerwań timera, które zostały zgłoszone po odczytaniu rejestru SR, a przed jego zapisem".

    0
  • #5 09 Gru 2013 15:24
    lycon5
    Poziom 11  

    Nie przemawia to do mnie, w stm32f0xx.h mamy coś takiego:

    Code:

    #define  TIM_SR_CC1IF                        ((uint16_t)0x0002)            /*!<Capture/Compare 1 interrupt Flag */

    jak na mój gust zapis:

    TIM1->SR = ~TIM_SR_CC1IF;

    spowoduje najpierw zanegowanie TIM_SR_CC1IF a potem przypisanie do rejestru SR, czyli de facto wrzucimy tam wartość 0xFFFD.

    Zapis:

    TIM1->SR &= ~(TIM_SR_CC1IF);

    Wyzeruje tylko ten jeden bit w rejestrze.

    Czy ja coś przegapiłem ? : < Proszę o wyjaśnienie.

    0
  • #6 09 Gru 2013 20:19
    BlueDraco
    Specjalista - Mikrokontrolery

    Przegapiłeś dwie rzeczy:
    - reakcję rejestru na zapis opisaną w manualu - "write 0 to clear" - zapis jedynek jest ignorowany
    - fakt, że operacja logiczna &= wymaga najpierw odczytania, a potem zapisania rejestru,a przez czas pomiędzy odczytem i zapisem może nastąpić sprzętowe ustawienie znacznika przerwania, który ten zapis natychmiast skasuje.

    Czyli: jest dokładnie odwrotnie, niż to sobie wyobrażasz:

    TIM1->SR = ~TIM_SR_CC1IF;
    wyzeruje tylko tej jeden bit w rejestrze, a

    TIM1->SR &= ~(TIM_SR_CC1IF);
    Wyzeruje ten bit oraz wszystkie, które w chwili czytania rejestru miały wartość zero.

    0
  • #7 09 Gru 2013 21:42
    lycon5
    Poziom 11  

    A moim zdaniem obie instrukcje są sobie równoważne. Nieważne czy ustawię rejestr z zerem w miejscu, w którym znajduje się bit do wyczyszczenia, czy zamaskuję rejestr i wstawię zero. Obie instrukcje działają tak samo, obie dają ten sam rezultat w debuggerze jak i z prostej obserwacji. To, że zapis jedynki jest ignorowany, to faktycznie zapomniałem o tym :D Dziękuję za pomoc i dyskusję. Mam nadzieję na ewentualną pomoc w przyszłości.

    0
  • #8 09 Gru 2013 21:58
    BlueDraco
    Specjalista - Mikrokontrolery

    No to po kolei. Załóżmy, że masz ustawiony znacznik przerwania X. Próbujesz wykonać swoją błędną operację:

    SR &= ~X;

    którą procesor wykonuje tak:
    temp = SR;
    temp &= ~X;
    SR = temp;

    Po wykonaniu temp = SR;
    w SR jest ustawiany bit Y, bo timer właśnie wygenerował inne przerwanie. Twój błędny kod skasuje bit Y, bo zapisze 0 na pozycję bitu Y. Kod poprawny zawiera o 2..3 instrukcje mniej i nie skasuje błędnie znacznika przerwania. Proste?

    0
  • #9 23 Gru 2013 19:11
    lycon5
    Poziom 11  

    Przepraszam, że z takim zapłonem piszę, ale zapomniałem już o tym temacie. Faktycznie, jak to wytłumaczyłeś to ma to sens. Dziękuję za poświęcony czas. Pozdrawiam i życzę Wesołych Świąt.

    0