Програмиране > C/C++

undefined reference to `round'

(1/3) > >>

jazzman:
Каква е причината да хвърчат тези грешки при компилиране:


--- Код: C ---#include <stdio.h>#include <math.h> int main(){       float i=5.4, j=5.6;       printf("round of  %f is  %f\n", i, round(i));       printf("round of  %f is  %f\n", j, round(j));       return 0;}  

--- Цитат ---dimt@dimt-Satellite-L670 ~/arduino $ gcc loop.c -o loop
/tmp/ccaj7ebD.o: In function `main':
loop.c:(.text+0x28): undefined reference to `round'
loop.c:(.text+0x4a): undefined reference to `round'
collect2: error: ld returned 1 exit status

--- Край на цитат ---

kjufte:
като компилираш трябва да линкнеш math библиотеката


--- Код: Text ---gcc loop.c -lm -o loop

Малко разяснение към темата.

Слагайки #include <math.h>, ти казваш на компайлера, че искаш да ползваш някоя от функциите, които са декларирани в този хедър файл.
В случая във math.h има ред подобен на този:

--- Код: C ---extern double round _PARAMS((double));
Та след като си инклуднал файла и напишеш round(25.7), компайлера знае, че има такава функция дефинирана, която се казва round връща double и взема като аргумент double.
Как обаче точно става тази магия в този момент не е ясно и няма значение.

След като си компилираш *.c файловете до обекти *.о, линкъра се опитва някак си да ги свърже. При тази операция ще види, че има изпозвана някаква функция round и ще се опита да намери дефиницията (не декларацията!) на тази функцията. Тя се намира примерно във файла библиотека libm.a, който се намира някъде в папката на toolchain-а дето ползваш. Библиотеките при C имат разширение ".а" и са един вид архив съдържащ прекомпилирани object файлове. Един от тези обекти вътре съдържа дефиницията на round.

Та за да знае линкъра, че искаш да ползваш някоя от тези библиотеки, ти трябва изрично да му я посочиш.

jazzman:
Аха...стана ми ясно вече :)

2-ри въпрос.

Имам следният код който има за задача да взима аналоговият сигнал от А0 порта и да го преобразува в дигитален, както и да върне изхода в терминала на компа ми. Кода работи и round() функцията също, само дето резултата е една голяма питанка  ??? Имам усещането, че нашият прочут експерт от научиМе с име Питанката е хакнал компа ми :)


--- Код: C ---#include <stdio.h>#include <math.h>#include <avr/io.h>#include <util/delay.h>#define NUM_READINGS 10#define USART_BAUDRATE 9600#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)#define PORT_ON(port,pin) port |= (1<<pin)#define PORT_OFF(port,pin) port &= ~(1<<pin) void USART0Init(void){// Set baud rateUBRR0H = (uint8_t)(UBRR_VALUE>>8);UBRR0L = (uint8_t)UBRR_VALUE;// Set frame format to 8 data bits, no parity, 1 stop bitUCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);//enable transmission and receptionUCSR0B |= (1<<RXEN0)|(1<<TXEN0);} int USART0SendByte(char u8Data, FILE *stream){   if(u8Data == '\n')   {        USART0SendByte('\r', stream);   }//wait while previous byte is completedwhile(!(UCSR0A&(1<<UDRE0))){};// Transmit dataUDR0 = u8Data;return 0;} //set stream pointerFILE usart0_str = FDEV_SETUP_STREAM(USART0SendByte, NULL, _FDEV_SETUP_WRITE); void InitADC(){    // Select Vref=AVcc    ADMUX |= (1<<REFS0);    //set prescaller to 128 and enable ADC     ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);    } uint16_t ReadADC(uint8_t ADCchannel){    //select ADC channel with safety mask    ADMUX = (ADMUX & 0xF0) | (ADCchannel & 0x0F);    //single conversion mode    ADCSRA |= (1<<ADSC);    // wait until ADC conversion is complete    while( ADCSRA & (1<<ADSC) );     return ADC;} /*int ReturnAVG(int r, int n) { int values[10]; int i,j; int  total = 0;  float average;  for (i = 0; i < n; i++) {    values[i] = r;  printf("%d\n", values[i]);   _delay_ms(500);  }  for (j = 0; j < n; j++) {   total = total + values[j]; }average = total / (float) n;    //return(round(average)); return total;   } */int main() { uint16_t A0; uint16_t values[10]; uint16_t total = 0; uint16_t i,j; float average; DDRB=0xff; // Set Port B as Output PORTB = 0x00; //initialize ADCInitADC();//Initialize USART0USART0Init(); //assign our stream to standard I/O streamsstdout=&usart0_str; while(1){     //read 10 values of A0 and recalculating them to mv      for( i = 0; i < 10; i++) {      A0 = ReadADC(0);      values[i] = A0;      printf("Current %d\n", values[i]);      _delay_ms(500);         }            for(j = 0; j < 10; j++) {    total = total + values[j];   }    average = total / (float) NUM_READINGS;    //send the total and avg reading in millivolts to terminal   printf("Total: %d\n",total);   printf("Average %f\n", average);    // reset total to 0   total = 0;    if(round(average) > 152) {    PORT_ON(PORTB,0);   }     else    {   PORT_OFF(PORTB,0);   }  }}  

