WedX - журнал о программировании и компьютерных науках

Arduino: printf/fprintf печатает вопросительный знак вместо числа с плавающей запятой

У меня есть следующий код для эскиза Arduino:

#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"

Есть идеи, в чем проблема?

03.01.2013

  • Глупая стандартная библиотека, которая не может обрабатывать преобразования с плавающей запятой? 04.01.2013
  • Что забавно, так это то, что если вы просто сделаете lcd.print(2.0), он напечатает именно то, что вы хотите. Нет необходимости в sprintf или что-то в этом роде. И вы можете указать количество цифр справа от десятичной точки с необязательным вторым параметром. Например. lcd.print(2,3) даст вам 2.000. 19.03.2016

Ответы:


1

Инструментальная цепочка GNU для AVR (которая входит в состав Arduino IDE) по умолчанию использует «минифицированную» версию стандартной библиотеки C, в которой, например, поддержка операций с плавающей запятой уменьшена/убрана из форматированных функций ввода-вывода. (просто для того, чтобы printf() поместиться в несколько килобайт памяти чипа.)

Если вы хотите, чтобы это работало, вы должны скомпоновать другую библиотеку, содержащую обычную версию printf(), используя флаги компоновщика -Wl,-u,vfprintf -lprintf_flt.

03.01.2013
  • Так что мне придется предоставить эти аргументы компоновщика, когда я компоную свой код, или мне придется снова скомпилировать библиотеку и предоставить эти аргументы компоновщика? 04.01.2013
  • @NicolaeSurdu Вам не нужно перекомпилировать библиотеку, вы должны указать эти флаги, когда вы связываете свой собственный код. 04.01.2013
  • Спасибо большое за вашу поддержку! 04.01.2013
  • Я бы не сказал, что минимизирован. минимизированный означает, что у него есть все функции, но код был запутан для экономии места. вы имеете в виду, что код был сокращен за счет удаления некоторых его функций. 17.04.2015

  • 2

    Из avr-libc документации:

    Если требуется полная функциональность, включая преобразования с плавающей запятой, следует использовать следующие параметры:

    -Wl,-u,vfprintf -lprintf_flt -l

    Обратите внимание, что если ваш MCU не поддерживает операции с плавающей запятой, вам следует полностью избегать операций с плавающей запятой. Операции с плавающей запятой будут выполняться в программном обеспечении, которое очень неэффективно и требует много флэш-памяти.

    03.01.2013
  • Вам даже не нужен -lprintf_min IIRC (я некоторое время играл с AVR), libc.a содержит минимизированную библиотеку. 04.01.2013
  • @ H2CO3 Я удалил строку. Я также думаю, что если вы не используете полный printf_flt, у вас не будет поддержки с плавающей запятой с printf. 04.01.2013
  • Да я это и говорю по существу. Вместо этого вы всегда будете получать ?. 04.01.2013

  • 3

    Я сделал это:

    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, когда отправляю значение от 0 до 255 через USB. Я должен найти способ преобразовать ascii char в int.

    например, я набираю 65, и он печатает A

    01.11.2014

    4

    У меня есть старый код, который может помочь, если вы хотите полностью избежать printf и просто должны печатать с заданным количеством цифр до и после десятичной дроби. Этот код компилируется на C и отлично работает в Arduino IDE. Почти наверняка это можно было бы сделать меньшим количеством строк C++. pow10 можно было сделать программно, но полномочия не поддерживались в версии C, с которой я работал:

    #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;
      }
    

    Вы можете поиграть с ним и посмотреть, как он работает здесь: https://ideone.com/AtYxPQ.

    19.03.2016
    Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]