Using printf and floating point printing for microcontrollers like (solved)

May 11, 2008
20,041
1,289
126
The gcc compiler i use for my arm chips, also supports stdio.h
But when i started programming i did not know how to get printf to work.
I did not know how to connect printf to write (pipe) to a buffer in ram and let me take it from there with either interrupt based uart transfers or through dma based transfers.
I do not mean to use sprintf or snprintf, i really mean printf.

Thus i wrote my own limited to one input variable but functional printf solution that can print in a buffer and transmit uart data through dma, pio or interrupt based transfers.
However, i am in the process to use as much standard functions as possible.
I would like to abandon my own printf function for standard use and only use it when i am memory limited.
When that is not the case, i prefer to use stdio instead.

Does anybody know what i have to do to get printf to print in a by me designated buffer in ram ?
I searched a bit but it is hard to find so far.

edit:
I changed the title to match the problem and solution.
 
Last edited:
May 11, 2008
20,041
1,289
126
I have been looking through the stdio.h header in the include map of the gcc compiler.
I am not sure but it seems printf uses putc or puts.
But i still have to tell printf to use my putc or puts and i wonder how.

I did find an alternative solution. With VA_ARGS , va_list it is possible to make a function that i can pass as many variables as needed just as with printf and then pass it on to snprintf in that function.
snprintf writes to a buffer in ram and copy that to the uart ringbuffer.
After snprintf, i simply call my own uart driver function to dma copy all bytes to the uart.
The only sad thing is that i am still not able to call it printf. That would probably clash.
 

Gryz

Golden Member
Aug 28, 2010
1,551
204
106
Open a pipe.
Let your application write into that pipe.

On the other side, have a task/process/thread/whatever read from that pipe, store stuff in a buffer, and do whatever you want to do with the data.
 
May 11, 2008
20,041
1,289
126
That would make it easier. But i am not running a linux based os.
It is for embedded microcontrollers. Newlib and such.
 

Gryz

Golden Member
Aug 28, 2010
1,551
204
106
I have no idea what that is. But it doesn't have the concept of a pipe ?
 
May 11, 2008
20,041
1,289
126
Not like that, but i have found out that in general function like printf writes its data as a stream.
and that stdin, stdout and stderr is used.
These are standard input and output connections for data streams.
stdout expect \n before it does something.
stderr sends something every character.
I have to figure out how to tell the newlib stdio.h functions to use my stdout and stderr.
I do not want to send every character, i want to save the output stream of data and send it at once.
I do not know yet how to accomplish that.

I am happy that for some reason i have not found out yet, that the floating point conversions are functional.
I think my floating point example code was optimized away but i am not sure .
I would benefit greatly from getting prntf and snprintf functional.


Here is some background about newlib.
https://sourceware.org/newlib/

tps://en.wikipedia.org/wiki/Standard_streams
 

Ken g6

Programming Moderator, Elite Member
Moderator
Dec 11, 1999
16,282
3,904
75
OK, I'm still not sure what your streams are exactly. But did you know you can use fprintf to printf to a stream? E.g. fprintf(stderr, "An error message\n");

stdout expect \n before it does something.
Or you can fflush(stdout);

Edit:
I do not want to send every character, i want to save the output stream of data and send it at once.
I do not know yet how to accomplish that.
Oh! You want to buffer your output. Perhaps you want setbuffer()?
 
Last edited:

sao123

Lifer
May 27, 2002
12,648
201
106
The only way I know to do what you want is to shell redirect into LPT1
I've seen it done this way, http://www.cplusplus.com/forum/beginner/195256/
but that only works again if you have a parallel port printer.


if you want to run a program code to send a stream into a printer, the way I know...you would need to create a device object using a driver API, and interact with that object.
 

serpretetsky

Senior member
Jan 7, 2012
642
26
101
You want to send a printf formatted string chunk by chunk ( 1 byte or 4 bytes at a time, whatever) to some buffer in ram so that a UART/DMA engine can pull the data, send it, and request the next chunkof data?

