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

[STM32] SPI1, kanały DMA: odbiera mniej niż wysyła

BoskiDialer 30 Paź 2011 00:17 3182 9
  • #1 30 Paź 2011 00:17
    BoskiDialer
    Poziom 34  

    W normalnym świecie SPI działa w ten sposób, że wysyłając jeden bajt, odbiera się jeden.

    Pisząc pewien ogromny program, używałem powyższego założenia implementując obsługę SPI1 z pomocą kanałów DMA (DMA1_Ch2 i DMA1_Ch3). Jeden kanał używany do transferu bloku z pamięci RAM do SPI1->DR, drugi kanał do transferu w drugą stronę. Wszystko działało bez najmniejszego problemu, cały kod praktycznie chodził na przerwaniach. Od pewnego punktu rozwoju procesor zaczął się zawieszać. Okazało się, że w momencie zawieszenia się, kanał DMA wysyłający dane do SPI wysłał wszystkie n bajtów, natomiast kanał DMA odbierający sygnalizował oczekiwanie na jeszcze jeden bajt danych. Sytuacja dość dziwna, procesor nigdzie nie podkradał bajtu z SPI1->DR, brak przepełnień stosu, brak innych błędów.

    Ze względu na niemożliwość znalezienia błędu przez 10 godzin zacząłem okrajać projekt do postaci minimalnej, w której występuje problem. Kod okrojony do prawie minimum znajduje się w załączniku stm32_spidma.zip.

    W okrojonym kodzie peryferia są używane w następujący sposób:
    - kanał DMA1_Ch2 jest od odbierania od SPI1. Po odebraniu całego bloku pojawia się przerwanie
    - kanał DMA1_Ch3 jest od wysyłania do SPI1
    - SPI1 można założyć, że jest do niczego nie podłączone - na płytce mam dwa układy pracujące na SPI, ale w toku cięcia udało mi się je wykluczyć z problemu.
    - TIM3 służy do wyzwalania ADC1
    - ADC1 próbkuje jeden z pinów, na który wprowadzany jest szum
    - kanał DMA1_Ch1 przenosi wartości z ADC1->DR do CRC->DR
    - moduł CRC używany jest do kumulacji danych losowych pochodzących z pinu z szumem
    - CAN1 jest skonfigurowany do odbierania danych
    - TIM2 używany jest do odbierzania czasu braku odpowiedzi SPI1, dzięki czemu wiem, że SPI się zawiesiło. Tutaj następuje też reset SPI1, który odwiesza ten interfejs komunikacyjny.

    Zestaw {TIM3,ADC1,DMA1_Ch1,CRC} używany jest do zbierania danych losowych bez angażowania rdzenia. TIM3 wyzwala ADC1 co 10us.

    Błąd występuje tylko wtedy, kiedy SPI1 aktualnie wymienia blok z pomocą kanałów DMA, kanał DMA1_Ch1 jest załączony oraz przychodzą komunikaty na CAN'a

    Podczas testów na okrojonym kodzie, na którym ciągle występuje problem, udało mi się dojść do następujących wniosków:
    - wyłączenie kanału DMA1_Ch1 (transfer danych losowych z ADC1 do CRC bez udziału rdzenia) powoduje, że problem nie występuje.
    - po wyłączeniu zewnętrznego urządzenia nadającego komunikaty na CAN'ie problem ustaje.
    - prawdopodobieństwo wystąpienia powyższego problemu zwiększa się, gdy w przerwaniu CAN1_RX0 zwiększę ilość dostępów do rejestrów skrzynki odbiorczej
    - podkręcając ilość dostępów do rejestrów skrzynki odbiorczej udało mi się przy przesyłaniu po SPI bloku 64 bajtowego utracić przy odbiorze nawet 4 bajty
    - zmniejszając powyższą ilość dostępów zmniejsza się prawdopodobieństwo wystąpienia problemu
    - bez dostępu do oscyloskopu nie udało mi się stwierdzić, czy utrata następuje przy odbiorze z SPI, czy przy nadawaniu do SPI (wysłanie o 1 bajt mniej)




    - dodanie dostępu do zmiennej volatile pomiędzy dostępami do rejestrów skrzynki odbiorczej powoduje, że problem nie występuje.
    - zmiana dzielnika na APB2 z /2 na /1 pomimo dodanych dostępów do zmiennej volatile pomiędzy dostępami do rejestrów skrzynki odbiorczej CAN'a ponownie powoduje powstanie problemu.
    - analizując kod wynikowy, dostęp do rejestrów RIR,RDTR,RDLR i RDHR odbywa się przez cztery postępujące po sobie instrukcje LDR.

    Po wszystkich spostrzeżeniach, jedyne co mi przychodzi na myśl, to że występuje problem z samymi kanałami DMA, kiedy to występuje arbitraż pomiędzy DMA a rdzeniem (spór o dostęp do APB2, na którym znajdują się i SPI1, i ADC1), a dodatkowo pojawia się konflikt pomiędzy dwoma kanałami DMA. W takim przypadku może to być wyjątkowo problematyczny błąd, którego będzie się ciężko pozbyć.

    Problem na pewno nie występuje w:
    - konfiguracji pinów: w warunkach normalnych przesyłanie danych wiesza się po średnio kilku minutach, w zależności od ilości komunikatów odbieranych po CAN'ie
    - płytce: reszta działa poprawnie, jest dużo 100nF blisko procesora itd
    - priorytetach kanałów DMA: priorytety są dobrane prawidłowo (odbieranie: bardzo wysoki prio.; nadawanie: wysoki prio.; transfer adc->crc: niski)
    - braku załączenia wymaganych zegarów: wszystkie potrzebne są załączane na początku funkcji bsp_rfe_init
    - konfigurowaniu kanałów DMA: kanały przed konfigurowaniem są blokowane (CCR=0), a wartość wpisywana do CNDTR jest taka sama dla obu kanałów.
    - występowaniu innych przerwań: nie występują żadne inne przerwania oprócz { DMA1_Channel2, CAN1_RX0, TIM2 }, brak hard_fault itp
    - ingerencji debugera: po odłączeniu debugera i ponownym zasileniu, problem dalej występuje
    - optymalizacji kodu: na optymalizacji low oraz high problem wygląda tak samo.

    Platforma:
    - procesor: stm32f103rbt6 (reszta opisu: gh20t 9u chn gh 113), taktowany z HSI 8MHz bez dzielników, bez PLL
    - środowisko: IAR EWARM 5.4 (bez znaczenia)

    Kod jest okrojony do tego stopnia, że nie powinno być w nim już żadnych błędów.
    Nie znalazłem żadnej wzmianki o powyższym problemie w erracie (chyba, że mam jakąś nieaktualną: luty 2009).
    Nie interesują mnie rozwiązania dodatkowe typu "odwieś, gdy się zawiesi", gdyż po SPI przesyłane są pewne kluczowe dane do pamięci FRAM, a nie mogę sobie pozwolić na dezintegrację danych. Odwieszanie zrealizowane jest i tak, ale jako mechanizm pomocniczy, lepiej mieć błąd w pamięci FRAM (który będzie wykryty przez sumę kontrolną) niż zawieszony cały układ. Chcę zrozumieć mechanizm powstawania tego błędu oraz poprawić ten kod tak, aby ten błąd nie występował.

    Moje pytanie brzmi:
    - przegapiłem jakiś błąd w moim kodzie, czy natknąłem się na nieudokumentowany błąd w tych procesorach?

    -- edit: podmieniłem załącznik

    -- edit2:
    sprawdziłem najnowszą erratę (kwiecień 2011, rev10), też nie znalazłem żadnej wzmianki o powyższym problemie.

    -- edit3:
    Dodatkowe obserwacje:
    - jakiekolwiek zmiany priorytetów kanałów nie mają żadnego wpływu na występowanie problemu - występuje cały czas
    - zmiana kierunku kanału DMA1_Ch1 oraz zamiana CMAR i CPAR nie wpływa na występowanie błędu.
    - zastąpienie transferu DMA ADC=>CRC transferem DMA ADC=>RAM powoduje, że problem nie występuje. Jakkolwiek zależy mi na transferze bezpośrednio do CRC aby odciążyć rdzeń od mieszania danych losowych. RM mówi, że transfery peripherial-to-peripherial są dopuszczalne.

    -- edit4:
    Następne obserwacje:
    - ustawienie dzielnika APB2 na /4 powoduje, że problem nie występuje
    - ustawienie efektywnego dzielnika AHB i APB2 na co najmniej /4 powoduje, że problem nie występuje
    (wykreślone: zapominałem aktualizować prędkość CAN'a, przez co nie otrzymywałem przerwań)
    - zmiana okresu wyzwalania ADC1 (TIM3) nie wpływa na występowanie problemu

    -- edit5:
    Następne obserwacje:
    - do odtworzenia problemu nie jest potrzebne odbieranie komunikatów na CAN'ie. Wystarczy odczytywanie rejestrów od skrzynki odbiorczej.
    - problem występuje niezależnie, czy kanał DMA nadający do SPI pobiera dane z RAM'u czy z pamięci FLASH (niezależnie od ustawień opóźnienia dostępu do pamięci flash).

    -- edit6:
    Wykreśliłem część błędnych obserwacji z czwartej edycji.

    0 9
  • #2 31 Paź 2011 18:39
    BoskiDialer
    Poziom 34  

    Ze względu na ilość argumentów przemawiających za błędem w krzemie pozostało mi tylko obejście problemu w sposób jakikolwiek. Jako, że zaobserwowałem, że ustawiając dzielnik APB2 na /4 problem zanika, użyłem tego rozwiązania, jednak bez większych podstaw, że to jest rozwiązanie pewne. Jedyne liczby wskazujące na to rozwiązanie, to że aktualizacja na układzie CRC zajmuje 4 cykle AHB, ale co to ma wspólnego z APB1 (can) oraz APB2 (adc1, spi1), że błąd zanika - nie mam pojęcia.

    Gdybym wiedział do kogo z st wysłać maila, to pewnie nie pisał bym tego tematu.

    Jako, że problem nie został rozwiązany do końca, temat zostawiam otwarty.

    0
  • #4 01 Lis 2011 10:02
    Smashing
    Poziom 20  

    Witam
    A spróbuj zmienić Baud rate z 000: fPCLK/2 na 001: fPCLK/4

    Cytat:
    SPI1->CR1 = SPI_CR1_SSM|SPI_CR1_SSI|SPI_CR1_SPE|SPI_CR1_MSTR|(0*SPI_CR1_BR_0);
    .
    i wróć z dzielnikiem APB2 na 2.
    W Stm32F1 max prędkość spi po dma to 18 Mega. Może jak jesteśmy na granicy to coś się dzieje. A może flash który używasz jest za wolny... ale w to raczej wątpię..

    0
  • #5 01 Lis 2011 11:34
    BoskiDialer
    Poziom 34  

    Tyle, że w tym przykładzie procesor chodzi na HSI 8MHz, więc jestem daleko od jakiegokolwiek ograniczenia.
    Faktycznie:
    SPI_BR=/2, PPRE2=/4: problem nie występuje
    SPI_BR=/2, PPRE2=/2: problem występuje
    SPI_BR=/4, PPRE2=/2: problem nie występuje
    SPI_BR=/4, PPRE2=/1: problem występuje (rzadziej niż zwykle, wiesza się dopiero po około średnio 200ms)
    SPI_BR=/8, PPRE2=/1: problem nie występuje

    Jakkolwiek, załączając procesor na HSE 16MHz, powyższa lista jest dalej prawdziwa, więc częstotliwość występowania błędu jest funkcją tylko zastosowanych dzielników.

    Trochę o odtwarzaniu błędu w moich warunkach: może być obarczone błędem, jako że każdy test przeprowadzam przez co najwyżej 1 minutę, a w grę może wchodzić jakaś fatalna synchronizacja pomiędzy odczytem z rejestrów CAN'a i transferem od trzech kanałów DMA jednocześnie.

    Aby zwiększyć prawdopodobieństwo występowania błędu podkręciłem jeszcze trochę transfery na DMA1_Ch1: pracuje praktycznie ciągle (załączone MEM2MEM i CIRC - niezbyt zalecane rozwiązanie, ale to są tylko testy) - sytuacja wygląda dalej tak samo.

    Jedyna wada Twojego rozwiązania, to fakt, że nie tłumaczy ono przyczyny powstawania błędu. Pod tym względem to rozwiązanie jest takie samo jak moje - problem nie występuje, ale nikt nie wie dlaczego.

    0
  • #6 01 Lis 2011 15:44
    janbernat
    Poziom 38  

    Zawsze jak iloczyn dzielników ==8 to problem nie występuje.

    0
  • #7 01 Lis 2011 17:19
    BoskiDialer
    Poziom 34  

    janbernat napisał:
    Zawsze jak iloczyn dzielników ==8 to problem nie występuje.

    Będąc bardziej precyzyjnym, jeśli PPRE2*SPI_BR >= /8, to problem nie występuje.
    Przy ustawieniu HPRE=/4, PPRE2=/1 i SPI_BR=/2 błąd występuje, więc dzielnik AHB nie ma wpływu na występowanie błędu: to wskazuje, jakoby problem był po stronie złego zachowania kanałów DMA, gdy jeden z nich zapisuje do CRC, a ten blokuje AHB na 4 cykle.

    Wysłałem zapytanie do ST wraz z opisem problemu, zobaczymy co odpiszą.

    0
  • #8 15 Lis 2011 20:00
    BoskiDialer
    Poziom 34  

    W wielkim skrócie mówiąc, to ST nie jest zbytnio pomocne - po moim mailu dostałem odpowiedź, że nie rozwiązują takich trywialnych(tak to zrozumiałem) problemów i jak chcę, to mogę użyć ichniejszego forum oraz poczytać dokumenty ze wskazówkami na temat programowania kanałów DMA. Na drugiego maila nie odpisali, moje zapytanie widnieje jako odrzucone.

    Ze względu na brak wyjaśnienia mechanizmu powstawania problemu, temat pozostawiam otwarty.

    0
  • #10 24 Paź 2013 09:40
    zielik
    Poziom 9  

    Witam, Odswiezam temat gdyz zauwazylem podobny blad w SPI nawet bez wlaczonego DMA W STM32F103RC i F4Discovery.
    Mianowicie, posiadam czujnik MEMS ADIS16405, ktory wymaga aby predkosc SPI byla ponizej 1MHz, posiadam tez karte SD, ktora uzywam na SPI.
    W przypadku karty SD akurat problem jest mniejszy bo na SPI1 wystarczy uzyc jakikolwiek inny dzielnik niz SPI1_Prescaler_4. Natomiast w przypadku MEMSa, sama manipulacja prescalerem SPI nic nie daje. Nalezy podzielic dodatkowo szyne APB2, konfiguracje ktore dzialaja to:
    APB2 /2 spi/256
    APB2 /4 spi/128
    Nadmienie iz SPI2 i SPI3 dzialaja bez zadnych problemow i udziwnien zarowno z MEMSem jak i SD.

    0