printf or some homebrew ?

Go To Last Post
38 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys

Just a short one.... do you use the build-in printf function, or have you done your own ?

I'm afraid that if I use printf it will take up all my space :/ (never tried it)

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Some compilers have a small printf option.

Leon Heller G1HSM

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I use WinAVR with the gcc compiler

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your compiler provides some fully debugged standard library functions. Why not use them?

I note that your smallest MCU is 16kB. Quite honestly, you can use as much printf() or floating point as you want.

There is no way that you would get much on your Tiny13. By the time you have a software UART or USI, you will have not much flash left.

Printf() comes in several flavours. The full shebang (with f-p) may take 4kB. ImageCraft will probably take a lot more.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

currently I'm developing on a mega48 (4KB)

but I only need some easy way to send small strings and numbers... right now I push every char into the buffer one by one :(

could any maybe link to a code example I could copy so its running using printf and so ?
Think actually the problem is that I'm a little scared for using others code :/

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

found this example in the gcc avr-libc stdio

    #include 

    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);
      loop_until_bit_is_set(UCSRA, UDRE);
      UDR = c;
      return 0;
    }

    int
    main(void)
    {
      init_uart();
      stdout = &mystdout;
      printf("Hello, world!\n");

      return 0;
    }

But as far as I can see I should still make my own interrupt driven send and recieve rutines right ?

And what does

FILE

stand for ?

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For strings and integers I'll typically use my own code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay I implemented it now...
went from 1460 bytes up to 1630
when I wrote

printf("Test streng!\n");

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ooooh! A Cheap Shot at the imagecraft compiler! Now I'll need to compile a program with all 3 printf options to measure the size diffs.

//file printftst.c

#include 
#include 
#include 

int putchar(char c){
  return c;
}

/*
void main(void){ //no printf 137 bytes
  puts("test");
}

void main(void){ //small printf 1910 bytes
  printf("test");
}

void main(void){ //med printf 4626 bytes
  printf("test");
}

void main(void){ //big printf 9709 bytes
  printf("test");
}
*/

#include 

long seconds;
char tmpstr[6];
void main(void){ //smallest prog to do printf("sec: % u \n ",seconds);  takes 890 bytes with ltoa, 716 with ultoa
  puts("sec: ");
  ultoa(tmpstr,seconds,10);
  puts(tmpstr);
  putchar('\n');
}

Imagecraft compiler user

Last Edited: Fri. Sep 2, 2011 - 11:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there anyway to come around using 1500 bytes "just" for printing this string where "seconds" is a 32bit variable ?

printf ("sec: % u \n ", seconds);

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why not just use ultoa() ?

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm really lost right now to be hornets :-/
It gives a really good overview the reply from bobgardner

And indianajones11 I dont know ultao() :/

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

'unsigned long to ascii'. Its in stdlib.h

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
And indianajones11 I dont know ultoa()

http://www.nongnu.org/avr-libc/u...

You need to get familiar with the library functions since you use Avr-Libc . :wink:

uint32_t  biggie = 1234567895;
void  main(void){

  char buffer[11]; // 32 bits is 4 billion+ MAX, so it takes 10 elements and the NULL.
   
  ultoa( biggie, buffer, 10); // now use buffer in whatever uart_string(), or LCD_string() code.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

Last Edited: Sat. Sep 3, 2011 - 12:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looks like it :)
Think I started on a bad leg years back when I first programmed an AVR :(

Got an idea off that standard stuff was WRONG and DANGEROUS to use :/

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

B4Me wrote:
Think actually the problem is that I'm a little scared for using others code
Your attitude makes no sense when dealing with a TRUSTED source !
Quote:
Think I started on a bad leg years back when I first programmed an AVR

Got an idea off that standard stuff was WRONG and DANGEROUS to use

Someone gave you DUM-DUM advice there, but we'll straighten you out ! :D ultoa() takes 200 bytes using my sign. #1 and #2, -Os.

FILE is a file pointer.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Use the standard printf. If your program turns out to be too big THEN worry about it. You can download the soucre for avr-libc printf() and pare it down to the minimum you need, which will probably still be better than trying to write your own...

(Note that if you're using an ATmega48, you have a range of hardware upgrade options that may be easier than struggling to minimize your code size.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

westfw gives good advice.

Also, you'll also want to read the avrlibc manual since it has quite a lot of discussion about this. There are several variations on printf with some being much larger than others.

Smiley

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maybe I got dum-dum advice back then.. but it got me started by making my own code, where I learned the hard way to setup registers and so on :)

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

B4Me wrote:
Maybe I got dum-dum advice back then.. but it got me started by making my own code, where I learned the hard way to setup registers and so on :)

It was certainly bad advice.

Forget about registers and any knowledge of ASM.

Study the documentation of your Standard Library. Note that these exist for your PC, washing machine, ... too.

Occasionally you might miss the odd trick. If you find something will not fit in your mega16, ask.