I'm not sure if there is a standard function for that. I think I would separate into two separate problems, first format the text into some memory buffer, then worry about how to send it through a DMA engine or have some hardware access it.

Also, be careful with printf in general. Depending on your memory limitations printf can occupy a surprisingly large amount of memory. I'm sure with many modern embedded systems with more than a few megabytes of memory it might not matter as much. If you don't need floating point support you can also use sniprintf.
 
May 11, 2008
20,041
1,289
126
OK, I'm still not sure what your streams are exactly. But did you know you can use fprintf to printf to a stream? E.g. fprintf(stderr, "An error message\n");


Or you can fflush(stdout);

Edit:

Oh! You want to buffer your output. Perhaps you want setbuffer()?

Yeah, i have seen that in the explanation about stdout , stderr and stdin.
I am still reading about it to get more familiar with it.

You want to send a printf formatted string chunk by chunk ( 1 byte or 4 bytes at a time, whatever) to some buffer in ram so that a UART/DMA engine can pull the data, send it, and request the next chunkof data?

I'm not sure if there is a standard function for that. I think I would separate into two separate problems, first format the text into some memory buffer, then worry about how to send it through a DMA engine or have some hardware access it.

Also, be careful with printf in general. Depending on your memory limitations printf can occupy a surprisingly large amount of memory. I'm sure with many modern embedded systems with more than a few megabytes of memory it might not matter as much. If you don't need floating point support you can also use sniprintf.

Well, all printf functions and sprintf functions seem to use stdout or stderr to write their stream of bytes to.
All i have to do is write a functional stdout and stderr and i am set.
I think with linux this is done autmatically when one uses pipe. But i use a bare metal embedded mcu. I have no OS running.
That is where newlib comes in.
I have to figure out how to tell the newlib printf functions to use the by me written stdio.
I think the stdio.h has stub functions. And i have to figure out how to replace them.
It might be as easy as turning the stubfunctions into comment and include the header file of the c module where my stdout and stderr functions are. I am going to test that out when i have some time.


I also had to write some minimum functionality standard syscalls like for example _sbrk, _kill _exit and _getpid to get for example malloc functional in the recent past.
But it makes life a lot easier when i can just use standard io that is so common.
The ARM7TDMI mcu i have has enough horsepower to do so.
And when i switch over to the Cortex M4 i have, it will be a lot easier to port my C code since then i will be using atmel studio.
 
Last edited:
May 11, 2008
20,041
1,289
126
OK, I'm still not sure what your streams are exactly. But did you know you can use fprintf to printf to a stream? E.g. fprintf(stderr, "An error message\n");

Oh! You want to buffer your output. Perhaps you want setbuffer()?

I think this might do the trick.
I
void setbuf(FILE *stream, char *buf);
The fprintf function seems interesting.
For the time being until i found the solution, i could use VA_list and then make a function that takes a variable amount of arguments and uses fprintf and my custom write to uart function.
http://www.cplusplus.com/reference/cstdarg/va_start/
http://www.cplusplus.com/reference/cstdarg/va_list/

 
May 11, 2008
20,041
1,289
126
I am a lucky dude, because of course this is a standard problem as it turns out.
I had some time to google now that the adafruit arduino graphics library i was working on porting to plain c and optimizing for 32 bit mcu where possible, is ready.
The supplied fontconverter code by adafruit to create a font converter, i have that compiled and running as well as a linux commandline program on my raspberry pi 3.
And it works. I can now convert freetype fonts to header file by making use of this nifty piece of code made by adafruit.

What is the crux :
Functions like printf have a certain way of printing data. I now know partially how. printf uses stdout or stderr if i am not mistaken, but then it needs to go somewhere.
It turns out this is the function _write.
the function _write is declared in the newlib header files that are supplied with the compiler i use and is declared as a weak function.
A weak function means that i can make my own function with exactly that name and that the compiler/ linker will use my function instead of the weakly declared _write.
I just tested it with some basic putc code i have and it works. I have printf running through a basic putc that is called in _write.
So, i can continue there to improve my code to make a ringbuffer that operates through programmed io or dma.
Of course my life would not be easy because snprintf parsed floating point numbers perfectly but now it does not anymore.
And printf does not work either with parsing floating points. While i have added "-u _printf_float" to the linker switches.
I have to figure out why it sometimes works and sometimes not.


