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

[AVR][c]LCD i typy zmiennych? / warningi

PO. 05 Lut 2009 20:18 3855 15
  • #1 6108138
    PO.
    Poziom 20  
    Na początek trochę autorskiego kodu do LCD, który sam tworzyłem na podstawie opisów z sieci i wzorując się na dostępnych bibliotekach - były mało czytelne, więc zamiast ślepo ich używać postanowiłem mieć coś swojego, co wiem jak działa i co się w tym dzieje...

    na poczatek fragment kodu lcd odpowiadającego za wyświetlanie różnych rzeczy:


    
    
    
    //zmienne i procedury obsługi lcd
    //pod lcdddr DDRx
    //pod lcdport PORTx
    //pod lcdbit (1<<n)
    //pod lcdpin PINx
    
    #define set1   |= 
    #define set0    &= ~ 
    #define toggle    ^=
    
    #define lcdddr_rs DDRB
    #define lcdport_rs PORTB
    #define lcdbit_rs (1<<0)
    #define lcdddr_rw DDRB
    #define lcdport_rw PORTB
    #define lcdbit_rw (1<<1)
    #define lcdddr_e DDRB
    #define lcdport_e PORTB
    #define lcdbit_e (1<<2)
    
    #define lcdddr_d7 DDRB
    #define lcdport_d7 PORTB
    #define lcdbit_d7 (1<<3)
    #define lcdpin_d7 PINB
    #define lcdddr_d6 DDRB
    #define lcdport_d6 PORTB
    #define lcdbit_d6 (1<<4)
    #define lcdpin_d6 PINB
    #define lcdddr_d5 DDRB
    #define lcdport_d5 PORTB
    #define lcdbit_d5 (1<<5)
    #define lcdpin_d5 PINB
    #define lcdddr_d4 DDRB
    #define lcdport_d4 PORTB
    #define lcdbit_d4 (1<<6)
    #define lcdpin_d4 PINB
    
    
    void lcd_checkstatus(void){
     uint8_t i=0;
     lcdddr_rs set1 lcdbit_rs;
     lcdddr_rw set1 lcdbit_rw;
    //wejścia na dane dla bezpieczeńśtwa
     lcdddr_d7 set0 lcdbit_d7;
     lcdddr_d6 set0 lcdbit_d6;
     lcdddr_d5 set0 lcdbit_d5;
     lcdddr_d4 set0 lcdbit_d4;
     lcdport_d7 set1 lcdbit_d7;
     lcdport_d6 set1 lcdbit_d6;
     lcdport_d5 set1 lcdbit_d5;
     lcdport_d4 set1 lcdbit_d4;
     
     do{
      lcdport_rs set0 lcdbit_rs;
      lcdport_rw set1 lcdbit_rw;
      lcdport_e set1 lcdbit_e;
      if(lcdpin_d7&lcdbit_d7) i=1;
      lcdport_e set0 lcdbit_e;
     }while(~i);
    } 
    
    
    void lcd_nibble(uint8_t nibbleToWrite)
    {
    
    if(nibbleToWrite & 0x01) lcdport_d4 set1 lcdbit_d4;
     else lcdport_d4 set0 lcdbit_d4;
    
    if(nibbleToWrite & 0x02) lcdport_d5 set1 lcdbit_d5;
     else lcdport_d5 set0 lcdbit_d5;
    
    if(nibbleToWrite & 0x04) lcdport_d6 set1 lcdbit_d6;
     else lcdport_d6 set0 lcdbit_d6;
    
    if(nibbleToWrite & 0x08) lcdport_d7 set1 lcdbit_d7;
     else lcdport_d7 set0 lcdbit_d7;
    }
    
    
    void lcd_rozkaz(uint8_t dataToWrite){
     lcd_checkstatus();
     lcdddr_d4 set1 lcdbit_d4;
     lcdddr_d5 set1 lcdbit_d5;
     lcdddr_d6 set1 lcdbit_d6;
     lcdddr_d7 set1 lcdbit_d7;
     lcdport_rs set0 lcdbit_rs;
     lcdport_rw set0 lcdbit_rw;
     lcd_nibble(dataToWrite >> 4);
     lcdport_e set1 lcdbit_e;
     lcdport_e set0 lcdbit_e;
     lcd_nibble(dataToWrite);
     lcdport_e set1 lcdbit_e;
     lcdport_e set0 lcdbit_e;
    }
    
    
    void lcd_znak(uint8_t dataToWrite){
     lcd_checkstatus();
     lcdddr_d4 set1 lcdbit_d4;
     lcdddr_d5 set1 lcdbit_d5;
     lcdddr_d6 set1 lcdbit_d6;
     lcdddr_d7 set1 lcdbit_d7;
     lcdport_rs set1 lcdbit_rs;
     lcdport_rw set0 lcdbit_rw;
     lcd_nibble(dataToWrite >> 4);
     lcdport_e set1 lcdbit_e;
     lcdport_e set0 lcdbit_e;
     lcd_nibble(dataToWrite);
     lcdport_e set1 lcdbit_e;
     lcdport_e set0 lcdbit_e;
    }
    
    
    void lcd_text(uint8_t * text){
     while(*text)
      lcd_znak(*text++);
    }
    
    void lcd_tint(uint8_t znak){
     lcd_znak(znak/10+0x30);
     lcd_znak(znak%10+0x30);
    
    }
    
    void lcd_zint(uint8_t znak){
     lcd_znak(znak+0x30);
     
    }
    


    i teraz co się dzieje:
    użycie:
     lcd_text("1.Czas odliczania");
    powoduje:
    Cytat:
    ../b1.c:534: warning: pointer targets in passing argument 1 of 'lcd_text' differ in signedness


    użycie:
     lcd_znak(":");
    powoduje:
    Cytat:
    ../b1.c:1077: warning: passing argument 1 of 'lcd_znak' makes integer from pointer without a cast


    użycie:
     
    
    volatile uint8_t tekst_p[20];
    ...
     for(uint8_t i=0;i<20;i++) tekst_p[i]=eeprom_read(0x0030+i);
    ...
    lcd_text(tekst_p);
    powoduje:
    Cytat:
    ../b1.c:1091: warning: passing argument 1 of 'lcd_text' discards qualifiers from pointer target type
    Aha, eeprom_read(); czyta spod adresu tak jak jest w manualu atmela.

    linijki:
    tekst_p[]={"costam"}; 
    i
    tekst_p[]="costam"; 
    zwracają:
    Cytat:
    ../b1.c:1089: error: expected expression before ']' token


    linijka:
       lcd_text(0x23232323);//krzyżyki
    Cytat:
    ../b1.c:1106: warning: passing argument 1 of 'lcd_text' makes pointer from integer without a cast


    Jak to wszystko zmusić do współpracy? Rozumiem, że najprawdopodobniej "takie same" typy zmiennych nie są takie same ale próby z deklaracjami char, unsigned char itp nie dały lepszych efektów.
    Oczywiście większość z tego to warningi ale wolę nie czekać na testy co się wyświetli na hardwarze a co nie...
  • #2 6108362
    Freddie Chopin
    Specjalista - Mikrokontrolery
    1. char a unsigned char (uint8_t) to co innego. string napisany tak jak w wywolaniu twojej funkcji jest typu char*. musisz wiec albo zmienic swoja funkcje, albo zrzutowac na (uint8_t*), albo to olac...

    2. znaki podaje sie w apostrofach. to jest znak -> 'a', to jest juz string -> "a" (a + \0)

    3. wynika z tego, ze typ twojej zmiennej to volatile uint8_t* - albo zrzutuj, albo wywal volatile.

    4. podaj co jest wczesniej, ogolnie zapis jest poprawny, pod warunkiem ze wczesniej masz np uint8_t, a nie jakis wskaznik

    5. twoja funkcja przyjmuje wskaznik na zmienna typu uint8_t (ktory jest 16-bitowy), ty natomiast podajesz jej konkretna wartosc i to jeszcze 32-bitowa... nie dziw sie ze to nie zadziala tak jak chcesz.

    4\/3!!
  • #3 6127887
    PO.
    Poziom 20  
    Przepraszam za opóźniony powrót do tematu ale musiałem do tego dojrzeć...

    Freddie Chopin napisał:
    1. char a unsigned char (uint8_t) to co innego. string napisany tak jak w wywolaniu twojej funkcji jest typu char*. musisz wiec albo zmienic swoja funkcje, albo zrzutowac na (uint8_t*), albo to olac...


    Wydawało mi się że dowolna ośmiobitowa zmienna będzie miała taką samą interpretację znakową, skoro ma te same bity 0/1 - rozumiem że tego dotyczy olanie :) . Tymczasem zmieniłem w procedurach wszystko co wychodzi na lcd na char i większość warningów zniknęła.
    Żeby być w przyszłości w zgodzie, jaka komenda jest na rzutowanie (w dowolną stronę) i jak je przeprowadzić?

    Cytat:

    2. znaki podaje sie w apostrofach. to jest znak -> 'a', to jest juz string -> "a" (a + \0)

    Pomogło.

    Cytat:

    3. wynika z tego, ze typ twojej zmiennej to volatile uint8_t* - albo zrzutuj, albo wywal volatile.

    Jak zrozumiałem, volatile odpowiada za globalny zakres zmiennej i jest w związku z tym nie do pominięcia u mnie tam gdzie jest...
    Pytanie jak wyżej o rzutowanie.

    A propos - inicjowanie volatile w procedurach powoduje, że znikają i są błędy... Wydawałoby się że jak globalna to gdziekolwiek ale widać nie.

    Cytat:

    4. podaj co jest wczesniej, ogolnie zapis jest poprawny, pod warunkiem ze wczesniej masz np uint8_t, a nie jakis wskaznik

    Zmienne typu volatile uint8_t, w zasadzie wstawialem to na szybko żeby do testów nie czytać dodatkowo z eepromu. Wynika z tego błędu że kompilator nie toleruje pustych nawiasów kwadratowych - ale jak podam [0] to stwierdzi że liczba spoza zakresu bo będzie chciał czytać jedną komórkę tylko?
    Chyba że ten zapis jest poprawny tylko dla charów? Jeszcze nie testowałem po zmianie.

    Cytat:

    5. twoja funkcja przyjmuje wskaznik na zmienna typu uint8_t (ktory jest 16-bitowy), ty natomiast podajesz jej konkretna wartosc i to jeszcze 32-bitowa... nie dziw sie ze to nie zadziala tak jak chcesz.

    4\/3!!

    Czyli jak chcę stringa podać w zapisie bajtowym a nie literkami to tylko deklarując tablicę wcześniej - przy procedurach obsługi tak jak napisałem? Chyba nie czuję jeszcze do końca wskaźników i stąd się to pojawiło.


    MIałem większą listę pytań ale coś po drodze umknęło, pewnie się jeszcze pojawi...
  • #4 6128088
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Ad. 1. char, unsigned char i signed char w istocie maja ta sama reprezentacje 'binarna', dlatego tylko ostrzezenie, a nie error.

    o rzutowaniu nalezy szukac pod haslem 'type casting'. poniewaz wiecej jest odpowiedzi z c++, gdzie wyglada to troche inaczej, podpowiem, ze wyglada to z grubsza tak:

    
    typ1_t zmienna1;
    typ2_t zmienna2;
    
    ...
    
    zmienna1=(typ1_t)zmienna2;
    zmienna2=(typ2_t)zmienna1;
    


    czyli na przyklad:

    
    uint8_t var_char;
    float var_float;
    
    ...
    
    var_char=(uint8_t)var_float;
    var_float=(float)var_char;
    


    dokladnie ta sama zasada dotyczy typow takich jak const char* czy volatile int.

    3. volatile za globalny zakres? z tego co mi wiadomo idea volatile jest troche inna...

    4. nie wiem czemu daje ci errora. tak czy siak przy inicializacji tablicy, nie mozna podac rozmiaru 0, bo jest to bez sensu. rozmiar powinien byc o 1 wiekszy niz dlugosc lancucha w znakach.

    5. tylko deklarujac tablice wczesniej. skoro nie czujesz wskaznikow, to lepiej o nich poczytaj, bo latwo zrobic fatalny blad przy ich uzyciu.

    4\/3!!
  • #5 6137735
    PO.
    Poziom 20  
    Zacznę od zera: czy możesz mi polecić co mogę sobie darmowo (znaczy w necie) poczytać ogólnie i o szczegółach, bo google zwraca masę na każdy temat - o różnej wartości, z błędami, nie do tego itp? Tyczasem kopię elkę i też jest różnie, nie wszystko co mi może być potrzebne jest, muszę się z kontekstu domyślać czemu tak a nie inaczej no i ogólnie brakuje mi podstaw... Przejdę się jeszcze na strych po stare książki po rodzinie ale nie wiem w ciemno czy są coś warte...

    Gdzie poczytać o volatile :) ? Bo to co pisałem, pisałem z przykładów i omówień z elki i z netu i takie wyciągnąłem wnioski...

    Co do konwersji, to nawet zacząłem poprawiać kod ale wrócę chyba do tego co jest i oleję warningi bo generuję sobie kolejne problemy...
    Dokopałem się tu do ciekawego tematu o deklaracjach (spróbuję potem znaleźć link). Potwierdza że typy unsigned char == uint8_t natomiast char == int8_t, tylko czy z tym nie ma żadnych problemów nigdzie? Pod którego chara można bezboleśnie podstawić znak i pod tablicę których stringa (tekstowego)? Wolę się posługiwać nowszymi deklaracjami, są bardziej uporządkowane jakby (no i krótsze w pisaniu [;). Czy z poziomu użytkowego są jakieś różnice przy operacjach liczbowych (poza zakresem i znakiem - co oczywiste)? Wolę nie deklarować byle gdzie pełnego inta (16bit) jeśli nie potrzebuję...

    Rzutowanie chcę sobie darować bo wiem_co_się_dzieje to znaczy na lcd litery idą i tak w reprezentacji bitowej a liczby konwertuję ręcznie. Mam nadzieję, że to się nie zemści ;) . Na pewno nie zabierze za to na próżno cykli na rzutowanie skoro nie jest potrzebna mi wartość a bity.

    Przy okazji wpadłem też na tematy o optymalizacjach - wyszło z nich że głupie dzielnie zabiera koszmarne ilości cykli i mnie to przeraża, nie zdawałem sobie sprawy, że aż tyle.
    Pytanie ile mogą zajmować (w c a nie w asm) proste if'y i deklaracje pętli? Nie podoba mi się moja lcd_nibble, żywcem przekopiowana z netu ale nie bardzo wiem co z nią zrobić i czy jest sens, bo może mimo długiego zapisu nie ma strat czasowych w wykonywaniu?
    Ile może zajmować goto?

    Napisz proszę proste podstawienie stringa pod tablicę tak jak powinno wyglądać, bo może znowu coś źle zinterpretowałem z przykładów.

    Piszesz, że tablica powinna mieć jedną komórkę więcej na zaznaczenie końca tablicy - to strata 8bitów czyli 1bajtu ;) ! Czy pętla z warunkiem zerowania tablicy nie skończy się "sama" z końcem tablicy, tylko poleci dalej, jeśli jest za nią w pamięci coś niezerowego? Na przykład następna tablica...


    Im dalej w las tym więcej wątpliwości... Dzięki za cierpliwość.
  • #6 6137931
    Freddie Chopin
    Specjalista - Mikrokontrolery
    http://en.wikibooks.org/wiki/Programming:C
    http://pl.wikibooks.org/wiki/Programowanie:C

    poszukaj ksiazki ANSI C - da sie ja znalezc w necie - to sa oficjalne podstawy C.

    rzutowanie signed char na unsigned (lub odwrotnie), rzutowanie z typu wiekszego na mniejszy (int->char, long->int, long long->char, ...) nie zajmuja cykli w ogole - tym prostym sposobem zmuszasz kompilator do nieprodukowania warningow. nic cie to nie kosztuje, a jest eleganckie.

    goto nie powinno nic zajmowac w C, bo stosowanie go bez EXTREMALNEJ potrzeby (np awaryjne wyjscie z kilku zagniezdzonych petli na raz) jest 100% zle. jesli interesuje cie ile zajmuje reszta, to poogladaj wynik deassemblacji, albo uzyj symulatora, albo cos w tym stylu.

    strata 1 bajtu na koniec tablicy to nie jest zadna strata. jak inaczej chcialbys sygnalizowac koniec lancucha? przeciez procek nie zgadnie, ze to juz koniec.

    poczytaj jakis kurs lub ksiazke, bez tego daleko nie zajedziesz.

    4\/3!!
  • #7 8874718
    panbosman
    Poziom 12  
    Witam.
    Podczepię się do tematu rzutowania.
    Czy jeżeli zmienna jest tupu char i zrobimy rzutowanie przed obliczeniem na
    int, to po wykonanej operacji jakiego typu będzie zmienna?
    Czy powróci do char?
  • #9 8874805
    panbosman
    Poziom 12  
    OK.
    mamy zmienną:
    unsigned int czas_podawania_odliczony;


    w obliczeniu rzutuję:

    czas_podawania_sredni=(unsigned long int) czas_podawania_odliczony *1000);


    zmienna była long tylko do obliczenia by nie przekroczyć zakresu zmiennej przy mnożeniu.

    w dalszej części programu zmienna dzielona jest przez 1000 i zapisywana jest do eeprom AVR'a

    eeprom_write_word(30,czas_podawania_odliczony);


    jaką zmienną zapisuje - typu int czy long?
  • #11 8874830
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Wynikiem operacji będzie taki typ jaki ma zmienna "czas_podawania_sredni", a stosując funkcję eeprom_write_word() zapisujesz konkretny rozmiar, a nie rozmiar zależny od zmiennej. Jaki? Wystarczy zerknąć do dokumentacji avr-libc i tej konkretnej funkcji.

    4\/3!!
  • #12 8874834
    tmf
    VIP Zasłużony dla elektroda
    Taką jakiego typu jest zmienna czas_podawania_sredni. To ty decydujesz o typie, nie kompilator. Typecast nie zmienia typu zmiennej, zmienia tylko sposób w jaki jest ona widziana w chwili rzutowania. W twoim kodzie następuje się rzutowanie na ulong, mnożenie i niejawne rzutowanie wyniku na uint.
  • #13 8874878
    panbosman
    Poziom 12  
    a więc tak:
    wklejam całą linijkę kodu
    czas_podawania_sredni=(((czas_podawania_sredni*liczba_wszystkich_podan)+((unsigned long int)czas_podawania_odliczony*100))/(liczba_wszystkich_podan+1));

    czas_podawania_sredni - unsigned long
    liczba_wszystkich_podan-unsigned int
    czas_podawania_odliczony- unsigned int

    Sprawdziłem, że jeżeli nie dokonam rzutowania czas_podawania_odliczony na long to przekroczony zostanie zakres zmiennej- czas_podawania_odliczony pomimo że wynik-czas_podawania_sredni jest w long.
    Chcę się dowiedzieć czy po rzutowaniu zmiennej -czas_podawania_odliczony na long w dalszych częściach programu ta zmienna jest long czy int?
  • #15 8874914
    panbosman
    Poziom 12  
    tmf
    czy możesz mi powiedzieć gdzie następuje niejawne rzutowanie wyniku na uint, bo coś mi się tu nie zgadza. Wynik przekracza zakres uint a pomimo to jest wyświetlany poprawnie na LCD. Sprawdziłem to na kalkulatorze.

    ps. dzięki za odpowiedzi
  • #16 8875681
    tmf
    VIP Zasłużony dla elektroda
    Niejawne rzutowanie ma przy operatorze przypisania (=). Jeśli wartość ulong przypisujesz miennej o typie uint to następuje niejawne rzutowanie z ulong na uint. W pewnych sytuacjach kompilator może wygenerować warninga, że takie przypisanie prowadzi do zgubienia części wyniku.
REKLAMA