A typical 'slip' is someone using pow() to produce 2 to the power of 13.

You will run into size problems with modest mega48 programs. Buy a mega328.

If you plan to sell a million mega48 products, and cannot fit into 4kB. For a modest percentage of your profits, I will squeeze it for you.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I will take it all to me, and try to use the libc better and a lot more form today ! :)

uC's: Atmega16, 32, 64, 128 and Attiny13
Lang.: C
Interests: Small scale robots AND sensor monitoring system

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
And what does

FILE

stand for ?


It defines a file. (was it not obvious from the name then?)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Now I'll need to compile a program with all 3 printf options to measure the size diffs.

Here's some results for avr-gcc (built for mega32):

//file printftst.c

#include 
#include 

int uart_putc(char c, FILE *unused) {
#ifdef __AVR_ATmega168__
	while (!(UCSR0A & (1<<UDRE0)));
	UDR0 = c;
#else
	while (!(UCSRA & (1<<UDRE)));
	UDR = c;
#endif
	return 0;
}

FILE uart_str = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);

int main(void){ //no printf 316 bytes, printf-std 1650 bytes, prinf-min 1270 bytes, printf-flt 3190 bytes
  stdout = &uart_str;
  printf("test");
}


/*
#include 

long seconds;
char tmpstr[6];

int main(void){ //smallest prog to do printf("sec: % u \n ",seconds);  takes 668 bytes with ltoa, 638 with ultoa
  stdout = &uart_str;
  puts("sec: ");
  ultoa(seconds,tmpstr, 10);
  puts(tmpstr);
  putchar('\n');
}
*/

As it's possibly tricky to spot the results in there. Just to reiterate:

no printf - that is just puts() 316 bytes,
printf-min 1270 bytes,
printf-std 1650 bytes,
printf-flt 3190 bytes

And for the other program:

668 bytes with ltoa,
638 with ultoa

If you think it's acceptable that for printf float support it takes just short of 10K in your compiler then I guess that's fine but you can hardly complain if people call it inefficient and bloaty.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And this is a nice succint one page readable demo of how to use printf with gcc. Lets put a keyword in it that we can remember so we can seach for it like 'rumpelstiltskin'?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Then for completeness I'll mention the example in the user manual that actually explains my code:

http://www.nongnu.org/avr-libc/u...

In particular:

http://www.nongnu.org/avr-libc/e...
http://www.nongnu.org/avr-libc/e...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, one more question along this subject Cliff.... is it possible to use FILE now to open, read write and close files on an sd card for example? In other words, a file system module for a stand alone mini operating system? I guess a command line interpreter would be a next step?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

is it possible to use FILE now to open, read write and close files on an sd card for example? In other words, a file system module for a stand alone mini operating system?

Nope, the only FILE support are the standard POSIX streams interface to provide stdout, stdin and stderr. That does not mean there's code to support a particular filesystem such as FAT12/16/32 - this stuff is a level above that. This is what the user manual says on the subject:
Quote:
The standard streams stdin, stdout, and stderr are provided, but contrary to the C standard, since avr-libc has no knowledge about applicable devices, these streams are not already pre-initialized at application startup. Also, since there is no notion of "file" whatsoever to avr-libc, there is no function fopen() that could be used to associate a stream to some device. (See note 1.) Instead, the function fdevopen() is provided to associate a stream to a device, where the device needs to provide a function to send a character, to receive a character, or both. There is no differentiation between "text" and "binary" streams inside avr-libc. Character \n is sent literally down to the device's put() function. If the device requires a carriage return (\r) character to be sent before the linefeed, its put() routine must implement this

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

no printf - that is just puts() 316 bytes,
printf-min 1270 bytes,
printf-std 1650 bytes,
printf-flt 3190 bytes

Haven't we been here before?
https://www.avrfreaks.net/index.p...

CodeVision 2.03.9

putsf                   150
int,width               820
long,width             1070
long,width,precision   1472
float,width,precision  3924

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The big PROBLEM with most printf implementations is that they are not re-entrant. I have direct experience with this in the AVR world with ImageCraft and CodeVision. ( Not sure about AVR-GCC.)I have experienced it in other micro "C" environments as well.

So, once you get a lot of interrupt activity and ISR traffic going in your program, the printf output will get jerky, start skipping characters and sending garbage to the terminal. In general, simple and quick ISR's (like e.g. a 10 milli-sec Timer Tick ) don't seem to bother printf, but the more complex the ISRs get, and the greater their time-rate,the more likely you are to provoke problems.

In some cases I have gotten a little relief by writing a more robust putchar in assembler and pushing all registers on entry and popping them on exit, but I believe most of the problem is in printf itself.

In a number of instances I have found a small note in the C user manuals or help about the re-entrancy problem, so read carefully!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The big PROBLEM with most printf implementations is that they are not re-entrant.