edit:
Forgot to mention that the function _read works the same way for scanf and stdin.
 
Last edited:
May 11, 2008
20,041
1,289
126
This morning i woke up with a revelation , because i read about that printf normally uses the stack but when floating point functionality is used, malloc is used to request memory from the heap.
And my malloc and _sbrk implementation are minimal.
That might be a clue to the problem i have to solve.
 

LevelSea

Senior member
Jan 29, 2013
943
53
91
ugh, I'd be scared about using functions that use the stack or heap for this kind of thing. what happens if you forget to null terminate your string? if you really wanted to use something like printf, i'd roll my own backend and use a static ring buffer or something. i'm generally in the camp of avoiding new/malloc at all costs on baremetal.
 
May 11, 2008
20,041
1,289
126
ugh, I'd be scared about using functions that use the stack or heap for this kind of thing. what happens if you forget to null terminate your string? if you really wanted to use something like printf, i'd roll my own backend and use a static ring buffer or something. i'm generally in the camp of avoiding new/malloc at all costs on baremetal.

Well, for when it needs to be fast, i can always use my own printstring function, it can do everything printf can with the exception of floating point conversion and can only handle one variable.
I am still improving on it.
This function with the dma is fast enough that the mcu can print messages(because it only writes in memory and the dma controller with the uart handles the uart communication) while doing usb communications. I do have to honestly say that my usb implementation is not yet finished.
I got it to work up to the point that windows load the inf file and my device would show up in the device hardware manager when plugging in the usb cable but after that there was an error and windows would disconnect the device again. In the near future when i have my current projects finished i am going to start working on that issue again.

Code:
//*************************************************************************************************
//            PrintString V3.0
// Outputs a string to the uart interface.
// This function is DMA based or PIO based and as such returns immediately after finishing.
// When OS_MEM_PRTSTR_DMAMODE is 1, DMA is used to transfer characters.
// When OS_MEM_PRTSTR_DMAMODE is 0, PIO is used to transfer characters.
// Current Printstring DMA capability is limited to uart0 only.
// See : os_functions.c about the function : [SetPrintStringConfiguration].
//
// When DMA is selected, when printstring is called while a DMA transfer is actived,
// the DMA transfer is stopped. The current string of data will be augmented with the new
// string of data. When printstring is finished, the driver is called and the DMA controller
// is updated and reactivated. The uart is already transmitting 1 character and as such we have to
// wait for the uart to finish the first character or the DMA controller data is not valid
//
//
// Input 1 : *string  of maximum length = 384 characters.
// Input 2 : A numerical value that must be translated to ascii characters
// if %h ,%b ,%d or %c is used.
//
// Printstring recognizes and converts \0 , \t , \r , \n , \\ to the corresponding ascii values.
// Printstring can convert hexadecimal values ,binary values and decimal values,
// by use of %h, %b, %d respectively.
// Printstring can convert an 8 bit binary number stored in ui_value by use of %c into
// an ascii value.
//
// Returns 0 if succesful.
// Returns 1 on error.
//
uint32_t PrintString(char *ch_pointer, uint32_t  value)

But i really want to adhere to standard c functions and that is why i want to use printf.
I discovered something just now.
snprintf always works with printing floating points with the "-u _printf_float" as long as i do not use printf.
This makes me certain that the flaw is to be found in my newlib csupport functions.

I have to make or get proper implementations of :
_write
_read
_sbrk
malloc
_close
_lseek
_fstat
__isatty

Although i think that my simplistic implementation of malloc and _sbrk are the reason for printf floating point conversion not working.

http://danluu.com/malloc-tutorial/
I have been reading this site about a proper implementation of malloc that makes use of linked lists.
I am planning to make such a version based on the writers examples.

