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

Konwersja ASCII na int za pomocą atoi w AVR ATtiny2313 UART

mdworak 17 Lip 2006 15:39 1902 5
REKLAMA
  • #1 2827915
    mdworak
    Poziom 10  
    Posty: 12
    Mam problem z przkeształceniem typu danych ASCII pobiernach przez urat na int.
    #include <avr/io.h>
    #include <avr/pgmspace.h>
    #include <stdlib.h>
    #include <avr/interrupt.h>
    #include <avr/signal.h>

    #define FCPU 8000000 //częstotliwość oscylatora CPU
    #define VUART 38400 //prędkość transmisji [b/s]
    #define VUBRR FCPU/(VUART*16)-1 //wpis do UBRR dla VUART
    //#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) konwersja na nowy dostęp
    //#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) konwersja na nowy dostęp
    unsigned char nr_wyj,nr_wej;
    int onoff;int j=0;

    char volatile iofifosio; //wskaźnik odczytu kolejki UART-u
    char volatile izfifosio; //wskaźnik zapisu kolejki UART-u
    char fifosio[32]; //kolejka UART-u
    unsigned char volatile ldanych=0; //liczba danych w buforze fifosio

    SIGNAL(SIG_UART_RECV) //procedura obsługi odbiornika UART-u
    {
    if(ldanych<32)
    {
    fifosio[izfifosio]=UDR; //zapamiętaj odebrany znak
    izfifosio==31?izfifosio=0:++izfifosio; //inkrementacja modulo 32
    ldanych++;
    }
    }
    int main (void)
    {
    UBRR=VUBRR; //ustaw prędkość transmisji
    UCR=1<<RXCIE | 1<<TXCIE | 1<<RXEN; //zezwolenie na przerwania od odbiornika i nadajnika, zezwolenie na odbiór i nadawanie

    sei(); //włącz przerwania

    izfifosio=0; //inicjuj zmienne
    iofifosio=0; //inicjuj zmienne

    DDRB=0xff; //konfiguracja wszystkich wyprowadzeń portu B w trybie wyjścia
    PORTB=0xff; //wyłączenie wszystkich wyprowadzeń wyjść
    DDRD=0x00; //konfiguracja wszystkich wyprowadzeń portu D w trybie wejścia
    PORTD=0xff; //właczenie podciągania wejść brak rezystorów podciągających

    while(1)

    {char * zn=UDR;
    j=atoi(zn);
    }

    }

    Podczas kompilacji pojawia się komunikat warning: initialization makes pointer from integer without a cast. Jeżeli char *zn = UDR; zastąpię char * zn ="1023u"; wszystko jest ok
  • REKLAMA
  • #2 2828130
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    mdworak napisał:

    
    ...
    {char * zn=UDR;
    j=atoi(zn);
    }
    
    }
    

    Podczas kompilacji pojawia się komunikat warning: initialization makes pointer from integer without a cast. Jeżeli char *zn = UDR; zastąpię char * zn ="1023u"; wszystko jest ok

    Bo powyższy zapis , jest do kitu.Próbujesz do wskaźnika wepchnąć wartość zmiennej , a powinieneś adres tej zmiennej.
    
    {char x[2],*zn=x;
    *zn=UDR;
    ...
    


    Piotrek
  • REKLAMA
  • #3 2828269
    mieczotronix
    Poziom 16  
    Posty: 163
    Pomógł: 12
    Ocena: 4
    zumek coś namieszał,


    funkcja atoi zamienia łańcuch na integer
    czyli zmienna zn powinna być pointerem do stringu, czyli ciągu znaków (zwykle cyfr) zakończonych znakiem o wartości zero.

    UDR to, o ile się nie mylę USART Data Register, czyli rejestr w którym jest jeden odebrany znak. Nie ma szans, żeby funkcja atoi coś z niego odczytała.

    musisz czytać kolejne znaki z portu szeregowego
    ewentualnie sprawdzać, czy wczytywane są cyfry (chyba że wiesz że zawsze nadawane są cyfry)
    jeśli cyfry się skończą i przesłany zostanie jakiś separator, musisz zakończyć zebrany ciąg cyfr znakiem o wartośći zero (koniec stringu) i
    tak spreparowany string przekazać do atoi

    pozatym musisz jakoś skomunikować obsługę przerwania z pętlą main. Tak, jak jest to teraz napisane jest niedobrze, bo main nie sprawdza, czy przez obsługę przerwania usartu został już wczytany jakiś sensowny (nadający się do przekształcenia funkcją atoi) ciąg znaków.

    co do errora który ci wywala kompilator, to jemu chodzi o to, że nie napisałeś:
    char *zn = (char *)UDR;

    czyli że nie powiedziałeś kompilatorowi wprost, że wartość UDR ma traktować jako adres pod którym jest jeden znak (jak jest faktycznie) albo pierwszy znak ciągu znaków (jak ty myślisz).
    W tym przypadku jest to bez sensu, bo jest to adres pod którym jest jeden znak, a nie ciąg znaków zakończony zerem.

    Ta procedura obsługii przerwania, która wstawia odczytywane znaki do bufora cyklicznego też się w tej formie, w jakiej jest w twoim programie nie przyda, bo nie preparuje stringu z cyframi zakońćzonego zerem - może się np. zdarzyć tak, że w połowie liczby wskaźnik bufora cyklicznego wyzeruje się i pół liczby będzie na jego końcu, a drugie pół na początku.

    Nie jestem też pewien, czy w avr-gcc zapis
    fifosio[izfifosio]=UDR;
    jest prawidłowy, bo nie wiem na 100% jak zdefiniowano UDR (to jest jakieś makro) i jak się z tym kompilator obchodzi. Jeśli UDR jest adresem portu, to trzebaby napisać

    fifosio[izfifosio]=*(char *)UDR;

    ale mówię, że nie jestem pewien, bo w avr-gcc dopuszczalne są przypisania typu
    PORTD=0xff;
    więc na logikę i powyższe powinno być okej, choć w "zwykłym c" byłoby błędne
  • REKLAMA
  • #4 2828332
    mdworak
    Poziom 10  
    Posty: 12
    UDR jest to rejestr. A zapis char *zn = (char *)UDR; powoduje warning: cast to pointer from integer of different size. Poza tym czy funkcja atoi nie zamienia danych aż do momentu napotkania danej spoza 0..9 w ascii?
  • REKLAMA
  • #5 2828693
    mieczotronix
    Poziom 16  
    Posty: 163
    Pomógł: 12
    Ocena: 4
    masz rację, atoi kończy na znaku nie będącym cyfrą (właśnie sprawdziłem w internecie i się o tym przekonałem).

    UDR jest zdefiniowane tak:
    #define UDR _SFR_IO8(0x0C)

    a z innych plików nagłówkowych, wynika, że (dla AVR-cośtam-cośtam-2313) dalsze definicje wygladają tak:

    #define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + 0x20)
    #define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

    czyli UDR rozwija się jako:
    *(volatile uint8_t *)(0x2c)
    czyli jest to zawartość komórki typu uint8_t o adresie 0x2c;
    więc, żeby typy się zgadzały i żeby dostać adres tej komórki, musisz użyć operatora &:

    *zn = (char *)&UDR;

    ale ponieważ AVR ma tą architekturę, w której dostęp do różnych części pamięci odbywa się za pomocą różnych instrukcji assemblerowych, to nie wiem czy powyższa operacja nie ogłupi całkiem kompilatora i lepiej chyba sobie będzie skopiować gdzieś wartość z UDR i dopiero na kopii siedzącej w "normalnej" pamięci robić atoi.
  • #6 2828883
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    mieczotronix napisał:
    zumek coś namieszał,
    funkcja atoi zamienia łańcuch na integer ...

    A co ma piernik do ... wiadomo czego.Ja tylko stwierdziłem , że nie zapiszesz do pointera(wskaźnika) , w normalny sposób , adresu z przestrzeni I/O czy rejestrów.Czyli że zapis np. char *x=TCCR1A; kompilator skwituje warrning-iem ;) Mało tego . Skąd pewność , że rejestr o kolejnym adresie nie będzie zawierał wartości , którą przetworzy atoi() :?:
    Panowie - troszku pomyślunku ;)
    mieczotronix napisał:

    ... i lepiej chyba sobie będzie skopiować gdzieś wartość z UDR i dopiero na kopii siedzącej w "normalnej" pamięci robić atoi.

    A niby co ja zrobiłem :?:

    Piotrek

    PS
    Możesz sobie załadować wskaźnik przez char *zn=_SFR_MEM_ADDR(UDR);
    , tylko patrz powyżej.
REKLAMA