【问题标题】:Problems with printf() on AVR in C with floating-point带有浮点的 C 中 AVR 上的 printf() 问题
【发布时间】:2018-07-20 05:27:16
【问题描述】:

我已经重新开始学习 C,并且一直在进行“学术”练习以再次提高一些旧技能。我的项目围绕着生成正弦的相当简单的过程展开。我刚开始只是在命令行(Fedora Core 20)上为 x86 编码,一切正常。然后我将代码迁移到 AVR,我开始学到很多东西。例如,如何将 UART 设置为标准输出。然而,由于正弦是浮点数,我在使用 printf 和 sprintf 时遇到了问题。

程序生成 30 个正弦,然后将值打印到终端。文本“正弦:”打印正确,但随后我得到浮点数的问号。 用常量替换变量没有效果。

我被建议的第一件事是,如果我记得链接器选项以支持完全浮点 - 实际上我已经忘记了。同样,将其添加到我的 makefile 中没有任何效果。

我不确定这里复制和粘贴代码的政策:我应该将它和我的 makefile 粘贴到这里以供检查吗?

编辑:抱歉耽搁了这么久。我已经阅读了更多内容,包括第一个答案链接到的内容。我之前已经阅读过该参考资料(GNU 参考资料),并且我已将链接包含在我的 Makefile 中,这就是我如此困惑的原因。这是我所有荣耀的 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 

我担心的是链接器参数的顺序可能不正确?据我所知,他们只是......

我很确定我的代码本身没问题。如果需要,我也可以在这里发布。 另外,感谢您将我的问题转移到这里。不太明白这两者的区别!

这里是源代码。它在 ATmega328P 上运行。这个当前版本正在打印一个常量作为调试,而不是 sinescalc() 的结果,即使我知道该函数正在工作(至少,它应该是,我很确定我曾经使用 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;
}

【问题讨论】:

  • 非常感谢您阅读此导览。这个问题属于 StackOverflow.com,因为它讨论了 implementation specific or compiler specific 问题。插入指向问题回答者的小代码 sn-ps 也是好的和适当的。我的猜测是您的浮点数正在提升为双精度,并且需要正确设置格式字符串。
  • 如果您向我们展示您遇到问题的代码会有所帮助,尤其是您调用 printf 等的地方。另外请说明您正在使用的环境(即哪个编译器)。跨度>
  • 我正在使用 avr-gcc,如果需要,我可以向您展示我所有的源代码。实际上,我会将其附加到我上面的问题中。

标签: c


【解决方案1】:

This page 声明要获得支持打印浮点数的完整 printf() 实现,您应该使用:

-Wl,-u,vfprintf -lprintf_flt -lm

【讨论】:

  • 是的,我知道,这就是我添加的内容,不是吗?我尝试使用整个字符串,有人建议我按照现在的方式分解它,两者仍然给我 ?s 用于花车。如何将整个字符串与 -Map,$(P).map 集成而不破坏它?我似乎无法理解这里参数的层次结构。
  • 感谢您的评论——它让我找到了正确的答案!实际上,您的答案是正确的,我只是不知道如何考虑 -Map 选项。
  • 现在我有足够的声誉来支持你的答案!再次感谢您的帮助:)
【解决方案2】:

所以我解决了。对于那些想知道的人,它确实归结为 -Wl 之后的参数顺序。这是似乎可以正常工作的最终生成文件(到目前为止,在模拟器中):

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 $@ $^

我以为我之前尝试过该命令,但它引发了错误。我认为我之前做错的是在 vfprintf 之后省略了逗号。此外,另一个网站的建议将 -u,vfprintf 放在 LDFLAGS 之后(以及 -o 部分之后)显然也是不正确的。


从 2018 年开始编辑: 最新版本的 GCC 似乎要求库作为最后一个参数,无论还有什么其他参数。如果您在编译时遇到问题(我不确定各种发行版正在分发哪些版本的 gcc/avr-gcc)并且您收到一堆“隐式声明 x 函数”错误,那是因为您的库参数是在错误的位置。这只是最近让我感到痛苦,我还没有看到任何关于为什么会发生这种变化或哪些版本受到影响的信息。


为什么它必须进入那个位置,我还不知道。也许有人可以对此有所了解?我不敢相信答案如此空洞。我刚开始移动东西,直到我碰到这个。然而,在再次查看 GNU 文档后,他们在 -u,vfprintf 之前显示了 -Wl,。让我失望的是 -Map,$(P).map 的存在,我认为它也必须直接放在 -Wl 之后。似乎 -lprintf_flt -lm 可以在之后出现。我知道 -lm 是链接 GNU 数学库的选项,这显然对浮点数学很重要。我也了解其他两个选项的作用(链接到正确版本的流函数,其中编译了浮点支持)。但正如我之前所说,也许有人可以将我(和其他人)指向有关 gcc 链接器选项层次结构的资源?这个问题已经困扰了我一个星期,没有人能够指出 -Map 可能会在之后出现,但中间仍然需要一个逗号。我可能会尝试翻转 -Map 和 -u 选项,仍然使用逗号,看看它是否在层次上很重要...... 它不是。只需将其更改为 -Wl,-Map,sines.map,-u,vfprintf 并且 t 仍然可以正常工作。所以答案与逗号有关,我认为这意味着所有链接器选项都需要附加逗号?为什么 -lm 也不需要在那里?我有点困惑,但松了一口气,它正在工作。现在我只需要在硬件上尝试一下,虽然我很确定它会正常工作。

感谢大家的帮助!这是对 Stack Overflow(以及所有 Stacks)的一个很好的介绍,我真的希望我能学到很多东西,并为这个社区做出贡献。我一直在仔细阅读所有关于提出好问题以及声誉和投票系统如何运作的文章,所以希望我做对了,我不会惹恼任何人:)

干杯!!

【讨论】:

    猜你喜欢
    • 2016-06-26
    • 1970-01-01
    • 2022-10-24
    • 2011-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-25
    • 2015-05-23
    相关资源
    最近更新 更多