This is site is very informative in general for any (hobby)programmer :
http://danluu.com/
 
May 11, 2008
20,041
1,289
126
I just found out that i have forgotten that the malloc i use is the newlib c malloc. I only made _sbrk implementation.
Interesting.
 
May 11, 2008
20,041
1,289
126
Well, i am getting closer and am kind of finished.

For some reason, snprintf and sprintf always work, unless i use printf.
Of course, "-u _printf_float" is added to the linker flags.
I also noticed that snprintf and sprintf , vsprintf and vsnprintf do not need the functions :

_write
_close
_lseek
_fstat
__isatty

It makes sense because these functions only write to a buffer in ram.

Thus i thought of making my own printf after all but one that is compatible with printf but has a slightly different name.
I added a w in front of it because my IDE is called W-ARM.

I made use of (...) and va_args;
Then i tried vsprintf and vsnprintf and passing the floating point as an argument like ("text \r\n %f"....)in the va_ list.
v sprintf and vsnprintf(preferred because it also needs the size of the buffer, hence the n) make use of va argument so that they can be used as variadic functions.
And that is what they are designed for to be used.
And for us humble programmers to make use of.

It works. ^_^

When i do it like this :

Code:
uint32_t WPrintf(const char *fmt, ...)
{
  uint32_t     returnvalue;
  char             buf[256];
  va_list     argument_list;

  va_start(argument_list, fmt);
  returnvalue = vsprintf(buf,255, fmt, argument_list);
  va_end(argument_list);
 
    if (returnvalue > 0)
    {
      PrintString(buf,0);
  }
    return returnvalue;
}

And use this code :

Code:
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

float fl1;
float fl2;
fl1 = 3.14567;
fl2 = -4.789123;
WPrintf("test fl1= %f and fl2= %f \r\n",fl1,fl2);

The text printed is :
test fl1= 3.145670 and fl2= -4.789123.

YAHOOOO!

The size of the buffer will be changed to a define in the header of course to match the size of the uart buffer.
PrintString in the function for test purposes is just temporary and i am going to create an improved function that does the dma and uart handling.
All Printstring does is send the string in buffer to the uart but this can be any device and a own implementation can be made to redirect the text to for example lcd or another uart.
Like for example a function named: " SetPrintOutput".

Printf requires all these functions which i suspect has todo with the ability to redirect output :

_write
_sbrk
malloc
_close
_lseek
_fstat
__isatty

When i do not use fprint, all these functions are not required and i have no need to implement them with the exception of s_brk because the standard c library function malloc requires it.
Malloc is handy.

edit : removed typing errors.
 
Last edited:
May 11, 2008
20,041
1,289
126
Slightly improved version:

Code:
//Wprintf buffer size.
#define WPRINTFBUFFERSIZE     256

uint32_t WPrintf(const char *fmt, ...)
{
  uint32_t     returnvalue;
  char             buf[WPRINTFBUFFERSIZE];
  va_list     argument_list;

  va_start(argument_list, fmt);
    returnvalue = vsnprintf(buf, (WPRINTFBUFFERSIZE -1), fmt, argument_list);
  va_end(argument_list);
 
    if (returnvalue > 0)
    {
      PrintString(buf,0);
  }
    return returnvalue;
}

And tested with the code :

Code:
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

float                                fl1;
float                                fl2;
float                                   fl3;
int16_t                               test;

fl1 = 3.14567;
    fl2 = 3.5;
    fl3 = multiplyfloat(fl1,fl2);

    WPrintf("test fl3 = fl1 * fl2 = %f * %f. fl3 = %f \r\n",fl1,fl2,fl3);
  
    fl1 = exp(2);
  
    fl2 = log(10);
  
    fl3 = fl1 / fl2;

    WPrintf("test fl3 = fl1/fl2 = %f / %f. fl3 = %f \r\n",fl1,fl2,fl3);

    fl1 = 3.14567;
    fl2 = -4.789123;
    WPrintf("test fl1= %f and fl2= %f \r\n",fl1,fl2);
    test = -12;
    WPrintf("negative int test %d \r\n",test);