Изхода е:


--- Цитат ---Current 144
Current 144
Current 144
Current 144
Current 144
Current 144
Current 144
Current 144
Current 144
Current 144
Current 144
Current 144
Current 144
Current 145
Total: 1441
Average ?
Current 145
Current 145
Current 145
Current 146
Current 147
Current 147
Current 147
Current 146
Current 147
Current 146
Total: 1461
Average ?

--- Край на цитат ---

Имам и още едно питане,  как да вкарам  кодa от main() в отделна функция. Написах ReturnAVG, но взема само първият резултат копирайки го 10 пъти. 

kjufte:
Ако хвърлиш едно око на документацията на avr-libc, или по-точно на функцията използвана от printf за форматиран изход vfprintf ще прочетеш следното:


--- Цитат на: http://nongnu.org/ ---Since the full implementation of all the mentioned features becomes fairly large, three different flavours of vfprintf() can be selected using linker options. The default vfprintf() implements all the mentioned functionality except floating point conversions. A minimized version of vfprintf() is available that only implements the very basic integer and string conversion facilities, but only the # additional option can be specified using conversion flags (these flags are parsed correctly from the format specification, but then simply ignored). This version can be requested using the following compiler options:


--- Код: Text ----Wl,-u,vfprintf -lprintf_min   
If the full functionality including the floating point conversions is required, the following options should be used:


--- Код: Text ----Wl,-u,vfprintf -lprintf_flt -lm
--- Край на цитат ---

Общо взето при toolchain-ите за uC, float почти винаги не е включен за printf по подразбиране. Това включва естествено и sprintf и всички функции, които опират до vfprintf.

Та или ползваш горепосочените флагчета при линкване, или както много често се прави си пишеш някаква workaround функция, която разделя стойността на два int-а (преди запетая и след запетая -> modulo).
Втория вариант има предимството, че намаля значително размера на кода, което при микроконтролерите е от голямо значение.

За втората питанка направо ти едитнах малко кода с inline коментари. Кода не е тестван, но чак толкова няма какво да е объркано. Ако има въпроси питай :)


