Skocz do zawartości

Zapis danych uint8_t do tablicy uint32_t


radek04

Pomocna odpowiedź

Cześć,

z uwagi na ograniczoną ilość pamięci w STM32H7, w celu pełnego wykorzystania jej zasobów, chciałbym współdzielić jeden duży bufor do dwóch zadań (gdy pierwsze zadanie się skończy, zaczyna się drugie, więc dane "nie kłócą się ze sobą").

Normalnie korzystam z tablic: uint32_t tab1[119256] w jednym programie oraz uint8_t tab2[278528] w drugim ( (round_No=4096)*(liczba_rejestrów=68) ).
Przyszedł czas połączyć programy i okazało się, że pamięci brakuje. Stąd pomysł, by wykorzystać jeden bufor (tablicę) do obu celów.
Mam jednak problem z poprawnym zapisem danych. Próbowałem bardzo różnych kombinacji, może nie będę ich wymieniał, by nie narzucać (złych) pomysłów.

Proszę o pomoc, jak poprawnie skonstruować zapis do tablicy, a następnie zapis tablicy na kartę. Dobrze byłoby, gdybym w pełni wykorzystał tab1 i mógł użyć 2 razy więcej komórek 8-bitowych niż jest komórek 32-bitowych, ale jeśli zostanie mi do dyspozycji owe 119256, to też da radę.

Poniżej krytyczne fragmenty programu, czyli akwizycja danych do tablicy oraz ich zapis na kartę pamięci.

HAL_I2C_Mem_Read_DMA(&hi2c1, MPU9250_ACC_ADDRESS_A, MPU9250_ACCEL_XOUT_H, 1, &tab[round_No*68 + 0*14], 14); //14 registers measurement
HAL_I2C_Mem_Read_DMA(&hi2c2, MPU9250_ACC_ADDRESS_B, MPU9250_ACCEL_XOUT_H, 1, &tab[round_No*68 + 1*14], 14); //14 registers measurement
HAL_I2C_Mem_Read_DMA(&hi2c3, MPU9250_ACC_ADDRESS_C, MPU9250_ACCEL_XOUT_H, 1, &tab[round_No*68 + 2*14], 14); //14 registers measurement
HAL_I2C_Mem_Read_IT (&hi2c4, MPU9250_ACC_ADDRESS_D, MPU9250_ACCEL_XOUT_H, 1, &tab[round_No*68 + 3*14], 14); //14 registers measurement

HAL_I2C_Mem_Read_DMA(&hi2c1, BMP280_ADDRESS_A, BMP280_REG_BAR_MSB, 1, &tab[round_No*68 + 4*14+0*3], 3); //3 registers measurement
HAL_I2C_Mem_Read_DMA(&hi2c2, BMP280_ADDRESS_B, BMP280_REG_BAR_MSB, 1, &tab[round_No*68 + 4*14+1*3], 3); //3 registers measurement
HAL_I2C_Mem_Read_DMA(&hi2c3, BMP280_ADDRESS_C, BMP280_REG_BAR_MSB, 1, &tab[round_No*68 + 4*14+2*3], 3); //3 registers measurement
HAL_I2C_Mem_Read_IT (&hi2c4, BMP280_ADDRESS_D, BMP280_REG_BAR_MSB, 1, &tab[round_No*68 + 4*14+3*3], 3); //3 registers measurement

f_write(&fil, readings_from_registers, sizeof(tab), &numread);

W tej drugiej części (BMP280) dane pobieram z 3 rejestrów 8-bitowych. Ponieważ może być to kłopotliwe, mogę ograniczyć się do 2 lub rozszerzyć do 4, w którym jeden będzie zawsze zerami.

Każda konstruktywna uwaga będzie na dla mnie cenna.

Link do komentarza
Share on other sites

Unia, oczywiste. Tak to jest, gdy się korzysta tylko z najprostszych mechanizmów, nie mając większego pojęcia o programowaniu.

Co do grzyba - fakt, wygląda to dziwnie. Temat jest bardziej złożony. W różnych fragmentach przedstawiałem go w innych tematach na forum. Czasami bez żadnej odpowiedzi. 

W skrócie - tablica ta służy do akwizycji danych z różnych czujników. Dopóki zapisuję je w tablicy, pomiary są robione ze stałym okresem próbkowania. W momencie zapisu na kartę ciągłość pomiaru zostaje zaburzona. Stąd potrzeba jak największej tablicy, by przez jak najdłuższy czas pobierać spójne dane. 

Próbowałem różnych sposobów np. Z 2 buforami używanymi na zmianę, z DMA i in., ale zawsze podczas zapisu na kartę następuje krótka przerwa w akwizycji danych. Pewnie da się to jakoś lepiej zrobić, ale z moimi umiejętnościami programistycznymi się nie powiodło. 

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Być może czegoś nie rozumiem, albo całości problemu. W moim mniemaniu chodzi o to żeby można było ten sam obszar traktować jako ciąg (tablicę) uint32_t lub uint8_t. Ja bym zrobił to np tak:

uint32_t tab_32[119256] = {0};
uint8_t* tab_8;
tab_8 =   &tab_32[0];
 
tab_32[0] = 0; // dostęp do obszaru tablicy jako 32 bit
tab_32[1] = 65536;
tab_8[2]  = 1; // zapis 3ciego bajtu tab_8 - dostęp do obszaru tablicy jako 8 bit

while (tab_32[0] == tab_32[1]) {} // wieczna pętla jeżeli dane 32 bit są takie same

 

Poprzez dereferencję z kwadratowymi nawiasami jest dostęp do danych. Dalsza sztuka, to jak obliczać offset do danych, to będzie wyrażenie w kwadratowych nawiasach - być może to jest głównym problemem? Choć wg mnie nie powinno. Z prostej zależności wielkości uint32_t i uint8_t danych traktowanych jako 8bit może być 4 razy więcej.

