Reputation: 1799
I have implemented fputc
and fgetc
in retarget.c to successfully use printf via UART0 on a Cortex-M3.
However, I want a second uart channel for additional debug information. How can I integrate this as nicely as I can UART0 using printf?
For example, using fprintf to a custom target and checking in fputc
which target to send the character to..
E.g. for normal output fprintf(UART0,"..");
and for debug output fprintf(UART1,"..");
But I cannot see if fopen is called for stdout so I am struggling to see how to manually implement this. (If I just call fprintf(RANDOM_VALUE,..)
, I don't know how this will behave.
I guess that once I have it directed to a different 'FILE', then it is simply a matter of checking which is being pointed to within fputc but it is the initial setting of the FILE pointer that I am struggling with.
Perhaps some way to differentiate between stdout and stderr, although then I still have the same problem for getting input from the two separate channels.
Also is fprintf in the microlib? If not, is there a better way to implement this?
Thanks!
Upvotes: 6
Views: 7039
Reputation: 93556
fputc() takes a stream pointer argument, there are two standard output streams stdin, stdout and stderr. At the lower level of the retargeting these are associated with the file descriptors 0, 1, and 2 respectively, you can use this information to associate stderr with the alternate UART at the device driver level.
You can then output debug data using stderr thus:
fprintf (stderr, "Error reading file" ) ;
for example.
A minimal retargeting (specific to Keil ARM-MDK/RealView) might look like this:
struct __FILE
{
int handle;
};
enum
{
STDIN_HANDLE,
STDOUT_HANDLE,
STDERR_HANDLE
} ;
FILE __stdin = {STDIN_HANDLE} ;
FILE __stdout = {STDOUT_HANDLE} ;
FILE __stderr = {STDERR_HANDLE} ;
int fputc(int ch, FILE *f)
{
int ret = EOF ;
switch( f->handle )
{
case STDOUT_HANDLE :
// Write character to UART0
...
ret = ch ;
break ;
case STDERR_HANDLE :
// Write character to UART1
...
ret = ch ;
break ;
default :
break ;
return ret ;
}
Obviously this is also where you might hook in a filesystem if you needed, in which case your __FILE struct would no doubt have additional members.
If you don't want to use stderr for this purpose, you will have to retarget fopen() to translate a device name ("dbg:" for example) into a file descriptor for the desired port and then use stdio to output to the associated stream.
Also is fprintf in the microlib? If not, is there a better way to implement this?
The documentation will tell you, but yes. Microlib stdio support is controlled by the #pragma import(__use_full_stdio)
directive, the documentation is not clear about what is excluded if this is not used. Try it without and use it if anything is missing. That said I would imagine that printf() is implemented as an fprintf() to the stdout stream, so if you have printf() you have fprintf().
Upvotes: 10