Aktyw Forum
Zarejestruj się na forum.ep.com.pl i zgłoś swój akces do Aktywu Forum. Jeśli jesteś już zarejestrowany wystarczy, że się zalogujesz.
Sprawdź punkty Zarejestruj siępołączenie dwóch zmiennych typy byte na jedną typu int w C
Moderatorzy:Jacek Bogusz, Moderatorzy
Cześć.
Jak połączyć wie zmnienne typu unsigned char w jadną zmnienną typu unsidneg int.
Mam dwie wartości odczytane z przetwornika A/C, i bity od 15 do 8 umieszczone w jednej zmiennej dana2 natomast młodsza część odczytanej warości bity od 7 do 0 są umieszczone w zmiennej dana1. Obliczenia chcę wykonać na zmiennej typu int i jak pomączyć dana1 i dana2 w jedną zmienna typu int aby odpowiednie bity były na odpowiednich miejscach.
Jak połączyć wie zmnienne typu unsigned char w jadną zmnienną typu unsidneg int.
Mam dwie wartości odczytane z przetwornika A/C, i bity od 15 do 8 umieszczone w jednej zmiennej dana2 natomast młodsza część odczytanej warości bity od 7 do 0 są umieszczone w zmiennej dana1. Obliczenia chcę wykonać na zmiennej typu int i jak pomączyć dana1 i dana2 w jedną zmienna typu int aby odpowiednie bity były na odpowiednich miejscach.
Można na wiele sposobów, np.
pomnożyć dana2 razy 256 i dodać dana1, W "C" można zastosować jeszcze wiele sztuczek które moga przyspieszyć ale są nie do końca przenośne, np. przesunięcie o 8 bitów w lewo zamiast mnożenia 256, Najszybsze działanie można uzyskać stosując unię składającą się z word (16 bit!) i struktury dwóch bajtów i wczytywać na jej bajtowe składniki, a potem używać składnika word. Celowo nie podaję konkretnych zapisów w "C" bo zdecydowanie łatwiej jest nauczyć się czytając jakikolwiek podręcznik i myśląc odrobinę jak to co jest w nim napisane ma się do rozwiązywanego problemu. Nie określę dosadniej, ale czasmi naprawdę wystarczy chwilę pomyśleć co się chce uzyskać, narysować sobie na papierze, ołówkiem, a nie walić pytanie na forum.
pomnożyć dana2 razy 256 i dodać dana1, W "C" można zastosować jeszcze wiele sztuczek które moga przyspieszyć ale są nie do końca przenośne, np. przesunięcie o 8 bitów w lewo zamiast mnożenia 256, Najszybsze działanie można uzyskać stosując unię składającą się z word (16 bit!) i struktury dwóch bajtów i wczytywać na jej bajtowe składniki, a potem używać składnika word. Celowo nie podaję konkretnych zapisów w "C" bo zdecydowanie łatwiej jest nauczyć się czytając jakikolwiek podręcznik i myśląc odrobinę jak to co jest w nim napisane ma się do rozwiązywanego problemu. Nie określę dosadniej, ale czasmi naprawdę wystarczy chwilę pomyśleć co się chce uzyskać, narysować sobie na papierze, ołówkiem, a nie walić pytanie na forum.
A to ciekawe że można pomnożyć zamiast 0x0100 przez 256 popatrz i jeszcze można dodać drugą zmienną. Z tym szybkim dostępem do unii to już znaczna przesada radzę zaglądnąć do kodu wygenerowanego przez kompilator.
Ponieważ zapis
unsigned int wyniczek = dana2*0x0100 + dana1;
nawet najgłupszy kompilator wykona operując na rejestrach, a dostęp do unii niestety nie.
Ponieważ zapis
unsigned int wyniczek = dana2*0x0100 + dana1;
nawet najgłupszy kompilator wykona operując na rejestrach, a dostęp do unii niestety nie.
Oczywiscie najlepiej jest odsylac do ksiazek ale w tedy forum bylo by tylko odsylaczem a jesli ktos nie ma doswiadczenia to najprostrze rzeczy sa problemem bo jakby wiedzial to by sie nie pytal. Rowniez uwazam ze jak sie cos robi powinno sie zastanowic nad problemem ale jak sie nie ma doswiadczenia to nieraz jest ciezko znalesc rozwiazanie. To ze zapis liczb w hex jest logiczny i prosty do przestawiania bajtow przychodzi z doswiadczeniem ktorego nowicjusz jeszcze ma
BTW podany przyklad porownal bym do ryby (jako rozwiazanie) a nie wedki (tlumaczacej co, jak i dlaczego)
BTW podany przyklad porownal bym do ryby (jako rozwiazanie) a nie wedki (tlumaczacej co, jak i dlaczego)
Guru, kiedy pisałem odpowiedź, to nie widziałem jeszcze Twojego postu i nie jest on komentarzem do Twojej wypowiedzi. Co do unii to się nie zgodzę. Jeżeli odpowiednie odczyty z przetwornika od razu umieścisz w uni (dana2 i dana1 bedą elementami unii) to potem słowo 16 bitowe masz od razu do dalszego przetwarzania. Jeżeli składniki i suma nie są wyłącznie chwilowymi zmiennymi to unia da najszybszy rezultat.
-
- -
- Posty:651
- Rejestracja:13 sty 2005, o 18:38
- Lokalizacja:Krasnystaw
- Kontaktowanie:
Masz rację a_antoniak. nie zwróciłem uwagi, że można w taki sposób
A czy możesz wyjaśnić mi taką rzezcz, którą niedawno spotkałem a nie mogę znaleść o co w tym chodzi.
Otóż spotkałem taką deklarację:
#define LCDPIN (*(volatile unsigned char *)0x36) // PIN register
#define LCDDDR (*(volatile unsigned char *)0x37) // Data Direction Register
#define LCDPORT (*(volatile unsigned char *)0x38) // PORT
a nierozumiem tego zwrotu: (*(volatile unsigned char *)0x38)
i dlaczego akurat tak jest użyte, czy to są wskaźniki, dlaczego tak i jeszcze volatie????
A czy możesz wyjaśnić mi taką rzezcz, którą niedawno spotkałem a nie mogę znaleść o co w tym chodzi.
Otóż spotkałem taką deklarację:
#define LCDPIN (*(volatile unsigned char *)0x36) // PIN register
#define LCDDDR (*(volatile unsigned char *)0x37) // Data Direction Register
#define LCDPORT (*(volatile unsigned char *)0x38) // PORT
a nierozumiem tego zwrotu: (*(volatile unsigned char *)0x38)
i dlaczego akurat tak jest użyte, czy to są wskaźniki, dlaczego tak i jeszcze volatie????
-
- -
- Posty:651
- Rejestracja:13 sty 2005, o 18:38
- Lokalizacja:Krasnystaw
- Kontaktowanie:
Sprawa jest dosyc prosta i sam mógłbyś łatwo do tego dojść. Ale wyjaśniamy:Masz rację a_antoniak. nie zwróciłem uwagi, że można w taki sposób
A czy możesz wyjaśnić mi taką rzezcz, którą niedawno spotkałem a nie mogę znaleść o co w tym chodzi.
Otóż spotkałem taką deklarację:
#define LCDPIN (*(volatile unsigned char *)0x36) // PIN register
#define LCDDDR (*(volatile unsigned char *)0x37) // Data Direction Register
#define LCDPORT (*(volatile unsigned char *)0x38) // PORT
a nierozumiem tego zwrotu: (*(volatile unsigned char *)0x38)
i dlaczego akurat tak jest użyte, czy to są wskaźniki, dlaczego tak i jeszcze volatie????
Kompilator wstawia za każde słowo LCDPORT wyrażenie (*(volatile unsigned char *)0x38).
Zewnętrzne nawiasy są dla scalania całości w 1 wyrażenie. Liczba 0x38 rzutowana jest na wskaźnik do typu unsigned char. Słowo volatile oznacza mniej więcej "ulotny". Oznacza to, że wartośc może zmieniać się niezauważenie dla kompilatora (np. zależy od zewnętrznych czynników). Dzięki temu kompilator przy odczycie nie będzie zakładał (na podstawie struktury kodu), że wartość wskazywanego bajta jest taka jaką miałby on na pdst. analizy kodu. Zamiast zakładać dokona odczytu. Innymi słowy - volatile zakazuje kompilatorowi stosowanie optymalizacji do odwołań do zmiennej.
Gwiazdka najbardziej po lewej oznacza po prostu to co wskazuje wskaźnik (volatile unsigned char *)0x38 - czyli miejsce w pamięci pod adresem 0x38.
Krótko o volatile:
Kod: Zaznacz cały
//deklaracja i definicja znacznika wystapienia przerwania
unsigned char fInterrupt;
//handler
void INTERRUPT(void)
{
fInterrupt = 1;
}
void main(void)
{
fInterrupt=0;
//czekamy na przerwanie
while(fInterrupt==0);
//tu sterowanie moze nie dojsc nawet jesli wystapi przerwanie.
//Kompilatorowi nic nie
//wiadomo, ze uzytkownik moze wcisnac przycisk na INT0
// i moze on (kompilator) zalorzyc, ze
//fInterrupt stale wynosi 0. Dostawienie volatile przed
//deklaracja znacznika zalatwia sprawe.
}
wpisując w www.google.pl słowa volatile C znajdziesz wyjaśnienia. Pierwsze lepsze są tu:
http://galaxy.uci.agh.edu.pl/~chwastek/ ... const.html
http://www.programmersheaven.com/articl ... ticle1.htm
http://publications.gbdirect.co.uk/c_bo ... atile.html
-
- -
- Posty:651
- Rejestracja:13 sty 2005, o 18:38
- Lokalizacja:Krasnystaw
- Kontaktowanie:
-
- -
- Posty:175
- Rejestracja:10 paź 2003, o 20:44
- Lokalizacja:Białystok
- Kontaktowanie:
To zajrzyjmy ( z treści wynika, ze chodzi o avr-gcc , optymalizacja -Os):Z tym szybkim dostępem do unii to już znaczna przesada radzę zaglądnąć do kodu wygenerowanego przez kompilator.
- załadowanie int1 poprzez mnożenie i dodawanie :
Kod: Zaznacz cały
int1=byte2*0x100 + byte1;
70: 80 91 60 00 lds r24, 0x0060
74: 99 27 eor r25, r25
76: 98 2f mov r25, r24
78: 88 27 eor r24, r24
7a: 20 91 61 00 lds r18, 0x0061
7e: 82 0f add r24, r18
80: 91 1d adc r25, r1
82: 90 93 65 00 sts 0x0065, r25
86: 80 93 64 00 sts 0x0064, r24
Kod: Zaznacz cały
int1=byte2<<8 | byte1;
8a: 80 91 60 00 lds r24, 0x0060
8e: 99 27 eor r25, r25
90: 98 2f mov r25, r24
92: 88 27 eor r24, r24
94: 20 91 61 00 lds r18, 0x0061
98: 33 27 eor r19, r19
9a: 82 2b or r24, r18
9c: 93 2b or r25, r19
9e: 90 93 65 00 sts 0x0065, r25
a2: 80 93 64 00 sts 0x0064, r24
Kod: Zaznacz cały
union
{
struct
{
uchar blow;
uchar bhigh;
} bytes;
uint integer;
} AdcData;
Kod: Zaznacz cały
AdcData.bytes.blow=byte1;
a6: 80 91 61 00 lds r24, 0x0061
aa: 80 93 62 00 sts 0x0062, r24
AdcData.bytes.bhigh=byte2;
ae: 80 91 60 00 lds r24, 0x0060
b2: 80 93 63 00 sts 0x0063, r24
młodszego i starszego bajtu zmiennej int1 :
Kod: Zaznacz cały
*(uchar*)&int1=byte1;
c6: 80 91 61 00 lds r24, 0x0061
ca: 80 93 64 00 sts 0x0064, r24
*((uchar*)&int1+1)=byte2;
ce: 80 91 60 00 lds r24, 0x0060
d2: 80 93 65 00 sts 0x0065, r24
Z treści wynika jasno że ze nie chodzi o avr-gcc , optymalizacja -Os
A przepraszam z jakiej treści i jaka optymalizacja?
No z przykładu widać że rewelacyjna.
Więc ja na to tak, kompilator SDCC dla procesora 80C390 włączona obsługa pamięci flat24
i wynik kompilacji:
nie chcę już tutaj liczyć bajtów w poszczególnych procedurach ale na oko widać że funkc1 jest "ładniejsza" czytelniejsza i w tym przypadku działa trochę szybciej.
Trochę mi porozwalało te bajty za co przepraszam.
A przepraszam z jakiej treści i jaka optymalizacja?
No z przykładu widać że rewelacyjna.
Więc ja na to tak, kompilator SDCC dla procesora 80C390 włączona obsługa pamięci flat24
Kod: Zaznacz cały
unsigned int funkc(unsigned char dana1, unsigned char dana2)
{
union
{
struct bytes
{
unsigned char blow;
unsigned char bhigh;
};
unsigned int integer;
}AdcData;
AdcData.blow = dana1;
AdcData.bhigh = dana2;
return(AdcData.integer);
}
/************************************************************/
unsigned int funkc1(unsigned char dana1, unsigned char dana2)
{
return(dana2<<8 | dana1);
}
/***********************************************************/
Kod: Zaznacz cały
;------------------------------------------------------------
;Allocation info for local variables in function 'funkc'
;------------------------------------------------------------
;dana2 Allocated with name '_funkc_PARM_2'
;dana1 Allocated to registers r2
;AdcData Allocated with name '_funkc_AdcData_1_1'
;------------------------------------------------------------
; I2C_pr2.c:380: unsigned int funkc(unsigned char dana1, unsigned char dana2)
; genFunction
; -----------------------------------------
; function funkc
; -----------------------------------------
_funkc:
; genReceive
mov r2,dpl ;2b
; I2C_pr2.c:392: AdcData.blow = dana1;
; genPointerSet
mov dptr,#_funkc_AdcData_1_1 ;4b
mov a,r2 ;1b
movx @dptr,a ;1b
; I2C_pr2.c:393: AdcData.bhigh = dana2;
; genPointerSet
mov dptr,#(_funkc_AdcData_1_1 + 0x0001) ;4b
mov dps, #1 ;3b
mov dptr, #_funkc_PARM_2 ;4b
movx a,@dptr ;1b
dec dps ;2b
movx @dptr,a ;1b
; I2C_pr2.c:395: return(AdcData.integer);
; genPointerGet
; genFarPointerGet
mov dptr,#_funkc_AdcData_1_1 ;4b
movx a,@dptr ;1b
inc dptr ;1b
mov dpl1,a ;2b
movx a,@dptr ;1b
mov dph1,a ;2b
; genRet
mov dpl,dpl1 ;3b
mov dph,dph1 ;3b
; genLabel
00101$:
; genEndFunction
ret ;1b
;------------------------------------------------------------
;Allocation info for local variables in function 'funkc1'
;------------------------------------------------------------
;dana2 Allocated with name '_funkc1_PARM_2'
;dana1 Allocated to registers r2
;------------------------------------------------------------
; I2C_pr2.c:398: unsigned int funkc1(unsigned char dana1, unsigned char dana2)
; genFunction
; -----------------------------------------
; function funkc1
; -----------------------------------------
_funkc1:
; genReceive
mov r2,dpl ;2b
; I2C_pr2.c:400: return(dana2<<8 | dana1);
; genCast
mov dptr,#_funkc1_PARM_2 ;4b
movx a,@dptr ;1b
mov r3,a ;1b
; genLeftShift
; genLeftShiftLiteral (8), size 2
; genlshTwo
; Peephole 228 redundant move
mov ar4,r3 ;2b
mov r3,#0 ;2b
; genCast
mov r5,#0 ;2b
; genOr
mov a,r3 ;1b
orl a,r2 ;1b
mov dpl,a ;2b
mov a,r4 ;1b
orl a,r5 ;1b
mov dph,a ;2b
; genRet
; genLabel
00101$:
; genEndFunction
ret ;1b
Trochę mi porozwalało te bajty za co przepraszam.
-
- -
- Posty:651
- Rejestracja:13 sty 2005, o 18:38
- Lokalizacja:Krasnystaw
- Kontaktowanie:
cała ta dyskusja o optymalizację i pisanie tego tak czy tak jest bez sensu. Można dyskutować i analizować kod dla takich prostych funkcji, ale jak pisze program jakis tam i mam analizować co mi kompilator wypisuje w asemblerze, to sam szybciej w tym asemblerze napiszę. Ludzie po to jest kompilator i optymalizacja żebym miał gdzieś jak on to zrobi w asemblerze. W ok 100% zastosowań amatorskich szybkośc działania i optymalizacja kodu ma drugorzedne znaczenie. W zastosowaniach profesjonalnych trzeba niestety stosowac dobre kompilatory i tez mieć gdzieś jak one to robią - liczy się czas tworzenia aplikacji. Jeżeli jest za wolny to biore szybszy procesor. Gorzej jak mam juz najszybszego AVR-ka i nie daje rady - co wtedy Eeeee nie taka możliwośc nie istnieje
Jeżeli chodzi o konkurs piekności to stawiam na
return(dana2<<8|dana1);
najbardziej zgrabna i szczupła
Jeżeli chodzi o konkurs piekności to stawiam na
return(dana2<<8|dana1);
najbardziej zgrabna i szczupła
-
- -
- Posty:651
- Rejestracja:13 sty 2005, o 18:38
- Lokalizacja:Krasnystaw
- Kontaktowanie:
Guru. każdy przykład można doprowadzić do absurdu i wykazać jego nieadekwatność. Zauważ że nigdzie nie sugerowałem pisania specjalnej funkcji dla tej konwersji. Sugerowałem zastosowanie uni "od razu" czyli od momentu odczytu z przetwornika i umieszczania tych odczytów bezpośrednio w unii a potem uzycie jej 16 bitowej postaci. Nawet Twój kompilator da lepszy kod. Podobny uzysk otrzymasz dla zapisu proponowanego przez Jurka S. Tak nawiasem to cała dyskusja dotyczy własnie takich rodzajów kompilatorów. Pierwotnie były one usprawnionymi makroassemblerami, dopiero potem pojawiły się wyspecjalizowane moduły optymalizacji (zawsze ściśle powiązane z docelowym procesorem). Zgadzam się że, w dzisiejszych czasach, sposób kodowania ma drugorzędne znaczenie. Ważniejsza jest czytelność i przenośność. Dlatego uwżam że lepiej jest zapisać mnożenie razy 256 niż razy 0x100 czy 0377 lub <<8. Optymalizacja przez zmianę postaci zapisu powinna byc robiona wyłącznie na kodzie którego działanie zostało już sprawdzone (wypracowanie techniki testowania) i tylko w obszarach o które naprawdę tego wymagają.
-
- -
- Posty:175
- Rejestracja:10 paź 2003, o 20:44
- Lokalizacja:Białystok
- Kontaktowanie:
Już odpowiadam - cytat :A w którym miejscu? No chyba że wszystko co sie tyczy C jest zwiazane nierozerwalnie z avr-gcc ............( z treści wynika, ze chodzi o avr-gcc , optymalizacja -Os):
-Pozdrowienia Jurek S.
Na zarzuty ze sie czepiam - odpowiadam od razu : Tak czepiam sie..
Pzdr
Tomek
Więc AVR , a avr-gcc to juz rzeczywiście domyślnieOtóż spotkałem taką deklarację:
#define LCDPIN (*(volatile unsigned char *)0x36) // PIN register
#define LCDDDR (*(volatile unsigned char *)0x37) // Data Direction Register
#define LCDPORT (*(volatile unsigned char *)0x38) // PORT
Pozdrowienia Jurek S.
Kto jest online
Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 36 gości