UART and sprintf

I am going around in circles with this one and hope that someone can help.

I am using an Atmega 328 running at 16MHz and Atmel Studio 7. Fuses are correct.

I am using the USART to send characters to PUTTY on my PC.

I can send single characters and an array of assigned characters BUT when I use sprintf to express a double as a string and then try to send each character of that string, it just responds with a single '?' indicating, I assume, that the character is not recognized.

Thanks for any hints on this issue, it seems that sprintf is not working???

CODE:


#define F_CPU 16000000UL
#define BAUDRATE 9600
#define BD ((F_CPU / (BAUDRATE * 16UL)) - 1)


#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h> // Needed for sprintf

int8_t d;

double xx;



int main(void)
{
uart_init(); // USART initialization code

while (1)
{
//FIRST TEST
while (!( UCSR0A & (1<<UDRE0))); // wait until register is free
UDR0 = 65; // load data to send
// Correctly sends an 'A'
while (!( UCSR0A & (1<<UDRE0))); // wait until register is free
UDR0 = 13; // load data to send carriage return
while (!( UCSR0A & (1<<UDRE0))); // wait until register is free
UDR0 = 10; // load data to send line feed
///////////////////////////////////////

//SECOND TEST
char cc[50];
cc[0]='Z';cc[1]='4'; cc[2]='B'; cc[3]='C'; cc[4]='D';
for (int i=0;i<5;i++)
{
while (!( UCSR0A & (1<<UDRE0))){}; // wait until register is free
UDR0 = cc[i]; // load data to send
_delay_ms(100);
}
//Correctly sends 'Z4BCD'

while (!( UCSR0A & (1<<UDRE0))); // wait until register is free
UDR0 = 13; // load data to send carriage return
while (!( UCSR0A & (1<<UDRE0))); // wait until register is free
UDR0 = 10; // load data to send line feed
//////////////////////////////////////

//THIRD TEST
xx=66.666;
char ccw[50];

sprintf(ccw, "%f", xx);

for (int i=0;i<=5;i++)
{
while (!( UCSR0A & (1<<UDRE0))){}; // wait until register is free
UDR0 = ccw[i]; // load data to send
_delay_ms(100);
}
//JUST SHOWS A '?'.

return(0);
}
}




Does the Atmega have hardware floating point? Perhaps this implementation of sprintf() just doesn't know how to convert doubles to strings. I have an implementation, if you'd like to use it, although it's not particularly fast: https://gist.github.com/Helios-vmg/abd9ef1153c5d43d7a29
Print sprintf's return value.
If it's negative (probably -1), then it failed, presumably because it doesn't know how to convert floating point.

Or if it's easier to print a single character, do something like:

1
2
3
4
    char ch = 'Y';
    if (sprintf(...) < 0)
        ch = 'N';
    // output ch 

Also make sure the compiler is using the maximum warning level.

Also, you should post your code in code tags to preserve indentation. It looks like you have an extra } at the end.
[code]
your code goes here
[/code]
Last edited on
Thanks for these hints.

The Atmega does support floating point.

The %f flag in sprintf is to specifically convert a double to a string.

The return from sprintf is the number of characters in the string created.

I passed in 66.666 which is positive so I don't believe I am dealing with a negative issue.

Also, I had this working on an Atmega 32 previously and it worked perfectly for positive and negative doubles.

Please let me know if you have other hints, my apologies for the code format and there is no extra }, it compiles just fine.

Thanks
I didn't ask what the manual says about sprintf. I'm well aware of what it returns. And I didn't ask if you passed in a negative value. I asked if the return value of sprintf is negative.

Instead of just apologizing for the code format, why not edit the post and recopy/paste it in code tags so it's actually readable?
Here is the code which,I hope, is more readable:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

#define F_CPU 16000000UL
#define BAUDRATE 9600
#define  BD ((F_CPU / (BAUDRATE * 16UL)) - 1)


#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>        // Needed for sprintf

int8_t d;

double xx;



int main(void)
{
    uart_init();        // USART initialization code

    while (1)
    {
      //FIRST TEST
        while (!( UCSR0A & (1<<UDRE0)));        // wait until register is free
        UDR0 = 65;                // load data to send
        // Correctly sends an 'A'
        while (!( UCSR0A & (1<<UDRE0)));        // wait until register is free
        UDR0 = 13;                // load data to send carriage return
        while (!( UCSR0A & (1<<UDRE0)));        // wait until register is free
        UDR0 = 10;                // load data to send line feed
        ///////////////////////////////////////

      //SECOND TEST
        char cc[50];
        cc[0]='Z';cc[1]='4'; cc[2]='B'; cc[3]='C'; cc[4]='D';
        for (int i=0;i<5;i++)
            {
                while (!( UCSR0A & (1<<UDRE0))){};    // wait until register is free
                UDR0 = cc[i];                // load data to send
                _delay_ms(100);
            }
        //Correctly sends 'Z4BCD'

        while (!( UCSR0A & (1<<UDRE0)));        // wait until register is free
        UDR0 = 13;                // load data to send carriage return
        while (!( UCSR0A & (1<<UDRE0)));        // wait until register is free
        UDR0 = 10;                // load data to send line feed
        //////////////////////////////////////

      //THIRD TEST
        xx=66.666;
        char ccw[50];

        sprintf(ccw, "%f", xx);

        for (int i=0;i<=5;i++)
            {
                while (!( UCSR0A & (1<<UDRE0))){};  // wait until register is free
                UDR0 = ccw[i];                              //  load data to send
                _delay_ms(100);
            }
        //JUST SHOWS A  '?'.

        return(0);
    }
} 