--- Код: C ---#include <stdio.h>#include <math.h>#include <avr/io.h>#include <util/delay.h> #define NUM_READINGS 10#define USART_BAUDRATE 9600#define ADC_CHANNEL 0 #define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)#define PORT_ON(port,pin) port |= (1<<pin)#define PORT_OFF(port,pin) port &= ~(1<<pin)   // първо декларираш всички функцииvoid USART0Init(void);void USART0SendByte(char u8Data, FILE *stream);void InitADC(void);uint16_t ReadADC(uint8_t ADCchannel);float ReadAndReturnAVG(uint8_t channel,  uint16_t *values, int sizeOfValues); // сега идват дефинициите на функциите void USART0Init(void){    // Set baud rate    UBRR0H = (uint8_t)(UBRR_VALUE>>8);    UBRR0L = (uint8_t)UBRR_VALUE;    // Set frame format to 8 data bits, no parity, 1 stop bit    UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);    //enable transmission and reception    UCSR0B |= (1<<RXEN0)|(1<<TXEN0);} // функция, която винаги връща 0 няма нужда да връща нищо// за това сменяме на voidvoid USART0SendByte(char u8Data, FILE *stream){    if(u8Data == '\n')    {        USART0SendByte('\r', stream);    }    //wait while previous byte is completed    while(!(UCSR0A&(1<<UDRE0))){};    // Transmit data    UDR0 = u8Data;} //set stream pointerFILE usart0_str = FDEV_SETUP_STREAM(USART0SendByte, NULL, _FDEV_SETUP_WRITE); void InitADC(void){    // Select Vref=AVcc    ADMUX |= (1<<REFS0);    //set prescaller to 128 and enable ADC     ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);    }  uint16_t ReadADC(uint8_t ADCchannel){    //select ADC channel with safety mask    ADMUX = (ADMUX & 0xF0) | (ADCchannel & 0x0F);    //single conversion mode    ADCSRA |= (1<<ADSC);    // wait until ADC conversion is complete    while( ADCSRA & (1<<ADSC) );        return ADC;} /* * Фунцкията приема като първи параметър канала от който ще чете  * Вторият параметър е указател към масива, в който ще запишем стойностите (uint16_t *values) * Tъй като ReadADC ни връща uint16_t, то и стойностите в масива ще са от този тип. * Накрая катваме на функцията колко голям е масива,който и подаваме, защото това при * 'C'  не може да се определи по време на изпълнение на програмата */float ReadAndReturnAVG(uint8_t channel,  uint16_t *values, int sizeOfValues) {    int i = 0;    int  total = 0;     float average = 0.0f;      for (i = 0; i < sizeOfValues; i++) {         values[i] = ReadADC(channel); // тука може да ползваме функцията ReadADC,защото                                      // тя вече е декларирана най-отгоре и е позната        total = total + values[j];    // няма нужда от екстра loop за сбора        printf("%d\n", values[i]);         _delay_ms(500);    }     average = (float)total / (float)sizeOfValues;     return average;}   int main() {    DDRB = 0xff; // Set Port B as Output    PORTB = 0x00;        //initialize ADC    InitADC();    //Initialize USART0    USART0Init();        //assign our stream to standard I/O streams    stdout = &usart0_str;     uint16_t values[NUM_READINGS] = {0};     float average = 0.0f;        while(1)    {        average = ReadAndReturnAVG(ADC_CHANNEL, values, NUM_READINGS);        printf("Average: %d\n", (int)(round(average))); // кастваш закръглената стойност към int                                                        // и си спестяваш линкване на float за printf    /**        // ако искаш може да изкараш всички стойности от последното четене        // Те са запаметени в масива, който подаваме на функцията        printf("Readings {\n");        int j = 0;        for(j = 0; j < NUM_READINGS; j++) {            printf("   [%d] %d\n", j, values[j]);        }        printf("}\n\n");*/    }}

jazzman:
Thanks buddy :) Оценям труда / помоща ти и то много, просто да знаеш. Ще разгледам кода ти по-подробно довечера и ще питам, ако има нещо неясно за мен ;)

Taзи стойност (141.6 ) ще бъде закръглена към 142, а тази към ( 141.4) към 141 нали? 

Навигация

[0] Списък на темите

[#] Следваща страница

Премини на пълна версия