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

Port i jego bit jako parametr funkcji w C

autoservice 14 Lip 2011 00:29 2487 13
  • #1 9715926
    autoservice
    Poziom 20  
    Witam
    W bibliotece zewnętrznej, która nie zna "PORTA" itp. mam funkcję, w której parametrami są port i jego bit. Ponieważ funkcja nie zna co to jest PORTA bo musiałbym dołączać include wszystkich avr'ków więc wymyśliłem, że odnoszę się do PORTA jak do zwykłego adresu pamięci...za pomocą funkcji pokeb, peekb...tj. odczytu i zapisu bajtu w pamięci SRAM AVR'a. Te funkcje wziąłem z biblioteki obsługi pamięci w kompilatorze CodeVision.
    Mam taki kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Czyli np. dla PORTD mam adres 0x2B, stąd PIND=0x29 więc _port-2, i DDRD=0x2A więc _port-1.

    Czy jest jakiś inny prostszy sposób aby to zrobić bez używania tych funkcji obsługi pamięci SRAM? Oczywiście zachowując funkcję w takiej postaci, że podaję tylko adres portu w pamięci oraz nr bitu który chcę sprawdzić.
    Jak wcześniej wspomniałem, funkcja jest w bibliotece a nie w kodzie programu więc nie wie co to znaczy PORTA i nie mogę tego użyć...
    Pozdrawiam
  • #4 9717355
    autoservice
    Poziom 20  
    No tak tylko, że w przykładzie z tamtego tematu wskaźniki otrzymują adres za pomocą:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    a skąd moja biblioteka ma wiedzieć co to jest PINB i PORTB?;)
    Musiałbym dołączyć includa z definicjami portów i rejestrów...a to blokuje mi bliblitekę tylko do 1 typu AVR'a... Może też nie głupi pomysł, bo wystarczy zmienić jedną linijkę przed kompilacją danego projektu i będzie ładnie działać.
    Fajnie by było gdyby wskaźnikowi można było przypisać wartość bezpośrednio np. ptr=0x2B i automatycznie wskazuje na adres 0x2B, podany jako parametr funkcji; no ale tak się nie da.
    Próbowałem nawet za pomocą wskaźnika do wskaźnika ale też nie chciało działać :) Mógłbym chętnie skorzystać z wstawek asm ale nie wiem jak zmiennej w C przypisać wartość bitu sprawdzonego w asm.
    Pzdr.
  • Pomocny post
    #5 9717386
    tmf
    VIP Zasłużony dla elektroda
    http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass
    W jaki sposób załączenie io.h blokuje ci użycie biblioteki do jednego typu AVR?
    Oczywiście wskaźnikowi można bezpośrednio przypisać wartość, jak inaczej miałoby się je inicjować?
  • Pomocny post
    #6 9717388
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Moduł z funkcjami 1wire nic nie musi wiedzieć o modelu, dostaje wskaźnik od wyższych warstw programu i koniec. Definicja tych wskaźników może (i powinna!) być w innym pliku. Jakaś część tego programu chyba wie o jaki model AVRa chodzi, no nie?

    4\/3!!
  • Pomocny post
    #7 9718143
    BoskiDialer
    Poziom 34  
    autoservice napisał:
    a skąd moja biblioteka ma wiedzieć co to jest PINB i PORTB?;)

    Wskaźnik przekazuje się z zewnątrz, biblioteka nic nie wie co to jest PINB i PORTB. Biblioteka ma w tym przypadku dostęp tylko do wskaźników wskazujących na coś, akurat to my zakładamy, że to coś wskazuje na PINx lub PORTx.
    Co do uniezależnienia się od io.h: A skąd wtedy program ma wiedzieć gdzie znajduje się PINB i PORTB? Wszak PINB i PORTB są tylko zakamuflowanymi makrami - zmiennymi osadzonymi pod konkretnymi adresami. W każdym procesorze mogą się one znaleźć w innych miejscach (różne adresy). Jeśli się uniezależnisz od io.h, to będziesz musiał sam jawnie zadawać adresy - jeszcze więcej roboty niż zrobienie (lub wręcz wygenerowanie) jakiegoś pliku zawierającego szereg #ifdef'ów, które tworzyły by jakieś struktury (w zależności, czy PORTB lub PINB istnieją). Można nawet zrobić tablicę składającą się z trzech elementów: literki reprezentującej port, cyferki reprezentującej bit oraz struktury zawierającej wskaźniki. Części takiej tablicy mogą być wkompilowywane warunkowo używając #ifdef'ów. Jakkolwiek każdy programista C o zdrowych zmysłach odrzuci to rozwiązanie po przeczytaniu pierwszego zdania. Przekazywanie samej struktury jest wystarczająco znośne, zawsze można napisać funkcję wypełniającą powyższą strukturę, do której przekazuje się znak identyfikujący port, a w środku na podstawie większego switch'a z wkompilowywanymi warunkowo case'ami struktura była by uzupełniania.
  • Pomocny post
    #8 9718578
    gaskoin
    Poziom 38  
    Może oftop, ale dlaczego:

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


    Nie napiszesz tak:

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


    Jestem ciekaw, czy optymalizator "zna" takie przypadki :)
  • Pomocny post
    #9 9719106
    BoskiDialer
    Poziom 34  
    Owszem, optymalizator zna takie przypadki i wygeneruje super nieoptymalny kod: będzie przesuwać jedynkę w lewo jedyną możliwą instrukcją (przesunięcie w lewo) tyle razy, ile wynosi wartość zmiennej _bit. Niestety, ale jeśli zmienna _bit nie jest znana w czasie kompilacji, to w przypadku AVR'ów kompilator rozwija przesuwanie o zmienną w pętlę z przesuwaniem o 1. Można zastosować jakąś prostą tablicę podstawieniową, było by odrobinę szybciej.

    Bardziej sensowym pytaniem było by: Po co przekazywać numer bitu, jak można przekazać maskę bitu? W tym przypadku taka zmienna i tak zajęła by jeden bajt, więc strat nie ma prawie żadnych, a korzyść jest ogromna - brak konieczności przeliczania numeru bitu na maskę.
  • Pomocny post
    #10 9719279
    gaskoin
    Poziom 38  
    Wprost z manuala do avr-libc do którego lektury zachęcam, wielu przeczytało i wielu nie zadaje już pytań na forum :)

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


    Mam nadzieję będziesz umiał wykorzystać to w swojej funkcji. Z resztą Freddie już napisał jak.
  • #11 9719574
    autoservice
    Poziom 20  
    Nie zrobię tak:

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


    bo nawet z optymalizatorem ustawionym max. szybkość funkcja ze switch działa o kilka cykli szybciej :) sprawdzone debugując w avrstudio... Twoja propozycja zadziała jeśli mask będzie zmienną w kodzie programu głównego i zmienna będzie rejestrze...a nie w pamięci SRAM w dodatku wewnątrz funkcji ...u mnie prawie identyczna liczba cykli...np. gdy 3 bit to 27 i 26 cykli a gdy 7 bit to 42 i 43 cykle...no może kod programu krótszy i zwięzły z Twoim rozwiązaniem :)

    W wywołaniu funkcji nie podaję maski bo w przypadku wywoływania kilku funkcji pod rząd w pętli np for(;;)... łatwo zmieniać zmienną bitową od 0 do 7...

    A kod ze wskaźnikami śmiga:) Nie wiem czy to koniecznie ale nie podawałem jako volatile...po prostu jako ... char *_port... Trochę się nagłowiłem bo wewnątrz funkcji mam kolejne funkcje, które także potrzebują adresu portu i nie wiedziałem jak przekazać wskaźnik jako argument kolejnej funkcji...
    Wszyscy pomogli...kliknę gdzie trzeba :)
    Pozdrawiam
  • Pomocny post
    #12 9719669
    tmf
    VIP Zasłużony dla elektroda
    Gdzie będzie zmienna _bit jest bez znaczenia, ten kod zawsze musi działać poprawnie. Jeśli zależy ci na prędkości to zamień switch/case na tablicę zawierającą kolejne maski na odpowiednich indeksach. Masz gwarantowany stały, najkrótszy możliwy czas konwersji.
    Co do przekazywania wskaźnika do IO - jeśli parametr nie jest volatile to kompilator nie wywala ci ostrzeżenia? Powinien.
    Powinno być volatile, inaczej optymalizator może robić kuku i cały kod będzie w dziwacznie skomplikowany sposób nie działał poprawnie.
  • #13 9719791
    autoservice
    Poziom 20  
    Faktycznie, jeśli maski zadeklaruję jako tablicę stałych w pamięci flash to "odczyt" maski jest stały i wynosi 8 cykli, czyli prędko :)

    Kompilator nie wywala błędu, w bibliotece funkcja wygląda tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    a wywołanie funkcji w kodzie programu:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Nie ma żadnego błedu, który przerywa kompilację ani nawet ostrzeżenia...a wszystkie mam włączone.
    Pzdr
  • #14 9719963
    tmf
    VIP Zasłużony dla elektroda
    Jaka wersja kompilatora? Powinno być ostrzeżenie, że w sposób niejawny pozbywasz się modyfikatora volatile. Bez niego kod może działać nieprawidłowo, sam sprawdź np.:
    PORTA=0;
    PORTA=255;
    i następnie przekaż PORTA do swojej funkcji, w której wykonasz powyższe operacje na wskaźniku. Przeanalizuj wygenerowany kod assemblerowy i konsekwencje dla swojej funkcji. To jedno słówko naprawdę robi różnicę :)
REKLAMA