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

[FAT] - Podkatalogi na karcie SD - obsługa, wyszukiwanie plików , itp

14 Maj 2014 01:14 3369 38
  • Poziom 22  
    Witajcie,

    Mam pytanko odnośnie katalogów i podkatalogów na karcie SD (FAT16/32).
    Chcę rozszerzyć funkcjonalność mojego programu do obsługi systemu plików o możliwość użycia podkatalogów - żeby łatwiej można było utrzymać porządek na dysku, zamiast wrzucać wszystkie pliki do jednego wora.

    Poczytałem trochę na ten temat, ale nie jestem pewien czy dobrze to wszystko rozumiem.

    A rozumiem to mniej więcej tak:

    Każdy plik na dysku ma swoje 32 bajtowe "entry", gdzie znajduje się nazwa (8) i rozszerzenie pliku (3), rozmiar, atrybuty, daty modyfikacji, itd....oraz co najważniejsze adres klastra gdzie zaczyna się plik (jego dane).

    Katalog też ma 32bajtowe entry, ale różni się tym od pliku, że ma ustawiony bit nr 1 (chociaż w bajcie atrybutów jest bit nr 4 oznaczony jako Directory - nie rozumiem dlaczego nie jest więc on wykorzystywany? Chyba, ze to bląd w publikacji?) oraz wielkość pliku = 0

    No i teraz najważniejsze - adres klastra w tym entry. Czy wskazuje on adres klastra do podkatalogu? (a nie początek danych - jak to jest w pliku)
    Czy w takim razie biorąc ten adres klastra otrzymam czytając po kolei sektory, nazwy plików (czyli ich 32bajtowe entry) poprzedzone "dot" i "dotdot" entry?

    To by miało sens, ale własnie nie udało mi się tego odczytać w publikacji z której korzystam.

    Teraz zastanawiam się nad algorytmem wyszukiwania plików.

    Chyba nie ma sensu robić przeszukiwania dysku po wszelkich możliwych podkatalogach, bo to zajmie za dużo czasu.
    Lepiej chyba będzie podać jawnie pełną ścieżkę do pliku - czy będzie ona zaszyta w pamięci mikrokontrolera na stałe czy też będzie podana w jakimś pliku konfiguracyjnym pobieranym z SD.

    W takim wypadku - o ile moje rozumienie katalogów jest takie jak powyżej i jest prawidłowe - najpierw musimy znaleźć entry odpowiadające nazwie katalogu, następnie pobrać stamtąd adres klastra podkatalogu i tam dokonać przeszukania na obecność już konkretnego pliku.

    Czy ktoś, kto "bawił się" własnym systemem plików może potwierdzić, że dobrze kombinuję lub wyjaśnić jak to naprawdę wygląda?

    Dodano po 1 [godziny] 19 [minuty]:

    Wygląda na to, że jest tak jak myślałem (chociaż zobaczymy jak to będzie w praktyce, heh...).
    Udało mi się wyszperać w końcu informację na konkretny temat:

    "Subdirectories
    As noted earlier, a subdirectory entry is flagged by the “subdirectory” bit in the
    “attribute” byte.
    The cluster that the “first cluster “ field describes is the memory locations that store the subdirectory information. The subdirectory information is laid out almost exactly like the root directory using the 32 byte descriptions. When subdirectories are created though, two entries are created that can never be deleted: the “.” and “..” entries.
    The “.” entry points to the current subdirectory; the “first cluster” field points to the
    cluster of the subdirectory. The “..” entry points to the parent directory.
    If the parent directory is the root directory, the entry for the “first cluster” field is a zero."

    Czyli w publikacji był błąd z tym bitem - jednak jest wykorzystywany bit 4 w atrybutach pliku - Directory.

    Może komuś się to przyda jeszcze :-)
  • IT SerwisIT Serwis
  • Poziom 20  
    Rozumiem aspekt edukacyjny, ale czy nie lepiej użyć gotowych bibliotek do fat16/32 (np fatfilelib) z posixowym interfejsem oszczędzając sobie czas i nerwy przy ewentualnych błędach własnej implementacji skutkujących najpewniej masakrą fs'a ? :-)
  • Poziom 22  
    Owszem, ale ja piszę wszystko na maszynach stanów i gotowce mi nie podejdą (napisany jest tak nie tylko FAT, ale i obsługa komunikacji z SD) - najwyżej, żeby podejrzeć jak to inni zaimplementowali.
    Często też niektóre biblioteki są nadmiarowe - pisząc samemu można "przyciąć na miarę" - w zależności od potrzeb.
    Poza tym - nie ma problemów z prawem autorskim, etc ,etc...

    Zresztą - gdyby zaszła taka potrzeba zawsze można przejść na gotowca, a znając dobrze temat będzie to łatwiejsze.
    Widziałem, że któryś z Kolegów napisał własną obsługę EXT2 - też można :-)

    Ktoś musi pisać te biblioteki, żeby korzystać mógł ktoś ;-)
  • Poziom 20  
    Ext2 na jakim mcu? I jakie zasoby pamięciowe ta implementacja wymagała? A ta maszyna stanów to aby łatwiej zaimplementować w tym projekcie wielozadaniowość kooperatywną czy z, innych powodów?
  • IT SerwisIT Serwis
  • Poziom 22  
    Ech...nawet znalazłem linka : http://sourceforge.net/projects/lwext4/
    Kolega Grzegorz chyba się nie obrazi skoro ma to w stopce ;-)
    A maszyny stanów do wielozadaniowości oczywiście, a może raczej do "nieblokującego" pisania programów.
  • Poziom 20  
    A jak w Twojej implementacji fs wygląda paradygmat read(,,,)/write(,,,)? Czytanie (pisanie) danych z pliku do jakiegoś bufora z reguły jest blokujące. Implementacje fat16/32 na mikrokontrolery (przynajmniej te co testowałem) nie są "reentrant" i w krytycznych obszarach są locki aby nie dopuścić równoległego wątku podczas gdy inny jest w trakcie tej samej operacji. Domyślam się, że znacznie rozdzieliłeś na poszczególne stany operacje na fs'e, czy w związku z tym nadal jest możliwe "otwarcie" przez inne zadania wiele plików na raz w różnych trybach (read/write itp)?
  • Poziom 22  
    Właściwie, to nie było mi to do niczego potrzebne, więc w tej chwili nie jest napisana taka obsługa (ale zawsze w razie potrzeby można dopisać).
    Minimalny odczyt/zapis z karty SD jest na poziomie jednego bloku 512 bajtowego i jeżeli odczyt/zapis z/do pliku miałby być "multipleksowany" pomiędzy różnymi plikami, to na poziomie właśnie takiego 512B sektora (jeden sektor dla 'plik_1', dwa dla 'plik_2', 10 dla 'plik_3', itd...).
    Po znalezieniu pliku otrzymujemy oprócz parametrów związanych z jego wielkością, atrybutami, itd..., również adres klastra w którym plik się mieści.
    Łatwo można by go zapamiętać w jakimś buforze czy nawet zmiennych i w razie potrzeby przeładowywać zmienne przed wywołaniem odczytu/zapisu do danego pliku.
    Wtedy odpada nam konieczność przeszukiwania za każdym razem dysku w poszukiwaniu interesującego nas pliku.

    Co do samego "nieblokowania", to w tym momencie mam na myśli nie blokowanie procesora na jednym zadaniu - wszelkie peryferiale własnie tym się zazwyczaj charakteryzują, że musi upłynąć pewien czas zanim coś wykonają (minimalny kwant czasu na obsługę minimalnego transferu danych do/z peryferiala)
    W tym czasie procesor zajmuje się innymi zadaniami (jeśli akurat jakieś może wykonać, bo nie jest uzależniony od danych na które właśnie czeka).

    Do przykładowej karty SD nie da się dokonać jednocześnie dwóch odczytów/zapisów, bo jest to fizycznie niemożliwe - można tylko trzymać gdzieś dane dotyczące "otwartego" pliku co przyspiesza ponowny dostęp do danych pliku.

    Jeśli chodzi o odczyt, to nie ma z tym żadnego problemu - komunikację z plikiem można zakończyć w dowolnym momencie - nawet wyjmując brutalnie kartę ze slotu.
    Gorzej przy zapisie - trzymanie za długo wciąż otwartych plików, bez uaktualnionego nagłówka pliku i FAT'u grozi w przypadku awarii sprzętowej utratą danych.
    Dlatego chyba lepiej po każdym zapisie danych do pliku, zamykać go, uaktualniając nowe dane (w nagłówku, FAT'cie, itd).
  • Poziom 20  
    Nie rozumiem, piszesz obsługę fat'a i nie były Ci potrzebne funkcje odczytu/zapisu plików? :-)
    Nie chodziło mi aby w odpowiedzi dostać wykład z teorii jak nie blokować zadań używając maszyny stanów, czy sposobu niskopoziomowego dostępu do sektora ;). W swoich projektach głównie opieram na m. stanów, z wyjątkiem zewnętrznych bibliotek np. do obsługi fs, gdzie z reguły jest interfejs posixowy (fread/fwrite) "blokujący". Myślałem, że Twoja implementacja fread/fwrite podobna jest do modelu "is_data_ready" użytego np. w stosie tcp/ip microchipa gdzie np. "sieciowe" read nie jest blokujące.
  • Poziom 22  
    Niestety nie znam tego stosu, więc nie mogę porównać jak tam jest to zrobione.
    Ja odniosłem się tylko do pytania "Czy możliwy jest u mnie dostęp do wielu plików jednocześnie" - więc u mnie nie ma póki co takiej możliwości (i na razie nie jest mi ona potrzebna).
    A sam dostęp do pojedynczego pliku jest - więc funkcja zapis/odczyt istnieje :-)

    Przy maszynach stanów trudno to nazwać klasyczną funkcją, bo ładuje się pewne zmienne (albo czasami nawet nie potrzeba) i po prostu wyzwala określony stan maszyny stanów a ona "dalej robi swoje". W praktyce maszyny wyzwalają kolejne, itd... Jak stosujesz je u siebie, to wiesz sam jak to wygląda.

    A właściwie po co Ci jednoczesny dostęp do wielu plików - i co to w praktyce oznacza? Może teraz ja jakiś wykład poczytam? ;-)
  • Poziom 20  
    "Jednoczesny dostęp" - oczywiście nieprecyzyjnie się wyraziłem, chodziło mi o to czy można w Twojej implementacji "otworzyć" kilka plików na raz (mieć dostęp) i z różnych zadań robić na nich operacje.odczytu/zapisu. Ale abstrakcja "plik" musi być zaimplementowana a z tego co zrozumiałem u Ciebie dostęp do pliku to raczej fizyczny dostęp na warstwie fizycznej nośnika bez wprowadzania abstrakcji pliku.Użycie maszyny stanów do uzyskania wielozadaniowości jest dobrze działającym kompromisem między użyciem gotowego (rt)osa z wywłaszczaniem a.. brakiem jakiejkolwiek wielozadaniowości w ogóle;). Teraz pisze sobie taki projekt z użyciem stosów tcpip, usb,fat wszystko na maszynie stanów oprócz biblioteki fat, która jest klasyczna z blokującym fread/fwrite. Efekt jest taki ze zapis/odczyt z pliku blokuje na chwilkę obsługę tcpip i usb. A że pochwaliłeś się, że piszesz obsługę fat na maszynie stanów myślałem, że może projekt jest wolny i można sobie "pooglądać" a może nawet skorzystać ;).
  • Poziom 22  
    Moja tzw. "biblioteka" jest dopiero w powijakach. Na razie to zaledwie parę funkcji - które mają służyć do płynnego odczytywania plików typu WAV celem generowania sygnałów mowy czy muzyki - w miarę bezobciążeniowo dla procesora.
    W zależności od potrzeb będę dobudowywał stopniowo kolejne funkcjonalności - tak jak ostatnio właśnie dodałem to wyszukiwanie plików w podkatalogach (ale tylko jeden poziom podkatalogów, choć pewnie można by mnożyć, tylko - po co? :-)).
    Pojęcie pliku jako abstrakcji nie jest wprowadzone - choć z czasem pewnie można by dodać jakieś wyższe warstwy. Na razie stosuję jednak ostry minimalizm - tylko to co jest mi potrzebne i raczej wyspecjalizowane do konkretnych potrzeb (rodzajów plików) - np. pliki WAV są sektor po sektorze wczytywanie naprzemiennie do dwóch buforów odbiorczych.

    Generalnie moim celem nie jest tworzenie biblioteki, tylko urządzenia o konkretnych funkcjach - i pod to piszę te fragmenty, które są mi potrzebne, pamiętając jednak, żeby pasowało to do mojej koncepcji maszyn stanów.
  • Poziom 33  
    Cześć,

    Została to wspomniana moja biblioteka do obsługi systemu plików ext2/3/4. Napisałem ta bibliotekę do własnych celów w zeszłym roku. Duża jej część bazuje na implementacji systemu operacyjnego HelenOS. Jednak trzeba było się trochę nagimnastykować aby odpalić ją na mikrokontrolerze ;). Biblioteka umożliwia obsługę:
    - ext2 (większość funkcjonalności)
    - ext3 (indeksowanie katalogów)
    - ext4 (ext3 + extents)

    Biblioteka potrzebuje trochę pamięci FLASH oraz RAM do działania. Zajętość flash na takim cortex-m4 to:
    - około 12KB - wyłączone indeksowanie katalogów i extenty (czyli taki podstawowy system plikow ext2)
    - około 20KB - wszystko włączone

    Zajętość pamięci RAM można określić za pomocą wzoru:
    8 * bsize + 1KB

    Gdzie bsize to wielkość bloku określona w superbloku ext. Wartość ta może mieć wartości
    1KB, 2KB, 4KB ... 64KB

    Dla mikrokontrolerów z małą ilością pamięci w zasadzie jedynym rozwiązaniem jest obsługa bloków o wielkości 1KB (może 2KB). Oczywiście biblioteka może sama dynamicznie dostosować wielkość cache po odczytaniu superbloku. Wymaga to jednak dynamicznej alokacji pamięci. Malloc’a również potrzebują extenty oraz indeksowanie katalogów. Czyli w najprostszym przypadku potrzebne jest około 10KB pamięci RAM i niepotrzebna jest dynamiczna alokacja pamięci. Ilość otwartych plików jest nieograniczona i nie powoduje dodatkowego narzutu (deskryptor pliku to około 20 bajtów). W fatFS deskryptor pliku ma w sobie bufor o rozmiarze 512 bajtów.

    Jakie korzyści w stosunku do FAT:
    - szybkie wyszukiwanie plików w katalogu
    - szybkie fseek
    - szybkie usuwanie dużych plików
    - wiele innych ;)

    Wady:
    - potrzebuje więcej pamięci
    - pod windowsa trzeba zainstalować driver obsługujący ext2

    Aby zbudować biblotekę trzeba mieć:
    Windows:
    - mingw oraz toolchain arm-none-eabi
    - cmake do budowania projektu
    - http://www.ext2fsd.com/ - do obsługi systemu plików ext2/3/4 pod windowsem
    - http://e2fsprogs.sourceforge.net/ext2.html - do tworzenia obrazów ext2/3/4 oraz fsck

    Linux:
    - toolchain arm-none-eabi
    - cmake

    Jak ktoś zdecyduje się na użycie mojej biblioteki to chętnie służę pomocą. Jak ktoś zechce testować bibliotekę to czekam też na uwagi (krytykę).
  • Poziom 22  
    Mam takie małe pytanko...
    W procedurze wyszukującej pliki po nazwie wykorzystuję raz nazwę
    zapisaną bezpośrednio w pamięci programu - ładowaną jako wskaźnik const, innym razem jest to nazwa dynamicznie zbudowana (wygenerowana) i umieszczona w buforze 11-bajtowym.
    W tej chwili istnieje flaga, która wskazuje procedurze wyszukującej z którego źródła informacji ma korzystać - czy z RAM czy z ROM - i w zależności od tego jest wybierany odp. blok programu.
    Trochę mnie jednak złości to powielanie kawałka programu, w sytuacji kiedy jest on identyczny, a różni się tylko nazwą wskaźnika/bufora.
    Z tego co wiem, to jeśli wskaźnik jest zadeklarowany jako const, to nie można nim wskazać bufora w pamięci RAM i odwrotnie (kompilator krzyczy) - a to byłoby idealne rozwiązanie - przeładować po prostu wskaźnik raz adresem z RAM'u, a raz z ROM'u wykorzystując ten sam program.

    Sama linia porównania dla obu wariantów wygląda tak:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Da się to jakoś zoptymalizować?
  • Specjalista - Mikrokontrolery
    Jado_one napisał:
    Z tego co wiem, to jeśli wskaźnik jest zadeklarowany jako const, to nie można nim wskazać bufora w pamięci RAM i odwrotnie (kompilator krzyczy) - a to byłoby idealne rozwiązanie - przeładować po prostu wskaźnik raz adresem z RAM'u, a raz z ROM'u wykorzystując ten sam program.

    No to ja nie wiem skąd Ty to wiesz, bo wcale tak nie jest...

    Kod: C
    Zaloguj się, aby zobaczyć kod


    Myślę że to wyjaśnia jakie są dozwolone kombinacje, ale jeśli nie, to idea jest taka:
    - wskaźnikiem "const T *" można wskazywać na cokolwiek,
    - wskaźnikiem "T *" (czyli nie-const), można wskazywać tylko na obiekty które są nie-const.

    Błąd pewnie masz dlatego, że Twoja funkcja ma w prototypie "char *", choć pewnie równie dobrze mogłaby mieć "const char *", bo nie modyfikuje tego na co wskazuje wskaźnik. Dochodzimy tutaj więc do tzw. "const correctness", co generalnie wiele osób ma "w głębokim poważaniu" - np. twórcy biblioteki SPL (czyli niby poważna firma ST) o czymś takim nie słyszeli nigdy...

    http://en.wikipedia.org/wiki/Const-correctness

    P.S. W rozważaniach tych celowo pominąłem kwestię "volatile", dla którego obowiązują podobne zasady co dla "const", z tym że dochodzą jeszcze kombinacje - nie ma co zaciemniać obrazu.

    4\/3!!
  • Poziom 22  
    Ano widzisz - wiedziałem, że coś mi umyka. Dzięki za wyjaśnienie.
    Pamiętam, że swojego czasu (dość dawno temu) coś takiego próbowałem, ale że kompilator 'krzyczał', to uznałem, że tak nie można i już - i dalej nie drążyłem tematu.
    Dopiero teraz mi to wyszło w praktyce, że coś takiego by mi się przydało, więc się spytałem :)
    No to dobrze - będę mógł się pozbyć niepotrzebnie nadmiarowego kodu - a i na przejrzystości całość zyska.

    Edit:
    A jak się pozbyć tego komunikatu?: sound.c:565:17: warning: assignment discards 'volatile' qualifier from pointer target type [enabled by default]
  • Poziom 33  
    Taka saka historia jak z const. Zadeklaruj wskaźnik na zmiena volatile. Mozesz to zrobic na 2 sposoby:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Albo żeby była analogia do posta Freddiego o const:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Jak wiesz co robisz to możesz użyć rzutowania na typ taki jaki zadeklarowales aby pozbyć sie warninga. W C++ używa się const_cast do usuwania modyfikatorów const i volatile.
  • Specjalista - Mikrokontrolery
    GrzegorzKostka napisał:
    Jak wiesz co robisz to możesz użyć rzutowania na typ taki jaki zadeklarowales aby pozbyć sie warninga. W C++ używa się const_cast do usuwania modyfikatorów const i volatile.

    W przypadku "const" i kodu na którym panujesz w całości nie ma praktycznie NIGDY potrzeby usuwania tego atrybutu - wystarczy poprawić kod, funkcje, prototypy, ... - co tam potrzeba. W przypadku "volatile" można się bawić w takie usuwanie jedynie w przypadku "standardowych funkcji" typu memcpy(), printf() itp.

    4\/3!!
  • Poziom 22  
    Coś dziwnie się program zachowuje...

    Może pytanie podstawowe - czy tablice potrzeba deklarować jako
    volatile? Bo tak z 'przyzwyczajenia' mam i dopiero jak zdejmę to volatile z tablicy to kompilator daje spokój.

    Próbowałem zrobić też coś takiego : const volatile u08 *FileNamePtr;
    i dzieje się rzecz dziwna - kompilator zaczyna mi wyrzucać błąd w zupełnie innym miejscu programu - przy innych deklaracjach, innych funkcji (gdzie też występują wskaźniki) - wygląda to jak za krótka kołderka - ja cie podciągnę tutaj, to tam coś wyskoczy.
  • Specjalista - Mikrokontrolery
    Jado_one napisał:
    czy tablice potrzeba deklarować jako
    volatile? Bo tak z 'przyzwyczajenia' mam i dopiero jak zdejmę to volatile z tablicy to kompilator daje spokój.

    Jak są współdzielone między programem a przerwaniami (albo występuje dostęp "wielowątkowy" - w różnych znaczeniach), to tak.

    Jado_one napisał:
    wygląda to jak za krótka kołderka - ja cie podciągnę tutaj, to tam coś wyskoczy.

    Dodanie const-correctness do istniejącego programu w którym do tej pory kwestia ta była pomijana to więcej niż 30 sekund roboty (; Generalnie często jest tak, że trzeba poprawić 95% funkcji operujących na wskaźnikach... Z tego powodu lepiej od początku robić porządnie (;

    Generalnie z volatile jest problem, dlatego lepiej unikać globalnych zmiennych tego typu (oczywiście globalnych zmiennych w ogóle również należy unikać <: ), bo właśnie tak się potem dzieje... Jak nie podasz konkretnego problemu, komunikatu, kodu, ..., to ciężko powiedzieć coś mądrego.

    4\/3!!
  • Poziom 22  
    Komunikat kompilatora był taki sam jak wyżej - że nie pasuje mu Volatile - ale przeniósł się na inną funkcję, która owszem korzystała ze wskaźnika, ale właśnie const, bo operowała na tablicach z ROM. Ani tam nie było żadnych tablic w RAM'ie, ani volatile, więc człowiek głupieje.
    Dla ciekawości dodałem mu nawet volatile po const - i wtedy warning zniknął, ale po wgraniu program działał niepoprawnie.
    Ale takie działanie na pałę, to bez sensu - albo się wie co robi, albo nie robi wcale :-)
    A tu wychodzi, że człowiek kombinuje jak się pozbyć warningów kompilatora zamiast skupić się nad programem.
    Czasami - jak zauważyłem - kompilator sygnalizuje błąd, ale nie do końca mu wychodzi utrafić we właściwe miejsce w kodzie źródłowym - łatwo to zauważyć jak się zapomni dodać klamrę na końcu funkcji - wtedy skacze niżej do ostatniej klamry w pliku i tam sygnalizuje błąd (a był dużo wyżej).
    Stąd też podejrzewam, że błąd nadal dotyczył tego samego miejsca, tylko "wylazł" gdzie indziej.
    A ja tylko chciałem przypisać do wskaźnika const adres bufora w pamięci ram.
    Jak bufor ma volatile - jest warning.
    Jak zdejmę - nie ma warninga (ale nie sprawdzałem czy dobrze działa, czy przyjmuje poprawny adres, itd...)
    Jak dodam volatile do deklaracji wskaźnika const - to błąd znika, ale 'wylatuje' w innym miejscu programu.
    Coś tu się nie lubi.
    Póki co program działa, więc nie ma zmartwienia - to była tylko mała próba optymalizacji :-)
    Funkcja z której "wyleciał" w/w błąd tak wygląda:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Wyświetla komunikat w określonym miejscu pamięci GLCD (bufor RAM), pobrany z z ROM'u. Prosta jak drut :-)
  • Specjalista - Mikrokontrolery
    Jado_one napisał:
    albo się wie co robi, albo nie robi wcale

    Po prostu popełniłeś bardzo poważny błąd w założeniach. "const" nie służy do wskazania, że dany obiekt jest we flash - "const" przy wskaźniku mówi tylko i wyłącznie tyle, że funkcja przyjmująca taki wskaźnik nie będzie modyfikowała tego na co tenże wskazuje. Tym samym wskaźnik na "const" może wskazywać na zmienną na stosie, zmienną globalną, stałą na stosie czy na globalną stałą, choć tylko ta ostatnia ma szansę być umieszczona w pamięci flash.

    Przykładowo funkcja którą chyba każdy zna:
    void * memcpy(void *destination, const void *source, size_t num);

    Funkcja ta nie służy do kopiowania z flash do RAM, tylko "skądkolwiek" do "miejsca które można zapisywać".

    Stosując const-correctness myślę że conajmniej 80% używanych w prototypach funkcji wskaźników może być "const" - i własnie o to chodzi. Jeśli funkcja tylko czyta, a nigdy nie zapisuje, to wskaźnik powinien być "const".

    Jado_one napisał:
    Czasami - jak zauważyłem - kompilator sygnalizuje błąd, ale nie do końca mu wychodzi utrafić we właściwe miejsce w kodzie źródłowym - łatwo to zauważyć jak się zapomni dodać klamrę na końcu funkcji - wtedy skacze niżej do ostatniej klamry w pliku i tam sygnalizuje błąd (a był dużo wyżej).

    Nie masz racji... W C można definiować funkcję wewnątrz innej funkcji - kompilator wskazuje gdzie jest błąd składniowy, a jest nim brak ostatniej klamry, a nie gdzie jest błąd logiczny (tego jeszcze nie potrafi (; ). Sprawdź sam - postaw klamrę na końcu, a błąd zniknie (;

    Jado_one napisał:
    Jak bufor ma volatile - jest warning.
    Jak zdejmę - nie ma warninga (ale nie sprawdzałem czy dobrze działa, czy przyjmuje poprawny adres, itd...)

    Volatile przy wskaźniku w prototypie funkcji ma mały sens, ale długo byłoby tłumaczyć dlaczego. Niemniej jednak z punktu widzenia semantyki ostrzeżenie jest prawidłowe. Z tego względu właśnie lepiej nie mieć globalnych obiektów volatile <: W takich przypadkach zwykle możesz sobie owe "volatile" od-rzutować, jednak warto się zastanowić, czy aby na pewno jest konieczne - współdzielenie jest tylko wtedy, gdy w przerwaniach i w programie głównym odwołujesz się przez NAZWĘ - jeśli w dowolnym z tych dwóch miejsc (lub w obydwóch) odwołujesz się przez wskaźnik (np. w pętli głównej przekazujesz tylko wskaźnik do jakiejś funkcji), to w zasadzie nie jest to współdzielenie i "volatile" możesz sobie spokojnie darować. Oczywiście kwestia data-race jest na Twojej głowie, ale to chyba oczywiste i volatile i tak tego nie załatwi za Ciebie. To o czym piszę dotyczy oczywiście tylko buforów, a nie "flag".

    Dodatkowo dochodzi kwestia tego jak działa optymalizator - zwykle dajesz volatile przy flagach, żeby móc sobie zrobić coś takiego:

    while (flaga == 0) { czekaj(); }

    W przypadku tablic raczej czegoś takiego nie robisz, a jak przekazujesz tablicę do jakiejś funkcji przez wskaźnik, to w ogóle problem nie istnieje. No chyba że masz funkcję która przyjmuje wskaźnik na flagę i w środku chce sobie zrobić taką pętlę jak wyżej, to oczywiście wtedy volatile jest konieczne, jednak taki kod jest niezbyt mądry...

    4\/3!!
  • Poziom 22  
    Zrobiłem eksperyment - a nóż coś w moim programie się chrzani - i napisałem
    minimum-minimorum - tylko kilka deklaracji i dwa przypisania do wskaźników.
    Tylko plik main.c:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    I niestety warning nadal występuje: main.c:37:16: warning: assignment discards 'volatile' qualifier from pointer target type [enabled by default]

    Oczywiście tylko przy próbie przypisania tablicy w RAM do wskaźnika.

    Co tu w takim razie należy poprawić, aby zadziałało?
  • Specjalista - Mikrokontrolery
    Jado_one napisał:
    I niestety warning nadal występuje: main.c:37:16: warning: assignment discards 'volatile' qualifier from pointer target type [enabled by default]

    Oczywiście tylko przy próbie przypisania tablicy w RAM do wskaźnika.

    To nie ma znaczenia czy ona jest w RAM - liczy się to, że na obiekt volatile chcesz wskazywać wskaźnikiem nie-volatile.

    "Poprawić" można albo usuwając volatile z definicji obiektu, albo używając wskaźnika z kwalifikatorem volatile. No i jeszcze jest rzutowanie, ale tego raczej nie nazywałbym "poprawką" (;

    A tak swoją drogą, to te bufory nie powinny być u Ciebie dłuższe? 8 + 3 + '.' + '\0' to 13, a nie 11.

    4\/3!!
  • Poziom 22  
    OK - Czyli nic więcej nie można tu zrobić ponad to co już robiłem.

    Pytanie dlaczego kompilacja programu sypie się (w innym miejscu) po dodaniu volatile do definicji wskaźnika - przy pełnym programie.
    Jak to teraz namierzyć? Czemu kompilator "głupieje"?
    Generalnie program mi działa i nie widzę, żeby coś się złego w nim działo - a działa całymi dniami (robi za odtwarzacz muzyki).

    No nic - zrobimy eksperyment. Wykomentuję tą funkcję która wysypuje się po dodaniu volatile do wskaźnika (i tak jej teraz nie używam, jest tylko zdefiniowana) i zobaczymy czy błąd przejdzie do innej funkcji czy też zniknie.

    Co do nazwy pliku - kropka tak naprawdę nie występuje w nazwie pliku - jest "wirtualna", a zera na końcu nie potrzebuję, bo liczę ilość znaków licznikiem - do 11.
  • Specjalista - Mikrokontrolery
    Jado_one napisał:
    Pytanie dlaczego kompilacja programu sypie się (w innym miejscu) po dodaniu volatile do definicji wskaźnika - przy pełnym programie.
    Jak to teraz namierzyć? Czemu kompilator "głupieje"?

    Bo ta funkcja pewnie wywołuje kolejną, przekazując jej swój wskaźnik "volatile", gdy tymczasem oczekiwany jest wskaźnik na "nie-volatile".

    Kod: C
    Zaloguj się, aby zobaczyć kod


    Uwierz mi na słowo - nie chcesz mieć buforów które są volatile (; Jeśli to są jakieś małe rzeczy, to prościej je skopiować do jakiejś tymczasowej tablicy "nie-volatile" i dopiero to przekazywać do funkcji. Jeśli są duże i kopiowanie nie wchodzi w grę, to najlepiej chwilę przemyśleć sprawę i jeśli się da pozbyć się volatile całkowicie. Albo po prostu rzutować, tyle że jeśli z rzutowaniem działa, to bez rzutowania i bez volatile pewnie też by działało - bo to przecież wychodzi na jedno.

    4\/3!!
  • Poziom 22  
    Freddie Chopin napisał:

    Bo ta funkcja pewnie wywołuje kolejną, przekazując jej swój wskaźnik "volatile", gdy tymczasem oczekiwany jest wskaźnik na "nie-volatile".
    4\/3!!

    Dokładnie :-) Za wcześnie wtedy "odpadłem" z dalszego "wnikania", bo wydawalo mi się że kompilator "głupieje", a tymczasem było jedno zapomniane odwołanie do tego wskaźnika i zmiana jego definicji dawała taki błąd.

    No to się pocieszyłem, że w programie nie jest tak naśmiecone ;-)

    No dobrze - ale jak ja mam maszynę stanów która ma wiele elementów typu case:.
    I w jednym z takich case: są wpisywane dane do tej tablicy -> nazwa pliku, a potem maszyna ta wychodzi ze swego stanu wyzwalając jednocześnie stan drugiej maszyny, a ta druga maszyna stanów bierze w jednym ze swoich stanów zawartość tej tablicy (poprzez wskaźnik), porównuje z kolejnymi danymi odczytanymi z SD i znajduje sobie (albo nie) poszukiwany plik. A potem robi już dalsze swoje zadania.

    Jak w tej sytuacji przekazać dane inaczej niż przez tablice globalną (obie maszyny mogą być w innych plikach).
  • Specjalista - Mikrokontrolery
    Ale te tablice nie muszą być volatile, ponieważ dostęp do nich nie jest "wielowątkowy".

    4\/3!!
  • Poziom 22  
    No jeśli chodzi o te na nazwy plików/katalogów to chyba rzeczywiście nie muszą.
    Przerwania po nich 'nie piszą'.

    A czym 'grozi' zadeklarowanie ich jak volatile?

    Widziałem w tym fragmencie, co podawałeś z wiki, że zmienne zadeklarowane jako volatile są jakoś specjalnie traktowane przez kompilator pod względem odczytu/zapisu. Na czym to polega?

    Hmm...może za bardzo przeciągam dyskusję? ;-)


    Edit: No dobra odświeżyłem pamięć z internetu :-)
    Eh......Po dwóch latach przerwy pewne rzeczy się ulatniają.....
  • Specjalista - Mikrokontrolery
    Ehh... Napisałem długiego posta i coś mi się stało z przeglądarką... /;

    Jado_one napisał:
    No jeśli chodzi o te na nazwy plików/katalogów to chyba rzeczywiście nie muszą.
    Przerwania po nich 'nie piszą'.

    Nie ma najmniejszej potrzeby żeby były "volatile" - ten modyfikator potrzebny jest tylko i wyłącznie przy dostępie "wielowątkowym" (przerwania i pętla główna lub wątki RTOSa [i przerwania]). Maszyna stanów pracująca w jednym wątku jest oczywiście jednowątkowa.

    Jado_one napisał:
    A czym 'grozi' zadeklarowanie ich jak volatile?

    Tym że kod ich używający nie będzie zoptymalizowany (np. buforowanie wartości w rejestrach rdzenia), oraz takimi problemami jak teraz obserwujesz.

    Jado_one napisał:
    Widziałem w tym fragmencie, co podawałeś z wiki, że zmienne zadeklarowane jako volatile są jakoś specjalnie traktowane przez kompilator pod względem odczytu/zapisu. Na czym to polega?

    Modyfikator "volatile" sprawia, że kompilator wykonuje KAŻDY dostęp do zmiennej, w dokładnie takiej kolejności jaką dałeś w kodzie, niezależnie od tego czy z punktu widzenia optymalizacji taki dostęp ma sens. Dwa przykłady:

    zmienna = 1;
    zmienna = 2;
    zmienna = 3;

    Bez "volatile" kompilator wykona tylko ostatni zapis, bo dwa pierwsze nie mają sensu. Z "volatile" - każdy zapis zostanie wykonany.

    while (flaga == 0) {}

    Bez "volatile" kompilator sprawdzi zmienną tylko jeden raz i przejdzie do nieskończonej pętli jeśli warunek był prawdziwy (bo nie ma potrzeby sprawdzać tej zmiennej wielokrotnie, skoro w pętli nie jest ona modyfikowana). Z "volatile" - każdy obieg pętli spowoduje faktyczne sprawdzenie zmiennej.

    Przy okazji to chyba wyjaśnia, dlaczego rejestry w mikrokontrolerze są zadeklarowane z "volatile" (a rejestry read-only - "const volatile").

    W skrócie - idea jest taka, że "volatile" informuje kompilator, że dany obiekt w pamieci może ulec zmianie w "nieznany sposób" i w "nieznanym momencie", a do tego jeszcze asynchronicznie.

    Na koniec ciekawostka - w takim oto fragmencie kodu:

    zmienna;

    kompilator wykona odczyt jeśli zmienna jest zadeklarowana jako "volatile", w przeciwnym wypadku fragment ten w ogóle będzie zignorowany i pewnie w gratisie będzie jeszcze ostrzeżenie o "statement without effect". Hint - ten konstrukt przydatny jest w świecie mikrokontrolerów do rejestrów które trzeba odczytać, ale których wartość w ogóle nie ma znaczenia (np. gdy wyczyszczenie flagi polega na odczycie rejestru statusowego albo innego).

    4\/3!!
  • Poziom 22  
    Serwer Elektrody chyba przeciążony - długo myśli zanim wejdzie w "pisanie nowego posta".
    Ale wracając do kodowania....

    Mam u siebie taki fragment:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Sytuacja taka jak piszesz wyżej - trzeba odczytać tylko rejestr, żeby skasować flagi.
    W dalszej części programu jeden tych rejestrów jest używany, a rola drugiego kończy się na powyższym.
    Kompilator natomiast "pluje się" warningiem, że "nieużywana zmienna".
    Myślisz, że wystarczyłoby samo: I2C1->SR2; ?
    Sytuacja dzieje się w przerwaniu.

    Edit: Dokonałem stosownych zmian i wydaje się, że działa :-)

    Widzę, że w pliku core_cm4.h jest taki zapis:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    To stąd kompilator "wie" jak traktować rejestry systemowe (jako volatile).