2013-01-03 131 views
7

我有一個Arduino草圖下面的代碼:的Arduino:printf的/打印fprintf中的問號,而不是浮

#include <LiquidCrystal.h> 

LiquidCrystal lcd(12, 11, 5, 4, 3, 2); 
static FILE lcdout = {0} ; 

static int lcd_putchar(char ch, FILE* stream) 
{ 
    lcd.write(ch) ; 
    return (0) ; 
} 

void setup() { 
    lcd.begin(16, 2); 
    fdev_setup_stream (&lcdout, lcd_putchar, NULL, _FDEV_SETUP_WRITE); 
} 

void loop() 
{ 
    stdout = &lcdout; 
    printf("%.2f Volts", 2.0); 
} 

這個問題是在代碼的最後一行。這應該打印出「2.00伏特」,而是打印「伏特」(問號而不是實際的浮點值)。如果我嘗試格式化一個整數,這很好。

所以基本上,如果我用下面的替換printf的線,它會正常工作:

printf("%d Volts", 2); //prints correctly "2 Volts" 

任何想法有什麼問題呢?

+1

一個簡單化向下STDLIB無法處理浮點轉換? –

+0

@DanielFischer沒錯。 – 2013-01-03 20:40:01

+0

有趣的是,如果你只是做一個lcd.print(2.0)它會打印你想要的。不需要sprintf或其他任何。並且您可以使用可選的第二個參數指定小數點右邊的位數。例如。 lcd.print(2,3)會給你「2.000」。 –

回答

6

AVR的GNU工具鏈(包含在Arduino IDE中)默認使用C標準庫的「縮小」版本,其中,例如,浮點支持從格式化中減少/帶走I/O功能(只需要printf()即可,以適應芯片的幾個KBytes長存儲空間)。

如果要使用此功能,則必須再次鏈接另一個包含printf()正常版本的庫,方法是使用-Wl,-u,vfprintf -lprintf_flt鏈接器標誌。

+0

因此,當我鏈接我的代碼時,我必須提供此鏈接器參數,否則我將不得不再次編譯庫並提供此鏈接器參數? –

+0

@NicolaeSurdu你不必重新編譯庫,你必須在鏈接自己的代碼時提供這些標誌。 – 2013-01-03 21:47:45

+0

非常感謝您的支持! –

3

avr-libcdocumentation

如果需要包括浮點轉換的全部功能,下面的選項應使用:

-Wl,-u,vfprintf -lprintf_flt -l

請注意,如果你的MCU沒有按沒有任何浮點支持,您應該嘗試完全避免浮點運算。浮點運算將以非常低效且需要大量閃存的軟件完成。

+0

你甚至不需要'-lprintf_min' IIRC(我一直在玩AVR),'libc.a'包含縮小的庫。 – 2013-01-03 20:44:27

+0

@ H2CO3我刪除了該行。我也認爲,如果你不使用完整的'printf_flt',你就沒有'printf'的任何浮點支持。 – ouah

+0

是的,這就是我所說的。你總是會得到'?'。 – 2013-01-03 20:48:08

0

我做了這一個:

unsigned char buffer[32]; 

void setup() { 
    serial.begin(); 
} 

void loop() { 
    if(serial.available()) { 
    int size = serial.read(buffer); 
    if (size!=0) { 
     //serial.write((const uint8_t*)buffer, size); 
     int bright = atoi((char *) buffer); 

     //int final = ((unsigned int)buffer[0]); 

     //int final = bright -'0'; 
     serial.write(bright); 
     serial.write('\n'); 
    } 
    } 
    serial.poll(); 
} 

,現在我得到一個ASCII字符,當我通過USB從0-255發送的值。 我應該找到一種方法將ascii char轉換爲int。

e.g我輸入65,並將其打印出

0

我有一些舊的代碼,如果你想完全避免printf和只需要前和小數點後的數字給定數量的打印,可以幫助。此代碼以C語言編譯,並且在Arduino IDE中也能正常工作。幾乎肯定可以用更少的C++語言來完成。該pow10可以通過編程完成,但權力沒有在CI的版本支持與合作:

#include <stdio.h> 

/* 
Because lcd and serial don't support printf, and its very costly, and all we need 
is simple formating with a certain number of digits and precision, this ftoa is enough. 
If digits is negative, it will pad left. 
*/ 
#define BUF_LEN 20 
char buf[BUF_LEN]; //need a buffer to hold formatted strings to send to LCD 

int ftoa(char * str, float f, char digits, char precision) { 
char i=0,k,l=0; 
long a,c; 
long pow10[10] = {1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000}; 
unsigned char b; 
char decimal='.'; 

    if(digits>=10) {return 0;}; 
    // check for negative float 
    if(f<0.0) { 
    str[i++]='-'; 
    f*=-1; 
    (0<digits?digits--:digits++); 
    } 
    a=(int)f; // extracting whole number 
    f-=a; // extracting decimal part 
    k = digits; 
    // number of digits in whole number 
    while(k>=0) { 
    c = pow10[k]; 
    c = a/c; 
    if(c>0) { break; } 
    k--; 
    } // number of digits in whole number are k+1 
    if (0<k && digits==k && c>10) { //overflow 
    decimal = 'e'; 
    } 
/* 
extracting most significant digit i.e. right most digit , and concatenating to string 
obtained as quotient by dividing number by 10^k where k = (number of digit -1) 
*/ 
    for(l=abs(k);l>=0;l--){ 
    c = pow10[l]; 
    b = a/c; 
    str[i++]=(l&&!b?' ':b+48); //digit or pad 
    a%=c; 
    } 
    if (precision) {str[i++] = decimal;}; 
/* extracting decimal digits till precision */ 
    if (0>precision) {k=0; precision=abs(precision);} 
    for(l=0;l<precision;l++) { 
    f*=10.0; 
    b = (int)f; //math floor 
    str[i++]=b+48; //48 is ASCII 0 
    f-=(float)b; 
    if (!k && 0==f) { break; } //nothing left, save chars. 
    //won't work if there are any floating point errors. 
    } 
    str[i]='\0'; 
    return i; 
    } 

你可以玩它,看看它是運行在這裏: http://ideone.com/AtYxPQ