logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[ATMega32][C]Kolejkowe SPI - jak wykonać?

A.T. 19 Lut 2011 00:11 1998 12
  • #1 9171322
    A.T.
    Poziom 20  
    Witam
    jak zrobić kolejkowe SPI na ATMEGA32? Bardzo bym prosił o jakiś fragment z przykładem czegoś takiego.
    Pozdrawiam A.T.

    Poprawiłem tytuł.
    [zumek]
  • #3 9171459
    A.T.
    Poziom 20  
    Dzięki za odpowiedź:) Chodzi o C.
  • #4 9171807
    tmf
    VIP Zasłużony dla elektroda
    Kolejkować trzeba transakcje - robisz bufor w którym trzymasz dane do wysłania, potrzebujesz jakieś funkcje dodające bufor do kolejki i badające jej status. Po nadaniu danych z bufora, znajdą się w nim dane odebrane. Całość może działać w oparciu o przerwanie SPI.
  • #5 9171961
    A.T.
    Poziom 20  
    a miałbyś jakiś przykład zastosowania tego? Rozjaśnił by mi sprawę.
  • #6 9177597
    nsvinc
    Poziom 35  
    Kolejkowanie transakcji SPI nie jest takie proste. Transakcja to nie jest niestety proste read/write, tylko proces oparty o konkretną maszynę stanu scalaka z którym odbywa się komunikacja.

    Przykład: transakcja zapisu dwóch bajtów do pamięci EEPROM to:

    1) CS w dół
    2) TX WRITE_ENABLE, (RX nieistotny)
    3) CS w górę
    4) delay
    5) CS w dół
    6) TX WRITE (RX nieistotny)
    7) TX ADRES0,ADRES1
    8) TX BAJT0,BAJT1
    9) CS w górę...

    Odczyt dwóch bajtów jest bardziej skomplikowany, bo od punktu 6:
    6) TX READ (RX nieistotny)
    7) TX ADRES0,ADRES1
    8) TX 0x00, RX do bufora (BAJT0)
    9) TX 0x00, RX do bufora (BAJT1)
    10) CS w górę...
  • #7 9177648
    tmf
    VIP Zasłużony dla elektroda
    Na tym właśnie polega transakcja, że jest bardziej skomplikowana niż proste R/W. Stąd też jak napisałem bufor musi zawierać kompletną transakcję i na zwrocie będzie zawierał to co ze scalaka przy okazji zostało odczytane. Ciągle nie widzę tu problemu. Jedyne co trzeba zrobić to prosty programik dodający transakcję do kolejki i sygnalizujący jej wykonanie. Ew. progblem może się pojawić jeśli jest więcej urządzeń SPI i trzeba odpowiednio sterować liniami SS. W takiej sytuacji transakcja musi zawierać pewne metadane, które taki nazwijmy go "dispatcher" wykorzysta do sterowania liniamii innymi niż SCK, MISO i MOSI.
  • #8 9179200
    nsvinc
    Poziom 35  
    No oczywiście że nie ma problemu, ale trzeba umieć to napisać :] Nie każdy potrafi....

    Trudność (nie problem) polega właśnie na napisaniu tego dispatchera, bo:
    1) transakcje nie powinny być wskaźnikami w fifo, lecz powinna być lista niezrealizowanych transakcji tak samo jak lista zrealizowanych transakcji
    2) transakcje powinny być priorytetowane
    3) dispatcher analizuje zawartość listy transakcji niezrealizowanych, arbitruje po priorytecie, wybiera jedną z nich i wykonuje, po czym wrzuca wskaznik na liste transakcji zrealizowanych...
    4) rozdawnictwo uchwytów - proces żądający danych transakcji musi wiedzieć kiedy one się skończyły: albo sygnały, albo polling bitu statusowego w "strukturze" transakcji
    5) chyba najwygodniej zbudować "klasę"/"typedef struct" transakcji, aby łatwo było napisać funkcje:
    -- dodawania transakcji do listy niezrealizowanych
    -- dispatcher (koniecznie w przerwaniu SPI lub DMA
    -- rozdającą uchwyty (wskazniki)

    ...no i oczywiście nadal nie ma tu nigdzie problemu, ale początkujący połamie sobie na tym paznokcie i zemby :P
  • #9 9179313
    tmf
    VIP Zasłużony dla elektroda
    Możliwe. Dlatego trzeba to max uprościć. Z priorytetów bym zrezygnował bo przy szybkości SPI raczej są niepotrzebne. Transakcja to przydzielony dynamicznie obszar pamięci identyfikowany przez wskaźnik. Dispatcher to kolejka FIFO, pobiera wskaźnik, realizuje transakcję, usuwa wskaźnik z kolejki. Stąd też aplikacja wywołująca jeśli po przeszukaniu kolejki w kierunku wystąpienia w niej wskaźnika do transakcji go nie znajdzie przyjmuje, że transakcja została zrealizowana. To nam upraszcza kwestie sygnalizacji. Całość działa w przerwaniu SPI, pierwsza transmisja inicjowana jest poprzez wpis do kolejki FIFO wskaźnika do obszaru pamięci zawierającego dane transakcji. Całość to kilkanaście linii kodu. Można to oczywiście skomplikować dodając np. powiadomienia na zasadzi ecallbacków, ale pooling jest bezpieczniejszy, bo nie trzeba za bardzo kombinować z funkcjami reentrant i sekcjami krytycznymi.
  • #10 9180083
    nsvinc
    Poziom 35  
    tmf napisał:
    Dispatcher to kolejka FIFO, pobiera wskaźnik, realizuje transakcję, usuwa wskaźnik z kolejki.

    Z definicji FIFO...
    Aczkolwiek skoro budujemy już jedno fifo wejściowe dispatchera, to zbudujmy dodatkową listę zrealizowanych transakcji. Polling wtedy jest humanitarnie prosty, bo szuka wskaznika na tej liście równego wskaznikowi który dany proces/wątek wrzucił do wejściowego FIFO. Jak znajdzie, wywala z listy wskaznik, a o wielkość listy i zerowe (użyte i wywalone) wskaźniki niech sie martwi mikro-garbage collector chodzący w przerwaniu i przebudowujący listę.

    Lepszym od powyższego jest nie tworzenie listy zrealizowanych transakcji, a po prostu ustawienie bitu typu _ready w strukturze transakcji. Wtedy kod który wrzucił "swoją" transakcję do fifo dispatchera sprawdza tylko ten bit w "swoich" strukturach. Dispatcher wtedy nie musi zarządzać stertą, bo fifo ma stałą wielkość, a wątki wrzucające transakcje do fifo same martwią się o zarządzanie przywłaszczoną sobie pamięcią.

    tmf napisał:
    [...]wskaźnika do obszaru pamięci zawierającego dane transakcji.

    Dane transakcji mogą być...różne. Zauważ, że jedna transakcja może (i często będzie) składać się z danych tx+rx. Dwa wskazniki na dwie przestrzenie zawierające surowe dane do tx i pusta przestrzen dla rx. RX komplikuje sytuacje, bo wymaga od dispatchera popchnięcie do SPI określoną ilość zer jednocześnie przechwytując odebrane bity do bufora rx, a to wszystko dopiero po skonczeniu tx...
  • #11 9180185
    tmf
    VIP Zasłużony dla elektroda
    Ja jednak uważam, że najprościej jest zrobić tak jak opisałem - po co jakieś dodatkowe listy zrealizowanych transakcji, czy flagi? Aplikacja umieszcza na liście do zrealizowania wskaźnik opisujący transakcję, po czym cyklicznie sprawdza, czy ten wskaźnik ciągle tam jest. Jak go nie ma to znaczy, że transakcja została zrealizowana.
    Co do drugiej części. Jeden wskaźnik wystarczy. W SPI ilość bajtów odebranych==ilość bajtów nadanych. Co więcej nadawanie jest jednoczesne z odbieraniem. Więc na początku w buforze są dane do nadania, po zakończeniu transakcji w tym samym buforze są dane odebrane.
  • #12 9180787
    A.T.
    Poziom 20  
    Dziękuję za odpowiedź:) Analizowałem Wasze wypowiedzi ale niestety nie zrozumiałem wszystkiego na tyle dobrze aby stworzyć sobie kod. Nie mielibyście jakiś adresów stron www na których jest to opisane w prostszy sposób lub z większą ilością przykładów. Jeśli chodzi o moje zastosowanie tego to potrzebuję odczytywać wartości z przetwornika analogowo-cyfrowego MCP3208 z częstotliwością ok. 50Hz. Muszę przy tym minimalnie wykorzystywać procesor aby mógł w tym czasie posłużyć do czegoś innego. Dlatego właśnie polecono mi użycie kolejkowego SPI, którego ATmega32 niestety nie posiada:/ Jeśli to ważne to niżej udostępniam fragment kodu który trzeba zmienić na kolejkowe SPI.
    Pozdrawiam
    A.T.

    // Definicje
    // ***********************************************
       #define SS_NISKI() (PORTB &= ~0x01); // stan niski na SS
       #define SS_WYSOKI() (PORTB |= 0x01); // stan wysoki na SS
       #define zero_g 1500
    // ***********************************************
    // Inicjacja MCP3208
    // ***********************************************
       void spi_init(void)
       {
          DDRB |= 0b00000111; // ustawienie kierunku bitów portu
          SPCR = _BV(SPE) | _BV(MSTR); // tryb nadrzędny, prędkość 4MHz, wlączenie SPI, wlączenie przerwania
          SS_WYSOKI(); //stan wysoki na SS
       }
    // ***********************************************
    // Odczytanie osi X akcelerometru
    // ***********************************************
       void czytaj_akcelerometr()
       {
          SS_NISKI(); // stan niski na SS
          SPDR = 0x06;
          while(!(SPSR & (1<<SPIF)));
          danaH = SPDR;
          SPDR = 0x00;
          while(!(SPSR & (1<<SPIF)));
          danaH = SPDR; // MSB
          SPDR = 0x00;
          while(!(SPSR & (1<<SPIF)));
          danaL = SPDR; // LSB
          SS_WYSOKI(); // stan wysoki na SS
    // ***********************************************
    // Utworzenie 16 bitowej liczby z MSB i LSB
    // ***********************************************
          danaK = danaH<<8 | danaL; // liczba 16 bitowa
          danaK &= 0x3FFF; // pozbycie się 4 znaczących bitów
          axis_X = danaK - zero_g;
    // ***********************************************
    // Odczytanie osi Y akcelerometru
    // ***********************************************
          SS_NISKI(); // stan niski na SS
          SPDR = 0x06;
          while(!(SPSR & (1<<SPIF)));
          danaH = SPDR;
          SPDR = 0x40;
          while(!(SPSR & (1<<SPIF)));
          danaH = SPDR; // MSB
          SPDR = 0x00;
          while(!(SPSR & (1<<SPIF)));
          danaL = SPDR; // LSB
          SS_WYSOKI(); // stan wysoki na SS
    // ***********************************************
    // Utworzenie 16 bitowej liczby z MSB i LSB
    // ***********************************************
          danaK = danaH<<8 | danaL; // liczba 16 bitowa
          danaK &= 0x3FFF; // pozbycie się 4 znaczących bitów
          axis_Y = danaK - zero_g;
    // ***********************************************
    // Wyliczenie kąta alfa
    // ***********************************************
          alfa = atan(axis_X / axis_Y); // wyliczenie odchylenia
       }
  • #13 11489754
    A.T.
    Poziom 20  
    Witam ponownie,
    problem powrócił i postanowiłem spróbować napisać taki kod.
    Czy mógłby mi ktoś powiedzieć na podstawie tego co stworzyłem do tej pory czy idea tego jest dobra. Podstawą na której się opieram jest wypowiedź Pana tmf:

    "Możliwe. Dlatego trzeba to max uprościć. Z priorytetów bym zrezygnował bo przy szybkości SPI raczej są niepotrzebne. Transakcja to przydzielony dynamicznie obszar pamięci identyfikowany przez wskaźnik. Dispatcher to kolejka FIFO, pobiera wskaźnik, realizuje transakcję, usuwa wskaźnik z kolejki. Stąd też aplikacja wywołująca jeśli po przeszukaniu kolejki w kierunku wystąpienia w niej wskaźnika do transakcji go nie znajdzie przyjmuje, że transakcja została zrealizowana. To nam upraszcza kwestie sygnalizacji. Całość działa w przerwaniu SPI, pierwsza transmisja inicjowana jest poprzez wpis do kolejki FIFO wskaźnika do obszaru pamięci zawierającego dane transakcji. Całość to kilkanaście linii kodu. Można to oczywiście skomplikować dodając np. powiadomienia na zasadzi ecallbacków, ale pooling jest bezpieczniejszy, bo nie trzeba za bardzo kombinować z funkcjami reentrant i sekcjami krytycznymi."

    Oto kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Teraz muszę tylko napisać funkcję do odczytu wskaźnika z bufora i wysyłania danych prze SPI.

    Pozdrawiam
REKLAMA