results in this output on my terminal program:

test fl3 = fl1 * fl2 = 3.145670 * 3.500000. fl3 = 11.009845
test fl3 = fl1/fl2 = 7.389056 / 2.302585. fl3 = 3.209026
test fl1= 3.145670 and fl2= -4.789123
negative int test -12
 
Last edited:
May 11, 2008
20,041
1,289
126
I changed Wprintf to Printf. There is no name clash and it is almost the same with the exception of the capital P.
I like my functions to start with a capital, makes it easier to distinguise them from variables in my code.
And is now readable as a standard c function, making my code more portable as well.

I can now start playing around with the NTC thermistors i bought recently.
 
May 11, 2008
20,041
1,289
126
I have a good tip: The ARM AAPCS and the ABI demand that the stack is aligned to an 8 byte boundary.
I noticed that when i compile a program and the stack is not on an 8 byte boundary, floating point conversion stops working again.
When i make sure with my linker script that the linker uses align(8) before the stack is layout in the ram memory map, then it is always on an 8 byte boundary like 0x0000 or 0x0008.
And the floatingpoint conversion from newlib then always works.
I was busy playing with a BMP280 pressure sensor from BOSCH sensortech and of course the floating point conversion stopped working. I made a BOOBOO.
Because i was adding more code to the interrupt routine that runs from ram and then the stack is shifted a few addresses up. And of course, it was no longer at an 8 byte boundary.
When that was solved, the floating point conversion works again.
I read the ARM AAPCS, it says that it must be done. I just do not know why.
 
sale-70-410-exam    | Exam-200-125-pdf    | we-sale-70-410-exam    | hot-sale-70-410-exam    | Latest-exam-700-603-Dumps    | Dumps-98-363-exams-date    | Certs-200-125-date    | Dumps-300-075-exams-date    | hot-sale-book-C8010-726-book    | Hot-Sale-200-310-Exam    | Exam-Description-200-310-dumps?    | hot-sale-book-200-125-book    | Latest-Updated-300-209-Exam    | Dumps-210-260-exams-date    | Download-200-125-Exam-PDF    | Exam-Description-300-101-dumps    | Certs-300-101-date    | Hot-Sale-300-075-Exam    | Latest-exam-200-125-Dumps    | Exam-Description-200-125-dumps    | Latest-Updated-300-075-Exam    | hot-sale-book-210-260-book    | Dumps-200-901-exams-date    | Certs-200-901-date    | Latest-exam-1Z0-062-Dumps    | Hot-Sale-1Z0-062-Exam    | Certs-CSSLP-date    | 100%-Pass-70-383-Exams    | Latest-JN0-360-real-exam-questions    | 100%-Pass-4A0-100-Real-Exam-Questions    | Dumps-300-135-exams-date    | Passed-200-105-Tech-Exams    | Latest-Updated-200-310-Exam    | Download-300-070-Exam-PDF    | Hot-Sale-JN0-360-Exam    | 100%-Pass-JN0-360-Exams    | 100%-Pass-JN0-360-Real-Exam-Questions    | Dumps-JN0-360-exams-date    | Exam-Description-1Z0-876-dumps    | Latest-exam-1Z0-876-Dumps    | Dumps-HPE0-Y53-exams-date    | 2017-Latest-HPE0-Y53-Exam    | 100%-Pass-HPE0-Y53-Real-Exam-Questions    | Pass-4A0-100-Exam    | Latest-4A0-100-Questions    | Dumps-98-365-exams-date    | 2017-Latest-98-365-Exam    | 100%-Pass-VCS-254-Exams    | 2017-Latest-VCS-273-Exam    | Dumps-200-355-exams-date    | 2017-Latest-300-320-Exam    | Pass-300-101-Exam    | 100%-Pass-300-115-Exams    |
http://www.portvapes.co.uk/    | http://www.portvapes.co.uk/    |