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

[LPC2148][C] sprintf() nie działa z double [PAR. SOLVED]

valarian 02 Sie 2009 18:04 4189 10
REKLAMA
  • #1 6849185
    valarian
    Poziom 22  
    Posty: 647
    Pomógł: 33
    Ocena: 112
    Witam,

    Od niedawna jestem świeżym posiadaczem płytki testowej dla LPC2148 (z Kamami) i powoli rozgrywam tajniki tych mikroprocków. Wcześniej sporo pisałem na AVR'ki. Mam problem z funkcją sprintf()/iprintf(). Otóż wszystko działa poprawnie, kiedy wywołuję np. taki kod:

    int a=2;
    char buf[16];
    sprintf(buf, "A: %d", a);
    LCDWrite(buf);
    


    Na wyświetlaczu dostaję piękne "A: 2". Natomiast nie działa, kiedy chcę operować na liczbach zmiennoprzecinkowych:

    double a=2;
    char buf[16];
    sprintf(buf, "A: %0.2lf", a);
    LCDWrite(buf);
    


    Na wyświetlaczu nie widać nic, program zdaje się wieszać (nie wywołuje się już żadna instrukcja po sprintf()). Nie wiem, co może być powodem problemu, może ktoś poratować?
    Dla wszystkiego, wrzucam plik syscalls.c i startup.s:

    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include "uart.h"
    
    #undef errno
    extern int errno;
    
    caddr_t _sbrk(int incr)
    {
       extern char _end;      /* Defined by the linker */
       extern char _ram_end;
       static char *heap_end;
       char *prev_heap_end;
    
       if(heap_end == 0)
       {
          heap_end = &_end;
       }
       prev_heap_end = heap_end;
       if(heap_end + incr > (char*)&_ram_end)
       {
          errno = ENOMEM;
          return (caddr_t) -1;
       }
    
    
       heap_end += incr;
       return (caddr_t) prev_heap_end;
    }
    
    int _isatty(int fd)
    {
      return 1;
    }
    
    int _lseek(int fd, off_t pos, int whence)
    {
      return 0;
    }
    
    int _close(int file) {
    return -1;
    }
    
    int _read(int fd, void *buffer, unsigned int count)
    {
    return(0);
    }
    
    int _fstat(int file, struct stat *st) {
    st->st_mode = S_IFCHR;
    return 0;
    }
    
    int open(const char *name, int flags, int mode) {
    return -1;
    }
    
    int
    _write(int file, char *ptr, int len) {
    	int todo;
    	for (todo = 0; todo < len; todo++) {
    		UART_PutChar((*ptr++));
    	}
    	return len;
    }
    


    
    /* *************************************************************************************************************** 
       crt.s                  STARTUP  ASSEMBLY  CODE 
                            ----------------------- 
    
       Module includes the interrupt vectors and start-up code. 
      *************************************************************************************************************** */ 
    
    /* Stack Sizes */ 
    .set  UND_STACK_SIZE, 0x00000200     
    .set  ABT_STACK_SIZE, 0x00000200      
    .set  FIQ_STACK_SIZE, 0x00000200      
    .set  IRQ_STACK_SIZE, 0x00000200    
    .set  SVC_STACK_SIZE, 0x00000200    
    #.set _MEMMAP,      0xE01FC040 
    
    /* Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs (program status registers) */ 
    .set  MODE_USR, 0x10                  /* Normal User Mode                               */ 
    .set  MODE_FIQ, 0x11                  /* FIQ Processing Fast Interrupts Mode                   */ 
    .set  MODE_IRQ, 0x12                  /* IRQ Processing Standard Interrupts Mode                */ 
    .set  MODE_SVC, 0x13                  /* Supervisor Processing Software Interrupts Mode          */ 
    .set  MODE_ABT, 0x17                  /* Abort Processing memory Faults Mode                   */ 
    .set  MODE_UND, 0x1B                  /* Undefined Processing Undefined Instructions Mode       */ 
    .set  MODE_SYS, 0x1F                  /* System Running Priviledged Operating System Tasks  Mode   */ 
    
    .set  I_BIT, 0x80                     /* when I bit is set, IRQ is disabled (program status registers) */
    .set  F_BIT, 0x40                     /* when F bit is set, FIQ is disabled (program status registers) */
    
    
    .text 
    .arm 
    
    .global   Reset_Handler 
    .global _startup 
    .func   _startup 
    
    _startup: 
    
    # Exception Vectors 
    _vectors:       ldr     PC, Reset_Addr          
                    ldr     PC, Undef_Addr 
                    ldr     PC, SWI_Addr 
                    ldr     PC, PAbt_Addr 
                    ldr     PC, DAbt_Addr 
                    nop                     /* Reserved Vector (holds Philips ISP checksum) */ 
                    ldr     PC, [PC,#-0xFF0]   /* see page 71 of "Insiders Guide to the Philips ARM7-Based Microcontrollers" by Trevor Martin  */ 
                    ldr     PC, FIQ_Addr 
    
    Reset_Addr:     .word   Reset_Handler      /* defined in this module below  */ 
    Undef_Addr:     .word   UNDEF_Routine      /* defined in main.c  */ 
    SWI_Addr:       .word   SWI_Routine         /* defined in main.c  */ 
    PAbt_Addr:      .word   UNDEF_Routine      /* defined in main.c  */ 
    DAbt_Addr:      .word   UNDEF_Routine      /* defined in main.c  */ 
    IRQ_Addr:       .word   IRQ_Routine         /* defined in main.c  */ 
    FIQ_Addr:       .word   FIQ_Routine         /* defined in main.c  */ 
                    .word   0               /* rounds the vectors and ISR addresses to 64 bytes total  */ 
    
    Reset_Handler:  
                /* Setup a stack for each mode - note that this only sets up a usable stack 
                for User mode.   Also each mode is setup with interrupts initially disabled. */ 
                   
                 ldr   r0, =_stack_end 
                 msr   CPSR_c, #MODE_UND|I_BIT|F_BIT    /* Undefined Instruction Mode  */ 
                 mov   sp, r0 
                 sub   r0, r0, #UND_STACK_SIZE 
                 msr   CPSR_c, #MODE_ABT|I_BIT|F_BIT    /* Abort Mode */ 
                 mov   sp, r0 
                 sub   r0, r0, #ABT_STACK_SIZE 
                 msr   CPSR_c, #MODE_FIQ|I_BIT|F_BIT    /* FIQ Mode */ 
                 mov   sp, r0    
                   sub   r0, r0, #FIQ_STACK_SIZE 
                 msr   CPSR_c, #MODE_IRQ|I_BIT|F_BIT    /* IRQ Mode */ 
                 mov   sp, r0 
                 sub   r0, r0, #IRQ_STACK_SIZE 
                 msr   CPSR_c, #MODE_SVC|I_BIT|F_BIT    /* Supervisor Mode */ 
                 mov   sp, r0 
                 sub   r0, r0, #SVC_STACK_SIZE 
                 msr   CPSR_c, #MODE_SYS|I_BIT|F_BIT    /* User Mode */ 
                 mov   sp, r0 
    
                /* copy .data section (Copy from ROM to RAM) */ 
                    ldr     R1, =_text_end
                    ldr     R2, =_data_start
                    ldr     R3, =_data_end
    1:              cmp     R2, R3 
                    ldrlo   R0, [R1], #4 
                    strlo   R0, [R2], #4 
                    blo     1b 
    
                /* Clear .bss section (Zero init)  */ 
                    mov     R0, #0 
                    ldr     R1, =_bss_start 
                    ldr     R2, =_bss_end 
    2:            cmp     R1, R2 
                    strlo   R0, [R1], #4 
                    blo     2b 
    
                /* Enter the C code  */ 
                    b       main 
    
    .endfunc 
    .end
    


    Pozdrawiam,

    Bartosz
  • REKLAMA
  • #2 6907338
    wojtekkk09
    Poziom 15  
    Posty: 99
    Pomógł: 15
    Ocena: 2
    Witam!
    Czy sprintf wywołuje Twoją funkcję _sbrk? Może wstaw do niej jakieś flagi, że sprawdzić co zwraca funkcja. A może problem jest w alokacji pamięci. Zobacz czy działa Ci funkcja malloc.
    Też miałem problemy ze sprintf.

    Pozdrawiam
    Wojt
  • #3 6908371
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    Dołączyłeś bibliotekę libm.a do linkowania? To może być problemem.

    EDIT: chodziło mi oczywiścię o bibliotekę matematyczną libm, a nie o standardową libc...

    4\/3!!
  • REKLAMA
  • #4 6908381
    flapo213
    Poziom 21  
    Posty: 566
    Pomógł: 35
    Ocena: 10
    Witaj kolego.

    Jak nie wiesz co się dzieje i program Ci się wiesza po wykonaniu sprintfa to spróbuj go zdebugować jak dojdziesz do pierwszego sprintfa to rozwiń sobie do asemblera i powoli debuguj zobaczsz czego Ci brakuje. Ja skłaniam się do braku biblioteki jak mój przedmówca.
  • #5 6908492
    valarian
    Poziom 22  
    Posty: 647
    Pomógł: 33
    Ocena: 112
    Poradziłem sobie trochę okrężną drogą. Po zamianie Codesourcery na Yagarto wszystko ruszyło z miejsca (zmieniłem tylko kilka rzeczy w makefile, żeby pasowało do nowego kompilatora).
    Znalazłem na sieci kilka postów z podobnym problemem, większość nierozwiązana. Ciekawe, że występuje na świeżo postawionym Codesourcery, z prostym programem i bez kombinowania.
    No nic, na Yagarto też działa, sprintf() i cała reszta.
    Pozdrawiam i dziękuję za zainteresowanie..

    Val
  • REKLAMA
  • #7 6908543
    valarian
    Poziom 22  
    Posty: 647
    Pomógł: 33
    Ocena: 112
    Ale rozwiązywałem na wiele sposobów, przerobiłem dziesiątki różnych makefilów, skryptów linkera wygrzebanych z sieci, kombinowałem z syscalls.c, kodem programu i jeszcze niewiadomo czym :-) Do szczęścia zabrakło mi tylko JTAG'a żeby debuggować program krok po kroczku.
    Zamiana na Yagarto pomogła, nie jest to eleganckie rozwiązanie, choć w moim wypadku praktycznie nie ma różnicy. W każym razie temat zostawiam otwarty, jako częściowo rozwiązany :-)
  • #8 6909108
    flapo213
    Poziom 21  
    Posty: 566
    Pomógł: 35
    Ocena: 10
    Twój problem polega na tym że no jakby ominąłeś problem. Coś mi się nie chce wierzyć ze codesourcery było z niedokompilowaną biblioteką libc.a, a yagartoo działa. Miałem taki sam problem co Ty ale tyle że z STM32, ja korzystam z Linuxa i przekompilowałem sobie kompilator, ale później sprawdziłem codesourcery i pracuję aktualnie na codesourcery jest ok. Pozdrawiam
  • #9 7086385
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    1. Biorę przykład projektu ARM (akurat ten dla STM32, bo posiadane przezemnie LPC2103 ma za mało pamięci flash na te okrutne funkcje printf()) z mojej strony - https://www.elektroda.pl/rtvforum/topic1339518.html

    2. Wrzucam do main() następujący kod:

    #include <stdio.h>
    #include <math.h>
    #include <errno.h>
    #include <sys/types.h>
    
    caddr_t _sbrk (int size)
    {
       extern char __heap_start;
       extern char __heap_end;
       static char *current_heap_end = &__heap_start;
       char *previous_heap_end;
    
       previous_heap_end = current_heap_end;
    
       if (current_heap_end + size > &__heap_end)
       {
          errno = ENOMEM;
          return (caddr_t) -1;
       }
    
       current_heap_end += size;
    
       return (caddr_t) previous_heap_end;
    }
    
    int main(void)
    {
    [...]
    	char buf[32];
    	int a=2;
    	double d = M_PI;
    
    	sprintf(buf, "int -> %d", a);
    	sprintf(buf, "double -> %0.2f", d);
    [...]
    


    3. Kompiluję, ładuję na procka, uruchamiam i... działa... Po pierwszym sprintf zawartość buf:
    "int -> 2"
    Po drugim:
    "double -> 3.14"

    Zastanowiłem się więc, czemu u was nie działa, a u mnie działa... Breakpoint postawiony w funkcji _sbrk() pokazuje, że w tym momencie cały łańcuch wywołań zajął już prawie 700B stosu! Tak więc bezpieczne minimum dla sprintf() to stos o rozmiarze rzędu 1kB. Jeśli pomniejszę stos do 0.5kB, to obserwuję niekontrolowane działanie całości, bo stos "najeżdża" na zmienne i masakruje wszystko dookoła, program często się wykłada.

    Niestety - zwiększenie rozmiaru stosów w kodzie zaprezentowanym w pierwszym poście nie zabezpiecza absolutnie przed "wjechaniem" na .data i .bss, bo (jak łatwo się domyślić) stosy te figurują jedynie jako cyferki, nie są uwzględnione w skrypcie linkera, więc tak naprawdę nie jest wykrywana kolizja. Nie wiadomo też jak umieszczony jest heap w tym konkretnym przypadku. U mnie jest .data, .bss, .stack a dopiero za tym wszystkim heap, aż do końca pamięci.

    4\/3!!
  • REKLAMA
  • #10 8614618
    aszewczyk
    Poziom 11  
    Posty: 45
    Ten sam problem. Procesor STR912. Udalo mi sie ustalic ze problem polega na wlasnosciach samego kompilatora. Przy konwersji floata procesor laduje w handlerze do data abort(co tlumaczy czemu wisi skoro defaultowo jest tam pusta petla). Problem podobno zalatwia dodanie odpowiednich flag kompilacji(niestety jeszcze nie wiem jakich. Wiec prosba do Freddiego o pomoc jezeli sam nie wygooglam).
  • #11 8614670
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Posty: 13336
    Pomógł: 1712
    Ocena: 870
    Z moich doświadczeń wynika, że źródłem większości problemów jest nie wyrównanie początkowych adresów stosu do 8 (double word) - takie wyrównanie wymagane jest przez standard ARM i bez tego wiele dziwnych rzeczy się dzieje, szczególnie w obszarach takich jak va_list (używane w printf), zmienne o rozmiarze większym od słowa (double, long long, ...) itp.

    4\/3!!

Podsumowanie tematu

✨ Problem dotyczy użycia funkcji sprintf() z formatowaniem liczb zmiennoprzecinkowych (double) na mikrokontrolerze LPC2148. Standardowe wywołanie sprintf() z typem %lf powoduje zawieszenie programu, co wskazuje na brak wsparcia lub błędy w bibliotece standardowej libc lub w konfiguracji linkera. Rozwiązania sugerowane w dyskusji obejmują: sprawdzenie funkcji _sbrk() i alokacji pamięci, dołączenie biblioteki matematycznej libm.a, debugowanie asemblerowe w celu identyfikacji brakujących elementów, a także zmianę kompilatora z Codesourcery na Yagarto, co w praktyce rozwiązało problem. Dodatkowo wskazano, że wyrównanie stosu do 8 bajtów (double word) jest wymagane przez standard ARM i brak tego wyrównania może powodować błędy przy obsłudze typów double i funkcji variadic takich jak printf. Podobne problemy zgłaszano także na innych procesorach ARM (np. STR912, STM32), gdzie konieczne jest odpowiednie ustawienie flag kompilacji i konfiguracja środowiska. Przykładowy kod funkcji _sbrk() oraz uwagi dotyczące pamięci heap zostały udostępnione jako pomoc w konfiguracji środowiska.
Wygenerowane przez model językowy.
REKLAMA