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

Sławek5
-
-
Posty:485
Rejestracja:15 sie 2003, o 16:40
Lokalizacja:Szczecin
Kontaktowanie:
połączenie dwóch zmiennych typy byte na jedną typu int w C

Postautor: Sławek5 » 24 paź 2005, o 06:26

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.

Guru
-
-
Posty:250
Rejestracja:30 cze 2003, o 13:26
Lokalizacja:Kraków

Postautor: Guru » 24 paź 2005, o 08:26

unsigned int wyniczek = dana2*0x0100 + dana1;
lub
unsigned int wyniczek = dana2<<8 + dana1;
lub
unsigned int wyniczek = dana2*0x0100 | dana1;
lub
unsigned int wyniczek = dana2<<8 | dana1;

Awatar użytkownika
bis
-
-
Posty:134
Rejestracja:12 maja 2005, o 08:11
Lokalizacja:Warszawa

Postautor: bis » 24 paź 2005, o 08:44

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.

Guru
-
-
Posty:250
Rejestracja:30 cze 2003, o 13:26
Lokalizacja:Kraków

Postautor: Guru » 24 paź 2005, o 09:51

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.

Awatar użytkownika
ACeK
-
-
Posty:1522
Rejestracja:30 mar 2003, o 19:35
Lokalizacja:Kielce

Postautor: ACeK » 24 paź 2005, o 10:52

:) Oczywiscie najlepiej jest odsylac do ksiazek ale w tedy forum bylo by tylko odsylaczem :idea: 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 :no: ma :wink:
:D

BTW podany przyklad porownal bym do ryby (jako rozwiazanie) a nie wedki (tlumaczacej co, jak i dlaczego) :idea:

Awatar użytkownika
bis
-
-
Posty:134
Rejestracja:12 maja 2005, o 08:11
Lokalizacja:Warszawa

Postautor: bis » 24 paź 2005, o 11:32

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.

a_antoniak
-
-
Posty:651
Rejestracja:13 sty 2005, o 18:38
Lokalizacja:Krasnystaw
Kontaktowanie:

Postautor: a_antoniak » 24 paź 2005, o 11:41

Sławku, myślę że jak bez dzielenia włosa na czworo zrobisz tak:

unsigned int wyniczek = dana2<<8 | dana1;

to będzie ok. To zostanie zrobione szybko i sprawnie przez uP.

Sławek5
-
-
Posty:485
Rejestracja:15 sie 2003, o 16:40
Lokalizacja:Szczecin
Kontaktowanie:

Postautor: Sławek5 » 24 paź 2005, o 19:13

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_antoniak
-
-
Posty:651
Rejestracja:13 sty 2005, o 18:38
Lokalizacja:Krasnystaw
Kontaktowanie:

Postautor: a_antoniak » 24 paź 2005, o 20:17

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????
Sprawa jest dosyc prosta i sam mógłbyś łatwo do tego dojść. Ale wyjaśniamy:

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. }
MOJA RADA DLA CIEBIE:
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

Sławek5
-
-
Posty:485
Rejestracja:15 sie 2003, o 16:40
Lokalizacja:Szczecin
Kontaktowanie:

Postautor: Sławek5 » 25 paź 2005, o 06:31

Dzieki za odpowiedz.
Troszke wyjasniajac to chodzilo mi o te nawiast i podwojne gwiazdki a nie samo slowko "volatile".

a_antoniak
-
-
Posty:651
Rejestracja:13 sty 2005, o 18:38
Lokalizacja:Krasnystaw
Kontaktowanie:

Postautor: a_antoniak » 25 paź 2005, o 08:33

Musisz mysleć analitycznie. Nie licz na to, że znajdziesz wyjaśnienie każdego konkretnrgo zapisu, bo to niemożliwe. Ale znajdziesz wyjaśnienie zasad. Gwiazdki i nawiasy oznaczaja tu dokladnie to samo co w innych przypadkach. Np: char *ptr;

Jurek Szczesiul
-
-
Posty:175
Rejestracja:10 paź 2003, o 20:44
Lokalizacja:Białystok
Kontaktowanie:

Postautor: Jurek Szczesiul » 25 paź 2005, o 09:25

Z tym szybkim dostępem do unii to już znaczna przesada radzę zaglądnąć do kodu wygenerowanego przez kompilator.
To zajrzyjmy ( z treści wynika, ze chodzi o avr-gcc , optymalizacja -Os):
- 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
- tak samo przez przesuwanie i sumowanie bitowe

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
- wpis do pól unii :

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
- tak samo najbliższy asemblerowi bezpośredni wpis pod adresy
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
Pozdrowienia Jurek S.

tomek_j
-
-
Posty:264
Rejestracja:14 sty 2004, o 09:06

Postautor: tomek_j » 25 paź 2005, o 10:33

......( z treści wynika, ze chodzi o avr-gcc , optymalizacja -Os):
-Pozdrowienia Jurek S.
A w którym miejscu? No chyba że wszystko co sie tyczy C jest zwiazane nierozerwalnie z avr-gcc ......
Na zarzuty ze sie czepiam - odpowiadam od razu : Tak czepiam sie..
Pzdr
Tomek

Guru
-
-
Posty:250
Rejestracja:30 cze 2003, o 13:26
Lokalizacja:Kraków

Postautor: Guru » 25 paź 2005, o 10:56

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. :566:
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); } /***********************************************************/
i wynik kompilacji:

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
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_antoniak
-
-
Posty:651
Rejestracja:13 sty 2005, o 18:38
Lokalizacja:Krasnystaw
Kontaktowanie:

Postautor: a_antoniak » 25 paź 2005, o 11:24

...I drugiego dnia stworzyła bogini przesunięcia bitowe. I widziała bogini, że są dobre....

tomek_j
-
-
Posty:264
Rejestracja:14 sty 2004, o 09:06

Postautor: tomek_j » 25 paź 2005, o 11:36

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 :cry: Eeeee nie taka możliwośc nie istnieje :D

Jeżeli chodzi o konkurs piekności to stawiam na
return(dana2<<8|dana1);
najbardziej zgrabna i szczupła 8)

a_antoniak
-
-
Posty:651
Rejestracja:13 sty 2005, o 18:38
Lokalizacja:Krasnystaw
Kontaktowanie:

Postautor: a_antoniak » 25 paź 2005, o 11:50

Coś takiego, zgadzam się z Tomkiem. Nie sądziłem, że dożyję takiej chwili :) (tyle, że nawiasik niekonieczny - mały kijek w mrowisko...)

Awatar użytkownika
bis
-
-
Posty:134
Rejestracja:12 maja 2005, o 08:11
Lokalizacja:Warszawa

Postautor: bis » 25 paź 2005, o 14:54

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ą.

Jurek Szczesiul
-
-
Posty:175
Rejestracja:10 paź 2003, o 20:44
Lokalizacja:Białystok
Kontaktowanie:

Postautor: Jurek Szczesiul » 25 paź 2005, o 15:48

......( z treści wynika, ze chodzi o avr-gcc , optymalizacja -Os):
-Pozdrowienia Jurek S.
A w którym miejscu? No chyba że wszystko co sie tyczy C jest zwiazane nierozerwalnie z avr-gcc ......
Na zarzuty ze sie czepiam - odpowiadam od razu : Tak czepiam sie..
Pzdr
Tomek
Już odpowiadam - cytat :
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
Więc AVR , a avr-gcc to juz rzeczywiście domyślnie :-)
Pozdrowienia Jurek S.

szymel
-
-
Posty:212
Rejestracja:16 sty 2005, o 16:42
Lokalizacja:Włocławek

Postautor: szymel » 25 paź 2005, o 15:51

A mnie zastanawia , po co delikwent wczytuje rejestry ADCx do zmiennych char , a potem martwi się jak je upchnąć do int. Czyż nie prościej skopiować je do int jednym pociągnięciem pióra :?: Chyba , że używa jakiegoś zewnętrznego :(

Pozdrawiam
Piotrek

Wróć do „PLD/FPGA i inne zagadnienia techniki cyfrowej”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 53 gości