Edytowano przez virtualny
Link do komentarza
Share on other sites

Sposobów jest mnóstwo. Możesz traktować tablicę jako obszar pamięci i operować wskaźnikami (co w C jest dość naturalne). Możesz stworzyć unię, która czasem jest wygodniejsza (też naturalna w C). Nie ma lepszego czy gorszego sposobu... a raczej jest, ale zależy od konkretnego zastosowania.

Link do komentarza
Share on other sites

Nie jestem biegły w C - gdybyś mógł zastąpić/przełożyć mój przykład na unię, to może bym się czegoś nauczył.

Dla mnie bardzo ważne jest doprowadzenie wyrażenia do tablicy z kwadratowymi nawiasami zamiast cudaczenia z gwiazdkami. Wtedy widzę i wiem co robię, a jak są gwiazdki, to widzę ciemność 🙂

  • Lubię! 1
Link do komentarza
Share on other sites

To się naucz tych gwiazdek, bo bez tego w C raczej nie ruszysz poza miganiem ledą.

union {
  uint8_t a[16];
  uint16_t b[8];
  uint32_t c[4];
  } d;

a potem

d.a[1] = coś;

Ogólnie podręczniki do C są dostępne odkąd pamiętam (czyli od czasu kiedy internet składał się z dwóch komputerów).

  • Pomogłeś! 1
Link do komentarza
Share on other sites

Podręcznik do C posiadam np książkę Helionu 900 stron Język C w pigułce. Zajrzałem tam do opisu unii i niestety nie za wiele zrozumiałem. Teraz po Twoim przykładzie zrozumiałem sens tej unii - wielkie dzięki. Jakimś wielkim mistrzem C to ja już nigdy nie będę. Chodzi o to że np wyrażenie:

(*(u16*)((uint32_t)(0x60000000)))

w porównaniu do :

tab[0]

 

Zanim zrozumiem to pierwsze to z 5 razy dłużej to potrwa. A drugie wyrażenie rozumiem natychmiast (pewnie skrzywienie zakorzenione po delphi erh).

Dzięki za przykład.

Link do komentarza
Share on other sites

Wskaźniki to podstawa, operacja na tablicach również odbywa się za pomocą wskaźnika tylko że tej gwiazdki nie widać. Zamiast Unii możesz zastosować taki zapis 

int main()
{
    
  uint32_t i = 0xffaacc22;
  uint8_t * p = (uint8_t*)&i;
    
    
    printf("%02X %02X %02X %02X", p[3],p[2],p[1],p[0]);

    return 0;
}

 

  • Lubię! 1
Link do komentarza
Share on other sites

1 godzinę temu, virtualny napisał:

Nie jestem biegły w C - gdybyś mógł zastąpić/przełożyć mój przykład na unię, to może bym się czegoś nauczył.

Dla mnie bardzo ważne jest doprowadzenie wyrażenia do tablicy z kwadratowymi nawiasami zamiast cudaczenia z gwiazdkami. Wtedy widzę i wiem co robię, a jak są gwiazdki, to widzę ciemność 🙂

No to chyba mamy identycznie z tymi światecznymi motywami 😉

Ja zaczynałem od "Symfonii C++" i teraz się zastanawiam, czy warto kupić książkę do ANSI C, czy to niewiele zmieni. W sumie to C używam jedynie do STM32...

Link do komentarza
Share on other sites

Dopiszę jeszcze jedną odpowiedź, żeby zostało dla potomnych.

Nie wiem, czy unia jest tu dobrym rozwiązaniem, bo liczba elementów w dwóch opisanych tablicach jest różna:

Cytat

uint32_t tab1[119256] w jednym programie oraz uint8_t tab2[278528] w drugim

Jeżeli chcemy współdzielić ten sam bufor pamięci dla dwóch różnych tablic (pod warunkiem oczywiście, że nie będziemy chcieli skorzystać z obu naraz, bo to oczywiście nie zadziała), to możemy zrobić coś, co się ładnie nazywa rzutowaniem typów.

Pierwsza tablica jest typu uint32_t[]. W C tablica jest równoważna wskaźnikowi (czyli inaczej mówiąc adresowi pamięci) do pierwszego jej elementu. Możemy więc zrobić następującą sztuczkę:

  • Pobrać adres pierwszego elementu tablicy
  • Powiedzieć kompilatorowi, że chcemy pamięć od tego miejsca potraktować jako tablicę drugiego typu (=wskaźnik na pierwszy element tablicy drugiego typu)
  • Od tego momentu możemy korzystać z tablicy drugiego typu.

Kod jest banalny:

// Gdzieś mamy zadeklarowaną pierwszą tablicę, na przykład tak:
uint32_t tab1[119256];

// Rzutujemy typ wskaźnika z tablicy elementów uint32_t na tablicę elementów uint8_t
uint8_t * tab2 = (uint8_t *)tab1;

// Teraz możemy korzystać z drugiej tablicy. Uwaga: obie tablice operują na tym samym
// obszarze pamięci, więc korzystanie z obu naraz skończy się zamazywaniem danych!
tab2[15] = 12;

Oczywiście trzeba sprawdzić, czy obie tablice zmieszczą się w tym samym miejscu. Jeżeli pierwszą tablicą jest tab1, to zajmuje ona 477024 bajtów (4 * 119256) i wtedy możemy tę pamięć wykorzystać na drugą, która zajmuje 278528 bajtów. W drugą stronę się już nie uda (to znaczy kompilator pozwoli, ale próba przypisania elementów o odpowiednio wysokich indeksach skończy się... w taki lub inny sposób źle (od zamazania istniejących danych do crasha aplikacji).

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.