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

Dostęp do zmiennych w C z poziomu asm w programie do komunikacji SPI

wojciechin 29 Sie 2016 16:23 3132 42
  • #1 15898884
    wojciechin
    Poziom 9  
    Witam. Ostatnio zacząłem pisanie programu do komunikacji po spi. Na i w ramach nauki asm użyłem kilku wstawek, no i wszystko fajnie tylko pojawił się taki problem: z poziomu asm chciałbym uzyskać dostęp do zmiennej (chociaż do adresu) zadeklarowanej w c. Asm niby posiada .byte ale dostęp do zmiennej musi być dwustronny więc nie wiem czy by tom wypaliło, a tak w ogóle chciałbym żeby zmienna ta była deklarowana z poziomu c. Z tego powodu chce prosić o pomoc.
    Próbowałem tak:
    Kod: AVR assembler
    Zaloguj się, aby zobaczyć kod

    no ale oczywiście dostawałem error. Nie widzi mi się kompilować szukać jaki adres ma ta zmienna a następnie "na żywca" przepisywać tą wartość do asm co dało by nie czytelny, zmasakrzony kod.
    Reszta wygląda tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Kod: AVR assembler
    Zaloguj się, aby zobaczyć kod


    No i mam jeszcze jedno pytanie czy taka zmienna powinna byś volatile?
    Zapomniałem dodać asm pisze w osobnym pliku .s.
  • #2 15898984
    BlueDraco
    Specjalista - Mikrokontrolery
    1. Masz błędny tytuł postu - nie chodzi o żadne wstawki asemblerowe, a o łączenie C z asemblerem, czyli program hybrydowy.

    2. Po co Ci te moduły i procedury asemblerowe? Kompilator C doskonale sobe radzi z dostępem do rejestrów.

    3. Gdzie i jak zadeklarowałeś zmienną numByte? Jaki błąd sygnalizuje kompilator.
  • #3 15899013
    grko
    Poziom 33  
    @wojciechin
    Może tak:
    Kod: AVR assembler
    Zaloguj się, aby zobaczyć kod
  • #4 15899061
    wojciechin
    Poziom 9  
    @BlueDraco Tytuł poprawiony. Narazie to co robię jest dla czystej nauki. Chodziło mi o zmienną SPInumByte, kompilator wywala że nie rozpoznaje zwrotu extern.

    @grko Dzięki! Mam jeszcze pytanie później używam tej etykiety jako adresu? I aby sczytać muszę ją załadować do rejestru np X, żeby następnie wywołać dyrektywę ld?
  • #5 15899426
    tmf
    VIP Zasłużony dla elektroda
    Przede wszystkim jeśli zastosujesz .extern to gdzieś ta zmienna musi zostać zdefiniowana. Możesz to zrobić w kodzie C, możesz w kodzie asemblerowym i potem w C dać extern typ zmienna. Dla asemblera będzie to tylko etykieta, więc sam musisz zadbać o jej poprawną interpretację. Możesz się do niej odwoływać poprzez rejestry indeksowe, lub korzystając z adresowania bezpośredniego, np. LDS rejestr, etykieta.
    Swoją drogą to akurat robienie wstawki w asemblerze zamieniającej pokazany kod C nic nie zmieni, w sensie wydajności. Zauważ, że optymalizacja tego kodu w ogóle jest niepotrzebna, bo po zapisie do rejestru danych SPI czekasz co najmniej 16 taktów, aż bajt zostanie wysłany... Większe przyśpieszenie uzyskałbyś wykorzystując interfejs USART w trybie SPI - dzięki zbuforowaniu rejestru nadajnika można uzyskać ciągłą transmisję po SPI.
  • #6 15899930
    wojciechin
    Poziom 9  
    Rejestry indeksowe? Chodzi o te 6 ostatnich rejestrów? No jak spowodować aby do tej etykiety kompilator przypisał mi adres zmiennej? Bo nie widzi mi się robienie tego ręcznie.
    Jeśli chodzi o szbkość znajomy uczył mnie aby wszystkie operacje oczekiwania nie blokowały procesora, tak więc robię tą transmisję na przerwaniach dzięki czemu nie muszę czekać na zakończenie.
  • #7 15900258
    JacekCz
    Poziom 42  
    BlueDraco napisał:
    1. Masz błędny tytuł postu - nie chodzi o żadne wstawki asemblerowe, a o łączenie C z asemblerem, czyli program hybrydowy.

    2. Po co Ci te moduły i procedury asemblerowe? Kompilator C doskonale sobe radzi z dostępem do rejestrów.

    3. ..


    Zgadzam się z tymi 3 myślami.

    wojciechin napisał:

    No i mam jeszcze jedno pytanie czy taka zmienna powinna byś volatile?


    To jest myślenie magiczne, być może to gdzieś znalazłeś, a że wielu programistów nie rozumie volatile ... wiesz jak to jest przysłowie o milionie much ... Nie jest to temat wątku. Więcej w necie jest błędnego kodu z volatile niż prawidłowego.

    Co do extern, deklaracja 'extern' i definicja - w odniesieniu do części C - fajnie się zgrywa jak projekt ma własny header
  • #8 15900317
    tmf
    VIP Zasłużony dla elektroda
    wojciechin napisał:
    Rejestry indeksowe? Chodzi o te 6 ostatnich rejestrów? No jak spowodować aby do tej etykiety kompilator przypisał mi adres zmiennej? Bo nie widzi mi się robienie tego ręcznie.
    Jeśli chodzi o szbkość znajomy uczył mnie aby wszystkie operacje oczekiwania nie blokowały procesora, tak więc robię tą transmisję na przerwaniach dzięki czemu nie muszę czekać na zakończenie.


    W programie używasz etykiet, linker je w odpowiednim momencie zamieni na adresy.
    Wykorzystanie przerwań przy SPI? To cie kolega wpuścił w maliny... Policz sam - wysyłasz 8 bitów, każdy przy max szybkości to 2 takty CPU, razem 16 taktów. A więc dokładnie co 16 taktów musisz SPI nakarmić nowymi danymi. Ponieważ nie masz bufora nadajnika, nie ma siły, zawsze będzie jakaś przerwa, w efekcie realna transmisja zwolni. W przypadku USART w trybie SPI jeden bajt jest nadawany, drugi czeka w buforze, w efekcie nie ma żadnych przerw. Teraz przerwania - samo wejście w przerwanie to jakieś 6 taktów (+/- bo to zależy od aktualnie wykonywanej instrukcji), powrót to 1 takt, a więc 7 taktów zmarnowane na samą obsługę przerwania. Teraz potrzeba zapisać rejestr stanu (nie zawsze jest to konieczne) i potem go odtworzyć, czyli kolejne 2-4 taktów. I ładowanie nowych danych do rejestru nadajnika. Jak się sprężysz to łącznie może w 16 taktach się wyrobisz. Problem w tym, że nowych danych nie możesz załadować przed upływem 16 taktów, więc oczekiwanie i tak trzeba wrzucić w ISR. Czyli jak widzisz wykorzystanie przerwań do obsługi SPI przy max szybkościach tego interfejsu się nie do końca sprawdza.
  • #9 15904917
    wojciechin
    Poziom 9  
    Tak więc jeśli napiszę tak:
    Kod: AVR assembler
    Zaloguj się, aby zobaczyć kod

    to linker to rozgryzie? Żeby nie okazało się że odwołuję się do pustej etykiety.

    A jeśli chodzi o przerwania no to myślę tak:
    na początku ładuję pierwszy bajt z bufora do rejestru ogólnego przeznaczenia a z niego do SPDR. Następnie pobieram następny bajt do rejestru o. p. i wychodzę z funkcji. W momencie wejścia w przerwanie wywalam wcześniej załadowany bajt i znowu mogę załadować następny - o ile istnieje. To raczej by się sprawdziło tym bardziej jeśli nie ustawie maksymalnej prędkości spi.
  • #10 15904968
    tmf
    VIP Zasłużony dla elektroda
    Nie możesz się odwołać do pustej etykiety, bo spowoduje to błąd linkera - undefined reference to... Więc dokładnie się to robi tak jak pokazałeś - .extern a potem odwołanie do etykiety w instrukcjach asemblera, czy czym etykieta musi gdzieś zostać zdefiniowana.
    Co do twojego myślenia o ISR - tak się tego nie robi. Po pierwsze, musisz założyć, że pomiędzy wywołaniami ISR twój rejestr, w którym przechowujesz bajt do wysłania, nie zmieni wartości, czyli program nie będzie z niego korzystał. Da się to założenie w gcc spełnić, ale nie obraź sie - z tego co widzę najpierw poucz się więcej o funkcjonowaniu toolchaina i bibliotek AVR-libc, potem rób takie cuda. Ale nawet jeśli iść tym tropem, to wyłączasz kompilatorowi jeden rejestr, co zazwyczaj przynosi więcej szkody niż pożytku. Druga sprawa - przerwnaie SPI zostanie zgłoszone, w chwili nadania bajtu. Po tym czasie musisz je obsłużyć i załadować nowy bajt - łącznie 7-8 taktów. Wysłanie bajtu to 16 taktów, a więc twoja przerwa będzie cię kosztować nawet 50% pasma. Widzisz, że to nie ma sensu. Więc albo niestety mozolnie się czeka w pętli i wysyła kolejne bajty, ew. przeplata oczekiwanie jakimiś użytecznymi instrukcjami (co nie jest proste ani czytelne), albo bierze AVR XMEGA i oprogramowuje SPI przy pomocy DMA, albo w każdym AVR - wykorzystuje się USART w trybie SPI, co dzięki buforowi nadajnika umożliwia uzyskanie ciągłej transmisji danych.
  • #11 15905088
    Konto nie istnieje
    Konto nie istnieje  
  • #12 15905548
    wojciechin
    Poziom 9  
    Dobra spróbuje z usart'em. Ale może coś dokładniej? Mam osobny rejestr do danych przychodzących a osobny do wychodzących. I jak ja mam niby zrobić aby załadować cały bufor do rejestrów (i których) żeby było dobrze? Nigdy się z tym nie spotkałe więc nie mam bladego pojęcia.
  • #13 15905860
    tmf
    VIP Zasłużony dla elektroda
    To ten sam rejestr, tylko przy odczycie zwraca bufor odczytu, a zapis powoduje zapisem do bufora zapisu. Zobacz rejestr stanu USART i przeczytaj jego opis, a nastanie jasność :)
  • #14 15907468
    wojciechin
    Poziom 9  
    Czytam i czytam i czytam. I nie mogę zrozumieć w jaki sposób mam tak to wykonać, aby wysłać kilka danych bez "doładowywania" ich do rejestru. Tak samo przecież mam 1 bajtowy rejestr tx/rx.
  • #15 15907490
    tmf
    VIP Zasłużony dla elektroda
    Tylko wydaje ci się, że jesto on 1 bajtowy. Hardware pokieruje kolejne bajty do bufora, jedyne co musisz zrobić to sprawdzić, czy jest miejsce - sygnalizują to stosowne flagi rejestru stanu.
  • #16 15907503
    wojciechin
    Poziom 9  
    Chodzi o USART Transmit Complete?

    Zrobiłem coś takiego:
    Kod: AVR assembler
    Zaloguj się, aby zobaczyć kod

    ale na pinie PD0 nie pojawia się stan wysoki. SPInumByte w pliku .c ma wartość 5.
  • #17 15907627
    Konto nie istnieje
    Konto nie istnieje  
  • #18 15907634
    wojciechin
    Poziom 9  
    To przeczyło by z tym co pisał kolega tmf.
  • #19 15907638
    tmf
    VIP Zasłużony dla elektroda
    wojciechin napisał:
    Chodzi o USART Transmit Complete?

    Nie, chodzi o UDRE. A poza tym, po co ten asembler? Żeby sobie życie skomplikować?:)
  • #20 15907647
    Konto nie istnieje
    Konto nie istnieje  
  • #21 15907726
    wojciechin
    Poziom 9  
    Hmmm... no dobra w takim wypadku chyba nie ma różnicy czy jest to SPI czy USART w trybie SPI?
  • #22 15907739
    Konto nie istnieje
    Konto nie istnieje  
  • #23 15907748
    wojciechin
    Poziom 9  
    Czyli coś nie tak zrozumieliśmy się z kolegą tmf? No dobra ale co jest z tym programem? Asm obrabiam w ramach utrudniania sobie życia i też nauki ;).
  • #24 15907751
    Konto nie istnieje
    Konto nie istnieje  
  • #25 15908183
    tmf
    VIP Zasłużony dla elektroda
    wojciechin napisał:
    Hmmm... no dobra w takim wypadku chyba nie ma różnicy czy jest to SPI czy USART w trybie SPI?


    Różnica jest olbrzymia. Napisz program wysyłający dane i przeanalizuj go w sytuacji, gdy masz bufor i gdy go nie ma. Jak pisałem, dla AVR różnica w szybkości wysyłania może wynieść nawet 50%. Dlaczego, też już ci napisałem. Czas na przemyślenie tematu, bez tego dalej nie zabrniesz.
  • #26 15908258
    Konto nie istnieje
    Konto nie istnieje  
  • #27 15908345
    kamyczek
    Poziom 38  
    Piotr tu ma rację nie ma spi mode w 128 chyba że zrobimy przesiadkę na 1281 . obsługa spi przy dużej szybkości niestety pożera sporo mocy obliczeniowej tak przy przerwaniach jak i przy czekaniu w bezczynnej pętli bo tam to już nie robi się zupełnie nic równie dobrze możesz w takim przypadku zrobić programowe spi będzie w prawdzie 2 razy wolniejsze od najszybszego sprzętowego ale odchodzi czekanie na zakończenie wysyłania bo dokładnie wiesz kiedy wysłałeś ostatni bit ;) . Ja zazwyczaj wolę robić to w przerwaniach lepsze 4 wolne takty niż bezczynna pętla sprawdzająca stan bitu w rejestrze . poza tym bufor niewiele daje jak trzeba wysyłać po jednym bajcie i sterować po każdym sygnałem CS . Troszkę wygodniej jest w przypadku Xmega DMA plus zdarzenia wyręcza mikrokontroler ...
    Wracając do zmiennych w asemblerze to po prostu dostęp do adresów ram czy rejestrów c pozwala chyba zadeklarować w jakim obszarze znajduje się bufor i wskaźnik do niego . Zasadniczo tu użycie asemblera nie wiele zmieni i nie wiem czy coś da w rezultacie w stosunku do c Poza tym pojedyncze zmienne można zawsze przekazać przez rejestr R0-R31 Te tak dla c jak asemblera mają takie samo znaczenie ...
  • #28 15908607
    tmf
    VIP Zasłużony dla elektroda
    Piotrus_999 napisał:
    tmf napisał:
    Różnica jest olbrzymia. Napisz program wysyłający dane i przeanalizuj go w sytuacji, gdy masz bufor i gdy go nie ma.


    Jaka jest z jego punktu widzenia? I tak może zapisać jeden bajt na raz, tak że obsługę dosyłania kolejnych musi napisać sam.


    Nie, może zapisać tyle bajtów ile zmieści się w buforze. A to robi istotną różnicę. Jeśli nie ma bufora (a tak jest w przypadku SPI), to kolejny bajt zapisze do SPDR dopiero po wysłaniu poprzedniego. Ponieważ na to musi czekać, więc nie nastąpi to wcześniej niż 4 takty. A więc marnuje czas co najmniej 2 bitów. Czyli efektywnie traci co najmniej 25%, więcej jeśli mniej sprytnie napisze funkcję przesyłającą dane.

    Dodano po 4 [minuty]:

    kamyczek napisał:
    Piotr tu ma rację nie ma spi mode w 128 chyba że zrobimy przesiadkę na 1281 . obsługa spi przy dużej szybkości niestety pożera sporo mocy obliczeniowej tak przy przerwaniach jak i przy czekaniu w bezczynnej pętli bo tam to już nie robi się zupełnie nic równie dobrze możesz w takim przypadku zrobić programowe spi będzie w prawdzie 2 razy wolniejsze od najszybszego sprzętowego ale odchodzi czekanie na zakończenie wysyłania bo dokładnie wiesz kiedy wysłałeś ostatni bit ;) .


    Z tym soft-SPI to kompletna bzdura. Przy SPI z buforem mamy max 16 taktów pomiędzy kolejnymi ładowaniami rejestrów. A to znaczy, że możemy tam wpleść wiele użytecznych instrukcji. Więć procesor wcale nie musi czekać w pętli i marnować czas. Nie jest to proste do zrealizowania, niemniej możliwe. Wygodne przy wysyłaniu np. danych graficznych do LCD, gdzie pomiędzy można przeliczyć np. stan kolejnych pikseli.

    kamyczek napisał:
    Ja zazwyczaj wolę robić to w przerwaniach lepsze 4 wolne takty niż bezczynna pętla sprawdzająca stan bitu w rejestrze . poza tym bufor niewiele daje jak trzeba wysyłać po jednym bajcie i sterować po każdym sygnałem CS .


    Niby w czym jest to lepsze? Poza tym przy przerwaniach nie będą to 4 takty - samo wejście w ISR trwa dłużej.
  • #29 15908690
    kamyczek
    Poziom 38  
    tmf napisał:
    Niby w czym jest to lepsze? Poza tym przy przerwaniach nie będą to 4 takty - samo wejście w ISR trwa dłużej.


    Tomku ile to będzie zależy od prescalera zegara spi i nie łudź się że wyśle się to w 8 taktach zegara taktującego mikrokontroler bo minimum dwa razy wolniej więc 16 - isr to ? ... Poza tym zamiast czekać można np. pobrać sobie kolejny bajt który chcesz wysłać po spi z flash , eeprom , czy ram to też zajmuje czas . W asemblerze łatwo policzyć ile kroków zegara zajmuje wykonanie procedury więc można zrobić sobie coś przez te cykle i najwyżej kliknąć jeden czy dwa nop .
  • #30 15908701
    Konto nie istnieje
    Konto nie istnieje  
REKLAMA