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

Rzutowanie unsigned char na short/int/float w ATSAM7/9 ARM – czy kod jest poprawny?

at91sam79 13 Paź 2011 22:44 2063 21
REKLAMA
  • #1 10024778
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    Coś w moim kodzie powoduje, że po jakims czasie mój ARM się wiesza. W programie wykorzystuję między inny rzutowanie z tablicy unsigned char na short, int i float oraz na operację odwrotną z zmiennych typu shortm int i float tworzę tablicę unsigned char. I tu właśnie mam pytanie, bo nie jestem pewiem czy robię to poprawnie. Oto kod, którego używam na AT91SAM256EK oraz AT91SAM9260.

    short ConvertToLSBShort(unsigned char * a_array, int a_StartIndex)
    {
        unsigned char frame[2] = {a_array[a_StartIndex], a_array[1 + a_StartIndex]};
        return ((short*)frame)[0];
    }
    
    int ConvertToLSBInt(unsigned char * a_array, int a_StartIndex)
    {
        unsigned char frame[4] = {a_array[a_StartIndex], a_array[1 + a_StartIndex], a_array[2 + a_StartIndex], a_array[3 + a_StartIndex]};
        return ((int*)frame)[0];
    }
    
    float ConvertToLSBSingle(unsigned char * a_array, int a_StartIndex)
    {
        unsigned char frame[4] = {a_array[a_StartIndex], a_array[1 + a_StartIndex], a_array[2 + a_StartIndex], a_array[3 + a_StartIndex]};
        return ((float*)frame)[0];
    } 


    a tu operacje odwrotne

    int PutShortIntoLSBArray(unsigned char * a_array, int a_StartIndex, short value)
    {
        unsigned char tempArray[2];
        ((short*)tempArray)[0] = value;
        a_array[a_StartIndex]   = tempArray[0];
        a_array[++a_StartIndex] = tempArray[1];
        return ++a_StartIndex;
    }
    
    int PutIntIntoLSBArray(unsigned char * a_array, int a_StartIndex, int value)
    {
        unsigned char tempArray[4];
        ((int*)tempArray)[0] = value;
        a_array[a_StartIndex]   = tempArray[0];
        a_array[++a_StartIndex] = tempArray[1];
        a_array[++a_StartIndex] = tempArray[2];
        a_array[++a_StartIndex] = tempArray[3];
        return ++a_StartIndex;
    }
    
    int PutSingleIntoLSBArray(unsigned char * a_array, int a_StartIndex, float value)
    {
        unsigned char tempArray[4];
        ((float*)tempArray)[0] = value;
        a_array[a_StartIndex]   = tempArray[0];
        a_array[++a_StartIndex] = tempArray[1];
        a_array[++a_StartIndex] = tempArray[2];
        a_array[++a_StartIndex] = tempArray[3];
        return ++a_StartIndex;
    }


    Zastanawia mnie kwestia wyrównania struktury w której jest tablica unsigned charów, którą przekazuję do tych funkcji. Oto struktura:

    typedef struct
    {
    	unsigned char MBRxBuffer[MB_MAX_FRAME_SIZE];
    	unsigned char MBTxBuffer[MB_MAX_FRAME_SIZE];
    	unsigned char MBCmdBuffer[MB_MAX_FRAME_SIZE];
    	volatile int MBRxBufferLenght;
    	volatile int IsMBFrameReady;
    
    }MbVbFrame;


    Po rozparsowaniu przychodzącej ramki danych, obliczeniu CRC na przechodze w kodzie do wyciągania różnego typu danych z tablicy MBRxBuffer.

    Co o tym myślicie?
  • REKLAMA
  • #2 10032696
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    ARM7 nie obsługuje dostępu niewyrównanego, ARM9 zapewne też nie. To co robisz w Swoim kodzie działa tylko dlatego, że tablica char którą tworzysz jest lokowana na stosie, który przy wejściu do funkcji jest zawsze wyrównany do czterech lub ośmiu bajtów.

    Zamiast rzutowania po prostu poskładałbyś te bajty ORami i byłoby poprawniej, może nawet przestałoby się wykładać, ale w to akurat wątpię i sądzę, że problem masz gdzie indziej.

    4\/3!!
  • #3 10039078
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    W którym miejscu jest to niewyrównanie? Tablice, które tworze mają parzystą liczbę bajtów - 2 lub 4.

    Pozdrawiam
  • REKLAMA
  • #5 10039295
    at91sam79
    Poziom 9  
    Posty: 42
    Jak do tej pory myślałem że wyrównanie dotyczy głównie struktur i to takich które chcemy używać do przesyłania danych np. po RS232, zeby się nie okazało że ślemy jakieś głupoty wynikające właśnie z braku wyrównania.

    Możesz takim razie wyjaśnić, co u mnie powoduje to niewyrównanie?
    Poza tym zauważyłem, że ta moja funkcja do konwersji tablicy charów na floata działa na AT91SAM9260 a nie działa na At91SAM256. Zmieniłem te funkcje na zwykłe przesuwanie bajtów i operację "^" ale faktycznie to nie to wiesza mój procesor. Poza tym jak można inaczej przekonwertować tablicę charów na zmienną float? Mam urządzenie które śle mi floata w MSB jako tablicę 4 bajtów.

    Pozd
  • #6 10039324
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    at91sam79 napisał:
    Możesz takim razie wyjaśnić, co u mnie powoduje to niewyrównanie?

    Wg mnie u Ciebie te dane są wyrównane, ale tylko przypadkiem - po wejściu do funkcji stos jest wyrównany do czterech lub ośmiu, więc tablica którą tworzysz jako jedyną na stosie mniejszą niż cztery bajty (bo po zachowaniu jakichś rejestrów dalej stos będzie wyrównany). Stąd właśnie moje przekonanie, że nie w tym jest problem.

    Cytat:
    Poza tym zauważyłem, że ta moja funkcja do konwersji tablicy charów na floata działa na AT91SAM9260 a nie działa na At91SAM256. Zmieniłem te funkcje na zwykłe przesuwanie bajtów i operację "^" ale faktycznie to nie to wiesza mój procesor. Poza tym jak można inaczej przekonwertować tablicę charów na zmienną float? Mam urządzenie które śle mi floata w MSB jako tablicę 4 bajtów.

    Na pewno chodzi Ci o XOR ("^")? Przecież to należy zrobić ORem ("|") mniej więcej tak:
    Kod: text
    Zaloguj się, aby zobaczyć kod

    (kolejność oczywiście przykładowa)

    Można też np. z unią float + char[4] - efekt będzie podobny.

    Ja bym podejrzał assemblera tych funkcji i ewentualnie przy pomocy debuggera spróbówał stwierdzić w którym miejscu występuje problem.

    4\/3!!
  • #7 10039377
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    Dziwne ale po sprawdzeniu działa tak samo i dla "^" i dla "|".
    Natomiast konwersja na float, w taki sposób jak podałeś niestety nie działa.
    Dla przykładu zrobilłem wartość,

    float a = 19.785461 co daje
    unsigned char test[4]={160, 72, 158, 65} - tak mi przychodzi z urządzeania.
    Rzutowanie takie jak w mojej funkcji daje 19.785461.

    Pozd
  • #8 10039405
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    at91sam79 napisał:
    Dziwne ale po sprawdzeniu działa tak samo i dla "^" i dla "|".

    Bo łączysz to z "czystą" (wyzerowaną zmienną). 0 ^ 1 == 0 | 1, tak samo jak 0 ^ 0 == 0 | 0

    Cytat:
    Natomiast konwersja na float, w taki sposób jak podałeś niestety nie działa.

    Pisałem przecież, że kolejność przykładowa. U Ciebie może być odwrotnie. Innym problemem jest próba zmuszenia kompilatora do "niekonwertowania" wartości zmiennej z całkowitych na zmiennoprzecinkowe, a pewnie dlatego nie działa. W takim razie lepsze wydaje mi się zastosowanie unii - tam problemu nie ma.

    4\/3!!
  • #9 10039446
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    Faktycznie unie już sprawdziłem i działają OK nawet dla float'ów.

    Spotkałeś się może kiedyś jeszcze z takim probleme. Od kilku dni siedzę i szukam w moim kodzie kwiatków, które mogą powodować nastepującą sytuację.

    Program działa przez powiedzmy 1h, procek nigdy się nie wiesza, ale:
    1. Od czasu do czasu pewne zmienne int przyjmują wartość 0x80000000 i mimo że powinny być nadpisywane z danych przychodzących z RS232 ich wartość pozostaje ta sama - 0x80000000.
    2. Widzę że jak zaczynam wysyłać wiecej danych do ARMa na port, program się wiesza na parę sekund a potem znów działa. Piszą więcej mam na myśli w zasadzie dość niewiele moim zdaniem bo zaledwie 90 bajtów/sek.
    3. Od czasu do czasu zmienne int, które służą wyłącznie za flagi nagle stają się wartością 1.
    4. A już szczytyem jest że dokąłdnie te zmienne flagowe były ustawiane na 1 gdy uruchamiałem taki kod w pętli głównej na ARM9, czyli szedł z pełną prekością ARMa:

    void ParseVBRxQueue()
    {
    	int nRxQueueIndex = m_nVBRxQueueIndex;
    	if(m_nVBStartIndex > nRxQueueIndex)
    		nRxQueueIndex = VB_MAX_QUEUE_SIZE;
    	int i;
    	for(i = m_nVBStartIndex; i < nRxQueueIndex; i++)
    	{
    		AddVBByteToParsingBuffer(VBFrame.VBRxQueue[i]);
    	}
    	m_nVBStartIndex = i;
    	if(m_nVBStartIndex >= VB_MAX_QUEUE_SIZE)
    		m_nVBStartIndex = 0;
    }
    Dzięki za pomoc.

    Zmienna nVBRxQueueIndex jest volatile int, pozostałe to int i tablica unsigned char.


    Problem zniknął jak cały kod w funkcji obudowałem if'em:

    if(m_nVBStartIndex < m_nVBRxQueueIndex)
    {
    ...
    }


    Czy ma to coś związek z tym że jak w pętli glównej wywoływałem taką funkcję to na stosie tworzyły się za szybko ten zmienne w tej funkcji?

    Pozd
  • #10 10039465
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    Jeśli zmienne w cudowny sposób przyjmują kosmiczne wartości, to zwykle problemem jest zbyt mały stos. Np stos przerwań nadpisuje Ci zmienne głównego wątku (lub odwrotnie) albo stos wchodzi na obszar zmiennych globalnych.. Jeśli używasz funkcji variadic (ze zmienną ilością argumentów, np. printf() ) to konieczne jest wyrównanie stosu do ośmiu - przy wyrównaniu do czterech też się takie kwiatki dzieją.

    4\/3!!
  • #11 10039522
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    W kodzie nie używam takich funkcji. Ale to co piszesz o tym stosie jest bardzo prawdopodobne. Zastanawiam się również jak to jest ze zmiennymi double, których też używam. Czy one mają 8 bajtów. Choć taki sam kod z doublami działa mi bez problemu na AT91SAM256.
    Wracając do stosu to w IAR w prosty sposób zaznacza się wyrównanie stotsu do 4 lub 8, więc zaraz to będę testował. Jeszcze zastanawia mnie czy na takie cudowne napisywanie zmiennych moze mieć tryb procesora: ARM lub Thumb. ja w sumie kompiluję w ARM.

    Dzięki i pozd.
  • REKLAMA
  • #13 10039565
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    Właśnie sprawdziłem dla stack aligment = 8 i niestety bez zmian.

    Jeśli chodzi o powiększenie sotsów, mógłbyś mnie nakierować gdzie szukac takich opcji, bo niestety nie wiem w którym miejscu są takie opcje.

    Pozd
  • #15 10039854
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    Okazało się że rozmiar stosu jest w pliku cstartup.
    Było 2*8*4, zwiększyłem na 12*8*4 ale bez zmiał.

    Szukam jeszcze innych ustawień.

    Pozd
  • REKLAMA
  • #16 10040122
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    No bo stos o rozmiarze 384 bajty dalej może być zbyt mały. Pozatym zwiększyłeś rozmiar którego stosu, bo w ARM7 jest ich chyba sześć.

    4\/3!!
  • #17 10041424
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    Jak narazie zwiększyłem jedynie IRQ_STACK_SIZE i faktycznie może dalej jest za mały. Problem w tym, że w pliku cstartup nie znalazłem nic na temat innych stosów. W opcjach IAR'a tez nie znalazłem ustawień dotyczących stosu. Czytam teraz w książce do ARM9 o rozmiarach wszystkich rodzajów stosów w ARM9.
    PS. Mój problem dotyczny AT91SAM9260 a nie ARM7.

    Pozdrawiam
  • #18 10041438
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    Używasz innych przerwań niż IRQ? Jakiegoś systemu operacyjnego który bazowałby na jakimś jeszcze innym przerwaniu? Jaka jest zajętość pamięci?

    4\/3!!
  • #19 10041827
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    Używam tylko IRQ, wiem że są jeszcze FIQ ale tych nie używam. Z tego co wiem to one mają nawet osobne piny na uC. Mój cały program zajmuje 46kB. A zajętość pamięci to nie bardzo wiem jak to sprawdzić. W sumie ponieważ używam wszystkie USART-4 oraz SPI do obsługi podpiętych urządzeń, w kodzie protokołów mam trochę tablic charowych 1024 elementowych ale z drugiej strony to jak czytam dokumentację do AT91SAM9260 to wszystko wygląda tak:

    1. Mój program (46kB) ładuję do SDRAM'u, mam dwie kości po po 32MB więc sporo.
    2. Po odpaleniu programu w inicie niskopoziomowym jest inicjalizacja obszarów pamięci SRAM1, które odpowiadają za stosy, między innymi IRQ_STACK. Z tego co widze to na dole jest FIQ_STACK, potem IRQ_STACK - który próbowałem zwiększać a potem jest supervisor stack (lub user stack - zalezy od ustawienia) i ponoć sięga do końca SRAM1 czyli do 4kB - tylko nie wiem co się dzieje po remapie. Jak rozumiem to ten supervisor stack to stack programu głównego ale nigdzie nie mogę znaleźć jak go zwiększyć. Jest jeszcze SRAM0 tez 4kB ale to jest on raczej zarezerwowany dla bootloadera żeby odpalał program z DATAFLASH'a. W SRAM0 jest też chyba robiona obsługa wyjątków.

    Pozdrawiam
  • #20 10041883
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    Jeśli te tablice po 1024 elementy masz jako lokalne zmienne automatyczne w funkcjach (nie "static") to nawet nie szukałbym problemu gdzie indziej... Nie wiem czy funkcje te masz odpalane z przerwań czy z wątku głównego, ale chyba oczywistym jest, że 384bajty, a nawet 4kB to mało jeśli tak wielkich tablic masz "kilka".

    4\/3!!
  • #21 10041908
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    Wszystkie tablice w moim kodzie większe niż 10 elementów ma jako zmiemme globalne. W funkcjach nie tworzę tablic wiekszych niż parę elementów. Oto przykład takich tablic:

    #define MB_MAX_QUEUE_SIZE                        1024
    #define MB_MAX_FRAME_SIZE 		256
    #define MB_MIN_FRAME_SIZE		5
    
    typedef struct _MbPbFrame
    {
        unsigned char MBRxQueue[MB_MAX_QUEUE_SIZE];
        unsigned char MBRxBuffer[MB_MAX_FRAME_SIZE];
        unsigned char MBTxBuffer[MB_MAX_FRAME_SIZE];
        unsigned char MBCmdBuffer[MB_MAX_FRAME_SIZE];
        volatile short MBRxBufferLenght;
        volatile unsigned char IsMBFrameReady;
    }MbPbFrame;
    
    extern MbPbFrame MBFrame;


    W samych przerwaniach jedyne co robię to dodaję kolejne odebrane bajty do MBRxQueue, w programie głównym ciąglę wywołuję funkcję, która monitoruje MBRxQueue i rozparsowuje ramki danych, nalicza CRC i na podstawie ramki steruje programem głównym.

    Jeśli chodzi o "static" to właśnie u mnie w programie praktycznie nie ma zmiennych czy funkcji static - nie twierdzę że to jest dobrze.

    Pozdrawiam
  • #22 10045836
    at91sam79
    Poziom 9  
    Posty: 42
    Witam,

    Przeniosłem cały kod do eclipsa i skompilowaliśmy go WinArmem. Niestety wynik jest ten sam. Czy wiesz może jak poustawiać te wszystkie stosy w makefile'u?

    Pozdrawiam

Podsumowanie tematu

LABEL_AI_GENERATED
W dyskusji poruszono problem zawieszania się procesora ARM podczas konwersji danych z tablicy unsigned char na typy short, int i float. Użytkownik zauważył, że jego kod działa na modelu AT91SAM9260, ale nie na AT91SAM256. Odpowiedzi wskazują na potencjalne problemy z wyrównaniem pamięci oraz zbyt małym rozmiarem stosu, co może prowadzić do nadpisywania zmiennych. Użytkownik eksperymentował z różnymi metodami konwersji, w tym użyciem unii, co przyniosło pozytywne rezultaty. Wskazano również na konieczność zwiększenia rozmiaru stosu w pliku cstartup oraz na sprawdzenie zajętości pamięci, aby zidentyfikować źródło problemów.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA