C语言中AVR的printf()问题

am46iovg  于 2023-02-03  发布在  其他
关注(0)|答案(3)|浏览(176)

我一直在重新使用C语言,并且一直在进行一项“学术”练习,以再次提高一些旧技能。我的项目围绕着生成西内斯的相当简单的过程。我开始只是在命令行上为x86编程(Fedora Core 20)而且一切都运行得很好,然后我把代码迁移到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

我担心的是,也许链接器参数的顺序不正确?从我所能告诉,他们是,但...
我相当肯定我的代码本身是好的。如果需要,我可以张贴在这里以及。另外,感谢转移我的问题在这里。不太明白两者之间的区别!
这是源代码。它在ATmega 328 P上运行。当前版本打印一个常量作为调试,而不是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;
}
lbsnaicq

lbsnaicq1#

本页说明要获得支持打印浮点数的完整printf()实现,您应该用途:

-Wl,-u,vfprintf -lprintf_flt -lm
q8l4jmvw

q8l4jmvw2#

所以我解决了这个问题。对于那些想知道的人来说,它确实归结为-Wl之后的参数顺序。下面是最后一个看起来工作正常的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 $@ $^

我想我以前试过这个顺序,它抛出了一个错误。我认为我以前做错的是省略了vfprintf后面的逗号。另外,另一个网站提出的将-u,vfprintf放在LDFLAGS之后(以及-o部分之后)的建议显然也是不正确的。

    • 编辑自2018:**最近版本的GCC似乎要求函数库作为最后一个参数出现,无论还有什么其他参数。如果你在编译时遇到问题(我不确定不同发行版发布的gcc/avr-gcc版本),并且你会遇到一堆"x函数的隐式声明"错误,那是因为你的函数库参数出现在了错误的位置。这最近让我很困扰,我还没有看到任何关于为什么会发生这种变化,或者哪些版本受到影响的信息。

我还不知道它为什么要放在那个地方。也许有人能解释一下?我不敢相信答案是如此的空洞。我只是开始四处移动东西,直到我发现了这个。然而,在再次查看GNU文档后,他们显示-Wl,直接在-u,vfprintf之前。让我困惑的是-Map,$的存在(P). map,* 我认为它也必须直接在-wl之后 。看起来-lprintf_flt-lm可以在之后。我知道*-lm**是GNU数学库中的链接选项,这对浮点数学很重要,很明显。2我也明白另外两个选项的作用(链接到正确版本的流函数中,并编译了浮点支持)。但正如我之前所说的,也许有人能指点我(和其他人)到一个关于GCC链接器选项层次结构的资源?这个问题已经困扰了我一个星期了,没有人能够指出-Map可以在后面出现,但仍然需要逗号在中间。我可能会尝试翻转-Map和-u选项,仍然使用逗号,看看它在层次结构上是否重要......它不是。只是将其更改为-Wl,-Map,sines.map,-u,vfprintf和t仍然可以正常工作。所以答案必须与逗号有关,我认为这意味着所有链接器选项都需要附加逗号?为什么不需要-lm也在那里?我有点困惑,但它的工作让我松了一口气。现在我只需要在硬件上尝试一下。不过我很确定它会很好用的。
谢谢大家的帮助!这是一个很好的介绍堆栈溢出(和所有的堆栈),我真的希望我能学到很多,并为这个社区做出贡献。我一直在仔细地重读所有的文章,问好问题,以及如何声誉和投票系统的工作,所以希望我得到正确的,我不得罪任何人:)
干杯!

5lhxktic

5lhxktic3#

您可以在打印之前将float(可以是double,但在avr-gcc中直到v9实现时都与float相同)转换为string,并且您可以通过ie不修改链接器:

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

这为我解决了它在AVR工作室4与atmega 32。然后是把液晶显示器和tera术语没有问题。

相关问题