That suggests you are either using printf() in an ISR or you are using an RTOS (which is really just calling it from a timer ISR). Neither probably a great idea on AVR anyway.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Chuck-Rowst wrote:
The big PROBLEM with most printf implementations is that they are not re-entrant.

I see absolutely no problem on it, since you need printf only, if you want to display something.
And writing to the LCD or UART was not reentrant also.

Every device, which need a stream of data, can not be accessed by another source.
The stream must be closed first to give access to another task.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Clawson,

Neither. No RTOS involved, no printf's in ISRs.

The situation was pPrintf statements in the main line code. ISR(s) running frequently with complex operations, but no printf's or multiple accesses to the pertinent UART.

Something in the ISR(s) is disturbing something to do with the printf execution. Maybe a specific register, maybe some helper variables, who knows since the source code for printf is not supplied. I know I have bumped into this a few times with different compilers. And I know I have read that printf is non-re-entrant for a few specific compilers in the compiler's own documentation. But, it may be that the printf function is not required to be re-entrant in ANSI-C. Maybe one of the AVR Freak C Experts will know the answer to this.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The situation was pPrintf statements in the main line code. ISR(s) running frequently with complex operations, but no printf's or multiple accesses to the pertinent UART.


How can re-entrancy be an issue if the current operation of printf() cannot be interrupted then the function re-entered? That can only happen from the context of an interrupt.
Quote:

Something in the ISR(s) is disturbing something to do with the printf execution.

But that's not re-entrancy - that's an out and out bug in one of your ISRs - did you have any written in Asm?

(note that printf() (in GCC) is fairly unusual in that it may use the T bit which is, I think, the only use of it in AVR-LibC - perhaps you had some Asm ISR code using T?)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I know I have bumped into this a few times with different compilers.

Perhaps you can list these "different compilers" when exclaiming that "The sky is falling; the sky is falling!".

I have a number of very busy apps with continual interrupt-driven ADC conversions (so an interrupt every 100-200us), several timers firing interrupts, and multiple USART channels so an ISR every 100-200us. A few times a second the display refresh will typically invoke several sprintf(). Surely some of these are interrupted; probably most of them. Yet my system is stable--with MY toolchain.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch,

Don't panic, the sky is NOT falling. And I doubt that any printf anomaly could make even a small section of it fall.

By other compilers I meant other non-AVR compilers. E.g. 8051, 68322, 80186, 68HC11, etc. that I have worked with in the past few decades. I did not run into the problem in each compiler, but in a few of them. I got "beat up" enough (in terms of lost time, mental anquish, etc) trying to analyse the problem, that I am now wary of printf misbehavior masquerading as a self-inflicted program bug.

At some point in time I came across a description of the "printf re-entrancy syndrome" (my phrase) which seemed to describe the same class of symptoms I was seeing in the few times I had encountered it.

I know what re-entrancy is. And the problem here is not that you are calling printf from two different streams, but rather that because printf was not written to be re-entrant, it has the potential to get itself in trouble when you interrupt it in certain ways. Again, since I/we do not have the source code for the printf routines (in the majority of instances), it's not possible to perform a standard code walk-thru to determine where the "problem" may lie.

I am not saying that every C compiler has this problem. I have only had the problem a handful of times myself and I have advised others who experienced the problem in their own projects.

What I am doing is presenting my experience to the group in case someone runs into a similar instance of it. And to comment on the OP's statement that he didn't (or shouldn't) trust built-in C functions. My take on that is if I don't have source code or a really thorough description of the function's operation and implementation, I maintain a degree of skepticism. I agree, my level of trust is dependent on the reputation & pedigree of the compiler.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That all sounds like mumbo-jumbo to me.

I see no reason why any interrupt should break a foreground function. (Unless you are doing something very foolish in the ISR() ).

Depending on your memory model, you can run fairly short of SRAM. Again, you need to be pretty foolish in the first place. If your compiler reports you have used less than 80% of SRAM, you should be pretty safe.

CodeVision does a stack analysis for you.
avr-gcc does not, but the percentage usage is a good clue.

So perhaps Chuck can give us a real-life example.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I know what re-entrancy is. And the problem here is not that you are calling printf from two different streams, but rather that because printf was not written to be re-entrant, it has the potential to get itself in trouble when you interrupt it in certain ways

Wait a minute... so you're defining re-entrancy as the ability of dealing with interrupts in the middle of a function? That's not re-entrancy. Any interrupt rtn that changes the behaviour of the code it interrupted in unwanted ways is buggy.

Re-entrancy is the capability of correctly processing i.e. a printf inside an interrupt routine that has just interrupted a printf right in the middle. If that holds true for i.e. a printf, then your printf is said to be re-entrant.

Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Two important rules:

1. you do not call printf() or any big function from inside an ISR(). period.
2. you do not re-enable interrupts inside an ISR()

Of course you will occasionally need to call small external functions. These must be re-entrant because they could be already running in the foreground.

If you can guarantee that your non-reentrant function is never called reentrantly, you will be fine.

David.