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

stm32 jednoczesny pomiar napiecia

27 Lut 2011 12:22 2712 14
  • Poziom 19  
    Witam,
    potrzebuje konwersji ADC trzech napięć w jednej chwili czasowej. Dokładnie chodzi o to by "złapać" trzy napięcia w jednej chwili, a konwersja do postaci cyfrowej, może zostać zrealizowana pojedynczo. Mikrokontroler dsPIC30f6010A posiada 4 układy sample and hlod. Czy jakiś stm32 posiada takie układy, jak to jest zrealizowane w nich? W reference manual napisano o możliwości pomiaru jednoczesnego dwóch napięć przy pomocy dwóch przetworników, wiem, że są też stm-y z trzema ADC i czy w tedy taki pomiar jest możliwy, ale z wykorzystaniem 3 przetworników czy np. każdy z przetworników ma jakieś układy sample and hold?
  • Poziom 35  
    Siłą rzeczy każdy tor ADC ma swój układ S-H. Jeśli weźmiesz stm32 z trzema ADC, to dostajesz trzy bloki S-H, kazdy przed odpowiednim SAR.

    Da się oczywiście wtedy jednym triggerem wymusić trzy równoległe konwersje.
    Istotne jednak jest to, żeby dopilnować aby pod jeden Analog Input był podłączony tylko jeden S-H. Czyli równoczesna konwersja napięcia na tym samym wejściu przez dwa ADC zwróci ci dwa pokrzaczone wyniki.
  • Poziom 19  
    Witam,
    przeglądając reference manual nie znalazłem informacji o możliwości pomiaru 3 przetwornikami jednocześnie. Jest napisane tylko o trybie dual (str.226) z ADC1 jako master i ADC2 jako slave, nie ma info o trzecim przetworniku. Czy mógłby ktoś to zweryfikować?
  • Pomocny post
    Poziom 35  
    ADC3 nie ma zintegrowanego triggera z ADC1/ADC2. Aby wyzwalać konwersję ADC3 wspólnie z ADC1/ADC2 musisz użyć zewn. triggera, i każdy z bloków ADC do niego podłączyć - patrz strona 200 manuala.

    Z tego co widzę nie potrzebujesz konwersji wstrzykiwanych - więc potrzebujesz ustawić każdy z ADC tak, aby były wyzwalane tym samym triggerem lub dwoma triggerami występującymi w tym samym momencie. Możesz np. użyć timera2 kanał 2 i 3, przy czym OC2==OC3. Wtedy mimo wykorzystania oddzielnego triggera dla ADC1/ADC2 (TIM2_CH2) i dla ADC3 (TIM2_CH3), triggery wystąpią w tym samym momencie i konwersja również odbędzie się w tym samym momencie na wszystkich trzech przetwornikach.
  • Poziom 19  
    ok sprawdzę jak to działa gdy będę już posiadał STMa z 3 ADC.
    Nie zakładając nowego tematu chciałbym jeszcze zapytać co robię źle z kalibracją ADC.

    Konfiguracja ADC:

    Code:
    void init_adc (void)
    
    {   
       // Niezależny tryb pracy
       ADC1->CR1 = 0 ;
       // Wybór czasu konwersji
       ADC1->SMPR2 = ADC_SMPR2_SMP1;
            // Kanał 1 (tylko 1 kanał)
       ADC1->SQR3 = ADC_SQR3_SQ1_0 ;
       ADC1->CR2 |= ADC_CR2_ADON;
            // Reset kalibracji
       ADC1->CR2 |= ADC_CR2_RSTCAL;
       while (ADC1->CR2 & ADC_CR2_RSTCAL) {}
            // Właściwe kalibrowanie
       ADC1->CR2 |= ADC_CR2_CAL;
       while ((ADC1->CR2 & ADC_CR2_CAL) == TRUE ) {}
              
    }


    Przetwornik zamiast 3,3V pokazuje mi 4,09V.
    Czy powinienem odczytać po kalibracji jej wartość z rejestru ADC1->DR i potem ją gdzieś uwzględniać?
  • Poziom 35  
    Jesteś pewny, że jest to błąd kalibracji?
    Skąd bierzesz wynik pomiaru 4,09V?
    Jaki masz procesor i ile wynosi Vref+?

    Kalibracja odbywa się automatycznie jak ją wymusisz i nie może się "źle" wykonać, tak samo jak nie musisz nic czytać i nigdzie zapisywać. Po kalibracji przetworniki są gotowe do pracy...
  • Poziom 19  
    Teraz mam problem z przerwaniami od końca konwersji ADC1. Działanie układu wygląda tak jakby mikrokontroler nie wchodził w obsługę uchwytu przerwania od przetwornika ADC 1,2.

    Konfiguracja przetwornika:

    Code:
    void init_adc (void)
    
    {   
       // Niezależny tryb pracy
       // Włączenie przerwań od końca przerwań - EOC
       ADC1->CR1 = ADC_CR1_EOCIE ;   
       //Wybór czasu konwersji
       ADC1->SMPR2 = ADC_SMPR2_SMP1;
       // 1 kanał (tylko pierwszy kanał)
       ADC1->SQR3 = ADC_SQR3_SQ1_0 ;
       // Włączenie zewnetrznego wyzwalania
       // Wybranie wyzwalania zewnętrznego od SWSTART
       // Włączenie przetwornika
       ADC1->CR2 |= ADC_CR2_EXTTRIG|ADC_CR2_EXTSEL|ADC_CR2_ADON;
       // Wyzerowanie kalibracji
       ADC1->CR2 |= ADC_CR2_RSTCAL;
       while (ADC1->CR2 & ADC_CR2_RSTCAL) {} //  ADC_CR2_RSTCAL = 0 - koniec zerowania
       // Kalibracja
       ADC1->CR2 |= ADC_CR2_CAL;
       while (ADC1->CR2 & ADC_CR2_CAL ) {}//  ADC_CR2_CAL = 0 - koniec kalibracji   
           
    }


    Konfiguracja przerwań:

    Code:
    void init_interrupt (void)
    
    {
       // Wybór grupy z 16 głównymi priorytetami   
       SCB->AIRCR = 0x05FA0000|SCB_AIRCR_PRIGROUP_0|SCB_AIRCR_PRIGROUP_1;
       
       //Ustawienie  priorytetu przerwania od przepełnienia Timer1
       NVIC->IP[ADC1_2_IRQn] = (4<<4); // 4 priorytet dla ADC
       NVIC->IP[TIM1_UP_IRQn] = (3<<4);//  3 priorytet dla Timer1
       NVIC->IP[EXTI15_10_IRQn] = (2<<4);
          NVIC->IP[EXTI0_IRQn] = (1<<4);
          NVIC->IP[TIM4_IRQn] = (0<<4);  // Ustawienie najwyższego priorytetu dla timera4 - 0
       // Włączenie przerwań Programming Manual, strona 121
       // Włączenie przerwań od Timer1 od przepełnienia
       // Włączenie przerwań od Timer4  - jednen uchwyt dla różnych flag
            // Włączenie przerwań zewnętrznych od lini 0
            // Włączenie przerwań od ADC 1,2 - jednen uchwyt dla różnych flag
       NVIC->ISER[0] = (1<<TIM1_UP_IRQn)|(1<<TIM4_IRQn ) |(1<<EXTI0_IRQn)|(1<<ADC1_2_IRQn); 
       NVIC->ISER[1] =   (1<<(EXTI15_10_IRQn-32));// Włączenie przerwań zewnętrznych od lini PC13 j jeden uchwyt przerwań dla linii 10_15
    }


    Obsługa przerwania:

    Code:
    void ADC1_2_IRQHandler(void)
    
    {
        if(ADC1->SR & ADC_SR_EOC)
       {   
          GPIOB ->ODR ^= GPIO_BSRR_BS12 ;
           ADC1->SR &= ~ADC_SR_EOC;
               }
    }


    Nawet jeśli w obsłudze przerwania nic nie sprawdzam tylko od razu steruje nóżką 12 nic nie działa. Tzn pętla główna programu nie jest obsługiwana tak jakby procesor reagując na przerwanie wskakiwał w inny uchwyt przerwania, w którym nie ma kasowania flagi żądania przerwania i dla tego te przerwania jest non stop obsługiwane. Gdy ustawie dla przerwania od ADC najwyższy priorytet - 0 , nie działa już żadne inne przerwanie, działa tylko sprzętowy pwm.
  • Poziom 35  
    Najpewniej Nie Zerujesz Flag Przerwań. To, że NVIC automatycznie zeruje flagę pending po wejsciu w przerwanie (pending->active) nie znaczy, że peryferia robią to samo - nie robią, więc musisz tego dopilnować sam.

    W DMA musisz w ISRrze ręcznie gasić flagę która wywołuje przerwanie, najpewniej w twoim przypadki TCIF.

    Możliwe oczywiście jest również, że twoje ISRy nazywają się inaczej niż w rozbiegówce. To już było parę razy męczone na forum.
    Sprawdź, czy nazwy funkcji w rozbiegówce nazywają się znak w znak tak samo jak nazwy funkcji wklepane w twój kod C.

    Skoro twierdzisz, że lock-in występuje po skoku pod wektor przerwania:

    a) dowiedz się co to za wektor - jeśli dysponujesz debuggerem, łatwo sprawdzisz adres "skoku pod skok" (asmowa wersja while(1), przykład hardfault handlera )
    Code:

    HardFault_Handler PROC
                    EXPORT  HardFault_Handler         [WEAK]
                    B       .  ;o, tu...
                    ENDP

    i po tym adresie dojdziesz gdzie procek kiśnie...

    b) Jesli faktycznie występuje lock-in w przerwaniu, to nie ma prawa wykonywać się kod w pętli głównej od momentu wystąpienia lock-ina - rdzeń w momencie returrnięcia z przerwania ma od razu ustawiony bit pending tego przerwania, więc NVIC nawet nie przywraca kontekstu z pętli głównej i wlazi natychmiast do tego przerwania spowrotem.
  • Poziom 19  
    Zeruje falgi żądań przerwań w rejestrach peryferiów:

    Code:
    void DMA1_Channel1_IRQHandler(void)
    
    {
       GPIOB ->ODR ^= GPIO_BSRR_BS13 ;
       // Zerwoanie flagi żądania przerwania wpisaniem 1
       DMA1->ISR |= DMA_ISR_HTIF1;       

    }

    void ADC1_2_IRQHandler(void)
    {
        if(ADC1->SR & ADC_SR_EOC)
       {   
          GPIOB ->ODR ^= GPIO_BSRR_BS12 ;
           ADC1->SR &= ~ADC_SR_EOC;
             }
    }


    W DMA już specjalnie nie sprawdzam czy flaga DMA_ISR_HTIF1 jest ustawiona.

    To wygląda właśnie jakby mikrokontroler obsługiwał inne adresy jako wektory przerwań.
    Obsługa kodu w pętli głównej po wywołaniu przerwania zostaje przerwana.
    Nie rozumiem co to jest lock-in (to taki "zacięcie się" uc w jakieś pętli?), i jeszcze nie bardzo zrozumiałem jak mam to sprawdzić.
  • Poziom 35  
    Tak, dokładnie, chodzi o zacięcie się/martwą pętlę/jak_zwał_tak_zwał :]

    Czy dysponujesz debuggerem?
  • Poziom 35  
    Skoro masz debugger to nie powinieneś mieć problemu ze znalezniem błędu. Co dzieje się jak zatrzymasz w losowym momencie rdzeń gdy masz pewność, że przerwanie wystąpiło? Jeśli nie zgadzają się nazwy funkcji ISR, to najpewniej rdzeń będzie bujał się w nieskonczonej pętli pod jakimś wektorem, i adres jak i prawidłową nazwę ISRa zauważysz...

    Korzystasz z Keila?
  • Poziom 19  
    Tak korzystam z Keila

    Dodano po 4 [godziny] 55 [minuty]:

    Problem nie jako rozwiązany.
    Przyczynną było inaczej nazwane wektory przerwań, które skopiowałem z pliku stm32f10x_it.h (te z stm32f10x.s okazały się poprawne)
    np. dla DMA kanał 1 w stm32f10x_it.h

    Code:
    void DMA1_Channel1_IRQHandler(void);


    a w startupie stm32f10x.s

    Code:
    void DMAChannel1_IRQHandler(void)



    dla ADC1 w stm32f10x_it.h

    Code:
    void ADC1_2_IRQHandler(void);


    w w startupie stm32f10x.s

    Code:
    void ADC_IRQHandler (void)



    Nazwy wektorów kopiowałem z przykładów od STMa gdzie wykorzystywane były ich biblioteki, dlaczego nazwy się różnią i czy te z startup to są jedyne które można używać (czyli czy przypisane są do nich właściwe adresy) ??
    W pliku stm32f10x.h są np dwa wektory dla ADC jeden wspólny dla ADC 1,2 oraz oddzielny dla ADC3. Nazwy wektorów z tego pliku są identyczne do tego w stm32f10x_it.h ale inne niż w stm32f10x.s.