简体   繁体   English

带有浮点数的 C 中 AVR 上 printf() 的问题

[英]Problems with printf() on AVR in C with floating-point

I've been getting back into C, and I've been working on an 'academic' exercise to sharpen some old skills again.我已经回到 C,并且我一直在进行“学术”练习以再次提高一些旧技能。 My project revolves around the rather simple process of generating sines.我的项目围绕着生成正弦的相当简单的过程展开。 I started out just coding for x86 on the command line (Fedora Core 20) and everything worked fine.我开始只是在命令行 (Fedora Core 20) 上为 x86 编码,一切正常。 I then migrated the code to AVR, and I started learning a lot.然后我将代码迁移到 AVR,我开始学到很多东西。 For example, how to set up the UART as stdout.例如,如何将 UART 设置为标准输出。 However, as sines are floating-point, I ran into problems using printf and sprintf.然而,由于正弦是浮点数,我在使用 printf 和 sprintf 时遇到了问题。

The program generates 30 sines, and then prints the values to terminal.该程序生成 30 个正弦波,然后将值打印到终端。 The text "Sine: " prints properly, but then I get question marks for the float.文本“正弦:”打印正确,但随后我得到了浮点数的问号。 Replacing the variable with a constant had no effect.用常量替换变量没有效果。

The first thing I was suggested was if I had remembered the linker option for full floating point support - indeed I had forgotten.我被建议的第一件事是,如果我记得 linker 选项以获得完整的浮点支持 - 事实上我已经忘记了。 Again, adding this into my makefile had no effect.同样,将其添加到我的 makefile 中也没有任何效果。

I'm not sure of the policy here of copying and pasting code: should I paste it and my makefile here for inspection?我不确定这里复制和粘贴代码的政策:我应该把它和我的 makefile 粘贴在这里以供检查吗?

EDIT: Sorry for the long delay.编辑:很抱歉耽搁了很长时间。 I've done some more reading, including what the first answer links to.我做了更多阅读,包括第一个答案链接到的内容。 I had already read that reference before (the GNU one) and I have included the link in my Makefile, which is why I'm so confused.我之前已经阅读过该参考资料(GNU 参考资料),并且我已将链接包含在我的 Makefile 中,这就是我如此困惑的原因。 Here's my Makefile in all its glory:这是我的 Makefile 的全部荣耀:

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 concerned about is that perhaps the linker arguments aren't in the correct order?我担心的是 linker arguments 的顺序可能不正确? From what I can tell, they are but...据我所知,他们只是...

I'm fairly sure my code itself is fine.我相当确定我的代码本身没问题。 If wanted, I can post it here as well.如果需要,我也可以在这里发布。 Also, thanks for transferring my question over here.另外,感谢您将我的问题转移到这里。 Didn't quite understand the difference between the two!不太明白两者的区别!

Here's the source code.这是源代码。 It's being run on an ATmega328P.它在 ATmega328P 上运行。 This current version is printing a constant as a debug, instead of the result from sinescalc(), even though I know that function is working (at least, it should be, I'm pretty sure I checked using avr-gdb at one point -- it definitely works on the command line, and also on an MSP430).当前版本正在打印一个常量作为调试,而不是 sinescalc() 的结果,即使我知道 function 正在工作(至少,它应该是,我很确定我曾经使用 avr-gdb 检查过-- 它绝对适用于命令行,也适用于 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;
}

该页面指出,要获得支持printfloat的完整printf()实现,应使用:

-Wl,-u,vfprintf -lprintf_flt -lm

So I solved it. 所以我解决了。 For those wondering, it really DOES come down to the order of arguments after -Wl. 对于那些想知道的人,它确实可以归结为-Wl之后的参数顺序。 Here's the final makefile that seems to be working (so far, in a simulator): 这是最后一个似乎有效的makefile(到目前为止,在模拟器中):

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 that order before, and it threw an error. 我以为我以前曾尝试过该命令,但它出错了。 What I think I did wrong before was omit the comma after vfprintf. 我认为我之前做错了什么,就是在vfprintf之后省略了逗号。 Also, the suggestion from another website of putting the -u,vfprintf AFTER the LDFLAGS (and after the -o section) was also clearly incorrect. 另外,另一个网站提出的将-u,vfprintf放在LDFLAGS之后(以及-o部分之后)的建议也显然是错误的。


