Problems with printf () on AVR in C floating point

I am going back to C and I was working on an "academic" exercise to sharpen some old skills again. My project revolves around a fairly simple sine generation process. I started just coding x86 on the command line (Fedora Core 20) and everything worked fine. Then I ported the code into AVR and I started to learn a lot. For example how to configure the UART as stdout. However, since sines are floating point, I ran into problems using printf and sprintf.

The program generates 30 sines and then outputs the values ​​to the terminal. The text "Sine:" prints correctly, but then I get question marks for the float. Replacing a variable with a constant had no effect.

The first thing I was suggested was if I remembered the linker option for full floating point support - indeed, I forgot. Again, adding this to my makefile had no effect.

I'm not sure about the copy and paste policy of the code: should I paste it and my makefile here to check?

EDIT: Sorry for the long delay. I did some more reading, including what the first answer is related to. I already read this link before (GNU one) and I included the link in my Makefile, which is why I am so confused. Here's my Makefile in all its glory:

P               = sines
OBJ             = sines.o
PROGRAMMER      = buspirate
PORT            = /dev/ttyUSB0
MCU_TARGET      = atmega328p
AVRDUDE_TARGET  = atmega328p
HZ              = 16000000
DEFS            =
LIBS            = -lprintf_flt -lm
CC              = avr-gcc

override CFLAGS = -g -DF_CPU=$(HZ) -Wall -O1 -mmcu=$(MCU_TARGET) $(DEFS)
override LDFLAGS= -Wl,-Map,$(P).map -u,vfprintf

OBJCOPY     = avr-objcopy
OBJDUMP     = avr-objdump

all: $(P).elf lst text

$(P).elf: $(OBJ)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)

clean:
    rm -rf *.hex *.bin *.map *~ sine*.csv *.o $(P).elf *.lst

lst: $(P).lst

%.lst: %.elf
    $(OBJDUMP) -h -S $< > $@

text: hex bin

hex: $(P).hex
bin: $(P).bin

%.hex: %.elf
    $(OBJCOPY) -j .text -j .data -O ihex $< $@

%.bin: %.elf
    $(OBJCOPY) -j .text -j .data -O binary $< $@

install:  $(P).hex
    avrdude -p $(AVRDUDE_TARGET) -c $(PROGRAMMER) -P $(PORT) -v -U flash:w:$(P).hex 

      

What I'm worried about is that maybe the linker arguments are not in the correct order? From what I can tell, they are only ...

I'm sure my code itself is fine. If you like, I can post it here. Also, thanks for submitting my question here. Didn't quite understand the difference between them!

Here is the source code. It runs on ATmega328P. This current version prints the constant as debug, not the output from sinescalc (), although I know this function works (at least it should, I'm sure I checked with avr-gdb at some point - it definitely works on the command line as well as on the MSP430).

#include <avr/io.h>
//#include <util/delay.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>


static int uart_putchar(char c, FILE *stream);

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

static int uart_putchar(char c, FILE *stream) {
    if (c == '\n')
        uart_putchar('\r', stream);
    while(!(UCSR0A & (1<<UDRE0)));
    UDR0 = c;
    return 0;
}

void sinescalc(double *sinptr, int cycles, int size) {

    double pi = acos(-1);
    double step = ((pi * (cycles*2))/ size);

    float temp;
    double z = step;

    int y;
    for(y = 0; y<= size; y++) {
        temp = sin(z); // calculate the current sine
        *sinptr = (double)temp; // pass it into the array from main()
        z += step; // add the step value to prepare for next sine
        sinptr++; // should move the pointer by correct size of variable
    }
}

int main(void) {

    unsigned long _fosc = 16000000;
    unsigned int _baud = 19200;
    unsigned long _myubrr = _fosc/16/_baud-1;
    unsigned int array_size = 256;

    UBRR0L = (unsigned char)_myubrr;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0); //enable receiver and transmitter

    stdout = &mystdout;
    double sines[array_size];
    double *sinepointer = sines; // set sinepointer to first element of sines array

    sinescalc(sinepointer, 2, array_size); // calculate two cycles of sine, with 255 data points

    int y;
    //char msg[6] = ("Sine: ");
    char output[40];

    for(y = 0; y <= array_size; y++) {
        sprintf(output, "Sine:\t %.6f", 1.354462);
        printf(output);
        printf("\n");
    }



    return 0;
}

      

+3


source to share


2 answers


So I solved it. For anyone wondering, it really MUST come to the order of the arguments after -Wl. Here is the final makefile that seems to work (in the simulator so far):

P           = sines
OBJ         = sines.o
PROGRAMMER  = buspirate
PORT        = /dev/ttyUSB0
MCU_TARGET  = atmega328p
AVRDUDE_TARGET  = atmega328p
HZ          = 16000000
DEFS        =
LIBS        = -lprintf_flt -lm
CC          = avr-gcc

override CFLAGS = -g -DF_CPU=$(HZ) -Wall -O2 -mmcu=$(MCU_TARGET) $(DEFS)
override LDFLAGS= -Wl,-u,vfprintf,-Map,$(P).map

OBJCOPY     = avr-objcopy
OBJDUMP     = avr-objdump

all: $(P).elf lst text

$(P).elf: $(OBJ)
    $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ $^

      

I thought that I had tried this order before and it made a mistake. I think I was wrong before omitting the comma after vfprintf. Also, a suggestion from another site for -u, vfprintf AFTER LDFLAGS (and after the -o section) was also clearly incorrect.



Why he should go exactly in this place, I do not know yet. Maybe someone can shed some light on it? I can't believe the answer was as crazy as this one. I just started moving things around until I stumbled upon this. However, looking at the GNU documentation again, they show -Wl, just before -u, vfprintf . What threw me off was the presence of a -Map, $ (P). I felt like I should go right after -Wl. It looks like -lprintf_flt -lm might come up later. I know that -lmis the ability to link in the GNU math libraries, which is important for floating point math, obviously. I also understand what the other two parameters are doing (link in the correct version of the floating point stream functions compiled into). But as I said before, maybe someone can point me (and others) to a resource regarding the gcc linker options hierarchy? This problem has been bothering me for a week and no one could just point out that -Map might appear after that, but it still requires a comma in between. I could try to flip around the -Map and -u options, still with their commas, to see if the value has a hierarchical meaning ... It doesn't. Just changed it to -Wl, -Map, sines.map, -u, vfprintf and t is still no problem. So the answer was about commas, which I believe meansthat all linker options should be attached with commas? Why shouldn't you be there? I am a bit puzzled but relieved that it works. Now I just need to try it out on hardware, although I'm sure everything will be fine.

Thanks everyone for your help! It was a great introduction to Stack Overflow (and all stacks) and I really hope I can learn a lot and contribute to this community. I have thoroughly re-read all the articles about asking good questions and how the reputation and voting system works, so I hope I get it right and I don’t piss on anyone :)

Hooray!!

+1


source


This page states that to get a complete implementation printf()

that supports printing floats, you should use:



-Wl,-u,vfprintf -lprintf_flt -lm

      

+2


source







All Articles