It's a lot more readable.

But unless you can tell us if the sprintf is returning a negative value or not then there's not much else we can do.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Put this right after the includes:
#define WAIT    while (!( UCSR0A & (1<<UDRE0)))

// Replace the sprintf with this:
    char ch = 'Y';
    if (sprintf(ccw, "%f", xx) < 0)
        ch = 'N';
    WAIT;
    UDR0 = ch;
    WAIT;
    UDR0 = 13;
    WAIT;
    UDR0 = 10;

If it prints 'Y' then the sprintf returned a "successful" value; if it prints 'N' then it returned a negative value.

EDIT:
Here's a rewrite of your code using some macros:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#define F_CPU      16000000UL
#define BAUDRATE   9600
#define BD         ((F_CPU / (BAUDRATE * 16UL)) - 1)

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>

#define WAIT()     while (!( UCSR0A & (1<<UDRE0)))

#define SEND(n)    do { WAIT();     \
                        UDR0 = n; } while(0)

#define EOL()      do { WAIT();      \
                        UDR0 = 13;   \
                        WAIT();      \
                        UDR0 = 10; } while(0)

int main() {
    uart_init();

    SEND(65);
    EOL();

    char cc[50] = "Z4BCD";
    for (int i = 0; i < 5; i++) {
        SEND(cc[i]);
        _delay_ms(100);
    }
    EOL();

    double xx = 66.666;
    char ccw[50];

    char ch = 'Y';
    int count = sprintf(ccw, "%f", xx);
    if (count < 0)
        ch = 'N';
    SEND(ch);
    EOL();

    for (int i = 0; i < count; i++) {
        SEND(ccw[i]);
        _delay_ms(100);
    }
    EOL();

    return 0;
}
Last edited on
OK, good suggestion,tpb.

The value returned by sprintf is Positive.

Now, What does that tell us?

I really appreciate all this help. Are we getting close?
no. If it does not work and the value made sense, you still have a problem.

compare the result of sprintf and the strlen() of the string you wrote into. Those should be the same.

What do you know about these doubles?
you can always do something rather moronic like this

int left,right;
left = doubleval;
right = (doubleval-left)*10000;
char zeros[10] = {0};
//loop to fill in leading zeros for right, if any
sprintf(target,"%i.%s%i", left,zeros,right);

or try a different format, if it is acceptable, like scientific notation format

the answer could just be that your compiler/library have a bug. Could check updates, or open a ticket with the provider.
Last edited on
Maybe this sprintf() just has a very liberal interpretation of "success".
Unfortunately a "successful" result tells us very little. If it was a "failure" then it would tell us that sprintf doesn't like "%f".

Try printing the count with the following code. If it prints the count then we can see if it's a reasonable value. If it doesn't print it then it means that it doesn't even work for "%d".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#define F_CPU      16000000UL
#define BAUDRATE   9600
#define BD         ((F_CPU / (BAUDRATE * 16UL)) - 1)

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>

#define WAIT()     while (!( UCSR0A & (1<<UDRE0)))

#define SEND(n)    do { WAIT();     \
                        UDR0 = n; } while(0)

#define EOL()      do { SEND(13);   \
                        SEND(10); } while(0)

void send_str(const char *str) {
    for (int i = 0; str[i]; i++) {
        SEND(str[i]);
        _delay_ms(100);
    }
}

int main() {
    uart_init();

    SEND('A');
    EOL();

    send_str("Z4BCD");
    EOL();

    double xx = 66.666;
    char ccw[50];
    int count = sprintf(ccw, "%f", xx);

    char scount[50];
    sprintf(scount, "%d", count);

    send_str(scount);
    EOL();

    send_str(ccw);
    EOL();

    send_str("HELLO");   // does this print?
    EOL();

    return 0;
}

Last edited on
Thanks for all the suggestions.

I just found a setting in Atmel Studio 7 that had to be changed and that fixed everything.

https://startingelectronics.org/articles/atmel-AVR-8-bit/print-float-atmel-studio-7/


Many thanks again.
Topic archived. No new replies allowed.