I am programming on an arm microprocessor and am trying to debug using print statements via UART. I do not want to add stdlibs
just for debugging. Is there a way to
I have found for background debugging, enqueuing characters into a circular buffer which is then drained by a polling routine on the uart transmit register, is my method of choice.
The enqueuing routines are based around a character, string and the variable size (to either hex or fixed width decimal). And a deluxe buffer routine could indicate an overflow with a reserved character.
The approach has the lowest overhead/impact on target operation, can be used (with care) in interrupt routine and the idea is easily transferable, so I have ignored the debugger on all the systems I have used.
Since printing out information through a serial port in an embedded system modifies the main program's timing, the best solution I've found is to send a small message encoded in 2 bytes (sometimes 1 byte works fine), and then using a program in the PC to decode those messages and provide the necessary information, which can include statistics and everything you may need. This way, I'm adding just a little bit of overhead to the main program, and letting the PC do the hard work to process the messages. Maybe something like this:
1 byte message: bits 7:4 = module ID, bits 3:0 = debug info.
2 bytes message: bits 15:12 = module ID, bits 11:8 = debug info, bits 7:0 = data.
Then, in the PC software, you have to declare a table with the messages that map to a given module ID/debug info pair, and use them to be printed on the screen.
Maybe it's not as flexible as the pseudo-printf function, since you need a fixed set of messages in the PC to decode, but it doesn't add too much overhead, as I mentioned before.
Hope it helps.
Fernando
Short answer: Yes, it's entirely possible to do both of your solutions.
The printf function is quite complex if you want to support all of the data types and formats. But it's not that hard to write something that can output a string or an integer in a few different bases (most people only need decimal and hex, but octal probably only adds another 3-4 lines of code once you have decimal and hex).
Typically, printf is written like this:
int printf(const char *fmt, ...)
{
int ret;
va_list args;
va_start(args, fmt)
ret = do_xprintf(outputfunc, NULL, fmt, args);
va_end(args);
return ret;
}
And then the do_xprintf()
does all the hard work for all variants (printf, sprintf, fprintf, etc)
int do_xprintf(void (*outputfunc)(void *extra, char c), void *extra, const char *fmt, va_list args)
{
char *ptr = fmt;
while(1)
{
char c = *ptr++;
if (c == '%')
{
c = *ptr++; // Get next character from format string.
switch(c)
{
case 's':
char *str = va_arg(args, const char *);
while(*str)
{
count++;
outputfunc(extra, *str);
str++;
}
break;
case 'x':
base = 16;
goto output_number;
case 'd':
base = 10;
output_number:
int i = va_arg(args, int);
// magical code to output 'i' in 'base'.
break;
default:
count++;
outputfunc(extra, c);
break;
}
else
count++;
outputfunc(extra, c);
}
return count;
}
Now, all you need to do is fill in a few bits of the above code and write an outputfunc() that outputs to your serial port.
Note that this is rough sketch, and I'm sure there are some bugs in the code - and if you want to support floating point or "widths", you will have to work a bit more at it...
(Note on the extra parameter - for output to a FILE *
that would be the filepointer, for sprintf
, you can pass a structure for the buffer and position in the buffer, or something like that)
The concept of "console" does not have much meaning outside of the context of the specific system you are using. Generally in embedded program there is no real concept of a console.
What you are looking for is a way to get data out of your system. If you want to use the UART, and you are not using a high-level OS like GNU/linux, you will need to write your own I/O drivers. Generally this means first configuring the UART for the desired butrate/parity/flow control via register writes. For any type of robust IO you will want it to be interrupt driven, so you will need to write ISRs for tx and rx that utilize circular buffers.
After you have that done, you can write your own printf like Mats indicated.