Edit from 2018: Recent versions of GCC seem to require the libraries to come as the last argument, no matter what other arguments there are. 从2018年开始编辑:无论是否有其他参数,最新版本的GCC似乎都要求将库作为最后一个参数。 If you are having problems compiling (I'm not sure what versions of gcc/avr-gcc are being distributed by the various distros) and you are getting a bunch of "implicit declaration of x function" errors, it's because your library arguments are in the wrong spot. 如果您在编译时遇到问题(我不确定各个发行版是否发行了gcc / avr-gcc的哪个版本),并且您收到一堆“ x函数的隐式声明”错误,那是因为您的库参数是在错误的位置。 This just bit me recently, and I haven't seen any information on why this change happened, or which versions are affected. 最近,这只是对我的困扰,而且我还没有看到有关发生此更改的原因或受影响的版本的任何信息。


Why it has to go in exactly that spot, I don't know yet. 我还不知道为什么它必须准确地进入那个位置。 Perhaps someone can shed some light on it? 也许有人可以对此有所了解? I can't believe the answer was as inane as this. 我不敢相信答案像这样愚蠢。 I just started moving things around until I hit on this. 我只是开始四处移动,直到遇到这个问题。 However, after looking at the GNU documentation again, they show the -Wl, directly before the -u,vfprintf . 但是,在再次查看GNU文档之后,它们在-u,vfprintf之前直接显示-Wl What threw me off was the presence of the -Map,$(P).map, which I thought had to go directly after -Wl as well . 使我震惊的是-Map,$(P).map的存在, 我认为它也必须在-Wl 之后直接使用 It seems like the -lprintf_flt -lm can come afterwards. 看来-lprintf_flt -lm可以随后出现。 I know that -lm is the option to link in the GNU math libraries, which is important for floating point math, obviously. 我知道-lm是在GNU数学库中进行链接的选项,显然,这对于浮点数学很重要。 I also understand what the other two options do (link in the correct version of the stream functions with floating-point support compiled in). 我也了解其他两个选项的作用(链接到正确版本的流函数中,并在其中编译了浮点支持)。 But as I said before, perhaps someone can point me (and others) to a resource regarding hierarchy of gcc linker options? 但是正如我之前所说,也许有人可以将我(和其他人)指向关于gcc链接器选项层次结构的资源? This problem has been dogging me for a week, and nobody was able to just point out that the -Map could come afterwards, but still needed a comma in between. 这个问题困扰了我一个星期,没有人能够指出-Map可能会在以后出现,但在两者之间仍然需要逗号。 I might try flipping around the -Map and the -u options, still with their commas, to see if it's THAT hierarchically important... It isn't. 我可能会尝试使用-Map和-u选项(仍然带有逗号),以查看它是否在层次上很重要……不是。 Just changed it to -Wl,-Map,sines.map,-u,vfprintf and t still works with no problem. 只需将其更改为-Wl,-Map,sines.map,-u,vfprintf,t仍然可以正常工作。 So the answer had to do with commas, which I take it means that all linker options need to be attached with commas? 因此答案与逗号有关,我认为这意味着所有链接器选项都必须附加逗号吗? Why doesn't -lm need to be there as well? 为什么-lm也不需要在那里? I'm a littl baffled, but relieved that it's working. 我有点困惑,但对它的工作感到欣慰。 Now I just need to try it on the hardware, though I'm pretty sure it'll work just fine. 现在,我只需要在硬件上尝试一下,尽管我很确定它可以正常工作。

Thanks everyone, for your help! 谢谢大家的帮助! This has been a great introduction to Stack Overflow (and all the Stacks) and I really hope that I can learn a lot, and contribute to this community. 这是Stack Overflow(以及所有Stack)的精彩介绍,我真的希望我能学到很多,并为这个社区做出贡献。 I've been carefully re-reading all the articles about asking good questions, and how the reputation and voting system works, so hopefully I get it right and I don't piss anyone off :) 我一直在认真地重新阅读所有有关问好的问题以及声誉和投票系统如何运作的文章,因此希望我做对了,而且我不会惹恼任何人:)

Cheers!! 干杯!!

If you can have double instead float you can go without linker alterations by ie:如果你可以有 double 而不是 float 你可以 go 没有 linker 改变,即:

double in_volt = (double)temp/1024.0*5.0; 
char in_volt_string[8]; 
dtostrf(in_volt, 6, 4, in_volt_string); 

That solved it for me in AVR Studio 4 with Atmega32.这在带有 Atmega32 的 AVR Studio 4 中为我解决了这个问题。 Then was put to lcd and tera term without problems.然后毫无问题地投入到 lcd 和 tera term 中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM